Initial import of HatariWii 0.0.5

This commit is contained in:
benoa 2018-05-25 20:45:09 +02:00
commit d439638ea4
594 changed files with 1050349 additions and 0 deletions

361
CMakeLists.txt Normal file
View File

@ -0,0 +1,361 @@
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
if(POLICY CMP0026)
# Should be removed if cmake_minimum_required >= 2.8.8
cmake_policy(SET CMP0026 OLD)
endif(POLICY CMP0026)
project(Hatari C)
SET(APP_NAME "Hatari")
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(CheckIncludeFiles)
include(CheckFunctionExists)
include(CheckStructHasMember)
include(CheckCCompilerFlag)
include(DistClean)
# Set build type to "Release" if user did not specify any build type yet
# Other possible values: Debug, Release, RelWithDebInfo and MinSizeRel
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif(NOT CMAKE_BUILD_TYPE)
# set(CMAKE_VERBOSE_MAKEFILE 1)
# ##########################
# Conditional build features
# ##########################
set(ENABLE_SDL2 0
CACHE BOOL "Enable building with libSDL2 instead of v1.2")
set(ENABLE_DSP_EMU 1
CACHE BOOL "Enable DSP 56k emulator for Falcon mode")
set(ENABLE_TRACING 1
CACHE BOOL "Enable tracing messages for debugging")
set(ENABLE_SMALL_MEM 0
CACHE BOOL "Enable to use less memory - at the expense of emulation speed")
set(ENABLE_WINUAE_CPU 0
CACHE BOOL "Enable WinUAE CPU core (experimental!)")
# Run-time checks with GCC "mudflap" etc features:
# - stack protection
# - checking of pointer accesses
#
# Before running CMake, install "mudflap" library development package
# (libmudflap0-4.4-dev in Debian Squeeze and libmudflap-devel in Fedora).
#
# After this you can configure Hatari with Mudflap:
# cd build; make clean; cmake -D ENABLE_MUDFLAP:BOOL=1 -D ..
# If everything's fine, CMake output should include:
# Performing Test MUDFLAP_AVAILABLE - Success"
#
# After (re-)building Hatari, run it with something like this:
# MUDFLAP_OPTIONS="-viol-gdb" src/hatari --sound off --mic off
# (sound&mic are disabled because threading doesn't work well with Mudflap)
#
# For more info:
# http://gcc.gnu.org/wiki/Mudflap_Pointer_Debugging
#
set(CMAKE_REQUIRED_LIBRARIES "mudflap")
CHECK_C_COMPILER_FLAG("-fmudflapth" MUDFLAP_AVAILABLE)
set(CMAKE_REQUIRED_LIBRARIES "")
if(MUDFLAP_AVAILABLE)
set(ENABLE_MUDFLAP 0
CACHE BOOL "Enable GCC run-time stack/pointer debugging - with huge slowdown")
endif(MUDFLAP_AVAILABLE)
if(APPLE)
set(ENABLE_OSX_BUNDLE 1
CACHE BOOL "Built Hatari as Mac OS X application bundle")
# set(CMAKE_OSX_ARCHITECTURES "i386" CACHE STRING "Target architectures" FORCE)
# set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk" CACHE STRING "10.6 SDK" FORCE)
# set(CMAKE_OSX_DEPLOYMENT_TARGET "10.5" CACHE STRING "Target Min 10.5" FORCE)
set(ADDITIONAL_INCLUDES ${FRAMEWORKS})
set_source_files_properties(${FRAMEWORKS} PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks)
else()
set(ENABLE_OSX_BUNDLE 0
CACHE BOOL "Built Hatari as Mac OS X application bundle")
endif(APPLE)
# ####################
# Check for libraries:
# ####################
if(ENABLE_SDL2)
find_package(SDL2 REQUIRED)
if(NOT SDL2_FOUND)
message(FATAL_ERROR "SDL2 library not found!")
endif(NOT SDL2_FOUND)
set(SDL_INCLUDE_DIR ${SDL2_INCLUDE_DIR})
set(SDL_LIBRARY ${SDL2_LIBRARY})
set(SDLMAIN_LIBRARY ${SDL2MAIN_LIBRARY})
else(ENABLE_SDL2)
find_package(SDL REQUIRED)
if(NOT SDL_FOUND)
message(FATAL_ERROR "SDL library not found!")
endif(NOT SDL_FOUND)
endif(ENABLE_SDL2)
find_package(Math)
find_package(Readline)
if(READLINE_FOUND)
set(HAVE_LIBREADLINE 1)
endif(READLINE_FOUND)
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_LIBZ 1)
set(HAVE_ZLIB_H 1)
endif(ZLIB_FOUND)
find_package(PNG)
if(PNG_FOUND)
set(HAVE_LIBPNG 1)
endif(PNG_FOUND)
if (NOT ENABLE_OSX_BUNDLE)
find_package(X11)
if(X11_FOUND)
set(HAVE_X11 1)
endif(X11_FOUND)
endif()
find_package(PortAudio)
if(PORTAUDIO_FOUND)
set(HAVE_PORTAUDIO 1)
endif(PORTAUDIO_FOUND)
find_package(CapsImage)
if(CAPSIMAGE_FOUND)
set(HAVE_CAPSIMAGE 1)
endif(CAPSIMAGE_FOUND)
# ################
# CPP Definitions:
# ################
# Test for large file support:
execute_process(COMMAND getconf LFS_CFLAGS
OUTPUT_VARIABLE DETECTED_LFS_CFLAGS
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(DETECTED_LFS_CFLAGS)
add_definitions(${DETECTED_LFS_CFLAGS})
# message(STATUS "Large filesystem flags: ${DETECTED_LFS_CFLAGS}")
endif(DETECTED_LFS_CFLAGS)
# Additional CFLAGS suggested by the SDL library:
if(ENABLE_SDL2)
add_definitions(-DWITH_SDL2)
execute_process(COMMAND pkg-config --cflags-only-other sdl2
OUTPUT_VARIABLE DETECTED_SDL_CFLAGS
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
else(ENABLE_SDL2)
execute_process(COMMAND pkg-config --cflags-only-other sdl
OUTPUT_VARIABLE DETECTED_SDL_CFLAGS
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
endif(ENABLE_SDL2)
if(DETECTED_SDL_CFLAGS)
add_definitions(${DETECTED_SDL_CFLAGS})
# message(STATUS "Additional CFLAGS of SDL: ${DETECTED_SDL_CFLAGS}")
endif(DETECTED_SDL_CFLAGS)
if(ENABLE_OSX_BUNDLE)
# Use OSX native alert windows
add_definitions(-DALERT_HOOKS=1)
if(ENABLE_SDL2)
# We still want to use our SDLMain.m with SDL2
add_definitions(-DSDL_MAIN_NEEDED=1)
endif(ENABLE_SDL2)
endif(ENABLE_OSX_BUNDLE)
# ###########################
# Check for optional headers:
# ###########################
check_include_files(termios.h HAVE_TERMIOS_H)
check_include_files(strings.h HAVE_STRINGS_H)
check_include_files(malloc.h HAVE_MALLOC_H)
check_include_files(${SDL_INCLUDE_DIR}/SDL_config.h HAVE_SDL_CONFIG_H)
check_include_files(sys/times.h HAVE_SYS_TIMES_H)
check_include_files("sys/socket.h;sys/un.h" HAVE_UNIX_DOMAIN_SOCKETS)
# #############################
# Check for optional functions:
# #############################
check_function_exists(cfmakeraw HAVE_CFMAKERAW)
check_function_exists(setenv HAVE_SETENV)
check_function_exists(select HAVE_SELECT)
check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN)
check_function_exists(memalign HAVE_MEMALIGN)
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
check_function_exists(nanosleep HAVE_NANOSLEEP)
check_function_exists(alphasort HAVE_ALPHASORT)
check_function_exists(scandir HAVE_SCANDIR)
check_function_exists(statvfs HAVE_STATVFS)
check_function_exists(fseeko HAVE_FSEEKO)
check_function_exists(ftello HAVE_FTELLO)
check_function_exists(flock HAVE_FLOCK)
check_function_exists(strlcpy HAVE_LIBC_STRLCPY)
check_struct_has_member("struct dirent" d_type dirent.h HAVE_DIRENT_D_TYPE)
# #############
# Other CFLAGS:
# #############
# GCC pointer debugging, huge run-time slowdown
if(ENABLE_MUDFLAP)
# SDL mixer threads so have to use threaded mudflap version
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -fstack-protector-all -fmudflapth")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fmudflapth -lmudflap")
endif(ENABLE_MUDFLAP)
# Warning flags:
if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-qual -Wbad-function-cast -Wpointer-arith")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-prototypes -Wstrict-prototypes")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wwrite-strings -Wsign-compare")
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wno-unused-parameter -Wno-empty-body")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-security")
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow -D_FORTIFY_SOURCE=2 -Werror")
endif(CMAKE_COMPILER_IS_GNUCC)
# Building Hatari w/o optimization is no fun...
IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS "-O ${CMAKE_C_FLAGS}")
ENDIF (CMAKE_BUILD_TYPE STREQUAL "Debug")
# ####################
# Paths configuration:
# ####################
if(NOT BINDIR)
set(BINDIR bin)
endif()
if(NOT DATADIR)
set(DATADIR share/hatari)
endif()
if(NOT BIN2DATADIR)
if(WIN32)
set(BIN2DATADIR "."
CACHE STRING "Relative path from bindir to datadir")
elseif(ENABLE_OSX_BUNDLE)
set(BIN2DATADIR "../Resources"
CACHE STRING "Relative path from bindir to datadir")
else()
set(BIN2DATADIR "../share/hatari"
CACHE STRING "Relative path from bindir to datadir")
endif(WIN32)
mark_as_advanced(BIN2DATADIR)
endif()
if(NOT MANDIR)
set(MANDIR share/man/man1)
endif()
if(NOT DOCDIR)
set(DOCDIR share/doc/hatari)
endif()
if(NOT ETCDIR)
if(WIN32)
set(ETCDIR .)
else()
set(ETCDIR /etc)
endif()
endif()
if(NOT ICONDIR)
set(ICONDIR share/icons/hicolor)
endif()
if(ENABLE_OSX_BUNDLE)
# put the config files in the app's bundle
add_definitions(-DCONFDIR=\"../Resources\")
else()
add_definitions(-DCONFDIR=\"${ETCDIR}\")
endif()
# #########################################
# Create config.h and recurse into subdirs:
# #########################################
configure_file(${CMAKE_SOURCE_DIR}/cmake/config-cmake.h
${CMAKE_BINARY_DIR}/config.h)
add_subdirectory(src)
add_subdirectory(doc)
add_subdirectory(tools)
include(FindPythonInterp)
if(PYTHONINTERP_FOUND)
add_subdirectory(python-ui)
endif(PYTHONINTERP_FOUND)
if(UNIX AND NOT ENABLE_OSX_BUNDLE)
add_subdirectory(share)
endif()
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake/Uninstall.cmake)
# ###################################################################
# Print a summary of the optional libraries with a short explanation:
# ###################################################################
message( "
Libraries summary :
-------------------
")
if(SDL2_FOUND)
message(" - sdl :\tusing SDL2 v${SDL2_VERSION_STRING}")
else()
if(SDL_VERSION_STRING)
message(" - sdl :\tusing SDL v${SDL_VERSION_STRING}")
else()
message(" - sdl :\tusing SDL1")
endif(SDL_VERSION_STRING)
endif(SDL2_FOUND)
if(READLINE_FOUND)
message( " - readline :\tfound, enables history/completion in the debugger" )
else()
message( " - readline :\tnot found, install it to enable debugger history/completion" )
endif(READLINE_FOUND)
if(ZLIB_FOUND)
message( " - zlib :\tfound, allows to use zip/gz files directly" )
else()
message( " - zlib :\tnot found, install it to use zip/gz files" )
endif(ZLIB_FOUND)
if(PNG_FOUND)
message( " - png :\tfound, allows to compress screenshot/avi files using png" )
else()
message( " - png :\tnot found, install it to compress screenshot/avi files using png" )
endif(PNG_FOUND)
if(PORTAUDIO_FOUND)
message( " - portaudio :\tfound, enables the microphone input in Falcon mode" )
else()
message( " - portaudio :\tnot found, install it to enable the Falcon microphone input" )
endif(PORTAUDIO_FOUND)
if(CAPSIMAGE_FOUND)
message( " - capsimage :\tv${CAPSIMAGE_VERSION} found, allow to use .IPF, .RAW and .CTR disk images" )
else()
message( " - capsimage :\tv${CAPSIMAGE_VERSION} not found, install it to use .IPF, .RAW and .CTR disk images" )
endif(CAPSIMAGE_FOUND)
message( "" )

6
Makefile Normal file
View File

@ -0,0 +1,6 @@
all:
cd src; $(MAKE) -f Makefile.wii
clean:
cd src; $(MAKE) -f Makefile.wii clean
run:
cd src; wiiload hatari.dol

132
Makefile-default.cnf Executable file
View File

@ -0,0 +1,132 @@
# Makefile configuration for Hatari.
#
# Use of '?=' for assignment allows overriding the given value with
# an environment variable, like this:
# HOSTCC=my-hostcc make
# or:
# export HOSTCC=my-hostcc
# make
#
# Following variables can be overridden like that:
# CPPFLAGS, LDFLAGS, LIBS, HOSTCC, DATADIR, CONFDIR, BINDIR, MANDIR, DOCDIR
#
# GNU make itself supports overriding CC like this:
# make CC=my-cc
# Set the C compiler (e.g. gcc)
CC = gcc
OPTFLAGS = -O2
# Architecture specific settings
#
# Omap2/ARMv6:
# OPTFLAGS += -mfpu=vfp -mfloat-abi=softfp -march=armv6 -finline-limit=64
#
# Wii/Gekko:
# OPTFLAGS = -MMD -MP -MF -O3 -mrvl -mcpu=750 -meabi -mhard-float
# CPPFLAGS = -DGEKKO -I$(DEVKITPRO)/libogc/include -I$(DEVKITPRO)/libogc/include/sdl
# LDFLAGS = -L$(DEVKITPRO)/libogc/lib/wii -Wl,-Map,hatari.map
# LIBS = -lz -lfat -lwiiuse -lbte -lasnd -logc -lm
# What warnings to use
WARNFLAGS = -Wmissing-prototypes -Wstrict-prototypes -Wsign-compare \
-Wbad-function-cast -Wcast-qual -Wpointer-arith \
-Wall -Wwrite-strings # -Wshadow -Wcast-align -Werror
ifneq ($(MUDFLAP),)
# Run-time checks with GCC "mudflap" etc:
# - stack protection
# - checking of pointer accesses (AFAIK works only on x86)
#
# Before build, install "libmudflap<version>-<gcc-version>-dev"
# package (libmudflap0-4.3-dev in Debian Lenny).
#
# To build, use:
# make clean; make MUDFLAP=1
#
# To run, use something like (disable sound as it can break things):
# MUDFLAP_OPTIONS="-viol-gdb" ./hatari --sound off
#
# For more info, see (for now, works properly only for x86 gcc):
# http://gcc.gnu.org/wiki/Mudflap_Pointer_Debugging
#
RUNCHECKS = -fstack-protector-all -fmudflapth #-fmudflapir
LDRUNCHECKS = -fmudflapth -lmudflap
endif
# Set flags passed to the compiler (e.g. optimization flags)
CFLAGS := -g $(WARNFLAGS) $(OPTFLAGS) $(RUNCHECKS)
# Set flags passed to the preprocessor (e.g. -I<include dir>)
CPPFLAGS ?=
# Additional libraries and linker flags:
LIBS ?= -lz -lm # -lreadline
LDFLAGS ?= $(LDRUNCHECKS)
# Ranlib - for generating an index of an archive
RANLIB = ranlib
# The native C compiler.
# This is normally the same as $(CC) unless you are using a cross compiler.
HOSTCC ?= $(CC)
# Native C compiler flags:
HOSTCFLAGS = -g -O -Wall
# Native linker flags:
HOSTLDFLAGS =
# SDL-Library configuration (compiler flags and linker options) - you normally
# don't have to change this if you have correctly installed the SDL library!
SDL_CFLAGS := $(shell sdl-config --cflags)
SDL_LIBS := $(shell sdl-config --libs)
# libpng configuration (for PNG format screenshots)
PNG_LIBS := $(shell pkg-config --silence-errors --libs libpng)
ifneq ($(PNG_LIBS),)
PNG_CFLAGS := -DHAVE_LIBPNG=1 $(shell pkg-config --cflags libpng)
endif
# X11 configuration (for SDL window embedding)
X11_LIBS := $(shell pkg-config --silence-errors --libs x11)
ifneq ($(X11_LIBS),)
X11_CFLAGS := -DHAVE_X11=1 $(shell pkg-config --cflags x11)
endif
# PORTAUDIO configuration (to support Falcon microphone)
PORTAUDIO_LIBS := $(shell pkg-config --silence-errors --libs portaudio-2.0)
ifneq ($(PORTAUDIO_LIBS),)
PORTAUDIO_CFLAGS := -DHAVE_PORTAUDIO=1 $(shell pkg-config --cflags portaudio-2.0)
endif
# Here you can define the default data directory for Hatari.
# The emulator looks there for the default TOS image etc.
# For example you can use the local directory with "." or if you want
# a system-wide installation, use something like "/usr/share/hatari".
DATADIR ?= .
# In this folder, Hatari searches the global configuration file.
# /etc or /usr/local/etc is a good place for this.
CONFDIR ?= /etc
# The executable will be installed in BINDIR
#BINDIR ?= /usr/local/bin
# The man-page will be install in MANDIR
#MANDIR ?= /usr/local/share/man/man1
# All other documentation will be installed in DOCDIR
#DOCDIR ?= /usr/local/share/doc/hatari
# Program used for "make install"
#INSTALL = install -c
#INSTALL_PROGRAM = $(INSTALL) -s -m 755
#INSTALL_SCRIPT = $(INSTALL) -m 755
#INSTALL_DATA = $(INSTALL) -m 644

62
Makefile-wii.cnf Executable file
View File

@ -0,0 +1,62 @@
# Makefile configuration for Hatari.
#
# Use of '?=' for assignment allows overriding the given value with
# an environment variable, e.g. like this "make CC=my-gcc"
#
# Following variables can be overridden:
# CC, CPPFLAGS, LDFLAGS, HOSTCC, DATADIR, CONFDIR, BINDIR
# Set the C compiler (e.g. gcc)
CC = powerpc-eabi-gcc
# Include directories
INCLUDE = -I$(DEVKITPRO)/libogc/include -I$(DEVKITPRO)/libogc/include/SDL
DEFINES = -DHAVE_DIRENT_D_TYPE
# Architecture specific optimizations
#
# Omap2/ARMv6:
# OPTFLAGS += -mfpu=vfp -mfloat-abi=softfp -march=armv6 -finline-limit=64
OPTFLAGS = -MMD -MP -MF -flto -O2 -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float
# What warnings to use
WARNFLAGS = -Wsign-compare \
-Wbad-function-cast -Wcast-qual -Wpointer-arith \
-Wall -Wwrite-strings # -Wshadow -Wcast-align -Werror
# Set flags passed to the compiler (e.g. optimization flags)
CFLAGS := -g $(WARNFLAGS) $(INCLUDE) $(OPTFLAGS) $(DEFINES)
# Set flags passed to the preprocessor (e.g. -I<include dir>)
CPPFLAGS ?=
# Additional libraries and linker flags:
LIBS = -lz # -lreadline
LDFLAGS ?= -g -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float -Wl,-Map,hatari.map
# Ranlib - for generating an index of an archive
RANLIB = ranlib
# The native C compiler.
# This is normaly the same as $(CC) unless you are using a cross compiler.
HOSTCC ?= gcc
# Native C compiler flags:
HOSTCFLAGS = -g -O -Wall
# Native linker flags:
HOSTLDFLAGS =
# SDL-Library configuration (compiler flags and linker options) - you normally
# don't have to change this if you have correctly installed the SDL library!
#SDL_CFLAGS := -I$(DEVKITPRO)/libogc/include
SDL_LIBS := -L$(DEVKITPRO)/libogc/lib/wii -lSDL_ttf -lSDL_image -lsmpeg -lSDL -lpng -ljpeg -lvorbisidec -lfat -lwiiuse -lbte -lz -logc -lm -lwiikeyboard
# Here you can define the default data directory for Hatari.
# The emulator looks there for the default TOS image etc.
# For example you can use the local directory with "." or if you want
# a system-wide installation, use something like "/usr/share/hatari".
#BIN2DATADIR ?= /apps/hatari

View File

@ -0,0 +1,80 @@
/*
* Hatari - Fix for compliation using Visual Studio 6
*
* This file is distributed under the GNU General Public License, version 2
* or at your option any later version. Read the file gpl.txt for details.
*/
#if defined(_VCWIN_)
#pragma comment(lib, ".\\SDL\\lib\\sdl.lib") // sdl.lib
#pragma comment(lib, ".\\zlib\\win32\\zlib1.lib") // zlib1.lib
#endif
#if defined(_VCWIN_)
#include <tchar.h>
#include <stdio.h>
#include <stdarg.h>
#endif
#include "log.h"
extern FILE *TraceFile;
#if defined(_VCWIN_)
#ifndef _INC_HATARI_TRACE
#define _INC_HATARI_TRACE
#if ENABLE_TRACING
void LOG_TRACE(int level, const char* format, ...)
{
va_list x;
va_start(x,format);
if ( HatariTraceFlags & level ) _vftprintf(TraceFile,format, x);
va_end (x);
};
#else /* ENABLE_TRACING */
void LOG_TRACE(int level, ...)
{
}
#endif /* ENABLE_TRACING */
void LOG_TRACE_PRINT(char* strFirstString, ...)
{
va_list x;
va_start(x,strFirstString);
_vftprintf(TraceFile,strFirstString, x);
va_end (x);
};
#endif
#endif
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#ifdef __cplusplus
extern "C"
{
#endif
extern int SDL_main(int argc, char *argv[]);
int main(int argc, char *argv[])
{
return SDL_main(argc,argv);
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
return SDL_main(1,&lpCmdLine);
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,135 @@
/*
* Hatari - Fix for compliation using Visual Studio 6
*
* This file is distributed under the GNU General Public License, version 2
* or at your option any later version. Read the file gpl.txt for details.
*
* This file tells Visual Studio to ignore a number of relatively minor "warnings" that have found their
* way into the HAtari source. None of the "warnings" will hamper the running or compliation or HAtari
* but it is possible not addressing them may make it difficult for other developers to be sure of the
* intentions of the original coders against whos code these warnings are raised.
* As long as the original coder was aware of the warnings and of the implicit result of adding no
* explicit casts to remove them then things are good.
*
* 2009 Vaughan Kaufman
*
*/
#if defined(_VCWIN_) // Stop Visual Studio complaining about all the implicit type conversions (wish we would make them explict guys/girls)
#pragma warning (disable:4244) // conversion with potential data loss
#pragma warning (disable:4761) // integral size mismatch in argument
#pragma warning (disable:4146) // unary minus operator applied to unsigned type
#pragma warning (disable:4018) // signed / unsigned mismatch
#pragma warning (disable:4102) // ignore unused label warning
#pragma warning (disable:4049) // (this one is silly, its not important) compiler limit, end of line numbering
#pragma warning (disable:4800) // Performance Warning on Conversion of bool to int
#pragma warning (disable:4805) // warning C4805: '|=' : unsafe mix of type 'int' and type 'bool' in operation
#endif
/*
* KVK - Fix for compliation using Visual Studio 6
*
* Microsoft have created multiple versions of the standard C calls, a specific version exists for each type of string encoding
* format (in this case UNICODE (Wide) and ANSI (Ascii) versions. This has lead to there being versions with a A or a W after
* the name to signify the encoding. There are other additional reasons why they have these different versions (something to
* do with the change from BSTR to string class passing I think, anyone?). The upshot is, we need to add a _ to the beginning of
* some of the function names for HAtari to compile..
*
* 2009 Vaughan Kaufman
*
*/
#if defined(_VCWIN_)
#define STATIC_INLINE static __inline
#define GLOB_ONLYDIR 0
#include <io.h>
#include <direct.h>
#include <stdbool.h>
#include <tchar.h>
#include <dirent.h>
#define stat _stat
#define S_IRUSR _S_IREAD
#define S_IWUSR _S_IWRITE
#define S_ISDIR(val) (_S_IFDIR & val)
#define S_IFDIR _S_IFDIR
#define strncasecmp _strnicmp
#ifndef strcasecmp
#define strcasecmp _stricmp
#endif
#define chdrive _chdrive
#define strdup _strdup
#define getcwd _getcwd
#define fileno _fileno
#define unlink _unlink
#define access _access
#ifndef mkdir
#define mkdir(name,mode) _mkdir(name)
#endif
#define rmdir _rmdir
#define chmod _chmod
#define itoa _itoa
#define stricmp _stricmp
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define __attribute__(x) /* x */
// For new UI
typedef unsigned short mode_t;
#ifndef _NEW_UI_TYPES
#define _NEW_UI_TYPES
typedef signed __int8 int8;
typedef unsigned __int8 uint8;
typedef signed __int16 int16;
typedef unsigned __int16 uint16;
typedef signed __int32 int32;
typedef unsigned __int32 uint32;
typedef signed __int64 int64;
typedef unsigned __int64 uint64;
typedef void* memptr;
#endif
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#ifndef __inline__
#define __inline__ __inline
#endif
/* The variable-types used in the CPU core: */
typedef uint8_t uae_u8;
typedef int8_t uae_s8;
typedef uint16_t uae_u16;
typedef int16_t uae_s16;
typedef uint32_t uae_u32;
typedef int32_t uae_s32;
typedef uae_u32 uaecptr;
extern void LOG_TRACE(int level, ...);
extern void LOG_TRACE_PRINT(char* strFirstString, ...);
#ifdef JOY_BUTTON1
#undef JOY_BUTTON1
#endif
#ifdef JOY_BUTTON2
#undef JOY_BUTTON2
#endif
extern void Win_OpenCon(void);
#endif

27
cmake/DistClean.cmake Normal file
View File

@ -0,0 +1,27 @@
#
# "distclean" target for removing the generated files from CMake
#
if(UNIX)
add_custom_target(distclean COMMENT "Cleaning up for distribution")
# Clean up Hatari specific files:
foreach(CLEAN_FILE config.h install_manifest.txt src/hatari
src/cpu/build68k src/cpu/cpudefs.c src/cpu/cpuemu_*.c
src/cpu/cpustbl.c src/cpu/cputbl.h src/cpu/gencpu
src/uae-cpu/build68k src/uae-cpu/gencpu
src/uae-cpu/cpudefs.c src/uae-cpu/cpuemu.c
src/uae-cpu/cpustbl.c src/uae-cpu/cputbl.h
tools/hmsa/hmsa tools/debugger/gst2ascii
python-ui/conftypes.py)
add_custom_command(TARGET distclean POST_BUILD
COMMAND rm -f ${CLEAN_FILE}
DEPENDS clean)
endforeach(CLEAN_FILE)
# Clean up files that can appear at multiple places:
foreach(CLEAN_FILE CMakeFiles CMakeCache.txt '*.a' '*.1.gz'
cmake_install.cmake Makefile)
add_custom_command(TARGET distclean POST_BUILD
COMMAND find . -depth -name ${CLEAN_FILE} | xargs rm -rf
DEPENDS clean)
endforeach(CLEAN_FILE)
endif(UNIX)

31
cmake/FindCapsImage.cmake Normal file
View File

@ -0,0 +1,31 @@
IF (CAPSIMAGE_INCLUDE_DIR)
# Already in cache, be silent
SET(CAPSIMAGE_FIND_QUIETLY TRUE)
ENDIF (CAPSIMAGE_INCLUDE_DIR)
# Choose the library version to use : 4 or 5
SET(CAPSIMAGE_VERSION 4)
if(CAPSIMAGE_VERSION STREQUAL 4)
SET(CAPSIMAGE_DIR caps)
FIND_PATH(CAPSIMAGE_INCLUDE_DIR ${CAPSIMAGE_DIR}/capsimage.h)
else()
SET(CAPSIMAGE_DIR caps5)
FIND_PATH(CAPSIMAGE_INCLUDE_DIR ${CAPSIMAGE_DIR}/CapsAPI.h)
endif()
if(WIN32)
FIND_LIBRARY(CAPSIMAGE_LIBRARY NAMES capsimg PATH_SUFFIXES ${CAPSIMAGE_DIR} )
else()
FIND_LIBRARY(CAPSIMAGE_LIBRARY NAMES capsimage PATH_SUFFIXES ${CAPSIMAGE_DIR} )
endif(WIN32)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CAPSIMAGE DEFAULT_MSG
CAPSIMAGE_LIBRARY CAPSIMAGE_INCLUDE_DIR)
MARK_AS_ADVANCED(CAPSIMAGE_LIBRARY CAPSIMAGE_INCLUDE_DIR)

15
cmake/FindMath.cmake Normal file
View File

@ -0,0 +1,15 @@
if(MATH_INCLUDE_DIR)
# Already in cache, be silent
set(MATH_FIND_QUIETLY TRUE)
endif(MATH_INCLUDE_DIR)
find_path(MATH_INCLUDE_DIR math.h)
find_library(MATH_LIBRARY NAMES m)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MATH DEFAULT_MSG
MATH_LIBRARY MATH_INCLUDE_DIR)
mark_as_advanced(MATH_LIBRARY MATH_INCLUDE_DIR)

35
cmake/FindPortAudio.cmake Normal file
View File

@ -0,0 +1,35 @@
#
# Find the native PORTAUDIO (version 2) includes and library
#
# PORTAUDIO_INCLUDE_DIR - where to find portaudio.h, etc.
# PORTAUDIO_LIBRARY - List of libraries when using portaudio.
# PORTAUDIO_FOUND - True if portaudio found.
include(FindPackageHandleStandardArgs)
include(CheckFunctionExists)
if(PORTAUDIO_INCLUDE_DIR)
# Already in cache, be silent
set(PORTAUDIO_FIND_QUIETLY TRUE)
endif(PORTAUDIO_INCLUDE_DIR)
find_path(PORTAUDIO_INCLUDE_DIR portaudio.h)
find_library(PORTAUDIO_LIBRARY NAMES portaudio)
# handle the QUIETLY and REQUIRED arguments and set PORTAUDIO_FOUND to TRUE if
# all listed variables are TRUE
find_package_handle_standard_args(PORTAUDIO DEFAULT_MSG
PORTAUDIO_LIBRARY PORTAUDIO_INCLUDE_DIR)
# Check if it's really a portaudio2 installation...
if(PORTAUDIO_FOUND)
set(CMAKE_REQUIRED_LIBRARIES ${PORTAUDIO_LIBRARY})
check_function_exists(Pa_GetDefaultInputDevice HAVE_PA_GETDEFAULTINPUTDEVICE)
if (NOT HAVE_PA_GETDEFAULTINPUTDEVICE)
unset (PORTAUDIO_FOUND)
endif(NOT HAVE_PA_GETDEFAULTINPUTDEVICE)
set(CMAKE_REQUIRED_LIBRARIES "")
endif(PORTAUDIO_FOUND)
mark_as_advanced(PORTAUDIO_LIBRARY PORTAUDIO_INCLUDE_DIR)

53
cmake/FindReadline.cmake Normal file
View File

@ -0,0 +1,53 @@
IF (READLINE_INCLUDE_DIR)
# Already in cache, be silent
SET(READLINE_FIND_QUIETLY TRUE)
ENDIF (READLINE_INCLUDE_DIR)
FIND_PATH(READLINE_INCLUDE_DIR readline.h PATH_SUFFIXES readline)
FIND_LIBRARY(READLINE_LIBRARY NAMES readline)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(READLINE DEFAULT_MSG
READLINE_LIBRARY READLINE_INCLUDE_DIR)
MARK_AS_ADVANCED(READLINE_LIBRARY READLINE_INCLUDE_DIR)
if(READLINE_FOUND)
set(CMAKE_REQUIRED_LIBRARIES "readline")
check_function_exists(rl_filename_completion_function
HAVE_RL_COMPLETION_FUNCTION)
# If linking did not work, we might have to link
# explicitely against libtermcap or libncurses
if(NOT HAVE_RL_COMPLETION_FUNCTION)
unset(READLINE_FOUND)
find_package(Termcap)
if(TERMCAP_FOUND)
set(CMAKE_REQUIRED_LIBRARIES "readline" "termcap")
check_function_exists(rl_filename_completion_function
HAVE_RL_COMPLETION_FUNCTION_TERMCAP)
endif(TERMCAP_FOUND)
if(HAVE_RL_COMPLETION_FUNCTION_TERMCAP)
set(READLINE_LIBRARY ${READLINE_LIBRARY} ${TERMCAP_LIBRARY})
set(READLINE_FOUND TRUE)
else(HAVE_RL_COMPLETION_FUNCTION_TERMCAP)
find_package(Curses)
if(CURSES_FOUND)
if(CURSES_NCURSES_LIBRARY)
set(CMAKE_REQUIRED_LIBRARIES "readline" "ncurses")
else()
set(CMAKE_REQUIRED_LIBRARIES "readline" "curses")
endif()
check_function_exists(rl_filename_completion_function
HAVE_RL_COMPLETION_FUNCTION_CURSES)
if(HAVE_RL_COMPLETION_FUNCTION_CURSES)
set(READLINE_LIBRARY
${READLINE_LIBRARY} ${CURSES_LIBRARIES})
set(READLINE_FOUND TRUE)
endif(HAVE_RL_COMPLETION_FUNCTION_CURSES)
endif(CURSES_FOUND)
endif(HAVE_RL_COMPLETION_FUNCTION_TERMCAP)
endif(NOT HAVE_RL_COMPLETION_FUNCTION)
set(CMAKE_REQUIRED_LIBRARIES "")
endif(READLINE_FOUND)

177
cmake/FindSDL2.cmake Normal file
View File

@ -0,0 +1,177 @@
# Locate SDL2 library
# This module defines
# SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARY variable.
#
# Don't forget to include SDLmain.h and SDLmain.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL2_LIBRARY
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
#
#
# $SDL2DIR is an environment variable that would
# correspond to the ./configure --prefix=$SDL2DIR
# used in building SDL2.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL guidelines.
# Added a search for SDL2main which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL2_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
# This needed to change because "proper" SDL convention
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL2/ (see FreeBSD).
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see http://www.cmake.org/cmake/project/license.html for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
SET(SDL2_SEARCH_PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
FIND_PATH(SDL2_INCLUDE_DIR SDL_scancode.h
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES include/SDL2 include
PATHS ${SDL2_SEARCH_PATHS}
)
FIND_LIBRARY(SDL2_LIBRARY_TEMP
NAMES SDL2
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
IF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# SDL2 may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
IF(SDL2_LIBRARY_TEMP)
# For SDL2main
IF(NOT SDL2_BUILDING_LIBRARY)
IF(SDL2MAIN_LIBRARY)
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(SDL2MAIN_LIBRARY)
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
ENDIF(SDL2_LIBRARY_TEMP)
if(SDL2_INCLUDE_DIR AND EXISTS "${SDL2_INCLUDE_DIR}/SDL_version.h")
file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$")
string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}")
set(SDL2_VERSION_STRING ${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH})
unset(SDL2_VERSION_MAJOR_LINE)
unset(SDL2_VERSION_MINOR_LINE)
unset(SDL2_VERSION_PATCH_LINE)
unset(SDL2_VERSION_MAJOR)
unset(SDL2_VERSION_MINOR)
unset(SDL2_VERSION_PATCH)
endif()
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)

14
cmake/FindTermcap.cmake Normal file
View File

@ -0,0 +1,14 @@
if(TERMCAP_INCLUDE_DIR)
# Already in cache, be silent
set(TERMCAP_FIND_QUIETLY TRUE)
endif(TERMCAP_INCLUDE_DIR)
find_path(TERMCAP_INCLUDE_DIR termcap.h)
find_library(TERMCAP_LIBRARY NAMES termcap)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(TERMCAP DEFAULT_MSG
TERMCAP_LIBRARY TERMCAP_INCLUDE_DIR)
mark_as_advanced(TERMCAP_LIBRARY TERMCAP_INCLUDE_DIR)

View File

@ -0,0 +1,48 @@
# This Toolchain file is used to cross compile the Windows 32 bit
# version of Hatari under linux using mingw32
# use : cmake -DCMAKE_TOOLCHAIN_FILE=Toolchain-mingw32-win64_32.cmake
# mingw32 versions of the different tools
# (change these depending on your system settings)
set (MINGW_EXE_PREFIX "i686-w64-mingw32")
set (MINGW_ROOT_PATH "mingw")
#-- Changes should not be required below this point
# The name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)
# Use the value provided to set mingw's tools
SET(CMAKE_C_COMPILER ${MINGW_EXE_PREFIX}-gcc)
SET(CMAKE_CXX_COMPILER ${MINGW_EXE_PREFIX}-g++)
SET(CMAKE_RC_COMPILER ${MINGW_EXE_PREFIX}-windres)
# Base directory for the target environment
# We use the output from '-print-sysroot'
EXECUTE_PROCESS(
COMMAND ${CMAKE_C_COMPILER} -print-sysroot
OUTPUT_VARIABLE CMAKE_FIND_ROOT_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# bin/, include/, lib/ and share/ are often in "mingw/"
# You might need to adjust the path for your system
SET(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH}/${MINGW_ROOT_PATH})
# Make the path absolute, a relative path could confuse some systems
get_filename_component ( CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ABSOLUTE )
#message ( "MINGW_ROOT_PATH ${MINGW_ROOT_PATH} MINGW_EXE_PREFIX ${MINGW_EXE_PREFIX} CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH}" )
# FindSDL.cmake doesn't search correctly in CMAKE_FIND_ROOT_PATH
# so we force SDLDIR here
set ( ENV{SDLDIR} ${CMAKE_FIND_ROOT_PATH}/include/SDL )
# Adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@ -0,0 +1,48 @@
# This Toolchain file is used to cross compile the Windows 64 bit
# version of Hatari under linux using mingw32
# use : cmake -DCMAKE_TOOLCHAIN_FILE=Toolchain-mingw32-win64_64.cmake
# mingw32 versions of the different tools
# (change these depending on your system settings)
set (MINGW_EXE_PREFIX "x86_64-w64-mingw32")
set (MINGW_ROOT_PATH "mingw")
#-- Changes should not be required below this point
# The name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)
# Use the value provided to set mingw's tools
SET(CMAKE_C_COMPILER ${MINGW_EXE_PREFIX}-gcc)
SET(CMAKE_CXX_COMPILER ${MINGW_EXE_PREFIX}-g++)
SET(CMAKE_RC_COMPILER ${MINGW_EXE_PREFIX}-windres)
# Base directory for the target environment
# We use the output from '-print-sysroot'
EXECUTE_PROCESS(
COMMAND ${CMAKE_C_COMPILER} -print-sysroot
OUTPUT_VARIABLE CMAKE_FIND_ROOT_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# bin/, include/, lib/ and share/ are often in "mingw/"
# You might need to adjust the path for your system
SET(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH}/${MINGW_ROOT_PATH})
# Make the path absolute, a relative path could confuse some systems
get_filename_component ( CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ABSOLUTE )
#message ( "MINGW_ROOT_PATH ${MINGW_ROOT_PATH} MINGW_EXE_PREFIX ${MINGW_EXE_PREFIX} CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH}" )
# FindSDL.cmake doesn't search correctly in CMAKE_FIND_ROOT_PATH
# so we force SDLDIR here
set ( ENV{SDLDIR} ${CMAKE_FIND_ROOT_PATH}/include/SDL )
# Adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

32
cmake/Uninstall.cmake Normal file
View File

@ -0,0 +1,32 @@
#
# "uninstall" target for reverting "make install"
#
# cmake_policy(SET CMP0007 NEW)
if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: \"${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\"")
endif()
file(READ "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
# list(REVERSE files)
foreach (file ${files})
message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
if (EXISTS "$ENV{DESTDIR}${file}")
execute_process(
COMMAND ${CMAKE_COMMAND} -E remove "$ENV{DESTDIR}${file}"
OUTPUT_VARIABLE rm_out
RESULT_VARIABLE rm_retval
)
if(NOT ${rm_retval} EQUAL 0)
message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
endif (NOT ${rm_retval} EQUAL 0)
else (EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
endif (EXISTS "$ENV{DESTDIR}${file}")
endforeach(file)
execute_process(
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt
)

101
cmake/config-cmake.h Normal file
View File

@ -0,0 +1,101 @@
/* CMake config.h for Hatari */
/* Define if you have a PNG compatible library */
#cmakedefine HAVE_LIBPNG 1
/* Define if you have a readline compatible library */
#cmakedefine HAVE_LIBREADLINE 1
/* Define if you have the PortAudio library */
#cmakedefine HAVE_PORTAUDIO 1
/* Define if you have the capsimage library */
#cmakedefine HAVE_CAPSIMAGE 1
#cmakedefine CAPSIMAGE_VERSION @CAPSIMAGE_VERSION@
/* Define if you have a X11 environment */
#cmakedefine HAVE_X11 1
/* Define to 1 if you have the `z' library (-lz). */
#cmakedefine HAVE_LIBZ 1
/* Define to 1 if you have the <zlib.h> header file. */
#cmakedefine HAVE_ZLIB_H 1
/* Define to 1 if you have the <termios.h> header file. */
#cmakedefine HAVE_TERMIOS_H 1
/* Define to 1 if you have the <glob.h> header file. */
#cmakedefine HAVE_GLOB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#cmakedefine HAVE_STRINGS_H 1
/* Define to 1 if you have the <SDL_config.h> header file. */
#cmakedefine HAVE_SDL_CONFIG_H 1
/* Define to 1 if you have the <sys/times.h> header file. */
#cmakedefine HAVE_SYS_TIMES_H 1
/* Define to 1 if you have the `cfmakeraw' function. */
#cmakedefine HAVE_CFMAKERAW 1
/* Define to 1 if you have the 'setenv' function. */
#cmakedefine HAVE_SETENV 1
/* Define to 1 if you have the `select' function. */
#cmakedefine HAVE_SELECT 1
/* Define to 1 if you have unix domain sockets */
#cmakedefine HAVE_UNIX_DOMAIN_SOCKETS 1
/* Define to 1 if you have the 'posix_memalign' function. */
#cmakedefine HAVE_POSIX_MEMALIGN 1
/* Define to 1 if you have the 'memalign' function. */
#cmakedefine HAVE_MEMALIGN 1
/* Define to 1 if you have the 'gettimeofday' function. */
#cmakedefine HAVE_GETTIMEOFDAY 1
/* Define to 1 if you have the 'nanosleep' function. */
#cmakedefine HAVE_NANOSLEEP 1
/* Define to 1 if you have the 'alphasort' function. */
#cmakedefine HAVE_ALPHASORT 1
/* Define to 1 if you have the 'scandir' function. */
#cmakedefine HAVE_SCANDIR 1
/* Define to 1 if you have the 'statvfs' function. */
#cmakedefine HAVE_STATVFS 1
/* Define to 1 if you have the 'fseeko' function. */
#cmakedefine HAVE_FSEEKO 1
/* Define to 1 if you have the 'ftello' function. */
#cmakedefine HAVE_FTELLO 1
/* Define to 1 if you have the 'flock' function. */
#cmakedefine HAVE_FLOCK 1
/* Define to 1 if you have the 'strlcpy' function. */
#cmakedefine HAVE_LIBC_STRLCPY 1
/* Define to 1 if you have the 'd_type' member in the 'dirent' struct */
#cmakedefine HAVE_DIRENT_D_TYPE 1
/* Relative path from bindir to datadir */
#define BIN2DATADIR "@BIN2DATADIR@"
/* Define to 1 to enable DSP 56k emulation for Falcon mode */
#cmakedefine ENABLE_DSP_EMU 1
/* Define to 1 to enable WINUAE cpu */
#cmakedefine ENABLE_WINUAE_CPU 1
/* Define to 1 to use less memory - at the expense of emulation speed */
#cmakedefine ENABLE_SMALL_MEM 1
/* Define to 1 to enable trace logs - undefine to slightly increase speed */
#cmakedefine ENABLE_TRACING 1

86
config.h Normal file
View File

@ -0,0 +1,86 @@
/* Default config.h for Hatari */
/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'. */
//#define HAVE_DIRENT_H 1
/* Define if you have a readline compatible library */
#undef HAVE_LIBREADLINE
/* Define to 1 if you have the `z' library (-lz). */
#define HAVE_LIBZ 1
/* Define to 1 if you have the <zlib.h> header file. */
#define HAVE_ZLIB_H 1
/* Define to 1 if you have the <termios.h> header file. */
//#if defined(WIN32)
# undef HAVE_TERMIOS_H
//#else
//# define HAVE_TERMIOS_H 1
//#endif
/* Define to 1 if you have the <glob.h> header file. */
//#if defined(WIN32)
# undef HAVE_GLOB_H
//#else
//# define HAVE_GLOB_H 1
//#endif
/* Define to 1 if you have the <strings.h> header file. */
//#if defined(__CEGCC__)
# undef HAVE_STRINGS_H
//#else
//# define HAVE_STRINGS_H 1
//#endif
/* Define to 1 if you have the `strcasecmp' function. */
#define HAVE_STRCASECMP 1
/* Define to 1 if you have the `strncasecmp' function. */
#define HAVE_STRNCASECMP 1
/* Define to 1 if you have the `cfmakeraw' function. */
# undef HAVE_CFMAKERAW
/* Define to 1 if you have the 'setenv' function. */
//#if defined(WIN32) || (defined(__sun) && defined(__SVR4))
# undef HAVE_SETENV
//#else
//# define HAVE_SETENV 1
//#endif
/* Define to 1 if you have unix domain sockets */
//#if defined(WIN32) || defined(__CEGCC__)
# undef HAVE_UNIX_DOMAIN_SOCKETS
//#else
//# define HAVE_UNIX_DOMAIN_SOCKETS 1
//#endif
/* Relative path from bindir to datadir */
#define BIN2DATADIR "/apps/hatari"
/* configuration file path */
#define CONFDIR "/apps/hatari"
/* games folder */
#define DATADIR "/hatari"
/* Define to 1 to use less memory - at the expense of emulation speed */
//#if defined(__CEGCC__)
#define ENABLE_SMALL_MEM 1
//#else
//# undef ENABLE_SMALL_MEM
//#endif
//#define ENABLE_WINUAE_CPU 1
/* Define to the full name of this package. */
#define PACKAGE_NAME "hatari"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "hatari CVS"
/* Define to the version of this package. */
#define PACKAGE_VERSION "CVS"

115
configure vendored Executable file
View File

@ -0,0 +1,115 @@
#!/bin/sh
# NOTE: this is a simple script wrapper around the cmake command line tools,
# for those used to the autotools configure script conventions
if ! which cmake > /dev/null; then
echo "ERROR: You need the 'cmake' program to configure the Hatari build process."
echo "Please install 'cmake' first, then try again."
exit 1
fi
print_help()
{
echo "This is a simple configure script wrapper around cmake build system."
echo "Parameters are:"
echo " --prefix=<path> Set the install prefix to path"
echo " --enable-debug Enable debug (non-optimized) build"
echo " --enable-small-mem Use less memory - at the expense of emulation speed"
echo " --disable-dsp Disable DSP emulation for Falcon mode."
echo " --disable-tracing Disable tracing messages for debugging"
echo " --enable-winuae-cpu Enable WinUAE CPU core (experimental!)"
echo " --disable-osx-bundle Disable application bundling on Mac OS X"
echo " --enable-sdl2 Compile with libsdl 2.0 instead of 1.2"
echo " --cross-compile-win64_32 Build the 32 bit Windows version under linux using mingw-w64"
echo " --cross-compile-win64_64 Build the 64 bit Windows version under linux using mingw-w64"
echo
echo "Please run cmake directly for full control over the build."
echo
}
cmake_args=""
build_type="Release"
while [ $# -gt 0 ]
do
preq=${1%=*} # get part before =
case $preq
in
--help)
print_help
exit 0
;;
--prefix)
prefix=${1##*=} # get part after =
cmake_args="$cmake_args -DCMAKE_INSTALL_PREFIX:PATH=$prefix"
;;
--enable-debug)
build_type="Debug"
cmake_args="$cmake_args -DCMAKE_BUILD_TYPE:STRING=Debug"
;;
--disable-debug)
build_type="Release"
cmake_args="$cmake_args -DCMAKE_BUILD_TYPE:STRING=Release"
;;
--enable-dsp)
cmake_args="$cmake_args -DENABLE_DSP_EMU:BOOL=1"
;;
--disable-dsp)
cmake_args="$cmake_args -DENABLE_DSP_EMU:BOOL=0"
;;
--enable-tracing)
cmake_args="$cmake_args -DENABLE_TRACING:BOOL=1"
;;
--disable-tracing)
cmake_args="$cmake_args -DENABLE_TRACING:BOOL=0"
;;
--enable-small-mem)
cmake_args="$cmake_args -DENABLE_SMALL_MEM:BOOL=1"
;;
--disable-small-mem)
cmake_args="$cmake_args -DENABLE_SMALL_MEM:BOOL=0"
;;
--enable-winuae-cpu)
cmake_args="$cmake_args -DENABLE_WINUAE_CPU:BOOL=1"
;;
--disable-winuae-cpu)
cmake_args="$cmake_args -DENABLE_WINUAE_CPU:BOOL=0"
;;
--enable-osx-bundle)
cmake_args="$cmake_args -DENABLE_OSX_BUNDLE:BOOL=1"
;;
--disable-osx-bundle)
cmake_args="$cmake_args -DENABLE_OSX_BUNDLE:BOOL=0"
;;
--enable-sdl2)
cmake_args="$cmake_args -DENABLE_SDL2:BOOL=1"
;;
--disable-sdl2)
cmake_args="$cmake_args -DENABLE_SDL2:BOOL=0"
;;
--cross-compile-win64_32)
cmake_args="$cmake_args -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-mingw32-win64_32.cmake"
;;
--cross-compile-win64_64)
cmake_args="$cmake_args -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-mingw32-win64_64.cmake"
;;
*)
echo "Invalid argument: $preq"
echo "Run $0 --help for a list of valid parameters."
exit 2
;;
esac
shift 1
done
# remove previous cmake's cache
rm -f `dirname $0`/CMakeCache.txt
rm -rf `dirname $0`/CMakeFiles/
cmake `dirname $0` $cmake_args || exit 1
echo
echo "Now you must type: make; make install"
echo "to actually build and install the software"
echo

18
doc/CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
INSTALL(FILES authors.txt emutos.txt keymap-sample.txt memory-usage.txt
midi-linux.txt release-notes.txt todo.txt
DESTINATION ${DOCDIR})
INSTALL(FILES compatibility.html manual.html
DESTINATION ${DOCDIR})
INSTALL(DIRECTORY images
DESTINATION ${DOCDIR})
# if(UNIX)
add_custom_target(manpages ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/hatari.1.gz)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/hatari.1.gz
COMMAND gzip -c -9 ${CMAKE_CURRENT_SOURCE_DIR}/hatari.1 > ${CMAKE_CURRENT_BINARY_DIR}/hatari.1.gz
DEPENDS hatari.1)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hatari.1.gz DESTINATION ${MANDIR})
# endif(UNIX)

198
doc/authors.txt Normal file
View File

@ -0,0 +1,198 @@
Active Hatari developers:
-------------------------
- Nicolas Pomarede <npomarede at corp.free.fr> : Project admin,
improving CPU, video, sound, IKBD and floppy emulation.
- Thomas Huth <huth at tuxfamily.org> : Project initiator and
admin, currently more or less in hibernation mode.
- Eero Tamminen <oak at helsinkinet fi> : Speed improvements & code
cleanup, small parts of the STE emulation, Python CLI, UI & TOS
tester + Hatari window embedding & remote control API, pause & auto
frameskip support, statusbar & overlay led, conditional breakpoints
and other debugger features, GEMDOS HD emulation improvements, PNG
saving.
- Laurent Sallafranque: Many fixes and speedups to DSP emulation,
DSP debugging support, crossbar emulation, falcon microphone
emulation, STE LMC1992/microwire emulation, Videl emulation.
Contributors:
-------------
Following people contributed code or patches to this projects and/or
helped to find bugs in Hatari (listed in random order - and if someone
is missing here, please remind me!):
- Jean-Baptiste Berlioz <tobe at freemind-tobe.com> : Cycle accurate
Blitter emulation.
- David Savinkoff : More accurate printer emulation, LMC1992 emulation
patches, IIR/Low Pass filters and many improvements to the YM2149 model
to get a close emulation of the circuit used to merge and filter the
output of the 3 YM2149 voices.
Great work to enhance the sound quality.
- Matthias Arndt <marndt at asmsoftware.de> : Wrote the original version
of the Hatari user manual, fixed the printer emulation functions.
- Sébastien Molines <clafou at gmail.com> : Wrote the main part of the
Mac OS X GUI of Hatari.
- Marco Herrn <marco at mherrn.de> : Wrote the initial version of the
"man" page of Hatari and maintained the Hatari Debian packages until
Hatari was included into Debian.
- Sven de Marothy : Screenshot functions, the initial CLI debugger,
the ACSI emulation and added support for ZIPed and GZIPed disk images.
- Emmanuel Anne <emanne at absysteme.fr> : Contributed lots of patches,
RTC emulation.
- Tuduri Benoît <skweek at users.sourceforge.net> : French man-page,
support for Doxygen.
- Markus Oberhumer : fixed a problem with ZIPed disk images, routine for
loading the configuration file from the $HOME directory.
- Philippe Gerin : Fixed a bug in the CPU core (bus errors problem).
- Steve Kemp : Found some possible buffer overflows.
- George Nakos : Helped to track down a bug in the GEMDOS HD emulation.
- Pieter van der Meer : Traced a bug in the VIDEL emulation.
- Patrice Mandin : Some improvements of the autoconf build system files,
original author of the DSP emulation core.
- Martin Doering : Code for compiling the font data into the executable
and some other ideas for cleaning up the source code.
- Matthias Alles : He initiated the port of Hatari to MiNT and helped
with a lot of technical questions about the ST.
- Ventzislav Tzvetkov : Joystick closing patch, Hatari for AmigaOS.
- "Jo" (?) : Patches for compiling Hatari on a 64-bit Alpha machine.
- Stefan Berndtsson <stefan at nocrew.org> : Patches to get Hatari
running on big endian machines.
- Anatol Paruntik (?) : Patches for compiling Hatari on QNX.
- Claus Windeler <goodoldgames at beemulated.net> : BeOS adaption.
- James Lampard : Adapted Hatari to Acorn RISC OS machines.
- Mark Keates : Patches for compiling Hatari with MinGW.
- Fredrik Noring : Tracked down a bug in the blitter emulation and a
bug in the PSG shadow register emulation.
- Volker Seebode: Fix to ASCI emulation to get other than AHDI drivers
working.
- Cyprian Konador: Found some bugs in the blitter cycles emulation,
duochrome and samplehold modes for TT video emulation.
- Jerome Vernet: Some updates to the OS X Xcode project file and OS X
GUI, supplied a french keymapping file for OS X.
- Kenneth Kaufman: MS VC6 & C++ compiler and multiple GEMDOS HD
partition support patches.
- Uwe Seimet: IDE emulation improvements and GEMDOS HD emulation
improvement suggestions.
- Anders Eriksson (Evil/DHS): Helped improving STE's emulation by
running many tests programs and providing the source code for some
non-working demos.
- Markus Fritze: New m68k disassembler with more Motorola like syntax
and options for controlling how the output looks.
- Deniz Turkoglu: Patches for the Max OS X GUI.
- Markus Heiden: SCSI class 1 (ICD) command support for drives > 1 GB
- nash67: tested hundreds (!) of games from various CD compilations
and reported the non working ones on atari-forum.com. Huge thanks for
that tedious work, it helped tracking down some less common cases
not used in demos (keyboard, joystick, FDC, tos, ...).
- Gilles Fetis: fixes to MMU emulation (from NeXT emulator project
using Hatari code).
- Peter Putnik (Petari, AtariZoll): for helping with tracking the cause
of the crash in Microprose Golf (FDC emulation). Also wrote some useful tools
to handle floppies (flofor, floimg), as well as opcovat.tos to report
valid/invalid opcodes with a real CPU and under emulation by testing all possible
opcode combinations.
- Jean Louis Guerin (DrCoolZic): for the 'Panzer' program, very useful to test
some FDC behaviours and timings on real hardware and to compare them
with the emulated system. Also wrote some nice docs on WD1772
and methods commonly used for games' protections.
- Christer Solskogen: for setting up an automatic build script on his site,
with up to date binary versions for Linux and Windows in 32 and 64 bit mode.
Very useful for end users wishing to try the devel version of Hatari, and
lots of interesting build logs too for various cpu architectures.
See http://antarctica.no/~hatari/latest
- Max Böhm: host <-> Atari filename encoding conversion routines and
related changes needed to gemdos.c.
Code from other projects
------------------------
As a true open source project, Hatari also uses some code from other
projects which we would like to acknowledge here:
- Most of the original ST hardware emulation comes from the WinSTon
source code which has been written by Paul Bates.
(http://www.sourceforge.net/projects/winston/)
- The original CPU core has been taken from UAE which has been written
by Bernd Schmidt and others. (http://uae.coresystems.de/)
- The new alternative CPU core has been taken from WinUAE which is
maintained by Toni Wilen. Huge thanks to Toni for accepting ideas
and patches not specific to Amiga emulation, as well as keeping on
improving the accuracy of 68000/20/30 CPU. (http://www.winuae.net/)
- Some parts have been taken from the emulator STonX that has been
written by Marinos Yannikos and Martin Griffiths.
(http://stonx.sourceforge.net/)
- A lot of code (e.g. the scancode keyboard mapping, Videl, NVRAM and
DSP emulation) has been adapted from the sources of the emulator
Aranym. (http://aranym.atari.org/)
- The code for decompressing ZIP files (unzip.c) has been taken from
Gilles Vollant's miniunzip program.
(http://www.winimage.com/zLibDll/unzip.html)
- The routines for saving and loading the ASCII configuration file
(cfgopts.c) have originally been written by Jeffry J. Brickley.
- The new sound core uses (or used) some code/ideas from the following GPL
projects :
* 5 bits volume table and 16*16*16 combinations of all volume are
from Sc68 by Benjamin Gerard.
* 4 bits to 5 bits volume interpolation from 16*16*16 to 32*32*32
are from YM blep synthesis by Antti Lankila.
* Since Hatari 1.7, volume table based on measures by Paulo Simoes
- The IDE hard disk emulation is based on code from QEMU.
(http://www.qemu.org/)
- The MMU emulation for the 68030 has been taken from the NeXT emulator
Previous (thanks to Andreas Grabher!). Since Hatari 1.9, this is now
taken from WinUAE which uses the same code base.

35504
doc/changelog.txt Normal file

File diff suppressed because it is too large Load Diff

37
doc/coding.txt Normal file
View File

@ -0,0 +1,37 @@
Hatari coding guidelines
========================
Before writing new code or changing existing files, please read through these
coding guidelines to make sure that your changes are in harmony with the
existing source code.
- all source text files have to use Unix Line ending convention (line-feed only)
(exception: Code like cart_asm.s which has to be compiled from the Atari TOS
side should have DOS line endings (CR-LF) instead).
- Avoid non-ASCII characters in source code. Use UTF-8 encoding for non-english
documentation files if necessary.
- Use TABs for indentation at the beginning of a line. Default TAB width is 8,
but your source code should also look fine with other TAB width (e.g. 4).
- Use doxygen-style comments to document what each function is doing.
- No "magic" variable values. Use either defines or enums to name the
values and document what they mean if it's not obvious.
- Hatari uses code from many projects, e.g. the UAE CPU files, which
use another coding style than the main source code. We keep the
original coding style there for compatibility with the origin. So
always try to adapt to the coding style of the file that you're
currently editing.
- For new files, pick one of the existing coding styles, don't add a new one.
- Try to avoid including a header file from within another header file.
Include all necessary header files in the right order in the *.c files
instead. Including a header file from within another header file easily leads
to a dependency hell which can become very painful when one of the header
files clashes with a system header for example and must only be included
in certain .c files only.

3983
doc/compatibility.html Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
# Below is a keymap file for Hatari running on Windows, which maps the SDL
# keys of a German PC keyboard to the corresponding Atari ST scancodes.
45,12 # ß
91,26 # Ü
93,27 # +
92,41 # #
96,43 # ~

240
doc/doxygen/Doxyfile Normal file
View File

@ -0,0 +1,240 @@
# Doxyfile 1.5.1-p1
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = Hatari
PROJECT_NUMBER = 1.9.0
OUTPUT_DIRECTORY = .
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
BUILTIN_STL_SUPPORT = NO
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = NO
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_BY_SCOPE_NAME = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_DIRECTORIES = NO
FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ../../src
FILE_PATTERNS = *.c \
*.cc \
*.cxx \
*.cpp \
*.c++ \
*.h \
*.hh \
*.hxx \
*.hpp \
*.h++
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = NO
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = YES
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4wide
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = NO
USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
MAX_DOT_GRAPH_DEPTH = 1000
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::additions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO

1218
doc/emutos.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
# This is an example for a keyboard mapping file that can be used in Hatari
# by loading it from the keyboard setup dialog.
#
# Lines starting with a '#' or with a ';' are comments.
# All other lines should contain exactly two numbers separated by a comma.
# The first number is the symbolic PC key code (see the SDL_keysym.h file
# from the SDL library header files usually in /usr/include/SDL/).
# The corresponding key will be mapped to the ST key which is specified by
# second number - the ST scan code of the key (see src/keymap.c in the Hatari
# sources).
#
# Here is an example for a french keymap for the MacBook:
97,16
122,17
113,30
109,39
119,44
38,2
160,3
34,4
39,5
40,6
161,7
163,8
33,9
162,10
164,11
29,12
45,13
94,26
36,27
165,40
44,50
59,51
58,52
61,53

277
doc/fr/hatari.1 Normal file
View File

@ -0,0 +1,277 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH "HATARI" "1" "2008-03-14" "Hatari" ""
.\" Please adjust this date whenever revising the manpage.
.SH "NAME"
Hatari \- emulateur Atari ST(e)
.SH "SYNOPSIS"
.B hatari
.RI [options]
.RI [diskimage]
.SH "DESCRIPTION"
Hatari est un emulateur d'Atari ST(e) pour Linux, FreeBSD, BeOS ainsi
que tout systemes d'exploitation supportant la bibliotheque SDL.
.PP
Avec Hatari vous pouvez lancer des jeux, demos ou des applications
ecrites pour l'Atari ST ou STE.
Il supporte egalement les images de disquettes les plus couramment
utilisees a savoir *.st et *.msa. Il permet aussi l'emulation de
disque dur.
.PP
Pour lancer l'emulateur une image ROM du TOS est requise. EmuTOS
est une implementation libre du TOS fournie avec Hatari.
Malheureusement cette derniere n'est pas entierement compatible avec
le TOS original donc certains programmes risquent de ne pas
fonctionner correctement. Pour cela, il est recommande d'utiliser
une ROM TOS issue d'un veritable Atari.
.SH "OPTIONS"
Les options sont classées en sous catégories:
.SH "Options générale"
.TP
.B \-h, \-\-help
affiche les options de la ligne de commande
.TP
.B \-v, \-\-version
affiche les informations sur la version
.TP
.B \-\-confirm-quit <bool>
Whether Hatari confirms quitting
.TP
.B \-c, \-\-configfile <filename>
utilise un fichier nomme explicitement comme fichier de configuration
en lieu et place du ~/.hatari/hatari.cfg
.SH "Options d'écran
.TP
.B \-\-monitor <x>
selectionne le type de moniteur (x = mono/rgb/vga/tv)
.TP
.B \-f, \-\-fullscreen
demarre l'emulateur en mode plein ecran
.TP
.B \-w, \-\-window
demarre l'emulateur en mode fenetre
.TP
.B \-z, \-\-zoom <x>
agrandir les petites résolutions (1=non, 2=oui)
.TP
.B \-\-frameskips <x>
Saute <x> images apres chaque image affichees pour accelerer l'emulation
(0 <= x <= 8)
.TP
.B \-\-borders <bool>
Show screen borders (for overscan demos etc)
.TP
.B \-\-spec512 <x>
Hatari uses this threshold to decide when to render a screen with
the slower but more accurate Spectrum512 screen conversion functions
(0 <= x <= 512, 0=disable)
.TP
.B \-\-bpp <bool>
Force given internal bitdepth (x=8/16/32, x=0 for autodetection).
8-bit color depth may be useful for older host computers
.SH "Options de VDI"
.TP
.B \-\-vdi\-planes <x>
utilise une resolution VDI etendue avec une profondeur de bit de <x>
(x = 1, 2 or 4)
.TP
.B \-\-vdi\-width <w>
utilise une resolution VDI etendue avec une largeur de <w> (384 <= w <= 1024)
.TP
.B \-\-vdi\-height <h>
utilise une resolution VDI etendue avec une hauteur de <h> (200 < h <= 768)
.SH "Options d'interface"
.TP
.B \-j, \-\-joystick <port>
emule le joystick ST sur le <port> 0 ou 1 avec le clavier
.TP
.B \-\-printer <file>
active le support de l'imprimante et ecrit les donnees dans le fichier <file>
.TP
.B \-\-midi <filename>
active la sortie MIDI experimentale vers un fichier mentionne par un nom
.TP
.B \-\-rs232 <filename>
active l'emulation experimentale du RS232 via un fichier/device
nomme explicitement
.SH "Options des disques"
.TP
.B \-d, \-\-harddrive <dir>
utilise <dir> comme un disque dur emule
.TP
.B \-\-acsi <file>
emule un disque dur ACSI avec un fichier image <file>
.TP
.B \-\-fastfdc <bool>
accelere l'emulation du FDC (peut entrainer des erreurs dans certains jeux ou demos)
.SH "Options de mémoire"
.TP
.B \-s, \-\-memsize <x>
fixe une quantite de memoire pour la RAM emulee, x = 1 a 14 MiB,
ou 0 pour 512 Ko
.TP
.B \-t, \-\-tos <imagefile>
specifie une image ROM TOS a utiliser
.TP
.B \-\-cartridge <imagefile>
utilise une image ROM de cartouche <file> (fonctionne uniquement si
l'emulation de disque dur et VDI etendue sont desactivee)
.TP
.B \-\-memstate <file>
Load memory snap-shot <file>
.SH "Option du processeur"
.TP
.B \-\-cpulevel <x>
specifie un CPU (680x0) a utiliser (TOS 2.06 uniquement!!)
.TP
.B \-\-cpuclock <x>
Set the CPU clock (8, 16 or 32 Mhz)
.TP
.B \-\-compatible <bool>
utilise le mode de compatibilite plus fidele mais plus lent
que le mode 68000
.SH "Options diverses du système"
.TP
.B \-\-machine <x>
selectionne un type de machine (x = st, ste, tt ou falcon)
.TP
.B \-\-blitter <bool>
active l'emulation du blitter
.TP
.B \-\-dsp <x>
emulation du DSP du Falcon (x = aucune, factice ou emulee)
.TP
.B \-\-sound <x>
Régle le son (x=off/low/med/hi)
.TP
.B \-\-keymap <file>
charge un fichier de refinition du clavier de <file>
.SH "Options de déboguer"
.TP
.B \-D, \-\-debug
active le deboggueur integre
.TP
.B \-\-log <file>
Sauvegarde le rapport vers le fichier <file> (peut aussi etre "stdout" ou
"stderr")
.TP
.B \-\-trace <trace1,...>
Activate debug traces, see \-\-trace help for tracing options
.SH "COMMANDS"
Les touches de raccourcis peuvent etre parametrees dans le fichier
de configurations.
Par defaut, les parametres sont:
.TP
.B AltGr + a
enregistre l'animation
.TP
.B AltGr + g
fait une capture d'ecran
.TP
.B AltGr + i
touche patron: quitte le mode plein ecran et met la fenetre en icone
.TP
.B AltGr + j
active l'emulation joystick via les touches de directions
.TP
.B AltGr + m
(active/desactive) la souris dans la fenetre
.TP
.B AltGr + r
eteint le ST (a chaud)
.TP
.B AltGr + c
eteint le ST a froid (comme le bouton original d'allumage)
.TP
.B AltGr + s
active/desactive le son
.TP
.B AltGr + q
quitte l'emulateur
.TP
.B AltGr + x
change la vitesse normale/maximum
.TP
.B AltGr + y
active/desactive l'enregistrement du son
.TP
.B AltGr + k
sauvegarde l'etat de la memoire
.TP
.B AltGr + l
restaure l'etat de la memoire
.TP
.B F11
change le mode entre plein ecran et fenetre
.TP
.B F12
active les commandes GUI de Hatari
.br
Vous pouvez avoir besoin de tenir la touche SHIFT en mode fenetre
.TP
.B Pause
Ouvrira le deboggueur, s'il etait active avec l'option \-\-debug
.SH Clavier d'Atari ST emule
Toutes les autres touches du clavier PC agissent comme celles de Atari ST
donc si vous appuyez sur ESPACE sur votre PC il en resultera sur
le clavier d'Atari ST un appuis sur la touche ESPACE. Les touches suivantes
ont une signification speciales :
.TP
.B Alt
Agira comme la touche ALTERNATE du clavier ST
.TP
.B left Ctrl
Agira comme la touche CONTROL du clavier ST
.TP
.B Page Up
Emulera la touche HELP du clavier ST
.TP
.B Page Down
Emulera la touche UNDO du clavier ST
.PP
.B AltGr
Agira comme
.B Alternate
tel sauf si vous appuyez sur les touches speciales d'Hatari.
La touche
.B right Ctrl
est utilisee comme le bouton feu d'un joystick emule
que vous aurez active precedement par l'emulation du joystick via
les touches du clavier.
Le touche de directions agiront comme les touches de directions sur
l'Atari ST tant que l'emulation du joystick par le clavier est inactive.
.SH "VOIR AUSSI"
La documentation originale du programme, habituellement
en /usr/share/doc/.
.PP
La page d'accueil d'Hatari : http://hatari.tuxfamily.org/
.SH "FICHIERS"
.TP
/etc/hatari.cfg (ou /usr/local/etc/hatari.cfg)
le fichier de configuration global d'Hatari
.TP
~/.hatari/hatari.cfg
Le fichier de configuration de l'utilisateur personnel
d'Hatari
.TP
tos.img
L'image ROM du TOS qui sera charge a partir du repertoire de donnees d'Hatari
si aucun argument n'est specifie sur la ligne de commande ou dans le fichier
de configuration.
.SH "AUTEURS"
Cette page du manuel a ete ecrite par Marco Herrn <marco@mherrn.de>,
pour le projet Debian et modifiee par la suite par Thomas Huth pour les
versions plus recentes d'Hatari
.SH "TRADUCTEUR"
Benoît TUDURI <skweek@users.sourceforge.net>

647
doc/hatari.1 Normal file
View File

@ -0,0 +1,647 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH "HATARI" "1" "2014-05-08" "Hatari" ""
.\" Please adjust this date whenever revising the manpage.
.SH "NAME"
hatari \- Atari ST/STE/TT/Falcon emulator
.SH "SYNOPSIS"
.B hatari
.RI [options]
.RI [directory|diskimage|program]
.SH "DESCRIPTION"
Hatari is an Atari ST/STE/TT/Falcon emulator for Linux, FreeBSD, BeOS and
other Systems which are supported by the SDL library.
.PP
With hatari one can run games, demos or applications written for Atari
ST, STE or Falcon. Atari TT support is experimental. Hatari supports
the commonly used *.st and *.msa disk images and hard disk emulation.
.PP
To run the emulator a TOS ROM image is needed. EmuTOS, a free
implementation of TOS is shipped with hatari. Since it is not yet
fully compatible with the original TOS, some programs won't run
correctly with it. Because of this it is recommended to use a TOS
ROM from a real Atari.
.PP
As an argument one can give either a name of a directory that should
be emulated as a virtual GEMDOS hard disk, a floppy disk image or an
Atari program that should be autostarted. In the last case the
program's directory will be used as the C: drive from where this
program will be started.
.PP
Booting will be done from the disk image or directory that's given
last on the command line as an option or the argument (and which
corresponds to A: or C:). If you want to give floppy image name with
an autostarting program name, give it with --disk-a option before the
program name.
.SH "OPTIONS"
Hatari options are split into several categories:
.SH "General options"
.TP
.B \-h, \-\-help
Print command line options and terminate
.TP
.B \-v, \-\-version
Print version information and terminate
.TP
.B \-\-confirm\-quit <bool>
Whether Hatari confirms quitting
.TP
.B \-c, \-\-configfile <filename>
Read additional configuration values from <file>, these
override values read from the global and user configuration
files
.TP
.B \-k, \-\-keymap <file>
Load keyboard mapping from <file>
.TP
.B \-\-fast\-forward <bool>
On fast machine helps skipping (fast forwarding) Hatari output
.SH "Common display options"
.TP
.B \-m, \-\-mono
Start in monochrome mode instead of color
.TP
.B \-\-monitor <x>
Select monitor type (x = mono/rgb/vga/tv)
.TP
.B \-f, \-\-fullscreen
Start the emulator in fullscreen mode
.TP
.B \-w, \-\-window
Start the emulator in windowed mode
.TP
.B \-\-grab
Grab mouse (also) in windowed mode
.TP
.B \-\-borders <bool>
Show ST/STE/Falcon screen borders (for low/med resolution overscan demos)
.TP
.B \-\-frameskips <x>
Skip <x> frames after each displayed frame to accelerate emulation
(0=disabled, >4 uses automatic frameskip with given value as maximum)
.TP
.B \-\-slowdown <x>
Slow down emulation by factor of x (used as multiplier for VBL wait time)
.TP
.B \-\-mousewarp <bool>
To keep host mouse better in sync with Atari mouse pointer, center it
to Hatari window on cold reset and resolution changes
.TP
.B \-\-statusbar <bool>
Show statusbar (with floppy leds etc etc)
.TP
.B \-\-drive\-led <bool>
Show overlay drive led when statusbar isn't shown
.TP
.B \-\-max\-width <x>
Preferred / maximum window width for borders / zooming
.TP
.B \-\-max\-height <x>
Preferred / maximum window height for borders / zooming
.TP
.B \-\-bpp <bool>
Force internal bitdepth (x = 8/15/16/32, 0=disable)
.SH "ST/STE specific display options"
.TP
.B \-\-desktop\-st <bool>
Whether fullscreen mode uses desktop resolution to avoid: messing
multi-screen setups, several seconds delay needed by LCD monitors
resolution switching and the resulting sound break. As Hatari ST/E
display code doesn't support zooming (except low-rez doubling), it
doesn't get scaled (by Hatari or monitor) when this is enabled.
Therefore this is mainly useful only if you suffer from the described
effects, but still want to grab mouse and remove other distractions
from the screen just by toggling fullscreen mode. (disabled by default)
.TP
.B \-\-spec512 <x>
Hatari uses this threshold to decide when to render a screen with
the slower but more accurate Spectrum512 screen conversion functions
(0 <= x <= 512, 0=disable)
.TP
.B \-z, \-\-zoom <x>
Zoom (double) low resolution (1=no, 2=yes)
.SH "TT/Falcon specific display options"
Zooming to sizes specified below is internally done using integer scaling
factors. This means that different Atari resolutions may show up with
different sizes, but they are never blurry.
.TP
.B \-\-desktop <bool>
Whether to use desktop resolution on fullscreen to avoid issues
related to resolution switching. Otherwise fullscreen will use
a resolution that is closest to the Hatari window size.
(enabled by default)
.TP
.B \-\-force\-max <bool>
Hatari window size is forced to specified maximum size and black borders
used when Atari resolution doesn't scale evenly to it. This is most
useful when recording videos of Falcon demos that change their
resolution. (disabled by default)
.TP
.B \-\-aspect <bool>
Whether to do monitor aspect ratio correction (enabled by default)
.SH "VDI options"
.TP
.B \-\-vdi <bool>
Whether to use VDI screen mode. Doesn't work with TOS v4.
TOS v3 memory detection isn't compatible with larger VDI modes
(i.e. you need to skip the detection at boot)
.TP
.B \-\-vdi\-planes <x>
Use extended VDI resolution with bit depth <x> (x = 1, 2 or 4)
.TP
.B \-\-vdi\-width <w>
Use extended VDI resolution with width <w> (320 < w <= 1280)
.TP
.B \-\-vdi\-height <h>
Use extended VDI resolution with height <h> (200 < h <= 960)
.SH "Screen capture options"
.TP
.B \-\-crop <bool>
Remove statusbar from the screen captures
.TP
.B \-\-avirecord
Start AVI recording. Note: recording will automatically
stop when emulation resolution changes.
.TP
.B \-\-avi\-vcodec <x>
Select AVI video codec (x = bmp/png). PNG compression can
be \fImuch\fP slower than using the uncompressed BMP format,
but uncompressed video content takes huge amount of space.
.TP
.B \-\-png\-level <x>
Select PNG compression level for AVI video (x = 0-9).
Both compression efficiency and speed depend on the compressed
screen content. Highest compression level (9) can be \fIreally\fP
slow with some content. Levels 3-6 should compress nearly as well
with clearly smaller CPU overhead.
.TP
.B \-\-avi\-fps <x>
Force AVI frame rate (x = 50/60/71/...)
.TP
.B \-\-avi\-file <file>
Use <file> to record AVI
.SH "Devices options"
.TP
.B \-j, \-\-joystick <port>
Emulate joystick with cursor keys in given port (0-5)
.TP
.B \-\-joy<port> <type>
Set joystick type (none/keys/real) for given port
.TP
.B \-\-printer <file>
Enable printer support and write data to <file>
.TP
.B \-\-midi\-in <filename>
Enable MIDI support and write MIDI data to <file>
.TP
.B \-\-midi\-out <filename>
Enable MIDI support and read MIDI data from <file>
.TP
.B \-\-rs232\-in <filename>
Enable serial port support and use <file> as the input device
.TP
.B \-\-rs232\-out <filename>
Enable serial port support and use <file> as the output device
.SH "Floppy drive options"
.TP
.B \-\-drive\-a <bool>
Enable/disable drive A (default is on)
.TP
.B \-\-drive\-b <bool>
Enable/disable drive B (default is on)
.TP
.B \-\-drive\-a\-heads <x>
Set number of heads for drive A (1=single sided, 2=double sided)
.TP
.B \-\-drive\-b\-heads <x>
Set number of heads for drive B (1=single sided, 2=double sided)
.TP
.B \-\-disk\-a <file>
Set disk image for floppy drive A
.TP
.B \-\-disk\-b <file>
Set disk image for floppy drive B
.TP
.B \-\-fastfdc <bool>
speed up FDC emulation (can cause incompatibilities)
.TP
.B \-\-protect\-floppy <x>
Write protect floppy image contents (on/off/auto). With "auto" option
write protection is according to the disk image file attributes
.SH "Hard drive options"
.TP
.B \-d, \-\-harddrive <dir>
Emulate harddrive partition(s) with <dir> contents. If directory
contains only single letter (C-Z) subdirectories, each of these
subdirectories will be treated as a separate partition, otherwise the
given directory itself will be assigned to drive "C:". In the multiple
partition case, the letters used as the subdirectory names will
determine to which drives/partitions they're assigned. If <dir> is
an empty string, then harddrive's emulation is disabled
.TP
.B \-\-protect\-hd <x>
Write protect harddrive <dir> contents (on/off/auto). With "auto" option
the protection can be controlled by setting individual files attributes
as it disables the file attribute modifications for the GEMDOS hard disk
emulation
.TP
.B \-\-gemdos\-case <x>
Specify whether new dir/filenames are forced to be in upper or lower case
with the GEMDOS HD emulation. Off/upper/lower, off by default
.TP
.B \-\-gemdos\-conv <bool>
Whether GEMDOS file names with 8-bit (non-ASCII) characters are
converted between Atari and host character sets. On Linux, host file
name character set is assumed to be UTF-8. This option is disabled by
default, in case you've transferred files from Atari machine without
proper file name conversion (e.g. by zipping them on Atari and
unzipping on PC).
.TP
.B \-\-acsi <id>=<file>
Emulate an ACSI hard disk with given BUS ID (0-7) using image <file>.
If just filename is given, it's assigned to BUS ID 0.
.TP
.B \-\-ide\-master <file>
Emulate an IDE master hard disk with an image <file>
.TP
.B \-\-ide\-slave <file>
Emulate an IDE slave hard disk with an image <file>
.SH "Memory options"
.TP
.B \-\-memstate <file>
Load memory snap-shot <file>
.TP
.B \-s, \-\-memsize <x>
Set amount of emulated ST RAM, x = 1 to 14 MiB, or 0 for 512 KiB
.TP
.B \-s, \-\-ttram <x>
Set amount of emulated TT RAM, x = 0 to 256 MiB (in 4MB steps)
.SH "ROM options"
.TP
.B \-t, \-\-tos <imagefile>
Specify TOS ROM image to use
.TP
.B \-\-patch\-tos <bool>
Use this option to enable/disable TOS ROM patching. Experts only! Leave
this enabled unless you know what you are doing!
.TP
.B \-\-cartridge <imagefile>
Use ROM cartridge image <file> (only works if GEMDOS HD emulation and
extended VDI resolution are disabled)
.SH "CPU options"
.TP
.B \-\-cpulevel <x>
Specify CPU (680x0) to use (use x >= 1 with EmuTOS or TOS >= 2.06 only!)
.TP
.B \-\-cpuclock <x>
Set the CPU clock (8, 16 or 32 Mhz)
.TP
.B \-\-compatible <bool>
Use a more compatible, but slower 68000 CPU mode with
better prefetch accuracy and cycle counting
.SH "Misc system options"
.TP
.B \-\-machine <x>
Select machine type (x = st, ste, tt or falcon)
.TP
.B \-\-blitter <bool>
Enable blitter emulation (ST only)
.TP
.B \-\-dsp <x>
Falcon DSP emulation (x = none, dummy or emu, Falcon only)
.TP
.B \-\-timer\-d <bool>
Patch redundantly high Timer-D frequency set by TOS. This about doubles
Hatari speed (for ST/e emulation) as the original Timer-D frequency causes
most of the interrupts.
.TP
.B \-\-fast\-boot <bool>
Patch TOS and initialize the so-called "memvalid" system variables to by-pass
the memory test of TOS, so that the system boots faster.
.TP
.B \-\-rtc <bool>
Enable real-time clock
.SH "Sound options"
.TP
.B \-\-mic <bool>
Enable/disable (Falcon only) microphone
.TP
.B \-\-sound <x>
Sound frequency: 6000-50066. "off" disables the sound and speeds up
the emulation. To prevent extra sound artifacts, the frequency should be
selected so that it either matches evenly with the STE/TT/Falcon sound
DMA (6258, 12517, 250033, 50066 Hz) or your sound card frequencies
(11025, 22050, 44100 or 6000...48000 Hz). Check what your sound card
supports.
.TP
.B \-\-sound\-buffer\-size <x>
SDL's sound buffer size: 10-100, or 0 to use default buffer size.
By default Hatari uses an SDL buffer size of 1024 samples, which
gives approximatively 20-30 ms of sound depending on the chosen sound
frequency. Under some OS or with not fully supported sound card, this
default setting can cause a bigger delay at lower frequency (nearly 0.5 sec).
In that case, you can use this option to force the size of the sound
buffer to a fixed number of milliseconds of sound (using 20 is often
a good choice if you have such problems). Most users will not need this option.
.TP
.B \-\-sound\-sync <bool>
The emulation rate is nudged by +100 or 0 or \-100 micro-seconds on occasion.
This prevents the sound buffer from overflowing (long latency and
lost samples) or underflowing (short latency and repeated samples).
The emulation rate smoothly deviates by a maximum of 0.58% until
synchronized, while the emulator continuously generates every sound
sample and the crystal controlled sound system consumes every sample.
.br
(on|off, off=default)
.TP
.B \-\-ym\-mixing <x>
Select a method for mixing the three YM2149 voice volumes together.
"model" uses a mathematical model of the YM voices,
"table" uses a lookup table of audio output voltage values measured
on STF and "linear" just averages the 3 YM voices.
.SH "Debug options"
.TP
.B \-W, \-\-wincon
Open console window (Windows only)
.TP
.B \-D, \-\-debug
Toggle whether CPU exceptions invoke the debugger
.TP
.B \-\-debug\-except <flags>
Specify which exceptions invoke debugger, see
.B \-\-debug\-except help
for available (comma separated) exception flags.
.TP
.B \-\-bios\-intercept
Toggle XBios command parsing. Allows Atari programs to use all Hatari
functionality and change Hatari state through Hatari specifit
XBios(255) calls. XBios(20) printscreen calls produce also Hatari
screenshots.
.TP
.B \-\-conout <device>
Enable console (xconout vector functions) output redirection for given
<device> to host terminal. Device 2 is for the (CON:) VT52 console,
which vector function catches also EmuTOS panic messages and MiNT
console output, not just normal BIOS console output.
.TP
.B \-\-disasm <x>
Set disassembly options. 'uae' and 'ext' select the dissasembly engine
to use, bitmask sets output options for the external disassembly engine
and 'help' lists them.
.TP
.B \-\-natfeats <bool>
Enable/disable (basic) Native Features support.
E.g. EmuTOS uses it for debug output.
.TP
.B \-\-trace <flags>
Activate debug traces, see
.B \-\-trace help
for available (comma separated) tracing flags
.TP
.B \-\-trace\-file <file>
Save trace output to <file> (default=stderr)
.TP
.B \-\-parse <file>
Parse/execute debugger commands from <file>
.TP
.B \-\-saveconfig
Save Hatari configuration and exit. Hatari UI needs Hatari configuration
file to start, this can be used to create it automatically.
.TP
.B \-\-no\-parachute
Disable SDL parachute to get Hatari core dumps. SDL parachute is enabled
by default to restore video mode in case Hatari terminates abnormally
while using non-standard screen resolution.
.TP
.B \-\-control\-socket <file>
Hatari reads options from given socket at run-time
.TP
.B \-\-log\-file <file>
Save log output to <file> (default=stderr)
.TP
.B \-\-log\-level <x>
Log output level (x=debug/todo/info/warn/error/fatal)
.TP
.B \-\-alert\-level <x>
Show dialog for log messages above given level
.TP
.B \-\-run\-vbls <x>
Exit after X VBLs
.SH "INPUT HANDLING"
Hatari provides special input handling for different purposes.
.SH "Emulated Atari ST joystick"
Joystick can be emulated either with keyboard or any real joystick
supported by your kernel / SDL library. First joystick button
acts as FIRE, second as SPACE key.
.SH "Emulated Atari ST mouse"
Middle button mouse click is interpreted as double click, this
is especially useful in Fast Forward mode.
.PP
Mouse scrollwheel will act as cursor up and down keys.
.SH "Emulated Atari ST keyboard"
Keys on the keyboard act as the normal Atari ST keys so pressing SPACE
on your PC will result in an emulated press of the SPACE key on the
ST. How the PC keys are mapped to Atari key codes, can be changed
with keyboard config file (-k option).
.PP
The following keys have special meanings:
.TP
.B Alt
will act as the ST's ALTERNATE key
.TP
.B left Ctrl
will act as the ST's CONTROL key
.TP
.B Page Up
will emulate the ST's HELP key
.TP
.B Page Down
will emulate the ST's UNDO key
.PP
.B AltGr
will act as
.B Alternate
as well as long as you do not press it together with a Hatari hotkey
combination.
.PP
The
.B right Ctrl
key is used as the fire button of the emulated joystick when you turn
on joystick emulation via keyboard.
.PP
The cursor keys will act as the cursor keys on the Atari ST as long as
joystick emulation via keyboard has been turned off.
.SH "Keyboard shortcuts during emulation"
The shortcut keys can be configured in the configuration file.
The default settings are:
.TP
.B AltGr + a
record animation
.TP
.B AltGr + g
grab a screenshot
.TP
.B AltGr + i
boss key: leave full screen mode and iconify window
.TP
.B AltGr + m
(un-)lock the mouse into the window
.TP
.B AltGr + r
warm reset the ST (same as the reset button)
.TP
.B AltGr + c
cold reset the ST (same as the power switch)
.TP
.B AltGr + d
open dialog to select/change disk A
.TP
.B AltGr + s
enable/disable sound
.TP
.B AltGr + q
quit the emulator
.TP
.B AltGr + x
toggle normal/max speed
.TP
.B AltGr + y
enable/disable sound recording
.TP
.B AltGr + k
save memory snapshot
.TP
.B AltGr + l
load memory snapshot
.TP
.B AltGr + j
toggle joystick emulation via cursor keys
.TP
.B AltGr + F1
switch joystick type on joy port 0
.TP
.B AltGr + F2
switch joystick type on joy port 1
.TP
.B AltGr + F3
switch joystick type for joypad A
.TP
.B AltGr + F4
switch joystick type for joypad B
.TP
.B F11
toggle between fullscreen and windowed mode
.TP
.B F12
activate the hatari options GUI
.br
You may need to hold SHIFT down while in windowed mode.
.TP
.B Pause
Pauses the emulation
.TP
.B AltGr + Pause
Invokes the internal Hatari debugger
.SH "Keyboard shortcuts for the SDL GUI"
There are multiple ways to interact with the SDL GUI.
.PP
TAB and cursor keys change focus between UI elements. Additionally
Home key moves focus to first item, End key to last one. Initially
focus is on default UI element, but focus changes are remembered
between dialog invocations. Enter and Space invoke focused item. UI
elements with underlined characters can be invoked directly with Alt +
key with that character. Alt + arrow keys will act on arrow buttons.
.PP
Most importantly:
.TP
.B Options GUI main view
Enter accepts configuration, ESC cancels it.
.TP
.B Options GUI dialogs
Enter (or End+Enter if focus was moved) returns back to main view.
.TP
.B Fileselector
Page up and down keys scroll the file list. Enter on focused file
name selects it. Enter on OK button accepts the selected file. ESC
cancels the dialog/selection.
.TP
.B Alert dialogs
Enter accepts and ESC cancels the dialog.
.SH "SEE ALSO"
The main program documentation, usually in /usr/share/doc/.
Among other things it contains an extensive usage manual,
software compatibility list and release notes.
.PP
The homepage of hatari: http://hatari.tuxfamily.org/
.PP
Other Hatari programs and utilities:
.br
.IR hmsa (1),
.IR zip2st (1),
.IR atari\-convert\-dir (1),
.IR atari\-hd\-image (1),
.IR hatariui (1),
.IR hconsole (1),
.IR gst2ascii (1),
.IR hatari_profile (1)
.SH "FILES AND DIRECTORIES"
.TP
/etc/hatari.cfg (or /usr/local/etc/hatari.cfg)
The global configuration file of Hatari.
.TP
~/.hatari/
The (default) directory for user's personal Hatari files;
.B hatari.cfg
(configuration file),
.B hatari.nvram
(NVRAM content file),
.B hatari.sav
(Hatari memory state snapshot file which Hatari can load/save automatically
when it starts/exits),
.B hatari.prn
(printer output file),
.B hatari.wav
(recorded sound output in WAV format),
.B hatari.ym
(recorded sound output in YM format).
.TP
/usr/share/hatari/ (or /usr/local/share/hatari/)
The global data directory of Hatari.
.TP
tos.img
The TOS ROM image will be loaded from the data directory of Hatari unless it
is specified on the command line or the configuration file.
.SH "AUTHOR"
This manual page was written by Marco Herrn <marco@mherrn.de> for the
Debian project and later modified by Thomas Huth and Eero Tamminen to
suit the latest version of Hatari.

BIN
doc/images/callgraph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

715
doc/images/callgraph.svg Normal file
View File

@ -0,0 +1,715 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.26.3 (20100126.1600)
-->
<!-- Title: profile Pages: 1 -->
<svg width="842pt" height="595pt"
viewBox="314.28 72.00 527.72 523.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph1" class="graph" transform="scale(0.137082 0.137082) rotate(0) translate(2296.65 3811.23)">
<title>profile</title>
<polygon fill="white" stroke="white" points="-4,5 -4,-3286 1554,-3286 1554,5 -4,5"/>
<text text-anchor="middle" x="774.5" y="-3265.4" font-family="Times Roman,serif" font-size="14.00">Executed instructions</text>
<text text-anchor="middle" x="774.5" y="-3247.4" font-family="Times Roman,serif" font-size="14.00">for badmood&#45;1&#45;frame&#45;CPU.txt</text>
<text text-anchor="middle" x="774.5" y="-3229.4" font-family="Times Roman,serif" font-size="14.00">(Hatari v1.6.2+ (May &#160;4 2013), WinUAE CPU core)</text>
<text text-anchor="middle" x="774.5" y="-3195.4" font-family="Times Roman,serif" font-size="14.00">own cost emphasis (gray bg) &amp; total cost emphasis (red) limit = 2.00%</text>
<text text-anchor="middle" x="774.5" y="-3177.4" font-family="Times Roman,serif" font-size="14.00">nodes which are subroutines and have accurate total costs, have diamond shape</text>
<text text-anchor="middle" x="774.5" y="-3159.4" font-family="Times Roman,serif" font-size="14.00">24 leaf and/or intermediate nodes below 0.20% were removed</text>
<!-- N_48100 -->
<g id="node1" class="node"><title>N_48100</title>
<polygon fill="none" stroke="red" stroke-width="2" points="844,-2742 710,-2644 844,-2546 978,-2644 844,-2742"/>
<text text-anchor="middle" x="844" y="-2676.4" font-family="Times Roman,serif" font-size="14.00">4.30%</text>
<text text-anchor="middle" x="844" y="-2658.4" font-family="Times Roman,serif" font-size="14.00">(own: 0.25%)</text>
<text text-anchor="middle" x="844" y="-2640.4" font-family="Times Roman,serif" font-size="14.00">5819</text>
<text text-anchor="middle" x="844" y="-2622.4" font-family="Times Roman,serif" font-size="14.00">visplane_tryflush</text>
<text text-anchor="middle" x="844" y="-2604.4" font-family="Times Roman,serif" font-size="14.00">(15 calls)</text>
</g>
<!-- N_480B2 -->
<g id="node12" class="node"><title>N_480B2</title>
<polygon fill="none" stroke="red" stroke-width="2" points="927,-2438 807,-2340 927,-2242 1047,-2340 927,-2438"/>
<text text-anchor="middle" x="927" y="-2372.4" font-family="Times Roman,serif" font-size="14.00">4.05%</text>
<text text-anchor="middle" x="927" y="-2354.4" font-family="Times Roman,serif" font-size="14.00">(own: 0.05%)</text>
<text text-anchor="middle" x="927" y="-2336.4" font-family="Times Roman,serif" font-size="14.00">5486</text>
<text text-anchor="middle" x="927" y="-2318.4" font-family="Times Roman,serif" font-size="14.00">flush_visplanes</text>
<text text-anchor="middle" x="927" y="-2300.4" font-family="Times Roman,serif" font-size="14.00">(7 calls)</text>
</g>
<!-- N_48100&#45;&gt;N_480B2 -->
<g id="edge64" class="edge"><title>N_48100&#45;&gt;N_480B2</title>
<path fill="none" stroke="red" stroke-width="2" d="M866.352,-2562.13C876.664,-2524.36 888.998,-2479.19 899.841,-2439.47"/>
<polygon fill="red" stroke="red" points="906.596,-2441.31 905.111,-2420.17 893.091,-2437.62 906.596,-2441.31"/>
<text text-anchor="middle" x="967" y="-2497.4" font-family="Times Roman,serif" font-size="14.00">visplane_tryflush+86</text>
<text text-anchor="middle" x="967" y="-2479.4" font-family="Times Roman,serif" font-size="14.00">($48156)</text>
</g>
<!-- N_48780 -->
<g id="node2" class="node"><title>N_48780</title>
<ellipse fill="none" stroke="black" cx="748" cy="-57" rx="125.865" ry="56.5685"/>
<text text-anchor="middle" x="748" y="-80.4" font-family="Times Roman,serif" font-size="14.00">0.98%</text>
<text text-anchor="middle" x="748" y="-62.4" font-family="Times Roman,serif" font-size="14.00">1326</text>
<text text-anchor="middle" x="748" y="-44.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_RHS</text>
<text text-anchor="middle" x="748" y="-26.4" font-family="Times Roman,serif" font-size="14.00">(587 calls)</text>
</g>
<!-- N_48780&#45;&gt;N_48780 -->
<g id="edge26" class="edge"><title>N_48780&#45;&gt;N_48780</title>
<path fill="none" stroke="black" d="M867.729,-74.8442C882.389,-71.6492 892,-65.7012 892,-57 892,-51.9696 888.788,-47.8594 883.245,-44.6694"/>
<ellipse fill="black" stroke="black" cx="875.267" cy="-41.8346" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="983.5" y="-80.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_RHS+4</text>
<text text-anchor="middle" x="983.5" y="-62.4" font-family="Times Roman,serif" font-size="14.00">($48784)</text>
<text text-anchor="middle" x="983.5" y="-44.4" font-family="Times Roman,serif" font-size="14.00">567 calls</text>
<text text-anchor="middle" x="983.5" y="-26.4" font-family="Times Roman,serif" font-size="14.00">=96.59%</text>
</g>
<!-- N_48728 -->
<g id="node8" class="node"><title>N_48728</title>
<ellipse fill="none" stroke="black" cx="419" cy="-588" rx="85.7684" ry="56.5685"/>
<text text-anchor="middle" x="419" y="-611.4" font-family="Times Roman,serif" font-size="14.00">0.03%</text>
<text text-anchor="middle" x="419" y="-593.4" font-family="Times Roman,serif" font-size="14.00">46</text>
<text text-anchor="middle" x="419" y="-575.4" font-family="Times Roman,serif" font-size="14.00">R_PopBSPNode</text>
<text text-anchor="middle" x="419" y="-557.4" font-family="Times Roman,serif" font-size="14.00">(23 calls)</text>
</g>
<!-- N_48780&#45;&gt;N_48728 -->
<g id="edge98" class="edge"><title>N_48780&#45;&gt;N_48728</title>
<path fill="none" stroke="black" d="M818.547,-104.367C826.395,-112.63 833.255,-121.87 838,-132 851.573,-160.979 859.381,-180.191 838,-204 813.061,-231.772 701.515,-200.504 671,-222 570.084,-293.089 624.549,-373.612 550,-472 532.594,-494.973 510.096,-516.851 488.671,-535.29"/>
<ellipse fill="black" stroke="black" cx="482.355" cy="-540.59" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="767" y="-325.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_RHS+18</text>
<text text-anchor="middle" x="767" y="-307.4" font-family="Times Roman,serif" font-size="14.00">($48792)</text>
<text text-anchor="middle" x="767" y="-289.4" font-family="Times Roman,serif" font-size="14.00">3 calls</text>
<text text-anchor="middle" x="767" y="-271.4" font-family="Times Roman,serif" font-size="14.00">=13.04%</text>
</g>
<!-- N_4872E -->
<g id="node11" class="node"><title>N_4872E</title>
<ellipse fill="lightgray" stroke="red" cx="419" cy="-302" rx="99.201" ry="56.5685"/>
<text text-anchor="middle" x="419" y="-325.4" font-family="Times Roman,serif" font-size="14.00">3.82%</text>
<text text-anchor="middle" x="419" y="-307.4" font-family="Times Roman,serif" font-size="14.00">5170</text>
<text text-anchor="middle" x="419" y="-289.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane</text>
<text text-anchor="middle" x="419" y="-271.4" font-family="Times Roman,serif" font-size="14.00">(36 calls)</text>
</g>
<!-- N_48780&#45;&gt;N_4872E -->
<g id="edge14" class="edge"><title>N_48780&#45;&gt;N_4872E</title>
<path fill="none" stroke="red" stroke-width="2" d="M673.714,-102.737C663.386,-111.48 653.695,-121.289 646,-132 626.4,-159.282 645.052,-178.658 623,-204 596.708,-234.215 559.009,-255.906 523.156,-271.07"/>
<ellipse fill="red" stroke="red" cx="515.366" cy="-274.196" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="742" y="-191.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_RHS+24</text>
<text text-anchor="middle" x="742" y="-173.4" font-family="Times Roman,serif" font-size="14.00">($48798)</text>
<text text-anchor="middle" x="742" y="-155.4" font-family="Times Roman,serif" font-size="14.00">11 calls</text>
<text text-anchor="middle" x="742" y="-137.4" font-family="Times Roman,serif" font-size="14.00">=30.56%</text>
</g>
<!-- N_481F8 -->
<g id="node31" class="node"><title>N_481F8</title>
<ellipse fill="none" stroke="black" cx="86" cy="-3093" rx="75.1594" ry="56.5685"/>
<text text-anchor="middle" x="86" y="-3116.4" font-family="Times Roman,serif" font-size="14.00">0.06%</text>
<text text-anchor="middle" x="86" y="-3098.4" font-family="Times Roman,serif" font-size="14.00">77</text>
<text text-anchor="middle" x="86" y="-3080.4" font-family="Times Roman,serif" font-size="14.00">ssector_node</text>
<text text-anchor="middle" x="86" y="-3062.4" font-family="Times Roman,serif" font-size="14.00">(16 calls)</text>
</g>
<!-- N_48780&#45;&gt;N_481F8 -->
<g id="edge28" class="edge"><title>N_48780&#45;&gt;N_481F8</title>
<path fill="none" stroke="black" d="M831.03,-99.7083C842.137,-108.884 852.068,-119.622 859,-132 886.149,-180.478 886.016,-331.429 863,-382 777.79,-569.226 687.127,-574.907 514,-686 415.897,-748.951 341.882,-698.533 275,-794 211.063,-885.263 256,-932.568 256,-1044 256,-1464 256,-1464 256,-1464 256,-1548.18 244.173,-1568.57 233,-1652 225.012,-1711.65 234.437,-1875.43 194,-1920 171.548,-1944.75 143.385,-1913.19 121,-1938 45.8411,-2021.3 86,-2075.81 86,-2188 86,-2907 86,-2907 86,-2907 86,-2944.32 86,-2985.86 86,-3020.2"/>
<ellipse fill="black" stroke="black" cx="86" cy="-3028.43" rx="8" ry="8"/>
<text text-anchor="middle" x="341" y="-1639.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_RHS+26</text>
<text text-anchor="middle" x="341" y="-1621.4" font-family="Times Roman,serif" font-size="14.00">($4879a)</text>
<text text-anchor="middle" x="341" y="-1603.4" font-family="Times Roman,serif" font-size="14.00">6 calls</text>
<text text-anchor="middle" x="341" y="-1585.4" font-family="Times Roman,serif" font-size="14.00">=37.50%</text>
</g>
<!-- N_48206 -->
<g id="node3" class="node"><title>N_48206</title>
<ellipse fill="none" stroke="black" cx="448" cy="-2907" rx="74.9533" ry="56.5685"/>
<text text-anchor="middle" x="448" y="-2930.4" font-family="Times Roman,serif" font-size="14.00">0.41%</text>
<text text-anchor="middle" x="448" y="-2912.4" font-family="Times Roman,serif" font-size="14.00">550</text>
<text text-anchor="middle" x="448" y="-2894.4" font-family="Times Roman,serif" font-size="14.00">build_ssector</text>
<text text-anchor="middle" x="448" y="-2876.4" font-family="Times Roman,serif" font-size="14.00">(15 calls)</text>
</g>
<!-- N_48206&#45;&gt;N_48100 -->
<g id="edge34" class="edge"><title>N_48206&#45;&gt;N_48100</title>
<path fill="none" stroke="red" stroke-width="2" d="M521.844,-2896.22C573.251,-2886.02 641.539,-2866.87 693,-2832 732.192,-2805.44 766.224,-2765.66 792.008,-2729.52"/>
<polygon fill="red" stroke="red" points="798.024,-2733.13 803.625,-2712.69 786.503,-2725.17 798.024,-2733.13"/>
<text text-anchor="middle" x="829.5" y="-2801.4" font-family="Times Roman,serif" font-size="14.00">build_ssector+116</text>
<text text-anchor="middle" x="829.5" y="-2783.4" font-family="Times Roman,serif" font-size="14.00">($4827a)</text>
</g>
<!-- N_482A0 -->
<g id="node7" class="node"><title>N_482A0</title>
<ellipse fill="lightgray" stroke="red" cx="336" cy="-2644" rx="79.196" ry="56.5685"/>
<text text-anchor="middle" x="336" y="-2667.4" font-family="Times Roman,serif" font-size="14.00">2.04%</text>
<text text-anchor="middle" x="336" y="-2649.4" font-family="Times Roman,serif" font-size="14.00">2766</text>
<text text-anchor="middle" x="336" y="-2631.4" font-family="Times Roman,serif" font-size="14.00">segment_loop</text>
<text text-anchor="middle" x="336" y="-2613.4" font-family="Times Roman,serif" font-size="14.00">(50 calls)</text>
</g>
<!-- N_48206&#45;&gt;N_482A0 -->
<g id="edge68" class="edge"><title>N_48206&#45;&gt;N_482A0</title>
<path fill="none" stroke="red" stroke-width="2" d="M419.781,-2854.5C416.026,-2847.01 412.328,-2839.35 409,-2832 391.648,-2793.65 374.495,-2749.87 361.181,-2714.2"/>
<ellipse fill="red" stroke="red" cx="358.312" cy="-2706.45" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="473.5" y="-2819.4" font-family="Times Roman,serif" font-size="14.00">build_ssector+150</text>
<text text-anchor="middle" x="473.5" y="-2801.4" font-family="Times Roman,serif" font-size="14.00">($4829c)</text>
<text text-anchor="middle" x="473.5" y="-2783.4" font-family="Times Roman,serif" font-size="14.00">15 calls</text>
<text text-anchor="middle" x="473.5" y="-2765.4" font-family="Times Roman,serif" font-size="14.00">=30.00%</text>
</g>
<!-- N_4A3E8 -->
<g id="node26" class="node"><title>N_4A3E8</title>
<polygon fill="none" stroke="black" points="560,-2724 433,-2644 560,-2564 687,-2644 560,-2724"/>
<text text-anchor="middle" x="560" y="-2667.4" font-family="Times Roman,serif" font-size="14.00">0.47%</text>
<text text-anchor="middle" x="560" y="-2649.4" font-family="Times Roman,serif" font-size="14.00">630</text>
<text text-anchor="middle" x="560" y="-2631.4" font-family="Times Roman,serif" font-size="14.00">process_lighting</text>
<text text-anchor="middle" x="560" y="-2613.4" font-family="Times Roman,serif" font-size="14.00">(15 calls)</text>
</g>
<!-- N_48206&#45;&gt;N_4A3E8 -->
<g id="edge96" class="edge"><title>N_48206&#45;&gt;N_4A3E8</title>
<path fill="none" stroke="black" d="M505.674,-2870.08C518.265,-2859.32 530.156,-2846.49 538,-2832 552.691,-2804.87 559.565,-2772.35 562.38,-2742.05"/>
<polygon fill="black" stroke="black" points="569.369,-2742.45 563.706,-2722.03 555.4,-2741.52 569.369,-2742.45"/>
<text text-anchor="middle" x="624.5" y="-2801.4" font-family="Times Roman,serif" font-size="14.00">build_ssector+112</text>
<text text-anchor="middle" x="624.5" y="-2783.4" font-family="Times Roman,serif" font-size="14.00">($48276)</text>
</g>
<!-- N_48716 -->
<g id="node4" class="node"><title>N_48716</title>
<ellipse fill="none" stroke="black" cx="755" cy="-892" rx="103.238" ry="56.5685"/>
<text text-anchor="middle" x="755" y="-915.4" font-family="Times Roman,serif" font-size="14.00">0.07%</text>
<text text-anchor="middle" x="755" y="-897.4" font-family="Times Roman,serif" font-size="14.00">96</text>
<text text-anchor="middle" x="755" y="-879.4" font-family="Times Roman,serif" font-size="14.00">R_RenderBSPNode</text>
<text text-anchor="middle" x="755" y="-861.4" font-family="Times Roman,serif" font-size="14.00">(16 calls)</text>
</g>
<!-- N_48716&#45;&gt;N_48728 -->
<g id="edge30" class="edge"><title>N_48716&#45;&gt;N_48728</title>
<path fill="none" stroke="black" d="M701.388,-843.494C641.636,-789.433 545.12,-702.108 481.86,-644.873"/>
<ellipse fill="black" stroke="black" cx="475.604" cy="-639.213" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="698" y="-763.4" font-family="Times Roman,serif" font-size="14.00">R_RenderBSPNode+16</text>
<text text-anchor="middle" x="698" y="-745.4" font-family="Times Roman,serif" font-size="14.00">($48726)</text>
<text text-anchor="middle" x="698" y="-727.4" font-family="Times Roman,serif" font-size="14.00">16 calls</text>
<text text-anchor="middle" x="698" y="-709.4" font-family="Times Roman,serif" font-size="14.00">=69.57%</text>
</g>
<!-- N_4819C -->
<g id="node5" class="node"><title>N_4819C</title>
<ellipse fill="none" stroke="black" cx="1320" cy="-2340" rx="62.9325" ry="56.5685"/>
<text text-anchor="middle" x="1320" y="-2363.4" font-family="Times Roman,serif" font-size="14.00">0.01%</text>
<text text-anchor="middle" x="1320" y="-2345.4" font-family="Times Roman,serif" font-size="14.00">20</text>
<text text-anchor="middle" x="1320" y="-2327.4" font-family="Times Roman,serif" font-size="14.00">finish_tree</text>
<text text-anchor="middle" x="1320" y="-2309.4" font-family="Times Roman,serif" font-size="14.00">(1 calls)</text>
</g>
<!-- N_48BDC -->
<g id="node9" class="node"><title>N_48BDC</title>
<polygon fill="none" stroke="black" points="1180,-2134 1069,-2036 1180,-1938 1291,-2036 1180,-2134"/>
<text text-anchor="middle" x="1180" y="-2068.4" font-family="Times Roman,serif" font-size="14.00">0.63%</text>
<text text-anchor="middle" x="1180" y="-2050.4" font-family="Times Roman,serif" font-size="14.00">(own: 0.05%)</text>
<text text-anchor="middle" x="1180" y="-2032.4" font-family="Times Roman,serif" font-size="14.00">857</text>
<text text-anchor="middle" x="1180" y="-2014.4" font-family="Times Roman,serif" font-size="14.00">get_flat_floor</text>
<text text-anchor="middle" x="1180" y="-1996.4" font-family="Times Roman,serif" font-size="14.00">(2 calls)</text>
</g>
<!-- N_4819C&#45;&gt;N_48BDC -->
<g id="edge56" class="edge"><title>N_4819C&#45;&gt;N_48BDC</title>
<path fill="none" stroke="black" d="M1317.49,-2283.23C1314.68,-2236.5 1308.98,-2174.43 1298,-2152 1287.69,-2130.95 1271.97,-2111.53 1255.33,-2094.84"/>
<polygon fill="black" stroke="black" points="1259.93,-2089.56 1240.58,-2080.91 1250.31,-2099.73 1259.93,-2089.56"/>
<text text-anchor="middle" x="1363.5" y="-2211.4" font-family="Times Roman,serif" font-size="14.00">finish_tree+64</text>
<text text-anchor="middle" x="1363.5" y="-2193.4" font-family="Times Roman,serif" font-size="14.00">($481dc)</text>
<text text-anchor="middle" x="1363.5" y="-2175.4" font-family="Times Roman,serif" font-size="14.00">1 calls</text>
<text text-anchor="middle" x="1363.5" y="-2157.4" font-family="Times Roman,serif" font-size="14.00">=50.00%</text>
</g>
<!-- N_48B74 -->
<g id="node15" class="node"><title>N_48B74</title>
<polygon fill="none" stroke="black" points="1429,-2134 1309,-2036 1429,-1938 1549,-2036 1429,-2134"/>
<text text-anchor="middle" x="1429" y="-2068.4" font-family="Times Roman,serif" font-size="14.00">0.35%</text>
<text text-anchor="middle" x="1429" y="-2050.4" font-family="Times Roman,serif" font-size="14.00">(own: 0.08%)</text>
<text text-anchor="middle" x="1429" y="-2032.4" font-family="Times Roman,serif" font-size="14.00">470</text>
<text text-anchor="middle" x="1429" y="-2014.4" font-family="Times Roman,serif" font-size="14.00">get_flat_ceiling</text>
<text text-anchor="middle" x="1429" y="-1996.4" font-family="Times Roman,serif" font-size="14.00">(3 calls)</text>
</g>
<!-- N_4819C&#45;&gt;N_48B74 -->
<g id="edge52" class="edge"><title>N_4819C&#45;&gt;N_48B74</title>
<path fill="none" stroke="black" d="M1366.98,-2301.85C1388.59,-2281.3 1412.05,-2254.03 1424,-2224 1433.62,-2199.82 1437.74,-2172.48 1438.84,-2146.29"/>
<polygon fill="black" stroke="black" points="1445.84,-2146.18 1439.14,-2126.08 1431.85,-2145.98 1445.84,-2146.18"/>
<text text-anchor="middle" x="1489.5" y="-2211.4" font-family="Times Roman,serif" font-size="14.00">finish_tree+40</text>
<text text-anchor="middle" x="1489.5" y="-2193.4" font-family="Times Roman,serif" font-size="14.00">($481c4)</text>
<text text-anchor="middle" x="1489.5" y="-2175.4" font-family="Times Roman,serif" font-size="14.00">1 calls</text>
<text text-anchor="middle" x="1489.5" y="-2157.4" font-family="Times Roman,serif" font-size="14.00">=33.33%</text>
</g>
<!-- N_4879E -->
<g id="node6" class="node"><title>N_4879E</title>
<ellipse fill="none" stroke="black" cx="124" cy="-57" rx="123.951" ry="56.5685"/>
<text text-anchor="middle" x="124" y="-80.4" font-family="Times Roman,serif" font-size="14.00">0.75%</text>
<text text-anchor="middle" x="124" y="-62.4" font-family="Times Roman,serif" font-size="14.00">1015</text>
<text text-anchor="middle" x="124" y="-44.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_LHS</text>
<text text-anchor="middle" x="124" y="-26.4" font-family="Times Roman,serif" font-size="14.00">(449 calls)</text>
</g>
<!-- N_4879E&#45;&gt;N_4879E -->
<g id="edge62" class="edge"><title>N_4879E&#45;&gt;N_4879E</title>
<path fill="none" stroke="black" d="M241.798,-74.9031C256.405,-71.7226 266,-65.7549 266,-57 266,-51.9386 262.793,-47.8087 257.265,-44.6104"/>
<ellipse fill="black" stroke="black" cx="249.333" cy="-41.7831" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="356.5" y="-80.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_LHS+4</text>
<text text-anchor="middle" x="356.5" y="-62.4" font-family="Times Roman,serif" font-size="14.00">($487a2)</text>
<text text-anchor="middle" x="356.5" y="-44.4" font-family="Times Roman,serif" font-size="14.00">433 calls</text>
<text text-anchor="middle" x="356.5" y="-26.4" font-family="Times Roman,serif" font-size="14.00">=96.44%</text>
</g>
<!-- N_4879E&#45;&gt;N_48728 -->
<g id="edge54" class="edge"><title>N_4879E&#45;&gt;N_48728</title>
<path fill="none" stroke="black" d="M73.2152,-108.58C68.2543,-115.973 63.9683,-123.851 61,-132 21.8903,-239.365 52.2545,-290.726 121,-382 176.875,-456.185 267.44,-513.327 334.086,-548.406"/>
<ellipse fill="black" stroke="black" cx="341.238" cy="-552.099" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="216" y="-325.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_LHS+18</text>
<text text-anchor="middle" x="216" y="-307.4" font-family="Times Roman,serif" font-size="14.00">($487b0)</text>
<text text-anchor="middle" x="216" y="-289.4" font-family="Times Roman,serif" font-size="14.00">4 calls</text>
<text text-anchor="middle" x="216" y="-271.4" font-family="Times Roman,serif" font-size="14.00">=17.39%</text>
</g>
<!-- N_4879E&#45;&gt;N_4872E -->
<g id="edge32" class="edge"><title>N_4879E&#45;&gt;N_4872E</title>
<path fill="none" stroke="red" stroke-width="2" d="M201.571,-101.325C212.915,-110.351 223.588,-120.618 232,-132 251.967,-159.015 234.121,-177.683 255,-204 273.437,-227.239 299.009,-246.202 324.48,-261.04"/>
<ellipse fill="red" stroke="red" cx="331.579" cy="-264.979" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="350" y="-191.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_LHS+26</text>
<text text-anchor="middle" x="350" y="-173.4" font-family="Times Roman,serif" font-size="14.00">($487b8)</text>
<text text-anchor="middle" x="350" y="-155.4" font-family="Times Roman,serif" font-size="14.00">8 calls</text>
<text text-anchor="middle" x="350" y="-137.4" font-family="Times Roman,serif" font-size="14.00">=22.22%</text>
</g>
<!-- N_4879E&#45;&gt;N_481F8 -->
<g id="edge74" class="edge"><title>N_4879E&#45;&gt;N_481F8</title>
<path fill="none" stroke="black" d="M60.061,-105.628C52.6545,-113.701 45.9821,-122.551 41,-132 5.43875,-199.443 18,-225.756 18,-302 18,-1464 18,-1464 18,-1464 18,-1515.57 15.8788,-1528.43 15,-1580 10.3955,-1850.19 10,-1917.77 10,-2188 10,-2907 10,-2907 10,-2907 10,-2949.27 27.0314,-2993.21 44.9692,-3027.75"/>
<ellipse fill="black" stroke="black" cx="48.8531" cy="-3034.92" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="110" y="-1639.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane_LHS+30</text>
<text text-anchor="middle" x="110" y="-1621.4" font-family="Times Roman,serif" font-size="14.00">($487bc)</text>
<text text-anchor="middle" x="110" y="-1603.4" font-family="Times Roman,serif" font-size="14.00">4 calls</text>
<text text-anchor="middle" x="110" y="-1585.4" font-family="Times Roman,serif" font-size="14.00">=25.00%</text>
</g>
<!-- N_483BA -->
<g id="node16" class="node"><title>N_483BA</title>
<ellipse fill="none" stroke="black" cx="466" cy="-2340" rx="99.9096" ry="56.5685"/>
<text text-anchor="middle" x="466" y="-2363.4" font-family="Times Roman,serif" font-size="14.00">0.26%</text>
<text text-anchor="middle" x="466" y="-2345.4" font-family="Times Roman,serif" font-size="14.00">357</text>
<text text-anchor="middle" x="466" y="-2327.4" font-family="Times Roman,serif" font-size="14.00">seg_prelight_done</text>
<text text-anchor="middle" x="466" y="-2309.4" font-family="Times Roman,serif" font-size="14.00">(21 calls)</text>
</g>
<!-- N_482A0&#45;&gt;N_483BA -->
<g id="edge44" class="edge"><title>N_482A0&#45;&gt;N_483BA</title>
<path fill="none" stroke="black" d="M372.952,-2593.79C387.379,-2576.84 405.019,-2558.96 424,-2546 442.818,-2533.16 459.022,-2546.73 472,-2528 495.111,-2494.65 494.375,-2449.39 487.496,-2411.63"/>
<ellipse fill="black" stroke="black" cx="485.859" cy="-2403.8" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="562" y="-2515.4" font-family="Times Roman,serif" font-size="14.00">segment_loop+&#45;498</text>
<text text-anchor="middle" x="562" y="-2497.4" font-family="Times Roman,serif" font-size="14.00">($480ae)</text>
<text text-anchor="middle" x="562" y="-2479.4" font-family="Times Roman,serif" font-size="14.00">1 calls</text>
<text text-anchor="middle" x="562" y="-2461.4" font-family="Times Roman,serif" font-size="14.00">=4.76%</text>
</g>
<!-- N_482A0&#45;&gt;N_483BA -->
<g id="edge58" class="edge"><title>N_482A0&#45;&gt;N_483BA</title>
<path fill="none" stroke="black" d="M322.923,-2587.97C316.716,-2549.18 314.218,-2497.3 333,-2456 344.344,-2431.05 363.997,-2409.38 384.767,-2391.73"/>
<ellipse fill="black" stroke="black" cx="391.192" cy="-2386.56" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="400.5" y="-2515.4" font-family="Times Roman,serif" font-size="14.00">segment_loop+278</text>
<text text-anchor="middle" x="400.5" y="-2497.4" font-family="Times Roman,serif" font-size="14.00">($483b6)</text>
<text text-anchor="middle" x="400.5" y="-2479.4" font-family="Times Roman,serif" font-size="14.00">20 calls</text>
<text text-anchor="middle" x="400.5" y="-2461.4" font-family="Times Roman,serif" font-size="14.00">=95.24%</text>
</g>
<!-- N_486CC -->
<g id="node18" class="node"><title>N_486CC</title>
<ellipse fill="none" stroke="black" cx="579" cy="-1750" rx="85.0596" ry="56.5685"/>
<text text-anchor="middle" x="579" y="-1773.4" font-family="Times Roman,serif" font-size="14.00">0.03%</text>
<text text-anchor="middle" x="579" y="-1755.4" font-family="Times Roman,serif" font-size="14.00">36</text>
<text text-anchor="middle" x="579" y="-1737.4" font-family="Times Roman,serif" font-size="14.00">sector_window</text>
<text text-anchor="middle" x="579" y="-1719.4" font-family="Times Roman,serif" font-size="14.00">(36 calls)</text>
</g>
<!-- N_482A0&#45;&gt;N_486CC -->
<g id="edge46" class="edge"><title>N_482A0&#45;&gt;N_486CC</title>
<path fill="none" stroke="black" d="M369.714,-2592.59C384.196,-2574.76 402.711,-2556.6 424,-2546 466.326,-2524.93 601.03,-2559.82 636,-2528 761.604,-2413.71 587.181,-2275.26 704,-2152 727.878,-2126.81 758.468,-2161.23 780,-2134 834.035,-2065.67 814.676,-2017.91 780,-1938 754.553,-1879.36 700.803,-1830.66 655.285,-1797.53"/>
<ellipse fill="black" stroke="black" cx="648.61" cy="-1792.8" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="771.5" y="-2211.4" font-family="Times Roman,serif" font-size="14.00">segment_loop+248</text>
<text text-anchor="middle" x="771.5" y="-2193.4" font-family="Times Roman,serif" font-size="14.00">($48398)</text>
<text text-anchor="middle" x="771.5" y="-2175.4" font-family="Times Roman,serif" font-size="14.00">15 calls</text>
<text text-anchor="middle" x="771.5" y="-2157.4" font-family="Times Roman,serif" font-size="14.00">=41.67%</text>
</g>
<!-- N_486D0 -->
<g id="node20" class="node"><title>N_486D0</title>
<ellipse fill="none" stroke="black" cx="476" cy="-1464" rx="72.832" ry="56.5685"/>
<text text-anchor="middle" x="476" y="-1487.4" font-family="Times Roman,serif" font-size="14.00">0.20%</text>
<text text-anchor="middle" x="476" y="-1469.4" font-family="Times Roman,serif" font-size="14.00">270</text>
<text text-anchor="middle" x="476" y="-1451.4" font-family="Times Roman,serif" font-size="14.00">seg_invisible</text>
<text text-anchor="middle" x="476" y="-1433.4" font-family="Times Roman,serif" font-size="14.00">(50 calls)</text>
</g>
<!-- N_482A0&#45;&gt;N_486D0 -->
<g id="edge70" class="edge"><title>N_482A0&#45;&gt;N_486D0</title>
<path fill="none" stroke="black" d="M310.202,-2590.56C241.361,-2444.35 63.9685,-2041.73 146,-1938 167.532,-1910.77 192.602,-1938.46 222,-1920 343.215,-1843.89 321.863,-1769.24 425,-1670 434.608,-1660.76 442.202,-1663.47 449,-1652 469.336,-1617.68 476.482,-1573.82 478.362,-1537.09"/>
<ellipse fill="black" stroke="black" cx="478.628" cy="-1528.78" rx="8" ry="8"/>
<text text-anchor="middle" x="213.5" y="-2059.4" font-family="Times Roman,serif" font-size="14.00">segment_loop+118</text>
<text text-anchor="middle" x="213.5" y="-2041.4" font-family="Times Roman,serif" font-size="14.00">($48316)</text>
<text text-anchor="middle" x="213.5" y="-2023.4" font-family="Times Roman,serif" font-size="14.00">14 calls</text>
<text text-anchor="middle" x="213.5" y="-2005.4" font-family="Times Roman,serif" font-size="14.00">=28.00%</text>
</g>
<!-- N_48728&#45;&gt;N_4872E -->
<g id="edge92" class="edge"><title>N_48728&#45;&gt;N_4872E</title>
<path fill="none" stroke="red" stroke-width="2" d="M419,-531.496C419,-486.692 419,-423.743 419,-375.103"/>
<ellipse fill="red" stroke="red" cx="419" cy="-366.876" rx="8" ry="8"/>
<text text-anchor="middle" x="482.5" y="-459.4" font-family="Times Roman,serif" font-size="14.00">R_PopBSPNode+2</text>
<text text-anchor="middle" x="482.5" y="-441.4" font-family="Times Roman,serif" font-size="14.00">($4872a)</text>
<text text-anchor="middle" x="482.5" y="-423.4" font-family="Times Roman,serif" font-size="14.00">17 calls</text>
<text text-anchor="middle" x="482.5" y="-405.4" font-family="Times Roman,serif" font-size="14.00">=47.22%</text>
</g>
<!-- N_48728&#45;&gt;N_481F8 -->
<g id="edge38" class="edge"><title>N_48728&#45;&gt;N_481F8</title>
<path fill="none" stroke="black" d="M365.136,-632.418C305.597,-686.837 218,-785.072 218,-892 218,-1330 218,-1330 218,-1330 218,-1473.17 255.048,-1516.44 209,-1652 174.401,-1753.85 103.015,-1746.64 67,-1848 16.3282,-1990.61 48,-2036.65 48,-2188 48,-2907 48,-2907 48,-2907 48,-2945.55 55.9339,-2987.71 64.5512,-3022.12"/>
<ellipse fill="black" stroke="black" cx="66.6417" cy="-3030.11" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="130.5" y="-1907.4" font-family="Times Roman,serif" font-size="14.00">R_PopBSPNode+2</text>
<text text-anchor="middle" x="130.5" y="-1889.4" font-family="Times Roman,serif" font-size="14.00">($4872a)</text>
<text text-anchor="middle" x="130.5" y="-1871.4" font-family="Times Roman,serif" font-size="14.00">6 calls</text>
<text text-anchor="middle" x="130.5" y="-1853.4" font-family="Times Roman,serif" font-size="14.00">=37.50%</text>
</g>
<!-- N_48C74 -->
<g id="node30" class="node"><title>N_48C74</title>
<polygon fill="lightgray" stroke="red" points="1180,-1830 1025,-1750 1180,-1670 1335,-1750 1180,-1830"/>
<text text-anchor="middle" x="1180" y="-1773.4" font-family="Times Roman,serif" font-size="14.00">2.41%</text>
<text text-anchor="middle" x="1180" y="-1755.4" font-family="Times Roman,serif" font-size="14.00">3264</text>
<text text-anchor="middle" x="1180" y="-1737.4" font-family="Times Roman,serif" font-size="14.00">stack_visplane_area</text>
<text text-anchor="middle" x="1180" y="-1719.4" font-family="Times Roman,serif" font-size="14.00">(13 calls)</text>
</g>
<!-- N_48BDC&#45;&gt;N_48C74 -->
<g id="edge78" class="edge"><title>N_48BDC&#45;&gt;N_48C74</title>
<path fill="none" stroke="red" stroke-width="2" d="M1180,-1937.98C1180,-1909.74 1180,-1878.9 1180,-1850.51"/>
<polygon fill="red" stroke="red" points="1187,-1850.35 1180,-1830.35 1173,-1850.35 1187,-1850.35"/>
<text text-anchor="middle" x="1240.5" y="-1907.4" font-family="Times Roman,serif" font-size="14.00">get_flat_floor+90</text>
<text text-anchor="middle" x="1240.5" y="-1889.4" font-family="Times Roman,serif" font-size="14.00">($48c36)</text>
<text text-anchor="middle" x="1240.5" y="-1871.4" font-family="Times Roman,serif" font-size="14.00">2 calls</text>
<text text-anchor="middle" x="1240.5" y="-1853.4" font-family="Times Roman,serif" font-size="14.00">=15.38%</text>
</g>
<!-- N_49C2C -->
<g id="node10" class="node"><title>N_49C2C</title>
<ellipse fill="lightgray" stroke="red" cx="998" cy="-302" rx="92.839" ry="56.5685"/>
<text text-anchor="middle" x="998" y="-325.4" font-family="Times Roman,serif" font-size="14.00">39.97%</text>
<text text-anchor="middle" x="998" y="-307.4" font-family="Times Roman,serif" font-size="14.00">54144</text>
<text text-anchor="middle" x="998" y="-289.4" font-family="Times Roman,serif" font-size="14.00">render_wall_1x1</text>
<text text-anchor="middle" x="998" y="-271.4" font-family="Times Roman,serif" font-size="14.00">(18 calls)</text>
</g>
<!-- N_4872E&#45;&gt;N_48780 -->
<g id="edge94" class="edge"><title>N_4872E&#45;&gt;N_48780</title>
<path fill="none" stroke="black" d="M433.862,-246.033C437.508,-232.289 441.398,-217.613 445,-204 453.466,-172.006 440.6,-155.404 464,-132 485.896,-110.1 550.943,-92.098 613.266,-79.2254"/>
<ellipse fill="black" stroke="black" cx="621.18" cy="-77.6376" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="541.5" y="-191.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane+80</text>
<text text-anchor="middle" x="541.5" y="-173.4" font-family="Times Roman,serif" font-size="14.00">($4877e)</text>
<text text-anchor="middle" x="541.5" y="-155.4" font-family="Times Roman,serif" font-size="14.00">20 calls</text>
<text text-anchor="middle" x="541.5" y="-137.4" font-family="Times Roman,serif" font-size="14.00">=3.41%</text>
</g>
<!-- N_4872E&#45;&gt;N_4879E -->
<g id="edge8" class="edge"><title>N_4872E&#45;&gt;N_4879E</title>
<path fill="none" stroke="black" d="M350.304,-261.002C322.551,-246.292 289.645,-231.104 258,-222 219.155,-210.824 104.131,-233.963 77,-204 57.3349,-182.282 63.0443,-152.298 76.0392,-125.071"/>
<ellipse fill="black" stroke="black" cx="79.8992" cy="-117.79" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="154.5" y="-191.4" font-family="Times Roman,serif" font-size="14.00">R_BSPHyperPlane+80</text>
<text text-anchor="middle" x="154.5" y="-173.4" font-family="Times Roman,serif" font-size="14.00">($4877e)</text>
<text text-anchor="middle" x="154.5" y="-155.4" font-family="Times Roman,serif" font-size="14.00">16 calls</text>
<text text-anchor="middle" x="154.5" y="-137.4" font-family="Times Roman,serif" font-size="14.00">=3.56%</text>
</g>
<!-- N_480B2&#45;&gt;N_48BDC -->
<g id="edge100" class="edge"><title>N_480B2&#45;&gt;N_48BDC</title>
<path fill="none" stroke="black" d="M962.908,-2271.14C970.315,-2255.81 977.753,-2239.51 984,-2224 996.553,-2192.84 983.463,-2175.97 1007,-2152 1024.43,-2134.25 1038.24,-2146.06 1060,-2134 1077.29,-2124.42 1094.53,-2112.26 1110.34,-2099.8"/>
<polygon fill="black" stroke="black" points="1115.19,-2104.88 1126.24,-2086.8 1106.33,-2094.05 1115.19,-2104.88"/>
<text text-anchor="middle" x="1074" y="-2211.4" font-family="Times Roman,serif" font-size="14.00">flush_visplanes+42</text>
<text text-anchor="middle" x="1074" y="-2193.4" font-family="Times Roman,serif" font-size="14.00">($480dc)</text>
<text text-anchor="middle" x="1074" y="-2175.4" font-family="Times Roman,serif" font-size="14.00">1 calls</text>
<text text-anchor="middle" x="1074" y="-2157.4" font-family="Times Roman,serif" font-size="14.00">=50.00%</text>
</g>
<!-- N_480B2&#45;&gt;N_48B74 -->
<g id="edge50" class="edge"><title>N_480B2&#45;&gt;N_48B74</title>
<path fill="none" stroke="black" d="M1013.19,-2312.15C1057.4,-2293.71 1108.62,-2265.2 1141,-2224 1161.45,-2197.98 1135.13,-2173.83 1160,-2152 1207.15,-2110.62 1241.95,-2157.78 1300,-2134 1319.92,-2125.84 1339.47,-2113.8 1357.08,-2100.92"/>
<polygon fill="black" stroke="black" points="1361.4,-2106.43 1372.99,-2088.69 1352.86,-2095.33 1361.4,-2106.43"/>
<text text-anchor="middle" x="1227" y="-2211.4" font-family="Times Roman,serif" font-size="14.00">flush_visplanes+62</text>
<text text-anchor="middle" x="1227" y="-2193.4" font-family="Times Roman,serif" font-size="14.00">($480f0)</text>
<text text-anchor="middle" x="1227" y="-2175.4" font-family="Times Roman,serif" font-size="14.00">2 calls</text>
<text text-anchor="middle" x="1227" y="-2157.4" font-family="Times Roman,serif" font-size="14.00">=66.67%</text>
</g>
<!-- N_48B00 -->
<g id="node23" class="node"><title>N_48B00</title>
<polygon fill="none" stroke="red" stroke-width="2" points="940,-2134 829,-2036 940,-1938 1051,-2036 940,-2134"/>
<text text-anchor="middle" x="940" y="-2068.4" font-family="Times Roman,serif" font-size="14.00">3.25%</text>
<text text-anchor="middle" x="940" y="-2050.4" font-family="Times Roman,serif" font-size="14.00">(own: 1.69%)</text>
<text text-anchor="middle" x="940" y="-2032.4" font-family="Times Roman,serif" font-size="14.00">4401</text>
<text text-anchor="middle" x="940" y="-2014.4" font-family="Times Roman,serif" font-size="14.00">get_ssector</text>
<text text-anchor="middle" x="940" y="-1996.4" font-family="Times Roman,serif" font-size="14.00">(4 calls)</text>
</g>
<!-- N_480B2&#45;&gt;N_48B00 -->
<g id="edge4" class="edge"><title>N_480B2&#45;&gt;N_48B00</title>
<path fill="none" stroke="red" stroke-width="2" d="M878.576,-2281.41C866.913,-2263.98 856.074,-2244.13 850,-2224 840.757,-2193.36 839.633,-2182.27 850,-2152 855.76,-2135.18 865.062,-2118.91 875.549,-2104.15"/>
<polygon fill="red" stroke="red" points="881.181,-2108.31 887.767,-2088.17 870.059,-2099.81 881.181,-2108.31"/>
<text text-anchor="middle" x="917" y="-2193.4" font-family="Times Roman,serif" font-size="14.00">flush_visplanes+10</text>
<text text-anchor="middle" x="917" y="-2175.4" font-family="Times Roman,serif" font-size="14.00">($480bc)</text>
</g>
<!-- N_494B4 -->
<g id="node13" class="node"><title>N_494B4</title>
<polygon fill="lightgray" stroke="red" points="1125,-1294 1005,-1196 1125,-1098 1245,-1196 1125,-1294"/>
<text text-anchor="middle" x="1125" y="-1228.4" font-family="Times Roman,serif" font-size="14.00">34.82%</text>
<text text-anchor="middle" x="1125" y="-1210.4" font-family="Times Roman,serif" font-size="14.00">(own: 27.62%)</text>
<text text-anchor="middle" x="1125" y="-1192.4" font-family="Times Roman,serif" font-size="14.00">47161</text>
<text text-anchor="middle" x="1125" y="-1174.4" font-family="Times Roman,serif" font-size="14.00">render_flats</text>
<text text-anchor="middle" x="1125" y="-1156.4" font-family="Times Roman,serif" font-size="14.00">(1 calls)</text>
</g>
<!-- N_494CC -->
<g id="node19" class="node"><title>N_494CC</title>
<ellipse fill="lightgray" stroke="red" cx="1276" cy="-892" rx="94.0452" ry="56.5685"/>
<text text-anchor="middle" x="1276" y="-915.4" font-family="Times Roman,serif" font-size="14.00">27.56%</text>
<text text-anchor="middle" x="1276" y="-897.4" font-family="Times Roman,serif" font-size="14.00">37335</text>
<text text-anchor="middle" x="1276" y="-879.4" font-family="Times Roman,serif" font-size="14.00">render_flats_1x1</text>
<text text-anchor="middle" x="1276" y="-861.4" font-family="Times Roman,serif" font-size="14.00">(1 calls)</text>
</g>
<!-- N_494B4&#45;&gt;N_494CC -->
<g id="edge76" class="edge"><title>N_494B4&#45;&gt;N_494CC</title>
<path fill="none" stroke="red" stroke-width="2" d="M1159.64,-1126.26C1184.31,-1076.58 1217.32,-1010.14 1241.87,-960.707"/>
<ellipse fill="red" stroke="red" cx="1245.48" cy="-953.437" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="1270.5" y="-1049.4" font-family="Times Roman,serif" font-size="14.00">render_flats+20</text>
<text text-anchor="middle" x="1270.5" y="-1031.4" font-family="Times Roman,serif" font-size="14.00">($494c8)</text>
</g>
<!-- N_47438 -->
<g id="node14" class="node"><title>N_47438</title>
<polygon fill="none" stroke="black" points="1277,-382 1155,-302 1277,-222 1399,-302 1277,-382"/>
<text text-anchor="middle" x="1277" y="-325.4" font-family="Times Roman,serif" font-size="14.00">0.28%</text>
<text text-anchor="middle" x="1277" y="-307.4" font-family="Times Roman,serif" font-size="14.00">378</text>
<text text-anchor="middle" x="1277" y="-289.4" font-family="Times Roman,serif" font-size="14.00">cache_resource</text>
<text text-anchor="middle" x="1277" y="-271.4" font-family="Times Roman,serif" font-size="14.00">(21 calls)</text>
</g>
<!-- N_48B74&#45;&gt;N_48C74 -->
<g id="edge16" class="edge"><title>N_48B74&#45;&gt;N_48C74</title>
<path fill="none" stroke="red" stroke-width="2" d="M1391.64,-1968.27C1369.18,-1930.71 1338.51,-1884.38 1305,-1848 1292.31,-1834.22 1277.55,-1820.86 1262.72,-1808.66"/>
<polygon fill="red" stroke="red" points="1266.69,-1802.88 1246.68,-1795.91 1257.98,-1813.83 1266.69,-1802.88"/>
<text text-anchor="middle" x="1425" y="-1907.4" font-family="Times Roman,serif" font-size="14.00">get_flat_ceiling+86</text>
<text text-anchor="middle" x="1425" y="-1889.4" font-family="Times Roman,serif" font-size="14.00">($48bca)</text>
<text text-anchor="middle" x="1425" y="-1871.4" font-family="Times Roman,serif" font-size="14.00">3 calls</text>
<text text-anchor="middle" x="1425" y="-1853.4" font-family="Times Roman,serif" font-size="14.00">=23.08%</text>
</g>
<!-- N_483BA&#45;&gt;N_486CC -->
<g id="edge66" class="edge"><title>N_483BA&#45;&gt;N_486CC</title>
<path fill="none" stroke="black" d="M532.392,-2297.67C556.5,-2278.41 580.995,-2253.34 594,-2224 627.862,-2147.61 600.339,-1931.31 594,-1848 593.363,-1839.63 592.425,-1830.9 591.323,-1822.24"/>
<ellipse fill="black" stroke="black" cx="590.215" cy="-1814.24" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="693.5" y="-2059.4" font-family="Times Roman,serif" font-size="14.00">seg_prelight_done+456</text>
<text text-anchor="middle" x="693.5" y="-2041.4" font-family="Times Roman,serif" font-size="14.00">($48582)</text>
<text text-anchor="middle" x="693.5" y="-2023.4" font-family="Times Roman,serif" font-size="14.00">6 calls</text>
<text text-anchor="middle" x="693.5" y="-2005.4" font-family="Times Roman,serif" font-size="14.00">=16.67%</text>
</g>
<!-- N_48650 -->
<g id="node24" class="node"><title>N_48650</title>
<ellipse fill="none" stroke="black" cx="505" cy="-2036" rx="67.1751" ry="56.5685"/>
<text text-anchor="middle" x="505" y="-2059.4" font-family="Times Roman,serif" font-size="14.00">0.33%</text>
<text text-anchor="middle" x="505" y="-2041.4" font-family="Times Roman,serif" font-size="14.00">450</text>
<text text-anchor="middle" x="505" y="-2023.4" font-family="Times Roman,serif" font-size="14.00">sector_wall</text>
<text text-anchor="middle" x="505" y="-2005.4" font-family="Times Roman,serif" font-size="14.00">(15 calls)</text>
</g>
<!-- N_483BA&#45;&gt;N_48650 -->
<g id="edge48" class="edge"><title>N_483BA&#45;&gt;N_48650</title>
<path fill="none" stroke="black" d="M446.004,-2284.62C435.202,-2246.81 426.253,-2195.83 438,-2152 442.961,-2133.49 451.67,-2114.88 461.2,-2098.15"/>
<ellipse fill="black" stroke="black" cx="465.405" cy="-2091.14" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="516" y="-2193.4" font-family="Times Roman,serif" font-size="14.00">seg_prelight_done+64</text>
<text text-anchor="middle" x="516" y="-2175.4" font-family="Times Roman,serif" font-size="14.00">($483fa)</text>
</g>
<!-- N_48C44 -->
<g id="node17" class="node"><title>N_48C44</title>
<polygon fill="lightgray" stroke="red" points="859,-1276 731,-1196 859,-1116 987,-1196 859,-1276"/>
<text text-anchor="middle" x="859" y="-1219.4" font-family="Times Roman,serif" font-size="14.00">2.28%</text>
<text text-anchor="middle" x="859" y="-1201.4" font-family="Times Roman,serif" font-size="14.00">3086</text>
<text text-anchor="middle" x="859" y="-1183.4" font-family="Times Roman,serif" font-size="14.00">init_stategroups</text>
<text text-anchor="middle" x="859" y="-1165.4" font-family="Times Roman,serif" font-size="14.00">(1 calls)</text>
</g>
<!-- N_486CC&#45;&gt;N_486D0 -->
<g id="edge88" class="edge"><title>N_486CC&#45;&gt;N_486D0</title>
<path fill="none" stroke="black" d="M559.141,-1694.86C542.594,-1648.91 518.934,-1583.21 501.081,-1533.64"/>
<ellipse fill="black" stroke="black" cx="498.334" cy="-1526.02" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="595.5" y="-1630.4" font-family="Times Roman,serif" font-size="14.00">sector_window</text>
<text text-anchor="middle" x="595.5" y="-1612.4" font-family="Times Roman,serif" font-size="14.00">36 calls</text>
<text text-anchor="middle" x="595.5" y="-1594.4" font-family="Times Roman,serif" font-size="14.00">=72.00%</text>
</g>
<!-- N_494CC&#45;&gt;N_47438 -->
<g id="edge36" class="edge"><title>N_494CC&#45;&gt;N_47438</title>
<path fill="none" stroke="black" d="M1334.85,-847.532C1355.77,-828.13 1376.89,-803.513 1388,-776 1405.77,-732.003 1408.65,-611.309 1375,-490 1363.91,-450.014 1343.58,-408.786 1324.3,-375.165"/>
<polygon fill="black" stroke="black" points="1330.26,-371.492 1314.07,-357.826 1318.2,-378.608 1330.26,-371.492"/>
<text text-anchor="middle" x="1473.5" y="-611.4" font-family="Times Roman,serif" font-size="14.00">render_flats_1x1+62</text>
<text text-anchor="middle" x="1473.5" y="-593.4" font-family="Times Roman,serif" font-size="14.00">($4950a)</text>
<text text-anchor="middle" x="1473.5" y="-575.4" font-family="Times Roman,serif" font-size="14.00">3 calls</text>
<text text-anchor="middle" x="1473.5" y="-557.4" font-family="Times Roman,serif" font-size="14.00">=14.29%</text>
</g>
<!-- N_49438 -->
<g id="node21" class="node"><title>N_49438</title>
<polygon fill="lightgray" stroke="red" points="1242,-668 1118,-588 1242,-508 1366,-588 1242,-668"/>
<text text-anchor="middle" x="1242" y="-611.4" font-family="Times Roman,serif" font-size="14.00">7.16%</text>
<text text-anchor="middle" x="1242" y="-593.4" font-family="Times Roman,serif" font-size="14.00">9701</text>
<text text-anchor="middle" x="1242" y="-575.4" font-family="Times Roman,serif" font-size="14.00">stream_texture</text>
<text text-anchor="middle" x="1242" y="-557.4" font-family="Times Roman,serif" font-size="14.00">(3 calls)</text>
</g>
<!-- N_494CC&#45;&gt;N_49438 -->
<g id="edge40" class="edge"><title>N_494CC&#45;&gt;N_49438</title>
<path fill="none" stroke="red" stroke-width="2" d="M1255.71,-836.702C1249.73,-817.729 1243.98,-796.186 1241,-776 1236.67,-746.627 1235.54,-714.391 1235.88,-685.035"/>
<polygon fill="red" stroke="red" points="1242.89,-684.906 1236.36,-664.749 1228.89,-684.581 1242.89,-684.906"/>
<text text-anchor="middle" x="1314.5" y="-745.4" font-family="Times Roman,serif" font-size="14.00">render_flats_1x1+66</text>
<text text-anchor="middle" x="1314.5" y="-727.4" font-family="Times Roman,serif" font-size="14.00">($4950e)</text>
</g>
<!-- N_486D0&#45;&gt;N_482A0 -->
<g id="edge90" class="edge"><title>N_486D0&#45;&gt;N_482A0</title>
<path fill="none" stroke="red" stroke-width="2" d="M481.942,-1520.34C484.341,-1558.12 484.467,-1608.69 474,-1652 453.221,-1737.97 429.639,-1753.64 385,-1830 355.76,-1880.02 328.966,-1882.6 312,-1938 273.604,-2063.37 305.539,-2397.39 317,-2528 318.251,-2542.25 320.241,-2557.4 322.473,-2571.88"/>
<ellipse fill="red" stroke="red" cx="323.786" cy="-2579.97" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="370.5" y="-2059.4" font-family="Times Roman,serif" font-size="14.00">seg_invisible+18</text>
<text text-anchor="middle" x="370.5" y="-2041.4" font-family="Times Roman,serif" font-size="14.00">($486e2)</text>
<text text-anchor="middle" x="370.5" y="-2023.4" font-family="Times Roman,serif" font-size="14.00">35 calls</text>
<text text-anchor="middle" x="370.5" y="-2005.4" font-family="Times Roman,serif" font-size="14.00">=70.00%</text>
</g>
<!-- N_486E6 -->
<g id="node25" class="node"><title>N_486E6</title>
<ellipse fill="none" stroke="black" cx="410" cy="-1196" rx="62.9325" ry="56.5685"/>
<text text-anchor="middle" x="410" y="-1219.4" font-family="Times Roman,serif" font-size="14.00">0.21%</text>
<text text-anchor="middle" x="410" y="-1201.4" font-family="Times Roman,serif" font-size="14.00">278</text>
<text text-anchor="middle" x="410" y="-1183.4" font-family="Times Roman,serif" font-size="14.00">do_ssector</text>
<text text-anchor="middle" x="410" y="-1165.4" font-family="Times Roman,serif" font-size="14.00">(15 calls)</text>
</g>
<!-- N_486D0&#45;&gt;N_486E6 -->
<g id="edge24" class="edge"><title>N_486D0&#45;&gt;N_486E6</title>
<path fill="none" stroke="black" d="M462.325,-1408.47C452.236,-1367.51 438.428,-1311.43 427.529,-1267.18"/>
<ellipse fill="black" stroke="black" cx="425.571" cy="-1259.23" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="505.5" y="-1335.4" font-family="Times Roman,serif" font-size="14.00">seg_invisible+12</text>
<text text-anchor="middle" x="505.5" y="-1317.4" font-family="Times Roman,serif" font-size="14.00">($486dc)</text>
</g>
<!-- N_4815C -->
<g id="node22" class="node"><title>N_4815C</title>
<polygon fill="lightgray" stroke="red" points="602,-1294 491,-1196 602,-1098 713,-1196 602,-1294"/>
<text text-anchor="middle" x="602" y="-1228.4" font-family="Times Roman,serif" font-size="14.00">59.72%</text>
<text text-anchor="middle" x="602" y="-1210.4" font-family="Times Roman,serif" font-size="14.00">(own: 9.63%)</text>
<text text-anchor="middle" x="602" y="-1192.4" font-family="Times Roman,serif" font-size="14.00">80888</text>
<text text-anchor="middle" x="602" y="-1174.4" font-family="Times Roman,serif" font-size="14.00">descend_bsp</text>
<text text-anchor="middle" x="602" y="-1156.4" font-family="Times Roman,serif" font-size="14.00">(1 calls)</text>
</g>
<!-- N_4815C&#45;&gt;N_48716 -->
<g id="edge20" class="edge"><title>N_4815C&#45;&gt;N_48716</title>
<path fill="none" stroke="black" d="M656.517,-1145.74C676.215,-1129.17 699.229,-1111.54 722,-1098 740.159,-1087.2 754.223,-1097.54 766,-1080 788.643,-1046.27 786.715,-1001 778.733,-963.33"/>
<ellipse fill="black" stroke="black" cx="776.858" cy="-955.489" rx="8.00001" ry="8.00001"/>
<text text-anchor="middle" x="843.5" y="-1067.4" font-family="Times Roman,serif" font-size="14.00">descend_bsp+60</text>
<text text-anchor="middle" x="843.5" y="-1049.4" font-family="Times Roman,serif" font-size="14.00">($48198)</text>
<text text-anchor="middle" x="843.5" y="-1031.4" font-family="Times Roman,serif" font-size="14.00">1 calls</text>
<text text-anchor="middle" x="843.5" y="-1013.4" font-family="Times Roman,serif" font-size="14.00">=6.25%</text>
</g>
<!-- N_48B00&#45;&gt;N_48C74 -->
<g id="edge22" class="edge"><title>N_48B00&#45;&gt;N_48C74</title>
<path fill="none" stroke="red" stroke-width="2" d="M974.906,-1968.75C996.168,-1931.11 1025.46,-1884.56 1058,-1848 1070.01,-1834.51 1084.01,-1821.42 1098.15,-1809.42"/>
<polygon fill="red" stroke="red" points="1103.04,-1814.47 1114.09,-1796.39 1094.17,-1803.63 1103.04,-1814.47"/>
<text text-anchor="middle" x="1118" y="-1907.4" font-family="Times Roman,serif" font-size="14.00">get_ssector+100</text>
<text text-anchor="middle" x="1118" y="-1889.4" font-family="Times Roman,serif" font-size="14.00">($48b64)</text>
<text text-anchor="middle" x="1118" y="-1871.4" font-family="Times Roman,serif" font-size="14.00">8 calls</text>
<text text-anchor="middle" x="1118" y="-1853.4" font-family="Times Roman,serif" font-size="14.00">=61.54%</text>
</g>
<!-- N_48650&#45;&gt;N_486CC -->
<g id="edge84" class="edge"><title>N_48650&#45;&gt;N_486CC</title>
<path fill="none" stroke="black" d="M481.732,-1982.59C468.384,-1943.85 457.582,-1890.95 476,-1848 483.299,-1830.98 495.166,-1815.52 508.296,-1802.17"/>
<ellipse fill="black" stroke="black" cx="514.274" cy="-1796.49" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="535" y="-1907.4" font-family="Times Roman,serif" font-size="14.00">sector_wall+120</text>
<text text-anchor="middle" x="535" y="-1889.4" font-family="Times Roman,serif" font-size="14.00">($486c8)</text>
<text text-anchor="middle" x="535" y="-1871.4" font-family="Times Roman,serif" font-size="14.00">15 calls</text>
<text text-anchor="middle" x="535" y="-1853.4" font-family="Times Roman,serif" font-size="14.00">=41.67%</text>
</g>
<!-- N_486E6&#45;&gt;N_48716 -->
<g id="edge60" class="edge"><title>N_486E6&#45;&gt;N_48716</title>
<path fill="none" stroke="black" d="M440.369,-1146.27C452.04,-1129.65 466.353,-1111.86 482,-1098 494.216,-1087.18 503.691,-1092.65 514,-1080 535.222,-1053.96 511.632,-1030.02 537,-1008 573.086,-976.675 599.838,-1010.51 643,-990 662.248,-980.856 680.719,-967.431 696.915,-953.434"/>
<ellipse fill="black" stroke="black" cx="702.893" cy="-948.04" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="588.5" y="-1067.4" font-family="Times Roman,serif" font-size="14.00">do_ssector+46</text>
<text text-anchor="middle" x="588.5" y="-1049.4" font-family="Times Roman,serif" font-size="14.00">($48714)</text>
<text text-anchor="middle" x="588.5" y="-1031.4" font-family="Times Roman,serif" font-size="14.00">15 calls</text>
<text text-anchor="middle" x="588.5" y="-1013.4" font-family="Times Roman,serif" font-size="14.00">=93.75%</text>
</g>
<!-- N_49B28 -->
<g id="node29" class="node"><title>N_49B28</title>
<polygon fill="none" stroke="red" stroke-width="2" points="1020,-990 876,-892 1020,-794 1164,-892 1020,-990"/>
<text text-anchor="middle" x="1020" y="-924.4" font-family="Times Roman,serif" font-size="14.00">42.19%</text>
<text text-anchor="middle" x="1020" y="-906.4" font-family="Times Roman,serif" font-size="14.00">(own: 1.70%)</text>
<text text-anchor="middle" x="1020" y="-888.4" font-family="Times Roman,serif" font-size="14.00">57144</text>
<text text-anchor="middle" x="1020" y="-870.4" font-family="Times Roman,serif" font-size="14.00">add_wall_segment</text>
<text text-anchor="middle" x="1020" y="-852.4" font-family="Times Roman,serif" font-size="14.00">(18 calls)</text>
</g>
<!-- N_486E6&#45;&gt;N_49B28 -->
<g id="edge42" class="edge"><title>N_486E6&#45;&gt;N_49B28</title>
<path fill="none" stroke="red" stroke-width="2" d="M436.252,-1144.51C447.856,-1126.9 463.186,-1108.9 482,-1098 543.147,-1062.56 586.545,-1126.24 640,-1080 665.031,-1058.35 633.678,-1029.31 659,-1008 694.498,-978.127 823.078,-1004.95 867,-990 890.917,-981.861 914.844,-969.223 936.434,-955.697"/>
<polygon fill="red" stroke="red" points="940.297,-961.535 953.203,-944.729 932.633,-949.819 940.297,-961.535"/>
<text text-anchor="middle" x="710.5" y="-1049.4" font-family="Times Roman,serif" font-size="14.00">do_ssector+20</text>
<text text-anchor="middle" x="710.5" y="-1031.4" font-family="Times Roman,serif" font-size="14.00">($486fa)</text>
</g>
<!-- N_49ACC -->
<g id="node32" class="node"><title>N_49ACC</title>
<polygon fill="lightgray" stroke="red" points="459,-972 284,-892 459,-812 634,-892 459,-972"/>
<text text-anchor="middle" x="459" y="-915.4" font-family="Times Roman,serif" font-size="14.00">2.84%</text>
<text text-anchor="middle" x="459" y="-897.4" font-family="Times Roman,serif" font-size="14.00">3851</text>
<text text-anchor="middle" x="459" y="-879.4" font-family="Times Roman,serif" font-size="14.00">add_partition_segment</text>
<text text-anchor="middle" x="459" y="-861.4" font-family="Times Roman,serif" font-size="14.00">(6 calls)</text>
</g>
<!-- N_486E6&#45;&gt;N_49ACC -->
<g id="edge72" class="edge"><title>N_486E6&#45;&gt;N_49ACC</title>
<path fill="none" stroke="red" stroke-width="2" d="M404.674,-1139.49C402.498,-1101.85 402.232,-1051.51 411,-1008 413.099,-997.583 416.14,-986.926 419.687,-976.498"/>
<polygon fill="red" stroke="red" points="426.353,-978.657 426.769,-957.471 413.232,-973.773 426.353,-978.657"/>
<text text-anchor="middle" x="462.5" y="-1049.4" font-family="Times Roman,serif" font-size="14.00">do_ssector+20</text>
<text text-anchor="middle" x="462.5" y="-1031.4" font-family="Times Roman,serif" font-size="14.00">($486fa)</text>
</g>
<!-- N_49BEA -->
<g id="node27" class="node"><title>N_49BEA</title>
<polygon fill="lightgray" stroke="red" points="980,-686 860,-588 980,-490 1100,-588 980,-686"/>
<text text-anchor="middle" x="980" y="-620.4" font-family="Times Roman,serif" font-size="14.00">40.49%</text>
<text text-anchor="middle" x="980" y="-602.4" font-family="Times Roman,serif" font-size="14.00">(own: 40.25%)</text>
<text text-anchor="middle" x="980" y="-584.4" font-family="Times Roman,serif" font-size="14.00">54844</text>
<text text-anchor="middle" x="980" y="-566.4" font-family="Times Roman,serif" font-size="14.00">render_wall</text>
<text text-anchor="middle" x="980" y="-548.4" font-family="Times Roman,serif" font-size="14.00">(18 calls)</text>
</g>
<!-- N_49BEA&#45;&gt;N_49C2C -->
<g id="edge82" class="edge"><title>N_49BEA&#45;&gt;N_49C2C</title>
<path fill="none" stroke="red" stroke-width="2" d="M985.869,-494.749C988.316,-455.875 991.115,-411.398 993.407,-374.971"/>
<ellipse fill="red" stroke="red" cx="993.919" cy="-366.834" rx="8" ry="8"/>
<text text-anchor="middle" x="1048" y="-441.4" font-family="Times Roman,serif" font-size="14.00">render_wall+62</text>
<text text-anchor="middle" x="1048" y="-423.4" font-family="Times Roman,serif" font-size="14.00">($49c28)</text>
</g>
<!-- N_49BEA&#45;&gt;N_47438 -->
<g id="edge18" class="edge"><title>N_49BEA&#45;&gt;N_47438</title>
<path fill="none" stroke="black" d="M1037.61,-536.946C1059.84,-516.945 1085.33,-493.643 1108,-472 1144.12,-437.513 1183.51,-398.006 1215.17,-365.761"/>
<polygon fill="black" stroke="black" points="1220.59,-370.225 1229.58,-351.037 1210.59,-360.431 1220.59,-370.225"/>
<text text-anchor="middle" x="1234" y="-459.4" font-family="Times Roman,serif" font-size="14.00">render_wall+26</text>
<text text-anchor="middle" x="1234" y="-441.4" font-family="Times Roman,serif" font-size="14.00">($49c04)</text>
<text text-anchor="middle" x="1234" y="-423.4" font-family="Times Roman,serif" font-size="14.00">18 calls</text>
<text text-anchor="middle" x="1234" y="-405.4" font-family="Times Roman,serif" font-size="14.00">=85.71%</text>
</g>
<!-- N_47A6C -->
<g id="node28" class="node"><title>N_47A6C</title>
<polygon fill="none" stroke="red" stroke-width="2" points="992,-1562 874,-1464 992,-1366 1110,-1464 992,-1562"/>
<text text-anchor="middle" x="992" y="-1496.4" font-family="Times Roman,serif" font-size="14.00">100.00%</text>
<text text-anchor="middle" x="992" y="-1478.4" font-family="Times Roman,serif" font-size="14.00">(own: 0.09%)</text>
<text text-anchor="middle" x="992" y="-1460.4" font-family="Times Roman,serif" font-size="14.00">135449</text>
<text text-anchor="middle" x="992" y="-1442.4" font-family="Times Roman,serif" font-size="14.00">display_engine</text>
<text text-anchor="middle" x="992" y="-1424.4" font-family="Times Roman,serif" font-size="14.00">(1 calls)</text>
</g>
<!-- N_47A6C&#45;&gt;N_494B4 -->
<g id="edge6" class="edge"><title>N_47A6C&#45;&gt;N_494B4</title>
<path fill="none" stroke="red" stroke-width="2" d="M1026.6,-1394.29C1043.31,-1360.61 1063.55,-1319.83 1081.31,-1284.03"/>
<polygon fill="red" stroke="red" points="1087.72,-1286.86 1090.35,-1265.83 1075.18,-1280.63 1087.72,-1286.86"/>
<text text-anchor="middle" x="1137.5" y="-1335.4" font-family="Times Roman,serif" font-size="14.00">display_engine+302</text>
<text text-anchor="middle" x="1137.5" y="-1317.4" font-family="Times Roman,serif" font-size="14.00">($47b9a)</text>
</g>
<!-- N_47A6C&#45;&gt;N_48C44 -->
<g id="edge10" class="edge"><title>N_47A6C&#45;&gt;N_48C44</title>
<path fill="none" stroke="red" stroke-width="2" d="M943.276,-1406.32C929.853,-1388.44 916.197,-1368.13 906,-1348 896.111,-1328.48 887.842,-1306.49 881.137,-1285.42"/>
<polygon fill="red" stroke="red" points="887.767,-1283.16 875.302,-1266.02 874.36,-1287.19 887.767,-1283.16"/>
<text text-anchor="middle" x="976.5" y="-1335.4" font-family="Times Roman,serif" font-size="14.00">display_engine+286</text>
<text text-anchor="middle" x="976.5" y="-1317.4" font-family="Times Roman,serif" font-size="14.00">($47b8a)</text>
</g>
<!-- N_47A6C&#45;&gt;N_4815C -->
<g id="edge102" class="edge"><title>N_47A6C&#45;&gt;N_4815C</title>
<path fill="none" stroke="red" stroke-width="2" d="M909.733,-1434.21C859.833,-1414.02 796.299,-1384.41 746,-1348 715.087,-1325.62 685.311,-1296.04 660.939,-1268.92"/>
<polygon fill="red" stroke="red" points="666.07,-1264.15 647.615,-1253.74 655.549,-1273.39 666.07,-1264.15"/>
<text text-anchor="middle" x="816.5" y="-1335.4" font-family="Times Roman,serif" font-size="14.00">display_engine+294</text>
<text text-anchor="middle" x="816.5" y="-1317.4" font-family="Times Roman,serif" font-size="14.00">($47b92)</text>
</g>
<!-- N_491EA -->
<g id="node33" class="node"><title>N_491EA</title>
<polygon fill="lightgray" stroke="red" points="1406,-1276 1263,-1196 1406,-1116 1549,-1196 1406,-1276"/>
<text text-anchor="middle" x="1406" y="-1219.4" font-family="Times Roman,serif" font-size="14.00">3.04%</text>
<text text-anchor="middle" x="1406" y="-1201.4" font-family="Times Roman,serif" font-size="14.00">4120</text>
<text text-anchor="middle" x="1406" y="-1183.4" font-family="Times Roman,serif" font-size="14.00">initialise_freetable</text>
<text text-anchor="middle" x="1406" y="-1165.4" font-family="Times Roman,serif" font-size="14.00">(2 calls)</text>
</g>
<!-- N_47A6C&#45;&gt;N_491EA -->
<g id="edge2" class="edge"><title>N_47A6C&#45;&gt;N_491EA</title>
<path fill="none" stroke="red" stroke-width="2" d="M1067.47,-1428.41C1110.83,-1406.94 1165.71,-1377.99 1212,-1348 1254.04,-1320.76 1298.2,-1286.65 1333.75,-1257.64"/>
<polygon fill="red" stroke="red" points="1338.45,-1262.84 1349.44,-1244.72 1329.55,-1252.03 1338.45,-1262.84"/>
<text text-anchor="middle" x="1336" y="-1335.4" font-family="Times Roman,serif" font-size="14.00">display_engine+6010</text>
<text text-anchor="middle" x="1336" y="-1317.4" font-family="Times Roman,serif" font-size="14.00">($491e6)</text>
</g>
<!-- N_49B28&#45;&gt;N_49BEA -->
<g id="edge12" class="edge"><title>N_49B28&#45;&gt;N_49BEA</title>
<path fill="none" stroke="red" stroke-width="2" d="M1008.16,-802.024C1003.81,-768.947 998.834,-731.14 994.289,-696.598"/>
<polygon fill="red" stroke="red" points="1001.22,-695.613 991.671,-676.697 987.34,-697.439 1001.22,-695.613"/>
<text text-anchor="middle" x="1087.5" y="-745.4" font-family="Times Roman,serif" font-size="14.00">add_wall_segment+186</text>
<text text-anchor="middle" x="1087.5" y="-727.4" font-family="Times Roman,serif" font-size="14.00">($49be2)</text>
</g>
<!-- N_481F8&#45;&gt;N_48206 -->
<g id="edge80" class="edge"><title>N_481F8&#45;&gt;N_48206</title>
<path fill="none" stroke="black" d="M148.23,-3061.03C210.061,-3029.26 305,-2980.48 371.423,-2946.35"/>
<ellipse fill="black" stroke="black" cx="378.862" cy="-2942.52" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="360.5" y="-3005.4" font-family="Times Roman,serif" font-size="14.00">ssector_node+12</text>
<text text-anchor="middle" x="360.5" y="-2987.4" font-family="Times Roman,serif" font-size="14.00">($48204)</text>
</g>
<!-- N_481F8&#45;&gt;N_4819C -->
<g id="edge86" class="edge"><title>N_481F8&#45;&gt;N_4819C</title>
<path fill="none" stroke="black" d="M159.833,-3082.9C309.764,-3059.61 656.148,-2991.43 898,-2832 1072.94,-2716.68 1216.64,-2508.85 1282,-2404.08"/>
<ellipse fill="black" stroke="black" cx="1286.31" cy="-2397.11" rx="8.00002" ry="8.00002"/>
<text text-anchor="middle" x="1046" y="-2801.4" font-family="Times Roman,serif" font-size="14.00">ssector_node+2</text>
<text text-anchor="middle" x="1046" y="-2783.4" font-family="Times Roman,serif" font-size="14.00">($481fa)</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 61 KiB

BIN
doc/images/devices.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
doc/images/fileselector.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
doc/images/floppydisks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
doc/images/harddisks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
doc/images/joystick.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
doc/images/kcachegrind.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
doc/images/keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
doc/images/main.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
doc/images/memory.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
doc/images/monitor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
doc/images/newfloppy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
doc/images/screen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
doc/images/sound.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
doc/images/system.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
doc/images/tos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

22
doc/keymap-sample.txt Normal file
View File

@ -0,0 +1,22 @@
# This is an example for a keyboard mapping file that can be used in Hatari
# by loading it from the keyboard setup dialog.
#
# Lines starting with a '#' or with a ';' are comments.
# All other lines should contain exactly two numbers separated by a comma.
#
# The first number is the libSDL symbolic PC key code.
# See the "--trace keymap" output from Hatari.
#
# The corresponding key will be mapped to the ST key which is specified by
# second number - the ST scan code of the key. "--trace keymap" output
# shows the already mapped scan code.
#
# tests/keymap/ directory contains programs to discover/test the PC SDL
# and Atari scan code values. Hatari's default PC key code -> ST scan
# code mappings are in src/keymap.c source file.
#
# Example: If you want to get the 'y' and 'z' keys right with a german TOS
# ROM, you can use the following two lines to map the PC keys to the right
# ST scan codes:
121,44
122,21

4309
doc/manual.html Normal file

File diff suppressed because it is too large Load Diff

145
doc/memory-usage.txt Normal file
View File

@ -0,0 +1,145 @@
HATARI MEMORY USAGE
Here are some stats on Hatari v1.2+ memory usage (on Linux) and what
could be done to decrease it.
First the binary size from "size ./hatari":
text data bss dec
1489120 12828 18799688 20301636
I.e. the Hatari binary size is 1.4MB, it has 12KB of non-const/pre-defined
arrays and 18MB of uninitialized (at build-time) fixed size arrays.
The names of latter are listed below.
To decrease the binary size slightly, disable DSP from src/Makefile,
don't enabled tracing in config.h (latter may have trivial improvement
on speed too). You may also try the gcc "-Os" or "-O3 -finline-limit=..."
options, their effect depends on the architecture for which Hatari is
being compiled.
To see the objects in the Hatari 18MB BSS section, get the datadump
script from here:
http://live.gnome.org/MemoryReduction_2fTools
Compile Hatari without stripping, and use:
datadump.py -n -s .bss -r ./hatari
As a result you see these array variables:
16777216 STRam hatari
404400 dsp_core hatari
324048 CyclePalettes hatari
262144 mem_banks hatari
262144 cpufunctbl hatari
176612 ConfigureParams hatari
131072 pInterceptWriteTable hatari
131072 pInterceptReadTable hatari
69632 InternalDTAs hatari
65536 ymout5_u16 hatari
32768 MixBuffer hatari
32768 DspOutBuffer hatari
...
These empty arrays aren't an issue unless Hatari actually writes to
them, but that will happen as Hatari uses them. Here are some ways
to minimize dirtying of the related memory i.e. use of the arrays
in Hatari:
* Enabling only required amount of memory for the emulation.
Hatari doesn't dirty (zero) all of STRam, just the part of the ST ram
that user has configured (accessible as ST ram, 0.5-14MB) and 2MB
at the top (used as IO-memory, TOS and cartridge memory).
* Disabling DSP from build gets rid of dsp_core
* Modifying Video_ClearOnVBL() and Video_ColorReg_WriteWord()
to call Spec512 functions only when nSpec512Threshold configuration
value is non-zero and run Hatari with spec512 support disabled.
This gets rid of CyclePalettes dirtying
* ConfigureParams size can be decreased 22*4KB by setting
MAX_HARDDRIVES in configuration.h to one (or by removing
memset from Configuration_SetDefault() as static variables in .bss
are zeroed already by kernel, memset()ting them just makes them
dirty and Configuration_SetDefault() is called only at Hatari startup).
When I profiled Hatari with Valgrind (valgrind.kde.org) Massif plugin,
it tells that Hatari does about 3MB of memory worth of dynamic
allocations. The allocations and ways to make them smaller are:
* In includes/vdi.h decrease MAX_VDI_WIDTH and MAX_VDI_HEIGHT
to 640 and 400. As Hatari allocates 2*2 framebuffers (of size
width*height/8) for page flipping in Screen_Init(), decreasing
their size can have have a large effect.
* Do not load bigfont in gui-sdl/sdlgui.c, especially if the device
screen is smaller than VGA. Both fonts together take about 1/3 MB.
* Change uncompressed file read to use mmap() instead, currently
memory is allocated for the whole disk image before reading it.
- With normal DD floppy image Hatari would use that amount which
might be acceptable, but with e.g. 2MB disk needed for running
Wolf3D v0.8, mmap() sounds much better
- For compressed disk images memory needs to be allocated for
uncompressed image data, i.e. there we cannot save memory.
* Check whether the m86k instruction table could be made smaller:
#include "uae-cpu/readcpu.h"
printf("%d -> %d\n", sizeof(struct instr), sizeof(struct instr) * 65536);
On x86 it's slightly over 1MB.
You can also Massif Hatari yourself, its allocation calltrees
are very short & the allocations are easy to find in the Hatari
source code.
From /proc/sysvipc/shm one can see how much shared memory Hatari/libSDL
has allocated and that it shares it with the X server:
- >100KB in lowrez
- ~200KB in lowrez with borders
- ~500KB in monochrome or zoomed lowrez
- >800KB in zoomed lowrez with borders
I don't think these could be made smaller from the code. Besides,
user can just use a the smaller Hatari screen mode in fullscreen and
let the display scale it to fullscreen.
According to Xrestop, Hatari doesn't keep any Pixmap resources
at the X server side.
Finally when looking at the Hatari process with "pmap", you can
see that the libraries Hatari links don't use so much private
(writable) memory, only couple of hundred KB:
pmap $(pidof hatari) | grep / | grep rw
To see how much from the memory pmap tells Hatari to have allocated is
actually used/dirtied, take a peek at: /proc/$(pidof hatari)/smaps.
Of the rest of the 40MB Hatari VMSIZE you see in "top" (about 16MB),
half is unused/clean 8MB memory (allocated by kernel for the SDL sound
thread stack) and half goes to shared library code (their .text
sections with "r-x" rights) that Hatari links against. The libraries
are most likely used also by other programs and even if they aren't,
it's memory mapped read-only / read in on-demand pages & pagable back
to disk so it's shouldn't be much of a problem either.
Unmodified Hatari runs on (Linux) systems having about 20MB of _free_
memory (e.g. according to /proc/meminfo free+buffers+cached fields)
and more RAM in total than the Hatari VMSIZE.
Using low-rez without borders nor zooming, setting emulated ST memory
amount to <=1MB, limiting the VDI screen size, disabling DSP and
removing bigfont (discussed with Massif findings above) should enable
running Hatari well on a system with only 10MB free memory, if it's
otherwise fast enough.
- Eero Tamminen
PS. Any device fast enough to run Hatari at reasonable speed
should already have enough memory for it...

264
doc/midi-linux.txt Normal file
View File

@ -0,0 +1,264 @@
Getting (Linux) ALSA midi support and MIDI networking working with Hatari
=========================================================================
If you don't have a real MIDI sequencer, you can use the MIDI synthesizer
of your sound card (if available) or use a software synthetizer.
For (Debian) package names and links to software referenced in this
text, see end of the text. Most of the distros should have in their
repositories packages at least for some of them though.
Contents:
- Using a soundcard with built-in MIDI synthesis capability
- Making MIDI soft-synthetizer to work with ALSA
- Using FluidSynth instead of Timidity
- Other software synthetizers
- Making it all to work with Hatari
- MIDI and networking
- Linux & Atari MIDI related software
- Additional documentation
Using a soundcard with built-in MIDI synthesis capability
---------------------------------------------------------
If your soundcard is capable of playing MIDI sound (i.e. you can play a .mid
file with the "aplaymidi" command using the appropriate port), you can use this
synthesis device for Hatari, too. However, you still might have to install and
connect a virtual midi device, so that Hatari can access it through a
/dev/snd/midiC*D* device file (see instructions below).
Please note that you might also have to load instrument patches into your sound
card first, for example with the program "sfxload" for AWE64 based sound cards,
or with the program "sbiload" for OPL3 based sound cards.
Making MIDI soft-synthetizer to work with ALSA
-----------------------------------------------
Make Timidity into an ALSA output device with:
timidity -Os -iA
(-O: output=alsa, -i: interface=alsa)
To make it use less CPU and be more responsive, use:
timidity -Os -iA -B2,8 -EFreverb=0 -EFchorus=0
(-B: 2,8=set small buffers, -EFx=0: disable effects)
Make vkeybd (virtual midi keyboard app) into an ALSA input device with:
vkeybd
Or use the newer & nicer looking "Virtual MIDI Piano Keyboard":
vmpk
View the resulting (software) ALSA input and output devices:
aconnect -i -o
Then connect the vkeybd output port to the timidity input port with:
aconnect <vkeybd port> <timidity port>
Or use one of the GUI programs for this like kaconnect, aconnectgui etc.
Now you can use the virtual midi keyboard for testing the sound
synthesis.
Finally you can test how well midi files are played.
Check which ALSA port Timidity provides:
aplaymidi -l
And use that port for playing a midi file:
aplaymidi -p <port, e.g. 129:0> test.mid
(or use 'pmidi')
Note: Remember that you need to re-connect the (virtual) device
ports each time you restart them.
Using FluidSynth instead of Timidity
------------------------------------
Instead of Timidity, you also use other soft-synthetizers,
like FluidSynth:
fluidsynth --audio-driver=alsa --midi-driver=alsa_seq soundfont.sf2
You could play a bit with other options to get more performance,
sound volume etc:
--reverb=no --chorus=no -o synth.polyphony=16 --gain=0.6
And if you don't like the FluidSynth shell, use:
--no-shell --server
Qsynth provides a GUI for above:
qsynth <soundfont>
Note: FluidSynth v1.0.7a in older (obsolete) Linux distros has buffer
overruns, but they're fixed in v1.0.8 or newer that are in the current
Linux distros.
Other software synthetizers
---------------------------
Of the other soft-synthetizers, I like also Horgand organ emulator
as it has pretty good organ sound, but it needs Jack connection kit
(+ e.g. qjackctl) for sound to work properly (not have sound underruns).
Making it all to work with Hatari
---------------------------------
Hatari requires midi hardware devices to work, it doesn't support
ALSA directly. To get the software synth ALSA devices to appear
as HW midi devices, run following as *root*:
modprobe snd-virmidi
When you list your ALSA output devices with:
aconnect -o
You should see in addition to the soft-synth also 4 virtual hardware
devices.
Then connect (with aconnect or one of the GUIs) the first virtual
HW port to the same soft-synth port where you connected the virtual
midi keyboard.
Check which number was assigned by ALSA to the new virtual midi card:
cat /proc/asound/cards
And give to Hatari the corresponding ALSA midi device. In my case
VirMidi was Card 1 and as the port used above was first one, I give
Hatari the following midi device:
hatari --midi-out /dev/snd/midiC1D0
(For the virtual midi keyboard, give same device with --midi-in option.)
Note: In older (obsolete) Linux distros SDL_mixer may take exclusive
access to the PCM (sound) device, but as the soft synthetizer is
already connected to it, one may need to use --nosound option to get
MIDI sound working. In recent distros this shouldn't anymore be
a problem thanks to Pulseaudio.
MIDI and networking
-------------------
If you direct the MIDI data to stdout, you can use just ssh to
forward the MIDI output over network:
hatari --midi-in "" --midi-out /dev/stdout --log /dev/stderr |\
ssh user@remote.site "cat>/dev/snd/midiC1D0"
(Note that logging is re-directed to stderr so that it doesn't
mess the MIDI output to standard output and --midi-in is set
empty in case you don't have MIDI input device locally.)
MIDI-networking two Hatari emulators can be most easily done with socat.
MIDI networking over normal TCP/IP network:
@remote.site:
socat -b1 PTY,raw,echo=0,link=/tmp/midi1 TCP4-LISTEN:33333 &
hatari --midi-in /tmp/midi1 --midi-out /tmp/midi1 &
@local.site:
socat -b1 PTY,raw,echo=0,link=/tmp/midi2 TCP4:remote.site:33333 &
hatari --midi-in /tmp/midi2 --midi-out /tmp/midi2 &
Buffer size (-b) is set to one just in case (by default socat buffer
size is 8K, but all the MIDI communication is done byte at the time).
You may need to open a hole into your firewall for the given port
(here 33333). Usually there's a hole for the www-traffic in firewalls,
but the port for that (80) is below 1000, so if you use "www" as
the port, most likely you need to run "socat" as root. To test this
with a single machine, use "localhost" as the "remote.site".
Local MIDI network:
socat -b1 PTY,raw,echo=0,link=/tmp/midi1 PTY,raw,echo=0,link=/tmp/midi2 &
hatari --midi-in /tmp/midi1 --midi-out /tmp/midi1 &
hatari --midi-in /tmp/midi2 --midi-out /tmp/midi2 &
If you don't have "socat" installed, local-midi-ring.sh script shows how
to join several (local) Hatari emulators into a MIDI ring using fifos.
Linux & Atari MIDI related Software
-----------------------------------
In Debian, the tools mentioned above come from following packages:
- alsa-utils (aconnect, aplaymidi)
- alsa-tools (sbiload)
- awesfx (sfxload)
- pmidi
- vkeybd
- vmpk
- aconnectgui
- qsynth
- fluidsynth
- fluid-soundfont-* (soundfonts)
- timidity
- horgand
- qjackctl
- socat
See http://packages.debian.org/ for more details on them.
Below are upstream links to some of these tools.
Vkeybd:
http://alsa.opensrc.org/Vkeybd
Virtual MIDI Piano Keyboard (vmpk):
http://vmpk.sourceforge.net/
Patch (ALSA connecting) utilities:
http://alsa.opensrc.org/AlsaMidiPatchbays
FluidSynth:
http://www.iiwu.org/fluidsynth/
Horgand:
http://horgand.berlios.de/
Soundfonts:
http://alsa.opensrc.org/SoundFontHandling
List of some soft-synthetizers:
http://alsa.opensrc.org/SoftSynths
Kaconnect:
http://alsamodular.sourceforge.net/
QjackCtl:
http://qjackctl.sourceforge.net/
socat:
http://www.dest-unreach.org/socat/
As to Atari MIDI programs, here's an incomplete list
of games supporting MIDI music:
http://www.atari-forum.com/viewtopic.php?f=3&t=21473&start=25#p195632
MidiMaze supports up to 16 players over MIDI network:
http://en.wikipedia.org/wiki/MIDI_Maze
Additional documentation
------------------------
ALSA midi overview:
http://alsa.opensrc.org/AlsaMidiOverview
How to set up soundcards with hardware MIDI synthesis capability (AWE & OPL3):
https://help.ubuntu.com/community/Midi/HardwareSynthesisSetup
Virtual midi hardware setup:
http://www.tldp.org/HOWTO/MIDI-HOWTO-10.html
Timidity Howto:
http://lau.linuxaudio.org/TiMidity-howto.html
Midi with ALSA (old):
http://www.linuxfocus.org/English/September2002/article259.shtml
Midi on Linux:
http://www.linuxjournal.com/article/7773
MIDI, Musical Instrument Digital Interface protocol:
http://en.wikipedia.org/wiki/Midi

1379
doc/release-notes.txt Normal file

File diff suppressed because it is too large Load Diff

220
doc/toc.js Normal file
View File

@ -0,0 +1,220 @@
/** toc.js
This is a simplified version of the scipt "generated_toc.js" written by:
Stuart Langridge, July 2007
The script is licensed under the terms of the MIT license.
See the following page for details:
http://www.kryogenix.org/code/browser/generated-toc/
Generate a table of contents, based on headings in the page.
To place the TOC on the page, add
<div id="generated-toc"></div>
to the page where you want the TOC to appear. If this element
is not present, the TOC will not appear.
*/
generated_toc = {
generate: function() {
// Identify our TOC element, and what it applies to
generate_from = '2';
tocparent = document.getElementById('generated-toc');
if (!tocparent) {
// They didn't specify a TOC element; exit
return;
}
// set top_node to be the element in the document under which
// we'll be analysing headings
top_node = document.getElementsByTagName('body')[0];
// If there isn't a specified header level to generate from, work
// out what the first header level inside top_node is
// and make that the specified header level
if (generate_from == 0) {
first_header_found = generated_toc.findFirstHeader(top_node);
if (!first_header_found) {
// there were no headers at all inside top_node!
return;
} else {
generate_from = first_header_found.toLowerCase().substr(1);
}
}
// add all levels of heading we're paying attention to to the
// headings_to_treat dictionary, ready to be filled in later
headings_to_treat = {"h6":''};
for (var i=5; i>= parseInt(generate_from); i--) {
headings_to_treat["h" + i] = '';
}
// get headings. We can't say
// getElementsByTagName("h1" or "h2" or "h3"), etc, so get all
// elements and filter them ourselves
// need to use .all here because IE doesn't support gEBTN('*')
nodes = top_node.all ? top_node.all : top_node.getElementsByTagName('*');
// put all the headings we care about in headings
headings = [];
for (var i=0; i<nodes.length;i++) {
if (nodes[i].nodeName.toLowerCase() in headings_to_treat) {
// if heading has class no-TOC, skip it
if ((' ' + nodes[i].className + ' ').indexOf('no-TOC') != -1) {
continue;
}
headings.push(nodes[i]);
}
}
// make the basic elements of the TOC itself, ready to fill into
cur_head_lvl = "h" + generate_from;
cur_list_el = document.createElement('ul');
tocparent.appendChild(cur_list_el);
// now walk through our saved heading nodes
for (var i=0; i<headings.length; i++) {
this_head_el = headings[i];
this_head_lvl = headings[i].nodeName.toLowerCase();
if (!this_head_el.id) {
// if heading doesn't have an ID, give it one
//this_head_el.id = 'heading_toc_' + i;
var id = generated_toc.innerText(this_head_el).replace(/[ \/]/g, "_");
id = id.replace(/[^a-zA-Z0-9-_]/g, '');
this_head_el.id = id;
}
while(this_head_lvl > cur_head_lvl) {
// this heading is at a lower level than the last one;
// create additional nested lists to put it at the right level
// get the *last* LI in the current list, and add our new UL to it
var last_listitem_el = null;
for (var j=0; j<cur_list_el.childNodes.length; j++) {
if (cur_list_el.childNodes[j].nodeName.toLowerCase() == 'li') {
last_listitem_el = cur_list_el.childNodes[j];
}
}
if (!last_listitem_el) {
// there aren't any LIs, so create a new one to add the UL to
last_listitem_el = document.createElement('li');
}
new_list_el = document.createElement('ul');
last_listitem_el.appendChild(new_list_el);
cur_list_el.appendChild(last_listitem_el);
cur_list_el = new_list_el;
cur_head_lvl = 'h' + (parseInt(cur_head_lvl.substr(1,1)) + 1);
}
while (this_head_lvl < cur_head_lvl) {
// this heading is at a higher level than the last one;
// go back up the TOC to put it at the right level
cur_list_el = cur_list_el.parentNode.parentNode;
cur_head_lvl = 'h' + (parseInt(cur_head_lvl.substr(1,1)) - 1);
}
// create a link to this heading, and add it to the TOC
li = document.createElement('li');
a = document.createElement('a');
a.href = '#' + this_head_el.id;
a.appendChild(document.createTextNode(generated_toc.innerText(this_head_el)));
li.appendChild(a);
cur_list_el.appendChild(li);
}
},
innerText: function(el) {
return (typeof(el.innerText) != 'undefined') ? el.innerText :
(typeof(el.textContent) != 'undefined') ? el.textContent :
el.innerHTML.replace(/<[^>]+>/g, '');
},
findFirstHeader: function(node) {
// a recursive function which returns the first header it finds inside
// node, or null if there are no functions inside node.
var nn = node.nodeName.toLowerCase();
if (nn.match(/^h[1-6]$/)) {
// this node is itself a header; return our name
return nn;
} else {
for (var i=0; i<node.childNodes.length; i++) {
var subvalue = generated_toc.findFirstHeader(node.childNodes[i]);
// if one of the subnodes finds a header, abort the loop and return it
if (subvalue) return subvalue;
}
// no headers in this node at all
return null;
}
},
getYPos: function(el) {
var y = 0;
while (el && !isNaN(el.offsetTop)) {
y += el.offsetTop;
el = el.parentNode;
}
return y;
},
init: function() {
// quit if this function has already been called
if (arguments.callee.done) return;
// flag this function so we don't do the same thing twice
arguments.callee.done = true;
generated_toc.generate();
if (location.hash.length != 0) {
// Make sure that the browser scrolled to the right location!
var anchor = location.hash.substring(1);
var y = generated_toc.getYPos(document.getElementById(anchor));
if ('scrollTo' in window) {
window.scrollTo(0, y);
}
else if ('scroll' in window) {
window.scroll(0, y);
}
}
}
};
/* Run generated_toc.init as soon as possible */
(function(i) {
var u =navigator.userAgent;
var e=/*@cc_on!@*/false;
var st = setTimeout;
if(/webkit/i.test(u)) {
st(function() {
var dr=document.readyState;
if(dr=="loaded" || dr=="complete") {
i()
}
else {
st(arguments.callee,10);
}
},10
);
}
else if((/mozilla/i.test(u) && !/(compati)/.test(u)) || (/opera/i.test(u))) {
document.addEventListener("DOMContentLoaded",i,false);
} else if(e) {
(function() {
var t=document.createElement('doc:rdy');
try{
t.doScroll('left');
i();
t=null;
}
catch(e) {
st(arguments.callee,0);
}
})();
}
else{
window.onload=i;
}}
)(generated_toc.init);

273
doc/todo.txt Normal file
View File

@ -0,0 +1,273 @@
Hatari TO-DO list
=================
If you think that you can help with one of the TO-DO list items, please get
in touch with us.
Emulation improvements
----------------------
- Add fastload program header flag support to GEMDOS HD emulation
(If PF_FASTLOAD flag is set, clear just BSS, not heap)
- Investigate what TOS version/functionality doesn't work for (Falcon)
programs started from the DESKTOP/NEWDESK.INF and why, e.g:
- Printing doesn't work with TOS versions v1.02 - v2.06
- Colors in 2-plane VDI resolution are not set correctly
- "Running" game preview and shareware versions crash
- Fun's "Men at War" game or its intro don't start
- "Push It" game crashes if auto-started
- NoCrew: Aggressive Party II 128k combo ("Whirlpool") bombs
- RG: "Sworm" & "Sky Fall" games want to access A: & B: when autostarted
- JAM packer packed version of Tony Benett' "Virtual City" demo bombs
- Sentry 2.2 packed version of Shadows' "Firestarter" demo bombs
Note: last 3 programs can be autostarted fine with EmuTOS.
Document that in manual (page) for the Hatari "autorun" feature.
- Keyboard detection sometimes fails when --fast-forward is enabled
while TOS boots / game is loaded (e.g. Falcon Snake game packed
with Sentry).
- Improve disk image formats support:
- Add support for .STT images
(created with the STEEM disk image program)
- Support .DIM images created with the "Get sectors: used" option
- Real HD 6301 (keyboard processor of the ST) emulation.
(Current special casing is enough for all known demos using 6301)
- Finish upgrading the CPU core of Hatari to the latest WinUAE
which has better cycle accuracy needed by some programs:
- Add Exception debugging support
- Instead of calling Reset_Cold()/m68k_reset()/uae_reset()
directly from newcpu.c, show user a notice (dialog)
about what happened and let user do the reboot
(see "Emulation reset, old-UAE vs. WinUAE core" mail-thread)
- Make WinUAE core ST/STE emulation as good as with old UAE core
- Document cmdline options for selecting prefetch etc
once they're stable
- Get the games/demos working that are marked as non-working in the manual.
- Improve TT and/or Falcon emulation, especially VIDEL, e.g:
- Palette switching during screen drawing
- ST screen modes centering when Videl borders are enabled
- Videl video register access (e.g. VIDEL_ScreenCounter_ReadByte()
never changes the register value unless it had first specifically
been written into):
http://listengine.tuxfamily.org/lists.tuxfamily.org/hatari-devel/2013/02/msg00155.html
- Add SCSI hard disk emulation for Falcon/TT mode.
- ACSI emulation doesn't work with TOS v3 (but works for TT/EmuTOS).
- Add SCC serial port emulation for Falcon/TT mode.
- Disable VDI mode for TOS v4 as it doesn't support it (and VDI mode
is redundant as Falcon's Videl allows similar resolutions)
- Fix falcon sound quality (sound is sometimes noisy).
Sound is also toggling between nice to noise with some demos.
- DSP emulation / Falcon sound matrix:
- Dsp SSI internal clock (is it used on falcon ?)
- Verify DSP instructions cycle count, especially with external RAM
- FPU 80-bit precision mode (selected with FPUCW instruction, and
extra instructions on 040), if there are programs depending on it.
UAE core implements only support for 64-bit precision. See "m68k
FPU precision issue" thread on debian-68k mailing list for details.
- The "memvalid" system variables currently have to be patched in most cases.
For improved compatibility (e.g. the game "Yolanda") it would be better to
skip this step, but we then run into multiple other problems, see:
http://listengine.tuxfamily.org/lists.tuxfamily.org/hatari-devel/2011/12/msg00123.html
Programs known as not fully functional (not an exhaustive list)
---------------------------------------------------------------
Demos :
- video : Omega - Full Overscan Screen, Phalanx Demo - Dragon,
Dragonnels - Happy Island, Phaleon Demo - Future Minds,
Decade Demo - Reset, TNT - Death Of The Left Border,
Anomaly Demo - Menu, Delirious Demo IV - STE Detection,
Ventura - Ultimate Dist, Syntax Terror - TCB, ICE - Kryos,
ICE - Intruding, ICE - Jamcols, Extreme Rage, Paradox - XMas 2004,
ICE - Space Tale, ICE - The Wave Of The Future, Snork - DNT Screen 1
Games :
- ikbd : Superior 109 - Special Forces
Other potential Hatari functionality improvements
-------------------------------------------------
- Improved boot drive & partition handling code:
- count partitions the same way in ACSI, IDE & GEMDOS
- move BootDrive stuff from floppy.c e.g. to tos.c where NumDrives is
- Support harddisk write protection also for IDE & ACSI drives?
- Fix GST symbol table detection in debugger & gst2ascii. Currently
it will just process whatever it thinks the symbol table to
contain (which output can mess the console). MiNT binaries can
contain GST symbol tables, so checking that isn't enough.
- Preliminary debugger work for the other features + cleanup:
- Eval_Number() could take DSP/CPU flag like Eval_Range()
does so that all commands taking numeric args can accept
also symbol, variable & register names
- Skip & tell user to rename any of the loaded symbols that
have same name as internal Hatari variables
- Change "" for expressions to {} so that quotes can
be used for e.g. search strings?
- While Hatari debugger has many features that Steem one doesn't have,
that also has debugging features missing from the Hatari debugger.
These ones should be straightforward to implement:
- Breakpoints on interrupts
- Breaking on exceptions 2-8 vs. bombs vs. don't break.
Hatari has only an option to break on address & bus errors
& uninitialized exception handler.
- Showing values both in different lengths and numeric bases.
(In Hatari one gets latter with "evaluate" command, e.g. "e a0",
and showing the value as long/word/byte requires ANDing it)
- All register values being shown with above info
(= Steem Register monitor)
- info commands for PSG, MFP, FDC and IKBD register values
(= Steem monitors for these)
- Info command for "timings" i.e. cycles since HBL/VBL,
timer values, video address & scanline
(= Steem Timings view)
- memory dump in text format
(= Steem Text monitor)
- Stack content display: m "a7-$40"-"a7"
(= Steem Stack display)
- Text string and binary values (hex) search
(= widgets in Steem monitor windows)
- Run for N cycles
(Hatari 'continue' command accepts only instructions, not cycles)
These are more complicated ones:
- Monitoring reads & writes to specific address. Hatari supports
only tracing changes to values, not having breakpoints of
reading values or writing the same value. Slow
- Showing breakpoints in instruction dumps. Hatari breakpoints
are more advanced than the trivial address breakpoints, so
this would require adding efficient support for plain PC
based breakpoints
- Adding new symbol names for arbitrary addresses one at the time.
Hatari debugger currently requires new symbols to be added to
a file containing all the symbols + reloading that file
- Memory dump that shows also disassembly and values
in different bases
(= Steem Memory monitor)
Basic GUI debugger features:
- Ability to open as many dump/info windows as wanted
(hex/asm/mfp/video/sound/...) and have their content
refreshed each time emulation is stopped.
- A stop/run button and a debugger "step" button
- Possibility to click to an address on dump window to define
a simple PC breakpoint (or monitor memory on B/W/L accesses)
- A way to search for hex/text in Hatari's RAM.
(See "Steem debugger features not in Hatari debugger"
on BerliOS hatari-devel mail thread for more info.)
- MonST debugger features missing from Hatari debugger
(ones not mentioned in Steem features):
- Address breakpoints can have conditions that are evaluated
only on that address
- Marking breakpoint types in disassembly (<count> for counted
ones, ? for conditional ones, * for others)
- Shortcut command for telling to run until given
(temporary) conditional breakpoint is hit
- Running until code returns from ROM (exiting from super mode?)
- Single stepping that skips Traps, Line-A, Line-F. And one that
skips also BSRs and JSRs. Run until RTS/RTE command
- Saving full machine status (like registers) to history buffer
each time debugger is entered (exception or breakpoint is hit)
and viewing of that history
- SP & SSP as CPU register names (active & supervisor stack)
- Fill and copy memory command
- Search for an address which disassembly output matches
given instruction substring
- User variables which values can be set & used in expressions
- Improved screen handling:
- Direct 16-bit & 32-bit support for monochrome and VDI modes
(currently they're converted through 8-bit surface)
- Line based screen change detection/checks:
- blit only changed lines
- simpler / faster (LED) overlay handling
- x3 and x4 zooming routines for ST-Low resolution
- Include some fancy zooming routines like 2xSaI or Super-Eagle
- Add support for hardware accelerated zooming with
SDL YUV overlays or OpenGL
- Check/clean RS232 code:
- polls at 2/20ms intervals and reads data byte at the time.
SDL_Delay()s & fgetc() could be replaced (at least on unix)
with select() and fread(). Or just remove the rs232 thread
and use an interrupt for it...
- The commented out rs232 stuff could be removed from gemdos.c
(RS emulation is done at HW, not Gemdos level).
- Improve directory handling:
- Currently screenshots & anim go always to current dir,
whereas memsave, sound recording, printer & midi & serial &
output and log output go to file specified in config file.
There should be support for setting also anim/screenshot
directory / file name from config file (needs change also
in screenSnapShot.c).
- By default the directory config values should be empty in
which case the code will at run-time decide to use current
directory, but not modify the path config setting.
Bug reports
-----------
- Hextracker freezes at start with Falcon emulation because Videl
screen address counter doesn't advance (its read & write are no-ops).
- Atari programs can crash Hatari in Falcon mode because sound DMA code isn't robust
against bad register values:
http://listengine.tuxfamily.org/lists.tuxfamily.org/hatari-devel/2012/02/msg00082.html
- When playing Tautology 2 I noticed the mod player sound goes in and out of
sync. fading into crackle and back. (Using Hatari 1.4 with TOS 4.04 on Mac
OS X 10.6.5)
http://developer.berlios.de/bugs/?func=detailbug&bug_id=17781&group_id=10436
- Problem: On real Falcon there is a minor feature in videl. You can simply
fade whole screen to black, if you just clear the Horizontal Border End
($ffff8286) and start fading colors from 0 to 255 to $ffff9800 or vice versa.
test <put picture to screen>
move.w #255-1,d7
moveq #0,d6
.loop <vsync, stop#$2300>
clr.w $ffff8286.w
move.l d6,$ffff9800.w
addq.l #1,d6
dbf d7,.loop
Now it does this exactly opposite direction, it fades bgcolor from 0 to 255.
http://developer.berlios.de/bugs/?func=detailbug&bug_id=18002&group_id=10436
- To compare my real Falcon with Hatari on my Windows 7 PC, I make some short
benchmarks with Kronos. The disk benchmark in Kronos runs in 1-2 minutes on
my real Falcon. The same disk benchmark in Kronos under Hatari 1.5 runs
longer than 20 minutes with GEMDOS emulation. Only when I use the AltGr + X
option, before the benchmark runs, then the disk benchmark in kronos is
comparable fast as my 16MHz Falcon.
http://developer.berlios.de/bugs/?func=detailbug&bug_id=18298&group_id=10436
-> Hatari GEMDOS emulation needs to do a lot of extra kernel file & directory
accesses which with current code seem unavoidable. They're a problem only
with test programs like Kronos, for those one should consider using
harddisk image files instead.

66
doc/video-recording.txt Normal file
View File

@ -0,0 +1,66 @@
How to record Atari videos with Hatari
======================================
Getting best output from Hatari
-------------------------------
* Do NOT use external recorders (such as Quicktime X on OSX), as they
won't get perfect framerate and sound sync like Hatari itself does.
* Disable (default) frame skip, either from the Hatari GUI, or with
following command line option:
--frameskips 0
* For STe you could set audio to 50066 Hz using ST table, either from
the Hatari GUI, or with following command line options:
--sound 50066 --ym-mixing table
* If you have enough free disk space, ask Hatari to use uncompressed
AVI format for the recording with the following command line option:
--avi-vcodec bmp
Hatari AVI compression notes
----------------------------
If Hatari is configured/built with PNG development installed headers
(normal case with Linux distros and pre-built binaries), Hatari will
use PNG compression to produce smaller AVI recordings.
Additionally, by default Hatari will use the highest PNG compression
level (same as with screenshots), but this is *really* CPU intensive.
Because of the PNG compression CPU usage, it's better to use
uncompressed BMP format. If you don't have enough disk space for
this, next best option is to ask Hatari to use lower compression
level, e.g. with:
--png-level 4
Valid compression levels are 0-9, with 9 being default/highest/slowest.
Preparing videos for uploading
------------------------------
If the end goal is Youtube, it's recommended to run Hatari's AVI
output through ffmpeg to do nearest neighbor upscale to 1080p. Then
Youtube will keep the 50 FPS and you have non-fuzzy pixels in the
recording.
This ffmpeg line should do the trick for a 320x200 stream (5x scale):
ffmpeg -i hatari.avi -vf "scale=1600:1000, \
pad=1920:1080:160:40:black" -sws_flags neighbor \
-vcodec png -acodec copy youtube1080p.mov
And for a 416x276 stream (so you get the overscan area as well, 4x scale):
ffmpeg -i hatari.avi -vf "crop=400:270:8:0, scale=1600:1080, \
pad=1920:1080:160:0:black" -sws_flags neighbor -vcodec png \
-acodec copy youtube1080p.mov
Above adds padding to 1920*1080 size, that can be removed if you trust
the re-encoder/player to scale properly (which has been known to
fail). It also saves the stream as PNG so it's manageable to upload
and store for future.
(Upload information is based on atari-forum post by "evil":
http://atari-forum.com/viewtopic.php?f=51&t=27595#p268185 )

View File

@ -0,0 +1,27 @@
#
# CMake crossdefs for GP2X Wiz target for Hatari
# (c) by 2011 by Matthias Arndt <marndt@asmsoftware.de>
#
#
# USE AT YOUR OWN RISK and tweak as required!
#
# 2011-04-05 v0.01
#
# this one is important
SET(CMAKE_SYSTEM_NAME Linux)
#this one not so much
SET(CMAKE_SYSTEM_VERSION 1)
# specify the cross compiler
SET(CMAKE_C_COMPILER /opt/arm-openwiz-linux-gnu/bin/arm-openwiz-linux-gnu-gcc)
SET(CMAKE_CXX_COMPILER /opt/arm-openwiz-linux-gnu/bin/arm-openwiz-linux-gnu-g++)
# where is the target environment
SET(CMAKE_FIND_ROOT_PATH /opt/arm-openwiz-linux-gnu/arm-openwiz-linux-gnu/sys-root-newsdl)
# search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@ -0,0 +1,8 @@
#!/bin/sh
#
# experimental Hatari launcher for GP2X Wiz
cd hatari
./hatari.gpe -f --bpp 16 -c ./wiz.cfg | tee ./hatari-log.txt
sync
cd /usr/gp2x
exec /usr/gp2x/gp2xmenu

57
etc/README Normal file
View File

@ -0,0 +1,57 @@
Hatari on constrained devices
-----------------------------
In this directory are Hatari configurations files for mobile etc
devices on which the builtin Hatari defaults aren't appropriate.
Just copy suitable config file to the device /etc/ directory as
'hatari.cfg'.
Below are some comments about the configuration files. Some comments
about suitable compiler options for these platforms can be found from
the Hatari v1.4 default Makefile (which is now replaced with CMake):
http://hg.tuxfamily.org/mercurialroot/hatari/hatari/file/dabb65b541c9/Makefile-default.cnf
Hatari v1.5 gets screen size automatically, so setting that shouldn't
anymore be necessary, but shouldn't harm either.
If these configuration settings don't provide sufficient performance,
use of an earlier Hatari version such as v0.95 (or even earlier)
should be considered. Their emulation wasn't as accurate as in latest
Hatari versions and therefore they're somewhat faster.
n810.cfg
--------
Configuration file for the Nokia 770/N800/N810 TI Omap2/ARMv6 based devices:
- Shortcut keys adapted for the device keys:
- Menu, Increase, Decrease, Fullscreen, Joystick keyemu fire
- Max Hatari screen size set to device screen size
- Options that are more suitable for the device performance:
- Non-compatible, but faster CPU mode (works fine with most things)
- No Falcon DSP emulation
- High spec512 threshold
- Automatic frameskip
- Sensible default paths even for things that don't yet exist (to minimize
typing when they're enabled as N770 & N800 don't have a keyboard and N810
keyboard is very small so typing is quite awkward)
For more information on these devices, see e.g:
http://en.wikipedia.org/wiki/Nokia_N800
win-ce.cfg
----------
Configuration file for Windows Mobile based devices:
- Start in fullscreen mode
- Smaller border values / screen size limited to 320x240 resolution
- Options that are more suitable for the device performance:
- Non-compatible (but faster) CPU mode
- Frameskip enabled
- High spec512 threshold
- 11kHz sound
For more information on Windows CE and device based on it, see:
http://en.wikipedia.org/wiki/Windowsce

200
etc/n810.cfg Normal file
View File

@ -0,0 +1,200 @@
[Floppy]
bAutoInsertDiskB = TRUE
nWriteProtection = 2
szDiskAFileName =
szDiskBFileName =
szDiskImageDirectory = /home/user/MyDocs/.games
szDiskAZipPath =
szDiskBZipPath =
[HardDisk]
bBootFromHardDisk = TRUE
bUseHardDiskDirectory = TRUE
bUseHardDiskImage = FALSE
szHardDiskDirectory = /home/user/MyDocs
szHardDiskImage = /home/user/MyDocs/acsi.img
bUseIdeMasterHardDiskImage = FALSE
bUseIdeSlaveHardDiskImage = FALSE
szIdeMasterHardDiskImage = /home/user/MyDocs/ide-master.img
szIdeSlaveHardDiskImage = /home/user/MyDocs/ide-slave.img
[Joystick0]
bEnableAutoFire = FALSE
nJoyId = 1
nJoystickMode = 0
# 5-way rocker keys (arrows + enter)
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick1]
bEnableAutoFire = FALSE
nJoyId = 0
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick2]
bEnableAutoFire = FALSE
nJoyId = 2
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick3]
bEnableAutoFire = FALSE
nJoyId = 3
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick4]
bEnableAutoFire = FALSE
nJoyId = 4
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick5]
bEnableAutoFire = FALSE
nJoyId = 5
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Memory]
bAutoSave = FALSE
nMemorySize = 1
szAutoSaveFileName = /home/user/.hatari/auto.sav
szMemoryCaptureFileName = /home/user/MyDocs/.games/hatari.sav
[Midi]
bEnableMidi = FALSE
sMidiInFileName = /dev/snd/midiC1D0
sMidiOutFileName = /dev/snd/midiC1D0
[Printer]
bEnablePrinting = FALSE
bPrintToFile = TRUE
szPrintToFileName = /home/user/MyDocs/.documents/hatari-printer.txt
[ROM]
szCartridgeImageFileName =
szTosImageFileName = /usr/share/hatari/tos.img
[RS232]
bEnableRS232 = FALSE
szOutFileName = /dev/modem
szInFileName = /dev/modem
[Screen]
bAllowOverscan = FALSE
bFullScreen = FALSE
bUseExtVdiResolutions = FALSE
nForceBpp = 0
nSpec512Threshold = 128
nFrameSkips = 5
nMonitorType = 1
nVdiColors = 0
nVdiWidth = 800
nVdiHeight = 480
bShowStatusbar = TRUE
bShowDriveLed = FALSE
bAspectCorrect = TRUE
nMaxWidth = 800
nMaxHeight = 480
[ShortcutsWithModifiers]
# check /usr/include/SDL/SDL_keysyms.h for numeric key values
keyOptions = 111
keyFullScreen = 102
keyMouseMode = 109
keyColdReset = 99
keyWarmReset = 114
keyScreenShot = 103
keyBossKey = 105
keyCursorEmu = 106
keyRecAnim = 97
keyRecSound = 121
keySound = 115
keyQuit = 113
keyFastForward = 120
keyPause = 0
keyDebugger = 19
keyLoadMem = 108
keySaveMem = 107
keyInsertDiskA = 100
[ShortcutsWithoutModifiers]
# 285: F4 (menu)
keyOptions = 285
# 287: F6 (fullsceen)
keyFullScreen = 287
keyMouseMode = 0
keyColdReset = 0
keyWarmReset = 0
keyScreenShot = 0
keyBossKey = 0
# 289: F8 (+ increase)
keyCursorEmu = 289
keyRecAnim = 0
keyRecSound = 0
# 288: F7 (- decrease)
keySound = 288
keyQuit = 0
keyFastForward = 0
keyPause = 19
keyDebugger = 0
keyLoadMem = 0
keySaveMem = 0
keyInsertDiskA = 0
[Sound]
bEnableSound = TRUE
szYMCaptureFileName = /home/user/MyDocs/.sounds/hatari.wav
nPlaybackFreq = 44100
[System]
bBlitter = FALSE
bCompatibleCpu = FALSE
bFastForward = FALSE
bPatchTimerD = TRUE
bRealTimeClock = TRUE
nCpuFreq = 8
nCpuLevel = 0
nDSPType = 0
nMachineType = 0
[Log]
sLogFileName = stderr
sTraceFileName = stderr
nTextLogLevel = 4
nAlertDlgLogLevel = 1
bConfirmQuit = TRUE
[Debugger]
nNumberBase = 10
nDisasmLines = 8
nMemdumpLines = 8
[Keyboard]
bDisableKeyRepeat = FALSE
nKeymapType = 0
szMappingFileName =

52
etc/win-ce.cfg Normal file
View File

@ -0,0 +1,52 @@
[Log]
sLogFileName = hatarilog.txt
nTextLogLevel = 3
nAlertDlgLogLevel = 3
bConfirmQuit = TRUE
[Screen]
bFullScreen = TRUE
bAllowOverscan = FALSE
nForceBpp = 0
nSpec512Threshold = 128
bUseExtVdiResolutions = FALSE
nVdiWidth = 320
nVdiHeight = 240
nVdiColors = 2
nMonitorType = 1
nFrameSkips = 5
bAspectCorrect = TRUE
bShowStatusbar = FALSE
bShowDriveLed = FALSE
nMaxWidth = 320
nMaxHeight = 240
[Joystick1]
nJoystickMode = 2
bEnableAutoFire = FALSE
nJoyId = 0
nKeyCodeUp = 273
nKeyCodeDown = 274
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeFire = 305
bEnableJumpOnFire2 = FALSE
[Keyboard]
bDisableKeyRepeat = TRUE
nKeymapType = 0
szMappingFileName =
[ShortcutsWithoutModifiers]
keyOptions = 282
[Sound]
bEnableSound = TRUE
nPlaybackFreq = 22050
[Memory]
nMemorySize = 1
bAutoSave = FALSE
[System]
bCompatibleCpu = FALSE

200
etc/wiz.cfg Executable file
View File

@ -0,0 +1,200 @@
[Floppy]
bAutoInsertDiskB = TRUE
nWriteProtection = 2
szDiskAFileName = /mnt/sd/game/hatari/FANTASIA.st
szDiskBFileName =
szDiskImageDirectory = /mnt/sd/game/hatari/
szDiskAZipPath =
szDiskBZipPath =
[HardDisk]
bBootFromHardDisk = FALSE
bUseHardDiskDirectory = FALSE
bUseHardDiskImage = FALSE
szHardDiskDirectory =
szHardDiskImage =
bUseIdeMasterHardDiskImage = FALSE
bUseIdeSlaveHardDiskImage = FALSE
szIdeMasterHardDiskImage =
szIdeSlaveHardDiskImage =
[Joystick0]
bEnableAutoFire = FALSE
nJoyId = 0
nJoystickMode = 0
# 5-way rocker keys (arrows + enter)
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick1]
bEnableAutoFire = FALSE
nJoyId = 1
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick2]
bEnableAutoFire = FALSE
nJoyId = 2
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick3]
bEnableAutoFire = FALSE
nJoyId = 3
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick4]
bEnableAutoFire = FALSE
nJoyId = 4
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Joystick5]
bEnableAutoFire = FALSE
nJoyId = 5
nJoystickMode = 0
nKeyCodeDown = 274
nKeyCodeFire = 13
nKeyCodeLeft = 276
nKeyCodeRight = 275
nKeyCodeUp = 273
[Memory]
bAutoSave = FALSE
nMemorySize = 1
szAutoSaveFileName = /mnt/sd/game/hatari/auto.sav
szMemoryCaptureFileName = /mnt/sd/game/hatari/hatari.sav
[Midi]
bEnableMidi = FALSE
sMidiInFileName = /dev/snd/midiC1D0
sMidiOutFileName = /dev/snd/midiC1D0
[Printer]
bEnablePrinting = FALSE
bPrintToFile = TRUE
szPrintToFileName = /mnt/sd/game/hatari/hatari-printer.txt
[ROM]
szCartridgeImageFileName =
szTosImageFileName = /mnt/sd/game/hatari/tos.img
[RS232]
bEnableRS232 = FALSE
szOutFileName = /dev/modem
szInFileName = /dev/modem
[Screen]
bAllowOverscan = FALSE
bFullScreen = TRUE
bUseExtVdiResolutions = FALSE
nForceBpp = 16
nSpec512Threshold = 128
nFrameSkips = 2
nMonitorType = 1
nVdiColors = 0
nVdiWidth = 320
nVdiHeight = 240
bShowStatusbar = FALSE
bShowDriveLed = FALSE
bAspectCorrect = FALSE
nMaxWidth = 320
nMaxHeight = 240
[ShortcutsWithModifiers]
# check /usr/include/SDL/SDL_keysyms.h for numeric key values
keyOptions = 111
keyFullScreen = 102
keyMouseMode = 109
keyColdReset = 99
keyWarmReset = 114
keyScreenShot = 103
keyBossKey = 105
keyCursorEmu = 106
keyRecAnim = 97
keyRecSound = 121
keySound = 115
keyQuit = 113
keyFastForward = 120
keyPause = 0
keyDebugger = 19
keyLoadMem = 108
keySaveMem = 107
keyInsertDiskA = 100
[ShortcutsWithoutModifiers]
# 285: F4 (menu)
keyOptions = 285
# 287: F6 (fullsceen)
keyFullScreen = 287
keyMouseMode = 0
keyColdReset = 0
keyWarmReset = 0
keyScreenShot = 0
keyBossKey = 0
# 289: F8 (+ increase)
keyCursorEmu = 289
keyRecAnim = 0
keyRecSound = 0
# 288: F7 (- decrease)
keySound = 288
keyQuit = 0
keyFastForward = 0
keyPause = 19
keyDebugger = 0
keyLoadMem = 0
keySaveMem = 0
keyInsertDiskA = 0
[Sound]
bEnableSound = TRUE
szYMCaptureFileName = /mnt/sd/game/hatari/hatari.wav
nPlaybackFreq = 22050
[System]
bBlitter = FALSE
bCompatibleCpu = FALSE
bFastForward = FALSE
bPatchTimerD = TRUE
bRealTimeClock = FALSE
nCpuFreq = 8
nCpuLevel = 0
nDSPType = 0
nMachineType = 0
[Log]
sLogFileName = stderr
sTraceFileName = stderr
nTextLogLevel = 4
nAlertDlgLogLevel = 1
bConfirmQuit = FALSE
[Debugger]
nNumberBase = 10
nDisasmLines = 8
nMemdumpLines = 8
[Keyboard]
bDisableKeyRepeat = FALSE
nKeymapType = 0
szMappingFileName =

339
gpl.txt Normal file
View File

@ -0,0 +1,339 @@
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.

108
hatari.spec Normal file
View File

@ -0,0 +1,108 @@
#
# RPM spec file for Hatari
#
# This file and all modifications and additions to the pristine
# package are under the same license as the package itself.
#
BuildRequires: bash coreutils cpio cpp diffutils file filesystem findutils glibc glibc-devel grep groff gzip libgcc m4 make man mktemp patch readline sed tar unzip util-linux zlib zlib-devel SDL SDL-devel autoconf binutils gcc libtool rpm
Name: hatari
URL: http://hatari.tuxfamily.org/
License: GPL
Group: System/Emulators/Other
Autoreqprov: on
Version: 1.9.0
Release: 1
Summary: an Atari ST emulator suitable for playing games
Source: %{name}-%{version}.tar.gz
#Patch: %{name}-%{version}.dif
BuildRoot: %{_tmppath}/%{name}-%{version}-build
Prefix: /usr
%description
Hatari is an emulator for the Atari ST, STE, TT and Falcon computers.
The Atari ST was a 16/32 bit computer system which was first released by Atari
in 1985. Using the Motorola 68000 CPU, it was a very popular computer having
quite a lot of CPU power at that time.
Unlike many other Atari ST emulators which try to give you a good environment
for running GEM applications, Hatari tries to emulate the hardware of a ST as
close as possible so that it is able to run most of the old ST games and demos.
%prep
%setup
#%patch
%build
# LDFLAGS="-static" LIBS=`sdl-config --static-libs` \
CFLAGS="-O3 -fomit-frame-pointer" \
./configure --prefix=/usr --sysconfdir=/etc
make
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
/usr/bin/hatari
/usr/share/hatari
%doc %_mandir/man1/hatari.1*
%dir %_docdir/%{name}
%_docdir/%{name}/*.txt
%_docdir/%{name}/*.html
%dir %_docdir/%{name}/images
%_docdir/%{name}/images/*.png
%changelog -n hatari
* Thu Sep 10 2015 - Nicolas Pomarede
- Hatari version 1.9.0
* Wed Jul 30 2014 - Nicolas Pomarede
- Hatari version 1.8.0
* Sun Jun 24 2013 - Nicolas Pomarede
- Hatari version 1.7.0
* Sun Jun 24 2012 - Nicolas Pomarede
- Hatari version 1.6.2
* Fri Jan 13 2012 - Nicolas Pomarede
- Hatari version 1.6.1
* Sun Jan 1st 2012 - Nicolas Pomarede
- Hatari "Happy New Year 2012" version 1.6.0
* Tue Jul 19 2011 - Nicolas Pomarede
- Hatari version 1.5.0
* Sat Jun 12 2010 - Nicolas Pomarede
- Hatari version 1.4.0
* Sat Sep 05 2009 - Thomas Huth
- Hatari version 1.3.1
* Sun Aug 16 2009 - Thomas Huth
- Hatari version 1.3.0
* Sat Jan 24 2009 - Thomas Huth
- Hatari version 1.2.0
* Sat Nov 29 2008 - Thomas Huth
- Hatari version 1.1.0
* Wed Jan 02 2008 - Thomas Huth
- Adapted RPM to the latest source code level (aiming at version 1.0.0)
* Sun May 06 2007 - Thomas Huth
- Adapted spec file to be able to build Hatari with RedHat, too
* Sun Aug 27 2006 - Thomas Huth
- Upgraded to version 0.90
* Tue Oct 18 2005 - Thomas Huth
- initial package

7
python-ui/.cvsignore Normal file
View File

@ -0,0 +1,7 @@
config.pyc
debugui.pyc
dialogs.pyc
hatari-console.pyc
hatari.pyc
hatariui.pyc
uihelpers.pyc

28
python-ui/CMakeLists.txt Normal file
View File

@ -0,0 +1,28 @@
# conftypes.py is created to source directory (instead of build directory)
# so that Hatari UI can be tested directly from the source directory
add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/conftypes.py
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gentypes.py < ${CMAKE_CURRENT_SOURCE_DIR}/../src/configuration.c > ${CMAKE_CURRENT_SOURCE_DIR}/conftypes.py
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../src/configuration.c ${CMAKE_CURRENT_SOURCE_DIR}/gentypes.py)
add_custom_target(conftypes ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/conftypes.py)
INSTALL(PROGRAMS hatariui
DESTINATION ${BINDIR})
INSTALL(PROGRAMS hatariui.py debugui.py
DESTINATION ${DATADIR}/hatariui/)
INSTALL(FILES README TODO release-notes.txt hatari-icon.png hatari-logo.png
config.py dialogs.py hatari.py uihelpers.py
${CMAKE_CURRENT_SOURCE_DIR}/conftypes.py
DESTINATION ${DATADIR}/hatariui/)
INSTALL(FILES hatariui.desktop
DESTINATION share/applications)
# if(UNIX)
add_custom_target(hatariui_man ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/hatariui.1.gz)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/hatariui.1.gz
COMMAND gzip -c -9 ${CMAKE_CURRENT_SOURCE_DIR}/hatariui.1 > ${CMAKE_CURRENT_BINARY_DIR}/hatariui.1.gz
DEPENDS hatariui.1)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hatariui.1.gz DESTINATION ${MANDIR})
# endif(UNIX)

20
python-ui/FILES Normal file
View File

@ -0,0 +1,20 @@
Code:
- config.py - Hatari configuration INI file handling
- debugui.py - Debugger UI (works also as standalone)
- dialogs.py - Different dialogs shown by Hatari UI
- hatari.py - Communication with Hatari and config option handling
- hatariui - Wrapper script for hatariui.py with suitable default options
- hatariui.py - Hatari UI main: option handling, main window etc
- Makefile - Rules for installing
- uihelpers.py - Misc helpers for Hatari UI
Data files:
- hatari-icon.png - Window icon
- hatari.png - About dialog image
- hatariui.desktop - Hatari UI .desktop file for application launchers
Documentation:
- release-notes.txt - UI & code changes
- FILES - This file
- README - Hatari UI intro / description
- TODO - missing features etc

78
python-ui/README Normal file
View File

@ -0,0 +1,78 @@
Hatari UI
---------
Hatari UI is an out-of-process user interface for the Hatari Atari
ST/STe/TT/Falcon emulator and its built-in debugger which can
(optionally) embed the Hatari emulator window.
Having the UI in another process allows doing it with a higher level
language while avoiding adding GUI toolkit dependencies to Hatari
itself. The UI is done with PyGtk i.e. in Python language, using the
Gtk widget set.
The main points of this new UI over the Hatari internal one are its
configurability, more usable file selector, internationalization
support and providing a GUI for the (console based) debugger included
with the Hatari emulator.
Note: this is an additional UI, the built-in Hatari SDL UI isn't being
replaced or going anywhere!
Requirements
------------
My guess at the required versions for the dependencies are:
- Python >= 2.6
- PyGtk >= 2.12 (on Debian/Ubuntu PyGtk is in python-gtk2 package)
Hatari UI is included with the Hatari sources:
http://hg.tuxfamily.org/mercurialroot/hatari/hatari/file/tip/python-ui
Hatari UI has been tested on several Linux versions. I would assume
it to work also on other unix systems such as Apple OSX. It may work
with the Hatari Windows version, as long as it is built with socket
support.
Embedding the Hatari emulator window is currently supported only for
systems using an X window system (from libSDL sources it would seem
that Windows would also support window embedding, but support for that
would need to be added both to Hatari and Hatari UI because SDL's
own embedding disables all keyboard events in SDL program).
Here are instructions on installing the dependencies for non-Linux
platforms (neither tested nor supported as I don't use/have them):
http://pygtk.org/downloads.html
Running
-------
Being a Python program, Hatari UI doesn't need to be built.
You can just run it from where you extracted it (or checked
it out of TuxFamily HG repo) by calling its wrapper script:
/path/to/script/hatariui
Or you can run just the debugger:
/path/to/script/debugui.py
But you can also install it to system along with Hatari:
make install
Notes
-----
Hatari UI runs a Hatari version found on $PATH. If you want
to use a version of Hatari that hasn't been installed, you
need to modify the search path, for example like this:
PATH=../build/src:.:$PATH hatariui
If UI is started without the embedding option, the actions
(in menus and toolbars) have also shortcuts. They cannot
be used when Hatari window is embedded because then those
shortcuts couldn't be used with Hatari.
A www-page with more information about Hatari UI is here:
http://koti.mbnet.fi/tammat/hatari/hatari-ui.shtml

61
python-ui/TODO Normal file
View File

@ -0,0 +1,61 @@
TODO
====
This lists potential TODO items that aren't listed in the Hatari/UI
sources.
* Separate "Atari monitor" and "Hatari window" settings dialogs
(move options from "Display" and monitor setting from "Machine"
dialogs to here and add VDI options to Atari Monitor dialog)
* Features for Help menu:
- Create empty floppy images (+ convert directories to hard disks?)
- MSA<->ST converter
- ZIP->ST converter
* Add more Hatari debugger features to the debug UI:
- DSP support
- History support
- Profiling support
- DSP symbols loading
(CPU symbols are loaded automatically from programs)
- Support for stepping the emulation (both step & next commands)
- multiple views to memory (refreshed whenever emulation is stopped)
- supporting also register relative views (register values
parsing should move to common functionality first)
- breakpoint support (issue: how to stop emulation on breakpoint
but still allow Hatari to process remote commands?)
- trace & debug log viewers?
* Put all configuration stuff into single notebook dialog?
* Translation support for the UI:
- use gettext
- build needs to build & install message catalogs
- some way for Hatari to forward dialog ID to the remote UI
with dialog string parameters (filenames etc) which then
need to be localized too & shown...
* Hatari UI specific configuration which stores:
- list of last used configuration files which would be shown
either in their own menu or dialog
- list of last used memory snapshots (10?)
- disk image dir (uses Hatari config value as default)
- trace settings
- remove dialog specific load/save stuff
- screenshot name
- needs support also to Hatari src/screenSnapShot.c
* Supporting other, less important Hatari configuration options:
- HD booting, toggling disk-b autoinsertion
- something for multiple GEMDOS partition directories
- separate A/B disk paths for images that are within ZIP archives
- joystick autofire toggling, defining the keys for joyemu
- keyboard repeat, key mapping type and file, mouse grabbing
- vdi planes and size, SDL bpp forcing, spec512 threshold, ST blitter
- cartridge image (where? it has many limitations)
- log file and levels, console output, bios intercept, run-VBLs
(Many of these aren't supported by the internal Hatari UI either, or
are missing corresponding command line options so they will require
additional support on the Hatari control.c side too or they can
be only enabled at boot, not disabled.)

270
python-ui/config.py Normal file
View File

@ -0,0 +1,270 @@
#!/usr/bin/env python
#
# Class and helper functions for handling (Hatari) INI style
# configuration files: loading, saving, setting/getting variables,
# mapping them to sections, listing changes
#
# Copyright (C) 2008-2012 by Eero Tamminen
#
# 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.
import os
# mapping from Hatari config variable name to type id (Bool, Int, String)
from conftypes import conftypes
# ------------------------------------------------------
# Helper functions for type safe Hatari configuration variable access.
# Map booleans, integers and strings to Python types, and back to strings.
def value_to_text(key, value):
"value_to_text(key, value) -> text, convert Python type to string"
assert(key in conftypes)
valtype = type(value)
if valtype == bool:
assert(conftypes[key] == "Bool")
if value:
text = "TRUE"
else:
text = "FALSE"
elif valtype == int:
assert(conftypes[key] == "Int")
text = str(value)
else:
assert(conftypes[key] == "String")
if value == None:
text = ""
else:
text = value
return text
def text_to_value(text):
"text_to_value(text) -> value, convert INI file values to real types"
# bool?
upper = text.upper()
if upper == "FALSE":
value = False
elif upper == "TRUE":
value = True
else:
try:
# integer?
value = int(text)
except ValueError:
# string
value = text
return value
# ------------------------------------------------------
# Handle INI style configuration files as used by Hatari
class ConfigStore:
def __init__(self, userconfdir, defaults = {}, miss_is_error = True):
"ConfigStore(userconfdir, fgfile[,defaults,miss_is_error])"
self.defaults = defaults
self.userpath = self._get_full_userpath(userconfdir)
self.miss_is_error = miss_is_error
def _get_full_userpath(self, leafdir):
"get_userpath(leafdir) -> config file default save path from HOME, CWD or their subdir"
# user's hatari.cfg can be in home or current work dir,
# current dir is used only if $HOME fails
for path in (os.getenv("HOME"), os.getenv("HOMEPATH"), os.getcwd()):
if path and os.path.exists(path) and os.path.isdir(path):
if leafdir:
hpath = "%s%c%s" % (path, os.path.sep, leafdir)
if os.path.exists(hpath) and os.path.isdir(hpath):
return hpath
return path
return None
def get_filepath(self, filename):
"get_filepath(filename) -> return correct full path to config file"
# user config has preference over system one
for path in (self.userpath, os.getenv("HATARI_SYSTEM_CONFDIR")):
if path:
file = "%s%c%s" % (path, os.path.sep, filename)
if os.path.isfile(file):
return file
# writing needs path name although it's missing for reading
return "%s%c%s" % (self.userpath, os.path.sep, filename)
def load(self, path):
"load(path) -> load given configuration file"
if os.path.isfile(path):
sections = self._read(path)
if sections:
self.sections = sections
else:
print("ERROR: configuration file loading failed!")
return
else:
print("WARNING: configuration file missing!")
if self.defaults:
print("-> using dummy 'defaults'.")
self.sections = self.defaults
self.path = path
self.cfgfile = os.path.basename(path)
self.original = self.get_checkpoint()
self.changed = False
def is_loaded(self):
"is_loaded() -> True if configuration loading succeeded"
if self.sections:
return True
return False
def get_path(self):
"get_path() -> configuration file path"
return self.path
def _read(self, path):
"_read(path) -> (all keys, section2key mappings)"
print("Reading configuration file '%s'..." % path)
config = open(path, "r")
if not config:
return ({}, {})
name = "[_orphans_]"
seckeys = {}
sections = {}
for line in config.readlines():
line = line.strip()
if not line or line[0] == '#':
continue
if line[0] == '[':
if line in sections:
print("WARNING: section '%s' twice in configuration" % line)
if seckeys:
sections[name] = seckeys
seckeys = {}
name = line
continue
if line.find('=') < 0:
print("WARNING: line without key=value pair:\n%s" % line)
continue
key, text = [string.strip() for string in line.split('=')]
seckeys[key] = text_to_value(text)
if seckeys:
sections[name] = seckeys
return sections
def get_checkpoint(self):
"get_checkpoint() -> checkpoint, get the state of variables at this point"
checkpoint = {}
for section in self.sections.keys():
checkpoint[section] = self.sections[section].copy()
return checkpoint
def get_checkpoint_changes(self, checkpoint):
"get_checkpoint_changes() -> list of (key, value) pairs for later changes"
changed = []
if not self.changed:
return changed
for section in self.sections.keys():
if section not in checkpoint:
for key, value in self.sections[section].items():
changed.append((key, value))
continue
for key, value in self.sections[section].items():
if (key not in checkpoint[section] or
value != checkpoint[section][key]):
text = value_to_text(key, value)
changed.append(("%s.%s" % (section, key), text))
return changed
def revert_to_checkpoint(self, checkpoint):
"revert_to_checkpoint(checkpoint), revert to given checkpoint"
self.sections = checkpoint
def get(self, section, key):
return self.sections[section][key]
def set(self, section, key, value):
"set(section,key,value), set given key to given section"
if section not in self.sections:
if self.miss_is_error:
raise AttributeError("no section '%s'" % section)
self.sections[section] = {}
if key not in self.sections[section]:
if self.miss_is_error:
raise AttributeError("key '%s' not in section '%s'" % (key, section))
self.sections[section][key] = value
self.changed = True
elif self.sections[section][key] != value:
self.changed = True
self.sections[section][key] = value
def is_changed(self):
"is_changed() -> True if current configuration is changed"
return self.changed
def get_changes(self):
"get_changes(), return (key, value) list for each changed config option"
return self.get_checkpoint_changes(self.original)
def write(self, fileobj):
"write(fileobj), write current configuration to given file object"
sections = list(self.sections.keys())
sections.sort()
for name in sections:
fileobj.write("%s\n" % name)
keys = list(self.sections[name].keys())
keys.sort()
for key in keys:
value = value_to_text(key, self.sections[name][key])
fileobj.write("%s = %s\n" % (key, value))
fileobj.write("\n")
def save(self):
"save() -> path, if configuration changed, save it"
if not self.changed:
print("No configuration changes to save, skipping")
return None
fileobj = None
if self.path:
try:
fileobj = open(self.path, "w")
except:
pass
if not fileobj:
print("WARNING: non-existing/writable configuration file, creating a new one...")
if not os.path.exists(self.userpath):
os.makedirs(self.userpath)
self.path = "%s%c%s" % (self.userpath, os.path.sep, self.cfgfile)
fileobj = open(self.path, "w")
if not fileobj:
print("ERROR: opening '%s' for saving failed" % self.path)
return None
self.write(fileobj)
print("Saved configuration file:", self.path)
self.changed = False
return self.path
def save_as(self, path):
"save_as(path) -> path, save configuration to given file and select it"
assert(path)
if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))
self.path = path
self.changed = True
return self.save()
def save_tmp(self, path):
"save_tmp(path) -> path, save configuration to given file without selecting it"
if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))
fileobj = open(path, "w")
if not fileobj:
print("ERROR: opening '%s' for saving failed" % path)
return None
self.write(fileobj)
print("Saved temporary configuration file:", path)
return path

571
python-ui/debugui.py Executable file
View File

@ -0,0 +1,571 @@
#!/usr/bin/env python
#
# A Debug UI for the Hatari, part of PyGtk Hatari UI
#
# Copyright (C) 2008-2011 by Eero Tamminen
#
# 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.
import os
# use correct version of pygtk/gtk
import pygtk
pygtk.require('2.0')
import gtk
import pango
from config import ConfigStore
from uihelpers import UInfo, create_button, create_toggle, \
create_table_dialog, table_add_entry_row, table_add_widget_row, \
get_save_filename, FselEntry
from dialogs import TodoDialog, ErrorDialog, AskDialog, KillDialog
def dialog_apply_cb(widget, dialog):
dialog.response(gtk.RESPONSE_APPLY)
# -------------
# Table dialogs
class SaveDialog:
def __init__(self, parent):
table, self.dialog = create_table_dialog(parent, "Save from memory", 3, 2)
self.file = FselEntry(self.dialog)
table_add_widget_row(table, 0, "File name:", self.file.get_container())
self.address = table_add_entry_row(table, 1, "Save address:", 6)
self.address.connect("activate", dialog_apply_cb, self.dialog)
self.length = table_add_entry_row(table, 2, "Number of bytes:", 6)
self.length.connect("activate", dialog_apply_cb, self.dialog)
def run(self, address):
"run(address) -> (filename,address,length), all as strings"
if address:
self.address.set_text("%06X" % address)
self.dialog.show_all()
filename = length = None
while 1:
response = self.dialog.run()
if response == gtk.RESPONSE_APPLY:
filename = self.file.get_filename()
address_txt = self.address.get_text()
length_txt = self.length.get_text()
if filename and address_txt and length_txt:
try:
address = int(address_txt, 16)
except ValueError:
ErrorDialog(self.dialog).run("address needs to be in hex")
continue
try:
length = int(length_txt)
except ValueError:
ErrorDialog(self.dialog).run("length needs to be a number")
continue
if os.path.exists(filename):
question = "File:\n%s\nexists, replace?" % filename
if not AskDialog(self.dialog).run(question):
continue
break
else:
ErrorDialog(self.dialog).run("please fill the field(s)")
else:
break
self.dialog.hide()
return (filename, address, length)
class LoadDialog:
def __init__(self, parent):
chooser = gtk.FileChooserButton('Select a File')
chooser.set_local_only(True) # Hatari cannot access URIs
chooser.set_width_chars(12)
table, self.dialog = create_table_dialog(parent, "Load to memory", 2, 2)
self.file = table_add_widget_row(table, 0, "File name:", chooser)
self.address = table_add_entry_row(table, 1, "Load address:", 6)
self.address.connect("activate", dialog_apply_cb, self.dialog)
def run(self, address):
"run(address) -> (filename,address), all as strings"
if address:
self.address.set_text("%06X" % address)
self.dialog.show_all()
filename = None
while 1:
response = self.dialog.run()
if response == gtk.RESPONSE_APPLY:
filename = self.file.get_filename()
address_txt = self.address.get_text()
if filename and address_txt:
try:
address = int(address_txt, 16)
except ValueError:
ErrorDialog(self.dialog).run("address needs to be in hex")
continue
break
else:
ErrorDialog(self.dialog).run("please fill the field(s)")
else:
break
self.dialog.hide()
return (filename, address)
class OptionsDialog:
def __init__(self, parent):
self.dialog = gtk.Dialog("Debugger UI options", parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
self.lines = gtk.Adjustment(0, 5, 50)
scale = gtk.HScale(self.lines)
scale.set_digits(0)
self.follow_pc = gtk.CheckButton("On stop, set address to PC")
vbox = self.dialog.vbox
vbox.add(gtk.Label("Memdump/disasm lines:"))
vbox.add(scale)
vbox.add(self.follow_pc)
vbox.show_all()
def run(self, lines, follow_pc):
"run(lines,follow_pc) -> (lines,follow_pc)"
self.follow_pc.set_active(follow_pc)
self.lines.set_value(lines)
self.dialog.show_all()
response = self.dialog.run()
if response == gtk.RESPONSE_APPLY:
lines = int(self.lines.get_value())
follow_pc = self.follow_pc.get_active()
self.dialog.hide()
return (lines, follow_pc)
# ----------------------------------------------------
# constants for the other classes
class Constants:
# dump modes
DISASM = 1
MEMDUMP = 2
REGISTERS = 3
# move IDs
MOVE_MIN = 1
MOVE_MED = 2
MOVE_MAX = 3
# class for the memory address entry, view (label) and
# the logic for memory dump modes and moving in memory
class MemoryAddress:
# class variables
debug_output = None
hatari = None
def __init__(self, hatariobj):
# hatari
self.debug_output = hatariobj.open_debug_output()
self.hatari = hatariobj
# widgets
self.entry, self.memory = self.create_widgets()
# settings
self.dumpmode = Constants.REGISTERS
self.follow_pc = True
self.lines = 12
# addresses
self.first = None
self.second = None
self.last = None
def clear(self):
if self.follow_pc:
# get first address from PC when next stopped
self.first = None
self.second = None
self.last = None
def create_widgets(self):
entry = gtk.Entry(6)
entry.set_width_chars(6)
entry.connect("activate", self._entry_cb)
memory = gtk.Label()
mono = pango.FontDescription("monospace")
memory.modify_font(mono)
entry.modify_font(mono)
return (entry, memory)
def _entry_cb(self, widget):
try:
address = int(widget.get_text(), 16)
except ValueError:
ErrorDialog(widget.get_toplevel()).run("invalid address")
return
self.dump(address)
def reset_entry(self):
self.entry.set_text("%06X" % self.first)
def get(self):
return self.first
def get_memory_label(self):
return self.memory
def get_address_entry(self):
return self.entry
def get_follow_pc(self):
return self.follow_pc
def set_follow_pc(self, follow_pc):
self.follow_pc = follow_pc
def get_lines(self):
return self.lines
def set_lines(self, lines):
self.lines = lines
def set_dumpmode(self, mode):
self.dumpmode = mode
self.dump()
def dump(self, address = None, move_idx = 0):
if self.dumpmode == Constants.REGISTERS:
output = self._get_registers()
self.memory.set_label("".join(output))
return
if not address:
if not self.first:
self._get_registers()
address = self.first
if not address:
print("ERROR: address needed")
return
if self.dumpmode == Constants.MEMDUMP:
output = self._get_memdump(address, move_idx)
elif self.dumpmode == Constants.DISASM:
output = self._get_disasm(address, move_idx)
else:
print("ERROR: unknown dumpmode:", self.dumpmode)
return
self.memory.set_label("".join(output))
if move_idx:
self.reset_entry()
def _get_registers(self):
self.hatari.debug_command("r")
output = self.hatari.get_lines(self.debug_output)
if not self.first:
# 2nd last line has first PC in 1st column, last line next PC in 2nd column
self.second = int(output[-1][output[-1].find(":")+2:], 16)
# OldUAE CPU core has ':' in both
offset = output[-2].find(":")
if offset < 0:
# WinUAE CPU core only in one
offset = output[-2].find(" ")
if offset < 0:
print("ERROR: unable to parse register dump line:\n\t'%s'", output[-2])
return output
self.first = int(output[-2][:offset], 16)
self.reset_entry()
return output
def _get_memdump(self, address, move_idx):
linewidth = 16
screenful = self.lines*linewidth
# no move, left/right, up/down, page up/down (no overlap)
offsets = [0, 2, linewidth, screenful]
offset = offsets[abs(move_idx)]
if move_idx < 0:
address -= offset
else:
address += offset
self._set_clamped(address, address+screenful)
self.hatari.debug_command("m $%06x-$%06x" % (self.first, self.last))
# get & set debugger command results
output = self.hatari.get_lines(self.debug_output)
self.second = address + linewidth
return output
def _get_disasm(self, address, move_idx):
# TODO: uses brute force i.e. ask for more lines that user has
# requested to be sure that the window is filled, assuming
# 6 bytes is largest possible instruction+args size
# (I don't remember anymore my m68k asm...)
screenful = 6*self.lines
# no move, left/right, up/down, page up/down
offsets = [0, 2, 4, screenful]
offset = offsets[abs(move_idx)]
# force one line of overlap in page up/down
if move_idx < 0:
address -= offset
if address < 0:
address = 0
if move_idx == -Constants.MOVE_MAX and self.second:
screenful = self.second - address
else:
if move_idx == Constants.MOVE_MED and self.second:
address = self.second
elif move_idx == Constants.MOVE_MAX and self.last:
address = self.last
else:
address += offset
self._set_clamped(address, address+screenful)
self.hatari.debug_command("d $%06x-$%06x" % (self.first, self.last))
# get & set debugger command results
output = self.hatari.get_lines(self.debug_output)
# cut output to desired length and check new addresses
if len(output) > self.lines:
if move_idx < 0:
output = output[-self.lines:]
else:
output = output[:self.lines]
# with disasm need to re-get the addresses from the output
self.first = int(output[0][1:output[0].find(":")], 16)
self.second = int(output[1][1:output[1].find(":")], 16)
self.last = int(output[-1][1:output[-1].find(":")], 16)
return output
def _set_clamped(self, first, last):
"set_clamped(first,last), clamp addresses to valid address range and set them"
assert(first < last)
if first < 0:
last = last-first
first = 0
if last > 0xffffff:
first = 0xffffff - (last-first)
last = 0xffffff
self.first = first
self.last = last
# the Hatari debugger UI class and methods
class HatariDebugUI:
def __init__(self, hatariobj, do_destroy = False):
self.address = MemoryAddress(hatariobj)
self.hatari = hatariobj
# set when needed/created
self.dialog_load = None
self.dialog_save = None
self.dialog_options = None
# set when UI created
self.keys = None
self.stop_button = None
# set on option load
self.config = None
self.load_options()
# UI initialization/creation
self.window = self.create_ui("Hatari Debug UI", do_destroy)
def create_ui(self, title, do_destroy):
# buttons at top
hbox1 = gtk.HBox()
self.create_top_buttons(hbox1)
# disasm/memory dump at the middle
align = gtk.Alignment()
# top, bottom, left, right padding
align.set_padding(8, 0, 8, 8)
align.add(self.address.get_memory_label())
# buttons at bottom
hbox2 = gtk.HBox()
self.create_bottom_buttons(hbox2)
# their container
vbox = gtk.VBox()
vbox.pack_start(hbox1, False)
vbox.pack_start(align, True, True)
vbox.pack_start(hbox2, False)
# and the window for all of this
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_events(gtk.gdk.KEY_RELEASE_MASK)
window.connect("key_release_event", self.key_event_cb)
if do_destroy:
window.connect("delete_event", self.quit)
else:
window.connect("delete_event", self.hide)
window.set_icon_from_file(UInfo.icon)
window.set_title(title)
window.add(vbox)
return window
def create_top_buttons(self, box):
self.stop_button = create_toggle("Stop", self.stop_cb)
box.add(self.stop_button)
monitor = create_button("Monitor...", self.monitor_cb)
box.add(monitor)
buttons = (
("<<<", "Page_Up", -Constants.MOVE_MAX),
("<<", "Up", -Constants.MOVE_MED),
("<", "Left", -Constants.MOVE_MIN),
(">", "Right", Constants.MOVE_MIN),
(">>", "Down", Constants.MOVE_MED),
(">>>", "Page_Down", Constants.MOVE_MAX)
)
self.keys = {}
for label, keyname, offset in buttons:
button = create_button(label, self.set_address_offset, offset)
keyval = gtk.gdk.keyval_from_name(keyname)
self.keys[keyval] = offset
box.add(button)
# to middle of <<>> buttons
address_entry = self.address.get_address_entry()
box.pack_start(address_entry, False)
box.reorder_child(address_entry, 5)
def create_bottom_buttons(self, box):
radios = (
("Registers", Constants.REGISTERS),
("Memdump", Constants.MEMDUMP),
("Disasm", Constants.DISASM)
)
group = None
for label, mode in radios:
button = gtk.RadioButton(group, label)
if not group:
group = button
button.connect("toggled", self.dumpmode_cb, mode)
button.unset_flags(gtk.CAN_FOCUS)
box.add(button)
group.set_active(True)
dialogs = (
("Memload...", self.memload_cb),
("Memsave...", self.memsave_cb),
("Options...", self.options_cb)
)
for label, cb in dialogs:
button = create_button(label, cb)
box.add(button)
def stop_cb(self, widget):
if widget.get_active():
self.hatari.pause()
self.address.clear()
self.address.dump()
else:
self.hatari.unpause()
def dumpmode_cb(self, widget, mode):
if widget.get_active():
self.address.set_dumpmode(mode)
def key_event_cb(self, widget, event):
if event.keyval in self.keys:
self.address.dump(None, self.keys[event.keyval])
def set_address_offset(self, widget, move_idx):
self.address.dump(None, move_idx)
def monitor_cb(self, widget):
TodoDialog(self.window).run("add register / memory address range monitor window.")
def memload_cb(self, widget):
if not self.dialog_load:
self.dialog_load = LoadDialog(self.window)
(filename, address) = self.dialog_load.run(self.address.get())
if filename and address:
self.hatari.debug_command("l %s $%06x" % (filename, address))
def memsave_cb(self, widget):
if not self.dialog_save:
self.dialog_save = SaveDialog(self.window)
(filename, address, length) = self.dialog_save.run(self.address.get())
if filename and address and length:
self.hatari.debug_command("s %s $%06x $%06x" % (filename, address, length))
def options_cb(self, widget):
if not self.dialog_options:
self.dialog_options = OptionsDialog(self.window)
old_lines = self.config.get("[General]", "nLines")
old_follow_pc = self.config.get("[General]", "bFollowPC")
lines, follow_pc = self.dialog_options.run(old_lines, old_follow_pc)
if lines != old_lines:
self.config.set("[General]", "nLines", lines)
self.address.set_lines(lines)
if follow_pc != old_follow_pc:
self.config.set("[General]", "bFollowPC", follow_pc)
self.address.set_follow_pc(follow_pc)
def load_options(self):
# TODO: move config to MemoryAddress class?
# (depends on how monitoring of addresses should work)
lines = self.address.get_lines()
follow_pc = self.address.get_follow_pc()
miss_is_error = False # needed for adding windows
defaults = {
"[General]": {
"nLines": lines,
"bFollowPC": follow_pc
}
}
userconfdir = ".hatari"
config = ConfigStore(userconfdir, defaults, miss_is_error)
configpath = config.get_filepath("debugui.cfg")
config.load(configpath) # set defaults
try:
self.address.set_lines(config.get("[General]", "nLines"))
self.address.set_follow_pc(config.get("[General]", "bFollowPC"))
except (KeyError, AttributeError):
ErrorDialog(None).run("Debug UI configuration mismatch!\nTry again after removing: '%s'." % configpath)
self.config = config
def save_options(self):
self.config.save()
def show(self):
self.stop_button.set_active(True)
self.window.show_all()
self.window.deiconify()
def hide(self, widget, arg):
self.window.hide()
self.stop_button.set_active(False)
self.save_options()
return True
def quit(self, widget, arg):
KillDialog(self.window).run(self.hatari)
gtk.main_quit()
def main():
import sys
from hatari import Hatari
hatariobj = Hatari()
if len(sys.argv) > 1:
if sys.argv[1] in ("-h", "--help"):
print("usage: %s [hatari options]" % os.path.basename(sys.argv[0]))
return
args = sys.argv[1:]
else:
args = None
hatariobj.run(args)
info = UInfo()
debugui = HatariDebugUI(hatariobj, True)
debugui.window.show_all()
gtk.main()
debugui.save_options()
if __name__ == "__main__":
main()

1024
python-ui/dialogs.py Normal file

File diff suppressed because it is too large Load Diff

32
python-ui/gentypes.py Executable file
View File

@ -0,0 +1,32 @@
#!/usr/bin/env python
#
# Utility to generate from Hatari C-code Python code for mapping
# Hatari configuration variable names and types of those variables.
#
# Copyright (C) 2012 by Eero Tamminen
#
# 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.
import os, re, sys
# match first two items (variable name and type) from lines like:
# { "bConfirmQuit", Bool_Tag, &ConfigureParams.Log.bConfirmQuit }
reg = re.compile("\"([a-zA-Z0-9_]+)\",\s*([BIS][a-z]+)_Tag\s*,")
print "# content generated by %s" % os.path.basename(sys.argv[0])
print "conftypes = {"
for line in sys.stdin.readlines():
match = reg.search(line)
if match:
print """ "%s": "%s",""" % match.groups()
print "}"

BIN
python-ui/hatari-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

BIN
python-ui/hatari-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

903
python-ui/hatari.py Normal file
View File

@ -0,0 +1,903 @@
#!/usr/bin/env python
#
# Classes for Hatari emulator instance and mapping its congfiguration
# variables with its command line option.
#
# Copyright (C) 2008-2015 by Eero Tamminen
#
# 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.
import os
import sys
import time
import signal
import socket
import select
from config import ConfigStore
# Running Hatari instance
class Hatari:
"running hatari instance and methods for communicating with it"
basepath = "/tmp/hatari-ui-" + str(os.getpid())
logpath = basepath + ".log"
tracepath = basepath + ".trace"
debugpath = basepath + ".debug"
controlpath = basepath + ".socket"
server = None # singleton due to path being currently one per user
def __init__(self, hataribin = None):
# collect hatari process zombies without waitpid()
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
if hataribin:
self.hataribin = hataribin
else:
self.hataribin = "hatari"
self._create_server()
self.control = None
self.paused = False
self.pid = 0
def is_compatible(self):
"check Hatari compatibility and return error string if it's not"
error = "Hatari not found or it doesn't support the required --control-socket option!"
pipe = os.popen(self.hataribin + " -h")
for line in pipe.readlines():
if line.find("--control-socket") >= 0:
error = None
break
try:
pipe.close()
except IOError:
pass
return error
def save_config(self):
os.popen(self.hataribin + " --saveconfig")
def _create_server(self):
if self.server:
return
self.server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
if os.path.exists(self.controlpath):
os.unlink(self.controlpath)
self.server.bind(self.controlpath)
self.server.listen(1)
def _send_message(self, msg):
if self.control:
self.control.send(msg)
return True
else:
print("ERROR: no Hatari (control socket)")
return False
def change_option(self, option):
"change_option(option), changes given Hatari cli option"
return self._send_message("hatari-option %s\n" % option)
def set_path(self, key, path):
"set_path(key, path), sets path with given key"
return self._send_message("hatari-path %s %s\n" % (key, path))
def set_device(self, device, enabled):
# needed because CLI options cannot disable devices, only enable
"set_path(device, enabled), sets whether given device is enabled or not"
if enabled:
return self._send_message("hatari-enable %s\n" % device)
else:
return self._send_message("hatari-disable %s\n" % device)
def trigger_shortcut(self, shortcut):
"trigger_shortcut(shortcut), triggers given Hatari (keyboard) shortcut"
return self._send_message("hatari-shortcut %s\n" % shortcut)
def insert_event(self, event):
"insert_event(event), synthetizes given key/mouse Atari event"
return self._send_message("hatari-event %s\n" % event)
def debug_command(self, cmd):
"debug_command(command), runs given Hatari debugger command"
return self._send_message("hatari-debug %s\n" % cmd)
def pause(self):
"pause(), pauses Hatari emulation"
return self._send_message("hatari-stop\n")
def unpause(self):
"unpause(), continues Hatari emulation"
return self._send_message("hatari-cont\n")
def _open_output_file(self, hataricommand, option, path):
if os.path.exists(path):
os.unlink(path)
# TODO: why fifo doesn't work properly (blocks forever on read or
# reads only byte at the time and stops after first newline)?
#os.mkfifo(path)
#raw_input("attach strace now, then press Enter\n")
# ask Hatari to open/create the requested output file...
hataricommand("%s %s" % (option, path))
wait = 0.025
# ...and wait for it to appear before returning it
for i in range(0, 8):
time.sleep(wait)
if os.path.exists(path):
return open(path, "r")
wait += wait
return None
def open_debug_output(self):
"open_debug_output() -> file, opens Hatari debugger output file"
return self._open_output_file(self.debug_command, "f", self.debugpath)
def open_trace_output(self):
"open_trace_output() -> file, opens Hatari tracing output file"
return self._open_output_file(self.change_option, "--trace-file", self.tracepath)
def open_log_output(self):
"open_trace_output() -> file, opens Hatari debug log file"
return self._open_output_file(self.change_option, "--log-file", self.logpath)
def get_lines(self, fileobj):
"get_lines(file) -> list of lines readable from given Hatari output file"
# wait until data is available, then wait for some more
# and only then the data can be read, otherwise its old
print("Request&wait data from Hatari...")
select.select([fileobj], [], [])
time.sleep(0.1)
print("...read the data lines")
lines = fileobj.readlines()
print("".join(lines))
return lines
def enable_embed_info(self):
"enable_embed_info(), request embedded Hatari window ID change information"
self._send_message("hatari-embed-info\n")
def get_embed_info(self):
"get_embed_info() -> (width, height), get embedded Hatari window size"
width, height = self.control.recv(12).split("x")
return (int(width), int(height))
def get_control_socket(self):
"get_control_socket() -> socket which can be checked for embed ID changes"
return self.control
def is_running(self):
"is_running() -> bool, True if Hatari is running, False otherwise"
if not self.pid:
return False
try:
os.waitpid(self.pid, os.WNOHANG)
except OSError as value:
print("Hatari PID %d had exited in the meanwhile:\n\t%s" % (self.pid, value))
self.pid = 0
if self.control:
self.control.close()
self.control = None
return False
return True
def run(self, extra_args = None, parent_win = None):
"run([parent window][,embedding args]), runs Hatari"
# if parent_win given, embed Hatari to it
pid = os.fork()
if pid < 0:
print("ERROR: fork()ing Hatari failed!")
return
if pid:
# in parent
self.pid = pid
if self.server:
print("WAIT hatari to connect to control socket...")
(self.control, addr) = self.server.accept()
print("connected!")
else:
# child runs Hatari
env = os.environ
if parent_win:
self._set_embed_env(env, parent_win)
# callers need to take care of confirming quitting
args = [self.hataribin, "--confirm-quit", "off"]
if self.server:
args += ["--control-socket", self.controlpath]
if extra_args:
args += extra_args
print("RUN:", args)
os.execvpe(self.hataribin, args, env)
def _set_embed_env(self, env, parent_win):
if sys.platform == 'win32':
win_id = parent_win.handle
else:
win_id = parent_win.xid
# tell SDL to use given widget's window
#env["SDL_WINDOWID"] = str(win_id)
# above is broken: when SDL uses a window it hasn't created itself,
# it for some reason doesn't listen to any events delivered to that
# window nor implements XEMBED protocol to get them in a way most
# friendly to embedder:
# http://standards.freedesktop.org/xembed-spec/latest/
#
# Instead we tell hatari to reparent itself after creating
# its own window into this program widget window
env["PARENT_WIN_ID"] = str(win_id)
def kill(self):
"kill(), kill Hatari if it's running"
if self.is_running():
os.kill(self.pid, signal.SIGKILL)
print("killed hatari with PID %d" % self.pid)
self.pid = 0
if self.control:
self.control.close()
self.control = None
# Mapping of requested values both to Hatari configuration
# and command line options.
#
# By default this doesn't allow setting any other configuration
# variables than the ones that were read from the configuration
# file i.e. you get an exception if configuration variables
# don't match to current Hatari. So before using this the current
# Hatari configuration should have been saved at least once.
#
# Because of some inconsistencies in the values (see e.g. sound),
# this cannot just do these according to some mapping table, but
# it needs actual method for (each) setting.
class HatariConfigMapping(ConfigStore):
_paths = {
"memauto": ("[Memory]", "szAutoSaveFileName", "Automatic memory snapshot"),
"memsave": ("[Memory]", "szMemoryCaptureFileName", "Manual memory snapshot"),
"midiin": ("[Midi]", "sMidiInFileName", "Midi input"),
"midiout": ("[Midi]", "sMidiOutFileName", "Midi output"),
"rs232in": ("[RS232]", "szInFileName", "RS232 I/O input"),
"rs232out": ("[RS232]", "szOutFileName", "RS232 I/O output"),
"printout": ("[Printer]", "szPrintToFileName", "Printer output"),
"soundout": ("[Sound]", "szYMCaptureFileName", "Sound output")
}
"access methods to Hatari configuration file variables and command line options"
def __init__(self, hatari):
userconfdir = ".hatari"
ConfigStore.__init__(self, userconfdir)
conffilename = "hatari.cfg"
self.load(self.get_filepath(conffilename))
self._hatari = hatari
self._lock_updates = False
self._desktop_w = 0
self._desktop_h = 0
self._options = []
def validate(self):
"exception is thrown if the loaded configuration isn't compatible"
for method in dir(self):
if '_' not in method:
continue
# check class getters
starts = method[:method.find("_")]
if starts != "get":
continue
# but ignore getters for other things than config
ends = method[method.rfind("_")+1:]
if ends in ("types", "names", "values", "changes", "checkpoint", "filepath"):
continue
if ends in ("floppy", "joystick"):
# use port '0' for checks
getattr(self, method)(0)
else:
getattr(self, method)()
def _change_option(self, option, quoted = None):
"handle option changing, and quote spaces for quoted part of it"
if quoted:
option = "%s %s" % (option, quoted.replace(" ", "\\ "))
if self._lock_updates:
self._options.append(option)
else:
self._hatari.change_option(option)
def lock_updates(self):
"lock_updates(), collect Hatari configuration changes"
self._lock_updates = True
def flush_updates(self):
"flush_updates(), apply collected Hatari configuration changes"
self._lock_updates = False
if not self._options:
return
self._hatari.change_option(" ".join(self._options))
self._options = []
# ------------ paths ---------------
def get_paths(self):
paths = []
for key, item in self._paths.items():
paths.append((key, self.get(item[0], item[1]), item[2]))
return paths
def set_paths(self, paths):
for key, path in paths:
self.set(self._paths[key][0], self._paths[key][1], path)
self._hatari.set_path(key, path)
# ------------ midi ---------------
def get_midi(self):
return self.get("[Midi]", "bEnableMidi")
def set_midi(self, value):
self.set("[Midi]", "bEnableMidi", value)
self._hatari.set_device("midi", value)
# ------------ printer ---------------
def get_printer(self):
return self.get("[Printer]", "bEnablePrinting")
def set_printer(self, value):
self.set("[Printer]", "bEnablePrinting", value)
self._hatari.set_device("printer", value)
# ------------ RS232 ---------------
def get_rs232(self):
return self.get("[RS232]", "bEnableRS232")
def set_rs232(self, value):
self.set("[RS232]", "bEnableRS232", value)
self._hatari.set_device("rs232", value)
# ------------ machine ---------------
def get_machine_types(self):
return ("ST", "STE", "TT", "Falcon")
def get_machine(self):
return self.get("[System]", "nMachineType")
def set_machine(self, value):
self.set("[System]", "nMachineType", value)
self._change_option("--machine %s" % ("st", "ste", "tt", "falcon")[value])
# ------------ CPU level ---------------
def get_cpulevel_types(self):
return ("68000", "68010", "68020", "68EC030+FPU", "68040")
def get_cpulevel(self):
return self.get("[System]", "nCpuLevel")
def set_cpulevel(self, value):
self.set("[System]", "nCpuLevel", value)
self._change_option("--cpulevel %d" % value)
# ------------ CPU clock ---------------
def get_cpuclock_types(self):
return ("8 MHz", "16 MHz", "32 MHz")
def get_cpuclock(self):
clocks = {8:0, 16: 1, 32:2}
return clocks[self.get("[System]", "nCpuFreq")]
def set_cpuclock(self, value):
clocks = [8, 16, 32]
if value < 0 or value > 2:
print("WARNING: CPU clock idx %d, clock fixed to 8 Mhz" % value)
value = 8
else:
value = clocks[value]
self.set("[System]", "nCpuFreq", value)
self._change_option("--cpuclock %d" % value)
# ------------ DSP type ---------------
def get_dsp_types(self):
return ("None", "Dummy", "Emulated")
def get_dsp(self):
return self.get("[System]", "nDSPType")
def set_dsp(self, value):
self.set("[System]", "nDSPType", value)
self._change_option("--dsp %s" % ("none", "dummy", "emu")[value])
# ------------ compatible ---------------
def get_compatible(self):
return self.get("[System]", "bCompatibleCpu")
def set_compatible(self, value):
self.set("[System]", "bCompatibleCpu", value)
self._change_option("--compatible %s" % str(value))
# ------------ Timer-D ---------------
def get_timerd(self):
return self.get("[System]", "bPatchTimerD")
def set_timerd(self, value):
self.set("[System]", "bPatchTimerD", value)
self._change_option("--timer-d %s" % str(value))
# ------------ RTC ---------------
def get_rtc(self):
return self.get("[System]", "bRealTimeClock")
def set_rtc(self, value):
self.set("[System]", "bRealTimeClock", value)
self._change_option("--rtc %s" % str(value))
# ------------ fastforward ---------------
def get_fastforward(self):
return self.get("[System]", "bFastForward")
def set_fastforward(self, value):
self.set("[System]", "bFastForward", value)
self._change_option("--fast-forward %s" % str(value))
# ------------ sound ---------------
def get_sound_values(self):
# 48kHz, 44.1kHz and STE/TT/Falcon DMA 50066Hz divisable values
return ("6000", "6258", "8000", "11025", "12000", "12517",
"16000", "22050", "24000", "25033", "32000",
"44100", "48000", "50066")
def get_sound(self):
enabled = self.get("[Sound]", "bEnableSound")
hz = str(self.get("[Sound]", "nPlaybackFreq"))
idx = self.get_sound_values().index(hz)
return (enabled, idx)
def set_sound(self, enabled, idx):
# map get_sound_values() index to Hatari config
hz = self.get_sound_values()[idx]
self.set("[Sound]", "nPlaybackFreq", int(hz))
self.set("[Sound]", "bEnableSound", enabled)
# and to cli option
if enabled:
self._change_option("--sound %s" % hz)
else:
self._change_option("--sound off")
def get_ymmixer_types(self):
return ("linear", "table", "model")
def get_ymmixer(self):
# values for types are start from 1, not 0
return self.get("[Sound]", "YmVolumeMixing")-1
def set_ymmixer(self, value):
self.set("[Sound]", "YmVolumeMixing", value+1)
self._change_option("--ym-mixing %s" % self.get_ymmixer_types()[value])
def get_bufsize(self):
return self.get("[Sound]", "nSdlAudioBufferSize")
def set_bufsize(self, value):
value = int(value)
if value < 10: value = 10
if value > 100: value = 100
self.set("[Sound]", "nSdlAudioBufferSize", value)
self._change_option("--sound-buffer-size %d" % value)
def get_sync(self):
return self.get("[Sound]", "bEnableSoundSync")
def set_sync(self, value):
self.set("[Sound]", "bEnableSoundSync", value)
self._change_option("--sound-sync %s" % str(value))
def get_mic(self):
return self.get("[Sound]", "bEnableMicrophone")
def set_mic(self, value):
self.set("[Sound]", "bEnableMicrophone", value)
self._change_option("--mic %s" % str(value))
# ----------- joystick --------------
def get_joystick_types(self):
return ("Disabled", "Real joystick", "Keyboard")
def get_joystick_names(self):
return (
"ST Joystick 0",
"ST Joystick 1",
"STE Joypad A",
"STE Joypad B",
"Parport stick 1",
"Parport stick 2"
)
def get_joystick(self, port):
# return index to get_joystick_values() array
return self.get("[Joystick%d]" % port, "nJoystickMode")
def set_joystick(self, port, value):
# map get_sound_values() index to Hatari config
self.set("[Joystick%d]" % port, "nJoystickMode", value)
joytype = ("none", "real", "keys")[value]
self._change_option("--joy%d %s" % (port, joytype))
# ------------ floppy handling ---------------
def get_floppydir(self):
return self.get("[Floppy]", "szDiskImageDirectory")
def set_floppydir(self, path):
return self.set("[Floppy]", "szDiskImageDirectory", path)
def get_floppy(self, drive):
return self.get("[Floppy]", "szDisk%cFileName" % ("A", "B")[drive])
def set_floppy(self, drive, filename):
self.set("[Floppy]", "szDisk%cFileName" % ("A", "B")[drive], filename)
self._change_option("--disk-%c" % ("a", "b")[drive], str(filename))
def get_floppy_drives(self):
return (self.get("[Floppy]", "EnableDriveA"), self.get("[Floppy]", "EnableDriveB"))
def set_floppy_drives(self, drives):
idx = 0
for drive in ("A", "B"):
value = drives[idx]
self.set("[Floppy]", "EnableDrive%c" % drive, value)
self._change_option("--drive-%c %s" % (drive.lower(), str(value)))
idx += 1
def get_fastfdc(self):
return self.get("[Floppy]", "FastFloppy")
def set_fastfdc(self, value):
self.set("[Floppy]", "FastFloppy", value)
self._change_option("--fastfdc %s" % str(value))
def get_doublesided(self):
driveA = self.get("[Floppy]", "DriveA_NumberOfHeads")
driveB = self.get("[Floppy]", "DriveB_NumberOfHeads")
if driveA > 1 or driveB > 1:
return True
return False
def set_doublesided(self, value):
if value: sides = 2
else: sides = 1
for drive in ("A", "B"):
self.set("[Floppy]", "Drive%c_NumberOfHeads" % drive, sides)
self._change_option("--drive-%c-heads %d" % (drive.lower(), sides))
# ------------- disk protection -------------
def get_protection_types(self):
return ("Off", "On", "Auto")
def get_floppy_protection(self):
return self.get("[Floppy]", "nWriteProtection")
def get_hd_protection(self):
return self.get("[HardDisk]", "nWriteProtection")
def set_floppy_protection(self, value):
self.set("[Floppy]", "nWriteProtection", value)
self._change_option("--protect-floppy %s" % self.get_protection_types()[value])
def set_hd_protection(self, value):
self.set("[HardDisk]", "nWriteProtection", value)
self._change_option("--protect-hd %s" % self.get_protection_types()[value])
# ------------ GEMDOS HD (dir) emulation ---------------
def get_hd_cases(self):
return ("No conversion", "Upper case", "Lower case")
def get_hd_case(self):
return self.get("[HardDisk]", "nGemdosCase")
def set_hd_case(self, value):
values = ("off", "upper", "lower")
self.set("[HardDisk]", "nGemdosCase", value)
self._change_option("--gemdos-case %s" % values[value])
def get_hd_drives(self):
return ['skip ACSI/IDE'] + [("%c:" % x) for x in range(ord('C'), ord('Z')+1)]
def get_hd_drive(self):
return self.get("[HardDisk]", "nGemdosDrive") + 1
def set_hd_drive(self, value):
value -= 1
self.set("[HardDisk]", "nGemdosDrive", value)
drive = chr(ord('C') + value)
if value < 0:
drive = "skip"
self._change_option("--gemdos-drive %s" % drive)
def get_hd_dir(self):
self.get("[HardDisk]", "bUseHardDiskDirectory") # for validation
return self.get("[HardDisk]", "szHardDiskDirectory")
def set_hd_dir(self, dirname):
if dirname and os.path.isdir(dirname):
self.set("[HardDisk]", "bUseHardDiskDirectory", True)
self.set("[HardDisk]", "szHardDiskDirectory", dirname)
self._change_option("--harddrive", str(dirname))
# ------------ ACSI HD (file) ---------------
def get_acsi_image(self):
self.get("[HardDisk]", "bUseHardDiskImage") # for validation
return self.get("[HardDisk]", "szHardDiskImage")
def set_acsi_image(self, filename):
if filename and os.path.isfile(filename):
self.set("[HardDisk]", "bUseHardDiskImage", True)
self.set("[HardDisk]", "szHardDiskImage", filename)
self._change_option("--acsi", str(filename))
# ------------ IDE master (file) ---------------
def get_idemaster_image(self):
self.get("[HardDisk]", "bUseIdeMasterHardDiskImage") # for validation
return self.get("[HardDisk]", "szIdeMasterHardDiskImage")
def set_idemaster_image(self, filename):
if filename and os.path.isfile(filename):
self.set("[HardDisk]", "bUseIdeMasterHardDiskImage", True)
self.set("[HardDisk]", "szIdeMasterHardDiskImage", filename)
self._change_option("--ide-master", str(filename))
# ------------ IDE slave (file) ---------------
def get_ideslave_image(self):
self.get("[HardDisk]", "bUseIdeSlaveHardDiskImage") # for validation
return self.get("[HardDisk]", "szIdeSlaveHardDiskImage")
def set_ideslave_image(self, filename):
if filename and os.path.isfile(filename):
self.set("[HardDisk]", "bUseIdeSlaveHardDiskImage", True)
self.set("[HardDisk]", "szIdeSlaveHardDiskImage", filename)
self._change_option("--ide-slave", str(filename))
# ------------ TOS ROM ---------------
def get_tos(self):
return self.get("[ROM]", "szTosImageFileName")
def set_tos(self, filename):
self.set("[ROM]", "szTosImageFileName", filename)
self._change_option("--tos", str(filename))
# ------------ memory ---------------
def get_memory_names(self):
# empty item in list shouldn't be shown, filter them out
return ("512kB", "1MB", "2MB", "4MB", "8MB", "14MB")
def get_memory(self):
"return index to what get_memory_names() returns"
sizemap = (0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5)
memsize = self.get("[Memory]", "nMemorySize")
if memsize >= 0 and memsize < len(sizemap):
return sizemap[memsize]
return 1 # default = 1BM
def set_memory(self, idx):
# map memory item index to memory size
sizemap = (0, 1, 2, 4, 8, 14)
if idx >= 0 and idx < len(sizemap):
memsize = sizemap[idx]
else:
memsize = 1
self.set("[Memory]", "nMemorySize", memsize)
self._change_option("--memsize %d" % memsize)
def get_ttram(self):
return self.get("[Memory]", "nTTRamSize")
def set_ttram(self, memsize):
# guarantee correct type (Gtk float -> config int)
memsize = int(memsize)
self.set("[Memory]", "nTTRamSize", memsize)
self._change_option("--ttram %d" % memsize)
if memsize:
# TT-RAM need 32-bit addressing (i.e. disable 24-bit)
self.set("[System]", "bAddressSpace24", False)
self._change_option("--addr24 off")
else:
# switch 24-bit addressing back for compatibility
self.set("[System]", "bAddressSpace24", True)
self._change_option("--addr24 on", False)
# ------------ monitor ---------------
def get_monitor_types(self):
return ("Mono", "RGB", "VGA", "TV")
def get_monitor(self):
return self.get("[Screen]", "nMonitorType")
def set_monitor(self, value):
self.set("[Screen]", "nMonitorType", value)
self._change_option("--monitor %s" % ("mono", "rgb", "vga", "tv")[value])
# ------------ frameskip ---------------
def get_frameskip_names(self):
return (
"Disabled",
"1 frame",
"2 frames",
"3 frames",
"4 frames",
"Automatic"
)
def get_frameskip(self):
fs = self.get("[Screen]", "nFrameSkips")
if fs < 0 or fs > 5:
return 5
return fs
def set_frameskip(self, value):
value = int(value) # guarantee correct type
self.set("[Screen]", "nFrameSkips", value)
self._change_option("--frameskips %d" % value)
# ------------ VBL slowdown ---------------
def get_slowdown_names(self):
return ("Disabled", "2x", "3x", "4x", "5x", "6x", "8x")
def set_slowdown(self, value):
value = 1 + int(value)
self._change_option("--slowdown %d" % value)
# ------------ spec512 ---------------
def get_spec512threshold(self):
return self.get("[Screen]", "nSpec512Threshold")
def set_spec512threshold(self, value):
value = int(value) # guarantee correct type
self.set("[Screen]", "nSpec512Threshold", value)
self._change_option("--spec512 %d" % value)
# --------- keep desktop res -----------
def get_desktop(self):
return self.get("[Screen]", "bKeepResolution")
def set_desktop(self, value):
self.set("[Screen]", "bKeepResolution", value)
self._change_option("--desktop %s" % str(value))
# --------- keep desktop res - st ------
def get_desktop_st(self):
return self.get("[Screen]", "bKeepResolutionST")
def set_desktop_st(self, value):
self.set("[Screen]", "bKeepResolutionST", value)
self._change_option("--desktop-st %s" % str(value))
# ------------ force max ---------------
def get_force_max(self):
return self.get("[Screen]", "bForceMax")
def set_force_max(self, value):
self.set("[Screen]", "bForceMax", value)
self._change_option("--force-max %s" % str(value))
# ------------ show borders ---------------
def get_borders(self):
return self.get("[Screen]", "bAllowOverscan")
def set_borders(self, value):
self.set("[Screen]", "bAllowOverscan", value)
self._change_option("--borders %s" % str(value))
# ------------ show statusbar ---------------
def get_statusbar(self):
return self.get("[Screen]", "bShowStatusbar")
def set_statusbar(self, value):
self.set("[Screen]", "bShowStatusbar", value)
self._change_option("--statusbar %s" % str(value))
# ------------ crop statusbar ---------------
def get_crop(self):
return self.get("[Screen]", "bCrop")
def set_crop(self, value):
self.set("[Screen]", "bCrop", value)
self._change_option("--crop %s" % str(value))
# ------------ show led ---------------
def get_led(self):
return self.get("[Screen]", "bShowDriveLed")
def set_led(self, value):
self.set("[Screen]", "bShowDriveLed", value)
self._change_option("--drive-led %s" % str(value))
# ------------ monitor aspect ratio ---------------
def get_aspectcorrection(self):
return self.get("[Screen]", "bAspectCorrect")
def set_aspectcorrection(self, value):
self.set("[Screen]", "bAspectCorrect", value)
self._change_option("--aspect %s" % str(value))
# ------------ max window size ---------------
def set_desktop_size(self, w, h):
self._desktop_w = w
self._desktop_h = h
def get_desktop_size(self):
return (self._desktop_w, self._desktop_h)
def get_max_size(self):
w = self.get("[Screen]", "nMaxWidth")
h = self.get("[Screen]", "nMaxHeight")
# default to desktop size?
if not (w or h):
w = self._desktop_w
h = self._desktop_h
return (w, h)
def set_max_size(self, w, h):
# guarantee correct type (Gtk float -> config int)
w = int(w); h = int(h)
self.set("[Screen]", "nMaxWidth", w)
self.set("[Screen]", "nMaxHeight", h)
self._change_option("--max-width %d" % w)
self._change_option("--max-height %d" % h)
# TODO: remove once UI doesn't need this anymore
def set_zoom(self, value):
print("Just setting Zoom, configuration doesn't anymore have setting for this.")
if value:
zoom = 2
else:
zoom = 1
self._change_option("--zoom %d" % zoom)
# ------------ configured Hatari window size ---------------
def get_window_size(self):
if self.get("[Screen]", "bFullScreen"):
print("WARNING: don't start Hatari UI with fullscreened Hatari!")
# VDI resolution?
if self.get("[Screen]", "bUseExtVdiResolutions"):
width = self.get("[Screen]", "nVdiWidth")
height = self.get("[Screen]", "nVdiHeight")
return (width, height)
# window sizes for other than ST & STE can differ
if self.get("[System]", "nMachineType") not in (0, 1):
print("WARNING: neither ST nor STE machine, window size inaccurate!")
videl = True
else:
videl = False
# mono monitor?
if self.get_monitor() == 0:
return (640, 400)
# no, color
width = 320
height = 200
# statusbar?
if self.get_statusbar():
sbar = 12
height += sbar
else:
sbar = 0
# zoom?
maxw, maxh = self.get_max_size()
if 2*width <= maxw and 2*height <= maxh:
width *= 2
height *= 2
zoom = 2
else:
zoom = 1
# overscan borders?
if self.get_borders() and not videl:
# properly aligned borders on top of zooming
leftx = (maxw-width)/zoom
borderx = 2*(min(48,leftx/2)/16)*16
lefty = (maxh-height)/zoom
bordery = min(29+47, lefty)
width += zoom*borderx
height += zoom*bordery
return (width, height)

42
python-ui/hatariui Executable file
View File

@ -0,0 +1,42 @@
#!/bin/sh
#
# Don't modify the 'path' or 'conf' variable names or initial values,
# those will be replaced by Makefile when this script is installed.
path=${0%/*}
name=${0##*/}
if [ ! -e $path/$name.py ]; then
# Assume package has been relocated, try relative data directory:
path=${0%/*}/../share/hatari/hatariui
fi
# Assume hatari system configuration file dir is relative to hatariui dir
# (usually system config file isn't installed, but if defaults need to be
# configured differently from Hatari source code defaults, they're better
# done with system config file than patching sources).
conf=${path%/*}/../etc
# checked by hatari UI
export HATARI_SYSTEM_CONFDIR=$conf
# example setup for Hatari UI
$path/$name.py --right "about,|,run,pause,forward,|,reset,|,quit" --embed $*
exit 0
# test setup without embedding, duplicate toggles
$path/$name.py --top "about,run,pause,quit" \
--panel "Testpanel,pause,>,close" \
--bottom "sound,|,forward,pause,|,Testpanel" \
$*
exit 0
# test setup with embedding and all available controls
$path/$name.py --embed \
--top "about,|,run,pause,|,reset,debug,|,quit" \
--left "run,pause,reset,machine,about" \
--panel "Keys,F1=59,F2=60,F3=61,F4=62,F5=63,F6=64,F7=65,F8=66,F9=67,F10=68,>,Macro=Test,Undo=97,Help=98,Enter=114,>,close" \
--panel "Misc,|,forward,full,|,sound,>,shot,>,close" \
--bottom "forward,full,Misc,Keys,input,display,debug,trace" \
--right "forward,full,Misc,Keys,input,display,Help=98" \
$*
exit 0

227
python-ui/hatariui.1 Normal file
View File

@ -0,0 +1,227 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH "hatariui" "1" "2010-05-30" "Hatari" "Hatari UI"
.SH "NAME"
hatariui \- Python/Gtk UI for Hatari
.SH "SYNOPSIS"
.B hatariui | hatariui.py
.RI [options]
.RI [directory|diskimage|program]
.SH "DESCRIPTION"
.I hatariui
is a Python/Gtk UI for Hatari which can either embed the Hatari window
(on X11 systems) or run in a separate window. By default it provides
a normal application menu and some extra button for faster access to
fast\-forward etc. functionality, but these are fully configurable
with the command line options. While it lacks support for some of
the Hatari configuration options that Hatari's SDL GUI has, it also
supports some options that the Hatari's built\-in SDL UI doesn't.
.PP
Besides the UI configurability, some of the other advantages hatariui has
over the SDL interface included with Hatari itself are use of a normal
Gtk file selector with all of its features (directory shortcuts etc),
support for UTF\-8 (in file names) and in general blending better to
the user's desktop environment.
.PP
Additionally, Hatari can run while one uses UI configuration dialogs,
and it can stop Hatari completely to better save the battery on mobile
computers. For devices without a keyboard, it offers a a text input
dialog and one can configure (from command line) buttons for often used
strings.
.SH "HATARIUI / HATARIUI.PY"
.I hatariui
is actually a shell script wrapper for the hatariui.py Python script.
It's used to run the Python script with suitable options for default
usage and to set up the correct installation directory for the rest
of the Hatari UI Python scripts and data files.
.PP
Options below are actually for hatariui.py script. If you want to
change options given for it, modify the hatariui shell script or
make your own based on the installed one.
.\" following command line helps in updating the options:
.\" hatariui.py --help|sed -e 's/^\t\+/.TP\n.B /' -e 's/\t\+/\n/g' -e 's/-/\\-/g' >> hatariui.1
.SH "OPTIONS"
.TP
.B \-h, \-\-help
Hatari UI command line help
.TP
.B \-n, \-\-nomenu
Omit menubar from the window
.TP
.B \-e, \-\-embed
Embed Hatari window (to middle of controls)
.TP
.B \-f, \-\-fullscreen
Start in fullscreen
.TP
.B \-l, \-\-left <controls>
Add a toolbar at left
.TP
.B \-r, \-\-right <controls>
Add a toolbar at right
.TP
.B \-t, \-\-top <controls>
Add a toolbar at top
.TP
.B \-b, \-\-bottom <controls>
Add a toolbar at bottom
.TP
.B \-p, \-\-panel <name>,<controls>
Add a separate window with given name and controls
.PP
You can have only one toolbar on each side of the Hatari window.
Panels are separate windows and you can has as many of them as you wish.
For each panel you need to add a control with the name of the panel
(see "MyPanel" in examples).
.PP
Following controls can are available for toolbars and panels:
.TP
.B |
Separator between controls
.TP
.B >
Start next toolbar row in panel windows
.TP
.B compatibility
Hatari compatibility list
.TP
.B harddisk
Hard disk images and directories
.TP
.B reset
Warm or cold reset Hatari
.TP
.B sconfig
Save configuration
.TP
.B bugs
Report a bug
.TP
.B display
Display settings
.TP
.B authors
Hatari authors
.TP
.B forward
Whether to fast forward Hatari (needs fast machine)
.TP
.B debug
Activate Hatari debugger
.TP
.B quit
Quit Hatari UI
.TP
.B hatari
Hatari home page
.TP
.B recanim
Record animation
.TP
.B recsound
Record YM/Wav
.TP
.B mails
Hatari mailing lists
.TP
.B floppy
Floppy images
.TP
.B device
Toggle Midi, Printer, RS232 peripherals
.TP
.B load
Load emulation snapshot
.TP
.B lconfig
Load configuration
.TP
.B path
Device & save file paths
.TP
.B machine
Hatari st/e/tt/falcon configuration
.TP
.B todo
Hatari TODO
.TP
.B hatariui
Hatari UI home page
.TP
.B save
Save emulation snapshot
.TP
.B joystick
Joystick settings
.TP
.B trace
Hatari tracing setup
.TP
.B pause
Pause Hatari to save battery
.TP
.B about
Hatari UI information
.TP
.B release
Hatari release notes
.TP
.B full
Toggle whether Hatari is fullscreen
.TP
.B manual
Hatari manual
.TP
.B input
Simulate text input and mouse clicks
.TP
.B sound
Sound settings
.TP
.B changes
Latest Hatari changes
.TP
.B run
(Re\-)run Hatari
.TP
.B shot
Grab a screenshot
.TP
.B <panel name>
Button for the specified panel window
.TP
.B <name>=<string/code>
Synthetize string or single key <code>
.PP
If no options are given, the UI uses basic controls.
.SH "EXAMPLES"
Example on how to add top, right and bottom toolbars and a separate
"MyPanel" panel window:
.nf
hatariui.py \-\-embed \\
\-t "about,run,pause,quit" \\
\-p "MyPanel,Macro=Test,Undo=97,Help=98,>,F1=59,F2=60,>,close" \\
\-r "paste,debug,trace,machine,MyPanel" \\
\-b "sound,|,forward,|,fullscreen"
.fi
.PP
For more examples on Hatari UI options usage, see the hatariui shell
script.
.SH "SEE ALSO"
.IR hmsa (1),
.IR hatariui (1),
.IR hconsole (1)
.SH "COPYRIGHT"
Hatari UI is written by Eero Tamminen <oak at helsinkinet fi>.
.PP
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.
.PP
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.

View File

@ -0,0 +1,7 @@
[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Name=Hatari UI
Exec=hatariui
Icon=hatari

763
python-ui/hatariui.py Executable file
View File

@ -0,0 +1,763 @@
#!/usr/bin/env python
#
# A PyGtk UI for Hatari that can embed the Hatari emulator window.
#
# Requires PyGtk (python-gtk2) package and its dependencies to be present.
#
# Copyright (C) 2008-2011 by Eero Tamminen
#
# 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.
import os
import sys
import getopt
# use correct version of pygtk/gtk
import pygtk
pygtk.require('2.0')
import gtk
import gobject
from debugui import HatariDebugUI
from hatari import Hatari, HatariConfigMapping
from uihelpers import UInfo, UIHelp, create_button, create_toolbutton, \
create_toggle, HatariTextInsert, get_open_filename, get_save_filename
from dialogs import AboutDialog, TodoDialog, NoteDialog, ErrorDialog, \
InputDialog, KillDialog, QuitSaveDialog, ResetDialog, TraceDialog, \
FloppyDialog, HardDiskDialog, DisplayDialog, JoystickDialog, \
MachineDialog, PeripheralDialog, PathDialog, SoundDialog
# helper functions to match callback args
def window_hide_cb(window, arg):
window.hide()
return True
# ---------------------------------------------------------------
# Class with Hatari and configuration instances which methods are
# called to change those (with additional dialogs or directly).
# Owns the application window and socket widget embedding Hatari.
class UICallbacks:
tmpconfpath = os.path.expanduser("~/.hatari/.tmp.cfg")
def __init__(self):
# Hatari and configuration
self.hatari = Hatari()
error = self.hatari.is_compatible()
if error:
ErrorDialog(None).run(error)
sys.exit(-1)
self.config = HatariConfigMapping(self.hatari)
try:
self.config.validate()
except (KeyError, AttributeError):
NoteDialog(None).run("Loading Hatari configuration failed!\nRetrying after saving Hatari configuration.")
self.hatari.save_config()
self.config = HatariConfigMapping(self.hatari)
self.config.validate()
# windows are created when needed
self.mainwin = None
self.hatariwin = None
self.debugui = None
self.panels = {}
# dialogs are created when needed
self.aboutdialog = None
self.inputdialog = None
self.tracedialog = None
self.resetdialog = None
self.quitdialog = None
self.killdialog = None
self.floppydialog = None
self.harddiskdialog = None
self.displaydialog = None
self.joystickdialog = None
self.machinedialog = None
self.peripheraldialog = None
self.sounddialog = None
self.pathdialog = None
# used by run()
self.memstate = None
self.floppy = None
self.io_id = None
# TODO: Hatari UI own configuration settings save/load
self.tracepoints = None
def _reset_config_dialogs(self):
self.floppydialog = None
self.harddiskdialog = None
self.displaydialog = None
self.joystickdialog = None
self.machinedialog = None
self.peripheraldialog = None
self.sounddialog = None
self.pathdialog = None
# ---------- create UI ----------------
def create_ui(self, accelgroup, menu, toolbars, fullscreen, embed):
"create_ui(menu, toolbars, fullscreen, embed)"
# add horizontal elements
hbox = gtk.HBox()
if toolbars["left"]:
hbox.pack_start(toolbars["left"], False, True)
if embed:
self._add_uisocket(hbox)
if toolbars["right"]:
hbox.pack_start(toolbars["right"], False, True)
# add vertical elements
vbox = gtk.VBox()
if menu:
vbox.add(menu)
if toolbars["top"]:
vbox.pack_start(toolbars["top"], False, True)
vbox.add(hbox)
if toolbars["bottom"]:
vbox.pack_start(toolbars["bottom"], False, True)
# put them to main window
mainwin = gtk.Window(gtk.WINDOW_TOPLEVEL)
mainwin.set_title("%s %s" % (UInfo.name, UInfo.version))
mainwin.set_icon_from_file(UInfo.icon)
if accelgroup:
mainwin.add_accel_group(accelgroup)
if fullscreen:
mainwin.fullscreen()
mainwin.add(vbox)
mainwin.show_all()
# for run and quit callbacks
self.killdialog = KillDialog(mainwin)
mainwin.connect("delete_event", self.quit)
self.mainwin = mainwin
def _add_uisocket(self, box):
# add Hatari parent container to given box
socket = gtk.Socket()
# without this, closing Hatari would remove the socket widget
socket.connect("plug-removed", lambda obj: True)
socket.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black"))
socket.set_events(gtk.gdk.ALL_EVENTS_MASK)
socket.set_flags(gtk.CAN_FOCUS)
# set max Hatari window size = desktop size
self.config.set_desktop_size(gtk.gdk.screen_width(), gtk.gdk.screen_height())
# set initial embedded hatari size
width, height = self.config.get_window_size()
socket.set_size_request(width, height)
# no resizing for the Hatari window
box.pack_start(socket, False, False)
self.hatariwin = socket
# ------- run callback -----------
def _socket_cb(self, fd, event):
if event != gobject.IO_IN:
# hatari process died, make sure Hatari instance notices
self.hatari.kill()
return False
width, height = self.hatari.get_embed_info()
print("New size = %d x %d" % (width, height))
oldwidth, oldheight = self.hatariwin.get_size_request()
self.hatariwin.set_size_request(width, height)
if width < oldwidth or height < oldheight:
# force also mainwin smaller (it automatically grows)
self.mainwin.resize(width, height)
return True
def run(self, widget = None):
if not self.killdialog.run(self.hatari):
return
if self.io_id:
gobject.source_remove(self.io_id)
args = ["--configfile"]
# whether to use Hatari config or unsaved Hatari UI config?
if self.config.is_changed():
args += [self.config.save_tmp(self.tmpconfpath)]
else:
args += [self.config.get_path()]
if self.memstate:
args += self.memstate
# only way to change boot order is to specify disk on command line
if self.floppy:
args += self.floppy
if self.hatariwin:
size = self.hatariwin.window.get_size()
self.hatari.run(args, self.hatariwin.window)
# get notifications of Hatari window size changes
self.hatari.enable_embed_info()
socket = self.hatari.get_control_socket().fileno()
events = gobject.IO_IN | gobject.IO_HUP | gobject.IO_ERR
self.io_id = gobject.io_add_watch(socket, events, self._socket_cb)
# all keyboard events should go to Hatari window
self.hatariwin.grab_focus()
else:
self.hatari.run(args)
def set_floppy(self, floppy):
self.floppy = floppy
# ------- quit callback -----------
def quit(self, widget, arg = None):
# due to Gtk API, needs to return True when *not* quitting
if not self.killdialog.run(self.hatari):
return True
if self.io_id:
gobject.source_remove(self.io_id)
if self.config.is_changed():
if not self.quitdialog:
self.quitdialog = QuitSaveDialog(self.mainwin)
if not self.quitdialog.run(self.config):
return True
gtk.main_quit()
if os.path.exists(self.tmpconfpath):
os.unlink(self.tmpconfpath)
# continue to mainwin destroy if called by delete_event
return False
# ------- pause callback -----------
def pause(self, widget):
if widget.get_active():
self.hatari.pause()
else:
self.hatari.unpause()
# dialogs
# ------- reset callback -----------
def reset(self, widget):
if not self.resetdialog:
self.resetdialog = ResetDialog(self.mainwin)
self.resetdialog.run(self.hatari)
# ------- about callback -----------
def about(self, widget):
if not self.aboutdialog:
self.aboutdialog = AboutDialog(self.mainwin)
self.aboutdialog.run()
# ------- input callback -----------
def inputs(self, widget):
if not self.inputdialog:
self.inputdialog = InputDialog(self.mainwin)
self.inputdialog.run(self.hatari)
# ------- floppydisk callback -----------
def floppydisk(self, widget):
if not self.floppydialog:
self.floppydialog = FloppyDialog(self.mainwin)
self.floppydialog.run(self.config)
# ------- harddisk callback -----------
def harddisk(self, widget):
if not self.harddiskdialog:
self.harddiskdialog = HardDiskDialog(self.mainwin)
self.harddiskdialog.run(self.config)
# ------- display callback -----------
def display(self, widget):
if not self.displaydialog:
self.displaydialog = DisplayDialog(self.mainwin)
self.displaydialog.run(self.config)
# ------- joystick callback -----------
def joystick(self, widget):
if not self.joystickdialog:
self.joystickdialog = JoystickDialog(self.mainwin)
self.joystickdialog.run(self.config)
# ------- machine callback -----------
def machine(self, widget):
if not self.machinedialog:
self.machinedialog = MachineDialog(self.mainwin)
if self.machinedialog.run(self.config):
self.hatari.trigger_shortcut("coldreset")
# ------- peripheral callback -----------
def peripheral(self, widget):
if not self.peripheraldialog:
self.peripheraldialog = PeripheralDialog(self.mainwin)
self.peripheraldialog.run(self.config)
# ------- sound callback -----------
def sound(self, widget):
if not self.sounddialog:
self.sounddialog = SoundDialog(self.mainwin)
self.sounddialog.run(self.config)
# ------- path callback -----------
def path(self, widget):
if not self.pathdialog:
self.pathdialog = PathDialog(self.mainwin)
self.pathdialog.run(self.config)
# ------- debug callback -----------
def debugger(self, widget):
if not self.debugui:
self.debugui = HatariDebugUI(self.hatari)
self.debugui.show()
# ------- trace callback -----------
def trace(self, widget):
if not self.tracedialog:
self.tracedialog = TraceDialog(self.mainwin)
self.tracepoints = self.tracedialog.run(self.hatari, self.tracepoints)
# ------ snapshot load/save callbacks ---------
def load(self, widget):
path = os.path.expanduser("~/.hatari/hatari.sav")
filename = get_open_filename("Select snapshot", self.mainwin, path)
if filename:
self.memstate = ["--memstate", filename]
self.run()
return True
return False
def save(self, widget):
self.hatari.trigger_shortcut("savemem")
# ------ config load/save callbacks ---------
def config_load(self, widget):
path = self.config.get_path()
filename = get_open_filename("Select configuration file", self.mainwin, path)
if filename:
self.hatari.change_option("--configfile %s" % filename)
self.config.load(filename)
self._reset_config_dialogs()
return True
return False
def config_save(self, widget):
path = self.config.get_path()
filename = get_save_filename("Save configuration as...", self.mainwin, path)
if filename:
self.config.save_as(filename)
return True
return False
# ------- fast forward callback -----------
def set_fastforward(self, widget):
self.config.set_fastforward(widget.get_active())
def get_fastforward(self):
return self.config.get_fastforward()
# ------- fullscreen callback -----------
def set_fullscreen(self, widget):
# if user can select this, Hatari isn't in fullscreen
self.hatari.change_option("--fullscreen")
# ------- screenshot callback -----------
def screenshot(self, widget):
self.hatari.trigger_shortcut("screenshot")
# ------- record callbacks -----------
def recanim(self, widget):
self.hatari.trigger_shortcut("recanim")
def recsound(self, widget):
self.hatari.trigger_shortcut("recsound")
# ------- insert key special callback -----------
def keypress(self, widget, code):
self.hatari.insert_event("keypress %s" % code)
def textinsert(self, widget, text):
HatariTextInsert(self.hatari, text)
# ------- panel callback -----------
def panel(self, action, box):
title = action.get_name()
if title not in self.panels:
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_transient_for(self.mainwin)
window.set_icon_from_file(UInfo.icon)
window.set_title(title)
window.add(box)
window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
window.connect("delete_event", window_hide_cb)
self.panels[title] = window
else:
window = self.panels[title]
window.show_all()
window.deiconify()
# ---------------------------------------------------------------
# class for creating menus, toolbars and panels
# and managing actions bound to them
class UIActions:
def __init__(self):
cb = self.callbacks = UICallbacks()
self.help = UIHelp()
self.actions = gtk.ActionGroup("All")
# name, icon ID, label, accel, tooltip, callback
self.actions.add_toggle_actions((
# TODO: how to know when these are changed from inside Hatari?
("recanim", gtk.STOCK_MEDIA_RECORD, "Record animation", "<Ctrl>A", "Record animation", cb.recanim),
("recsound", gtk.STOCK_MEDIA_RECORD, "Record sound", "<Ctrl>W", "Record YM/Wav", cb.recsound),
("pause", gtk.STOCK_MEDIA_PAUSE, "Pause", "<Ctrl>P", "Pause Hatari to save battery", cb.pause),
("forward", gtk.STOCK_MEDIA_FORWARD, "Forward", "<Ctrl>F", "Whether to fast forward Hatari (needs fast machine)", cb.set_fastforward, cb.get_fastforward())
))
# name, icon ID, label, accel, tooltip, callback
self.actions.add_actions((
("load", gtk.STOCK_OPEN, "Load snapshot...", "<Ctrl>L", "Load emulation snapshot", cb.load),
("save", gtk.STOCK_SAVE, "Save snapshot", "<Ctrl>S", "Save emulation snapshot", cb.save),
("shot", gtk.STOCK_MEDIA_RECORD, "Grab screenshot", "<Ctrl>G", "Grab a screenshot", cb.screenshot),
("quit", gtk.STOCK_QUIT, "Quit", "<Ctrl>Q", "Quit Hatari UI", cb.quit),
("run", gtk.STOCK_MEDIA_PLAY, "Run", "<Ctrl>R", "(Re-)run Hatari", cb.run),
("full", gtk.STOCK_FULLSCREEN, "Fullscreen", "<Ctrl>U", "Toggle whether Hatari is fullscreen", cb.set_fullscreen),
("input", gtk.STOCK_SPELL_CHECK, "Inputs...", "<Ctrl>N", "Simulate text input and mouse clicks", cb.inputs),
("reset", gtk.STOCK_REFRESH, "Reset...", "<Ctrl>E", "Warm or cold reset Hatari", cb.reset),
("display", gtk.STOCK_PREFERENCES, "Display...", "<Ctrl>Y", "Display settings", cb.display),
("floppy", gtk.STOCK_FLOPPY, "Floppies...", "<Ctrl>D", "Floppy images", cb.floppydisk),
("harddisk", gtk.STOCK_HARDDISK, "Hard disks...", "<Ctrl>H", "Hard disk images and directories", cb.harddisk),
("joystick", gtk.STOCK_CONNECT, "Joysticks...", "<Ctrl>J", "Joystick settings", cb.joystick),
("machine", gtk.STOCK_HARDDISK, "Machine...", "<Ctrl>M", "Hatari st/e/tt/falcon configuration", cb.machine),
("device", gtk.STOCK_PRINT, "Peripherals...", "<Ctrl>V", "Toggle Midi, Printer, RS232 peripherals", cb.peripheral),
("sound", gtk.STOCK_PROPERTIES, "Sound...", "<Ctrl>O", "Sound settings", cb.sound),
("path", gtk.STOCK_DIRECTORY, "Paths...", None, "Device & save file paths", cb.path),
("lconfig", gtk.STOCK_OPEN, "Load config...", "<Ctrl>C", "Load configuration", self.config_load),
("sconfig", gtk.STOCK_SAVE_AS, "Save config as...", None, "Save configuration", cb.config_save),
("debug", gtk.STOCK_FIND, "Debugger...", "<Ctrl>B", "Activate Hatari debugger", cb.debugger),
("trace", gtk.STOCK_EXECUTE, "Trace settings...", "<Ctrl>T", "Hatari tracing setup", cb.trace),
("manual", None, "Hatari manual", None, None, self.help.view_hatari_manual),
("compatibility", None, "Hatari compatibility list", None, None, self.help.view_hatari_compatibility),
("release", None, "Hatari release notes", None, None, self.help.view_hatari_releasenotes),
("todo", None, "Hatari TODO", None, None, self.help.view_hatari_todo),
("mails", None, "Hatari mailing lists", None, None, self.help.view_hatari_mails),
("changes", None, "Latest Hatari changes", None, None, self.help.view_hatari_repository),
("authors", None, "Hatari authors", None, None, self.help.view_hatari_authors),
("hatari", None, "Hatari home page", None, None, self.help.view_hatari_page),
("hatariui", None, "Hatari UI home page", None, None, self.help.view_hatariui_page),
("about", gtk.STOCK_INFO, "Hatari UI info", "<Ctrl>I", "Hatari UI information", cb.about)
))
self.action_names = [x.get_name() for x in self.actions.list_actions()]
# no actions set yet to panels or toolbars
self.toolbars = {}
self.panels = []
def config_load(self, widget):
# user loads a new configuration?
if self.callbacks.config_load(widget):
print("TODO: reset toggle actions")
# ----- toolbar / panel additions ---------
def set_actions(self, action_str, place):
"set_actions(actions,place) -> error string, None if all OK"
actions = action_str.split(",")
for action in actions:
if action in self.action_names:
# regular action
continue
if action in self.panels:
# user specified panel
continue
if action in ("close", ">"):
if place != "panel":
return "'close' and '>' can be only in a panel"
continue
if action == "|":
# divider
continue
if action.find("=") >= 0:
# special keycode/string action
continue
return "unrecognized action '%s'" % action
if place in ("left", "right", "top", "bottom"):
self.toolbars[place] = actions
return None
if place == "panel":
if len(actions) < 3:
return "panel has too few items to be useful"
return None
return "unknown actions position '%s'" % place
def add_panel(self, spec):
"add_panel(panel_specification) -> error string, None if all is OK"
offset = spec.find(",")
if offset <= 0:
return "invalid panel specification '%s'" % spec
name, panelcontrols = spec[:offset], spec[offset+1:]
error = self.set_actions(panelcontrols, "panel")
if error:
return error
if ",>," in panelcontrols:
box = gtk.VBox()
splitcontrols = panelcontrols.split(",>,")
for controls in splitcontrols:
box.add(self._get_container(controls.split(",")))
else:
box = self._get_container(panelcontrols.split(","))
self.panels.append(name)
self.actions.add_actions(
((name, gtk.STOCK_ADD, name, None, name, self.callbacks.panel),),
box
)
return None
def list_actions(self):
yield ("|", "Separator between controls")
yield (">", "Start next toolbar row in panel windows")
# generate the list from action information
for act in self.actions.list_actions():
note = act.get_property("tooltip")
if not note:
note = act.get_property("label")
yield(act.get_name(), note)
yield ("<panel name>", "Button for the specified panel window")
yield ("<name>=<string/code>", "Synthetize string or single key <code>")
# ------- panel special actions -----------
def _close_cb(self, widget):
widget.get_toplevel().hide()
# ------- key special action -----------
def _create_key_control(self, name, textcode):
"Simulate Atari key press/release and string inserting"
if not textcode:
return None
widget = gtk.ToolButton(gtk.STOCK_PASTE)
widget.set_label(name)
try:
# part after "=" converts to an int?
code = int(textcode, 0)
widget.connect("clicked", self.callbacks.keypress, code)
tip = "keycode: %d" % code
except ValueError:
# no, assume a string macro is wanted instead
widget.connect("clicked", self.callbacks.textinsert, textcode)
tip = "string '%s'" % textcode
widget.set_tooltip_text("Insert " + tip)
return widget
def _get_container(self, actions, horiz = True):
"return Gtk container with the specified actions or None for no actions"
if not actions:
return None
#print("ACTIONS:", actions)
if len(actions) > 1:
bar = gtk.Toolbar()
if horiz:
bar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
else:
bar.set_orientation(gtk.ORIENTATION_VERTICAL)
bar.set_style(gtk.TOOLBAR_BOTH)
# disable overflow menu to get toolbar sized correctly for panels
bar.set_show_arrow(False)
else:
bar = None
for action in actions:
#print(action)
offset = action.find("=")
if offset >= 0:
# handle "<name>=<keycode>" action specification
name = action[:offset]
text = action[offset+1:]
widget = self._create_key_control(name, text)
elif action == "|":
widget = gtk.SeparatorToolItem()
elif action == "close":
if bar:
widget = create_toolbutton(gtk.STOCK_CLOSE, self._close_cb)
else:
widget = create_button("Close", self._close_cb)
else:
widget = self.actions.get_action(action).create_tool_item()
if not widget:
continue
if bar:
if action != "|":
widget.set_expand(True)
bar.insert(widget, -1)
if bar:
return bar
return widget
# ------------- handling menu -------------
def _add_submenu(self, bar, title, items):
submenu = gtk.Menu()
for name in items:
if name:
action = self.actions.get_action(name)
item = action.create_menu_item()
else:
item = gtk.SeparatorMenuItem()
submenu.add(item)
baritem = gtk.MenuItem(title, False)
baritem.set_submenu(submenu)
bar.add(baritem)
def _get_menu(self):
allmenus = (
("File", ("load", "save", None, "shot", "recanim", "recsound", None, "quit")),
("Emulation", ("run", "pause", "forward", None, "full", None, "input", None, "reset")),
("Devices", ("display", "floppy", "harddisk", "joystick", "machine", "device", "sound")),
("Configuration", ("path", None, "lconfig", "sconfig")),
("Debug", ("debug", "trace")),
("Help", ("manual", "compatibility", "release", "todo", None, "mails", "changes", None, "authors", "hatari", "hatariui", "about",))
)
bar = gtk.MenuBar()
for title, items in allmenus:
self._add_submenu(bar, title, items)
if self.panels:
self._add_submenu(bar, "Panels", self.panels)
return bar
# ------------- run the whole UI -------------
def run(self, floppy, havemenu, fullscreen, embed):
accelgroup = None
# create menu?
if havemenu:
# this would steal keys from embedded Hatari
if not embed:
accelgroup = gtk.AccelGroup()
for action in self.actions.list_actions():
action.set_accel_group(accelgroup)
menu = self._get_menu()
else:
menu = None
# create toolbars
toolbars = { "left":None, "right":None, "top":None, "bottom":None}
for side in ("left", "right"):
if side in self.toolbars:
toolbars[side] = self._get_container(self.toolbars[side], False)
for side in ("top", "bottom"):
if side in self.toolbars:
toolbars[side] = self._get_container(self.toolbars[side], True)
self.callbacks.create_ui(accelgroup, menu, toolbars, fullscreen, embed)
self.help.set_mainwin(self.callbacks.mainwin)
self.callbacks.set_floppy(floppy)
# ugly, Hatari socket window ID can be gotten only
# after Socket window is realized by gtk_main()
gobject.idle_add(self.callbacks.run)
gtk.main()
# ------------- usage / argument handling --------------
def usage(actions, msg=None):
name = os.path.basename(sys.argv[0])
uiname = "%s %s" % (UInfo.name, UInfo.version)
print("\n%s" % uiname)
print("=" * len(uiname))
print("\nUsage: %s [options] [directory|disk image|Atari program]" % name)
print("\nOptions:")
print("\t-h, --help\t\tthis help")
print("\t-n, --nomenu\t\tomit menus")
print("\t-e, --embed\t\tembed Hatari window in middle of controls")
print("\t-f, --fullscreen\tstart in fullscreen")
print("\t-l, --left <controls>\ttoolbar at left")
print("\t-r, --right <controls>\ttoolbar at right")
print("\t-t, --top <controls>\ttoolbar at top")
print("\t-b, --bottom <controls>\ttoolbar at bottom")
print("\t-p, --panel <name>,<controls>")
print("\t\t\t\tseparate window with given name and controls")
print("\nAvailable (panel/toolbar) controls:")
for action, description in actions.list_actions():
size = len(action)
if size < 8:
tabs = "\t\t"
elif size < 16:
tabs = "\t"
else:
tabs = "\n\t\t\t"
print("\t%s%s%s" % (action, tabs, description))
print("""
You can have as many panels as you wish. For each panel you need to add
a control with the name of the panel (see "MyPanel" below).
For example:
\t%s --embed \\
\t-t "about,run,pause,quit" \\
\t-p "MyPanel,Macro=Test,Undo=97,Help=98,>,F1=59,F2=60,F3=61,F4=62,>,close" \\
\t-r "paste,debug,trace,machine,MyPanel" \\
\t-b "sound,|,fastforward,|,fullscreen"
if no options are given, the UI uses basic controls.
""" % name)
if msg:
print("ERROR: %s\n" % msg)
sys.exit(1)
def main():
info = UInfo()
actions = UIActions()
try:
longopts = ["embed", "fullscreen", "nomenu", "help",
"left=", "right=", "top=", "bottom=", "panel="]
opts, floppies = getopt.getopt(sys.argv[1:], "efnhl:r:t:b:p:", longopts)
del longopts
except getopt.GetoptError as err:
usage(actions, err)
menu = True
embed = False
fullscreen = False
error = None
for opt, arg in opts:
print(opt, arg)
if opt in ("-e", "--embed"):
embed = True
elif opt in ("-f", "--fullscreen"):
fullscreen = True
elif opt in ("-n", "--nomenu"):
menu = False
elif opt in ("-h", "--help"):
usage(actions)
elif opt in ("-l", "--left"):
error = actions.set_actions(arg, "left")
elif opt in ("-r", "--right"):
error = actions.set_actions(arg, "right")
elif opt in ("-t", "--top"):
error = actions.set_actions(arg, "top")
elif opt in ("-b", "--bottom"):
error = actions.set_actions(arg, "bottom")
elif opt in ("-p", "--panel"):
error = actions.add_panel(arg)
else:
assert False, "getopt returned unhandled option"
if error:
usage(actions, error)
if len(floppies) > 1:
usage(actions, "multiple floppy images given: %s" % str(floppies))
if floppies:
if not os.path.exists(floppies[0]):
usage(actions, "floppy image '%s' doesn't exist" % floppies[0])
actions.run(floppies, menu, fullscreen, embed)
if __name__ == "__main__":
main()

111
python-ui/release-notes.txt Normal file
View File

@ -0,0 +1,111 @@
User visible changes in Hatari (Python Gtk) UI
----------------------------------------------
2015-05:
- Add support for --gemdos-drive, --ttram option features
and new tracepoints
- Debugger window supports WinUAE CPU core
- Updated UI version to 1.3
2014-06:
- Add support for --sound-sync, --sound-buffer-size,
--slowdown, --gemdos-case, --drive-*-heads and
--drive-* option features and new tracepoints
- Improved option names & descriptions
- update UI version to 1.2
2012-05:
- Add --desktop-st and --force-max options support
(latter helps video recording of Falcon demos
doing lots of resolution changes)
2012-01:
- Add microphone and YM voice mixing sound options
- Fix asserts and empty hatari config file caused by
Hatari v1.6 config variable names changes by changing
how Hatari config variable types are handled
- Update UI version to v1.1 (mainly due to config change)
- Support spaces in file paths/names
2011-10:
- Replace --slowfdc with --fastfdc
2011-04:
- Support RTC and "keep desktop resolution" options
2011-02:
- Support new tracepoints (AES, DSP, Videl, Crossbar)
- Disasm update for new Hatari disassembly output
2011-01:
- Use new Gtk v2.12 tooltip API
- Support capture cropping
2010-10:
- Improvements to text & key inserting
- Move hatari-console.py elsewhere
2010-05:
- Manual page for Hatari UI
2010-04:
- UI handles Hatari system configuration properly
- New settings dialog for HD dir and image configuration
- Maximum/preferred zoom support to display settings dialog
- Removed --spec512 support
- Option for whether debugger will change to new PC address
whenever emulation is stopped again
2010-03:
- With the new Hatari --saveconfig option Hatari UI can ask Hatari
to save its configuration (required by the UI) before the UI itself
starts, user doesn't need to do it manually anymore
(if user config is missing or out of date)
- Added --slowfdc support to Floppy settings dialog
2009-09:
- Support for setting CPU level & clock and Falcon DSP type
2009-08:
- Update to latest Hatari 1.3.0:
- Debug/trace fixes (Hatari 1.3.1 includes these)
2009-07:
- Add Help menu items pointing to Hatari docs & site
- --timer-d support + doc updates
2009-06:
- Move to BerliOS Hatari repo
- Update to latest Hatari 1.2.0:
- midi in/out, sound freq etc
2008-10:
- Support paths & peripherals settings
2008-09:
- Support for auto frameskip, statusbar and overlay led
- Remove support for multiple machine setups
(now that run-time Hatari config/saving loading works)
2008-07:
- Support recanim/sound, config load/save and memory snapshot load/save
- First properly working with menus and toolbars instead of buttons
- Can adapt properly also to Hatari window getting smaller
(works on desktop, maemo/Matchbox WM have still issues)
- Makefile for installation
2008-06:
- Fairly usable version with configurable buttons
- Can adapt to Hatari window size changes
2008-05:
- Loading & saving Hatari configuration and checking
changes against saved configuration works
2008-04:
- First version with debugger UI
2008-02:
- First version that can embed Hatari window (needed quite
a lot of testing to find method that works well enough)

6
python-ui/tests/README Normal file
View File

@ -0,0 +1,6 @@
Files
-----
pygtk-hatari-embed-test.py -- Several tries at embedding Hatari window
pygtk-hello-world.py -- simplest PyGtk program

View File

@ -0,0 +1,200 @@
#!/usr/bin/python
#
# Tests embedding hatari with three different methods:
# "hatari": ask Hatari to reparent to given window
# "sdl": Give SDL window into which it should reparent
# -> SDL doesn't handle (mouse, key, expose) events
# although according to "xev" it's window receives them!
# Bug in SDL (not one of the originally needed features?)?
# "reparent": Find Hatari window and reparent it into pygtk widget in python
# - Needs "xwininfo" and "awk"
#
# Using three alternative widgets:
# "drawingarea"
# "eventbox"
# "socket"
#
# Results:
# reparent+eventbox
# -> PyGtk reparents Hatari under something on rootwindow instead
# (reparening eventbox under Hatari window works fine though...)
# reparent+socket
# -> Hatari seems to be reparented back to where it was
# sdl+anything
# -> all events are lost
# hatari+socket
# -> seems to work fine
import os
import sys
import gtk
import time
import gobject
def usage(error):
print "\nusage: %s <widget> <embed method>\n" % sys.argv[0].split(os.path.sep)[-1]
print "Opens window with given <widget>, runs Hatari and tries to embed it"
print "with given <method>\n"
print "<widget> can be <drawingarea|eventbox|socket>"
print "<method> can be <sdl|hatari|reparent>\n"
print "ERROR: %s\n" % error
sys.exit(1)
class AppUI():
hatari_wd = 640
hatari_ht = 400
def __init__(self, widget, method):
if method in ("hatari", "reparent", "sdl"):
self.method = method
else:
usage("unknown <method> '%s'" % method)
if widget == "drawingarea":
widgettype = gtk.DrawingArea
elif widget == "eventbox":
widgettype = gtk.EventBox
elif widget == "socket":
# XEMBED socket for Hatari/SDL
widgettype = gtk.Socket
else:
usage("unknown <widget> '%s'" % widget)
self.window = self.create_window()
self.add_hatari_parent(self.window, widgettype)
gobject.timeout_add(1*1000, self.timeout_cb)
def create_window(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect("destroy", self.do_quit)
return window
def do_quit(self, widget):
if self.hatari_pid:
os.kill(self.hatari_pid, 9)
print "killed Hatari PID %d" % self.hatari_pid
self.hatari_pid = 0
gtk.main_quit()
def add_hatari_parent(self, parent, widgettype):
# Note: CAN_FOCUS has to be set for the widget embedding Hatari
# and *unset* for everything else, otherwise Hatari doesn't
# receive *any* keyevents.
self.hatari_pid = 0
vbox = gtk.VBox()
button = gtk.Button("Test Button")
button.unset_flags(gtk.CAN_FOCUS)
vbox.add(button)
widget = widgettype()
widget.set_size_request(self.hatari_wd, self.hatari_ht)
widget.set_events(gtk.gdk.ALL_EVENTS_MASK)
widget.set_flags(gtk.CAN_FOCUS)
self.hatariparent = widget
# TODO: when running 320x200, parent could be centered to here
vbox.add(widget)
# test focus
label = gtk.Label("Test SpinButton:")
vbox.add(label)
spin = gtk.SpinButton()
spin.set_range(0, 10)
spin.set_digits(0)
spin.set_numeric(True)
spin.set_increments(1, 2)
# otherwise Hatari doesn't receive keys!!!
spin.unset_flags(gtk.CAN_FOCUS)
vbox.add(spin)
parent.add(vbox)
def timeout_cb(self):
self.do_hatari_method()
return False # only once
def do_hatari_method(self):
pid = os.fork()
if pid < 0:
print "ERROR: fork()ing Hatari failed!"
return
if pid:
# in parent
if self.method == "reparent":
hatari_win = self.find_hatari_window()
if hatari_win:
self.reparent_hatari_window(hatari_win)
self.hatari_pid = pid
else:
os.kill(pid, signal.SIGKILL)
print "killed process with PID %d" % pid
self.hatari_pid = 0
else:
# method == "sdl" or "hatari"
self.hatari_pid = pid
else:
# child runs Hatari
args = ("hatari", "-m", "-z", "2")
os.execvpe("hatari", args, self.get_hatari_env())
def get_hatari_env(self):
if self.method == "reparent":
return os.environ
# tell SDL to use (embed itself inside) given widget's window
win_id = self.hatariparent.window.xid
env = os.environ
if self.method == "sdl":
env["SDL_WINDOWID"] = str(win_id)
elif self.method == "hatari":
env["HATARI_PARENT_WIN"] = str(win_id)
return env
def find_hatari_window(self):
# find hatari window by its WM class string and reparent it
cmd = """xwininfo -root -tree|awk '/"hatari" "hatari"/{print $1}'"""
counter = 0
while counter < 8:
pipe = os.popen(cmd)
windows = []
for line in pipe.readlines():
windows.append(int(line, 16))
try:
pipe.close()
except IOError:
# handle child process exiting silently
pass
if not windows:
counter += 1
print "WARNING: no Hatari window found yet, retrying..."
time.sleep(1)
continue
if len(windows) > 1:
print "WARNING: multiple Hatari windows, picking first one..."
return windows[0]
print "ERROR: no windows with the 'hatari' WM class found"
return None
def reparent_hatari_window(self, hatari_win):
window = gtk.gdk.window_foreign_new(hatari_win)
if not window:
print "ERROR: Hatari window (ID: 0x%x) reparenting failed!" % hatari_win
return False
if not self.hatariparent.window:
print "ERROR: where hatariparent disappeared?"
return False
print "Found Hatari window ID: 0x%x, reparenting..." % hatari_win
print "...to container window ID: 0x%x" % self.hatariparent.window.xid
window.reparent(self.hatariparent.window, 0, 0)
#window.reparent(self.hatariparent.get_toplevel().window, 0, 0)
#window.reparent(self.hatariparent.get_root_window(), 0, 0)
#window.show()
#window.raise_()
# If python would destroy the Gtk widget when it goes out of scope,
# the foreign window widget destructor would delete Hatari window.
# So, keep a reference
#self.hatariwindow = window
return True
def run(self):
self.window.show_all()
gtk.main()
if len(sys.argv) != 3:
usage("wrong number of arguments")
app = AppUI(sys.argv[1], sys.argv[2])
app.run()

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
import gtk
class AppUI():
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("destroy", gtk.main_quit)
label = gtk.Label("Hello World!")
self.window.add(label)
def run(self):
self.window.show_all()
gtk.main()
app = AppUI()
app.run()

417
python-ui/uihelpers.py Normal file
View File

@ -0,0 +1,417 @@
#!/usr/bin/env python
#
# Misc common helper classes and functions for the Hatari UI
#
# Copyright (C) 2008-2012 by Eero Tamminen
#
# 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.
import os
import sys
# use correct version of pygtk/gtk
import pygtk
pygtk.require('2.0')
import gtk
import gobject
# leak debugging
#import gc
#gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
# ---------------------
# Hatari UI information
class UInfo:
"""singleton constants for the UI windows,
one instance is needed to initialize these properly"""
version = "v1.3"
name = "Hatari UI"
logo = "hatari-logo.png"
# TODO: use share/icons/hicolor/*/apps/hatari.png instead
icon = "hatari-icon.png"
copyright = "UI copyright (C) 2008-2015 by Eero Tamminen"
# path to the directory where the called script resides
path = os.path.dirname(sys.argv[0])
def __init__(self, path = None):
"UIinfo([path]), set suitable paths for resources from CWD and path"
if path:
self.path = path
if not os.path.exists(UInfo.icon):
UInfo.icon = self._get_path(UInfo.icon)
if not os.path.exists(UInfo.logo):
UInfo.logo = self._get_path(UInfo.logo)
def _get_path(self, filename):
sep = os.path.sep
testpath = "%s%s%s" % (self.path, sep, filename)
if os.path.exists(testpath):
return testpath
# --------------------------------------------------------
# functions for showing HTML files
class UIHelp:
def __init__(self):
"""determine HTML viewer and where docs are"""
self._view = self.get_html_viewer()
self._path = self.get_doc_path()
def get_html_viewer(self):
"""return name of html viewer or None"""
path = self.get_binary_path("xdg-open")
if path:
return path
path = self.get_binary_path("firefox")
if path:
return path
return None
def get_binary_path(self, name):
"""return true if given binary is in path"""
# could also try running the binary with "--version" arg
# and check the exec return value
if os.sys.platform == "win32":
splitter = ';'
else:
splitter = ':'
for i in os.environ['PATH'].split(splitter):
fname = os.path.join(i, name)
if os.access(fname, os.X_OK) and not os.path.isdir(fname):
return fname
return None
def get_doc_path(self):
"""return path or URL to Hatari docs or None"""
# first try whether there are local Hatari docs in standard place
# for this Hatari/UI version
sep = os.sep
path = self.get_binary_path("hatari")
path = sep.join(path.split(sep)[:-2]) # remove "bin/hatari"
path = path + sep + "share" + sep + "doc" + sep + "hatari" + sep
if os.path.exists(path + "manual.html"):
return path
# if not, point to latest Hatari HG version docs
print("WARNING: Hatari manual not found at:", path + "manual.html")
return "http://hg.tuxfamily.org/mercurialroot/hatari/hatari/raw-file/tip/doc/"
def set_mainwin(self, widget):
self.mainwin = widget
def view_url(self, url, name):
"""view given URL or file path, or error use 'name' as its name"""
if self._view and "://" in url or os.path.exists(url):
print("RUN: '%s' '%s'" % (self._view, url))
os.spawnlp(os.P_NOWAIT, self._view, self._view, url)
return
if not self._view:
msg = "Cannot view %s, HTML viewer missing" % name
else:
msg = "Cannot view %s,\n'%s' file is missing" % (name, url)
from dialogs import ErrorDialog
ErrorDialog(self.mainwin).run(msg)
def view_hatari_manual(self, dummy=None):
self.view_url(self._path + "manual.html", "Hatari manual")
def view_hatari_compatibility(self, dummy=None):
self.view_url(self._path + "compatibility.html", "Hatari compatibility list")
def view_hatari_releasenotes(self, dummy=None):
self.view_url(self._path + "release-notes.txt", "Hatari release notes")
def view_hatari_todo(self, dummy=None):
self.view_url(self._path + "todo.txt", "Hatari TODO items")
def view_hatari_mails(self, dummy=None):
self.view_url("http://hatari.tuxfamily.org/contact.html", "Hatari mailing lists")
def view_hatari_repository(self, dummy=None):
self.view_url("http://hg.tuxfamily.org/mercurialroot/hatari/hatari", "latest Hatari changes")
def view_hatari_authors(self, dummy=None):
self.view_url(self._path + "authors.txt", "Hatari authors")
def view_hatari_page(self, dummy=None):
self.view_url("http://hatari.tuxfamily.org/", "Hatari home page")
def view_hatariui_page(self, dummy=None):
self.view_url("http://koti.mbnet.fi/tammat/hatari/hatari-ui.shtml", "Hatari UI home page")
# --------------------------------------------------------
# auxiliary class+callback to be used with the PasteDialog
class HatariTextInsert:
def __init__(self, hatari, text):
self.index = 0
self.text = text
self.pressed = False
self.hatari = hatari
print("OUTPUT '%s'" % text)
gobject.timeout_add(100, _text_insert_cb, self)
# callback to insert text object to Hatari character at the time
# (first key down, on next call up), at given interval
def _text_insert_cb(textobj):
char = textobj.text[textobj.index]
if char == ' ':
# white space gets stripped, use scancode instead
char = "57"
if textobj.pressed:
textobj.pressed = False
textobj.hatari.insert_event("keyup %s" % char)
textobj.index += 1
if textobj.index >= len(textobj.text):
del(textobj)
return False
else:
textobj.pressed = True
textobj.hatari.insert_event("keydown %s" % char)
# call again
return True
# ----------------------------
# helper functions for buttons
def create_button(label, cb, data = None):
"create_button(label,cb[,data]) -> button widget"
button = gtk.Button(label)
if data == None:
button.connect("clicked", cb)
else:
button.connect("clicked", cb, data)
return button
def create_toolbutton(stock_id, cb, data = None):
"create_toolbutton(stock_id,cb[,data]) -> toolbar button with stock icon+label"
button = gtk.ToolButton(stock_id)
if data == None:
button.connect("clicked", cb)
else:
button.connect("clicked", cb, data)
return button
def create_toggle(label, cb, data = None):
"create_toggle(label,cb[,data]) -> toggle button widget"
button = gtk.ToggleButton(label)
if data == None:
button.connect("toggled", cb)
else:
button.connect("toggled", cb, data)
return button
# -----------------------------
# Table dialog helper functions
def create_table_dialog(parent, title, rows, cols, oktext = gtk.STOCK_APPLY):
"create_table_dialog(parent,title,rows, cols, oktext) -> (table,dialog)"
dialog = gtk.Dialog(title, parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(oktext, gtk.RESPONSE_APPLY,
gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
table = gtk.Table(rows, cols)
table.set_data("col_offset", 0)
table.set_col_spacings(8)
dialog.vbox.add(table)
return (table, dialog)
def table_set_col_offset(table, offset):
"set column offset for successive table_* ops on given table"
table.set_data("col_offset", offset)
def table_add_entry_row(table, row, label, size = None):
"table_add_entry_row(table,row,label,[entry size]) -> entry"
# add given label to given row in given table
# return entry for that line
label = gtk.Label(label)
align = gtk.Alignment(1) # right aligned
align.add(label)
col = table.get_data("col_offset")
table.attach(align, col, col+1, row, row+1, gtk.FILL)
col += 1
if size:
entry = gtk.Entry(size)
entry.set_width_chars(size)
align = gtk.Alignment(0) # left aligned (default is centered)
align.add(entry)
table.attach(align, col, col+1, row, row+1)
else:
entry = gtk.Entry()
table.attach(entry, col, col+1, row, row+1)
return entry
def table_add_widget_row(table, row, label, widget, fullspan = False):
"table_add_widget_row(table,row,label,widget) -> widget"
# add given label right aligned to given row in given table
# add given widget to the right column and returns it
# return entry for that line
if fullspan:
col = 0
else:
col = table.get_data("col_offset")
if label:
label = gtk.Label(label)
align = gtk.Alignment(1)
align.add(label)
table.attach(align, col, col+1, row, row+1, gtk.FILL)
if fullspan:
col = table.get_data("col_offset")
table.attach(widget, 1, col+2, row, row+1)
else:
table.attach(widget, col+1, col+2, row, row+1)
return widget
def table_add_radio_rows(table, row, label, texts, cb = None):
"table_add_radio_rows(table,row,label,texts[,cb]) -> [radios]"
# - add given label right aligned to given row in given table
# - create/add radio buttons with given texts to next row, set
# the one given as "active" as active and set 'cb' as their
# "toggled" callback handler
# - return array or radiobuttons
label = gtk.Label(label)
align = gtk.Alignment(1)
align.add(label)
col = table.get_data("col_offset")
table.attach(align, col, col+1, row, row+1, gtk.FILL)
radios = []
radio = None
box = gtk.VBox()
for text in texts:
radio = gtk.RadioButton(radio, text)
if cb:
radio.connect("toggled", cb, text)
radios.append(radio)
box.add(radio)
table.attach(box, col+1, col+2, row, row+1)
return radios
def table_add_separator(table, row):
"table_add_separator(table,row)"
widget = gtk.HSeparator()
endcol = table.get_data("n-columns")
# separator for whole table width
table.attach(widget, 0, endcol, row, row+1, gtk.FILL)
# -----------------------------
# File selection helpers
def get_open_filename(title, parent, path = None):
buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
fsel = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_OPEN, buttons)
fsel.set_local_only(True)
if path:
fsel.set_filename(path)
if fsel.run() == gtk.RESPONSE_OK:
filename = fsel.get_filename()
else:
filename = None
fsel.destroy()
return filename
def get_save_filename(title, parent, path = None):
buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
fsel = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_SAVE, buttons)
fsel.set_local_only(True)
fsel.set_do_overwrite_confirmation(True)
if path:
fsel.set_filename(path)
if not os.path.exists(path):
# above set only folder, this is needed to set
# the file name when the file doesn't exist
fsel.set_current_name(os.path.basename(path))
if fsel.run() == gtk.RESPONSE_OK:
filename = fsel.get_filename()
else:
filename = None
fsel.destroy()
return filename
# File selection button with eject button
class FselAndEjectFactory:
def __init__(self):
pass
def get(self, label, path, filename, action):
"returns file selection button and box having that + eject button"
fsel = gtk.FileChooserButton(label)
# Hatari cannot access URIs
fsel.set_local_only(True)
fsel.set_width_chars(12)
fsel.set_action(action)
if filename:
fsel.set_filename(filename)
elif path:
fsel.set_current_folder(path)
eject = create_button("Eject", self._eject, fsel)
box = gtk.HBox()
box.pack_start(fsel)
box.pack_start(eject, False, False)
return (fsel, box)
def _eject(self, widget, fsel):
fsel.unselect_all()
# Gtk is braindead, there's no way to set a default filename
# for file chooser button unless it already exists
# - set_filename() works only for files that already exist
# - set_current_name() works only for SAVE action,
# but file chooser button doesn't support that
# i.e. I had to do my own (less nice) container widget...
class FselEntry:
def __init__(self, parent, validate = None, data = None):
self._parent = parent
self._validate = validate
self._validate_data = data
entry = gtk.Entry()
entry.set_width_chars(12)
entry.set_editable(False)
hbox = gtk.HBox()
hbox.add(entry)
button = create_button("Select...", self._select_file_cb)
hbox.pack_start(button, False, False)
self._entry = entry
self._hbox = hbox
def _select_file_cb(self, widget):
fname = self._entry.get_text()
while True:
fname = get_save_filename("Select file", self._parent, fname)
if not fname:
# assume cancel
return
if self._validate:
# filename needs validation and is valid?
if not self._validate(self._validate_data, fname):
continue
self._entry.set_text(fname)
return
def set_filename(self, fname):
self._entry.set_text(fname)
def get_filename(self):
return self._entry.get_text()
def get_container(self):
return self._hbox

255
readme.txt Normal file
View File

@ -0,0 +1,255 @@
Hatari
Version 1.9, September 2015
http://hatari.tuxfamily.org/
Contents:
---------
1. License
2. What is Hatari?
3. Compiling and installing
3.1 WinUAE and "old" UAE CPU cores
3.2 IPF support using capsimage library
3.3 Notes for Linux distribution packagers
3.3.1 Known distro problems
4. Running Hatari
5. Contact
1) License
----------
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 Soft-
ware 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
Linking Hatari statically or dynamically with other modules is making a
combined work based on Hatari. Thus, the terms and conditions of the GNU
General Public License cover the whole combination.
In addition, as a special exception, the copyright holders of Hatari give you
permission to combine Hatari with free software programs or libraries that are
released under the GNU LGPL and with code included in the standard release
of the IPF support library (a.k.a. libcapsimage, see http://www.softpres.org/
for more information) under the Software Preservation Society Licence Agreement
as it has been defined for IPF library version 4.2 and 5.1. Linking against modified
versions of the IPF library is also allowed, as long as neither the license
nor the purpose of the library (accessing .ipf or .ctr disk images) was changed.
You may copy and distribute such a system following the terms of the GNU GPL
for Hatari and the licenses of the other code concerned.
2) What is Hatari?
------------------
Hatari is an Atari ST/STE/TT/Falcon emulator for Linux, FreeBSD, NetBSD,
BeOS, Mac-OSX and other Systems which are supported by the SDL library.
Unlike most other open source ST emulators which try to give you a good
environment for running GEM applications, Hatari tries to emulate the hardware
as close as possible so that it is able to run most of the old Atari games
and demos. Because of this, it may be somewhat slower than less accurate
emulators.
3) Compiling and installing
---------------------------
For using Hatari, you need to have installed the following libraries:
Required:
- The SDL library v1.2.10 or newer (http://www.libsdl.org)
- The zlib compression library (http://www.gzip.org/zlib/)
Optional:
- The PNG image library for PNG format screenshots and to decrease
AVI video recording file sizes (http://www.libpng.org/)
- The GNU Readline library for Hatari debugger command line editing
- The Xlib library to support Hatari Python UI window embedding on
systems with the X window system (Linux and other unixes)
- The portaudio library for Falcon microphone handling
- The IPF support library (http://www.softpres.org/download)
Don't forget to also install the header files of these libraries for compiling
Hatari (some Linux distributions use separate development packages for these
header files)!
For compiling Hatari, you need a C compiler (preferably GNU C), and a working
CMake (v2.8 or newer) installation, see http://www.cmake.org/ for details.
CMake can generate makefiles for various flavours of "Make" (like GNU-Make)
and various IDEs like Xcode on Mac OS X. To run CMake, you've got to pass the
path to the sources of Hatari as parameter, for example run the following if
you are in the topmost directory of the Hatari source tree:
cmake .
If you're tracking Hatari version control, it's preferable to do
the build in a separate build directory as above would overwrite
the (non-CMake) Makefiles coming with Hatari:
mkdir -p build
cd build
cmake ..
Have a look at the manual of CMake for other options. Alternatively, you can
use the "cmake-gui" program to configure the sources with a graphical
application or "ccmake" to configure them with ncurses UI.
For your convenience we also ship an old-fashioned configure script which can
be used as a wrapper for running cmake. Type "./configure --help" to see the
options of this script.
Assuming that you've used the Makefile generator of CMake, and cmake finished
the configuration successfully, you can compile Hatari by typing "make". If all
works fine, you'll get the executable "hatari" in the src/ subdirectory of the
build tree. You can then install the emulator by typing "make install".
3.1) WinUAE and "old" UAE CPU cores
By default Hatari is built with the "old" UAE CPU core used in the
earlier Hatari releases, but versions starting from v1.5 support also
new & experimental WinUAE CPU core which offers more cycle accurate
030 & DSP emulation and from v1.6 onwards also working 030 MMU
emulation.
The WinUAE CPU core can be enabled by toggling the ENABLE_WINUAE_CPU
variable in the Hatari CMake configuration (e.g. with the interactive
"ccmake" program). Alternatively, you can run "./configure
--enable-winuae-cpu", which will run cmake with the correct
parameters.
The plan is to eventually have WinUAE CPU core enabled by default and
deprecate the "old" UAE CPU core, but currently WinUAE CPU core:
- is lacking all the ST/STE specific tweaks and proper testing
for ST/STE compatibility
- despite better emulation, it still doesn't run all the Falcon
programs that run with the "old" core although it works better
for most of them
- doesn't have full debugger support
It's recommended to use Hatari built with the "old" (default) UAE CPU
core for ST/STE emulation and the new WinUAE core for Falcon emulation.
And test also the old core if Falcon programs don't work with the new
one...
3.2) IPF support using capsimage library
Hatari can use the optionnal capsimage library to access IPF and CTR
files. Those files are created using the Kryoflux board and allow to
record MFM exact copies of original games, including the protection.
Version 4.2 of the library allows to access IPF files, while the more recent
version 5.1 fixes some bugs, as well as adding support for CTR files.
Since version 5.1 is not yet available for all OSes in binary form, Hatari
still default to version 4.2 (but you can compile capsimage 5.1 sources
to build your library). You can change this by modifying "SET(CAPSIMAGE_VERSION 4)"
into cmake/FindCapsImage.cmake
Refer to http://softpres.org/download and get the corresponding file
from the "User Distribution" section that matches your OS.
For version 4.2, you should have the following files in your include path :
/usr/local/include/caps/
capsimage.h
fdc.h
form.h
For version 5.1, you should have the following files in your include path :
/usr/local/include/caps5/
CapsAPI.h
CapsFDC.h
CapsForm.h
CapsLibAll.h
CapsLib.h
CapsLibVersion.h
ComLib.h
CommonTypes.h
You should also copy the libcapsimage.so* files in your library path,
for example in /usr/local/lib/caps/ or /usr/local/lib/caps5/
3.3) Notes for Linux distribution packagers
TOS tester in tests/tosboot/ directory can be used to verify that
Hatari was built fine enough that it's able to boot all tested TOS
versions in various different HW configurations and run some GEMDOS
based tests. For EmuTOS, use version v0.8.7 or newer, older versions
are buggy and fail the GEMDOS tests.
If Hatari package will have two application menu entries for Hatari,
one for the Python UI embedding Hatari, and another one for the plain
SDL version, the latter could open also a terminal window for Hatari
command line debugger and its console messages:
x-terminal-emulator -T "Hatari debug window, invoke debugger with AltGr+Pause" -e hatari
tools/hatari-tos-register.sh is a minimal example of Linux init script
registering Hatari as a (binfmt_misc) handler for TOS binaries.
Alternatively one could add a mime type for TOS binaries with xdg-mime:
http://portland.freedesktop.org/xdg-utils-1.0/xdg-mime.html
But registering handlers for mime-types seems desktop specific.
3.3.1) Known distro problems
Old RHEL 5 and the derived CentOS v5.x Linux distributions ship
with a broken readline library:
https://bugzilla.redhat.com/show_bug.cgi?id=499837
To get CMake readline detection and linking working on them,
you need to give these as extra arguments to the "cmake" command:
-DCMAKE_C_FLAGS=-lncurses -DCMAKE_EXE_LINKER_FLAGS=-lncurses
They also have too old Python/PyGtk version for the python based
Hatari scripts. Here are patches for Hatari v1.5/v1.6 Python UI:
http://listengine.tuxfamily.org/lists.tuxfamily.org/hatari-devel/2012/01/msg00008.html
4) Running Hatari
-----------------
For information about how to use the running emulator, please read the file
doc/manual.html. Here are just some hints for the impatient people:
* Before you can run the emulator, you need a TOS ROM image. If one
named as "tos.img" is neither in the data directory of the emulator
(DATADIR variable in CMake configuration), or in the current
directory, Hatari will ask you to select one.
- Hatari binary packages ship unmodified EmuTOS ROM image with them
(renamed as tos.img), but you need an original Atari TOS ROM image
for best compatibility. For more information on EmuTOS, see
doc/emutos.txt.
* While the emulator is running, you can open the configuration menu
by pressing F12, the F11 key will toggle fullscreen/windowed mode.
Pressing ALTGR-q will quit the emulator.
5) Contact
----------
If you want to contact the authors of Hatari, please have a look at the file
doc/authors.txt for the e-mail addresses or use the Hatari mailing list.
Visit the website of Hatari on Tuxfamily.org for more details:
http://hatari.tuxfamily.org/contact.html

25
share/CMakeLists.txt Normal file
View File

@ -0,0 +1,25 @@
foreach(size 32x32 48x48 64x64 128x128 256x256)
install(FILES icons/hicolor/${size}/apps/hatari.png
DESTINATION ${ICONDIR}/${size}/apps)
install(FILES icons/hicolor/${size}/mimetypes/application-x-st-disk-image.png
DESTINATION ${ICONDIR}/${size}/mimetypes)
foreach(type vnd.msa vnd.fastcopy x-stx)
install(CODE "execute_process(COMMAND ln -sf application-x-st-disk-image.png
${CMAKE_INSTALL_PREFIX}/${ICONDIR}/${size}/mimetypes/application-${type}-disk-image.png)
")
endforeach()
endforeach()
install(FILES icons/hicolor/scalable/apps/hatari.svg
DESTINATION ${ICONDIR}/scalable/apps)
install(FILES icons/hicolor/scalable/mimetypes/application-x-st-disk-image.svg
DESTINATION ${ICONDIR}/scalable/mimetypes)
foreach(type vnd.msa vnd.fastcopy x-stx)
install(CODE "execute_process(COMMAND ln -sf application-x-st-disk-image.svg
${CMAKE_INSTALL_PREFIX}/${ICONDIR}/scalable/mimetypes/application-${type}-disk-image.svg)
")
endforeach()
install(FILES mime/packages/hatari.xml DESTINATION share/mime/packages)
install(FILES applications/hatari.desktop DESTINATION share/applications)

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Hatari
GenericName=ST emulator
Comment=Run old ST/STE/TT/Falcon software
Exec=hatari %f
Icon=hatari
MimeType=application/x-st-disk-image;application/vnd.msa-disk-image;application/vnd.fastcopy-disk-image;application/x-stx-disk-image
Categories=Game;Emulator;
Terminal=false

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,63 @@
<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg2" version="1.1" inkscape:version="0.48.4 r9939" width="256" height="256">
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<defs id="defs6">
<linearGradient id="linearGradient5300">
<stop style="stop-color:#282850;stop-opacity:1;" offset="0" id="stop5302"/>
<stop style="stop-color:#6060a0;stop-opacity:1;" offset="1" id="stop5304"/>
</linearGradient>
<linearGradient id="linearGradient5290">
<stop style="stop-color:#000030;stop-opacity:1;" offset="0" id="stop5292"/>
<stop style="stop-color:#101060;stop-opacity:1;" offset="1" id="stop5294"/>
</linearGradient>
<linearGradient id="linearGradient5280">
<stop style="stop-color:#000032;stop-opacity:1;" offset="0" id="stop5282"/>
<stop style="stop-color:#101040;stop-opacity:1;" offset="1" id="stop5284"/>
</linearGradient>
<linearGradient id="linearGradient5270">
<stop style="stop-color:#404080;stop-opacity:1;" offset="0" id="stop5272"/>
<stop style="stop-color:#000040;stop-opacity:1;" offset="1" id="stop5274"/>
</linearGradient>
<linearGradient id="linearGradient5241">
<stop style="stop-color:#282840;stop-opacity:1;" offset="0" id="stop5243"/>
<stop style="stop-color:#b4b4d0;stop-opacity:1;" offset="1" id="stop5245"/>
</linearGradient>
<linearGradient id="linearGradient5231">
<stop style="stop-color:#000000;stop-opacity:1;" offset="0" id="stop5233"/>
<stop style="stop-color:#404040;stop-opacity:1;" offset="1" id="stop5235"/>
</linearGradient>
<linearGradient id="linearGradient5221">
<stop style="stop-color:#acacac;stop-opacity:1;" offset="0" id="stop5223"/>
<stop style="stop-color:#202020;stop-opacity:1;" offset="1" id="stop5225"/>
</linearGradient>
<linearGradient id="linearGradient5209">
<stop style="stop-color:#000000;stop-opacity:1;" offset="0" id="stop5211"/>
<stop style="stop-color:#0060e0;stop-opacity:1;" offset="1" id="stop5213"/>
</linearGradient>
<linearGradient id="linearGradient3769" osb:paint="solid">
<stop style="stop-color:#00fb00;stop-opacity:1;" offset="0" id="stop3771"/>
</linearGradient>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient5221" id="linearGradient5227" x1="69.9" y1="1.8" x2="191.9" y2="87.9" gradientUnits="userSpaceOnUse"/>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient5231" id="linearGradient5237" x1="141.2" y1="38.4" x2="176.3" y2="57.3" gradientUnits="userSpaceOnUse"/>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient5241" id="linearGradient5247" x1="41" y1="105.2" x2="221" y2="254.9" gradientUnits="userSpaceOnUse"/>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient5270" id="linearGradient5276" x1="9.9" y1="127.9" x2="249.8" y2="127.9" gradientUnits="userSpaceOnUse"/>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient5280" id="linearGradient5286" x1="32.6" y1="5.9" x2="195.2" y2="87.5" gradientUnits="userSpaceOnUse"/>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient5290" id="linearGradient5296" x1="17.5" y1="224.9" x2="29.3" y2="236.2" gradientUnits="userSpaceOnUse"/>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient5300" id="linearGradient5306" x1="17.4" y1="27.8" x2="25" y2="31.3" gradientUnits="userSpaceOnUse"/>
</defs>
<path style="color:#000000;fill:#000080;stroke:url(#linearGradient5276);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" d="m 13.8 1.7 18.4 0 3.1 4.6 6.9 0 2.7 -3.5 148 0 0.1 3.8 6.8 0 4.5 -4 33.1 0.2 11.9 13 0 234 -3.1 4.1 -232.4 0 -3.5 -4.6 0 -244.7 z" id="path5268" inkscape:connector-curvature="0"/>
<path style="color:#000000;fill:none;stroke:url(#linearGradient5286);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" d="m 33.5 4.7 0.1 78.4 1.4 2.9 2.8 2 152.9 0 2.3 -2.5 1.9 -2.8 -0 -75.5" id="path5278" inkscape:connector-curvature="0"/>
<path style="fill:#808080;stroke:url(#linearGradient5227);stroke-width:1.01021373px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 68.1 2.1 0 80.4 6.3 4.5 116.2 -0 3.5 -4.5 -0 -80.4 z" id="path5219" inkscape:connector-curvature="0"/>
<path style="color:#000000;fill:#000080;stroke:url(#linearGradient5237);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" d="m 142.7 15.9 0.2 65.4 32 0 -0.4 -65.6 z" id="path5229" inkscape:connector-curvature="0"/>
<path style="color:#000000;fill:#ececec;stroke:url(#linearGradient5247);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" d="m 40.7 106 176.8 -0.3 5.2 5.2 0.3 142.9 -186.8 0 0 -143.2 z" id="path5239" inkscape:connector-curvature="0"/>
<rect style="fill:#000000;fill-rule:evenodd;stroke:url(#linearGradient5296);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="rect5288" width="13" height="10.3" x="16.8" y="225.4" ry="0.5"/>
<path style="color:#000000;fill:none;stroke:url(#linearGradient5306);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" d="m 19.5 18.7 4 8.8 0 4 -2 -0 -0 7 -3.9 -0 -0 -6.9 -2 -0 0 -4 z" id="path5298" inkscape:connector-curvature="0"/>
<path id="path5358" d="m 74.9 201.2 c 0 2.7 -0.7 7 1 9.3 2.3 3.1 8.5 3.4 12 3.9 11.4 1.9 29.2 1.4 34.4 -11.2 1.2 -2.8 1.6 -6 1.6 -9 -0 -24.9 -27.7 -13.5 -37.2 -28 -4.1 -6.3 1.4 -12.6 7.3 -14.8 8.5 -3.1 18.1 0.8 26 3.9 -0 -20.4 -39 -16.1 -44.2 1 -0.8 2.6 -0.9 5.3 -0.7 8 1.7 22 28.2 11.8 37.2 26 4 6.3 -1.1 13.5 -7.3 15.7 -10 3.7 -20.8 -1 -30 -4.7 m 55 -57 0 8 25 0 0 62 10 0 0 -62 25 0 0 -8 -60 0 z" style="fill:#ff0000;stroke:none" inkscape:connector-curvature="0"/>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-st-disk-image">
<sub-class-of type="application/octet-stream"/>
<comment>ST disk image</comment>
<glob pattern="*.st"/>
</mime-type>
<mime-type type="application/vnd.msa-disk-image">
<sub-class-of type="application/octet-stream"/>
<comment>Magic Shadow Archiver disk image</comment>
<glob pattern="*.msa"/>
<magic priority="90">
<match type="big16" offset="0" value="0x0E0F"/>
</magic>
</mime-type>
<mime-type type="application/vnd.fastcopy-disk-image">
<sub-class-of type="application/octet-stream"/>
<comment>FastCopy DIM disk image</comment>
<glob pattern="*.dim"/>
<magic priority="90">
<match type="big16" offset="0" value="0x4242"/>
</magic>
</mime-type>
<mime-type type="application/x-stx-disk-image">
<sub-class-of type="application/octet-stream"/>
<comment>Pasti STX disk image</comment>
<glob pattern="*.stx"/>
<magic priority="90">
<match type="big32" offset="0" value="0x52535900"/>
</magic>
</mime-type>
</mime-info>

Some files were not shown because too many files have changed in this diff Show More