Weird Science - Tales from the
Vectrex Academy Lab
Vectrex
Project Title
- An analysis of the original BEDLAM cartridge
and source code
Project
Status
Synopsis
- Digital Archeology - An investigation of the
Bedlam cartridge
- In a more general discussion of
Vectrex wobble and flicker (see here),
Malban mentioned that several of the original
GCE games deliberately set the refresh timer to
a value other than 50Hz. One of those games is
Bedlam, running at ~30Hz, which results in a
noticeable flicker on real consoles. I became
interested in why that specific framerate was
chosen in Bedlam. Especially, because in
comparison to other games, there are not that
many things drawn on the screen. So why such a
low framerate?
- Here is a summary of some interesting,
yet probably utterly useless findings...
An
analysis of the BEDLAM source code
- These findings are deductions (and some
speculations) based on a full disassembly of the
original Bedlam cartridge code.
- First of all, let me emphasize that I really like
Bedlam. Liked it as a child, and still like it
today. It was and still is one of my absolute
favorite Vectrex games. And I hold the highest
respect for all the designers and programmers who
worked on the original Vectrex games back in the
Eighties, and who were using development
environments which are completely different than
those of our modern times. So, if some of what I
write here might sound negative or presumptuous, it
is not meant like that at all.
- The game code uses some strange(?) timing design.
An interrupt handler routine for the refresh timer
T2 is set up. The main action loop does not contain
a Wait_Recal() call, but at some point halts by
means of the "cwai" instruction and then waits for
the T2 interrupt to fire. Then, inside the interrupt
handler routine, Wait_Recal() is called, which does
not make any sense at all, since the timer has
already expired at this point, and there is no need
for a Wait(). At this point, the wait loop
immediately exits, and a mere Recalibrate() would
have been sufficient. Also, all the other stuff done
by the interrupt handler could just as well have
been done in the main action loop, with a
Wait_Recal() in place there. I tested that, and it
works just the same. Well, actually, quicker.
- I do not understand why such a complicated
interrupt design was implemented, especially as it
wastes some amount of CPU cycles for saving and
restoring the processor state when servicing the
interrupt routine. The interrupt handler also sets
up its own distinct stack space, and on exit
restores the stack to its entry state. Again, this
is unnecessary and wastes some more CPU cycles.
- The T2 refresh timer is set the odd value of
49407. Why? I do not know. A proper 30Hz would
correspond to a value of 50000 (yes, minus some few
cycles to account for the polling instructions, but
certainly not minus 593 cycles). I have two theories
on that: Either they simply tweaked this value until
the resulting speed of the game action felt right,
posing an acceptable challenge to the players (and
customers). Or this is some kind of inside joke, as
49407 decimal corresponds C0FF hex,
which could be read as "coff(ee)" ;-)
- The interrupt design is the reason
why Bedlam's cycle count shows up in Vide as a
more or less constant 50000 cycles. When changed
to a regular non-interrupt Wait_Recal() design,
one gets much more accurate readings of how many
cycles are effectively used for game logic and
drawing. Bedlam averages around 33.000 cycles
during the first stage, with some peaks going as
high as 42000 cycles. The next stages avarage
around 35.000 to 38.000 cycles.
- I wrote a patched version, using a
regular Wait_Recal() loop design, with the
framerate set to the standard 50Hz. Wow, that
looks great on a real console, almost no
flicker! But also the speed of the action is now
much faster, making the game really hard and
even more challenging.
- While disassembling and analyzing
the source code, I noticed several things I have
listed below. I think that the code can be
optimized in various placed in order to come
close to a true 50Hz framerate. I will likely do
some of this and then put out some kind of
"Speedlam" version. Update: done, see section
below.
- Several lines of assembly code can
easily be optimized. Short branches instead of
long branches, no need to reload a reg with the
same value if the reg has not changed, "puls x +
rts" can be combined to "puls x,pc", etc.
Another example: "clrb + decb" is quicker
achieved by "ldb #$ff". Same with: "clra +
coma". And "sta $00,y" is quicker done by "sta
,y". And all this is not done to achieve some
specific timing, or to manipulate the processor
flags in special ways. And there are many more
examples of such type. Maybe it is just me not
seeing the reasons for this. Or maybe the
programmer was not yet that experienced at 6809
coding?
- For storing the level number
information, a 7(!) character score string is
used and printed (separately) on the screen. Did
anyone ever reach a 2-digit level in Bedlam?
This printing wastes a lot of cycles, printing
all the blank characters. Room for improvement.
- The game just uses the X-axis of
controller 1 only. However, the Y-axis, and also
controller 2, are not disabled and also
evaluated in each call of Joy_Digital(), wasting
several hundred cycles in each round. Room for
improvement.
- All vector rotations are computed
on-the-fly by means of Rot_VL_dft(), which
explains where lots of the CPU cycles go. I
think that precomputing and storing the vectors
in ROM tables was not an option in those days,
as ROM space was still rather expensive.
However, the walls of the playfield are all
computed (rotated) and drawn separately. This
could have been done more efficiently with a
one-go rotation of a complete vector list, and
then also a one-go drawing of a complete vector
list.
- There is a by now well known easter egg hidden in
Bedlam. Press buttons 1+2+4 during and until the end
of the title screen, and then a short message will
appear telling you the name of the programmer,
accompanied by a dixie melody. Quite some effort was
taken to hide this message in the ROM code. The
ascii text has been XOR coded, so that it cannot
easily be read in a hex monitor. It is copied and
decoded to RAM before it is then displayed on the
screen.
- The small life-icons are actually drawn at
different scales (4,3,2), depending on how many
lifes are left (3,2,1). However, at such tiny
scales, this goes (almost) unnoticed, and I wonder,
if this was intended, as the corresponding part of
code is unnecessarily complicated and also contains
redundant instructions.
- I never noticed that until I saw it in the code.
As long as the zap weapon is still available, there
is a small dot drawn inside the cannon.
- The code also contains some dead
code (functions that are not used), and also
some dead data. There is a short melody which is
apparently not used throughout the whole game.
There are also two additional, but unused sound
handlers. One implements an alternate firing
sound, and the other one a background ringing
noise.
- The RAM is highly fragmented and
not used contiguously, but with several gaps
between chunks of used bytes. No idea why they
did not simply put all their RAM data in
sequence, just one after the other.
- The code writes some RAM locations
which are never read (and never used). There is
also a counter set up, counting down in steps of
10, which is also not used at all for anything.
- Some trivia: Up to 4 shots can be
fired at once, and up to 6 enemies can be
present on the screen.
- To be continued. Comments are welcome!
Source
Code
- Though almost
fully analyzed and commented, this is still
ongoing work. I did no make the code
disassembly beautiful in any way. Some
comments might be outdated. If you spot any
mistakes, or if you have additional insight,
please let me know.
Patched
50Hz Version
- Patched 50Hz
Bedlam version: bedlam_50hz.bin,
contains the following modifications:
- Interrupt scheme changed to
standard Wait_Recal() loop, refresh
rate set to 50Hz
- Title text
changed to "Bedlam 50Hz"
- Title
melody changed to unused
melody hidden in the
source code
- Firing
sound changed to unused
alternate firing sound
hidden in the source
code
- Background
sound changed to unused
alternate background
sound hidden in the
source code
- Only X-axis
of controller 1 is
evaluated (speedup)
- Printing of
level message and level
number combined
(speedup)
- Redundant
stack scheme removed
(speedup), unused
counter removed
(speedup)
- Drawing of
tiny life icons
simplified (speedup)
- Lots of
low-level code
optimizations, replacing
instructions by quicker
alternatives (speedup)
- I strongly
suggest that you play
this on a real
console, and that
you play the original
Bedlam first and then
this patched version, in
order to fully
experience the
difference. Let me know
what you think!
- Downloads are free and for non commercial use
only. Use at your own
risk.
- Please respect the
copyright and credit the author and
the origin of this game.
Online
Playing
- Link to Dr. Snuggles' online
emulator to directly play the game in your
browser: Bedlam 50Hz
- For comparison (though comparing this on real
consoles is much more telling): original Bedlam
Author
Latest
modification on 01/31/2023, 09:30
|