Action Replay Cartridges
Entering 'sysop mode'
How Action Replay 2 works
Entering 'Sysop Mode'
Action Replay is great fun, even more so if you get into the
'sysop mode' (Allows disassembly of ram areas not previously
allowed by Action Replay, including non-autoconfig ram and
the cartridge rom!)
To get into sysop mode on Action Replay 1 type:
To get into sysop mode on Action Replay 2 type:
To get into sysop mode on Action Replay 3 type the same as
Action Replay 2. After this you get a message
"Try a new one".
Then type in
and sysop powers are granted!
The Action Replay 2, How it and the Amiga works, and why
For all the hackers amongst you lot, especially those with one of Datel's
excellent Action Replay Mk.][s (unlike the first one, which was useless), here
is a little technical info about it, and how to protect against it.
The Cartridge Internals
Pressing the button
What happens, and why..
Two ways to get into the cart without pressing the button
How does it know what's going on with the custom chip registers?
How to protect against Action Replay Mk. ][
The cart's internals
The cartridge contains 128k of Rom (contained in two 27512 (64kx8bits) roms)
and 32k of static ram. All the chips are surface-mount types, and are buggers
to get off intact with a soldering iron. My advice is, don't bother! (There
is also a scattering of custom PLAs and things, but they're not very
interesting.) The Rom is addressable from $400000-$41ffff (and is repeated due
to partial address decoding from $420000-$43ffff). The Ram is from
$440000-$447fff, and is repeated loads of times for the same reason.
Normally, this Rom & Ram is totally invisible & undetectable. It is switched
out of the address space, and there is *nothing* you can do to switch it in
via software (there is an exception to this, see later).
When you press the button
When you press the button, the cartridge generates a level 7 (non-maskable)
interrupt, and then when the 68000 comes to get the vector (at $7c) for the
interrupt, the cartridge whangs on the OVR* line on the expansion port, which
has the effect of disabling all the amiga's internal chip memory!
The cart then makes its internal Rom appear instead of the chip ram from
$00000-$80000. This Rom (and its Ram) simultaneously appears at their normal
addresses at $400000/$440000. If this sounds like magic, it's actually
incredibly simple to do in hardware!
A Quick explanation of what happens and why
As a matter of interest, the cart is doing something very similar to what the
Amiga normally does on a reset, which is to switch all of Kickstart in from
$00000-$80000, thereby providing the reset vector. That's why if you do a
RESET instruction, then your code will most likely blow up instantly as all
chip ram disappears! (The way to make thing return to normal is to set the
CIA OVL* (OVerLay) signal back to 0, but if your code is dead, this is easier
said than done.. however it can be done by some deeply sneaky programming..
see the Double Dragon ][/Shinobi protection that I coded a while back)
Anyway, I digress. This new level 7 vector from the cartridge is $400088, a
routine in the Rom proper which first restores the chip ram,etc to normal.
Then it treks off into the bowels of the cartridge software.. you can follow
it through and ReSource it if you like!
This is, however, only one of the ways to get into the cartridge. There are
Two ways to get into the cart without pressing the button
(Or: 'Futility- an object lesson'!)
1) This first, is the way the cart boots up on a reset (displaying that little
piccy). The way this works is indecently sneaky! Instead of (as I had
expected) either intercepting the reset wholesale, or appearing to the
Kickstart reset code as an autoboot cartridge (which can be done by making
your Rom appear at $F00000 on reset with $1111 as the first word - see $fc00d2
in the Rom), the AR2 goes for a MUCH sneakier route!
It detects processor accesses to location $0000008, and when one happens, it
effectively 'presses' it's own button. (i.e. does the level 7 business) Now,
it just so happens that the Kickstart Rom does a MOVE to location $8 very
early on, when it sets up the exception vectors, so voila! There it goes into
the cart, returning later when it feels like it! At this point, I feel I
should deeply disappoint those of you with a more technical bent (oo-er!)...
You may be thinking that if you use the processor to generate a reset (with
the RESET instruction), and then access location $8, then the cart will reveal
it's presence, and thereby you can protect against it!
No way jose!
The designer of the cart was clever enough to put in a circuit that can
actually tell the difference between you plonking your fingers on the 3-keys
of doom, and the processor doing a RESET.. Not easy to do! (It actually
times the reset pulse, and whereas the processor RESET instruction makes a
really short one, the keyboard reset lasts for about ½ a second, and only
responds to a location 8 access after a genuine reset.)
2) The second way is a bit simpler. When you set a breakpoint with the cart, or
set the exceptions with SETEXCEPT, what the cart does is to put a set of TST.B
$BFE001 instructions at location $100, with the vectors in question pointing
there. What then happens, is then when you get an exception, the TST.B
$BFE001 is executed, the cart detects it happening, and does the Level 7
thang. Now. This TST.B can't just be anywhere. It has to be from $100 to
"Ah HA!", you think,"I'll just have a few 'TST.B $BFE001's executed at $100
before my game loads!!"
....Oh no you won't! This whole thing is only enabled after the user does a
SETEXCEPT or sets up a breakpoint! Normally the above has NO EFFECT AT ALL!
Who's a clever little cart designer then???
How does it know what's going on with the custom chip registers?
I knew you would ask me that one! Ok, get ready for some interesting (?)
As you may well know, all the custom chip registers at $DFF000-$DFF200 are
EITHER read-only OR write-only, never, ever, both. This is for a good design
reason (take my word for it). Some of the more interesting registers have
separate read registers (e.g. DMACONR) so you can tell what the register
itself contains. Most don't...
It is therefore simply NOT POSSIBLE to tell what was last written to, say,
COLOUR00 ($DFF180), without either; A) Asking the person looking at the screen
to describe the border colour, or B) Extra hardware.
Not entirely suprisingly, the AR2 goes for the latter solution. First, a
quickie lesson in how the amiga's custom chips communicate internally...
The custom chip 'registers' are not really actual memory at all. What that
area ($Dff000-$dff200) actually is is a 'window' into the internal custom-chip
computer system. This system consists of a 'bus' (i.e. a channel for data
consisting of several physical connections grouped together, each one carrying
one bit of the data that it being passed) that is connected to all the custom
chips inside the Amiga. (See the pins labeled 'RGA1-8' which contains the
number $000-$200 of the custom register, and 'D0-D15' which contain the 16
bits of data being transferred, that are on all of the main custom chips)
Using this, all the various registers (which in physical reality are located
scattered about in various of the custom chips, according to their usage.
I.e. DIWSTRT is in Denise, which generates the display, and BLTSIZE is in Fat
Agnus, which contains the blitter amongst other things) can be read or
These registers do not exist solely for the purpose of being read/written by
the 68000. Certainly most of them have no purpose than to be set up by the
main processor in order to tell the appropriate custom chip what to do, but
others are really of no use to the programmer whatsoever. For example, the
xxxxxxDAT registers (more on them later).
Apart from the 68000 using this bus to communicate with the custom chip
registers, the chips themselves use it all the time too!
For example, how the screen display is generated...
Fat Agnus does all the DMA handling; i.e. it is the chip that, when a bit of
data (screen,disk,audio,etc) has to be transferred to/from chip memory,
actually does the donkey work of read/writing the value to the appropriate
main memory location. (Main memory is a separate system to this internal
custom chip world, and Agnus is the interface)
For screen DMA, the data actually is destined for the Denise chip, which is a
separate lump of silicon, so how does it get from Agnus (that has just fetched
it from Ram) to Denise? Via this internal bus! This is where those registers
that no-one seems to know about come in. I mean the xxxxDAT registers!
BPL1DAT (for example) is not really meant to be accessed by the user at all!
It is there for internal usage.. i.e. Agnus reads a word of the screen
memory from the outside world, and writes it into BPL1DAT, which is a physical
register inside the Denise chip.
Now, this operation is functionally the same as you doing a MOVE.W into
BPL1DAT, but it is done by Agnus, with no help from the processor. This is
DMA. If you read from VHPOSR, then the read request from the 68000 would be
passed to Agnus, which would then consult the internal bus, and then deliver
the value back.
Ok, basically the processor is just a spectator on this internal register bus.
(Am I being too patronising? Sorry.)
Right, so now you know that the custom chip registers are not really part of
main memory at all (that's why the Copper can only work within this small
world of registers and not with all of Ram), and you know/already knew that
there are many registers that you cannot read at all.
Back on the actual subject in hand.. i.e. how the AR2 knows what these
internal registers contain, when it is simply not possible to read them....!
Answer: It doesn't.
What it actually does, is use an idea stolen from Romantic Robot (a company
that made the first decent freezer cart for the Amstrad CPC, which also had
write-only registers) which is to make a bit of sneaky hardware that
effectively sits there and watches the 68000 like a hawk. Whenever the 68000
does a write operation to a chip register, it makes an internal copy of the
value being written! What this amounts to is $200 bytes of totally invisible
(invisible, that is, until you whack the button, when it appears - to be read
- in another area of memory) Ram that contains all the values most recently
written by the processor to any custom chip register.
Handy eh? So the action replay has all those values you wrote to COLOUR00,
DSKSYNC,etc,etc copied down in its' own bit of ram!
Now, here's where the major catch comes...
You may have noticed two things....
A) Only the value last written is kept, and
B) Only VALUES WRITTEN BY THE PROCESSOR can be kept.
The first argument is not really that important, but the second one IS!
3 example ways to protect against Action Replay Mk.][
A) There is NO POSSIBLE WAY that the AR2 can EVER TELL WHAT THE COPPER HAS
WRITTEN TO A CUSTOM CHIP REGISTER.
A fundimental flaw, and one which it is not possible to solve in hardware
without having a set of leads connected inside your amiga. The chip register
bus is not available on the explansion port.
"Hang on!", I hear you cry (?),"how come it knew about that DIWSTRT that I
only wrote in my copper list?"
Ah ha. Well, this is where the sneaky programming in the cart comes in. It
knows where your copper list is (after all, you must have written to COP1LC at
some point.. with the processor) and so what it does is to follow your copper
list through and it actually updates its little internal copy of the custom
registers to do the changes that the copper must have done!
I.e. if you did a CLR $DFF180 to clear colour0, and then in your copper list
you had a MOVE $FFFF,$0180 to there, the AR2 would only have your processor
CLR stored by the hardware, but the software would see the copper instruction
and alter its record. In fact, it has no way of actually knowing whether this
copper instruction was ever run or not, it just guesses!
So, the main form of protection is to set up all the important chip registers
using a temporary one-off copper list, one that, once it has run, is dumped
and a new copper list (the proper in-game one) is started instead.
The AR2 will never know about any of the chip writes done in the first copper
list, if you press the button when the final one is running.
This is better than it seems, because the AR2 changes loads of things when it
runs it's own monitor (i.e. resolution, # of bitplanes, pointers,etc,etc) and
relies on knowing what to put back into the registers when it returns to your
code. If it has a totally false idea of what was in, for example, BLTCON0, or
DSKSYNC, then there is no way your program will ever run again!
That's the only practical way to protect against the cart, and as you can see,
it is naff in that it doesn't either a) detect the cart before the program
runs, or b) stop you going into the monitor.
The other ways are....
("The Dog-In-A-Manger Approch".. if I can't.. neither can you!)
B) Point the Supervisor stack to an ODD address, and run your program in user
mode, with NO INTERRUPTS! When you get an interrupt, the processor always
enters supervisor mode, switches over to the supervisor stack, and pushes on
the address to return to after the interrupt and the current Status Register
value. If the address that it tries to push these to is odd...? Kapooof.
Not just an address error, but the address error itself also tries to push
words onto the odd-stack, and you get a double-exception.. i.e. total 68000
lock-up. It will not recover until you do a hardware reset.
This is absolutely the best way to fuck ANY cart up. Press the button when
this has been done and the entire computer crashes totally. But... Can you
write a game without using interrupts?
(The 'Say kids.. what time is it?' approach)
C) Use the CIA Time-of-day alarm. This is semi-complex..
Each of the 2 CIAs have 'Time of Day' clocks. These are clocks that run on
the conventional hour/minute/second scale, and are driven by the system clock.
They have to be set to the right time after every reset, so are almost never
used for their intended purpose. Thing is, the clocks also have an alarm
facility, whereby you can get an interrupt from the CIA when the current
time=the preset alarm time. There are 3 registers (hours/minutes/seconds)
that if read, contain the current time, if written in mode 1 (mode is set by a
register bit), will change the current time, and if written in mode 2, will
change the alarm time.
This alarm time cannot ever be read. So.. what you do is...
Set your alarm time for, say, 00:00:10, then set the current time to 00:00:00,
and enable the interrupt. Start your game going. When you get the alarm
interrupt, set the current time back to 00:00:00, and in another 10 seconds
you will get another interrupt, and so on. If, however, you notice that the
time has ever gone past 00:00:10 without you getting an interrupt, or that the
alarm occurs at the wrong time, then you know that someone has tampered with
the program and didn't set the right alarm time! I know it sounds
complicated, but if you use a weird alarm time, then noone will ever know what
the correct value to set it to is, and so the 'freezer' can never produce a
copy of the game that will unfreeze and work.
Both these approches use features of the Amiga/68000 that cannot be got
around. The first one can be fixed by having loads of internal connections
into the Amiga, but no-one will want to do that. Unfortunately, at time of
writing (31st Jan'91), I can't think of another way to stop the cart getting
into the monitor. Like I said, it's quite well designed!
If you can find a bug in the software to exploit, all well and good, but
remember bugs can be fixed!
P.P.P.P.S. Thanx to Bob & Jim for the help.
Brought to you by GREMLIN of MAYHEM (finished 5:50am 31st Jan 1991)