BizHawk/BizHawk.Emulation/Consoles/Atari/docs/Atari_Mappers.txt

1410 lines
53 KiB
Plaintext

Copied from:
http://blog.kevtris.org/blogfiles/Atari%202600%20Mappers.txt
Mostly Inclusive Atari 2600 Mapper / Selected Hardware Document
---------------------------------------------------------------
03/04/12
Kevin Horton aka kevtris
---
Version 1.00
Things covered in this document:
--------------------------------
"original" mappers from when the system was in production:
2K
4K
CV (Commavid)
F8 (Atari 8K)
F6 (Atari 16K)
F4 (Atari 32K)
FE (Activision 8K)
E0 (Parker bros. 8K)
3F (Tigervision 8K)
FA (CBS RAM Plus 12K)
E7 (M-network 16K)
F0 (Megaboy 64K)
UA (UA Ltd 8K)
"homebrew" mappers which are more recent:
3F (Enhanced Tigervision style, up to 512K)
3E (up to 512K of ROM, 256K of RAM)
0840 (Econobanking)
MC (Megacart)
EF (16K)
X07 (Atariage)
4A50 (no name)
Atari 2600 peripherals:
Spectravideo Compumate
Pitfall 2 DPC
Supercharger
Supercharger tape format
Supercharger demo unit
---
"original" 2600 Mappers
-----------------------
2K (no name)
-----
2K of ROM. no bankswitching.
4K (no name)
-----
4K of ROM. no bankswitching.
CV Commavid
-----
This was used by Commavid. It allowed for both ROM and RAM on the cartridge,
without using bankswitching. There's 2K of ROM and 1K of RAM.
2K of ROM is mapped at 1800-1FFF.
1K of RAM is mapped in at 1000-17FF.
The read port is at 1000-13FF.
The write port is at 1400-17FF.
F8 (Atari style 8K)
-----
This is the fairly standard way 8K of cartridge ROM was implemented. There are two
4K ROM banks, which get mapped into the 4K of cartridge space. Accessing 1FF8 or
1FF9 selects one of the two 4K banks. When one of these two addresses are accessed,
the banks switch spontaniously.
ANY kind of access will trigger the switching- reading or writing. Usually games use
LDA or BIT on 1FF8/1FF9 to perform the switch.
When the switch occurs, the entire 4K ROM bank switches, including the code that is
reading the 1FF8/1FF9 location. Usually, games put a small stub of code in BOTH banks
so when the switch occurs, the code won't crash.
F6 (Atari style 16K)
-----
This is a fairly standard 16K bankswitching method. It works like F8, except there's
four 4K banks of ROM, selected by accessing 1FF6 through 1FF9. These sequentially
select one of the 4 banks. i.e. 1FF6 selects bank 0, 1FF7 selects bank 1, etc.
F4 (Atari style 32K)
-----
Again, this works like F8 and F6 except now there's 8 4K banks. Selection is performed
by accessing 1FF4 through 1FFB.
FE (Activision special)
-----
Activision used this method on only three games: Decathlon, Robot Tank, and the
prototype Thwocker. This mapper is one of the more interesting ones in that it uses
stack access to select banks. It is composed of two 4K banks, similar to F8.
Unlike F8, however, switching occurs when the stack is accessed.
This mapper allows for "automatic" bankswitching to occur, using JSR and RTS. The
addresses for all JSRs and RTS' are either Fxxx or Dxxx, and the mapper uses this to
figure out which bank it should be going to.
The cycles of a JSR are as such:
1: opcode fetch
2: fetch low byte of address
3: read 100,s : garbage fetch
4: write 100,s : PCH, decrement S
5: write 100,s : PCL, decrement S
6: fetch high byte of address
The cycles of an RTS are as such:
1: opcode fetch
2: fetch next opcode (and throw it away)
3: read 100,S : increment S
4: read 100,S : pull PCL from stack, increment S
5: read 100,S : pull PCH from stack
The chip can determine what instruction is being executed by watching the data and
address bus.
It watches for 20 (JSR) and 60 (RTS), and accesses to 100-1ff:
(opcode cycles)
20 (opcode)
add low (new add low)
stack (garbage read)
stack (push PCH)
stack (push PCL)
add high (new add hi) : latch D5. This is the NEW bank we need to be in.
60 (opcode)
xx (garbage fetch)
stack
stack (pull PCL)
stack (pull PCH) : latch D5. This is the NEW bank we need to be in.
Using emulators or similar there is a large cheat that can be used. A13 can be used
to simply select which 8K bank to be in.
E0 (Parker Bros)
-----
Parker Brothers used this, and it was used on one other game (Tooth Protectors). It
uses 8K of ROM and can map 1K sections of it.
This mapper has 4 1K banks of ROM in the address space. The address space is broken up
into the following locations:
1000-13FF : To select a 1K ROM bank here, access 1FE0-1FE7 (1FE0 = select first 1K, etc)
1400-17FF : To select a 1K ROM bank, access 1FE8-1FEF
1800-1BFF : To select a 1K ROM bank, access 1FF0-1FF7
1C00-1FFF : This is fixed to the last 1K ROM bank of the 8K
Like F8, F6, etc. accessing one of the locations indicated will perform the switch.
3F (Tigervision)
-----
Traditionally, this method was used on the Tigervision games. The ROMs were all 8K, and
there's two 2K pages in the 4K of address space. The upper bank is fixed to the last 2K
of the ROM.
The first 2K is selectable by writing to any location between 0000 and 003F. Yes, this
overlaps the TIA, but this is not a big deal. You simply use the mirrors of the TIA at
40-7F instead! To select a bank, the games write to 3Fh, because it's not implemented
on the TIA.
The homebrew community has decided that if 8K is good, more ROM is better! This mapper
can support up to 512K bytes of ROM just by implementing all 8 bits on the mapper
register, and this has been done... however I do not think 512K ROMs have been made just
yet.
FA (RAM Plus)
-----
CBS Thought they'd throw a few tricks of their own at the 2600 with this. It's got
12K of ROM and 256 bytes of RAM.
This works similar to F8, except there's only 3 4K ROM banks. The banks are selected by
accessing 1FF8, 1FF9, and 1FFA. There's also 256 bytes of RAM mapped into 1000-11FF.
The write port is at 1000-10FF, and the read port is 1100-11FF.
E7 (M-Network)
-----
M-network wanted something of their own too, so they came up with what they called
"Big Game" (this was printed on the prototype ASICs on the prototype carts). It
can handle up to 16K of ROM and 2K of RAM.
1000-17FF is selectable
1800-19FF is RAM
1A00-1FFF is fixed to the last 1.5K of ROM
Accessing 1FE0 through 1FE6 selects bank 0 through bank 6 of the ROM into 1000-17FF.
Accessing 1FE7 enables 1K of the 2K RAM, instead.
When the RAM is enabled, this 1K appears at 1000-17FF. 1000-13FF is the write port, 1400-17FF
is the read port.
1800-19FF also holds RAM. 1800-18FF is the write port, 1900-19FF is the read port.
Only 256 bytes of RAM is accessable at time, but there are four different 256 byte
banks making a total of 1K accessable here.
Accessing 1FE8 through 1FEB select which 256 byte bank shows up.
F0 (Megaboy)
-----
This was used on one game, "megaboy".. Some kind of educational cartridge. It supports
64K of ROM making it the biggest single production game made during the original run
of the 2600.
Bankswitching is very simple. There's 16 4K banks, and accessing 1FF0 causes the bank
number to increment.
This means that you must keep accessing 1FF0 until the bank you want is selected. Each
bank is numbered by means of one of the ROM locations, and the code simply keeps accessing
1FF0 until the bank it is looking for comes up.
UA (UA Ltd)
-----
This one was found out later on, lurking on a proto of Pleaides. It works with 8K of ROM
and banks it in 4K at a time.
Accessing 0220 will select the first bank, and accessing 0240 will select the second.
That's about it for the "Traditional" mappers that were used on commercial releases.
-----------------------------------
The 2600 programming community's been pretty busy with adding new mappers. Here's the list
of known (to me) mappers used on homebrew games.
3E (Boulderdash
-----
This works similar to 3F (Tigervision) above, except RAM has been added. The range of
addresses has been restricted, too. Only 3E and 3F can be written to now.
1000-17FF - this bank is selectable
1800-1FFF - this bank is the last 2K of the ROM
To select a particular 2K ROM bank, its number is poked into address 3F. Because there's
8 bits, there's enough for 256 2K banks, or a maximum of 512K of ROM.
Writing to 3E, however, is what's new. Writing here selects a 1K RAM bank into
1000-17FF. The example (Boulderdash) uses 16K of RAM, however there's theoretically
enough space for 256K of RAM. When RAM is selected, 1000-13FF is the read port while
1400-17FF is the write port.
0840 (Econobanking)
-----
This is another 8K bankswitching method with two 4K banks. The rationale is that it's
cheap and easy to implement with only a single 74HC153 or 253 dual 4:1 multiplexer.
This multiplexer can act as a 1 bit latch AND the inverter for A12.
To bankswitch, the following mask it used:
A13 A0
----------------
0 1xxx xBxx xxxx
Each bit corresponds to one of the 13 address lines. a 0 or 1 means that bit must be
0 or 1 to trigger the bankswitch. x is a bit that is not concidered (it can be either
0 or 1 and is thus a "don't care" bit).
B is the bank we will select. sooo, accessing 0800 will select bank 0, and 0840
will select bank 1.
SB (Superbanking)
-----
This is the same as 0840, except A0-A6 are used instead of just A6, as so:
A13 A0
----------------
0 1xxx xBBB BBBB
By using 6 bits, the maximum ROM size has been increased from 8K to 256K.
MC (Megacart)
-----
This is the mapper for the "Chris Wilkson's Megacart".
Only four addresses are used to bankswitch on this one.
Up to 128K of ROM and 64K of RAM can be accessed.
1000-13FF is selected by address 3C
1400-17FF is selected by address 3D
1800-1BFF is selected by address 3E
1C00-1FFF is selected by address 3F
The value written determines what will be selected:
00-7F written will select one of the 128 1K ROM banks
80-FF written will select one of the 128 512 byte RAM banks
When a RAM bank is selected, the lower 512 bytes is the write port, while
the upper 512 bytes is the read port.
On accessing address FFFC or FFFD, the last 1K bank points to the last bank in ROM,
to allow for system initialization. Jumping out of the last bank disables this.
It's debatable how easy this system would be to implement on a real system.
Detecting when to disable the last bank fixing is difficult. The documentation
says:
"
Megacart Specification, Rev1.1
(c) 1997 Chris Wilkson
cwilkson@mit.edu
Because the console's memory is randomized at powerup, there is no way to
predict the data initially contained in the "hot addresses". Therefore,
hardware will force slot 3 to always point to ROM block $FF immediately
after any read or write to the RESET vector at $FFFC-$FFFD. Block $FF
must contain code to initialize the 4 memory slots to point to the desired
physical memory blocks before any other code can be executed. After program
execution jumps out of the boot code, the hardware will release slot 3 and
it will function just like any other slot.
"
Unfortunately, there's not an easy way to detect this. Just watching the address
bus won't work easily: Writing anywhere outside the bank 1C00-1FFF (i.e. bank
registers, RAM, TIA registers) will cause the switching to revert bank 3, crashing
the system.
The only way I can see it working is to disregard any access to addresses 3C-3F.
Emulators have it easier: they can simply watch the program counter, vs. the
address bus. An actual system doesn't have that luxury, unfortunately, so it must
disregard accesses to 3C-3F instead.
EF (no name?)
-----
This is a fairly simple method that allows for up to 64K of ROM, using 16 4K banks.
It works similar to F8, F6, etc. Only the addresses to perform the switch is
1FE0-1FEF. Accessing one of these will select the desired bank. 1FE0 = bank 0,
1FE1 = bank 1, etc.
X07 (Atariage)
-----
Apparently, this was only used on one cart: Stella's Stocking.
Similar to EF, there are 16 4K banks, for a total of up to 64K of ROM.
The addresses to select banks is below the ROM area, however.
The following TWO masks are used:
A13 A0
----------------
0 1xxx nnnn 1101
This means the address 80B selects bank 0, 81B selects bank 1, etc.
In addition to this, there is another way:
A13 A0
----------------
0 0xxx 0nxx xxxx
This is somewhat special purpose: Accessing here does nothing, unless one of the
last two banks are selected (banks 14 or 15). In that case, the new bank is:
111n i.e. accessing 0000 will select bank 14 (Eh, 1110b) while accessing 0040
will select bank 15 (Fh, 1111b). This allows for bankswitching by accessing
TIA registers at 00-3F or 40-7F without incurring any overhead.
4A50 (no name)
-----
Upon review, I don't think this method is terribly workable on real
hardware. There's so many problems that I kinda gave up trying to
count them all. Seems that this is more of a "pony" method than something
actually usable. ("pony" referring to "I want this, and that, and that, and
a pony too!")
One major problem is that it specifies that memory can be read and written
to at the same address, but this is nearly impossible to detect on a 2600
cartridge. You'd almost have to try and figure out what opcodes are being
run, and what cycle it's on somehow, all just by watching address and
data bus state. Not very practical.
The other problem is just the sheer volume of things it is supposed to do.
There's just tons and tons of unnecessary things like attempting to detect
BIT instructions, handling page wraps and other silly things.
This all supposidly fit into a Xilinx XC9536XL but I am not sure how the
chip could handle the RAM issue above at all. It almost needs to see R/W
and M2 (clock) to be able to properly do most of the things it's doing.
----------------------------------------------
Peripherals
-----------
Spectravideo Compumate Add-on
-----
This is more than just a cartridge mapper- it's also a "computer" add-on.
There's two 8K EPROMs soldered on top of each other. There's two short
wires with DB-9's on them which you plug into the two controller ports.
A 42 or so key membrane keyboard with audio in and audio out, and 1K of RAM.
Port A on the RIOT is used to run most of the functions on the Compumate:
7 0
---------
ACRE 31BB
A - Audio input from tape player
C - Audio out to tape player and 4017 CLK
R - 4017 RST, and RAM direction. (high = write, low = read)
E - RAM enable. 1 = disable RAM, 0 = enable RAM
3 - Row 3 of keyboard
1 - Row 1 of keyboard
B - 2 bit ROM bank number
All bits are outputs except for the 2 row inputs from the keyboard.
Unlike most things, the Compumate uses all three of the TIA inputs on each
joystick port (paddles and fire).
TIA inputs:
0 - function key
1 - pulled high thru 20K resistor
2 - pulled high thru 20K resistor
3 - shift key
4 - Row 0
5 - Row 2
Memory Map:
-----------
1000-1FFF : selectable 4K ROM bank (selected by D0, D1 on portA)
On powerup, the port is all 1's, so the last bank of ROM is enabled, RAM is
disabled.
when RAM is enabled:
1000-17FF : 2K of RAM. It's mapped into 1000-17FF. Unlike most 2600 carts,
bit 5 of portA controls if the RAM is readable or writable. When it's high,
the RAM is write only. When it's low, it is read only. There's no separate
read and write ports.
Keyboard:
---------
The keyboard's composed of a 4017 1 of 10 counter, driving the 10 columns of
the keyboard. It has 4 rows. The 4 row outputs are buffered by inverters.
Bit 5 of portA controls the reset line on the 4017. Pulling it high will reset
scanning to column 0. Pulling it low will allow the counter to be clocked.
Bit 6 of portA clocks the 4017. Each rising edge advances the column one
count.
There's 10 columns labelled 0-9, and 4 rows, labelled 0-3.
Column
0 1 2 3 4 5 6 7 8 9
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
| 7 | | 6 | | 8 | | 2 | | 3 | | 0 | | 9 | | 5 | | 1 | | 4 | 0
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
| U | | Y | | I | | W | | E | | P | | O | | T | | Q | | R | 1
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ Row
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
| J | | H | | K | | S | | D | |ent| | L | | G | | A | | F | 2
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
| M | | N | | < | | X | | C | |spc| | > | | B | | Z | | V | 3
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
Function and Shift are separate keys that are read by 2 of the paddle inputs.
These two buttons pull the specific paddle input low when pressed.
Because the inputs are inverted, a low indicates a pressed button, and a high
is an unpressed one.
The audio input/output are designed to drive a tape player. The audio output is
buffered through an inverter and 2 resistors and a capacitor to reduce the level
to feed it into the tape player.
The audio input is passed through a .1uf capacitor and is pulled to 1/2 supply
by two 20K resistors, then it goes through a hex inverting schmitt trigger to
square it up. This then runs into bit 7 of portA.
DPC (Pitfall 2)
-----
Back in the day, this game was da shizzle (and IMO still is). It did its trick via
a custom chip in the cartridge. Fortunately for us, there's a patent that describes
lots of the internal workings of the chip (number 4644495, "video memory system").
Interestingly, the patent shows the DPC as a *separate* device. You plug a
passthrough cartridge into your 2600, then plug the game cartridge into the
passthrough. Apparently, Activision thought that people wouldn't like this, or
there was some other reasoning behind it and they ditched that idea and went with
the DPC inside the cartridge.
Unfortunately for Activision, it was filed in January of 1984, during the height of
the crash. The inventor is listed as David Crane.
OK, enough background. Now onto the meat:
The DPC chip is just 24 pins, and needs to pass through the chip enable to the
game ROM on the cartridge, so it can only address 2K of memory. This means the
DPC shows up twice in the address space, once at 1000-107F and again at 1800-18FF.
There's been some discussion about the pitch of the music generated by this chip,
and how different carts will play the music at different pitches. Turns out, on the
cart, the frequency is determined by a resistor (560K ohms) and a capacitor integrated
onto the die of the DPC chip itself. The resistor is a 5% tolerance part, and the
process variations of the DPC itself will control the frequency of the music produced
by it.
If you touch the resistor on the cartridge board, the music pitch will drastically
change, almost like you were playing it on a theremin! Lowering the resistance makes
the music pitch increase, increasing the resistance makes the pitch lower.
It's extremely high impedance so body effects of you touching the pin makes it
vary wildly.
Thus, I say there's really no "one true" pitch for the music. The patent, however,
says that the frequency of this oscillator is 42KHz in the "preferred embodiment".
The patent says that it can range from 15KHz to 80KHz depending on the application
and the particular design of the sound generator. I chose 21KHz (half their preferred
value) and it sounds fairly close to my actual cartridge.
Address map:
Read Only:
1000-1003 : random number generator
1004-1005 : sound value (and MOVAMT value ANDed with draw line carry, with draw line add)
1006-1007 : sound value (and MOVAMT value ANDed with draw line carry, no draw line add)
1008-100F : returned data value for fetcher 0-7
1010-1017 : returned data value for fetcher 0-7, masked
1018-101F : returned data value for fetcher 0-7, nybble swapped, masked
1020-1027 : returned data value for fetcher 0-7, byte reversed, masked
1028-102F : returned data value for fetcher 0-7, rotated right one bit, masked
1030-1037 : returned data value for fetcher 0-7, rotated left one bit, masked
1038-103F : fetcher 0-7 mask
Write Only:
1040-1047 : fetcher 0-7 start count
1048-104F : fetcher 0-7 end count
1050-1057 : fetcher 0-7 pointer low
1058-105B : fetcher 0-3 pointer high
105C : fetcher 4 pointer high and draw line enable
105D-105F : fetcher 5-7 pointer high and music enable
1060-1067 : draw line movement value (MOVAMT)
1068-106F : not used
1070-1077 : random number generator reset
1078-107F : not used
random number generator
-----------------------
The random number generator is used on Pitfall 2 to make the eel flash between white and
black, and nothing else. Failure to emulate this will result in the eel not flashing.
It's an 8 bit LFSR which can be reset to the all 0's condition by accessing 1070-1077.
Unlike a regular LFSR, this one uses three XOR gates and an inverter, so the illegal
condition is the all 1's condition.
There's 255 states and the following code emulates it:
LFSR = ((LFSR << 1) | (~(((LFSR >> 7) ^ (LFSR >> 5)) ^ ((LFSR >> 4) ^ (LFSR >> 3))) & 1)) & 0xff;
Bits 3, 4, 5, and 7 are XOR'd together and inverted and fed back into bit 0 each time the
LFSR is clocked.
The LFSR is clocked each time it is read. It wraps after it is read 255 times. (The
256th read returns the same value as the 1st).
data fetchers
-------------
Internal to the DPC is a 2K ROM containing the graphics and a few other bits and pieces
(playfield values I think) of data that can be read via the auto-incrementing data
fetchers.
Each set of 8 addresses (1008-100F for example) return the data from one of the 8
data fetcher pointers, returning the data in a slightly different format for each.
The format for the 6 possible register ranges is as follows:
For the byte "ABCDEFGH" (bit 7 to bit 0) it is returned:
1008-100F: ABCDEFGH (never masked)
1010-1017: ABCDEFGH
1018-101F: EFGHABCD (nybble swap)
1020-1027: HGFEDCBA (bit reversed)
1028-102F: 0ABCDEFG (shifted right)
1030-1037: BCDEFGH0 (shifted left)
Reading from each set of locations above returns the byte of data from the DPC's
internal ROM. Reading from 1008 accesses data at DF (data fetcher) 0's pointer,
then decrements the pointer. Reading from 1009 accesses data at DF1, and so on.
There is no difference except how the data is returned when reading from 1008,
1010, 1018, 1020, etc. All of them return data pointed to by DF0's pointer. Only
the order of the bits returned changes.
I am not sure what purpose returning the data shifted left or right 1 bit serves,
and it was not used on Pitfall 2, but that's what it does. I guess you could
use it to make a sprite appear to "wiggle" left and right a bit, if it were 6 pixels
wide.
All of these read ports returns the data masked by an enable signal, except for
1008-100F. The data here is never masked. (more about this in a minute)
To read data out of the chip, first you program in its start address into the
pointer registers. These are at 1050-1057 for the lower 8 bits of the pointer
value, and 1058-105F for the upper 4 bits of the pointer value. This forms the
12 bit address which can then be used to index the DPC's ROM.
A few of the upper bits on 105C-105F are used for a few other purposes, which will be
described later.
Masking the data:
-----------------
1038-103F is the readback for the mask value
1040-1047 is the start count
1048-104F is the end count
The mask value can be read via 1038-103F. It returns 0 when graphics are masked, and
FFh when they are not masked. (0 = reset, 1 = set)
The basic synopsis is thus:
When the lower 8 bits of the pointer equals the start count, the mask register is set.
When the lower 8 bits of the pointer equals the end count, the mask register is reset.
Writing to the start count register also sets the register.
This allows one to have the sprites only show up on specific scanlines, by programming
the proper start and end counts, and the proper starting value into the pointer. This
way, the sprite can be drawn from top to bottom of the screen, and have it only appear
where it is desired without having to do anything else in the 2600 code.
Making Music:
-------------
The music is generated by repurposing three of the fetchers, the last three.
Each fetcher can be individually selected for music or fetching.
7 0
---------
105D-105F: xxSM PPPP
S: Select clock input to fetching counter. 0 = read pulse when the proper returned
data register is read (i.e. for fetcher 5, 1015 is being read) 1 = music oscillator.
M: Music mode. 1 = enable music mode, 0 = disable music mode.
P: upper 4 bits of the 12 bit data fetcher pointer.
I am not sure why you can separately select the clock source and the music mode,
but you can. Maybe they had some plans for externally clocking the chip via some
logic to bump the pointers.
Normally you set both the M and P bits to make music.
When in music mode, the lower 8 bits of the fetcher pointer is used as an 8 bit down
counter. Each time the lower 8 bits equals FFh, it is reloaded from the start count
register.
To turn the data fetcher into a square wave generator takes very little hardware. The
start/end count registers are used as-is to toggle the flag register.
This means that the duty cycle of the square waves produced can be varied by adjusting
the end count register relative to the start count register. I suspect the game simply
right shifts the start count by one and stuffs it into the end count to produce a
50% duty cycle waveform.
The three flag outputs for fetchers 5 to 7 are fed into a cool little circuit composed
of a 3 to 8 decoder and four 4 input NAND gates to produce the 4 bit audio output.
The output is as follows:
fetcher result
567
---------------------
000 0h
001 4h
010 5h
011 9h
100 6h
101 Ah
110 Bh
111 Fh
This is a somewhat nonlinear mixing of the three channels, so the apparent volume of them
is different relative to each other.
The final 4 bit output value from the above table is then available to read at address
1004-1007, in bits 0 to 3.
Pitfall 2 just reads this location and stuffs it into the audio register every scanline or
so. The value read at 1004-1007 is the instantanious value generated by the fetchers and
mixing hardware.
HMOVE adjustment stuff:
-----------------------
I have not done much research on how this works, and P2 doesn't use it. It appears to
let you draw curved lines using a missile or two, probably to make swinging vines.
The patent explains how it works, but it just appears to be an adder that adds the start
count value to itself via a multiplexer and stuff. It appears to let you input an
offset and it calculates a new hmove value. Not sure how useful this really is.
AR (Starpath Supercharger)
--------------------------
Aaah the good ol' Supercharger. This is the large "cartridge" that you could plug into
your 2600 that had 6K of RAM which you could download games into off of cassette tape.
The idea being you plug your cassette player into the cable coming off of the cartridge,
and then you can download games into RAM from it.
When first turned on, it says "rewind tape" and "press play". When this is done, it will
start loading the game off tape and fill the screen in with blue bars from the sides
to the inside while a rising tone plays. When the screen is fully blue, it then clears
the screen and says "stop tape", and then the game starts.
If there is an error (corrupt tape, etc) it will continously flash "rewind tape" on
the screen until the audio input stops toggling.
Games can re-enable the BIOS ROM and load more data off tape at any time.
So, that's what it is... but how does it work?
I didn't find a whole huge amount of information on how the Supercharger actually
WORKS. There was some info about how to program it, but nothing on how the
ASIC in the Supercharger does its thing.
Well, turns out it's pretty cool and as far as I know, unique.
First off, there are two registers on the Supercharger. The first is the audio
input register. It's at FFF9:
7 0
---------
FFF9: 0000 000A
A: Supercharger audio data. 0 = low input, 1 = high input.
The audio input's just a filtered and schmitt triggered version of the audio data
coming into the Supercharger. The upper 7 bits must all be 0's, or else it
will not properly read the data. For testing, I fed my FPGA 2600's Supercharger
with data from Bankzilla's tape emulator. This is simply a digital output that
is fed into the Supercharger's audio pin (and to my FPGA board).
The next register is the control register.
7 0
---------
FFF8: DDDB BBWE
D: write delay (see below)
B: bankswitching mode (see below)
W: RAM write enable (1 = enabled, 0 = disabled)
E: ROM power enable (0 = enabled, 1 = turn off ROM)
To write to RAM or the control register on the Supercharger requires a somewhat
round-about method. First, you read a location at F000-F0FF, then you read
either FFF8 to write to the control register, or somewhere else in RAM.
Sooo, to write to the control register, the following type of code is
used:
;A = value we will write to the banking register
TAX
LDA F000,X
LDA FFF8
The TAX moves the value we wish to write into F000,X (i.e. if we wish to write 16h
to the control register, the location F016 is read).
Next, we read FFF8 which performs the write. *any* instruction that reads FFF8
will suffice. The BIOS uses BIT FFF8 and LDX FFF8 sometimes, depending on
what's convenient, and what flags or registers need to be saved. The
instruction really does not matter, so long as it simply READS.
Note: THERE IS NO UPPER LIMIT on the number of cycles that can elapse between
reading F0xx and reading FFF8 to write to the control register. Usually,
it is written to immediately after reading F0xx, but not always. The very
last write to FFF8 which sets the RAM banking before launching the game
after loading waits a very long time before reading FFF8 to set the banking
mode.
The basic method it does the final write is as such:
LDA F000,X
<thousands of cycles>
FA0B : 9A TXS ;X entered at ffh
FA0C : A2 3 LDX #$3
FA0E : BC AB FD LDY $FDAB,X
FA11 : 94 FA STY $FA,X ;load cmp fff8h, jmp opcode only
FA13 : CA DEX
FA14 : 10 F8 BPL $FA0E ;jmp target was at fe/ff
FA16 : A0 0 LDY #$0
FA18 : 84 81 STY $81
FA1A : 4C FA 0 JMP $FA ;run the code we loaded in RAM
00FA : CD F8 FF CMP $FFF8 ;sets up control register for the game
00FD : 4C xx xx JMP xxxx ;we now jump to the start of our code
FDAB : CD F8 FF 4C ;this is the code loaded into RAM
The control register contains a few different things.
The ROM power can be turned on and off (which IMO is dumb. The ROM doesn't
draw appriciable power, but they have a PNP transistor that can turn it on
and off anyways. Most games turn it off when they are done loading, and
then turn it back on if they need to load again from tape. I guess they
were worried it was going to vampire too much current from the system, so
running it only as long as necessary was their solution.
The next bit is the write enable. If set, the RAM can be written to.
If clear, it is read-only. This is useful for loading 2K and 4K games into
the Supercharger to play them. Not much use otherwise. The Supercharger
will never attempt to write to ROM, and it will not write to RAM if this
bit is clear.
Banking mode. The Supercharger allows for 8 different memory maps, depending
on how these three bits are set. The following are supported:
BBB 1000-17FF, 1800-1FFF
--- ---------------------
000 - RAM bank 3 ROM
001 - RAM bank 1 ROM
010 - RAM bank 3 RAM bank 1
011 - RAM bank 1 RAM bank 3
100 - RAM bank 3 ROM
101 - RAM bank 2 ROM
110 - RAM bank 3 RAM bank 2
111 - RAM bank 2 RAM bank 3
ROM can never be mapped into 1000-17FF but this makes sense, since the code is
written to only run from 1800-1FFF. Also, a few combinations are not possible
such as RAM bank 1, RAM bank 2, and RAM bank 2, RAM bank 1. All others are
possible.
The last part of the control register is the write pulse delay. I don't think
anything uses this and just keeps it at 0. Heck, it may not even be implemented.
So that takes care of how to read audio and how to access the control register.
That leaves writing to RAM.
The Supercharger watches all 13 address lines to determine when RAM should be
written. It first watches for a write to F0xx, just like when writing to the
control register, then it waits 5 address bus transitions, then it attempts to
write data to the RAM chip by pushing the value onto the data bus, while the CPU
is reading.
The usual way this is done in the BIOS is like so:
;X = byte to write
LDA F000,X ;tell the supercharger what to write
LDA (8B),Y ;write it!
If we look at bus transitions, it looks like this:
Assume that the code is sitting at 1800, and 8B/8C holds the address 1100h.
Also assume X = 66h
c# address, data
1: 1800 BD ;LDA,X opcode
2: 1801 00 ;low byte of add
3: 1802 10 ;hi byte of add
4: 1066 ?? ;read address 1066 (this is the RAM byte, could hold anything).
5: 1803 B1 ;LDA (),y opcode
6: 1804 8B ;zeropage address of pointer
7: 008B 00 ;low byte of destination
8: 008C 11 ;hi byte of destination
9: 1100 66 ;Supercharger writes data now to RAM by forcing value on bus
As we can see, cycle 9 is exactly 5 cycles after cycle 4, where we read from F0xx.
The supercharger will only attempt to write if writes are enabled, and the area
we're attempting to write to is set up for RAM. It will not attempt to write to
ROM, or outside the range of 1000-1FFF.
This method of operation has an interesting side effect. Because the Supercharger
forces a value onto the bus while the CPU is reading, the CPU will actually
READ this value! This makes sense- the CPU doesn't know if it's reading RAM, ROM,
or the Supercharger's write value.
In fact, this is REQUIRED for it to even work. It does some pretty extensive
RAM tests on powerup, and if it cannot write to RAM it will flash the screen
yellowish/black as it attempts to test RAM over and over. If RAM fails to test
properly, it will reset and start again.
The RAM testing code looks like this:
FF58 : A9 F1 LDA #$F1
FF5A : 85 8C STA $8C
FF5C : A0 0 LDY #$0 ;start address = F100
FF5E : 84 8B STY $8B
FF60 : AD 0 F0 LDA $F000 ;write 00h
FF63 : B1 8B LDA ($8B),Y ;write !
FF65 : D1 8B CMP ($8B),Y ;is it 00h?
FF67 : D0 14 BNE $FF7D ;no
FF69 : AD FF F0 LDA $F0FF ;write ffh
FF6C : B1 8B LDA ($8B),Y ;write !
FF6E : D1 8B CMP ($8B),Y ;is if ffh?
FF70 : D0 B BNE $FF7D ;nope
FF72 : C8 INY
FF73 : D0 EB BNE $FF60 ;last byte of page?
FF75 : E6 8C INC $8C
FF77 : A5 8C LDA $8C
FF79 : C9 F8 CMP #$F8
FF7B : D0 E3 BNE $FF60 ;last page?
FF7D : 60 RTS ;return zero set if passed
Basically what happens is this: It first sets up the address F100 (aka 1100) into
8B/8C, then it steps through the entire RAM area up to 17FF, and returns with
Z flag set, if it passed, or NZ if it fails.
The code at FF60-FF66 is very interesting: It first reads from F000 so that
we will write 00 to memory. Then it writes it and immediately compares it.
Remember that the CPU will READ what the Supercharger writes, so the accumulator
will hold 00 after executing that LDA (),y!
The same test is repeated, this time by writing FF. If it can step through all
locations without erroring out, it passes the test. This code only tests one
2K bank (minus the first 256 locations, more on that in a minute). The code is
then called three times, testing each bank of RAM in turn.
This testing brings up an interesting note about the Supercharger's RAM.
When writes are enabled to RAM, you CANNOT READ FROM 10xx WITHOUT CORRUPTING RAM.
This means, you cannot read data here, and you cannot excute code here! Doing
so will trigger writes, which will overwrite whatever happens to be 5 cycles later
if it's in RAM! You *CAN* write to 1000-10FF, however just fine... you just
cannot read it or execute from it without first disabling writes. In fact, the
BIOS starts writing at 1000 when it's loading the game from tape. Since it never
attempts to execute code from there or read back the data, this is legal.
The BIOS' RAM testing routine must therefore skip testing the first 256 bytes
of each bank. It *could* test it, but I guess they figured running the code from
RAM to do it would've been too much of a hassle, concidering space was at
a premium.
Remember how I said the Supercharger counted *BUS TRANSITIONS* to know when to
write to RAM? Well, this is very very important. You cannot just count CPU cycles.
That won't work (because the Supercharger cannot see the CPU's clock line for one).
Here's the other way that you can write to RAM:
FFC7 : A0 8 LDY #$8
FFC9 : D9 0 F0 CMP $F000,Y
FFCC : EA NOP
FFCD : CD E3 F7 CMP $F7E3
This writes 8 to location 17E3. Notice the NOP to "fill up" the cycles. If we
look at what the address/data bus is doing, we will see how this can work, even
though the write occurs 6 cycles after the CMP, instead of 5.
c# address, data
1: FFC9 D9 ;CMP,Y opcode
2: FFCA 00 ;low byte of add
3: FFCB F0 ;hi byte of add
4: F008 xx ;read RAM here, contents unimportant
5: FFCC EA ;NOP opcode
6: FFCD CD ;CMP opcode, but throw it away (NOP is 2 cycles)
7: FFCD CD ;CMP opcode
8: FFCE E3 ;low byte of add
9: FFCF F7 ;hi byte of add
10:F7E3 08 ;write to F7E3
Yes, it does indeed take 6 cycles to get to the write... however, look at the address
bus. There's only FIVE transitions! The byte at FFCD gets read twice. This is
because NOP is a 2 cycle instruction. NOP's second cycle reads the next opcode
and throws it away, and does NOT increment the program counter. So, the next
cycle reads it too and fetches CD for the CMP opcode. Pretty tricky stuff!
Note that when I say "transition", I am talking about when one or more of the 13
address lines changes. The Supercharger can only count cycles by watching these
address lines. That's why it only counts up 5 transitions on the above code snippet.
The double access to FFCD counts as 1 transition as far as the Supercharger is
concerned.
That about wraps up the Supercharger's hardware.
Supercharger Tape Format
------------------------
The tape format on the Supercharger is fairly straight forward. It's a form of
pulse width modulation. Interestingly, the length of the pulses is not fixed, and
the software on the Supercharger can adapt to a pretty wide range.
According to the "tapedocs.txt" file:
"0" pulse "1" pulse
-------------------------------
minimum 158uS 317uS
optimal* 227uS 340uS
maximum 317uS 2450uS
* According to the tapedocs.txt file, the filters on the Supercharger are tuned
specifically for 227uS and 340uS pulse lengths.
NOTE: these times are for the entire pulse, i.e. the low and high portions of it.
The low and high portions would thus be half these values.
i.e. if the pulse is 300uS long...
low high
____________------------
\--150uS--/ \--150uS--/
Here is a generic representation of what a typical signal looks like:
____----____----____----________--------________--------____----________--------
\--0--/ \--0--/ \--0--/ \------1------/ \------1------/ \--0--/ \------1------/
The "one" pulses only have to be approximately 90uS wider than the "zero" pulses,
but it's a good idea to have them at least 25% wider so the Supercharger can
adapt to tape speed fluctuations during the load.
A Supercharger tape load is composed of 6 separate parts:
---------------------------------
1. Preamble
The preamble is a 50% duty cycle low frequency waveform that causes all the
elements in the tape chain (amplifiers, the filter in the Supercharger proper,
etc) to stabilize. Without this preamble, the volume level into the 'charger
can change while the capacitors in the signal chain charge.
A frequency of around 750Hz (666uS low, 666uS high) is pretty decent.
The preamble needs to be at least 800 pulses long (i.e. around 1 second or more).
---------------------------------
2. Synchronization
After the preamble, at least 256 or more bytes of 55h (0/1/0/1) bits need to be
sent at the chosen bit rate. The Supercharger software uses this to synchronize
its decoder software.
---------------------------------
3. Start pulse
To tell the Supercharger our data is coming, a final 0 pulse is sent. After this
point, the Supercharger header follows.
---------------------------------
4. Header data
0: WORD Start address
2: BYTE Control word
3: BYTE Block count
4: BYTE Header checksum
5: BYTE Multiload number
6: WORD Progress bar speed
All values are little endian. Bytes are sent out, MSB first (i.e. left shift
the data byte).
Start address: The start address specifies where the 6502 will
jump to after all the data is loaded from the tape.
Control word: Specifies the value to load into the Supercharger's control register.
Usually the upper 3 bits should be clear. (See above for more info on the control
register in the Supercharger section).
Block count: How many 256 byte blocks will be sent in this transfer.
Header checksum: As the name indicates, a checksum of the header. The checksum
is computed by starting out with 55h and subtracting every byte in the header
(not including the sumcheck byte itself).
The result is that when the Supercharger adds up ALL bytes of the header (including
the sumcheck byte) it will be 55h. All carries/borrows are ignored when calculating
the sumchecks.
This is what the demo unit's header contains:
.dw 0ffc0h ;start address of code
.db 01bh ;control word: 1000-17ff= b.3, 1800-1fff= b.2 ROM=off WR=on
.db 001h ;# of 256 byte blocks
.db 06dh ;header checksum
.db 000h ;this is not a multiload
.dw 0010ch ;speed for progress bars
when all these bytes (c0, ff, 1b, 01, 6d, 00, 0c, 01) are added together, the result
is 255h... dropping carries (AND with ffh) results in 55h... meaning that the
sumcheck passes and the data was properly received.
Multiload number: If the game has no multiload, this byte is simply kept at 0. If
this game IS a multiload game, then the sequence number is stored here. This lets
Supercharger BIOS know if the proper game load is being received or not.
The first load of a multiload game is always 0. When the first load wishes to
load the second (or subsequent) part(s), the current load tells the Supercharger
BIOS which load it is looking for and runs the BIOS' receive routine again.
If the data received is not the proper load #, it will do a "false load" and load
the data, but then throw the results away and wait for the next load to come around.
This is repeated until the correct load number is reached and loaded.
On Escape from the Mindmaster this can be seen if you lose all your lives in the first
load. It will instruct you to press "play" on your tape player, and will load part 2.
But the ending is not in part 2, so it throws the data away after loading it and
proceeds to load part 3, which is thrown away, and then the final part is loaded.
To prevent someone from getting cute, the multiload sequence numbers were never
recycled. They were allocated like so:
Escape from the mindmaster uses 1, 2, and 3
Dragonstomper uses 4 and 5
Survival Island uses 6 and 7
Progress bar speed: The value here specifies how fast the progress bars.. well..
progress as the game is loaded. If the value is wrong the bars will proceed to the
middle before the game is fully loaded.
The value is calculated by taking the block count and multiplying it by
approximately 22.8 i.e. for a game that's 6K in size (each block being 256 bytes)
is 24 blocks * 22.8, which is 547 (223h).
This works for most bank sizes, however when only 1 bank is loaded, the value
has to be 10ch for some reason I have not researched.
---------------------------------
5. Data blocks
Once the header is finished sending, the data is sent in 256 byte blocks. Each
block has a 2 byte header, followed by the 256 bytes of data.
Block header:
0: BYTE Block location
1: BYTE Checksum
Block location: This is the block location in RAM where the data will be loaded.
Blocks are specified in the following format:
7 0
---------
000B BBRR
B: block number, from 0 to 7
R: RAM chip #
Ram chip number is specified like so (reffing the bank numbers in the Supercharger
documentation above)
00 - RAM bank 1
01 - RAM bank 2
10 - RAM bank 3
11 - invalid (would select ROM)
The block number is the particular 256 byte block in the specified RAM bank.
So, to load data sequentially, from RAM bank 1's first block, to RAM bank 3's
last block, the following block locations would be used:
RAM 1: 00, 04, 08, 0C, 10, 14, 18, 1C
RAM 2: 01, 05, 09, 0D, 11, 15, 19, 1D
RAM 3: 02, 06, 0A, 0E, 12, 16, 1A, 1E
Checksum: The checksum for the block is calculated exactly like the header's
checksum is. All 258 bytes (256 data bytes, block location byte, and sumcheck)
are added together, and the result must be 55h. If the sumcheck fails, the
"rewind tape" message flashes as long as audio data is coming in, then when the
audio data stops coming in, it reverts to the "press play" message.
---------------------------------
6. Postamble
After all the blocks of data are sent, the Supercharger will play the "Press stop"
message until audio data stops coming in, and then it starts running the game.
The postamble is used to detect when stop is pressed. The postamble is simply
a string of "0" bits. The length of the postamble should be at least 256 bits,
but it can be as long as desired. The Supercharger will not run the game
until the postamble stops, however.
---
Supercharger Demo Unit
----------------------
This doodad is pretty cool. First, you plug a Supercharger into your 2600. Then
you plug the audio cable from it into this black box. Then the black box plugs
into the joystick port.
You can optionally plug a regular joystick into the other joystick port.
The demo unit itself is a nondescript black box with a label on it. Inside the box is
quite a bit of hardware.
* 8 2764 EPROMs
* 1 2732 EPROM
* 1 6805E CPU
* various discrete logic
The basics of operation are as such:
When powered up, a small bootloader is loaded into the Supercharger through its
audio cable. Once this initial bootloader is loaded, the audio cable is never used
again until the system is reset or power cycled.
After the bootloader is sent, the rest of the data to the Supercharger's RAM is loaded
through the joystick port 4 bits at a time.
The net result is incredibly high loading speeds. A complete 6K of RAM data can be
sent in around 250ms.
Internally, there is a 6805E CPU made by Motorola. This is similar to the 68705 and other
microcontrollers Motorola made, except it's the external ROM version. There is no
ROM on the chip.
The two IO ports are connected as such:
PortA: This port is used to send data out 8 bits at a time to the 2600 through the joystick
port. The byte of data is simply written here, and the 2600 picks it off when it's good
and ready (there's an explaination later on of how this works exactly).
PortB:
7 0
---------
AExB BBBB
A: Audio output. This port pin connects to the Supercharger's audio cable through a resistor
divider that cuts the audio level down to a level of a few hundred millivolts to prevent
overloading the audio input.
E: EPROM enable. Must be low to enable the 8 2764 EPROMs.
B: EPROM bank. Selects a 2K EPROM bank from the 2764's (more about this later)
PortA's data is passed through a quad 2:1 multiplexer (a 74LS157). The select line connects
to bit 3 of the joystick port. When it's low, the multiplexer outputs the lower nybble of
the byte on PortA. When it's high, it outputs the upper nybble.
The data bits from the muxer connect like so:
bit 3: this is acting as an output- it runs the select line of the 74LS157, and also
connects to /IRQ on the CPU.
bits 2 to 0 connect to the multiplexer's upper 3 bits.
The fire button input connects to the multiplexer's lowest bit.
So, to read a byte of data in through the controller port, the select line is pulled low
via the RIOT register at 280h. Bit 3 must be set as an output, while bits 0-2 are inputs.
bits 3,2, and 1 of the data byte are read on 280 bits 2 to 0. bit 0 of the data byte is
read via 3Dh bit 7.
the select line is thin pulled high via 280.3
bits 7,6, and 5 are then read from 280 bits 2 to 0 . bit 4 of the data byte is read via
3Dh bit 7.
There's a very elegant piece of code that does this on the bootloader, which gets loaded
into RAM:
;enters with Y = 3fh
0081 : CD 0 F0 CMP $F000 ;select proper RAM bank (note: self modifying)
0084 : CD F8 FF CMP $FFF8
0087 : A9 7F LDA #$7F
0089 : C5 3D CMP $3D ;puts fire button data into carry and invert it
008B : 4D 80 2 EOR $280 ;get data, and invert it (data is sent inverted)
008E : 8D 80 2 STA $280 ;write inverted data back (toggles bit 3 to demo unit)
0091 : 2A ROL
0092 : A ASL
0093 : A ASL
0094 : A ASL ;put lower 3 bits into bits 6 to 4, carry into 3
0095 : 85 B7 STA $B7 ;save for now
0097 : A9 7F LDA #$7F
0099 : C5 3D CMP $3D ;put fire button into carry and invert it
009B : 4D 80 2 EOR $280 ;get data, and invert it
009E : 8D 80 2 STA $280 ;write inverted data back
00A1 : 29 7 AND #$7 ;strip off upper bits
00A3 : 5 B7 ORA $B7 ;or it on to add into bits 2 to 0
00A5 : 2A ROL ;finish up by putting carry into 0 and shifting the mess left
00A6 : AA TAX
00A7 : DD 0 F0 CMP $F000,X ;write it to supercharger RAM
00AA : D1 B9 CMP ($B9),Y
00AC : 88 DEY
00AD : 10 D8 BPL $FF8A ;do all bytes
00AF : CD 19 F0 CMP $F019 ;bankswitch
00B2 : CD F8 FF CMP $FFF8
00B5 : 60 RTS
This core routine is used to load data. It lives in the FF00-FFBF range and is loaded
into RIOT RAM when data needs to be transferred.
Since bit 3 of 280h is also connected to the /IRQ line on the CPU, this is used to
tell the 6805 when a byte of data has been read, and it should get the next one ready
to go.
That's about it for the 2600 to demo unit interface.
The memory map of the demo unit is extremely simple:
0000-0FFF : on-chip resources (ports, the timer, RAM)
1000-17FF : 2K bank of data from the 64K worth of 2764's
1800-1FFF : 2K of BIOS code from the 2732 EPROM.
The 2K bank of data at 1000-17FF comes from the 64K of EPROMs. PortB bits 0-4 select
a desired 2K bank.
That's about it for the details. There are a few other amusing (to me) details of the
hardware, however.
When the unit starts, there's a 10 second countdown. This is there for a reason.
Those 8 2764's can draw a lot of power if they were all turned on at once, so to
enable the EPROMs there are 8 PNP transistors connected to a decoder, which connect
to the VCC lines of each EPROM. So, when you wish to read from the EPROMs, you must
actually enable the power line to it via a decoder chip (74LS42).
To prevent a huge power suck from the 2600, there's a large capacitor that supplies
power to the EPROMs. I can't remember how big it was, but I think it was 4700uF.
This is connected through a resistor to 5V. So, that 10 second countdown at the
start is so this capacitor can charge. The countdown code and bootstraps are stored
in the 2K EPROM.
Only one of the 8K EPROMs may be enabled at any time, and they can all be disabled
to prevent them drawing any power by clearing bit 6 of portB. The desired EPROM is
selected via bits 2,3,4 of portB. (bits 0 and 1 of portB connect to A11 and A12
of the 8 EPROMs.)
The 74LS42 is a 1 of 10 decoder, and the inputs are connected as such:
7 0
---------
xDxC BAxx
ABCD: the 4 selector inputs (A = LSB, D = MSB). x = not used.
Thus, to enable an EPROM, bit 6 must be clear. When this bit is set, no outputs
connected to EPROM power enable transistors will be turned on.
I am not 100% sure of the speed the demo unit's CPU runs at. It was 12 years ago,
but I think it was 4.1952MHz. I am using this on my demo unit FPGA emulation and
it works good.
That about wraps up this mysterious piece of hardware.