Starpath Supercharger

(originally hosted at http://www.io.com/~nickb/atari/doc/sctech.txt, written by Christopher Salomon)

Preface

Copyright (c) 1997 by Christopher Salomon, and others where noted.

The data contained herein is provided for information purposes only. No warranty is made with regards to the accuracy of this information.

Standard disclaimers apply, don't take anything you find here for granted, perhaps some things may still be wrong, if so please email me for corrections. (Christopher Salomon (email:moc.loa|olassirhc#moc.loa|olassirhc))

All trademarks, copyrights names or whatever belong to their owners. :-)
(Atari, Video Computer System, VCS and Atari 2600 are trademarks of Atari Inc. Frogger is a trademark of Sega Inc.)
(probably some others,… you know who you are, and where you belong to)

Special thanks to the 'CyberPuNKS', whose compilation CD. 'Stella gets a new Brain' inspired me to look at Starpaths supercharger a bit closer in the first place. Much information contained in this document is derived from the material they released on that CD. All quoted documents (marked as: 'tapedocs.txt', 'cntlbyte.doc', 'excal48a.asm', 'frogger.asm') are contained on that CD. These documents are copyrighted material, the copyright belongs to the CyberPuNKS, Jim Nitchals in special (who edited/created them).

The CyberPuNKS are: Glenn Saunders, Russ Perry Jr., Jim Nitchals and Dan Skelton (no specific order)

The Supercharger

The supercharger has 6KB RAM on board where games are loaded to. Furthermore there is a ROM of 2KB for startup and loading of the programs. Thus it has 8KB of accessible memory. These 8 KB are divided in 2KB banks. These banks can be 'bankswitched' to the Atari VCS 2600 module area ($1000-$1fff).

For information how the tape format looks like read this: Following is a text contained on the 'Stella gets a new brain CD'. ('tapedocs.txt')

The Supercharger Tape format

Low level format

Tapes are recorded with variable pulse widths. A pulse (or a single cycle of a sine wave ideally) is shorter in width when encoding "0" bits, and longer when encoding "1" bits.

The upper and lower bounds on the pulse widths are:

  • about 158 microseconds for a ZERO bit, and 317 microseconds for a ONE bit
  • about 900 microseconds for a ZERO bit, and 2450 microseconds for a ONE bit

While ONE bits only need to be about 90 microseconds wider, it's better if they're at least 25% wider in order to allow the Supercharger to adapt better to any fluctuations in tape speed.

The upper bound on pulse widths is due to limitations in the decoding software. The lower bound is due to a filter in the Supercharger designed to remove high frequency hum from 15KHz video interference.

The optimal widths that maximize signal strength through the filter are:

  • 0 bit: 227 microseconds
  • 1 bit: 340 microseconds

Byte order

Bytes are transmitted with the most significant bit ($80) first, and the least significant bit last. There are no start or stop bits.

Initial header

Supercharger tapes start with a lower frequency start tone, but it's not used by the tape decoder.

A pattern of alternating one's and zero's (byte value of $AA), with a recommended minimum length of 256 bytes, allows the Supercharger to determine the widths of one and zero bits.

After the $AA's, a byte of $00 follows. This allows the Supercharger to synchronize to the start of the byte stream no matter where in the $AA header it started picking up bits.

An 8 byte header packet follows. If you're offering to help with the CD at this point I'll tell you its format in more detail. I'm not excited about creating competition for the CD quite yet, and we've put in a lot of serious work and don't want to lose money on the project.

The header indicates the starting point of execution, how many packets of game data, the bank switching configuration for the game, how quickly to scroll inward the blue progress bars, and a checksum.

Its format is:

  • Low order byte of the address to start executing the game's startup code
  • High order byte of same
  • Bank configuration as noted below
  • Block count (number of 256 byte program data packets)
  • Checksum: computed like game data checksums. Sum of whole header is $55.
  • Multiload index #. Set to 0 for first or only load of the game. Each new multiload game was assigned new numbers sequentially so that no other multiload stage from another game would be accidentally loaded
  • (Low, high) 16 bit speed value for progress bars. $224 is perfect for a 6K game image. $16D is right for 4K, and $00B6 is right for 2K game images.

After the header packet follow packets for every 256 bytes of game data.

The game data

For each 256 bytes of data in the game, a packet is written consisting of a block number that encodes the address page offset * 4 plus the bank number, and a checksum that encompasses all 256 bytes of data plus the block number as written to tape. The data then follows in normal linear fashion, all 256 bytes being written.

The checksum is calculated by adding all checksummed data, ignoring carries or overflows. The value [$55 minus the sum], again ignoring carries and underflows, is the checksum to write to tape. Hence, the sum of the whole data packet including the checksum byte itself will be $55.

It's recommended you write a byte of 0's and some silence after the last data packet in order to avoid glitching the audio system of your tape deck and ruining the last data packet while recording.

Bank Configuration

D7-D5 of this byte: Write Pulse Delay (set to 0, and the tape loader will choose the correct setting.)

D4-D0: RAM/ROM configuration:

$F000-F7FF    $F800-FFFF Address range that banks map into
000wp     3            ROM
001wp     1            ROM
010wp     3            1      as used in Commie Mutants and many others
011wp     1            3      as used in Suicide Mission
100wp     3            ROM
101wp     2            ROM
110wp     3            2      as used in Killer Satellites
111wp     2            3      as we use for 2k/4k ROM cloning

w = Write Enable (1 = enabled; accesses to $F000-$F0FF cause writes to happen. 0 = disabled, and the cart acts like ROM.)

p = ROM Power (0 = enabled, 1 = off.) Only power the ROM if you're wanting to access the ROM for multiloads. Otherwise set to 1.

ROM = bank 4

CNTLBYTE.DOC

*-------------------------------
*        L' BYTE DE CONTROL
*-------------------------------

D7-D5 . . . WRITE PULSE DELAY

D4-D2 . . . RAM/ROM CONFIGURATION
             VALUE  $F000  $F800
              000XX   3     ROM
              001XX   1     ROM
              010XX   3      1        used in Frogger, Suicide Mission
              011XX   1      3
              100XX   3     ROM
              101XX   2     ROM
              110XX   3      2        used in Killer Satellites
              111XX   2      3

D1   . . . . WRITE ENABLED (=1)

D0   . . . . ROM POWER (OFF=1)

*-------------------------------
*         MULTILOAD FORMATS
*-------------------------------

PREPARATION
  . . . . . POWER UP ROM
  . . . . . WAIT > 1000 CYCLES
  . . . . . $80 - D7-D5 ONE SHOT (leave location $80, bits 5-7 intact!)
  . . . . . $FA - SEQUENCE BYTE  (multiload number to load next in
                                  location $80)
  . . . . . STACK POINTER > $FD

PRESS PLAY
  . . . . . JMP $F800

REWIND TAPE
  . . . . . LDA $80
  . . . . . AND #$E0
  . . . . . STA $80
  . . . . . JMP $F80A

MEMORY
  . . . . . $80 POWERUP FROM TAPE (bank number and write pulse delay)
  . . . . . $81-$9D ZEROED
  . . . . . $FA-$FF VALUES:
            $FA: LDA $FFF8
            $FD: JMP <ADDR>
            <ADDR> IS FROM TAPE (starting address to run code at in
                                 first header)
  . . . . . PAGE 7 OF R1 IS USED
            FOR STARS AND MUST
            BE RELOADED (last page in bank 1 gets trashed when
                         doing multiloads)
  . . . . . REGISTERS
            A: SEED FOR RANDOM #
            X: $FF
            Y: $00

FROGGER.ASM

org TAPE_HEADERS
        dc.b    START&255,START/256

        dc.b    $0B         ; RAM bank select control

; We don't have to send all 6K.  If the program's smaller, so is
; the tape.
        dc.b    24          ; number of 256 byte blocks to send
                            ; (24 for 6K tapes)

        dc.b    $00         ; checksum (computed by tape gen tool.)

; Leave this 0 unless you're up to the challenge of writing a multiload
; game.
        dc.b    $00         ; multiload index number
                            ; (0 for single-load games or first load)

; $224 is the correct speed for a 6K tape image.
        dc.w    $224        ; $110-$224 for real fast to normal
                            ; progress indicator bars

; Checksums for the individual 256 byte blocks are recalculated by
; the tape generation software.  Ignore warnings about the checksums
; being wrong. That's there only to verify the accuracy of transcribed
; audio tapes.

; 256 byte bank offsets for the image data to load into.
        org     TAPE_HEADERS+$10
        hex     00 04 08 0c 10 14 18 1c       ; page numbers for bank 1
        hex     01 05 09 0d 11 15 19 1d       ; page numbers for bank 2
        hex     02 06 0a 0e 12 16 1a 1e       ; page numbers for bank 3

If you have *.bin files on your computer (like from the
'Stella Gets a new Brain' CD), these files should be 8448 bytes long.
(the *.bin files on the stella CD can be truncated to that size)

These 8448 bytes contain 4 banks of 2048 bytes and one tape header of
the above format, which has 256 bytes.

2048 bytes bank 1
2048 bytes bank 2
2048 bytes bank 3
2048 bytes bank 4 (all zeros, since that is the ROM part)
256 bytes tape header information
8448 bytes ($2100 bytes) total

Startup

An extract of an example tape header (frogger.bin):

00002000: E2 F2 0B 18 38 00 24 02 00 00 00 00 00 00 00 00
           \_/   I        I
            I    I        ---- Multiload Index (0=first or only)
            I    I             (Sequence number)
            I    I
            I    ---- Write Pulse Delay, bankswitch scheme,
            I         RAM enable/disable, ROM configuration
            I
            ---- Start address low/high

Bankswitchscheme on startup

After loading the tape, the value contained in the 3rd byte of the header is loaded to $80. And bankswitching is set according to the value of that byte.

(remember?)

D7-D5 . . . WRITE PULSE DELAY

   D4-D2 . . . RAM/ROM CONFIGURATION
                  VALUE   $F000  $F800
1.              XXX0 00XX   3     ROM
2.              XXX0 01XX   1     ROM
3.              XXX0 10XX   3      1
4.              XXX0 11XX   1      3
5.              XXX1 00XX   3     ROM
6.              XXX1 01XX   2     ROM
7.              XXX1 10XX   3      2
8.              XXX1 11XX   2      3

   D1   . . . . WRITE ENABLED (=1)

   D0   . . . . ROM POWER (OFF=1)

For example a $0b found in the header at position 3 would correspond to:

$0b = 0000 1011
      XXX? ??XX
      ---------
         0 10     == bankswitch scheme 3

Startaddress of Cartridge

The first two bytes of the header contain the start location of the cartridge. Low byte first, high byte second.

Thus a $ef at location $2000 and a $f2 at location $2001 would result in a start address of: $f2ef (which in turn would correspond to $12ef on a real VCS 2600, since addressing is done via modulo $1fff)

Bank Switching while running (accessing $fff8)

Someone told me that $fff9 is a redundant address for $fff8. I know of no game using this and I have no further information. I have not tested it in any way. If it is indeed so, than in the following text $fff8 might be replaced with $fff9.

Like several other bankswitch schemes, supercharger has a so called hot spot. This is an address which initiates a bankswitch upon accessing that address(es). Supercharger has only one hot spot, it is located at $fff8. The bankswitch scheme is chosen in a somewhat different manner than in other bankswitch methods.

Perhaps it is easiest to just provide an example:

ldx $80     ; load value at $80 to X register
               ; $80 is located in the zero page and in the
               ; native RAM area of Atari VCS 2600
               ; let us assume a value of $0b is stored there

   cmp $f010,x ; than this instruction will compare the accumulator
               ; with the value stored in $f01b
               ; the result is of no concern for bankswitching,
               ; we are only concerned with the low order byte
               ; of the resulting address (which is $1b)
               ; even the compare is of no matter, this could
               ; have also been a 'lda' instruction, the
               ; bankswitch access which follows only needs
               ; the low order byte of the last accessed operand
               ; address (for special cases see below)

   cmp $fff8   ; this now does the actual bankswitching
               ; accessing memory location $fff8 will trigger
               ; bankswitching
               ; this will set the bankswitching scheme
               ; to $1b (the lower byte of the lastoperandaddress)
               ; $1b = 0001 1011
               ; that is
               ; 7. XXX1 10XX   3      2
               ; which means that VCS 4KB $1000-$1fff contains:
               ;
               ;               Supercharger memory
               ;   Bank 1     Bank 2      Bank 3     Bank 4 (ROM)
               ; $0000-07ff $0800-$0fff $1000-$17ff $1800-$1fff
               ;          _______I__________/
               ;         /        \_______________
               ;        /                          \
               ; $1000-$17ff                        $1800-$1fff
               ;                  VCS memory
               ;
               ; how $fff8 is accessed does not matter
               ; some programs even run over that location...
               ; be aware however that the bankswitching occurs
               ; spontanously, so if you run into the code
               ; and switch the bank which loads to
               ; $1800 - $1fff you will end up being on another
               ; bank
               ; furthermore, the above 3 instruction sequence
               ; pokes (stores) the value to location $fff8
               ; in the supercharger RAM (see below)

Again: The above code is an example for bankswitching. What it does:

  • it combines on the second line $1010 with the value of x that is $101b (this is the lastoperandaddress, see below)
  • the instruction on the third line selects the bankswitch scheme ($1b) (only the lower byte of the lastoperandaddress counts): XXXX1=110XX that means scheme 3 2

Don't worry about the compares, they are just a way to combine to an address, and/or to 'call' a visit to location $fff8.

lastoperandaddress is stored while accessing via:

absolut     \
absolut,x     \
absolut,y      I-
indirect,x    /   Indirect addresses store the value found at the
indirect,y  /     addressed location plus the values of x or y
                  operandaddress=
                  what's_in_there(what's_in_there(zeropage address)+x)
                                           or
                  operandaddress=
                  what's_in_there(what's_in_there(zeropage address))+y

NOTE: (not sure for bankswitching about the following, for RAM access it applies)

The access to location $fff8 must be within 4-6 cycles after addressing the lastoperandaddress, otherwise it might not be known what bankswitching scheme will be used!

Furthermore: Bit 0 and bit 1 are very important too. (remember?)

D1 … . WRITE ENABLED (=1) DISABLE (=0)
D0 … . ROM POWER (OFF=1) (ON=0)

Bit 0

This bit is explained further down again. In non multiload games this will usually be set to 1, which means the superchargers internal ROM is switched off. If you want to access the ROM again after loading the game in the first place, for example for a multiload game, you have to clear this bit. Clearing this bit will enable the internal supercharger ROM. When the ROM is enabled the programm must at least wait for 1000 Cycles before it can access the ROM routines. (for further information about loading see below)

Bit 1

Setting this bit will enable access to the superchargers inherent 6KB of RAM. This can have strange effects if not done on purpose. Well, and clearing it will disable this ability, the supercharger than will act as any other ROM module.

NOTE: Location $fff8 is allways write enabled, no matter what state bit 1 may have been set to.

Supercharger RAM access

In order to be able to access the superchargers 6KB of RAM, you have to set bit 1 while switching to another (or the same) bankswitching scheme. (see a few lines further up)

The general idea of how to access RAM is the same as with setting a new bankswitch scheme. Here again an example:

ldx #$2f     ; this loads a value $2f to x register
                ; this is in no way needed, just an example
                ; don't worry, fake, just for fun, has no meaning
                ; (not with RAM access anyway :-))

   cmp $f010,x  ; access a memory location
                ; usually the format for this is like
                ; 'cmp RAM,x', where RAM must be a value between
                ; 0xf000-0xf0ff
                ; whether it is a cmp, a lda or something
                ; else does not matter, as in the bankswitch section
                ; the only thing that counts is the lower byte of
                ; the resulting operand address, in this
                ; case:
                ;       $f010 + $2f = $f03f  | only lower byte
                ;                     -----
                ;                       $3f
                ; this is the value we will poke to a yet to
                ; be chosen RAM location
                ;
                ; the above RAM parameter MUST be
                ; from 0xf000-0xf0ff the access to
                ; these locations enable RAM access for the next
                ; instruction

   lda $f77b    ; this instruction triggers the actual
                ; poking, the value from above is stored
                ; to location $f77b, in good old plain
                ; BASIC:
                ;         poke $f77b, $3f
                ; (well, BASIC wouldn't understand the '$'s, but
                ;  who cares...)
                ;
                ; NOTE: the poking instruction must be at least
                ; 4 cycles <away> from the accessed operand address
                ; and at most 6 cycles.
                ; you can fill up to the needed cycle count with
                ; any instuction that doesn't use an operand address
                ; by itself (mostly a NOP, but sometimes a DEX or so
                ; will be used...)

Again: The above code is an example for poking to location $f77b a value of $3f. What it does:

  • it combines on the second line $f010 (which is in between $f000-$f0ff, and thus prepares a poke) with the value of x that is $f03f (this is the lastoperandaddress, see below)
  • this selects poke value $3f (only the lower byte of the resulting address counts)
  • with a memory access the value is written to that memory (when write bit is set) that might be a lda, cmp, and, bit…

lastoperandaddress is stored while accessing via:

absolut     \     the value before the ',y' and ',x' must be
absolut,x     \   an address from 0xf000 to 0xf0ff to enable RAM!
absolut,y      I-
indirect,x    /   Indirect addresses store the value found at the
indirect,y  /     addressed location plus the values of x or y
                  operandaddress=
                  what's_in_there(what's_in_there(zeropage address)+x)
                                           or
                  operandaddress=
                  what's_in_there(what's_in_there(zeropage address))+y

The instruction that triggers the poking process may also be of the above kind (absolut, absolut_x, absolut_y, indirect_x, indirect_y).

And another thing again…: The cycles, be sure your instructions need more cycles than 3 and less than 7, otherwise no value will be stored.

Loading a second (third, fourth…) part

For information how to load a second part from tape: Following is a text contained on the 'Stella gets a new brain CD'. ('excal48a.asm')

EXCAL48A.ASM

RAM     .EQ $F000
CONTROL .EQ $FFF8
...
NEWLOAD CMP RAM
        CMP CONTROL     ; ROM/R3
        LDA #4
        STA $FA         ; NEXT LOAD
        ldx #100
.0      ldy #8
.1      dey
        bne .1
        dex
        bne .0
        JMP $F800       ; ROM ENTRY

Please also have a second look at the further above provided text called 'cntlbyte.doc'.

PREPARATION
 . POWER UP ROM
 . WAIT > 1000 CYCLES
 . $80 - D7-D5 ONE SHOT (leave location $80, bits 5-7 intact!)
 . $FA - SEQUENCE BYTE  (multiload number to load next in location $80)
 . STACK POINTER > $FD
PRESS PLAY
 . JMP $F800

First you have to enable the superchargers native ROM. Why? You want to access the loading routines that come with the supercharger. It has these loading routine stored in it's ROM, you might call that a mini OS :-) (with I/O features).

In order to be able to access that ROM, you have to power it up, this is done via bit 0 of the bankswitch select byte. This bit must be cleared in order to power the rom. (see above)

Furthermore you have to select bank 4 (ROM) so that you can access it. (look at the bankswitch section, schemes 0,1,4,5)

In the above provided example this is all done via the first two instruction:

NEWLOAD CMP RAM                    ; CMP $f000, which
                                   ; selects a value to be stored
                                   ; of 0
                                   ; 0000 0000
                                   ; XXX0 00w0     3-----ROM
                                   ; which powers the ROM (bit0=0)
                                   ; and puts the 2KB ROM into
                                   ; VCS 2600 memory $1800-$1fff

        CMP CONTROL     ; ROM/R3   ; which makes everything happen!

If $80 in the zero page is not properly set, you probably will want to do that now. (in the above example this is skipped)

The sequence number must be stored to zero page location $FA. The sequence number for single load games or first parts is 0, the other parts have a sequential >unique< number.

Name of game multiload-number
Mindmaster part 2 1
Mindmaster part 3 2
Mindmaster part 4 3
Dragonstomper part 2 4
Dragonstomper part 3 5
Survival Island part 2 6
Survival Island part 3 7

The sequence number is also stored in the tape header, location 5 (see the above header example). For the first load or single load games this is set to 0. For multiloads it is set to the corresponding multiload number.

Than (or before) you have to wait for those at least 1000 cycles. So the ROM of the supercharger can <power up>. If you skip this->your load will fail.

Than do the above mentioned $f800 jump. This is a jump to the superchargers inbuilt ROM.

This jump will load your new part from tape, cd or whatever.

After loading supercharger starts of at location that is located at $2000-$2001 in the header of the tape images, which is supposed to be the starting vector.

Now have a look at the 'cntlbyte.doc' again, to see how loading influenced your memory.

short extract from the above 'cntlbyte.doc for memory refresh

MEMORY after multiload
. $80 POWERUP FROM TAPE (bank number and write pulse delay)
. $81-$9D ZEROED
. $FA-$FF VALUES:
  $FA: LDA $FFF8
  $FD: JMP <ADDR>
  <ADDR> IS FROM TAPE (starting address to run code at in first header)
. PAGE 7 OF R1 IS USED
  FOR STARS AND MUST
  BE RELOADED (last page in bank 1 gets trashed when doing multiloads)
. REGISTERS
  A: SEED FOR RANDOM #
  X: $FF
  Y: $00

Taperewinding

REWIND TAPE
. LDA $80
. AND #$E0
. STA $80
. JMP $F80A

Another ROM routine, dunno what this does, I would imagine a message to be displayed, that says: 'REWIND TAPE' And after rewinding…and pressing <play> loads… whatever comes next.

That's all folks

March 1997

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License