Cartridges

## RAM/ROM Control On An XL/XE Computer

by John Picken
Introduction
Beyond the 130
256XL problems
Up to a Meg
Before you upgrade
Accessing the banks
Major RAM - Major glitch
Fixing it
Auxiliary ROM control
Atari-type Cartridges
OSS Super Cartridges
The SpartaDOS X Cartridge
The R-Time 8
The Multiplexer! Operating System (MUX)
Cold and Warm Starting and Parallel Devices (PD's)
Utils

### Introduction

Let's first look at what you've got to play with as far as extra RAM, how it's done, and what kind of problems you can run into: No matter how much memory is installed in your computer, the 6502 can never access an address outside the range 0-65535 (64k). With BASIC off, this limits normal RAM to 48k as the Operating System (OS) and hardware chips take up the last 16k. With BASIC on, or a cartridge installed, you lose yet another 8k.

Software like SpartaDOS and Turbo BASIC utilize another 14k of RAM by deselecting the OS. As far as I know, nobody ever produced 6, 14, or 62k chips so there has to be another 2k of RAM in there. But, unfortunately, the only way you can deselect the hardware registers is with the power switch.

Since the 6502 can't "see" extra memory, you have to trick the computer into believing it's not seeing what it is, which is how all software that uses extra RAM works. It operates by "bank switching", a concept more understandable with a memory map so here's a rough picture of the 64k address range in your computer broken up into four 16k "banks":

    |---------------------------------------------------------------|
    | Bank    Address Range                                         |
    | Num.    Dec      Hex                  Usage                   |
    |---------------------------------------------------------------|
    |  0  00000 to   $0000 to    RAM for OS, DOS,                   |
    |        16383      $3FFF    and user programs.                 |
    |---------------------------------------------------------------|
    |  1  16384 to   $4000 to    User RAM. Some ROM                 |
    |        32767      $7FFF    if selftest enabled.               |
    |---------------------------------------------------------------|
    |  2  32768 to   $8000 to    User RAM, screen RAM. Top half is  |
    |        49151      $BFFF    ROM if BASIC on or cartridge used. |
    |---------------------------------------------------------------|
    |  3  49152 to   $C000 to    OS ROM and i/o chips.              |
    |        65535      $FFFF    User RAM if os off                 |
    |---------------------------------------------------------------|

Usage indicates the only bank which is almost always all RAM and can safely be switched in or out is number 1, which is exactly how the 130XE is designed. Its extra RAM is divided into four 16k banks, each of which can replace normal memory in bank 1. In effect, the 130 memory map looks like this:

          Main    Extended Memory
         |----|
         | m0 |
         |----|--------------------
         | m1 | x0 | x1 | x2 | x3 |
         |----|--------------------
         | m2 |
         |----|
         | m3 |
         |----|

Compatible memory upgrades are mapped in the same way. The only difference is that there are more extended banks - up to 64 with 1 meg. Each bank when switched in, appears to the computer as main bank 1. The diagram is slightly misleading in that it implies the extra banks sort of slide into place one after another - they don't. Any one can be selected in any order you wish.

Switching RAMs and ROMs is physically simple. Almost all control is handled by a single hardware address known as PORTB (54017 or $D301). The bits in PORTB have the following functions on a 130XE:

         --------------------------------------------
         | Bit #   7   6   5   4   3   2   1   0    |
         |       ---------------------------------  |
         | Usage | t | y | x | e | j | i | b | o |  |
         |       ---------------------------------  |
         | Hex.   $80  40  20  10  08  04  02  01   |
         | Dec.   128  64  32  16   8   4   2   1   |
         --------------------------------------------

Bit 0 (o) if high enables the OS ROM

Bit 0 is used by Sparta and Turbo to toggle between RAM and ROM to make use of that hidden 14k. This requires a juggling act since the OS interrupt handlers are in ROM and must be re-enabled, 60 times per second, to avoid a crash, or else the program must provide its own interrupt handlers. Additionally, since Antic updates the screen before the 6502 gets to process the interrupt, it is necessary to use an alternate character set in lower memory or to copy the ROM one into RAM at the same address.

Bit 1 (b) if low enables the BASIC ROM

To figure values in PORTB you add the decimal values under each bit to be set to 1. Since, with all bits set, the total is 255, it's often simpler to subtract. PEEK (54017) in BASIC will produce 253 meaning everything's high except bit 1. The same PEEK with Turbo gives 255 as the BASIC ROM is off. This is the only bit you normally see change.

Bit 7 (t) if low enables Self Test ROM

As with RAM, there has to be 2k of ROM left over. There is, and it's used for part of the Self Test routines. When bit 7 is low, it's enabled. Since hardware registers can't be deselected, this portion of ROM deselects and replaces RAM from $5000-$57FF in bank 1. Unless you're really into listening to simple music, a really slow and incomplete RAM test, or watching keys flash on an image of a 1200XL keyboard, you can consider this 2k of ROM to be free space if designing an OS upgrade.

Bit 4 (e) if low enables extra RAM

Bit 4 is the master switch for extra memory. No matter what values are in the other bits, the 6502 will always use normal memory when this bit is high.

Bits 3 and 2 (ji) select one of four 16k banks

These bits together determine which one of the four 16k banks is enabled. They simply count from 0 to 3 in binary where, if j is set you add 2, and if i is set you add 1:

          j i  bank
          0 0   00
          0 1   01
          1 0   02
          1 1   03

Here's what the memory map would look like with bits 3 and 4 low (remember the 6502 only "sees" the RAM in the "Main" column):

          Main    Extended Memory
         |----|
         | m0 |
         |----|--------------------
         | x1 | x0 | x1 | x2 | x3 |
         |----|--------------------
         | m2 |  Note: main bank 1 doesn't move
         |----|  anywhere, it just disappears
         | m3 |  until you deselect extra RAM.
         |----|

Bit 5 (x) enables Antic access

On a 130XE, each of the four extra banks can be assigned individually to either Antic or the 6502 or to both, a setup that is rarely used--forget about them if you only have a 130. On the 130 these bits are normally set to one.

Bit 6 (y) does nothing

### Beyond the 130

Once you exceed 64k extra RAM you need more bits so the upgrade designers took the bits that I just told 130 owners to forget about. Bits 6 and 5 (yx) select one of four 64k blocks. These work similarly to 2 and 3 except that you use them to select one of up to four 64k blocks and within each of these, you use bits 2 and 3 to select one of the four 16k banks (just as for the 130). Because the 130 normally keeps these bits high, their sense is reversed from that of 2 and 3. Don't worry about the difference, all you need to know is which values work for your upgrade. The following bit patterns work for 256XL upgrades:

         y x   64k Block      Available On
         1 1      00 (130XE)   All
         1 0      01           All
         0 1      02           Bucholz/Peterson/Rambo
         0 0      03           Newell

### 256XL problems

With a Bucholz/Peterson/Rambo 256XL you can run into a significant problem enabling the third extra block. If your software is set up for a Newell, not only will you not be able to use the third block, but any attempt to do so results in various other main banks showing up as main bank 01. What you end up with is a mapping like the following which results from attempting to access extended bank x08 using Newell bit patterns:

          Main
         |---| ---------------- 64k blocks--------------------
         |m00|      00       |      01       |      02       |
         |---|------------------------------------------------
         |m00|x00|x01|x02|x03|x04|x05|x06|x07|x08|x09|x0A|x0B|
         |---|------------------------------------------------
         |m02|             Extended RAM Banks (hex)
         |---|
         |m03|
         |---|

Obviously, this is a prelude to disaster as trying to write to bank x08 will overwrite DOS or the OS low RAM storage. Similarly, attempting to write to bank B could write to the hardware chips. With the Newell upgrade, wrong bit patterns enable Antic access to the extra RAM which should be harmless as the display list and screen RAM are rarely in this address range. It still however, restricts extra RAM usage to 128k.

So if you're having RAMdisk problems with a 256XL, odds are you're using the wrong software. ICD put out separate versions of their RD handler to address this and you need to obtain the correct one. With MYDOS you won't have a problem if you use the default RD configuration (menu item "O") but if you select a custom sequence or enter one manually, you must be aware of which banks are available.

### Up to a Meg

If you've been keeping count, you can see there's no more bits available in PORTB. To get past 256k, the designers decided to take over the BASIC bit, and to go to a meg, the Self Test bit.

         b if 0 enables the second 256k
         t if 0 enables the second 512k

If both of these are high, you're in the first 256k. If bit 7 is low and bit 1 high, you're in first half of the second 512k (third 256k), etc.

### Before you upgrade

You might run into a problem with bit 07 if you have installed a custom operating system chip: It may be used, by the chip, for other purposes such as switching in its own RAMdisk or display handler. Check this out before investing in an upgrade past 576K. Also, there are some XE's on which the 1-meg upgrade does not work. I have not experienced this personally but have read message traffic about it which implicates the model of the Antic chip installed. If you're unsure, ask questions first.

### Accessing the banks

Rather than figure bit patterns, it's a lot simpler to use a table. The one following gives the access value for every possible bank up to 1 megabyte. The values are with the OS on and BASIC (if present) off. They are given in hex and are exactly the values needed to set up a MYDOS RAMdisk for the banks you have and wish to assign to the RD.

         RAM BANK ACCESS TABLE 64-1024K

          PORTB     Available on Type

        E3,E7,EB,EF   1,2,3,4,5,6,7,8,9
        C3,C7,CB,CF     2,3,4,5,6,7,8,9
        A3,A7,AB,AF     2,  4,5,6,7
        83,87,8B,8F       3,4,5,6,7
        E1,E5,E9,ED           5,6,7
        C1,C5,C9,CD           5,6,7
        A1,A5,A9,AD           5,6,7,8
        81,85,89,8D           5,6,7,8
        63,67,6B,6F             6,7
        43,47,4B,4F             6,7
        23,27,2B,2F             6,7
        03,07,0B,0F             6,7
        61,65,69,6D               7
        41,45,49,4D               7
        21,25,29,2D               7
        01,05,09,0D               7

        Type          Upgrade Mapping
         1 =  130 XE
         2 =  256 XL Bucholz/Peterson/Rambo
         3 =  256 XL Newell
         4 =  320 XE Peterson
         5 =  576 XE    "
         6 =  832 XE    "
         7 = 1088 XE    "
         8 =  832k+     " (BASIC on)
         9 =  576 XE    "    "    "

Note 1: The documentation by Scott Peterson on his upgrades indicates that only 128k is available in XE-compatible mode (Types 8/9 above). Two more 64k blocks are available as indicated with 832 or 1088k machines though not all RD handlers will find and use them.

Note 2: Based on an article by Jeff McWilliams in the April 1993 edition of AC, it appears the Newell 1-meg upgrade is mapped similarly to the Peterson.

Note 3: With version 2.1 of the SpartaDOS Wedge, Ed Bachman describes a simple fix for Peterson upgrades to reenable Antic access to extended RAM. It is definitely worth checking out, even if you don't use SpartaDOS.

### Major RAM - Major glitch

Taking the BASIC bit caused a serious problem which has not been well documented. I found it the hard way: trash and crash. When the computer boots, the OS does not test to see if BASIC exists and is available; it simply assumes it is. The result is that even though pointers for the screen and so on are correctly set, bit 1 in PORTB is set or cleared based solely on the Option key. The BASIC flag is similarly set so that PORTB is restored to the booted value on Reset.

With a 512k+ upgrade this means the instant you enable extra RAM by dropping bit 4 in PORTB, you're in the second 256k instead of the first. If you use your extra memory only as a RAMdisk this mixup might not cause a problem depending on the handler code. But unless it's fixed, before using any other program which accesses extra RAM, 99% of them will trash your RD if they don't completely crash the computer.

### Fixing it

You can't just correct PORTB since Reset restores it. You have to get a non-zero value into BASICF (1016 or $03F8). The problem is compounded under SpartaDOS which maintains an eight-byte stack of PORTB values for exit from various internal routines. One solution is to hold Option every time you boot. Under SpartaDOS with no cartridge, a BASIC OFF followed by a Reset should fix it. With a cartridge stuff a non-zero value into BASICF and press Reset.

I always build in a test routine to programs which are going to manipulate RAM or ROM to ensure that BASICF and PORTB are correctly set if BASIC is off or absent. In addition, I search for and correct the stack in Sparta. BobTerm 1.22 corrects the Sparta stack for version 3.2d or f but, as it uses absolute addressing, this might lead to problems with different Sparta versions. So, if you have a problem with BobTerm and Sparta, the answer might be to switch to version 3.2d/f.

The way I find the stack in Sparta is to look for a LDA PORTB instruction, followed by a STA ADDRESS,X where ADDRESS will be the address of the stack. With version 3.2+ you should find it on page 7; with version 2.3 it's on page 9. Once found, it's a simple matter to set bit 1 in each of the eight values correctly.

### Auxiliary ROM control

Program control of Atari BASIC, OSS Super Cartridges, the R-Time 8 cartridge and, in a limited fashion, SpartaDOS X, is fairly simple but for one fact: there's nearly no documentation available on the subject. What I present here is gleaned from a bit of disassembly and a lot of experimentation (pretentious word for "try it until it don't crash").

Note that all following references to a cartridge being "present" imply that it is turned on--if it's plugged in but turned off, consider it "absent". You may always consider the RT8 to be absent unless you're actually trying to access it. First let's look at the addresses used for, or in conjunction with, auxiliary ROM/RAM control.

PORTB has already been covered; just keep in mind the function of the BASIC bit.

BASICF is a flag in low memory to tell the OS, on system reset, how to set bit 1 of PORTB. If this flag contains any non-zero value the BASIC ROM will be disabled.

TRIG3 is an address on the GTIA chip which was used for joystick trigger #3 on the 400/800. On the XL/XE it is a cartridge status indicator; if a cartridge is present it reads 1, otherwise it will be 0. There is no other possible reading at this address.

GINTLK is set, on boot, by the OS and is a copy of TRIG3. The OS compares GINTLK with TRIG3 during the deferred vertical blank interrupt and, if the two don't match, goes into a "soft" lockup (i.e. a reset will re-boot).

CARTCK holds a checksum, calculated on boot, by the OS. On a reset, if a cartridge is present as signalled by TRIG3, the OS re-calculates the sum and compares it with CARTCK. If the two don't match, the OS assumes you've pulled or inserted a cartridge and immediately re-boots. Note that Mapping the Atari is vague on this: it applies to all XL/XE's, not just the 1200.

CARTCK, TRIG3 and GINTLK are effective for all cartridges except (in part) the RT8. One other important thing to note is that the TRIG3/GINTLK comparison occurs during the deferred vertical blank. This means you can fool around with a cartridge to your heart's content as long as the stage two vblank doesn't occur and you don't hit Reset. You can prohibit vblank2 in any of three ways: disable all NMI's, set CRITIC to a non-zero value or, most simply, use a SEI opcode.

The hardware address range for all cartridge control is $D500-$D5FF. Within that page, OSS cartridges use $D500-$D50F, the CSS MUX OS uses $D570-$D57F, the RT8 uses $D5B8-$D5B9, and SDX uses $D5E0-$D5EF. This sounds straightforward; unfortunately it isn't.

### Atari-type Cartridges

I made no mention of Atari cartridges in the address ranges because once you stick one of the beasts into the slot, your only control over it comes with the power switch or by using SDX. An Atari cartridge can not be turned off by software unless SDX is present (even if turned off). SDX can control one because it sits between the computer and the cartridge and can, thereby, zap it electronically. However, the foregoing discussion of TRIG3 and GINTLK remains fully applicable.

### OSS Super Cartridges

Though the control range is $D500 to $D50F, the cartridge address decode logic is only four bits wide. This means that any access (read from, write to, or otherwise manipulate) a $D5xy address affects the cartridge which ignores the "x". OSS cartridges react to the whole $D500 page based on the low four bits of the address.

To enable an OSS cartridge bank, add the bank number to $D500 and access that address (i.e. for bank n, STA $D50n, LDA $D50n, STA $D500,X where the x register holds n, etc.) In theory, a cartridge should be able to contain up to sixteen 8k banks and still allow you to turn it off. In practice, they contain two or three switchable 4k banks and one "master" 4k bank.

For OSS cartridges, the ROM bank number is found at location $AFFF. Valid values at $AFFF are 0, 3 and 4 for Action! and 0, 1 and 9 for MAC/65. Other bank values produce varying results. MAC/65 ignores bits 1 and 2 so any value from 0 to 7 results in selection of either the odd or even bank. With Action! attempts to select other bank numbers result in selection of one of the real ones or in selection of nothing i.e. a monitor shows a pile of $AF's in the $AF page just as when you examine page $D7 and get $D7 at all addresses. BASIC XE has banks 0, 1 and 9 but bank 9 is RAM. In bank 9, the BXE cartridge is off but TRIG3 stays high; a sneaky way to avoid having to worry about GINTLK while using the RAM under the cartridge.

There are two constants for cartridges: Addressing bit 3 alone turns cartridge ROM off and, bank 0 is the bank in which the cartridge boots and initializes. Here are "maps" of the banks in two cartridges:

    MAC/65
    0123456789ABCDEF: Bank Selection
    rrrrrrrr r r r r: r=rom, empty=ram
    01010101 9 9 9 9: bank #
    01       9      : valid rom banks

    Action!
    0123456789ABCDEF: Bank Selection
    rrrrrr r        : r=rom, empty=ram
    000340 3        : bank #
    0  34           : valid rom banks

### OSS Cartridge Examples

Following are several examples of cartridge and BASIC control with SDX not present. I'll start with equates for all examples from Mapping the Atari (XL edition):

    WARMST = $08
    BOOT?  = $09
    CRITIC = $42
    RAMTOP = $6A
    COLDST = $0244
    CARTCK = $03E8
    BASICF = $03F8
    GINTLK = $03FA
    TRIG3  = $D013
    PORTB  = $D301
    NMIEN  = $D40E
    EDITRV = $E400

Here's the simplest: turn off a cartridge and enable BASIC assuming both are actually present.

     SEI         Kill stage 2 vblank
     STA $D508   Kill any cartridge
     LDA PORTB
     AND #$FD    Drop basic bit
     STA PORTB
     LDA TRIG3   This should be 0
     STA BASICF  Flag it and
     STA GINTLK  correct the cart shadow
     CLI         Enable stage 2 vblank

    Now let's access RAM under a cartridge:

     SEI         Kill stage 2 vblank
     LDA PORTB
     PHA         Save Portb
     ORA #$02    Kill basic rom
     STA PORTB
     LDA #$08    Assume no cartridge.
     LDX TRIG3   Check assumption.
     BEQ GOTBNK  Go if none or off,
     LDA $AFFF   else get the bank
    GOTBNK
     PHA         Save cartridge bank.
     STA $D508   Kill any OSS cartridge
     LDA TRIG3   Set the shadow before
     STA GINTLK  the stage 2 vblank!
     CLI         Enable stage 2 vblank

    Do whatever in the RAM, then restore the previous status:

     SEI         Kill stage 2 vblank
     PLA         Recover cartridge bank
     TAX         and restore the cart
     STA $D500,X to it's prior status
     LDA TRIG3   Reset Gintlk to
     STA GINTLK  correct status
     PLA         Restore prior Basic
     STA PORTB   rom status
     CLI         Enable stage 2 vblank

    Just turning a cartridge off is simple:

     SEI         Kill vblank 2
     STA $D508
     LDA TRIG3   Make sure it went
     STA GINTLK  off and flag it
     CLI         Enable stage 2 vblank

    Cold-starting an OSS cartridge is only slightly more complex:

     SEI         Kill vblank 2
     STA $D500   Enable cart bank 0
     LDA TRIG3   Set the shadow
     STA GINTLK  correctly
     LDA PORTB
     PHA
     ORA #$01
     STA PORTB   Ensure OS is on
     CLC         Calculate the
     LDX #0      checksum
     TXA         for reset.
    CSLOOP
     ADC $BFF0,X Note the sum
     INX         includes the
     BNE CSLOOP  first 240 bytes
     STA CARTCK  of the OS ROM.
     PLA         Restore any RAM
     STA PORTB   OS (or Sparta)
     CLI         Enable stage 2

After cold-starting an OSS cartridge or BASIC, set WARMST to 0 to flag a boot so that the buffer pointers are cleared (if you don't, you can, for example enter BASIC, type LIST and get an endless display of zeros and/or a lockup). Then initialize the ROM. With Action! and BASIC this doesn't matter as the initialization routines just RTS; with BASIC XE I'm not sure; with MAC/65 it's required. To initialize any cartridge:

     LDX #$FF    Say we're on a boot
     STX COLDST  and make sure all
     INX         flags reflect this
     STX WARMST
     INX
     STX BOOT?
     JSR INIT    Go do it
     LDX #$FF    Say we're back to
     STX WARMST  normal status, i.e.
     INX         what happens on Reset.
     STX COLDST  Note that some or all
     INX         carts play with some
     STX BOOT?   of these flags!
     RTS

    INIT
     JMP ($BFFE) Cartridge init vector

After enabling or disabling a cartridge or BASIC, you also have to ensure top of RAM and screen pointers are correct. To do this, execute a "GRAPHICS 0". In machine language terms, you set RAMTOP and then close and re-open channel 0 to the "E:" device. You can do this in the traditional manner via CIOV or more simply by calling the following subroutine with the accumulator holding $C0 if turning ROM off and $A0 if turning it on.

    GRAPH0
     STA RAMTOP Either $A0 or $C0
     LDX #0     Indicate channel 0
     LDY #2     Point to Close vector
     JSR EDO
     LDY #0     and now to Open vector
    EDO
     LDA EDITRV+1,Y
     PHA
     LDA EDITRV,Y
     PHA
     RTS

Be aware that turning off BASIC XE does not free up the RAM under the cartridge if you intend to later restore the cartridge. BXE uses that RAM as well as that under the OS floating point routines and also (undocumented) sets an interrupt vector in the last page of RAM ($FFFx).

One final note on turning ROM off or on: following the Graphics 0, an RTS under Sparta will usually lock up the keyboard requiring a reset. Sparta installs its own E: handler so when you use the OS handler to reopen E: Sparta's vectors are no longer valid. The simple way around this is to exit via a JMP (DOSVEC). This is probably a good idea with any command-processor FMS where you can use a batch file instead of an autorun to set things up.

### The SpartaDOS X Cartridge

SDX boots in bank 0 but normally works in bank 1 with one subroutine call back to bank 0 via low RAM which I suspect is used to load files from CAR: The cartridge contains eight different ROM banks (0 to 7), but I have not discovered any single location containing a bank identifier and I doubt there is one as its existence essentially would mean a "hole" in the middle of each ROMdisk bank. The control address for the X cartridge is $D5E0 used similarly to $D500 with an OSS cartridge.

The following code will leave the currently selected bank in the Y register with version 4.20. With other versions, you're on your own.

     LDA $A004
     LDY #7
    LOOP
     CMP XBANK,Y
     BEQ GOTIT
     DEY
     BPL LOOP
     LDY #$01    Can't figure, make it 1
    GOTIT
     RTS

    XBANK
     .BYTE $32,$1F,$02,$1D
     .BYTE $D4,$1C,$61,$56

The ROMdisk directory is at the beginning of bank 2 and follows normal Sparta format except that the address of the first sector map is the actual starting location in ROM of the stored program. The first two entries look like this:

     .BYTE $08   Status: In use
     .WORD 16384 Start: Bank 2 Offset 0
     .WORD 598   Length
     .BYTE 0     Length (high byte)
     .BYTE "MAIN       "
     .BYTE 1,1,70 Date
     .BYTE 251,0,0 Time
    ;
     .BYTE $08   Status: In use
     .WORD 16982 Start: Bank 2 Offset 598
     .WORD 7288  Length
     .BYTE 0     Length (high byte)
     .BYTE "SPARTA  SYS"
     .BYTE 6,2,89 Date
     .BYTE 15,28,40 Time
    ;

To convert the starting address to a bank and offset within the bank:

          bank = int(address/$2000)   and
        offset = address-$2000*bank

CAR.COM uses the following combinations to control all three ROMs in the cartridge area. The values under "SDX" and "OSS" are offsets from $D5E0 and $D500 respectively and those under BAS are the value in bit 1 of PORTB. The "on" under the OSS column is the value found at $AFFF before the cartridge was last turned off and used to reenable it. The $0C value for SDX is what causes it to latch any cartridge off and the $08 makes it transparent so that the cartridge ROM is accessible.

         SDX  OSS  BAS  OPERATING CONDITION
         $01  $08   1   in DOS or low RAM
         $0C  $08   1   in high RAM (X.COM)
         $0C  $08   0   in BASIC
         $08   on   1   in cartridge

If you're going to play with SDX banks remember that any read or write to a $D5Ex address will affect an OSS cartridge and TRIG3. Since TRIG3 is affected, GINTLK and CARTCK also come into play. So the example given of how to access RAM under a cartridge needs to be modified if SDX is present. Let's look at a subroutine to access RAM in the cartridge space taking in the possibility of the presence of BASIC, SDX, or an OSS cartridge.

    ROMCTL
     LDA PORTB
     PHA
     ORA #$02    Any Basic rom off
     STA PORTB
     LDA RAMTOP  This might be easy
     CMP #$A0+1
     BCC NOLUCK  Not quite.
     PLA         See note a. following
     STA BASICF  for an explanation
     JMP DOSTUFF

    NOLUCK
     LDA TRIG3   Is a cart present?
     BNE CART    Yes, go.
     JSR DOSTUFF Still fairly easy
     PLA
     STA PORTB
     RTS

    CART
     PLA         See note a. following
     STA BASICF  for an explanation.

     SEI         Kill stage 2 vblank
     LDA $AFFF   Get OSS bank number
     PHA         Save it
     STA $D5E8   Turn off both carts
     JSR DOSTUFF

We know a cartridge was on. Now we have to restore it correctly.

     PLA         Recover bank number
     CMP #$10    Valid for OSS? (note c.)
     BCS SDX     No, must be SDX
     TAY
     STA $D500,Y Restore OSS cart bank
     BCC CARXIT  Go always
    SDX
     STA $D5E1   Enable SDX normal bank
     STA $D508   Kill OSS cart (note b.)
    CARXIT
     CLI
     RTS

a. The reason for discarding the PORTB entry value is to allow for 512k+ RAM expansions. As mentionned previously, the OS doesn't know extra RAM exists and has no way of knowing BASIC may not exist on large upgrades. As a result, it sets PORTB and BASICF based solely on the Option key at boot and uses BASICF to determine which status to restore on a reset. On large RAM upgrades this leads to major problems for programs using extra RAM as the program can end up in the wrong 256k bank. Unless BASIC is actually on, it is always advisable to flag it off and to set its bit high in PORTB.

b. We knew a cartridge was on or we never would have got to that portion of the code. As it wasn't the OSS cartridge, it had to be SDX. But, because the dumb OSS cartridge reacts to the $D5E1 address, we had to turn it off again after enabling SDX. For the same reason, a single access of $D5E8 was sufficient to turn both off.

c. The comparison of the bank number to 16 to determine its validity as an OSS bank number is that used by ICD in the code for the RT8 handler. The test is, I believe, made on the assumption that the X cartridge is in bank 1 where the value at $AFFF is 87 for version 4.20. There are two SDX banks where values less than 16 are found at $AFFF, namely 0 (value 7) and 4 (value 3).

### The R-Time 8

Control of the RT8 is built into all versions of Sparta from 3.2 on. As far as I know, all you can do with the RT8 is set or get time and date information. The only problem in doing this is that accessing the RT8 registers will affect an OSS cartridge. Because of this the RT8 has two identical user accessible registers $D5B8 and $D5B9. According to the RT8 source, addressing $D5B8 will turn off a cartridge and $D5B9 will turn one on.

The RT8 has seven internal registers which work in binary coded decimal. Starting from #0 they are: seconds, minutes, hour, day of month, month, year, and day of the week (#6). Seconds and minutes range from 0-59, hours from 0-23, day from 1-31, month from 1-12, year from 0-99. Day of the week ranges from 0 (Saturday) to 6 (Friday). When you read or write one of these registers the sequence is always the same:

1. Wait until the RT8 is not busy.

2. Store a value from 0 to 6 into $D5B8 or $D5B9 indicating the register you wish to address.

3. Read/write the same address to get/set the most significant digit (the low four bits are the valid data).

4. Read/write the same address to get/set the least signifigant digit (the low four bits are the valid data).

The source code released by ICD indicates that reading a register should be repeated up to three times accepting two values that match or, failing a match, the first one. When setting a register, it recommends reading it immediately afterward to ensure the value was really accepted and allowing 10 tries.

Here's one way to read and write RT8 registers without worrying about an OSS cartridge. Much of this is from the source released by ICD. In this example, the buffer is set up in the same order as the RT8 registers. With Sparta, you would have to cross refer to the order in which DOS saves time and date and keep a separate byte for day of the week.

     *= $F0   Floating point zero page
    TEMP1
     *= *+1
    TEMP2
     *= *+1
    RETRY
     *= *+1
    BUFFER
     *= *+1   Seconds
     *= *+1   Minutes
     *= *+1   Hours
     *= *+1   Day of month
     *= *+1   Month
     *= *+1   Year
     *= *+1   Day of week

     *= WHEREVER
     SEI         Kill vblank2
     LDA $AFFF   Get any cart bank
     CMP #$10    Is it valid?
     BCC SAVBNK  Yes go, else use
     LDA #$08    the "off" value.
    SAVBNK
     PHA         Save cart bank

    Verify rt8 present and working

     JSR READ    Get seconds
     CMP #60
     BCS GLITCH  if >59 then error
     STA TEMP1
     LDA RTCLOK+2
     ADC #90
    WAIT
     CMP RTCLOK+2 Wait about 1.5"
     BNE WAIT
     JSR READ     Read again
     CMP TEMP1    Same as last?
     BEQ GLITCH   Yes, not working
     SEC
     SBC TEMP1    Ensure <3
     BCS CHECK3   It is
     ADC #60      else did it roll over?
    CHECK3
     CMP #3
     BCC RT8OK    Yes, rt8 is ok
    GLITCH
     LDA # <RT8ERR Set for error message
     LDX # >RT8ERR
    EXIT
     STA ICBAL
     STX ICBAH
     LDA #9      Print to eol
     STA ICCOM
     STA ICBLH   Plenty of length
     PLA
     TAX         Restore cart bank
     STA $D500,X
     CLI         restore vblank2
     LDX #0      Select channel 0
     JMP CIOV    Exit with message

    First read the clock regs into the buffer

    RT8OK
     LDX #6    Point to day of week
    RCLOOP
     JSR READ
     DEX
     BPL RCLOOP

    Change values you want in the buffer and
    then write it back to the clock

     LDX #6
    RCLOOP
     JSR WRITE
     BNE GLITCH Exit if write failed
     DEX
     BPL RCLOOP else do all 7
     LDA # <RT8SET Set success message
     LDX # >RT8SET
     BNE EXIT   Branch always
    RT8ERR
     .BYTE "RT8 Error",155
    RT8ERR
     .BYTE "RT8 Set",155

    ; Subroutine: wait til clock is
    ; not busy or exit on time out.
    ; Enter: x=clock reg to access (0-6)
    ; Exit: x unchanged, clock ready,
    ;       and clock register selected
    WAITCL
     LDY #$FF    Timeout value
    WAITC
     LDA $D5B8
     AND #$0F    If low nybble=0
     BEQ READY   clock not busy
     DEY
     BNE WAITC   Else time out
    READY
     STX $D5B8   Set reg #x to read/wrt
     RTS

    ; Subroutine: read rt8 reg once
    ;  In x=reg#
    ; Out a=byte x=reg#

    READ1
     JSR WAITCL
     LDA $D5B8   Get high byte
     LDY $D5B8   Get low byte
     AND #$0F    Convert bcd to hex
     STA TEMP1
     ASL A       Clears carry
     ASL A
     ADC TEMP1
     ASL A
     STA TEMP1   Temp1=(high*10)
     TYA         Add in low byte
     AND #$0F
     ADC TEMP1   Return byte in a
     RTS         Note c=0 x=x y=trig3

    ; Subroutine: read a clock register
    ; and accept best 2 of 3 readings
    ; or the first if none match.
    ;  in: x=reg#
    ; out: a=value(}

    READ
     JSR READ1
     STA TEMP1
     JSR READ1
     CMP TEMP1
     BEQ REXIT
     STA TEMP2
     JSR READ1
     CMP TEMP2
     BEQ REXIT
     LDA TEMP1
    REXIT
     RTS

    ; Subroutine: write clock register
    ; with value stored in buffer offset
    ; by x. Allow 10 tries.
    ;  in: x=reg#
    ; out: x=reg#, z flag set if ok

    WRITE
     LDA #9
     STA RETRY
    WRT2
     LDA BUFFER,X
     LDY #$FF      Convert to bcd
     SEC
    SUB10
     INY
     SBC #10
     BCS SUB10
     ADC #10
     PHA           low byte
     TYA
     PHA           high byte
     JSR WAITCL    y=trig3
     PLA           High byte
     STA $D5B8
     PLA
     STA $D5B8     Low byte
     JSR READ      Verify it set
     CMP BUFFER,X  correctly
     BEQ WRTXIT    It did!
     DEC RETRY
     BPL WRT2      Never 0 if failed
    WRTXIT
     RTS

### The Multiplexer! Operating System (MUX)

The MUX OS makes frequent access of registers in the $D57x range. A cursory glance at the ROM reveals it uses the following registers on a read-only basis 1, 6 and 7. Registers 2, 3, B, C and E are accessed as write-only while 0 is read/write. Every one of these addresses will affect an OSS cartridge. I found no indication in the MUX code that it makes any effort to accommodate a cartridge, GINTLK or TRIG3.

While I have managed to work around an SDX cartridge and an RT8 in controlling BASIC, OSS cartridges, and even Atari cartridges (with SDX present), I can see no way of doing so with the MUX OS. I believe the idea with the MUX is that once the plug's in the port, you can't use a cartridge anyway. That's kind of unfortunate as it denies you use of a cartridge and access to the built-in monitor. As I don't have a MUX to experiment with, I leave that to someone else.

### Cold and Warm Starting and Parallel Devices (PD's)

If you turn off an OSS cartridge and then re-boot under most versions of SpartaDOS, you get a "soft" lockup as DOS will enable a cartridge as part of its boot process in testing for BASIC XE. Simply hit Reset and the computer will boot again with the cartridge on. If you want to reenable the SDX cartridge after having turned it off using the COLD command, the method should be obvious by now. What is not obvious are a few other addresses and quirks in warm and cold starting.

On boot the OS calculates ROM checksums and compares them to ones stored in the OS itself. If these don't match, boot doesn't happen; you end up staring at the Self Test screen and a red bar under the heading "ROM". This can easily occur on a system with a PD because cold starting the computer does not cold start the PD any more than it does a cartridge. If PD ROM is enabled, as for a modem handler on a BBS, and you attempt to cold start, you will inevitably end up in Self Test because the ROM checksum will fail. It is supposed to include the floating point ROM at $D800, but instead gets a bank of the PD ROM.

Finally, if you're just going to warm start, decide whether or not you want to emulate a press of the Reset key. Jumping to the warm start vector at $E474 is not the same as pressing the key; the vector points past the hardware initialization routines. If you want to ensure you clear out all garbage (left over player missiles, keypress, etc.) you have to use the chip reset vector.

The following routine has varying results dependant on the entry point. To enable SDX, enter at XCART. To enable an OSS cart alone, enter at CART. To just cold start without touching the cartridges, enter at COLD. To simulate a press of Reset, enter at WARM.

    XCART
     SEI         Always before $D5xx access
     STA $D5E0   Enable SDX
    CART
     SEI         Again, dependant on entry
     STA $D500   Enable OSS cart
    COLD
     DEC COLDST  Force a boot
    WARM
     SEI         Just in case
     LDX #0
     STX NMIEN   Ditto
     STX $D1FF   Enable floating point
     STX $D1E2   Kill MIO RAM (this and
     DEX         following just in case)
     STX $D1BC   turn off BlackBox RAM
     STX PORTB   Ensure ROM OS is on to go
     JMP ($FFFC) through chip reset vector

I won't go any further on this as the Black Box and MIO are, unlike Atari and OSS products, fairly well documented. Whew, when I started this I never thought it would turn into such a monster nor did I think it would take so long to come up with all the ins and outs. Now that you know how easy it is to make use of the RAM under cartridges, under the OS and in extra memory, I look forward to seeing some practical utilities. Here's few suggestions:

"Pop-up" help screens for use in MAC/65, Action! or BASIC.

An 8k RAM cache.

A "pop-up" calculator.

A resident DUP.SYS.

### Late notes

I recently was browsing through some old computer magazines and came across an article by Bill Wilkinson dealing with 130XE RAM control. In it he stated that 16k Atari cartridges such as Atari Writer Plus occupy the address space from $8000 to $BFFF rather than using bank switching.

As a final addition to this text, I've tacked on part 4 which is MAC/65 source code to produce two simple COM files to dump cartridges to disk for examination. The programs are not sophisticated but will do the trick.

Revision: 16 Feb 96

### Later note

A message by Bill Wilkinson posted on comp.sys.atari.8bit on 12 August 95 validates much of what has been said and revealed a bit more of cartridge construction. His memory was a bit off on address ranges, but his stuff about 4k banking is right on. I dumped MAC/65 and Action! to disk files and verified that, in each cartridge, the code from $B000-$BFFF is identical in all three banks. In effect, this indicates a mapping as follows (using MAC/65 as an example):

         Addr: $D500   $D501   $D509   $D508
         $A000 ------  ------  ------  ------
               | c0 |  | c1 |  | c9 |  |RAM |
         $AFFF ------  ------  ------  ------
         $B000 ------  ------  ------  ------
               | cc |  | cc |  | cc |  |RAM |
         $BFFF ------  ------  ------  ------

Where: "Addr" is the access address used to enable the configuration, "c0" etc. is the switchable 4k bank and "cc" is the common 4k bank. Substituting "3" and "4" for "1" and "9" above would produce a map of the Action! cartridge.

### Utils

cartdump.m65 (3 KB M65)
sdxdump.m65 (3 KB M65)
Back (c) 1998-2004 Jindroush Last modified: Mon Mar 18 13:31:20 2002