Re-hacking the 80’s- part 4
Unbugging the past
In this part I’m going to describe how I removed a ‘feature’ of the game that I consider to be a bug.
Backup material
But first: Maybe you imaginary readers would like to have the disassembled source I use, so I uploaded the currenty version. I still add comments to it when I find out stuff, so there will be newer versions later (1).
Bug or not?
In the game there is a finite number of swords. Some are just lying around, and some can be retrieved after being thrown by enemies. But once a thrown sword is picked up, that enemy will never throw another sword.
And a sword thrown at an enemy will be spent as well.
So you’d better make good use of that sword. That is part of the challenge of the game: you really have to think about when you use your sword on what.
But you could be carrying a sword and then die by walking to another screen you’ve never been to and be immediately killed by an enemy.
You will not only loose a life but also that sword, probably making the game impossible to finish (I erroneously stated that you would lose a sword when killed by a bridge giant, but that’s not true).
I think that’s unfair so I will try to fix that. Furthermore I think I will not add it to the game as an optional trainer option but always incorporate that in the game.
Side note
I wondered how much code this game has. I stripped all data and comments from my disassemled document and ended up with less than 2000 lines of code.
Considering how little one line of assembly code can do (for instance, ‘add 1 to x register’) that’s not much!
More detectoring
Now I have to find the piece of code responsible for handling the acquiring and losing of a sword.
You get a sword by walking over one. That happens when the sprites of the player and the sprite of a sword collide.
The Commodore 64 has a memory location that detects sprite collisions: $D01E (to be more accurate: the video chip detects sprite collisions and updates $D01E accordingly). In the first episode I’ve shown a small part where that memory is read to detect a collision of the player with a giant’s arm. But that part only codes for collision with the giants’ arms. So where else is collision detected? Sadly that is the only part where $D01E is read, so collision detection works differently for picking up swords.
So what now? By now I have a pretty good understanding of the code, so I know where collision detection takes place in the code, but there is is a different way to solve the puzzle: if you press the joystick button AND you have a sword then a sword is thrown.
Finding the code for that is not that hard. The joystick states can be read from memory $DC00 and $DC01 (joystick port 2 resp. 1). I know which port is used so I can search with VICE for the byte sequence 8D 00 DC (LDA $DC00, it could also be LDX or LDY, but that’s less likely)(2).
You get a few hits but one looks promising. Here is the exerpt of the code (you can see some auto-commented code but also some code I added when figuring out how it works).
Two earlier discoveries help me here (3):
- the 8 sprites of the C64 all have its own role in this program, you can turn sprites on and off and see this pattern emerging:
sprite# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
player | bridge-left | samurai & left giant |
middle giant | bridge-right & right giant |
not used | enemy sword | sword & key& treasure | |
- $0DA0 is the address of a subroutine that turns sprites on (the X register is the number of the sprite to turn on, numbered from 1-8).
$0DAA does the same, except that it turns that sprite off
L9C00 LDY $49 ; BNE L9C57 ; LDA $8E ; bit 7 set in $8E: has sword BPL L9C56 ; LDA LDC00 ; Data Port A (Keyboard, Joystick, Paddles, Light-Pen) 7-0 Write Keyboard Column Values for Keyboard Scan 7-6-2006 Read Paddles on Port A / B (01 = Port A, 10 = Port B) 4 Joystick A Fire Button: 1 = Fire 3-2-2006 Paddle Fire Buttons 3-0 Joystick A Direction (0-15) MOS 6526 Complex Interface Adapter (CIA) #2 AND #$10 ; FIRE button bit. Is zero if pressed! BEQ L9C15 ; [code that checks keyboard is left out here, the game can be controlled by keyboard and joystick] L9C15 LDX L01 ; copy player pos to sword pos L9C17 LDA LD000,X ; Sprite 0 X Pos MOS 6566 VIDEO INTERFACE CONTROLLER (VIC) STA LD00A,X ; Sprite 5 X Pos MOS 6566 VIDEO INTERFACE CONTROLLER (VIC) DEX ; BPL L9C17 ; LDY L4343 ; 4343 = player direction? STY L9C8C ; LDA L40A8,Y ; STA spr6point ; set sprite pointer depending on player direction LDX L40A4,Y ; STX $4B ; sword direction? LDA L40A0,Y ; some more table reading CLC ; and adding that to the sprite position based on that table ADC LD00A,X ; Sprite 5 X Pos MOS 6566 VIDEO INTERFACE CONTROLLER (VIC) STA LD00A,X ; Sprite 5 X Pos MOS 6566 VIDEO INTERFACE CONTROLLER (VIC) LDA #$0F ; set color of the sprite STA LD02C ; Sprite 5 Color MOS 6566 VIDEO INTERFACE CONTROLLER (VIC) LDA #$60 ; not sure what this does, yet STA $49 ; LDX #$05 ; position sprite 5 JSR L0DA0 ; LDA #$00 ; set 0 in three memory locations STA $8E ; set to not carrying sword STA L4371 ; STA L436F ; [....]
On a side note: I think there is a bug at ‘adc ld00a,x’. That part places the sword sprite in an offset from the sprite position. But X is always 1 at that point of the code, so indexed addressing is useless here. It could be replaced by ‘adc ld00b’. Now only D00B (Y position of sprite) is changedI think it was meant to be in the loop so the X position would also be changed.
The end of the code here is interesting: 3 memory locations are set to zero. Maybe one of them means ‘not carrying sword’?
I can see $8E is also read in the beginning of the subroutine. It determines if the joystick button is read at all.
So it probably it determines if the player has a sword or not.
We can test it by changing that value live with VICE. It would mean the player could throw a sword again:
Does it work?
So I know what to change. I also found out that address $4371 is as a sword-indicator but only for the visual part. Putting value #$10 in it, makes sure the player walks holding a sword.
If you start to look for parts where both are reset (which typically happens when you die or start a new game), you find a few regions, but the region starting at $9443 seems to be the right part.
; handle death? L9443 LDA #$12 ; only sprites 1 and 5 are multicolour, the brige parts STA LD01C ; Sprites 0-7 Multi-Color Mode Select: 1 = M.C.M. MOS 6566 VIDEO INTERFACE CONTROLLER (VIC) LDA #$00 ; STA LD017 ; Sprites 0-7 Expand 2x Vertical (Y) MOS 6566 VIDEO INTERFACE CONTROLLER (VIC) >> STA L4371 ; 0 = sprite carrying no sword (only visual) LDA #$38 ; start position STA $L5F ; screen number LDX #$01 ; STX $D015 ; Sprite display Enable: 1 = Enable MOS 6566 VIDEO INTERFACE CONTROLLER (VIC) L9476 LDA #$00 ; STA $60 ; STA $61 ; STA $49 ; >> L947E STA $8E ; $8E, value #$80 means, carrying sword LDY #$63 ; STY spr0point ; LDA #$02 ; STA $3F ; JMP L9936 ; goto Main Loop
So I have the way to ‘bugfix’ the game. It will be incorporated in the trained version.
Finally
I’d like to end with an animation of a feature I’m working on: the map. I succeeded in generating a map of the game and the objects when the game starts. You can toggle the map. It’s not finished yet, but almost there (I’ll change the colors I promise!).
(1): I made my disassembler program before I used version control software. The newest file I used turned out to have several bugs, that don’t bother me, but make it less readable: you can’t tell the difference between zero page addressing ($xx) and numbers (#$xx).
(2): The A register can be used to for arithmetic (adding subtracting) and bit manipulation which is useful if want to know stuff about the joystick. Bit 5 for instance is the fire button, so AND #$10 sets the zero bit in the status register if bit 5 is not set in the A register.
(3): knowledge of the C64 is necessary to understand what’s happening. Here: knowing $D015 turns sprites on where each bit codes for a sprite.
Re-hacking the 80’s-part 5: training run | Goerp
[…] Re-hacking the 80’s- part 4<< […]