
I grew up in the 80s, in the decade, when home computers turned from a curiosity to a mainstream. In my elementary school there were several home computers Phillips P2000T and a pair of Apple Macintosh. My friend had a Commodore 64, in which we played games, and once my father bought Commodore 128 for managing finances. close approach "did not break - do not fix it.")
Almost immediately after the C128, we bought the C64. C128 used for business, and C64 - for entertainment. I clearly remember playing Space Taxi, Super Cycle, Velocipede, Last Ninja II, Electrix and other games. In addition, on this computer, I began to learn programming.
')
Before the invention of the World Wide Web, there was still a couple of years, so the study of programming basically consisted in reading books and magazines. Computer magazines often published source code listings that the reader needed to reprint. The result could be any: a game, a utility for copying a disk, a drawing program for GEOS or, more often than we would like, something that works somehow due to
typos . At some point, the journals began to publish listings with a checksum next to each line. They had special programs that checked the checksum of each input string. Such programs helped a lot. But still it was one of the slowest and error-prone ways to copy computer programs in the history of computers. Nevertheless, the process was quite interesting (at least, it seemed to me so).
One of these magazines was
Commodore Dossier , a “practical magazine for active Commodore owners,” which was produced from 1984 to 1988. In the seventeenth, which was the last, the release contained a listing of a rather interesting game. It was called
Blindganger , which in Dutch means “sly”, but here the meaning of the word blind was also used.
Cover of the seventeenth edition of Commodore DossierAfter a
well- spent night, the blind hero of the game wakes up and discovers that he somehow fell into the city sewer. The player's task is to bring him back to the street, using only the sounds made by his cane.
City sewer services, overly concerned about the cleanliness of pipes, drain water every five
minutes . At each discharge, our blind hero throws another sewer, from where he must begin his search for a way out again. After the third drain the game ends. The player can see the sewage map showing the location of the exit and the location he visited.
Around the autumn of 1988, having entered into the computer listing, I started the game. It had a sprite animation of a bat flying on a completely black screen in front of a full moon, which seemed to me great. However, I could not figure out how to manage. The sewer map certainly helped, but there was something wrong with it. Locations marked on the map as visited did not resemble the path I traveled. Moreover, even walls were often visited by visitors.
Sample sewer map from the original gameAfter a couple of attempts, I gave up and did something else, but the problem of the map and unintelligible management was remembered to me. So when I came across a scan of this
Commodore Dossier release, I realized that it was time to deal with it once and for all.
Of course, before I started, I had
to drive the whole listing
again . Then, in 1988, I did not have the checksum verification utility published in
Commodore Dossier . Maybe the sewer map was confused because of my carelessness and typos?
A quick search in Google brought me to the disk image with the program "CHECKSUM DOSSIER". It sounds promising! To check, I launched the program and printed a short line from the listing of
Blindganger : “240 RETURN”, next to which was the checksum “7E”. Then I tried to insert a
typo in the string itself, and in the checksum. In both cases, the message “FOUT IN REGEL”, that is, “ERROR IN THE LINE”, was displayed on the screen. Fine!
Isn't that cute?With this program, I entered the entire listing. Some lines of the scanned journal were poorly read, so the utility turned out to be irreplaceable. Often I had to select different variants of combinations of strings and checksums, until they matched. Well, so far everything is going well. Now I had a copy of the original, in which there were definitely no typos. Of course, in this copy should not be those bugs that arose in me in 1988. But ...
they were !
To find out the reason, I needed to find a code showing a map of the sewer on the screen. The card is shown at the end of the game, that is, when the player finds a way out or after the third water drain. I focused on the second case and started searching in the listing on BASIC for the numbers 0 or 3.
In line 1780, the variable
MAAL was found, which was compared to zero. This variable was initialized with a value of 3 in line 1550 and decreased with each drain.
1780 MAAL=MAAL-1:IFMAAL=0THENGOTO1810
The code block in lines 1810-1840 is executed if the
MAAL is zero. Line 1840 is an infinite loop. Looking at the remaining three lines of BASIC, I realized that our next goal is a routine in machine code located at a memory address of 16540 ($ 409C).
1810 POKECROSS+4096,1 :REM MARK EXIT ON THE MAP 1820 SYS16540 :REM COPY MAP TO SCREEN 1830 POKESID+11,0:POKE53248+21,0:REM STOP SOUND AND DISABLE SPRITES 1840 GOTO1840 :REM ENDLESS LOOP
This subprogram consists of two parts. The first copies 1000 bytes from the memory area, starting at $ 6000, into screen memory ($ 0400- $ 07E7). It fills the entire screen (40 columns for 25 lines = 1000 bytes) with a sewer map.
As it turned out, there are no bugs in this part. The map may
seem strange until you notice that all the solid parts are walls, the horizontal sections of pipes are marked with "@", the vertical sections are marked with "A", the characters from "B" to "J" denote different types of angles and intersections The “K” is the pit, and the “L” is the exit. These characters are not accidental. They correspond to screen codes from 0 to 12. The map is just a dump to the screen of the internal map view in the game.
The second part of the subroutine copies 1000 bytes from the memory area, starting at $ 7,000, into the color RAM ($ D800- $ DBE7). So all visited locations are painted yellow.
L40CB: LDA #$FF ; >-- -- STA $FB ; | LDA #$6F ; | STA $FC ; | LDA #$F4 ; | STA $FD ; | LDA #$D7 ; | STA $FE ; <-------------------- LDX #$04 ; >-- ----------------- L40DD: LDY #$FA ; >-- . I --- | L40DF: LDA ($FB),Y ; ( ) | | STA ($FD),Y ; | | DEY ; | | BNE L40DF ; <------------------- | LDY #$FA ; >-- . II ----------- | L40E8: INC $FB ; ( ) | | BNE L40EE ; | | INC $FC ; | | L40EE: INC $FD ; | | BNE L40F4 ; | | INC $FE ; | | L40F4: DEY ; | | BNE L40E8 ; <---------------------------- | DEX ; | BNE L40DD ; <-------------------------------
Bytes are copied in four blocks of 250 bytes. The outer loop uses register X as a counter from 4 to 0.
LDX
The first inner loop uses the Y register as a counter from # $ FA (250) to 0.
L40DD: LDY #$FA L40DF: LDA ($FB),Y STA ($FD),Y DEY BNE L40DF
Indirect indexing is used to copy bytes in the internal loop. In this addressing mode, the Y register is used as the offset added to the 16-bit starting address stored in the
zero page .
For example, the
LDA ($FB),Y
command
LDA ($FB),Y
is executed as follows:
- A 16-bit starting address is read in direct byte order , stored in the $ FB and $ FC sections of the zero page. Direct byte ordering means that the least significant byte is stored in the lowest address ($ FB). $ FB is initialized to # $ FF, and $ FC is initialized to # $ 6F, so the 16-bit starting address will be $ 6FFF.
- To calculate the real address, the value of the register Y is added to the starting address. The register Y is initialized at the beginning of the cycle in # $ FA. Adding to $ 6FFF, we get a valid address of $ 70F9.
- The drive (register A) is loaded bytes from a valid address.
The Y register counts down from 250 to 0. Note that the minimum Y value used as an index is 1. (When the DEY command reduces Y to zero, the next branch command BNE passes by and exits the internal loop.)
The first downloadable byte is located at $ 7000. Since the minimum value of Y is 1, the starting address of the
source must be initialized to $ 7000 - 1 = $ 6FFF. Similarly, the first byte to be stored is located at $ D800, so the
target start address must be initialized to $ D800 - 1 = $ D7FF. Instead, it is initialized to $ D7F4!
Random typing and typing # $ F4 instead of # $ FF is unlikely. But in decimal notation, this corresponds to entering 244 instead of 255. It seems that the author of the game accidentally clicked on 4 instead of 5 when entering the constant 255! This shifted the colors used in the map by 11 positions relative to the map itself. In turn, this means that the locations visited were incorrectly marked on the map.
The fault byte is in the data set statement in line 3670 of the BASIC listing:
3670 DATA 252,169,244,133,253
After replacing it with the correct value (255), the sewer map starts to be displayed correctly. And that means ... that we fixed a mistake 29 years ago!
To celebrate this, I compiled a version of the original game of 2017 with this fix, as well as corrections of some other errors found while studying the code:
- Visited locations are now correctly displayed on the map shown at the end of the game.
- When calculating the remaining time, the penalty for falling into the pit is taken into account.
- The volume of noise from the streets is changed in such a way that now depends on the distance to the exit (as the game authors intended).
- Translation into English. (Press and hold the H key to view help.)
Blindganger 2017 is also available in BASIC listing form,
including checksums . To feel yourself in the 80s, download and run the Commodore Dossier checksum utility (see link below), and then start typing!
Later, I also made improvements to the (already correct) sewer map to make it easier to read (see example below).
Cross marked the exit!Links