Altirra Help: Introduction

Toggle theme
Altirra Help
Introduction

This help file is the documentation for Altirra, an emulator that simulates the hardware of the Atari 8-bit computer systems, including the Atari 800, 800XL, and 130XE computers.


Altirra Help: Frequently Asked Questions (FAQ)

Frequently Asked Questions (FAQ)
Contents
  • 1. System requirements
    • 1.1. Why does Altirra require a 1GHz processor to emulate a 1.79MHz machine?
    • 1.2. How fast should I expect Altirra to run?
    • 1.3. Would multiple CPU cores help?
  • 2. For first timers
    • 2.1. What's that beeping sound I hear sometimes when a game is loading?
    • 2.2. Okay, what about the grinding sound when I don't have a disk set?
  • 3. Hardware support
    • 3.1. Does Altirra support the Atari 2600 or 7800?
  • 4. Input
    • 4.1. How come key presses don't always seem to register in the emulator?
    • 4.2. How come Shift+2 doesn't work like it does on the Atari?
    • 4.3. How do I type Ctrl+Esc and Ctrl+Shift+Esc?
    • 4.4. How come Ctrl+Shift+0 doesn't work in Windows Vista/7?
    • 4.5. How do I make the arrow keys work?
    • 4.6. Why does pressing Caps Lock turn all characters lowercase, instead of toggling uppercase?
  • 5. Display
    • 5.1. What are NTSC and PAL?
    • 5.2. Why is part of the display cut off?
    • 5.3. Why do the colors seem subdued in PAL programs?
    • 5.4. How come when I run in VBXE mode, palette changes don't take place immediately and I see color mismatches in programs?
    • 5.5. Why can't I use a palette file (*.pal, *.act)?
    • 5.6. Why is PAL mode a little jittery?
  • 6. Sound
    • 6.1. The sound seems a bit crackly. How do I fix this?
    • 6.2. How do I reduce the delay between inputs and the sound?
    • 6.3. Why does the sound stutter when the emulation speed is adjusted?
  • 7. Cartridges
    • 7.1. Why doesn't cartridge X work with cartridge Y when both are used together?
  • 8. Emulation options
    • 8.1. Why can't I choose PAL mode when 5200 hardware mode is selected?
    • 8.2. How come the BASIC option is inverted after I switched to custom OS firmware?
    • 8.3. Why is the BASIC option grayed out in 400/800/1200XL mode?
    • 8.4. Why can't I change memory size, firmware, or BASIC while Ultimate1MB is enabled?
  • 9. Other
    • 9.1. Can I use a physical Atari disk drive over a serial or USB adapter with Altirra?

System requirements

Why does Altirra require a 1GHz processor to emulate a 1.79MHz machine?

Yeah, that does seem a bit inefficient, doesn't it?

Altirra uses a precise but slow way to emulate the Atari hardware: it emulates hardware on a per-cycle basis. This is required to properly sequence memory accesses between ANTIC and the 6502 CPU. Event queues are used whenever possible to reduce the load, particularly with GTIA and POKEY, but there are still patterns of memory writes to hardware registers that will force frequent synchronization.

In the past, emulators have made some compromises like atomically executing 6502 instructions and only firing POKEY interrupts on a scan line basis. This was required for good performance on older machines and is still required on some embedded platforms, but doing this introduces slight emulation inaccuracies. Altirra deliberately uses slower algorithms in order to do more precise emulation.

Further optimizations are always possible, but in general it takes a lot of cycles on a sequential CPU to precisely emulate parallel hardware.

How fast should I expect Altirra to run?

On a 2.5GHz Core 2 Duo, I get from about 300-950 fps in NTSC (500-1500%) in turbo/warp mode. On a 1.6GHz Atom, it's more about 150-300 fps (250-500%).

You can generally expect the emulation to run in real-time, so it's more of a question of how fast it can run in turbo. After all, load times are probably the least interesting part of the Atari experience to emulate.

Would multiple CPU cores help?

No. To take advantage of multiple cores, you need to have multiple workloads that are relatively decoupled so the cores can work separately. Precision cycle-level emulation is the exact opposite case where everything is tightly coupled together.

It is possible to offload tasks that aren't in the critical path and aren't visible to programs running in emulation, such as the latter parts of audio and video generation. Unfortunately, these aren't very time-consuming with the Atari hardware, so there isn't much to offload.

For first timers

What's that beeping sound I hear sometimes when a game is loading?

That's the Atari disk load sound. Specifically, the serial I/O (SIO) routines in the Atari OS normally set up the POKEY chip to play audible tones when reading data from the floppy disk drive. You don't often hear it in emulators because either the emulator doesn't emulate it or the emulator is accelerating the disk load (SIO patch). If you are hearing it in Altirra, it means that the disk load is unaccelerated.

Okay, what about the grinding sound when I don't have a disk set?

That's the XL/XE OS polling for non-disk devices. Just wait a couple of seconds, or enable the "fast boot" firmware option.

Hardware support

Does Altirra support the Atari 2600 or 7800?

No, these are completely different architectures to the 800/5200 lineage. Fundamentally, the 2600 uses a CPU-driven display, while the 800/5200 are primarily playfield DMA driven and the 7800 uses a line-buffered renderer.

The 5200 SuperSystem did support a 2600 adapter, but it was essentially a 2600 mounted on an expansion board that used a video bypass on the 5200.

Input

How come key presses don't always seem to register in the emulator?

Sometimes programs periodically check for a key being held down instead of trapping the key press interrupt. By default, the keyboard mappings are set up in Altirra so that typing a character on the host PC pushes one character into the emulation. This prevents the emulated OS from duplicating or dropping keys, even at warp speed. It also allows the emulator to queue keys so you can type faster than you could on a real Atari. However, it sometimes means that the emulated key presses are too quick for the program to see.

Under Input > Keyboard, you can change the keyboard mode from cooked mode to raw mode so that the emulation sees the keys being held down.

How come Shift+2 doesn't work like it does on the Atari?

Altirra maps the keyboard by symbol when possible so that characters you type on the host PC match the ones produced in the emulation. You typed an @ (Shift+2 on the host), but the Atari has " in that spot instead. The upside is that you don't have to deal with having the Break key next to the Backspace key like on the original keyboard.

How do I type Ctrl+Esc and Ctrl+Shift+Esc?

As you've probably discovered, these are reserved keys in Windows. Use Ctrl+\ and Ctrl+Shift+\ instead.

How come Ctrl+Shift+0 doesn't work in Windows Vista/7?

By default, Windows Vista/7 assign Ctrl+Shift+digit keyboard shortcuts for switching keyboard layouts. To disable this shortcut:

  • Open Control Panel.
  • Select Region and Language.
  • Select the Keyboards and Languages tab.
  • Click Change Keyboards.
  • Select the Advanced Key Settings tab.
  • Click Change Key Sequence.
  • Under Switch Keyboard Layout, select Not Assigned.

How do I make the arrow keys work?

If you have an input map enabled that maps the arrow keys to a controller, the emulation won't see the arrow keys as such, as they'll be shunted to the controller. There are two ways to fix this: either disable the input map, or hold down Alt when pressing the arrow keys to bypass the input map.

Note that some programs do cursor movement by checking the keys that are used for arrow keys without requiring Control. For these you don't actually want the arrow keys.

Why does pressing Caps Lock turn all characters lowercase, instead of toggling uppercase?

Caps Lock works differently on the Atari than on a PC. On a PC, pressing Caps Lock toggles the caps lock state, switching between forced and unforced capital letters. On the Atari OS-B firmware, pressing the CAPS button switches to default lower case, Shift+CAPS switches to default upper case, and Ctrl+CAPS switches to "control-case." Programs reading the keyboard directly can also handle the CAPS key like any other key. Accordingly, Altirra simulates all of this using the host's Caps Lock key.

Display

What are NTSC and PAL?

NTSC and PAL are types of TV signals which are put out by the Atari. NTSC is used in North America, while PAL is used in Europe. Early Ataris were only made for NTSC, while later Ataris were made for both NTSC and PAL. A particular computer was only made for one or the other; there was no way to switch modes in software nor any simply hardware modification to do so either.

A lot of software will run on both types of computers, but there are a few big caveats. First, most speed-sensitive software was made for one or the other. Auto-adjust was possible but not common. This is most noticeable with games, where it affects both the action on-screen and the music. Running an NTSC game on PAL makes it run in slow motion (-16% slower) and running a PAL game on NTSC makes it run too fast (+20% faster) or possibly even crash. The solution is to match the type of hardware being emulated to the software being run.

Why is part of the display cut off?

The Atari hardware puts out a bigger display than most TVs normally show, with some of the display hidden behind the frame (bezel). This is called overscan. The OS restricts its display to be mostly visible on a wide variety of TVs, but programs that create custom screens can put out bigger displays that are more easily cut off. The amount of overscan varies by TV.

The emulator's default overscan settings can be modified in the View menu to show more or all of the display.

Why do the colors seem subdued in PAL programs?

Some PAL programs (demos) make use of screens that have alternating grayscale and color lines to simulate higher color depth output. This takes advantage of a quirk of PAL TVs to blur the color into the grayscale lines, producing a more colorful display. This can be reproduced in the emulator through the PAL artifacting options.

How come when I run in VBXE mode, palette changes don't take place immediately and I see color mismatches in programs?

Unlike the regular GTIA, Video Board XE (VBXE) allows the program to modify the 256 color Atari palette. Altirra copies its palette into VBXE palette 0 on startup, but afterward it can't update it because some colors may have been modified. Mismatches occur when programs only modify part of the palette and rely on the exact colors provided in palette 0 (which is a bit risky, since they are only ever loaded on cold boot and not on warm reset).

Why can't I use a palette file (*.pal, *.act)?

The graphics code requires knowledge of the upstream luma and chroma signals in order to properly handle some video output modes, particularly artifacting modes. That is why the Adjust Colors dialog exposes the parameters used to generate the palette instead of the palette colors themselves.

Why is PAL mode a little jittery?

PAL displays use a 50Hz refresh rate, while computer displays often run at 60Hz or higher. Displaying PAL video on a 60Hz display results in a tiny jerk 10 times a second from the difference (60Hz - 50Hz = 10Hz). If you can set your host PC to use a 50Hz display, this effect will mostly go away. NTSC mode shows this too if the refresh rate is 72Hz or higher, but that isn't as common anymore.

The reason it doesn't entirely go away is that the Atari actually produces a video signal that deviates a little bit from the standards and has a frame rate that's slightly slower than it should be. By default, Altirra matches this rate. This can be changed in Speed Options to run the emulation slightly faster to better match the display refresh rate.

Sound

The sound seems a bit crackly. How do I fix this?

If your video card is unable to keep up or the CPU is loaded, the sound can break up because the emulator can't keep the sound card fed consistently. Increase the latency in System > Audio > Options to fix this.

How do I reduce the delay between inputs and the sound?

This is the opposite problem of too much latency — decrease the latency in System > Audio > Options. You can also switch to DirectSound mode, as that usually enables lower latency than waveOut mode, particularly on Windows Vista and Windows 7.

Why does the sound stutter when the emulation speed is adjusted?

Altirra runs a synchronous sound emulation, which constrains it to generate sound at accurate speed. Running the emulation at a slower or faster speed, like 80% or 120%, forces it to warp the sound to match. This is a tricky problem known as pitch shifting (time stretching). For simpler sounds it is possible to produce good sound at slower or faster speeds through asynchronous sound emulation, but this is not supported.

Cartridges

Why doesn't cartridge X work with cartridge Y when both are used together?

There are only a few specific cases where two cartridges works. One is when the first cartridge has a pass-through port. The SpartaDOS X cartridge was the most well-known cartridge with this capability, allowing a cartridge tower to be built. Even then, not all cartridges worked in the pass-through port due to conflicts. The XL/XE computers only had one cartridge port, so without a pass-through the only way to get two cartridges into the computer would have been with a hammer.

The 800 computer was unique in that it had two cartridge ports, a left port and a right port. However, almost all cartridges were made for and would only work in the left port, and very few cartridges were made for the right port. The emulator does support having both a left cartridge and a right cartridge in 800 mode, but such working combinations are rare and useful combinations extremely rare.

Emulation options

Why can't I choose PAL mode when 5200 hardware mode is selected?

A PAL version of the 5200 SuperSystem was never made. All manufactured consoles were NTSC.

How come the BASIC option is inverted after I switched to custom OS firmware?

The official XL/XE OS is set up so that by default it boots with BASIC enabled, and you have to hold down OPTION on boot to turn it off. That's annoying, since a lot of the good software doesn't use BASIC and needs OPTION held down, so custom OSes frequently have this swapped around.

Unfortunately, Altirra doesn't know which convention the custom OS uses, so you have to flip the option around if the OS works that way. This can be set on the firmware properties in the Firmware Images dialog.

Why is the BASIC option grayed out in 400/800/1200XL mode?

This option corresponds to whether the Option button is held on boot. On most XL/XE machines, this enables or disables the built-in BASIC. The 400, 800, and 1200XL did not have built-in BASIC and required a cartridge to be plugged in instead. THis can be done via File > Attach Special Cartridge.

Why can't I change memory size, firmware, or BASIC while Ultimate1MB is enabled?

The Ultimate1MB hardware takes over all of these functions -- they need to be selected from the U1MB BIOS instead (Help+Reset). To use new OS or BASIC ROMs, they need to be flashed into the U1MB flash image.

Other

Can I use a physical Atari disk drive over a serial or USB adapter with Altirra?

No, this is not currently supported. The best way to use such hardware is to use another program to image the entire disk through the drive, then mount that disk image in Altirra.

It is possible for an emulator to directly use a disk drive in this manner, but the timing is critical enough that it isn't a good idea, at least in Windows. The main reason is that while a PC is fast enough to emulate an Atari, it isn't necessarily fast enough to support the stable timing and low latency needed to use a live drive during emulation.

For instance, the Atari SIO specification states that the computer must negate the command line within 650-950 microseconds of transmitting the last bit of the command frame, and begin transmission of a data frame 1.0-1.8 milliseconds after receiving the last bit of an ACK byte from the peripheral. This timing is even more critical if a protected disk is being read, as slight timing variations can cause the wrong sector to be read and the game to crash. In contrast, a single call to Present() to draw a frame on screen can delay the emulator by up to 16.7 milliseconds when vsync is enabled.


Altirra Help: Getting started

Getting started
Contents
  • 1. Software to run
  • 2. Loading the image
    • 2.1. Tape images
    • 2.2. BASIC programs
  • 3. Hooking up actual kernel ROM images
  • 4. Tweaking options
    • 4.1. Hardware / Firmware
    • 4.2. BASIC
    • 4.3. PAL vs. NTSC

Software to run

First, you need software to run on the emulation. This can be in the form of:

  • Disk images (ATR, DCM, XFD).
  • Tape images (CAS, WAV).
  • Cartridge ROM images (BIN, CAR, ROM).
  • Executable images (OBX, EXE, XEX).
  • BASIC programs (BAS).

Make sure any images you have are actually meant to run on an 8-bit Atari. 16/32-bit programs written for the Atari ST will not work. A COM/EXE program written for DOS definitely won't work.

Loading the image

Almost all images can be booted easily through the File > Boot Image... command on the menu. This clears any existing mounted images, mounts the desired image, and then cold-reboots the emulation. This is usually all you need for disk images, cartridges, and executables.

Tape images

For tape images, you will need to determine whether the program on the tape is a machine language program or a BASIC program. If it is a machine language program, enable the System > Cassette > Auto-boot on startup option. This causes the emulator to hold the START button on power-up to request a boot off cassette tape.

BASIC programs

Running a BASIC program that is supplied directly as a BAS file — and not a disk image — takes a bit more work. In order to run the program, rename it to an 8.3 compatible filename (i.e. MYPROGRM.BAS), and then assign the directory holding that file to the virtual H: device through System > Hard disk. Then, enable System > Firmware > BASIC, cold reset the emulation, and type the following:

load "H:MYPROGRM.BAS"
run

Hooking up actual kernel ROM images

By default, Altirra uses its internal kernel. This provides a reasonable level of compatibility with software, but BASIC is not included and some software will not work properly. For maximum compatibility, you will need to use real Atari ROM images. These are not included with Altirra and must be obtained elsewhere, but if you have them, place them in the Altirra program directory with the following names:

ROM | Filename
Atari 800 kernel ROM (revision A) | ATARIOSA.ROM
Atari 800 kernel ROM (revision B) | ATARIOSB.ROM
Atari XL/XE kernel ROM | ATARIXL.ROM
Atari BASIC ROM | ATARIBAS.ROM
Atari 5200 kernel ROM | ATARI5200.ROM

You do not need all of these ROM images; Altirra will use the ones that are available.

Tweaking options

Hardware / Firmware

Typically, you will want to run with 800XL hardware and 600/800XL firmware for best compatibility. Some programs will require older 800 hardware and OS-B firmware to work.

BASIC

The simple rule for enabling BASIC is: enable it only for BASIC programs. Many games written in machine language will crash if BASIC is enabled. There are a few machine language programs that do require BASIC to be enabled, but they are rare.

PAL vs. NTSC

The Atari 800 was originally an NTSC-based machine, and much of the early commercial software was written with this in mind. Later software, however, was mostly written in Europe and thus targeted PAL machines.

If software intended for NTSC is run on PAL, it will typically run about 16% too slowly. If software intended for PAL is run on an NTSC machine, however, not only may it run 20% too quickly, but it may also crash due to the shorter frame time. Therefore, PAL is the safer option, as it is very rare for NTSC software to be incompatible with PAL. However, choose NTSC when appropriate for a more authentic experience with software intended to run with that standard.


Altirra Help: Keyboard mapping

Keyboard mapping

Atari Keys

When possible, Atari keys are mapped to the host keys that generate the same characters. For instance, Shift+2 on a US English keyboard layout generates an @ key press in the emulated system, even though Shift+2 generates a double quote (") on the Atari keyboard. Here are the less obvious mappings:

Atari key | Host key
Start | F2
Select | F3
Option | F4
System Reset (Warm Reset) | F5
Power (Cold Reset) | Shift+F5
Help | F6
Break | F7/Pause
Shift+< (Clear) | Home
Shift+> (Insert Line) | Shift+Insert
Ctrl+> (Insert Character) | Insert
Shift+Backspace (Delete Line) | Shift+Delete
Ctrl+Backspace (Delete Character) | Delete
Fuji / Invert | End or `
Ctrl+Esc | Ctrl+\
Ctrl+Shift+Esc | Ctrl+Shift+\
Ctrl+- | Up Arrow*
Ctrl+= | Down Arrow*
Ctrl++ | Left Arrow*
Ctrl+* | Right Arrow*
  • The Options / Keyboard... dialog allows configuration of the arrow key handling and can be set to either require or not require use of the Ctrl modifier key. Also, holding the Alt key will allow the arrow keys on the host to issue keyboard input even if those keys are already mapped to an input controller.

Host Keys

These are the mappings from host keys to emulator functions:

Host key | Function
Alt+Shift+C | Copy frame to clipboard
Alt+Shift+V | Paste text
Ctrl+\ | Ctrl+Esc
Ctrl+Shift+\ | Ctrl+Shift+Esc
Alt+Enter | Toggle full-screen mode
F1 | Warp speed
Ctrl+F1 | Toggle filter mode
F2 | Start
F3 | Select
F4 | Option
F5 | System Reset (Warm Reset)
Shift+F5 | Power (Cold Reset)
F6 | Help key (computer)
F7 | Break key (computer)
F8 | Run / Break (debugger)
F9 | Toggle Breakpoint (debugger)
F10 | Step Over (debugger)
F11 | Step Into (debugger)
Pause | Break key (computer)
Home | Shift+< (Clear)
Insert | Ctrl+> (Insert Character)
Shift+Insert | Shift+> (Insert Line)
Delete | Ctrl+Backspace (Delete Character)
Shift+Delete | Shift+Backspace (Delete Line)
End or ` | Fuji / Invert
Up Arrow* | Ctrl+-
Down Arrow* | Ctrl+=
Left Arrow* | Ctrl++
Right Arrow* | Ctrl+*
Right Alt (AltGr) | Release mouse capture


Altirra Help: Using the emulated computer

Using the emulated computer
Contents
  • 1. Introduction
  • 2. Powering on - 800
    • 2.1. The cartridge ports
    • 2.2. Disk drives
    • 2.3. System Reset
  • 3. The XL/XE series
    • 3.1. OS compatibility and the Translator
  • 4. The XE Game System (XEGS)

Introduction

Altirra emulates the behavior of an Atari 8-bit home computer. If you never owned one, you might not know how to operate one.

Powering on - 800

By default, an Atari 800 computer comes with an operating system in 10K of ROM, 48K of RAM... and not much else. If you power it on, you get a tiny program built into the OS called the Memo Pad:

This program lets you... type text on screen. Very exciting.

The cartridge ports

One way to get actual useful software onto the Atari was to plug in a cartridge. There were two cartridge ports on the 800, a left cartridge and a right cartridge. Cartridges had to be made for either the left slot or the right slot, and almost all cartridges were left slot (no other model had a right slot). One of the more popular cartridges was the BASIC language cartridge, which would give the READY prompt on start:

The BYE command would exit BASIC and start the Memo Pad.

In Altirra, you can either attach an image of the BASIC cartridge, or use the Attach Special Cartridge > BASIC menu option.

Disk drives

With a computer and a BASIC program, you could write and run programs, but not save them for later use. One way to do this was a tape drive, if you were very patient. A more useful alternative was the 810 disk drive. This connected to the computer via a serial I/O (SIO) cable, and upon power-up, the computer with automatically try to boot from the disk. If you didn't have a bootable disk inserted, this resulted:

The computer would simply try to boot from the disk drive repeatedly, printing BOOT ERROR each time. Note that if the BASIC cartridge was inserted, the computer tried to boot from the disk drive before loading BASIC. This meant that if you didn't want to boot a disk, you needed to turn off the disk drive. The Disk Drives dialog in Altirra has modes to distinguish between a drive that is absent or powered off (Off) and a drive that is powered on (R/O, R/W, VirtRW). Two shortcuts to power off drives are to either use the Detach menu option, or click the Eject button twice in the Disk Drives dialog.

If a bootable disk was inserted, the computer would sound beep-beep-beep as it loaded sectors off the disk. Atari disk drives were reasonably fast and took less than a minute to boot DOS. The computer didn't need to make the beep sound, but the OS was configured to do so by default and you could tell by the sound whether the computer was reading from the disk, writing to the disk, or hitting an error.

When booting a disk, what happened afterward depended on what was booted and what cartridge was plugged in. Program disks usually booted directly into the application or game on the disk. Others that were based on plain DOS and weren't set up to auto-run would go to the DOS menu:

When the BASIC cartridge was also inserted, DOS would boot and then the computer would enter the BASIC READY prompt. The DOS menu could be accessed by typing the DOS command.

System Reset

One of the keys on the keyboard was labeled System Reset. Its job was to reset the computer back to a sane state in case you messed up... usually. For instance, in BASIC, pressing System Reset would stop the running program, clear the screen, and display the READY prompt, usually with the BASIC program intact.

The System Reset key performed a warm start, meaning that it skipped some of the OS initialization. For instance, it preserved user memory, which in the case of BASIC meant the running program. However, it also meant that if you needed the full boot-up process, particularly booting a disk, you'd need to power the computer off and on again instead of using System Reset.

The XL/XE series

The XL/XE series brought some major changes to the 8-bit home computer line. For this case we'll focus on the popular 800XL and forget about the 1200XL, which is the weirdo of the family.

One of the changes is the presence of a built-in self-test, which replaces the not very useful Memo Pad:

The self test waits for a bit for the user to select a mode, after which it automatically cycles through the tests itself. (The simulated keyboard test was not so useful.)

Another change was the presence of a built-in BASIC, instead of requiring a BASIC cartridge to be inserted. Annoyingly, this BASIC was enabled by default, unless you held down the Option key when powering on the computer. This was a problem because a lot of disk-based software didn't work if you had a cartridge inserted and would crash if you forgot to disable BASIC. Altirra automatically holds down the Option key for you when requested under System > Firmware, and displays an indicator when it is doing so. This behavior was annoying enough that some people modified the OS ROM on their computer specifically to flip the interpretation of the Option key on boot.

The XL/XE series computers only have two joystick ports, instead of the four of the 400/800 computers, and only have a single (left) cartridge port.

OS compatibility and the Translator

There were two main revisions of the built-in ROM OS on the 400/800, revision A and revision B. Revision B fixed a couple of bugs and was the prevalent OS version. This OS is commonly called OS-B.

Atari had defined specific interfaces to use when using the OS, such that sticking to those interfaces would ensure compatibility with future versions of the OS. A fair amount of software ignored those guidelines and poked directly into the OS, which meant that they failed when Atari created the upgraded 14K OS for the XL/XE line. To run such older programs, Atari created the Translator disk, which would load a modified version of OS-B into the extra 14K of RAM in the XL/XE computer. You can use the Translator on an emulator, although it's much easier just to emulate a 400/800 instead.

History repeated itself after the release of the XL/XE line, and software was created that would only work correctly with the XL/XE OS.

The XE Game System (XEGS)

The XEGS was an attempt to make a game console out of an XL/XE computer. Functionally, it was very similar to an 800XL with 64K of memory, almost the same OS, and the ability to use the same SIO peripherals, and had nearly 100% compatibility. There were a couple of minor differences:

First, the XEGS had a game built-in, in addition to BASIC. Holding down the Select key on power-up would start Missile Command.

Second, the keyboard was detachable, and the XEGS knew if the keyboard was attached. If the keyboard wasn't attached, it would flip the meaning of the Select key, so the console would start Missile Command unless the Select key was pressed. In Altirra, this can be simulated using the System > Console > Keyboard Attached option.


Altirra Help: Configuring input

Configuring input
Contents
  • 1. Using input maps
  • 2. Managing input maps
    • 2.1. Selecting existing input maps
    • 2.2. Creating a new input map
    • 2.3. Renaming an input map
    • 2.4. Adding input maps to the menu
  • 3. Editing input maps
    • 3.1. Structure of an input map
    • 3.2. Adding and managing controllers
    • 3.3. Mapping input to a controller
    • 3.4. Quickly remapping inputs
    • 3.5. Reusing buttons with flags
  • 4. Special topics
    • 4.1. Analog controllers
    • 4.2. Mice

Using input maps

An input map is used in Altirra to define a connection between input devices on the host and emulated controllers. Altirra comes with several of these predefined, which can be selected from the Input menu. For instance, the Arrow keys -> Joystick (port 1) map connects the arrow keys on the keyboard to an emulated joystick.

By default, Altirra only allows one input map to be enabled per port, and automatically disables any other input map targeting that port. You can explicitly enable multiple input maps to the same port through the Input Maps dialog.

The arrow keys are commonly mapped to controllers, but occasionally a game will require use of both a game controller and the arrow keys on the Atari keyboard. You can bypass the input maps and force the arrows to be interpreted as keyboard presses by holding down the Alt key when pressing the arrow keys.

Managing input maps

To edit or create new input maps, use the Input Maps dialog.

Selecting existing input maps

The checkboxes beside each input map show which ones are enabled for use. Click on the checkboxes to toggle individual input maps on and off. You can have many input maps enabled at the same time.

Creating a new input map

There are three ways to create a new input map. You can start from scratch by clicking the Add button, or you can select an existing map and copy it with the Clone button. Finally, you can select a built-in preset to use as a template using the Preset button.

Renaming an input map

To rename an input map, select it and then click once on its name, which will then become editable. Type the new name and press Enter.

Adding input maps to the menu

Any input map that is created will automatically show up in the Port submenus in the Input menu for any controllers that target those ports, using the name created for it.

Editing input maps

Select an input map and then Edit to bring up the Edit Input Map dialog. This allows you to edit the input map itself.

Structure of an input map

An input map consists of a series of controllers, which in turn have mappings between that controller and host input. The controller is the device seen by the program running under emulation, and represents a device that would have been used with a real Atari.

Adding and managing controllers

To add an emulated controller, click the Add Controller button. You can also select an existing controller and then Edit to change an existing controller. The Port selection on the Add/Edit Controller dialog determines which of the controller ports on the Atari the controller is connected to; for joysticks, port 1 is used for the main player and other players use port 2 and up. For paddles, each port supports a pair of paddles, where the first paddle is paddle A.

There are also two additional virtual controllers you can select. One is the Console controller, which represents extra functions on the Atari keyboard and in the emulator itself, like the Start button and the power switch (cold reset). The second is Input State, which allows the input map to modify itself (see below).

When adding a new controller, the "add default mappings" option specifies whether to pre-populate the controller with mapping entries appropriate for that controller. This is handy for quickly binding a controller where no fancy mappings are needed.

Mapping input to a controller

Once a controller has been created, host inputs must be mapped to it. If "add default mappings" was checked when the controller was added, there will already be blank entries present and these can simply be edited; otherwise, new ones can be added with the Add Mapping... button. The Target entry on the mapping refers to an aspect of the emulated controller, like the trigger button on a joystick, while the Source entry determines which button or input on the host activates that target.

When using a game controller connected to the host PC, you can press a button or axis on the game controller to automatically select that input as the source for the mapping.

The Mode and Speed parameters allow the mapping to be customized, particularly when connecting digital inputs. Such mappings can be altered to support auto-fire or to move an analog axis with varying speed.

Mappings may share targets and sources. If two mappings share a source, that input will activate both mappings: a host button can both push forward and press the trigger on an emulated joystick. Similarly, if two mappings have the same target, either of them can be used to activate it. This is useful for either mapping multiple buttons on a host game controller to the same function, or to activate the same function with multiple modes, e.g. joystick button with and without auto-fire.

Quickly remapping inputs

You can quickly remap a series of inputs by using the Rebind button. This will bring up a sub-dialog where pressing any key on the keyboard or a button on a game controller will bind that input to the current mapping's target and then proceed to the next mapping. This will continue with subsequent mappings until all remaining mappings are rebound or the Stop button is clicked. This is generally a much faster way of setting up mappings.

Reusing buttons with flags

A button on a host controller can be overloaded with more than one function through flags. Every input map has two binary flags that can either be set or unset, and those flags can in turn enable or disable controllers on the input map. This lets the input map have multiple modes without requiring too many extra buttons.

Flags are controlled by adding input mappings to a controller of type Input State, and binding inputs to the Flag 1 or Flag 2 targets. The mode can either be Default, in which case the flag is only set while the button is down, or Toggle, in which case it alternates states on each press of the button. These flags can then be interpreted by controllers that are flag-sensitive, as determined by the Condition checkboxes on the Add/Edit Controller Mapping dialog; each controller can either require a flag to be off, on, or ignore that flag entirely. Using one flag gives two modes on the input map, and both can be combined to give four modes.

Special topics

Analog controllers

Several of the controllers used with the Atari computer and 5200 SuperSystem are analog, meaning that they have variable position. This includes paddles and the 5200 joystick. The most straightforward way to map such a controller is to bind it to an analog controller on the host PC, such as the analog stick on a game controller. However, it is sometimes useful to bind analog axes to other input types.

For games that do not require fine positioning, the analog controller can be bound to digital inputs:

5200 Controller (port 1):
    Up -> Joy POV Up
    Down -> Joy POV Down
    Left -> Joy POV Left
    Right -> Joy POV Right

This maps the nine positions on the D-pad of a PC game controller to the center and eight extremes of the 5200 analog joystick. This works fine for games that use relative motion, where pushing the stick in a direction moves a character in that direction. In other games that use absolute positioning, such as 5200 Missile Command and 5200 Gorf, the position of the stick determines the position of the cursor or player, and the above mapping only gives nine positions on screen. In that case, relative mapping should be used instead:

5200 Controller (port 1):
    Up -> Joy POV Up (Relative 5)
    Down -> Joy POV Down (Relative 5)
    Left -> Joy POV Left (Relative 5)
    Right -> Joy POV Right (Relative 5)

This causes the various directions on the D-pad to gradually move the joystick in a particular direction. Note that the stick does not auto-center when mapped this way — when no buttons are pressed on the D-pad, the emulated 5200 joystick will remain in its current position.

Mice

A mouse has no absolute position and simply reports motion. This means that it has no limits and no center position. As such, binding to or from a mouse is more limited than for the other input types. In particular, you cannot bind a mouse to an analog axis and get analog inputs. You can, however, get digital inputs from the mouse, and get analog impulses for an emulated mouse.


Altirra Help: On-screen indicators

On-screen indicators

A series of on-screen indicators can appear at the bottom of the display window to indicate I/O activity.

The indicators are as follows:

Modem status (Connected to 192.168.0.1:8000)
Displays outgoing addresses and state when dialing out and incoming addresses and state when receiving connections. Addresses are displayed in raw IPv4 or IPv6 format.
Cassette tape position (00:00:00)
Appears when a cassette tape image is mounted and the cassette tape is moving. A "C" is also displayed before the timestamp when cassette SIO acceleration is occurring.
PCLink activity (PCL:R/W)
Displayed when the PCLink device is activated.
Recording length (R02:00:00)
Shows how long a video or audio recording session has run.
IDE read/write indicator (W1000000)
Indicates "R" for read or "W" for write activity to the IDE hard disk and the logical block address (LBA) of the accessed sector.
Flash write indicator (F)
An "F" is displayed whenever writes occur to cartridge or peripheral flash memory.
Floppy drive LEDs (1-15)
Appear on a floppy drive access. By default the drive number is displayed; this can be changed to the sector number in the menu under System > Disk.

Audio Monitor

When the Audio Monitor is enabled, the state of POKEY's four channels is displayed in the lower-left corner:

Channels 1-4 are shown in order from top to bottom. If stereo POKEY mode is enabled, then a second audio monitor is shown at the lower-right corner with channels 1-4 on the second (right) POKEY.

For each channel, the following are shown:

Estimated frequency (large number)
Half the rate at which the timer is counting. For the square wave (toggle) setting, this is the fundamental frequency of the output. For distortion modes or when special effects are being played, the main frequency may be a harmonic or subdivision of this frequency.
Clock rate (15K/64K/1.79/16)
Shows the clock being input to the channel's timer. For audio playback, this is typically the 64KHz clock, though all channels can be gang-switched to the 15KHz clock instead. Channels 1 and 3 can also be individually switched to the 1.79MHz master clock.
When channels are linked, the clock rate is not shown and '16' is shown instead, to indicate that the high channel is being clocked with the output of the lower channel.
Audio clock input (L/5)
Indicates the clock input to the audio half of the channel. 'L' indicates that the standard timer clock is being used, while '5' means that clock pulses are being filtered by the 5-bit noise generator.
Audio waveform selection (T/4/9/17/V)
Shows the type of waveform produced by the clocked portion of the audio circuit: 'T' for toggle (square wave), '4' for 4-bit noise, '9' for 9-bit noise, '17' for 17-bit noise, and 'V' for volume-only output.
High-pass filter (H) (channels 1 and 2 only)
'H' indicates that channel 1 or 2 is being high-pass filtered using the timer output of channel 3 or 4.
Two-tone mode (2) (channels 1 and 2 only)
'2' is shown on channel 1 or 2 when two-tone mode is enabled. The frequency estimate is also adjusted to take the extra two-cycle delay into account.
Asynchronous receive mode (A) (channels 3 and 4 only)
'A' is shown on channels 3 and 4 when asynchronous mode is enabled, which halts timers 3 and 4 except when data is being received over the serial I/O bus. This mode is enabled during disk access and typically needs to be shut off before playing audio, or else channels 3 and 4 will be silent.
Output waveform and level
The right portion of the graph shows the output level and waveform for each channel. The vertical bar on the left shows the volume level, while the graph shows the channel's audio output as the volume-modulated audio waveform.
From left to right, the output is shown from the beginning to the end of the frame. There is often a break in the waveform near the end of the waveform; this corresponds to the beginning of vertical blank, where it is common to run music routines and update the audio registers.
The waveform is sampled on each scanline. Dynamic effects from high-speed changes to the audio registers are shown, but the displayed waveform may be inaccurate if changes are made at faster than scanline granularity.


Altirra Help: Profiles

Profiles
Contents
  • 1. Overview
  • 2. How profiles work
    • 2.1. Settings categories
  • 3. Switching profiles
    • 3.1. Temporary profiles
  • 4. Editing profiles
    • 4.1. Default profiles
    • 4.2. Visible and hidden profiles
  • 5. Using profiles from the command line

Overview

Profiles allow for easy switching between groups of settings without having to switch everything manually. An example is keeping computer and 5200 console settings separate, as the 5200 needs different controllers and different cartridges. By default, Altirra comes with profiles pre-configured for the major computer and console types. Additional profiles can also be created by the user.

How profiles work

Profiles work by keeping settings attached to the profile. For instance, the 5200 Console profile is set to store input settings in the profile, so switching to that profile loads 5200-specific settings and any changes made in that mode are kept private to the 5200 Console profile. Switching back to the XL/XE Computer profile then unloads the 5200-specific input settings, removing the 5200 controllers and replacing them with computer-compatible joysticks and paddle settings.

Settings categories

Not all settings need to be kept in a profile. The default profiles leave out the Full Screen setting and inherit this setting from their parent, which is the Global profile. This means that the full screen mode setting is shared between all of the profiles, so switching to full screen mode in one profile also changes that setting for the rest.

Switching profiles

The profiles are accessed in the System | Profiles menu. Selecting a profile switches the emulation settings to the ones in that profile. Note that switching between profiles causes the emulated computer to reset.

Temporary profiles

Also on this menu is an option to use a temporary profile. A temporary profile can be modified, but isn't saved. This is useful when you want to experiment with some settings based on an existing profile, but don't want to deal with the hassle of manually creating a new profile just to prevent the existing one from getting changed. If the changes do turn out to be worth keeping, turning the temporary profile option off will allow the changes to be saved back to the profile.

Editing profiles

The Edit Profiles... menu option allows profiles to be created, deleted, and modified. To create a new profile, select the parent profile to inherit from, click Add, and type a name for the new profile. Then, use Edit Categories to choose which groups of settings to store in the profile.

Profiles can also be dragged in the tree to re-parent them. This changes the profile to inherit settings from a different parent profile.

Default profiles

The Set as Default button allows a profile to be tagged as the default for a specific computer type. This is used when the emulator needs to switch to a particular computer type and needs to find a profile to use for it. As an example, attempting to boot a floppy disk image while in 5200 mode will cause the emulator to switch to the default profile for the XL/XE computer type.

Visible and hidden profiles

The Visible checkbox determines which profiles show up in the Profile menu. Profiles without this checkbox set can only be accessed from the Edit Profiles dialog or from the command line. This is particularly useful for profiles that are only used when launching the emulator from another program.

Using profiles from the command line

Several command line switches are provided to use profiles when launching the emulator from the command line:

/profile
Selects a profile by name. For instance: /profile:dos2 will select the profile called "dos2." Note that the name is case sensitive.
/defprofile
Selects a default profile by computer type. Valid computer types are xl, xegs, 800, 1200xl, and 5200. Example: /defprofile:xegs.
/tempprofile
Enables temporary profile mode, preventing changes from being saved back to the profile unless the Temporary Profile mode is turned back off in the UI. This is useful for a kiosk style launch where settings should not be saved. /tempprofile does not select a profile by itself, so it must be combined with /profile or /defprofile to use a specific profile as the template for the temporary profile.


Altirra Help: Portable mode

Portable mode

Altirra can be run in portable mode to ease transport of working setup between host machines. In portable mode, Altirra stores settings in an .ini file instead of in the Windows Registry, allowing both the emulator and the emulator configuration to be placed on a removable storage device.

To switch to portable mode, either start Altirra with the /portable switch, or create Altirra.ini directly. Altirra automatically switches to portable mode whenever the INI file is present, so once this file is created, there is no need to continue specifying /portable.

Limitations of portable mode

For the most part, portable mode acts substantially the same as the regular Registry-based mode. However, there are slight differences in some corner cases. The main difference is that if multiple instances of Altirra are started and exited at the same time, they may hit a locking conflict on the Altirra.ini file, resulting in one of the instances being unable to load or save settings. A warning will be displayed on startup if the emulator is unable to load settings in portable mode. Launching multiple copies in portable mode that frequently is a rare corner case, but it can be easily worked around by using multiple installations of Altirra, each with their own INI file.


Altirra Help: Touch support

Touch support

Altirra is primarily meant for use with keyboard, mouse, and game controller devices. However, it does have some support for touch input.

Pointer-based controller input

By default, the main display window operates with standard touch input support in Windows, such that the press-and-hold gesture activates the simulated right mouse button. This activates the copy/paste context menu. However, if an input controller is set up with bindings to the left mouse button, and not the right mouse button, this gesture is automatically disabled. This improves responsiveness by allowing touch presses to immediately be detected and passed to emulation instead of incurring the usual delays.

On-screen keyboard

The on-screen keyboard that is available via the game controller can also be activated by touch by swiping up from the bottom portion of the display window. This UI also modifies the gesture settings so that touching the display immediately presses down keys on the on-screen keyboard. Touching elsewhere on the display closes the on-screen keyboard.


Altirra Help: Adjusting colors

Adjusting colors

Checking the colors on physical hardware

To tune the colors in the emulator to match physical hardware, it is best to run a program to display the full 256 color palette produced by GTIA. There are many such programs available, but for emulator color tuning, the colormap.xex utility on the Additions disk is recommended. You can either boot the additions disk image or extract the program and boot it directly with an SIO2PC device, which allows the computer to run a program or boot off an emulated disk drive from the PC.

When the color map tool is run, it will generate a display like this (simulated NTSC example):

The Adjust Colors dialog will also display the 256 colors, but you may find it easier to run colormap.xex within the emulator as well.

The color map

The color table section on the right displays all 256 colors that can be produced by the GTIA chip. The hue values are labeled on the left, with each row being a different hue; hue 0 at the top is grayscale with no color. Labeled on the bottom are the luma values, with each color having the same luma value. The labels are in hexadecimal, where A-F are 10 through 15.

In BASIC, the hue and luma are the second and third values passed to the SETCOLOR statement. For instance, the typical bluish background of the GRAPHICS 0 screen is SETCOLOR 2,9,4, shown here by the row labeled 9 on the left and the column labeled 4 at the bottom.

Row 1 is intentionally repeated from the top of the chart back to the bottom to make it easier to calibrate for specific hue settings (as will be described in more detail below).

There are a few rare cases where the color table may display differently:

  • The computer has a CTIA chip. These are rare and only found in old 800 computers, not XL/XE models. On a CTIA system, only 8 luminances and 128 colors are displayed.
  • The computer is a SECAM model with an FGTIA chip. These are extremely rare. The color table will only show 8 lumas and 128 colors with an FGTIA.
  • The GTIA chip is defective. Some late XL/XE models have a defective GTIA chip that incorrectly displays some GTIA modes, including the GTIA 9 mode used here.

Artifacting

The area on the left shows the colors produced by artifacting, the use of alternating bright and dark pixels to produce a color signal for the display. For NTSC, this gives another set of colors that need to be tuned. This area is not useful for PAL displays.

The top row displays the "classic" artifacting colors produced by alternating white and black pixels. These are the colors referred to when people mention "purple/green" and "red/blue" artifacting. The other rows below it are artifacting colors produced by also mixing hues into the pattern. These are less common but do occur in some software. They are also generally very sensitive to adjustments and are useful for fine tuning the color calibration.

In the emulator, this will only show colors if one of the artifacting modes is enabled in the video options. Otherwise, it will show the raw pixels as uncolored stripes. Artifacted colors will also be absent on physical hardware if an S-Video or SCART video connection is being used.

Tuning procedure

Below is a procedure to adjust the colors generated by the hardware, displayed by the TV, or generated by the emulator. In Altirra, the Adjust Colors dialog accessible from the View menu is used to tune the emulator's color palette generation.

A warning about NTSC vs. PAL

Before we begin, a point to keep in mind:

NTSC and PAL computers do not generate the same colors.

This is important due to some rather bad assumptions made in the community resulting in misconceptions about NTSC color, particularly due to earlier emulators defaulting to PAL and using PAL colors even in NTSC modes. NTSC and PAL colors don't generate the same colors. The NTSC palette is generally shifted about a full hue from PAL, and due to differences in both the video standards and the computer hardware, it is not possible to adjust a NTSC computer to match a PAL computer in palette.

Thus, some guidelines when trying to tune colors:

  • There is no One True Palette. Just by video standards alone, there will have to be at least two.

  • Always make sure that the emulator and the physical computer match: tune NTSC against NTSC, and PAL against PAL.

  • Be wary of using any screenshots you find online of games as references, since they may have been generated using an emulator with inaccurate colors. (Even some old versions of Altirra had not so good default palettes.) Screenshots of NTSC games that have sharp pixels and the wrong aspect ratio are a telltale sign that they were generated with an emulator and possibly with a PAL palette.

  • Check for multiple versions of even classic games which may differ in colors used, either because they were released in and adjusted for different regions, or unofficially modified to suit PAL colors. Star Raiders, for instance, has multiple versions floating around, one of which uses $72 for the Galactic Chart background and $A8 for the "SHIPS" label, while another version uses $90 for the background and $B8 for "TARGETS."

  • Even physical computers and TVs can vary in adjustment, so matching the emulator against a single physical computer does not guarantee accurate color, just realistic color.

Black and white points

The first step is to tune black and white. Both the TV and the emulator will have brightness and contrast controls, where the brightness control determines the black level and the contrast control determines how much brighter white is than black. This means that brightness should be adjusted first to set the black point, shifting both black and white together, and then contrast adjusted to set the white point.

Typically, the approach here is to turn up brightness until the black background turns gray, then turn it down until it is just black. Similarly, the contrast is then adjusted to make white bright without blowing out colors. This is more important on newer LCD TVs that are more prone to distorting colors that are too bright for the display to show. If you see oranges turning into nasty yellows, the contrast or color setting is too high and needs to be lowered.

For NTSC, there is an additional consideration with the brightness. The way that the Atari encodes the NTSC signal causes luma 0 to be "blacker than black" due to its signal level being below the setup/pedestal level. However, the actual effect on the display varies for many reasons including AGC, NTSC-J not using a pedestal, and TVs having adjustments or dynamic adaptation for black level. The amount you may need to turn brightness down to match your TV may vary. In Altirra's default color preset, the brightness is set to -4% to pull down luma 0 a bit without darkening luma 1 too much or overly distorting colors at luma 0.

Saturation and color

Before adjusting the colors, the color reproduction settings on the TV or emulator should be checked. It is common for some TVs to have a "digital vibrance" or "color enhancement" setting enabled by default. Typically this actually means "obscenely oversaturated colors" and should be turned down. "Game mode" is a good choice in some TVs as it reduces the amount of image enhancement processing. The color rainbow should smooth over most or all of the range without flattening out to primary colors.

Most displays will also have a color warmth or color temperature setting to adjust, where "warmer" settings produce more red colors while "colder" settings produce bluer colors. Monitors in particular are prone to be set way too blue to boost their apparent brightness, with a color temperature setting as high as 9300K. Higher-end monitors may have a pre-calibrated sRGB setting, which is ideal when used with the emulator.

In the emulator, there is a color correction setting that can improve the display of NTSC colors by compensating for differences between the NTSC color specification and sRGB. This particularly changes mid-hues by shifting blue closer to red, reducing the amount of purple in the center. The NTSC to sRGB conversion mode is now enabled by default in the default NTSC preset. For PAL there is much less of a difference and so the conversion is not enabled by default.

The main thing to watch for is the dark red and blue peaks on the left side of the color table, at luma 0 and hues 4/8. These should appear to be about the same brightness. If one is noticeably brighter than the other, that should be corrected first before proceeding, as otherwise it will make hue tuning difficult: it will be hard to keep the text screen blue without turning one of the other colors "sour" in a classic game.

Hue start

Next is to adjust the hue 1, shown as the second row from the top on the color table. This hue is special because it is the color burst color defined in the NTSC standard and the only color not affected by the GTIA color adjustment knob on the bottom of the Atari computer. Adjustments to this color shift the entire table and thus it should be adjusted before all the rest of the hues.

On physical hardware, the hue 1 color is determined by the Tint setting on the TV set. In the emulator, this is determined by the Hue Start setting. For modern NTSC, this is defined as a light yellowish color sometimes referred to as goldenrod corresponding to an emulator setting of -57 degrees, and this is the color that modern TVs will typically generate with their default color settings. For older TVs such as the Commodore 1702, though, the default may be more orange, with a setting in the -40 to -30 degree range. Altirra's default NTSC palette uses the modern NTSC color setting.

The PAL standard was designed to avoid color shift problems that required a tint setting on NTSC displays, so there is generally more agreement on its base hue. Due to differences between the NTSC GTIA and the PAL GTIA, however, the PAL GTIA generates a different hue 1 color than the NTSC GTIA. This is normally at -24 degrees, 33 degrees from NTSC.

Note: If PAL high artifacting is active in the emulator, the Hue Start setting is ignored and locked at -24 degrees.

Hue step

Next is adjusting the rest of the colors. On the physical hardware, this is done through the color adjustment knob accessible through a small hole at the bottom of the computer. In Altirra, this corresponds to the Hue Range setting.

On an NTSC computer, the color adjustment knob controls the size of the hue step from hue 1 to each successive hue. As a result, hue 2 is affected the least and hue 15 the most. This is the trickiest setting to adjust because a small adjustment significantly affects lots of colors and it is very easy to detune critical colors in common graphics screens. The color map intentionally repeats hue 1 below hue 15 to allow two common settings to be easily targeted:

  • Hue 15 continuous with hue 1. Adjust the hue step until hue 15 appears to naturally change into hue 1 with the same hue step between the rest of the colors. This matches manuals like Atari 400/800 Hardware Manual that describe hue 15 as being yellow, just short of hue 1 gold. In the emulator, this is a hue step setting of 24 degrees (360 degrees divided by 15 hues).

  • Hue 15 same as hue 1. Adjust the hue step until hue 15 and hue 1 are the same color. This matches manuals like the SALT manual. In the emulator, use 25.7 degrees (360 divided by 14).

  • Hue 15 overshooting past hue 1 (>25.7 degrees) Adjust the hue step until hue 15 goes beyond hue 1, into slight reddish-orange. While not matching official Atari manuals, this is commonly seen in actual computers and helps prevent some classic games from displaying "sour" colors. In the emulator, this is a hue step setting higher than 25.7 degrees.

On a PAL computer, this adjustment is a bit different due to some differences in quirks in the PAL GTIA, which result in colors varying less between computers but the adjustment knob also acting differently. First, on a PAL system, hues 1 and 15 are always the same. This is not an adjustment issue, the PAL GTIA chip itself is hardwired to generate the same color for these hues. Any emulator palette for PAL that has different hue 1 and 15 colors is wrong. Second, the hue steps generated by the PAL GTIA aren't evenly spaced and the delays are generated in different directions for different hues. The result is that unlike on an NTSC computer where changing the color adjustment smoothly slides the hues, on a PAL system the hues are boxed into several bands and cannot be shifted as far as on an NTSC computer. The emulator simulates these quirks when the "Use PAL quirks" option is checked, which is enabled in the default preset.

Artifacting colors

On physical hardware, the colors generated by artifacting are determined by the circuitry design within the computer and the TV set, and there are no separate settings for it other than the ones already described. The emulator has artifacting phase and saturation settings, though, to tune the emulator to match the computer model, which is the most important factor.

For NTSC artifacting, the artifacting section on the left side of the color map utility display is used to tune artifacted colors. The top row of colors is used as the main starting point since these are the "primary" artifacting colors that show up the most. The exact colors vary for each computer since it depends on luma-chroma circuit delays and even the TV, but purple/green is the most common pair, with red/blue and green/blue being less common.

Once the primary artifacting colors are tuned, the remaining ones can be adjusted. These vary not only on the artifacting hue and saturation settings, but also on the main hue settings. The artifacting saturation setting is particularly important here and corresponds to the video signal strength produced by the computer; 800 have a weak video signal and strongly saturated colors, while XEs produce a strong video signal and less saturated colors. This is the hardest part of the color palette to tune, so it is best to get everything adjusted as close as possible before attempting to tune this part of the artifacting palette.

Problems with color clamping

With highly saturated colors, it is possible to run into difficulties with colors being generated outside of the RGB cube. When this happens, the colors can't be displayed by the screen and have to be clamped in range, distorting the color. This is normal even on the physical hardware, but can be a problem with bright colors in the emulator. Areas of the color table "flattening" to primaries like red, blue, yellow, and purple are signs that this is happening and that the color settings need to be reduced.

The Atari 800 deserves special mention here because of its weak video signal, which results in its output being overall dimmer but with much more saturated colors than the newer XL/XE models. The blue/green artifacting colors, in particular, are prone to being trashed when trying to use normal color ranges, causing the deep blue color to be smashed to bright purple. The Intensity Scale setting is specifically designed to combat this by darkening the entire palette to make room for the deeply saturated colors. The NTSC 800 preset makes use of this, reducing the white point to light gray to make room for the artifacting saturation to be turned up.

Gamma ramp issues

Certain applications make use of a video card feature called a gamma ramp to change the color settings of a display. These are typically applications that tint the display orange-red to reduce blue light at night, such as f.lux or Light Bulb. These programs are generally compatible with Altirra but may cause issues if forgotten about when calibrating colors, the blue light reduction may vary during the day or cause difficulty in tuning the blue portion of the palette.

For these reasons, the Adjust Colors dialog displays a warning if it detects a gamma ramp setting that will affect color reproduction. This can be ignored, but it is recommended that the palette be tuned with the color tint disabled. One reason is that a normally tuned palette with the orange tint applied usually looks better than a night-light tuned palette displayed normally, which will be blown out in blue. Another problem is that the color tint may not be enabled when the emulator is full-screen. In any case, programs that do night time blue light reduction will have a feature in their options to temporarily disable the feature.

Note: Altirra will not be able to detect and warn if the tint is coming from either the Windows 10 Night Light feature or the video driver's desktop color controls.


Altirra Help: The 400/800 Home Computer

The 400/800 Home Computer

Description

The 400 and 800 Home Computers were the original computers in the Atari 8-bit computer line.

Configurations

The 400 has two distinctive features relative to the 800: a crappy membrane keyboard and only 8K or 16K of RAM. Almost nothing runs in 8K, and the majority of programs will not run with 16K. Hardware modifications were available to expand 400s to 48K to fix this problem.

The 800 has an expansion bay in the back containing RAM modules and the "personality module" that provides the OS ROM. Different configurations of RAM modules can provide various memory configurations from 8K to 48K; the 48K configuration is the most common and the one expected by most software. Certain hardware add-ons add another 4K of RAM to give the less common 52K configuration.

Both the 400/800 have four controller ports, but only the 800 has two cartridge ports. The left cartridge port on the 800 is the regular one; the right cartridge port is the oddball. A cartridge designed for one port cannot be used in the other and very few cartridges were designed for the right port.

Emulation-specific concerns

Because of the similarity of the 400 and 800, Altirra does not have distinct hardware modes for the two; the 400 is emulated by selecting the 800 hardware mode and lowering the memory configuration to 8K or 16K. There is only one minor hardware difference which is not emulated, which is that a light pen works on all ports on an 800 but only port 4 on a 400.

The emulator also does not have distinct left and right cartridge ports; instead, the 'port' used by a cartridge is determined by the cartridge mapper. Cartridge images using the .CAR format are pre-marked and automatically detected by the emulator as a left cartridge or right cartridge. If both a left and a right cartridge are to be used at the same time, the right cartridge should be set as the secondary cartridge.

The 400/800 do not have internal BASIC and therefore Altirra's default option for controlling BASIC via the Option key is inoperative with these systems. Instead, BASIC is attached from the cartridge menu, which creates a BASIC cartridge on the fly.


Altirra Help: The 1200XL Home Computers

The 1200XL Home Computers

Description

The 1200XL was a redesigned and upgraded version of the 800 computer. Despite the numbering scheme, it predates the 800XL, which fixed some of the 1200XL's shortcomings.

Configurations

Compared to a 48K 800, the 1200XL has 64K of RAM, and an updated 16K OS with built-in self test. There is only one cartridge port, with no right cartridge support, and only two joystick ports instead of four. It has some unique features not found in any other model in the computer line: two software controllable LEDs and four F1-F4 function keys.

The 1200XL shipped with an updated OS that had some unfortunate incompatibilities with the OS-B that was used with the 800. A Translator disk was provided to load OS-B into the 1200XL's high RAM, permitting some incompatible programs to run. The 800XL's updated OS fixed some of the compatibility issues.

There is no equivalent to the Parallel Bus Interface (PBI) or Enhanced Cartridge Interface (ECI) ports on the 1200XL. The 1200XL also does not have internal BASIC.

Emulation-specific concerns

The 1200XL is a bit of an odd duck. Usually, it's preferable to emulate an 800XL instead of a 1200XL since there was little, if any, software that required a 1200XL.

Altirra has both separate hardware modes and default profiles for the 1200XL. The function keys are seldom used and not mapped by default; they can be enabled in Keyboard Options, replacing the default turbo and console key mappings unless remapped. LEDs are displayed at the bottom, if lit.

Like the 400/800, the 1200XL does not have internal BASIC, and the BASIC cartridge must be attached if desired.

The 1200XL's unique OS is required to reproduce the quirks of that system, but it can be run with an 800XL OS instead.


Altirra Help: The 800XL/130XE Home Computers

The 800XL/130XE Home Computers

Description

The 800XL and 130XE computers were upgraded versions of the 800 Home Computer. 600XL and 800XE were minor variants in this line.

Configurations

Compared to a 48K 800, the 800XL has 64K of RAM, an updated 16K OS with built-in self test, and built-in BASIC. There is only one cartridge port, with no right cartridge support, and only two joystick ports instead of four. The 800XL also adds a new port on the back called the Parallel Bus Interface (PBI), which adds additional expansion capabilities by devices that can directly interface to the CPU bus and was popular for hard drive expansions.

The 130XE adds another 64K of extended RAM for 128K total. Unlike earlier third-party memory expansions, the 130XE has the unique capability of independent CPU and ANTIC access enables for the extended RAM. The PBI port was also replaced by the Enhanced Cartridge Interface (ECI), which combines the cartridge port with an additional side port to form a comparable PBI-like interface.

Emulation-specific concerns

Altirra has separate hardware modes for the 800XL and 130XE, but only a common default XL/XE profile. It is rare for software to work on one and not the other; the extended RAM separate access feature was rarely used, so software that used the 130XE's extra RAM would usually run on an expanded 800XL. There are also obscure minor hardware differences between the two that can cause incompatibilities and are toggled between the two hardware modes, but software that is sensitive to those is extremely rare.

A rather annoying addition to the 800XL was the introduction of the internal BASIC, which would not have been an issue except that it defaults to being enabled unless the Option key is held on power-up. This does not cause problems for cartridge-based software, but a substantial amount of disk-based software will crash if loaded with BASIC enabled. For this reason, the emulator has a internal BASIC toggle option that will automatically hold the Option key on startup. In rare cases this can cause problems due to being interpreted as something other than a request to disable BASIC, so the screen will show [Option] briefly on the side to indicate that the Option key is being held down automatically by the emulator. Some modified OS ROMs also invert this behavior for convenience; these ROMs need to be tagged appropriately when added in Firmware Images for the emulator to recognize the flipped behavior and adjust its use of the Option button accordingly.

While there are some differences in capability between the Parallel Bus Interface (PBI) port of the 800XL and the Enhanced Cartridge Interface (ECI) of the 130XE, adapters were often available to use the same hardware with both, and the emulator does not make a distinction between the two: hardware is added via the Devices tree in the same way regardless of whether the emulated system has a PBI or ECI port. This does allow for the minor impossibility of using a cartridge together with an ECI device that did not originally have a cartridge pass-through, though.


Altirra Help: The XE Game System (XEGS)

The XE Game System (XEGS)

Description

The XE Game System, or XEGS, is a mildly adapted version of the 800XL computer intended to double both as a game console and a computer. It is much more similar to the computer line than the 5200 and the vast majority of software that runs on an 800XL will also run on the XEGS.

Configuration

The XEGS is very similar internally to the 800XL: 64K of RAM, 16K OS ROM with built-in self test, 8K internal BASIC ROM, and the same ANTIC/GTIA/POKEY/PIA chips. It also takes the same cartridges and has a serial I/O bus connection for disk drives and other SIO peripherals. Thus, it can run the same cartridge programs, BASIC programs, DOS, etc.

There are a couple of minor hardware additions. The keyboard is detachable and optional on the XEGS, and software can detect if the keyboard is attached. The XEGS also has an additional 8K internal game ROM containing Missile Command, which activates if the keyboard is detached or SELECT is pressed. The OS ROM is also slightly revised over the XL/XE line, with a newer version 4 OS that contains support for booting without the keyboard.

Emulation-specific concerns

Emulating the XEGS in Altirra is nearly the same as emulating an 800XL. By default, the XEGS profile has its own hardware and firmware settings, but the majority of settings are shared with the rest of the computer line.

The keyboard can be "detached" or "attached" in System > Console Switches > Keyboard Present (XEGS). For this to work, the version 4 OS must be used, sometimes called atarixegs.rom. The common version 2 OS ("atarixl.rom") can be used if the version 4 OS is not available, but detached operation and Missile Command will not activate.

The version of Missile Command that ships with the XEGS is modified to work with the XEGS's version 4 OS. Some older versions of Missile Command were hardcoded to the 800's OS and will break if run on the XEGS; the symptom of a version mismatch is that game's keyboard controls won't work and the level parameters can't be changed.


Altirra Help: The 5200 SuperSystem

The 5200 SuperSystem

Description

The 5200 SuperSystem is a game console that is similar internally to the 400/800 computer line, but designed exclusively to run pre-packaged games on cartridges with controller input, without a keyboard or writable storage like floppy disks and tape. It shares enough hardware internals that games can be ported between the 5200 and the 800 with minor difficulty, but the two are not directly compatible with each other.

Ports

The 5200 has a cartridge port and either two or four controller ports, depending on the model. Both take 5200-specific cartridges and controllers that are incompatible with the 800. There are no buttons on the console itself besides a power button; the buttons to start and reset games are on the controllers.

The 5200 is notorious for its controllers, which are analog but do not have self-centering. Besides the stubby analog stick, there are two fire buttons on the side, which are distinct and can trigger different actions depending on the game, a 12 button keypad at the bottom, and Start/Select/Reset buttons on the top.

Hardware versions

The original version of the 5200 had four controller ports, whereas a later version was made with only two. There are two additional differences between the consoles, both minor. First, the 2-port version contained hardware updates to support the 2600 adaptor, which is not applicable to emulation. Second, the 2-port version also came with an updated BIOS ROM to support a future PAL version that never shipped. There are a few games which are incompatible with the 2-port version of the BIOS as a result of these changes.

Emulation-specific concerns

Because the 5200 is incompatible with computer peripherals and software, Altirra defaults to having a separate configuration profile for 5200 mode. Running a 5200 cartridge will automatically switch to this profile, and cartridge and input maps set in this mode will only affect 5200 operation. This prevents accidentally mixing settings between the two modes and creating impossible configurations that don't work.

There is no default screen on the 5200, since the 5200 cannot be powered on without a cartridge. (The console is designed so that the cartridge is required to complete the power circuit, and removing it turns off the power.) In Altirra, a standby cartridge is substituted whenever a cartridge is removed. This cartridge also doubles as a controller test to check that input mappings are working as expected.

Some games use the analog stick for precise positioning, like Missile Command, while others use it for less-precise directional control, like Pengo. The emulator cannot tell which control style the game prefers, so it comes with defaults to bind the controller both in "absolute" and "relative" styles. Absolute positioning maps input directions directly to those same directions on the analog stick; relative positioning uses input to nudge the stick in that direction. You will need to manually choose the input map that more closely suits the control style of each game.


Altirra Help: Additions disk

Additions disk

For some devices, additional software must be loaded on the computer to make use of that device. Altirra comes with an additions disk called Additions.atr containing additional software for use with emulated devices. Some of the drivers can also be used on the real hardware.

Software on the Additions disk

ALTXEP80.SYS
XEP80 Interface Module 80-column E:/S: driver.
ALTXEP8U.SYS
Ultra speed version of XEP80 driver. The ultra driver uses 31.5Kbaud for both transmission and reception from the XEP80 instead of the standard 15.7Kbaud, for better output speed. Note that the XEP80 must be powered off and back on again to work at standard speed after using the ultra speed driver.
ALTXEP8P.SYS
ALTXEPUP.SYS
PAL versions of the ALTXEP80.SYS and ALTXEP8U.SYS drivers. These versions automatically emit the control codes to switch the XEP80 to a 50Hz display.
ALTXEPX.SYS

SpartaDOS X version of Altirra XEP80 driver, which loads low instead of high and supports SDX features. Four switches are supported: /P to default to PAL, /V to adjust video timing, /U to enable 31.5Kbaud ultra mode, and /1 to use port 1 instead of port 2.

ATDEBUGX.SYS

Altirra debug link driver, to send SDX symbols to the debugger. Requires debug link to be enabled in debugger options and for the debugger to be open when the driver loads.

BOOT850.COM
Downloads R: handler software from an 850 Interface Module, for when it isn't already being downloaded by the boot process. In Altirra, this is only usable if full SIO emulation is enabled. It will work but do nothing when stub loader emulation is enabled.
BOOT850.COM attempts to workaround some shortcomings in the 850 handler download process, particularly that the 850 only answers poll requests once. If the poll request fails, a special SIO routine is used to download the relocator blind without having to know its length.
The downloader will detect if R: is already installed and avoid installing it again.
RHND850.COM
Disk-based version of Altirra's built-in 850 R: handler. This handler is intended for use with the 850 emulation but should also work on real 850 hardware.
Like BOOT850.COM, RHND850.COM will also avoid installing its R: handler if one is already installed.
BOOT1030.COM
Downloads T: handler software from a 1030 Modem, for when it isn't already being downloaded by the boot process. In Altirra, this is only usable if full SIO emulation is enabled. It will work but do nothing when stub loader emulation is enabled.
The downloader will detect if T: is already installed and avoid installing it again.
THND1030.COM
Disk-based version of Altirra's built-in 1030 T: handler. This handler is intended for use with the 1030 emulation but should also work on real 1030 hardware. Unlike the firmware-loaded version, it is relocatable and will leave more memory free whenever MEMLO is below $1D00.
Like BOOT1030.COM, THND1030.COM will also avoid installing its T: handler if one is already installed.
SX212.COM
T: handler for SX212 modem. This handler is intended for use with the SX212 emulation but should also work on real hardware.
ATBASICX.COM
Disk-based version of Altirra BASIC; requires 48K RAM and no cartridge inserted. Will attempt to run AUTORUN.BAS unless a filename is specified on the command line.
COLORMAP.COM
Displays color charts for color calibration purposes.
LOADEXE.COM
Triggers loading of a program loaded directly in the emulator when the program load mode is set to Type 3 Poll or Deferred. This can be renamed to AUTORUN.SYS to automatically trigger the program load once DOS has booted.
XEPVHOLD.COM
Reprograms the XEP80 video timing chain to use a shorter character cell in NTSC to reduce vertical overscan and improve compatibility with modern video displays that may roll with standard XEP80 timing. May be used with either the Atari XEP80 driver or the Altirra XEP80 driver.
XEPVHLDP.COM
Reprograms the XEP80 video timing chain for more standard timing in PAL.


Altirra Help: Cartridge-based devices

Cartridge-based devices

Cartridge-based devices plug into the cartridge port of the Atari computer.

Devices

The following cartridge-based devices are emulated by Altirra:

  • DragonCart
  • MyIDE
  • R-Time 8

Conflicts

Since these devices use the cartridge port, they can conflict with other devices that also use the same port. Typically, only one cartridge can be plugged in at a time. However, a few cartridges, notably the SpartaDOS X cartridge, have a pass-through expansion port that allows a secondary cartridge to be plugged. Altirra supports cartridge stacks, but like on real hardware, the pass-through port can interfere with the secondary cartridge's operation.

In rare cases, it is possible to set up an impossible configuration in emulation with two cartridge-based devices simultaneously active. This most often occurs when two emulated devices are activated when neither has a pass-through port. On a real computer, this would be noticed because the only way to get two such devices into the cartridge port at the same time would be with a hammer; in emulation this can result in bizarre and broken behavior.


Altirra Help: DragonCart

DragonCart

The DragonCart adds an Ethernet interface to the computer. It has no on-board storage, so external software must be loaded from disk to actually use the DragonCart.

Contents
  • 1. What DragonCart does and does not do
  • 2. Setting up the emulation network
  • 3. No external access mode
  • 4. Host only mode
  • 5. Network Address Translation (NAT) mode
  • 6. Inbound connections
  • 7. VXLAN tunneling
  • 8. Configuring network software in emulation
  • 9. Limitations
  • 10. Debugging with the DragonCart

What DragonCart does and does not do

The DragonCart is based around an Ethernet interface chip called the CS8900A. It handles the basic Ethernet protocol, and buffers both incoming and outgoing packets. Essentially, it serves the same purpose as a traditional Network Interface Card (NIC).

What the DragonCart doesn't do is act as a server or network node in any way. It has no on-board processor or software, doesn't respond to or send any packets on its own, doesn't track any connections or provide any services or external computers. Software running on the computer must interpret Ethernet packets in order to bind to a hardware address (ARP) and then a network address (IPv4 + DHCP), and establish connections (TCP/UDP). This software is not built into the DragonCart and is not provided with the emulator either, so it must be obtained externally.

Setting up the emulation network

For technical reasons, the emulated DragonCart is connected to an emulation network which is separate than the physical networks the host computer is connected to. Some additional setup is required to allow the emulation to connect with the outside network, particularly the choice of bridging mode.

The emulation network must have an IPv4 address for IP connections to work. The default is to use the class C private network 192.168.0.0/24. This can be changed to any valid IPv4 address as needed, except for the important caveat noted below in NAT mode. There is no need to set it to match any IP address on the host computer that the emulator is running on, and in NAT mode it must not be the same. There are three private network address blocks in IPv4 that are guaranteed never to be used on the public Internet:

  • 10.*.*.*
  • 172.16.*.* to 172.31.*.*
  • 192.168.*.*

Any of these is guaranteed to never overlap with any server on the Internet. However, internal networks often also use these ranges, so the emulation network must be placed at an address that isn't public and also doesn't overlap the physical LAN. Typically a class C network is carved out of the last range, such as 192.168.200.* (address 192.168.200.0, network mask 255.255.255.0).

No external access mode

In this mode, the emulated DragonCart is connected to an isolated network that has no connection to the outside world. A support server is emulated at the gateway (.1) address that has basic DHCP services. This supports basic functionality testing only. However, the packets sent and received by the CS8900A can be monitored in the debugger.

Host only mode

In host only mode, any TCP or UDP connections to the gateway address are reflected to the computer running the emulator. This allows access to services running on the host computer, but no external access.

Network Address Translation (NAT) mode

In NAT mode, the emulated gateway also routes outgoing UDP and TCP connections through the gateway to the outside world. This is done through network address translation so that the connections originate from the host computer itself, not from the emulation network. In effect, the gateway intercepts the connection so that the emulation is actually connecting to the gateway, the gateway connects to the external server, and data is then reflected between the internal and external connections.

Because NAT reflects between internal and external networks, the emulation network must have a nonconflicting address different than external networks. In particular, it must not be the same as the LAN that the host computer is on or of any external sites that you want to connect to. For instance, if the host computer is on 192.168.0.* and the emulation wants to connect to 8.8.8.8, the emulation network must not overlap either of those addresses. If it does, the connection attempt will fail because the destination address of the connection request will match the emulation network and will be routed there instead of externally. Typically the emulation network should be set to a different private network address instead, like 192.168.1.*.

Inbound connections

By default and also due to OS limitations, the emulation will typically not see incoming connections destined for it even if they are target at the emulation network. However, the emulator can be configured to enable port forwarding so that UDP and TCP connections on a particular port to the host are forwarded to the emulation.

Due to the way that the port forwarding works, TCP connections on the host side will always connect. If the connection is rejected on the emulation side, the gateway will then close the external connection. This means that the external client will see a successful connection followed by a hard close instead of a connection failure.

VXLAN tunneling

The emulator also has support for tunneling over the VXLAN protocol, which allows the emulation to exchange actual Ethernet packets with external hosts. VXLAN is a simple protocol used by routers and some virtual machines to encapsulate Ethernet packets in UDP datagrams, typically sent over UDP port 4789. The official protocol is detailed in RFC 7348.

In order to use VXLAN tunneling, another endpoint must be available that also supports the VXLAN protocol. This can be another instance of the emulator, either running on the same computer or another computer, or it can even be a different type of machine like a Linux box. Effectively, VXLAN allows two private networks to be tied together into a single combined network, while still isolating it from the public network.

The emulator must be configured with the IP address and port to send VXLAN packets to (tunnel address and destination port), and the port to listen on to receive packets (source port). Once this is established, packets sent by the emulation are reflected to the VXLAN tunnel and packets received on the VXLAN tunnel are reflected into the emulation network.

Note: Altirra currently always uses and responds only to tunnel ID 0.

On a Linux box, it is possible to set up VXLAN tunnelling as follows. Note that this is not a production-level setup and may vary based on the specific configuration and distribution you are using. Also, there may be better ways to do this.

  • Run ifconfig on the Linux box and ipconfig on the Windows box and confirm the two IP addresses being used. ping should work in both directions.

  • Add a VXLAN tunnel to the Linux box, aimed at the Windows box by unicast IP. In this case, the Windows box has the IP address 192.168.37.1.

    ip link add vxlan0 type vxlan id 0 remote 192.168.37.1 dstport 4789 srcport 4789 4789

  • Configure Altirra similarly: tunnel address 192.168.37.128 (the address of the Linux box), port 4789 for both source and target port.

  • On the Linux box, bring up the vxlan0 interface, and force a DHCP update:

    ifconfig vxlan0 up
    dhclient vxlan0

    With luck, Altirra's debugger will show DHCP messages being exchanged, and ifconfig on the Linux box should show that it has obtained an IP address. Client applications like Mozilla Firefox can now be used to connect to services running in the emulation, and network diagnostic utilities like tcpdump and Wireshark can be used too. If NAT and incoming port forwarding is enabled, the Linux box will also see traffic between the NAT gateway and the emulation servers.

It should be noted that VXLAN is not a secure protocol and delivers all packets in the clear (plaintext). This means it is susceptable to many types of attacks including snooping. However, this is not typically an issue for DragonCart emulation where the connections established are not used for sensitive information.

Important: If the physical network that the VXLAN packets travel over is being reflected into the tunnel, the UDP packets for the tunnel should be filtered out from the tunnel, or else a packet storm may occur.

Configuring network software in emulation

In order to actually communicate on the network, software must be running in the emulation that provides an IP stack. If that software includes a DHCP client, the IP stack can obtain an automatically assigned IP address from the emulator gateway.

If the IP stack doesn't support DHCP, then an IP address must be assigned manually. The network address and network mask must match the emulation network that was set up in DragonCart settings, and the network IP address must be within that network. The gateway address is at .1 or .0.1 and must not be used; also, addresses .100 to .254 are used by the gateway DHCP server and are also best avoided.

Limitations

The IPv4 stack used by the emulated gateway is fairly primitive and does not understand many modern IP options. However, it should be compatible with clients that follow the IPv4 standard. ICMP forwarding is not supported, so pings will not work. ARP and DHCPv4 are supported.

IPv6 is not supported by the gateway. All attempts to establish an outgoing connection with IPv6 will fail. However, incoming connections over IPv6 to the host can still be forwarded to an IPv4 address on the emulation network.

Debugging with the DragonCart

The emulator's debugger has logging channels to aid with diagnosing issues with network connections through the DragonCart. The CS8900AN channel is the most useful and shows all traffic on the emulated Ethernet network. Where possible, the debugger attempts to decode IPv4, ARP, UDP, and TCP traffic to a more readable tcpdump-like format. The CS8900AD channel provides a verbose dump of the data payloads of each packet. Finally, for interface level debugging, the CS8900AR and CS8900AW channels display raw reads and writes to the CS8900A's interface region.

For protocol level debugging, VXLAN tunneling can be used to reflect the network to an external box running network diagnostic tools. For instance, the VXLAN tunnel can be aimed at virtual interface on a Linux box, which can then diagnose the traffic on the emulated tunnel using Wireshark.


Altirra Help: MyIDE emulation

MyIDE emulation
Contents
  • 1. Setting up hardware emulation
    • 1.1. Hard drive capacity
    • 1.2. I/O region
    • 1.3. Device speed
  • 2. Software support
    • 2.1. Disk-based driver
    • 2.2. Flash-based driver
    • 2.3. Custom OS kernel ROM

Setting up hardware emulation

Both the internal and cartridge varieties of the MyIDE interface are supported. To enable MyIDE emulation, select System > Hard disk... from the menu, and specify an image file to use for the emulated hard drive in the MyIDE section.

Hard drive capacity

The capacity of the emulated IDE hard drive is specified in megabytes. Note that the space available to the Atari is generally less, since only 128 or 256 bytes per sector are used. This means that only one-quarter to one-half of the drive capacity is used. Regardless, the hard drive image file always contains the full 512 bytes per sector.

I/O region

Two I/O regions can be used for the IDE interface. Select $D5xx as the interface region for a cartridge-based interface, and $D1xx for an internal interface.

Device speed

The emulated device can run at either slow or fast speed. At fast speed the emulated device responds almost immediately to commands, simulating a solid state device. At slow speed, there is additional moderate delay in command completion, comparable to average access time for a traditional hard disk.

Software support

Altirra does not include software to make use of the emulated IDE drive. You can use the MyIDE firmware or software, however. Consult the documentation for the software for full details, but here are some starting tips:

Disk-based driver

Boot the MyIDE-OS loader disk and wait for the load to complete. Once MyIDE-OS has loaded, either use the Open Image... command to swap the disk in D1:, or eject the disk in Disk Images. Do not use Cold Reset (Shift+F1), as this will kick out the soft-loaded OS. Use Ctrl+Shift+\ on the host keyboard to send Ctrl+Shift+Esc to the emulation, which initiates a soft-cold-reset.

Flash-based driver

You will need to convert the cartridge flasher disk image to a cartridge image by mounting an empty 1Mbit+IDE cartridge and then booting the flasher disk. Use $D500-D51F as the I/O window for the IDE interface.

Custom OS kernel ROM

Rename the MyIDE OS image as otheros.rom in the Altirra program directory and switch the firmware mode to Other OS. Use $D500-D51F for the external interface or $D1xx for the internal interface.


Altirra Help: R-Time 8

R-Time 8

The R-Time 8 cartridge allows the computer to keep track of accurate time. It contains a small battery backed up real-time clock (RTC) chip.

Using the R-Time 8

The intended use for the R-Time 8 is to boot a DOS that keeps track of file timestamps and load a driver to sync its clock with the one on the R-Time 8. Typically this is either disk-based SpartaDOS or the cartridge-based SpartaDOS X. In the latter case, the R-Time 8 is plugged into the pass-through port on the SDX cartridge instead of directly into the computer.

The R-Time 8 can also be read using a special Z: CIO device. The device driver for this handler must be loaded from disk and is not included with the emulator.

Emulated behavior

In Altirra, the R-Time 8 always reads the true date and time in the local time zone. Any attempts to change the date or time on the real-time clock chip are discarded. Also, the alarm functionality is not currently implemented.


Altirra Help: Rapidus Accelerator

Rapidus Accelerator

The Rapidus Accelerator adds a 20MHz 65C816, 1MB of SRAM, and 30MB of SDRAM.

Setting up the accelerator

The accelerator is ready to go without prior setup and will automatically boot into 65C816 mode. However, it can be configured by holding the Inverse key during boot. With the built-in bootstrap firmware, doing so will bring up the following configuration screen:

Use the up/down arrow keys to select an option and left/right arrow keys to change the value of the option. Help is displayed on the right for each option.

Limitations of the bootstrap firmware

The Rapidus accelerator is dependent upon firmware to bootstrap and configure the FPGA and cannot boot into 65C816 mode without it. To allow the accelerator to be emulated without this firmware, Altirra ships with a replacement bootstrap firmware that supplies similar functionality. This bootstrap firmware is not the actual Rapidus firmware, however, and so there are some differences in operation. The official hardware has a different configuration menu, and also ships with a 65C816-capable Rapidus OS pre-flashed into the flash ROM.

One other difference is that the bootstrap firmware has been written to allow easier access to the menu. In the official firmware, holding down any key at the end of the core upload causes the firmware to bypass 65C816 boot and boot on the 6502. This means that to enter the menu, the Inverse key must be held down during the short time right after the core upload finishes and before the OS is reinitialized on the 65C816. With the bootstrap firmware, only the Shift key forces 6502 boot and Inverse can be held earlier.

Using different firmware with the emulator

Altirra supports both firmware images and flashing for Rapidus, and so the same update software that would be used with the actual hardware can also be used in the emulator. There are three components that reside in the flash ROM and can be updated:

Rapidus OS
Contains the OS that replaces the operating system on the motherboard when Rapidus OS is selected in the configuration menu. This is typically a 16-bit capable OS that allows switching into 65C816 native mode with interrupts enabled.
8-bit PBI firmware
Displays the initial logo on power-up and uploads the core stream to the FPGA chip.
FPGA core
Contains the core logic in the FPGA for timing and memory mapping support for the 65C816, as well as the 16-bit PBI firmware that initializes the Rapidus on the 65C816 side.

Updating the Rapidus OS and 8-bit PBI firmware are straightforward and will work as expected. Updating the FPGA core, however, will have no effect in the emulator as the FPGA is not actually emulated due to complexity. This means that the 16-bit PBI firmware will not actually be updated by a FPGA core update.

To deal with this issue, the bootstrap firmware contains a rewritten 16-bit PBI firmware that is compatible with the official firmware as of this writing, and the firmware options contains a second firmware slot for the 16-bit PBI firmware, containing the contents of $D800-DFFF when PBI device 0 (ID=$01) is selected. This second slot can be replaced with a dump of the PBI firmware from the updated core to reflect any changes in that firmware. However, a method of obtaining such a dump is not included.

Accelerator limitations

Altirra's Rapidus emulation is functionally equivalent, but does not emulate the exact timing of the Rapidus. However, there are some fundamental limitations that still hold regardless, emulation or not. The primary limitation of Rapidus is that it can only accelerate accesses to hardware that is either part of the accelerator or is simulated (shadowed) by the accelerator. Any other external hardware must be accessed at normal speeds and will slow down the 65C816 to 1.8MHz.

The default (motherboard) ROM is not shadowed and always runs at 1.8MHz, while the Rapidus OS resides in much faster on-board flash that runs at 10MHz (1 wait state). This means that the default OS is mostly unaccelerated while the on-board OS will run at close to, but not quite, full speed.

Main memory can be shadowed in 16KB blocks. The default is unshadowed, which slows all accesses to RAM to 1.8MHz. Since the 65C816 reads more often than it writes, the normal mode of acceleration is fast read mode, which uses SRAM as a fast write-through cache for the main RAM; writes go to both fast SRAM and main RAM, while reads are serviced directly from SRAM. This has a high degree of compatibility but the $8000-BFFF block will interfere with cartridges if enabled. $0000-3FFF can additionally be switched to fast read/write to speed up writes by inhibiting write-through, but this will break any software placing graphics in the low 16KB of memory.

Extended memory and external hardware cannot be accelerated at all and will always be accessed at 1.8MHz. This means that devices such as VideoBoard XE will always be accessed at slow rate, and if the CPU is executing from external memory it will effectively have little or no acceleration.


Altirra Help: Ultimate1MB emulation

Ultimate1MB emulation
Contents
  • 1. Basic setup
    • 1.1. Hardware description
    • 1.2. Flashing the firmware
  • 2. Gotchas
    • 2.1. Entering the BIOS menu
    • 2.2. Inactive menu items
    • 2.3. PBI disk emulation
    • 2.4. SIDE SpartaDOS X cartridge
    • 2.5. Reflashing the firmware

Basic setup

Hardware description

Ultimate1MB is a hardware expansion that provides 1MB of memory, flashable BIOS/BASIC/GAME slots, SpartaDOS X, real-time clock, and SIDE parallel bus interface (PBI) disk emulation facilities.

Altirra emulates the U1MB with version 2 of the CPLD firmware, which includes PBI support.

Flashing the firmware

U1MB has 512KB of flash memory that must be loaded with an image containing both a BIOS and an OS. The BIOS is needed to initialize the U1MB hardware prior to handing control over to a conventional OS.

Altirra does not come with the U1MB firmware, which must be obtained separately. However, it does contain a recovery image with a minimal BIOS and OS that can be used to run the flasher disk to load the real hardware. Alternatively, if the image is available in raw image form it can be bound directly through the System | Firmware | ROM images menu item.

Gotchas

Entering the BIOS menu

The U1MB BIOS has a menu that is accessed by holding down the Help key while powering on or resetting the computer. In Altirra, it is easier to enter this mode if the keyboard mode is switched from cooked keys to raw keys.

Inactive menu items

When U1MB is enabled, the addressing for VBXE, SoundBoard, and Covox devices is controlled by the U1MB mapping hardware and needs to be set through the U1MB BIOS. Similarly, the memory sizing modes do not apply when U1MB is enabled as the U1MB hardware emulates various memory models under software control.

PBI disk emulation

When PBI disk emulation is enabled in the U1MB BIOS, the SIO Patch and Fast Boot options should be disabled or the SIO Override Detection mode should be turned on. Otherwise, the emulator can intercept disk accesses before the PBI BIOS does and prevent the PBI disk emulation from working.

SIDE SpartaDOS X cartridge

If a SIDE cartridge is used with U1MB, the SpartaDOS X (SDX) facility on the SIDE cartridge should be disabled. The reason is that the SIDE SDX and the U1MB SDX mapping registers overlap and this can cause difficulty in disabling SDX. A symptom of this conflict is that the PBI BIOS hangs on startup because it cannot unmap the SDX cartridge.

Reflashing the firmware

Altirra emulates the flash memory in the U1MB, so it is possible to reflash the U1MB to newer firmware in the same manner as on the real hardware. However, an important caveat that applies equally to the real and emulated hardware is that SIDE PBI support should be turned off in the U1MB BIOS prior to running the flasher. Otherwise, the flasher will fail by erasing the PBI BIOS and then calling into it via a disk read request, resulting in a crash and bricking the U1MB. Fortunately, under emulation this is simple to recover from.


Altirra Help: Video Board XE

Video Board XE

VideoBoard XE, or VBXE, is an internal expansion that greatly increases the graphics output capabilities of an Atari 8-bit computer.

Overview

VBXE installs internally within the computer, shadowing the PIA and GTIA chips and producing its own video output. It is designed to emulate the original capabilities of the computer well enough that the VBXE video output can be used all the time, even when VBXE capabilities are not being used. The VBXE graphics hardware is an improvement over the standard hardware in many ways:

  • 512K of video memory with two independent memory access windows
  • 640 pixel wide modes
  • 16-color and 256-color modes
  • 80-column text mode
  • Hardware blitter
  • Attribute map
  • Extended versions of original ANTIC/GTIA modes, including extended color and overlays

VBXE does not occupy any of the regular expansion ports like the cartridge or PBI ports, however, so it can coexist with other such expansions.

VBXE configuration options

VBXE requires a register memory access window in the parallel bus interface (PBI) space, either at $D6xx or $D7xx. Both options are supported in Altirra. $D6xx is the more common configuration in real hardware and should be used except in case of conflict; most VBXE software is compiled for this address and will not adapt to a $D7xx setup.

If Ultimate1MB is also active, Altirra automatically routes the VBXE through the U1MB module so that it can control the VBXE register configuration. In this case, the VBXE register window must be configured through U1MB setup rather than emulator options.

The VBXE firmware comes in two forms, a standard version and a "RAMBO" or 'r' version. The 'r' version emulates a RAMBO 256K memory expansion. This is supported in Altirra through the Use VBXE memory for expansion option, but the memory configuration must be adjusted to match.

Using VBXE

VBXE only has device firmware and no computer firmware, so booting a system with VBXE installed only has the VBXE emulating the standard display and no new functionality is available. To use VBXE extended features, software must be loaded that specifically supports VBXE.

Emulator limitations

Because VBXE can produce video with higher color depth and resolution than standard GTIA, some emulation options are altered or limited when VBXE is active. Screenshots are captured twice as wide, and the high artifacting modes are not available. VBXE emulation is also a bit slower than standard GTIA emulation.

On a real VBXE, the starting palette is baked into the device firmware itself, and so the color adjustment on the Atari has no effect on the VBXE display. In Altirra, the color adjustments are reflected in the VBXE's initial palette 0. This can lead to some slight color errors in VBXE-based software that relies on the initial color values in the original VBXE firmware. Newer revisions of the firmware also allow for adjusted palettes, so it is best that software be able to handle this in any case.


Altirra Help: Serial bus based devices

Serial bus based devices

Serial I/O (SIO) bus based devices plug into the trapezoidal SIO port on the computer. The SIO bus has lower speed than an internal or cartridge-based interface, but supports a wider variety of devices. Multiple devices can be connected to the serial bus by daisy-chaining.

Devices

The following serial bus based devices are emulated by Altirra:

  • Cassette tape drives
    • 410/1010 Program Recorder
  • Disk drives
    • 810 Disk Drive
    • 1050 Disk Drive
    • XF551 Disk Drive
  • Serial port interfaces
    • 850 Interface Module
  • Other devices
    • PCLink

Conflicts

Because the SIO bus allows for daisy chaining, conflicts on the SIO bus are rare. Some devices, like the 410 Program Recorder, don't have a daisy chaining port and must be at the end of the chain on real hardware; Altirra allows more than one such device to be attached, but this doesn't pose a problem in emulation.


Altirra Help: 410/1010 Program Recorder

410/1010 Program Recorder

The 410 and 1010 Program Recorders were cassette tape drives which recorded Atari programs onto standard audio cassette tape.

Booting a tape

Machine-language programs

The Atari OS has provisions for booting machine language programs from cassette tape. This is done by holding down the Start button while booting the computer. The computer emits one buzz to prompt the user to press Play on the cassette tape player and then hit a key, after which the computer begins loading from tape.

The emulator can automate this if the System / Cassette / Auto-Boot on Startup option is enabled; this causes the emulator to both hold down Start and then press the Space Bar.

BASIC programs

If the cassette tape has a BASIC program, it cannot be booted via the above procedure. Instead, the BASIC interpreter must be started first and then the CLOAD command issued. As with the ML boot, the computer will buzz once when ready and a key should be pressed after the tape player is ready in Play mode.

Talking tapes

Atari tapes are recorded in stereo and have an audio track that is piped through the computer to the TV set. This means that music and speech can be played while a program is loading. Altirra emulates this if you load a suitable tape recording in .wav format as a cassette tape.

NOTE The .cas format has no provision for an audio track. Tapes loaded in CAS will never have an audio track and resaving a .wav tape as CAS will drop the audio.

Tape control

By default, the emulator rewinds the tape to the beginning and puts the cassette tape deck in Play mode on power-up. This is fine for most simple uses, but for more complex scenarios, the Tape Control dialog can be used to control the emulated tape recorder.

The tape control dialog contains the normal controls of a tape recorder, including Stop, Pause, Play, and Record. Seeking controls are slightly different: instead of having Rewind and Fast Forward, two buttons allow for seeking to the beginning or the end, and intermediate seeking is done either by dragging the position slider or clicking on the waveform graph.

The waveform graph shows the two channels of the tape. Atari 8-bit tapes are stereo and contain an audio track (left channel, red) and data track (right channel, blue). The waveform shown for these two tracks makes it easier to find the records on the tape.

WARNING While the computer can stop or start the cassette tape motor, it cannot detect whether the tape drive is moving or whether it is in Play or Record mode. Like on a real 410/1010, if you try to load a program with the tape drive in Record mode instead of Play mode, the emulator will happily record blank tape over the program you are trying to load. Similarly, trying to save a program while the tape drive is Stopped or Paused will produce the right sounds but nothing on the tape.

Other considerations

Minimal leader

When loading a .cas file, Altirra checks if the leader at the beginning of the tape is at least 10 seconds and extends it to that amount if needed. The reason for this is that the Operating System waits nine seconds after starting the motor before trying to read from the cassette tape in order to skip past leader noise. Normally this isn't a problem because the OS writes an 18 second leader when it records to tape, but it can be an issue if part of the leader was dropped when the tape was digitized to an image file.


Altirra Help: 850 Interface Module emulation

850 Interface Module emulation

Altirra emulates the serial port side of the Atari 850 Interface Module, as well as a Hayes-compatible modem.

Contents
  • 1. Hardware description
  • 2. Emulation levels
  • 3. High-level emulation (HLE) R: handler
    • 3.1. Feature support
  • 4. 6502-based R: handler
  • 5. Modem emulation
    • 5.1. Escaping to command mode
    • 5.2. Inbound calls
    • 5.3. Dialing out
    • 5.4. Terminating a call
    • 5.5. Stored profile
  • 6. Options
    • 6.1. Telnet emulation
    • 6.2. Automatic CR/LF conversion
    • 6.3. Extended baud rate support
    • 6.4. Connection speed
    • 6.5. Force DTE rate to match connection speed

Hardware description

The 850 Interface Module is a device which connects to the SIO bus of the Atari computer and provides it with four serial ports, as well as a printer port. The serial ports are accessed through a CIO device called R:, and the printer port is used through the P: driver that is already built into the OS.

Altirra emulates an 850 that has a Hayes-compatible modem plugged into it, and simulates dialing in and out over the phone line with TCP/IP connections over the Internet instead.

Emulation levels

Three levels of 850 hardware emulation are supported:

  • None. In this mode, no hardware emulation is enabled, and a high-level emulation (HLE) R: handler is registered with CIO that communicates directly with the emulator. This provides the best performance and works with most software.

  • Minimal. This mode adds minimal support for emulating the 850 Interface Module to so that programs which expect to be able to bootstrap from the R: handler from the 850 will still work. The HLE R: handler is still used in this mode; the bootstrap command simply returns a stub loader that does nothing, as the R: handler will already be resident.

  • Full. In this mode, the 850 Interface Module hardware is fully emulated, including SIO-level commands. Instead of the simulated R: handler, a 6502-based R: handler is bootstrapped from the 850 instead. This mode is required if a communication program is being tested for adequate performance, if a custom R: handler is used, or if for some reason the HLE R: handler is not able to bootstrap properly.

High-level emulation (HLE) R: handler

Access to the serial port is provided by means of the CIO R: device. When the hardware emulation mode is set to None or Minimal, this is provided by a high-level handler, so this does not result in a reduction of available memory as with the original 850 R: handler, nor is there a need to load the handler from disk.

Feature support

Units | R1-R4:
Block mode | Not supported
Continuous mode | Always enabled
Baud rates | All standard 850 rates emulated; Atari800 extended modes available by option
Data bits | 8 data bits only
Stop bits | Ignored
Input parity | Cleared, but not checked
Output parity | Supported
Control signals | Carrier detect (CRX) and Data Terminal Ready (DTR) supported
Input buffer | Default buffer (32 byte) and external buffer supported
Output buffer | Supported (32 byte)
Translation | All modes supported (light, heavy, none)
EOL-to-CR/LF | Supported

6502-based R: handler

In the Full hardware emulation mode, a standard 6502-based R: handler is used instead. As with the real 850 hardware, this can be loaded in one of two ways:

  • Emulated disk boot. If there is no disk in D1:, the 850 responds as D1: instead and "boots" a disk that contains the R: handler. This path allows the R: handler to be used with a communication program loaded from cartridge or off of cassette tape.

  • Direct bootstrap. The 850 also exposes commands to allow direct bootstrap of the handler. The standard AUTORUN.SYS that comes with DOS 2.5 will automatically attempt this bootstrap, and other DOSes come with programs to initiate this bootstrap. The Additions disk contains both a downloader (BOOT850.COM) and a disk based version of the Altirra's R: handler (RHND850.COM).

The 6502-based R: handler is a replacement handler written from scratch, but is designed to be compatible with the original 850 hardware and handler. Like the original 850's R: handler, this handler relocates itself at and raises MEMLO when it loads. Altirra's R: handler takes slightly less memory than the original.

Modem emulation

Altirra emulates a Hayes-compatible modem in order to provide line control and answer/dial support. Supported commands are:

A/ | Repeat last command.
ATA | Answer incoming call. Reports NO CARRIER if there is currently no incoming connection.
ATB | Change communication standard (currently ignored).
ATD | Dial out.
ATE | Change command echo mode.
ATH | Hang up.
ATL | Change speaker loudness (currently ignored).
ATM | Change speaker mode (currently ignored).
ATO | Exit command mode and return to online mode.
ATP | Switch to pulse dialing.
ATQ | Enable or disable quiet response mode.
ATS/AT= | Set or query S-registers (S0, S2, S3, S4, S5).
ATT | Switch to tone dialing.
ATV | Enable or disable verbose response mode.
ATX | Change extended result codes mode (stored but ignored).
ATZ | Reset mode to default (power-up) state.
AT&C | Set carrier detect mode.
AT&D | Set DTR (Data Terminal Ready) handling.
AT&F | Reset to factory default settings.
AT&G | Select guard tone (stored but ignored).
AT&P | Select pulse dial make/break ratio (stored but ignored).
AT&T | Local analog loopback test (stored but ignored).
AT&V | Display current profile.
AT&W | Store current configuration.

The modem starts in command mode.

Escaping to command mode

In order to return to command mode after establishing a connection, the sequence +++ must be transmitted with the proper guard time before and after the sequence (time determined by S12). This can only be done from the local side; the sequence is ignored if sent from the remote side. The Time Independent Escape Sequence (TIES) is not supported, as it is not data transparent like the guarded sequence.

Inbound calls

Inbound connections are supported, if enabled. When the modem is idle and listening is enabled, an incoming connection is immediately accepted, but no transfers take place until the modem connection is enabled. The modem reports RING responses in this state, and will establish a full connection if either the ATA command is issued or the auto-answer register (S0) is set.

By default, both IPv4 and IPv6 connections are accepted if supported by the host. IPv6 connections can be disabled in serial port options. Only one connection can be active at a time.

Dialing out

The dial command (D) is used to connect across the network to a server using TCP/IP. The syntax for doing this is as follows:

ATDI hostname port

(This syntax is compatible with that used by the Atari800 emulator.)

For instance, to connect to a telnet server on the local machine, you could use atdi localhost 23 or atdi 127.0.0.1 23. If the host OS is IPv6 capable, you can also connect to IPv6 based addresses.

If a connection is successfully established, you will get a CONNECT banner as with a real phone line, and the modem will switch to the online state. If the connection fails, the modem will emit a NO ANSWER result code instead.

Outbound connections must be enabled in serial options for the Dial command to function. If outbound connections are disabled, any attempt to dial out will result in a NO DIALTONE response.

Terminating a call

If the server ends the connection gracefully, such as a web server ending transmission, the modem will drop the call with NO CARRIER once the remaining data in the connection has been read by the guest program.

If the connection drops less than gracefully, the modem will emit NO CARRIER immediately.

The connection can be terminated at any time by sending the ATH command.

Stored profile

The AT&W command allows writing to the stored profile, which is necessary to make the ATZ command work; some programs rely on being able to change the profile restored by a soft reset. However, there is no actual non-volatile storage on the modem; a cold reset of the simulation will reset both the active and stored profiles to factory default. This prevents stored profiles from persisting across a cold reset.

Options

Telnet emulation

Telnet emulation allows the emulated program to connect to a server that uses the Telnet protocol by escaping command bytes and processing incoming commands. This is necessary as some Telnet servers will not send any data until protocol options have been negotiated. For incoming connections, the Telnet emulation code will also attempt to disable local echo on the remote terminal. All requests to change options from the remote side are declined.

If Telnet emulation is disabled, then raw data is transferred in both directions without protocol translation. However, any translation modes requested on the R: device will still activate.

Automatic CR/LF conversion

The Telnet protocol requires that CR/LF be used as the line ending. While this is fine for text sent to the terminal, the LF is not usually expected on the incoming side. When this option is enabled, the network code will ensure that outgoing line endings are normalized to CR/LF and incoming line endings to CR only. This can fix problems with BBS software showing bogus keystrokes after a return due to the LF. The downside is that this translation prevents binary transfers from working correctly.

If an ATASCII EOL ($9B) is received from the remote side on an incoming connection, CR/LF translation is automatically disabled for that session. This allows full binary transparency for ATASCII connections.

Extended baud rate support

Enables support for extended baud rates, as supported by the Atari800 emulator. In this mode, the following alternate baud rates are available from XIO 36:

Encoding | Baud rate
0001 | 57600 baud
0011 | 115200 baud
AUX1 bit 6 = 1 | 230400 baud

Connection speed

By default, connections are reported as simply CONNECT. The connection speed option allows the connection speed to be set and to force that connection speed to appear in the response, e.g. CONNECT 2400. This is useful for software that checks the reported connection speed.

Force DTE rate to match connection speed

The baud rate set on the serial port controls the maximum data transfer rate in both directions; the network code buffers data both ways, but it is throttled according to the baud rate set on the R: device. If the terminal software running in emulation doesn't detect the connection rate properly on incoming calls, it can end up forcing a slow 300 baud connection. This option forces the modem to send and receive garbage until the correct baud rate is used, emulating the effect of baud rate mismatches in hardware and allowing detection routines that rely on that effect to work. When this is active, the character 'U' is sent in both directions until the correct baud rate is used.


Altirra Help: 1030 Modem emulation

1030 Modem emulation

Altirra emulates the Atari 1030 Modem with a simulated connection over TCP/IP.

Contents
  • 1. Hardware description
  • 2. Emulation levels
  • 3. High-level emulation (HLE) T: handler
  • 4. 6502-based T: handler
  • 5. Dialing out
  • 6. ModemLink

Hardware description

The 1030 Modem connects to the SIO bus of the Atari computer and establishes Bell 103 compatible connections at 300 baud over standard phone lines. The device contains built-in downloadable software to provide the T: CIO device as well as a primitive ModemLink program for communications.

Emulation levels

Three levels of 1030 hardware emulation are supported:

  • None. In this mode, no hardware emulation is enabled, and a high-level emulation (HLE) T: handler is registered with CIO that communicates directly with the emulator. This provides the best performance and works with most software. No ModemLink software is available.

  • Minimal. This mode adds minimal support for emulating the 1030 Modem to so that programs which expect to be able to bootstrap from the T: handler from the 1030 will still work. The HLE T: handler is still used in this mode; the bootstrap command simply returns a stub loader that does nothing, as the T: handler will already be resident.

  • Full. In this mode, the 1030 Modem hardware is fully emulated, including SIO-level commands. Instead of the simulated T: handler, a 6502-based T: handler is bootstrapped from the 850 instead. This mode is required if a communication program is being tested for adequate performance, if a custom T: handler is used, or if for some reason the HLE T: handler is not able to bootstrap properly. This mode also allows ModemLink firmware to be used.

High-level emulation (HLE) T: handler

Access to the serial port is provided by means of the CIO T: device. When the hardware emulation mode is set to None or Minimal, this is provided by a high-level handler, so this does not result in a reduction of available memory as with the original 1030 T: handler, nor is there a need to load the handler from disk.

6502-based T: handler

In the Full hardware emulation mode, a standard 6502-based T: handler is used instead. As with the real 850 hardware, this can be loaded in one of two ways:

  • Emulated disk boot. If there is no disk in D1:, the 850 responds as D1: instead and "boots" a disk that contains the R: handler. This path allows the R: handler to be used with a communication program loaded from cartridge or off of cassette tape.

  • Direct bootstrap. The 850 also exposes commands to allow direct bootstrap of the handler. The standard AUTORUN.SYS that comes with DOS 2.5 will automatically attempt this bootstrap, and other DOSes come with programs to initiate this bootstrap. The Additions disk contains both a downloader (BOOT1030.COM) and a disk based version of the Altirra's T: handler (THND1030.COM).

The 6502-based T: handler is a replacement handler written from scratch, but is designed to be compatible with the original 1030 hardware and handler. Like the original 1030's T: handler, this handler relocates itself at and raises MEMLO when it loads. The downloadable handler loads at $1D00 like the original; the disk-based version is relocatable.

Dialing out

Most of the 1030's commands are emulated, but dialing is only minimally supported. Both pulse and touch dialing will work, and the touch dialing mode will even generate DTMF tones, but the actual number dialed is ignored. Instead, any attempt to dial will initiate a connection to the dial proxy address in the 1030 device settings.

ModemLink

Currently Altirra does not have a replacement for the ModemLink software; an attempt to run it will give a message to that effect. The actual ModemLink software can be used if bound as firmware in Firmware Images. The image is 10,368 bytes and contains the boot sector followed by the combined ModemLink and T: handler software.


Altirra Help: Disk drives

Disk drives

Floppy disk drives were the most popular and relatively speedy form of disk storage available on the Atari 8-bit computer. Altirra emulates three main specific models of floppy disk drive as well as general forms of other enhanced drives.

Contents
  • 1. Overview
  • 2. Using emulated disk images
    • 2.1. Write modes
    • 2.2. Temporary disk images
    • 2.3. Virtual folders
    • 2.4. .ARC archives
  • 3. Hardware emulation modes
  • 4. Acceleration support
    • 4.1. SIO patch
    • 4.2. Burst transfers
  • 5. Copy-protected disks
    • 5.1. Disk image formats
    • 5.2. Load timing
  • 6. Conflicts

Overview

Floppy disk drives connect to the serial I/O (SIO) bus and can be daisy-chained to support more than one drive. The first drive is accessible in the OS as D1:, with subsequent drives being D2:, D3:, and so on. Two-drive configurations are supported by plenty of software programs, and some support up to 15 disk drives.

Using emulated disk images

Write modes

Disk images can be mounted on emulated drives with different write modes:

Read only (RO)
No write access allowed, and no changes written back to source image. This is equivalent to placing the write protect tab on a physical disk.
Read/write (R/W)
Write access allowed, with changes written back to the source image. This is equivalent to a physical disk with the write enable notch uncovered.
Virtual read/write (VRW)
Write access allowed, but changes are kept in memory without automatically writing them back to the source. This allows temporary modification of disks while keeping source files pristine. Modified disks are shown in orange in the Disk Drives dialog and can be written back to image files via the Save command on each drive.
Newly created disk images are automatically mounted in virtual read/write mode as they haven't been associated with an image file yet.
Virtual read/write safe (VRWSafe)
Similar to virtual read/write, except that format commands are blocked with a read-only disk error. This allows virtualized write access to disks while still blocking writes for programs that attempt to format their disk on boot as part of a protection check. Altirra defaults to this mode until changed in Options.

For disks mounted via drag-and-drop or the Boot Image command, the default mode is set in Options. Individual disk drives can also be changed to specific modes via the Disk Drives dialog.

Temporary disk images

Sometimes, a disk image that is dropped into the emulator via drag-and-drop will show up as blue in the Disk Options dialog. This means that the disk image is a temporary disk image created during the drag-and-drop process. Temporary disk images have no associated disk image file and are common when dropping images from a compressed archive, such as a .zip file.

Note that this does not apply to .zip files opened directly in the emulator, since in that case the emulator knows the .zip file that the image came from.

Virtual folders

Altirra can also mount folders as virtualized disk images, creating a DOS 2 or SpartaDOS file system from the contents of the folder. This is done through the flyout menu button on each drive in the Disk Drives dialog and is useful for bringing files into the emulation without having to assemble them into a disk. The disk image is dynamic, so it can adapt to live filesystem changes and the mapped files can be larger in total than can normally fit on a disk image. A virtual DOS 2 image can have up to 64 files; a virtual SpartaDOS image can have unlimited files. Virtual folder images are always read only.

.ARC archives

An .ARC archive can be mounted as a disk image. The emulator decompresses the contents of the archive and creates a virtual SpartaDOS disk image to hold the contents.

Hardware emulation modes

Altirra supports several different emulation modes, selectable through the Disk Drives dialog.

Emulation mode | RPM | High-speed rate | Supported densities
Generic | 288 | 38400 baud | Any
810 Disk Drive | 288 | Not supported | Single-density only
1050 Disk Drive | 288 | Not supported | Single-density and medium-density only
XF551 Disk Drive | 300 | 38400 baud | Any
US-Doubler | 288 | 52000 baud | Any
Speedy 1050 | 288 | 56000 baud | Any
Indus GT | 288 | 68000 baud | Any
Happy Drive | 288 | 52000 baud | Any
1050 Turbo | 288 | 68000 baud | Any

The default is Generic, which allows all disk images to be used and enables the popularly supported XF551-compatible high speed mode. The emulation mode can be changed to model-specific modes if necessary for software that requires a specific drive model for high-speed operation or for testing.

Note

While Altirra supports high speed transfer timings for various drives, it doesn't support the extended commands for drives other than the 1050 and XF551. In particular, copy protection emulation, firmware upload, and track buffering operation commands are not supported.

Acceleration support

While the Atari's SIO-based floppy drives were fairly fast for their time, they are slow by today's standards, and it isn't always desirable to emulate them at their original speed. Altirra supports a few options to speed up disk access.

SIO patch

The disk SIO patch option allows the emulator to intercept floppy disk requests at the OS level, handling them immediately. This provides near instantaneous access to the emulated floppy disk and the fastest disk operation. Altirra's SIO patch is designed to emulate as many behaviors of the standard SIO routine as possible and is highly compatible, even with some copy protected disk loaders. The SIO patch only handles a subset of disk commands and will punt to the standard OS routine if an unusual command is issued.

Programs that directly access the disk drive over the SIO bus cannot be accelerated by the SIO patch. This is most easily detected by the characteristic beep-beep-beep transfer sound if the disk routine leaves the audio channels enabled. High-speed I/O routines frequently bypass SIO, and SpartaDOS X in particular is not affected by the SIO patch.

The SIO patch can only operate from ROM and will not activate from RAM-based code, even with a jump through the SIOV vector at $E459. This avoids emulation errors that would be caused by intercepting calls at that location when the code there is not actually for the OS SIO routine. However, it also means that programs that copy the OS from ROM into RAM disable the acceleration when they do so.

Burst transfers

For programs that cannot be accelerated by the SIO patch, Altirra also supports burst transfer mode. In this mode, the disk routine accesses the floppy drive over the SIO bus as usual, but once the transfer starts the emulator feeds data to the disk routine as fast as it can handle, raising the transfer rate from the standard 19200 baud to as high as 90000 baud. While not quite as fast as the SIO patch, burst mode still results in much faster disk I/O and can work with many programs that the SIO patch cannot.

Burst mode can be enabled simultaneously with the SIO patch. When both are enabled, the SIO patch will activate if the program uses the standard OS serial routines, and the burst mode will activate otherwise.

There are two sub-modes available for burst mode, standard and polled. Standard should be used by default and works with disk routines that use interrupt-based transfers; polled mode should be used instead for disk routines that poll the serial hardware instead.

Copy-protected disks

Some software was released on disks with special disk formats designed to impede copying of the disk. These programs can still be run in Altirra if imaged appropriately, but sometimes some disk emulation options must be altered for it to load properly.

Disk image formats

Most copy protected software must be imaged in the VAPI (ATX) format in order to faithfully capture the disk format and allow the copy protection checks to pass. The ProSystem (PRO) format can also work, but is inferior to the VAPI format.

The ATR, DCM, and XFD formats do not contain disk layout, timing, or error information, and usually cannot properly contain a copy protected disk. Therefore, any copy protected software on such a disk image typically either has had the protection removed or simply won't work. Rarely, there are copy protection mechanisms that will happen to work with the ATR file format due to the way the emulator reconstructs the disk sector positioning, but this is not guaranteed to work in future versions.

Load timing

Copy protection mechanisms are often sensitive to disk timing and frequently measure sector load latencies to determine whether the disk has been copied. Disk acceleration options can therefore prevent copy protected disks from booting properly. The burst I/O option is most often the one that causes problems, since it can activate with any type of loader; the SIO patch is generally more compatible, but sometimes needs to be disabled too.

For very sensitive disk loaders, the accurate sector timing option enables the highest level of disk timing emulation, where disk sectors are returned by the emulator with the same delays and timing that a real disk drive would have. This usually is enough to load any program, but also results in the slowest disk load speed as both rotational and seek delays are emulated in real time. When this option is disabled, the emulator attempts to compensate for the accelerated load timing by warping the disk position, which allows most software to load at the higher speeds.

Conflicts

Floppy disk drives are common enough that conflicts with other hardware are rare, but there are a few cases to watch out for in emulation:

  • 850 Interface Module. The 850 simulates a disk drive to bootstrap its firmware if no disk drive exists, but if one does, it must be manually bootstrapped. 850 + disk drive was a common configuration, so many DOSes and communication programs have provisions to bootstrap the R: handler after DOS has loaded and this is rarely a problem in practice.

  • Parallel Bus Interface (PBI) drives. Devices on the Parallel Bus can also simulate disk drives with appropriate firmware. In Altirra, this is possible if PBI-capable Ultimate1MB firmware is used. On real hardware, this can result in the PBI drive shadowing the real drive of the same ID, and this is faithfully emulated.

    However, in emulation specifically, it is also possible for the reverse to happen and for the disk drive to shadow the PBI drive if the disk drive SIO patch is enabled. This happens because the emulator cannot detect the PBI drive and intercepts the disk request before the PBI firmware can handle it. The SIO Override Detection option is designed to correct this by having the emulator let the first disk request through and disabling acceleration if the PBI firmware handles the request.


Altirra Help: PCLink support

PCLink support

PCLink support allows a guest OS to access files and directories on the host machine through an SIO-based protocol.

Contents
  • 1. Overview
  • 2. Configuring PCLink
  • 3. Accessing PCLink through CIO
  • 4. Accessing PCLink with SpartaDOS X
    • 4.1. Loading PCLINK.SYS
    • 4.2. Accessing files through PCLink
  • 5. Host file mapping
  • 6. SIO protocol
  • 7. References

Overview

A PCLink device is a device on the serial I/O (SIO) bus that responds to file and directory I/O commands, typically a modern computer connected over an SIO2PC style adapter and running a PCLink server program. Unlike traditional disk emulation solutions, PCLink works directly at the file I/O layer level, avoiding the limitations of disk emulation and allowing full write access. In Altirra's case, the server program is integrated into the emulator itself, making it even easier to set up.

Configuring PCLink

The menu item System > PCLink is used to configure PCLink support. The base path controls the root directory exposed to the guest OS; files and directories within this directory and in subdirectories made available.

The Allow write access option controls whether the guest OS is allowed to create or modify any files. If disabled, all write operations including file creation, modification, renaming, deletion and directory creation/removal are blocked.

The SIO patch and burst I/O transfer options under the Disk submenu also apply to PCLink, since the transfer mechanism is similar.

Accessing PCLink through CIO

Accessing the PCLink device requires special OS support; it cannot be accessed through CIO. This means that neither BASIC nor Atari DOS can normally access PCLink, and for those the H: device should be used instead.

Accessing PCLink with SpartaDOS X

To access the PCLink device with SpartaDOS X, you must be using at least version 4.43 and have the PCLINK.SYS device from the Toolkit disk.

Loading PCLINK.SYS

The PCLINK.SYS driver can be executed directly or loaded via a DEVICE PCLINK.SYS statement in CONFIG.SYS. When it loads, it should display a message like this:

  D1:PCLINK.SYS
  PCLink v0.3, dev. 6, SIO id $6F

  D1:

The /S flag can also be specified to the PCLINK.SYS driver to use the OS SIO routine instead of SpartaDOS X's SIO routine. This allows the SIO Patch option to work and permits much faster transfers.

Accessing files through PCLink

Once loaded, the PCLINK.SYS device exports a PCL: device to SpartaDOS X. Files can be copied from and to the device, and programs can be loaded from it.

Host file mapping

Since filenames are more restricted in Atari operating systems than on modern OSes, the filenames on the host must be mapped using more restricted rules. Whenever possible, files are mapped directly, but there are some limitations on the mapping:

  • Files that are 16MB (16,777,216 bytes) or larger cannot be seen or accessed.
  • Files whose names do not fit an 8.3 pattern or use characters other than A-Z, 0-9, or _ are inaccessible.
  • Filename mangling is used to avoid collisions with reserved DOS device names, such as LPT1 or COM1. These filenames are prefixed with ! on the host, i.e. !COM1.

SIO protocol

The PCLink device is accessed through SIO device $6F, using the DOS2DOS protocol. Unlike a disk image, this protocol exposes the host filesystem using a file and handle based paradigm instead as raw blocks. While documenting the whole protocol is beyond the scope of this help, here is a summary:

  • Three main SIO commands are supported: Put ($50), Read ($52), Status ($53).
  • The Put command is used to send command parameters.
  • The Read command is used to exchange command data or responses.
  • The Status command retrieves error codes and buffer sizes.
  • Supported function codes include:
    • init
    • read
    • write
    • seek
    • get file position
    • open file
    • close handle
    • get file length
    • find first file/directory
    • find next file/directory
    • change attributes
    • create directory
    • remove directory
    • change current directory
    • get current directory
    • get disk info

When the SIO patch is enabled, Altirra accelerates PCLink requests issued through the SIOV vector.

References

  • DOS2DOS protocol (external site, in Polish)


Altirra Help: Pocket Modem

Pocket Modem

The Pocket Modem by BOT Engineering is small adapter that connects to the SIO port and provides 300 baud modem connectivity. A special proprietary 500 baud mode is also available.

Setting up the Pocket Modem

Adding the device

Under System, Devices, add the Pocket Modem under the SIO devices category as a child of the Computer node.

The Pocket Modem consists of an SIO end plug, which means it must be at the end of the SIO device chain. This means that it normally would not be usable at the same time as other devices that needed to be at the end, notably cassette tape drives. However, it can be used at the end of a chain including disk drives.

Loading handler software

Booting off the Pocket Modem is not possible, so handler and terminal software must be loaded off disk. This is not provided with the emulator and must be obtained externally. The handler typically loads as the M: device, which is then used by terminal software written in either BASIC or assembly language.

There is an unfortunate stability issue with the M: driver software provided on the Driver Disk and the DOS DUP menu. The driver software loads low in memory and hooks several interrupts including the vertical blank interrupt. The area in which it loads is also used by DUP, however, so any attempt to load DUP while the M: driver is present results in a crash. As DOS will automatically attempt to load DUP if no cartridge is present, this typically means that a disk which is set up to load the M: handler on startup will crash on boot if BASIC is not enabled.

Hardware capabilities

The Pocket Modem is capable of full-dupliex communications at 110, 210, 300, and 500 baud. Pulse dialing is also possible, but not tone dialing. It can detect both carrier and ringing, and auto-answer the phone under software control. Unlike with the 850, it does not require dropping out of communications in order to check carrier state.

Emulation details

The baud rate used over the connection is controlled solely by the rate that POKEY shifts bits in and out, and the modem itself is never told of the baud rate. This means that the user must pre-configure the terminal software beforehand to the baud rate that will be used by the other end. As this is inconvenient for emulation, and emulating baud rate mismatches is of limited use, Altirra simply automatically changes the baud rate based on the last byte transmitted.

Any attempt to pulse-dial a number results in the dial address in the modem settings being used to establish a connection.


Altirra Help: SX212 Modem emulation

SX212 Modem emulation

Altirra emulates the Atari SX212 Modem with a simulated connection over TCP/IP.

Contents
  • 1. Hardware description
  • 2. Emulation levels
  • 3. Supported commands
  • 4. Dialing out

Hardware description

The SX212 modem is a Hayes-compatible modem that connects to a computer either by the Atari SIO bus or an RS-232 serial port connection. It can communicate at either 300 baud or 1200 baud. Unlike the 1030 modem, it does not contain built-in handler or communications software.

When used with an SIO connection, the SX212 modem is designed to be used at the end of the SIO chain, as it only has one SIO plug and uses the same bus signals as a cassette tape recorder. Therefore, it cannot be used with cassette tape.

Emulation levels

Altirra's SX212 emulation is for an SIO connection to the modem. Two levels of emulation are supported:

  • None. In this mode, no hardware emulation is enabled, and a high-level emulation (HLE) R: handler is registered with CIO that communicates directly with the emulator. This provides the best performance and works with most software.

  • Full. In this mode, the SIO protocol for the SX212 modem is emulated. A software handler is required to provide the R: device to communicate with the hardware. The Additions disk contains such a handler.

Supported commands

The SX212 supports the following commands:

Command | Function
A/ | Repeat last command
ATA | Set answer mode
ATB | Set Bell modulation mode
ATC | Set transmit carrier
ATD | Dial
ATE | Set echo
ATF | Set full duplex
ATH | Set on/off hook
ATI | Information
ATL | Speaker loudness
ATM | Speaker mode
ATO | Set originate mode
ATP | Set pulse dial mode
ATQ | Set quiet mode
ATR | Set reverse mode
ATS | Set or query register
ATT | Set touch dialing
ATV | Set verbose reporting
ATX | Set connect/busy/dialtone reporting
ATY | Set long space disconnect enable
ATZ | Reset modem

The escape sequence +++ is used to escape from online state to command state. It must be both preceded and followed by a second of guard time to be recognized.

Commands can be sent at either 300 or 1200 baud, and the modem will automatically switch speeds to match. (On the real hardware, this is reflected in the high speed (HS) light.) Online communication, however, must occur at the baud rate of the connection.

Dialing out

Most of the SX212's commands are emulated, but dialing is only minimally supported. Both pulse and touch dialing will work, and the touch dialing mode will even generate DTMF tones, but the actual number dialed is ignored. Instead, any attempt to dial will initiate a connection to the dial proxy address in the 1030 device settings.


Altirra Help: The Black Box

The Black Box

The Black Box is a Parallel Bus Interface (PBI) based device that adds hard disk, serial, and parallel printing capabilities.

Setting up the Black Box hard drive

Under System, Devices, add the Black Box to the Computer, then select the Black Box or the empty slot under it and add a hard drive.

Next, set up Black Box firmware in System, Firmware. You must have the Black Box firmware to proceed; it is not included with the emulator. The firmware auto-scan can detect three versions of firmware and as of this writing v2.16 is recommended.

On a physical Black Box, the menu is entered by pressing the menu button on the device. In the emulator, this is done in the menu via System, Console Switches, Black Box: Menu. This will then switch the display to the menu:

Under Port Statuses, set the sector number for the partition table:

The starting sector for the partition table can vary, but 1 or 2 is fine. Save, then exit out of Port Statuses and then open the Drive Configuration sub-menu:

This screen allows ranges of sectors on the hard disk to be mapped to the D1-D9: drives seen by the OS and by programs. Consult the Black Box documentation for full usage details. For new partitions, you'll want to enter a new name in the Name field, the the sector ranges and density, then write the partition to the partition table, after which it should show up in the partition list for future reuse.

The first emulated hard disk has address ID 0, LUN 0 on the SCSI bus, the second has ID 1 LUN 0, etc.

Make sure to Save Config before exiting. After doing this, the partition table will be saved on the hard disk image and won't need to be re-entered even after restarting.

Note that if you have set up D1:, the computer will attempt to boot from it and crash, which is a problem if you haven't initialized the image yet. Solutions are to either swap it to another drive slot temporarily or boot off of a Sparta DOS X cartridge.

Emulation considerations

Because the Black Box can emulate disk drives, it is important to make sure that the emulator doesn't interfere with the Black Box emulation. In previous versions of the emulator, it was necessary to disable the SIO patch for disks or enable SIO override detection to make this work, or else the emulator would intercept the disk request before the Black Box firmware could, bypassing the Black Box.

As of 2.80+, the SIO acceleration mode can be changed from SIOV to PBI, which causes the emulator to use a PBI device instead of intercepting the SIOV vector. The emulator's PBI device is at lowest priority (ID 7), so it does not interfere with the Black Box. This allows accelerated disk access for disk drives not handled by the Black Box.


Altirra Help: KMK/JŻ IDE 2.0 (IDEPlus 2.0)

KMK/JŻ IDE 2.0 (IDEPlus 2.0)

The Black Box is a Parallel Bus Interface (PBI) based device that adds hard disk, serial, and parallel printing capabilities.

Accessing the IDE+2 menu

To access the IDE+2 menu, hold down Start while pressing Reset. In the emulator, use F2+F5 with default key bindings, or use the Hold Keys function to hold down Start before selecting Cold Reset.

SpartaDOS X cartridge option

A subtle but documented quirk of the menu is that the SDX cartridge option is not saved and always reverts on power-up. Instead, the SDX hardware switch is used to configure the power-up state of this option. In the emulator, this switch is configured in the device settings for the IDE+2.

External cartridge option

The IDE+2 has a built-in pass-through cartridge port similar to that on a SpartaDOS X cartridge. The emulated device has full support for this port as provided by a 130XE's ECI or an 800XL's PBI with the additional cartridge port connection, and thus the External cartridge setting is fully functional.

This setting is not used when the internal SpartaDOS X cartridge is enabled because in that case SDX manages the external cartridge: it initializes the external cart, boots SDX as DOS if requested by the cart, and then runs the external cart. Some cartridges will allow SDX to boot, while others will launch immediately almost as if SDX were not present.

NVRAM reset issue

On a real IDE+2, the menu must be exited before resetting or powering off the computer. This is because of a design issue with the Real Time Clock (RTC) chip that contains the configuration: the NVRAM must be temporarily overwritten by the time to read the clock and the config data written back afterward. If this operation is interrupted, the config is corrupted and lost.

Since the emulator can be restarted or closed more often, it protects the NVRAM by default to save the config data it is still overwritten by clock data at reset or close. This behavior can be disabled in device options if accurate behavior is desired. In that case, exiting the menu improperly will corrupt the configuration in the same way as on real hardware.


Altirra Help: Multi-I/O (MIO)

Multi-I/O (MIO)

The Multi-I/O (MIO) is a Parallel Bus Interface (PBI) based device that adds hard disk, serial, and parallel printing capabilities.

Setting up the MIO hard drive

Under System, Devices, add the MIO to the Computer, then select the MIO or the empty slot under it and add a hard drive.

Next, set up MIO firmware in System, Firmware. You must have the MIO firmware to proceed; it is not included with the emulator. As of this writing, v1.41 is recommended.

The menu for the MIO is entered by holding SELECT while powering on or resetting the computer:

The menu sets which OS drives (D1-D8:) are handled the MIO and whether they map to ramdisks or to images on the hard drive. Press N to select the desired slot and then T to change the drive type. Use ID,LUN 0,0 for the first hard drive, 1,0 for the second, etc. Adjust the parameters on the right to locate and set the size of the disk image. Refer to the MIO manual for more details.

Note that if you have set up D1:, the computer will attempt to boot from it and crash, which is a problem if you haven't initialized the image yet. Solutions are to either swap it to another drive slot temporarily or boot off of a Sparta DOS X cartridge.

Emulation considerations

Because the MIO can emulate disk drives, it is important to make sure that the emulator doesn't interfere with the MIO emulation. In previous versions of the emulator, it was necessary to disable the SIO patch for disks or enable SIO override detection to make this work, or else the emulator would intercept the disk request before the Black Box firmware could, bypassing the Black Box.

As of 2.80+, the SIO acceleration mode can be changed from SIOV to PBI, which causes the emulator to use a PBI device instead of intercepting the SIOV vector. The emulator's PBI device is at lowest priority (ID 7), so it does not interfere with the MIO. This allows accelerated disk access for disk drives not handled by the MIO.


Altirra Help: Full disk drive emulation

Full disk drive emulation

As of Altirra 2.90, full disk drive emulation is now supported, which means that the disk drive's controller and firmware is emulated instead of just the behavior of the disk drive. This allows for more precise emulation of disk drive behavior, particularly for disk drive types that allow custom code to be uploaded.

Enabling full drive emulation

Full disk drive emulation is enabled by adding the desired disk drive in the Devices tree. Many variants of the Atari 810 and 1050 are supported, as well as the Z80-based Indus GT and the 8050-based XF551. Adding one of these devices will replace the regular disk drive emulator for D1:. Currently only one full drive emulator can be used at a time.

Firmware images are required for any of the full drive emulations to work. These must be obtained externally as they are not included with the emulator, nor are replacements provided. Firmware images for disk drives are set up in System, Firmware, Firmware Images. If the correct firmware is not available, the disk drive controller will fail to boot, typically signified by the disk drive motor staying on.

Empty drive behavior

A notable difference between normal and full drive emulation is the behavior when no disk is mounted. The normal drive emulator automatically turns off the emulated drive when the disk is removed, whereas the full drive emulator stays on. This correctly emulates what happens when booting the computer with the disk drive turned on but with no disk, and some enhanced disk drives even boot the computer from software in the firmware ROM in this case. However, as on the real hardware, this also causes the OS to always attempt to boot off the disk drive, and in particular prevents entry into internal BASIC without booting DOS.

Power-on delay

Some disk drives take an abnormally long time to initialize before they are ready to accept commands from the computer, as long as several seconds. This is worst for drives like the ATR-8000 and Percom RFD-40S1 that have multiple mechanisms to reset on power-up. If the computer is powered on too quickly after the drives, the computer can time out on the disk commands before the drive becomes ready.

With real hardware, this is handled by powering on the drives first and then the computer instead of both at the same time (i.e. not by a common power strip). In Altirra, this is simulated using the Power-on Delay setting. The "Auto" value selects a value appropriate for the typical firmware for the drive type selected.

Limitations

While full drive emulation is more precise, it is also more expensive performance-wise and limits the ability of the emulator to monitor and enhance disk drive operation. The following limitations are in effect when using a full drive emulation device:

  • D1: acceleration is not possible, either with SIOV/PBI patch or burst I/O. However, rotational delays can still be disabled (Accurate Sector Timing option disabled).
  • Only disk types actually supported by the physical drive and firmware can be read and written. For instance, mounting an enhanced density 130K image in an emulated 810 drive will not work, because the 810 is incapable of reading enhanced density disks. Non-physical disk image geometries, like 16MB mega-disk images, will not work at all.
  • Booting BASIC programs directly will not work. This is because the emulated drive is always turned on, even without a disk, and thus the OS will always attempt to boot from D1: instead of running BASIC.
  • The VRWSafe write mode, which allows virtualized writes but blocks formatting, may not work because the emulator may not be able to block the drive firmware from doing a format. The format will still be virtualized and usually prevented even in the virtual image, but the drive firmware may still format and verify all 40 tracks.
  • Drives that use track buffering, such as the Happy 1050 and the Speedy 1050, will perform poorly with or possibly even cause read errors when used with virtual folder mounts. This is because the track buffering obscures what files DOS is trying to read. The virtual folder mount code attempts to accommodate track buffering, but performance is very poor and using a track-buffered drive with a folder mount is not recommended.

In addition, running full drive emulation also exposes all bugs in the original firmware, some of which that are rather annoying -- such as drive firmware that takes too long to initialize on boot, or is incapable of properly detecting a density change sometimes when swapping disks.

For these reasons, it is recommended that full disk drive emulation only be used when running software that specifically needs it, such as Indus CP/M or an enhanced copy program.


Altirra Help: Atari XF551

Atari XF551

The Atari XF551 disk drive supports single-density (90K), enhanced density (130K), double density (180K), and double-sided double density (360K) disks.

Density switching issue

The real XF551 running on stock firmware has problems switching from single-density or enhanced-density to double-density modes, which is emulated faithfully if the same firmware is used. The symptom is that inserting a double-density disk after reading or booting either a single-density or enhanced-density disks times out with a read error after a significant delay. This is caused by the firmware failing to recognize the double-density disk and trying to read track 13, sector 22 when DOS requests a read of the first directory sector.

Booting off of a double-density disk avoids this problem because the firmware successfully switches to double-density when DOS.SYS is read during the boot process. Enhanced DOSes that allow direct control of the density setting for a drive will work around this, as well, XF551-specific DOSes that read sector 4 to trigger a density switch on the drive.


Altirra Help: Indus GT

Indus GT

The Indus GT disk drive supports single-density (90K), enhanced density (130K), and double density (180K) disks. It does not support double-sided disks, however.

Booting Indus CP/M

Altirra's Indus GT emulation includes the RAMCharger, which means that it can run Indus CP/M. The trickiest part of booting Indus CP/M is obtaining a working image of it. The disk is a double-density disk with 256 byte boot sectors, which is unusual. Some images of this disk encoded in the .ATR file format are corrupted because they only hold the first 128 bytes of the the first three sectors. To boot properly, the full 256 byte contents of these sectors are required, which cannot be encoded safely in .ATR. A working image of the Indus CP/M disk must be encoded in .XFD for the emulator to use it.

A good image of Indus CP/M disk must be paired with an accompanying boot disk for the computer, which acts as the terminal. The procedure for booting CP/M is as follows:

  • Boot the terminal disk on the computer.
  • Start the terminal program (typically E.COM or F.COM, for 40 and 80 column operation).
  • Remove the terminal disk and insert the CP/M disk. This must be done without rebooting the computer, so the Open Image, Disk Drive dialog, or Attach disk commands should be used instead. Alternately, mounting both disks on D1: and D2: and using the rotate disk commands can be convenient.
  • Instruct the Indus drive to boot CP/M by holding Drive Type and pressing Error. This is not possible in the emulator, so a special command has been added to do it: System, Console Switches, Indus GT: Boot CP/M.

If everything has been done correctly, CP/M will boot and be displayed in the terminal:

Interleaving performance issue

Indus CP/M will boot very slowly by default if the Accurate Sector Timing option is enabled. The reason for this is that the ATR and XFD disk image formats lack interleave information, so the emulator assumes an interleave appropriate for normal Atari disks. Indus CP/M expects its disks to be formatted with a 1:1 interleave and runs very slowly with the standard Atari 9:1 or 15:1 interleave.

There are two solutions to this problem. One is to reinterleave the disk image to 1:1 through the Disk Drives dialog. This restores expected and authentic performance, but unfortunately it can't be saved back to the disk image file. The other is to disable the Accurate Sector Timing Option.

Problems with enhanced density disks

Indus GT drives with version 1.1 firmware cannot read enhanced density disks properly. They will appear to work and then fail with error 139 when trying to read beyond the single density size (90K). This bug is fixed in version 1.2. Unfortunately, there is an image of firmware 1.1 floating around the Internet that is mislabeled as version 1.4, which has added some confusion.


Altirra Help: I.S. Plate

I.S. Plate

The I.S. Plate is a modified Atari 1050 with support for single, enhanced, and double density disks.

Accurate sector timing

The "accurate sector timing" option is forced on when emulating I.S. Plate drives. This is to work around a bug in the ISP firmware where the firmware crashes and reboots if it sees more than about 100 sectors within a quarter of a second.

NTSC compatibility

The US/IS Doubler emulation option in the ISP Lightspeed rev. 2.0 software will not work if the computer is set to NTSC. This is due to out of tolerance serial timing in the IS Doubler firmware preventing the computer from being able to receive. The timing is just barely within POKEY's requirements for an ideal PAL machine, but beyond what POKEY can receive for ideal NTSC. Your mileage may vary on real hardware due to real-world clock variance.


Altirra Help: Speedy 1050

Speedy 1050

The Speedy 1050 is a modified Atari 1050 with support for single, enhanced, and double density disks.

NTSC compatibility

The Speedy 1050 cannot reliably handle high speed writes with an NTSC computer and will constantly trigger SIO device NAK errors (error 139). This happens because NTSC computers send data slightly faster than PAL computers and the Speedy 1050 firmware's receive loop is slightly too slow to handle the faster NTSC speed. Firmware versions up through 1.6 have this problem; a fixed 1.7 version is available for the MegaSpeedy add-on, which includes Speedy 1050 as one of its modes.


Altirra Help: Host device

Host device

The host device allows the programs using the OS character I/O (CIO) facilities to access files directly on the host file system. This is an easier way to exchange files with programs running under emulation than manipulating disk images.

Contents
  • 1. Setting up the host device
    • 1.1. Long filename encoding
    • 1.2. Burst I/O
  • 2. Accessing the host device
    • 2.1. End-of-line character translation
    • 2.2. File locking
    • 2.3. Subdirectory access
    • 2.4. Reserved host name handling
  • 3. Host device restrictions
    • 3.1. CIO access
    • 3.2. Supported special CIO commands
    • 3.3. Hook page requirement
    • 3.4. Device driver invocation

Setting up the host device

To set up the host device, open the Devices dialog using the System > Devices... menu option, and add the H: device from the High-level Emulation section of the device list.

Long filename encoding

The H: device follows DOS 2 rules for filenames, which restrict filenames to no more than 8.3. The filename encoding option allows access to files that have names which don't fit this rule by encoding the filenames within the 8.3 convention. For instance, the filename "(New disk)" is accessed as NEWDI_5M.

There is no support for long/short name tunneling with this feature, as it is intended only to allow access to files and not as a extension mechanism. All calls to create or rename a file from the emulation side will always result in the short name being used in the host filesystem, with any previous long filename lost.

Wildcards are resolved with the encoded filenames, i.e. the 8.3 filenames seen by the emulation.

Burst I/O

This option allows the host device driver to detect when CIO is doing a block read/write and accelerate the process by skipping the very slow per-byte calls that CIO normally does. This is similar to what Atari DOS does for its D: device. Disable this option if programs are receiving garbled data from GET CHARS commands.

Accessing the host device

The host device appears as an H: device in the CIO device table.

End-of-line character translation

By default, the H: device does not do any translations on read or written data. This is inconvenient for text files since the host-based text programs use a LF or CR/LF convention instead of the Atari's $9B end-of-line character. To deal with this, the host device maps H6: through H9: to the same paths as H1:-H4:, but accesses through the H6-H9: aliases do EOL conversion. On read, CR, LF, or CR/LF pairs are converted to ATASCII EOL characters, and on write, EOL is converted back to CR/LF.

All file sizes and file positions seen by the emulation are in the ATASCII version of the file stream and are consistent with GET/PUT commands.

File locking

The DOS locked file attribute is mapped to the read only attribute on the host: a read-only host file is reported as locked and the LOCK/UNLOCK FILE commands will change the host read-only status.

A different kind of locking results when the emulation opens a file: Altirra will hold an appropriate lock on the host file based on the access type. Files opened for read access will be opened with a read-only lock, and files opened for write or append status will have an exclusive lock. This has two effects, one being that it prevents other host applications from interfering with the file access, and another being that it prevents the emulation from simultaneously opening a file twice with conflicting access parameters. This also means that if the emulation tries to open a file with access flags that conflict with itself or another host application, the SDX Access Denied error code ($B0) is returned.

Subdirectory access

The H: device also allows for subdirectory access in a style similar to used by MyDOS and SpartaDOS. The directory separator may either be > or \. In addition, the special names . and .. refer to the current and parent directories from the current location.

Each of the four H: devices has also has a current directory associated with it, which is by default used to resolve relative paths. Use of a directory separator immediately after the device specifier, i.e. H:>FOO, forces resolution as an absolute path from the root regardless of the current directory. H6-H9: share the same current directory as H1-H5:.

Reserved host name handling

On Windows platforms, several base filenames are reserved for devices, including CON and LPT1. To prevent inadvertent access to these devices, any accesses from the emulation to paths with these names will be translated by the driver to use a $ prefix, resulting in a path component like $CON. This translation is bidirectional and is invisible to the emulation for both file and directory access.

If the host filesystem actually contains files named with reserved device names, as is possible with use of NT file path escaping (\\.\C:\COM1), these files will not be accessible to the emulation.

Host device restrictions

CIO access

Because H: is a CIO device, only programs and disk operating systems that use CIO to access devices will allow access to files on H:. DOS 2.0S uses CIO access and allows use of the H: device. MyDOS 4.5 and SpartaDOS 3.2 will also use H:, with some bugs related to wildcard handling.

On the other hand, SpartaDOS X does not and thus attempts to access H: will fail, as it will attempt to access D8: with its own routines. With SDX, the PCLink device should be used instead.

Supported special CIO commands

  • $20: Rename
  • $21: Delete
  • $23: Lock
  • $24: Unlock
  • $25: Point (seek)
  • $26: Note (tell)
  • $27: SDX Get File Length
  • $29: MyDOS Change Directory (chdir)
  • $2A: SDX Create Directory (mkdir)
  • $2B: SDX Remove Directory (rmdir)
  • $2C: SDX Change Directory (chdir)
  • $30: SDX Get Current Directory (getcwd)

Hook page requirement

The host device drivers requires a free page in hardware address space for the CIO device driver definition. The two pages that the host device driver can use are $D600 and $D700. If both of these pages are in use, the host device will not work. Note that this hook page can be shared with other emulated devices, like the printer.

Device driver invocation

The H: device driver has a real CIO device entry point table and real entry points that can be called from 6502 code. This includes both going through CIO and by directly accessing the entry points, as Atari Basic does for the PUT BYTE handler. However, the code at these entry points is not real as the device driver is virtual, and attempts to relocate or patch the device driver routines will fail.


Altirra Help: Corvus Disk Interface

Corvus Disk Interface

The Corvus Disk Interface is an external hard disk interface that connects to joystick ports 3 and 4, and provides access to a 6MB hard disk.

Setting up the Corvus

Device connections

The Corvus interface is added through the Devices tree, under the Controller port devices category. Underneath it, a hard disk must be added. The hard disk must be at least 6MB in size (12240 blocks).

Note

Because the Interface uses joystick ports 3 and 4, it can only be used on 400/800 hardware.

Initializing the Corvus drive

Before it can be used, the Corvus drive must be initialized first. The Interface has no firmware and the SysGen disk that comes with the Corvus drive must be used to initialize the drive. Altirra does not include software to do so, but you can use the same utility disks that are used with a real Corvus drive.

Follow the instructions in the Corvus Atari 800 Disk System Installation Guide, which include running SYSGEN from the SysGen disk to initialize the disk and WRITBOOT.OBJ to create a boot disk. The SYSGEN utility is a BASIC program, so you must use the BASIC cartridge in addition to the utility disks. Since the hardware mode will be 400/800 mode, there will be no built-in BASIC and the BASIC cartridge will need to be attached via the Attach Special Cartridge sub-menu.

Emulation details

Altirra emulates a H-series model 6 (6MB) drive. The disk geometry is 306 cylinders, 2 heads per cylinder, 20 sectors per track, and 512 bytes per sector. The controller is emulated but not the controller CPU, so it is possible to trash the firmware area and still interact with the drive. For this and other reasons, it is not recommended to try using an Altirra-created image on a real Corvus.


Altirra Help: XEP80 Interface Module

XEP80 Interface Module

The XEP80 Interface Module was an 80-column display module that connected to the joystick port of the Atari. Altirra emulates all official and some unofficial functionality of the XEP80, but not the printer port.

Usage

XEP80 emulation is enabled via the System > XEP80 menu and emulates an XEP80 device plugged into joystick port 2. For this to work, no other input device should be activated that also uses port 2 (this is impossible on real hardware except by soldering or other physical means).

Once XEP80 emulation is enabled, display driver software must be loaded to use it. You can either use the XEP80.SYS included with the original XEP80 software disk, or you can use the replacement ALTXEP80.SYS from the Additions disk.

The XEP80 generates a different display parallel to the one usually created by the Atari. In Altirra, use View > XEP80 View to toggle between the standard display and the XEP80 display. The Auto-switch XEP80 View option makes this easier by automatically switching to the XEP80's display if it receives valid data and back to the standard display on reset.

Caveats

The printer interface isn't emulated.

Altirra attempts to emulate a fair amount of undocumented behavior within the XEP80, particularly with regard to altering the display registers and changing UART baud rates. However, the XEP80 testing command set allows hardware registers and internal memory to be overwritten in ways that can cause very unusual behavior, and not all of that is emulated.

Only software specifically designed for the XEP80 or that works through standard CIO E: and S: interfaces will work with it. Programs that write directly to the screen buffer or hardware registers will not work either in emulation or real hardware. This excludes most games and even some productivity software.

The actual XEP80 hardware is notorious for extreme amounts of vertical overscan since it generates 250 active scanlines in NTSC and 300 in PAL in text mode. As such, while the emulator shows all 25 rows of text, the top and bottom rows are seldom completely visible on real hardware, subject to depending on the TV set.

It is possible to reprogram the XEP80 for custom horizontal and vertical refresh rates. Altirra allows horizontal refresh rates of 14.7-16.7KHz and vertical rates of 48-52Hz (PAL) or 58-62Hz (NTSC). Scanning rates that are out of tolerance will result in an unsupported screen mode message similar.


Altirra Help: About Custom Devices

About Custom Devices

A custom device allows the emulator to be extended to support new hardware without modifying the emulator itself. It is implemented through a custom device host architecture that allows the device to be described solely through a device description file and scripting, both of which can be created with a text editor.

The detailed specification for a custom device is given in the specification topic. Below is an overview.

Device description

All custom devices have an .atdevice file that describes the parts of the custom device. This file is a JSON file and can optionally refer to other files for scripts or for initialization data. When a custom device is added to the device tree, the settings dialog has a path property pointing to the custom device description file.

The facilities exposed to a custom device are a subset of the internal interfaces within the emulator, and the scripting engine is simplified compared to what can be implemented directly in C/C++. The custom device, however, appears the same to the rest of the emulator as any other internal device. The design also reduces the amount of work that must be done by script, minimizing performance impact. For instance, a bank switching cartridge need only respond to bank switching accesses, with the memory mapping performed by the emulator itself.

Security

For security reasons, the input and output functionality in a custom device is slightly restricted. Any files referenced by the device configuration must be local to the .atdevice file, and any network connection specified will connect only to localhost. This prevents arbitrary files on disk from being unexpectedly sent over the network connection.

Hot reload

The settings dialog for a custom device has an option to enable hot reload for the device configuration files. When this is enabled, the .atdevice file and any associated files it references are monitored for changes, and automatically reloaded on a change. This speeds up iteration.

When using hot reload, disabling the "pause when inactive" function is recommended so the emulator continues to run while the custom device configuration is edited in another program. Also, note that the custom device will be reset when the reload occurs, so often it is necessary to trigger a cold reset after a successful reload.


Altirra Help: Custom device specification, v0.3

Custom device specification, v0.3
Contents
  • 1. Basic device description structure
    • 1.1. Top-level statements
    • 1.2. Literal values
    • 1.3. Variables
    • 1.4. Functions
    • 1.5. Structured data
    • 1.6. Options
    • 1.7. Comments and whitespace
  • 2. Segments
    • 2.1. Initialization data
    • 2.2. Persistence
  • 3. Memory layers
    • 3.1. Direct mapped layers
    • 3.2. Binding-controlled layers
    • 3.3. Cartridge-enabled layers
    • 3.4. PBI-enabled layers
    • 3.5. Hardware address ranges
  • 4. Serial I/O (SIO) devices
    • 4.1. Declaring SIO devices
    • 4.2. Declaring SIO commands
    • 4.3. SIO protocol handling
    • 4.4. Protocol timing
    • 4.5. Using sent/received data
    • 4.6. Concurrency with other scripts
    • 4.7. Accelerated transfers
    • 4.8. Raw SIO transfers
    • 4.9. Conflicts with built-in devices
  • 5. Parallel Bus Interface (PBI)
    • 5.1. Selection support
    • 5.2. Auto-PBI memory layers
    • 5.3. PBI interrupts
  • 6. Controller ports
  • 7. Video outputs
    • 7.1. Creating the video output
    • 7.2. Drawing the image
    • 7.3. Drawing character displays
    • 7.4. Fine-tuning image display
    • 7.5. Pass-through
    • 7.6. Activity detection
    • 7.7. Supporting text copy
  • 8. Scripting
    • 8.1. Global variables
    • 8.2. Special variables
    • 8.3. Local variables
    • 8.4. Expressions
    • 8.5. Statements
    • 8.6. Returning values
    • 8.7. Branching
    • 8.8. Looping
    • 8.9. Conditional compilation
    • 8.10. Functions
    • 8.11. Calling functions
    • 8.12. Calling methods
    • 8.13. Threading
    • 8.14. Scripted events
  • 9. Network server
    • 9.1. Enabling server actions
    • 9.2. Events sent to the server
    • 9.3. Synchronous communication
    • 9.4. Asynchronous communication
    • 9.5. Restrictions on script interrupts
    • 9.6. Request types
  • 10. Sample device descriptions
    • 10.1. 8K cartridge
    • 10.2. Single-density disk drive
  • 11. Changes

Basic device description structure

An .atdevice file is a text file that describes the device and its core functionality. The file consists of a series of declarations which describe the parts of the custom device:

  // Comment
  option "name": "My custom device";

  Segment status_seg: {
      size: 4
  };

  function void init_status() {
      status_seg.write_byte(3, $E0);
  }

  event "cold_reset": function {
      init_status();
  }

The file is mostly processed in order from the beginning. Except for cases where one section refers to another, sections can generally be mixed in any order within the device description file.

Top-level statements

At the top level, the device description is a series of statements. Each statement ends with a semicolon, no matter how many lines it spans. The exception is a function, which ends with a closing brace without a semicolon; this is to be syntactically familiar to the C programming language.

Literal values

Integer constants can be specified either as decimal or hexadecimal (base 16). Hex constants are prefixed with $ (not 0x).

String constants are specified within double quotes, e.g. "abc".

Variables

Variables are used to store values used or modified by a device, as well as objects used to interact with the host emulation. Variables are statically typed: they are always defined with a type and can only ever hold values of that type.

There are two types of variables. Variables of primitive type, currently only type int, are modifiable by script and used to hold device state. These are described in the Scripting section. They may also be of class object type, such as Segment for data segments, or MemoryLayer for emulation memory layers. Defining a variable of object type usually unlocks behaviors in the custom device, such as enabling serial I/O bus support.

Variable names start with a letter or an underscore (_), followed by zero or more letters, numbers, or underscores. A variable name cannot contain spaces, dollar signs ($), or non-ASCII characters. Both lower and upper case may be used in variable names, and case is significant: abc and Abc are different variables. Variables must have unique names even if they are of different types.

The following names are reserved as keywords in a device file and are not available as variable names:

else | event | false | function
if | int | loop | option
return | true | void

A global variable is declared at top-level with the type followed by the variable name, and ending with a semicolon:

  int current_bank;
  Thread recv_thread;

Functions

Functions hold statements that drive device logic. They are declared using the function keyword:

  function void reset_bank() {
      bank = 0;
  }

These are described in more detail in the Scripting section. A function by itself does nothing unless it is called, typically from an event handler or a memory access binding.

Structured data

Many object types require structured data to configure the object when it is created. This is specified with a colon after the variable name, followed by the data:

  Segment status_buffer: { size: 4 };

The object data is structured similarly to JSON, with some syntax adaptations. The top-level data value can be a boolean (true or false), an integer, a function, a variable, an array, or an data object. Arrays and data objects can in turn contain sub-values, creating a tree of nested values.

Arrays are specified with [brackets] and contain a comma-separated list of sub-values, e.g. [1, 2, "abc"]. The values may be of different types. The array can also be empty: [].

Data objects are contained within {braces} and have a comma-separated list of named members. Each member is specified with the form name: value, where the name is a valid variable name. { x: 4, y: 7 } and {} are valid data objects. Member names must be unique within an object. Unlike in JSON, however, the name is not quoted.

Variables are referenced in a few cases within object data, particularly to connect to segments. These are simply specified by the variable name without quotes.

Finally, object data may have inline script functions, which are small fragments of script code within the object data itself. The syntax is function { ... }, where the body of the function is specified within the braces.

Options

The option statement is used to set miscellaneous device parameters. The syntax is:

option "option-name": option-value;

The option value is a structured data value which depends on the option. One option is "name", which is a string that determines the display name of the device in UI. Another is "network", which enables a connection to the network server.

The option "debug" is handled by the compiler itself. The option value is either true or false and determines whether conditional code prefixed by the [debug] attribute is compiled. This allows logging statements and debugging code to be quickly turned on and off by changing the option value.

Comments and whitespace

Most constructs in device descriptions are terminated by a semicolon or delimited by braces and are insensitive to whitespace and layout. A statement or definition can be freely split across as many lines as necessary. Indentation may also be used without affecting the meaning of the definitions or code. Either hard tabs or soft tabs (spaces) can be used.

Comments start with two forward slashes (//). Everything from that point to the end of the line is ignored.

Segments

Custom devices may have one or more segments, which are uncommitted buffers of data. Each segment has a name for referencing, a fixed size, and an optional initialization pattern:

  Segment rom: {
      size: 8192,
      source: "cart.bin"
  };

  Segment ram: {
      size: 8192,
      init_pattern: 255
  };

  Segment nvram: {
    size: 256,
    init_pattern: [0, 255],
    persistence: "nonvolatile"
  };

Segments have multiple uses, including backing memory for memory layers, loading predefined blobs from files, and writable temporary storage. Segments may be any size, including zero bytes, subject to implementation limits. Currently, Altirra limits the total size of all segments to 256MB.

The member key is the segment name used to reference it from other parts. It is case sensitive and must conform to the naming conventions of the scripting engine.

Initialization data

The segment may be initialized with either source data or an initialization pattern. For source data, the source is a filename in the same location as the .atconfig file. Only a filename is allowed, no relative or absolute paths allowed. Altirra does support ATVFS paths for this, which allows the source to be zipped or gzipped (gz://file.bin or zip://file.zip/subfile.bin).

For initialization data, the init data is a blob as specified earlier. The init data may be from 1-N bytes where N is the size of the segment. If the init data is shorter than the segment, it is repeated as needed to fill the segment.

If no source or initialization data is given, the segment memory is zeroed.

Persistence

By default, segments that are not backed by data are volatile and will be automatically reinitialized on cold reset. This can be overridden with the "persistence" property:

volatile (default)
The segment is reset to its initialization state on cold reset. This has no effect on segments that are backed by file.
nonvolatile
Auto-reinitialization on cold reset is disabled. This is useful for segments that are preinitialized by an init script.

Memory layers

Memory layers allow the custom device to interact with the emulated CPU using memory mapped I/O. A memory layer is a contiguous memory address range, enabled for some or all types of accesses. More precisely:

  • The address range of a memory layer is contiguous and page aligned, within the 64K address space.
  • A memory layer may be enabled or disabled independently for both reads and writes. ANTIC and CPU reads are handled identically.
  • Memory layers have a defined priority relative to other memory layers. A higher priority memory layer can intercept accesses and prevent them from being seen by lower layers. Currently, this is fixed at cartridge priority (see the output of the .map debugger command).
  • Memory layers can either be directly mapped to a segment or bound to a set of address bindings. Direct segment mapping is faster and preferred when possible, but requires that the memory layer be a solid mapping.
  MemoryLayer wina: {
    name: <Debugger readable name>,
    address: <Starting address of memory layer, page-aligned>,
    size: <Length of memory layer, in bytes>,

    // for layers with a direct segment mapping
    segment: { ... },

    // for dynamically controlled memory layers
    control: { ... },

    // for cartridge-enabled layers (optional)
    cart_mode: ...
  };

A memory layer must either be "segment" or "control" type, but not both.

Direct mapped layers

A direct mapped layer is a layer that is directly mapped to segment memory. Layers with a direct segment mapping have the following members:

  segment: {
    source: <segment-variable>,
    offset: <byte offset from start of segment>,
    mode: "r|ro|w|rw|wt"
  }

source (required): The name of the segment to use. Note that this is a direct variable reference and not a string, so it isn't quoted.

offset (required): The byte offset from the start of the segment to map. The mapped range must be page-aligned and be contained entirely within the source segment. It is allowed to map part of a segment but the memory layer may not extend beyond the end of the segment.

mode (required): The memory access mode for the layer. "r" and "w" mean reads and writes, respectively. A "r" mode mapping will handle reads but allow writes to pass through to lower priority layers; an "rw" mapping will handle both reads and writes. An "ro" mode layer is read-only and will block writes to lower layers without modifying the mapped segment. "wt" selects write-through mode, where writes are stored in the segment without blocking the writes from lower priority layers.

Mapping overlapping segment ranges in more than one memory layer simultaneously is supported. In this case, reads to the layers return the same data and writes to any of the layers are reflected in the other layers. This is used to implement address mirroring or dual banking windows.

A direct mapping does NOT require that the memory layer always map to a fixed location in the segment. The memory layer cannot be changed to binding-based mappings on the fly, but it can be revectored to different offsets or segments under script control.

Binding-controlled layers

A layer with a "control" member is a binding-controlled layer. Each binding determines the response for a unique address range and access type in the memory layer. This allows behavior specification down to byte granularity.

The value of the control member is an array of objects:

  [
    {
      address: <starting address>,
      size: <optional size in bytes>,
      mode: "r|w|rw",

      // one of "data", "action", or "script" required
      data: <data-blob>,
      action: "block|network",
      script: "...",
      debug_script: "..."
    }
  ]

address (required): The absolute starting address of the binding. This must be within the address range of the layer.

size (optional): The size of the binding in bytes. Must be non-zero and the entire binding must fit within the layer. If omitted, the binding is one byte in size.

mode (required): Whether the binding applies only to read accesses ("r"), write accesses ("w"), or both read and write accesses ("rw"). Both the layer and binding modes apply and the effective mode is the intersection of the two, i.e. an "rw" binding will not respond to writes in an "r" or "ro" mode layer.

All bindings must have a unique address range and mode. They can overlap in addresses for different modes, i.e. $4000/r and $4000/w. It is OK for some addresses to not be covered by a binding, in which case they are pass-through to lower layers.

The response of a binding to a matching access is specified by one of the following members:

"data"
Return the given constant blob data. The blob data must either one byte to repeat for the entire address range of the binding, or a blob the same size as the binding. Data responses are only valid for read bindings.
"action"
Specifies a special action:
"block"
Prevent lower layers from seeing the access. This is only valid for write bindings.
"network"
Send the access to the remote server.
"script"
Specifies a script to run on the read or write access. For write bindings, no return value is needed. For read or read/write bindings, the byte result of the read access must be returned. The result does not need to be restricted to 0-255; for convenience, the lowest 8 bits are used and any higher bits are ignored.
"debug_script"
For read or read/write bindings using the script method, a debug script can also be supplied to handle debug reads differently by suppressing side effects. This avoids altering machine state when the debugger inspects memory. For instance, an address that would normally trigger a bank switch on a read should have a debug read override to prevent the bank switch.
If debug_script is not specified, the same script is used for both debug and non-debug reads. The [debug_read] and [!debug_read] attributes can be used to conditionally compile statements for only one of the modes. See Conditional Compilation in the scripting section for details.
"variable"
Read or write the named variable: "variable": "r0" causes the variable r0 to be read or written on accesses to the given address.
If the value of the variable exceeds the range of a byte (0-255), the lowest 8 bits are taken on a read access.
"copy_from"
"copy_from": Copies bindings from another address in the same layer. The value is the starting address to copy from. For instance, given a layer at $D500-D5FF, "address": "$D580", "mode": "r", "copy_from": "$D500" will copy bindings from $D500-D57F to $D580-D5FF.
The source bindings must be declared before the copy_from entry. The copy is always ascending so that a pattern can be repeated:
{ address: $D500, mode: "rw", variable: "r0" },
{ address: $D501, mode: "rw", variable: "r1" },
{ address: $D502, mode: "rw", variable: "r2" },
{ address: $D503, mode: "r", variable: "status" },
{ address: $D504, size: 252, mode: "rw", copy_from: $D500 }
This copies $D500-D503 to $D504-D5FF, which causes the first four entries to be repeated.

Cartridge-enabled layers

The cart_mode member specifies that a memory layer is intended for a cartridge and should be connected to the emulator's cartridge logic:

  • Memory layers assigned to the left cartridge slot automatically assert the RD5 signal when active. This affects the TRIG3 input on XL/XE computers that indicate the presence of a cartridge, as well as cartridge sensing for pass-through cartridge ports.
  • Pass-through cartridge ports can disable cartridge-enabled memory layers. This happens independently of the mapping mode of the layer.

Both direct-mapped and binding-controlled layers can be cartridge-enabled.

There are three cartridge enable modes available:

cart_mode: "left"
Hook the layer into the S5-to-RD5 signal chain for the $A000-BFFF address range (left cartridge region).
cart_mode: "right"
Hook the layer into the S4-to-RD4 signal chain for the $8000-9FFF address range (right cartridge region).
cart_mode: "cctl"
Hook the layer into the CCTL signal for the $D500-D5FF cartridge control region.
cart_mode: "auto"
Select left, right, or cctl modes based on the address range of the memory layer.

Cartridge-enabled layers are not restricted to cartridge ranges and can map ranges outside of the normal cartridge address spaces ($8000-BFFF and $D500-D5FF).

PBI-enabled layers

A memory layer marked as "auto_pbi": true is automatically enabled only when the PBI device is selected. It can only be used when pbi_device is declared in the device specification.

Hardware address ranges

The hardware address range of $D000-D7FF contains many address ranges dedicated to motherboard resources. This includes GTIA in page $D0, POKEY in page $D2, and ANTIC in page $D4. Currently, these cannot be overlaid by custom device memory layers. Pages $D1 and $D5-D7 can be overlaid, with the except of the single address $D1FF being intercepted if PBI devices are present.

The presence of memory layers in the $D1 and $D5-D7 pages will also affect the emulator's ability to allocate those pages for high-level emulation hooks, particularly CIO devices like the H: device. The custom device code will automatically advertise any memory layers overlapping these regions so that the hook page can relocated to avoid them. This happens whether or not the layers are enabled, as the layer modes can be changed on the fly.

Serial I/O (SIO) devices

Custom devices can also connect to the serial I/O bus to emulate SIO peripherals. This is done in cooperation with the central SIO management code, allowing custom peripherals to benefit from common logging and acceleration code that is also used by the internal device emulators.

Declaring SIO devices

SIO devices are declared as objects of type SIODevice:

  SIODevice disk_device: {
      device_id: $31,
      commands: ...
  };

  SIODevice printer_device: {
      device_id: $40,
      device_count: 4,
      commands: ...
  };

device_id indicates the device ID as seen on the SIO bus. device_count, if present, specifies that more than one device ID should be handled, starting at the given device ID. This is used to claim a range of device IDs without having to repeat the command definitions.

The custom device will only respond to commands for the given device IDs. Commands for other device IDs are ignored and not seen by the custom device. It is assumed that all of the device IDs are being implemented by a single physical device that can only handle one command at a time, so it is not possible for the custom device to handle simultaneous commands to different devices or the same device.

Declaring SIO commands

The commands member of a device declaration contains the list of commands supported by that device. It is an array of objects, one for each command:

    commands: [
      { id: $48 },
      {
        id: $53,
        auto_transfer: { mode: "read", segment: "status", offset: 0, length: 4 }
      },
      {
          id: $50,
          script: function {
              $sio.ack();
              $sio.recv_frame(128);
              disk.copy(($aux - 1)*128, $sio_frame, 0, 128);
              $sio.complete();
          }
      },
      { id: $57, copy_from: $50 }
    ]

The id member is required and specifies the command ID for the command, as specified by the second byte in the SIO command frame. All commands within a device must have unique IDs. This can also be the special name default, which means all commands not already implemented.

A command with only id simply returns ACK and then Complete, implementing a no-op command that completes successfully without doing anything.

A command with auto_transfer implements a command that automatically issues a read transfer from a segment or a write transfer to a segment. The mode member specifies the transfer direction, from the standpoint of the computer. A read command allows the computer to read from a device segment, while write has the computer writing to a device segment. segment specifies the segment name, while offset and length specify the starting offset in the segment and the transfer length. The transfer range must fit within the segment.

script is the most flexible option as it runs a script to drive the SIO transaction. The script has access to the SIO command frame and can send command status bytes as well as sending or receiving data frames. It can also inject delays.

Finally, a copy_from command copies a command definition to another command ID, reusing it. The value is the command ID to copy from, which must have been declared earlier. One use for this is to reuse the same commmand definition for the Put and Write commands of a disk drive.

SIO protocol handling

The command frame is automatically validated prior to dispatching to the command handlers. A command frame target at a device ID not declared in the custom device is ignored. Command frames with a matching device but a non-matching command are NAKed. Command frames with invalid checksums are ignored.

For non-script commands, the entire protocol is handled by the host. For scripted commands, the script begins execution after the command line is deasserted and is responsible for everything including sending the initial ACK/NAK for the command. This is done through the $sio object, which has methods for all remaining bus transactions needed.

The high-level flow that scripts need to implement for standard SIO transactions is as follows:

  • Check if the command is valid. If it is not, send a NAK ($sio.nak()) and end command processing. An example of a condition that should cause a command NAK is a Read Sector command to a disk drive with an invalid sector number. Checks done here can generally only depend upon immediate device and command state since the ACK/NAK must be sent in 16ms; a disk drive could not read or write sectors before sending the command ACK/NAK, for instance.
  • ACK the command ($sio.ack()).
  • If the command is a write command, where the computer writes data to the device:
    • Receive the data frame from the computer ($sio.recv_frame()).
    • [The host will automatically NAK the data frame and end the script here if a checksum error occurs.]
    • ACK the data frame ($sio.ack()).
    • Process the data frame.
  • Send Complete ($sio.complete()) or Error ($sio.error()) depending on whether the operation completes successfully. Note that command processing does not stop here on Error, since a data frame still needs to be sent even if it has no useful data in it.
  • If the command is a read command, where the computer reads data from the device:
    • Send the data frame to the computer ($sio.send_frame()).

The current command is automatically aborted by the host if the SIO command line is asserted before the command completes or a reset event occurs.

Protocol timing

The SIO protocol has minimum and maximum timing requirements between phases of bus transactions. These are mainly related to periods where either the computer or the peripheral need to reconfigure its serial interface between sending and receiving data. Violating minimum bounds can cause lost or corrupted data bytes, while violating maximum bounds results in timeouts.

When implementing a custom device, all timings are in emulation time, where time only passes when the script deliberately executes a delay or sends or receives bytes. All other actions, such as executing a script or performing a network transaction take zero emulation time since the 6502 does not run. Making a network call that takes too long will slow down the emulator but not change the timing relationship between the 6502, the chipset, and the device. Thus, in general, there are no problems with the custom device being too slow to respond.

Where problems can occur is with the custom device being too fast in emulation time, violating minimum bounds because it can do all processing in zero time. To prevent this, the custom device host automatically inserts delays into specific parts of the transaction:

  • The command handler starts running after the command line is deasserted, so the script can send the command frame ACK/NAK immediately.
  • A delay of 850 µs is inserted before the ACK/NAK of a received data frame.
  • A delay of 250 µs is inserted before Error/Complete bytes.

Therefore, no explicit delays are needed in a script to meet minimum timing requirements. A script may freely call ack(), complete(), and send_frame() back-to-back and the custom device engine will ensure that the minimum protocol delays are observed.

Using sent/received data

Typically a custom SIO device will use additional segments to handle data transfers, such as a disk image segment or a status frame segment. These segments should be marked with nonvolatile persistence so that they are not reset when the peripheral is power-cycled. This is only needed if they are not file-sourced, however, as those are implicitly nonvolatile.

Canned replies, such as Status requests from some devices, can put the predefined reply into a segment with an init_pattern and use the auto_transfer mode, allowing the host to drive the entire SIO protocol. Otherwise, the Segment.copy() method is useful in SIO command scripts, particularly in write commands to copy from the $sioframe receive segment to a work segment. This is less needed for read commands, which can send_frame() directly from segments.

Concurrency with other scripts

Because SIO command scripts can be suspended by a send/receive operation or an explicit delay, they can run in parallel with other scripts in the custom device, most notably memory access handlers. Only one script can execute at a time, so the scripts can only switch off at suspension points -- there is no risk of data races as in a multi-core CPU. However, global state variables may change if shared between the scripts.

Some special variables are instanced per thread and not shared. In particular, the $timestamp variable is thread-local. In an SIO script, it always gives the timestamp of the start of script execution regardless of suspend points within the script. Any other scripts that execute during the command receive their own $timestamp values.

Accelerated transfers

Two types of acceleration can be implemented by the host, burst I/O transfers and HLE accelerated transfers. Both are handled automatically and do not require handling by script. Acceleration support is controlled by device ID, so enabling D: acceleration will affect both internal and custom devices that use $31-3F device IDs.

In a burst I/O transfer, handshaking is implemented between POKEY and the device so that the time between serial input ready interrupts is shortened, depending on how fast the 6502 can acknowledge it. This results in the effective transfer rate rising above 19,200 baud adaptively to as fast as the serial IRQ handler can handle. For a custom device, this results in recv_frame() operations occurring more quickly than normal.

HLE acceleration uses CPU interception instead and transfers data directly between memory and the device. When this occurs, SIO operations are short-circuited and occur in zero time. All delays are skipped, ACK/NAK/Complete/Error bytes are handled directly, and serial transfers occur immediately via DMA. The HLE code does some checks for whether accelerating the transfer is safe and will skip doing so in some cases, such as if it cannot verify that the OS SIO routines are being used or the transfer overlaps OS variables that would be used in the transfer. It also assumes that the device implementation conforms to the standard SIO protocol and that at most one read or write data frame transfer takes place, which is compared against the DCB parameters to determine the resulting status and buffer contents.

HLE acceleration can be disabled on either a device or command basis by specifying "allow_accel": false in the device or command object.

Raw SIO transfers

For the lowest level of SIO device emulation, a custom device can bypass the SIO manager and use raw SIO to directly send and receive bytes on the SIO bus. This allows full control over transfer format and timing and permits emulation of SIO devices that do not use standard SIO command frames, such as serial port devices.

To use raw SIO, one or more script threads must be set up to handle transfers on the SIO bus and raw SIO must then be enabled. This can be done either autonomously or triggered from an SIO command, depending on the requirements of the custom device. However, in the latter case, the SIO command script itself cannot drive the raw SIO transaction -- it must hand off to a worker thread to do so:

  Thread rawsio_thread;

  void handle_raw_transfer() {
    $sio.enable_raw(true);

    $sio.send_raw_byte($00, 94);
    Thread.sleep(100);
    $sio.send_raw_byte($01, 94);
    $sio.send_raw_byte($02, 94);
    int data = $sio.recv_raw_byte();

    $sio.enable_raw(false);
  }

  void handle_command() {
    $sio.ack();
    $sio.complete();
    rawsio_thread.run(handle_raw_transfer);
  }

A single thread can be used for both sending and receiving or two threads can handle the two directions independently, depending on whether the emulated device uses half-duplex or full-duplex communication. A disk drive can use one thread since its controller typically can only handle half-duplex, whereas a serial device would use two threads for full-duplex operation.

The SIO class provides some assistance for common SIO emulation patterns. Wait functions are provided to efficiently wait for transitions on the SIO command line, to synchronize phases of the SIO command protocol or allow a monitoring thread to efficiently interrupt serial transfer. Checksums are also automatically computed for bytes sent and received by script, avoiding the need to manually re-compute the checksums for verification.

When raw SIO is active, all standard SIO command processing for the custom device is suspended. If standard SIO command processing is desired, raw SIO must be cancelled prior to the trailing (deasserting) edge of the SIO command line for the custom device's SIO command tables to be re-enabled. However, all other devices in the system will still monitor for standard SIO commands regardless of the custom device's raw SIO state, as occurs in an actual SIO device chain on real hardware.

Conflicts with built-in devices

A custom device may not work correctly if it attempts to implement an SIO device that is also being handled by an active internal device in the emulator. This is the emulation equivalent of a bus fight. In these cases, the internal device must be disabled for the custom device to work.

In Altirra, the most likely conflict of this type is with the disk emulator. To turn off the built-in emulation, the disk drive must be changed to Off in Disk Drives. This can be done either through the drop-down or by clicking the Eject button twice.

Parallel Bus Interface (PBI)

A pbi_device section enables Parallel Bus Interface (PBI) support. It has two member, the required device_id for the PBI device, and an optional IRQ enable:

  PBIDevice pbi_device: {
      device_id: $40,   // required
      has_irq: true     // optional
  };

Exactly one bit must be set in the device ID, with $01..$80 corresponding to PBI devices 0-7. Only one PBIDevice object can be defined. For the purposes of the custom device, the XE Enhanced Cartridge Interface (ECI) is considered as equivalent to the PBI.

When enabled, Altirra's built-in PBI disk acceleration hardware uses PBI device 7 (ID $80). The Atari OS scans enabled PBI devices from device 0 to 7 ($01 to $80), so ID $01 has the highest priority. SpartaDOS X, however, scans PBI devices in reverse order and thus priority.

Selection support

When PBI device support is enabled, the host automatically monitors writes to the $D1FF PBI device selection address and will enable the custom PBI device when the specified device ID bit is set. This avoids the need to manually map memory layer over the $D1xx page with a write mapping over that address and allows it to be multiplexed between multiple PBI devices on different IDs. The pbi_select and pbi_deselect script events are dispatched when the PBI device is selected or deselected, allowing script to respond to device selection. The host will also automatically deselect any active PBI device when a computer reset occurs.

Auto-PBI memory layers

Memory layers set to "auto_pbi": true are enabled for PBI support: their priority in the MMU is raised so they can properly overlay motherboard memory resources and the math pack ROM, and they are automatically enabled and disabled when the device is selected or deselected. Thus, simple PBI devices can switch in both a firmware ROM over the math pack at $D800-DFFF and control registers without requiring explicit script actions.

Additionally, any auto-PBI layer mapped to within $D800-DFFF also automatically disables emulation hooks in the math pack ROM when it is active so that they do not interfere with PBI firmware.

PBI interrupts

Setting has_irq: true enables IRQ support for the PBI device. The assert_irq() and negate_irq() methods are used to request a device IRQ.

As with other IRQs, the PBI IRQ is a level-based maskable interrupt and must be held asserted until the CPU has a chance to service the interrupt. This is done by calling assert_irq() on the PBIDevice object. Once called, this will continually request a maskable interrupt on the CPU until negate_irq() is called. This is necessary since the CPU may have IRQs masked or may be servicing higher priority IRQs from other devices, and the IRQ must continue to be requested until either the CPU gets to the PBI device.

Once the device IRQ handler runs, it then acknowledges the IRQ by a device-specific method so an IRQ is no longer requested by the device, which in this case means a call to negate_irq(). As there is no generic way to control or acknowledge device IRQs, the device IRQ must be disabled on reset until the handler has initialized and set the device's ID bit in the operating system's PDMSK variable to enable the device's IRQ handler. If the IRQ is asserted too early before interrupt handling is in place, the system will typically hang as the CPU is forced to re-run the system IRQ dispatcher without having a way to acknowledge the device interrupt. To assist in complying with this, the PBI device IRQ is automatically negated whenever a cold or warm reset occurs.

As required by the PBI specification, the device's IRQ status bit is also automatically exposed on reads from $D1FF when the IRQ is enabled so the operating system can identify the source of the PBI interrupt. Only the device's ID bit is driven on the bus, so other bits in $D1FF will reflect open bus data if not driven by another PBI device in the system.

Putting it all together, the following is an example of a working PBI interrupt implementation:

  • $D1F0 [W] enables the device interrupt.
  • $D1F1 [W] acknowledges the device interrupt.
  • The PBI firmware's initialization handler sets the device bit in PDMSK to enable OS interrupt handling for the device, and then writes $D1F0 to enable the device interrupt.

Controller ports

Controller ports are bound by creating objects of type ControllerPort:

  ControllerPort port1: 0;
  ControllerPort port2: 1;

Each member is the variable name to bind the controller port and the index (0-3). The variable then points to an object of type ControllerPort, which can be manipulated through script.

On XL/XE machines, ports 3 and 4 can be bound but are inactive. Their state is preserved and will become active if the machine type is switched to 400/800.

Video outputs

Devices with auxiliary video outputs, like 80-column video boards, can be emulated by declaring a VideoOutput object. This allows the custom device to add an entry to the video output list, which when selected displays an image maintained by the custom device.

Creating the video output

Defining a VideoOutput object automatically creates it and adds it to the list of available video outputs. The only required property is display_name, which sets the name of the video output in the UI.

  VideoOutput video_output: {
      display_name: "XEP80"
  };

This is sufficient to enable the video output, but it is not very useful as it will simply show "no signal". For the video output to actually show something it needs to have an Image bound to it to provide the image data for the video output:

  Image frame_buffer: {
      width: 320,
      height: 192
  };

  event "init": function {
      video_output.set_image(frame_buffer);
      frame_buffer.clear(0);
  };

The video output will then begin displaying the contents of the bound image each frame. Images are full 24-bit RGB and can be as colorful as the host can display.

The image object contains several methods for drawing on the image, and any changes made to the image are shown when the next refresh occurs, which is either at the end of the frame or whenever the displays are redrawn. Multiple framebuffer images can also be used and the video output rebound to different images on the fly; this can be useful to emulate page flipping hardware.

Drawing the image

While image does provide a put_pixel() method, using it to draw the whole image every frame would be absurdly slow and painful. Image provides several methods to draw on the image, including clear, rectangle fill, and rectangle copies (blits).

One particular blit that is of interest is a tile map blit, provided by blt_tile_map(). This is a powerful blit type that uses a tile map to select regular sized tile images from a tile source. This can be used to emulate both graphical tile and character modes and is much faster than doing an individual blit per tile.

Multiple Image objects can be declared, and off-screen images are useful as blit sources. An image can be pre-initialized from the contents of a PNG file. The blt_expand_1() call is also useful for expanding monochrome bitmap data from ROM data into an image object.

The image bound to a framebuffer does not need to be redrawn every frame if its contents don't need to change. Left alone, an Image object will retain its image data and display the same on the next frame. Thus, it can be useful to maintain a dirty flag indicating when a redraw is needed. The composite event on a video object is triggered right before a video output is redrawn and is useful for deferring redraw work until actually needed, avoiding it entirely if the video output is not actually shown or has not changed. A valid strategy is to just maintain the contents of video RAM from the emulation bindings and only convert it to a displayable image on the composite event if a write has occurred to the video hardware or VRAM since the last composite.

Drawing character displays

It is common for text mode video hardware to use bit 7 as an inverse video bit, and some hardware uses non-ASCII character codes. These can be handled efficiently by pre-transforming the character graphics. Inverse video can be supported by copying the character or image data from the first 128 characters to the second 128 characters and then inverting the graphics for the second half. For a different ordering like INTERNAL, permute the characters in the character set to fit the code ordering.

Character-mode video hardware, particularly those driven by a CRTC, often implement hardware scrolling by adjusting the display start address. This can tricky to emulate when it involves wrapping around the VRAM address space since the wrap can occur not only between rows but even within a row. This can be handled by manually unwrapping the character map into a temporary segment, but this involves a bunch of temporary copies.

An alternate way to handle this is to pre-unwrap video memory by maintaining two copies of it, back-to-back. This can be done simply by adjusting the size of the VRAM segment to twice the size of VRAM and writing each byte written to both halves of the segment. Wrapping is then supported during blits by seamlessly reading from the first copy to the second copy of VRAM within the segment, producing the same data as wrapping within a single copy.

Fine-tuning image display

The stretching filter used to display a video output is unspecified and under user control; it may be nearest neighbor (point sampling), bilinear interpolation, or a more specialized filter. It's a good idea to use an image resolution that matches any pixel replication performed by the hardware. For instance, hardware that displays a 160x100 image by doubling each scanline to produce a 200 line high image is best displayed as 160x200 or 320x200 to avoid overly blurring the display.

It is common for video hardware of the time to display images with non-square pixels, and replicting that is important to avoid misrepresenting the hardware and distorting the output. Almost no hardware of the time actually displayed completely square pixels, although some PAL video outputs were close (within 4%). The set_par() method on the video output allows the pixel aspect ratio (PAR) to be specified so the video output can properly scale the image output. This aspect ratio compensation is accelerated with graphics hardware at native display resolution whenever possible and is much faster than can be done within an Image.

Pass-through

Some video hardware supports pass-through in that they also have a video input and can switch between their own video signal or passing through the input signal. This allows software-driven switching between two video outputs through one connector, without requiring manual plugging and unplugging. This can be emulated in a custom device with set_pass_through(), which allows the standard ANTIC+GTIA display to appear for the custom display. It should only used for hardware that actually had that arrangement, however, as otherwise forcing pass-through could preclude the sometimes useful and real scenario of dual monitors on dual video outputs.

Activity detection

For better usability with a single host display, video outputs have provision for activity detection. This allows the emulator to start with the video output for boot and then automatically switch to an alternate video output when it is initialized and starts to be used. Activity-based switching is optional under user control.

The mechanism for activity detection is simple: mark_active() is called on the video output whenever activity is detected, usually a non-trivial write to the video hardware. It only needs to be called whenever a switch might need to occur, with once per frame in composite being a good time.

mark_active() is designed to be fast in case it is called many times in a row, but it's still a good idea to be careful about insertion points and use dirty flags to minimize calls. For instance, with a video port that has both address and data registers and requires a write to both ports to change a byte in VRAM, it is sufficient to instrument only the data write port and not the address port.

Supporting text copy

For video outputs that display text, a custom device can also support copying text from the video output to the clipboard. This is done by supplying a mapping from the video output image to a character grid and an ATASCII text backing store for the grid. This then allows drag selection over the video output image and copy-to-clipboard operations.

The mapping from positions in the video output image to character cells is supplied with set_text_area(), which sets the size of the character grid in columns and rows. This is used only to map drag positions to character cell positions and to draw a selection highlight; it does not automatically render characters into the video output. The text area mapping can be changed on the fly, including setting it to 0x0 for graphical modes that have no text.

The actual text to copy is set by set_copy_text_source(), which maps character cells in the text area to ATASCII bytes in a segment buffer. For some video hardware, this can map directly to the raw VRAM which already holds ASCII or ATASCII character codes. Otherwise, the pre_copy event allows script to generate an ATASCII representation of the framebuffer on the fly only when it is needed.

Scripting

Custom device behaviors are driven through scripts. These are small fragments of code that can maintain state in the custom device and alter its behavior, such as by altering memory layers. The scripts are written in a C-like language and compiled to bytecode. Thus, they are a bit slower than native code, but much faster than using the network server.

Example:

    if (bank_a != $value) {
        bank_a = $value;

        window_a.set_offset(bank_a << 14);
    }

Global variables

As noted earlier, global variables are declared at top-level scope with type followed by variable name:

  int r0;

Variables of type int are read/write and can be changed by script. They always default to zero when the device is created, but are not automatically cleared afterward; script must explicitly reset their values if desired on a cold reset.

Object variables, such as ones of type SIODevice or Thread, are read-only and are permanently bound to a unique object instance on creation. They cannot be reassigned to point to a different object instance, and are never null.

Special variables

Variables prefixed with $ are special read-only variables provided by the host, containing special values of use to the script related to the emulated computer or the request that the script is being run for.

$address
Contains the access memory address or memory read or write scripts.
$value
Contains the byte value being written for memory write scripts.
$timestamp
Contains the current machine clock cycle, counting up at 1.77/1.79MHz. The absolute value of this counter has no meaning, only differences between values. This measures emulation time, which measures clock cycles run by the CPU or chipset logic; it diverges from real-time whenever the emulator is paused or running in turbo mode.
Emulation time is monotonic and counts forward even if a save state is loaded or a reset occurs. However, because the timestamp variable is 32-bit, it is negative half the time and wraps every 40 minutes of emulated time. Timestamp calculations must take wrapping into account. For instance, $timestamp > last_time is unsafe and should be replaced with $timestamp - last_time > 0. The emulator deliberately starts the timestamp counter at a value other than 0 to expose timestamp handling issues.
$device
For an SIO command handler, contains the device ID of the SIO command (first byte).
$command
For an SIO command handler, contains the command ID of the SIO command (second byte).
$aux1
For an SIO command handler, contains the first argument byte of the SIO command (third byte).
$aux2
For an SIO command handler, contains the second argument byte of the SIO command (fourth byte).
$aux
For an SIO command handler, contains the argument bytes of the SIO command combined into a single 16-bit value. This is equivalent to $aux1 + 256*$aux2.

Local variables

A variable declaration inside of a function or script is a local variable, which exists only during execution of that function or script. It is declared the same way as a variable, with the type followed by one or more variable names:

  int local_var1, local_var2;

Local variables must be defined before they are used, and are only valid within the scope in which they are defined:

  {
    int x;

    x = 1;

    {
      int y;
      y = 2;
    }

    y = 3;    // error, y not defined in this scope
  }

To ensure consistent behavior, local variables are always initialized to zero at the beginning of the function. This ensures that random data is never used. However, it does not guarantee that the variable is zero when declared within a loop, so relying on the initial value of a variable is not recommended.

Each invocation of a script function has a unique set of local variables. This is true either in the case of recursion -- a function calling itself either directly or indirectly -- or through execution on multiple threads, like a common function used from both an SIO command handler and a memory access handler.

Expressions

Expressions are similar to the C language, consisting of the following operators, from highest to lowest precedence:

   Postfix          .               (dereference)
   Unary            +, -, ~, !      (unary plus/minus, bitwise/logical inversion)
   Multiplicative   *, /, %         (multiply, divide, remainder)
   Additive         +, -            (add, subtract)
   Shift            <<, >>          (arithmetic left/right shift)
   Bitwise AND      &               (bitwise AND)
   Bitwise XOR      ^               (bitwise XOR)
   Bitwise OR       |               (bitwise OR)
   Relational       <, <=, >, >=    (less [equal] and greater [equal])
   Equality         ==, !=          (equal, not equal)
   Logical AND      &&              (short-circuited AND)
   Logical OR       ||              (short-circuited OR)
   Assignment       =               (assignment to variable)

All operators work with 32-bit signed two's complement integer arithmetic. As in C, operations that take boolean inputs treat non-zero values as 'true' and those that return boolean results give 0 for false and 1 for true.

The logical AND (&&) and logical OR (||) operators are short-circuiting and will evaluate their right-hand operand only if the result is not already determined by the left-hand operand. This has functional consequences for a function or method call on the right side.

There are some differences from C:

  • Hexadecimal numbers are specified with $ instead of 0x prefix.
  • There is no octal.
  • Signed overflow is defined as wrapping in 32-bit.
  • Division/remainder by 0 gives 0, and -$80000000/-1 = -$80000000. No exception is raised.
  • The shift operators mask their shift counts by 31.
  • Assignment does not return a value (result is void type).
  • There are no increment/decrement or assignment arithmetic operators.
  • There is no floating point or unsigned math.

The keywords 'true' and 'false' are also provided for readability in boolean logic. They evaluate to the integers 1 and 0, respectively.

Statements

A function body consists of a sequence of statements, which can be one of:

  • A local variable declaration.
  • An expression, which may include an assignment or a method/function call.
  • An if statement.
  • A return statement.
  • A loop statement.
  • A block of statements, contained in braces { ... }.

In all cases except for a block, the statement ends in a semicolon. Statements may be freely split across lines and indentation is ignored by the compiler.

Note that a block does not end in a semicolon, and adding one will result in a syntax error (doing this results in two statements in C, typically with unfortunate consequences).

Returning values

Calls to script to request a value, mainly memory read accesses, require the script to return a value. This is done with the return statement:

  return x + 1;
  return $FF;

Execution of the function or script ends when the return statement is executed and all code after it is skipped.

Some scripts are required to return a value, such as those called for a memory read access. This is enforced by the compiler, which checks that all branch paths through the code end in a return. If one does not, such as a return only inside of an if(), the script compiler will throw an error.

Scripts that don't need to return a value, such as memory write handlers, have "void" return type and don't need a return statement. A return can still be used with no argument, however, to end the script early:

return;

Branching

The 'if' statement is used to conditionally execute some statements:

  if ($timestamp & 1)
    counter = counter + 1;

The expression after the if keyword is the condition, and the statement after the condition is the controlled statement, executed only if the condition is non-zero. The controlled statement may be a block, which is required if more than one statement is to be controlled:

  if ($timestamp & 1) {
    counter = counter + 1;
    counter2 = counter2 + 3;
  }

An 'else' clause may also be used, which is executed only if the condition is false:

  if ($timestamp & 1)
    counter = counter + 1;
  else
    counter = counter - 1;

Looping

The loop statement creates an infinite loop, which runs its controlled statement over and over:

  loop {
      Thread.sleep(100);
      counter = counter + 1;
  }

This is most often used in threads to run a background process. Generally, a loop must have a return statement or a yielding method call; otherwise, the loop would run infinitely without ending or pausing. The virtual machine guards against this and will force a script to terminate if it takes too long and would hang the emulator.

A while loop executes its controlled statement as long as the while condition is true:

  while (i < 128) {
      seg.write_byte(i, i);
      i = i + 1;
  }

The loop condition is tested before the loop begins and each time the loop restarts. It is not tested while the loop body executes. If the loop condition is false at the beginning of the loop, the whole while loop is skipped without being executed once.

A do...while loop repeats its controlled statement as long as the while condition is true:

  do {
      seg.write_byte(i, i);
      i = i + 1;
  } while (i < 128);

The loop condition is only checked at the end of each loop, so unlike a while loop, the body of a do-while loop always runs at least once.

Finally, the break statement allows for early exit from the a loop, continuing execution after the loop:

  loop {
      if (seg.read_byte(i) >= 128)
          break;

      seg.write_byte(i, 0);
      i = i + 1;
  }

If a break occurs within nested loops, only the innermost loop is exited. Using a break statement outside of a loop is an error.

Conditional compilation

In some cases, the same script can be reused for two similar but slightly different scenarios. Conditional compilation allows an inline script to be specialized for the different scenarios without requiring duplication of the script or a costly runtime branch. This is used for memory read handlers to allow the same script to handle debug and non-debug reads, suppressing side effects for the debug read case.

A statement is conditionally compiled by prefixing it with an attribute consisting of a conditional name within square brackets, optionally prefixed by ! for inversion:

  [!debug_read] layer.set_modes(0, 0);

  [debug]
  if (condition)
      Debug.log("condition false");
  else
      Debug.log("condition true");

The [debug] attribute is used to guard code that should only be enabled for debugging. Code marked with it is compiled out unless the debug option is enabled:

  option "debug": true;

  [debug]
  Debug.log("condition hit");

The [debug_read] attribute only includes the next statement for a debug read, while the [!debug_read] attribute only includes it for a non-debug read. For a loop statement, this includes the body of the loop, and for an if statement, this includes the controlled statement along with any associated else clause. The conditional statement can also be a block:

  [!debug] {
      wina.set_offset(($address & 7) << 13);
      wina.set_modes(1, 1);
  }

The conditional attribute does not introduce a new scope, so variable declarations can be conditionally compiled with the declared variable visible to later code:

  [!debug] int prev_value = value;

  int v = read_state();

  [!debug]
  if (value != prev_value)
      update();

  return v;

Functions

Functions can be declared to group common logic for reuse in multiple scripts. This must be done from a script include. The syntax is as follows:

  'function' return-type function-name() {
    ...function-body...
  }

For example:

  function void update_bank() {
    bank = bank + 1;
  }

Scripts specified in-line in event handlers in the .atdevice file are actually just anonymous functions where the function return type is implicit and only the function body is given.

Calling functions

A function is called by specifying its name, followed by an empty set of parentheses after it:

  update_bank();

When the function returns a value, it can also be used inside of an expression:

  bank = get_last_bank() + 1;

Functions can call other functions, but if a recursion loop occurs the function call chain will be truncated to prevent a crash.

A function must be declared before it can be called, so calling a function that appears later in the source code won't work. This can be worked around by declaring the function first, which specifies its name and types without the function body, which can be provided later. This is done with the same syntax as a function definition, just ending after the argument parentheses instead of following with the function body:

  Thread thread_a;
  Thread thread_b;

  function void threadfn_b();

  function void threadfn_a() {
      loop {
        thread_b.run(threadfn_b);
        thread_b.join();
      }
  }

  function void threadfn_b() {
      int i = 0;
      while (i < 10) {
          Debug.log_int("i = ", i);
          Thread.sleep(500);
      }
  }

Calling methods

Host-provided objects such as segments and memory layers have methods associated with them that can be called to query or modify the object. These are called methods instead of functions because they are called in the context of an object. The syntax is similar to a function call, except that the name is specified after the object with a dot:

  object-expr '.' method-name '(' [arg [, arg...]] ')'

For instance, memory layers have a set_offset() method to change the segment offset of a direct-mapped layer. It takes one integer argument, the new starting byte offset. Thus, it can be called on a layer object called window_a as follows:

  window_a.set_offset($4000);

Some classes also have static methods, which are called wtihin the context of the class and don't need an object instance. For these, the class name is specified before the dot instead of an object expression:

  Thread.sleep(100);

Threading

Script threads can be created to emulate parallel processes. A thread is simply declared as a variable of class type Thread:

  Thread cmd_thread, monitor_thread;

A script thread is used by calling the run() method on the thread object with the name of a function to run on the thread. This function must have a return type of void with no arguments. Note that no () is used after the name of the function, as the function is meant to be passed to run() instead of being executed immediately.

  cmd_thread.run(cmd_handler);

Threads are cooperatively scheduled with no preemption, meaning that only one thread is running at a time until either it completes execution of the top-level function that was passed to run() or it is suspended in a method call. Between those points, script execution on the currently running thread is atomic: no other script threads run and no emulation time passes. Thus, locking primitives are generally not necessary for shared mutable data between threads.

Script threads are not OS-level threads and are relatively cheap; creating several threads that spend the majority of the time waiting on events is fine. Polling with threads in tight Thread.sleep() loops, however, is very bad for emulator performance and to be avoided, especially since script performance is lower than native code. For instance, a clock cycle counter is much more efficiently updated at read with $timestamp than by using a thread to continuously maintain it.

Scripted events

Certain global events are exposed by the emulator and can have scripts attached to them. These are bound using the event statement:

  event "cold_reset": function {
      bank = 0;
  };

Supported events are:

"init"
Called once when the device is instantiated, and never again. This is used for one-time initialization, like reformatting data from disk.
"cold_reset"
Called on a power-cycle event for the device. This is normally where script does its state initialization. Typically all state should be reset here except for state that is supposed to be non-volatile, like battery backed up memory. Any volatile segments are reinitialized before this script is run.
"warm_reset"
Called on a System Reset of the computer.
"vblank"
Called every frame at the start of vertical blank at line 248. Note that this event is invoked regardless of whether the vertical blank interrupt is enabled.
"sio_command_changed"
Called whenever the state of the SIO command line changes. Note that this is only called when raw SIO is enabled.
"sio_motor_changed"
Called whenever the state of the SIO motor line changes. Note that this is only called when raw SIO is enabled.
"sio_received_byte"
Called whenever a byte is sent on the SIO bus in with raw SIO enabled. This hook has priority over recv_raw_byte() calls; the two should not be used together.
"pbi_select"
Called when the declared PBI device is selected.
"pbi_deselect"
Called when the declared PBI device is deselected.
"network_interrupt"
Called when a network server sends an interrupt request.

Network server

For the most complex behaviors, the custom device can be linked to an external server. This is the slowest method of emulating behavior and can affect emulator performance, but permits the most complex behaviors and connections to external data and peripherals. The server communication is over a local TCP/IP socket and the server may be written in any language.

A networked server is enabled with the "network" option:

  option "network": {
      port: 6502
  };

"port" specifies the TCP/IP port to use for the server connection, which must be from 1024-49151. The emulator attempts to make a connection to this port on localhost. Connecting to servers on external hosts is not currently supported, due to latency and security issues.

When enabled, the emulator connects to the specified server as soon as the custom device is instantiated. The server is required; if the connection fails or drops, the emulator may continue in degraded state but the custom device will not function correctly. Altirra will time out after one second; it will attempt to re-establish dropped connections, but the custom device state may be inconsistent between the emulation and the server until the next cold reset.

Enabling server actions

The server is invoked whenever certain simulator events happen or a memory layer control binding has an action type of "network". In this case, the emulator will send a command to the remote server and wait for it to respond with one or more commands for the emulator to perform to complete the action. This is a synchronous process, so the emulator is halted while waiting for the server responses and the server should respond as quickly as possible. However, except for rare events like system resets, the server is only invoked for events pertinent to the custom device.

The communications protocol is a binary protocol, with packets exchanged in both directions. The details of the protocol are not described here, but deviceserver.py script is Python 3 script which handles this protocol.

It is permitted for the server to improve throughput by overlapping commands, sending a new command before the reply to the previous one has been received. All commands are processed sequentially and the replies sent in the same order. However, the server must be prepared to receive data for earlier commands while in the middle of sending a new command. Otherwise, a deadlock may occur with the emulator unable to receive the new command due to being blocked on sending the response from the last one.

Events sent to the server

Cold reset
The emulated system is powering-on with everything reset except for non-volatile storage (memory and registers reset).
Warm reset
A system reset has occurred through the Reset button on the console; memory is preserved and only some registers are reset. Only devices intended to be internal devices should respond to this, as external devices like SIO peripherals and cartridges do not receive the reset signal.
Read/write access
A memory read or write has occurred to an address binding of network type. The server can issue commands for side effects related to the access, such as changes to memory layers. For a read access, the server must also respond with the byte value resulting from the read access.
Debug read access
A memory read access has occurred from the debugger. In this case, the server must respond with the same value that a read access would have provided, without any associated side effects. In other words, the debug read access must not affect the device state in any way.
Explicit script call
A script uses the $network object to get assistance from the remote server.

Synchronous communication

Automatically reflected events and messages sent from script are handled in synchronous mode: the emulator stops execution and waits for the server to handle the event, processing all requests until the final reply from the server. These requests can include both querying and changing emulation state, such as modifying the contents of memory segments or changing memory layers. Processing is ended when the server sends the final reply -- which for some cases like memory reads also includes a return value -- and then execution resumes. The Request Types section below lists the requests that can be issued to the emulator during this processing.

Script can manually send messages to the server using the $network.send_message() method, with two integer parameters. This allows for conditional or custom server communication.

Asynchronous communication

A downside to synchronous processing is that it lock-steps the emulator while it waits for the round-trip through the network stack and for the server to respond. Since emulation timing requirements are tight, this can slow down the emulator if communication is too frequent. Asynchronous communication can alleviate this, by allowing messages to be posted to the other side without requiring synchronization. For instance, the server can be notified that a byte was sent without waiting for a response. This greatly reduces the timing requirements and allows for pipelining between the emulator and server processes.

For messages from the emulator to the server, the $network.post_message() method is used instead of send_message. This is a fire-and-forget call that does not wait for a response and does not return a value. In the other direction, req_interrupt() from Python invokes the script bound to the network_interrupt event. Both calls are naturally pipelined in the socket buffers, so it is up to script and the network server to implement a common protocol if throttling is required.

The server may send any type of request to the emulator at any time, not just interrupts. The emulator services these at periodic intervals, typically within a frame (16-20ms in emulation time), but neither precise timing nor atomicity is guaranteed; two requests issued in quick succession outside of a sent message handling may execute on different ticks. However, requests are always processed in order.

Restrictions on script interrupts

Script interrupts are executed on a separate script thread and may execute suspendable methods, such as send_raw_byte. However, if another script interrupt arrives during this time, it will abort handling of the previous interrupt to service the new one.

Executing calls from the script interrupt thread that require synchronous messages to the server is possible but discouraged, for a couple of reasons. The first is that it can result in undesirable request reordering:

  • Server sends interrupt A
  • Server sends request R
  • Emulator begins processing interrupt A
  • Emulator sends message to server
  • Server issues request Ar and ends processing
  • Emulator receives and executes request R
  • Emulator receives and executes Ar

In-order request processing and the network stream connection require that request R be received and handled by the emulator before request Ar associated with interrupt A. This is best avoided by performing the necessary actions within the script itself, which allows them to execute before other queued requests from the server.

The second issue is script interrupt recursion. When the script interrupt thread sends a message to the server, it must execute any previously asynchronously queued requests from the server to process the replies to the sent message. If any of those previously queued messages is a script interrupt, those interrupts will be dropped.

Request types

The supported request types:

req_enable_layer
Changes the mode of a memory layer to respond or not respond to reads and writes.
req_set_layer_offset
Changes the starting offset of a memory layer within its source segment. The segment must be direct map type and the new range must be contained within the segment. The contents of the segment are not changed.
req_set_layer_segment_and_offset
Changes both the source segment and the starting offset of a memory layer. The segment must be direct map type and the new range must be contained within the new segment. The contents of the segments are not changed.
req_set_layer_readonly
Changes whether a memory layer is read-only, i.e. blocks but does not respond to writes.
req_read_seg_mem
Reads data from a range in a segment. The range must be entirely contained within the segment.
req_write_seg_mem
Writes data to a range in a segment. The range must be entirely contained within the segment. Any direct-mapped layers mapping this segment will immediately reflect the new contents.
req_copy_seg_mem
Copies data from a range in a source segment to a range in a destination segment. This is much faster than doing segment reads and writes as it avoids sending the data across to the server. Copying between overlapping ranges in the same segment is allowed, in which case the copy direction is chosen to avoid corruption (similar to memmove() in C).
req_interrupt
Requests an interrupt to be delivered to script code, invoking the script handler for the network_interrupt event.

Sample device descriptions

8K cartridge

The following is a device description that implements a simple 8K cartridge designed to be inserted into the left slot of an 800. It loads a predefined image from a file and maps it to a hardcoded, read-only mapping at $A000-BFFF. This mapping is set as cartridge-enabled so that it is detected by the OS on XL/XE systems.

  option "name": "Sample 8K cartridge";

  Segment flash: {
      size: 8192,
      source: "cart.bin"
  };

  MemoryLayer wina: {
      name: "Left cartridge window",
      address: $A000,
      size: $2000,
      segment: {
        source: flash,
        offset: 0,
        mode: "ro"
      },
      cart_mode: "left"
  };

Single-density disk drive

This device implements a single-density disk drive similar to the Atari 810. The ATR disk image is pre-loaded into a segment, and another segment is used to hold a predefined status response. The Status command simply sends the status segment; the Read and Write commands validate the sector number before transferring to or from the disk segment. The Write command is a mirror of the Put command.

Note that writes to this device are ephemeral and are not written back to the image file.

  option "name": "Sample disk drive device";

  Segment disk: {
      source: "disk.atr",
      source_offset: 16,
      size: 92160
  };

  Segment status: {
      size: 4,
      init_pattern: [ 0, 0, 224, 0 ]
  };

  SIODevice disk_device: {
      device_id: $31,
      commands: [
        {
          id: $50,
          script: function {
            if ($aux <= 0 || $aux > 720)
                $sio.nak();
            else {
                $sio.ack();
                $sio.recv_frame(128);
                disk.copy(($aux - 1)*128, $sio_frame, 0, 128);
                $sio.complete();
            }
          }
        },
        {
          id: $52,
          script: function {
            if ($aux <= 0 || $aux > 720)
                $sio.nak();
            else {
                $sio.ack();
                $sio.complete();
                $sio.send_frame(disk, ($aux - 1)*128, 128);
            }
          }
        },
        {
          id: $53,
          auto_transfer: {
            mode: "read",
            segment: status,
            offset: 0,
            length: 4
          }
        },
        {
          id: $57, copy_from: $50
        }
      ]
  };

Changes

v0.4
  • Added Image and VideoOutput classes.
v0.3
  • JSON format replaced with common script-based format.
  • Added conditional compilation.
v0.2
  • cctl_mode can now be auto.
  • offset is now optional for SIO auto transfers.
  • controller_ports added.
  • vblank script event added.


Altirra Help: Custom device specification: scripting reference

Custom device specification: scripting reference
Contents
  • 1. Scripting method reference
    • 1.1. Segments
    • 1.2. Memory layers
    • 1.3. Network connection
    • 1.4. SIO connection
    • 1.5. PBI device
    • 1.6. Clock
    • 1.7. Controller ports
    • 1.8. Debug class
    • 1.9. Thread class
    • 1.10. Image class
    • 1.11. VideoOutput class

Scripting method reference

All methods are described in terms of C prototypes for recognizability, even though the scripting language does not use such syntax. Argument types should be omitted and the names do not have to be used. For instance, for a method with the following prototype:

  void copy_from(int offset, Segment *arg2);

It could be called as follows:

  segment1.copy_from(0, segment2);

A static method is one that is called with the class name instead of an object instance:

  Thread.sleep(100);

Segments

A segment is effectively a byte array, with some additional considerations for efficient memory mapping into the emulation and auto-init/reset capabilities. Block memory operations are provided for more efficient manipulation than would be possible in script a byte at a time.

void clear(int value);
Clears the entire segment segment with the given byte value.
void fill(int offset, int value, int size);
Fills a portion of a segment with the given starting byte offset, byte value, and number of bytes to fill. The range to fill must be fully contained within the segment.
void xor_const(int offset, int value, int size);
XORs a constant byte value into all bytes within the specified region, starting at offset and with a length of size bytes. The range must be fully contained within the segment.
A value of $00 is a no-op; a value of $FF inverts all bits within the range.
void reverse_bits(int offset, int size);
Reverse the order of bits in each byte within the specified region, starting at offset and with a length of size bytes. The range must be fully contained within the segment.
A reverse-bits operation swaps bits 0 and 7, bits 1 and 6, bits 2 and 5, and bits 3 and 4. One used for a revbits operation is to display bitmap data that uses LSB-to-MSB order instead of the MSB-to-LSB order used by ANTIC.
void copy(int dest_offset, Segment *src_segment, int src_offset, int size);
Copies a range from a source segment to this segment, given starting byte offsets in both segments and the number of bytes to copy. The source and destination ranges must be fully contained within the respective segments. size=0 is silently ignored.
The source and destination segments can be the same. If the copy ranges overlap, the copy direction is selected to preserve the contents of the copied range at the new location (memmove semantics).
Copying large amounts of data with copy() is slow and should not be used to implement memory banking. Changing the offset of a memory layer is faster.
void copy_rect(int dest_offset, int dest_skip, Segment *src_segment, int src_offset, int src_skip, int width, int height);
Copies a rectangular range of bytes from a source segment to this segment. The destination region is specified as dest_offset, the offset of the top-left byte, and dest_skip, the number of bytes between the end of one row and the start of the next. The source region is similarly specified by src_offset and src_skip.
width and height specify the size of the region to be copied, such that each row is a contiguous range of width bytes with height rows. The region to be copied must be entirely contained in both the source and the destination. A copy rect operation of width or height 0 is allowed as a no-op.
The source and destination segments may also be the same. If the source and destination rects overlap -- that is, they contain one or more common bytes -- then the skip values must be the same. In that case, the copy direction is chosen as needed to preserve the contents of the region, allowing for in-place scrolling. If the regions do not actually overlap, such as one rect tucked into the padding gaps of the other, the skip values may be different.
void translate(int dest_offset, Segment *src_segment, int src_offset, int size, Segment *table_segment, int table_offset);
Copies a region from one segment to another, translating each byte through a translation table. The destination region starts at dest_offset in the called segment and the source region starts at src_offset in src_segment, both extending for size bytes. The translation table starts at table_offset in table_segment and is 256 bytes long, each byte containing the new value for the corresponding original byte value in the source.
All regions must fit completely within their segments. The source and destination regions may be in the same segment, but may not overlap unless the regions are exactly the same (in-place). The translation table must not overlap the destination region.
int read_byte(int offset);
Reads the byte at the given offset within the segment, as value 0-255. The offset must be within the segment.
void write_byte(int offset, int value);
Writes a byte value at the given offset within the segment. The byte value to be written is 0-255, but other values may be given, in which case the lowest 8 bits are used.
int read_word(int offset);
Reads the 16-bit word at the given starting offset, with the LSB at the given offset and the MSB in the next byte. The word is returned as a value 0-65535.
void write_word(int offset, int value);
Writes a 16-bit word to the given starting offset, with the LSB at the given offset and the MSB in the next byte. If the value is outside of 0-65535, the lowest 16 bits are used.
int read_rev_word(int offset);
Reads the 16-bit big endian word at the given starting offset, with the MSB at the given offset and the LSB in the next byte. This is reversed from the usual 6502 byte ordering. The word is returned as a value 0-65535.
void write_rev_word(int offset, int value);
Writes a 16-bit big endian word to the given starting offset, with the MSB at the given offset and the LSB in the next byte. If the value is outside of 0-65535, the lowest 16 bits are used.

Memory layers

void set_offset(int offset);
Sets the starting byte offset of a direct-mapped layer within its mapped segment. For instance, a value of $2000 for a layer size of $1000 will map $2000-3FFF, which requires that the mapped segment be at least $4000 bytes. Attempting to call this for a binding-controlled segment or with an invalid offset is an error.
void set_segment_and_offset(Segment *seg, int offset);
Sets both the mapped segment and offset within that segment used by a direct-mapped memory layer. The offset must satisfy the requirements of set_offset() for the new segment.
void set_modes(int read_enable, int write_enable);
Enables or disables the layer for read or write accesses. For a read-only layer, the write enable controls whether the layer blocks write accesses to lower layers; it will not enable write accesses into the layer itself.
For cartridge-enabled or auto-PBI layers, set_modes() will not enable a layer when the cartridge is disabled or PBI device is deselected. However, the desired mode is still changed and will become active when the cartridge or PBI device is enabled.
void set_read_only(int enabled);
Sets or clears the read-only state for a memory layer. A memory layer that is read-only and mapped for write access will intercept writes from lower memory layers but not change the contents of the backing segment. The read-only state has no effect if the memory layer is not mapped for write.
void set_base_address(int address);
Changse the base address of a memory layer. The memory layer must be direct mapped to a segment. The address must be page-aligned and the new address window must fit within the 64K address space.

Network connection

The network connection is accessed through the $network object.

int send_message(int param1, int param2);
Sends a message to the network server and waits for an integer response, servicing requests from the server during the transaction. The meaning of the two parameters and the return value are user-defined and up to the server implementation.
void post_message(int param1, int param2);
Posts a message to the network server without waiting for a response. The meaning of the two parameters and the return value are user-defined and up to the server implementation.
This is faster than send_message because it does not require the emulator to wait for the server.

SIO connection

The serial I/O (SIO) bus is accessed through the $sio object. The methods on this object are only valid in scripts that are processing SIO requests.

For methods in this class, "send" and "receive" are from the standpoint of the device. A send operation transmits data from the device to the computer, and a receive operation transmits data from the computer to the device.

void ack();
Send an ACK byte ($41) to the computer. This is used to affirm reception of a valid command or data frame.
There is a built-in delay of 850 µs before the A byte when an ACK is sent after a received data frame to ensure that SIO protocol requirements are met and the OS can receive the byte properly.
void nak();
Send an NAK byte ($4E) to the computer. This is used to signal an invalid command or a corrupted data frame.
There is a built-in delay of 850 µs before the N byte when an ACK is sent after a received data frame to ensure that SIO protocol requirements are met and the OS can receive the byte properly.
void error();
Send an Error byte ($45) to the computer. This is used to signal completion of a failed command.
There is a built-in delay of 250 µs before the E byte is sent to ensure that SIO protocol requirements are met and the OS can receive the byte properly.
void complete();
Send a Complete byte ($43) to the computer. This is used to signal completion of a successful command.
There is a built-in delay of 250 µs before the C byte is sent to ensure that SIO protocol requirements are met and the OS can receive the byte properly.
void send_frame(Segment *seg, int offset, int length);
Send a data frame to the computer from the given segment, starting at offset and with length bytes, not including the checksum. The range must fit within the segment. A standard SIO checksum is automatically appended at the end of the frame (1's complement of all payload bytes). Script execution is suspended until the send completes.
Data is copied from the segment to an internal buffer as soon as the send request is queued, so any modifications to the segment after that are not reflected in the transfer. This is possible since memory access handlers can run in parallel to SIO transactions.
There is a limit of 8192 bytes per frame sent.
void recv_frame(int length);
Receive a data frame from the computer with the given number of bytes, not including the checksum. The checksum is automatically checked and if it fails, a NAK is automatically sent and command processing is terminated. Script execution is suspended until the receive completes.
When the frame is received, script execution is resumed. The received frame is available in a special segment called $sioframe. This segment is only valid during the current script until it exits or is suspended; if the data is required beyond that it should be copied into another segment. The $sioframe segment is read-only and cannot be mapped into a memory layer.
There is a limit of 8192 bytes per frame received.
void delay(int cycles);
Suspend execution of the script for the given number of machine cycles.
int command_asserted();
Returns true if the SIO COMMAND line is asserted, indicating that a command frame is being transferred.
int motor_asserted();
Returns true if the SIO MOTOR line is asserted, meaning that the computer is requesting a tape deck to run or a communications device to take control of the SIO bus for non command frame based transfers.
void enable_raw(int enable);
Enables or disables raw SIO. This is needed to send raw bytes, signals on the SIO PROCEED or INTERRUPT lines, or to receive raw SIO events. However, performance is impacted when this is enabled, so it should only be enabled when needed. The default is disabled.
Disabling raw SIO automatically deasserts the PROCEED and INTERRUPT lines, if they have been asserted.
void set_proceed(int asserted);
Asserts or deasserts the SIO PROCEED control line.
Raw SIO must be enabled to use this method.
void set_interrupt(int asserted);
Asserts or deasserts the SIO INTERRUPT control line.
Raw SIO must be enabled to use this method and it must be called on a script thread.
int send_raw_byte(int ch, int cycles_per_bit);
Send a raw byte on the SIO bus from the device to the computer's SIO DATA IN line.
ch is the data byte to send, as it will be received in POKEY's SERIN register. Start and stop bits are automatically added around this byte, for a total of 10 bits to transfer.
While cycles_per_bit is the number of computer machine cycles between each bit. A value 93 is used for 19200 baud (19069-19245 baud).
Raw SIO must be enabled to use this method and it must be called on a script thread. Thread execution is suspended until the byte is fully sent.
int recv_raw_byte();
Wait for a byte to arrive on the SIO bus and return the byte received. The bit rate in cycles per bit is stored in the thread-local $aux variable.
Raw SIO must be enabled to use this method and it must be called on a script thread.
recv_raw_byte() will not work if the sio_received_byte script event is bound.
void wait_command();
Wait for the SIO command line to assert, signaling the beginning of a command frame. Only a leading edge resumes execution; if SIO command is already asserted when this method is called, the method waits until the signal is deasserted and re-asserted.
This method is intended for waiting for the beginning of a command frame.
Raw SIO must be enabled to use this method and it must be called on a script thread.
void wait_command_off();
Wait for the SIO command line to be deasserted, signaling the not-command-frame state. If SIO command is already deasserted when this method is called, execution continues immediately.
This method is intended for waiting until the computer is ready to receive the device ACK/NAK response to the command frame.
Raw SIO must be enabled to use this method and it must be called on a script thread.
void wait_motor_changed();
Wait for the SIO motor line to change state from the current polarity. The motor_asserted() method can be used to check the state before or after this call.
Raw SIO must be enabled to use this method and it must be called on a script thread.
void reset_recv_checksum();
Resets the checksum value accumulator for receive operations to zero.
void reset_send_checksum();
Resets the checksum value accumulators for send operations to zero.
int get_send_checksum();
Reads the computed SIO checksum for all bytes sent with raw SIO since the last call to reset_send_checksum(). This is the 8-bit one's complement sum of all bytes.
Typically this is called after the payload of a data frame is sent, with the checksum computed by this method being sent afterward.
int get_recv_checksum();
Reads the computed SIO checksum for all bytes received with raw SIO since the last call to reset_recv_checksum(). This is the 8-bit one's complement sum of all bytes.
When using this to verify the received checksum for a command or data frame, get_recv_checksum() should be called before the checksum is received, or else the checksum computation will also include the received checksum byte. For instance, when receiving a 128 byte data frame, the sequence would be: reset_recv_checksum(), 128x recv_raw_byte(), get_recv_checksum(), recv_raw_byte(), then compare the computed and received checksum bytes.
The checksum accumulator is updated whenever a script receives a byte with recv_raw_byte(). It is not updated if a byte is received when a script is not waiting to receive (thus missing the byte).
int check_recv_checksum();
Compares the last byte received against the computed SIO checksum for all bytes received with raw SIO since the last call to reset_recv_checksum(), not including the last byte, and returns true if they match. This is a shortcut for saving the result of get_recv_checksum() and comparing it after receiving the checksum byte, allowing:
  $sio.recv_raw_byte();
  if ($sio.check_recv_checksum())
    ...

PBI device

Declaring an instance of the PBIDevice class creates a custom Parallel Bus Interface (PBI) device. There can only be one instance.

device_id: int
Required: Sets the device ID for the PBI device. This must be exactly one bit set from bit 0 to bit 7, as a power of two from $01 to $80. The lower the device ID, the higher priority the PBI device in SIO, CIO, and IRQ handling.
has_irq: int
Optional: If true, enables IRQ handling for the PBI device. The device's ID bit is exposed as an IRQ status bit in $D1FF and the assert_irq() and negate_irq() methods are enabled.
void assert_irq();
Asserts the PBI device's interrupt request (/IRQ) signal. This will cause a maskable interrupt on the CPU, the next time maskable interrupts are enabled. If the PBI device's IRQ is already asserted, this does nothing.
The IRQ line stays asserted until either negate_irq() is called or a system reset occurs.
void negate_irq();
Negates the PBI device's interrupt request (/IRQ) signal, so that it no longer requests a maskable interrupt from the CPU. If the IRQ is already negated, this does nothing.

Clock

The $clock object provides methods to access the host real-time clock for emulating real-time clock devices.

void capture_local_time();
Snapshot the current time in the current local time zone. This captures the local time in one pass so that accesses to individual fields are consistent. The local time is cached so that the time is not fetched more than once per script; it cannot be used to measure execution time within a script.
int local_year();
Return the year of the last captured local time as a four-digit value, e.g. 2020.
int local_month();
Return the month of the last captured local time (1-12).
int local_day();
Return the day of the month of the last captured local time (1-31).
int local_dayofweek();
Return the day of the week of the last captured local time as a value 0-6, where 0 means Sunday.
int local_hour();
Return the hour of the last captured local time (0-23).
int local_minute();
Return the minute of the last captured local time (0-59).
int local_second();
Return the second of the last captured local time (0-59).

Controller ports

Controller port objects represent one of the 9-pin controller ports on the system, bound to a scripting variable with the controller_ports section.

On a computer with only two controller ports (XL/XE), the third and fourth controller ports are disabled; they maintain state and all functions can be called, but the controller state is not applied to hardware state.

void set_paddle_a(int pos);
void set_paddle_b(int pos);
Sets the position of paddle A or paddle B, as the value returned from the POT0-7 register and as reflected by the BASIC PADDLE() function. The valid range is 1-228; other values are clamped to this range.
void set_trigger(int down);
Sets the joystick trigger button state, where false means up and true means down.
void set_dirs(int mask);
Sets a bitmask of which joystick direction inputs should be asserted. Bits 0-3 correspond to up, down, left, and right, with a '1' bit meaning that the direction is active (grounded). This is inverted from the state as reflected in the PORTA/PORTB registers or reported by the BASIC STICK() function. Zero deactivates all inputs (joystick centered).
As on the real hardware, the left and right inputs (bits 2-3) also double as the paddle triggers.
When emulating a joystick, it is the responsibility of the device to not send both left+right or up+down to the computer. This is a valid input hardware-wise, but generally not possible with joystick hardware and some programs that use joysticks will malfunction if this is seen.

Debug class

The Debug class contains methods to aid in development of custom devices.

static void log(String *s);
Prints a string to the CUSTOMDEV logging channel in the debugger.
static void log_int(String *s, int value);
Prints a string to the CUSTOMDEV logging channel in the debugger, followed by an integer value formatted as decimal.

Thread class

The Thread class contains methods to control execution of script threads. These methods only work with explicitly declared script threads; they cannot be used on the implicit threads created for direct script handlers or SIO command execution. However, those threads can control script threads.

All script threads are cooperatively scheduled on a single thread of execution.

int is_running();
Returns true if the thread is currently running. This includes being temporarily suspended in a method call, or while waiting for its turn to execute (since only one thread can execute at a time).
int run(function f);
Starts execution of the specified function on the given thread. The function must return void. If the thread is currently running, it is stopped before being restarted at the beginning of the given function.
run() cannot be called on the current thread.
void interrupt();
Stops execution on the thread, interrupting all currently executing functions on the thread's stack. For threads stopped in a yielding method, what side effects if any occur from the interrupted method call depend on the specific method.
The currently executing thread cannot interrupt itself.
static void sleep(int cycles);
Suspends execution of the thread for the specified number of machine cycles (at 1.77/1.79MHz). Negative or zero values are silently ignored and do not suspend.
This method cannot be used in an SIO command handler script, which must use $sio.delay() instead. The reason for this is that SIO command handling is sequenced through the SIO manager's action queue and it may virtualize actions that would ordinarily take time.
void join();
Join threads of execution by waiting until the given thread finishes. This includes either the thread returning from its top-level function or being stopped with interrupt().
Ordinarily two threads should not join() on each other, as this would cause the two threads to deadlock by endlessly waiting for each other. This is still allowed, however, and the deadlock can be broken by a third thread interrupting one of the two.
It is guaranteed that join() does not return until the thread has become idle, but it does not prevent another thread from being scheduled in between and restarting the thread before the thread that called join() can resume. If more than one thread calls join() on the same target thread, all waiting threads are resumed when the target thread finishes.

Image class

The Image class contains a 24-bit writable image bitmap. Images are either created new with width and height in pixels, or loaded from an image file in PNG format with the source property.

Individual images are limited to a max of 65536 pixels in width or height and four million pixels (2^22 to be exact). Thus, images of size 8x65536 and 2048x2048 can be used, but not 4096x4096.

The image coordinate system is top down with (0,0) being the top-left corner of the image. Rectangles are specified as a top-left corner position, width, and height. A rectangle entirely outside of the image or with negative width or height is fully clipped and causes the operation to be silently dropped (fully clipped). A rectangle partially within the image is partially clipped and may or may not be supported by the operation, with some calls still operating on the partially clipped area and others dropping the operation.

Pixel values in the image API are 24-bit pixels of the form $rrggbb, with RGB values being conventional 24-bit sRGB values similarly to as seen in HTML and CSS. The highest 8 bits of the pixel value (alpha) are ignored on input and zero on output. Images that are created without a backing image are initialized with a hot pink color to make obvious when uninitialized image data is being used.

Image processing occurs on the CPU and can get expensive, so large pixel area operations should be used sparingly.

width: int
height: int
Optional; specify the width and height when an image is defined, in pixels. Required if source is not specified.
source: path
Required; specifies the path to a PNG file to initialize the image. Required if width and height are not specified.
void clear(int color);
Clears the entire image to a solid color.
int get_pixel(int x, int y)
Reads the value of a pixel. Locations outside of the image return zero.
void put_pixel(int x, int y, int color);
Writes a pixel to the image with the specified color. Locations outside of the image are ignored.
void fill_rect(int x, int y, int width, int height, int color);
Fills a rectangle of the image with the specified color, using (x, y) as the top-left corner of the rectangle. Partial clipping is supported.
void invert_rect(int x, int y, int width, int height);
Converts the pixels within the given rectangle to their inverse, equivalent to the bitwise complement of the bits, or reversing the 0-255 range of each channel. Partial clipping is supported.
void blt(int dx, int dy, Image *source, int sx, int sy, int width, int height);
Performs a block transfer of a rectangle from the source image to this image as the destination, using (dx, dy) as the top-left corner in the destination and (sx, sy) as the top-left corner in the source. No stretching is performed. Partial clipping is supported, copying the largest rectangle completely within both the source and the destination images.
The source and destination images may be the same, and in the case that the source and destination rectangles overlap, the copy direction is chosen to preserve the pixels in the source rectangle. Thus, a blit operation may be used to scroll a portion of an image.
void blt_expand_1(int dx, int dy, Segment *source, int offset, int pitch, int width, int height, int color0, int color1);
Expands raw monochrome (1-bit) data in a source segment to pixels in an image. The monochrome data is expected to be in MSB-to-LSB bit ordering, the same as expected by ANTIC. 0 bits are expanded to color0 and 1 bits are expanded to color1. Partial clipping is not allowed in either source or destination.
The source data is specified as a pointer to the first row of data, which is the byte containing the top-left pixel, and a pitch value, which is the difference from the starting offset of one row to the start of the next. For a packed bitmap with no gaps between rows, the pitch is equal to the number of bytes needed per row, rounded up. For instance, a 320-wide packed bitmap would have a pitch of 40 bytes. The pitch value may also be negative to flip the image vertically or zero to repeat a single row.
void blt_tile_map(int x, int y, Image *tile_source, int tile_width, int tile_height, Segment *tile_map_source, int tile_map_start, int tile_map_skip, int tile_map_width, int tile_map_height);
Performs a tile map blit, where a tile map in a segment is used to map fixed-size tiles in a tile source image to tiles in the destination image (the image this method is called on). This is useful for both character modes and background tiled graphic modes. Partial clipping is not allowed in any of the images or segments.
The tile map is stored as bytes in a segment, where each byte refers to one of 256 tiles in the tile source image. The tile map is stored left-to-right, then top-to-bottom. Thus, a 80x24 tile map requires 1920 bytes. A positive number of bytes can be specified to be skipped at the end of each row via tile_map_skip, which allows the tile map to be larger than the rectangle used for the tile map blit. For instance, a 40x25 region may be used from a 256x256 tile map by setting the skip value to 256 - 40 = 216 bytes. A skip value of 0 specifies a packed tile map with no space between rows.
The tile source is an image with tiles stacked vertically, with tile 0 being at the top of the image and each successive tile below it in order. tile_width and tile_height specify the size of a tile in pixels. The tile source must be large enough to store a full set of 256 tiles. A set of 8x8 tiles therefore requires a 8x2048 image. The init event can be useful to prepare this tile source image from other sources, such as a PNG file containing the tiles stored in a more easily editable 16x16 grid, or raw bitmap data in a ROM image.
(x, y) specifies the top-left corner of the destination area, and tile_map_width and tile_map_height specify the area to be written in tiles.

VideoOutput class

VideoOutput objects are used to create alternate video outputs produced by devices, which can then in turn be displayed. These can either be entirely parallel video outputs like the XEP80, or they can have pass-through capability to show either a custom video output or the main computer output, like the Bit-3 Full View 80. Image objects supply the pixel content to display through a VideoOutput.

display_name: string
Required; provides the user-readable name of the video output in the UI. The video output name should be the name of the device or video output without "output" or "video output" in the name; the UI adds this as needed.
composite: function
Optional; invoked before the video output is refreshed to allow the contents of the backing Image to be updated. This allows for lazy updates, such as redrawing the contents of the image from a framebuffer in a segment. However, it is not necessary to hook this event if the image contents are kept up to date from other scripts.
Note that the composite event is not guaranteed to be dispatched once per frame. It may be dispatched more often if the system is stopped in the debugger mid-frame or less often if refreshing is not required. As a result, composite must not cause observable side effects in the core emulation, such as changing CPU-visible registers or memory. Use other mechanisms such as the global vblank event or a timer thread if a per-frame tick is needed.
pre_copy: function
Optional; invoked before a copy text operation completes. This gives script a chance to update an ATASCII copy of the framebuffer contents and bind it to the video output with set_copy_text_source() before the copy operation occurs, so those contents don't have to be constantly up to date. Because this is only invoked on copy operations, it can perform relatively expensive operations that would be undesirable to run as the framebuffer is updated. It can be omitted if those contents are always available, such as a framebuffer that is natively ATASCII and in a fixed location.
void set_par(int numerator, int denominator);
Sets the pixel aspect ratio (PAR) for the video output, as a fraction of two integers giving the ratio of width to height. This is used to correctly display the video output with non-square pixels when aspect ratio correction is enabled in view options. The default PAR is 1, which means square pixels. PAR values less than 1 produce pixels that are tall and skinny, while PAR values greater than 1 produce pixels that are fat and wide.
The fraction need not be reduced to lowest terms, but the denominator may not be zero and the ratio should not be negative. PAR values are clamped to 0.01-100. The PAR is specified as a fraction to work with integers; under the hood it is simply converted to a floating-point value to scale the image at display time. It is not necessary to compute an exact ratio, nor are there any issues with large numerator or denominator values. Two significant digits of accuracy is generally fine, three will get within one pixel of error on a modern display.
Note that PAR (pixel aspect ratio) should not be confused with DAR (display aspect ratio), the latter being referred to when calling displays 4:3 or 16:9. The PAR is not determined by the resolution of the display image in pixels or the shape of the screen. In hardware, the PAR is determined by the dot clock (pixel clock) and the horizontal and vertical frequency timing. 320x192 and 320x240 screens with the same H/V timing and dot clock have the same PAR.
For an NTSC output, a common dot clock is 7.16MHz or twice the color subcarrier frequency, which produces a "lores" display of around 280-320 pixels across and a PAR of 6/7. This is the rate used by an ANTIC mode F screen. A "hires" 640 display with a 14.28MHz dot clock would have a PAR of 6/14. For PAL, the ANTIC mode F equivalent is a dot clock of 7.10MHz and a PAR of 1.0396. For other dot clocks, the PAR can be computed by dividing the standard square pixel rates of 12.273MHz (NTSC) or 14.75MHz (PAL) by the dot clock, and then dividing the result by two if the display is non-interlaced.
void set_image(Image *image);
Sets the image used as the framebuffer for the video output. This must be called before the video output can display a signal.
void mark_active();
Marks a video output as being active, which means there is "interesting activity" in the video output. This is used to aid in automatically detecting and switching to the video output when it is first used. For instance, the built-in XEP80 emulation uses this to automatically switch to the XEP80's output when it is initialized and video output autoswitching is enabled. A video output should be marked active whenever any program output is sent to the display. It should not be automatically marked as active on reset unless the video output was originally usable in that way, such as the firmware automatically initializing and activating the display.
void set_pass_through(int enabled);
Enables pass-through for a video output, causing the computer's normal ANTIC+GTIA output to appear through this video output. This is used for video outputs that actually have pass-through capability in hardware, particularly ones under software control. It should not be used for video outputs that were manually switched and can be simultaneously active like the XEP80, as this would prevent dual-monitor usage.
void set_text_area(int columns, int rows);
Sets the dimensions for a character grid of the video output, in terms of a regular grid of characters mapped over the size of the display output. For instance, set_text_area(80, 24) on a 640x192 display would declare the text mapping to be 24 rows of 80 characters, with an 8x8 cell per character. The character grid size does not have to evenly divide into the image size, though this is recommended to avoid rounding errors.
The character grid is used to enable text selection and text copy from the custom output. It does not affect the display output or generate a text screen image. Once set, character spans can be drag-selected on screen and copy requests issued to read out the text, with pre_copy and set_copy_text_source() assisting. Setting a grid size of 0x0 will disable text selection.
void set_copy_text_source(Segment *seg, int offset, int skip);
Sets a segment region to read ATASCII characters out of when a text copy operation is performed. This should occur either when the video output is set up or on the pre_copy event, depending on how dynamic the video mode is. When the character grid and text source are set up, a copy operation will read ATASCII characters from the segment and translate them to text to place into the clipboard.
offset specifies the starting offset of the character map, which is assumed to have the column and row counts specified to set_text_area(). skip specifies the number of bytes between the end of a row and the start of the next, with 0 indicating back-to-back rows. For instance, an 80x24 text area with skip=0 requires a contiguous 1920 byte region. The entire region must be contained within the segment for a copy operation to succeed, but it is OK for the mapping to be temporarily invalid since set_text_area() and set_copy_text_source() must be called separately. For cases where this mapping is inconvenient, the pre_copy event allows script to translate from the running format of the framebuffer to a copyable text buffer.


Altirra Help: Using the debugger

Using the debugger

Altirra includes a powerful debugger for diagnosing programs that are not working properly or that are under development.

Contents
  • 1. Switching to debugger mode
  • 2. Using the command line
  • 3. Execution control
  • 4. Setting breakpoints

Switching to debugger mode

To switch to debugger mode, select Debug > Enable Debugger from the menu. This will change the window layout to the default for debugging, which you can then modify by dragging panes or opening new ones from the Debug > Windows menu. Debug mode and normal mode have separate window layouts, which are saved independently.

Using the command line

In debug mode, various panes are available to display the state of the CPU, of memory, and of emulation. However, the main port for interacting with the debugger is the Output pane, which contains both a log window and a command line at the bottom. While some of the debugger commands can be used through the GUI, all of them are available through the command line, and thus mastering the command line is important for debugging proficiency.

All commands are of the form:

command-name [optional arguments]

To execute a command, type it in the command line and hit Enter. The command will be echoed in the log, along with any results. The command line supports history and allows past commands to be repeated using the up and down arrow keys.

The .help command gives a list of available commands. You can also specify a command name, i.e. .help s, in order to get help on a specific command.

Execution control

In order to execute commands, however, simulator execution should be halted. This can be done by Run/Break (F8) or by Break (Ctrl+Break). Once execution has been halted, the title bar will change to indicate that the simulator is stopped and all of the debug panes will be updated with the current emulation status. No changes will occur to emulation until a command is given.

The t (trace) command allows the emulation to be single-stepped under debugger control. The trace command runs the simulator until the CPU has completed executing an instruction, after which it is halted again and the current execution state is reported. If an interrupt occurs, the simulator will step into the interrupt handler routine.

For cases where a particular execution path is being followed, the s (step) command is useful. It acts like the trace command except that any interrupt handler or subroutine call is stepped through automatically. If the simulator has already stepped into a subroutine, the gr (go until return) command will execute instructions until the subroutine is complete.

While it is possible to debug a program using the t, s, and gr commands in the command line, these commands are so frequently used that they have shortcuts: Step Into (F11), Step Over (F10), and Step Out (Shift+F11).

The go (g) command resumes execution. Its analogous GUI command is Run/Break (F8).

Setting breakpoints

Breakpoints allow the simulator to automatically break into the debugger when a specific condition is reached, permitting much faster debugging than just manually stepping the program since the program runs at full speed while the debugger waits for a breakpoint to fire. A common approach is to use breakpoints to detect an particular condition and then single-step the program around the area of interest.

The most common type of breakpoint is a PC breakpoint. These are set with the bp (breakpoint) command, and listed or cleared with the bl (breakpoint list) and bc (breakpoint clear) commands, and cause the simulator to break into the debugger when the CPU PC register equals certain values. There is no limit to the number of PC breakpoints that can be set, and breakpoints persist until cleared. Since PC breakpoints are also a frequently used feature, they can be toggled in the GUI by selecting an instruction in the Disassembly view and pressing the F9 key. The Disassembly view shows a red background on any instruction on which a breakpoint is set.

Unlike traditional on-machine debuggers which may insert BRK instructions into the instruction stream to implement breakpoints, breakpoints in Altirra work by address matching. Memory is never modified by a breakpoint, and they can be placed even in ROM or on hardware registers. Execution timing is also unaffected by breakpoints.

Altirra also supports memory access breakpoints, which allow detection of reads or writes to a particular address. These are set and cleared using the ba (break on access) command. Memory range breakpoints can also be set, which trip whenever any location within the memory range is accessed.


Altirra Help: Source-level debugging

Source-level debugging

While it is possible to debug a program via disassembly, doing so is an arduous task, particularly with large programs. Altirra supports both symbolic and source level debugging to make it easier to debug.

Contents
  • 1. Generating and hooking up symbols
  • 2. Generating symbols with specific toolchains
  • 3. Debugging at listing or source level

Generating and hooking up symbols

In order for Altirra to provide an enhanced view of the program, there must be symbols available for objects in memory. Symbols indicate label addresses and source line positions to the debugger, allowing the debugger to match addresses and disassembly from the simulator with the source code of the program. Altirra includes symbols for the hardware and OS page zero and page two/three databases, but symbols must be provided for any additional programs that are loaded.

There are several types of symbols:

Labels
Matches addresses in memory to label names in the program source. When labels are loaded, Altirra displays label names instead of raw addresses in disassembly, and commands which take addresses can also take label names.
Listing
Displays a list of all instructions in the program along with source code lines, along with their generated addresses and opcodes. This permits a limited form of source code debugging, where execution can be traced through the listing. Listings are produced by assemblers.
Source/line information
Matches addresses of instructions in the program with source code lines. This allows full source-level debugging, where tracing and breakpoint control can occur directly in source code.

Files containing symbols can be loaded and unloaded manually using the .loadsym and .unloadsym commands. When booting an executable using the Boot Image command, Altirra will automatically load any symbol files that have the same name and path as the executable and one of the extensions .lst, .lab, or .lbl.

Generating symbols with specific toolchains

In order to generate symbols, appropriate options have to be set on the assembler or compiler generating the program:

MADS 1.9.x (assembler)
Use -t to generate labels and -l to generate a listing. Full label translation, listing-level, and source-level debugging are possible with this assembler.
XASM 3.0.x (assembler)
Use /t to generate labels and /l to generate a listing. Full label translation, listing-level, and source-level debugging are possible with this assembler.
ATasm 1.07 (assembler)
Use -l:program.lab to generate labels; only label translation is supported. Source-level debugging is not possible with ATasm since it does not output source line information in its listing.

Debugging at listing or source level

A program can be debugged at listing level through the Debug > Open Source File... command. This opens a source code view window with the contents of the listing file. Any lines that are detected to correspond to instructions are displayed with a white background, and other lines have a gray background. When broken into the debugger, the current line is shown in amber, and any breakpoints in red. The Step Into (F11), Step Out (Shift+F11), Step Over (F10), and Toggle Breakpoint (F9) commands work in the source code window. In addition, the Go command has an F5 shortcut in source windows.

When source/line information is available, the Open Source File command can also be used with the original source code files. Again, lines with a white background indicate that source information has been hooked up correctly and that stepping and breakpoints can occur on source lines. In addition, you can jump between source code and disassembly through Go to Source Code and Go to Disassembly commands on the context menus of the source and disassembly views. Unlike in disassembly and listing views, the stepping commands step by a source line in the source view.


Altirra Help: Addresses and expressions

Addresses and expressions

Many debugger commands take addresses or expressions as arguments.

Contents
  • 1. Addresses
    • 1.1. Global addressing
  • 2. Expressions
    • 2.1. Constants
    • 2.2. Grouping
    • 2.3. Arithmetic operators
    • 2.4. Relational and logical operators
    • 2.5. Bitwise operators
    • 2.6. Low and high byte operators
    • 2.7. Memory access operators
    • 2.8. CPU register operators
    • 2.9. ANTIC register operators
    • 2.10. Breakpoint context operators
  • 3. Length expressions

Addresses

Simple addresses can be either specified in default base, as set by the .base command, or in hex using a $ prefix. The default base is mixed, which selects hex for simple addresses and decimal for numbers in expressions.

Global addressing

For commands that support global addressing, 24-bit and alternate memory space addresses can be used.

65C816 24-bit addresses can be specified in bank+offset form using a : to separate the bank and offset, e.g. $01:0000. A $ prefix selects hex for both the bank and the offset together.

PORTB-based extended memory can be addressed in two ways. The x: prefix linearly addresses extended memory, while $xx'yyyy syntax addresses it using the PORTB value and CPU address. For instance, $EF'4000 maps to the extended memory visible to the CPU at $4000 with a PORTB value of $EF.

Additional prefixes:

  • n: selects ANTIC address space
  • v: selects VBXE memory by VBXE linear address
  • r: selects main memory by CPU address, even if currently shadowed by ROM or hardware
  • rom: selects system ROM by CPU address, even if currently disabled or shadowed by hardware
  • cart: selects cartridge memory with linear addressing
  • t:$xx'yyyy selects cartridge memory with banked addressing, e.g. t:$02'A000 accesses $A000 with a bank value of $02. (This is currently only supported with the AtariMax cartridge types.)

Expressions

Constants

Constants may be specified in the default base, base 16 (hex), or base 2 (binary). Hex numbers are preceeded by $ and binary numbers by %. In mixed base mode, expressions beyond a simple address use decimal constants.

A : may be used to select an alternate address space or a banked 24-bit address.

Grouping

Parentheses () are used to group subexpressions. They do not request memory indirection in the debugger as they do in 6502 assembly language.

Grouping parentheses can also be used to force expression evaluation instead of address evaluation.

Arithmetic operators

The +, -, *, /, and % operators perform arithmetic addition, subtraction, multiplication, division, and modulus, respectively. All arithmetic is performed according to 32-bit, two's complement signed integer rules.

Relational and logical operators

<, <=, >, >=, =, and != binary operators compare two values, evaluating to 0 for false and 1 for true. Note that unlike C-inspired programming languages, the equality operator is a single = and not ==.

and and or perform logical AND or OR combination and evaluate to 1 if both arguments and/or non-zero and 0 otherwise.

The unary ! operator does logical inversion, evaluating to 1 if the argument is zero and 0 otherwise.

Bitwise operators

&, |, and ^ are bitwise AND, OR, and XOR operators. ~ is bitwise inversion.

Low and high byte operators

< and > are unary operators that return the low and high bytes of a 16-bit argument. They operate similarly to the low and high byte prefixes used in 6502 assembly language.

Memory access operators

The db, dsb, and dw unary operators read an unsigned byte, signed byte, and unsigned word from memory given an address.

CPU register operators

The pc, a, x, y, s, and p operators return the values of the corresponding CPU registers. In 65C816 mode, the low bytes of the A, X, Y, and S registers are returned.

Note: The a operator is overridden by the hex value $a if mixed base mode is enabled. This only happens in expressions that contain only a and nothing else. Use (a) or @a to force the operator instead.

ANTIC register operators

hpos and vpos return the horizontal and vertical beam positions.

Breakpoint context operators

The read and write operators refer to the current memory read or write address and are used in the address tests of a memory breakpoint. They are only valid within a breakpoint condition expression.

In a breakpoint filter expression or when executing commands from a breakpoint action, the address operator returns the memory access address and the value operator returns the value read or written to memory.

Length expressions

Commands that operate on a memory range take a length parameter which begins with L, which indicates the length of the range in bytes:

f 4000 L1000 00 01 02 03
f $1600 L($a00+x) 00 01 02 03

The form L> can also be used to set the length based on an inclusive end address rather than a byte count:

f 4000 L>4FFF 00 01 02 03

Note that this takes priority over the high-byte expression operator. Parentheses can be used to force the operator:

f pagetable L(>bytes) 00 01 02 03


Altirra Help: Diagnostics

Diagnostics

Altirra's debugger allows for non-intrusive trace and assert statements to be added to assembly code.

Contents
  • 1. Prerequisites
  • 2. Trace statements
  • 3. Assertions
  • 4. Gotchas

Prerequisites

You must be using the MADS assembler and have the listing file loaded into the debugger as a symbols file for trace and assert statements to work.

Trace statements

A trace statement is a comment with the following form:

;##TRACE "format-string"arguments...

This expands to the .printf command and therefore the format string and arguments are of the same form. For instance, this will print out the current display list vector address:

;##TRACE "Display list address = $%04X" dw(vdslst)

The output of a trace statement goes to the debugger's Output window.

Assertions

An assertion statement declares that a condition should be true at a point in execution and stops execution if that turns out to be false:

;##ASSERT dw(adress) >= $0400

These are used to catch broken assumptions in code. A failed assertion will stop execution in the debugger with an "assertion failed" message.

Gotchas

Trace statements and assertions execute in the debugger context and not in 6502 code, so they are "free" and do not impact timing or any register or memory state. Therefore, unlike real hardware, they can be inserted in time critical code such as a DLI handler.

However, it is important to know that these statements are implemented as conditional breakpoints with an on-hit command. One important implication is that they are bound to the following instruction, so attempting to separate it with a label will not work:

    ;This trace statement will actually execute on each loop iteration.
    ;##TRACE "Initial loop value: %d" y
loop:
    sta    (adress),y
    dey
    bpl    loop

The second issue to be aware of is that as breakpoints they are immune to any modification from the simulation. This means that any attempt to self-modify or overlay code will be ignored by the statements, and if two routines are assembled into the same region and swapped dynamically, their diagnostics will overlap.

Finally, because trace statements and assertion statements are comments and are detected by parsing the assembler listing, they are not subject to code generation switching and will activate even if they are in a .if 0 block or other construct that would normally disable a block of code. They are affected by enabling and disabling of listing output, however (OPT L- and OPT L+).


Altirra Help: Logging

Logging

The simulation has a number of optional logging channels which can report additional information about what is going on in the emulated program. The debugger has commands to manage the logging configuration.

Displaying logging channels

The lfl (log filter list) command displays the list of available logging channels and their current status:

Altirra> lfl
850         off  850 interface SIO activity
CAS         on   Cassette I/O
CASDATA     off  Cassette data
CASDRDATA   off  Cassette direct data
CASIMAGE    off  Cassette image processing
CS8900AD    off  CS8900A network data (requirse CS8900AN channel)
CS8900AN    on   CS8900A network transmit/receive
CS8900AR    off  CS8900A reads
CS8900AW    on   CS8900A writes
DISK        off  Disk activity
DISKCMD     off  Disk commands
DISKIMAGE   off  Disk image load details
DISKXFR     off  Disk data transfer
DS1305CR    off  DS1305 real time clock reads
DS1305UR    off  DS1305 real time clock user reads
DS1305W     off  DS1305 real time clock writes
FLASH       off  Flash memory erase operations
FLASHWR     off  Flash memory write operations
FPACCEL     off  Floating-point acceleration
HOOKSIO     off  OS SIO hook messages
HOOKSIOREQS  off  OS SIO hook requests
IDE         off  IDE activity
IDEERROR    off  IDE interface errors
MODEM       off  Modem activity
MODEMDATA   off  Modem interface data
MODEMTCP    off  Modem TCP/IP activity
PCLINK      off  PCLink activity
V3021       off  V3021 real time clock accesses
VDISK       off  Virtual disk activity
XEPCMD      off  XEP-80 Commands
XEPDATA     off  XEP-80 Data Transfer

Most channels default to off, although a few are enabled by default. The lfe (log filter enable), lfd (log filter disable), and lft (log filter enable tagged) commands are used to modify the state of a channel. For example, enabling the MODEM channel is useful for checking the modem commands being sent out over the R: device:

Altirra> lfe modem
Enabled logging channel: MODEM
Altirra> g
MODEM: Executing command: [Z]
MODEM: Sending response: OK

Additional logging paths

There are a couple of other useful logging paths that are not controlled via the logging system:

  • .traceser enables logging of raw serial I/O bus traffic.
  • .tracecio enables logging of CIO commands.


Altirra Help: Profiling

Profiling

Profiling a program means measuring its performance. The debugger contains a built-in profiler to aid in determining which parts of a program are slowest and which ones are most promising to optimize.

Running the profiler

To run the profiler, select the Debug > Profile > Profile View menu item. This opens up the Profile View pane, which can then be docked anywhere in the UI like other debugger panes:

The toolbar at the top controls the profiler's behavior. The first three buttons are stop, pause and start; to perform a profiling session, the profiler must be started and then stopped.

Profiling modes

The profiler supports several collection modes:

Instruction sampling
Collects information on every instruction executed, and shows which instructions are executed more often and which take more total time.
Function sampling
Also collects information on instructions, but groups them by function calls instead, where function scopes are opened by JSR or interrupts and closed by RTS/RTI. This provides a higher level view of performance than instruction sampling. Note that JMP and branch instructions do not change function scope.
Call graph sampling
Similar to function sampling, but groups samples by context instead of by function, where a context is the complete call stack on function entry. For instance, this distinguishes between function C being called by function A, by function B, and by function B called from function A. This allows not only determining which functions are taking time, but also which call paths are most leading to that function.
Basic block sampling
Groups samples by basic blocks, where a basic block is a series of instructions ending in a control flow instruction (branch, jump, return). This mode is best for identifying loops that are taking the most time.
BASIC line sampling
Groups samples by the BASIC line executing at the time. This works with Atari BASIC compatible interpreters (Altirra BASIC, TurboBasic XL, BASIC XL, and BASIC XE in non-EXTEND mode). This is useful for identifying hotspots in BASIC programs.

Once the profiling session is ended, the results are displayed in the profile view. In non call graph views, multiple lines can be selected to display the summary at the bottom.

Multiple columns are displayed for each instruction or function. In list view based modes, the list of samples may be sorted by a column by clicking on a column; clicking the column again reverses the sort order. The meanings are as follows:

Thread
Execution thread that the sample occurred on. In instruction mode, the thread is determined by the state of the processor's interrupt mask (I) flag and instructions with the I flag set are considered to execute in interrupt thread. In function and call graph modes, samples are grouped under separate IRQ, VBI and DLI threads when they are executed within the call stack for those interrupt types.
Address
The memory address of the instruction or start of the function. If symbols are loaded, the name of the symbol for that address is also shown.
Calls
The number of times that the function was executed.
Insns
The number of times that an instruction was executed, or in function mode, the number of instructions executed in the function.
Clocks
The number of clock cycles spent in each instruction or function. These are machine cycles, so they include time that the CPU was halted by ANTIC DMA.
CPU clocks
The number of clock cycles spent by the CPU actually executing each instruction or function, excluding cycles that the CPU was halted by ANTIC DMA. This is also called unhalted cycles.
Clocks%, Insns%, CPUClocks%
Ratio of the sample's statistic to the overall sum for the frame or frames selected. 13.6% for CPUClocks%, for instance, means that the CPU spent 13.6% of its total available time executing code on that function.
One use of this statistic is to determine the maximum possible impact of an optimization. If a function only takes 10% of the CPU to begin with, then the best possible improvement in overall execution time is to reduce it to 90%, or about 11% faster than before. On the other hand, optimzing a function that takes 75% of the time will have a substantial impact on main loop time.
DMA%
The proportion of time that the CPU was halted during the sample, i.e. ratio of halted cycles to total cycles. A higher percentage means that the CPU had less time to actually execute code relative to the total amount of time taken due to more cycles being stolen by ANTIC DMA.

Performance counters

In addition to the sampling modes, performance counters may also be captured during the profiling session. This captures additional specific information about instructions. Up to two counters may be enabled:

Branch taken / branch not taken
Counts conditional branches (Bcc instructions) that are either taken and redirect execution or not taken and fall through. This is useful for identifying branches that should be reversed to save 1-2 clock cycles per branch.
Page crossing
Counts conditional branches and absolute indexing operations that take an extra clock cycle due to crossing a page boundary. This helps identify hot code or data that should be realigned to avoid the page crossing penalty. Absolute indexed store instructions (STA abs,X) and read modify write instructions (INC abs,X) are not counted as they always execute the extra clock cycle.
Redundant instruction
Counts flag instructions that set or clear a flag that is already set or cleared, e.g. CLC when C=0, and LDA/X/Y #imm instructions that set a register to the same value. This can help identify instructions that are actually no-ops due to the required state already being set earlier, and can thus be removed.
Note that the redundant instruction statistic is only valid during the profiling run. A 100% result for an instruction is a strong indicator that an instruction is unnecessary and can be removed, but it could be necessary for a corner case that wasn't hit during the profile.

Detailed view

Double-clicking on a sample opens the detailed view, which shows the disassembly in the vicinity of that sample along with profiling data for the listed instructions. Note that these are based on global instruction data across all function calls in that frame, not just the instructions executed within a particular function.

The disassembly is based on the instructions currently in memory, and may differ from the ones actually executed during the profiling session if the code has subsequently been modified.

Tip: The rate at which a branch is taken can be estimated from the average CPU cycle count listed, since branch instructions take one additional clock cycle when the branch is taken versus not taken. For instance, a branch listed with an average CCPI (CPU cycles per instruction) value of 2.6 can be estimated to be taken about 60% of the time.

Multi-frame profiling

The profiler can also be used to collect profiling data on a per-frame basis, which aids in diagnosing performance issues that only occur in a single frame and are hard to catch manually. The boundaries of a "frame" do not have to correspond to video frames and can be defined in terms of custom triggers in the code, allowing the frames in the profile view to correspond to sections of the program's main loop instead.

To enable multi-frame profiling, a trigger must be set via the Options drop-down:

None
Disables frame triggering. When no frame triggers are used, the frame view is hidden in the profile view.
Vertical blank
Marks frame boundaries at the beginning of vertical blank (scan line 248), thus matching profiling frames to video frames.
PC trigger
Marks frame boundaries when CPU execution reaches a specific PC address. If only a start address is specified, frames will both end and begin at that address. If an end address is also specified, then frames will only cover the time from when the start address is hit to the next time either the start or end addresses is hit. Function mode may also be used instead of an end address, in which case the profiling frame ends when the stack pointer rises above the value when the start address was hit.
Note that the start and end address may be specified in terms of expressions, but the expressions are only evaluated once at the beginning of the profiling session. For instance, using dw vvblki as the start expression will capture each execution of the immediate vertical blank interrupt routine as a separate frame. The expressions are re-evaluated each time, though, so if updated code is loaded where addresses have shifted, the expressions will be re-evaluated to pick up the new values on the next profiling run.

When more than one frame is captured, a frame graph appears at the top of the profiling view. Single frames may be selected with the left mouse button, or a range of frames may be viewed together by drag-selecting over a range. The horizontal zoom may be adjusted by either the mouse wheel or the +/- keys when the graph is selected; the vertical zoom is automatically fit to a filtered average of the samples by total cycles.


Altirra Help: Debugging BASIC programs

Debugging BASIC programs

Altirra's debugger is primarily geared toward debugging machine-language programs, but does have facilities to aid in debugging BASIC programs as well. The debugging commands will work with Atari BASIC, OSS Basic XL, and OSS Basic XE.

Note

The debugger doesn't know about the status of the BASIC interpreter and therefore can read program tables that are in the process of being modified by the interpreter. This can produce garbled results. However, this also means that the debugger can read the state of the BASIC program when the interpreter has failed or is otherwise inoperable.

Displaying BASIC table locations

The .basic command dumps out the base pointer table used by BASIC:

BASIC table pointers:
LOMEM   Low memory bound      0700
VNTP    Variable name table   0800 (2 bytes)
VVTP    Variable value table  0802 (8 bytes)
STMTAB  Statement table       080A (100 bytes)
STMCUR  Current statement     082F
STARP   String/array table    086E (0 bytes)
RUNSTK  Runtime stack         086E (16 bytes)
MEMTOP  Top of used memory    087E

The most useful locations here are STMTAB, which points to the beginning of the statement table (program text), and STMCUR, which points to the currently executing statement if there is one.

Note

The values reported by the .basic command may be inconsistent if Basic XE is operating in Extend mode. In that case, the program is stored in extended memory and accessed through the $4000-7FFF window, and thus the pointer values will only be valid when that window is active.

Displaying BASIC program text

The .basic_dumpline command can be used to decode a tokenized BASIC program line. It takes one argument, the base address of the line to dump:

Altirra> .basic_dumpline 80a
10 FOR I=1 TO 100 {end $0821}

This command is useful when used with the current value of the STMCUR pointer, as it can be used to determine the currently executing line. Using an asterisk as the address, i.e. .basic_dumpline *, is a shortcut for the current line. The end pointer shown is the address after the last byte in the line and which should be the start of the next line.

Using 0 as the start address, i.e. .basic_dumpline *, dumps the first line. If no argument is used, the next line is dumped. This can also be invoked simply by pressing Enter in the debugger to re-issue the command.

The -c flag dumps the remainder of the program from the start point. Thus, .basic_dumpline -c 0 dumps the entire program.

Displaying variable tables

The .basic_vars command dumps out the BASIC variable table, composed of the variable name table (VNT) and the variable value table (VVT):

Altirra> .basic_vars
BASIC variables:
$80  I       = 29

The token, variable name, and current value are printed. The first two columns are mainly only useful when digging around in the interpreter or in the tokenized source; the value column is the more useful one when debugging a (mostly) working BASIC program.

Recovering a damaged or protected BASIC program

A common method of protecting a BASIC program involves corrupting the end of the BASIC program. This causes the interpreter to hang or crash when attempting to find the hidden immediate mode line 32768, resulting in a program that can only be run via a disk-based RUN command and can't be listed or edited. In the debugger, this can be seen when dumping the end of the BASIC program's statement table:

Altirra> .basic_dumpline 80a
10 FOR I=1 TO 100 {end $0821}
Altirra>
20 PRINT I {end $0828}
Altirra>
30 NEXT I {end $082F}
Altirra>
0

The last line at address $082F is where the immediate mode statement should be. Dumping that location reveals that it has been edited to be line 0 with no length, causing the interpreter to jam in a loop:

Altirra> db 82f
082F: 00 00 00 15 1F 0E 41 20-96 00 00 00 12 0E 00 00 |......A ........|
083F: 00 00 00 00 14 27 1F 0E-41 20 97 00 00 00 12 0E |.....'..A ......|
084F: 00 00 00 00 00 00 14 39-19 0F 0D 44 3A 50 52 4F |.......9...D:PRO|
085F: 47 52 41 4D 2E 42 41 53-16 00 80 00 06 04 16 41 |GRAM.BAS.......A|
086F: 01 00 00 00 00 40 01 00-00 00 00 80 0A 00 04 00 |.....@..........|
087F: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 |................|
088F: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 |................|
089F: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 |................|

This can be rectified by fixing the line number and resetting the length to the distance to the end of the statement table (STARP):

Altirra> e 82f 00 80 $86e-$82f
Altirra> .basic_dumpline 82f
32768 POKE 2096,0: POKE 2097,0: SAVE "D:PROGRAM.BAS" {end $0868}

Verifying the result also shows how the program was originally protected.

The .basic_rebuildvnt command will rebuild the Variable Name Table (VNT) region of a BASIC program. This table is only needed by the BASIC interpreter for parsing or LISTing, so if it is damaged, those actions can be blocked. .basic_rebuildvnt rebuilds the VNT by substituting generated variable names, using the type information in the Variable Value Table (VVT).

Saving a BASIC program

The .basic_save command will save a BASIC program from memory, even if the BASIC interpreter has failed. This is done using the same process that the BASIC interpreter itself uses when executing a SAVE. The program is saved to a path on the host, not on the emulated disk, so this can be done even without DOS. Afterward, if desired, the program can be brought back into BASIC by the H: device, a virtual disk, or direct booting from the emulator.

Note

.basic_save cannot be used on a Basic XE program in Extend mode.


Altirra Help: Symbol options

Symbol options

Default bank handling

By default, the debugger treats any address in an extended bank as separate from the main bank. This includes addresses above bank 0 in 65C816 mode, as well as PORTB and cartridge bank global addresses. This prevents bogus mapping of main bank symbols to extended banks.

When using listings from the MADS assembler, symbols in extended banks are interpreted as being in 65C816 linear memory. This can be changed by using symbol options to change the interpretation of banks.

Supported symbol options

Symbols can be defined with special names to influence how the debugger interprets labels emitted by the assembler/linker. Only the name of the symbol is used; the value may be any address, to allow them to be placed out of the way for programs that might interpret them as regular symbols. The address $FFFF is recommended for this purpose. The debugger will remove any symbol that it recognizes as an option.

Symbol options only apply to the file they are declared in. The order of the symbol options and the symbols they affect does not matter.

Note: The names of all symbol options begin with two underscores.

__ATBANK_xx_RAM
Interpret symbols in bank $xx as being in RAM. This is used for cartridges that can be disabled, exposing the main RAM underneath.
__ATBANK_xx_CART
Interpret symbols in bank $xx within $A000-BFFF as being in cartridge space, instead of PORTB space.
__ATBANK_00_GLOBAL
Interpret all symbols in bank 0 as global symbols, mapping to that address in any bank.


Altirra Help: Options

Options

Altirra has a number of options that can be used to customize the emulated Atari hardware.

Contents
  • 1. View
    • 1.1. Filter Mode
    • 1.2. Stretch Mode
    • 1.3. Overscan Mode
    • 1.4. Vertical Sync
    • 1.5. Show FPS

View

Filter Mode

Selects the interpolation mode for the display. Point gives the sharpest display, but can give artifacts depending on the scaling factor. Bilinear generates a smoother display. Bicubic generates an even higher quality display, but can be slow on some video cards.

Filter modes are not available unless a Direct3D 9 video card is available; if Altirra is running in DirectDraw or GDI mode, filtering is controlled by the OS or video card driver.

Stretch Mode

Controls how the display is stretched to fit the screen. Fit to Window just stretches, regardless of how it distorts; Preserve Aspect Ratio attempts to match the aspect ratio that would be seen on a properly adjusted TV set. Square Pixels always assumes square pixels instead, which is mostly the same for PAL, but noticeably different for NTSC. Integral Square Pixels is similar, but also ensures that the scaling factor is always an integer, which prevents distortion in Point filter mode.

Overscan Mode

Selects how much of the display is visible. Normal only shows the maximum visible playfield region; Extended shows additional border regions that are visible on some TV sets. Full expands the display to cover the entire video signal, even horizontal and vertical blank regions.

The Extended PAL Height option enables viewing of additional scan lines above and below the normal picture area. This is only useful when running software that explicitly uses a special trick to display more than the 240 scan lines normally allowed by ANTIC.

Vertical Sync

Lock the display to the vertical sync period, thus reducing the amount of tearing seen on screen.

Show FPS

Display the current emulator performance in frames per second in the window caption. This can introduce occasional glitching into the video output.


Altirra Help: Options: Acceleration

Options: Acceleration

Acceleration options allow the emulator to intercept and speed up processes that would normally occur at representative real-time speeds within the emulation, such as disk and cassette loads. This can greatly reduce load times, such as reducing a 10 minute cassette tape load to a few seconds.

Contents
  • 1. OS acceleration options
    • 1.1. Fast boot
    • 1.2. Fast FP math
  • 2. SIO device acceleration options
    • 2.1. C: patch
    • 2.2. D: patch
    • 2.3. PRT: patch
    • 2.4. SIO override detection
    • 2.5. SIO burst transfers
    • 2.6. SIO acceleration mode

OS acceleration options

Fast boot

Accelerates the startup delay, ROM checksum, and type 3 polling done by the XL/XE OS on startup for faster boot. This typically cuts a couple of seconds off of cold boot.

Fast FP math

Intercepts known entry points in the floating point library with native math routines. This accelerates Atari decimal math to a large degree, particularly speeding up Atari BASIC.

The emulated FP library is higher accuracy than the real library due to better rounding and use of native math routines, so it will produce slightly different results.

SIO device acceleration options

SIO acceleration allows the emulator to run input/output (I/O) requests faster than real time, so loading takes place almost instantaneously. This is done through an SIO patch, which patches the I/O path to use a direct call into the emulator instead of sending requests over the SIO bus as would happen on real hardware.

Note that while the patches are referred to in terms of CIO devices (D:) for recognizability, the acceleration happens at the SIO level and does not require CIO to be used.

C: patch

Enables acceleration of cassette I/O (C:).

D: patch

Enables acceleration of disk drive I/O (D:). In addition to the standard SIOV OS vector, this also accelerates the DSKINV vector.

PRT: patch

Enables acceleration of other device I/O, including the printer (P:) and serial ports (R: and T:).

SIO override detection

Enables detection for custom OS or PBI device firmware that override disk drive accesses. Traditional SIO patching inserts a shim between the program or DOS requesting the I/O and the operating system. This causes problems with the OS or a PBI device tries to create a virtual disk, such as a disk image partition on an IDE drive; the emulator intercepts the request and forwards it to the disk drive, bypassing the virtual disk.

When the SIO override detection option is enabled, the emulator watches the first request to a disk after boot to see if the disk drive is accessed or if the request is intercepted. Acceleration is enabled for a drive only if the disk drive is activated. This resolves most virtual disk issues, with the caveat that if a virtual drive mapping is changed, a warm reset is required to redetect the new virtual drive or re-enable acceleration for a drive number.

This option is generally only necessary for SIOV-based acceleration. PBI-based acceleration allows the OS and other PBI devices to hook ahead of the emulator patch.

Activating this option will disable the nonexistent disk acceleration of the Fast Boot option.

SIO burst transfers

Enables faster transfer of data across the emulated SIO bus, by shortening the delay between bytes. This speeds up transfers that cannot be intercepted by the regular SIO patch mechanisms, such as a custom disk loader. The speed depends on how fast the loader is; the faster the loader can respond, the faster the burst transfer. Burst transfers can be enabled independently for disk and non-disk devices.

Most disk routines are compatible with burst transfers, but a few may glitch at the high speed or detect the fast transfer as an attempt to bypass copy protection. Burst transfers should be disabled in these cases.

Older versions of Altirra had separate modes for standard and polled transfer modes, with the polled mode particularly needed for SpartaDOS X. This is no longer necessary, as the burst transfer modes have been merged into a universal method.

SIO acceleration mode

Allows switching between SIOV, PBI, and dual patching modes. The SIOV patching mode is the one used by most emulators and intercepts requests to the OS SIO routines through the SIOV vector. It generally works with most DOSes and custom bootloaders that use the OS routines to read the disk.

The PBI patching mode uses a Parallel Bus Interface (PBI) device to intercept requests. This is slightly slower and only works on XL/XE hardware with an XL/XE OS. However, it resolves some conflict issues with virtual disk functionality in custom OSes and PBI devices, by hooking as a low-priority PBI device. It also allows easier acceleration with recent versions of SpartaDOS X that do not use SIOV by default but do have support for PBI disks.

The SIOV hook has priority over the PBI hook; the PBI hook only activates if the SIOV hook is unable to intercept the request.


Altirra Help: Options: Hardware

Options: Hardware

The hardware options set the base computer or console that is being emulated.

Contents
  • 1. Hardware options
    • 1.1. 400/800
    • 1.2. 1200XL
    • 1.3. 800XL, 130XE, and XEGS
    • 1.4. 5200
  • 2. Memory size options
    • 2.1. 16K
    • 2.2. 48K
    • 2.3. 52K
    • 2.4. 64K (XL)
    • 2.5. 128K (130XE)
    • 2.6. 256K
    • 2.7. 320K
    • 2.8. 576K
    • 2.9. 1088K
    • 2.10. 8K/24K/32K/40K
    • 2.11. 52K
  • 3. Video options
    • 3.1. NTSC
    • 3.2. PAL
    • 3.3. SECAM
    • 3.4. PAL-60 and NTSC-50
    • 3.5. Frame blending
    • 3.6. Interlace
    • 3.7. VBXE
    • 3.8. Use VBXE memory for expansion
    • 3.9. Use $D7xx for VBXE registers
    • 3.10. NTSC artifacting (standard)
    • 3.11. NTSC artifacting (high)
    • 3.12. PAL artifacting
    • 3.13. Enhanced text output (hardware intercept)
  • 4. CPU options
    • 4.1. Record instruction history
    • 4.2. Track code paths
    • 4.3. Stop on BRK instruction
    • 4.4. Enable illegal instructions
    • 4.5. CPU model > 6502C
    • 4.6. CPU model > 65C02
    • 4.7. CPU model > 65C816
  • 5. Audio options
    • 5.1. Stereo
    • 5.2. Non-linear mixing

Hardware options

400/800

Enables the base Atari 800 hardware. The 400/800 are similar, with the main difference being that the 400 shipped with 16K of RAM while the 800 varied from 8K to 48K.

1200XL

Selects the Atari 1200XL hardware configuration. This enables a number of hardware features including 64K RAM, an extra 4K of OS ROM, F1-F4 function keys, cartridge detection, and the Parallel Bus Interface (PBI). It also switches the firmware configuration to 1200XL mode, since the 1200XL OS has some quirks and somewhat poor compatbility.

800XL, 130XE, and XEGS

Selects the XL/XE/XEGS hardware configuration. These are all very similar except for minor differences, and firmware images are shown in common for these hardware types. Software compatibility differences between these modes is very rare, although there are a couple of programs that have managed to break only one of them, due to odd bugs.

The actual physical 130XE differs in a couple of ways. One is 128K of RAM with separate CPU/ANTIC switching, which is selected in the emulator under the memory configuration. Another is the presence of a floating data bus, which is enabled by the emulator in 130XE mode. The third difference is the presence of the Enhanced Cartridge Interface (ECI) port instead of the Parallel Bus Interface (PBI) port. Most PBI devices can be made to work with ECI using an adapter, and this difference is not emulated; all emulated devices are simply both PBI/ECI compatible.

The XEGS is very similar to a 64K 800, the main difference being the detachable keyboard and a switchable Missile Command ROM. Enabling XEGS mode in the emulator enables the detachable keyboard setting and also enables the game ROM firmware slot. The XEGS OS (rev.4) has minor changes to support keyboardless operation; the emulator will try to default to the ver.4 OS first and then fall back to an XL/XE OS otherwise.

5200

Enables Atari 5200 SuperSystem hardware. The 5200 has the same CPU and core hardware as the computer lines but very different peripherals, so the emulator ships by default with a separate profile for the 5200. This keeps cartridges and controller setups separate between the computer and console modes.

PAL is not available in 5200 mode; all released 5200 consoles were NTSC only.

Memory size options

16K

Standard Atari 400/600XL/5200 configuration with RAM from 0000-3FFF. The range from C000-CFFF is unconnected.

48K

Standard Atari 800 configuration with RAM from 0000-BFFF. The range from C000-CFFF is unconnected.

52K

Extended Atari 800 configuration with RAM from 0000-CFFF.

64K (XL)

Standard Atari 800XL configuration with RAM from 0000-FFFF, with the upper 14K swappable with Kernel ROM via PORTB.

128K (130XE)

Atari 130XE configuration with main ROM from 0000-FFFF and external RAM bank switched via a window at 4000-7FFF. This mode supports ANTIC bank switching.

256K

64K of base memory plus 192K of extended memory (12 banks). This is the memory configuration used by the ICD RAMBO XL. It is a bit unusual because 4 of the 16 addressable banks (64K of 256K) are aliases of the main 64K of RAM, and therefore some programs can malfunction if they do not detect this properly. This mode is most useful for verifying programs that use extended memory.

320K

Extended Atari 800/800XL configuration with an additional 16 banks of memory. This mode does not support separate ANTIC enable.

576K

Extended Atari 800/800XL configuration with an additional 32 banks of memory. This mode does not support either separate ANTIC enable or enabling BASIC while extended memory is enabled.

1088K

Extended Atari 800/800XL configuration with an additional 64 banks of memory. In addition to disabling ANTIC banking and BASIC, this mode also takes an additional bit in PORTB.

8K/24K/32K/40K

These are unusual Atari 800 RAM configurations resulting from various combinations of pluggable 8K and 16K RAM cartridges, each inserted into one of the three RAM slots in the computer. The 8K configuration is particularly rare but reported to have been seen in Europe; it can run BASIC and some cartridges, but is too small to boot DOS.

52K

48K 800 configuration with an extra 4K of RAM added at $C000-CFFF.

Video options

NTSC

Selects NTSC ANTIC/GTIA operation, including aspect ratio and a 60Hz refresh rate. This is the configuration used by the original 8-bit Ataris and is recommended for most commercial software.

PAL

Selects PAL ANTIC/GTIA operation, including aspect ratio and a 50Hz refresh rate. This is recommended for demos since many are written in Europe.

SECAM

Selects PAL ANTIC operation with a SECAM GTIA (FGTIA), including aspect ratio and a 50Hz refresh rate. This is used for verifying compatibility against SECAM systems, which have a couple of quirks in GTIA mode 9 rendering and in cartridge sensing.

PAL-60 and NTSC-50

Mixed configurations resulting from putting a PAL ANTIC in an NTSC computer or an NTSC ANTIC in a PAL computer, resulting in a computer that produces a video signal for one standard with the frame rate of the other. The PAL-60 configuration is sometimes used for better compatibility with software written for PAL while still being able to run with NTSC displays, since modern displays can sometimes display these mixed modes due to their use in converted video tapes or DVDs.

There is a software difference from a standard PAL or NTSC computer since the frame timing from ANTIC differs from the video type reported by GTIA, both of which are detectable in software. The standard XL/XE OS will have some issues running in these modes unless patched, particularly with cassette tape operations.

Frame blending

Averages adjacent frames together to reduce flickering with programs that use alternating frames to multiplex sprites or increase apparent frame buffer depth.

Interlace

Enables display of interlaced fields (480 scan lines) instead of the standard 240 scan lines. This is only useful when running software that supports interlaced display.

VBXE

Enables Video Board XE (VBXE) emulation. This is an internally installed module with 512K of VRAM and a custom FPGA core that greatly expands the video capabilities of the 8-bit Atari. The FX 1.20 core is emulated.

Use VBXE memory for expansion

When VBXE emulation is also enabled, this option causes extended memory mapped through the PIA (most expansion modes) to overlap memory used by VBXE. This matches the extended memory emulation done by the FX1.20r core. If disabled, the memories are separate as with the FX1.20a core.

Use $D7xx for VBXE registers

Locates the VBXE registers at $D7xx instead of $D6xx. This is sometimes done on actual hardware to avoid conflicts with Parallel Bus Interface (PBI) devices.

NTSC artifacting (standard)

Enables emulation of false colors from alternating high resolution pixels with NTSC video encoding. This is necessary to see colors in some games that use artifacting, such as Choplifter and Pitstop II.

NTSC artifacting (high)

Emulates NTSC artifacts by simulating the NTSC decoding process. This produces artifacts that are more realistic. However, the picture is also blurrier due to NTSC limitations.

PAL artifacting

Enables emulation of false colors from chroma blending in the delay line of a PAL video decoder. This gives more accurate color output in programs that alternate color and grayscale lines to increase the effective color depth.

Enhanced text output (hardware intercept)

Replaces the standard emulated video display with a text screen using native Windows fonts. This disables emulation of most Atari video features and only supports basic text modes, but produces a higher quality text display.

CPU options

The first three options should not be used unless you are debugging code in the interactive debugger, as they will slow down execution or cause debug breaks in code that may be executing normally.

Record instruction history

Causes the CPU to record a trace record for every instruction executed.

Track code paths

Enables tracking of all memory where instructions are executed and which instructions are branch or call targets.

Stop on BRK instruction

Causes the debugger to stop when a breakpoint (BRK) instruction is executed. This often indicates that the Atari has crashed.

Enable illegal instructions

Allows execution of undocumented NMOS 6502 instructions, which are used by some programs. This only works in 6502C mode.

CPU model > 6502C

Selects the base NMOS 6502C as the CPU model. This is the CPU type used by all official Atari models.

CPU model > 65C02

Selects the CMOS 65C02 as the CPU model, which contains some extra instructions. This is occasionally used as a homebrew upgrade option. It may introduce some compatibility problems with games.

CPU model > 65C816

Selects the 16-bit 65C816 as the CPU model, which contains many more instructions and a new 16-bit native mode. This is used in some homebrew and commercial accelerators to both increase speed and memory address space.

Audio options

Stereo

Enables a second POKEY. Addresses in the D2xx range with bit 4 clear address the left channel, and addresses with bit 4 set address the right channel. The IRQ and serial facilities of the second POKEY are active but unconnected.

Non-linear mixing

Emulates the effective mixing curve of the hardware, which causes the output of two voices to be lower than the sum of their volume levels. This is needed for some effects and also causes some compression of the output.


Altirra Help: Options: Firmware

Options: Firmware

The firmware options allow selection of the software built into the machine, including the Operating System and BASIC interpreter. The name "firmware" refers to the fact that the software is stored in ROM chips, which cannot be modified.

Changing the firmware used by the emulated computer will cause the computer to reboot.

Contents
  • 1. Operating System
    • 1.1. Autoselect
    • 1.2. Atari 400/800 OS-B
    • 1.3. Atari XL/XE OS ver. 2
    • 1.4. AltirraOS for 400/800 and for XL/XE/XEGS
    • 1.5. Atari 5200 OS
    • 1.6. AltirraOS for 5200
  • 2. BASIC
    • 2.1. Atari BASIC rev. A, B, and C
    • 2.2. Altirra BASIC
  • 3. Firmware options
    • 3.1. Internal BASIC (Hold Option on Boot)

Operating System

The Operating System submenu lists all OS firmware available for use with the emulator, including both built-in firmware and any additional firmware image files hooked up to the emulator through the Firmware Images dialog. Besides the built-in and recognized firmware images, you can also add other images with customized names, and those names will show up in this submenu for selection.

Autoselect

Chooses an external firmware image appropriate for the hardware if one is available, otherwise uses an internal firmware.

Atari 400/800 OS-B

The most common operating system for 400/800 computers, and the best for compatibility in that mode. This entry will show up if you have added the OS-B firmware to the firmware list. ATARIOSB.ROM will show up as a patched version of this ROM.

Atari XL/XE OS ver. 2

The most common operating system for XL/XE computers, and the best for compatibility in that mode. You will have this entry if you have added the XL/XE OS firmware to the firmware list. ATARIXL.ROM will show up as this entry. Version 4, the XEGS ROM, is a good alternative for this ROM as it has practically the same compatbiility and functionality.

AltirraOS for 400/800 and for XL/XE/XEGS

These are internal OS ROM replacements for the 400/800 and XL/XE/XEGS computers, respectively, written in pure 6502 code. These are used by default if no other firmware is available. They are functionally compatible with the standard Atari OSes and have a good compatibility rate, but you may run into software with issues due to depending on OS ROM internals.

Atari 5200 OS

Selects the Atari 5200 SuperSystem firmware. You will have this entry if ATARI5200.ROM has been hooked up. The 4-port and 2-port versions are similar but not the same; the 4-port firmware is more common and more compatible.

AltirraOS for 5200

Selects the internal replacement for the 5200 firmware; you can run most 5200 games with this firmware.

BASIC

The BASIC submenu selects the BASIC interpreter used with the machine. With 400/800 computers, BASIC must be supplied via an external cartridge, and so this option selects the image used by the emulator when the BASIC cartridge is attached from the special cartridge menu. With XL/XE/XEGS computers, the BASIC interpreter ROM is included in the computer itself and switchable under software control, and is selected by this menu.

Atari BASIC rev. A, B, and C

Selects one of the three revisions of Atari BASIC. You will have this entry if ATARIBAS.ROM is hooked up. Most software will work with any version, but in general Revision A is buggy, revision B is more buggy, and revision C is less buggy.

Altirra BASIC

This is the built-in BASIC interpreter. It is source- and binary-compatible with Atari BASIC, has additional commands, and generally runs faster. See the Altirra BASIC Reference Manual for details. Some software may not work with Altirra BASIC due to dependencies on Atari BASIC internals, most notably disks that have special BASIC boot loaders (that are sometimes not so well written).

Firmware options

Internal BASIC (Hold Option on Boot)

On XL/XE/XEGS computers, BASIC is built into the computer and is enabled by default. The Option key must be held down on boot to disable BASIC. A lot of software requires BASIC to be disabled, and holding down Option quickly becomes annoying on both emulation and the real hardware. This option determines whether the emulator itself will automatically hold down Option on boot to disable BASIC, saving this hassle. The emulator's display will flash Option for a short time to show when it is holding down the Option key.

In some rare cases, the emulator may hold down Option too long or in a case where it has different meaning. This is most often the case with cartridges with special menus or add-on hardware with boot menus. In that case, the Internal BASIC option may need to be enabled to prevent the emulator from doing so, even though it won't really enable Internal BASIC.


Altirra Help: Options: System

Options: System

Altirra has a number of options that can be used to customize the emulated Atari hardware.

Contents
  • 1. Disk options
    • 1.1. Accurate sector timing
    • 1.2. Show sector counter
  • 2. Cassette options
    • 2.1. Auto-boot on startup
    • 2.2. Load data as audio
  • 3. Misc options
    • 3.1. Warp speed
    • 3.2. Pause when inactive

Disk options

Accurate sector timing

Causes the emulator to simulate rotational delays caused by sector position and seek delays (step and head settling time). This results in a slower disk load, but is sometimes necessary to load protected disks.

When disabled or when the SIO patch is enabled, Altirra still attempts to track disk rotation for protected disks. This means that CPU timing is not preserved, but rotational position is still noted.

Show sector counter

Displays the current sector being read or written instead of the unit number in the on-screen indicator.

Cassette options

Auto-boot on startup

Automatically holds down START during system startup and then hits a key to initiate a cassette tape load. This only works with cassette tapes that have a machine language program; BASIC tapes must be loaded via CLOAD at the BASIC prompt instead.

Load data as audio

Converts the data from a .CAS tape image into raw audio data so it plays through the speaker.

Misc options

Warp speed

Disables the speed limiter and audio sync, and runs the emulator as fast as possible.

Pause when inactive

When set, causes Altirra to pause emulation when its window goes inactive. Use this to prevent the emulated Atari from using CPU time when in the background.


Altirra Help: Custom effects

Custom effects
Contents
  • 1. Using a custom effect
  • 2. Basic structure
  • 3. Global declarations
  • 4. Pass declarations
  • 5. Pass execution
    • 5.1. Input geometry
    • 5.2. Shaders
    • 5.3. Rasterization and output merging
    • 5.4. Uniform inputs
    • 5.5. Custom texture inputs
    • 5.6. Texture settings
  • 6. Interaction with host UI
  • 7. Precompiling shaders for distribution
  • 8. Performance optimization
  • 9. Sample effect
    • 9.1. Invert
      • 9.1.1. invert.cgp
      • 9.1.2. invert.fx

The custom effect file formats are modeled after the .cgp format used by the RetroArch emulator. Some adjustments have been made to account for differences between the emulators and between the Cg and HLSL shader languages, so custom effects will sometimes require adjustment to run on Altirra.

Using a custom effect

A custom effect is configured in Options under Display Effects, Custom Effect path. This points to a .cgp file that describes the shader stages of the effect to render during display composition.

Currently, the display mode must be set to Direct3D 9 in Display options for custom effects to work. They will not function if Direct3D 11 or another mode is in use.

Basic structure

A custom effect pipeline is composed of one or more passes, each of which runs a pair of vertex and pixel shaders to produce a new image. Each pass can refer to the output of any previous stage, not just the source. Passes can also read from past frames on inputs, allowing for multi-frame filters.

The effect pipeline is described in a primary .cgp file which enumerates the passes and their settings, and also references auxiliary shader source and texture image files. The cgp format is a simple text file containing a property list of the form name = value. Double-quotes around the value are optional unless the value contains spaces. The order of properties in the file does not matter.

Comments start with a hash mark (#). They can begin mid-line, in which case everything to the end of the line is treated as a comment.

For properties that have boolean values, the value may either be 0 or false for a false value, and 1 or true for a true value.

Global declarations

shader_profile_d3d9
Sets the shader profile used for compiling shaders for Direct3D 9. Valid values are 2_0, 2_a, 2_b, and 3_0. These correspond to shader model 2.0, 2.a, 2.b, and 3.0, with 2.0 being the most compatible and 3.0 being the most powerful/flexible.
Shader model 2.a/2.b and 3.0 hardware can run 2.0 shaders, and 3.0 hardware can run all profiles. If the hardware does not support the specified profile, the whole custom effect will fail to load.
The default is 2_0, as it is supported by all shader model 2 hardware or newer.
shader_precompile
If true, always compile the shaders from source and write the precompiled shaders to disk with the suffix -d3d9.vsh or -d3d9-psh. This is the same binary format produced by the fxc tool in the DirectX or Windows SDK.
The default is false, which means to use precompiled shaders if they exist or otherwise attempt to compile from source, but never to write precompiled shaders.
shader_show_stats
Displays stats on screen for the output surface format and GPU timings for each pass if available. This is useful to tell which passes are the most expensive. The default is false.
textures
Semicolon-delimited list of names of textures to load: abc;def specifies that abc and def are texture names. Each of these is then expected to be declared with the name of the file to load as the texture image, e.g. abc = image.png.
Texture source images may either be in PNG or TARGA format. For PNG files, only opaque images are supported. For TARGA, the alpha channel is optional but supported.
texture_linear
If true, use bilinear filtering for texture. For instance, background_linear sets the filtering mode for texture background. If false, use nearest (point) sampling. If omitted, the default is true (bilinear).

Pass declarations

shaderN
Declares pass N and sets the name of the shader source file for that pass. Passes are numbered from 0 upward and must be numbered sequentially.
The shader file is in HLSL format for Direct3D 9. Conventionally, it has the .fx file extension, though this is not required.
shader_profile_d3d9_N
Same as shader_profile_d3d9, but overriding it only for pass N. If omitted, the global setting is used.
filter_linearN
If true, use bilinear filtering for the default input of pass N. If false, use nearest (point) sampling. If omitted, the default is true (bilinear).
scale_typeN, scale_type_xN, scale_type_N
Sets the output size type for pass N. source uses the size of the default source input for the pass (not necessarily the original source to the whole effect). viewport uses the size of the final output that the effect renders to. absolute just uses a reference size of 1x1 so that the scale factor is the output size in pixels.
If not specified, the default is source, with one exception for the final pass. If the final pass has no scale type specified, it renders at source size but the output is routed through the default scaler to viewport size. If the final pass does specify a scale type, the default scaler is bypassed.
scaleN, scale_xN, scale_yN
Sets the output scaling factors for pass N for both axes, only the x-axis, or only the y-axis. Required if a scale type is specified. For instance, scale_x2 = 2.5 makes the output width of pass 2 2.5 times the size of the scaling reference size (source/viewport/absolute, depending on scaling type).
frame_count_modN
Specifies a modulus value for pass N's frame counter, such that the frame count repeats from 0 before reaching the specified value. For instance, a frame_count_mod of 3 causes the frame count to cycle 0, 1, and 2.
If frame_count_mod is omitted, the frame counter counts up indefinitely from 0.
float_framebufferN
If true, specifies that the output surface for pass N should use 32-bit floating point format for each channel instead of 8-bit fixed point. This gives much better precision and range, but is significantly slower and should be used sparingly.
If the device does not support full floating point textures, half-float textures are used if available.
halffloat_framebufferN
If true and float_framebuffer is also set, specifies that the output surface for pass N should use 16-bit per channel half-float format instead of full 32-bit float per channel. This is generally faster than full float while still better in precision and range than fixed point.
If the device does not support half-float point textures, full-float textures are used if available.

Pass execution

Input geometry

The input geometry for each pass is a single quad aligned to the output surface, with two UV attribute sets. The first UV set (TEXCOORD0) contains texture coordinates for the pass input texture, while the second UV set (TEXCOORD1) contains normalized 0-1 coordinates for the quad and is useful for calculating other texture coordinates or quantities. The two will be different if the source image is in a sub-rect of the texture.

On Direct3D 9, the quad vertices are automatically adjusted to account for the half pixel offset, so the UVs should not be adjusted. For a 512x512 image in a 512x512 texture, the UVs should span 0-1 exactly.

Shaders

The .fx file referenced by the shaderN property is expected to contain two shader programs, a vertex shader called main_vertex and a fragment (pixel) shader called main_fragment. These are used together in the graphics pipeline to draw the pass output.

The vertex shader main_vertex can accept input attributes with POSITION, TEXCOORD/TEXCOORD0, and TEXCOORD1 semantics. It can output any attributes needed by the fragment shader and allowed by the shader profile.

By default, the vertex and fragment shader are compiled as vs_2_0 and ps_2_0 for best compatibility. This may be limiting for more complex shaders. The shader_profile_d3d9 property can be used to raise the profile.

Rasterization and output merging

Backface culling is disabled, so triangles may be output in either winding order. Viewport and scissor are also enabled so that oversize triangles may be used to cover the viewport. Passes are expected to render over the entire output surface each frame.

Blending is disabled on the output surface, so the output of the fragment shader is written without modification. Destination alpha is enabled.

In the final pass, the output surface may be 16-bit (555) if the final pass is writing directly to the output framebuffer and 16-bit output has been forced in host options. The default is 32-bit output, however.

Uniform inputs

The original .cgp specification assumed a Cg-based implementation, so some adjustments have been made for HLSL support. In particular, HLSL on Direct3D 9 does not support texture objects within structures, so they have been broken apart as separate _texture suffixed parameters.

Texture/sampler 0 is also special-cased as the IN parameter, the pass input. This is the output from the previous pass, or the original source for pass 0. Because it is always at slot 0, it should be declared explicitly so that this slot is not used for any other textures:

extern sampler IN_texture : register(s0);

All other textures can be referred to by name, with slots auto-allocated by the shader compiler. Each input FOO has a sampler parameter named FOO_texture and a struct parameter named FOO. Both of these are declared as uniform parameters to the shader and not as a global parameters.

IN
The pass input, either the output of the previous pass or the source texture for pass 0.
ORIG
The input to pass 0, the original source.
PASSN
The pass input for pass N. For instance, PASS2 gives the input to pass 2, or the third pass. N must be be less than the current pass number.
PASS0 is not allowed for the original source; use ORIG for that. Similarly, PASSN is not allowed for the current pass input, which must use IN (register s0) instead.
PREV, PREVM
The pass input from the previous frame, or from M+1 frames back. That is, PREV is the previous frame version of IN, while PREV1 is one frame behind PREV. Up to PREV6 is supported.
Note that the number on PREVM is a frame offset number, not a pass number. Previous frames can only be referenced on the immediate pass input.

The struct parameter has the following fields:

float2 video_size;
The size of the valid image within the texture. For a 320x200 image within a 512x256 texture, video_size is (320, 200).
float2 texture_size;
The size of texture in texels. For a 320x200 image within a 512x256 texture, video_size is (512, 256).
float frame_count;
The frame number represented by this input, starting with 0. For a PREV frame, this frame number is appopriately lower from IN.
float frame_direction;
Always 1, since custom effects are always run forward in Altirra.

The matrix shader parameter modelViewProj provides the model-view-projection transform to convert from the input vertex positions to output clip coordinates. The input position should be transformed by this matrix in the vertex shader.

Note: For compatibility reasons, modelViewProj is expected to be multiplied on the left of the input position vector, i.e. mul(modelViewProj, position).

Custom texture inputs

Any custom textures declared with textures appear as global uniform parameters with the same name.

Texture settings

All textures, including the texture texture and pass output textures, use top-down orientation for texture coordinates with (0,0) as the top left.

Non-power-of-two textures may be stored in power-of-two sized textures due to hardware limitations or for speed. When available, BORDER addressing is used to ensure that texture sampling outside of the valid image area of the texture returns transparent black.

Interaction with host UI

Custom effects are expected to render source contents in approximately the same location as the standard rendering path. They may still render with arbitrary transform, but this may cause a visual disconnect between the host UI and the rendered image. The UI depends upon the position of elements in the rendered image for the following features:

  • Text selection
  • Light pen/gun position tracking
  • The inspection tool, triggered by Ctrl+Alt+click
  • Debugger tools showing the current beam position

These features will all still work regardless of custom effect rendering, but the image produced by the custom effect may not line up with the position shown or used by the UI. For instance, if the custom effect scales down the image vertically, the text selection highlight may be drawn larger than the location of the text in the final image.

The scaling and aspect ratio options, however, do not need to be handled by custom effects. These settings are already taken into account in the viewport size rendered by the custom effect, and any letterboxing performed to maintain scaling/aspect is outside of the viewport. The image processing performed in the custom effect does not affect the border color.

Precompiling shaders for distribution

Compiling shaders from source requires D3DCompiler_47.dll. This DLL is always available as a pre-installed system DLL on Windows 8.1 and Windows 10, but for earlier versions of the OS requires a redistributable that is not shipped with Altirra. For this reason, custom shaders can be precompiled to binary form with shader_precompile = true to work on all systems. The .cgp should be loaded once with this setting to create or refresh the .vsh and .psh files, then the setting turned back off for distribution.

There is no difference in performance between source-compiled and precompiled shaders, besides a very small amount of time to compile from source on load in the former case.

Performance optimization

The speed of the vertex shader is a non-issue due to the very low vertex count involved, but the performance of the fragment shader can become a serious bottleneck with complex or multi-pass effects are involved. The shader_show_stats property can be used to display GPU pass timings to identify the most expensive passes that are in need of optimization. All passes of the custom effect must run in under 16ms to maintain 60fps and needs to run considerably faster than that to avoid adversely affecting vsync lock. Running within 8ms or lower is highly recommended.

If the final pass is rendering at 1:1 pixels at viewport resolution, make sure it has no scales defined so that the built-in blit is skipped.

Structure passes so that as much processing as possible is done at the smallest size. Color processing is often commutative with scaling, so do the color processing at source resolution and then upscale.

Floating-point format surfaces are more expensive due to the memory bandwidth involved. When possible, use the default fixed point surface format (32-bit RGBA), or use half-float if full float precision is not needed.

Hoisting non-trivial math from the fragment shader to the vertex shader can substantially speed up the fragment shader, as long as the linear attribute interpolation is acceptable for the hoisted quantity.

Some GPUs benefit from use of the half type in fragment shaders. Intel Skylake architecture GPUs, for instance, can run double-speed math with half precision.

Sample effect

Invert

This effect inverts all colors in the image, producing a negative where black becomes white and vice versa.

invert.cgp

shader0 = "invert.fx"
filter_linear0 = "true"
scale_type_x0 = "viewport"
scale_x0 = "1"
scale_type_y0 = "viewport"
scale_y0 = "1.000000"

invert.fx

extern sampler src : register(s0);

void main_vertex(
    float4 pos : POSITION,
    float2 uv : TEXCOORD0,
    uniform float4x4 modelViewProj,
    out float4 oPos : POSITION,
    out float2 oT0 : TEXCOORD0)
{
    oPos = mul(modelViewProj, pos);
    oT0 = uv;
}

half4 main_fragment(float2 uv : TEXCOORD0) : COLOR0 {
    half4 c = tex2D(src, uv);
    c.rgb = 1.0h - c.rgb;

    return c;
}


Altirra Help: General issues

General issues
Contents
  • 1. PAL vs. NTSC
  • 2. Memory size

PAL vs. NTSC

The 8-bit Atari was originally created for the NTSC standard and later adapted for PAL. As such, much of commercial software was designed for NTSC. Productivity software will usually work fine on either setting, but many games will only run at their correct speed on NTSC hardware settings. These games will run correctly on PAL, but 16% too slowly.

Later software is the opposite, as much of the later and more advanced programs and games were developed in Europe on PAL hardware. Almost all demos are designed for PAL, as are the very late games that take advantage of expanded memory and push the hardware to its limits. These will run too fast or malfunction if run with NTSC timing.

Note that while almost all NTSC-designed software will run in PAL, some PAL software may malfunction in NTSC. This is usually due to the shorter vertical blank period. Screen corruption at the top of the frame is one common symptom, if the program doesn't lock up entirely.

Memory size

The typical choices here are 48K, 64K, and 320K. Very little software requires any other setting than these three.

48K was the maximum memory configuration for the original Atari 800 and is only possible in that hardware mode, as XL computers came with 64K minimum. In Altirra, selecting 48K is synonymous with switching to the 800 hardware configuration. This is generally only necessary when dealing with very old software that carelessly changes the PORTB register and accidentally swaps out the kernel ROM.

64K was the standard configuration for 800XL and 1200XL models. This memory size will support most later commercial software.

The 320K memory configuration encompasses most of the common expanded memory configurations, including the 128K of the 130XE as well as several third-party 256K expansions. This setting will run the majority of software making use of expanded memory.


Altirra Help: Program-specific issues

Program-specific issues

Some programs have bugs or are otherwise not well behaved and will malfunction in mysterious ways. This can be due to incompatibility with specific hardware or firmware configurations, timing sensitivities that manifest more or less often on emulators, or just plain bizarre bugs. This is a list of programs with known issues and under what conditions they break.

Note that there are often several versions of programs in the wild, which can display different behavior. In particular, versions that have been "frozen" and stored in a turbo-load configuration or mega-disk can act differently than the originals. Unless specified otherwise, the compatibility entries here are for the original versions.

Compatibility list

Category | Name | Issues
Applications | Basic XE 4.2 | Extension disk compatibility issue
 | FoReM XL/XE/XE Pro | Hardware compatibility issue Minor program bug
 | Ice-T XE 2.72 | Major program bug
 | MicroCalc (cartridge) | Requires XL/XE OS
 | Text Wizard V1.3 | Requires OS-B
 | The Newsroom | Requires writable disk
Demos | 1st Top Demo 1989 | Requires XL/XE OS
 | Cup of Tea | Minor program bug Requires XL/XE OS
 | Joyride | Minor program bug
 | Total Daze | Minor program bug
 | Vantage Point | Major program bug
Disk Operating Systems | Atari DOS 2.5 | OS hackery
 | DOS II+/D Version 6.4 | Requires XL/XE OS
 | MyDOS | 65C816 incompatibility
 | SpartaDOS 3.2 | Emulation feature issue
 | SpartaDOS X | Emulation feature issue
Games | 63-on-1 cartridge | Requires XL/XE OS
 | Adebar | Requires XL/XE OS
 | Alien Garden | GTIA incompatibility
 | Assault Force 3D | Requires special hardware
 | Astro Chase | CIO device compatibility issue
 | Berzerk | Minor program bug
 | Bounty Bob Strikes Back! | Hardware compatibility issue Expansion hardware incompatibility
 | Bubble Bobble (demo) | Major program bug
 | Castle Wolfenstein | Requires writable disk
 | Choplifter (cartridge) | OS hackery
 | Cohen's Towers | Requires OS-B or 48K/64K memory
 | Crownland | Uses illegal instructions
 | DARG | Acceleration issue
 | E.T. Phone Home! (cartridge) | Major program bug
 | Formula 1 Racing | Requires Atari BASIC
 | Fort Apocalypse | Requires disk SIO patch off (timing)
 | G.I.L.P. | Uses illegal instructions
 | Ghost Hunter | Major program bug
 | Gruds in Space | Requires disk SIO patch off (timing)
 | Gyruss | 65C816 incompatibility
 | Henry's House | Major program bug
 | His Dark Majesty | Major program bug OS hackery
 | M.U.L.E. | Minor program bug
 | Missile Command | Requires OS-B
 | Mr. DO! | Requires disk SIO patch off (timing)
 | Parsec XL | Requires XL/XE OS
 | Preppie-II | Minor program bug
 | Protector II | Requires OS-B
 | Return of Heracles | Requires OS-B
 | The Tail of Beta Lyrae | Requires writable disk
 | The Wizard and the Princess | Requires OS-B
 | TL Cars (128) | Requires XL Hardware
 | Transylvania | Requires disk SIO patch off (timing)
 | Twerps | Requires disk SIO patch off (timing)
 | Ultima I | Requires OS-B
 | Zebuland | Requires writable disk

Applications

Basic XE 4.2

Basic XE 4.2 will appear to hang on the title screen if used with the Extension disk normally used with 4.1. This is due to a change in the initialization code in 4.2 that prevents the Screen Editor display from being restored after the Extension loads. This can be worked around by blindly typing a GR.0 command to restore the screen.

FoReM XL/XE/XE Pro

FoReM relies on particular short response values for CONNECT messages from the modem. These were not standardized for higher baud rates. Altirra's emulated modem returns the Conexant code for 9600 baud (12), while FoReM by default expects the value returned by U.S. Robotics modems (13). Either lower the connection speed limit to 2400 baud or follow the directions in the FXL/FXE documentation to change the expected code.

The clock in FoReM XL/XE also tends to lose a couple of seconds per day because it is written to expect exactly 60 or 50 frames per second, whereas the actual Atari runs slightly slower than that (59.92 fps / 49.87 fps). A workaround is to adjust the emulator speed to 50/60Hz in Speed Options.

Ice-T XE 2.72

The keyboard click feature of this program has a bug: it calls directly into the OS K: handler's GET BYTE routine without going through CIO and without setting ICAX1Z. This causes the program to sometimes jam up endlessly transmitting the "j" character. Disabling the keyboard click and then waiting for the output buffer to drain fixes the problem.

This bug is fixed in Ice-T XE 2.73.

MicroCalc (cartridge)

Jumps directly into SIO at $E959 instead of using the SIOV vector, and so will fail unless the OS-B or XL/XE OS is used.

Text Wizard V1.3

Requires OS-B as it has illegal calls into the screen editor code.

The Newsroom

Requires program disk to be writable, or else it will keep asking for the program disk to be inserted. Unusually, this check is done by checking the write protection tab directly rather than being detected from a write failure.

Demos

1st Top Demo 1989

Contains a direct jump into the XL/XE OS at $C0E2 and may fail with other OSes.

Cup of Tea

There is a bug in the IRQ handling that causes it to occasionally crash after playing the sample: it assumes that an IRQ cannot happen after it is shut off in IRQEN, but there is a one-cycle window where this can still occur.

This demo also contains a strange check for the low byte of the cold start vector (COLDSV) and forces a cold reboot if it doesn't match the address byte in the XL/XE OS, so it won't work with other OSes.

Joyride

Has a custom SIO loader which fails out immediately on an error without doing any retries and therefore has very low tolerance for communication errors. It tends to work reliably in emulation, but can sporadically fail with a red screen on real hardware.

Total Daze

Due to a bug in the detection routine, this demo occasionally misdetects a single POKEY as dual POKEYs, resulting in garbled audio during the loading screen.

Vantage Point

The executable has a malformed segment (two bytes too long). The built-in executable loader will load the program, but complain; DOS may barf.

The startup code for this demo is very broken and it does not run reliably on real hardware. It banks out the kernel ROM, hooks the NMI vector, and then returns to an invalid location in RAM. This causes the 6502 to run garbage as code in mainline while display list interrupts run the initial display and music. The most serious problem is that the IRQ/BRK vector is uninitialized, which can cause the CPU to jump to random locations depending on the RAM power-on pattern.

Disk Operating Systems

Atari DOS 2.5

Does an unsupported OS check by looking for a BCC opcode ($90) at address $EA9C in the OS ROM, and replaces the VSEROR vector if it is found. This can cause a failure with custom OSes.

DOS II+/D Version 6.4

Jumps directly into the XL/XE OS E: put byte handler at $F2B0 from $18F0. Will not work with OS-B or alternative operating systems.

MyDOS

Wraps absolute indexed addressing around the 64K address space, and thus does not work on a 65C816 CPU with 24-bit addressing.

The Load Binary command in the MyDOS menu uses a MyDOS-specific XIO call to load the binary instead of regular CIO read calls. This prevents Load Binary from working with the H: handler as the handler does not know how to handle MyDOS XIO calls.

There is also a bug in MyDOS that prevents wildcarded copies from working when using H: as the source: MyDOS passes the wrong flags when enumerating files from non-disk devices, causing the copy to fail. Specifically, it attempts to read a directory listing using an AUX1 mode of $04 (open file) instead of $06 (open directory).

SpartaDOS 3.2

While the emulator's H: device can be accessed by SpartaDOS 3, there is a bug in its COPY command that causes it to malfunction when copying from H: to a destination path:

D1:>COPY H:FILE.BIN D2:

This fails with 163 Illegal wildcard in name because COPY attempts to create a file called *.* on the destination drive. It works for copies from the regular D: device because there it issues a directory search to get the source name. The workaround is to specify a full destination filename:

D1:>COPY H:FILE.BIN D2:FILE.BIN

SpartaDOS X

SpartaDOS X uses a custom SIO routine and thus will not work with standard SIOV patch based D: acceleration. Burst I/O and PBI-based acceleration will work.

The H: device is not supported at all by the SDX kernel, so specific drivers or tools must be used to copy to and from the emulator devices. PCLink is the recommended alternative for SDX 4.44 and above.

Games

63-on-1 cartridge

Requires XL/XE OS as it directly hardcodes vector entry points.

Adebar

Jumps directly into the display code at $F1A4 and needs the XL/XE OS.

Alien Garden

Erroneously trips GTIA video modes, resulting in colored garbage on screen during transitions.

Assault Force 3D

This game requires the SuperCharger 3D cartridge, which is used for hardware multiply/divide.

Astro Chase

Hangs during intro if any custom CIO devices are registered, particularly disk (D:) or host device (H:).

Berzerk

Some versions do not initialize SKCTL properly and therefore fail to play shot sounds.

Bounty Bob Strikes Back!

Some versions require exactly 64K of RAM to work.

Joystick control will not work if Ultimate1MB hardware is present. This is because BBSB! reads the joystick from a shadow of PORTA at $D3F0, which is blocked by the U1MB hardware.

Bubble Bobble (demo)

Some versions of this homebrew game copy the kernel into RAM, write over part of the SIO routine, and then invoke the corrupted SIO routine. This will only work in some emulators when SIO patch is enabled, bypassing the broken code; it does not work in Altirra because SIO hooks are not allowed to activate from code in RAM.

Castle Wolfenstein

This program writes to the disk during normal operation and requires read/write or virtual read/write mode to be selected.

Choplifter (cartridge)

Copies out OS ROM at addresses $E500-FEFF and $C500-CBFF, and so can fail with OSes other than the stock XL/XE OS.

Cohen's Towers

Program tries to clear PIA registers in the wrong order from within the extended memory window ($4000-7FFF), which means on a machine with 128K or more of memory it accidentally bank switches itself out of CPU address space and crashes. It works if it is running on a machine without extended memory banks (48K/64K) or on an OS that configures PIA port B for input (OS-B or AltirraOS/800).

Crownland

Program will crash on horizontal scroll if illegal instructions are not enabled or if 65C02/65C816 CPU is selected.

DARG

The clock runs too quickly if accelerated floating-point is enabled.

E.T. Phone Home! (cartridge)

This cartridge has a protection check which requires the OS to boot within a certain amount of time. The XL/XE OS takes more time to initialize and can cause this check to fail on NTSC systems.

Formula 1 Racing

Has a USR() routine that jumps directly into the Atari BASIC ROM, and therefore won't work with an alternate BASIC.

Fort Apocalypse

Unmodified versions of this game contain a RTCLOK check during the disk load and must be booted with the SIO patch disabled.

G.I.L.P.

Requires 6502 CPU with illegal instructions enabled; will crash on a 65C816.

Ghost Hunter

Relies on the location of RUNAD+1 to be $00 during playfield generation. Some versions of this game are packaged as an executable such that the run address is set during load, and since it is not cleared afterward the playfield breaks.

Gruds in Space

Unmodified versions of this game contain a RTCLOK check during the disk load and must be booted with the SIO patch disabled.

Gyruss

Wraps absolute indexed addressing around the 64K address space. This causes the game to malfunction if run on a 65C816 without memory at bank $01, by preventing it from expiring enemy explosions. It runs fine with either 16-bit addressing (bank $00 = bank $01) or with memory in that bank.

Henry's House

The game can crash randomly on NTSC machines when Henry dies in Room 05. This is due to the game spending too long in a deferred VBI handler and subsequently scrambling the order of its display list interrupts.

His Dark Majesty

Version 1.0 has a bug that can cause it to randomly crash due to infinite DLI interrupts if the opening animation is interrupted at certain times in the video frame. The workaround is to hit a key during the title screen instead. This bug is fixed in version 1.01.

This game will not work with some custom OSes due to copying out parts of the OS ROM for private use (the SIO routines, to be specific).

M.U.L.E.

The tiny sound glitches that occur when notes are played happen on a real Atari, too.

Missile Command

Has a hardcoded reference to the keyboard lookup table in the OS-B ROM. The game is playable on XL/XE or custom OSes, but the keys for selecting the starting wave will not work.

Mr. DO!

Unmodified versions should be run with the SIO patch disabled and with accurate disk timing enabled. The game may appear to load correctly, but will malfunction in certain game elements due to the silently failed timing check. Specifically, if a particular section of the boot process runs faster than a certain amount of time, the game will deliberately break itself and sprites will fall through to the bottom of the screen.

Parsec XL

Directly jumps into XL/XE OS at $C28A and may fail with other OSes.

Preppie-II

Has a hidden function in the game that skips levels if the buttons on paddle 4 and 5 (port 3) are pressed simultaneously. This is normally impossible on an XL/XE because there is no port 3, and although the OS mirrors port 1 to port 3 in the OS variables and the paddle buttons overlap with the joystick direction lines, it's impossible to trigger this with the joystick (it can't be both left and right). However, this can be triggered if a custom OS doesn't set PTRIG4/PTRIG5 properly.

Protector II

Requires OS-B, as it directly pulls the vector table from the OS ROM.

Return of Heracles

Requires OS-B due to hardcoded references to the keyboard translation table. The game will appear to work on the XL/XE or a custom OS, but keyboard entry will fail late in the game with the Riddle of the Sphinx puzzle.

The Tail of Beta Lyrae

Rewrites the disk to store a load counter, which is used to gradually reveal more game elements as the game is loaded more times. The game will work if the disk is write protected, but the new game elements will not appear if the counter is not already high enough.

The Wizard and the Princess

Directly jumps into the OS-B screen editor; fails with XL/XE or custom OSes.

TL Cars (128)

Original version has an unusual problem where its loader will fail with hardware that has a floating data bus, where varying data is returned from the $D1xx region. In Altirra, this is emulated when XE hardware is selected. XL hardware has pull-ups on the data bus that cause $FF to be consistently returned from those addresses and will work with this version.

Transylvania

Unmodified versions of this game contain a RTCLOK check during the disk load and must be booted with the SIO patch disabled.

Twerps

Unmodified versions of this game contain a RTCLOK check during the disk load and must be booted with the SIO patch disabled.

Ultima I

This game requires the OS-B kernel ROM to run. It fails on HLE or XL kernels because it has hardcoded references to keyboard translation tables in the OS-B ROM.

Zebuland

This program writes to the disk during normal operation and requires read/write or virtual read/write mode to be selected.


Altirra Help: Bugs to avoid in new programs

Bugs to avoid in new programs

There are some common errors which appear to be bugs in Altirra, but are actually bugs in the program that will manifest on real Atari hardware.

Contents
  • 1. POKEY
    • 1.1. Initialization mode
    • 1.2. Interrupt timing
    • 1.3. Asynchronous serial port clocking mode
    • 1.4. Disabling IRQs
  • 2. ANTIC
    • 2.1. Changing display lists
    • 2.2. WSYNC timing
  • 3. PIA
    • 3.1. Direction register vs. output register

POKEY

Initialization mode

Bits 0 and 1 of SKCTL control keyboard scan and debounce. They also activate POKEY initialization mode when both cleared, which resets all clocks within POKEY. Both bits should be set to 1 before attempting to play sound or transfer data on the SIO bus. Otherwise, timers using the 15KHz or 64KHz clocks will not run.

Interrupt timing

Some programs developed under emulators have been written with the assumption that 15KHz timer interrupts always occur at a specific position in the scan line. They do not; the scan line offset of the 15KHz clock is determined by when initialization mode (SKCTL bits 0-1) is ended. Manually enable and disable initialization mode if you need this interrupt to have specific timing.

Asynchronous serial port clocking mode

Bits 4-6 of SKCTL control the clocking mode used by the serial port. SIO normally changes this register to $13 during an SIO transfer, which enables asynchronous clocking for timer 4. This causes problems for audio playback if not reset, since asynchronous timing mode causes timer 4 to be locked when a byte is not being received. Resetting SKCTL to $03 before playing audio fixes the problem.

Disabling IRQs

Disabling an interrupt by writing to IRQEN while interrupts are unmasked on the 6502 does not guarantee that the interrupt will no longer occur. There is a one-cycle window after the write during which the recently disabled IRQ can still fire, which can lead to the interrupt being re-enabled if the IRQ routine does so. Masking interrupts with SEI prior to writing IRQEN safely avoids this problem.

ANTIC

Changing display lists

Display lists should only be changed during vertical blank or when display list DMA is turned off. Attempting to write DLISTL/DLISTH during the active region can cause ANTIC to read random data if the display list DMA cycle falls between the writes. When using the OS shadow registers SDLSTL/SDLSTH, the program must wait until vertical blank for the change to take effect before changing the display list itself. Failing in either case can cause ANTIC to read a display list instruction with the interrupt bit set, which can cause stray DLIs if enabled. This is often catastrophic in the case of a $C1 byte, which causes ANTIC to fire a DLI on every scan line within the active region, usually resulting in a program crash.

WSYNC timing

An STA WSYNC does not guarantee a completely stable position within the scan line. The exact cycle at which the CPU resumes execution can be up to three cycles different depending on the DMA timing on that scan line. One cycle is from possible playfield DMA at cycle 105, a second is from refresh DMA on cycle 105 or 106, and a third is from DMA on a cycle right after STA WSYNC. The third cycle of uncertainty can be eliminated by doing two STA WSYNCs in a row or by using INC WSYNC.

PIA

Direction register vs. output register

The data address for a PIA port serves as both the data direction register and the output register for a port, controlled by bit 2 of the control register. When changing memory banks, it is important to ensure that port B has all bits set to output, as the default is for all bits to be input bits. Failing to do this causes the writes to port B to be ignored as the output register will not be used for any output lines.


Altirra Help: License

License

Altirra - Atari 800/800XL/5200 emulator
Copyright (C) 2009-2010 Avery Lee

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.

GNU General Public License

                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.


Altirra Help: Emulation accuracy

Emulation accuracy
Contents
  • 1. Emulation goal
  • 2. Timing
  • 3. Known issues: CPU
  • 4. Known issues: ANTIC
  • 5. Known issues: GTIA
  • 6. Known issues: POKEY
  • 7. Known issues: PIA
  • 8. Known issues: Disk
  • 9. Known issues: Video Board XE (VBXE)

Emulation goal

It is impossible for a PC host to perfectly emulate all aspects of an Atari 8-bit computer without actually being that computer exactly. Therefore, for the purposes of grading emulation accuracy, we will define perfection as follows:

A perfect emulator is one in which a program running on the emulated architecture cannot distinguish the emulator from the real hardware.

This definition restricts evaluation criteria to only elements that are program-visible; it excludes elements such as analog audio/video output circuitry. Those elements are also important, but their quality is subjective and also varying even on real hardware. In contrast, the synchronous response of the hardware to executed program code is almost entirely deterministic and mostly well-defined.

In some cases, behavior on real hardware may vary. This includes the contents of system memory on power-on (particularly if the system has recently been turned off), and disk drive timing. In these cases, the emulated behavior should at least be possible, with ideal or better behavior being desirable.

Timing

Altirra strives for cycle-exact timing in all hardware emulation. This includes:

  • CPU memory read/write timing, including dummy instruction fetches and false read/write cycles.
  • ANTIC DMA timing. All DMA cycles, including display list, playfield, P/M graphics, and refresh DMA cycles, are spaced out in the scan line to match the hardware.
  • Mid-screen writes to all hardware registers.
  • POKEY interrupt and SIO disk transfer timing. POKEY timer interrupts fire with cycle accuracy, not scan line accuracy.

Timing on the host does necessarily vary, as the audio and video output cannot be produced with the exact timing used by the real Atari hardware. However, the emulation is stepped synchronously such that a program running under emulation does not see these timing variations.

Known issues: CPU

  • Missed DLIs are not enabled by default. On real hardware, NMIs from ANTIC are missed by the CPU if BRK or IRQ interrupt processing begins on exactly cycle 4 of a scan line where an NMI is supposed to take place. This must be enabled in CPU options in the emulator.
  • Reads from unmapped memory are only partially emulated. Floating data bus is emulated in 800 and XE modes, but is only accurate for single cycle reads; it is not accurate during a WSYNC wait (only visible by ANTIC).

Known issues: ANTIC

  • The invert/blink state is not correct on characters when vertical scrolling is used to replay buffered data on a character mode scan line.

Known issues: GTIA

  • Incorrect pixels may be shown during the transition when GTIA modes are switched mid-scanline with non-uniform bit patterns.

Known issues: POKEY

  • The phases of the 15KHz clock may not be correct if initialization mode is toggled off very briefly (less than 7 cycles).

Known issues: PIA

  • Not all PIA control line modes are implemented. (The only ones that are program visible are ones that affect the SIO motor or control lines, or cause interrupts.)

Known issues: Disk

  • The emulation core is more tolerant than real hardware about commands being prematurely aborted. It may allow subsequent commands to go through that should be ignored because the device is still processing the previous command.

Known issues: Video Board XE (VBXE)

  • XDL, overlay and attribute DMA run on a per-scanline basis.
  • The blitter runs on a per scan line basis, and will not complete a blit until the end of a scan line. It does accumulate available cycles according to overlay and attribute map modes, and it does carry over unused cycles between blits in the same blit list.
  • The blitter reads blit list entries instantaneously. This means that the CPU cannot start a blit list and then overwrite the first entry before the blitter can read it, even though this is possible in rare circumstances (HR overlay, 8 pixel wide attribute tiles, executing code from a MEMAC window).
  • MEMAC accesses by the CPU do not slow down the blitter.
  • Attribute map collision is not supported.
  • Palette 0 is initialized with the palette currently specified in System > Video > Adjust Colors instead of the laoo palette used by the VBXE hardware. This can result in color mismatches when software attempts to mix a custom palette with the cold start palette.