Technical Documentation: joncampbell123/dosbox-x
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (README.md)
Welcome to the DOSBox-X project homepage located on GitHub.
Useful links
- DOSBox-X's website (https://dosbox-x.com or http://dosbox-x.software)
- DOSBox-X's Wiki page
- The DOSBox-X project has a Discord server. Please ask in the discussion tab for a one-time invite link to join.
- Releases
- Development/Nightly builds
- Install instructions
- Build instructions
Table of Contents
- Introduction to DOSBox-X
- Notable features in DOSBox-X
- DOSBox-X supported platforms and releases
- Compatibility with DOS programs and games
- Contributing to DOSBox-X
- DOSBox-X development and release pattern
- Future development experiments
- Software security comments
- Features that DOSBox-X is unlikely to support at this time
- Origin and history of the DOSBox-X project
- Known DOSBox-X forks
- Support for international language translations and keyboard layouts
Introduction to DOSBox-X
DOSBox-X is a cross-platform DOS emulator based on the DOSBox project (www.dosbox.com).
Like DOSBox, it emulates a PC necessary for running many MS-DOS games and applications that simply cannot be run on modern PCs and operating systems. However, while the main focus of DOSBox is for running DOS games, DOSBox-X goes much further than this. Started as a fork of the DOSBox project, it retains compatibility with the wide base of DOS games and DOS gaming DOSBox was designed for. But it is also a platform for running DOS applications, including emulating the environments to run Windows 3.x, 9x and ME and software written for those versions of Windows. By adding official support for Windows 95, 98, ME emulation and acceleration, we hope that those old Windows games and applications could be enjoyed or used once more. Moreover, DOSBox-X adds support for DOS/V and NEC PC-98 emulations so that you can play DOS/V and PC-98 games with it.
Compared with DOSBox, DOSBox-X focuses more on general emulation and accuracy. In order to help running DOS games and applications, Windows 3.x/9x/ME, as well as for the purpose of historical preservation, testing and continued DOS developments, it is our desire to implement accurate emulation, accurate enough to help make new DOS developments possible with confidence the program will run properly on actual DOS systems. DOSBox-X includes various features for different purposes (some of them ported from other projects), which are implemented as incremental changes since it was forked from DOSBox SVN Daum. DOSBox-X provides many ways to tweak and configure the DOS virtual machine, as we believe a better way to emulate the DOS platform is to give users all the options they need to emulate everything from the original IBM PC system all the way up to late 1990's configuration, whatever it takes to get your game or software package to run. Our goal is to eventually make DOSBox-X a complete emulation package that covers all pre-2000 DOS and Windows 9x based system scenarios, including peripherals, motherboards, CPUs, and all manner of hardware that was made for PC hardware of that time.
Please check out the DOSBox-X homepage for common packages of the latest release for the supported platforms, as well as screenshots of some DOS programs and games running in DOSBox-X. Also see the INSTALL page for DOSBox-X installation instructions and other packages, and the Releases page for archives of all released DOSBox-X versions. For more information about DOSBox-X, such as setting up and running DOSBox-X including its usage tips, please read the user guide in the DOSBox-X Wiki. Steps for building the source code can be found in the BUILD page.
DOSBox-X is completely open-source and free of charge to use and distribute. It is released under the GNU General Public License, version 2. See also the About DOSBox-X page for more information about DOSBox-X's goals and non-goals, along with some links to other projects.
This project has a Code of Conduct, please read it for general information on contributing to or getting support from the project.
Brought to you by: joncampbell123 (Jonathan Campbell)
Legal notice
If you reside in a jurisdiction that enforces OS level age verification, you are only permitted to use the main-osfree branch of this project (including builds), and are prohibited from using the main (master) branch of this project.
Notable features in DOSBox-X
Although based on the DOSBox project, DOSBox-X is now a separate project because both have their own separate schedules and development priorities. For example, the main focus of DOSBox is for running DOS games whereas DOSBox-X goes way beyond this. At this time DOSBox-X already has a great number of features that do not exist in DOSBox. Examples of such features include:
GUI drop-down menu and built-in graphical configuration tool
Save and load state support (with up to 100 save slots + save files)
NEC PC-98, AX, DOS/V emulation and Chinese/Japanese/Korean support
Fully translatable user interfaces (with language files available)
Better support and compatibility with DOS applications
Support for more DOS commands and built-in external tools
Support for different ways to customize the internal Z: drive
Support for CPU types like Pentium Pro, II, III and MMX instructions
Support for IDE interfaces and improved Windows 3.x/9x emulation
Support for long filenames and FAT32 disk images (DOS 7+ features)
Support for pixel-perfect scaling output for improved image quality
Support for TrueType font (TTF) output for text-mode DOS programs
Support for printing features, either to a real or to a virtual printer
Support for starting programs to run on the host systems (-hostrun option)
Support for 3dfx Voodoo chip and Glide emulation (including Glide wrapper)
Support for cue sheets with FLAC, MP3, WAV, OGG Vorbis and Opus CD-DA tracks
Support for FluidSynth MIDI synthesizer (with sound fonts) and MT-32 emulation
Support for NE2000 Ethernet for networking features and modem phone book mapping
Support for features such as V-Sync, overscan border and stereo swapping
Plus many more..
While the vast majority of features in DOSBox-X are cross-platform, DOSBox-X does also have several notable platform-dependent features, such as Direct3D output and support for automatic drive mounting on the Windows platform. These features cannot be easily ported to other platforms. More information about DOSBox-X's features can be found in DOSBox-X’s Feature Highlights page in the DOSBox-X Wiki.
DOSBox-X officially supports both SDL 1.2 and SDL 2.0; both 32-bit and 64-bit builds are also supported.
DOSBox-X supported platforms and releases
DOSBox-X is a cross-platform DOS emulator, so all major host operating systems are officially supported, including:
Windows (95/NT4 or higher), 32-bit and 64-bit
Linux (with X11), 32-bit and 64-bit
macOS (Mac OS X), Intel and ARM-based 64-bit
DOS (MS-DOS 5.0+ or compatible)
Windows binaries (both 32-bit and 64-bit), Linux Flatpak or RPM packages (64-bit), macOS packages (64-bit) and DOS versions are officially released periodically, typically on the last day of a month or the first day of the next month. Please check out the DOSBox-X homepage and the INSTALL page for the latest DOSBox-X packages on these platforms and further installation instructions. You can also find ZIP packages or Windows installers for all released versions and their change logs in the Releases page. The Window installers are intended to ease the installation process, and they allow you to start DOSBox-X as soon as the installation ends.
For running DOSBox-X in a real DOS system (MS-DOS or compatible), you can find the HX-DOS package that makes use of the freely-available HX DOS Extender. Type DOSBOX-X to run it from a DOS system. There is also the DOS LOADLIN package which can run from within DOSBox-X itself in addition to a DOS system. Note, however, that not all features of DOSBox-X that are supported in other platforms can be supported in the real DOS environment.
Development (preview) builds intended for testing purposes for various platforms are also available from the DOSBox-X Development Builds page.
The full source code is officially provided with each DOSBox-X release, which may be compiled to run on the above and possibly other operating systems too. You can also get the latest development source code from the repository directly. See also the BUILD page for information on building/compiling the DOSBox-X source code.
Compatibility with DOS programs and games
With the eventual goal of being a complete DOS emulation package that covers all pre-2000 DOS and Windows 3.x/9x based hardware scenarios, we are making efforts to ensure that the vast majority of DOS games and applications will run in DOSBox-X, and these include both text-mode and graphical-mode DOS programs. Microsoft Windows versions that are largely DOS-based (such as Windows 3.x and 9x) are officially supported by DOSBox-X as well. Note that certain config settings may need to be changed from the default ones for some of these programs to work smoothly. Take a look at the DOSBox-X Wiki for more information.
Efforts are also made to aid continued DOS developments by attempting to accurately emulate the hardware, which is why DOSBox-X used to focus on the demoscene software (especially anything prior to 1996) because that era of the MS-DOS scene tends to have all manner of weird hardware tricks, bugs, and speed-sensitive issues that make them the perfect kind of stuff to test emulation accuracy against, even more so than old DOS games. But without a doubt we are also making a lot of efforts to test DOSBox-X against other DOS games and applications, as well as PC-98 programs (most of them are games).
We add new features and make other improvements in every new DOSBox-X version, so its compatibility with DOS programs and games are also improving over time. If you have some issue with a specific DOS program or game, please feel free to post it in the issue tracker.
Contributing to DOSBox-X
We encourage new contributors by removing barriers to entry.
Ideas and patches are always welcome, though not necessarily accepted.
If you really need that feature or change, and your changes are not
accepted into this main project (or you just want to mess around with
the code), feel free to fork this project and make your changes in
your fork.
As joncampbell123 only has limited time to work on DOSBox-X, help is
greatly appreciated:
- Testing
- Features of DOSBox-X, such as its commands and functions
- The normal operation of DOS games and applications
- Windows 1.0/2.x/3.x & Windows 95/98/ME guest system support
- Software or hardware emulation accuracy, helped by for example demoscene software
- Write more unit tests to test various functions (see existing unit tests in tests/)
- Developments of new DOS software (possibly aided by DOSLIB/DOSLIB2)
- Bug fixes, patches, improvements, refinements
- Suggestions, ideas, assistance of other users, and/or general conversation
- Platform support (Windows, Linux, macOS, DOS, but others are welcome)
- Documentation, language file translation, and software packaging
- Notes regarding DOS and Win3.x/9x games, applications, hacks or weird tricks, etc.
See the CONTRIBUTING page for more contribution guidelines.
If you want to tweak or write some code and you don't know what to work on,
feel free to visit the issue tracker to get some ideas.
For more descriptions on the source code, please take a look at the
DOSBox-X source code description page. Information on
building on the source code can be found in the BUILD page.
Information about the debugger is also available in the
DOSBox-X Debugger page.
See also the CREDITS page for crediting information.
DOSBox-X development and release pattern
In order to make DOSBox-X's development process more smooth, we have implemented a general development/release pattern for DOSBox-X. The current release pattern for DOSBox-X is as follows:
New DOSBox-X versions are made public at the start (typically on the first day) of each month, including the source code and binary releases. Then the DOSBox-X developments will be re-opened for new features, pull requests, etc. There will be no new features added 6 days before the end of the month, but only bug fixes. The last day of the month is DOSBox-X’s build day to compile for binary releases the first of the next month, so there will be no source code changes on this day including pull requests or bug fixes.
For example, suppose August is the current month - August 25th will be the day pull requests will be ignored unless only bug fixes. August 31st (the last day of August) will be DOSBox-X build day.
This is DOSBox-X’s official release pattern, although it may change later.
Future development experiments
Scattered experiments and small projects are in experiments/ as proving grounds for future revisions to DOSBox-X and its codebase.
These experiments may or may not make it into future revisions or the next version.
Comments are welcome on the experiments, to help improve the code overall.
There are also patches in patch-integration/ for possible feature integrations in the future. We have already integrated many community-developed patches into DOSBox-X in the past.
See also General TODO.txt for some plans of future DOSBox-X developments.
Software security comments
DOSBox-X cannot claim to be a "secure" application. It contains a lot of
code designed for performance, not security. There may be vulnerabilities,
bugs, and flaws in the emulation that could permit malicious DOS executables
within to cause problems or exploit bugs in the emulator to cause harm.
There is no guarantee of complete containment by DOSBox-X of the guest
operating system or application.
If security is a priority, then:
Do not use DOSBox-X on a secure system.
Do not run DOSBox-X as root or Administrator.
If you need to use DOSBox-X, run it under a lesser privileged user, in
a chroot jail or sandbox, or enable DOSBox-X's secure mode with its
command-line option -securemode, which disables commands that may
allow access to the host system.
If your Linux distribution has it enabled, consider using the auditing
system to limit what the DOSBox-X executable is allowed to do.
Features that DOSBox-X is unlikely to support at this time
DOSBox-X aims to be a fully-featured DOS emulation package, but there are
some things the design as implemented now cannot accommodate.
Pentium 4 or higher CPU level emulation.
DOSBox-X contains code only to emulate the 8086 through the Pentium III.
Real DOS systems (MS-DOS and compatibles) also work best with these CPUs.If Pentium 4 or higher emulation is desired, consider using a PC
emulator like Bochs or QEMU instead. DOSBox-X may eventually develop
Pentium 4 emulation, if wanted by the DOSBox-X community in general.Emulation of PC hardware 2001 or later.
The official cutoff for DOSBox-X is 2001, when updated "PC 2001"
specifications from Microsoft mandated the removal of the ISA slots
from motherboards. The focus is on implementing hardware emulation
for hardware made before that point.Contributors are free to focus on emulating hardware within the
time frame between 1980 and 2000/2001 of their choice.Windows guest emulation, Windows Vista or later.
DOSBox-X emulation, in terms of running Windows in DOSBox-X, will
focus primarily on Windows 1.0 through Windows ME (Millennium Edition),
and then on Windows NT through Windows XP. Windows Vista and later
versions are not a priority and will not be considered at this time.
These versions of Windows are not based on DOS.If you need to run Windows XP and later, please consider using
QEMU, Bochs, VirtualBox, or VMware.Any MS-DOS system other than IBM PC/XT/AT, AX, Tandy, PCjr, and PC-98.
Only the above listed systems will be considered for development
in DOSBox-X. This restriction prevents stretching of the codebase
to an unmanageable level and helps keep the code base organized.It would be easier on myself and the open source community if
developers could focus on emulating their platform of interest in
parallel instead of putting everything into one project that,
most likely, will do a worse job overall emulating all platforms.
However, if adding emulation of the system requires only small
minimal changes, then the new system in question may be considered.You are strongly encouraged to fork this project and implement
your own variation if you need to develop MS-DOS emulation for
any other system or console. In doing that, you gain the complete
freedom to focus on implementing the particular MS-DOS based
system of interest, and if desired, the ability to strip away
conflicting IBM PC/XT/AT emulation and unnecessary code to keep
your branch's code manageable and maintainable.If you are starting a fork, feel free to let me know where your
fork is and what system it is emulating, so I can list it in
this README file for others seeking emulation of that system. To
help, I have added machine and video mode enumerations as "stubs"
to provide a starting point for your branch's implementation of
the platform. A stub implemented so far is "FM Towns emulation"
(machine=fm_towns).Cycle-accurate timing of x86 instructions and execution.
Instructions generally run one per cycle in DOSBox-X, except for I/O
and memory access.If accurate emulation of cycles per instruction is needed, please
consider using PCem, 86Box, or VARCem instead.Full precision floating point emulation.
Unless using the dynamic core, DOSBox and DOSBox-X emulate the FPU
registers using the "double" 64-bit floating point data type.The Intel FPU registers are 80-bit "extended precision" floating point
values, not 64-bit double precision, so this is effectively 12 bits of
precision loss and 5 bits of range loss (64 to 53 mantissa bits and 16
to 11 exponent bits). This slight loss of precision is perfectly fine
considering DOSBox's original goal in supporting DOS games, but may
cause problems in other cases that need the full precision.It is known at this time that this lack of precision is enough to
cause otherwise straightforward comparisons against integers to
fail in DOS applications originally written in QBasic or Turbo Basic.
There are such DOS games written that check their file size using
a floating point compare that will fail in this manner. To run these
games, you will need to disable FPU emulation (fpu=false) to force
the QBasic/TurboBasic runtime to use software emulation instead.
Origin and history of the DOSBox-X project
DOSBox-X started as a fork of the original DOSBox project sometime
in mid-2011. It was started out of a desire to improve the emulator
without having to fight with or worry about submitting patches
upstream.
As its developers have made it clear, DOSBox's main focus is on
DOS games. This is evident by the fact that much of the code is
somewhat accurate code with kludges to make DOS games run,
instead of focusing on the actual behaviors of real DOS systems.
Jonathan Campbell, the DOSBox-X project maintainer wanted to make
various changes to the source code, but many of them were non-game
related, and thus were unlikely to be accepted by the DOSBox developers.
Since then, Jonathan Campbell has been modifying the source code over
time to improve emulation, fix bugs, and resolve incompatibilities
with Windows 95 through ME. He has added options so that DOSBox-X
by default can emulate a wider variety of configurations more
accurately, while allowing the user to enable various techniques or
hacks if needed to run their favorite DOS games or programs. He has
also been cleaning up and organizing the code to improve stability
and portability where possible.
The original DOSBox project was not written by one programmer. It
has been under development since late 2000 with patches, fixes,
and improvements from members all over the Vogons forums. Despite
not having a major official release since DOSBox 0.74 over 10
years ago, the project is still in semi-active development today
in the form of DOSBox SVN. Meanwhile, some of the changes themselves
incorporated code from other projects.
Some features and improvements in DOSBox-X also came from another
branch of DOSBox known as DOSBox SVN Daum
which itself incorporated features from the original DOSBox
project, DOSBox-X, and many experimental patches. Although the
Daum branch seems to be dead, the features borrowed from it still
exists in DOSBox-X. Later on, DOSBox-X also incorporated several
features and improvements from other projects such as DOSBox ECE,
DOSBox Staging, DOSVAX/DOSVAXJ3, and vDosPlus.
The DOSBox-X project is also helped by its other developers and
contributors such as Wengier, aybe, Allofich, and rderooy, who have
done significant work to improve the DOSBox-X project, including
adding new features, fixing bugs, creating the documentation,
maintaining the website, and porting code from other projects.
See also the CREDITS page for crediting of the source code.
Known DOSBox-X forks
DOSBox-X Emscripten port (runnable in a web browser) by Yksoft1
Significant changes are made in order to run efficiently within the web browser when compiled using LLVM/Emscripten.
These significant changes require dropping some useful features (including the menus) but are required for performance.URL: https://github.com/yksoft1/dosbox-x-vanilla-sdl/tree/emscripten (look for clone URL and use the emscripten branch)
DOSBox-X-App (for Windows and macOS) by emendelson
DOSBox-X-App is a slightly customized version of DOSBox-X, combined with external programs and commands that make it
easy to print and create PDFs from DOS applications. It is customized for use with applications, not games.DOSBoxWP (for WordPerfect for DOS) by emendelson
DOSBoxWP is a customized version of DOSBox-X targeted for users of WordPerfect for DOS.
URL (Windows): http://www.columbia.edu/~em36/wpdos/dosboxwp.html
URL (macOS): http://www.columbia.edu/~em36/wpdos/wpdosboxmac.html
Win31DOSBox (Windows 3.1 for 64-bit Windows) by emendelson
Win31DOSBox aims to be an easy method of running Windows 3.x software for 64-bit Windows systems.
The system uses a custom build of DOSBox-X when running Windows 3.1x.
Support for international language translations and keyboard layouts
Translations
DOSBox-X displays English as the default language, and uses the U.S. code page (437) by default, just like DOSBox.
All messages displayed by DOSBox-X are in English with the default setting. DOSBox-X does support the feature to
change the display messages with the use of language files. The language files control all visible output of the
internal commands and the internal DOS, as well as the text in DOSBox-X's drop-down menus.
Language files can be found in the languages directory of your DOSBox-X installation or
https://github.com/joncampbell123/dosbox-x/blob/master/contrib/translations/ .
Language files currently available are:
Chinese (Simplified),
Chinese (Traditional),
Dutch,
French,
German,
Hungarian,
Italian,
Japanese,
Korean,
Portuguese (Brazilian),
Russian,
Spanish,
Turkish
If you are a speaker of any language not included above, you are encouraged to create additional language files by translating the messages to your language using the
English (Template) or generate one by CONFIG -wl filename command.
Keyboard layouts
DOSBox-X supports various keyboard layouts by keyboardlayout option in the .conf file or KEYB command.
On Windows, DOSBox-X will try to match the layout with your physical keyboard when keyboardlayout=auto.
On other platforms, the keyboard will work as US keyboard if not set otherwise.
The SDL1 version additionally requires use of scancodes by enabling usescancodes option, when using non-US keyboards.
The default setting (usescancodes=auto) should work in most cases, and this setting is NOT required and ignored for SDL2 versions.
If you find that a keyboard layout is not yet supported by DOSBox-X, in order to add additional layouts for use with DOSBox-X, please see file README.keyboard-layout-handling
on how to do so as a developer.
Codepage settings
You need to switch to the appropriate codepage in order to display various characters for your language.
You can set the codepage by KEYB or CHCP command. Type KEYB /? or CHCP /? for details.
For further information on international support and regional settings of DOSBox-X, such as steps to create DOSBox-X
language files or use external keyboard files in DOSBox-X, as well as support for the Euro symbol and country-specific
date and time formats, please look at the guide Regional settings in DOSBox-X in the DOSBox-X Wiki. For more information on East Asian (Chinese/Japanese/Korean) language support, see the East Asian language and system support guide page.
Vs Libpdcurses Doc README (`vs/libpdcurses/doc/README.md`)
PDCurses Documentation
Some plain text documents to provide a basic overview of PDCurses and
details for specific platforms, along with a program to combine them
with the "man page" sections from the source code files, into
"PDCurses.md".
Building
- Type "make". ("make clean" to remove the built files.) A Unix-like
environment is required for the Makefile. This is called automatically
when doing a top-level make of the X11 port.
Distribution Status
Public Domain, except for manext.c, which is GPL 2+.
Vs Libpdcurses Doc Intro (`vs/libpdcurses/doc/intro.md`)
PDCurses User's Guide
Curses Overview
The X/Open Curses Interface Definition describes a set of C-Language
functions that provide screen-handling and updating, which are
collectively known as the curses library.
The curses library permits manipulation of data structures called
windows which may be thought of as two-dimensional arrays of
characters representing all or part of a terminal's screen. The
windows are manipulated using a procedural interface described
elsewhere. The curses package maintains a record of what characters
are on the screen. At the most basic level, manipulation is done with
the routines move() and addch() which are used to "move" the curses
around and add characters to the default window, stdscr, which
represents the whole screen.
An application may use these routines to add data to the window in any
convenient order. Once all data have been added, the routine
refresh() is called. The package then determines what changes have
been made which affect the screen. The screen contents are then
changed to reflect those characters now in the window, using a
sequence of operations optimized for the type of terminal in use.
At a higher level routines combining the actions of move() and addch()
are defined, as are routines to add whole strings and to perform
format conversions in the manner of printf().
Interfaces are also defined to erase the entire window and to specify
the attributes of individual characters in the window. Attributes
such as inverse video, underline and blink can be used on a
per-character basis.
New windows can be created by allowing the application to build
several images of the screen and display the appropriate one very
quickly. New windows are created using the routine newwin(). For
each routine that manipulates the default window, stdscr, there is a
corresponding routine prefixed with w to manipulate the contents of a
specified window; for example, move() and wmove(). In fact, move(...)
is functionally equivalent to wmove( stdscr, ...). This is similar to
the interface offered by printf(...) and fprintf(stdout, ...).
Windows do not have to correspond to the entire screen. It is
possible to create smaller windows, and also to indicate that the
window is only partially visible on the screen. Furthermore, large
windows or pads, which are bigger than the actual screen size, may be
created.
Interfaces are also defined to allow input character manipulation and
to disable and enable many input attributes: character echo, single
character input with or without signal processing (cbreak or raw
modes), carriage returns mapping to newlines, screen scrolling, etc.
Data Types and the \ Header
The data types supported by curses are described in this section.
As the library supports a procedural interface to the data types, actual
structure contents are not described. All curses data are manipulated
using the routines provided.
The \ Header
The <curses.h> header defines various constants and declares the data
types that are available to the application.
Data Types
The following data types are declared:
WINDOW * pointer to screen representation
SCREEN * pointer to terminal descriptor
bool boolean data type
chtype representation of a character in a window
cchar_t the wide-character equivalent of chtype
attr_t for WA_-style attributesThe actual WINDOW and SCREEN objects used to store information are
created by the corresponding routines and a pointer to them is provided.
All manipulation is through that pointer.
Variables
The following variables are defined:
LINES number of lines on terminal screen
COLS number of columns on terminal screen
stdscr pointer to the default screen window
curscr pointer to the current screen image
SP pointer to the current SCREEN struct
Mouse_status status of the mouse
COLORS number of colors available
COLOR_PAIRS number of color pairs available
TABSIZE size of one TAB block
acs_map[] alternate character set map
ttytype[] terminal name/descriptionConstants
The following constants are defined:
General
FALSE boolean false value
TRUE boolean true value
NULL zero pointer value
ERR value returned on error condition
OK value returned on successful completionVideo Attributes
Normally, attributes are a property of the character.
For chtype:
A_ALTCHARSET use the alternate character set
A_BLINK bright background or blinking
A_BOLD bright foreground or bold
A_DIM half bright -- no effect in PDCurses
A_INVIS invisible -- no effect in PDCurses
A_ITALIC italic
A_LEFT line along the left edge
A_PROTECT protected -- no effect in PDCurses
A_REVERSE reverse video
A_RIGHT line along the right edge
A_STANDOUT terminal's best highlighting mode
A_UNDERLINE underline
A_ATTRIBUTES bit-mask to extract attributes
A_CHARTEXT bit-mask to extract a character
A_COLOR bit-mask to extract a color-pairNot all attributes will work on all terminals. A_ITALIC is not standard,
but is shared with ncurses.
For attr_t:
WA_ALTCHARSET same as A_ALTCHARSET
WA_BLINK same as A_BLINK
WA_BOLD same as A_BOLD
WA_DIM same as A_DIM
WA_INVIS same as A_INVIS
WA_ITALIC same as A_ITALIC
WA_LEFT same as A_LEFT
WA_PROTECT same as A_PROTECT
WA_REVERSE same as A_REVERSE
WA_RIGHT same as A_RIGHT
WA_STANDOUT same as A_STANDOUT
WA_UNDERLINE same as A_UNDERLINEThe following are also defined, for compatibility, but currently have no
effect in PDCurses: A_HORIZONTAL, A_LOW, A_TOP, A_VERTICAL and their
WA_* equivalents.
The Alternate Character Set
For use in chtypes and with related functions. These are a portable way
to represent graphics characters on different terminals.
VT100-compatible symbols -- box characters:
ACS_ULCORNER upper left box corner
ACS_LLCORNER lower left box corner
ACS_URCORNER upper right box corner
ACS_LRCORNER lower right box corner
ACS_RTEE right "T"
ACS_LTEE left "T"
ACS_BTEE bottom "T"
ACS_TTEE top "T"
ACS_HLINE horizontal line
ACS_VLINE vertical line
ACS_PLUS plus sign, cross, or four-corner pieceVT100-compatible symbols -- other:
ACS_S1 scan line 1
ACS_S9 scan line 9
ACS_DIAMOND diamond
ACS_CKBOARD checkerboard -- 50% grey
ACS_DEGREE degree symbol
ACS_PLMINUS plus/minus sign
ACS_BULLET bulletTeletype 5410v1 symbols -- these are defined in SysV curses, but are not
well-supported by most terminals. Stick to VT100 characters for optimum
portability:
ACS_LARROW left arrow
ACS_RARROW right arrow
ACS_DARROW down arrow
ACS_UARROW up arrow
ACS_BOARD checkerboard -- lighter (less dense) than
ACS_CKBOARD
ACS_LANTERN lantern symbol
ACS_BLOCK solid blockThat goes double for these -- undocumented SysV symbols. Don't use them:
ACS_S3 scan line 3
ACS_S7 scan line 7
ACS_LEQUAL less than or equal
ACS_GEQUAL greater than or equal
ACS_PI pi
ACS_NEQUAL not equal
ACS_STERLING pounds sterling symbolBox character aliases:
ACS_BSSB same as ACS_ULCORNER
ACS_SSBB same as ACS_LLCORNER
ACS_BBSS same as ACS_URCORNER
ACS_SBBS same as ACS_LRCORNER
ACS_SBSS same as ACS_RTEE
ACS_SSSB same as ACS_LTEE
ACS_SSBS same as ACS_BTEE
ACS_BSSS same as ACS_TTEE
ACS_BSBS same as ACS_HLINE
ACS_SBSB same as ACS_VLINE
ACS_SSSS same as ACS_PLUSFor cchar_t and wide-character functions, WACS_ equivalents are also
defined.
Colors
For use with init_pair(), color_set(), etc.:
COLOR_BLACK
COLOR_BLUE
COLOR_GREEN
COLOR_CYAN
COLOR_RED
COLOR_MAGENTA
COLOR_YELLOW
COLOR_WHITEUse these instead of numeric values. The definition of the colors
depends on the implementation of curses.
Input Values
The following constants might be returned by getch() if keypad() has
been enabled. Note that not all of these may be supported on a
particular terminal:
KEY_BREAK break key
KEY_DOWN the four arrow keys
KEY_UP
KEY_LEFT
KEY_RIGHT
KEY_HOME home key (upward+left arrow)
KEY_BACKSPACE backspace
KEY_F0 function keys; space for 64 keys is reserved
KEY_F(n) (KEY_F0+(n))
KEY_DL delete line
KEY_IL insert line
KEY_DC delete character
KEY_IC insert character
KEY_EIC exit insert character mode
KEY_CLEAR clear screen
KEY_EOS clear to end of screen
KEY_EOL clear to end of line
KEY_SF scroll 1 line forwards
KEY_SR scroll 1 line backwards (reverse)
KEY_NPAGE next page
KEY_PPAGE previous page
KEY_STAB set tab
KEY_CTAB clear tab
KEY_CATAB clear all tabs
KEY_ENTER enter or send
KEY_SRESET soft (partial) reset
KEY_RESET reset or hard reset
KEY_PRINT print or copy
KEY_LL home down or bottom (lower left)
KEY_A1 upper left of virtual keypad
KEY_A3 upper right of virtual keypad
KEY_B2 center of virtual keypad
KEY_C1 lower left of virtual keypad
KEY_C3 lower right of virtual keypad
KEY_BTAB Back tab key
KEY_BEG Beginning key
KEY_CANCEL Cancel key
KEY_CLOSE Close key
KEY_COMMAND Cmd (command) key
KEY_COPY Copy key
KEY_CREATE Create key
KEY_END End key
KEY_EXIT Exit key
KEY_FIND Find key
KEY_HELP Help key
KEY_MARK Mark key
KEY_MESSAGE Message key
KEY_MOVE Move key
KEY_NEXT Next object key
KEY_OPEN Open key
KEY_OPTIONS Options key
KEY_PREVIOUS Previous object key
KEY_REDO Redo key
KEY_REFERENCE Reference key
KEY_REFRESH Refresh key
KEY_REPLACE Replace key
KEY_RESTART Restart key
KEY_RESUME Resume key
KEY_SAVE Save key
KEY_SBEG Shifted beginning key
KEY_SCANCEL Shifted cancel key
KEY_SCOMMAND Shifted command key
KEY_SCOPY Shifted copy key
KEY_SCREATE Shifted create key
KEY_SDC Shifted delete char key
KEY_SDL Shifted delete line key
KEY_SELECT Select key
KEY_SEND Shifted end key
KEY_SEOL Shifted clear line key
KEY_SEXIT Shifted exit key
KEY_SFIND Shifted find key
KEY_SHELP Shifted help key
KEY_SHOME Shifted home key
KEY_SIC Shifted input key
KEY_SLEFT Shifted left arrow key
KEY_SMESSAGE Shifted message key
KEY_SMOVE Shifted move key
KEY_SNEXT Shifted next key
KEY_SOPTIONS Shifted options key
KEY_SPREVIOUS Shifted prev key
KEY_SPRINT Shifted print key
KEY_SREDO Shifted redo key
KEY_SREPLACE Shifted replace key
KEY_SRIGHT Shifted right arrow
KEY_SRSUME Shifted resume key
KEY_SSAVE Shifted save key
KEY_SSUSPEND Shifted suspend key
KEY_SUNDO Shifted undo key
KEY_SUSPEND Suspend key
KEY_UNDO Undo keyThe virtual keypad is arranged like this:
A1 up A3
left B2 right
C1 down C3This list is incomplete -- see curses.h for the full list, and use the
testcurs demo to see what values are actually returned. The above are
just the keys required by X/Open. In particular, PDCurses defines many
CTL_ and ALT_ combinations; these are not portable.
Vs Sdl2 Docs README (`vs/sdl2/docs/README.md`)
Simple DirectMedia Layer
Simple DirectMedia Layer is a cross-platform development library designed
to provide low level access to audio, keyboard, mouse, joystick, and graphics
hardware via OpenGL and Direct3D. It is used by video playback software,
emulators, and popular games including Valve's award winning catalog
and many Humble Bundle games.
SDL officially supports Windows, macOS, Linux, iOS, and Android.
Support for other platforms may be found in the source code.
SDL is written in C, works natively with C++, and there are bindings
available for several other languages, including C# and Python.
This library is distributed under the zlib license, which can be found
in the file "LICENSE.txt".
The best way to learn how to use SDL is to check out the header files in
the "include" subdirectory and the programs in the "test" subdirectory.
The header files and test programs are well commented and always up to date.
More documentation and FAQs are available online at the wiki
- Android
- CMake
- DirectFB
- DynAPI
- Emscripten
- GDK
- Gesture
- Git
- iOS
- Linux
- macOS
- OS/2
- Native Client
- Pandora
- Supported Platforms
- Porting information
- PSP
- PS2
- Raspberry Pi
- Touch
- Versions
- WinCE
- Windows
- WinRT
- PSVita
- Nokia N-Gage
If you need help with the library, or just want to discuss SDL related
issues, you can join the SDL Discourse,
which can be used as a web forum or a mailing list, at your preference.
If you want to report bugs or contribute patches, please submit them to
our bug tracker
Enjoy!
Sam Lantinga mailto:[email protected]
BDA BIOS Data Area PC Memory Map (`docs/BDA - BIOS Data Area - PC Memory Map.txt`)
http://stanislavs.org/helppc/bios_data_area.html
BDA - BIOS Data Area - PC Memory Map
Address Size Description
00:00 256dwords Interrupt vector table
30:00 256bytes Stack area used during post and bootstrap
40:00 word COM1 port address
40:02 word COM2 port address
40:04 word COM3 port address
40:06 word COM4 port address
40:08 word LPT1 port address
40:0A word LPT2 port address
40:0C word LPT3 port address
40:0E word LPT4 port address (except PS/2)
Extended BIOS Data Area segment (PS/2, see EBDA)
40:10 2 bytes Equipment list flags (see INT 11)
|7|6|5|4|3|2|1|0| 40:10 (value in INT 11 register AL)
| | | | | | | `- IPL diskette installed
| | | | | | `-- math coprocessor
| | | | |-+--- old PC system board RAM < 256K
| | | | | `-- pointing device installed (PS/2)
| | | | `--- not used on PS/2
| | `------ initial video mode
`--------- # of diskette drives, less 1
|7|6|5|4|3|2|1|0| 40:11 (value in INT 11 register AH)
| | | | | | | `- 0 if DMA installed
| | | | `------ number of serial ports
| | | `------- game adapter
| | `-------- not used, internal modem (PS/2)
`----------- number of printer ports
40:12 byte PCjr: infrared keyboard link error count
40:13 word Memory size in Kbytes (see INT 12)
40:15 byte Reserved
40:16 byte PS/2 BIOS control flags
40:17 byte Keyboard flag byte 0 (see KB FLAGS)
|7|6|5|4|3|2|1|0| keyboard flag byte 0
| | | | | | | `--- right shift key depressed
| | | | | | `---- left shift key depressed
| | | | | `----- CTRL key depressed
| | | | `------ ALT key depressed
| | | `------- scroll-lock is active
| | `-------- num-lock is active
| `--------- caps-lock is active
`---------- insert is active
40:18 byte Keyboard flag byte 1 (see KB FLAGS)
|7|6|5|4|3|2|1|0| keyboard flag byte
| | | | | | | `--- left CTRL key depressed
| | | | | | `---- left ALT key depressed
| | | | | `----- system key depressed and held
| | | | `------ suspend key has been toggled
| | | `------- scroll lock key is depressed
| | `-------- num-lock key is depressed
| `--------- caps-lock key is depressed
`---------- insert key is depressed
40:19 byte Storage for alternate keypad entry
40:1A word Offset from 40:00 to keyboard buffer head
40:1C word Offset from 40:00 to keyboard buffer tail
40:1E 32bytes Keyboard buffer (circular queue buffer)
40:3E byte Drive recalibration status
|7|6|5|4|3|2|1|0| drive recalibration status
| | | | | | | `-- 1=recalibrate drive 0
| | | | | | `--- 1=recalibrate drive 1
| | | | | `---- 1=recalibrate drive 2
| | | | `----- 1=recalibrate drive 3
| `---------- unused
`----------- 1=working interrupt flag
40:3F byte Diskette motor status
|7|6|5|4|3|2|1|0| diskette motor status
| | | | | | | `-- 1=drive 0 motor on
| | | | | | `--- 1=drive 1 motor on
| | | | | `---- 1=drive 2 motor on
| | | | `----- 1=drive 3 motor on
| `---------- unused
`----------- 1=write operation
40:40 byte Motor shutoff counter (decremented by INT 8)
40:41 byte Status of last diskette operation (see INT 13,1)
|7|6|5|4|3|2|1|0| status of last diskette operation
| | | | | | | `--- invalid diskette command
| | | | | | `---- diskette address mark not found
| | | | | `----- sector not found
| | | | `------ diskette DMA error
| | | `------- CRC check / data error
| | `-------- diskette controller failure
| `--------- seek to track failed
`---------- diskette time-out
40:42 7 bytes NEC diskette controller status (see FDC)
40:49 byte Current video mode (see VIDEO MODE)
40:4A word Number of screen columns
40:4C word Size of current video regen buffer in bytes
40:4E word Offset of current video page in video regen buffer
40:50 8 words Cursor position of pages 1-8, high order byte=row
low order byte=column; changing this data isn't
reflected immediately on the display
40:60 byte Cursor ending (bottom) scan line (don't modify)
40:61 byte Cursor starting (top) scan line (don't modify)
40:62 byte Active display page number
40:63 word Base port address for active 6845 CRT controller
3B4h = mono, 3D4h = color
40:65 byte 6845 CRT mode control register value (port 3x8h)
EGA/VGA values emulate those of the MDA/CGA
40:66 byte CGA current color palette mask setting (port 3d9h)
EGA and VGA values emulate the CGA
40:67 dword CS:IP for 286 return from protected mode
dword Temp storage for SS:SP during shutdown
dword Day counter on all products after AT
dword PS/2 Pointer to reset code with memory preserved
5 bytes Cassette tape control (before AT)
40:6C dword Daily timer counter, equal to zero at midnight;
incremented by INT 8; read/set by INT 1A
40:70 byte Clock rollover flag, set when 40:6C exceeds 24hrs
40:71 byte BIOS break flag, bit 7 is set if Ctrl-Break was
*ever* hit; set by INT 9
40:72 word Soft reset flag via Ctl-Alt-Del or JMP FFFF:0
1234h Bypass memory tests & CRT initialization
4321h Preserve memory
5678h System suspend
9ABCh Manufacturer test
ABCDh Convertible POST loop
????h many other values are used during POST
40:74 byte Status of last hard disk operation (see INT 13,1)
40:75 byte Number of hard disks attached
40:76 byte XT fixed disk drive control byte
40:77 byte Port offset to current fixed disk adapter
40:78 4 bytes Time-Out value for LPT1,LPT2,LPT3(,LPT4 except PS/2)
40:7C 4 bytes Time-Out value for COM1,COM2,COM3,COM4
40:80 word Keyboard buffer start offset (seg=40h,BIOS 10-27-82)
40:82 word Keyboard buffer end offset (seg=40h,BIOS 10-27-82)
40:84 byte Rows on the screen (less 1, EGA+)
40:85 word Point height of character matrix (EGA+)
byte PCjr: character to be repeated if the typematic
repeat key takes effect
40:86 byte PCjr: initial delay before repeat key action begins
40:87 byte PCjr: current Fn function key number
byte Video mode options (EGA+)
|7|6|5|4|3|2|1|0| Video mode options (EGA+)
| | | | | | | `-- 1=alphanumeric cursor emulation enabled
| | | | | | `--- 1=video subsystem attached to monochrome
| | | | | `---- reserved
| | | | `----- 1=video subsystem is inactive
| | | `------ reserved
| `--------- video RAM 00-64K 10-192K 01-128K 11-256K
`---------- video mode number passed to INT 10, function 0
40:88 byte PCjr: third keyboard status byte
EGA feature bit switches, emulated on VGA
|7|6|5|4|3|2|1|0| EGA feature bit switches (EGA+)
| | | | | | | `-- EGA SW1 config (1=off)
| | | | | | `--- EGA SW2 config (1=off)
| | | | | `---- EGA SW3 config (1=off)
| | | | `----- EGA SW4 config (1=off)
| | | `------ Input FEAT0 (ISR0 bit 5) after output on FCR0
| | `------- Input FEAT0 (ISR0 bit 6) after output on FCR0
| `-------- Input FEAT1 (ISR0 bit 5) after output on FCR1
`--------- Input FEAT1 (ISR0 bit 6) after output on FCR1
40:89 byte Video display data area (MCGA and VGA)
|7|6|5|4|3|2|1|0| Video display data area (MCGA and VGA)
| | | | | | | `-- 1=VGA is active
| | | | | | `--- 1=gray scale is enabled
| | | | | `---- 1=using monochrome monitor
| | | | `----- 1=default palette loading is disabled
| | | `------ see table below
| | `------- reserved
| `-------- 1=display switching enabled
`--------- alphanumeric scan lines (see table below)
Bit7 Bit4 Scan Lines
0 0 350 line mode
0 1 400 line mode
1 0 200 line mode
1 1 reserved
40:8A byte Display Combination Code (DCC) table index (EGA+)
40:8B byte Last diskette data rate selected
|7|6|5|4|3|2|1|0| last diskette data rate selected
| | | | `--------- reserved
| | `------------ last floppy drive step rate selected
`-------------- last floppy data rate selected
Data Rate Step Rate
00 500K bps 00 step rate time of 0C
01 300K bps 01 step rate time of 0D
10 250K bps 10 step rate time of 0A
11 reserved 11 reserved
40:8C byte Hard disk status returned by controller
40:8D byte Hard disk error returned by controller
40:8E byte Hard disk interrupt control flag(bit 7=working int)
40:8F byte Combination hard/floppy disk card when bit 0 set
40:90 4 bytes Drive 0,1,2,3 media state
|7|6|5|4|3|2|1|0| drive media state (4 copies)
| | | | | `------- drive/media state (see below)
| | | | `------- reserved
| | | `------- 1=media/drive established
| | `------- double stepping required
`--------- data rate: 00=500K bps 01=300K bps
10=250K bps 11=reserved
Bits
210 Drive Media State
000 360Kb diskette/360Kb drive not established
001 360Kb diskette/1.2Mb drive not established
010 1.2Mb diskette/1.2Mb drive not established
011 360Kb diskette/360Kb drive established
100 360Kb diskette/1.2Mb drive established
101 1.2Mb diskette/1.2Mb drive established
110 Reserved
111 None of the above
40:94 byte Track currently seeked to on drive 0
40:95 byte Track currently seeked to on drive 1
40:96 byte Keyboard mode/type
|7|6|5|4|3|2|1|0| Keyboard mode/type
| | | | | | | `--- last code was the E1 hidden code
| | | | | | `---- last code was the E0 hidden code
| | | | | `----- right CTRL key depressed
| | | | `------ right ALT key depressed
| | | `------- 101/102 enhanced keyboard installed
| | `-------- force num-lock if Rd ID & KBX
| `--------- last char was first ID char
`---------- read ID in process
40:97 byte Keyboard LED flags
|7|6|5|4|3|2|1|0| Keyboard LED flags
| | | | | | | `--- scroll lock indicator
| | | | | | `---- num-lock indicator
| | | | | `----- caps-lock indicator
| | | | `------ circus system indicator
| | | `------- ACK received
| | `-------- re-send received flag
| `--------- mode indicator update
`---------- keyboard transmit error flag
40:98 dword Pointer to user wait complete flag
40:9C dword User wait Time-Out value in microseconds
40:A0 byte RTC wait function flag
|7|6|5|4|3|2|1|0| INT 15,86 RTC wait function flag
| | | | | | | `--- 1= wait pending
| `-------------- not used
`--------------- 1=INT 15,86 wait time elapsed
40:A1 byte LANA DMA channel flags
40:A2 2 bytes Status of LANA 0,1
40:A4 dword Saved hard disk interrupt vector
40:A8 dword BIOS Video Save/Override Pointer Table address
(see VIDEO TABLES)
40:AC 8 bytes Reserved
40:B4 byte Keyboard NMI control flags (convertible)
40:B5 dword Keyboard break pending flags (convertible)
40:B9 byte Port 60 single byte queue (convertible)
40:BA byte Scan code of last key (convertible)
40:BB byte NMI buffer head pointer (convertible)
40:BC byte NMI buffer tail pointer (convertible)
40:BD 16bytes NMI scan code buffer (convertible)
40:CE word Day counter (convertible and after)
40:F0 16bytes Intra-Applications Communications Area (IBM Technical
Reference incorrectly locates this at 50:F0-50:FF)
Address Size Description (BIOS/DOS Data Area)
50:00 byte Print screen status byte
00 = PrtSc not active,
01 = PrtSc in progress
FF = error
50:01 3 bytes Used by BASIC
50:04 byte DOS single diskette mode flag, 0=A:, 1=B:
50:05 10bytes POST work area
50:0F byte BASIC shell flag; set to 2 if current shell
50:10 word BASICs default DS value (DEF SEG)
50:12 dword Pointer to BASIC INT 1C interrupt handler
50:16 dword Pointer to BASIC INT 23 interrupt handler
50:1A dword Pointer to BASIC INT 24 disk error handler
50:20 word DOS dynamic storage
50:22 14bytes DOS diskette initialization table (INT 1E)
50:30 4bytes MODE command
70:00 I/O drivers from IO.SYS/IBMBIO.COMThe following map varies in size and locus
07C0:0 Boot code is loaded here at startup (31k mark)
A000:0 EGA/VGA RAM for graphics display mode 0Dh & above
B000:0 MDA RAM, Hercules graphics display RAM
B800:0 CGA display RAM
C000:0 EGA/VGA BIOS ROM (thru C7FF)
C400:0 Video adapter ROM space
C600:0 256bytes PGA communication area
C800:0 16K Hard disk adapter BIOS ROM
C800:5 XT Hard disk ROM format, AH=Drive, AL=Interleave
D000:0 32K Cluster adapter BIOS ROM
D800:0 PCjr conventionalsoftware cartridge address
E000:0 64K Expansion ROM space (hardwired on AT+)
128K PS/2 System ROM (thru F000)
F000:0 System monitor ROM
PCjr: software cartridge override address
F400:0 System expansion ROMs
F600:0 IBM ROM BASIC (AT)
F800:0 PCjr software cartridge override address
FC00:0 BIOS ROM
FF00:0 System ROM
FFA6:E ROM graphics character table
FFFF:0 ROM bootstrap code
FFFF:5 8 bytes ROM date (not applicable for all clones)
FFFF:E byte ROM machine id (see MACHINE ID)DOS GAMES THAT NEED A HIGHER MCB BASE SEGMENT (`docs/DOS GAMES THAT NEED A HIGHER MCB BASE SEGMENT.TXT`)
DOS games/applications that will not work correctly unless given a minimum MCB segment that is higher than default:
recommendedgame/application value reason
Shamaani (1994 demo) 800 Will crash if ems=true and loaded too low after GUS I/O port selection menu
Space Pigs Megademo (1990) C00 Demo will crash at startup, claim EXE corruption or files are screwed up, if too low
Vicky (Space Pigs demo) 1000 Demo is compressed with EXEPACK, needs to be loaded 64KB or higher in memory to run
Warlock (1992 demo) C00 Final credits part will crash if too low
PC 98 Display Terminology (`docs/PC-98 display terminology.txt`)
This is posted here in the source tree in the event
it gets lost in Github's issue tracker.
From hdk1983:
...
The second part of the undocumented_mem/io_prn.txt is for ハイレゾ (hairezo, high-resolution) mode. The games that read the port for detecting system clock are for normal mode.
...
It's "ハイレゾ", its resolution is super high resolution...
normal mode = 15khz 200-line mode
high resolution = 24/31khz 400/480-line mode
super high resolution = ?khz 1120x750 modeModes are named as:
ノーマルモード, normal mode, is 15kHz/24kHz/31kHz.
ハイレゾモード, hi-res mode, high resolution mode, is the 750 lines mode.However resolution of the high resolution mode is super high resolution, that term is normally not used, but only used in Wikipedia for describing it is different from the 高解像度, high resolution, of the normal mode.
When ハイレゾモード was released, ハイレゾ term might be used only for PC-98 series in Japan, so many Japanese speakers using the ハイレゾ term might not think about that ハイレゾ was a abbreviation for high resolution and the same meaning as 高解像度. So if you find ハイレゾ in documentation, normally it means the 750 lines mode. If you use a translator, maybe you need to look at the original sentense of "high resolution".
A display for 15kHz is called as 標準ディスプレイ、a standard display. The 15kHz mode was normally not used. That might be used in early 1980s, because its display might compatible with other PCs like Sharp X1 and Fujitsu FM-7, but I've never seen PC-98 systems that are connected with such displays in 1990s.
Runtime Media Swapping (`docs/Runtime media swapping.txt`)
== Floppy image swapping ==
If you are booting from floppy using the BOOT command
(but not yet the IMGMOUNT command), you can use the
SwapFloppy key combination through the mapper to swap
between floppies.
This key combination is F11+CTRL+D by default,
but can be changed through the mapper interface.
== CD-ROM image swapping ==
IMGMOUNT can be used to attach multiple ISO images to
a CD-ROM drive. At first, the first ISO image is presented
to the guest as the CD-ROM drive. To emulate the act of
removing the CD and putting in the next one, use the
SwapCD key combination through the mapper.
This key combination is F11+CTRL+C by default,
but can be changed through the mapper interface.
To provide a realistic CD change delay, CD switching is
emulated as first the removal of the CD-ROM media from
the drive, followed by a 4 second delay, followed by the
emulation of inserting the new CD and waiting for the
CD-ROM drive to spin up and read it. This delay is
necessary for some OSes like Windows 95 who cannot
detect CD changes if the changeover happens too fast.
WINDOWS DOS VM TEST RESULTS (`docs/WINDOWS DOS VM TEST RESULTS.TXT`)
Windows 3.11/95 DOS VM save/restore test results
machine=svga_s3
N - WIN31 WIN95
0 - PASSED PASSED
1 - PASSED PASSED
2 - PASSED PASSED
3 - PASSED PASSED
4 - PASSED PASSED
5 - PASSED PASSED
6 - PASSED PASSED
7 - FAIL[1] FAIL[1]
13 - PASSED PASSED
14 - PASSED PASSED
15 - PASSED PASSED
16 - PASSED PASSED
17 - PASSED PASSED
18 - PASSED PASSED
19 - *----- PASSED
19x- *----- PASSED
[1] The DOS VM is able to draw the mode windowed correctly,
but the S3 driver doesn't seem to switch it back to fullscreen
properly.
- NTS: Windows 3.1 will not show the DOS screen windowed for 320x200x256
color (mode 19) for some reason.
Despite that, the DOS VM is able to restore the mode correctly
going back to fullscreen.
Windows 3.11/95 DOS VM save/restore test results
machine=svga_et4000
N - WIN31 WIN95
0 - PASSED PASSED
1 - PASSED PASSED
2 - PASSED PASSED
3 - PASSED PASSED
4 - FAIL PASSED
5 - FAIL PASSED
6 - FAIL PASSED
7 - PASSED FAIL[1]
13 - FAIL PASSED
14 - FAIL PASSED
15 - FAIL PASSED
16 - FAIL PASSED
17 - FAIL PASSED
18 - FAIL PASSED
19 - FAIL PASSED
19x- FAIL PASSED
Windows 95:
[1] The DOS VM is able to draw the mode windowed correctly,
but the driver doesn't seem to switch it back to fullscreen
properly.
Windows 3.1:
The ET4000 driver in the test VM seems to mistake all
modes 4-6 and 13-18 as some variant of 256-color mode
for some weird reason, the graphics are drawn as
"squeezed". And for some reason, the DOS VM refuses to
run 320x200x256-color mode windowed, which is strange,
I remember running that mode just fine on my ancient
386...
Windows 3.11/95 DOS VM save/restore test results
machine=vgaonly
N - WIN31 WIN95
0 - PASSED PASSED
1 - PASSED PASSED
2 - PASSED PASSED
3 - PASSED PASSED
4 - PASSED PASSED
5 - PASSED PASSED
6 - PASSED PASSED
7 - PASSED PASSED
13 - PASSED PASSED
14 - PASSED PASSED
15 - PASSED PASSED
16 - PASSED PASSED
17 - PASSED PASSED
18 - PASSED PASSED
19 - *----- PASSED
19x- PASSED PASSED
- NTS: Windows 3.1's standard VGA driver (640x480x16 mode) will not
show the DOS screen windowed for 320x200x256 color (mode 19)
obviously because that would require the DOS VM window to
create a color palette and map it somehow to 16 colors.
But... for some reason, 320x200x256 mode X is OK to run
windowed, even if it allows the general palette on screen
to get messed up doing so. Weird.
Despite that, the DOS VM is able to restore the mode correctly
going back to fullscreen.
Windows 3.0 And Later EGA, VGA Driver Notes (`docs/Windows 3.0 and later EGA, VGA driver notes.txt`)
Notes:
- Starting with Windows 3.0, the stock EGA/VGA drivers require a 80186 or
higher, they do not work on an 8086. This is due to the use of instructions
not supported by the 8086.
Rpc Agent Usage (`docs/rpc-agent-usage.md`)
DOSBox-X Debugger Agent 使用手册
本文面向需要调用 DOSBox-X 调试后端的 AI Agent。目标是让 Agent 通过结构化 RPC 对 DOS 程序进行动态逆向:启动目标、停在入口、设置断点、运行到断点、读取寄存器和内存、单步、收集 trace,并在有明确前置条件时修改 guest memory。
Agent 不应依赖 DOSBox-X 窗口、stdout、LOGCPU.TXT 或 debugger 的人类可读文本来获得核心状态。正式数据必须来自 JSON-RPC;debugger.execute_command 只用于有限的只读诊断。
1. 运行模型
Agent
|
| Python client 或 JSON-RPC 2.0
v
DOSBox-X named pipe agent
|
| emulation-thread queue
v
DOSBox-X debugger / emulator
|
v
DOS program一次分析通常包含两个进程角色:
- Agent 启动一个 DOSBox-X 进程,并传入
--agent-config。 - Agent 通过配置中的 Windows named pipe 发送 JSON-RPC 请求。
v1 的约束:
- 仅支持 Windows。
- 默认 transport 是当前用户可访问的 named pipe,不开放未认证 TCP。
- 一个 DOSBox-X 进程只有一个活动 session 和一个受控的
C:mount。 session.start固定在entry停止。- target 必须是 DOS 安全文件名,例如
GAME.COM;不能传宿主机路径或 shell 命令。 session.start会等待入口停止后才返回;这不是一个只返回 operation id 的后台启动调用。execution.continue、execution.pause、session.stop返回operation_id,终态通过execution.wait获取。
2. 准备分析目录
建议为每次分析创建一个新的 runtime 目录,并只把待分析程序和必要的数据文件复制进去:
reverse/
configs/
game.env
runtime/
GAME.COM
DATA.DATdosbox_workdir 必须是受控目录。session.start 的 mounts[0].host_path 必须解析为同一个目录,不能临时切换到其他路径。目标程序和参数应使用 DOS 目录中的名字:
session = agent.start(
"GAME.COM",
["LEVEL1", "-DEBUG"],
mount_path=workdir,
)target.command 和 target.arguments 只允许字母、数字、.、_、-,因此应在启动前完成文件准备,不要把 C:\...、引号、重定向或 shell 元字符传给 RPC。
3. 配置文件
配置来自显式的 .env 文件。server 和 Python client 都不会从系统环境变量读取 endpoint、目标程序、工作目录或超时。
生产配置使用绝对路径:
transport=named_pipe
endpoint=\\.\pipe\dosbox-agent-game-01
dosbox_executable=D:\workspace\dosbox-x\bin\x64\Agent Debug SDL2\dosbox-x.exe
dosbox_workdir=D:\workspace\reverse\game-01
profile=production
request_timeout_ms=5000
max_message_bytes=1048576
max_memory_read_bytes=65536
max_trace_events=10000配置键说明:
| 键 | 说明 |
|---|---|
transport |
v1 使用 named_pipe。 |
endpoint |
Windows named pipe 名称;每个并行 DOSBox-X 进程应使用不同名称。 |
dosbox_executable |
要启动的 DOSBox-X 可执行文件绝对路径。 |
dosbox_workdir |
唯一受控的 C: mount 目录绝对路径。 |
profile |
production 或 test;正式 Agent 使用 production。 |
request_timeout_ms |
server 等待 emulation-thread 操作的上限。 |
max_message_bytes |
单条 JSON request/response 的消息上限。 |
max_memory_read_bytes |
单次 memory.read 和 memory.write 的最大字节数。 |
max_trace_events |
session trace store 的最大事件数。 |
profile=test 只用于仓库测试夹具。生产配置使用相对的 executable 或 workdir 会被拒绝;不要通过把路径放进 tests 目录来绕过校验。
4. 启动 DOSBox-X
PowerShell 示例:
$config = 'D:\workspace\reverse\configs\game.env'
$process = Start-Process `
-FilePath 'D:\workspace\dosbox-x\bin\x64\Agent Debug SDL2\dosbox-x.exe' `
-ArgumentList @('--agent-config', $config) `
-WindowStyle Hidden `
-PassThru
# 等待进程完成 DOS shell 和 debugger 初始化。
Start-Sleep -Seconds 2Agent 应通过进程句柄、named pipe readiness 或有限的启动等待确认 DOSBox-X 已经启动。不要无限重试 RPC,也不要把带副作用的请求自动重发。
Python Agent 通常在 finally 中关闭 client、停止 session,并回收 DOSBox-X 进程:
process = subprocess.Popen(
[str(config.dosbox_executable), "--agent-config", str(config.path)],
cwd=repository_root,
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
)
try:
# 连接并进行 RPC 分析
...
finally:
client.close()
if process.poll() is None:
process.kill()
process.wait()5. Python client 快速开始
仓库当前的 Python client 位于 client/python,还没有独立安装包。Agent 脚本可将该目录加入自身模块搜索路径:
from pathlib import Path
import sys
repository_root = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repository_root / "client" / "python"))
from dosbox_agent import AgentClient, MemoryAddress
config_path = repository_root / "reverse" / "configs" / "game.env"
workdir = Path(r"D:\workspace\reverse\game-01")
with AgentClient.from_config(config_path) as agent:
capabilities = agent.capabilities()
session = agent.start("GAME.COM", mount_path=workdir)
print(session.id, session.state, session.stop_reason)
registers = agent.get_registers(session.id)
code = agent.read_memory(
session.id,
MemoryAddress.segmented(registers.segments["cs"], registers.instruction_pointer),
32,
)
print(registers)
print(code.data.hex(), code.sha256)
stop = agent.stop(session.id)
stopped = agent.wait(session.id, stop.id, timeout_ms=5000)
while stopped.running:
stopped = agent.wait(session.id, stop.id, timeout_ms=5000)关键返回对象是 typed model,不需要 Agent 手动解析 JSON 或 base64:
Session:id、state、state_revision、stop_reason。RegisterSnapshot:general、segments、instruction_pointer、flags、cpu_mode。MemoryRead:address、data: bytes、sha256、state_revision。MemoryWrite:写入前后 SHA-256 和byte_count。Breakpoint、Operation、WaitResult、OutputPage、TracePage。
6. 标准动态逆向流程
6.1 检查能力
先调用 agent.capabilities(),根据返回值决定是否使用 trace 或 memory-change breakpoint:
capabilities = agent.capabilities()
if not capabilities["debugger"]:
raise RuntimeError("current DOSBox-X build has no debugger capability")
trace_enabled = capabilities["trace"]["cpu"] is True
memory_breakpoint_enabled = capabilities["breakpoints"]["memory_change"] is True不要把 capability 缺失当成普通断点或空 trace;server 会返回 CapabilityUnavailableError。
6.2 在入口建立基线
agent.start() 成功后 session 应为 stopped,stop_reason.kind 应为 startup。此时保存:
CS:IP和DS:SI/SS:SP等段寄存器组合。- 通用寄存器和 FLAGS。
- 入口附近的 code bytes。
- 关键数据区的初始 bytes 和 SHA-256。
示例:
session = agent.start("GAME.COM", mount_path=workdir)
assert session.state == "stopped"
assert session.stop_reason is not None
assert session.stop_reason.kind == "startup"
regs = agent.get_registers(session.id)
entry = MemoryAddress.segmented(regs.segments["cs"], regs.instruction_pointer)
entry_bytes = agent.read_memory(session.id, entry, 64)不要假设 CS、DS 相同,也不要把 segmented 地址拼成字符串。使用 MemoryAddress.segmented()、.linear() 或 .physical() 明确指定 address space。
6.3 设置断点并运行
执行断点使用稳定的 breakpoint_id,不使用底层 debugger 的列表索引:
breakpoint = agent.create_execution_breakpoint(
session.id,
segment=regs.segments["cs"],
offset="0x00001234",
)
operation = agent.continue_(session.id)
result = agent.wait(session.id, operation.id, timeout_ms=5000)
while result.running:
result = agent.wait(session.id, operation.id, timeout_ms=5000)
assert result.session.stop_reason is not None
print(result.session.stop_reason.kind, result.session.stop_reason.address)执行断点当前要求 segmented 地址。once=True 可创建命中后自动删除的一次性断点:
temporary = agent.create_execution_breakpoint(
session.id, regs.segments["cs"], "0x00001400", once=True
)6.4 读取寄存器和内存
寄存器和内存观察只能在 stopped 状态进行。运行时调用会抛出 TargetRunningError。
regs = agent.get_registers(session.id)
stack = agent.read_memory(
session.id,
MemoryAddress.segmented(regs.segments["ss"], regs.general["esp"]),
32,
)
linear_page = agent.read_memory(
session.id,
MemoryAddress.linear("0x00001000"),
256,
)
physical = agent.read_memory(
session.id,
MemoryAddress.physical("0x000A0000"),
64,
)注意:regs.general["esp"] 是固定宽度十六进制字符串,适合直接传给 MemoryAddress.segmented()。如果地址未映射、selector 无效、超过 segment limit 或 page 不存在,client 会抛出 AddressNotMappedError;错误对象的 data 中包含失败地址和底层原因。
6.5 单步进入与越过
stopped, after_step = agent.step(session.id, mode="into")
print(stopped.stop_reason.kind, after_step.instruction_pointer)
stopped, after_over = agent.step(session.id, mode="over")into 进入当前指令的执行路径;over 使用 debugger 的现有 step-over 语义越过 call、interrupt 或重复指令的目标。每次返回都包含新的 register snapshot,不需要再次调用 get_registers,但可以再读内存验证副作用。
6.6 观察内存变化
优先使用已知代码位置的 execution breakpoint 和前后内存快照。heavy-debug build 还可以使用 memory-change breakpoint:
if memory_breakpoint_enabled:
watch = agent.create_breakpoint(
session.id,
"memory_change",
MemoryAddress.segmented(regs.segments["ds"], "0x00000200"),
)不支持 memory-change breakpoint 时,必须改变分析策略,不能自动降级为 execution breakpoint。
6.7 修改 guest memory
写入前先读取并保留 SHA-256,然后用 expected_sha256 做乐观并发保护:
address = MemoryAddress.segmented(regs.segments["ds"], "0x00000200")
before = agent.read_memory(session.id, address, 4)
written = agent.write_memory(
session.id,
address,
b"\x90\x90\x90\x90",
expected_sha256=before.sha256,
)
print(written.before_sha256, written.after_sha256)如果目标在读取和写入之间发生变化,server 返回 MemoryPreconditionFailedError,本次写入不会发生。写入超时后不要自动重试;如果要由 Agent 明确重试,复用同一个 request_id 并确认请求 payload 完全相同。
7. Trace 和 debugger 输出
7.1 CPU trace
CPU trace 需要 C_HEAVY_DEBUG=1。trace 是 session-local 的结构化数据,不依赖 LOGCPU.TXT:
if capabilities["trace"]["cpu"]:
agent.start_trace(session.id, detail="normal", instruction_count=200)
operation = agent.continue_(session.id)
stopped = agent.wait(session.id, operation.id, timeout_ms=10000)
while stopped.running:
stopped = agent.wait(session.id, operation.id, timeout_ms=10000)
cursor = None
while True:
page = agent.read_trace(session.id, cursor, limit=50)
for event in page.events:
print(event.sequence, event.address, event.instruction, event.register_changes)
if page.next_cursor is None:
break
cursor = page.next_cursor
agent.stop_trace(session.id)trace.read 的 cursor 初始为 None,下一页使用上一次返回的 next_cursor。不要在 cursor 过期后从头拼接;应重新建立 trace 分析窗口。instruction_count 受配置的 max_trace_events 限制。
7.2 Debugger 输出
正式 API 不需要解析 debugger 文本。需要诊断时只能执行当前 adapter allowlist 中的只读命令:HELP、CPU、PIC。
diagnostic = agent.execute_command(session.id, "CPU")
if diagnostic.accepted:
print(diagnostic.raw_output)输出记录来自 session-local ring buffer,可用 cursor 分页:
cursor = None
while True:
page = agent.read_output(session.id, cursor, limit=100)
for record in page.records:
print(record.sequence, record.source, record.message)
if page.next_cursor is None:
break
cursor = page.next_cursorRUN、SM、SR、BP*、LOG* 等会改变调试状态或 collector 状态的命令不属于稳定诊断 API,server 会拒绝它们。请使用对应的结构化 RPC 方法。
8. 会话状态和等待规则
| 状态 | Agent 可以做什么 |
|---|---|
starting |
查询 session.status、读取输出;等待 session.start 完成。 |
stopped |
读取/写入 memory、读取 registers、断点管理、continue、pause、step、trace 操作。 |
running |
session.status、execution.pause、execution.wait、debug.output.read。其他状态观察和修改请求返回 TARGET_RUNNING。 |
exited |
查询最终状态、读取最后 stop reason、重复 stop;不能读取 guest registers/memory。 |
failed |
查询状态、读取输出、执行 stop 清理;不要继续调用 target 操作。 |
每次成功响应都带 state_revision。Agent 应保存最后一次 revision,在断点命中、单步、memory.write 或断点集合变化后更新自己的观察缓存。旧 revision 的内存快照不能直接当成当前状态。
execution.wait 的超时不是失败:
result = agent.wait(session.id, operation.id, timeout_ms=100)
if result.running:
# 目标仍在运行;继续使用同一个 operation_id 轮询或先 pause。
...不要把 running=true 当成程序退出,也不要为“确认停止”创建新的 continue 请求。
9. 错误处理和重试
Python client 会把 RPC business error 映射为 typed exception:
from dosbox_agent import (
AddressNotMappedError,
AgentConnectionError,
CapabilityUnavailableError,
MemoryPreconditionFailedError,
TargetRunningError,
)
try:
registers = agent.get_registers(session.id)
except TargetRunningError:
# 先 pause 或 wait,不要读取不一致状态。
...
except AddressNotMappedError as error:
print(error.data)
except MemoryPreconditionFailedError:
# 重新 read,确认目标状态后再决定是否写入。
...
except CapabilityUnavailableError:
# 改用 capability 已声明支持的分析方法。
...
except AgentConnectionError:
# 进程或 named pipe 已不可用;不要重发有副作用请求。
...重要错误:
| reason | Agent 行为 |
|---|---|
SESSION_BUSY |
当前进程已有 active session;先 stop 并 wait,或创建新的 DOSBox-X 进程。 |
SESSION_NOT_FOUND |
丢弃本地 session 引用,不能继续使用旧 operation。 |
TARGET_RUNNING |
先 pause 或等待停止。 |
TARGET_NOT_STOPPED / TARGET_EXITED |
修正状态机,不要重试相同请求。 |
CAPABILITY_UNAVAILABLE |
读取 capabilities,选择其他策略。 |
ADDRESS_NOT_MAPPED |
检查 address space、segment、offset 和 CPU mode。 |
MEMORY_PRECONDITION_FAILED |
重新读取并重新判断,不能盲写。 |
REQUEST_TOO_LARGE |
减小 memory/trace/page 请求;server 不截断。 |
OPERATION_TIMEOUT |
对 start 或 adapter 操作记录失败/不确定状态;不要自动重复有副作用调用。 |
CURSOR_EXPIRED |
丢弃旧 cursor,重新开始一个明确的新读取窗口。 |
REQUEST_ID_CONFLICT |
同一 request id 被用于不同 payload;生成新的逻辑请求 id。 |
server 会缓存 session 生命周期内最近完成的请求结果。只有 Agent 明确确认“同一个逻辑请求、同一个 payload”时,才可以用相同 request_id 重试。Python client 不会自动重试写内存、断点、单步、原始命令或其他有副作用调用。
10. 非 Python Agent 的最小 JSON-RPC 调用
传输是 UTF-8 JSON Lines,一行一个 request,一行一个 response。下面是入口启动请求:
{"jsonrpc":"2.0","id":"start-1","method":"session.start","params":{"target":{"command":"GAME.COM","arguments":[]},"mounts":[{"drive":"C","host_path":"D:/workspace/reverse/game-01"}],"break_at":"entry"}}入口成功响应的核心字段:
{
"jsonrpc": "2.0",
"id": "start-1",
"result": {
"session_id": "ses-1",
"state": "stopped",
"state_revision": 1,
"stop_reason": {
"kind": "startup",
"address": {
"space": "segmented",
"segment": "0x0812",
"offset": "0x00000100"
}
}
}
}读取寄存器:
{"jsonrpc":"2.0","id":"regs-1","method":"state.get_registers","params":{"session_id":"ses-1"}}读取内存:
{"jsonrpc":"2.0","id":"mem-1","method":"memory.read","params":{"session_id":"ses-1","address":{"space":"segmented","segment":"0x0812","offset":"0x00000100"},"length":16}}11. 结束分析
正常结束必须停止 session 并等待终态。当前 server 的终止 deadline 使用配置中的 request_timeout_ms;graceful_timeout_ms 会被 client 发送,但目前不改变 server 的终止 deadline,因此不要依赖它延长等待:
stop_operation = agent.stop(session.id, graceful_timeout_ms=5000)
result = agent.wait(session.id, stop_operation.id, timeout_ms=5000)
while result.running:
result = agent.wait(session.id, stop_operation.id, timeout_ms=5000)
assert result.session.state == "exited"无论分析成功还是异常,都应在 finally 中:
- 对仍存在的 session 执行 stop/wait。
- 关闭
AgentClient。 - 回收 DOSBox-X 进程。
- 为下一次分析使用新的 endpoint 或确认上一次进程已经完全退出。
不要复用上一次运行产生的 LOGCPU.TXT、MEMDUMP.*、trace 文件或未清理的 runtime 目录来推断本次分析结果。
12. 推荐的 Agent 分析策略
一个稳定的逆向 Agent 可以按以下顺序工作:
- 创建干净 runtime,复制目标和输入数据。
- 启动 DOSBox-X,调用
agent.capabilities。 session.start停在入口,保存寄存器、入口 code bytes 和关键内存 SHA-256。- 根据静态分析或前一轮动态结果设置少量 execution breakpoint。
continue_+wait到断点,记录 stop reason、register snapshot 和state_revision。- 围绕当前
CS:IP、栈和数据段读取小范围内存,不要一次读取整个 guest memory。 - 对可疑指令使用
step("into")或step("over"),每一步只做必要的观察。 - 需要批量指令历史时,在 heavy-debug build 中使用 session-local CPU trace。
- 需要验证 patch 时使用
read -> expected_sha256 -> write -> read闭环。 - 每轮实验结束后 stop/wait,保存结构化结果,再清理进程和 runtime。
这样可以把“静态假设”和“动态证据”绑定到明确的地址、寄存器、内存 hash、断点 id、trace sequence 和 state revision,而不是依赖窗口截图或文本日志。
13. 相关文件和验收命令
- 协议契约:rpc.md
- 开发和验收规范:rpc-development.md
- Python client:
client/python/dosbox_agent/ - Python E2E:
client/python/tests/test_e2e.py - 测试配置:
tests/agent/agent-test.env - 测试夹具:
tests/agent/fixtures/
开发者可用以下命令验证 client 和完整闭环:
py -m unittest discover -s client\python\tests -t client\python -v
py client\python\tests\test_e2e.py --config tests\agent\agent-test.env测试配置使用 profile=test 和仓库 runtime;生产 Agent 不应直接复用它,而应创建绝对路径的 profile=production 配置。
Rpc Development (`docs/rpc-development.md`)
DOSBox-X Debugger Agent RPC 开发与验收规范
状态:计划基线
版本:v1.0-draft
日期:2026-09-02
需求来源:rpc.md
本文是 docs/rpc.md 的可实施、可验收版本。开发任务、代码评审和发布验收必须以本文的检查项为准。检查项只有在命令执行成功、结果满足预期且证据已记录后才能勾选;不得用“接口存在”“命令未报错”替代行为验证。
1. 目标与边界
目标是在不重写 CPU、内存和断点机制的前提下,将 DOSBox-X 现有 debugger 能力封装为稳定的结构化 Agent API。Agent 只消费 JSON 数据,不依赖 ncurses 窗口或 debugger 的人类可读文本。
v1 的部署、构建和验收范围仅限 Windows。
第一期必须支持:
- 启动、停止并查询 DOS 调试会话。
- 继续、暂停、单步进入、单步越过和等待停止事件。
- 获取通用寄存器、段寄存器、IP/EIP 和 FLAGS/EFLAGS。
- 在 segmented、linear、physical 三种地址空间读取和写入内存。
- 创建、列出和删除执行断点;仅在底层实际支持时提供内存变化断点。
- 读取结构化 debugger 输出和执行 trace。
- 执行原始 debugger 命令,作为诊断接口而不是主 API。
- 提供 Python client 和端到端验收夹具。
第一期禁止:
- 修改
src/cpu/的执行语义、指令实现或调度语义。 - 修改
src/hardware/memory.cpp的内存映射、分页和读写语义。 - 自行实现第二套断点、单步或内存访问机制。
- 让 RPC 直接返回
AX=...、CS:IP=...等 debugger 文本并要求客户端解析。 - 因地址无法解析、capability 不存在或 target 正在运行而悄悄降级、填充默认值或自动重试写操作。
2. 当前代码基线
实现必须复用以下现有能力,并在 adapter 内提供结构化包装:
| 能力 | 现有入口 | 说明 |
|---|---|---|
| Debugger 生命周期 | include/debug.h、src/debug/debug.cpp |
DEBUG_EnableDebugger()、DEBUG_ExitLoop() 和 DEBUG_Enable_Handler() 已管理 debugger 进入/退出。 |
| 执行与单步 | src/debug/debug.cpp |
现有 RUN、RUNWATCH、F10/F11 和 StepOver() 提供底层控制。 |
| 断点 | src/debug/debug.cpp |
CBreakpoint 已实现执行、interrupt、memory/protected/linear 等类型。 |
| 寄存器与地址解析 | src/debug/debug.cpp、include/cpu.h |
debugger 已读取 reg_*、段寄存器及 GetAddress()。 |
| 内存访问 | src/debug/debug.cpp、src/hardware/memory.cpp |
debugger 已通过 checked memory API 访问 guest memory。 |
| Debugger 命令 | README.debugger、src/debug/debug.cpp |
现有 RUN、SR、SM、BP、BPLIST、BPDEL、MEMDUMP*、LOG* 等命令可作兼容和诊断参考。 |
| 单元测试入口 | tests/tests.h、tests/readme.txt |
Debug build 可通过 dosbox-x -tests 运行 gTest。 |
构建前置条件:
C_DEBUG=1是 v1 的硬性条件;未启用时 server 必须拒绝启动并返回DEBUGGER_UNAVAILABLE。C_HEAVY_DEBUG=1是 CPU trace 的硬性条件;未启用时agent.capabilities必须声明trace.cpu=false,trace.start返回CAPABILITY_UNAVAILABLE。- Windows 当前
.vcxproj配置没有显式定义C_DEBUG。实现必须新增或调整一个专用调试配置,例如Agent Debug SDL2,显式定义C_DEBUG=1;C_HEAVY_DEBUG=1;C_DOSBOX_AGENT=1。不得使用现有 Release 输出作为 RPC 验收对象。
3. 总体架构
Python client / AI Agent
|
| JSON-RPC 2.0
v
dosbox-agent server
- transport
- session manager
- request validation
|
| authenticated local IPC
v
DOSBox-X in-process agent bridge
- emulation-thread command queue
- DebuggerAdapter
- output/trace collector
|
v
DOSBox-X debugger and emulator职责划分:
dosbox-agent server管理 RPC transport、DOSBox-X 子进程、会话状态和客户端连接。它不直接读取reg_*或 guest memory。- in-process bridge 仅在 DOSBox-X 的 emulation thread 上调用 debugger、CPU 和 memory 已有能力。任何 RPC worker thread 都不得直接访问 debugger globals。
DebuggerAdapter以 C++ 类型返回 registers、memory、breakpoints、stop reason 和 trace events;transport 层只负责编解码 JSON。DebuggerOutputParser只服务于debugger.execute_command的非结构化诊断输出。核心方法不得以 parser 作为数据来源。
现有 debugger 使用大量全局状态,例如 debugging、debug_running、runnormal 和 CBreakpoint::BPoints。因此 v1 每个 DOSBox-X 进程只允许一个活动调试会话。第二个 session.start 必须返回 SESSION_BUSY,不得共享或覆盖已有 session。
4. 目录与构建交付物
实现完成后的目标目录如下。可以按仓库既有布局微调文件名,但职责不能合并或缺失。
src/agent/
protocol/ # JSON model、错误码、schema version
server/ # host-side process/session manager
transport/ # Windows named pipe / test transport
bridge/ # in-process queue and emulation-thread dispatch
debugger/ # DebuggerAdapter and structured collectors
parser/ # raw debugger output parser only
trace/ # trace sink and paged store
include/agent/
debugger_adapter.h
agent_bridge.h
agent_protocol.h
client/python/dosbox_agent/
__init__.py
client.py
models.py
errors.py
tests/agent/
agent_adapter_tests.cpp
agent_parser_tests.cpp
agent_protocol_tests.cpp
fixtures/agent_fixture.asm
fixtures/agent_fixture.com
agent-test.env
client/python/tests/
test_client.py
test_e2e.py必须更新 Windows 的 vs/dosbox-x.vcxproj 和对应 solution configuration。新增源文件未进入 Windows 构建系统即视为未完成。
5. 配置、传输与安全
5.1 配置文件
不得读取系统环境变量决定 endpoint、目标程序、工作目录、超时或认证信息。server 仅从显式传入的 --agent-config 读取 dotenv 格式的配置文件,例如 tests/agent/agent-test.env:
transport=named_pipe
endpoint=\\.\pipe\dosbox-agent-test
dosbox_executable=bin\x64\Agent Debug SDL2\dosbox-x.exe
dosbox_workdir=tests\agent\runtime
profile=production
request_timeout_ms=5000
max_message_bytes=1048576
max_memory_read_bytes=65536
max_trace_events=10000生产配置必须使用绝对路径;测试配置可使用相对 --agent-config 文件所在目录的路径。启动时必须将解析后的配置写入结构化启动日志,但不得记录认证 token。
5.2 传输
IRpcTransport 是唯一 transport 抽象,协议层不得依赖具体 named pipe 实现或 stdio。
- Windows v1 默认使用 named pipe,并设置仅当前用户可访问的 ACL。
- v1 不开放 TCP listener,不支持远程无认证连接。
- 所有 transport 必须按 JSON-RPC 2.0 传输完整 request/response;流式 transport 的 framing 采用 UTF-8 JSON Lines,一行一个 JSON object,最大消息长度受
max_message_bytes限制。 - server 与 bridge 使用独立的本地 IPC endpoint,并使用每次启动生成的一次性 token 做握手;token 只经受限配置文件或命令行参数传递,不写入日志。
5.3 并发、超时和幂等
- 每个 session 的调试操作严格串行;请求按接收顺序进入 emulation-thread queue。
execution.continue、execution.pause和session.stop返回operation_id。客户端通过execution.wait获取停止结果。- 写内存、创建/删除断点、单步和原始 debugger 命令是有副作用操作。超时后 server 不得自动重试。
- 客户端重试必须携带相同
request_id。server 在 session 生命周期内缓存最近 1024 个已完成 request 的结果;同一request_id的 payload 不同返回REQUEST_ID_CONFLICT。 execution.wait超时只表示仍在运行,返回running=true;不得把超时伪造为断点、暂停或程序退出。
6. 会话状态机
created -> starting -> stopped -> running -> stopped
| | |
v v v
failed exited stopping -> exited状态定义:
starting:DOSBox-X 已启动,DOS environment 或 target 正在初始化。stopped:emulation 已停在 entry、breakpoint、step、明确 pause 或 fault;可读取/写入 state、memory 和 breakpoints。running:target 正在执行;除session.status、execution.pause、execution.wait、debug.output.read外,所有观察/修改状态方法必须返回TARGET_RUNNING。exited:target 已退出;会话状态与最后 stop reason 可读,内存和寄存器不可读。failed:启动或 bridge 出错;只允许session.status、debug.output.read和session.stop。
所有成功响应必须包含 session_id 和递增的 state_revision。state_revision 在每次停止、单步、写内存和断点集合变化后增加,用于客户端发现过期观察结果。
7. 数据约定
7.1 数值和地址
JSON number 不能无损表达所有未来 guest address,v1 所有 guest 寄存器、offset、selector、flags 和 breakpoint 地址均使用固定宽度的大写十六进制字符串。
{
"ax": "0x1234",
"eip": "0x00000100",
"flags": "0x00000202"
}地址是 tagged object,不接受模糊的 "segment:offset" 字符串:
{
"space": "segmented",
"segment": "0x1234",
"offset": "0x00000020"
}支持的 space 值:
segmented:按当前 CPU mode 解析 selector:offset;real/v86 mode 中按段地址计算,protected mode 中遵从 descriptor 和 paging。linear:CPU linear/virtual address;需要正常的 paging 检查。physical:物理内存地址;不通过 CPU paging。
地址未映射、selector 无效、offset 越界或 page 不存在时必须返回 ADDRESS_NOT_MAPPED,并在 error.data 返回 space、address 和底层原因(invalid_selector、segment_limit、page_not_present 等)。不得将访问悄悄转换为 physical 地址。
7.2 二进制和分页
- 内存 bytes 一律使用 RFC 4648 base64;返回
byte_count、data_base64和 SHA-256。 - 单个
memory.read最大值为配置的max_memory_read_bytes;超限返回REQUEST_TOO_LARGE,不截断。 debug.output.read和trace.read使用显式cursor、limit和next_cursor分页。cursor 过期返回CURSOR_EXPIRED,不从头补发。
7.3 统一响应和错误
成功响应:
{
"jsonrpc": "2.0",
"id": "req-0001",
"result": {
"session_id": "ses-...",
"state_revision": 7
}
}错误响应:
{
"jsonrpc": "2.0",
"id": "req-0001",
"error": {
"code": -32012,
"message": "Target must be stopped before reading memory",
"data": {
"reason": "TARGET_RUNNING",
"session_id": "ses-...",
"state": "running"
}
}
}保留 JSON-RPC 标准错误 -32600、-32601、-32602 和 -32603。业务错误使用 -32000 到 -32099,至少包含:
| reason | 使用场景 |
|---|---|
DEBUGGER_UNAVAILABLE |
构建未启用 C_DEBUG。 |
SESSION_BUSY / SESSION_NOT_FOUND |
会话生命周期错误。 |
TARGET_RUNNING / TARGET_NOT_STOPPED / TARGET_EXITED |
状态机违反。 |
CAPABILITY_UNAVAILABLE |
例如未启用 C_HEAVY_DEBUG 的 trace,或底层不支持的 memory breakpoint。 |
ADDRESS_NOT_MAPPED / ADDRESS_OUT_OF_RANGE |
地址解析或访问失败。 |
REQUEST_TOO_LARGE / INVALID_BINARY_LENGTH |
二进制请求不满足限额或长度。 |
REQUEST_ID_CONFLICT |
相同 request id 的 payload 不一致。 |
COMMAND_REJECTED / COMMAND_PARSE_FAILED |
原始 debugger 命令失败。 |
OPERATION_TIMEOUT / CURSOR_EXPIRED |
等待或分页语义错误。 |
8. RPC v1 方法
所有方法名和 request/response field 是兼容性契约。新字段只能可选新增;删除或改变现有字段语义必须升级 major version。
8.1 Capability 和会话
| 方法 | 参数 | 成功结果 | 约束 |
|---|---|---|---|
agent.capabilities |
无 | protocol_version、debugger、trace.cpu、breakpoints.memory_change、limits、address spaces |
无 session 时也可调用。 |
session.start |
target.command、target.arguments、mounts、break_at |
session_id、state=stopped、stop_reason=startup |
break_at v1 固定为 entry;目标必须在受控 workdir 内启动。 |
session.status |
session_id |
state、target、last_stop、state_revision | 任意 session 状态均可调用。 |
session.stop |
session_id、graceful_timeout_ms |
operation_id |
只请求停止;结果由 execution.wait 获取。 |
session.start 示例:
{
"jsonrpc": "2.0",
"id": "req-start",
"method": "session.start",
"params": {
"target": {
"command": "AGENTFIX.COM",
"arguments": []
},
"mounts": [
{"drive": "C", "host_path": "tests/agent/runtime"}
],
"break_at": "entry"
}
}8.2 执行控制
| 方法 | 参数 | 成功结果 | 底层对应 |
|---|---|---|---|
execution.continue |
session_id |
operation_id、state=running |
RUN 及现有 normal loop。 |
execution.pause |
session_id |
operation_id |
调度 debugger break;不能从 worker thread 直接改 debugger state。 |
execution.step |
session_id、`mode=into |
over` | stop_reason=step、register snapshot |
execution.wait |
session_id、operation_id、timeout_ms |
stopped/exited 状态,或 running=true |
等待 continue、pause 或 stop 的终态。 |
停止事件必须是结构化对象:
{
"kind": "breakpoint",
"breakpoint_id": "bp-3",
"address": {
"space": "segmented",
"segment": "0x1234",
"offset": "0x00000106"
}
}合法 kind:startup、step、breakpoint、pause、program_exit、fault。execution.wait 返回 running=true 时不得同时给出 stop_reason。
8.3 CPU state
state.get_registers 仅允许在 stopped 状态调用,返回:
{
"general": {
"eax": "0x00001234",
"ebx": "0x00000000",
"ecx": "0x00000000",
"edx": "0x00000000",
"esi": "0x00000000",
"edi": "0x00000000",
"ebp": "0x00000000",
"esp": "0x0000FFFE"
},
"segments": {
"cs": "0x1234",
"ds": "0x1234",
"es": "0x1234",
"fs": "0x0000",
"gs": "0x0000",
"ss": "0x1234"
},
"instruction_pointer": "0x00000103",
"flags": "0x00000202",
"cpu_mode": "real",
"state_revision": 8
}v1 不提供 state.set_registers。现有 SR 仅可通过 debugger.execute_command 用于人工诊断,不能成为 Python client 的稳定写寄存器 API。
8.4 Memory
memory.read 参数:session_id、address、length。成功结果包含原始地址、byte_count、data_base64、sha256。
memory.write 参数:session_id、address、data_base64、可选 expected_sha256。实现要求:
- base64 decode 后长度必须大于 0 且不超过
max_memory_read_bytes。 expected_sha256存在时,先读取同一范围并精确匹配;不匹配返回MEMORY_PRECONDITION_FAILED,不得写入任何字节。- 写入前后读取同一范围,返回
before_sha256、after_sha256和byte_count。 - 任意一个字节地址不可访问时,整次写入失败并返回失败地址;不得只写可访问前缀。
8.5 Breakpoint
| 方法 | 参数 | 结果 | 规则 |
|---|---|---|---|
breakpoints.create |
session_id、kind、address、可选 once |
稳定 breakpoint_id、规范化地址 |
kind=execution 为 v1 必选。 |
breakpoints.list |
session_id |
所有 breakpoint 的 id、kind、enabled、address、once | 不能解析 BPLIST 文本作为正式实现。 |
breakpoints.delete |
session_id、breakpoint_id |
删除确认和 revision | 删除不存在 id 返回 BREAKPOINT_NOT_FOUND。 |
kind=memory_change 仅在 agent.capabilities.breakpoints.memory_change=true 时可创建。实现必须明确 address space 和 CPU mode 的支持范围;不支持时返回 CAPABILITY_UNAVAILABLE,不得创建普通执行断点替代。
RPC id 不得复用 CBreakpoint 当前显示列表 index。adapter 必须生成 session 内稳定的 bp-* id,并维护其与底层 breakpoint object 的映射,避免 BPDEL 后索引变化导致误删。
8.6 Debugger 输出、原始命令和 trace
| 方法 | 作用 | 结果约束 |
|---|---|---|
debug.output.read |
读取 debugger output ring buffer | 每条记录有 sequence、timestamp、level、source、message。 |
debugger.execute_command |
执行原始 debugger command | 返回 accepted、raw_output、可选 parser result;不替代任何结构化方法。 |
trace.start |
开始指定条数的 CPU trace | 仅 C_HEAVY_DEBUG;参数为 `detail=short |
trace.read |
分页读取 trace events | event 必含 address、instruction、register_changes 和 sequence。 |
trace.stop |
停止尚未完成的 trace | 返回已收集 event 数,不能伪称已完成。 |
trace 的数据源必须是 in-process TraceSink,在既有 heavy debug 指令日志点采集结构化事件。LOGCPU.TXT 可以作为人工对照产物,但不得作为正式 API 的唯一来源,也不得在多个 session 间共享文件名。
DebuggerOutputParser 的规则:
- parser 输入、输出和失败原因必须可单元测试;测试样本存放在
tests/agent/fixtures/。 - 解析失败返回
parse_status=failed及unparsed_output,不得返回部分 register object 伪装为完整结果。 - raw command 不能改变 session 状态机以外的状态;检测到
RUN、LOG*、BP*、SM、SR等影响正式 API 状态的命令时,server 必须更新state_revision和相关 collector,或拒绝该命令并返回COMMAND_REJECTED。第一期推荐采用 allowlist,只允许只读诊断命令:HELP、CPU、PIC、GDT、IDT、PAGING、EMU MEM、EMU MACHINE。
9. Python Client 契约
Python client 必须只调用 JSON-RPC,不得通过 stdout、窗口自动化或直接读取 DOSBox-X 输出文件工作。
from dosbox_agent import AgentClient
with AgentClient.from_config("tests/agent/agent-test.env") as agent:
session = agent.start("AGENTFIX.COM", break_at="entry")
agent.create_execution_breakpoint(session.id, segment="0x1234", offset="0x00000106")
operation = agent.continue_(session.id)
stop = agent.wait(session.id, operation.id, timeout_ms=5000)
registers = agent.get_registers(session.id)
memory = agent.read_memory(session.id, space="segmented", segment="0x1234", offset="0x00000200", length=4)要求:
- 公开方法使用 typed model,并把 RPC error 转为
AgentError子类;不得让调用方检查错误字符串。 continue_使用尾部下划线避免与 Python keyword 冲突。- bytes 参数和结果使用
bytes;client 负责 base64 编解码。 - client 对有副作用调用不自动重试;可由调用方以同一 request id 明确重试。
- 每个 RPC method 至少有一个 client unit test 和一个反序列化/错误映射测试。
10. 测试夹具
新增确定性的 16-bit COM 测试程序 tests/agent/fixtures/agent_fixture.asm,编译产物 agent_fixture.com 必须提交或由确定性构建步骤生成。程序固定从 CS:0100 开始,执行以下语义:
org 100h
start:
mov ax, 0x1234
mov bx, 0x5678
mov si, 0x0200
mov byte [si], 0x41
mov word [si + 1], 0x4243
nop
int 0x20夹具的明确断言:
- entry 的
IP为0x00000100。 - 单步进入一次后,
AX为0x00001234,IP为0x00000103。 CS:0106是第二条指令的起点;在此创建执行断点后 continue 必须停在该地址。- 五次单步后,
DS:0200的四字节是41 43 42 00(最后一个字节为 fixture 未写入的原始值,不作为固定断言);前三字节必须精确为41 43 42。 - memory.write 向可读写的 fixture data 区写入四字节后,随后 memory.read 的 bytes 和 SHA-256 必须一致。
夹具必须在 runtime 目录的干净副本中运行。验收不得复用上次运行的 LOGCPU.TXT、MEMDUMP.*、DOSBox-X 配置或 trace 文件。
11. 分阶段实施与检查表
每一项格式为:[ ] ID - 交付物;检查;通过标准;证据,完成项使用 [x]。证据可填写测试日志路径、CI job URL 或 commit hash。
以下所有命令均在 Windows PowerShell 中执行,路径使用 Windows 分隔符。
当前状态(2026-09-02):阶段 A、B、C、D、E 已完成并已在 Windows 验证。
阶段 A:构建和架构(已完成)
-
RPC-A01- 新增Agent Debug SDL2Windows 配置,定义C_DEBUG=1;C_HEAVY_DEBUG=1;C_DOSBOX_AGENT=1;运行msbuild .\vs\dosbox-x.sln /m /t:dosbox-x /p:Configuration="Agent Debug SDL2" /p:Platform=x64;退出码为 0,生成bin\x64\Agent Debug SDL2\dosbox-x.exe;证据:2026-09-02 Windows PowerShell,msbuild退出码 0,0 个警告、0 个错误。 -
RPC-A02- Windows 项目编入全部 agent 源文件;检查vs\dosbox-x.vcxproj的ClCompile项包含src\agent\,并执行msbuild .\vs\dosbox-x.sln /m /t:dosbox-x /p:Configuration="Agent Debug SDL2" /p:Platform=x64;退出码为 0;证据:2026-09-02,项目已编入 bridge、debugger、protocol、server 和 transport 的 5 个 agent 源文件;同次msbuild退出码 0。 -
RPC-A03- agent bridge、server、transport 和 adapter 有独立职责与头文件边界;审查src\agent\,确认 transport 层没有reg_、mem_read、CBreakpoint引用;证据:2026-09-02,rg -n 'reg_|mem_read|CBreakpoint' src\agent\transport无匹配。 -
RPC-A04- 所有 DOSBox-X 状态访问都在 emulation-thread queue 执行;执行& '.\bin\x64\Agent Debug SDL2\dosbox-x.exe' --agent-config tests\agent\agent-test.env --agent-self-test,连续并发提交 1000 个只读请求;退出码为 0,日志确认 FIFO 响应顺序与提交顺序一致,且无死锁或失败请求;证据:2026-09-02,命令退出码 0,输出Agent self-test completed: success。 -
RPC-A05- 配置只从--agent-config加载;执行Remove-Item Env:DOSBOX_AGENT_ENDPOINT, Env:DOSBOX_AGENT_WORKDIR, Env:DOSBOX_AGENT_REQUEST_TIMEOUT_MS -ErrorAction SilentlyContinue后,以--agent-config tests\agent\agent-test.env启动;endpoint、workdir 和 limit 与 env 文件一致;证据:2026-09-02,清空当前 PowerShell 进程变量后自检成功;日志确认 endpoint=\\.\pipe\dosbox-agent-test、workdir=D:\workspace\ai\yl\dosbox-x\tests\agent\runtime、request_timeout_ms=5000、max_message_bytes=1048576、max_memory_read_bytes=65536、max_trace_events=10000。
阶段 B:协议和会话(已完成)
-
RPC-B01- JSON-RPC 2.0 request validation;对缺失jsonrpc、未知 method、错误 field type 和超过max_message_bytes的请求运行 protocol unit tests;分别返回标准 JSON-RPC 错误或REQUEST_TOO_LARGE;证据:2026-09-02,--agent-self-test成功,覆盖缺失jsonrpc、未知 method、params 类型错误和超限 request。 -
RPC-B02-agent.capabilities;在C_DEBUG+C_HEAVY_DEBUGbuild 中调用;声明debugger=true、trace.cpu=true、三种 address space 和数值 limits;证据:2026-09-02,Windows named pipe 返回debugger=true、trace.cpu=true、segmented、linear、physical及配置 limits。 -
RPC-B03- non-heavy build capability 退化;在仅C_DEBUGbuild 中调用agent.capabilities和trace.start;前者trace.cpu=false,后者返回CAPABILITY_UNAVAILABLE;证据:2026-09-02,Agent Debug No Heavy SDL2|x64named-pipe 验收返回上述结果。 -
RPC-B04-session.start在 fixture entry 停止;调用 start 后session.status.state=stopped、last_stop.kind=startup、IP=0x00000100;证据:2026-09-02,AGENTFIX.COM返回0x0812:0x00000100和kind=startup。 -
RPC-B05- 单 session 限制;会话处于 stopped 或 running 时再次调用session.start;返回SESSION_BUSY,原会话的 state_revision 和 breakpoint 集合不变;证据:2026-09-02,重复 start 返回SESSION_BUSY,原 session revision 保持1。 -
RPC-B06- 状态机错误;会话 running 时调用state.get_registers、memory.read、memory.write;全部返回TARGET_RUNNING,不改变 target;证据:2026-09-02,AGENTRUN.COMcontinue 后三种请求均返回TARGET_RUNNING。 -
RPC-B07- operation timeout 语义;continue 后用极短 timeout 调用 wait;返回running=true且没有 stop_reason,随后 pause/wait 可以得到kind=pause;证据:2026-09-02,execution.wait(timeout_ms=1)返回running=true,pause 后 wait 返回 stopped、revision2和kind=pause。 -
RPC-B08- request id 幂等;相同 payload 使用同一 request id 重发 memory.write,验证只执行一次;同 id 不同 payload 返回REQUEST_ID_CONFLICT;证据:2026-09-02,干净 runtime 中对AGENTFIX.COM的0x0812:0x00000200写入AGENT后,两次id=write-once响应均为 revision2、SHA-256=b62db7d0b79d11100c7103df97dd87bc90ff6ffbd88de6019cf1e933069c8525;相同 id 的不同 payload 返回REQUEST_ID_CONFLICT。
阶段 C:Debugger Adapter
-
RPC-C01-state.get_registers结构化完整性;fixture entry 和单步后分别读取;general、segments、instruction_pointer、flags、cpu_mode 均存在且使用固定宽度十六进制字符串;证据:2026-09-02,Windows named-pipe 对AGENTFIX.COMentry 返回cpu_mode=real、IP=0x00000100、8 个 32-bit general registers、6 个 16-bit segments 和 32-bit flags;单步结果亦返回完整 snapshot。 -
RPC-C02-execution.step(mode=into);从 fixture entry 单步一次;AX=0x00001234、IP=0x00000103、stop_reason.kind=step;证据:2026-09-02,Windows named-pipe 对AGENTFIX.COM返回kind=step、EAX=0x00001234、instruction_pointer=0x00000103。 -
RPC-C03-execution.step(mode=over);在 call、int、rep 夹具扩展用例中验证越过语义;执行后不进入被越过目标,且没有泄漏临时 breakpoint;证据:2026-09-02,Windows named-pipe 使用独立进程运行AGCALL.COM、AGENTINT.COM、AGENTREP.COM;分别停在0x00000103、0x00000102、0x0000010B,REP destination 为41 42,三次breakpoints.list均无泄漏的用户可见 breakpoint,over 响应均含 registers snapshot。 -
RPC-C04-memory.read;读取 fixture code 开头 6 bytes;base64 decode 后严格等于B8 34 12 BB 78 56,byte_count=6,SHA-256 匹配;证据:2026-09-02,Windows named-pipe 分别使用 segmented、linear、physical 读取,均返回 base64uDQSu3hW、byte_count=6、SHA-25699b28729074f19ab5d3edfab4f425f3d88e4601acd37ae9e6fd43e46b85a5c9f。 -
RPC-C05- 三地址空间和失败路径;分别测试 segmented、linear、physical 成功访问,以及 invalid selector、segment limit、page not present;失败必须返回ADDRESS_NOT_MAPPED和精确 reason;证据:2026-09-02,tests\agent\verify_memory_mapping.ps1在 Windows named-pipe 运行AGPMODE.COM,保护模式分页停止后,三地址空间各读取 1 byte 成功;失败请求均返回ADDRESS_NOT_MAPPED,cause 分别为invalid_selector、segment_limit、page_not_present。 -
RPC-C06-memory.write原子性;expected SHA 不匹配时验证写前后 bytes 相同;匹配时验证写后 bytes、after SHA 和 byte_count;证据:2026-09-02,Windows named-pipe 对 fixtureDS:0200写入DE AD BE EF;错误 SHA 返回MEMORY_PRECONDITION_FAILED且读回 bytes 未变,正确 SHA 返回byte_count=4、after SHA5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953,随后读回DE AD BE EF。 -
RPC-C07- 执行断点;在CS:0106创建后依次调用 continue 和 wait;stop_reason.kind=breakpoint且 address 与创建地址一致;证据:2026-09-02,Windows named-pipe 创建bp-1于CS:0x00000106,continue/wait 返回kind=breakpoint、breakpoint_id=bp-1和同一 segmented address。 -
RPC-C08- breakpoint stable id;创建多个 breakpoint、删除中间一个并列出;其他 id 不改变,按原 id 删除目标不会误删;证据:2026-09-02,Windows named-pipe 创建bp-1、bp-2、bp-3,删除bp-2后 list 仍为bp-1、bp-3,再按bp-3删除成功。 -
RPC-C09- memory-change breakpoint capability;capability true 的 build 使用实际底层支持的 CPU mode 验证一次触发;capability false 的 build 返回CAPABILITY_UNAVAILABLE,不得转换为 execution breakpoint;证据:2026-09-02,heavy build 在 real mode 对DS:0200创建bp-1并由 fixture 写入实际触发;Agent Debug No Heavy SDL2|x64capability 返回memory_change=false,同一 create 返回CAPABILITY_UNAVAILABLE。 -
RPC-C10-session.stop;running 和 stopped 状态各执行一次 stop/wait;最终 state=exited,重复 stop 为幂等且不崩溃;证据:2026-09-02,Windows named-pipe 分别在 stoppedAGENTFIX.COM和 runningAGENTRUN.COM调用 stop/wait,均返回state=exited、kind=program_exit;exited 后重复 stop 返回op-stop-complete。
阶段 D:输出、trace 和兼容命令
-
RPC-D01- debugger output ring buffer;执行一个只读诊断命令后debug.output.read返回递增 sequence、非空 source 和完整 message;分页连接无重复无遗漏;证据:2026-09-02,tests\agent\verify_output_trace.ps1以limit=1完整读取HELP、CPU、PIC三条输出记录,sequence 连续且 source/message 非空。 -
RPC-D02- 原始命令 allowlist;HELP、CPU、PIC可执行并返回 raw_output;RUN、SM、SR、BP*在 v1 被拒绝或由 adapter 显式同步 state,测试覆盖每种结果;证据:2026-09-02,tests\agent\verify_output_trace.ps1验证三个允许命令均有非空raw_output,并验证RUN、SM、SR、BP 1234:0100均返回COMMAND_REJECTED。 -
RPC-D03- parser 失败显式化;向 parser fixture 输入缺少字段和异常空格的文本;返回parse_status=failed和未解析文本,不产生伪完整对象;证据:2026-09-02,新增tests\agent\agent_parser_tests.cpp和两个 fixture;& '.\bin\x64\Agent Debug SDL2\dosbox-x.exe' -tests退出码为 0。 -
RPC-D04- CPU trace;在 heavy build 对 fixture 启动trace.start(instruction_count=2)并运行;trace.read恰好得到 2 条递增 sequence,每条包含 address、instruction、register_changes;证据:2026-09-02,tests\agent\verify_output_trace.ps1返回通过,两个 trace event 的 sequence 为 1、2,均包含所需字段。 -
RPC-D05- trace 隔离;连续两个独立 session 的 trace 不读取前一 session 的事件或文件;删除 runtime 后重跑结果一致;证据:2026-09-02,tests\agent\verify_output_trace.ps1在同一 DOSBox-X 进程的第二个 session 启动独立 1 条 trace,并确认 sequence 从 1 开始且没有前一 session 的事件;另以原本不存在的tests\agent\runtime-d05-clean目录、显式agent-d05-clean.env和新生成 fixture 重跑同一脚本通过,未复用原 runtime 文件。
阶段 E:Client 和端到端
-
RPC-E01- Python client unit tests;执行py -m unittest discover -s client\python\tests -t client\python -v;退出码为 0,覆盖 model、base64、error mapping、request id retry;证据:2026-09-02,3 个 unittest 全部通过;覆盖显式 dotenv 相对路径解析、全部 v1 client method 的 typed response、base64 写入、TARGET_RUNNINGerror mapping 和相同request_id的显式重试。 -
RPC-E02- 端到端 fixture;执行py client\python\tests\test_e2e.py --config tests\agent\agent-test.env;依次完成 start、breakpoint、continue、wait、register read、memory read、memory write、step、stop;退出码为 0;证据:2026-09-02,Windows named-pipe E2E 通过,同时验证CPU诊断输出和trace.start(instruction_count=2)的 session-local sequence。 -
RPC-E03- DOSBox-X 既有单元测试未回归;执行& '.\bin\x64\Agent Debug SDL2\dosbox-x.exe' -tests;退出码为 0 且输出Unit test completed: success;证据:2026-09-02,-tests退出码为 0;当前 Windows GUI build 不向调用 PowerShell 转发测试日志,shell.cpp的RUN_ALL_TESTS()返回值为进程退出状态。 -
RPC-E04- 干净运行可重复;执行if (Test-Path tests\agent\runtime) { Remove-Item -Recurse -Force tests\agent\runtime }; New-Item -ItemType Directory -Path tests\agent\runtime后,连续运行 E02 三次;三次均通过,trace 和输出无跨运行数据;证据:2026-09-02,tests\agent\verify_client_clean_runs.ps1创建三个此前不存在的独立 runtime/config 目录并串行运行 E02 三次,三次均通过;每次 E2E 断言 output sequence 从 1 开始、trace sequence 恰为 1、2。
12. 发布门禁
以下条件全部满足才能将 RPC 标为可用:
- 阶段 A 到 E 所有必选检查项已勾选并有可访问证据。
docs/rpc.md中列出的第一期能力均映射到一个 v1 RPC method 或在agent.capabilities中有明确、可测试的 unavailable 说明。- 所有结构化接口都有正向、非法参数、running 状态和底层 capability 缺失测试。
- Python client 的 public API 与本文第 9 节一致,且示例可通过 E02。
- 默认 transport 只暴露本机当前用户;没有未认证 TCP listener。
- 不存在依赖 parser、ncurses 截图、stdout scraping 或共享
LOGCPU.TXT的核心 API 数据路径。
13. 非目标与后续版本
以下能力不属于 v1,不能为了“先跑通”混入稳定 API:CPU instrumentation、全量 memory access trace、interrupt trace、file I/O trace、screenshot analysis、IDA/Ghidra 集成、多目标并行 session、远程 TCP transport 和写寄存器 API。
后续能力必须先补充本文:数据模型、capability 标志、线程模型、测试夹具和至少一个端到端检查项,然后才能实现。
Rpc (`docs/rpc.md`)
开发一个 DOSBox-X Debugger Agent Interface,将 DOSBox-X 现有 Debugger 能力封装成 Agent 可调用接口。
面向 AI Agent 的实际使用流程请先阅读:DOSBox-X Debugger Agent 使用手册。
协议契约与验收规范分别见:rpc-development.md。
目标:
让 AI Agent 能够通过结构化 API 控制 DOSBox-X 调试环境,实现:
- 启动/停止 DOS 程序
- 控制程序运行
- 单步执行
- 获取 CPU 状态
- 读取/修改内存
- 设置/管理断点
- 获取调试输出
- 获取执行日志
用于:
- DOS 程序逆向分析
- 老游戏行为分析
- 二进制重构辅助
- 动态验证静态分析结果
不要重新实现:
- CPU 调试逻辑
- 内存访问逻辑
- 断点机制
- 单步机制
利用 DOSBox-X 已有能力。
目标:
Agent
 |
RPC Interface
 |
Debugger Adapter
 |
DOSBox-X Debugger
 |
DOSBox-X Emulator
 |
DOS Program第一阶段:
不要修改:
- CPU execution core
- memory subsystem
- DOS kernel simulation
除非现有 debugger 能力不足。
不要简单暴露:
输入命令
返回文本需要提供:
结构化接口:
例如:
{
 "registers":{
  "ax":123,
  "cs":"1000",
  "ip":"0020"
 }
}而不是:
AX=0000 BX=0001...  AI Agent
  |
  |
  Agent API / RPC Layer
  |
  |
  DOSBox Debugger Adapter
  |
  |
  DOSBox-X Debugger
  |
  |
  DOSBox-X Runtime
  |
  |
  DOS Executable负责:
- 启动 DOSBox-X
- 管理调试会话
- 维护连接状态
- 发送 debugger command
- 获取 debugger response
抽象接口:
start()
stop()
execute(command)
status()要求:
- 不绑定具体通信方式
提供 Agent 调用入口。
要求:
支持:
- JSON RPC
- 或等价结构化接口
API 设计保持稳定。
提供:
功能:
- 加载 DOS 环境
- 启动目标程序
继续运行程序。
对应 debugger:
G单步执行。
对应:
T暂停执行。
提供:
- 通用寄存器
- 段寄存器
- IP
- FLAGS
例如:
请求:
get\_registers返回:
{
 "ax": "...",
 "bx": "...",
 "cs": "...",
 "ip": "...",
 "flags": "..."
}提供:
能力:
- 指定地址读取
- 返回二进制数据
例如:
{
 "address":"segment:offset",
 "length":128
}能力:
修改目标内存。
提供:
支持:
- 地址断点
- 内存访问断点(如果 debugger 支持)
保留底层能力。
提供:
execute\_debugger\_command例如:
Agent 可以执行:
CPU
MEMDUMP
HELP用途:
- 调试
- 新功能验证
- 后续扩展
利用 DOSBox-X 已有日志能力。
提供:
例如:
trace.start返回结构化数据:
\[
 {
  "address":"",
  "instruction":"",
  "register\_change":{}
 }
]如果当前 debugger 输出无法完全解析:
设计独立 parser 模块。
不要让 Agent 直接依赖 DOSBox-X 文本输出。
增加:
Debugger Output Parser负责:
文本:
AX=xxxx
CS:IP=xxxx转换:
结构:
{
 "ax":"",
 "cs":"",
 "ip":""
}dosbox-agent-interface
├── server
│
├── debugger
│ ├── session
│ ├── command
│ └── parser
│
├── api
│
├── client
│
├── tests
│
└── docs目标:
成功控制 DOSBox-X debugger。
实现:
- session管理
- command发送
- 输出读取
- 基础解析
实现:
- run
- step
- continue
- registers
- memory
- breakpoint
实现:
- debugger log采集
- trace解析
- 结构化输出
提供:
Python client:
让 Agent 可以:
debugger.run()
debugger.get\_registers()
debugger.step()
debugger.read\_memory()保持接口可扩展:
未来可以加入:
- CPU instrumentation
- memory access tracing
- interrupt tracing
- file IO tracing
- screenshot analysis
- IDA/Ghidra integration
但不要在当前项目阶段实现。
完成后,一个 Agent 可以:
1. 启动 DOS 程序
2. 连接 debugger
3. 设置断点
4. 运行到断点
5. 获取寄存器状态
6. 读取内存
7. 单步执行
8. 收集执行日志
9. 根据结构化数据进行分析
最终目标:
把 DOSBox-X Debugger 转换为一个 AI Agent 可以调用的动态分析后端,而不是重新开发一个调试器。
PLANS General TODO (`docs/PLANS/General TODO.txt`)
Some plans for future DOSBox-X developments
Clock domains
- Current DOSBox-X emulation is CPU-centric, which is OK, I guess,
but not ideal for much of the emulation. - Many delays and timing are based on floating point values in milliseconds.
If clock domains were implemented, devices could count very accurately
in clocks at a rate of their choosing and better maintain timing that way. - Clock domains could be coded to cascade as well. The obvious example is
original PC hardware where the ISA 14.31818MHz clock is divided by 12 to
produce the 1.193182MHz clock used to drive the 8253 PIT and other system
components. - One part of the emulation that would definitely benefit from clock domain
based timing would be the VGA emulation. Pixel and scanline counting would
become very accurate if counted with integers according to pixel clocks
instead of using the floating math it does now. - Another part of the emulation that would benefit would be the I/O delay
emulation. Instead of bumping a fixed number of CPU cycles, the code could
more accurately delay CPU timing according to, say, an ISA bus clock
domain set to 8.3333MHz and knowledge of a typical ISA bus cycle. Or, if
the I/O cycle is directed at the PCI bus (clock domain set to 33.3333Mhz)
delays emulated according to PCI bus cycle or PCI burst I/O cycle counting.
- Current DOSBox-X emulation is CPU-centric, which is OK, I guess,
Runtime hooks for patching
- If told to do so, DOSBox-X can look for an IPS file (or DOSBox-X equivalent)
and when a particular EXE is loaded into memory, patch the executable image
before executing it. The idea being that DOS gamers can use this mechanism
to fix bugs in a game or demo without modifying the original EXE. It can also
be a fun way to cheat :) - We could assist this mechanism with the ability for the user to force an EXE
file to be loaded at a specific segment, in case the patch is sensitive to
segment values. Loading must fail of course if we cannot put the EXE at that
location. - We could also add the ability for DOSBox-X to patch data files when the game
reads them from disk, to hack textures, sounds, etc. Again the data file
remains unmodified on disk. - On a related note, it would be cool if we could implement something like
Bochs volatile mode where data is read from the file, but writes to the file
are directed to a temporary file which is thrown away on exit, keeping your
files unmodified. - If executable unpacking is involved, some additional tools could be provided
where the patch does not modify the loaded EXE, but can be instructed to
patch an area of memory when the instruction pointer hits a specific location
in the EXE (usually when the EXE has finished unpacking and checking it's
integrity).
- If told to do so, DOSBox-X can look for an IPS file (or DOSBox-X equivalent)
Support for loading BIOS images and mapping them into adapter ROM
- We could test various VGA BIOSes in DOSBox-X that way
- We could also run old motherboard BIOSes in DOSBox-X that way
Convert I/O and memory handler callback system to on-demand bus mapping and
tracing design- Instead of statically assigning I/O and memory handlers, all handlers are
initialized to a lookup function. - The DOSBox motherboard emulator at this point would also maintain its own
setup of a system bus hierarchy (system resources and RAM vs forwarding to
the ISA bus, or a PCI-based system that forwards to PCI which can forward
via PCI-ISA bridge to ISA, etc.). - When the lookup function is called, the function goes through the "slow"
path where it asks the motherboard what function to call, and the
motherboard returns a function pointer according to its designation of
routing, which is then written into the I/O handler. In other words, once
an I/O port has been accessed, further access is a direct call to the
I/O callback. - If resources or mappings change on the bus, then all I/O ports affected
have the I/O handler changed back to the slow path lookup function, so
that changes take effect when the lookup function returns the new
result according to changed routing. - If the memory access involves resources that don't overlap but are too
small for DOSBox's 4KB granularity, then the "slow path" function will
always be invoked for that memory I/O because it is impossible to write
both callbacks to that memory page callback. - Once all I/O and memory resources are routed this way, it becomes easy
for DOSBox to emulate devices that can change I/O ports such as ISA PnP
type devices that allow you to change the base I/O port of a resource.
Sound Blaster ISA PnP emulation will no longer emit a warning when
Windows or any DOS program attempts to reprogram the base address.
It would also allow DOS or Windows to change, relocate, enable/disable
PCI resources. The only performance penalty is that every time a mapping
changes, the emulator has to invalidate all I/O and memory callbacks
to allow the "slow" path to do its work again. - The reason we don't do bus lookup every time is one of performance.
DOSBox-X already has an array of IO handlers and memory page handlers,
so why not make use of it as a cache for callbacks once the slow path
has been taken? Doing it this way should have almost no impact on
emulator performance.
- Instead of statically assigning I/O and memory handlers, all handlers are
Motherboard & BIOS oriented system construction
Rather than the one way the DOSBox kernel does it, the user could instead
specify that DOSBox-X emulate a particular motherboard with a particular
BIOS. The code could then construct I/O and memory management to emulate
the known chipsets on that board.Example: If instructed to, it would emulate the system board on an old
laptop of mine with a Chips & Tech 386 AT ASIC, or it could emulate an
old AST motherboard and instruct PIC emulation to act like a Unicorn
Microelectronics UM8259A-2. The default of course would carefully
mimic for backwards compatibility the DOSBox mainline "motherboard" and
"bios" configuration.Such construction may include (if on original hardware), VGA emulation
tuned to mimic the onboard VGA of the original hardware if user says it
should be enabled.
CD audio
- Refactor the code to ensure we feed out CD audio in a way the guest
can track its time accurately. - IDE/ATAPI emulation needs to support the mode select or whatever commands
used by DOS programs to change CD audio volume.
- Refactor the code to ensure we feed out CD audio in a way the guest
Full Intellimouse scrollwheel emulation
- Apparently it's possible to respond to scrollwheel events via SDL, so
add code to receive them. - Add code to store scroll wheel deltas in mouse event queue.
- Add code to take overall scrollwheel delta and transmit as 4th byte
in Intellimouse mouse on AUX. - And toughest of them all: Figure out how the hell Windows 98 is able
to use scrollwheel data when it is still reliant on the INT 15h
device callback that's documented only to carry the X, Y, and button
state data. If VirtualBox can do it, so can I! The ONLY documentation
I have on this are snippets of code from CuteMouse that is apparently
able to turn on PS/2 BIOS scroll wheel capture for DOS. In any case
perhaps the best course of action is to write a program in DOSLIB that
hooks the device callback and records the values on the stack during
the call, with and without the Intellimouse knock sequence in effect.
- Apparently it's possible to respond to scrollwheel events via SDL, so
Multiple DOS kernel emulation
- Instead of only emulating a general mishmash of MS-DOS 5.0 to 6.x syscalls,
allow dosbox-x.conf setting (and command line at runtime) to choose that
a particular brand and version is emulated. For example, if you say that
you want DOSBox-X to emulate MS-DOS 3.3, then it will return values and act
like MS-DOS 3.3 (including the shorter form of the disk parameter table
prior to MS-DOS 4.0). With the current code, if you say that you want
DOSBox-X to emulate MS-DOS 7.1, then it tries to return values and act
like MS-DOS 7.1 (including support for FAT32 drives and long filenames).
- Instead of only emulating a general mishmash of MS-DOS 5.0 to 6.x syscalls,
Misc
- If PCI emulation is enabled, then byte-sized writes to port 0xCF9
should follow Intel PIIX chipset documentation: if bit 1 changes from
0 to 1 the system undergoes a "soft reset", and if bit 2 changes from
0 to 1 the system undergoes a "hard reset". Reset method confirmed
working on an old Pentium system of mine, and it complements the
keyboard and port 0x92 methods well. - So what exotic method are older demos using the clear the screen anyway?
Can you figure it out? Their failure to clear the screen properly is
causing graphic anomalies during certain parts. - Memory and I/O resource management code. Rather than just pick regions
of memory and I/O space, the emulation code should have a way to mark
that such and such region has been taken for e.g. "VGA BIOS" and
"ROM BIOS". It would make it possible for adapter emulation to know
if the region it is trying to take is already occupied, and provide
a way for said adapter (unless instructed by user) to automatically
pick a location that it can claim. It would also make for good
debugging information. - Peripherals should assign themselves to a bus if possible. Sound Blaster
emulation for example would register itself to the I/O ports on the
ISA bus. Voodoo 3dfx emulation would register itself to the PCI bus. - Support not just for declaring an ISA bus and its frequency, but also
for ISA devices that you would declare "attached" to the bus (same with
PCI). Instead of DOSBox's design where peripherals are static and only
one at a time is possible, we would allow you to declare peripherals
and declare them attached to the ISA bus slots. If you want to emulate
a DOS machine with three Sound Blaster cards, you could declare three
of them (with different resources of course) and have them attached to
the ISA bus. - If I wanted DOSBox-X to emulate a VESA local bus peripheral, would I
have to do anything extra to do it? - CMOS emulation. The current code sucks. Why exactly are all values
hard-coded? That's not how the RTC/CMOS works! The real deal has
clock registers in the first 14 bytes and the rest is non-volatile
RAM! Fix! - RTC/CMOS emulation: support non-realtime mode (where the clock does
NOT follow host time but follows time accurately according to emulation).
But of course the option to follow realtime would also be available. - IDE/ATA emulation: What exactly is triggering the debugger when
you boot MS-DOS 6.22 using the OAK CD-ROM driver? It needs to be removed,
it makes using MS-DOS 6.22 with debugger builds annoying. - VGA Linewise rendering is currently hardcoded for 16bpp RGB. Why?
Add option to allow the user to choose 32bpp RGBA linewise rendering. - VGA output: add optional scalar or post-processing that can auto-detect
fake high-color VGA modes in demos, and process the scanlines such that
the RGB spreads vertically to mask the raster scanline effect (so that
the picture is presentable even if rescaled later) - VGA output: Add support for VESA 24bpp modes (DOSBox's scalers are
currently hard-coded for 8/16/32bpp). There are some demos (a 1997
Coma demo comes to mind) where if a 24/32bpp mode is chosen the demo
assumes 24bpp RGB arrangement. - VGA debugging function: Add code that, if triggered, allows DOSBox to
pause emulation and allow you to examine/play with the VGA palette, DAC,
registers, screen bytes, etc. The reason I want to do this is that there
are some demos (listed elsewhere in the BUGS section) where for whatever
reason there are VGA palette errors, some entries remain black despite
palette animation. The "debugger" would overlay the VGA screen, of course. - VGA "fixed output" mode. A scalar would be invoked if enabled that
upscales all VGA raster output to a specific resolution and frame rate
(the frame rate would be 250Hz, 300Hz, or 1000Hz so that refresh rate
changes are better represented accurately). This would be one way to
capture demos that change VGA modes once per section in one AVI file.
Scalar would include common VGA resolutions like 640x400, 640x480,
800x600, and 1024x768. The scalar could also distort the picture if
the CRT timing is known to cause older VGA monitors to squeeze the
picture (such as some older DOS demos that speed up vertical retrace
knowing it would cause older VGA monitors to focus the raster beam
in the center for a "widescreen" effect). - VGA overscan border rendering. Update/rewrite the raster linewise
renderers to include overscan border in output if asked to. - ODML AVI capture. The current code works great until you hit 2GB, then
falls apart. I would be willing to throw in code I've written for other
professional projects to write AVI files that are ODML compliant to
enable longer captures >= 2GB (contain both the AVIOLDINDEX and ODML
AVI index).
- If PCI emulation is enabled, then byte-sized writes to port 0xCF9
PLANS VMX Core (Using Linux KVM Virtualization Driver) (`docs/PLANS/VMX core (using Linux KVM virtualization driver).txt`)
For more demanding emulation I thought it might be a neat idea if
DOSBox could use the KVM virtualization driver in Linux (and eventually,
equivalent interfaces in other OSes) which could then be selectable
by setting core=vmx.
To explain: Almost all modern CPUs have virtualization extensions
built in (Intel VMX and AMD AMD-V). In cases where a DOS or Windows
game is very demanding, why not code DOSBox to allow using those
virtualization extensions? If done right, the game could run at
almost native speed of the processor. It would also open the
possibility of running more modern OSes under DOSBox (ones that
would normally run too slow in the full and dynamic cores).
Windows 3.1 INSTALL (`docs/Windows 3.1/INSTALL.MD`)
Installing Windows 3.X in DOSBox-X
(instructions tested against dosbox-x-windows-20180901-124401-windows.zip)
Remarks
Mouse
For the mouse to work after Windows installation, either of the following scenarios has to be met:
Scenario 1
- Mouse integration driver is not installed
- Mouse is captured with Ctrl+F10
- DOSBox-X has been reset at least once with F11+R
Scenario 2
- Mouse integration driver is installed and defined in dosbox-x.conf
- DOSBox-X has been reset at least once with F11+R
Video
This guide assumes you are using the default DOSBox-X machine svga_s3 for which DOSBox-X sets video memory to 512Kb by default. To use higher resolutions and color depths, adjust vmemsize in [dosbox] accordingly to your needs (see dosbox-x.reference.conf).
Installation
Windows 3.1
- download and extract DOSBox-X
- next to
DOSBox-X.exe, create aVirtualHDdirectory - inside
VirtualHDcreate aWINSETUPdirectory - extract Windows 3.1 floppies into it (7-Zip can extract IMG files)
- next to
DOSBox-x.execreate adosbox-x.confwith the following content:
[cpu]
cycles=25000
integration device=true
[sblaster]
irq=5
[autoexec]
@ECHO OFF
PATH=%PATH%;C:\WINDOWS
MOUNT C .\VirtualHD
C:- start DOSBox-X
- launch Windows installation
C:\WINSETUP\SETUP.EXE - install Windows using
Express Setup (Recommended)option - follow the installation instructions
- when asked, reboot, start Windows by typing
WIN
Mouse integration driver
- launch
Windows Setupprogram - open menu item
Options\Change System Settings - in
Mouse, pickOther mouse (Requires disk from OEM)... - type location to where you've extracted the driver and press
OKDOSBox-X Mouse Pointer Integrationis selected by default
- press
OK - press
OKagain - press
Restart Windows
S3 video driver
- launch
Windows Setupprogram - open menu item
Options\Change System Settings - in
Display, pickOther display (Requires disk from OEM)... - type location to where you've extracted the driver and press
OKS3 Trio64V 1.70.04 640x480 256is selected by default
- press
OK - press
OKagain - when asked for the driver location again, type
C:\WINDOWS\SYSTEM - press
OK - press
Restart Windows
Sound Blaster 16 sound driver
- installation must be done from command-prompt, exit Windows if not already
cdto where you've extract the driver and runINSTALL.EXE- choose
Full Installation (Recommended) - follow installation instructions
- when asked to overwrite some files, choose
Backup - restart DOSBox-X
- start Windows
- the installer finishes with setting up the group of audio software for Windows
Links
Here are some extra references to further tweak your installation:
Tip : backup your working installation before attempting to install other drivers.
PLANS Sound Blaster AWE32 Emulation AWE32P10 (`docs/PLANS/Sound Blaster AWE32 emulation/AWE32P10.TXT`)
=========================================================
|| The Un-official Sound Blaster AWE32 Programming Guide ||
|| ||
|| by Vince Vu a.k.a. Judge Dredd ||
|| ||
|| V.1.0 September 28, 1995 ||
=========================================================
Table of Contents
- Last minute notes
I. Introduction
II. Understanding the registers
III. Detection
IV. Initialization
V. Sample uploading/downloading
VI. Playing a sample
VII. The registers
VIII. Effects Engine data.
IX. Other documents you should refer to.
- Last minute notes
Yes, I know most of this is poorly written. (Thoughts not carefully
organized). But if you have any questions, of misunderstandings, or
difficulties, mail me.
I like getting mail. It helps feed my ego ;)
BTW Don't believe any of the b.s. Creative Labs says about the effects
engine on the EMU8000 not being programmable, because it is. Please
pester them to release info on the effects engine.
Greets:
Morbius My homie.
Thor Cool Hungarian dude that wrote AWEPlay
BLR BlazeRuner, long time no see.
Darron Myrick Here's the whole enchilada.
Mathias Hjelt Now, you can tweak your AWE32 to perfection.
Maybe...
And...
All the other guys, gals, neuters, etc... not mentioned.I. Introduction
Mail all questions, comments, etc... to [email protected]
Well, a month overdue, but its finally here. This guide will attempt to
describe in detail how to program the EMU8000 chip on the Sound Blaster AWE32,
directly. This past summer, I spent nearly a month, figuring out how to
program the EMU8000 on a low-level. And I succeeded. This is a compilation
of all that I figured out, and please, if you use this information in any
production, program, etc..., acknowledge me.
Why am I writing down and distributing my hard-earned knowledge? Well, after
reading an AWE32 in demos thread on comp.sys.ibm.pc.demos, I realized that the
best way to improve the AWE32's status would be to release a programming
guide.
This document will not cover Sound Blaster basics, only the EMU8000 sub-system
on the SBAWE32 card. If you need info on programming the OPL3, the DAC/ADC,
or the mixer then you need to refer to a SB16 programming guide.
Umm... please refer to the register list in section VII, when reading this
document, in case you have trouble with recognizing what register 0x5400 or
something else is.
The EMU8000 Specs:
32 Oscillators of 16-Bit, 44100Hz sound.
32 Megabytes of address space. The first 4 or reserved for ROM, and the last
28 are for RAM. Internally the EMU8000 only has 24-Bit address space. So
only 16-bit samples can be used.
3 Point sample interpolation.
1 Onboard Digital Effects Processor.
2 Low Frequency Oscillators' per oscillator.
2 DAHDSR Envelope Generators' per oscillator.
1 Resonant Filter per oscillator.
32 Independant reverb send levels for each oscillator.
32 Independant chorus send levels for each oscillator.
1 SP/DIF Digital Output.
Glossary:
EG1 Envelope Generator 1 (Filter/Pitch)
EG2 Envelope Generator 2 (Volume)
LFO1 Low Frequency Oscillator 1 (Pitch/Volume/Filter)
LFO2 Low Frequency Oscillator 2 (Pitch)
FilterQ Filter Resonance
Explaination of EG's:
Delay: The time before the attack, during which the envelope's
level stays at 0.
Attack: The time during which the envelope's level fades from
0 to peak.
Hold: The time before the decay, during which the envelope's
level stays at peak.
Decay: The time during which the envelope's level fades from peak
to the sustain level.
Sustain:The level at which the envelope stays before release.
Release:The time during which the envelope's level fades from sustain
to 0.
_
/ \ ______/ \
^ ^ ^^ ^ ^
| | || | |
| | || | - Release Time
| | || ---- Sustain Level
| | | ------- Decay Time
| | ------- Hold Time
| ----------- Attack Time
---------------- Delay Time
II. Understanding the registers
Here are the ports that are mapped to the EMU8000 (they are all word ports):
0x620 Data Read/Write
0x622 Data Read/Write
0xA20 Data Read/Write
0xA22 Data Read/Write
0xE20 Data Read/Write
0xE22 IndexSimply replace the middle digit of the port with the proper base. e.g. 0xE30
if the SBAWE32's base port is 0x230. The rest of this document will refer to
the ports as 0x620, 0xE20, etc... But remember to replace the middle digit.
In order to write or read from a register, one would first have to select the
proper index (if not already selected) by writing the index to the index port,
0xE22.
Example:
outpw(0xE22, 0);That's it. Now that you have selected the proper index you can now write/read
the data from the anyone of the registers that uses that index, by
reading/writing the correct port.
Example:
outpw(0xE20, 57344); //Port 0xE20, Index 0, is the pitch register
//for oscillator 0.Some registers, however, maybe 32-bits (double word), writing to them is
almost the same as writing to a 16-bit register as above.
Example:
outpw(0xA20, 0x0000); //Port 0xA20, Index 0 is the
//FilterQ/DramControl/PlayPosition register
outpw(0xA22, 0x0002); //for oscillator 0. What we just did is write
//the low word to
//0xA20, and then write the high word to
//0xA22.Reading from a register is exactly the same as writing, except you read (in)
from that port instead of write.
IMPORTANT!!! MUST READ!!!
For the rest of this document I will refer to registers the following way:
0x141d
^^^^
|| |
|| - The oscillator number in hex. Here it is 0x1d or 29 in
|| decimal.
||
| --- The port. 0=0x620, 2=0x622, 4=0xA20, 6=0xA22, 8=0xE20. Here it
| is 4, 0xA20.
|
---- The base index. Multiply by 32 and add the oscillator number to
get the real index, to write to 0xE22. Here it is 1, so the base
index is 32.III. Detection
There are several methods for detecting the presence of the EMU8000. But I
will only show one way. Here's a simple detection routine in Ansi C:
int awe32Detect(unsigned baseport) {
int scratch;
outpw(0xc02+baseport, 224); //Select index 224 by writing to 0xe22
inpw(0xc00+baseport); //Read from port 0xe20
scratch = inpw(0xc00+baseport); //Read from port 0xe20 again
if((scratch&0xf) != 0xc)
return 1; //Not detected
outpw(0xc02+baseport, 51); //Select index 51 (base index 32,
//oscillator 29)
scratch = inpw(0x800+baseport); //Read from port 0xa20
if((scratch&0x7e) != 0x58)
return 1; //Not detected
outpw(0xc02+baseport, 52); //Select index 52 (base index 32,
//oscillator 30)
scratch = inpw(0x800+baseport); //Read from port 0xa20
if((scratch&0x03) != 0x03)
return 1; //Not detected
return 0; //Woo-hoo! Detected!}
IV. Initialization
The most complex part of programming the EMU8000 is probably initialization.
There are several things you can initialize, but you do not have to. You
should initialize the effects engine. You should initialize OPL3/FM pass-
through, which also enables DRAM refresh. And you should detect the ammount
of DRAM. oh, and you should also stop all oscillators from playing, just in
case they are.
You should do those things in this order:
Stop the oscillators from playing, just in case.
Initialize the Effects Engine.
Initialize the FM Passthrough (You don't need to if you use only ROM sounds)
Detect the DRAM
Initializing the effects engine:
Registers 0x2400 through 0x361f are entirely devoted to effects engine data.
In order to initialize the effects engine, here's C code to intialize the
engine to Reverb&Chorus:
unsigned Reset_FX[128] = {...} //The real data is in section VIII. of
//this document.
unsigned Reverb&Chorus_FX[128] = {...} //The real data is in section VIII. of
//this document.
unsigned clock_wait;
outpw(0xE22, 32+20); //Select base index 32+oscillator 20
outpw(0xA20, 0);
outpw(0xE22, 32+21); //Select base index 32+oscillator 21
outpw(0xA20, 0);
outpw(0xE22, 32+22); //Select base index 32+oscillator 22
outpw(0xA20, 0);
outpw(0xE22, 32+23); //Select base index 32+oscillator 23
outpw(0xA20, 0);
outpw(0xE22, 32+21); //Select base index 32+oscillator 21
outpw(0xA20, 0);
for(i=0; i<32; i++) {
outpw(0xE22, 96+i); //Select base index 96+oscillator
//number
outpw(0xA22, Reset_FX[i]);
}
for(i=0; i<32; i++) {
outpw(0xE22, 96+i); //Select base index 96+oscillator
//number
outpw(0xA20, Reset_FX[32+i]);
}
for(i=0; i<32; i++) {
outpw(0xE22, 64+(i/32)); //Select base index 64+oscillator
//number
outpw(0xA22, Reset_FX[64+i]);
}
for(i=0; i<32; i++) {
outpw(0xE22, 64+(i/32)); //Select base index 64+oscillator
//number
outpw(0xA20, Reset_FX[96+i]);
}
outpw(0xE22, 32+27); //Select base index 32+oscillator 27
clock = inpw(0xA22); //This is the 44100Hz clock counter.
clock += 0x400; //Add 1024 to the counter.
if((clock-0x400)<clock) //Do this just in case clock
//overflowed
while(inpw(0xA22)>clock); //i.e. clock went past 65535
while(inpw(0xA22)<clock); //Wait 1024*1/44100 seconds
for(i=0; i<20; i++) {
outpw(0xE22, 32+i); //Select base index 32+oscillator
//number
outpw(0xA20, 0); //These are 32-bit registers
outpw(0xA22, 0);
}
for(i=0; i<32; i++) {
outpw(0xE22, 96+i); //Select base index 96+oscillator
//number
outpw(0xA22, Reset_FX[i]);
}
for(i=0; i<32; i++) {
outpw(0xE22, 96+i); //Select base index 96+oscillator
//number
if(i&1)
outpw(0xA20, 0x8000|Reset_FX[i]);
else
outpw(0xA20, Reset_FX[i]
}
for(i=0; i<32; i++) {
outpw(0xE22, 96+i); //Select base index 96+oscillator
//number
if(i&1)
outpw(0xA20, 0x8000|Reset_FX[32+i]);
else
outpw(0xA20, Reset_FX[32+i]
}
for(i=0; i<32; i++) {
outpw(0xE22, 64+(i/32)); //Select base index 64+oscillator
//number
if(i&1)
outpw(0xA20, 0x8000|Reset_FX[64+i]);
else
outpw(0xA20, Reset_FX[64+i]
}
for(i=0; i<32; i++) {
outpw(0xE22, 64+(i/32)); //Select base index 64+oscillator
//number
if(i&1)
outpw(0xA20, 0x8000|Reset_FX[96+i]);
else
outpw(0xA20, Reset_FX[96+i]
}
/* Upload the Reverb&Chorus Algorithm */
for(i=0; i<32; i++) {
outpw(0xE22, 96+i); //Select base index 96+oscillator
//number
if(i&1)
outpw(0xA20, 0x8000|Reverb&Chorus_FX[i]);
else
outpw(0xA20, Reverb&Chorus_FX[i]
}
for(i=0; i<32; i++) {
outpw(0xE22, 96+i); //Select base index 96+oscillator
//number
if(i&1)
outpw(0xA20, 0x8000|Reverb&Chorus_FX[32+i]);
else
outpw(0xA20, Reverb&Chorus_FX[32+i]
}
for(i=0; i<32; i++) {
outpw(0xE22, 64+(i/32)); //Select base index 64+oscillator
//number
if(i&1)
outpw(0xA20, 0x8000|Reverb&Chorus_FX[64+i]);
else
outpw(0xA20, Reverb&Chorus_FX[64+i]
}
for(i=0; i<32; i++) {
outpw(0xE22, 64+(i/32)); //Select base index 64+oscillator
//number
if(i&1)
outpw(0xA20, 0x8000|Reverb&Chorus_FX[96+i]);
else
outpw(0xA20, Reverb&Chorus_FX[96+i]
}
outpw(0xE22, 32+9); //Select base index 32+oscillator 9
outpw(0xA20, 0); //This register holds an unknown chorus
//parameter
outpw(0xA22, 0); //for the Reverb&Chorus Algorithm
outpw(0xE22, 32+10); //Select base index 32+oscillator 10
outpw(0xA20, 0x83); //This register holds the chorus
outpw(0xA22, 0x00); //parameter for chorus modulation
//frequency
outpw(0xE22, 32+13); //Select base index 32+oscillator 13
outpw(0xA20, 0); //This register holds an unknown chorus
outpw(0xA22, 80); //parameter for the Reverb&Chorus
//Algorithm
outpw(0xE22, 32+14); //Select base index 32+oscillator 13
outpw(0xA20, 0); //This register holds an unknown chorus
outpw(0xA22, 0); //parameter for the Reverb&Chorus
//Algorithm
for(i=0; i<32; i++) {
outpw(0xE22, 96+i); //Select base index 96+oscillator
//number
outpw(0xA20, Reverb&Chorus_FX[i]
}
for(i=0; i<32; i++) {
outpw(0xE22, 96+i); //Select base index 96+oscillator
//number
outpw(0xA20, Reverb&Chorus_FX[32+i]
}
for(i=0; i<32; i++) {
outpw(0xE22, 64+(i/32)); //Select base index 64+oscillator
//number
outpw(0xA20, Reverb&Chorus_FX[64+i]
}
for(i=0; i<32; i++) {
outpw(0xE22, 64+(i/32)); //Select base index 64+oscillator
//number
outpw(0xA20, Reverb&Chorus_FX[96+i]
}
return;
Wowie, that's it! Obviously, this code is not optimized. Some of the
outpw's to select the index, are redundant. But speed is not crucial, because
this is an initialization routine.
Initializing FM Passthrough:
This routine is uses a timing loop to synchronize the oscillator with the
refreshing of the DRAM. Which also enables OPL3 passthrough.
Here's a list of what to do:
Write 0x0080 to 0x541E (16-bit), the EG2 volume ramp register for
oscillator 30. This disables EG2, from affecting volume.
Write 0xFFFFFFE0 to 0x601E (32-bit), the loopstart/pan register.
This sets the pan to 0xFF (full left), and the loopstart to
0xFFFFE0 for oscillator 30. Of course you can set the pan to whatever
you like.
Write 0x00FFFFE8 to 0x701E (32-bit), the loopend/chorus send register.
This sets the chorus send to 0x00 (no send), and the loopend to
0xFFFFE8 for oscillator 30. Of course, you can set the chorus send to
whatever you like.
Write 0x00000000 to 0x101E (32-bit), the ??/reverb send/?? register.
This sets the reverb send to 0x00 (no send), the oscillator frequency
to 0x0000 Hz, and other things I don't know. Yes, you can change the
reverb send to your liking.
Write 0x00000000 to 0x001E (32-Bit, I have no idea what this does, but
it is for oscillator 30 (duh.).
Write 0x00FFFFE3 to 0x041E (32-Bit, the FilterQ/DramControl/PlayPosition
This sets the FilterQ to 0, DramControl to normal, and PlayPosition to
0xFFFFE3. Yes you can set the FilterQ to your liking.
Write 0x0080 to 0x541F (16-bit), the EG2 volume ramp register for
oscillator 31. This disables EG2, from affecting volume.
Write 0x00FFFFF0 to 0x601F (32-bit), the loopstart/pan register.
This sets the pan to 0xFF (full right), and the loopstart to
0xFFFFF0 for oscillator 31. Of course you can set the pan to whatever
you like.
Write 0x00FFFFF8 to 0x701F (32-bit), the loopend/chorus send register.
This sets the chorus send to 0x00 (no send), and the loopend to
0xFFFFF8 for oscillator 31. Of course, you can set the chorus send to
whatever you like.
Write 0x000000FF to 0x101F (32-bit), the ??/reverb send/?? register.
This sets the reverb send to 0x00 (no send), the oscillator frequency
to 0x0000 Hz, and other things I don't know. Yes, you can change the
reverb send to your liking.
Write 0x00000000 to 0x001F (32-Bit, I have no idea what this does, but
it is for oscillator 31 (duh.).
Write 0x00FFFFF3 to 0x041F (32-Bit, the FilterQ/DramControl/PlayPosition
This sets the FilterQ to 0, DramControl to normal, and PlayPosition to
0xFFFFF3. Yes you can set the FilterQ to your liking.
Okay, this is the crucial timing loop, ill have to show it in C:outpw(0xE22, 32+30); //Select index
outpw(0x620, 0); //Register 0x101E
//Recall that 0x101E is 32-Bit
//But do not write to the high word
while(!(inpw(0xE22)&0x1000)); //Wait for that bit to set
while(inpw(0xE22)&0x1000); //Wait for that bit to clear
outpw(0x622, 0x4828); //Okay, now write the high word
//This sets the frequency for
//oscillator 30 to ???
outpw(0xE22, 32+28); //Select index
outpw(0xA20, 0); //Register 0x141C
outpw(0xE22, 96+30); //Select index 96, oscillator 30
outpw(0x620, 0xFFFF); //Register 0x301E (32-Bit)
outpw(0x622, 0xFFFF); //This sets the overall volume (after
//EG2
//and LFO1) to 0xFFFF, and the overall
//filter to 0xFFFF(after LFO1 and EG1)
outpw(0xE22, 96+31); //Same as above except for oscillator
outpw(0x620, 0xFFFF); //31
outpw(0x622, 0xFFFF);Detecting DRAM:
Basically, to detect the ammount of DRAM, you write a sample to an offset in
the memory, and then read from that offset to check if it was actually
written, and make sure the actual write offset did not wrap around.
Here's a detection routine in C:
unsigned long dramsize;
unsigned i;/* Setup 15 Oscillators for DRAM writing, and 15 for read */
for(i=0; i<30; i++) {
outpw(0xE22, 160+i); //Select Index (i won't comment these
//anymore)
outpw(0xA20, 0x80); //Disable EG2, register 0x5400|i
outpw(0xE22, 96+i);
outpw(0x620, 0x0000); //Register 0x3000+i
outpw(0x622, 0x0000); //Set RawFilter/Raw Volume to 0
outpw(0xE22, 64+i);
outpw(0x620, 0x0000); //Register 0x2000+i
outpw(0x622, 0x0000); //I don't know what this does
outpw(0xE22, 192+i);
outpw(0x620, 0x0000); //Register 0x6000+i
outpw(0x622, 0x0000); //Set pan, and loopstart to 0
outpw(0xE22, 224+i);
outpw(0x620, 0x0000); //Register 0x7000+i
outpw(0x622, 0x0000); //Set chorus send, and loopend to 0
outpw(0xE22, 32+i);
outpw(0x620, 0x0000); //Register 0x1000+i
outpw(0x622, 0x4000); //Set frequency to ???
outpw(0xE22, 0+i);
outpw(0x620, 0x0000); //Register 0x0000+i
outpw(0x622, 0x4000); //I don't know what this does
outpw(0xE22, 0+i);
outpw(0xA20, 0x0000); //Register 0x0400+i
if(i&1)
outpw(0xA22, 0x0600); //Write mode
else
outpw(0xA22, 0x0400); //Read mode
}
outpw(0xE22, 32+22); //Register 0x1416
outpw(0xA20, 0x0000); //Set write offset to 0x200000
outpw(0xA22, 0x0020); //(the beginning of DRAM)
outpw(0xE22, 32+26); //Index for register 0x141A
//this register is the read/write
//data register.
outpw(0xA20, 0xFFFF); //Write 0xFFFF to offset 0x200000
//0x1416 (write offset register)
//is automatically incremented by
//one every write.
outpw(0xA20, 0xAAAA); //Write 0xAAAA to offset 0x200001
outpw(0xA20, 0x5555); //Write 0x5555 to offset 0x200002
outpw(0xE22, 32+20); //Register 0x1414
outpw(0xA20, 0x0000); //Set read offset to 0x200000
outpw(0xA22, 0x0020);
outpw(0xE22, 32+26); //Set index for register 0x141A
inpw(0xA20); //Ignore the first read.
if(inpw(0xA20) != 0xFFFF)
return 0; //No DRAM Detected
if(inpw(0xA20) != 0xAAAA)
return 0; //No DRAM Detected
if(inpw(0xA20) != 0x5555)
return 0; //No DRAM Detected
dramsize = 0;while(dramsize<0xdf8000) {
dramsize += 0x20000; //Everything checks out
//so add 0x20000 to dramsize
outpw(0xE22, 32+22); //Set the write offset
outpw(0xA20, (dramsize+0x200000)&0xFFFF);
outpw(0xA22, (dramsize+0x200000)>>16);
outpw(0xE22, 32+26); //Select register 0x141A
outpw(0xA20, 0x1234); //Write 0x1234 to memory
outpw(0xA20, 0x1234); //Write it to the next offset.
outpw(0xA20, 0x1234); //Write it to the next offset.
outpw(0xE22, 32+20); //Register 0x1414
outpw(0xA20, 0x0000); //Set the read offset to begginning
outpw(0xA20, 0x0020); //of DRAM
/* Check for wrap around */
outpw(0xE22, 32+26); //Set index for register 0x141A
inpw(0xA20); //Ignore the first read.
if(inpw(0xA20) != 0xFFFF)
return dramsize; //Write wrapped around
/* Check to make sure it was written */
outpw(0xE22, 32+20); //Register 0x1414 (read offset)
outpw(0xA20, (dramsize+0x200000)&0xFFFF);
outpw(0xA22, (dramsize+0x200000)>>16);
outpw(0xE22, 32+26); //Set index for register 0x141A
inpw(0xA20); //Ignore the first read.
if(inpw(0xA20) != 0x1234)
return dramsize; //Did not write!
if(inpw(0xA20) != 0x1234)
return dramsize; //Did not write!
if(inpw(0xA20) != 0x1234)
return dramsize; //Did not write!}
/* Reset the oscillators */
for(i=0; i<30; i++) {
outpw(0xE22, 0+i);
outpw(0xA20, 0x0000);
outpw(0xA22, 0x0000);
outpw(0xE22, 160+i);
outpw(0xA20, 0x807F);
}
return dramsize;The dramsize is returned in words, so you need to multiply by 2 to get the
number of bytes.
V. Sample Uploading/Downloading
When I say uploading, I mean transferring samples from your computer to
the EMU8000's memory. And downloading is transferring samples from the
EMU8000's memory (ROM included) to your computer.
All transfers are done through I/O ports, and not DMA.
This is the simplest part of programming the EMU8000. Here's C code
to setup the oscillators for transfering:
/* i is the oscillator number (0-31) */
outpw(0xE22, 160+i); //Select Index (i won't comment these
//anymore)
outpw(0xA20, 0x80); //Disable EG2, register 0x5400|i
outpw(0xE22, 96+i);
outpw(0x620, 0x0000); //Register 0x3000+i
outpw(0x622, 0x0000); //Set RawFilter/Raw Volume to 0
outpw(0xE22, 64+i);
outpw(0x620, 0x0000); //Register 0x2000+i
outpw(0x622, 0x0000); //I don't know what this does
outpw(0xE22, 192+i);
outpw(0x620, 0x0000); //Register 0x6000+i
outpw(0x622, 0x0000); //Set pan, and loopstart to 0
outpw(0xE22, 224+i);
outpw(0x620, 0x0000); //Register 0x7000+i
outpw(0x622, 0x0000); //Set chorus send, and loopend to 0
outpw(0xE22, 32+i);
outpw(0x620, 0x0000); //Register 0x1000+i
outpw(0x622, 0x4000); //Set frequency to ???
outpw(0xE22, 0+i);
outpw(0x620, 0x0000); //Register 0x0000+i
outpw(0x622, 0x4000); //I don't know what this does
outpw(0xE22, 0+i);
outpw(0xA20, 0x0000); //Register 0x0400+i
outpw(0xA22, 0x0600); //Write mode
//replace 0x0600 with 0x0400You can setup as many oscillators as you want for transferring. You can setup
as little as 1 and as many as 32. But if you setup 32, remember to
reinitialize fm passthrough. The more oscillators you use the faster the
transfers. You do not have to setup a block of oscillators for transfers.
You can setup say, for example oscillators: 0, 3, 7, and 12. That is
perfectly fine, and you can transfer samples, while still playing. Just as
long as the oscillator you setup for transfer is not in use (playing, or
transferring).
Here's pseudo-c code to upload (assuming the oscillators are setup already):
outpw(0xE22, 32+22);
outpw(0xA20, write_offset&0xFFFF);
outpw(0xA22, write_offset>>16);
outpw(0xE22, 32+26);
for(i=0; i<no_samples; i++)
outpw(0xA20, sample[i]); //Samples should be 16-Bit
//Intel, Signed, PCMThat's it. After you are done with the transfers, reset the oscillators
that you used. Here's how to reset the oscillator:
outpw(0xE22, 0+i);
outpw(0xA20, 0x0000);
outpw(0xA22, 0x0000);
outpw(0xE22, 160+i);
outpw(0xA20, 0x807F);i is the oscillator number.
Uploading is exactly the same as downloading:
outpw(0xE22, 32+22);
outpw(0xA20, write_offset&0xFFFF);
outpw(0xA22, write_offset>>16);
outpw(0xE22, 32+26);
inpw(0xA20); //Discard the first read
//I don't know why, but do it.
for(i=0; i<no_samples; i++)
sample[i] = inppw(0xA20);Wow! That was simple huh?
VI. Playing a sample
You should keep in mind that the EMU8000 does not have a sample length
register. So it is always constantly playing. So how do you keep it from
playing beyond the end of a sample, if the sample is not looped?
You setup a silence loop at the end of the sample. So for example, you have
a sample that is 26000 samples long, and it is not looped. What you would do
is add 4 or more samples of silence (0) to the end of that sample when
uploading, and set the loopstart, and loopend when playing that sample to
the silence padding at the end of the sample. Understand?
The EMU8000 does not keep track of instruments, and samples. That is up to
the software. The EMU8000 only knows playposition, loopstart, and loopend.
and that is for each oscillator.
Let's say you wanted to play a sample that was located at offset 0x1000,
which is in the ROM. And you want to use envelopes and lfo's and you want
it looped. How would you do that?
First you need to allocate an oscillator for playback. How do you do that?
You can either keep track of the oscillators yourself, or you can go through
all 30/32 (depending if you use ram or not), and check for the one with the
lowest overall volume. The overall volume or overall volume is located in
register 0x3000+oscillator_no. 0x3000 is a 32bit register, and the overall
volume is located at the high word. You need to read all 32bits, and then
store only the high word. After you have found the oscillator, remember its
number (so that when you want to turn off the sample, you know which
oscillator its on).
Once you have an oscillator, you can now setup it's parameters. You do not
need to rewrite any of the parameters for the oscillator unless you know it
has changed or if the parameter is playposition.
The first thing you do with that oscillator is disable the volume envelope,
and turn off the volume. You can disable the volume envelope by writing 0x80
to the 16-bit register 0x5400. And then you can write 0x0000FFFF to the 32bit
register 0x3000, which sets the overall volume to 0, and the overall filter to
0xFFFF. Of course you can set any other filter but, it doesn't matter,
because, the filter envelope hasn't been disabled (EG1).
Now, you can set the parameters to your liking without getting clicks, or
twirps, or other strange noises. You should save play-position for last.
Because, remember, the EMU8000 is always constantly playing.
Once you have set all the parameters you can, now enable EG2 (volume eg), by
writing the appropriate value to 0x5400. (for a full explaination of
0x5400, refer to section VII.
Okay, how do you release or turn off the sample?
You write the appropriate values to 0x5400 and 0x7400. (refer to section VII)
VII. The registers
NOTE!!!
The scales in this list, may or may not be 100% correct.
Format of this list:
Register Number U/O Base Index Base Port W/D
Name
Description
U/O = Unique/Applies to all oscillators. If it applies to all oscillators
you can access the register for any particular oscillator by adding
the oscillator number to the base index.
W/D = The size of the register. 16-bit or 32-bit. All 16-bits of a 16-bit
register must be read/write. And all 32-bits of a 32-bit register
must be read/write.
Unlisted registers are unknown.
Registers 0x2400-0x3600 contain effects engine data, and will not be listed.
0x0400 O 000 A20 D
FilterQ/DramControl/PlayPosition
The upper 4 bits of this register contain the filter resonance coefficient.
Refer to the NRPN LSB 22 in the awe32faq for the coefficient table.
The middle 4 bits of this register contain the control nibble of the
oscillator. 0=Normal, 4=Read, 6=Write. All other values are unknown.
The lower 24 bits of this register contain the play position for the
oscillator. This is the currently playing offset for the oscillator. It
can be changed on the fly, and is updated continuously, by the EMU8000.
Notes!!!
Setting the PlayPosition greater than the Loop End Offset, will cause
the oscillator to play in reverse, back to the Loop End Offset. It's
pretty neat, but appears to be uncontrollable (the rate at which the
samples are played in reverse).
0x0800 O 000 E20 W
Initial Pitch
The scale for the pitch register is logarithmic. 57344 is Middle C, or
44100Hz. Every 4096 is one octave, or double the frequency of the previous
4096. Example: 57344-4096 = 22050Hz. 4096/12 is one semitone. 57344+341=
C# or Db (D-Flat). This register is the initial pitch of the oscillator.
0x1000 O 032 620 D
Unknown/ReverbSend/Unknown
16Bit/8Bit/8bit
The purpose of the upper 16bits of this register is unknown. The upper byte
of the lower word contains the reverb send level. The range for the
reverb send level is 0-255 0 is no send, and 255 is full send. The lower
byte is also unknown.
0x1409 U 041 A20 D
Effects Engine Parameter
Under the Reverb&Chorus Algorithm this register holds an unknown parameter
for the chorus effect.
0x140A U 042 A20 D
Effects Engine Parameter
Under the Reverb&Chorus Algorithm this register holds the modulation
frequency parameter for the chorus effect.
0x140D U 045 A20 D
Effects Engine Parameter
Under the Reverb&Chorus Algorithm this register holds an unknown parameter
for the chorus effect.
0x140E U 046 A20 D
Effects Engine Parameter
Under the Reverb&Chorus Algorithm this register holds an unknown parameter
for the chorus effect.
0x1414 U 052 A20 D
Memory Read Offset
This register holds the offset of the data that will be read from the memory
by reading register 0x141A. This register is automatically incremented each
read.
0x1416 U 054 A20 D
Memory Write Offset
This register holds the offset of the data that will be write from the memory
by writing register 0x141A. This register is automatically incremented each
write.
0x141A U 058 A20 W
Memory Read/Write Data
This register writes to memory at the offset found in 0x1416, each time this
register is written to. This register reads from memory at the offset found
in 0x1414, each time this register is read from.
0x161B U 059 A22 W
44100Hz Counter
This register is incremented every 1/44100th of a second. When it reaches
65536, it rolls back to 0.
0x1800 O 000 E20 W
Initial Filter/Initial Volume
The upper byte of this register contains the initial filter cutoff for that
register. Zero sets the cutoff at 100Hz, and 255 sets the cutoff at
8068.75Hz. The formula for the initial filter cutoff is as follows:
InitialFilterCutoff = RegisterValue*31.25Hz+100Hz
The lower byte ofthis registers holds the initial volume. The EMU8000
uses a logarithmic volume scale, with 96dB as the max. volume, and -oo
(infinity), the minimum. A register value of 0 sets the initial volume
of that oscillator to 96dB above silence (-oo dB). A value of 255 sets
the initial volume to -oo dB.
dB to register value:
InitialVolume (dB above silence) = 96dB - RegisterValue/16*6dB
Linear scale to register vale (if LinearVolume = 0, then register
value = 255):
RegisterValue = 20*log(LinearVolume/MaxLinearVolume)*16/6
How to understand the decibel system (dB): 12dB is twice as loud as
6dB. 18dB is twice as loud as 12dB, and four times as loud as 6dB.
0x2800 O 064 E20 W
EG1 to Pitch/EG1 to Filter Cutoff
The upper byte of this register, contains the ammount the pitch of the
oscillator will be changed by, at the peak of EG1. This byte is a
signed byte. The pitch of the oscillator will be doubled at peak, if
this byte is set to 127 (0x7F), if the byte is set to -127 (0x80), then
the pitch will be halved at peak. Each unit changes the pitch by
9.375 cents (divide by 100 to get the number of semitones).
The lower byte of this register, contains the ammount the filter cutoff
of the oscillator will be changed by, at the peak of EG1. This byte is
a signed byte. The filter cutoff of the oscillator will be multiplied
by 8 at peak, if this byte is set to 127 (0x7F), if the byte is set to -127
(0x80), then the filter cutoff will be divided by 8 at peak. Each unit
changes the filter cutoff by 28.125 cents.
0x3000 O 096 620 D
Overall Volume/Overall Filter Cutoff
The upper word of this register contains the overall volume of the
oscillator. The overall volume, is the total volume of the oscillator,
after initial volume, EG2, and possibly LFO1 (i'm not sure).
The lower word of this reigster contains the overall filter cutoff of
the oscillator. The overall volume is the total filter cutoff of the
oscillator after initial filter cutoff, EG1, and possibly LFO1 (i'm not
sure).
0x3800 O 096 E20 W
LFO1 to Pitch/LFO1 to Filter Cutoff
This register is exactly the same as register 0x2800, except it controls
the LFO1 instead of EG1.
0x4400 O 128 A20 W
EG2 Delay Time
This register controls the time EG2 will wait before beginning the
attack.
RegisterValue = 32768-(DelayTime_ms*1000/725)
The range for DelayTime is [0ms -> 23756.8ms]
0x4600 O 128 A22 W
EG2 Hold Time/EG2 Attack Time
The upper byte of the register contains the time at which EG2 will
stay at peak before beginning the decay.
RegisterValue = 127-(HoldTime_ms/92)
The range for HoldTime is [0ms -> 11684ms]
The lower byte of the register contains the attack, the time during
which EG2 fades from 0 to peak.
if AttackTime_ms >= 360ms:
RegisterValue = 11878/AttackTime_ms - 1
if AttackTime_ms < 360ms and AttackTime != 0:
RegisterValue = 32 + [16/log(1/2)] * log(360_ms/AttackTime_ms)
if AttackTime_ms == 0
RegisterValue = 0x7FThe range for attack time is [0ms -> 11878ms]
0x4800 O 128 E20 W
LFO1 to Volume/LFO1 Frequency
The upper byte contains the the ammount the volume of the oscillator
will be changed by at the peak of LFO1. This byte is signed.
RegisterValue = Volume_dB * 12/128
The range for Volume_dB is [-12dB -> 12dB]
The lower byte contains the frequency at which LFO1 oscillates.
RegisterValue = Frequency_Hz * 21.44/256
0x5400 O 160 A20 W
EG2 Override/EG2 Sustain/EG2 Decay, Release or EG2 Ramper
If the MSB (most significant bit or bit 15) of this register is set,
the Decay/Release will begin immediately, overriding the Delay, Attack,
and Hold. Otherwise the Decay/Release will wait until the Delay, Attack,
and Hold are finished. If you set the MSB of this register, you can use
it as a volume ramper, as on the GUS. The upper byte (except the MSB),
contains the destination volume, and the lower byte contains the ramp time.
Sustain Portion:
RegisterValue = SustainLevel_dB*4/3
Decay/Release Portion:
RegisterValue = 2*log(1/2)*log(23756_ms/DecayTime_ms)
range: [0ms, 23756ms], obviously at 0ms, the register value is 127.
Notes!!!
Setting the sustain of this register above zero, turns on the volume.
Basically, setting this register with proper decay, and sustain time
(any value other than zero), will start the audible playing of the
sample. (remember: the emu is always constantly playing).
Setting the sustain to zero, and the decay to appropriate time, will
start the release of the sample. Thus turning off the sample,
audibly.
What I did in my modplayer (AWEMod), is each time I turned the note on,
I set the upper byte to 0xFF, which sets the upper byte, and sets the
sustain level to 96dB, and set the decay time to 0x7F (0 ms). This
gave me the sharpest attacks, because EG2, skips the delay, attack,
hold, and goes straight to sustain.
0x5600 O 160 A22 W
LFO1 Delay Time
This is exactly the same as EG1 Delay Time, register 0x4400.
0x5800 O 160 E20 W
LFO2 to Pitch/LFO2 Frequency
The format of the upper byte (LFO2 to Pitch) is exactly the same as for
EG1 (register 0x2800), and the format of the lower byte is exactly the
same as for register 0x4800 (LFO1 Frequency).
0x6000 O 192 620 D
Pan Level/Loop Start Offset
The highest byte contains the pan level. Zero is full right, 128 is
dead center, and 255 is full left. Simple.
The last 3 bytes are the Loop Start Offset.
0x6400 O 192 A20 W
EG1 Delay Time
Same as EG2 Delay Time, register 0x4400.
0x6600 O 192 A22 W
EG1 Hold Time/EG1 Attack Time
Same as EG2 Hold/EG2 Attack, register 0x4600.
0x7000 O 224 620 D
Chorus Send/Loop End Offset
The highest byte contains the Chorus Send level. Zero is none, and
255 is full. The last 3 bytes contain the Loop End Offset.
Notes!!!
Setting the Loop Start Offset and the Loop End Offset to the same
value, will cause the oscillator to loop the entire memory.
0x7400 O 224 A20 W
EG1 Override/EG1 Sustain/EG1 Decay, Release or EG1 Ramper
This is exactly the same as register 0x5400. Except it controls the
filter cutoff, and pitch of the oscillator.
0x7600 O 224 A22 W
LFO2 Delay
Same as LFO1 Delay, register 0x5600.
VIII. Effects Engine Data
Data to reset/clear the engine, not quite sure what it does (128 Words):
0x3FF,0x30,0x7FF,0x130,0xBFF,0x230,0xFFF,0x330,0x13FF,0x430,0x17FF,0x530,
0x1BFF,0x630,0x1FFF,0x730,0x23FF,0x830,0x27FF,0x930,0x2BFF,0xA30,0x2FFF,0xB30,
0x33FF,0xC30,0x37FF,0xD30,0x3BFF,0xE30,0x3FFF,0xF30,0x43FF,0x030,0x47FF,0x130,
0x4BFF,0x230,0x4FFF,0x330,0x53FF,0x430,0x57FF,0x530,0x5BFF,0x630,0x5FFF,0x730,
0x63FF,0x830,0x67FF,0x930,0x6BFF,0xA30,0x6FFF,0xB30,0x73FF,0xC30,0x77FF,0xD30,
0x7BFF,0xE30,0x7FFF,0xF30,0x83FF,0x030,0x87FF,0x130,0x8BFF,0x230,0x8FFF,0x330,
0x93FF,0x430,0x97FF,0x530,0x9BFF,0x630,0x9FFF,0x730,0xA3FF,0x830,0xA7FF,0x930,
0xABFF,0xA30,0xAFFF,0xB30,0xB3FF,0xC30,0xB7FF,0xD30,0xBBFF,0xE30,0xBFFF,0xF30,
0xC3FF,0x030,0xC7FF,0x130,0xCBFF,0x230,0xCFFF,0x330,0xD3FF,0x430,0xD7FF,0x530,
0xDBFF,0x630,0xDFFF,0x730,0xE3FF,0x830,0xE7FF,0x930,0xEBFF,0xA30,0xEFFF,0xB30,
0xF3FF,0xC30,0xF7FF,0xD30,0xFBFF,0xE30,0xFFFF,0xF30
Reverb&Chorus Algorithm Data [Hall 2, Chorus 3] (128 Words):
0x0C10,0x8470,0x14FE,0xB488,0x167F,0xA470,0x18E7,0x84B5,0x1B6E,0x842A,0x1F1D,
0x852A,0x0DA3,0x0F7C,0x167E,0x7254,0x0000,0x842A,0x0001,0x852A,0x18E6,0x0BAA,
0x1B6D,0x7234,0x229F,0x8429,0x2746,0x8529,0x1F1C,0x6E7,0x229E,0x7224,0x0DA4,
0x8429,0x2C29,0x8529,0x2745,0x7F6,0x2C28,0x7254,0x383B,0x8428,0x320F,0x8528,
0x320E,0xF02,0x1341,0x7264,0x3EB6,0x8428,0x3EB9,0x8528,0x383A,0xFA9,0x3EB5,
0x7294,0x3EB7,0x8474,0x3EBA,0x8575,0x3EB8,0x44C3,0x3EBB,0x45C3,0x0000,0xA404,
0x0001,0xA504,0x141F,0x671,0x14FD,0x287,0x3EBC,0xE610,0x3EC8,0xC7B,0x031A,
0x07E6,0x3EC8,0x86F7,0x3EC0,0x821E,0x3EBE,0xD280,0x3EBD,0x21F,0x3ECA,0x0386,
0x3EC1,0xC03,0x3EC9,0x31E,0x3ECA,0x8C4C,0x3EBF,0xC55,0x3EC9,0xC280,0x3EC4,
0xBC84,0x3EC8,0xEAD,0x3EC8,0xD380,0x3EC2,0x8F7E,0x3ECB,0x219,0x3ECB,0xD2E6,
0x3EC5,0x31F,0x3EC6,0xC380,0x3EC3,0x327F,0x3EC9,0x265,0x3EC9,0x8319,0x1342,
0xD3E6,0x3EC7,0x337F,0x0000,0x8365,0x1420,0x9570
Modifications to effects engine data registers to get other reverb&chorus
variations (also stuff about custom effects), and different EQ's:
Registers to write the Chorus Parameters to (all are 16-bit, unless noted):
0x3409
0x340C
0x3603
0x1409 (32-Bit)
0x140A (32-Bit)
then write 0x8000 to 0x140D (32-Bit)
and then 0x0000 to 0x140E (32-Bit)
Chorus Parameters:
Chorus 1 Chorus 2 Chorus 3 Chorus 4 Feedback Flanger
0xE600 0xE608 0xE610 0xE620 0xE680 0xE6E0
0x03F6 0x031A 0x031A 0x0269 0x04D3 0x044E
0xBC2C 0xBC6E 0xBC84 0xBC6E 0xBCA6 0xBC37
0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
0x006D 0x017C 0x0083 0x017C 0x005B 0x0026
Short Delay Short Delay + Feedback
0xE600 0xE6C0
0x0B06 0x0B06
0xBC00 0xBC00
0xE000 0xE000
0x0083 0x0083
Registers to write the Reverb Parameters to (they are all 16-bit):
0x2403,0x2405,0x361F,0x2407,0x2614,0x2616,0x240F,0x2417,
0x241F,0x2607,0x260F,0x2617,0x261D,0x261F,0x3401,0x3403,
0x2409,0x240B,0x2411,0x2413,0x2419,0x241B,0x2601,0x2603,
0x2609,0x260B,0x2611,0x2613
Reverb Parameters:
Room 1:
0xB488,0xA450,0x9550,0x84B5,0x383A,0x3EB5,0x72F4,0x72A4,
0x7254,0x7204,0x7204,0x7204,0x4416,0x4516,0xA490,0xA590,
0x842A,0x852A,0x842A,0x852A,0x8429,0x8529,0x8429,0x8529,
0x8428,0x8528,0x8428,0x8528
Room 2:
0xB488,0xA458,0x9558,0x84B5,0x383A,0x3EB5,0x7284,0x7254,
0x7224,0x7224,0x7254,0x7284,0x4448,0x4548,0xA440,0xA540,
0x842A,0x852A,0x842A,0x852A,0x8429,0x8529,0x8429,0x8529,
0x8428,0x8528,0x8428,0x8528
Room 3:
0xB488,0xA460,0x9560,0x84B5,0x383A,0x3EB5,0x7284,0x7254,
0x7224,0x7224,0x7254,0x7284,0x4416,0x4516,0xA490,0xA590,
0x842C,0x852C,0x842C,0x852C,0x842B,0x852B,0x842B,0x852B,
0x842A,0x852A,0x842A,0x852A
Hall 1:
0xB488,0xA470,0x9570,0x84B5,0x383A,0x3EB5,0x7284,0x7254,
0x7224,0x7224,0x7254,0x7284,0x4448,0x4548,0xA440,0xA540,
0x842B,0x852B,0x842B,0x852B,0x842A,0x852A,0x842A,0x852A,
0x8429,0x8529,0x8429,0x8529
Hall 2:
0xB488,0xA470,0x9570,0x84B5,0x383A,0x3EB5,0x7254,0x7234,
0x7224,0x7254,0x7264,0x7294,0x44C3,0x45C3,0xA404,0xA504,
0x842A,0x852A,0x842A,0x852A,0x8429,0x8529,0x8429,0x8529,
0x8428,0x8528,0x8428,0x8528
Plate:
0xB4FF,0xA470,0x9570,0x84B5,0x383A,0x3EB5,0x7234,0x7234,
0x7234,0x7234,0x7234,0x7234,0x4448,0x4548,0xA440,0xA540,
0x842A,0x852A,0x842A,0x852A,0x8429,0x8529,0x8429,0x8529,
0x8428,0x8528,0x8428,0x8528
Delay:
0xB4FF,0xA470,0x9500,0x84B5,0x333A,0x39B5,0x7204,0x7204,
0x7204,0x7204,0x7204,0x72F4,0x4400,0x4500,0xA4FF,0xA5FF,
0x8420,0x8520,0x8420,0x8520,0x8420,0x8520,0x8420,0x8520,
0x8420,0x8520,0x8420,0x8520
Panning Delay:
0xB4FF,0xA490,0x9590,0x8474,0x333A,0x39B5,0x7204,0x7204,
0x7204,0x7204,0x7204,0x72F4,0x4400,0x4500,0xA4FF,0xA5FF,
0x8420,0x8520,0x8420,0x8520,0x8420,0x8520,0x8420,0x8520,
0x8420,0x8520,0x8420,0x8520
Registers to write the EQ Parameters to (16-Bit):
Bass:
0x3601
0x3611
Treble:
0x3411
0x3413
0x341B
0x3607
0x360B
0x360D
0x3617
0x3619
Total:
write the 0x0263 + 3rd parameter of the Bass EQ + 9th parameter of Treble EQ to 0x3615.
write the 0x8363 + 3rd parameter of the Bass EQ + 9th parameter of Treble EQ to 0x3615.
Bass Parameters:
0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
0xD26A 0xD25B 0xD24C 0xD23D 0xD21F 0xC208 0xC219 0xC22A 0xC24C 0xC26E 0xC248 0xC26A
0xD36A 0xD35B 0xD34C 0xD33D 0xC31F 0xC308 0xC308 0xC32A 0xC34C 0xC36E 0xC384 0xC36A
0x0000 0x0000 0x0000 0x0000 0x0000 0x0001 0x0001 0x0001 0x0001 0x0001 0x0002 0x0002
Treble Parameters:
0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
0x821E 0x821E 0x821E 0x821E 0x821E 0x821E 0x821E 0x821E 0x821E 0x821E 0x821D 0x821C
0xC26A 0xC25B 0xC24C 0xC23D 0xC21F 0xD208 0xD208 0xD208 0xD208 0xD208 0xD219 0xD22A
0x031E 0x031E 0x031E 0x031E 0x031E 0x031E 0x031E 0x031E 0x031E 0x031E 0x031D 0x031C
0xC36A 0xC35B 0xC34C 0xC33D 0xC31F 0xD308 0xD308 0xD308 0xD308 0xD308 0xD319 0xD32A
0x021E 0x021E 0x021E 0x021E 0x021E 0x021E 0x021D 0x021C 0x021A 0x0219 0x0219 0x0219
0xD208 0xD208 0xD208 0xD208 0xD208 0xD208 0xD219 0xD22A 0xD24C 0xD26E 0xD26E 0xD26E
0x831E 0x831E 0x831E 0x831E 0x831E 0x831E 0x831D 0x831C 0x831A 0x8319 0x8319 0x8319
0xD308 0xD308 0xD308 0xD308 0xD308 0xD308 0xD3019 0xD32A 0xD34C 0xD36E 0xD36E 0xD36E
0x0001 0x0001 0x0001 0x0001 0x0001 0x0002 0x0002 0x0002 0x0002 0x0002 0x0002 0x0002
To see what the parameters do, use the program FXTweak, which I wrote and included with the
programming guide.
IX. Other documents you should refer to
ftp.cdrom.com:/pub/demos/music/programs/players/omega06.zip
This is the only modplayer for the AWE32 that comes with source code.
Although the source, isn't very helpful, but it's working code.
ftp.creaf.com:/pub/creative/files/awe/awe32faq.exe
Great resource. Refer to NRPN 22, in this faq, for a description of
the resonance coefficients.
Vs Freetype Docs FTL (`vs/freetype/docs/FTL.TXT`)
The FreeType Project LICENSE
----------------------------
2006-Jan-27
Copyright 1996-2002, 2006 by
David Turner, Robert Wilhelm, and Werner LembergIntroduction
The FreeType Project is distributed in several archive packages;
some of them may contain, in addition to the FreeType font engine,
various tools and contributions which rely on, or relate to, the
FreeType Project.
This license applies to all files found in such packages, and
which do not fall under their own explicit license. The license
affects thus the FreeType font engine, the test programs,
documentation and makefiles, at the very least.
This license was inspired by the BSD, Artistic, and IJG
(Independent JPEG Group) licenses, which all encourage inclusion
and use of free software in commercial and freeware products
alike. As a consequence, its main points are that:
o We don't promise that this software works. However, we will be
interested in any kind of bug reports. (`as is' distribution)
o You can use this software for whatever you want, in parts or
full form, without having to pay us. (`royalty-free' usage)
o You may not pretend that you wrote this software. If you use
it, or only parts of it, in a program, you must acknowledge
somewhere in your documentation that you have used the
FreeType code. (`credits') We specifically permit and encourage the inclusion of this
software, with or without modifications, in commercial products.
We disclaim all warranties covering The FreeType Project and
assume no liability related to The FreeType Project.
Finally, many people asked us for a preferred form for a
credit/disclaimer to use in compliance with this license. We thus
encourage you to use the following text:
"""
Portions of this software are copyright ©
Project (www.freetype.org). All rights reserved.
"""
Please replace
actually use.
Legal Terms
- Definitions
Throughout this license, the terms package', FreeType Project',
and FreeType archive' refer to the set of files originally distributed by the authors (David Turner, Robert Wilhelm, and Werner Lemberg) as the FreeType Project', be they named as alpha,
beta or final release.
You' refers to the licensee, or person using the project, where using' is a generic term including compiling the project's source
code as well as linking it to form a program' or executable'.
This program is referred to as `a program using the FreeType
engine'.
This license applies to all files distributed in the original
FreeType Project, including all source code, binaries and
documentation, unless otherwise stated in the file in its
original, unmodified form as distributed in the original archive.
If you are unsure whether or not a particular file is covered by
this license, you must contact us to verify this.
The FreeType Project is copyright (C) 1996-2000 by David Turner,
Robert Wilhelm, and Werner Lemberg. All rights reserved except as
specified below.
- No Warranty
THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
USE, OF THE FREETYPE PROJECT.
- Redistribution
This license grants a worldwide, royalty-free, perpetual and
irrevocable right and license to use, execute, perform, compile,
display, copy, create derivative works of, distribute and
sublicense the FreeType Project (in both source and object code
forms) and derivative works thereof for any purpose; and to
authorize others to exercise some or all of the rights granted
herein, subject to the following conditions:
o Redistribution of source code must retain this license file
(`FTL.TXT') unaltered; any additions, deletions or changes to
the original files must be clearly indicated in accompanying
documentation. The copyright notices of the unaltered,
original files must be preserved in all copies of source
files.
o Redistribution in binary form must provide a disclaimer that
states that the software is based in part of the work of the
FreeType Team, in the distribution documentation. We also
encourage you to put an URL to the FreeType web page in your
documentation, though this isn't mandatory. These conditions apply to any software derived from or based on
the FreeType Project, not just the unmodified files. If you use
our work, you must acknowledge us. However, no fee need be paid
to us.
- Advertising
Neither the FreeType authors and contributors nor you shall use
the name of the other for commercial, advertising, or promotional
purposes without specific prior written permission.
We suggest, but do not require, that you use one or more of the
following phrases to refer to this software in your documentation
or advertising materials: FreeType Project', FreeType Engine',
FreeType library', or FreeType Distribution'.
As you have not signed this license, you are not required to
accept it. However, as the FreeType Project is copyrighted
material, only this license, or another one contracted with the
authors, grants you the right to use, distribute, and modify it.
Therefore, by using, distributing, or modifying the FreeType
Project, you indicate that you understand and accept all the terms
of this license.
- Contacts
There are two mailing lists related to FreeType:
o [email protected]
Discusses general use and applications of FreeType, as well as
future and wanted additions to the library and distribution.
If you are looking for support, start in this list if you
haven't found anything to help you in the documentation.
o [email protected]
Discusses bugs, as well as engine internals, design issues,
specific licenses, porting, etc.Our home page can be found at
https://www.freetype.org--- end of FTL.TXT ---
Vs Freetype Docs GPLv2 (`vs/freetype/docs/GPLv2.TXT`)
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, 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 Library 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
- 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.
- 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.
- 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.
- 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.
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.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.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.
- 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.
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.textNO WARRANTYBECAUSE 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.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.textEND OF TERMS AND CONDITIONSHow 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 St, Fifth Floor, Boston, MA 02110-1301 USAAlso 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.
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 Library General
Public License instead of this License.
Vs Freetype Docs VERSIONS (`vs/freetype/docs/VERSIONS.TXT`)
Due to our use of `libtool' to generate and install the FreeType 2
libraries on Unix systems, as well as other historical events, it is
generally very difficult to know precisely which release of the font
engine is installed on a given system.
This file tries to explain why and to document ways to properly detect
FreeType on Unix.
- Version and Release numbers
For each new public release of FreeType 2, there are generally three
distinct `version' numbers to consider:
The official FreeType 2 release number, like 2.7.0 or 2.10.2.
The libtool (and Unix) specific version number, like 23.2.17.
This is whatpkg-config freetype2 --modversion
or
freetype-config --version
returns.
The platform-specific shared object number, used for example when
the library is installed as `/usr/lib/libfreetype.so.6.17.2'.
The platform-specific number is, unsurprisingly, platform-specific and
varies with the operating system you are using (several variants of
Linux, FreeBSD, Solaris, etc.). You should thus never use it, even
for simple tests.
The libtool-specific number does not equal the release number but is
tied to it.
The release number is available at compile time through the
following macros defined in `freetype.h':
- FREETYPE_MAJOR: major release number
- FREETYPE_MINOR: minor release number
- FREETYPE_PATCH: patch release number
See below for a small autoconf fragment.
The release number is also available at runtime through the
`FT_Library_Version' API.
- History
The following table gives, for all releases since 2.5.0, the
corresponding libtool number, as well as the shared object number
found on most systems, but not all of them:
release libtool so2.13.1 26.0.20 6.20.0
2.13.0 25.0.19 6.19.0
2.12.1 24.3.18 6.18.3
2.12.0 24.2.18 6.18.2
2.11.1 24.1.18 6.18.1
2.11.0 24.0.18 6.18.0
2.10.4 23.4.17 6.17.4
2.10.3 23.3.17 6.17.3
2.10.2 23.2.17 6.17.2
2.10.1 23.1.17 6.17.1
2.10.0 23.0.17 6.17.0
2.9.1 22.1.16 6.16.1
2.9.0 22.0.16 6.16.0
2.8.1 21.0.15 6.15.0
2.8.0 20.0.14 6.14.0
2.7.1 19.0.13 6.13.0
2.7.0 18.6.12 6.12.6
2.6.5 18.5.12 6.12.5
2.6.4 18.4.12 6.12.4
2.6.3 18.3.12 6.12.3
2.6.2 18.2.12 6.12.2
2.6.1 18.1.12 6.12.1
2.6.0 18.0.12 6.12.0
2.5.5 17.4.11 6.11.4
2.5.4 17.3.11 6.11.3
2.5.3 17.2.11 6.11.2
2.5.2 17.1.11 6.11.1
2.5.1 17.0.11 6.11.0
2.5.0 16.2.10 6.10.2- Autoconf Code Fragment
Lars Clausen contributed the following autoconf fragment to check
which version of FreeType is installed on a system (now updated to usepkg-config' instead of freetype-config'). This one tests for a
version that is at least 2.10.2; you should change it to check against
other release numbers.
AC_MSG_CHECKING([whether FreeType version is 2.10.2 or higher])
old_CPPFLAGS="$CPPFLAGS"
CPPFLAGS=pkg-config freetype2 --cflags
AC_TRY_CPP([
#include <ft2build.h>
#include <freetype/freetype.h>
#if FREETYPE_MAJOR10000 + FREETYPE_MINOR100 + FREETYPE_PATCH < 21002
error FreeType version too low.
#endif
],
[AC_MSG_RESULT(yes)
FREETYPE_LIBS=pkg-config freetype2 --libs
AC_SUBST(FREETYPE_LIBS)
AC_DEFINE(HAVE_FREETYPE,1,[Define if you have the FreeType2 library])
CPPFLAGS="$old_CPPFLAGS"],
[AC_MSG_ERROR([Need FreeType library version 2.10.2 or higher])])
Copyright (C) 2002-2023 by
David Turner, Robert Wilhelm, and Werner Lemberg.
This file is part of the FreeType project, and may only be used,
modified, and distributed under the terms of the FreeType project
license, LICENSE.TXT. By continuing to use, modify, or distribute
this file you indicate that you have read the license and understand
and accept it fully.
--- end of VERSIONS.TXT ---
Vs Freetype Docs Formats (`vs/freetype/docs/formats.txt`)
This file contains a list of various font formats. It gives the
reference document and whether it is supported in FreeType 2.
Table fields
wrapper format
The format used to represent the font data. In the table below it
is used only if the font format differs. Possible values are
SFNT binary
PFB binary
PS a text header, followed by binary or text data
LZW compressed with either `gzip' or `compress'
BZ2 compressed with `bzip2'. font format
How the font is to be accessed, possibly after converting the file
type and wrapper format into a generic form. Bitmap formats are
BDF', PCF', and one form of WINFNT'; all others are vector formats. PS' indicates third-order, `TT' second-order Bézier
curves.
font type
Sub-formats of the font format. SBIT' and MACSBIT' are bitmap
formats, MM' and VAR' support optical axes. `CFF2' supports
optical axes also.
glyph access
If not specified, the glyph access is standard' to the font format. Values are CID' for CID-keyed fonts, SYNTHETIC' for fonts that are modified versions of other fonts by means of a transformation matrix, and TYPE_0' for PS fonts which are to be
accessed in a tree-like structure.
FreeType driver
The module in the FreeType library which handles the specific font
format. A missing entry means that FreeType doesn't support the
font format (yet).
Notes
The SFNT container format also provides collections' (usually having the file extension .ttc' or `.otc'). A collection contains
multiple font faces that share some tables to avoid redundancy, thus
reducing the file size. In FreeType, elements of a collection can
be accessed with a proper face index.
Both the GX and the OpenType 1.8 variation fonts provide `named
instances'. FreeType maps them to face indices (they can also be
accessed with the standard MM interface).
Other font formats (not using the SFNT wrapper) also provide
multiple faces within one file; they are marked with an asterisk
(`*') in the table below.
FreeType can be configured to support Mac files (on older Mac OS
versions, a file' is stored as a data and a resource fork, that is, within two separate data chunks). If a file can't be opened as a font, FreeType then checks whether it is a resource fork, trying to extract the contained font data from either a POST' or `sfnt'
resource.
Please send additions and/or corrections to [email protected] or to the
FreeType developer's list at [email protected] (for
subscribers only). If you can provide a font example for a format
which isn't supported yet please send a mail too.
wrapper font font glyph FreeType reference format format type access driver documents
--- BDF --- --- bdf 5005.BDF_Spec.pdf, X11
SFNT PS TYPE_1 --- type1 Type 1 GX Font Format [7]
(for the Mac; not supported)
SFNT PS TYPE_1 CID cid 5180.sfnt.pdf (for the Mac) [3]
SFNT PS CFF --- cff OT spec, 5176.CFF.pdf
(`OTTO' format)
SFNT PS CFF CID cff OT spec, 5176.CFF.pdf
SFNT PS CFF SYNTHETIC --- OT spec, 5176.CFF.pdf
SFNT PS CFF2 --- cff OT spec 1.8
SFNT TT SBIT --- sfnt XFree86 (bitmaps only;
with head' table) SFNT TT MACSBIT --- sfnt OT spec (for the Mac; bitmaps only; bhed' table)
SFNT TT --- --- truetype OT spec (normal' TT font) SFNT TT VAR --- truetype GX spec (?var' tables)
SFNT TT VAR --- truetype OT spec 1.8
(?var' + ?VAR' tables)
WOFF --- --- --- cff, Compressed SFNT, ver. 1.0 [6]
truetype
WOFF2 --- --- --- cff, Compressed SFNT, ver. 2.0 [6]
truetype
--- PS TYPE_1 --- type1 T1_SPEC.pdf
(PFA, Type 1 font resource)
PFB PS TYPE_1 --- type1 T1_SPEC.pdf,
5040.Download_Fonts.pdf
(normal' Type 1 font) --- PS TYPE_1 CID cid PLRM.pdf (CID Font Type 0; Type 9 font) --- PS MM --- type1 5015.Type1_Supp.pdf (Multiple Masters) --- PS CFF --- cff 5176.CFF.pdf (pure' CFF)
--- PS* CFF CID cff 5176.CFF.pdf (pure' CFF) --- PS CFF SYNTHETIC --- 5176.CFF.pdf (pure' CFF)
--- PS CFF/MM --- cff old 5167.CFF.pdf (pure' CFF) [3] --- PS* CFF/MM CID cff old 5167.CFF.pdf (pure' CFF)
[3]
--- PS CFF/MM SYNTHETIC --- old 5167.CFF.pdf (`pure' CFF)
[3]
PS PS CFF --- --- PLRM.pdf (Type 2) [1]
PS PS* CFF CID --- PLRM.pdf (Type 2) [1]
PS PS CFF SYNTHETIC --- PLRM.pdf (Type 2) [1]
PS PS CFF/MM --- --- PLRM.pdf (Type 2) [1]
PS PS* CFF/MM CID --- PLRM.pdf (Type 2) [1]
PS PS CFF/MM SYNTHETIC --- PLRM.pdf (Type 2) [1]
--- PS --- TYPE_0 --- PLRM.pdf
--- PS TYPE_3 --- --- PLRM.pdf (never supported)
--- PS TYPE_3 CID --- PLRM.pdf (CID Font Type 1;
Type 10 font; never supported)
PS PS TYPE_14 --- --- PLRM.pdf (Chameleon font;
Type 14 font; never supported?)
--- PS TYPE_32 CID --- PLRM.pdf (CID Font Type 4;
Type 32 font; never supported?)
PS TT --- --- type42 5012.Type42_Spec.pdf
(Type 42 font)
PS TT --- CID --- PLRM.pdf (CID Font Type 2;
Type 11 font)
? ? CEF ? cff ?
--- PCF --- --- pcf X11 [4]
LZW PCF --- --- pcf X11 [4]
BZ2 PCF --- --- pcf X11 [4]
--- PFR* PFR0 --- pfr [2]
--- PFR PFR1 --- --- (undocumented, proprietary;
probably never supported)
--- WINFNT* --- --- winfonts Windows developer's notes [5]
--- WINFNT VECTOR --- --- Windows developer's notes [5]
[1] Support should be rather simple since this is identical to `CFF'
but in a PS wrapper.
[2] The official PFR specification is no longer available, but
archive.org has archived it:
https://web.archive.org/web/20091014062300/http://www.bitstream.com/font_rendering/products/truedoc/pfrspec.html
https://web.archive.org/web/20081115152605/http://www.bitstream.com/font_rendering/pdfs/pfrspec1.3.pdf
The syntax of the auxiliary data is not defined there, but is
partially defined in MHP 1.0.3 (also called ETSI TS 101812 V1.3.1)
section 7.4.
https://www.etsi.org/
https://webapp.etsi.org/workprogram/Report_WorkItem.asp?WKI_ID=18799[3] Support is rudimentary currently; some tables or data are not
loaded yet.
[4] See
THE X WINDOW SYSTEM SERVER: X VERSION 11, RELEASE 5
Elias Israel, Erik Fortune, Digital Press, 1992
ISBN 1-55558-096-3
for a specification given in Appendix D on pgs. 436-450. However,
this information might be out of date; unfortunately, there is no
PCF specification available online, and this book is out of print.
George Williams deduced the font format from the X11 sources and
documented it for his FontForge font editor:
https://fontforge.github.io/pcf-format.html[5] This is from MS Windows 3; see Microsoft's Knowledge Base article
at
https://support.microsoft.com/kb/65123[6] Supported font formats are TrueType and OpenType fonts as
defined in the OpenType specification 1.6 and newer.
[7] The Type 1 GX Font Format' (dated 1995-09-27) was distributed in Apple Developer CD-ROM in those days. The content of TYP1' table
is a PostScript Type 1 font without the eexec encryption. Current
versions of FreeType don't not support this format, but FontForge
can load it.
Copyright (C) 2004-2023 by
David Turner, Robert Wilhelm, and Werner Lemberg.
This file is part of the FreeType project, and may only be used,
modified, and distributed under the terms of the FreeType project
license, LICENSE.TXT. By continuing to use, modify, or distribute this
file you indicate that you have read the license and understand and
accept it fully.
--- end of formats.txt ---
Local Variables:
coding: utf-8
End:
Vs Freetype Docs Raster (`vs/freetype/docs/raster.txt`)
How FreeType's rasterizer work
by David Turner
Revised 2007-Feb-01This file is an attempt to explain the internals of the FreeType
rasterizer. The rasterizer is of quite general purpose and could
easily be integrated into other programs.
I. Introduction
II. Rendering Technology
1. Requirements
2. Profiles and Spans
a. Sweeping the Shape
b. Decomposing Outlines into Profiles
c. The Render Pool
d. Computing Profiles Extents
e. Computing Profiles Coordinates
f. Sweeping and Sorting the Spans
I. Introduction
A rasterizer is a library in charge of converting a vectorial
representation of a shape into a bitmap. The FreeType rasterizer
has been originally developed to render the glyphs found in
TrueType files, made up of segments and second-order Béziers.
Meanwhile it has been extended to render third-order Bézier curves
also. This document is an explanation of its design and
implementation.
While these explanations start from the basics, a knowledge of
common rasterization techniques is assumed.
II. Rendering Technology
- Requirements
We assume that all scaling, rotating, hinting, etc., has been
already done. The glyph is thus described by a list of points in
the device space.
All point coordinates are in the 26.6 fixed float format. The
used orientation is:^ y
| reference orientation
|
*----> x
026.6' means that 26 bits are used for the integer part of a value and 6 bits are used for the fractional part. Consequently, thedistance' between two neighbouring pixels is
64 `units' (1 unit = 1/64 of a pixel).Note that, for the rasterizer, pixel centers are located at
integer coordinates. The TrueType bytecode interpreter,
however, assumes that the lower left edge of a pixel (which is
taken to be a square with a length of 1 unit) has integer
coordinates.text^ y ^ y | | | (1,1) | (0.5,0.5) +-----------+ +-----+-----+ | | | | | | | | | | | | | o-----+-----> x | | | (0,0) | | | | | o-----------+-----> x +-----------+(0,0) (-0.5,-0.5)
TrueType bytecode interpreter FreeType rasterizer
A pixel line in the target bitmap is called a `scanline'.A glyph is usually made of several contours, also called
`outlines'. A contour is simply a closed curve that delimits an
outer or inner region of the glyph. It is described by a series
of successive points of the points table.Each point of the glyph has an associated flag that indicates
whether it ison' oroff' the curve. Two successive `on'
points indicate a line segment joining the two points.One
off' point amidst twoon' points indicates a second-degree
(conic) Bézier parametric arc, defined by these three points
(theoff' point being the control point, and theon' ones the
start and end points). Similarly, a third-degree (cubic) Bézier
curve is described by four points (twooff' control points between twoon' points).Finally, for second-order curves only, two successive
off' points forces the rasterizer to create, during rendering, anon' point amidst them, at their exact middle. This greatly
facilitates the definition of successive Bézier arcs.
The parametric form of a second-order Bézier curve is:
P(t) = (1-t)^2*P1 + 2*t*(1-t)*P2 + t^2*P3
(P1 and P3 are the end points, P2 the control point.)The parametric form of a third-order Bézier curve is:
P(t) = (1-t)^3*P1 + 3*t*(1-t)^2*P2 + 3*t^2*(1-t)*P3 + t^3*P4
(P1 and P4 are the end points, P2 and P3 the control points.)For both formulae, t is a real number in the range [0..1].
Note that the rasterizer does not use these formulae directly.
They exhibit, however, one very useful property of Bézier arcs:
Each point of the curve is a weighted average of the control
points.
As all weights are positive and always sum up to 1, whatever the
value of t, each arc point lies within the triangle (polygon)
defined by the arc's three (four) control points.
In the following, only second-order curves are discussed since
rasterization of third-order curves is completely identical.
Here some samples for second-order curves.
* # on curve
* off curve
__---__
#-__ _-- -_
--__ _- -
--__ # \
--__ #
-#
Two `on' points
Two `on' points and one `off' point
between them
*
# __ Two `on' points with two `off'
\ - - points between them. The point
\ / \ marked `0' is the middle of the
- 0 \ `off' points, and is a `virtual
-_ _- # on' point where the curve passes.
-- It does not appear in the point
* list.- Profiles and Spans
The following is a basic explanation of the kind of computations
made by the rasterizer to build a bitmap from a vector
representation. Note that the actual implementation is slightly
different, due to performance tuning and other factors.
However, the following ideas remain in the same category, and are
more convenient to understand.
a. Sweeping the Shape
The best way to fill a shape is to decompose it into a number of
simple horizontal segments, then turn them on in the target
bitmap. These segments are called `spans'.
__---__
_-- -_
_- -
- \
/ \
/ \
| \
__---__ Example: filling a shape
_----------_ with spans.
_--------------
----------------\
/-----------------\ This is typically done from the top
/ \ to the bottom of the shape, in a
| | \ movement called a `sweep'.
V
__---__
_----------_
_--------------
----------------\
/-----------------\
/-------------------\
|---------------------\
In order to draw a span, the rasterizer must compute its
coordinates, which are simply the x coordinates of the shape's
contours, taken on the y scanlines.
/---/ |---| Note that there are usually
/---/ |---| several spans per scanline.
| /---/ |---|
| /---/_______|---| When rendering this shape to the
V /----------------| current scanline y, we must
/-----------------| compute the x values of the
a /----| |---| points a, b, c, and d.
- - - * * - - - - * * - - y -
/ / b c| |d
/---/ |---|
/---/ |---| And then turn on the spans a-b
/---/ |---| and c-d.
/---/_______|---|
/----------------|
/-----------------|
a /----| |---|
- - - ####### - - - - ##### - - y -
/ / b c| |db. Decomposing Outlines into Profiles
For each scanline during the sweep, we need the following
information:
o The number of spans on the current scanline, given by the
number of shape points intersecting the scanline (these are
the points a, b, c, and d in the above example).
o The x coordinates of these points.
x coordinates are computed before the sweep, in a phase called
`decomposition' which converts the glyph into *profiles*.
Put it simply, a `profile' is a contour's portion that can only
be either ascending or descending, i.e., it is monotonic in the
vertical direction (we also say y-monotonic). There is no such
thing as a horizontal profile, as we shall see.
Here are a few examples:
this square
1 2
---->---- is made of two
| | | |
| | profiles | |
^ v ^ + v
| | | |
| | | |
----<----
up down
this triangle
P2 1 2
|\ is made of two | \
^ | \ \ | \
| | \ \ profiles | \ |
| | \ v ^ | \ |
| \ | | + \ v
| \ | | \
P1 ---___ \ ---___ \
---_\ ---_ \
<--__ P3 up down
A more general contour can be made of more than two profiles:
__ ^
/ | / ___ / |
/ | / | / | / |
| | / / => | v / /
| | | | | | ^ |
^ | |___| | | ^ + | + | + v
| | | v | |
| | | up |
|___________| | down |
<-- up down
Successive profiles are always joined by horizontal segments
that are not part of the profiles themselves.
For the rasterizer, a profile is simply an *array* that
associates one horizontal *pixel* coordinate to each bitmap
*scanline* crossed by the contour's section containing the
profile. Note that profiles are *oriented* up or down along the
glyph's original flow orientation.
In other graphics libraries, profiles are also called `edges' or
`edgelists'.c. The Render Pool
FreeType has been designed to be able to run well on _very_
light systems, including embedded systems with very few memory.
A render pool will be allocated once; the rasterizer uses this
pool for all its needs by managing this memory directly in it.
The algorithms that are used for profile computation make it
possible to use the pool as a simple growing heap. This means
that this memory management is actually quite easy and faster
than any kind of malloc()/free() combination.
Moreover, we'll see later that the rasterizer is able, when
dealing with profiles too large and numerous to lie all at once
in the render pool, to immediately decompose recursively the
rendering process into independent sub-tasks, each taking less
memory to be performed (see `sub-banding' below).
The render pool doesn't need to be large. A 4KByte pool is
enough for nearly all renditions, though nearly 100% slower than
a more comfortable 16KByte or 32KByte pool (that was tested with
complex glyphs at sizes over 500 pixels).d. Computing Profiles Extents
Remember that a profile is an array, associating a _scanline_ to
the x pixel coordinate of its intersection with a contour.
Though it's not exactly how the FreeType rasterizer works, it is
convenient to think that we need a profile's height before
allocating it in the pool and computing its coordinates.
The profile's height is the number of scanlines crossed by the
y-monotonic section of a contour. We thus need to compute these
sections from the vectorial description. In order to do that,
we are obliged to compute all (local and global) y extrema of
the glyph (minima and maxima).
P2 For instance, this triangle has only
two y-extrema, which are simply
|\
| \ P2.y as a vertical maximum
| \ P3.y as a vertical minimum
| \
| \ P1.y is not a vertical extremum (though
| \ it is a horizontal minimum, which we
P1 ---___ \ don't need).
---_\
P3
Note that the extrema are expressed in pixel units, not in
scanlines. The triangle's height is certainly (P3.y-P2.y+1)
pixel units, but its profiles' heights are computed in
scanlines. The exact conversion is simple:
- min scanline = FLOOR ( min y )
- max scanline = CEILING( max y )
A problem arises with Bézier Arcs. While a segment is always
necessarily y-monotonic (i.e., flat, ascending, or descending),
which makes extrema computations easy, the ascent of an arc can
vary between its control points.
P2
*
# on curve
* off curve
__-x--_
_-- -_
P1 _- - A non y-monotonic Bézier arc.
# \
- The arc goes from P1 to P3.
\
\ P3
#
We first need to be able to easily detect non-monotonic arcs,
according to their control points. I will state here, without
proof, that the monotony condition can be expressed as:
P1.y <= P2.y <= P3.y for an ever-ascending arc
P1.y >= P2.y >= P3.y for an ever-descending arc
with the special case of
P1.y = P2.y = P3.y where the arc is said to be `flat'.
As you can see, these conditions can be very easily tested.
They are, however, extremely important, as any arc that does not
satisfy them necessarily contains an extremum.
Note also that a monotonic arc can contain an extremum too,
which is then one of its `on' points:
P1 P2
#---__ * P1P2P3 is ever-descending, but P1
-_ is an y-extremum.
-
---_ \
-> \
\ P3
#
Let's go back to our previous example:
P2
*
# on curve
* off curve
__-x--_
_-- -_
P1 _- - A non-y-monotonic Bézier arc.
# \
- Here we have
\ P2.y >= P1.y &&
\ P3 P2.y >= P3.y (!)
#
We need to compute the vertical maximum of this arc to be able
to compute a profile's height (the point marked by an `x'). The
arc's equation indicates that a direct computation is possible,
but we rely on a different technique, which use will become
apparent soon.
Bézier arcs have the special property of being very easily
decomposed into two sub-arcs, which are themselves Bézier arcs.
Moreover, it is easy to prove that there is at most one vertical
extremum on each Bézier arc (for second-degree curves; similar
conditions can be found for third-order arcs).
For instance, the following arc P1P2P3 can be decomposed into
two sub-arcs Q1Q2Q3 and R1R2R3:
P2
*
# on curve
* off curve
original Bézier arc P1P2P3.
__---__
_-- --_
_- -_
- -
/ \
/ \
# #
P1 P3
P2
*
Q3 Decomposed into two subarcs
Q2 R2 Q1Q2Q3 and R1R2R3
* __-#-__ *
_-- --_
_- R1 -_ Q1 = P1 R3 = P3
- - Q2 = (P1+P2)/2 R2 = (P2+P3)/2
/ \
/ \ Q3 = R1 = (Q2+R2)/2
# #
Q1 R3 Note that Q2, R2, and Q3=R1
are on a single line which is
tangent to the curve.
We have then decomposed a non-y-monotonic Bézier curve into two
smaller sub-arcs. Note that in the above drawing, both sub-arcs
are monotonic, and that the extremum is then Q3=R1. However, in
a more general case, only one sub-arc is guaranteed to be
monotonic. Getting back to our former example:
Q2
*
__-x--_ R1
_-- #_
Q1 _- Q3 - R2
# \ *
-
\
\ R3
#
Here, we see that, though Q1Q2Q3 is still non-monotonic, R1R2R3
is ever descending: We thus know that it doesn't contain the
extremum. We can then re-subdivide Q1Q2Q3 into two sub-arcs and
go on recursively, stopping when we encounter two monotonic
subarcs, or when the subarcs become simply too small.
We will finally find the vertical extremum. Note that the
iterative process of finding an extremum is called `flattening'.e. Computing Profiles Coordinates
Once we have the height of each profile, we are able to allocate
it in the render pool. The next task is to compute coordinates
for each scanline.
In the case of segments, the computation is straightforward,
using the Euclidean algorithm (also known as Bresenham).
However, for Bézier arcs, the job is a little more complicated.
We assume that all Béziers that are part of a profile are the
result of flattening the curve, which means that they are all
y-monotonic (ascending or descending, and never flat). We now
have to compute the intersections of arcs with the profile's
scanlines. One way is to use a similar scheme to flattening
called `stepping'.
Consider this arc, going from P1 to
--------------------- P3. Suppose that we need to
compute its intersections with the
drawn scanlines. As already
--------------------- mentioned this can be done
directly, but the involved
* P2 _---# P3 algorithm is far too slow.
------------- _-- --
_-
_/ Instead, it is still possible to
---------/----------- use the decomposition property in
/ the same recursive way, i.e.,
| subdivide the arc into subarcs
------|-------------- until these get too small to cross
| more than one scanline!
|
-----|--------------- This is very easily done using a
| rasterizer-managed stack of
| subarcs.
# P1f. Sweeping and Sorting the Spans
Once all our profiles have been computed, we begin the sweep to
build (and fill) the spans.
As both the TrueType and Type 1 specifications use the winding
fill rule (but with opposite directions), we place, on each
scanline, the present profiles in two separate lists.
One list, called the `left' one, only contains ascending
profiles, while the other `right' list contains the descending
profiles.
As each glyph is made of closed curves, a simple geometric
property ensures that the two lists contain the same number of
elements.
Creating spans is thus straightforward:
1. We sort each list in increasing horizontal order.
2. We pair each value of the left list with its corresponding
value in the right list.
/ / | | For example, we have here
/ / | | four profiles. Two of
>/ / | | | them are ascending (1 &
1// / ^ | | | 2 3), while the two others
// // 3| | | v are descending (2 & 4).
/ //4 | | | On the given scanline,
a / /< | | the left list is (1,3),
- - - *-----* - - - - *---* - - y - and the right one is
/ / b c| |d (4,2) (sorted).
There are then two spans, joining
1 to 4 (i.e. a-b) and 3 to 2
(i.e. c-d)!
Sorting doesn't necessarily take much time, as in 99 cases out
of 100, the lists' order is kept from one scanline to the next.
We can thus implement it with two simple singly-linked lists,
sorted by a classic bubble-sort, which takes a minimum amount of
time when the lists are already sorted.
A previous version of the rasterizer used more elaborate
structures, like arrays to perform `faster' sorting. It turned
out that this old scheme is not faster than the one described
above.
Once the spans have been `created', we can simply draw them in
the target bitmap.Copyright (C) 2003-2023 by
David Turner, Robert Wilhelm, and Werner Lemberg.
This file is part of the FreeType project, and may only be used,
modified, and distributed under the terms of the FreeType project
license, LICENSE.TXT. By continuing to use, modify, or distribute this
file you indicate that you have read the license and understand and
accept it fully.
--- end of raster.txt ---
Local Variables:
coding: utf-8
End:
Vs Libpdcurses Doc Sdl (`vs/libpdcurses/doc/sdl.md`)
SDL Considerations
There are no special requirements to use PDCurses for SDL -- all
PDCurses-compatible code should work fine. (In fact, you can even build
against the Windows console pdcurses.dll, and then swap in the SDL
pdcurses.dll.) Nothing extra is needed beyond the base SDL library.
However, there are some optional special features, described here.
The SDL ports operate in one of two ways, depending on whether or not
they were built with WIDE=Y:
8-bit mode
The font is a simple BMP, 32 characters wide by 8 characters tall,
preferably with a palette. (BMPs without palettes still work, but in
that case, no attributes will be available, nor will the cursor work.)
The first entry in the palette (usually black) is treated as the
background color; the last entry (usually white) is treated as the
foreground. These are changed or made transparent as appropriate; any
other colors in the palette are passed through unchanged. So -- although
a one-bit depth is sufficient for a normal font -- you could redraw some
characters as multi-colored tiles.
The font must be monospaced. The size of each character is derived by
dividing the width of the BMP by 32 and the height by 8. There is no
constraint on the dimensions.
As provided in the default font and expected by acs_map[], the font is
in Code Page 437 form. But you can of course use any layout if you're
not relying on correct values for the ACS_* macros.
The font can be set via the environment variable PDC_FONT. If it's not
set, PDCurses looks for a file named "pdcfont.bmp" in the current
directory at the time of initscr(). If neither is found, it uses the
built-in default font encoded in deffont.h.
16-bit mode
Instead of a BMP, PDC_FONT points to a TrueType font. Only true
monospaced fonts work well. The font can be set at compile time via
PDC_FONT_PATH, and/or at runtime via pdc_ttffont. The environment
variable PDC_FONT_SIZE is also available to control the font size (also
as a compile-time define, and at runtime as pdc_font_size.) The
character mapping for chtypes is 16-bit Unicode (the Basic Multilingual
Plane).
The default font (if not redefined) is based on the OS:
Windows: C:/Windows/Fonts/lucon.ttf
Mac: /Library/Fonts/Courier New.ttf
Other: /usr/share/fonts/truetype/freefont/FreeMono.ttf
Backgrounds
PDCurses for SDL supports an optional background image BMP. This is used
whenever start_color() has not been called (see the ptest demo for an
example), or when use_default_colors() has been called after
start_color(), and the background color of a pair has been set to -1
(see ozdemo, worm, and rain for examples). The usage parallels that of
ncurses in an appropriate terminal (e.g., Gnome Terminal). The image is
tiled to cover the PDCurses window, and can be any size or depth.
As with the font, you can point to a location for the background via the
environment variable PDC_BACKGROUND; "pdcback.bmp" is the fallback.
(There is no default background.)
Icons
The icon (used with SDL_WM_SetIcon() -- not used for the executable
file) can be set via the environment variable PDC_ICON, and falls back
to "pdcicon.bmp", and then to the built-in icon from deficon.h. The
built-in icon is the PDCurses logo, as seen in ../x11/little_icon.xbm.
The SDL docs say that the icon must be 32x32, at least for use with MS
Windows.
If pdc_screen is preinitialized (see below), PDCurses does not attempt
to set the icon.
Screen size
The default screen size is 80x25 characters (whatever size they may be),
but you can override this via the environment variables PDC_COLS and/or
PDC_LINES. (Some other ports use COLS and LINES; this is not done here
because those values are, or should be, those of the controlling
terminal, and PDCurses for SDL is independent of the terminal.) If
pdc_screen is preinitialized (see below), these are ignored.
Integration with SDL
If you want to go further, you can mix PDCurses and SDL functions. (Of
course this is extremely non-portable!) To aid you, there are several
external variables and functions specific to the SDL ports; you could
include pdcsdl.h, or just add the declarations you need in your code:
PDCEX SDL_Surface *pdc_screen, *pdc_font, *pdc_icon, *pdc_back;
PDCEX int pdc_sheight, pdc_swidth, pdc_yoffset, pdc_xoffset;
PDCEX void PDC_update_rects(void);
PDCEX void PDC_retile(void);The SDL2 port adds:
PDCEX SDL_Window *pdc_window;pdc_screen is the main surface, unless it's preset before initscr(). In
SDL1, pdc_screen is created by SDL_SetVideoMode(); in SDL2, pdc_window
is the main window, created by SDL_CreateWindow(), and pdc_screen is set
by SDL_GetWindowSurface(pdc_window). (See sdltest.c for examples.) You
can perform normal SDL operations on this surface, but PDCurses won't
respect them when it updates. (For that, see PDC_retile().) As an
alternative, you can preinitialize this surface before calling
initscr(). In that case, you can use pdc_sheight, pdc_swidth,
pdc_yoffset and/or pdc_xoffset (q.v.) to confine PDCurses to only a
specific area of the surface, reserving the rest for other SDL
operations. If you preinitialize pdc_screen, you'll have to close it
yourself; PDCurses will ignore resize events, and won't try to set the
icon. Also note that if you preinitialize pdc_screen, it need not be the
display surface.
pdc_font (in 8-bit mode), pdc_icon, and pdc_back are the SDL_surfaces
for the font, icon, and background, respectively. You can set any or all
of them before initscr(), and thus override any of the other ways to set
them. But note that pdc_icon will be ignored if pdc_screen is preset.
pdc_sheight and pdc_swidth are the dimensions of the area of pdc_screen
to be used by PDCurses. You can preset them before initscr(); if either
is not set, it defaults to the full screen size minus the x or y offset,
as appropriate.
pdc_xoffset and pdc_yoffset are the x and y offset for the area of
pdc_screen to be used by PDCurses. See the sdltest demo for an example.
PDC_retile() makes a copy of pdc_screen, then tiles it with the
background image, if any. The resulting surface is used as the
background for transparent character cells. PDC_retile() is called from
initscr() and resize_term(). However, you can also use it at other
times, to take advantage of the way it copies pdc_screen: Draw some SDL
stuff; call PDC_retile(); do some curses stuff -- it will use whatever
was on pdc_screen as the background. Then you can erase the curses
screen, do some more SDL stuff, and call PDC_retile() again to make a
new background. (If you don't erase the curses screen, it will be
incorporated into the background when you call PDC_retile().) But this
only works if no background image is set.
PDC_update_rects() is how the screen actually gets updated. For
performance reasons, when drawing, PDCurses for SDL maintains a table of
rectangles that need updating, and only updates (by calling this
function) during getch(), napms(), or when the table gets full.
Normally, this is sufficient; but if you're pausing in some way other
than by using napms(), and you're not doing keyboard checks, you may get
an incomplete update. If that happens, you can call PDC_update_rects()
manually.
Interaction with stdio
As with X11, it's a bad idea to mix curses and stdio calls. (In fact,
that's true for PDCurses on any platform; but especially these two,
which don't run under terminals.) Depending on how SDL is built, stdout
and stderr may be redirected to files.
Vs Libpdcurses Doc X11 (`vs/libpdcurses/doc/x11.md`)
X11 Considerations
PDCurses for X11 uses the System V IPC shared memory facility, along
with sockets, to share data between the curses program and the child
process created to manage the X stuff.
When compiling your application, you need to include the <curses.h> or
<xcurses.h> that comes with PDCurses. You also need to link your code
with libXCurses. You may need to link with the following libraries under
X11R5:
Xaw Xmu Xt X11or, under X11R6:
Xaw Xmu Xt X11 SM ICE XextYou can run "xcurses-config --libs" to show the link parameters for your
system. If using dynamic linking, on some systems, "-lXCurses" suffices.
By calling Xinitscr() rather than initscr(), you can pass your program
name and resource overrides to PDCurses. The program name is used as the
title of the X window, and for defining X resources specific to your
program.
Interaction with stdio
Be aware that curses programs that expect to have a normal tty
underneath them will be very disappointed! Output directed to stdout
will go to the xterm that invoked the PDCurses application, or to the
console if not invoked directly from an xterm. Similarly, stdin will
expect its input from the same place as stdout.
X Resources
PDCurses for X11 recognizes the following resources:
lines
Specifies the number of lines the "screen" will have. Directly equates
to LINES. There is no theoretical maximum. The minimum value must be 2.
Default: 24
cols
Specifies the number of columns the "screen" will have. Directly equates
to COLS. There is no theoretical maximum. The minimum value must be 2.
Default: 80
normalFont
The name of a fixed width font. Defaults:
- narrow: -misc-fixed-medium-r-normal--13-120-75-75-c-70-iso8859-1
- wide: -misc-fixed-medium-r-normal--20-200-75-75-c-100-iso10646-1
italicFont
The name of a fixed width font to be used for characters with A_ITALIC
attributes. Must have the same cell size as normalFont. Defaults:
- narrow: -misc-fixed-medium-o-normal--13-120-75-75-c-70-iso8859-1
- wide: -misc-fixed-medium-o-normal--20-200-75-75-c-100-iso10646-1
boldFont
The name of a fixed width font to be used for characters with A_BOLD
attributes. Must have the same cell size as normalFont. Defaults:
- narrow: -misc-fixed-bold-r-normal--13-120-75-75-c-70-iso8859-1
- wide: -misc-fixed-bold-r-normal--20-200-75-75-c-100-iso10646-1
pointer
The name of a valid pointer cursor. Default: xterm
pointerForeColor
The foreground color of the pointer. Default: black
pointerBackColor
The background color of the pointer. Default: white
cursorColor
textCursor
The alignment of the text cursor; horizontal or vertical. Default:
horizontal
colorBlack
The color of the COLOR_BLACK attribute. Default: Black
colorRed
The color of the COLOR_RED attribute. Default: red3
colorGreen
The color of the COLOR_GREEN attribute. Default: green3
colorYellow
The color of the COLOR_YELLOW attribute. Default: yellow3
colorBlue
The color of the COLOR_BLUE attribute. Default: blue3
colorMagenta
The color of the COLOR_MAGENTA attribute. Default: magenta3
colorCyan
The color of the COLOR_CYAN attribute. Default: cyan3
colorWhite
The color of the COLOR_WHITE attribute. Default: Grey
colorBoldBlack
COLOR_BLACK combined with A_BOLD. Default: grey40
colorBoldRed
COLOR_RED combined with A_BOLD. Default: red1
colorBoldGreen
COLOR_GREEN combined with A_BOLD. Default: green1
colorBoldYellow
COLOR_YELLOW combined with A_BOLD. Default: yellow1
colorBoldBlue
COLOR_BLUE combined with A_BOLD. Default: blue1
colorBoldMagenta
COLOR_MAGENTA combined with A_BOLD. Default: magenta1
colorBoldCyan
COLOR_CYAN combined with A_BOLD. Default: cyan1
colorBoldWhite
COLOR_WHITE combined with A_BOLD. Default: White
bitmap
The name of a valid bitmap file of depth 1 (black and white) used for
the application's icon. The file is an X bitmap. Default: a 32x32 or
64x64 pixmap depending on the window manager
pixmap
The name of a valid pixmap file of any depth supported by the window
manager (color) for the application's icon, The file is an X11 pixmap.
This resource is only available if the libXpm package has been installed
(most systems have this by default). This resource overrides the
"bitmap" resource. Default: none, uses default bitmap above
translations
Translations enable the user to customize the action that occurs when a
key, combination of keys, or a button is pressed. The translations are
similar to those used by xterm.
Defaults:
<Key>: XCursesKeyPress()
<KeyUp>: XCursesKeyPress()
: XCursesButton()
: XCursesButton()
: XCursesButton()The most useful action for KeyPress translations is string(). The
argument to the string() action can be either a string or a hex
representation of a character; e.g., string(0x1b) will send the ASCII
escape character to the application; string("[11~") will send [ 1 1 ~ ,
as separate keystrokes.
shmmin
On most systems, there are two Unix kernel parameters that determine the
allowable size of a shared memory segment. These parameters are usually
something like SHMMIN and SHMMAX. To use shared memory, a program must
allocate a segment of shared memory that is between these two values.
Usually these values are like 1 for SHMMIN and some large number for
SHMMAX. Sometimes the Unix kernel is configured to have a value of
SHMMIN that is bigger than the size of one of the shared memory segments
that libXCurses uses. On these systems an error message like:
Cannot allocate shared memory for SCREEN: Invalid argumentwill result. To overcome this problem, this resource should be set to
the kernel value for SHMMIN. This ensures that a shared memory segment
will always be bigger than the kernel value for SHMMIN (and hopefully
less than SHMMAX!) Default: 0
borderColor
The color of the border around the screen. Default: black
borderWidth
The width in pixels of the border around the screen. Default: 0
clickPeriod
The period (in milliseconds) between a button press and a button release
that determines if a click of a button has occurred. Default: 100
doubleClickPeriod
The period (in milliseconds) between two button press events that
determines if a double click of a button has occurred. Default: 200
composeKey
The name of the X key that defines the "compose key", which is used to
enter characters in the Latin-1 character set above 0xA0. (See "Compose
Keys for Latin-1" below.) This is used only when PDCurses is built
without XIM support. While in compose mode, the text cursor will appear
as a hollow rectangle. Default: Multi_key
Using Resources
All applications have a top-level class name of "XCurses". If Xinitscr()
is used, it sets an application's top-level widget name. (Otherwise the
name defaults to "PDCurses".)
Examples for app-defaults or .Xdefaults:
!
! resources for XCurses class of programs
!
XCurses*lines: 30
XCurses*cols: 80
XCurses*normalFont: 9x13
XCurses*bitmap: /tmp/xcurses.xbm
XCurses*pointer: top_left_arrow
!
! resources for testcurs - XCurses
!
testcurs.colorRed: orange
testcurs.colorBlack: midnightblue
testcurs.lines: 25
*testcurs.Translations: #override \n \
<Key>F12: string(0x1b) string("[11~") \n
!
! resources for THE - XCurses
!
! resources with the * wildcard can be overridden by a parameter passed
! to initscr()
!
the*normalFont: 9x15
the*lines: 40
the*cols: 86
the*pointer: xterm
the*pointerForeColor: white
the*pointerBackColor: black
!
! resources with the . format can not be overridden by a parameter passed
! to Xinitscr()
!
the.bitmap: /home/mark/the/the64.xbm
the.pixmap: /home/mark/the/the64.xpmResources may also be passed as parameters to the Xinitscr() function.
Parameters are strings in the form of switches; e.g., to set the color
"red" to "indianred", and the number of lines to 30, the string passed
to Xinitscr would be: "-colorRed indianred -lines 30"
Compose Keys for Latin-1
When built without XIM support, PDCurses for X11 provides its own,
limited compose key system for Latin-1 characters. The available
combinations are listed here. For a given character, any of the
combinations shown in the last column may be used. To generate a
character, press the "compose" key followed by one of the pairs of
keystrokes. Where no key is evident, the spacebar is used. Thus, to
generate the NO-BREAK SPACE, press the "compose" key followed by two
hits of the spacebar.
With a typical modern X server, you can get many more compose key
combinations by using XIM instead. Configure PDCurses with --enable-xim
to use XIM support.
This document is encoded in UTF-8.
Hex| Dec |Chr| Description ISO 10646-1:1993(E) | Compose key combinations
---+-----+---+---------------------------------+-------------------------
A0 | 160 | | NO-BREAK SPACE |
A1 | 161 | ¡ | INVERTED EXCLAMATION MARK | ! !!
A2 | 162 | ¢ | CENT SIGN | c| |c c/ c$ C$ C|
A3 | 163 | £ | POUND SIGN | L- L$ L= l- l$ l= |-
A4 | 164 | ¤ | CURRENCY SIGN | xo ox XO g$
A5 | 165 | ¥ | YEN SIGN | =y y= =Y Y= Y- y$ y-
A6 | 166 | ¦ | BROKEN BAR | | || vb VB |^
A7 | 167 | § | SECTION SIGN | SO SS s! S! so
A8 | 168 | ¨ | DIAERESIS | " ""
A9 | 169 | © | COPYRIGHT SIGN | CO co OC
AA | 170 | ª | FEMININE ORDINAL INDICATOR | sa SA a_ A_
AB | 171 | « | LEFT DOUBLE ANGLE QUOTES | <<
AC | 172 | ¬ | NOT SIGN | -, no NO
AD | 173 | | SOFT HYPHEN | - --
AE | 174 | ® | REGISTERED SIGN | RO ro OR
AF | 175 | ¯ | MACRON | -^ _^ __
B0 | 176 | ° | DEGREE SIGN | o 0^ 0* de DE ^0
B1 | 177 | ± | PLUS-MINUS SIGN | -+ +-
B2 | 178 | ² | SUPERSCRIPT TWO | 2 2^ s2 ^2
B3 | 179 | ³ | SUPERSCRIPT THREE | 3 3^ s3 ^3
B4 | 180 | ´ | ACUTE ACCENT | ' ''
B5 | 181 | µ | MICRO SIGN | u /u /U *m *M
B6 | 182 | ¶ | PILCROW SIGN | p! P! pg PG
B7 | 183 | · | MIDDLE DOT | . .^ ..
B8 | 184 | ¸ | CEDILLA | , ,,
B9 | 185 | ¹ | SUPERSCRIPT ONE | 1 1^ s1 ^1
BA | 186 | º | MASCULINE ORDINAL INDICATOR | o_ s0 S0
BB | 187 | » | RIGHT DOUBLE ANGLE QUOTES | >>
BC | 188 | ¼ | VULGAR FRACTION ONE QUARTER | 14
BD | 189 | ½ | VULGAR FRACTION ONE HALF | 12
BE | 190 | ¾ | VULGAR FRACTION THREE QUARTERS | 34
BF | 191 | ¿ | INVERTED QUESTION MARK | ? ??
C0 | 192 | À | CAPITAL A WITH GRAVE ACCENT | `A A`
C1 | 193 | Á | CAPITAL A WITH ACUTE ACCENT | 'A A'
C2 | 194 | Â | CAPITAL A WITH CIRCUMFLEX ACCENT| ^A A^ A>
C3 | 195 | Ã | CAPITAL A WITH TILDE | ~A A~ A-
C4 | 196 | Ä | CAPITAL A WITH DIAERESIS | "A A"
C5 | 197 | Å | CAPITAL A WITH RING ABOVE | oA Ao A* OA *A
C6 | 198 | Æ | CAPITAL LIGATURE AE | AE
C7 | 199 | Ç | CAPITAL C WITH CEDILLA | ,C C,
C8 | 200 | È | CAPITAL E WITH GRAVE ACCENT | `E E`
C9 | 201 | É | CAPITAL E WITH ACUTE ACCENT | 'E E'
CA | 202 | Ê | CAPITAL E WITH CIRCUMFLEX ACCENT| ^E E^ E>
CB | 203 | Ë | CAPITAL E WITH DIAERESIS | "E E"
CC | 204 | Ì | CAPITAL I WITH GRAVE ACCENT | `I I`
CD | 205 | Í | CAPITAL I WITH ACUTE ACCENT | 'I I'
CE | 206 | Î | CAPITAL I WITH CIRCUMFLEX ACCENT| ^I I^ I>
CF | 207 | Ï | CAPITAL I WITH DIAERESIS | "I I"
D0 | 208 | Ð | CAPITAL ETH | D-
D1 | 209 | Ñ | CAPITAL N WITH TILDE | ~N N~ N-
D2 | 210 | Ò | CAPITAL O WITH GRAVE ACCENT | `O O`
D3 | 211 | Ó | CAPITAL O WITH ACUTE ACCENT | 'O O'
D4 | 212 | Ô | CAPITAL O WITH CIRCUMFLEX ACCENT| ^O O^ O>
D5 | 213 | Õ | CAPITAL O WITH TILDE | ~O O~ O-
D6 | 214 | Ö | CAPITAL O WITH DIAERESIS | "O O"
D7 | 215 | × | MULTIPLICATION SIGN | x xx XX mu MU
D8 | 216 | Ø | CAPITAL O WITH STROKE | /O O/
D9 | 217 | Ù | CAPITAL U WITH GRAVE ACCENT | `U U`
DA | 218 | Ú | CAPITAL U WITH ACUTE ACCENT | 'U U'
DB | 219 | Û | CAPITAL U WITH CIRCUMFLEX ACCENT| ^U U^ U>
DC | 220 | Ü | CAPITAL U WITH DIAERESIS | "U U"
DD | 221 | Ý | CAPITAL Y WITH ACUTE ACCENT | 'Y Y'
DE | 222 | Þ | CAPITAL THORN | P TH |P
DF | 223 | ß | SMALL SHARP S | ss
E0 | 224 | à | SMALL A WITH GRAVE ACCENT | `a a`
E1 | 225 | á | SMALL A WITH ACUTE ACCENT | 'a a'
E2 | 226 | â | SMALL A WITH CIRCUMFLEX ACCENT | ^a a^ a>
E3 | 227 | ã | SMALL A WITH TILDE | ~a a~ a-
E4 | 228 | ä | SMALL A WITH DIAERESIS | "a a"
E5 | 229 | å | SMALL A WITH RING ABOVE | oa ao Oa a* *a
E6 | 230 | æ | SMALL LIGATURE AE | ae
E7 | 231 | ç | SMALL C WITH CEDILLA | ,c c,
E8 | 232 | è | SMALL E WITH GRAVE ACCENT | `e e`
E9 | 233 | é | SMALL E WITH ACUTE ACCENT | 'e e'
EA | 234 | ê | SMALL E WITH CIRCUMFLEX ACCENT | ^e e^ e>
EB | 235 | ë | SMALL E WITH DIAERESIS | "e e"
EC | 236 | ì | SMALL I WITH GRAVE ACCENT | `i i`
ED | 237 | í | SMALL I WITH ACUTE ACCENT | 'i i'
EE | 238 | î | SMALL I WITH CIRCUMFLEX ACCENT | ^i i^ i>
EF | 239 | ï | SMALL I WITH DIAERESIS | "i i"
F0 | 240 | ð | SMALL ETH | d-
F1 | 241 | ñ | SMALL N WITH TILDE | ~n n~ n-
F2 | 242 | ò | SMALL O WITH GRAVE ACCENT | `o o`
F3 | 243 | ó | SMALL O WITH ACUTE ACCENT | 'o o'
F4 | 244 | ô | SMALL O WITH CIRCUMFLEX ACCENT | ^o o^ o>
F5 | 245 | õ | SMALL O WITH TILDE | ~o o~ o-
F6 | 246 | ö | SMALL O WITH DIAERESIS | "o o"
F7 | 247 | ÷ | DIVISION SIGN | -: :-
F8 | 248 | ø | SMALL O WITH OBLIQUE BAR | /o o/
F9 | 249 | ù | SMALL U WITH GRAVE ACCENT | `u u`
FA | 250 | ú | SMALL U WITH ACUTE ACCENT | 'u u'
FB | 251 | û | SMALL U WITH CIRCUMFLEX ACCENT | ^u u^ u>
FC | 252 | ü | SMALL U WITH DIAERESIS | "u u"
FD | 253 | ý | SMALL Y WITH ACUTE ACCENT | 'y y'
FE | 254 | þ | SMALL THORN | p th |p
FF | 255 | ÿ | SMALL Y WITH DIAERESIS | "y y"Deprecated
XCursesProgramName is no longer used. To set the program name, you must
use Xinitscr(), or PDC_set_title() to set just the window title.
The XCursesExit() function is now called automatically via atexit().
(Multiple calls to it are OK, so you don't need to remove it if you've
already added it for previous versions of PDCurses.)
XCURSES is no longer defined automatically, but need not be defined,
unless you want the X11-specific prototypes. (Normal curses programs
won't need it.)
Vs Sdl2 Docs CONTRIBUTING (`vs/sdl2/docs/CONTRIBUTING.md`)
Contributing to SDL
We appreciate your interest in contributing to SDL, this document will describe how to report bugs, contribute code or ideas or edit documentation.
Table Of Contents
Filing a GitHub issue
Reporting a bug
If you think you have found a bug and would like to report it, here are the steps you should take:
- Before opening a new issue, ensure your bug has not already been reported on the GitHub Issues page.
- On the issue tracker, click on New Issue.
- Include details about your environment, such as your Operating System and SDL version.
- If possible, provide a small example that reproduces your bug.
Suggesting enhancements
If you want to suggest changes for the project, here are the steps you should take:
- Check if the suggestion has already been made on:
- the issue tracker;
- the discourse forum;
- or if a pull request already exists.
- On the issue tracker, click on New Issue.
- Describe what change you would like to happen.
Contributing code
This section will cover how the process of forking the project, making a change and opening a pull request.
Forking the project
The first step consists in making a fork of the project, this is only necessary for the first contribution.
Head over to https://github.com/libsdl-org/SDL and click on the Fork button in the top right corner of your screen, you may leave the fields unchanged and click Create Fork.
You will be redirected to your fork of the repository, click the green Code button and copy the git clone link.
If you had already forked the repository, you may update it from the web page using the Fetch upstream button.
Following the style guide
Code formatting is done using a custom .clang-format file, you can learn more about how to run it here.
Some legacy code may not be formatted, as such avoid formatting the whole file at once and only format around your changes.
For your commit message to be properly displayed on GitHub, it should contain:
- A short description of the commit of 50 characters or less on the first line.
- If necessary, add a blank line followed by a long description, each line should be 72 characters or less.
For example:
Fix crash in SDL_FooBar.
This addresses the issue #123456 by making sure Foo was successful
before calling Bar.Running the tests
Tests allow you to verify if your changes did not break any behaviour, here are the steps to follow:
- Before pushing, run the
testautomationsuite on your machine, there should be no more failing tests after your change than before. - After pushing to your fork, Continuous Integration (GitHub Actions) will ensure compilation and tests still pass on other systems.
Opening a pull request
- Head over to your fork's GitHub page.
- Click on the
Contributebutton andOpen Pull Request. - Fill out the pull request template.
- If any changes are requested, you can add new commits to your fork and they will be automatically added to the pull request.
Contributing to the documentation
Editing a function documentation
The wiki documentation for API functions is synchronised from the headers' doxygen comments. As such, all modifications to syntax; function parameters; return value; version; related functions should be done in the header directly.
Editing the wiki
Other changes to the wiki should done directly from https://wiki.libsdl.org/
Vs Sdl2 Docs README Android (`vs/sdl2/docs/README-android.md`)
Android
Matt Styles wrote a tutorial on building SDL for Android with Visual Studio:
http://trederia.blogspot.de/2017/03/building-sdl2-for-android-with-visual.html
The rest of this README covers the Android gradle style build process.
If you are using the older ant build process, it is no longer officially
supported, but you can use the "android-project-ant" directory as a template.
Requirements
Android SDK (version 34 or later)
https://developer.android.com/sdk/index.html
Android NDK r15c or later
https://developer.android.com/tools/sdk/ndk/index.html
Minimum API level supported by SDL: 19 (Android 4.4)
How the port works
- Android applications are Java-based, optionally with parts written in C
- As SDL apps are C-based, we use a small Java shim that uses JNI to talk to
the SDL library - This means that your application C code must be placed inside an Android
Java project, along with some C support code that communicates with Java - This eventually produces a standard Android .apk package
The Android Java code implements an "Activity" and can be found in:
android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
The Java code loads your game code, the SDL shared library, and
dispatches to native functions implemented in the SDL library:
src/core/android/SDL_android.c
Building an app
For simple projects you can use the script located at build-scripts/androidbuild.sh
There's two ways of using it:
androidbuild.sh com.yourcompany.yourapp < sources.list
androidbuild.sh com.yourcompany.yourapp source1.c source2.c ...sourceN.csources.list should be a text file with a source file name in each line
Filenames should be specified relative to the current directory, for example if
you are in the build-scripts directory and want to create the testgles.c test, you'll
run:
./androidbuild.sh org.libsdl.testgles ../test/testgles.cOne limitation of this script is that all sources provided will be aggregated into
a single directory, thus all your source files should have a unique name.
Once the project is complete the script will tell you where the debug APK is located.
If you want to create a signed release APK, you can use the project created by this
utility to generate it.
Finally, a word of caution: re running androidbuild.sh wipes any changes you may have
done in the build directory for the app!
For more complex projects, follow these instructions:
Get the source code for SDL and copy the 'android-project' directory located at SDL/android-project to a suitable location. Also make sure to rename it to your project name (In these examples: YOURPROJECT).
(The 'android-project' directory can basically be seen as a sort of starting point for the android-port of your project. It contains the glue code between the Android Java 'frontend' and the SDL code 'backend'. It also contains some standard behaviour, like how events should be handled, which you will be able to change.)
Move or symlink the SDL directory into the "YOURPROJECT/app/jni" directory
(This is needed as the source of SDL has to be compiled by the Android compiler)
- Edit "YOURPROJECT/app/jni/src/Android.mk" to include your source files.
(They should be separated by spaces after the "LOCAL_SRC_FILES := " declaration)
4a. If you want to use Android Studio, simply open your 'YOURPROJECT' directory and start building.
4b. If you want to build manually, run './gradlew installDebug' in the project directory. This compiles the .java, creates an .apk with the native code embedded, and installs it on any connected Android device
If you already have a project that uses CMake, the instructions change somewhat:
- Do points 1 and 2 from the instruction above.
- Edit "YOURPROJECT/app/build.gradle" to comment out or remove sections containing ndk-build
and uncomment the cmake sections. Add arguments to the CMake invocation as needed. - Edit "YOURPROJECT/app/jni/CMakeLists.txt" to include your project (it defaults to
adding the "src" subdirectory). Note that you'll have SDL2, SDL2main and SDL2-static
as targets in your project, so you should have "target_link_libraries(yourgame SDL2 SDL2main)"
in your CMakeLists.txt file. Also be aware that you should use add_library() instead of
add_executable() for the target containing your "main" function.
If you wish to use Android Studio, you can skip the last step.
- Run './gradlew installDebug' or './gradlew installRelease' in the project directory. It will build and install your .apk on any
connected Android device
Here's an explanation of the files in the Android project, so you can customize them:
android-project/app
build.gradle - build info including the application version and SDK
src/main/AndroidManifest.xml - package manifest. Among others, it contains the class name of the main Activity and the package name of the application.
jni/ - directory holding native code
jni/Application.mk - Application JNI settings, including target platform and STL library
jni/Android.mk - Android makefile that can call recursively the Android.mk files in all subdirectories
jni/CMakeLists.txt - Top-level CMake project that adds SDL as a subproject
jni/SDL/ - (symlink to) directory holding the SDL library files
jni/SDL/Android.mk - Android makefile for creating the SDL shared library
jni/src/ - directory holding your C/C++ source
jni/src/Android.mk - Android makefile that you should customize to include your source code and any library references
jni/src/CMakeLists.txt - CMake file that you may customize to include your source code and any library references
src/main/assets/ - directory holding asset files for your application
src/main/res/ - directory holding resources for your application
src/main/res/mipmap-* - directories holding icons for different phone hardware
src/main/res/values/strings.xml - strings used in your application, including the application name
src/main/java/org/libsdl/app/SDLActivity.java - the Java class handling the initialization and binding to SDL. Be very careful changing this, as the SDL library relies on this implementation. You should instead subclass this for your application.Customizing your application name
To customize your application name, edit AndroidManifest.xml and replace
"org.libsdl.app" with an identifier for your product package.
Then create a Java class extending SDLActivity and place it in a directory
under src matching your package, e.g.
src/com/gamemaker/game/MyGame.javaHere's an example of a minimal class file:
--- MyGame.java --------------------------
package com.gamemaker.game;
import org.libsdl.app.SDLActivity;
/**
* A sample wrapper class that just calls SDLActivity
*/
public class MyGame extends SDLActivity { }
------------------------------------------Then replace "SDLActivity" in AndroidManifest.xml with the name of your
class, .e.g. "MyGame"
Customizing your application icon
Conceptually changing your icon is just replacing the "ic_launcher.png" files in
the drawable directories under the res directory. There are several directories
for different screen sizes.
Loading assets
Any files you put in the "app/src/main/assets" directory of your project
directory will get bundled into the application package and you can load
them using the standard functions in SDL_rwops.h.
There are also a few Android specific functions that allow you to get other
useful paths for saving and loading data:
- SDL_AndroidGetInternalStoragePath()
- SDL_AndroidGetExternalStorageState()
- SDL_AndroidGetExternalStoragePath()
See SDL_system.h for more details on these functions.
The asset packaging system will, by default, compress certain file extensions.
SDL includes two asset file access mechanisms, the preferred one is the so
called "File Descriptor" method, which is faster and doesn't involve the Dalvik
GC, but given this method does not work on compressed assets, there is also the
"Input Stream" method, which is automatically used as a fall back by SDL. You
may want to keep this fact in mind when building your APK, specially when large
files are involved.
For more information on which extensions get compressed by default and how to
disable this behaviour, see for example:
http://ponystyle.com/blog/2010/03/26/dealing-with-asset-compression-in-android-apps/
Pause / Resume behaviour
If SDL_HINT_ANDROID_BLOCK_ON_PAUSE hint is set (the default),
the event loop will block itself when the app is paused (ie, when the user
returns to the main Android dashboard). Blocking is better in terms of battery
use, and it allows your app to spring back to life instantaneously after resume
(versus polling for a resume message).
Upon resume, SDL will attempt to restore the GL context automatically.
In modern devices (Android 3.0 and up) this will most likely succeed and your
app can continue to operate as it was.
However, there's a chance (on older hardware, or on systems under heavy load),
where the GL context can not be restored. In that case you have to listen for
a specific message (SDL_RENDER_DEVICE_RESET) and restore your textures
manually or quit the app.
You should not use the SDL renderer API while the app going in background:
SDL_APP_WILLENTERBACKGROUND:
after you read this message, GL context gets backed-up and you should not
use the SDL renderer API.When this event is received, you have to set the render target to NULL, if you're using it.
(eg call SDL_SetRenderTarget(renderer, NULL))SDL_APP_DIDENTERFOREGROUND:
GL context is restored, and the SDL renderer API is available (unless you
receive SDL_RENDER_DEVICE_RESET).
Mouse / Touch events
In some case, SDL generates synthetic mouse (resp. touch) events for touch
(resp. mouse) devices.
To enable/disable this behavior, see SDL_hints.h:
- SDL_HINT_TOUCH_MOUSE_EVENTS
- SDL_HINT_MOUSE_TOUCH_EVENTS
Misc
For some device, it appears to works better setting explicitly GL attributes
before creating a window:
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
Threads and the Java VM
For a quick tour on how Linux native threads interoperate with the Java VM, take
a look here: https://developer.android.com/guide/practices/jni.html
If you want to use threads in your SDL app, it's strongly recommended that you
do so by creating them using SDL functions. This way, the required attach/detach
handling is managed by SDL automagically. If you have threads created by other
means and they make calls to SDL functions, make sure that you call
Android_JNI_SetupThread() before doing anything else otherwise SDL will attach
your thread automatically anyway (when you make an SDL call), but it'll never
detach it.
If you ever want to use JNI in a native thread (created by "SDL_CreateThread()"),
it won't be able to find your java class and method because of the java class loader
which is different for native threads, than for java threads (eg your "main()").
the work-around is to find class/method, in you "main()" thread, and to use them
in your native thread.
see:
https://developer.android.com/training/articles/perf-jni#faq:-why-didnt-findclass-find-my-class
Using STL
You can use STL in your project by creating an Application.mk file in the jni
folder and adding the following line:
APP_STL := c++_sharedFor more information go here:
https://developer.android.com/ndk/guides/cpp-support
Using the emulator
There are some good tips and tricks for getting the most out of the
emulator here: https://developer.android.com/tools/devices/emulator.html
Especially useful is the info on setting up OpenGL ES 2.0 emulation.
Notice that this software emulator is incredibly slow and needs a lot of disk space.
Using a real device works better.
Troubleshooting
You can see if adb can see any devices with the following command:
adb devicesYou can see the output of log messages on the default device with:
adb logcatYou can push files to the device with:
adb push local_file remote_path_and_fileYou can push files to the SD Card at /sdcard, for example:
adb push moose.dat /sdcard/moose.datYou can see the files on the SD card with a shell command:
adb shell ls /sdcard/You can start a command shell on the default device with:
adb shellYou can remove the library files of your project (and not the SDL lib files) with:
ndk-build cleanYou can do a build with the following command:
ndk-buildYou can see the complete command line that ndk-build is using by passing V=1 on the command line:
ndk-build V=1If your application crashes in native code, you can use ndk-stack to get a symbolic stack trace:
https://developer.android.com/ndk/guides/ndk-stack
If you want to go through the process manually, you can use addr2line to convert the
addresses in the stack trace to lines in your code.
For example, if your crash looks like this:
I/DEBUG ( 31): signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 400085d0
I/DEBUG ( 31): r0 00000000 r1 00001000 r2 00000003 r3 400085d4
I/DEBUG ( 31): r4 400085d0 r5 40008000 r6 afd41504 r7 436c6a7c
I/DEBUG ( 31): r8 436c6b30 r9 435c6fb0 10 435c6f9c fp 4168d82c
I/DEBUG ( 31): ip 8346aff0 sp 436c6a60 lr afd1c8ff pc afd1c902 cpsr 60000030
I/DEBUG ( 31): #00 pc 0001c902 /system/lib/libc.so
I/DEBUG ( 31): #01 pc 0001ccf6 /system/lib/libc.so
I/DEBUG ( 31): #02 pc 000014bc /data/data/org.libsdl.app/lib/libmain.so
I/DEBUG ( 31): #03 pc 00001506 /data/data/org.libsdl.app/lib/libmain.soYou can see that there's a crash in the C library being called from the main code.
I run addr2line with the debug version of my code:
arm-eabi-addr2line -C -f -e obj/local/armeabi/libmain.soand then paste in the number after "pc" in the call stack, from the line that I care about:
000014bc
I get output from addr2line showing that it's in the quit function, in testspriteminimal.c, on line 23.
You can add logging to your code to help show what's happening:
#include <android/log.h>
__android_log_print(ANDROID_LOG_INFO, "foo", "Something happened! x = %d", x);If you need to build without optimization turned on, you can create a file called
"Application.mk" in the jni directory, with the following line in it:
APP_OPTIM := debugMemory debugging
The best (and slowest) way to debug memory issues on Android is valgrind.
Valgrind has support for Android out of the box, just grab code using:
svn co svn://svn.valgrind.org/valgrind/trunk valgrind... and follow the instructions in the file README.android to build it.
One thing I needed to do on Mac OS X was change the path to the toolchain,
and add ranlib to the environment variables:
export RANLIB=$NDKROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-ranlib
Once valgrind is built, you can create a wrapper script to launch your
application with it, changing org.libsdl.app to your package identifier:
--- start_valgrind_app -------------------
#!/system/bin/sh
export TMPDIR=/data/data/org.libsdl.app
exec /data/local/Inst/bin/valgrind --log-file=/sdcard/valgrind.log --error-limit=no $*
------------------------------------------Then push it to the device:
adb push start_valgrind_app /data/localand make it executable:
adb shell chmod 755 /data/local/start_valgrind_appand tell Android to use the script to launch your application:
adb shell setprop wrap.org.libsdl.app "logwrapper /data/local/start_valgrind_app"If the setprop command says "could not set property", it's likely that
your package name is too long and you should make it shorter by changing
AndroidManifest.xml and the path to your class file in android-project/src
You can then launch your application normally and waaaaaaaiiittt for it.
You can monitor the startup process with the logcat command above, and
when it's done (or even while it's running) you can grab the valgrind
output file:
adb pull /sdcard/valgrind.logWhen you're done instrumenting with valgrind, you can disable the wrapper:
adb shell setprop wrap.org.libsdl.app ""Graphics debugging
If you are developing on a compatible Tegra-based tablet, NVidia provides
Tegra Graphics Debugger at their website. Because SDL2 dynamically loads EGL
and GLES libraries, you must follow their instructions for installing the
interposer library on a rooted device. The non-rooted instructions are not
compatible with applications that use SDL2 for video.
The Tegra Graphics Debugger is available from NVidia here:
https://developer.nvidia.com/tegra-graphics-debugger
Why is API level 19 the minimum required?
The latest NDK toolchain doesn't support targeting earlier than API level 19.
As of this writing, according to https://www.composables.com/tools/distribution-chart
about 99.7% of the Android devices accessing Google Play support API level 19 or
higher (August 2023).
A note regarding the use of the "dirty rectangles" rendering technique
If your app uses a variation of the "dirty rectangles" rendering technique,
where you only update a portion of the screen on each frame, you may notice a
variety of visual glitches on Android, that are not present on other platforms.
This is caused by SDL's use of EGL as the support system to handle OpenGL ES/ES2
contexts, in particular the use of the eglSwapBuffers function. As stated in the
documentation for the function "The contents of ancillary buffers are always
undefined after calling eglSwapBuffers".
Setting the EGL_SWAP_BEHAVIOR attribute of the surface to EGL_BUFFER_PRESERVED
is not possible for SDL as it requires EGL 1.4, available only on the API level
17+, so the only workaround available on this platform is to redraw the entire
screen each frame.
Reference: http://www.khronos.org/registry/egl/specs/EGLTechNote0001.html
Ending your application
Two legitimate ways:
return from your main() function. Java side will automatically terminate the
Activity by calling Activity.finish().Android OS can decide to terminate your application by calling onDestroy()
(see Activity life cycle). Your application will receive a SDL_QUIT event you
can handle to save things and quit.
Don't call exit() as it stops the activity badly.
NB: "Back button" can be handled as a SDL_KEYDOWN/UP events, with Keycode
SDLK_AC_BACK, for any purpose.
Known issues
- The number of buttons reported for each joystick is hardcoded to be 36, which
is the current maximum number of buttons Android can report.
Vs Sdl2 Docs README Cmake (`vs/sdl2/docs/README-cmake.md`)
CMake
SDL's build system was traditionally based on autotools. Over time, this
approach has suffered from several issues across the different supported
platforms.
To solve these problems, a new build system based on CMake was introduced.
It is developed in parallel to the legacy autotools build system, so users
can experiment with it without complication.
The CMake build system is supported on the following platforms:
- FreeBSD
- Linux
- Microsoft Visual C
- MinGW and Msys
- macOS, iOS, and tvOS, with support for XCode
- Android
- Emscripten
- RiscOS
- Playstation Vita
Building SDL
Assuming the source for SDL is located at ~/sdl
cd ~
mkdir build
cd build
cmake ~/sdl
cmake --build .This will build the static and dynamic versions of SDL in the ~/build directory.
Installation can be done using:
cmake --install . # '--install' requires CMake 3.15, or newerIncluding SDL in your project
SDL can be included in your project in 2 major ways:
- using a system SDL library, provided by your (*nix) distribution or a package manager
- using a vendored SDL library: this is SDL copied or symlinked in a subfolder.
The following CMake script supports both, depending on the value of MYGAME_VENDORED.
cmake_minimum_required(VERSION 3.5)
project(mygame)
# Create an option to switch between a system sdl library and a vendored sdl library
option(MYGAME_VENDORED "Use vendored libraries" OFF)
if(MYGAME_VENDORED)
add_subdirectory(vendored/sdl EXCLUDE_FROM_ALL)
else()
# 1. Look for a SDL2 package, 2. look for the SDL2 component and 3. fail if none can be found
find_package(SDL2 REQUIRED CONFIG REQUIRED COMPONENTS SDL2)
# 1. Look for a SDL2 package, 2. Look for the SDL2maincomponent and 3. DO NOT fail when SDL2main is not available
find_package(SDL2 REQUIRED CONFIG COMPONENTS SDL2main)
endif()
# Create your game executable target as usual
add_executable(mygame WIN32 mygame.c)
# SDL2::SDL2main may or may not be available. It is e.g. required by Windows GUI applications
if(TARGET SDL2::SDL2main)
# It has an implicit dependency on SDL2 functions, so it MUST be added before SDL2::SDL2 (or SDL2::SDL2-static)
target_link_libraries(mygame PRIVATE SDL2::SDL2main)
endif()
# Link to the actual SDL2 library. SDL2::SDL2 is the shared SDL library, SDL2::SDL2-static is the static SDL libarary.
target_link_libraries(mygame PRIVATE SDL2::SDL2)A system SDL library
For CMake to find SDL, it must be installed in a default location CMake is looking for.
The following components are available, to be used as an argument of find_package.
| Component name | Description |
|---|---|
| SDL2 | The SDL2 shared library, available through the SDL2::SDL2 target [^SDL_TARGET_EXCEPTION] |
| SDL2-static | The SDL2 static library, available through the SDL2::SDL2-static target |
| SDL2main | The SDL2main static library, available through the SDL2::SDL2main target |
| SDL2test | The SDL2test static library, available through the SDL2::SDL2test target |
Using a vendored SDL
This only requires a copy of SDL in a subdirectory.
CMake configuration options for platforms
iOS/tvOS
CMake 3.14+ natively includes support for iOS and tvOS. SDL binaries may be built
using Xcode or Make, possibly among other build-systems.
When using a recent version of CMake (3.14+), it should be possible to:
- build SDL for iOS, both static and dynamic
- build SDL test apps (as iOS/tvOS .app bundles)
- generate a working SDL_config.h for iOS (using SDL_config.h.cmake as a basis)
To use, set the following CMake variables when running CMake's configuration stage:
CMAKE_SYSTEM_NAME=<OS>(eitheriOSortvOS)CMAKE_OSX_SYSROOT=<SDK>(examples:iphoneos,iphonesimulator,iphoneos12.4,/full/path/to/iPhoneOS.sdk,
appletvos,appletvsimulator,appletvos12.4,/full/path/to/AppleTVOS.sdk, etc.)CMAKE_OSX_ARCHITECTURES=<semicolon-separated list of CPU architectures>(example: "arm64;armv7s;x86_64")
Examples
for iOS-Simulator, using the latest, installed SDK:
bashcmake ~/sdl -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_ARCHITECTURES=x86_64for iOS-Device, using the latest, installed SDK, 64-bit only
bashcmake ~/sdl -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos -DCMAKE_OSX_ARCHITECTURES=arm64for iOS-Device, using the latest, installed SDK, mixed 32/64 bit
cmakecmake ~/sdl -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos -DCMAKE_OSX_ARCHITECTURES="arm64;armv7s"for iOS-Device, using a specific SDK revision (iOS 12.4, in this example):
cmakecmake ~/sdl -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos12.4 -DCMAKE_OSX_ARCHITECTURES=arm64for iOS-Simulator, using the latest, installed SDK, and building SDL test apps (as .app bundles):
cmakecmake ~/sdl -DSDL_TESTS=1 -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_ARCHITECTURES=x86_64for tvOS-Simulator, using the latest, installed SDK:
cmakecmake ~/sdl -DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_OSX_SYSROOT=appletvsimulator -DCMAKE_OSX_ARCHITECTURES=x86_64for tvOS-Device, using the latest, installed SDK:
cmakecmake ~/sdl -DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_OSX_SYSROOT=appletvos -DCMAKE_OSX_ARCHITECTURES=arm64`
[^SDL_TARGET_EXCEPTION]: SDL2::SDL2 can be an ALIAS to a static SDL2::SDL2-static target for multiple reasons.
Vs Sdl2 Docs README Directfb (`vs/sdl2/docs/README-directfb.md`)
DirectFB
Supports:
- Hardware YUV overlays
- OpenGL - software only
- 2D/3D accelerations (depends on directfb driver)
- multiple displays
- windows
What you need:
- DirectFB 1.0.1, 1.2.x, 1.3.0
- Kernel-Framebuffer support: required: vesafb, radeonfb ....
- Mesa 7.0.x - optional for OpenGL
The /etc/directfbrc file should contain the following lines to make
your joystick work and avoid crashes:
disable-module=joystick
disable-module=cle266
disable-module=cyber5k
no-linux-input-grabTo disable to use x11 backend when DISPLAY variable is found use
export SDL_DIRECTFB_X11_CHECK=0To disable the use of linux input devices, i.e. multimice/multikeyboard support,
use
export SDL_DIRECTFB_LINUX_INPUT=0To use hardware accelerated YUV-overlays for YUV-textures, use:
export SDL_DIRECTFB_YUV_DIRECT=1This is disabled by default. It will only support one
YUV texture, namely the first. Every other YUV texture will be
rendered in software.
In addition, you may use (directfb-1.2.x)
export SDL_DIRECTFB_YUV_UNDERLAY=1to make the YUV texture an underlay. This will make the cursor to
be shown.
Simple Window Manager
The driver has support for a very, very basic window manager you may
want to use when running with wm=default. Use
export SDL_DIRECTFB_WM=1to enable basic window borders. In order to have the window title rendered,
you need to have the following font installed:
/usr/share/fonts/truetype/freefont/FreeSans.ttfOpenGL Support
The following instructions will give you software OpenGL. However this
works at least on all directfb supported platforms.
As of this writing 20100802 you need to pull Mesa from git and do the following:
git clone git://anongit.freedesktop.org/git/mesa/mesa
cd mesa
git checkout 2c9fdaf7292423c157fc79b5ce43f0f199dd753aEdit configs/linux-directfb so that the Directories-section looks like this:
# Directories
SRC_DIRS = mesa glu
GLU_DIRS = sgi
DRIVER_DIRS = directfb
PROGRAM_DIRS =Then do the following:
make linux-directfb
make
echo Installing - please enter sudo pw.
sudo make install INSTALL_DIR=/usr/local/dfb_GL
cd src/mesa/drivers/directfb
make
sudo make install INSTALL_DIR=/usr/local/dfb_GLTo run the SDL - testprograms:
export SDL_VIDEODRIVER=directfb
export LD_LIBRARY_PATH=/usr/local/dfb_GL/lib
export LD_PRELOAD=/usr/local/dfb_GL/libGL.so.7
./testglVs Sdl2 Docs README Dynapi (`vs/sdl2/docs/README-dynapi.md`)
Dynamic API
Originally posted on Ryan's Google+ account.
Background:
- The Steam Runtime has (at least in theory) a really kick-ass build of SDL2,
but developers are shipping their own SDL2 with individual Steam games.
These games might stop getting updates, but a newer SDL2 might be needed later.
Certainly we'll always be fixing bugs in SDL, even if a new video target isn't
ever needed, and these fixes won't make it to a game shipping its own SDL. - Even if we replace the SDL2 in those games with a compatible one, that is to
say, edit a developer's Steam depot (yuck!), there are developers that are
statically linking SDL2 that we can't do this for. We can't even force the
dynamic loader to ignore their SDL2 in this case, of course. - If you don't ship an SDL2 with the game in some form, people that disabled the
Steam Runtime, or just tried to run the game from the command line instead of
Steam might find themselves unable to run the game, due to a missing dependency. - If you want to ship on non-Steam platforms like GOG or Humble Bundle, or target
generic Linux boxes that may or may not have SDL2 installed, you have to ship
the library or risk a total failure to launch. So now, you might have to have
a non-Steam build plus a Steam build (that is, one with and one without SDL2
included), which is inconvenient if you could have had one universal build
that works everywhere. - We like the zlib license, but the biggest complaint from the open source
community about the license change is the static linking. The LGPL forced this
as a legal, not technical issue, but zlib doesn't care. Even those that aren't
concerned about the GNU freedoms found themselves solving the same problems:
swapping in a newer SDL to an older game often times can save the day.
Static linking stops this dead.
So here's what we did:
SDL now has, internally, a table of function pointers. So, this is what SDL_Init
now looks like:
Uint32 SDL_Init(Uint32 flags)
{
return jump_table.SDL_Init(flags);
}Except that is all done with a bunch of macro magic so we don't have to maintain
every one of these.
What is jump_table.SDL_init()? Eventually, that's a function pointer of the real
SDL_Init() that you've been calling all this time. But at startup, it looks more
like this:
Uint32 SDL_Init_DEFAULT(Uint32 flags)
{
SDL_InitDynamicAPI();
return jump_table.SDL_Init(flags);
}SDL_InitDynamicAPI() fills in jump_table with all the actual SDL function
pointers, which means that this _DEFAULT function never gets called again.
First call to any SDL function sets the whole thing up.
So you might be asking, what was the value in that? Isn't this what the operating
system's dynamic loader was supposed to do for us? Yes, but now we've got this
level of indirection, we can do things like this:
export SDL_DYNAMIC_API=/my/actual/libSDL-2.0.so.0
./MyGameThatIsStaticallyLinkedToSDL2And now, this game that is statically linked to SDL, can still be overridden
with a newer, or better, SDL. The statically linked one will only be used as
far as calling into the jump table in this case. But in cases where no override
is desired, the statically linked version will provide its own jump table,
and everyone is happy.
So now:
- Developers can statically link SDL, and users can still replace it.
(We'd still rather you ship a shared library, though!) - Developers can ship an SDL with their game, Valve can override it for, say,
new features on SteamOS, or distros can override it for their own needs,
but it'll also just work in the default case. - Developers can ship the same package to everyone (Humble Bundle, GOG, etc),
and it'll do the right thing. - End users (and Valve) can update a game's SDL in almost any case,
to keep abandoned games running on newer platforms. - Everyone develops with SDL exactly as they have been doing all along.
Same headers, same ABI. Just get the latest version to enable this magic.
A little more about SDL_InitDynamicAPI():
Internally, InitAPI does some locking to make sure everything waits until a
single thread initializes everything (although even SDL_CreateThread() goes
through here before spinning a thread, too), and then decides if it should use
an external SDL library. If not, it sets up the jump table using the current
SDL's function pointers (which might be statically linked into a program, or in
a shared library of its own). If so, it loads that library and looks for and
calls a single function:
Sint32 SDL_DYNAPI_entry(Uint32 version, void *table, Uint32 tablesize);That function takes a version number (more on that in a moment), the address of
the jump table, and the size, in bytes, of the table.
Now, we've got policy here: this table's layout never changes; new stuff gets
added to the end. Therefore SDL_DYNAPI_entry() knows that it can provide all
the needed functions if tablesize <= sizeof its own jump table. If tablesize is
bigger (say, SDL 2.0.4 is trying to load SDL 2.0.3), then we know to abort, but
if it's smaller, we know we can provide the entire API that the caller needs.
The version variable is a failsafe switch.
Right now it's always 1. This number changes when there are major API changes
(so we know if the tablesize might be smaller, or entries in it have changed).
Right now SDL_DYNAPI_entry gives up if the version doesn't match, but it's not
inconceivable to have a small dispatch library that only supplies this one
function and loads different, otherwise-incompatible SDL libraries and has the
right one initialize the jump table based on the version. For something that
must generically catch lots of different versions of SDL over time, like the
Steam Client, this isn't a bad option.
Finally, I'm sure some people are reading this and thinking,
"I don't want that overhead in my project!"
To which I would point out that the extra function call through the jump table
probably wouldn't even show up in a profile, but lucky you: this can all be
disabled. You can build SDL without this if you absolutely must, but we would
encourage you not to do that. However, on heavily locked down platforms like
iOS, or maybe when debugging, it makes sense to disable it. The way this is
designed in SDL, you just have to change one #define, and the entire system
vaporizes out, and SDL functions exactly like it always did. Most of it is
macro magic, so the system is contained to one C file and a few headers.
However, this is on by default and you have to edit a header file to turn it
off. Our hopes is that if we make it easy to disable, but not too easy,
everyone will ultimately be able to get what they want, but we've gently
nudged everyone towards what we think is the best solution.
Vs Sdl2 Docs README Emscripten (`vs/sdl2/docs/README-emscripten.md`)
Emscripten
The state of things
(As of September 2023, but things move quickly and we don't update this
document often.)
In modern times, all the browsers you probably care about (Chrome, Firefox,
Edge, and Safari, on Windows, macOS, Linux, iOS and Android), support some
reasonable base configurations:
- WebAssembly (don't bother with asm.js any more)
- WebGL (which will look like OpenGL ES 2 or 3 to your app).
- Threads (see caveats, though!)
- Game controllers
- Autoupdating (so you can assume they have a recent version of the browser)
All this to say we're at the point where you don't have to make a lot of
concessions to get even a fairly complex SDL-based game up and running.
RTFM
This document is a quick rundown of some high-level details. The
documentation at emscripten.org is vast
and extremely detailed for a wide variety of topics, and you should at
least skim through it at some point.
Porting your app to Emscripten
Many many things just need some simple adjustments and they'll compile
like any other C/C++ code, as long as SDL was handling the platform-specific
work for your program.
First, you probably need this in at least one of your source files:
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endifSecond: assembly language code has to go. Replace it with C. You can even use
x86 SIMD intrinsic functions in Emscripten!
Third: Middleware has to go. If you have a third-party library you link
against, you either need an Emscripten port of it, or the source code to it
to compile yourself, or you need to remove it.
Fourth: You still start in a function called main(), but you need to get out of
it and into a function that gets called repeatedly, and returns quickly,
called a mainloop.
Somewhere in your program, you probably have something that looks like a more
complicated version of this:
void main(void)
{
initialize_the_game();
while (game_is_still_running) {
check_for_new_input();
think_about_stuff();
draw_the_next_frame();
}
deinitialize_the_game();
}This will not work on Emscripten, because the main thread needs to be free
to do stuff and can't sit in this loop forever. So Emscripten lets you set up
a mainloop.
static void mainloop(void) /* this will run often, possibly at the monitor's refresh rate */
{
if (!game_is_still_running) {
deinitialize_the_game();
#ifdef __EMSCRIPTEN__
emscripten_cancel_main_loop(); /* this should "kill" the app. */
#else
exit(0);
#endif
}
check_for_new_input();
think_about_stuff();
draw_the_next_frame();
}
void main(void)
{
initialize_the_game();
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(mainloop, 0, 1);
#else
while (1) { mainloop(); }
#endif
}Basically, emscripten_set_main_loop(mainloop, 0, 1); says "runmainloop over and over until I end the program." The function will
run, and return, freeing the main thread for other tasks, and then
run again when it's time. The 1 parameter does some magic to make
your main() function end immediately; this is useful because you
don't want any shutdown code that might be sitting below this code
to actually run if main() were to continue on, since we're just
getting started.
There's a lot of little details that are beyond the scope of this
document, but that's the biggest intial set of hurdles to porting
your app to the web.
Do you need threads?
If you plan to use threads, they work on all major browsers now. HOWEVER,
they bring with them a lot of careful considerations. Rendering must
be done on the main thread. This is a general guideline for many
platforms, but a hard requirement on the web.
Many other things also must happen on the main thread; often times SDL
and Emscripten make efforts to "proxy" work to the main thread that
must be there, but you have to be careful (and read more detailed
documentation than this for the finer points).
Even when using threads, your main thread needs to set an Emscripten
mainloop that runs quickly and returns, or things will fail to work
correctly.
You should definitely read Emscripten's pthreads docs
for all the finer points. Mostly SDL's thread API will work as expected,
but is built on pthreads, so it shares the same little incompatibilities
that are documented there, such as where you can use a mutex, and when
a thread will start running, etc.
IMPORTANT: You have to decide to either build something that uses
threads or something that doesn't; you can't have one build
that works everywhere. This is an Emscripten (or maybe WebAssembly?
Or just web browsers in general?) limitation. If you aren't using
threads, it's easier to not enable them at all, at build time.
If you use threads, you have to run from a web server that has
COOP/COEP headers set correctly
or your program will fail to start at all.
If building with threads, __EMSCRIPTEN_PTHREADS__ will be defined
for checking with the C preprocessor, so you can build something
different depending on what sort of build you're compiling.
Audio
Audio works as expected at the API level, but not exactly like other
platforms.
You'll only see a single default audio device. Audio capture also works;
if the browser pops up a prompt to ask for permission to access the
microphone, the SDL_OpenAudioDevice call will succeed and start producing
silence at a regular interval. Once the user approves the request, real
audio data will flow. If the user denies it, the app is not informed and
will just continue to receive silence.
Modern web browsers will not permit web pages to produce sound before the
user has interacted with them (clicked or tapped on them, usually); this is
for several reasons, not the least of which being that no one likes when a
random browser tab suddenly starts making noise and the user has to scramble
to figure out which and silence it.
SDL will allow you to open the audio device for playback in this
circumstance, and your audio callback will fire, but SDL will throw the audio
data away until the user interacts with the page. This helps apps that depend
on the audio callback to make progress, and also keeps audio playback in sync
once the app is finally allowed to make noise.
There are two reasonable ways to deal with the silence at the app level:
if you are writing some sort of media player thing, where the user expects
there to be a volume control when you mouseover the canvas, just default
that control to a muted state; if the user clicks on the control to unmute
it, on this first click, open the audio device. This allows the media to
play at start, and the user can reasonably opt-in to listening.
Many games do not have this sort of UI, and are more rigid about starting
audio along with everything else at the start of the process. For these, your
best bet is to write a little Javascript that puts up a "Click here to play!"
UI, and upon the user clicking, remove that UI and then call the Emscripten
app's main() function. As far as the application knows, the audio device was
available to be opened as soon as the program started, and since this magic
happens in a little Javascript, you don't have to change your C/C++ code at
all to make it happen.
Please see the discussion at https://github.com/libsdl-org/SDL/issues/6385
for some Javascript code to steal for this approach.
Rendering
If you use SDL's 2D render API, it will use GLES2 internally, which
Emscripten will turn into WebGL calls. You can also use OpenGL ES 2
directly by creating a GL context and drawing into it.
Calling SDL_RenderPresent (or SDL_GL_SwapWindow) will not actually
present anything on the screen until your return from your mainloop
function.
Building SDL/emscripten
First: do you really need to build SDL from source?
If you aren't developing SDL itself, have a desire to mess with its source
code, or need something on the bleeding edge, don't build SDL. Just use
Emscripten's packaged version!
Compile and link your app with -sUSE_SDL=2 and it'll use a build of
SDL packaged with Emscripten. This comes from the same source code and
fixes the Emscripten project makes to SDL are generally merged into SDL's
revision control, so often this is much easier for app developers.
-sUSE_SDL=1 will select Emscripten's JavaScript reimplementation of SDL
1.2 instead; if you need SDL 1.2, this might be fine, but we generally
recommend you don't use SDL 1.2 in modern times.
If you want to build SDL, though...
SDL currently requires at least Emscripten 3.1.35 to build. Newer versions
are likely to work, as well.
Build:
This works on Linux/Unix and macOS. Please send comments about Windows.
Make sure you've installed emsdk
first, and run source emsdk_env.sh at the command line so it finds the
tools.
(These configure options might be overkill, but this has worked for me.)
cd SDL
mkdir build
cd build
emconfigure ../configure --host=wasm32-unknown-emscripten --disable-pthreads --disable-assembly --disable-cpuinfo CFLAGS="-sUSE_SDL=0 -O3"
emmake make -j4If you want to build with thread support, something like this works:
emconfigure ../configure --host=wasm32-unknown-emscripten --enable-pthreads --disable-assembly --disable-cpuinfo CFLAGS="-sUSE_SDL=0 -O3 -pthread" LDFLAGS="-pthread"Or with cmake:
mkdir build
cd build
emcmake cmake ..
emmake make -j4To build one of the tests:
cd test/
emcc -O2 --js-opts 0 -g4 testdraw2.c -I../include ../build/.libs/libSDL2.a ../build/libSDL2_test.a -o a.htmlBuilding your app
You need to compile with emcc instead of gcc or clang or whatever, but
mostly it uses the same command line arguments as Clang.
Link against the SDL/build/.libs/libSDL2.a file you generated by building SDL,
link with -sUSE_SDL=2 to use Emscripten's prepackaged SDL2 build.
Usually you would produce a binary like this:
gcc -o mygame mygame.c # or whateverBut for Emscripten, you want to output something else:
emcc -o index.html mygame.cThis will produce several files...support Javascript and WebAssembly (.wasm)
files. The -o index.html will produce a simple HTML page that loads and
runs your app. You will (probably) eventually want to replace or customize
that file and do -o index.js instead to just build the code pieces.
If you're working on a program of any serious size, you'll likely need to
link with -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=1gb to get access
to more memory. If using pthreads, you'll need the -sMAXIMUM_MEMORY=1gb
or the app will fail to start on iOS browsers, but this might be a bug that
goes away in the future.
Data files
Your game probably has data files. Here's how to access them.
Filesystem access works like a Unix filesystem; you have a single directory
tree, possibly interpolated from several mounted locations, no drive letters,
'/' for a path separator. You can access them with standard file APIs like
open() or fopen() or SDL_RWops. You can read or write from the filesystem.
By default, you probably have a "MEMFS" filesystem (all files are stored in
memory, but access to them is immediate and doesn't need to block). There are
other options, like "IDBFS" (files are stored in a local database, so they
don't need to be in RAM all the time and they can persist between runs of the
program, but access is not synchronous). You can mix and match these file
systems, mounting a MEMFS filesystem at one place and idbfs elsewhere, etc,
but that's beyond the scope of this document. Please refer to Emscripten's
page on the topic
for more info.
The easiest (but not the best) way to get at your data files is to embed
them in the app itself. Emscripten's linker has support for automating this.
emcc -o index.html loopwave.c --embed-file=../test/sample.wav@/sounds/sample.wavThis will pack ../test/sample.wav in your app, and make it available at
"/sounds/sample.wav" at runtime. Emscripten makes sure this data is available
before your main() function runs, and since it's in MEMFS, you can just
read it like you do on other platforms. --embed-file can also accept a
directory to pack an entire tree, and you can specify the argument multiple
times to pack unrelated things into the final installation.
Note that this is absolutely the best approach if you have a few small
files to include and shouldn't worry about the issue further. However, if you
have hundreds of megabytes and/or thousands of files, this is not so great,
since the user will download it all every time they load your page, and it
all has to live in memory at runtime.
Emscripten's documentation on the matter
gives other options and details, and is worth a read.
Debugging
Debugging web apps is a mixed bag. You should compile and link with-gsource-map, which embeds a ton of source-level debugging information into
the build, and make sure the app source code is available on the web server,
which is often a scary proposition for various reasons.
When you debug from the browser's tools and hit a breakpoint, you can step
through the actual C/C++ source code, though, which can be nice.
If you try debugging in Firefox and it doesn't work well for no apparent
reason, try Chrome, and vice-versa. These tools are still relatively new,
and improving all the time.
SDL_Log() (or even plain old printf) will write to the Javascript console,
and honestly I find printf-style debugging to be easier than setting up a build
for proper debugging, so use whatever tools work best for you.
Questions?
Please give us feedback on this document at the SDL bug tracker.
If something is wrong or unclear, we want to know!
Vs Sdl2 Docs README Gdk (`vs/sdl2/docs/README-gdk.md`)
GDK
This port allows SDL applications to run via Microsoft's Game Development Kit (GDK).
Windows (GDK) and Xbox One/Xbox Series (GDKX) are both supported and all the required code is included in this public SDL release. However, only licensed Xbox developers have access to the GDKX libraries which will allow you to build the Xbox targets.
Requirements
- Microsoft Visual Studio 2022 (in theory, it should also work in 2017 or 2019, but this has not been tested)
- Microsoft GDK June 2022 or newer (public release here)
- For Xbox, you will need the corresponding GDKX version (licensed developers only)
- To publish a package or successfully authenticate a user, you will need to create an app id/configure services in Partner Center. However, for local testing purposes (without authenticating on Xbox Live), the identifiers used by the GDK test programs in the included solution will work.
Windows GDK Status
The Windows GDK port supports the full set of Win32 APIs, renderers, controllers, input devices, etc., as the normal Windows x64 build of SDL.
Additionally, the GDK port adds the following:
Compile-time platform detection for SDL programs. The
__GDK__is#defined on every GDK platform, and the__WINGDK__is#defined on Windows GDK, specifically. (This distinction exists because other GDK platforms support a smaller subset of functionality. This allows you to mark code for "any" GDK separate from Windows GDK.)GDK-specific setup:
- Initializing/uninitializing the game runtime, and initializing Xbox Live services
- Creating a global task queue and setting it as the default for the process. When running any async operations, passing in
NULLas the task queue will make the task get added to the global task queue.
An implementation on
WinMainthat performs the above GDK setup (you should link against SDL2main.lib, as in Windows x64). If you are unable to do this, you can instead manually callSDL_GDKRunAppfrom your entry point, passing in yourSDL_mainfunction andNULLas the parameters.Global task queue callbacks are dispatched during
SDL_PumpEvents(which is also called internally if usingSDL_PollEvent).You can get the handle of the global task queue through
SDL_GDKGetTaskQueue, if needed. When done with the queue, be sure to useXTaskQueueCloseHandleto decrement the reference count (otherwise it will cause a resource leak).
Single-player games have some additional features available:
- Call
SDL_GDKGetDefaultUserto get the default XUserHandle pointer. SDL_GetPrefPathstill works, but only for single-player titles.
These functions mostly wrap around async APIs, and thus should be treated as synchronous alternatives. Also note that the single-player functions return on any OS errors, so be sure to validate the return values!
- Call
What doesn't work:
- Compilation with anything other than through the included Visual C++ solution file
VisualC-GDK Solution
The included VisualC-GDK/SDL.sln solution includes the following targets for the Gaming.Desktop.x64 configuration:
- SDL2 (DLL) - This is the typical SDL2.dll, but for Gaming.Desktop.x64.
- SDL2main (lib) - This contains a drop-in implementation of
WinMainthat is used as the entry point for GDK programs. - tests/testgamecontroller - Standard SDL test program demonstrating controller functionality.
- tests/testgdk - GDK-specific test program that demonstrates using the global task queue to login a user into Xbox Live.
NOTE: As of the June 2022 GDK, you cannot test user logins without a valid Title ID and MSAAppId. You will need to manually change the identifiers in theMicrosoftGame.configto your valid IDs from Partner Center if you wish to test this. - tests/testsprite2 - Standard SDL test program demonstrating sprite drawing functionality.
If you set one of the test programs as a startup project, you can run it directly from Visual Studio.
Windows GDK Setup, Detailed Steps
These steps assume you already have a game using SDL that runs on Windows x64 along with a corresponding Visual Studio solution file for the x64 version. If you don't have this, it's easiest to use one of the test program vcxproj files in the VisualC-GDK directory as a starting point, though you will still need to do most of the steps below.
1. Add a Gaming.Desktop.x64 Configuration
In your game's existing Visual Studio Solution, go to Build > Configuration Manager. From the "Active solution platform" drop-down select "New...". From the drop-down list, select Gaming.Desktop.x64 and copy the settings from the x64 configuration.
2. Build SDL2 and SDL2main for GDK
Open VisualC-GDK/SDL.sln in Visual Studio, you need to build the SDL2 and SDL2main targets for the Gaming.Desktop.x64 platform (Release is recommended). You will need to copy/keep track of the SDL2.dll, XCurl.dll (which is output by Gaming.Desktop.x64), SDL2.lib, and SDL2main.lib output files for your game project.
Alternatively, you could setup your solution file to instead reference the SDL2/SDL2main project file targets from the SDL source, and add those projects as a dependency. This would mean that SDL2 and SDL2main would both be built when your game is built.
3. Configuring Project Settings
While the Gaming.Desktop.x64 configuration sets most of the required settings, there are some additional items to configure for your game project under the Gaming.Desktop.x64 Configuration:
- Under C/C++ > General > Additional Include Directories, make sure the
SDL/includepath is referenced - Under Linker > General > Additional Library Directories, make sure to reference the path where the newly-built SDL2.lib and SDL2main.lib are
- Under Linker > Input > Additional Dependencies, you need the following:
SDL2.libSDL2main.lib(unless not using)xgameruntime.lib../Microsoft.Xbox.Services.141.GDK.C.Thunks.lib
- Note that in general, the GDK libraries depend on the MSVC C/C++ runtime, so there is no way to remove this dependency from a GDK program that links against GDK.
4. Setting up SDL_main
Rather than using your own implementation of WinMain, it's recommended that you instead #include "SDL_main.h" and declare a standard main function. If you are unable to do this, you can instead manually call SDL_GDKRunApp from your entry point, passing in your SDL_main function and NULL as the parameters.
5. Required DLLs
The game will not launch in the debugger unless required DLLs are included in the directory that contains the game's .exe file. You need to make sure that the following files are copied into the directory:
- Your SDL2.dll
- "$(Console_GRDKExtLibRoot)Xbox.Services.API.C\DesignTime\CommonConfiguration\Neutral\Lib\Release\Microsoft.Xbox.Services.141.GDK.C.Thunks.dll"
- XCurl.dll
You can either copy these in a post-build step, or you can add the dlls into the project and set its Configuration Properties > General > Item type to "Copy file," which will also copy them into the output directory.
6. Setting up MicrosoftGame.config
You can copy VisualC-GDK/tests/testgdk/MicrosoftGame.config and use that as a starting point in your project. Minimally, you will want to change the Executable Name attribute, the DefaultDisplayName, and the Description.
This file must be copied into the same directory as the game's .exe file. As with the DLLs, you can either use a post-build step or the "Copy file" item type.
For basic testing, you do not need to change anything else in MicrosoftGame.config. However, if you want to test any Xbox Live services (such as logging in users) or publish a package, you will need to setup a Game app on Partner Center.
Then, you need to set the following values to the values from Partner Center:
- Identity tag - Name and Publisher attributes
- TitleId
- MSAAppId
7. Adding Required Logos
Several logo PNG files are required to be able to launch the game, even from the debugger. You can use the sample logos provided in VisualC-GDK/logos. As with the other files, they must be copied into the same directory as the game's .exe file.
8. Copying any Data Files
When debugging GDK games, there is no way to specify a working directory. Therefore, any required game data must also be copied into the output directory, likely in a post-build step.
9. Build and Run from Visual Studio
At this point, you should be able to build and run your game from the Visual Studio Debugger. If you get any linker errors, make sure you double-check that you referenced all the required libs.
If you are testing Xbox Live functionality, it's likely you will need to change to the Sandbox for your title. To do this:
- Run "Desktop VS 2022 Gaming Command Prompt" from the Start Menu
- Switch the sandbox name with:
XblPCSandbox SANDBOX.# - (To switch back to the retail sandbox):
XblPCSandbox RETAIL
10. Packaging and Installing Locally
You can use one of the test program's PackageLayout.xml as a starting point. Minimally, you will need to change the exe to the correct name and also reference any required game data. As with the other data files, it's easiest if you have this copy to the output directory, although it's not a requirement as you can specify relative paths to files.
To create the package:
- Run "Desktop VS 2022 Gaming Command Prompt" from the Start Menu
cdto the directory containing thePackageLayout.xmlwith the correct paths (if you use the local path as in the sample package layout, this would be from your .exe output directory)mkdir Packageto create an output directory- To package the file into the
Packagedirectory, use:
makepkg pack /f PackageLayout.xml /lt /d . /nogameos /pc /pd Package - To install the package, use:
wdapp install PACKAGENAME.msixvc - Once the package is installed, you can run it from the start menu.
- As with when running from Visual Studio, if you need to test any Xbox Live functionality you must switch to the correct sandbox.
Xbox GDKX Setup
In general, the same process in the Windows GDK instructions work. There are just a few additional notes:
- For Xbox One consoles, use the Gaming.Xbox.XboxOne.x64 target
- For Xbox Series consoles, use the Gaming.Xbox.Scarlett.x64 target
- The Xbox One target sets the
__XBOXONE__define and the Xbox Series target sets the__XBOXSERIES__define - You don't need to link against the Xbox.Services Thunks lib nor include that dll in your package (it doesn't exist for Xbox)
- The shader blobs for Xbox are created in a pre-build step for the Xbox targets, rather than included in the source (due to NDA and version compatability reasons)
- To create a package, use:
makepkg pack /f PackageLayout.xml /lt /d . /pd Package - To install the package, use:
xbapp install [PACKAGE].xvc - For some reason, if you make changes that require SDL2.dll to build, and you are running through the debugger (instead of a package), you have to rebuild your .exe target for the debugger to recognize the dll has changed and needs to be transferred to the console again
- While there are successful releases of Xbox titles using this port, it is not as extensively tested as other targets
Troubleshooting
Xbox Live Login does not work
As of June 2022 GDK, you must have a valid Title Id and MSAAppId in order to test Xbox Live functionality such as user login. Make sure these are set correctly in the MicrosoftGame.config. This means that even testgdk will not let you login without setting these properties to valid values.
Furthermore, confirm that your PC is set to the correct sandbox.
"The current user has already installed an unpackaged version of this app. A packaged version cannot replace this." error when installing
Prior to June 2022 GDK, running from the Visual Studio debugger would still locally register the app (and it would appear on the start menu). To fix this, you have to uninstall it (it's simplest to right click on it from the start menu to uninstall it).
Vs Sdl2 Docs README Gesture (`vs/sdl2/docs/README-gesture.md`)
Dollar Gestures
SDL provides an implementation of the $1 gesture recognition system. This allows for recording, saving, loading, and performing single stroke gestures.
Gestures can be performed with any number of fingers (the centroid of the fingers must follow the path of the gesture), but the number of fingers must be constant (a finger cannot go down in the middle of a gesture). The path of a gesture is considered the path from the time when the final finger went down, to the first time any finger comes up.
Dollar gestures are assigned an Id based on a hash function. This is guaranteed to remain constant for a given gesture. There is a (small) chance that two different gestures will be assigned the same ID. In this case, simply re-recording one of the gestures should result in a different ID.
Recording:
To begin recording on a touch device call:
SDL_RecordGesture(SDL_TouchID touchId), where touchId is the id of the touch device you wish to record on, or -1 to record on all connected devices.
Recording terminates as soon as a finger comes up. Recording is acknowledged by an SDL_DOLLARRECORD event.
A SDL_DOLLARRECORD event is a dgesture with the following fields:
- event.dgesture.touchId - the Id of the touch used to record the gesture.
- event.dgesture.gestureId - the unique id of the recorded gesture.
Performing:
As long as there is a dollar gesture assigned to a touch, every finger-up event will also cause an SDL_DOLLARGESTURE event with the following fields:
- event.dgesture.touchId - the Id of the touch which performed the gesture.
- event.dgesture.gestureId - the unique id of the closest gesture to the performed stroke.
- event.dgesture.error - the difference between the gesture template and the actual performed gesture. Lower error is a better match.
- event.dgesture.numFingers - the number of fingers used to draw the stroke.
Most programs will want to define an appropriate error threshold and check to be sure that the error of a gesture is not abnormally high (an indicator that no gesture was performed).
Saving:
To save a template, call SDL_SaveDollarTemplate(gestureId, dst) where gestureId is the id of the gesture you want to save, and dst is an SDL_RWops pointer to the file where the gesture will be stored.
To save all currently loaded templates, call SDL_SaveAllDollarTemplates(dst) where dst is an SDL_RWops pointer to the file where the gesture will be stored.
Both functions return the number of gestures successfully saved.
Loading:
To load templates from a file, call SDL_LoadDollarTemplates(touchId,src) where touchId is the id of the touch to load to (or -1 to load to all touch devices), and src is an SDL_RWops pointer to a gesture save file.
SDL_LoadDollarTemplates returns the number of templates successfully loaded.
=========================================================================== Multi Gestures
SDL provides simple support for pinch/rotate/swipe gestures.
Every time a finger is moved an SDL_MULTIGESTURE event is sent with the following fields:
- event.mgesture.touchId - the Id of the touch on which the gesture was performed.
- event.mgesture.x - the normalized x coordinate of the gesture. (0..1)
- event.mgesture.y - the normalized y coordinate of the gesture. (0..1)
- event.mgesture.dTheta - the amount that the fingers rotated during this motion.
- event.mgesture.dDist - the amount that the fingers pinched during this motion.
- event.mgesture.numFingers - the number of fingers used in the gesture.
=========================================================================== Notes
For a complete example see test/testgesture.c
Please direct questions/comments to:
[email protected]
Vs Sdl2 Docs README Git (`vs/sdl2/docs/README-git.md`)
git
The latest development version of SDL is available via git.
Git allows you to get up-to-the-minute fixes and enhancements;
as a developer works on a source tree, you can use "git" to mirror that
source tree instead of waiting for an official release. Please look
at the Git website ( https://git-scm.com/ ) for more
information on using git, where you can also download software for
macOS, Windows, and Unix systems.
git clone https://github.com/libsdl-org/SDLIf you are building SDL via configure, you will need to run autogen.sh
before running configure.
There is a web interface to the Git repository at:
http://github.com/libsdl-org/SDL/
Vs Sdl2 Docs README Hg (`vs/sdl2/docs/README-hg.md`)
We are no longer hosted in Mercurial. Please see README-git.md for details.
Thanks!
Vs Sdl2 Docs README Ios (`vs/sdl2/docs/README-ios.md`)
iOS
Building the Simple DirectMedia Layer for iOS 9.0+
Requirements: Mac OS X 10.9 or later and the iOS 9.0 or newer SDK.
Instructions:
- Open SDL.xcodeproj (located in Xcode/SDL) in Xcode.
- Select your desired target, and hit build.
Using the Simple DirectMedia Layer for iOS
- Run Xcode and create a new project using the iOS Game template, selecting the Objective C language and Metal game technology.
- In the main view, delete all files except for Assets and LaunchScreen
- Right click the project in the main view, select "Add Files...", and add the SDL project, Xcode/SDL/SDL.xcodeproj
- Select the project in the main view, go to the "Info" tab and under "Custom iOS Target Properties" remove the line "Main storyboard file base name"
- Select the project in the main view, go to the "Build Settings" tab, select "All", and edit "Header Search Path" and drag over the SDL "Public Headers" folder from the left
- Select the project in the main view, go to the "Build Phases" tab, select "Link Binary With Libraries", and add SDL2.framework from "Framework-iOS"
- Select the project in the main view, go to the "General" tab, scroll down to "Frameworks, Libraries, and Embedded Content", and select "Embed & Sign" for the SDL library.
- In the main view, expand SDL -> Library Source -> main -> uikit and drag SDL_uikit_main.c into your game files
- Add the source files that you would normally have for an SDL program, making sure to have #include "SDL.h" at the top of the file containing your main() function.
- Add any assets that your application needs.
- Enjoy!
TODO: Add information regarding App Store requirements such as icons, etc.
Notes -- Retina / High-DPI and window sizes
Window and display mode sizes in SDL are in "screen coordinates" (or "points",
in Apple's terminology) rather than in pixels. On iOS this means that a window
created on an iPhone 6 will have a size in screen coordinates of 375 x 667,
rather than a size in pixels of 750 x 1334. All iOS apps are expected to
size their content based on screen coordinates / points rather than pixels,
as this allows different iOS devices to have different pixel densities
(Retina versus non-Retina screens, etc.) without apps caring too much.
By default SDL will not use the full pixel density of the screen on
Retina/high-dpi capable devices. Use the SDL_WINDOW_ALLOW_HIGHDPI flag when
creating your window to enable high-dpi support.
When high-dpi support is enabled, SDL_GetWindowSize() and display mode sizes
will still be in "screen coordinates" rather than pixels, but the window will
have a much greater pixel density when the device supports it, and the
SDL_GL_GetDrawableSize() or SDL_GetRendererOutputSize() functions (depending on
whether raw OpenGL or the SDL_Render API is used) can be queried to determine
the size in pixels of the drawable screen framebuffer.
Some OpenGL ES functions such as glViewport expect sizes in pixels rather than
sizes in screen coordinates. When doing 2D rendering with OpenGL ES, an
orthographic projection matrix using the size in screen coordinates
(SDL_GetWindowSize()) can be used in order to display content at the same scale
no matter whether a Retina device is used or not.
Notes -- Application events
On iOS the application goes through a fixed life cycle and you will get
notifications of state changes via application events. When these events
are delivered you must handle them in an event callback because the OS may
not give you any processing time after the events are delivered.
e.g.
int HandleAppEvents(void *userdata, SDL_Event *event)
{
switch (event->type)
{
case SDL_APP_TERMINATING:
/* Terminate the app.
Shut everything down before returning from this function.
*/
return 0;
case SDL_APP_LOWMEMORY:
/* You will get this when your app is paused and iOS wants more memory.
Release as much memory as possible.
*/
return 0;
case SDL_APP_WILLENTERBACKGROUND:
/* Prepare your app to go into the background. Stop loops, etc.
This gets called when the user hits the home button, or gets a call.
*/
return 0;
case SDL_APP_DIDENTERBACKGROUND:
/* This will get called if the user accepted whatever sent your app to the background.
If the user got a phone call and canceled it, you'll instead get an SDL_APP_DIDENTERFOREGROUND event and restart your loops.
When you get this, you have 5 seconds to save all your state or the app will be terminated.
Your app is NOT active at this point.
*/
return 0;
case SDL_APP_WILLENTERFOREGROUND:
/* This call happens when your app is coming back to the foreground.
Restore all your state here.
*/
return 0;
case SDL_APP_DIDENTERFOREGROUND:
/* Restart your loops here.
Your app is interactive and getting CPU again.
*/
return 0;
default:
/* No special processing, add it to the event queue */
return 1;
}
}
int main(int argc, char *argv[])
{
SDL_SetEventFilter(HandleAppEvents, NULL);
... run your main loop
return 0;
}Notes -- Accelerometer as Joystick
SDL for iPhone supports polling the built in accelerometer as a joystick device. For an example on how to do this, see the accelerometer.c in the demos directory.
The main thing to note when using the accelerometer with SDL is that while the iPhone natively reports accelerometer as floating point values in units of g-force, SDL_JoystickGetAxis() reports joystick values as signed integers. Hence, in order to convert between the two, some clamping and scaling is necessary on the part of the iPhone SDL joystick driver. To convert SDL_JoystickGetAxis() reported values BACK to units of g-force, simply multiply the values by SDL_IPHONE_MAX_GFORCE / 0x7FFF.
Notes -- OpenGL ES
Your SDL application for iOS uses OpenGL ES for video by default.
OpenGL ES for iOS supports several display pixel formats, such as RGBA8 and RGB565, which provide a 32 bit and 16 bit color buffer respectively. By default, the implementation uses RGB565, but you may use RGBA8 by setting each color component to 8 bits in SDL_GL_SetAttribute().
If your application doesn't use OpenGL's depth buffer, you may find significant performance improvement by setting SDL_GL_DEPTH_SIZE to 0.
Finally, if your application completely redraws the screen each frame, you may find significant performance improvement by setting the attribute SDL_GL_RETAINED_BACKING to 0.
OpenGL ES on iOS doesn't use the traditional system-framebuffer setup provided in other operating systems. Special care must be taken because of this:
- The drawable Renderbuffer must be bound to the GL_RENDERBUFFER binding point when SDL_GL_SwapWindow() is called.
- The drawable Framebuffer Object must be bound while rendering to the screen and when SDL_GL_SwapWindow() is called.
- If multisample antialiasing (MSAA) is used and glReadPixels is used on the screen, the drawable framebuffer must be resolved to the MSAA resolve framebuffer (via glBlitFramebuffer or glResolveMultisampleFramebufferAPPLE), and the MSAA resolve framebuffer must be bound to the GL_READ_FRAMEBUFFER binding point, before glReadPixels is called.
The above objects can be obtained via SDL_GetWindowWMInfo() (in SDL_syswm.h).
Notes -- Keyboard
The SDL keyboard API has been extended to support on-screen keyboards:
void SDL_StartTextInput()
-- enables text events and reveals the onscreen keyboard.
void SDL_StopTextInput()
-- disables text events and hides the onscreen keyboard.
SDL_bool SDL_IsTextInputActive()
-- returns whether or not text events are enabled (and the onscreen keyboard is visible)
Notes -- Mouse
iOS now supports Bluetooth mice on iPad, but by default will provide the mouse input as touch. In order for SDL to see the real mouse events, you should set the key UIApplicationSupportsIndirectInputEvents to true in your Info.plist
Notes -- Reading and Writing files
Each application installed on iPhone resides in a sandbox which includes its own Application Home directory. Your application may not access files outside this directory.
Once your application is installed its directory tree looks like:
MySDLApp Home/
MySDLApp.app
Documents/
Library/
Preferences/
tmp/When your SDL based iPhone application starts up, it sets the working directory to the main bundle (MySDLApp Home/MySDLApp.app), where your application resources are stored. You cannot write to this directory. Instead, I advise you to write document files to "../Documents/" and preferences to "../Library/Preferences".
More information on this subject is available here:
http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Introduction/Introduction.html
Notes -- xcFramework
The SDL.xcodeproj file now includes a target to build SDL2.xcframework. An xcframework is a new (Xcode 11) uber-framework which can handle any combination of processor type and target OS platform.
In the past, iOS devices were always an ARM variant processor, and the simulator was always i386 or x86_64, and thus libraries could be combined into a single framework for both simulator and device. With the introduction of the Apple Silicon ARM-based machines, regular frameworks would collide as CPU type was no longer sufficient to differentiate the platform. So Apple created the new xcframework library package.
The xcframework target builds into a Products directory alongside the SDL.xcodeproj file, as SDL2.xcframework. This can be brought in to any iOS project and will function properly for both simulator and device, no matter their CPUs. Note that Intel Macs cannot cross-compile for Apple Silicon Macs. If you need AS compatibility, perform this build on an Apple Silicon Mac.
This target requires Xcode 11 or later. The target will simply fail to build if attempted on older Xcodes.
In addition, on Apple platforms, main() cannot be in a dynamically loaded library. This means that iOS apps which used the statically-linked libSDL2.lib and now link with the xcframwork will need to define their own main() to call SDL_UIKitRunApp(), like this:
#ifndef SDL_MAIN_HANDLED
#ifdef main
#undef main
#endif
int
main(int argc, char argv[])
{
return SDL_UIKitRunApp(argc, argv, SDL_main);
}
#endif / !SDL_MAIN_HANDLED */
Using an xcFramework is similar to using a regular framework. However, issues have been seen with the build system not seeing the headers in the xcFramework. To remedy this, add the path to the xcFramework in your app's target ==> Build Settings ==> Framework Search Paths and mark it recursive (this is critical). Also critical is to remove "*.framework" from Build Settings ==> Sub-Directories to Exclude in Recursive Searches. Clean the build folder, and on your next build the build system should be able to see any of these in your code, as expected:
#include "SDL_main.h"
#include <SDL.h>
#include <SDL_main.h>
Notes -- iPhone SDL limitations
Windows:
Full-size, single window applications only. You cannot create multi-window SDL applications for iPhone OS. The application window will fill the display, though you have the option of turning on or off the menu-bar (pass SDL_CreateWindow() the flag SDL_WINDOW_BORDERLESS).
Textures:
The optimal texture formats on iOS are SDL_PIXELFORMAT_ABGR8888, SDL_PIXELFORMAT_ABGR8888, SDL_PIXELFORMAT_BGR888, and SDL_PIXELFORMAT_RGB24 pixel formats.
Loading Shared Objects:
This is disabled by default since it seems to break the terms of the iOS SDK agreement for iOS versions prior to iOS 8. It can be re-enabled in SDL_config_iphoneos.h.
Notes -- CoreBluetooth.framework
SDL_JOYSTICK_HIDAPI is disabled by default. It can give you access to a lot
more game controller devices, but it requires permission from the user before
your app will be able to talk to the Bluetooth hardware. "Made For iOS"
branded controllers do not need this as we don't have to speak to them
directly with raw bluetooth, so many apps can live without this.
You'll need to link with CoreBluetooth.framework and add something like this
to your Info.plist:
Game Center
Game Center integration might require that you break up your main loop in order to yield control back to the system. In other words, instead of running an endless main loop, you run each frame in a callback function, using:
int SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, SDL_iOSAnimationCallback callback, void *callbackParam);This will set up the given function to be called back on the animation callback, and then you have to return from main() to let the Cocoa event loop run.
e.g.
extern "C"
void ShowFrame(void*)
{
... do event handling, frame logic and rendering ...
}
int main(int argc, char *argv[])
{
... initialize game ...
#ifdef __IPHONEOS__
// Initialize the Game Center for scoring and matchmaking
InitGameCenter();
// Set up the game to run in the window animation callback on iOS
// so that Game Center and so forth works correctly.
SDL_iPhoneSetAnimationCallback(window, 1, ShowFrame, NULL);
#else
while ( running ) {
ShowFrame(0);
DelayFrame();
}
#endif
return 0;
}Deploying to older versions of iOS
SDL supports deploying to older versions of iOS than are supported by the latest version of Xcode, all the way back to iOS 8.0
In order to do that you need to download an older version of Xcode:
https://developer.apple.com/download/more/?name=Xcode
Open the package contents of the older Xcode and your newer version of Xcode and copy over the folders in Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport
Then open the file Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/SDKSettings.plist and add the versions of iOS you want to deploy to the key Root/DefaultProperties/DEPLOYMENT_TARGET_SUGGESTED_VALUES
Open your project and set your deployment target to the desired version of iOS
Finally, remove GameController from the list of frameworks linked by your application and edit the build settings for "Other Linker Flags" and add -weak_framework GameController
Vs Sdl2 Docs README Kmsbsd (`vs/sdl2/docs/README-kmsbsd.md`)
KMSDRM on *BSD
KMSDRM is supported on FreeBSD and OpenBSD. DragonFlyBSD works but requires being a root user. NetBSD isn't supported yet because the application will crash when creating the KMSDRM screen.
WSCONS support has been brought back, but only as an input backend. It will not be brought back as a video backend to ease maintenance.
OpenBSD note: Note that the video backend assumes that the user has read/write permissions to the /dev/drm* devices.
SDL2 WSCONS input backend features
- It is keymap-aware; it will work properly with different keymaps.
- It has mouse support.
- Accent input is supported.
- Compose keys are supported.
- AltGr and Meta Shift keys work as intended.
Partially working or no input on OpenBSD/NetBSD.
The WSCONS input backend needs read/write access to the /dev/wskbd* devices, without which it will not work properly. /dev/wsmouse must also be read/write accessible, otherwise mouse input will not work.
Partially working or no input on FreeBSD.
The evdev devices are only accessible to the root user by default. Edit devfs rules to allow access to such devices. The /dev/kbd* devices are also only accessible to the root user by default. Edit devfs rules to allow access to such devices.
Vs Sdl2 Docs README Linux (`vs/sdl2/docs/README-linux.md`)
Linux
By default SDL will only link against glibc, the rest of the features will be
enabled dynamically at runtime depending on the available features on the target
system. So, for example if you built SDL with XRandR support and the target
system does not have the XRandR libraries installed, it will be disabled
at runtime, and you won't get a missing library error, at least with the
default configuration parameters.
Build Dependencies
Ubuntu 18.04, all available features enabled:
sudo apt-get install build-essential git make autoconf automake libtool \
pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev \
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libwayland-dev \
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-devUbuntu 22.04+ can also add libpipewire-0.3-dev libdecor-0-dev to that command line.
Fedora 35, all available features enabled:
sudo yum install gcc git-core make cmake autoconf automake libtool \
alsa-lib-devel pulseaudio-libs-devel nas-devel pipewire-devel \
libX11-devel libXext-devel libXrandr-devel libXcursor-devel libXfixes-devel \
libXi-devel libXScrnSaver-devel dbus-devel ibus-devel fcitx-devel \
systemd-devel mesa-libGL-devel libxkbcommon-devel mesa-libGLES-devel \
mesa-libEGL-devel vulkan-devel wayland-devel wayland-protocols-devel \
libdrm-devel mesa-libgbm-devel libusb-devel libdecor-devel \
libsamplerate-devel pipewire-jack-audio-connection-kit-devel \NOTES:
- This includes all the audio targets except arts and esd, because Ubuntu
(and/or Debian) pulled their packages, but in theory SDL still supports them.
The sndio audio target is also unavailable on Fedora. - libsamplerate0-dev lets SDL optionally link to libresamplerate at runtime
for higher-quality audio resampling. SDL will work without it if the library
is missing, so it's safe to build in support even if the end user doesn't
have this library installed. - DirectFB isn't included because the configure script (currently) fails to find
it at all. You can do "sudo apt-get install libdirectfb-dev" and fix the
configure script to include DirectFB support. Send patches. :)
Joystick does not work
If you compiled or are using a version of SDL with udev support (and you should!)
there's a few issues that may cause SDL to fail to detect your joystick. To
debug this, start by installing the evtest utility. On Ubuntu/Debian:
sudo apt-get install evtestThen run:
sudo evtestYou'll hopefully see your joystick listed along with a name like "/dev/input/eventXX"
Now run:
cat /dev/input/event/XXIf you get a permission error, you need to set a udev rule to change the mode of
your device (see below)
Also, try:
sudo udevadm info --query=all --name=input/eventXXIf you see a line stating ID_INPUT_JOYSTICK=1, great, if you don't see it,
you need to set up an udev rule to force this variable.
A combined rule for the Saitek Pro Flight Rudder Pedals to fix both issues looks
like:
SUBSYSTEM=="input", ATTRS{idProduct}=="0763", ATTRS{idVendor}=="06a3", MODE="0666", ENV{ID_INPUT_JOYSTICK}="1"
SUBSYSTEM=="input", ATTRS{idProduct}=="0764", ATTRS{idVendor}=="06a3", MODE="0666", ENV{ID_INPUT_JOYSTICK}="1"You can set up similar rules for your device by changing the values listed in
idProduct and idVendor. To obtain these values, try:
sudo udevadm info -a --name=input/eventXX | grep idVendor
sudo udevadm info -a --name=input/eventXX | grep idProductIf multiple values come up for each of these, the one you want is the first one of each.
On other systems which ship with an older udev (such as CentOS), you may need
to set up a rule such as:
SUBSYSTEM=="input", ENV{ID_CLASS}=="joystick", ENV{ID_INPUT_JOYSTICK}="1"Vs Sdl2 Docs README Macos (`vs/sdl2/docs/README-macos.md`)
Mac OS X (aka macOS).
These instructions are for people using Apple's Mac OS X (pronounced
"ten"), which in newer versions is just referred to as "macOS".
From the developer's point of view, macOS is a sort of hybrid Mac and
Unix system, and you have the option of using either traditional
command line tools or Apple's IDE Xcode.
Command Line Build
To build SDL using the command line, use the standard configure and make
process:
mkdir build
cd build
../configure
make
sudo make installCMake is also known to work, although it continues to be a work in progress:
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make
sudo make installYou can also build SDL as a Universal library (a single binary for both
64-bit Intel and ARM architectures), by using the build-scripts/clang-fat.sh
script.
mkdir build
cd build
CC=$PWD/../build-scripts/clang-fat.sh ../configure
make
sudo make installThis script builds SDL with 10.9 ABI compatibility on 64-bit Intel and 11.0
ABI compatibility on ARM64 architectures. For best compatibility you
should compile your application the same way.
Please note that building SDL requires at least Xcode 6 and the 10.9 SDK.
PowerPC support for macOS has been officially dropped as of SDL 2.0.2.
32-bit Intel and macOS 10.8 runtime support has been officially dropped as
of SDL 2.24.0.
To use the library once it's built, you essential have two possibilities:
use the traditional autoconf/automake/make method, or use Xcode.
Caveats for using SDL with Mac OS X
If you register your own NSApplicationDelegate (using [NSApp setDelegate:]),
SDL will not register its own. This means that SDL will not terminate using
SDL_Quit if it receives a termination request, it will terminate like a
normal app, and it will not send a SDL_DROPFILE when you request to open a
file with the app. To solve these issues, put the following code in your
NSApplicationDelegate implementation:
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
if (SDL_GetEventState(SDL_QUIT) == SDL_ENABLE) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
}
return NSTerminateCancel;
}
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
if (SDL_GetEventState(SDL_DROPFILE) == SDL_ENABLE) {
SDL_Event event;
event.type = SDL_DROPFILE;
event.drop.file = SDL_strdup([filename UTF8String]);
return (SDL_PushEvent(&event) > 0);
}
return NO;
}Using the Simple DirectMedia Layer with a traditional Makefile
An existing autoconf/automake build system for your SDL app has good chances
to work almost unchanged on macOS. However, to produce a "real" Mac binary
that you can distribute to users, you need to put the generated binary into a
so called "bundle", which is basically a fancy folder with a name like
"MyCoolGame.app".
To get this build automatically, add something like the following rule to
your Makefile.am:
bundle_contents = APP_NAME.app/Contents
APP_NAME_bundle: EXE_NAME
mkdir -p $(bundle_contents)/MacOS
mkdir -p $(bundle_contents)/Resources
echo "APPL????" > $(bundle_contents)/PkgInfo
$(INSTALL_PROGRAM) $< $(bundle_contents)/MacOS/You should replace EXE_NAME with the name of the executable. APP_NAME is
what will be visible to the user in the Finder. Usually it will be the same
as EXE_NAME but capitalized. E.g. if EXE_NAME is "testgame" then APP_NAME
usually is "TestGame". You might also want to use @PACKAGE@ to use the
package name as specified in your configure.ac file.
If your project builds more than one application, you will have to do a bit
more. For each of your target applications, you need a separate rule.
If you want the created bundles to be installed, you may want to add this
rule to your Makefile.am:
install-exec-hook: APP_NAME_bundle
rm -rf $(DESTDIR)$(prefix)/Applications/APP_NAME.app
mkdir -p $(DESTDIR)$(prefix)/Applications/
cp -r $< /$(DESTDIR)$(prefix)Applications/This rule takes the Bundle created by the rule from step 3 and installs them
into "$(DESTDIR)$(prefix)/Applications/".
Again, if you want to install multiple applications, you will have to augment
the make rule accordingly.
But beware! That is only part of the story! With the above, you end up with
a barebones .app bundle, which is double-clickable from the Finder. But
there are some more things you should do before shipping your product...
The bundle right now probably is dynamically linked against SDL. That
means that when you copy it to another computer, it will not run,
unless you also install SDL on that other computer. A good solution
for this dilemma is to static link against SDL. On OS X, you can
achieve that by linking against the libraries listed bybashsdl-config --static-libsinstead of those listed by
bashsdl-config --libsDepending on how exactly SDL is integrated into your build systems, the
way to achieve that varies, so I won't describe it here in detailAdd an 'Info.plist' to your application. That is a special XML file which
contains some meta-information about your application (like some copyright
information, the version of your app, the name of an optional icon file,
and other things). Part of that information is displayed by the Finder
when you click on the .app, or if you look at the "Get Info" window.
More information about Info.plist files can be found on Apple's homepage.
As a final remark, let me add that I use some of the techniques (and some
variations of them) in Exult and
ScummVM; both are available in source on
the net, so feel free to take a peek at them for inspiration!
Using the Simple DirectMedia Layer with Xcode
These instructions are for using Apple's Xcode IDE to build SDL applications.
First steps
The first thing to do is to unpack the Xcode.tar.gz archive in the
top level SDL directory (where the Xcode.tar.gz archive resides).
Because Stuffit Expander will unpack the archive into a subdirectory,
you should unpack the archive manually from the command line:
cd [path_to_SDL_source]
tar zxf Xcode.tar.gzThis will create a new folder called Xcode, which you can browse
normally from the Finder.
Building the Framework
The SDL Library is packaged as a framework bundle, an organized
relocatable folder hierarchy of executable code, interface headers,
and additional resources. For practical purposes, you can think of a
framework as a more user and system-friendly shared library, whose library
file behaves more or less like a standard UNIX shared library.
To build the framework, simply open the framework project and build it.
By default, the framework bundle "SDL.framework" is installed in
/Library/Frameworks. Therefore, the testers and project stationary expect
it to be located there. However, it will function the same in any of the
following locations:
- ~/Library/Frameworks
- /Local/Library/Frameworks
- /System/Library/Frameworks
Build Options
There are two "Build Styles" (See the "Targets" tab) for SDL.
"Deployment" should be used if you aren't tweaking the SDL library.
"Development" should be used to debug SDL apps or the library itself.
Building the Testers
Open the SDLTest project and build away!
Using the Project Stationary
Copy the stationary to the indicated folders to access it from
the "New Project" and "Add target" menus. What could be easier?
Setting up a new project by hand
Some of you won't want to use the Stationary so I'll give some tips:
(this is accurate as of Xcode 12.5.)
- Click "File" -> "New" -> "Project...
- Choose "macOS" and then "App" from the "Application" section.
- Fill out the options in the next window. User interface is "XIB" and
Language is "Objective-C". - Remove "main.m" from your project
- Remove "MainMenu.xib" from your project
- Remove "AppDelegates.*" from your project
- Add "$(HOME)/Library/Frameworks/SDL.framework/Headers" to include path
- Add "$(HOME)/Library/Frameworks" to the frameworks search path
- Add "-framework SDL -framework Foundation -framework AppKit" to "OTHER_LDFLAGS"
- Add your files
- Clean and build
Building from command line
Use xcode-build in the same directory as your .pbxproj file
Running your app
You can send command line args to your app by either invoking it from
the command line (in *.app/Contents/MacOS) or by entering them in the
Executables" panel of the target settings.
Implementation Notes
Some things that may be of interest about how it all works...
Working directory
In SDL 1.2, the working directory of your SDL app is by default set to its
parent, but this is no longer the case in SDL 2.0 and later. SDL2 does not
change the working directory, which means it'll be whatever the command line
prompt that launched the program was using, or if launched by double-clicking
in the Finder, it will be "/", the root of the filesystem. Plan accordingly!
You can use SDL_GetBasePath() to find where the program is running from and
chdir() there directly.
You have a Cocoa App!
Your SDL app is essentially a Cocoa application. When your app
starts up and the libraries finish loading, a Cocoa procedure is called,
which sets up the working directory and calls your main() method.
You are free to modify your Cocoa app with generally no consequence
to SDL. You cannot, however, easily change the SDL window itself.
Functionality may be added in the future to help this.
Bug reports
Bugs are tracked at the GitHub issue tracker.
Please feel free to report bugs there!
Vs Sdl2 Docs README N3ds (`vs/sdl2/docs/README-n3ds.md`)
Nintendo 3DS
SDL port for the Nintendo 3DS Homebrew toolchain contributed by:
Credits to:
- The awesome people who ported SDL to other homebrew platforms.
- The Devkitpro team for making all the tools necessary to achieve this.
Building
To build for the Nintendo 3DS, make sure you have devkitARM and cmake installed and run:
cmake -S. -Bbuild -DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/3DS.cmake" -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install buildNotes
- Currently only software rendering is supported.
- SDL2main should be used to ensure ROMFS is enabled.
- By default, the extra L2 cache and higher clock speeds of the New 2/3DS lineup are enabled. If you wish to turn it off, use
osSetSpeedupEnable(false)in your main function. SDL_GetBasePathreturns the romfs root instead of the executable's directory.- The Nintendo 3DS uses a cooperative threading model on a single core, meaning a thread will never yield unless done manually through the
SDL_Delayfunctions, or blocking waits (SDL_LockMutex,SDL_SemWait,SDL_CondWait,SDL_WaitThread). To avoid starving other threads,SDL_SemTryWaitandSDL_SemWaitTimeoutwill yield if they fail to acquire the semaphore, see https://github.com/libsdl-org/SDL/pull/6776 for more information.
Vs Sdl2 Docs README Nacl (`vs/sdl2/docs/README-nacl.md`)
Native Client
Requirements:
- Native Client SDK (https://developer.chrome.com/native-client),
(tested with Pepper version 33 or higher).
The SDL backend for Chrome's Native Client has been tested only with the PNaCl
toolchain, which generates binaries designed to run on ARM and x86_32/64
platforms. This does not mean it won't work with the other toolchains!
================================================================================ Building SDL for NaCl
Set up the right environment variables (see naclbuild.sh), then configure SDL with:
configure --host=pnacl --prefix some/install/destinationThen "make".
As an example of how to create a deployable app a Makefile project is provided
in test/nacl/Makefile, which includes some monkey patching of the common.mk file
provided by NaCl, without which linking properly to SDL won't work (the search
path can't be modified externally, so the linker won't find SDL's binaries unless
you dump them into the SDK path, which is inconvenient).
Also provided in test/nacl is the required support file, such as index.html,
manifest.json, etc.
SDL apps for NaCl run on a worker thread using the ppapi_simple infrastructure.
This allows for blocking calls on all the relevant systems (OpenGL ES, filesystem),
hiding the asynchronous nature of the browser behind the scenes...which is not the
same as making it disappear!
================================================================================ Running tests
Due to the nature of NaCl programs, building and running SDL tests is not as
straightforward as one would hope. The script naclbuild.sh in build-scripts
automates the process and should serve as a guide for users of SDL trying to build
their own applications.
Basic usage:
./naclbuild.sh path/to/pepper/toolchain (i.e. ~/naclsdk/pepper_35)This will build testgles2.c by default.
If you want to build a different test, for example testrendercopyex.c:
SOURCES=~/sdl/SDL/test/testrendercopyex.c ./naclbuild.sh ~/naclsdk/pepper_35Once the build finishes, you have to serve the contents with a web server (the
script will give you instructions on how to do that with Python).
================================================================================ RWops and nacl_io
SDL_RWops work transparently with nacl_io. Two functions control the mount points:
int mount(const char* source, const char* target,
const char* filesystemtype,
unsigned long mountflags, const void *data);
int umount(const char *target);
For convenience, SDL will by default mount an httpfs tree at / before callingthe app's main function. Such setting can be overridden by calling:
umount("/");And then mounting a different filesystem at /
It's important to consider that the asynchronous nature of file operations on a
browser is hidden from the application, effectively providing the developer with
a set of blocking file operations just like you get in a regular desktop
environment, which eases the job of porting to Native Client, but also introduces
a set of challenges of its own, in particular when big file sizes and slow
connections are involved.
For more information on how nacl_io and mount points work, see:
https://developer.chrome.com/native-client/devguide/coding/nacl_io
https://src.chromium.org/chrome/trunk/src/native_client_sdk/src/libraries/nacl_io/nacl_io.hTo be able to save into the directory "/save/" (like backup of game) :
mount("", "/save", "html5fs", 0, "type=PERSISTENT");And add to manifest.json :
"permissions": [
"unlimitedStorage"
]================================================================================ TODO - Known Issues
- Testing of all systems with a real application (something other than SDL's tests)
- Key events don't seem to work properly
Vs Sdl2 Docs README Ngage (`vs/sdl2/docs/README-ngage.md`)
Nokia N-Gage
SDL2 port for Symbian S60v1 and v2 with a main focus on the Nokia N-Gage
(Classic and QD) by Michael Fitzmayer.
Compiling
SDL is part of the N-Gage SDK. project.
The library is included in the
toolchain as a
sub-module.
A complete example project based on SDL2 can be found in the GitHub
account of the SDK: Wordle.
Current level of implementation
The video driver currently provides full screen video support with
keyboard input.
At the moment only the software renderer works.
Audio is not yet implemented.
Acknowledgements
Thanks to Hannu Viitala, Kimmo Kinnunen and Markus Mertama for the
valuable insight into Symbian programming. Without the SDL 1.2 port
which was specially developed for CDoom (Doom for the Nokia 9210), this
adaptation would not have been possible.
I would like to thank my friends
Razvan and Dan
Whelan, for their continuous support. Without
you and the N-Gage community, I would
have lost my patience long ago.
Last but not least, I would like to thank the development team of
EKA2L1 (an experimental Symbian OS emulator). Your
patience and support in troubleshooting helped me a lot.
Vs Sdl2 Docs README Os2 (`vs/sdl2/docs/README-os2.md`)
Simple DirectMedia Layer 2 for OS/2 & eComStation
SDL port for OS/2, authored by Andrey Vasilkin [email protected], 2016
OpenGL not supported by this port.
Additional optional environment variables:
SDL_AUDIO_SHARE
Values: 0 or 1, default is 0
Initializes the device as shareable or exclusively acquired.
SDL_VIDEODRIVER
Values: DIVE or VMAN, default is DIVE
Use video subsystem: Direct interface video extensions (DIVE) or
Video Manager (VMAN).
You may significantly increase video output speed with OS4 kernel and patched
files vman.dll and dive.dll or with latest versions of ACPI support and video
driver Panorama.
Latest versions of OS/4 kernel:
http://gus.biysk.ru/os4/
(Info: https://www.os2world.com/wiki/index.php/Phoenix_OS/4)
Patched files vman.dll and dive.dll:
http://gus.biysk.ru/os4/test/pached_dll/PATCHED_DLL.RAR
Compiling:
Open Watcom 1.9 or newer is tested. For the new Open Watcom V2 fork, see:
https://github.com/open-watcom/ and https://open-watcom.github.io
WATCOM environment variable must to be set to the Open Watcom install
directory. To compile, run: wmake -f Makefile.os2
Installing:
eComStation:
If you have previously installed SDL2, make a Backup copy of SDL2.dll
located in D:\ecs\dll (where D: is disk on which installed eComStation).
Stop all programs running with SDL2. Copy SDL2.dll to D:\ecs\dllOS/2:
Copy SDL2.dll to any directory on your LIBPATH. If you have a previous
version installed, close all SDL2 applications before replacing the old
copy. Also make sure that any other older versions of DLLs are removed
from your system.
Joysticks in SDL2:
The joystick code in SDL2 is a direct forward-port from the SDL-1.2 version.
Here is the original documentation from SDL-1.2:
The Joystick detection only works for standard joysticks (2 buttons, 2 axes
and the like). Therefore, if you use a non-standard joystick, you should
specify its features in the SDL_OS2_JOYSTICK environment variable in a batch
file or CONFIG.SYS, so SDL applications can provide full capability to your
device. The syntax is:
SET SDL_OS2_JOYSTICK=[JOYSTICK_NAME] [AXES] [BUTTONS] [HATS] [BALLS]
So, it you have a Gravis GamePad with 4 axes, 2 buttons, 2 hats and 0 balls,
the line should be:
SET SDL_OS2_JOYSTICK=Gravis_GamePad 4 2 2 0
If you want to add spaces in your joystick name, just surround it with
quotes or double-quotes:
SET SDL_OS2_JOYSTICK='Gravis GamePad' 4 2 2 0
or
SET SDL_OS2_JOYSTICK="Gravis GamePad" 4 2 2 0
Note however that Balls and Hats are not supported under OS/2, and the
value will be ignored... but it is wise to define these correctly because
in the future those can be supported.
Also the number of buttons is limited to 2 when using two joysticks,
4 when using one joystick with 4 axes, 6 when using a joystick with 3 axes
and 8 when using a joystick with 2 axes. Notice however these are limitations
of the Joystick Port hardware, not OS/2.
Vs Sdl2 Docs README Pandora (`vs/sdl2/docs/README-pandora.md`)
Pandora
A pandora specific video driver was written to allow SDL 2.0 with OpenGL ES
support to work on the pandora under the framebuffer. This driver do not have
input support for now, so if you use it you will have to add your own control code.
The video driver name is "pandora" so if you have problem running it from
the framebuffer, try to set the following variable before starting your application :
"export SDL_VIDEODRIVER=pandora"OpenGL ES support was added to the x11 driver, so it's working like the normal
x11 driver one with OpenGLX support, with SDL input event's etc..
David Carré (Cpasjuste)
[email protected]
Vs Sdl2 Docs README Platforms (`vs/sdl2/docs/README-platforms.md`)
Platforms
We maintain the list of supported platforms on our wiki now, and how to
build and install SDL for those platforms:
https://wiki.libsdl.org/InstallationVs Sdl2 Docs README Porting (`vs/sdl2/docs/README-porting.md`)
Porting
Porting To A New Platform
The first thing you have to do when porting to a new platform, is look at
include/SDL_platform.h and create an entry there for your operating system.
The standard format is "PLATFORM", where PLATFORM is the name of the OS.
Ideally SDL_platform.h will be able to auto-detect the system it's building
on based on C preprocessor symbols.
There are two basic ways of building SDL at the moment:
The "UNIX" way: ./configure; make; make install
If you have a GNUish system, then you might try this. Edit configure.ac,
take a look at the large section labelled:"Set up the configuration based on the host platform!"
Add a section for your platform, and then re-run autogen.sh and build!
Using an IDE:
If you're using an IDE or other non-configure build system, you'll probably
want to create a custom SDL_config.h for your platform. Edit SDL_config.h,
add a section for your platform, and create a custom SDL_config_{platform}.h,
based on SDL_config_minimal.h and SDL_config.h.inAdd the top level include directory to the header search path, and then add
the following sources to the project:src/.c
src/atomic/.c
src/audio/.c
src/cpuinfo/.c
src/events/.c
src/file/.c
src/haptic/.c
src/joystick/.c
src/power/.c
src/render/.c
src/render/software/.c
src/stdlib/.c
src/thread/.c
src/timer/.c
src/video/.c
src/audio/disk/.c
src/audio/dummy/.c
src/filesystem/dummy/.c
src/video/dummy/.c
src/haptic/dummy/.c
src/joystick/dummy/.c
src/main/dummy/.c
src/thread/generic/.c
src/timer/dummy/.c
src/loadso/dummy/*.c
Once you have a working library without any drivers, you can go back to each
of the major subsystems and start implementing drivers for your platform.
If you have any questions, don't hesitate to ask on the SDL mailing list:
http://www.libsdl.org/mailing-list.php
Enjoy!
Sam Lantinga ([email protected])
Vs Sdl2 Docs README Ps2 (`vs/sdl2/docs/README-ps2.md`)
PS2
SDL2 port for the Sony Playstation 2 contributed by:
- Francisco Javier Trujillo Mata
Credit to
- The guys that ported SDL to PSP & Vita because I'm taking them as reference.
- David G. F. for helping me with several issues and tests.
Building
To build SDL2 library for the PS2, make sure you have the latest PS2Dev status and run:
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=$PS2DEV/ps2sdk/ps2dev.cmake
cmake --build build
cmake --install buildHints
The PS2 port has a special Hint for having a dynamic VSYNC. The Hint is SDL_HINT_PS2_DYNAMIC_VSYNC.
If you enabled the dynamic vsync having as well SDL_RENDERER_PRESENTVSYNC enabled, then if the app is not able to run at 60 FPS, automatically the vsync will be disabled having a better performance, instead of droping FPS to 30.
Notes
If you trying to debug a SDL app through ps2client you need to avoid the IOP reset, otherwise you will lose the conection with your computer.
So to avoid the reset of the IOP CPU, you need to call to the macro SDL_PS2_SKIP_IOP_RESET();.
It could be something similar as:
.....
SDL_PS2_SKIP_IOP_RESET();
int main(int argc, char *argv[])
{
.....For a release binary is recommendable to reset the IOP always.
Remember to do a clean compilation everytime you enable or disable the SDL_PS2_SKIP_IOP_RESET otherwise the change won't be reflected.
Getting PS2 Dev
Running on PCSX2 Emulator
To Do
- PS2 Screen Keyboard
- Dialogs
- Others
Vs Sdl2 Docs README Psp (`vs/sdl2/docs/README-psp.md`)
PSP
SDL2 port for the Sony PSP contributed by:
- Captian Lex
- Francisco Javier Trujillo Mata
- Wouter Wijsman
Credit to
Marcus R.Brown,Jim Paris,Matthew H for the original SDL 1.2 for PSP
Geecko for his PSP GU lib "Glib2d"
Building
To build SDL2 library for the PSP, make sure you have the latest PSPDev status and run:
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=$PSPDEV/psp/share/pspdev.cmake
cmake --build build
cmake --install buildGetting PSP Dev
Running on PPSSPP Emulator
Compiling a HelloWorld
To Do
- PSP Screen Keyboard
- Dialogs
Vs Sdl2 Docs README Raspberrypi (`vs/sdl2/docs/README-raspberrypi.md`)
Raspberry Pi
Requirements:
Raspbian (other Linux distros may work as well).
Features
- Works without X11
- Hardware accelerated OpenGL ES 2.x
- Sound via ALSA
- Input (mouse/keyboard/joystick) via EVDEV
- Hotplugging of input devices via UDEV
Raspbian Build Dependencies
sudo apt-get install libudev-dev libasound2-dev libdbus-1-dev
You also need the VideoCore binary stuff that ships in /opt/vc for EGL and
OpenGL ES 2.x, it usually comes pre-installed, but in any case:
sudo apt-get install libraspberrypi0 libraspberrypi-bin libraspberrypi-dev
NEON
If your Pi has NEON support, make sure you add -mfpu=neon to your CFLAGS so
that SDL will select some otherwise-disabled highly-optimized code. The
original Pi units don't have NEON, the Pi2 probably does, and the Pi3
definitely does.
Cross compiling from x86 Linux
To cross compile SDL for Raspbian from your desktop machine, you'll need a
Raspbian system root and the cross compilation tools. We'll assume these tools
will be placed in /opt/rpi-tools
sudo git clone --depth 1 https://github.com/raspberrypi/tools /opt/rpi-toolsYou'll also need a Raspbian binary image.
Get it from: http://downloads.raspberrypi.org/raspbian_latest
After unzipping, you'll get file with a name like: "
Let's assume the sysroot will be built in /opt/rpi-sysroot.
export SYSROOT=/opt/rpi-sysroot
sudo kpartx -a -v .img
sudo mount -o loop /dev/mapper/loop0p2 /mnt
sudo cp -r /mnt $SYSROOT
sudo apt-get install qemu binfmt-support qemu-user-static
sudo cp /usr/bin/qemu-arm-static $SYSROOT/usr/bin
sudo mount --bind /dev $SYSROOT/dev
sudo mount --bind /proc $SYSROOT/proc
sudo mount --bind /sys $SYSROOT/sysNow, before chrooting into the ARM sysroot, you'll need to apply a workaround,
edit $SYSROOT/etc/ld.so.preload and comment out all lines in it.
sudo chroot $SYSROOT
apt-get install libudev-dev libasound2-dev libdbus-1-dev libraspberrypi0 libraspberrypi-bin libraspberrypi-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxi-dev libxss-dev
exit
sudo umount $SYSROOT/dev
sudo umount $SYSROOT/proc
sudo umount $SYSROOT/sys
sudo umount /mntThere's one more fix required, as the libdl.so symlink uses an absolute path
which doesn't quite work in our setup.
sudo rm -rf $SYSROOT/usr/lib/arm-linux-gnueabihf/libdl.so
sudo ln -s ../../../lib/arm-linux-gnueabihf/libdl.so.2 $SYSROOT/usr/lib/arm-linux-gnueabihf/libdl.soThe final step is compiling SDL itself.
export CC="/opt/rpi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc --sysroot=$SYSROOT -I$SYSROOT/opt/vc/include -I$SYSROOT/usr/include -I$SYSROOT/opt/vc/include/interface/vcos/pthreads -I$SYSROOT/opt/vc/include/interface/vmcs_host/linux"
cd <SDL SOURCE>
mkdir -p build;cd build
LDFLAGS="-L$SYSROOT/opt/vc/lib" ../configure --with-sysroot=$SYSROOT --host=arm-raspberry-linux-gnueabihf --prefix=$PWD/rpi-sdl2-installed --disable-pulseaudio --disable-esd
make
make installTo be able to deploy this to /usr/local in the Raspbian system you need to fix up a few paths:
perl -w -pi -e "s#$PWD/rpi-sdl2-installed#/usr/local#g;" ./rpi-sdl2-installed/lib/libSDL2.la ./rpi-sdl2-installed/lib/pkgconfig/sdl2.pc ./rpi-sdl2-installed/bin/sdl2-configApps don't work or poor video/audio performance
If you get sound problems, buffer underruns, etc, run "sudo rpi-update" to
update the RPi's firmware. Note that doing so will fix these problems, but it
will also render the CMA - Dynamic Memory Split functionality useless.
Also, by default the Raspbian distro configures the GPU RAM at 64MB, this is too
low in general, specially if a 1080p TV is hooked up.
See here how to configure this setting: http://elinux.org/RPiconfig
Using a fixed gpu_mem=128 is the best option (specially if you updated the
firmware, using CMA probably won't work, at least it's the current case).
No input
Make sure you belong to the "input" group.
sudo usermod -aG input `whoami`No HDMI Audio
If you notice that ALSA works but there's no audio over HDMI, try adding:
hdmi_drive=2to your config.txt file and reboot.
Reference: http://www.raspberrypi.org/phpBB3/viewtopic.php?t=5062
Text Input API support
The Text Input API is supported, with translation of scan codes done via the
kernel symbol tables. For this to work, SDL needs access to a valid console.
If you notice there's no SDL_TEXTINPUT message being emitted, double check that
your app has read access to one of the following:
- /proc/self/fd/0
- /dev/tty
- /dev/tty[0...6]
- /dev/vc/0
- /dev/console
This is usually not a problem if you run from the physical terminal (as opposed
to running from a pseudo terminal, such as via SSH). If running from a PTS, a
quick workaround is to run your app as root or add yourself to the tty group,
then re-login to the system.
sudo usermod -aG tty `whoami`The keyboard layout used by SDL is the same as the one the kernel uses.
To configure the layout on Raspbian:
sudo dpkg-reconfigure keyboard-configurationTo configure the locale, which controls which keys are interpreted as letters,
this determining the CAPS LOCK behavior:
sudo dpkg-reconfigure localesOpenGL problems
If you have desktop OpenGL headers installed at build time in your RPi or cross
compilation environment, support for it will be built in. However, the chipset
does not actually have support for it, which causes issues in certain SDL apps
since the presence of OpenGL support supersedes the ES/ES2 variants.
The workaround is to disable OpenGL at configuration time:
./configure --disable-video-openglOr if the application uses the Render functions, you can use the SDL_RENDER_DRIVER
environment variable:
export SDL_RENDER_DRIVER=opengles2Notes
- When launching apps remotely (via SSH), SDL can prevent local keystrokes from
leaking into the console only if it has root privileges. Launching apps locally
does not suffer from this issue.
Vs Sdl2 Docs README Riscos (`vs/sdl2/docs/README-riscos.md`)
RISC OS
Requirements:
- RISC OS 3.5 or later.
- SharedUnixLibrary.
- DigitalRenderer, for audio support.
- Iconv, for
SDL_iconvand related functions.
Compiling:
Currently, SDL2 for RISC OS only supports compiling with GCCSDK under Linux. Both the autoconf and CMake build systems are supported.
The following commands can be used to build SDL2 for RISC OS using autoconf:
./configure --host=arm-unknown-riscos --prefix=$GCCSDK_INSTALL_ENV --disable-gcc-atomics
make
make installThe following commands can be used to build SDL2 for RISC OS using CMake:
cmake -Bbuild-riscos -DCMAKE_TOOLCHAIN_FILE=$GCCSDK_INSTALL_ENV/toolchain-riscos.cmake -DRISCOS=ON -DCMAKE_INSTALL_PREFIX=$GCCSDK_INSTALL_ENV -DCMAKE_BUILD_TYPE=Release -DSDL_GCC_ATOMICS=OFF
cmake --build build-riscos
cmake --build build-riscos --target installCurrent level of implementation
The video driver currently provides full screen video support with keyboard and mouse input. Windowed mode is not yet supported, but is planned in the future. Only software rendering is supported.
The filesystem APIs return either Unix-style paths or RISC OS-style paths based on the value of the __riscosify_control symbol, as is standard for UnixLib functions.
The audio, loadso, thread and timer APIs are currently provided by UnixLib.
GCC atomics are currently broken on some platforms, meaning it's currently necessary to compile with --disable-gcc-atomics using autotools or -DSDL_GCC_ATOMICS=OFF using CMake.
The joystick, locale and power APIs are not yet implemented.
Vs Sdl2 Docs README Touch (`vs/sdl2/docs/README-touch.md`)
Touch
System Specific Notes
Linux:
The linux touch system is currently based off event streams, and proc/bus/devices. The active user must be given permissions to read /dev/input/TOUCHDEVICE, where TOUCHDEVICE is the event stream for your device. Currently only Wacom tablets are supported. If you have an unsupported tablet contact me at [email protected] and I will help you get support for it.
Mac:
The Mac and iPhone APIs are pretty. If your touch device supports them then you'll be fine. If it doesn't, then there isn't much we can do.
iPhone:
Works out of box.
Windows:
Unfortunately there is no windows support as of yet. Support for Windows 7 is planned, but we currently have no way to test. If you have a Windows 7 WM_TOUCH supported device, and are willing to help test please contact me at [email protected]
=========================================================================== Events
SDL_FINGERDOWN:
Sent when a finger (or stylus) is placed on a touch device.
Fields:
- event.tfinger.touchId - the Id of the touch device.
- event.tfinger.fingerId - the Id of the finger which just went down.
- event.tfinger.x - the x coordinate of the touch (0..1)
- event.tfinger.y - the y coordinate of the touch (0..1)
- event.tfinger.pressure - the pressure of the touch (0..1)
SDL_FINGERMOTION:
Sent when a finger (or stylus) is moved on the touch device.
Fields:
Same as SDL_FINGERDOWN but with additional:
- event.tfinger.dx - change in x coordinate during this motion event.
- event.tfinger.dy - change in y coordinate during this motion event.
SDL_FINGERUP:
Sent when a finger (or stylus) is lifted from the touch device.
Fields:
Same as SDL_FINGERDOWN.
=========================================================================== Functions
SDL provides the ability to access the underlying SDL_Finger structures.
These structures should never be modified.
The following functions are included from SDL_touch.h
To get a SDL_TouchID call SDL_GetTouchDevice(int index).
This returns a SDL_TouchID.
IMPORTANT: If the touch has been removed, or there is no touch with the given index, SDL_GetTouchDevice() will return 0. Be sure to check for this!
The number of touch devices can be queried with SDL_GetNumTouchDevices().
A SDL_TouchID may be used to get pointers to SDL_Finger.
SDL_GetNumTouchFingers(touchID) may be used to get the number of fingers currently down on the device.
The most common reason to access SDL_Finger is to query the fingers outside the event. In most cases accessing the fingers is using the event. This would be accomplished by code like the following:
float x = event.tfinger.x;
float y = event.tfinger.y;To get a SDL_Finger, call SDL_GetTouchFinger(SDL_TouchID touchID, int index), where touchID is a SDL_TouchID, and index is the requested finger.
This returns a SDL_Finger *, or NULL if the finger does not exist, or has been removed.
A SDL_Finger is guaranteed to be persistent for the duration of a touch, but it will be de-allocated as soon as the finger is removed. This occurs when the SDL_FINGERUP event is added to the event queue, and thus before the SDL_FINGERUP event is polled.
As a result, be very careful to check for NULL return values.
A SDL_Finger has the following fields:
- x, y:
The current coordinates of the touch. - pressure:
The pressure of the touch.
=========================================================================== Notes
For a complete example see test/testgesture.c
Please direct questions/comments to:
[email protected]
(original author, API was changed since)
Vs Sdl2 Docs README Versions (`vs/sdl2/docs/README-versions.md`)
Versioning
Since 2.23.0
SDL follows an "odd/even" versioning policy, similar to GLib, GTK, Flatpak
and older versions of the Linux kernel:
The major version (first part) increases when backwards compatibility
is broken, which will happen infrequently.If the minor version (second part) is divisible by 2
(for example 2.24.x, 2.26.x), this indicates a version of SDL that
is believed to be stable and suitable for production use.In stable releases, the patchlevel or micro version (third part)
indicates bugfix releases. Bugfix releases should not add or
remove ABI, so the ".0" release (for example 2.24.0) should be
forwards-compatible with all the bugfix releases from the
same cycle (for example 2.24.1).The minor version increases when new API or ABI is added, or when
other significant changes are made. Newer minor versions are
backwards-compatible, but not fully forwards-compatible.
For example, programs built against SDL 2.24.x should work fine
with SDL 2.26.x, but programs built against SDL 2.26.x will not
necessarily work with 2.24.x.
If the minor version (second part) is not divisible by 2
(for example 2.23.x, 2.25.x), this indicates a development prerelease
of SDL that is not suitable for stable software distributions.
Use with caution.The patchlevel or micro version (third part) increases with
each prerelease.Each prerelease might add new API and/or ABI.
Prereleases are backwards-compatible with older stable branches.
For example, 2.25.x will be backwards-compatible with 2.24.x.Prereleases are not guaranteed to be backwards-compatible with
each other. For example, new API or ABI added in 2.25.1
might be removed or changed in 2.25.2.
If this would be a problem for you, please do not use prereleases.Only upgrade to a prerelease if you can guarantee that you will
promptly upgrade to the stable release that follows it.
For example, do not upgrade to 2.23.x unless you will be able to
upgrade to 2.24.0 when it becomes available.Software distributions that have a freeze policy (in particular Linux
distributions with a release cycle, such as Debian and Fedora)
should usually only package stable releases, and not prereleases.
Before 2.23.0
Older versions of SDL followed a similar policy, but instead of the
odd/even rule applying to the minor version, it applied to the patchlevel
(micro version, third part). For example, 2.0.22 was a stable release
and 2.0.21 was a prerelease.
Vs Sdl2 Docs README Visualc (`vs/sdl2/docs/README-visualc.md`)
Using SDL with Microsoft Visual C++
by Lion Kimbro with additions by James Turk
You can either use the precompiled libraries from the SDL web site, or you can build SDL
yourself.
Building SDL
To build SDL, your machine must, at a minimum, have the DirectX9.0c SDK installed. It may or may not be retrievable from
the Microsoft website, so you might need to locate it online.
Editor's note: I've been able to successfully build SDL using Visual Studio 2019 without the DX9.0c SDKOpen the Visual Studio solution file at
./VisualC/SDL.sln.Your IDE will likely prompt you to upgrade this solution file to whatever later version of the IDE you're using. In the
Retarget Projectsdialog,
all of the affected project files should be checked allowing you to use the latestWindows SDK Versionyou have installed, along with
thePlatform Toolset.
If you choose NOT to upgrade to use the latest Windows SDK Version or Platform Toolset, then you'll need the Visual Studio 2010 Platform Toolset.
- Build the
.dlland.libfiles by right clicking on each project in turn (Projects are listed in the Workspace
panel in the FileView tab), and selectingBuild.
You may get a few warnings, but you should not get any errors.
Later, we will refer to the following .lib and .dll files that have just been generated:
./VisualC/Win32/Debug/SDL2.dllor./VisualC/Win32/Release/SDL2.dll./VisualC/Win32/Debug/SDL2.libor./VisualC/Win32/Release/SDL2.lib./VisualC/Win32/Debug/SDL2main.libor./VisualC/Win32/Release/SDL2main.lib
Note for the x64 versions, just replace Win32 in the path with x64
Creating a Project with SDL
Create a project as a
Win32 Application.Create a C++ file for your project.
Set the C runtime to
Multi-threaded DLLin the menu:Project|Settings|C/C++ tab|Code Generation|Runtime Library.Add the SDL
includedirectory to your list of includes in the menu:Project|Settings|C/C++ tab|Preprocessor|Additional include directories
VC7 Specific: Instead of doing this, I find it easier to add the
include and library directories to the list that VC7 keeps. Do this by
selecting Tools|Options|Projects|VC++ Directories and under the "Show
Directories For:" dropbox select "Include Files", and click the "New
Directory Icon" and add the [SDLROOT]\include directory (e.g. If you
installed to c:\SDL\ add c:\SDL\include). Proceed to change the
dropbox selection to "Library Files" and add [SDLROOT]\lib.
The "include directory" I am referring to is the ./include folder.
Now we're going to use the files that we had created earlier in the Build SDL step.
Copy the following file into your Project directory:
SDL2.dll
Add the following files to your project (It is not necessary to copy them to your project directory):
SDL2.libSDL2main.lib
To add them to your project, right click on your project, and selectAdd files to project.
Instead of adding the files to your project, it is more desirable to add them to the linker options: Project|Properties|Linker|Command Line
and type the names of the libraries to link with in the "Additional Options:" box. Note: This must be done for each build configuration
(e.g. Release,Debug).
Hello SDL2
Here's a sample SDL snippet to verify everything is setup in your IDE:
#include "SDL.h"
int main( int argc, char* argv[] )
{
const int WIDTH = 640;
const int HEIGHT = 480;
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("SDL2 Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}That's it!
I hope that this document has helped you get through the most difficult part of using the SDL: installing it.
Suggestions for improvements should be posted to the Github Issues.
Credits
Thanks to Paulus Esterhazy, for the work on VC++ port.
This document was originally called "VisualC.txt", and was written by Sam Lantinga.
Later, it was converted to HTML and expanded into the document that you see today by Lion Kimbro.
Minor Fixes and Visual C++ 7 Information (In Green) was added by James Turk
Vs Sdl2 Docs README Vita (`vs/sdl2/docs/README-vita.md`)
PS Vita
SDL port for the Sony Playstation Vita and Sony Playstation TV
Credit to
- xerpi, cpasjuste and rsn8887 for initial (vita2d) port
- vitasdk/dolcesdk devs
- CBPS discord (Namely Graphene and SonicMastr)
Building
To build for the PSVita, make sure you have vitasdk and cmake installed and run:
cmake -S. -Bbuild -DCMAKE_TOOLCHAIN_FILE=${VITASDK}/share/vita.toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install buildNotes
- gles1/gles2 support and renderers are disabled by default and can be enabled by configuring with
-DVIDEO_VITA_PVR=ON
These renderers support 720p and 1080i resolutions. These can be specified with:SDL_setenv("VITA_RESOLUTION", "720", 1);andSDL_setenv("VITA_RESOLUTION", "1080", 1); - Desktop GL 1.X and 2.X support and renderers are also disabled by default and also can be enabled with
-DVIDEO_VITA_PVR=ONas long as gl4es4vita is present in your SDK.
They support the same resolutions as the gles1/gles2 backends and require specifyingSDL_setenv("VITA_PVR_OGL", "1", 1);
anytime before video subsystem initialization. - gles2 support via PIB is disabled by default and can be enabled by configuring with
-DVIDEO_VITA_PIB=ON - By default SDL emits mouse events for touch events on every touchscreen.
Vita has two touchscreens, so it's recommended to useSDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");and handle touch events instead.
Individual touchscreens can be disabled with:SDL_setenv("VITA_DISABLE_TOUCH_FRONT", "1", 1);andSDL_setenv("VITA_DISABLE_TOUCH_BACK", "1", 1); - Support for L2/R2/R3/R3 buttons, haptic feedback and gamepad led only available on PSTV, or when using external ds4 gamepad on vita.
Vs Sdl2 Docs README Wince (`vs/sdl2/docs/README-wince.md`)
WinCE
Windows CE is no longer supported by SDL.
We have left the CE support in SDL 1.2 for those that must have it, and we
have support for Windows Phone 8 and WinRT in SDL2, as of SDL 2.0.3.
--ryan.
Vs Sdl2 Docs README Windows (`vs/sdl2/docs/README-windows.md`)
Windows
LLVM and Intel C++ compiler support
SDL will build with the Visual Studio project files with LLVM-based compilers, such as the Intel oneAPI C++
compiler, but you'll have to manually add the "-msse3" command line option
to at least the SDL_audiocvt.c source file, and possibly others. This may
not be necessary if you build SDL with CMake instead of the included Visual
Studio solution.
Details are here: https://github.com/libsdl-org/SDL/issues/5186
OpenGL ES 2.x support
SDL has support for OpenGL ES 2.x under Windows via two alternative
implementations.
The most straightforward method consists in running your app in a system with
a graphic card paired with a relatively recent (as of November of 2013) driver
which supports the WGL_EXT_create_context_es2_profile extension. Vendors known
to ship said extension on Windows currently include nVidia and Intel.
The other method involves using the
ANGLE library. If an OpenGL ES 2.x
context is requested and no WGL_EXT_create_context_es2_profile extension is
found, SDL will try to load the libEGL.dll library provided by ANGLE.
To obtain the ANGLE binaries, you can either compile from source from
https://chromium.googlesource.com/angle/angle or copy the relevant binaries
from a recent Chrome/Chromium install for Windows. The files you need are:
- libEGL.dll
- libGLESv2.dll
- d3dcompiler_46.dll (supports Windows Vista or later, better shader
compiler) or d3dcompiler_43.dll (supports Windows XP or later)
If you compile ANGLE from source, you can configure it so it does not need the
d3dcompiler_* DLL at all (for details on this, see their documentation).
However, by default SDL will try to preload the d3dcompiler_46.dll to
comply with ANGLE's requirements. If you wish SDL to preload
d3dcompiler_43.dll (to support Windows XP) or to skip this step at all, you
can use the SDL_HINT_VIDEO_WIN_D3DCOMPILER hint (see SDL_hints.h for more
details).
Known Bugs:
- SDL_GL_SetSwapInterval is currently a no op when using ANGLE. It appears
that there's a bug in the library which prevents the window contents from
refreshing if this is set to anything other than the default value.
Vulkan Surface Support
Support for creating Vulkan surfaces is configured on by default. To disable
it change the value of SDL_VIDEO_VULKAN to 0 in SDL_config_windows.h. You
must install the Vulkan SDK in order to
use Vulkan graphics in your application.
Vs Sdl2 Docs README Winrt (`vs/sdl2/docs/README-winrt.md`)
WinRT
This port allows SDL applications to run on Microsoft's platforms that require
use of "Windows Runtime", aka. "WinRT", APIs. Microsoft may, in some cases,
refer to them as either "Windows Store", or for Windows 10, "UWP" apps.
In the past, SDL has supported Windows RT 8.x, Windows Phone, etc, but in
modern times this port is focused on UWP apps, which run on Windows 10,
and modern Xbox consoles.
Requirements
- Microsoft Visual C++ (aka Visual Studio) 2019.
- Free, "Community" or "Express" editions may be used, so long as they
include support for either "Windows Store" or "Windows Phone" apps.
"Express" versions marked as supporting "Windows Desktop" development
typically do not include support for creating WinRT apps, to note.
(The "Community" editions of Visual C++ do, however, support both
desktop/Win32 and WinRT development).
- Free, "Community" or "Express" editions may be used, so long as they
- A valid Microsoft account - This requirement is not imposed by SDL, but
rather by Microsoft's Visual C++ toolchain. This is required to launch or
debug apps.
Status
Here is a rough list of what works, and what doesn't:
What works:
- compilation via Visual C++ 2019.
- compile-time platform detection for SDL programs. The C/C++ #define,
__WINRT__, will be set to 1 (by SDL) when compiling for WinRT. - GPU-accelerated 2D rendering, via SDL_Renderer.
- OpenGL ES 2, via the ANGLE library (included separately from SDL)
- software rendering, via either SDL_Surface (optionally in conjunction with
SDL_GetWindowSurface() and SDL_UpdateWindowSurface()) or via the
SDL_Renderer APIs - threads
- timers (via SDL_GetTicks(), SDL_AddTimer(), SDL_GetPerformanceCounter(),
SDL_GetPerformanceFrequency(), etc.) - file I/O via SDL_RWops
- mouse input (unsupported on Windows Phone)
- audio, via SDL's WASAPI backend (if you want to record, your app must
have "Microphone" capabilities enabled in its manifest, and the user must
not have blocked access. Otherwise, capture devices will fail to work,
presenting as a device disconnect shortly after opening it.) - .DLL file loading. Libraries MUST be packaged inside applications. Loading
anything outside of the app is not supported. - system path retrieval via SDL's filesystem APIs
- game controllers. Support is provided via the SDL_Joystick and
SDL_GameController APIs, and is backed by Microsoft's XInput API. Please
note, however, that Windows limits game-controller support in UWP apps to,
"Xbox compatible controllers" (many controllers that work in Win32 apps,
do not work in UWP, due to restrictions in UWP itself.) - multi-touch input
- app events. SDL_APP_WILLENTER* and SDL_APP_DIDENTER* events get sent out as
appropriate. - window events
- using Direct3D 11.x APIs outside of SDL. Non-XAML / Direct3D-only apps can
choose to render content directly via Direct3D, using SDL to manage the
internal WinRT window, as well as input and audio. (Use
SDL_GetWindowWMInfo() to get the WinRT 'CoreWindow', and pass it into
IDXGIFactory2::CreateSwapChainForCoreWindow() as appropriate.)
What partially works:
- keyboard input. Most of WinRT's documented virtual keys are supported, as
well as many keys with documented hardware scancodes. Converting
SDL_Scancodes to or from SDL_Keycodes may not work, due to missing APIs
(MapVirtualKey()) in Microsoft's Windows Store / UWP APIs. - SDLmain. WinRT uses a different signature for each app's main() function.
SDL-based apps that use this port must compile in SDL_winrt_main_NonXAML.cpp
(inSDL\src\main\winrt\) directly in order for their C-style main()
functions to be called.
- keyboard input. Most of WinRT's documented virtual keys are supported, as
What doesn't work:
- compilation with anything other than Visual C++
- programmatically-created custom cursors. These don't appear to be supported
by WinRT. Different OS-provided cursors can, however, be created via
SDL_CreateSystemCursor() (unsupported on Windows Phone) - SDL_WarpMouseInWindow() or SDL_WarpMouseGlobal(). This are not currently
supported by WinRT itself. - joysticks and game controllers that either are not supported by
Microsoft's XInput API, or are not supported within UWP apps (many
controllers that work in Win32, do not work in UWP, due to restrictions in
UWP itself). - turning off VSync when rendering on Windows Phone. Attempts to turn VSync
off on Windows Phone result either in Direct3D not drawing anything, or it
forcing VSync back on. As such, SDL_RENDERER_PRESENTVSYNC will always get
turned-on on Windows Phone. This limitation is not present in non-Phone
WinRT (such as Windows 8.x), where turning off VSync appears to work. - probably anything else that's not listed as supported
Upgrade Notes
SDL_GetPrefPath() usage when upgrading WinRT apps from SDL 2.0.3
SDL 2.0.4 fixes two bugs found in the WinRT version of SDL_GetPrefPath().
The fixes may affect older, SDL 2.0.3-based apps' save data. Please note
that these changes only apply to SDL-based WinRT apps, and not to apps for
any other platform.
SDL_GetPrefPath() would return an invalid path, one in which the path's
directory had not been created. Attempts to create files there
(via fopen(), for example), would fail, unless that directory was
explicitly created beforehand.SDL_GetPrefPath(), for non-WinPhone-based apps, would return a path inside
a WinRT 'Roaming' folder, the contents of which get automatically
synchronized across multiple devices. This process can occur while an
application runs, and can cause existing save-data to be overwritten
at unexpected times, with data from other devices. (Windows Phone apps
written with SDL 2.0.3 did not utilize a Roaming folder, due to API
restrictions in Windows Phone 8.0).
SDL_GetPrefPath(), starting with SDL 2.0.4, addresses these by:
making sure that SDL_GetPrefPath() returns a directory in which data
can be written to immediately, without first needing to create directories.basing SDL_GetPrefPath() off of a different, non-Roaming folder, the
contents of which do not automatically get synchronized across devices
(and which require less work to use safely, in terms of data integrity).
Apps that wish to get their Roaming folder's path can do so either by using
SDL_WinRTGetFSPathUTF8(), SDL_WinRTGetFSPathUNICODE() (which returns a
UCS-2/wide-char string), or directly through the WinRT class,
Windows.Storage.ApplicationData.
Setup, High-Level Steps
The steps for setting up a project for an SDL/WinRT app looks like the
following, at a high-level:
- create a new Visual C++ project using Microsoft's template for a,
"Direct3D App". - remove most of the files from the project.
- make your app's project directly reference SDL/WinRT's own Visual C++
project file, via use of Visual C++'s "References" dialog. This will setup
the linker, and will copy SDL's .dll files to your app's final output. - adjust your app's build settings, at minimum, telling it where to find SDL's
header files. - add files that contains a WinRT-appropriate main function, along with some
data to make sure mouse-cursor-hiding (via SDL_ShowCursor(SDL_DISABLE) calls)
work properly. - add SDL-specific app code.
- build and run your app.
Setup, Detailed Steps
1. Create a new project
Create a new project using one of Visual C++'s templates for a plain, non-XAML,
"Direct3D App" (XAML support for SDL/WinRT is not yet ready for use). If you
don't see one of these templates, in Visual C++'s 'New Project' dialog, try
using the textbox titled, 'Search Installed Templates' to look for one.
2. Remove unneeded files from the project
In the new project, delete any file that has one of the following extensions:
- .cpp
- .h
- .hlsl
When you are done, you should be left with a few files, each of which will be a
necessary part of your app's project. These files will consist of:
- an .appxmanifest file, which contains metadata on your WinRT app. This is
similar to an Info.plist file on iOS, or an AndroidManifest.xml on Android. - a few .png files, one of which is a splash screen (displayed when your app
launches), others are app icons. - a .pfx file, used for code signing purposes.
3. Add references to SDL's project files
SDL/WinRT can be built in multiple variations, spanning across three different
CPU architectures (x86, x64, and ARM) and two different configurations
(Debug and Release). WinRT and Visual C++ do not currently provide a means
for combining multiple variations of one library into a single file.
Furthermore, it does not provide an easy means for copying pre-built .dll files
into your app's final output (via Post-Build steps, for example). It does,
however, provide a system whereby an app can reference the MSVC projects of
libraries such that, when the app is built:
- each library gets built for the appropriate CPU architecture(s) and WinRT
platform(s). - each library's output, such as .dll files, get copied to the app's build
output.
To set this up for SDL/WinRT, you'll need to run through the following steps:
- open up the Solution Explorer inside Visual C++ (under the "View" menu, then
"Solution Explorer") - right click on your app's solution.
- navigate to "Add", then to "Existing Project..."
- find SDL/WinRT's Visual C++ project file and open it, in the
VisualC-WinRT
directory. - once the project has been added, right-click on your app's project and
select, "References..." - click on the button titled, "Add New Reference..."
- check the box next to SDL
- click OK to close the dialog
- SDL will now show up in the list of references. Click OK to close that
dialog.
Your project is now linked to SDL's project, insofar that when the app is
built, SDL will be built as well, with its build output getting included with
your app.
4. Adjust Your App's Build Settings
Some build settings need to be changed in your app's project. This guide will
outline the following:
- making sure that the compiler knows where to find SDL's header files
- Optional for C++, but NECESSARY for compiling C code: telling the
compiler not to use Microsoft's C++ extensions for WinRT development. - Optional: telling the compiler not generate errors due to missing
precompiled header files.
To change these settings:
- right-click on the project
- choose "Properties"
- in the drop-down box next to "Configuration", choose, "All Configurations"
- in the drop-down box next to "Platform", choose, "All Platforms"
- in the left-hand list, expand the "C/C++" section
- select "General"
- edit the "Additional Include Directories" setting, and add a path to SDL's
"include" directory - Optional: to enable compilation of C code: change the setting for
"Consume Windows Runtime Extension" from "Yes (/ZW)" to "No". If you're
working with a completely C++ based project, this step can usually be
omitted. - Optional: to disable precompiled headers (which can produce
'stdafx.h'-related build errors, if setup incorrectly: in the left-hand
list, select "Precompiled Headers", then change the setting for "Precompiled
Header" from "Use (/Yu)" to "Not Using Precompiled Headers". - close the dialog, saving settings, by clicking the "OK" button
5. Add a WinRT-appropriate main function, and a blank-cursor image, to the app.
A few files should be included directly in your app's MSVC project, specifically:
- a WinRT-appropriate main function (which is different than main() functions on
other platforms) - a Win32-style cursor resource, used by SDL_ShowCursor() to hide the mouse cursor
(if and when the app needs to do so). If this cursor resource is not
included, mouse-position reporting may fail if and when the cursor is
hidden, due to possible bugs/design-oddities in Windows itself.
To include these files for C/C++ projects:
- right-click on your project (again, in Visual C++'s Solution Explorer),
navigate to "Add", then choose "Existing Item...". - navigate to the directory containing SDL's source code, then into its
subdirectory, 'src/main/winrt/'. Select, then add, the following files:SDL_winrt_main_NonXAML.cppSDL2-WinRTResources.rcSDL2-WinRTResource_BlankCursor.cur
- right-click on the file
SDL_winrt_main_NonXAML.cpp(as listed in your
project), then click on "Properties...". - in the drop-down box next to "Configuration", choose, "All Configurations"
- in the drop-down box next to "Platform", choose, "All Platforms"
- in the left-hand list, click on "C/C++"
- change the setting for "Consume Windows Runtime Extension" to "Yes (/ZW)".
- click the OK button. This will close the dialog.
NOTE: C++/CX compilation is currently required in at least one file of your
app's project. This is to make sure that Visual C++'s linker builds a 'Windows
Metadata' file (.winmd) for your app. Not doing so can lead to build errors.
For non-C++ projects, you will need to call SDL_WinRTRunApp from your language's
main function, and generate SDL2-WinRTResources.res manually by using rc via
the Developer Command Prompt and including it as a
first block in your Visual Studio project file.
6. Add app code and assets
At this point, you can add in SDL-specific source code. Be sure to include a
C-style main function (ie: int main(int argc, char *argv[])). From there you
should be able to create a single SDL_Window (WinRT apps can only have one
window, at present), as well as an SDL_Renderer. Direct3D will be used to
draw content. Events are received via SDL's usual event functions
(SDL_PollEvent, etc.) If you have a set of existing source files and assets,
you can start adding them to the project now. If not, or if you would like to
make sure that you're setup correctly, some short and simple sample code is
provided below.
6.A. ... when creating a new app
If you are creating a new app (rather than porting an existing SDL-based app),
or if you would just like a simple app to test SDL/WinRT with before trying to
get existing code working, some working SDL/WinRT code is provided below. To
set this up:
- right click on your app's project
- select Add, then New Item. An "Add New Item" dialog will show up.
- from the left-hand list, choose "Visual C++"
- from the middle/main list, choose "C++ File (.cpp)"
- near the bottom of the dialog, next to "Name:", type in a name for your
source file, such as, "main.cpp". - click on the Add button. This will close the dialog, add the new file to
your project, and open the file in Visual C++'s text editor. - Copy and paste the following code into the new file, then save it.
#include <SDL.h>
int main(int argc, char **argv)
{
SDL_DisplayMode mode;
SDL_Window * window = NULL;
SDL_Renderer * renderer = NULL;
SDL_Event evt;
SDL_bool keep_going = SDL_TRUE;
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
return 1;
} else if (SDL_GetCurrentDisplayMode(0, &mode) != 0) {
return 1;
} else if (SDL_CreateWindowAndRenderer(mode.w, mode.h, SDL_WINDOW_FULLSCREEN, &window, &renderer) != 0) {
return 1;
}
while (keep_going) {
while (SDL_PollEvent(&evt)) {
if ((evt.type == SDL_KEYDOWN) && (evt.key.keysym.sym == SDLK_ESCAPE)) {
keep_going = SDL_FALSE;
}
}
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
SDL_Quit();
return 0;
}6.B. Adding code and assets
If you have existing code and assets that you'd like to add, you should be able
to add them now. The process for adding a set of files is as such.
- right click on the app's project
- select Add, then click on "New Item..."
- open any source, header, or asset files as appropriate. Support for C and
C++ is available.
Do note that WinRT only supports a subset of the APIs that are available to
Win32-based apps. Many portions of the Win32 API and the C runtime are not
available.
A list of unsupported C APIs can be found at
http://msdn.microsoft.com/en-us/library/windows/apps/jj606124.aspx
General information on using the C runtime in WinRT can be found at
https://msdn.microsoft.com/en-us/library/hh972425.aspx
A list of supported Win32 APIs for WinRT apps can be found at
http://msdn.microsoft.com/en-us/library/windows/apps/br205757.aspx. To note,
the list of supported Win32 APIs for Windows Phone 8.0 is different.
That list can be found at
http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj662956(v=vs.105).aspx
7. Build and run your app
Your app project should now be setup, and you should be ready to build your app.
To run it on the local machine, open the Debug menu and choose "Start
Debugging". This will build your app, then run your app full-screen. To switch
out of your app, press the Windows key. Alternatively, you can choose to run
your app in a window. To do this, before building and running your app, find
the drop-down menu in Visual C++'s toolbar that says, "Local Machine". Expand
this by clicking on the arrow on the right side of the list, then click on
Simulator. Once you do that, any time you build and run the app, the app will
launch in window, rather than full-screen.
7.A. Running apps on older, ARM-based, "Windows RT" devices
These instructions do not include Windows Phone, despite Windows Phone
typically running on ARM processors. They are specifically for devices
that use the "Windows RT" operating system, which was a modified version of
Windows 8.x that ran primarily on ARM-based tablet computers.
To build and run the app on ARM-based, "Windows RT" devices, you'll need to:
- install Microsoft's "Remote Debugger" on the device. Visual C++ installs and
debugs ARM-based apps via IP networks. - change a few options on the development machine, both to make sure it builds
for ARM (rather than x86 or x64), and to make sure it knows how to find the
Windows RT device (on the network).
Microsoft's Remote Debugger can be found at
https://msdn.microsoft.com/en-us/library/hh441469.aspx. Please note
that separate versions of this debugger exist for different versions of Visual
C++, one each for MSVC 2015, 2013, and 2012.
To setup Visual C++ to launch your app on an ARM device:
- make sure the Remote Debugger is running on your ARM device, and that it's on
the same IP network as your development machine. - from Visual C++'s toolbar, find a drop-down menu that says, "Win32". Click
it, then change the value to "ARM". - make sure Visual C++ knows the hostname or IP address of the ARM device. To
do this:- open the app project's properties
- select "Debugging"
- next to "Machine Name", enter the hostname or IP address of the ARM
device - if, and only if, you've turned off authentication in the Remote Debugger,
then change the setting for "Require Authentication" to No - click "OK"
- build and run the app (from Visual C++). The first time you do this, a
prompt will show up on the ARM device, asking for a Microsoft Account. You
do, unfortunately, need to log in here, and will need to follow the
subsequent registration steps in order to launch the app. After you do so,
if the app didn't already launch, try relaunching it again from within Visual
C++.
Troubleshooting
Build fails with message, "error LNK2038: mismatch detected for 'vccorlib_lib_should_be_specified_before_msvcrt_lib_to_linker'"
Try adding the following to your linker flags. In MSVC, this can be done by
right-clicking on the app project, navigating to Configuration Properties ->
Linker -> Command Line, then adding them to the Additional Options
section.
For Release builds / MSVC-Configurations, add:
/nodefaultlib:vccorlib /nodefaultlib:msvcrt vccorlib.lib msvcrt.lib
For Debug builds / MSVC-Configurations, add:
/nodefaultlib:vccorlibd /nodefaultlib:msvcrtd vccorlibd.lib msvcrtd.lib
Mouse-motion events fail to get sent, or SDL_GetMouseState() fails to return updated values
This may be caused by a bug in Windows itself, whereby hiding the mouse
cursor can cause mouse-position reporting to fail.
SDL provides a workaround for this, but it requires that an app links to a
set of Win32-style cursor image-resource files. A copy of suitable resource
files can be found in src/main/winrt/. Adding them to an app's Visual C++
project file should be sufficient to get the app to use them.
SDL's Visual Studio project file fails to open, with message, "The system can't find the file specified."
This can be caused for any one of a few reasons, which Visual Studio can
report, but won't always do so in an up-front manner.
To help determine why this error comes up:
- open a copy of Visual Studio without opening a project file. This can be
accomplished via Windows' Start Menu, among other means. - show Visual Studio's Output window. This can be done by going to VS'
menu bar, then to View, and then to Output. - try opening the SDL project file directly by going to VS' menu bar, then
to File, then to Open, then to Project/Solution. When a File-Open dialog
appears, open the SDL project (such as the one in SDL's source code, in its
directory, VisualC-WinRT/UWP_VS2015/). - after attempting to open SDL's Visual Studio project file, additional error
information will be output to the Output window.
If Visual Studio reports (via its Output window) that the project:
"could not be loaded because it's missing install components. To fix this launch Visual Studio setup with the following selections:
Microsoft.VisualStudio.ComponentGroup.UWP.VC"
... then you will need to re-launch Visual Studio's installer, and make sure that
the workflow for "Universal Windows Platform development" is checked, and that its
optional component, "C++ Universal Windows Platform tools" is also checked. While
you are there, if you are planning on targeting UWP / Windows 10, also make sure
that you check the optional component, "Windows 10 SDK (10.0.10240.0)". After
making sure these items are checked as-appropriate, install them.
Once you install these components, try re-launching Visual Studio, and re-opening
the SDL project file. If you still get the error dialog, try using the Output
window, again, seeing what Visual Studio says about it.
Game controllers / joysticks aren't working!
Windows only permits certain game controllers and joysticks to work within
WinRT / UWP apps. Even if a game controller or joystick works in a Win32
app, that device is not guaranteed to work inside a WinRT / UWP app.
According to Microsoft, "Xbox compatible controllers" should work inside
UWP apps, potentially with more working in the future. This includes, but
may not be limited to, Microsoft-made Xbox controllers and USB adapters.
(Source: https://social.msdn.microsoft.com/Forums/en-US/9064838b-e8c3-4c18-8a83-19bf0dfe150d/xinput-fails-to-detect-game-controllers?forum=wpdevelop)
Vs Sdl2 Docs Release Checklist (`vs/sdl2/docs/release_checklist.md`)
Release checklist
When changing the version, run build-scripts/update-version.sh X Y Z,
where X Y Z are the major version, minor version, and patch level. So2 28 1 means "change the version to 2.28.1". This script does much of the
mechanical work.
New feature release
Update
WhatsNew.txtBump version number to 2.EVEN.0:
./build-scripts/update-version.sh 2 EVEN 0
Do the release
Update the website file include/header.inc.php to reflect the new version
New bugfix release
Check that no new API/ABI was added
- If it was, do a new feature release (see above) instead
Bump version number from 2.Y.Z to 2.Y.(Z+1) (Y is even)
./build-scripts/update-version.sh 2 Y Z+1
Do the release
Update the website file include/header.inc.php to reflect the new version
After a feature release
Create a branch like
release-2.24.xBump version number to 2.ODD.0 for next development branch
./build-scripts/update-version.sh 2 ODD 0
New development prerelease
Bump version number from 2.Y.Z to 2.Y.(Z+1) (Y is odd)
./build-scripts/update-version.sh 2 Y Z+1
Do the release
Vs Zlib Doc Algorithm (`vs/zlib/doc/algorithm.txt`)
- Compression algorithm (deflate)
The deflation algorithm used by gzip (also zip and zlib) is a variation of
LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in
the input data. The second occurrence of a string is replaced by a
pointer to the previous string, in the form of a pair (distance,
length). Distances are limited to 32K bytes, and lengths are limited
to 258 bytes. When a string does not occur anywhere in the previous
32K bytes, it is emitted as a sequence of literal bytes. (In this
description, `string' must be taken as an arbitrary sequence of bytes,
and is not restricted to printable characters.)
Literals or match lengths are compressed with one Huffman tree, and
match distances are compressed with another tree. The trees are stored
in a compact form at the start of each block. The blocks can have any
size (except that the compressed data for one block must fit in
available memory). A block is terminated when deflate() determines that
it would be useful to start another block with fresh trees. (This is
somewhat similar to the behavior of LZW-based compress.)
Duplicated strings are found using a hash table. All input strings of
length 3 are inserted in the hash table. A hash index is computed for
the next 3 bytes. If the hash chain for this index is not empty, all
strings in the chain are compared with the current input string, and
the longest match is selected.
The hash chains are searched starting with the most recent strings, to
favor small distances and thus take advantage of the Huffman encoding.
The hash chains are singly linked. There are no deletions from the
hash chains, the algorithm simply discards matches that are too old.
To avoid a worst-case situation, very long hash chains are arbitrarily
truncated at a certain length, determined by a runtime option (level
parameter of deflateInit). So deflate() does not always find the longest
possible match but generally finds a match which is long enough.
deflate() also defers the selection of matches with a lazy evaluation
mechanism. After a match of length N has been found, deflate() searches for
a longer match at the next input byte. If a longer match is found, the
previous match is truncated to a length of one (thus producing a single
literal byte) and the process of lazy evaluation begins again. Otherwise,
the original match is kept, and the next match search is attempted only N
steps later.
The lazy match evaluation is also subject to a runtime parameter. If
the current match is long enough, deflate() reduces the search for a longer
match, thus speeding up the whole process. If compression ratio is more
important than speed, deflate() attempts a complete second search even if
the first match is already long enough.
The lazy match evaluation is not performed for the fastest compression
modes (level parameter 1 to 3). For these fast modes, new strings
are inserted in the hash table only when no match was found, or
when the match is not too long. This degrades the compression ratio
but saves time since there are both fewer insertions and fewer searches.
- Decompression algorithm (inflate)
2.1 Introduction
The key question is how to represent a Huffman code (or any prefix code) so
that you can decode fast. The most important characteristic is that shorter
codes are much more common than longer codes, so pay attention to decoding the
short codes fast, and let the long codes take longer to decode.
inflate() sets up a first level table that covers some number of bits of
input less than the length of longest code. It gets that many bits from the
stream, and looks it up in the table. The table will tell if the next
code is that many bits or less and how many, and if it is, it will tell
the value, else it will point to the next level table for which inflate()
grabs more bits and tries to decode a longer code.
How many bits to make the first lookup is a tradeoff between the time it
takes to decode and the time it takes to build the table. If building the
table took no time (and if you had infinite memory), then there would only
be a first level table to cover all the way to the longest code. However,
building the table ends up taking a lot longer for more bits since short
codes are replicated many times in such a table. What inflate() does is
simply to make the number of bits in the first table a variable, and then
to set that variable for the maximum speed.
For inflate, which has 286 possible codes for the literal/length tree, the size
of the first table is nine bits. Also the distance trees have 30 possible
values, and the size of the first table is six bits. Note that for each of
those cases, the table ended up one bit longer than the ``average'' code
length, i.e. the code length of an approximately flat code which would be a
little more than eight bits for 286 symbols and a little less than five bits
for 30 symbols.
2.2 More details on the inflate table lookup
Ok, you want to know what this cleverly obfuscated inflate tree actually
looks like. You are correct that it's not a Huffman tree. It is simply a
lookup table for the first, let's say, nine bits of a Huffman symbol. The
symbol could be as short as one bit or as long as 15 bits. If a particular
symbol is shorter than nine bits, then that symbol's translation is duplicated
in all those entries that start with that symbol's bits. For example, if the
symbol is four bits, then it's duplicated 32 times in a nine-bit table. If a
symbol is nine bits long, it appears in the table once.
If the symbol is longer than nine bits, then that entry in the table points
to another similar table for the remaining bits. Again, there are duplicated
entries as needed. The idea is that most of the time the symbol will be short
and there will only be one table look up. (That's whole idea behind data
compression in the first place.) For the less frequent long symbols, there
will be two lookups. If you had a compression method with really long
symbols, you could have as many levels of lookups as is efficient. For
inflate, two is enough.
So a table entry either points to another table (in which case nine bits in
the above example are gobbled), or it contains the translation for the symbol
and the number of bits to gobble. Then you start again with the next
ungobbled bit.
You may wonder: why not just have one lookup table for how ever many bits the
longest symbol is? The reason is that if you do that, you end up spending
more time filling in duplicate symbol entries than you do actually decoding.
At least for deflate's output that generates new trees every several 10's of
kbytes. You can imagine that filling in a 2^15 entry table for a 15-bit code
would take too long if you're only decoding several thousand symbols. At the
other extreme, you could make a new table for every bit in the code. In fact,
that's essentially a Huffman tree. But then you spend too much time
traversing the tree while decoding, even for short symbols.
So the number of bits for the first lookup table is a trade of the time to
fill out the table vs. the time spent looking at the second level and above of
the table.
Here is an example, scaled down:
The code being decoded, with 10 symbols, from 1 to 6 bits long:
A: 0
B: 10
C: 1100
D: 11010
E: 11011
F: 11100
G: 11101
H: 11110
I: 111110
J: 111111
Let's make the first table three bits long (eight entries):
000: A,1
001: A,1
010: A,1
011: A,1
100: B,2
101: B,2
110: -> table X (gobble 3 bits)
111: -> table Y (gobble 3 bits)
Each entry is what the bits decode as and how many bits that is, i.e. how
many bits to gobble. Or the entry points to another table, with the number of
bits to gobble implicit in the size of the table.
Table X is two bits long since the longest code starting with 110 is five bits
long:
00: C,1
01: C,1
10: D,2
11: E,2
Table Y is three bits long since the longest code starting with 111 is six
bits long:
000: F,2
001: F,2
010: G,2
011: G,2
100: H,2
101: H,2
110: I,3
111: J,3
So what we have here are three tables with a total of 20 entries that had to
be constructed. That's compared to 64 entries for a single table. Or
compared to 16 entries for a Huffman tree (six two entry tables and one four
entry table). Assuming that the code ideally represents the probability of
the symbols, it takes on the average 1.25 lookups per symbol. That's compared
to one lookup for the single table, or 1.66 lookups per symbol for the
Huffman tree.
There, I think that gives you a picture of what's going on. For inflate, the
meaning of a particular symbol is often more than just a letter. It can be a
byte (a "literal"), or it can be either a length or a distance which
indicates a base value and a number of bits to fetch after the code that is
added to the base value. Or it might be the special end-of-block code. The
data structures created in inftrees.c try to encode all that information
compactly in the tables.
Jean-loup Gailly Mark Adler
[email protected] [email protected]
References:
[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data
Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3,
pp. 337-343.
``DEFLATE Compressed Data Format Specification'' available in
http://tools.ietf.org/html/rfc1951
Vs Zlib Doc Rfc1950 (`vs/zlib/doc/rfc1950.txt`)
Network Working Group P. Deutsch
Request for Comments: 1950 Aladdin Enterprises
Category: Informational J-L. Gailly
Info-ZIP
May 1996
ZLIB Compressed Data Format Specification version 3.3Status of This Memo
This memo provides information for the Internet community. This memo
does not specify an Internet standard of any kind. Distribution of
this memo is unlimited.
IESG Note:
The IESG takes no position on the validity of any Intellectual
Property Rights statements contained in this document.
Notices
Copyright (c) 1996 L. Peter Deutsch and Jean-Loup Gailly
Permission is granted to copy and distribute this document for any
purpose and without charge, including translations into other
languages and incorporation into compilations, provided that the
copyright notice and this notice are preserved, and that any
substantive changes or deletions from the original are clearly
marked.
A pointer to the latest version of this and related documentation in
HTML format can be found at the URL
ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html.
Abstract
This specification defines a lossless compressed data format. The
data can be produced or consumed, even for an arbitrarily long
sequentially presented input data stream, using only an a priori
bounded amount of intermediate storage. The format presently uses
the DEFLATE compression method but can be easily extended to use
other compression methods. It can be implemented readily in a manner
not covered by patents. This specification also defines the ADLER-32
checksum (an extension and improvement of the Fletcher checksum),
used for detection of data corruption, and provides an algorithm for
computing it.
Deutsch & Gailly Informational [Page 1]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
Table of Contents
Introduction ................................................... 2
1.1. Purpose ................................................... 2
1.2. Intended audience ......................................... 3
1.3. Scope ..................................................... 3
1.4. Compliance ................................................ 3
1.5. Definitions of terms and conventions used ................ 3
1.6. Changes from previous versions ............................ 3Detailed specification ......................................... 3
2.1. Overall conventions ....................................... 3
2.2. Data format ............................................... 4
2.3. Compliance ................................................ 7References ..................................................... 7
Source code .................................................... 8
Security Considerations ........................................ 8
Acknowledgements ............................................... 8
Authors' Addresses ............................................. 8
Appendix: Rationale ............................................ 9
Appendix: Sample code ..........................................10
Introduction
1.1. Purpose
The purpose of this specification is to define a lossless
compressed data format that:text* Is independent of CPU type, operating system, file system, and character set, and hence can be used for interchange; * Can be produced or consumed, even for an arbitrarily long sequentially presented input data stream, using only an a priori bounded amount of intermediate storage, and hence can be used in data communications or similar structures such as Unix filters; * Can use a number of different compression methods; * Can be implemented readily in a manner not covered by patents, and hence can be practiced freely.The data format defined by this specification does not attempt to
allow random access to compressed data.
Deutsch & Gailly Informational [Page 2]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
1.2. Intended audience
This specification is intended for use by implementors of software
to compress data into zlib format and/or decompress data from zlib
format.
The text of the specification assumes a basic background in
programming at the level of bits and other primitive data
representations.1.3. Scope
The specification specifies a compressed data format that can be
used for in-memory compression of a sequence of arbitrary bytes.1.4. Compliance
Unless otherwise indicated below, a compliant decompressor must be
able to accept and decompress any data set that conforms to all
the specifications presented here; a compliant compressor must
produce data sets that conform to all the specifications presented
here.1.5. Definitions of terms and conventions used
byte: 8 bits stored or transmitted as a unit (same as an octet).
(For this specification, a byte is exactly 8 bits, even on
machines which store a character on a number of bits different
from 8.) See below, for the numbering of bits within a byte.1.6. Changes from previous versions
Version 3.1 was the first public release of this specification.
In version 3.2, some terminology was changed and the Adler-32
sample code was rewritten for clarity. In version 3.3, the
support for a preset dictionary was introduced, and the
specification was converted to RFC style.Detailed specification
2.1. Overall conventions
In the diagrams below, a box like this:
text+---+ | | <-- the vertical bars might be missing +---+
Deutsch & Gailly Informational [Page 3]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
represents one byte; a box like this:
+==============+
| |
+==============+
represents a variable number of bytes.
Bytes stored within a computer do not have a "bit order", since
they are always treated as a unit. However, a byte considered as
an integer between 0 and 255 does have a most- and least-
significant bit, and since we write numbers with the most-
significant digit on the left, we also write bytes with the most-
significant bit on the left. In the diagrams below, we number the
bits of a byte so that bit 0 is the least-significant bit, i.e.,
the bits are numbered:
+--------+
|76543210|
+--------+
Within a computer, a number may occupy multiple bytes. All
multi-byte numbers in the format described here are stored with
the MOST-significant byte first (at the lower memory address).
For example, the decimal number 520 is stored as:
0 1
+--------+--------+
|00000010|00001000|
+--------+--------+
^ ^
| |
| + less significant byte = 8
+ more significant byte = 2 x 2562.2. Data format
A zlib stream has the following structure:
0 1
+---+---+
|CMF|FLG| (more-->)
+---+---+Deutsch & Gailly Informational [Page 4]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
(if FLG.FDICT set)
0 1 2 3
+---+---+---+---+
| DICTID | (more-->)
+---+---+---+---+
+=====================+---+---+---+---+
|...compressed data...| ADLER32 |
+=====================+---+---+---+---+
Any data which may appear after ADLER32 are not part of the zlib
stream.
CMF (Compression Method and flags)
This byte is divided into a 4-bit compression method and a 4-
bit information field depending on the compression method.
bits 0 to 3 CM Compression method
bits 4 to 7 CINFO Compression info
CM (Compression method)
This identifies the compression method used in the file. CM = 8
denotes the "deflate" compression method with a window size up
to 32K. This is the method used by gzip and PNG (see
references [1] and [2] in Chapter 3, below, for the reference
documents). CM = 15 is reserved. It might be used in a future
version of this specification to indicate the presence of an
extra field before the compressed data.
CINFO (Compression info)
For CM = 8, CINFO is the base-2 logarithm of the LZ77 window
size, minus eight (CINFO=7 indicates a 32K window size). Values
of CINFO above 7 are not allowed in this version of the
specification. CINFO is not defined in this specification for
CM not equal to 8.
FLG (FLaGs)
This flag byte is divided as follows:
bits 0 to 4 FCHECK (check bits for CMF and FLG)
bit 5 FDICT (preset dictionary)
bits 6 to 7 FLEVEL (compression level)
The FCHECK value must be such that CMF and FLG, when viewed as
a 16-bit unsigned integer stored in MSB order (CMF*256 + FLG),
is a multiple of 31.Deutsch & Gailly Informational [Page 5]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
FDICT (Preset dictionary)
If FDICT is set, a DICT dictionary identifier is present
immediately after the FLG byte. The dictionary is a sequence of
bytes which are initially fed to the compressor without
producing any compressed output. DICT is the Adler-32 checksum
of this sequence of bytes (see the definition of ADLER32
below). The decompressor can use this identifier to determine
which dictionary has been used by the compressor.
FLEVEL (Compression level)
These flags are available for use by specific compression
methods. The "deflate" method (CM = 8) sets these flags as
follows:
0 - compressor used fastest algorithm
1 - compressor used fast algorithm
2 - compressor used default algorithm
3 - compressor used maximum compression, slowest algorithm
The information in FLEVEL is not needed for decompression; it
is there to indicate if recompression might be worthwhile.
compressed data
For compression method 8, the compressed data is stored in the
deflate compressed data format as described in the document
"DEFLATE Compressed Data Format Specification" by L. Peter
Deutsch. (See reference [3] in Chapter 3, below)
Other compressed data formats are not specified in this version
of the zlib specification.
ADLER32 (Adler-32 checksum)
This contains a checksum value of the uncompressed data
(excluding any dictionary data) computed according to Adler-32
algorithm. This algorithm is a 32-bit extension and improvement
of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073
standard. See references [4] and [5] in Chapter 3, below)
Adler-32 is composed of two sums accumulated per byte: s1 is
the sum of all bytes, s2 is the sum of all s1 values. Both sums
are done modulo 65521. s1 is initialized to 1, s2 to zero. The
Adler-32 checksum is stored as s2*65536 + s1 in most-
significant-byte first (network) order.Deutsch & Gailly Informational [Page 6]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
2.3. Compliance
A compliant compressor must produce streams with correct CMF, FLG
and ADLER32, but need not support preset dictionaries. When the
zlib data format is used as part of another standard data format,
the compressor may use only preset dictionaries that are specified
by this other data format. If this other format does not use the
preset dictionary feature, the compressor must not set the FDICT
flag.
A compliant decompressor must check CMF, FLG, and ADLER32, and
provide an error indication if any of these have incorrect values.
A compliant decompressor must give an error indication if CM is
not one of the values defined in this specification (only the
value 8 is permitted in this version), since another value could
indicate the presence of new features that would cause subsequent
data to be interpreted incorrectly. A compliant decompressor must
give an error indication if FDICT is set and DICTID is not the
identifier of a known preset dictionary. A decompressor may
ignore FLEVEL and still be compliant. When the zlib data format
is being used as a part of another standard format, a compliant
decompressor must support all the preset dictionaries specified by
the other format. When the other format does not use the preset
dictionary feature, a compliant decompressor must reject any
stream in which the FDICT flag is set.References
[1] Deutsch, L.P.,"GZIP Compressed Data Format Specification",
available in ftp://ftp.uu.net/pub/archiving/zip/doc/[2] Thomas Boutell, "PNG (Portable Network Graphics) specification",
available in ftp://ftp.uu.net/graphics/png/documents/[3] Deutsch, L.P.,"DEFLATE Compressed Data Format Specification",
available in ftp://ftp.uu.net/pub/archiving/zip/doc/[4] Fletcher, J. G., "An Arithmetic Checksum for Serial
Transmissions," IEEE Transactions on Communications, Vol. COM-30,
No. 1, January 1982, pp. 247-252.[5] ITU-T Recommendation X.224, Annex D, "Checksum Algorithms,"
November, 1993, pp. 144, 145. (Available from
gopher://info.itu.ch). ITU-T X.244 is also the same as ISO 8073.
Deutsch & Gailly Informational [Page 7]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
Source code
Source code for a C language implementation of a "zlib" compliant
library is available at ftp://ftp.uu.net/pub/archiving/zip/zlib/.Security Considerations
A decoder that fails to check the ADLER32 checksum value may be
subject to undetected data corruption.Acknowledgements
Trademarks cited in this document are the property of their
respective owners.Jean-Loup Gailly and Mark Adler designed the zlib format and wrote
the related software described in this specification. Glenn
Randers-Pehrson converted this document to RFC and HTML format.Authors' Addresses
L. Peter Deutsch
Aladdin Enterprises
203 Santa Margarita Ave.
Menlo Park, CA 94025Phone: (415) 322-0103 (AM only)
FAX: (415) 322-1734
EMail: [email protected]Jean-Loup Gailly
EMail: [email protected]
Questions about the technical content of this specification can be
sent by email toJean-Loup Gailly [email protected] and
Mark Adler [email protected]Editorial comments on this specification can be sent by email to
L. Peter Deutsch [email protected] and
Glenn Randers-Pehrson [email protected]
Deutsch & Gailly Informational [Page 8]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
Appendix: Rationale
8.1. Preset dictionaries
A preset dictionary is specially useful to compress short input
sequences. The compressor can take advantage of the dictionary
context to encode the input in a more compact manner. The
decompressor can be initialized with the appropriate context by
virtually decompressing a compressed version of the dictionary
without producing any output. However for certain compression
algorithms such as the deflate algorithm this operation can be
achieved without actually performing any decompression.The compressor and the decompressor must use exactly the same
dictionary. The dictionary may be fixed or may be chosen among a
certain number of predefined dictionaries, according to the kind
of input data. The decompressor can determine which dictionary has
been chosen by the compressor by checking the dictionary
identifier. This document does not specify the contents of
predefined dictionaries, since the optimal dictionaries are
application specific. Standard data formats using this feature of
the zlib specification must precisely define the allowed
dictionaries.8.2. The Adler-32 algorithm
The Adler-32 algorithm is much faster than the CRC32 algorithm yet
still provides an extremely low probability of undetected errors.The modulo on unsigned long accumulators can be delayed for 5552
bytes, so the modulo operation time is negligible. If the bytes
are a, b, c, the second sum is 3a + 2b + c + 3, and so is position
and order sensitive, unlike the first sum, which is just a
checksum. That 65521 is prime is important to avoid a possible
large class of two-byte errors that leave the check unchanged.
(The Fletcher checksum uses 255, which is not prime and which also
makes the Fletcher check insensitive to single byte changes 0 <->
255.)The sum s1 is initialized to 1 instead of zero to make the length
of the sequence part of s2, so that the length does not have to be
checked separately. (Any sequence of zeroes has a Fletcher
checksum of zero.)
Deutsch & Gailly Informational [Page 9]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
Appendix: Sample code
The following C code computes the Adler-32 checksum of a data buffer.
It is written for clarity, not for speed. The sample code is in the
ANSI C programming language. Non C users may find it easier to read
with these hints:& Bitwise AND operator.
textBitwise right shift operator. When applied to an#define BASE 65521 /* largest prime smaller than 65536 */
/*
Update a running Adler-32 checksum with the bytes buf[0..len-1]
and return the updated checksum. The Adler-32 checksum should be
initialized to 1.
Usage example:
unsigned long adler = 1L;
while (read_buffer(buffer, length) != EOF) {
adler = update_adler32(adler, buffer, length);
}
if (adler != original_adler) error();
*/
unsigned long update_adler32(unsigned long adler,
unsigned char *buf, int len)
{
unsigned long s1 = adler & 0xffff;
unsigned long s2 = (adler >> 16) & 0xffff;
int n;
for (n = 0; n < len; n++) {
s1 = (s1 + buf[n]) % BASE;
s2 = (s2 + s1) % BASE;
}
return (s2 << 16) + s1;
}/* Return the adler32 of the bytes buf[0..len-1] */
Deutsch & Gailly Informational [Page 10]
RFC 1950 ZLIB Compressed Data Format Specification May 1996
unsigned long adler32(unsigned char *buf, int len)
{
return update_adler32(1L, buf, len);
}Deutsch & Gailly Informational [Page 11]
Vs Zlib Doc Rfc1951 (`vs/zlib/doc/rfc1951.txt`)
Network Working Group P. Deutsch
Request for Comments: 1951 Aladdin Enterprises
Category: Informational May 1996
DEFLATE Compressed Data Format Specification version 1.3Status of This Memo
This memo provides information for the Internet community. This memo
does not specify an Internet standard of any kind. Distribution of
this memo is unlimited.
IESG Note:
The IESG takes no position on the validity of any Intellectual
Property Rights statements contained in this document.
Notices
Copyright (c) 1996 L. Peter Deutsch
Permission is granted to copy and distribute this document for any
purpose and without charge, including translations into other
languages and incorporation into compilations, provided that the
copyright notice and this notice are preserved, and that any
substantive changes or deletions from the original are clearly
marked.
A pointer to the latest version of this and related documentation in
HTML format can be found at the URL
ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html.
Abstract
This specification defines a lossless compressed data format that
compresses data using a combination of the LZ77 algorithm and Huffman
coding, with efficiency comparable to the best currently available
general-purpose compression methods. The data can be produced or
consumed, even for an arbitrarily long sequentially presented input
data stream, using only an a priori bounded amount of intermediate
storage. The format can be implemented readily in a manner not
covered by patents.
Deutsch Informational [Page 1]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
Table of Contents
Introduction ................................................... 2
1.1. Purpose ................................................... 2
1.2. Intended audience ......................................... 3
1.3. Scope ..................................................... 3
1.4. Compliance ................................................ 3
1.5. Definitions of terms and conventions used ................ 3
1.6. Changes from previous versions ............................ 4Compressed representation overview ............................. 4
Detailed specification ......................................... 5
3.1. Overall conventions ....................................... 5
3.1.1. Packing into bytes .................................. 5
3.2. Compressed block format ................................... 6
3.2.1. Synopsis of prefix and Huffman coding ............... 6
3.2.2. Use of Huffman coding in the "deflate" format ....... 7
3.2.3. Details of block format ............................. 9
3.2.4. Non-compressed blocks (BTYPE=00) ................... 11
3.2.5. Compressed blocks (length and distance codes) ...... 11
3.2.6. Compression with fixed Huffman codes (BTYPE=01) .... 12
3.2.7. Compression with dynamic Huffman codes (BTYPE=10) .. 13
3.3. Compliance ............................................... 14Compression algorithm details ................................. 14
References .................................................... 16
Security Considerations ....................................... 16
Source code ................................................... 16
Acknowledgements .............................................. 16
Author's Address .............................................. 17
Introduction
1.1. Purpose
The purpose of this specification is to define a lossless
compressed data format that:
* Is independent of CPU type, operating system, file system,
and character set, and hence can be used for interchange;
* Can be produced or consumed, even for an arbitrarily long
sequentially presented input data stream, using only an a
priori bounded amount of intermediate storage, and hence
can be used in data communications or similar structures
such as Unix filters;
* Compresses data with efficiency comparable to the best
currently available general-purpose compression methods,
and in particular considerably better than the "compress"
program;
* Can be implemented readily in a manner not covered by
patents, and hence can be practiced freely;
Deutsch Informational [Page 2]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
* Is compatible with the file format produced by the current
widely used gzip utility, in that conforming decompressors
will be able to read data produced by the existing gzip
compressor.
The data format defined by this specification does not attempt to:
* Allow random access to compressed data;
* Compress specialized data (e.g., raster graphics) as well
as the best currently available specialized algorithms.
A simple counting argument shows that no lossless compression
algorithm can compress every possible input data set. For the
format defined here, the worst case expansion is 5 bytes per 32K-
byte block, i.e., a size increase of 0.015% for large data sets.
English text usually compresses by a factor of 2.5 to 3;
executable files usually compress somewhat less; graphical data
such as raster images may compress much more.1.2. Intended audience
This specification is intended for use by implementors of software
to compress data into "deflate" format and/or decompress data from
"deflate" format.
The text of the specification assumes a basic background in
programming at the level of bits and other primitive data
representations. Familiarity with the technique of Huffman coding
is helpful but not required.1.3. Scope
The specification specifies a method for representing a sequence
of bytes as a (usually shorter) sequence of bits, and a method for
packing the latter bit sequence into bytes.1.4. Compliance
Unless otherwise indicated below, a compliant decompressor must be
able to accept and decompress any data set that conforms to all
the specifications presented here; a compliant compressor must
produce data sets that conform to all the specifications presented
here.1.5. Definitions of terms and conventions used
Byte: 8 bits stored or transmitted as a unit (same as an octet).
For this specification, a byte is exactly 8 bits, even on machinesDeutsch Informational [Page 3]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
which store a character on a number of bits different from eight.
See below, for the numbering of bits within a byte.
String: a sequence of arbitrary bytes.1.6. Changes from previous versions
There have been no technical changes to the deflate format since
version 1.1 of this specification. In version 1.2, some
terminology was changed. Version 1.3 is a conversion of the
specification to RFC style.Compressed representation overview
A compressed data set consists of a series of blocks, corresponding
to successive blocks of input data. The block sizes are arbitrary,
except that non-compressible blocks are limited to 65,535 bytes.Each block is compressed using a combination of the LZ77 algorithm
and Huffman coding. The Huffman trees for each block are independent
of those for previous or subsequent blocks; the LZ77 algorithm may
use a reference to a duplicated string occurring in a previous block,
up to 32K input bytes before.Each block consists of two parts: a pair of Huffman code trees that
describe the representation of the compressed data part, and a
compressed data part. (The Huffman trees themselves are compressed
using Huffman encoding.) The compressed data consists of a series of
elements of two types: literal bytes (of strings that have not been
detected as duplicated within the previous 32K input bytes), and
pointers to duplicated strings, where a pointer is represented as a
pair <length, backward distance>. The representation used in the
"deflate" format limits distances to 32K bytes and lengths to 258
bytes, but does not limit the size of a block, except for
uncompressible blocks, which are limited as noted above.Each type of value (literals, distances, and lengths) in the
compressed data is represented using a Huffman code, using one code
tree for literals and lengths and a separate code tree for distances.
The code trees for each block appear in a compact form just before
the compressed data for that block.
Deutsch Informational [Page 4]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
Detailed specification
3.1. Overall conventions In the diagrams below, a box like this:
text+---+ | | <-- the vertical bars might be missing +---+represents one byte; a box like this:
text+==============+ | | +==============+represents a variable number of bytes.
Bytes stored within a computer do not have a "bit order", since
they are always treated as a unit. However, a byte considered as
an integer between 0 and 255 does have a most- and least-
significant bit, and since we write numbers with the most-
significant digit on the left, we also write bytes with the most-
significant bit on the left. In the diagrams below, we number the
bits of a byte so that bit 0 is the least-significant bit, i.e.,
the bits are numbered:text+--------+ |76543210| +--------+Within a computer, a number may occupy multiple bytes. All
multi-byte numbers in the format described here are stored with
the least-significant byte first (at the lower memory address).
For example, the decimal number 520 is stored as:text0 1 +--------+--------+ |00001000|00000010| +--------+--------+ ^ ^ | | | + more significant byte = 2 x 256 + less significant byte = 83.1.1. Packing into bytes
textThis document does not address the issue of the order in which bits of a byte are transmitted on a bit-sequential medium, since the final data format described here is byte- rather than
Deutsch Informational [Page 5]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
bit-oriented. However, we describe the compressed block format
in below, as a sequence of data elements of various bit
lengths, not a sequence of bytes. We must therefore specify
how to pack these data elements into bytes to form the final
compressed byte sequence:
* Data elements are packed into bytes in order of
increasing bit number within the byte, i.e., starting
with the least-significant bit of the byte.
* Data elements other than Huffman codes are packed
starting with the least-significant bit of the data
element.
* Huffman codes are packed starting with the most-
significant bit of the code.
In other words, if one were to print out the compressed data as
a sequence of bytes, starting with the first byte at the
*right* margin and proceeding to the *left*, with the most-
significant bit of each byte on the left as usual, one would be
able to parse the result from right to left, with fixed-width
elements in the correct MSB-to-LSB order and Huffman codes in
bit-reversed order (i.e., with the first bit of the code in the
relative LSB position).3.2. Compressed block format
3.2.1. Synopsis of prefix and Huffman coding
Prefix coding represents symbols from an a priori known
alphabet by bit sequences (codes), one code for each symbol, in
a manner such that different symbols may be represented by bit
sequences of different lengths, but a parser can always parse
an encoded string unambiguously symbol-by-symbol.
We define a prefix code in terms of a binary tree in which the
two edges descending from each non-leaf node are labeled 0 and
1 and in which the leaf nodes correspond one-for-one with (are
labeled with) the symbols of the alphabet; then the code for a
symbol is the sequence of 0's and 1's on the edges leading from
the root to the leaf labeled with that symbol. For example:Deutsch Informational [Page 6]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
/\ Symbol Code
0 1 ------ ----
/ \ A 00
/\ B B 1
0 1 C 011
/ \ D 010
A /\
0 1
/ \
D C
A parser can decode the next symbol from an encoded input
stream by walking down the tree from the root, at each step
choosing the edge corresponding to the next input bit.
Given an alphabet with known symbol frequencies, the Huffman
algorithm allows the construction of an optimal prefix code
(one which represents strings with those symbol frequencies
using the fewest bits of any possible prefix codes for that
alphabet). Such a code is called a Huffman code. (See
reference [1] in Chapter 5, references for additional
information on Huffman codes.)
Note that in the "deflate" format, the Huffman codes for the
various alphabets must not exceed certain maximum code lengths.
This constraint complicates the algorithm for computing code
lengths from symbol frequencies. Again, see Chapter 5,
references for details.
3.2.2. Use of Huffman coding in the "deflate" format
The Huffman codes used for each alphabet in the "deflate"
format have two additional rules:
* All codes of a given bit length have lexicographically
consecutive values, in the same order as the symbols
they represent;
* Shorter codes lexicographically precede longer codes.Deutsch Informational [Page 7]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
We could recode the example above to follow this rule as
follows, assuming that the order of the alphabet is ABCD:
Symbol Code
------ ----
A 10
B 0
C 110
D 111
I.e., 0 precedes 10 which precedes 11x, and 110 and 111 are
lexicographically consecutive.
Given this rule, we can define the Huffman code for an alphabet
just by giving the bit lengths of the codes for each symbol of
the alphabet in order; this is sufficient to determine the
actual codes. In our example, the code is completely defined
by the sequence of bit lengths (2, 1, 3, 3). The following
algorithm generates the codes as integers, intended to be read
from most- to least-significant bit. The code lengths are
initially in tree[I].Len; the codes are produced in
tree[I].Code.
1) Count the number of codes for each code length. Let
bl_count[N] be the number of codes of length N, N >= 1.
2) Find the numerical value of the smallest code for each
code length:
code = 0;
bl_count[0] = 0;
for (bits = 1; bits <= MAX_BITS; bits++) {
code = (code + bl_count[bits-1]) << 1;
next_code[bits] = code;
}
3) Assign numerical values to all codes, using consecutive
values for all codes of the same length with the base
values determined at step 2. Codes that are never used
(which have a bit length of zero) must not be assigned a
value.
for (n = 0; n <= max_code; n++) {
len = tree[n].Len;
if (len != 0) {
tree[n].Code = next_code[len];
next_code[len]++;
}Deutsch Informational [Page 8]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
}
Example:
Consider the alphabet ABCDEFGH, with bit lengths (3, 3, 3, 3,
3, 2, 4, 4). After step 1, we have:
N bl_count[N]
- -----------
2 1
3 5
4 2
Step 2 computes the following next_code values:
N next_code[N]
- ------------
1 0
2 0
3 2
4 14
Step 3 produces the following code values:
Symbol Length Code
------ ------ ----
A 3 010
B 3 011
C 3 100
D 3 101
E 3 110
F 2 00
G 4 1110
H 4 1111
3.2.3. Details of block format
Each block of compressed data begins with 3 header bits
containing the following data:
first bit BFINAL
next 2 bits BTYPE
Note that the header bits do not necessarily begin on a byte
boundary, since a block does not necessarily occupy an integral
number of bytes.Deutsch Informational [Page 9]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
BFINAL is set if and only if this is the last block of the data
set.
BTYPE specifies how the data are compressed, as follows:
00 - no compression
01 - compressed with fixed Huffman codes
10 - compressed with dynamic Huffman codes
11 - reserved (error)
The only difference between the two compressed cases is how the
Huffman codes for the literal/length and distance alphabets are
defined.
In all cases, the decoding algorithm for the actual data is as
follows:
do
read block header from input stream.
if stored with no compression
skip any remaining bits in current partially
processed byte
read LEN and NLEN (see next section)
copy LEN bytes of data to output
otherwise
if compressed with dynamic Huffman codes
read representation of code trees (see
subsection below)
loop (until end of block code recognized)
decode literal/length value from input stream
if value < 256
copy value (literal byte) to output stream
otherwise
if value = end of block (256)
break from loop
otherwise (value = 257..285)
decode distance from input stream
move backwards distance bytes in the output
stream, and copy length bytes from this
position to the output stream.
end loop
while not last block
Note that a duplicated string reference may refer to a string
in a previous block; i.e., the backward distance may cross one
or more block boundaries. However a distance cannot refer past
the beginning of the output stream. (An application using aDeutsch Informational [Page 10]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
preset dictionary might discard part of the output stream; a
distance can refer to that part of the output stream anyway)
Note also that the referenced string may overlap the current
position; for example, if the last 2 bytes decoded have values
X and Y, a string reference with <length = 5, distance = 2>
adds X,Y,X,Y,X to the output stream.
We now specify each compression method in turn.
3.2.4. Non-compressed blocks (BTYPE=00)
Any bits of input up to the next byte boundary are ignored.
The rest of the block consists of the following information:
0 1 2 3 4...
+---+---+---+---+================================+
| LEN | NLEN |... LEN bytes of literal data...|
+---+---+---+---+================================+
LEN is the number of data bytes in the block. NLEN is the
one's complement of LEN.
3.2.5. Compressed blocks (length and distance codes)
As noted above, encoded data blocks in the "deflate" format
consist of sequences of symbols drawn from three conceptually
distinct alphabets: either literal bytes, from the alphabet of
byte values (0..255), or <length, backward distance> pairs,
where the length is drawn from (3..258) and the distance is
drawn from (1..32,768). In fact, the literal and length
alphabets are merged into a single alphabet (0..285), where
values 0..255 represent literal bytes, the value 256 indicates
end-of-block, and values 257..285 represent length codes
(possibly in conjunction with extra bits following the symbol
code) as follows:Deutsch Informational [Page 11]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
Extra Extra Extra
Code Bits Length(s) Code Bits Lengths Code Bits Length(s)
---- ---- ------ ---- ---- ------- ---- ---- -------
257 0 3 267 1 15,16 277 4 67-82
258 0 4 268 1 17,18 278 4 83-98
259 0 5 269 2 19-22 279 4 99-114
260 0 6 270 2 23-26 280 4 115-130
261 0 7 271 2 27-30 281 5 131-162
262 0 8 272 2 31-34 282 5 163-194
263 0 9 273 3 35-42 283 5 195-226
264 0 10 274 3 43-50 284 5 227-257
265 1 11,12 275 3 51-58 285 0 258
266 1 13,14 276 3 59-66
The extra bits should be interpreted as a machine integer
stored with the most-significant bit first, e.g., bits 1110
represent the value 14.
Extra Extra Extra
Code Bits Dist Code Bits Dist Code Bits Distance
---- ---- ---- ---- ---- ------ ---- ---- --------
0 0 1 10 4 33-48 20 9 1025-1536
1 0 2 11 4 49-64 21 9 1537-2048
2 0 3 12 5 65-96 22 10 2049-3072
3 0 4 13 5 97-128 23 10 3073-4096
4 1 5,6 14 6 129-192 24 11 4097-6144
5 1 7,8 15 6 193-256 25 11 6145-8192
6 2 9-12 16 7 257-384 26 12 8193-12288
7 2 13-16 17 7 385-512 27 12 12289-16384
8 3 17-24 18 8 513-768 28 13 16385-24576
9 3 25-32 19 8 769-1024 29 13 24577-32768
3.2.6. Compression with fixed Huffman codes (BTYPE=01)
The Huffman codes for the two alphabets are fixed, and are not
represented explicitly in the data. The Huffman code lengths
for the literal/length alphabet are:
Lit Value Bits Codes
--------- ---- -----
0 - 143 8 00110000 through
10111111
144 - 255 9 110010000 through
111111111
256 - 279 7 0000000 through
0010111
280 - 287 8 11000000 through
11000111Deutsch Informational [Page 12]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
The code lengths are sufficient to generate the actual codes,
as described above; we show the codes in the table for added
clarity. Literal/length values 286-287 will never actually
occur in the compressed data, but participate in the code
construction.
Distance codes 0-31 are represented by (fixed-length) 5-bit
codes, with possible additional bits as shown in the table
shown in Paragraph 3.2.5, above. Note that distance codes 30-
31 will never actually occur in the compressed data.
3.2.7. Compression with dynamic Huffman codes (BTYPE=10)
The Huffman codes for the two alphabets appear in the block
immediately after the header bits and before the actual
compressed data, first the literal/length code and then the
distance code. Each code is defined by a sequence of code
lengths, as discussed in Paragraph 3.2.2, above. For even
greater compactness, the code length sequences themselves are
compressed using a Huffman code. The alphabet for code lengths
is as follows:
0 - 15: Represent code lengths of 0 - 15
16: Copy the previous code length 3 - 6 times.
The next 2 bits indicate repeat length
(0 = 3, ... , 3 = 6)
Example: Codes 8, 16 (+2 bits 11),
16 (+2 bits 10) will expand to
12 code lengths of 8 (1 + 6 + 5)
17: Repeat a code length of 0 for 3 - 10 times.
(3 bits of length)
18: Repeat a code length of 0 for 11 - 138 times
(7 bits of length)
A code length of 0 indicates that the corresponding symbol in
the literal/length or distance alphabet will not occur in the
block, and should not participate in the Huffman code
construction algorithm given earlier. If only one distance
code is used, it is encoded using one bit, not zero bits; in
this case there is a single code length of one, with one unused
code. One distance code of zero bits means that there are no
distance codes used at all (the data is all literals).
We can now define the format of the block:
5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286)
5 Bits: HDIST, # of Distance codes - 1 (1 - 32)
4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19)Deutsch Informational [Page 13]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
(HCLEN + 4) x 3 bits: code lengths for the code length
alphabet given just above, in the order: 16, 17, 18,
0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
These code lengths are interpreted as 3-bit integers
(0-7); as above, a code length of 0 means the
corresponding symbol (literal/length or distance code
length) is not used.
HLIT + 257 code lengths for the literal/length alphabet,
encoded using the code length Huffman code
HDIST + 1 code lengths for the distance alphabet,
encoded using the code length Huffman code
The actual compressed data of the block,
encoded using the literal/length and distance Huffman
codes
The literal/length symbol 256 (end of data),
encoded using the literal/length Huffman code
The code length repeat codes can cross from HLIT + 257 to the
HDIST + 1 code lengths. In other words, all code lengths form
a single sequence of HLIT + HDIST + 258 values.3.3. Compliance
A compressor may limit further the ranges of values specified in
the previous section and still be compliant; for example, it may
limit the range of backward pointers to some value smaller than
32K. Similarly, a compressor may limit the size of blocks so that
a compressible block fits in memory.
A compliant decompressor must accept the full range of possible
values defined in the previous section, and must accept blocks of
arbitrary size.Compression algorithm details
While it is the intent of this document to define the "deflate"
compressed data format without reference to any particular
compression algorithm, the format is related to the compressed
formats produced by LZ77 (Lempel-Ziv 1977, see reference [2] below);
since many variations of LZ77 are patented, it is strongly
recommended that the implementor of a compressor follow the general
algorithm presented here, which is known not to be patented per se.
The material in this section is not part of the definition of the
Deutsch Informational [Page 14]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
specification per se, and a compressor need not follow it in order to
be compliant.
The compressor terminates a block when it determines that starting a
new block with fresh trees would be useful, or when the block size
fills up the compressor's block buffer.
The compressor uses a chained hash table to find duplicated strings,
using a hash function that operates on 3-byte sequences. At any
given point during compression, let XYZ be the next 3 input bytes to
be examined (not necessarily all different, of course). First, the
compressor examines the hash chain for XYZ. If the chain is empty,
the compressor simply writes out X as a literal byte and advances one
byte in the input. If the hash chain is not empty, indicating that
the sequence XYZ (or, if we are unlucky, some other 3 bytes with the
same hash function value) has occurred recently, the compressor
compares all strings on the XYZ hash chain with the actual input data
sequence starting at the current point, and selects the longest
match.
The compressor searches the hash chains starting with the most recent
strings, to favor small distances and thus take advantage of the
Huffman encoding. The hash chains are singly linked. There are no
deletions from the hash chains; the algorithm simply discards matches
that are too old. To avoid a worst-case situation, very long hash
chains are arbitrarily truncated at a certain length, determined by a
run-time parameter.
To improve overall compression, the compressor optionally defers the
selection of matches ("lazy matching"): after a match of length N has
been found, the compressor searches for a longer match starting at
the next input byte. If it finds a longer match, it truncates the
previous match to a length of one (thus producing a single literal
byte) and then emits the longer match. Otherwise, it emits the
original match, and, as described above, advances N bytes before
continuing.
Run-time parameters also control this "lazy match" procedure. If
compression ratio is most important, the compressor attempts a
complete second search regardless of the length of the first match.
In the normal case, if the current match is "long enough", the
compressor reduces the search for a longer match, thus speeding up
the process. If speed is most important, the compressor inserts new
strings in the hash table only when no match was found, or when the
match is not "too long". This degrades the compression ratio but
saves time since there are both fewer insertions and fewer searches.
Deutsch Informational [Page 15]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
References
[1] Huffman, D. A., "A Method for the Construction of Minimum
Redundancy Codes", Proceedings of the Institute of Radio
Engineers, September 1952, Volume 40, Number 9, pp. 1098-1101.[2] Ziv J., Lempel A., "A Universal Algorithm for Sequential Data
Compression", IEEE Transactions on Information Theory, Vol. 23,
No. 3, pp. 337-343.[3] Gailly, J.-L., and Adler, M., ZLIB documentation and sources,
available in ftp://ftp.uu.net/pub/archiving/zip/doc/[4] Gailly, J.-L., and Adler, M., GZIP documentation and sources,
available as gzip-*.tar in ftp://prep.ai.mit.edu/pub/gnu/[5] Schwartz, E. S., and Kallick, B. "Generating a canonical prefix
encoding." Comm. ACM, 7,3 (Mar. 1964), pp. 166-169.[6] Hirschberg and Lelewer, "Efficient decoding of prefix codes,"
Comm. ACM, 33,4, April 1990, pp. 449-459.Security Considerations
Any data compression method involves the reduction of redundancy in
the data. Consequently, any corruption of the data is likely to have
severe effects and be difficult to correct. Uncompressed text, on
the other hand, will probably still be readable despite the presence
of some corrupted bytes.It is recommended that systems using this data format provide some
means of validating the integrity of the compressed data. See
reference [3], for example.Source code
Source code for a C language implementation of a "deflate" compliant
compressor and decompressor is available within the zlib package at
ftp://ftp.uu.net/pub/archiving/zip/zlib/.Acknowledgements
Trademarks cited in this document are the property of their
respective owners.Phil Katz designed the deflate format. Jean-Loup Gailly and Mark
Adler wrote the related software described in this specification.
Glenn Randers-Pehrson converted this document to RFC and HTML format.
Deutsch Informational [Page 16]
RFC 1951 DEFLATE Compressed Data Format Specification May 1996
Author's Address
L. Peter Deutsch
Aladdin Enterprises
203 Santa Margarita Ave.
Menlo Park, CA 94025Phone: (415) 322-0103 (AM only)
FAX: (415) 322-1734
EMail: [email protected]Questions about the technical content of this specification can be
sent by email to:Jean-Loup Gailly [email protected] and
Mark Adler [email protected]Editorial comments on this specification can be sent by email to:
L. Peter Deutsch [email protected] and
Glenn Randers-Pehrson [email protected]
Deutsch Informational [Page 17]
Vs Zlib Doc Rfc1952 (`vs/zlib/doc/rfc1952.txt`)
Network Working Group P. Deutsch
Request for Comments: 1952 Aladdin Enterprises
Category: Informational May 1996
GZIP file format specification version 4.3Status of This Memo
This memo provides information for the Internet community. This memo
does not specify an Internet standard of any kind. Distribution of
this memo is unlimited.
IESG Note:
The IESG takes no position on the validity of any Intellectual
Property Rights statements contained in this document.
Notices
Copyright (c) 1996 L. Peter Deutsch
Permission is granted to copy and distribute this document for any
purpose and without charge, including translations into other
languages and incorporation into compilations, provided that the
copyright notice and this notice are preserved, and that any
substantive changes or deletions from the original are clearly
marked.
A pointer to the latest version of this and related documentation in
HTML format can be found at the URL
ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html.
Abstract
This specification defines a lossless