← All articles

Medieval Madness Keeps Score on Its Own Drawbridge

In the middle of a Medieval Madness playfield stands a castle, and in front of it a drawbridge. To destroy a castle you knock the drawbridge down, then batter the gate behind it, then shoot the ball inside. Over a machine's life that drawbridge takes thousands of hits from a steel ball, and the little switches that tell the game where it is take a beating too.

The game knows this. Its program quietly times every move the drawbridge makes, keeps a tally of the ones that go wrong, and after enough of them decides a switch is broken and leaves a note for the operator. It also keeps watching, and if the switch starts working again, it changes its mind.

This article walks through that bookkeeping in the original 1997 program, as it sits in the machine's ROM.

One motor, two switches

The drawbridge is moved by a single motor that the game can only switch on or off. There is no forward and reverse: the MAME driver's list of the machine's outputs has one entry for it, "Drawbridge motor", and nothing for direction. Two switches report its position, one that closes when the bridge is up and one that closes when it is down.

So to raise the bridge, the game starts the motor and waits for the up switch. To lower it, it starts the motor and waits for the down switch. If the bridge passes the other switch on the way, the game ignores it.

One move, timed

Each move runs as its own small task, so the rest of the game carries on while the motor turns. The heart of it, in the 6809 program:

JSR  motor_on      ; start the drawbridge motor
LDX  #$003C        ; allow 60 checks
JSR  sleep         ; wait a moment (8 ticks of the game's clock)
LDB  <$E1          ; has the bridge arrived where we asked?
BNE  arrived       ; yes: a good move
LEAX -1,X          ; no: one check fewer
BNE  ...           ; and wait again

That is sixty looks, a little over a tenth of a second apart. If the bridge has not arrived by the last one, the task gives up, turns the motor off and writes down a miss. That took 7.88 seconds every time.

One drawbridge move Two timelines from 0 to 7.88 seconds. In the first the motor runs until the switch closes partway through, when it stops at once and the move counts as good. In the second no switch closes, the motor runs for all 60 of the task's checks, and the move counts as a miss. Small ticks mark the checks, about 0.13 seconds apart; the switches themselves are watched 244 times a second. MOTOR ON THE TASK LOOKS THE SWITCH CLOSES switch closes: motor off at once, then a good move NOTHING HAPPENS 60 looks: a miss 0 s 7.88 s The switches are watched 244 times a second; the task looks every 0.13 s, 60 times at most.
One drawbridge move: the motor runs until the right switch closes, or until sixty checks have gone by.

The task does not stop the motor itself. A separate piece of code that runs 244 times a second looks at the two switches, and the moment the switch it is waiting for goes from open to closed, it cuts the motor and marks the bridge as arrived. The task only finds out at its next look. That split means the motor stops when the switch closes, not up to a tenth of a second later when the task wakes.

The "open to closed" part matters. Reading the code, a switch that is already closed when a move starts never produces that change, so it cannot count as an arrival either. That case was not tested.

Three strikes

Each of the two switches has a pair of bytes in memory: misses in a row, and good moves in a row. A miss adds one to the first:

LDA  up_misses     ; how many misses in a row so far?
BMI  done          ; already marked bad: nothing to add
INCA               ; one more
CMPA #$03          ; is that three?
BCS  store         ; not yet
LDA  #$80          ; three in a row: mark the switch bad

A good move while the switch is still trusted wipes the misses back to zero. So it is not three failures in total but three in a row. The same move that fails because a ball knocked the bridge sideways once will not get a switch condemned.

These bytes live in memory that is kept alive by a battery when the machine is off. The tally carries across games and across power cycles. That is why the operations manual warns that the game finds a broken drawbridge automatically but "may take several games" to do it.

How the game decides a drawbridge switch is broken A state diagram for one switch. While trusted, each miss adds one: 0, 1, 2. A good move goes straight back to 0. A third miss in a row marks the switch bad, which shows CHECK DRAWBRIDGE in the test report. While marked bad, a good move counts one, a miss starts the count over, and a second good move in a row clears the mark and returns to 0 misses. TRUSTED 0 misses 1 miss 2 misses MISS MISS 3RD MISS GOOD MOVE MARKED BAD: "CHECK DRAWBRIDGE" BAD 0 good moves BAD 1 good move GOOD 2ND GOOD MOVE MISS: START OVER Both counts sit in battery-backed memory, so they carry over from game to game.
The up switch's tally: three misses in a row mark it bad, and two good moves in a row clear the mark.

Once a switch is marked bad, the next time the coin door is opened the operator's test report shows "CHECK DRAWBRIDGE" with "UP SW. BROKEN" or "DOWN SW. BROKEN".

Two good moves

Being marked bad is not permanent. While the mark is set, every good move adds one to the second byte, and any miss resets it. Two good moves in a row and the game clears both the mark and the misses. The manual says the same from the operator's side: the error clears after the drawbridge completes two good operations in a row, and the drawbridge test in the service menu is the place to do it.

The game also gives a condemned switch a chance on its own. At power-up, if a switch is marked bad, the program sends the drawbridge towards that switch and waits, so a repaired switch can start earning its two good moves without anyone opening the service menu.

A bridge that never moves

The original program was run under a test script. The emulator does not model the drawbridge at all, which turns out to be useful: with nobody moving it, neither switch ever closes, exactly like a drawbridge with both switches dead. The script logged every move and the tally bytes while the machine was switched on and off repeatedly, keeping its memory between runs.

Power-upWhat the game didUp switch misses
1 (blank memory)set itself up from factory settings, no move0
2ran the motor 7.88 s, no switch1
3the same2
4the samemarked bad
5tried twice, 7.88 s each, still nothingmarked bad

With the mark set, the test report showed "CHECK DRAWBRIDGE" over "UP SW. BROKEN".

Then the script played the part of a working drawbridge: while the motor ran, it turned a pretend cam that closed the up switch once every four seconds of motor time. (The four seconds is made up; it is not the real mechanism's speed.)

Power-upWhat the game didGood movesUp switch misses
6motor stopped within about 0.02 s of the switch closing1still marked bad
7the same20, mark cleared

A last run checked the "in a row" part: after two misses, one good move set the count straight back to zero.

What is still open

Everything above was watched for the up switch. The down switch uses the same code with its own pair of bytes, but no test run asked the bridge to go down, so that half is read from the program only.

The manual promises that, with the drawbridge disabled, the game makes up for it in the rules. Other parts of the program do check the switch marks, but those have not been read yet, so what changes for the player is an open question.

The same tally code appears ten more times in the program, each on its own pair of bytes next to the drawbridge's. Eight of those give up after three misses in a row; two allow nine. The manual lists stuck-open and stuck-closed errors for the two loop gates, the tower diverter and the castle gate, so the drawbridge is probably one of a family. Which pair belongs to which part has not been traced.

Credits and further reading

  • Williams Electronics Games, Medieval Madness Operations Manual (July 1997): the drawbridge test (page 1-22), the "Disable Drawbridge" and related adjustments (page 1-38), and the error messages (page 1-52).
  • The MAME team, the WPC-95 driver wpc_95.cpp at MAME 0.289, for the machine's memory map, switch and output names. The driver credits the PinMAME developers for their research.
  • The full notes behind this article, with addresses and the script used: Drawbridge in this lab.