!!!xBIOS Overview

xBIOS is like a programmers version of DOS. With it you can easily access files from your programs without using Atari DOS. It is smaller than DOS and therefore saves memory in your programs. You can even run programs from as low as $0200, however $0800 or $2000 are more common.

!!General Notes

In this section you will find some general notes about xBIOS and how it functions.

!Download Location

To allow an executable to auto run on bootup, have it named as “XAUTORUN”.
If no such file exists on the disk, the xBIOS loader menu will appear.

!Filename Format
The filenames are in 8.3 format but without the dot.
Filenames are padded out to 11 characters and are converted to uppercase.
Therefore, “file.txt” will become:
    ‘FILE    TXT’  (4 letters, 4 spaces and a 3 character extension)

xBIOS is case insensitive.

!xBIOS Limitations

a) New files cannot be created.

b) Existing files cannot be extended in length.

c) New directories cannot be created programatically.

!Default Memory Layout
$0000-$00FF        Free
$0200-$06FF        Free
$0700-$07FF        xBIOS I/O buffer
$0800-$0BFF        xBIOS
$0C00-$CFFF        Free
$D000-$D7FF        Atari H/W
$D800-$FFFF        Free

!Disk Images
Disk images (.atr files) need to be prepared in advance for xBIOS to work.

A disk image is a file which simulates a disk. It will contain 1 or more files which can be used by your programs.

In order to prepare them, an external tool needs to be used. 

__‘dir2atr’__ : [http://www.horus.com/~hias/atari]

A tool for creating an ATR file from a folder.

__‘franny’__ : [http://atariage.com/forums/topic/159325-program-to-add-to-and-extract-files-from-atr/]

Allows the additional of individual files to an ATR file.

!!!Practical Usage

In order to have a disk which is usable, a project build system should complete the following steps:

a)    Make a copy of the xBIOS.atr file, renaming the new file to something relevant for your project.

b)    Add all the new files to the file. ‘franny’ and ‘dir2atr’ are good tools for this. Ensure that the program which you want to initially run is named ‘XAUTORUN’.

Example Windows batch file:
    mads myProg.asm
    copy /Y myProg.obx myatr\XAUTORUN
    dir2atr -md -B xboot.obx myatr.atr myatr

xBIOS now also needs adding into the development project which is being created. 

There are two ways of achieving this:

__Option 1:__
The following code needs to be pasted into a project:

(Valid for xBIOS v4.3)
xBIOS                      equ $800
xBIOS_VERSION              equ xBIOS+$02
xBIOS_RENAME_ENTRY         equ xBIOS+$03
xBIOS_LOAD_FILE            equ xBIOS+$06
xBIOS_OPEN_FILE            equ xBIOS+$09
xBIOS_LOAD_DATA            equ xBIOS+$0c
xBIOS_WRITE_DATA           equ xBIOS+$0f
xBIOS_GET_BYTE             equ xBIOS+$15
xBIOS_PUT_BYTE             equ xBIOS+$18
xBIOS_FLUSH_BUFFER         equ xBIOS+$1b
xBIOS_SET_LENGTH           equ xBIOS+$1e
xBIOS_SET_INIAD            equ xBIOS+$21
xBIOS_SET_RUNAD            equ xBIOS+$27
xBIOS_OPEN_DIR             equ xBIOS+$2d
xBIOS_SET_DEVICE           equ xBIOS+$36
xBIOS_GET_ENTRY            equ xBIOS+$3c
xBIOS_READ_SECTOR          equ xBIOS+$42
xBIOS_FIND_ENTRY           equ xBIOS+$45

xDIRSIZE        equ xBIOS+$3e5 ; current directory size in sectors (1 byte)
xSPEED          equ xBIOS+$3e6 ; STANDARD SPEED (1 byte)
xHSPEED         equ xBIOS+$3e7 ; ULTRA SPEED (1 byte)
xIRQEN          equ xBIOS+$3e8 ; User IRQ (1 byte)
xAUDCTL         equ xBIOS+$3e9 ; AUDCTL
xFILE           equ xBIOS+$3ea ; File handle (2 bytes)
xDIR            equ xBIOS+$3ec ; Root directory handle (2 bytes)
xIOV            equ xBIOS+$3ee ; I/O module entry (2 bytes)
xBUFFERH        equ xBIOS+$3f0 ; Buffer adr hi byte (1 byte)
xBUFSIZE        equ xBIOS+$3f1 ; Buffer size lo byte $100-SIZE (1 byte)
xDAUX3          equ xBIOS+$3f2 ; Buffer offset (1 byte)
xSEGMENT        equ xBIOS+$3f3 ; Bytes to go in binary file segment (2 bytes)
xNOTE           equ xBIOS+$3f5 ; File pointer (3 bytes)
xDEVICE         equ xBIOS+$3fc ; Device ID
xDCMD           equ xBIOS+$3fd ; CMD (1 byte)
xDAUX1          equ xBIOS+$3fe ; Sector lo byte (1 byte)
xDAUX2          equ xBIOS+$3ff ; Sector hi byte (1 byte)

Each entry in this jump table specifies where in memory that the code for each function resides. If you ever wish to start from a different address, change the value for ‘xBIOS’ above and change the value in the binary header of xBIOS.com.

__Option 2:__
From xBIOS v4.2 and above, you can use the xBIOS.cfg file which is as below.

opt h-
.byte   $43             ; config for v4.3 version
.byte c'XAUTORUN   '    ; autorun file fname
.byte   >$0800          ; xB adress for relocator
.byte   >$0700          ; xB buffer adress for relocator
.word   $02e2           ; INITAD
.word   $02e0           ; RUNAD
.word   $0000           ; custom I/O module - $0000 means use internal xB SIO
.word   $0000           ; relocator for custom I/O module
.byte   $ff             ; PORTB - you can't swith off BASIC this way
.byte   $40             ; NMIEN - config at start
.byte   $c0             ; IRQEN - config at start

Please note that xBIOS do not use page zero. You can change the start address from the third entry above.
Now that the above exists, the following examples can be used to access files in the ATR file.


Here are some of the examples taken from http://xxl.atari.pl and other online sources, but with a few extra/modified comments in English.


This function allows you to rename a file or directory. There is no limit to the characters used in the filename apart from that they must fit a case insensitive “8.3” format without the dot.
If your filename is not 8 characters long, pad it out with spaces.

ldy <fname
ldx >fname 
.byte c'FILENAMESRC '; 11 char ATASCII (8.3 without the dot, space padded)
.byte c'FILENAMEDST '; 11 char ATASCII (8.3 without the dot, space padded)


Load and run the file, INIT and RUN headers are supported. "Boot Loader" has an analogous function xBOOT_LOAD_FILE. In the case of the boot loader if the file does not have a defined block RUN will be launched from the beginning of the first block.

ldy <fname 
ldx >fname 
fname .byte c'MYFILE  COM'; 11 characters ATASCII (8.3 without the dot, space padded)

Open a file in order to carry out subsequent IO operations.

ldy <fname 
ldx >fname 
fname .byte c'MYFILE  COM'; 11 characters ATASCII (8.3 without the dot, space padded)


Load data from file to a specified address. You can set the file offset (FILE_OFFSET) and the amount of data to be loaded (SET_LENGTH). If you do not define these values, data will be loaded from the current position of the file pointer to the end of the file. If you want to load a binary file using the headers defined in it or use the functions xBIOS_BINARY_LOAD or xBIOS_LOAD_FILE.

ldy <dest 
ldx >dest 


Save data from memory to a file, starting from the current position in the file. You can set the file pointer offset current (FILE_OFFSET) and the amount of data to be saved (SET_LENGTH). If you do not define these values, data from the current file position to the end of the file is written to the file.

ldy <src 
ldx >src 

Opens the current directory.


The next byte of an open file is loaded into the register A (accumulator).



The byte value in the accumulator is written to a file at it’s current pointer location.

lda BYTE 


All write operations are cached, use this to flush the buffer to the current file.



Defines the amount of data to process while reading or writing. If your OPEN_FILE value is not defined this operation will be carried out until the end of the file.

ldy <len 
ldx >len 


Allows you to change the init address vector INITAD ($2E2) for loaded binary files.

ldy <addr
ldx >addr 


Sets the current read/write position in the current file with a value stored in A, X, Y. This item is calculated relative to the beginning of the file. In DOS speak, the operation is called "POINT".

ldy <pos 
ldx >pos 
lda ^pos 


Allows you to change the run address vector RUNAD ($2E0) for loaded binary files.

ldy <addr 
ldx >addr 


Restores the standard IO device.

Allows you to change the current directory.

ldy <fname 
ldx >fname 

Note that the directory cannot be created by xBIOS and therefore must be created with an external tool such as ‘franny’.


Load and run the binary file from the current read/write position. INIT and RUN headers are supported.



Opens the default directory.



Change the IO device.

ldy <dev 
ldx >dev 


Change address IO buffer. If before the call to set the marker C = 1, the relocation can be carried out even during IO. The data will not be lost. If the marker before calling C = 0, buffer contents will not be copied to a new location.

ldx >buffer 


Gets another entry in the directory. The X register returns the index to the filename or folder (byte of buffer address is stored in the variable xBUFFERH). The accumulator is set with the status. The carry flag is set when the end of the directory is found.



Opens the default file. The function does not search the directory, the file handle is derived from the variable 'Xfile'.



Load a sector into a buffer.
lda >sector ; High byte of the sector number
ldy <sector ; Low byte of the sector number



This function allows you to find the specified directory entry. The X register returns the index to the filename or folder (byte of buffer address is stored in the variable xBUFFERH). The accumulator is the status byte. If an entry is not found, the carry flag is set.

ldy <fname 
ldx >fname 


This feature allows you to set the buffer size for IO operations. Buffer Size is also stored in the variable xBUFSIZE in bytes format.
lda # $ 100-SIZE 


!Counters During I/O

xIRQEN          equ xBIOS+$3ea ; User IRQ (1 byte)
xSEGMENT        equ xBIOS+$3f4 ; Bytes to go in binary file segment (2 bytes)
xNOTE           equ xBIOS+$3f6 ; File pointer (3 bytes)

PUPBT1          equ $033D      ; Power-up validation byte 1

                icl     'atarihw.ah'

                opt     h+o+l+

                org     $c00
start_sio       sei
                lda     #$00
                sta     nmien
                sta     irqen
                sta     xIRQEN
                sta     dmactl
                lda     #$fe
                sta     portb
                lda     <MyNMI
                sta     nmiv
                lda     >MyNMI
                sta     nmiv+1
                lda     <mydlist
                sta     dlistl
                lda     >mydlist
                sta     dlisth
                jsr     xBIOS_SET_DEFAULT_DEVICE ; SIO drive
                ldx     >RESETV
                ldy     <RESETV
                jmp     xBIOS_SET_RUNAD    ; new runad vector
                ini     start_sio

                org     RESETV
                .word   run_adr            ; RUN

                org     PUPBT1
                .byte   d'xxl'             ; Let the RESET key work as a RESET key

                org $0000

run_adr         lda     #$74
                sta     colbak
_stop           jmp     _stop              ; Endless loop

            :12 .byte $70
                .byte $46,<counter,>counter
                .byte $06
                .byte $41,<mydlist,>mydlist
counter         .byte d'SEGMENT:            '
                .byte d'FILE OFFSET:        '

start           sei
                ldx     #3
                ldy     #0
@               inc     PORTB
                lda     $e000,y
_csrc           equ     *-1                
                dec     PORTB
                sta     (_csrc-1),y
_cdst           equ     *-1                
                bne     @-
                inc     _csrc
                bpl     @-
@               lda     vcount
                bne     @-
                lda     #$22
                sta     dmactl
                lda     #$40
                sta     nmien
MyNMI           sta     nmiA
                sty     nmiY
                inc     licz
                lda     #0
licz            equ     *-1
                lsr     @
                bcc     secondnmi
firstnmi        ldy     #16
                lda     xSEGMENT+1
                jsr     puthex
                lda     xSEGMENT
                jsr     puthex
                jmp     endnmi    
secondnmi       ldy     #20+14
                lda     xNOTE+2
                jsr     puthex
                lda     xNOTE+1
                jsr     puthex
                lda     xNOTE
                jsr     puthex

endnmi          lda     #0
nmiA            equ     *-1
                ldy     #0
nmiY            equ     *-1

puthex          pha
                lsr     @
                lsr     @
                lsr     @
                lsr     @
                jsr     nibble
                and     #$0f
nibble          cmp     #$0a
                adc     #$10
                sta     counter,y
                ini start

                org $0c00
    :$c300      .byte $ff

!Playing Music During I/O

                opt h+o+l+

                org     PUPBT1
                .byte   d'xxl'                       ; Let the RESET key work as a RESET key

                org     $c00
start_sio       sei
                lda     #$00
                sta     nmien
                sta     irqen
                sta     xIRQEN
                sta     dmactl
                lda     #$fe
                sta     portb
                lda     <MyNMI
                sta     nmiv
                lda     >MyNMI
                sta     nmiv+1
                lda     #$40
                sta     nmien
                jmp     xBIOS_SET_DEFAULT_DEVICE     ; SIO drive

MyNMI           sta     vbisaveA+1
                stx     vbisaveX+1
speed           lda     #0
                bne     wp
loadpat         ldx     #0
                lda     pat,x
                bmi     pass
                asl     @
                asl     @
                asl     @
                asl     @
                sta     wp+1
pass            inx
                lda     #$3f
                sax     loadpat+1
wp              ldx     #0
                lda     ins,x
                sta     audc1
                lda     frq,x
                sta     audf1
                inc     wp+1
                dec     speed+1
                bpl     pass2
                lda     #5
                sta     speed+1
wp1             lda     #$20
                sta     pass2+1
                eor     #$60
                sta     wp1+1
pass2           ldx     #0
                lda     ins,x
                sta     audc2
                inc     pass2+1

vbisaveA        lda     #0
vbisaveX        ldx     #0

ins             .byte   $0F,$AF,$AC,$A7,$A2,$00,$04,$A3
                .byte   $22,$21,$A1,$00,$00,$00,$00,$00
                .byte   $AF,$AF,$09,$07,$05,$04,$04,$03
                .byte   $03,$03,$02,$02,$02,$01,$01,$00
                .byte   $87,$84,$83,$82,$81,$00,$00,$00
                .byte   $00,$00,$00,$00,$00,$00,$00,$00
                .byte   $84,$84,$84,$84,$84,$84,$83,$83
                .byte   $83,$83,$82,$82,$81,$81,$00,$00
                .byte   $83,$82,$81,$81,$81,$00,$00,$00
                .byte   $00,$00,$00,$00,$00,$00,$00,$00

frq             .byte   $04,$C0,$D0,$E0,$F0,$00,$04,$C0
                .byte   $D0,$E0,$F0,$00,$00,$00,$00,$00
                .byte   $98,$A8,$03,$03,$03,$03,$03,$03
                .byte   $03,$03,$03,$03,$03,$03,$03,$03
                .byte   $00,$00,$00,$00,$00,$00,$00,$00
                .byte   $00,$00,$00,$00,$00,$00,$00,$00
                .byte   $00,$00,$00,$00,$00,$00,$00,$00
                .byte   $00,$00,$00,$00,$00,$00,$00,$00

pat             .byte   $00,$80,$00,$80,$01,$80,$00,$00
                .byte   $80,$00,$00,$80,$01,$80,$03,$80
                .byte   $00,$80,$00,$80,$01,$80,$02,$02
                .byte   $00,$80,$01,$01,$80,$00,$03,$80
                .byte   $00,$80,$00,$80,$01,$80,$00,$00
                .byte   $80,$00,$00,$80,$01,$80,$03,$80
                .byte   $00,$00,$00,$80,$01,$00,$00,$80
                .byte   $00,$80,$01,$01,$80,$00,$01,$00

                ini     start_sio

                org     $1000
    :$C000      .byte   $ff
                org     $D800
    :$2700      .byte   $ff

run_adr         lda     #$74
                sta     colbak
_stop           jmp     _stop        ; endless loop

                run     run_adr

!Indexed Data within File - LZ4 Graphics Decompression

xFILE           equ     xBIOS+$3ec ; File handle
xDAUX3          equ     xBIOS+$3f3 ; Buffer offset if AtariDOS FS
xDAUX2          equ     xBIOS+$3fd ; Sector hi if AtariDOS FS
xDAUX1          equ     xBIOS+$3fe ; Sector lo if AtariDOS FS
PUPBT1          equ     $033D      ; Power-up validation byte 1
myscr           equ     $8150
myscr1          equ     $9000
colour          equ     myscr+$1e01
fhandle         equ     $80

                org     PUPBT1
                .byte   d'xxl'     ; Let the RESET key work as a RESET key

                org     $c00

Mydlist         .byte   $70,$70,$70
                .byte   $4e,a(myscr)
            :93 .byte   $0e
                .byte   $4e,a(myscr1)
            :97 .byte   $0e
                .byte   $41,a(Mydlist)

myinit          lda     #$00
                sta     DMACTLS
                lda     <Mydlist
                sta     DLISTLS
                lda     >Mydlist
                sta     DLISTHS
                lda     #%00100010
                sta     DMACTLS

load_index      ldx     load_gfx+1
                lda     xDAUX1
                sta     fhandle,x
                lda     xDAUX2
                sta     fhandle+1,x
                lda     xDAUX3
                sta     fhandle+2,x
                cpx     #$0c
                bne     skip
                lda     #$100-(ldgfx+2-load_gfx)
                sta     ldgfx+1
                bne     skip
load_gfx        ldx     #0     
                lda     fhandle,x
                sta     xFILE
                lda     fhandle+1,x
                sta     xFILE+1
                jsr     xBIOS_OPEN_DEFAULT_FILE
                ldx     load_gfx+1
                lda     fhandle+2,x
                sta     xDAUX3
skip            lda     #%1111
                sbx     #$100-$04
                sax     load_gfx+1
                lda     <myscr
                sta     dest
                lda     >myscr
                sta     dest+1
                jsr     unlz4
                ldx     #$02
@               lda     colour,x
                sta     COLPF0S,x
                bpl     @-
ldgfx           bmi     load_index

unlz4           icl    'unlz4.asm'

                ini     myinit

                opt     h-
                ins     'DRUID.LZ4',$0b,.FILESIZE 'DRUID.LZ4'-$0b-$06
                ins     'LOADING.LZ4',$0b,.FILESIZE 'LOADING.LZ4'-$0b-$06
                ins     'SHPOON2.LZ4',$0b,.FILESIZE 'SHPOON2.LZ4'-$0b-$06
                ins     'POTATERR.LZ4',$0b,.FILESIZE 'POTATERR.LZ4'-$0b-$06

!Use of High Speed Devices

When you first load xBIOS, you can complete a check to see if your disk drive is ultraspeed (see next example). If so, save a value to xHSPEED. When loading data, copy the value of xHSPEED to xSPEED and use the following code: 

bmi no_hispeed
sta xSPEED

This works in conjunction with: "jsr xBIOS_SET_DEFAULT_DEVICE"
If you are using the AtariOS I/O module, speed control is not possible.

!Detecting High Speed Devices

If you want to detect if the user’s disk drive has high speed capabilities, you can detect it’s high speed status using this code.

xBUFSIZE equ xBIOS+$3f1                              ; Buffer size lo byte $100-SIZE (1 byte)
xDEVICE equ xBIOS+$3fc                               ; Device ID
xIOV equ xBIOS+$3ee                                  ; I/O module entry (2 bytes)
xBSIO      jmp     (xIOV)
start      jsr     xBIOS_SET_DEFAULT_DEVICE          ; I want to use xB SIO I/O
           lda     #$100-$01                         ; set buffer size
           sta     xBUFSIZE
           lda     #'1'                              ; AtariOS device '1' = DOS device D1:
           sta     xDEVICE                           ; you can use '2', '3' and so for D2: D3: ...
           ldx     #$3F                              ; set command GET HI SPEED FROM DRIVE
           jsr     xBSIO
           lda     $7ff                              ; get HiSpeedIndex byte from buffer 
                                                     ; (xBUFSIZE = lo byte,xBUFFERH = hi byte)

Remember to preserve old xBUFSIZE and xDEVICE values if these are required later in your code.

!Using Alternative Disk Drives

If you are using disk drives other than the default D1:, you can save values into xDEVICE in order to refer to the other disk drives.

xDEVICE equ xBIOS+$3fc ; Device ID

lda #2                 ; Use disk drive number 2

!Checking if Alternative Disk Drives Exist

Apply this code to check if a particular disk drive exists (1-8).

lda #device_number
jsr status

!Varying Directory Sizes

xBIOS can handle directories of varying sizes as per the directories of different versions of DOS.
‘xDIRSIZE’ (xBIOS+$3e5 in xBIOS 4.3) is used as a read only way to find out the current directory size in sectors. It is one byte in length and is read-only.

A directory entry within Atari DOS 2 / MyDOS and similar DOS’s uses up 16 bytes and looks like this:

.byte status         ; Byte 1
.word size           ; - in standard directories always $08 ; Bytes 2 and 3
.word first_sector   ; Bytes 4 and 5
.byte c'FILENAMEEXT' ; Byte 6 to 16

A standard directory uses 8 sectors (size = $08) which means 8 sectors * 8 entry = 64 max.
Top DOS / Bibo DOS with sector sizes of 256 bytes may looks like 8 sectors * 16 entry = 128 maximum.
You can make subdirectories of differing sizes eg. size=$02 (16/32 entry) or size = $ff (2040 / 4080 entry).
There is no tool to make different size catalogues, if at any time such a tool appears, xBIOS will handle it.


This isn’t xBIOS as such but a related loader mechanism which is simpler than xBIOS.
Simply download xBOOT from [http://xxl.atari.pl] and add it to your disk using an external tool such as ‘franny’. Name the first file to load ‘AUTORUN’. This will auto-load.
Within this executable, have the following code to load the next file in a chain.

xBOOT_LOAD_FILE equ $5f1

ldy <fname
ldx >fname
fname .byte c'NEXTPARTCOM' ; 11 chars ATASCII


xBOOT uses 384 bytes at $480 and $f9-$ff in page zero.

Original document from Steve Nicklin (snicklin at AtariAge).
Imported into AtariWiki (23/01/2016) to allow peer feedback and improvement.