Quantcast
Viewing all articles
Browse latest Browse all 18

Turing Welchman Bombe BASIC code.

This is a follow on from my software Bombe post here: http://www.asciimation.co.nz/bb/2015/06/14/a-turingwelchman-bombe

The code is written to run on my home made Orwell 6502 computer. Orwell runs a modified version of MS Basic which is the same BASIC run on many 80s computers so this code should be easily portable to run on any similar machines. The code can be run on an Apple 2 (or the online Applesoft emulator here) with one simple change. More modern BASIC version will probably need more tweaks as MS BASIC has some quirks (like dimensioning arrays!).

Image may be NSFW.
Clik here to view.
IMG_3881_1
 Image may be NSFW.
Clik here to view.
IMG_3882_1

All the code is below, just copy and paste it. I will describe the general algorithm. Anyone really interested can work the rest out from there. My software Bombe has a few limitations, well simplifications. The BASIC code is written to run the Bletchley Park weather forecast crib and menu. It can be modified by changing the code directly but you need a good understanding of what menus are and how they work. It does not simulate the entire mechanical Bombe which has three banks of drums, called chains, that can be hooked together in various ways. My Bombe is really just one chain but that chain can have many scramblers (although more than 13 or so would be unusual). The BP example menu uses 13.

Given that BASIC is so slow you can configure the code to start offset from ZZZ. This is so you can see it working quickly. Remember a Bombe goes from AZZ to ZZZ in a complete cycle. Running on Orwell as I write this it has been running now for 68 hours and 45 minutes and it’s only up to YMZ!

I don’t guarantee that this code is bug free! But it should run the Bletchley Park menu and give the correct two stops. These are DXK:Q and FAN:K. The first three letters are what the indicator drums will show and the second is what the test register should show.

The general idea is as follows. Everything works around the diagonal board, the 26 by 26 array of letters. The rows are the possible menu letters, the columns are the 26 different voltages that make up the inputs and output of the scramblers.

  • For each menu letter we look at which columns have an untraced voltage.
  • If we find one we then feed that voltage into each scrambler connected to that menu letter.
  • We feed the output voltage from the scrambler back into the menu letter that scrambler is connected to.
  • Keep tracing around each voltage on each menu letter on the diagonal board until there are no more untraced voltages.
  • When there are no more voltages to trace check for a stop, i.e. less than 26 voltages are set.
  • If not all are set we stop. If all of them are set we clear the diagonal board, increment the drums and start again.

I also don’t know if this is the best way to do it. I am sure there are better, more efficient ways. But I haven’t seen any actual code online or even any algorithms explained so this is what I came up with. If you can do better knock yourself out, feel free to take this code and play with it and improve it. In the code and explanation I talk about currents and voltages interchangeably. I just mean there is a signal present at a certain point. In practical terms whether it is a voltage or a current depends on how you measure it!

OK, the code explained in the order it follows to run (more or less). The actual code is at the bottom.

Lines 100 – 210:

First the drums which represent the Enigma rotors. I have represented the Enigma rotors in my Bombe as arrays. My Bombe only uses the 5 Enigma rotors. Each array is in two parts to handle the double endedness of the Bombe drums. The first half is a direct lookup so input on input x give output y. The second half gives the reverse. With just one array the forward direction is easy, the input letter is the index into the array. In the reverse direction you would have to scan the whole array to find the matching letter then output the index.  My double ended array means you can use the index in both directions, you just offset half way into the array for the reverse direction. That was a big speed improvement in Orwell BASIC.

Another small detail to note is that the Bombe drums were wired up incorrectly! The wiring positions are offset from what they should be. Drums I, II and III are one position out. Drum IV is 2 positions out and Drum V is three positions out. I store the offset for each drum then that is added or subtracted as necessary in the code. Basically it works the same as a ring setting would! Since the drums are hard coded in the setup subroutine (10000 onwards) these offsets are also hard coded.

The reflectors are also stored here as lookup arrays.

Lines 230 – 980:

Various variables are declared here. The important ones to note are the arrays for the scrambler offsets and the scrambler connections. This is one of the Quirks in MS BASIC. The code DIM SO$(12) will give you a 13 element array! The array is indexed from 0 to 12.

Similarly we have a 2 dimensional array to represent the diagonal board so it is declared like this: DIM DB(25,25) to give us a 26×26 array.

Lines 700 to 720 are for the menu. We maintain a list of all the letters in the menu and for each letter a list of what scramblers it is connected to.

Line 1000:

The main program starts here. The first thing we so is jump to the setup subroutine to configure the Bombe.

Lines 10000 onwards:

This is where the Bombe and menu are configured. Everything is hard coded and it is here you would need to modify things to run different menus. That isn’t difficult but you need a very good understanding of how to do this.

Lines 10010 – 10030:

Here we select the drums in use and specify their offsets. For the BP menu we are using drums II, V and III (which have offsets 1, 3 and 1).

Line 10040:

The reflector to use, reflector B.

Lines 10050 – 10170:

The scrambler offsets used in the menu for each scrambler. The order these are listed here is no important except that the order of the next array must match it.

Lines 10200 – 10320:

The menu letters each scrambler above is connected to. Here you must match the two arrays, i.e. the scrambler in position ZZK on the menu is between letters U and E on the menu.

Lines 10330 – 10340:

Here you specify the input value and the test register letter, in this case A and G. The test register is connected to the menu letter marked as input on the menu.

Line 10350:

This is where you can specify the starting offset. The Bombe will step once form this offset then start tracing so if you want to start tracing at DKX you specify EKX here. Remember the Bombe moves the top drum first and the indicator drums letters go backwards!

Lines 10360 – 10370:

These lines configure debug printing. Even though it is terribly slow I recommend turning on the diagonal board printing. This will output what is happening on the diagonal board as the machine runs and it really beautifully shows exactly what is happening in the machine. In a real machine this all happens almost instantaneously. In code we have to trace every single little current around the system by iterating through until we’ve traced as far as we can. This is why software Bombes are so slow compared to an original. It takes a lot of computer power to do it.

This is another thing the film misportrayed. The Bombe is not a Turing machine. It’s not any kind of computer at all. It’s just a (bloody complicated) continuity tester! I do find it amusing though that I’ve written a simulation of the non Turing machine Bombe on a machine that is basically a Turing machine (not quite though, it gets complicated).

Lines 10400 – 10520:

The menu letter array. This is just the letter in the menu. This array need to match the following array of menu letter connections.

Lines 10530 – 10710:

The array of which scrambler is connected to each menu letter. This array must match the array above.

Lines 1050 – 1460:

Printing of the setup data. Here I just output the various settings so they can be checked on screen before starting the Bombe run. Here we also jump to a subroutine to offset all the scramblers if we have specified an offset in the setup. For each scrambler in turn we call the offset scrambler subroutine.

Lines 2800 – 2990:

The offset scrambler subroutine. If an offset was specified here we offset the drums of the scrambler that was passed in via the CS$ variable (current scrambler).

Lines 1800 – 1870:

Here we print out the scrambler settings to the screen after applying any offset.

Lines 5000 – 5520:

This is the main routine in the program. The PRINT CHR$(7) is the bell command. It makes the computer go Beep! The first thing we do is clear the diagonal board.

Lines 4700 – 4780:

Here we clear the diagonal board array. We also set the count of untraced voltages to 0. We do at the start of checking every drum position on the machine.

Lines 4000 – 4200:

These two subroutines set values on the diagonal board. This is a very important function and is called constantly as currents are traced around the system.

For each input letter we check we set not only it’s own value but it’s diagonal partner. It’s a simple 2 dimensional array so if we set (x,y) we also set (y,x). The rows represent the potential menu letters and the columns are the currents on those letters.

We do however set different values depending on the circumstance.

  • If a current is untraced the value is 0 (the board is initialised to 0).
  • If a letter is being set to itself (x,x) we leave it as a 0.
  • If the letter of the row is a menu letter we set the value to -1 which means an untraced voltage.
  • If the letter of the row is not a menu letter we set a 2 since this is a current we don’t need to trace.

Line 5030. 

Here we set the initial starting current. We set the current on the given input letter (‘A’) on the given menu letter that the test register (‘G’) is connected to.

Next we increment the drums and decrement the indicator.

Lines 2700 – 2770:

The move drums subroutine. For each scrambler we call the increment drums subroutine to move the drums one position on and handle any turnovers.

Lines 3000 – 3300:

The increment drums subroutine. Here we increment each drum starting with the top (fastest) drum. If the top drum wraps around (i.e. has done one revolution) we increment the second drum. Similarly with the third drum if the second has done one full revolution.

This is where my Bombe differs for the mechanical Bombe. If you watch that very closely when it is running you’ll note that the top drum actually does one an half revolutions before the middle drum moves. I haven’t seen this mentioned in any of the Bombe books or documentation online. The reason is the real Bombe is a physical machine so they carry over takes some time. Since the top drum is rotating continuously by the time the second (and third) drums have moved it has already moved on some positions. Obviously we can’t trace currents while things are moving like that so the real Bombe has a half cycle wait built in. It waits half a cycle before starting to measure after each revolution. I have modelled that behaviour in my mechanical model of the Bombe and my C code is modified to handle it.

Lines 3500 – 3700:

Here we decrement the indicator drums.

Line 5080:

Here we stop the Bombe run if we have checked all possibilities. Remember if the start position of the indicators is ZZZ the machine steps once to take the first measurement, AZZ, so it’s after we have checked ZZZ that we are done.

Lines 5110 – 5200:

This is where we loop around tracing the currents for this drum position. We loop until there are zero untraced voltages then we check the test register to see where the currents went.

We do this for each letter in the menu (the ML$ array). For each letter in the menu we scan across the diagonal board for the 26 different positions. Anywhere we find an untraced voltage (a -1) we jump to the trace voltage subroutine.

Line 6000 – 6190:

This is the tricky bit. Here, given an untraced voltage on a menu letter we figure out where the current will flow too.

For each scrambler connected to this menu letter (the MC array) we send the given input current through the scrambler and set the appropriate output. The scrambler subroutine gives us the output current after it has been through the scrambler. We check that scramblers connected menu letter (the SC$ array) and set the current on the one that is the other end of the scrambler (i.e. this menu letter connects to a scrambler that connects to another menu letter). We only set that output current if the value isn’t already set.

The current setting is done via the diagonal board so if we are checking current on letter ‘a’ on menu letter ‘E’ which connects to menu letter ‘U’ via scrambler 1 and the scrambler 1 give an output of ‘s’ for an input of ‘a’ on the diagonal board we will set ‘s’ on ‘U’ and also ‘u’ on ‘S’. Now since ‘S’ is also a menu letter we would set the diagonal board to -1 if it is currently not already -1 or 1 (meaning we’d already traced it). If the scrambler had output ‘t’ when given ‘a’ as the input we would set on the diagonal board ‘t’ on menu letter ‘U’ and also set the diagonal ‘u’ and ‘T’. But since ‘T’ isn’t a menu letter we set it to 2 since we will never trace it. The 2 is just for display purposes really. That current will never be traced anyway since we only trace current through the menu letters.

See, simple! If you understood that (good luck!) you can now see why the diagonal board is so important.

Every time we set a new untraced current (a -1 on the diagonal board) we increment a count of the untraced currents. Every time we trace one we set it’s value to 1 on the diagonal board and decrement the count. When the count is zero we’ve finished tracing.

Lines 2000 – 2660:

Here we handle feeding and input into a scrambler and getting back the output. Basically these subroutines implement an Enigma machine without the plugboard.

Lines 5210 – 5310:

When there are no more voltages to trace we check the test register. This is a simple matter of scanning the appropriate letter on the diagonal board (‘G’) and counting up all the set voltages. If it is less than 26 we have a stop. If we have a stop we print out the result and stop running.

If all 26 voltages were set it isn’t a stop so we start again. We increment the drums, clear the diagonal board. Feed in the test voltage to the test register again and start tracing current.

That’s basically it! There is other code there to print out various things such as the test register. The diagonal board printing subroutine (lines 4300 – 4450) will print out the diagonal board in a screen pretty format. The following characters are used:

  • A blank space for an untraced voltage.
  • A | for a traced voltage.
  • An x for an untraced voltage.
  • An o for a voltage we don’t trace.
  • The menu letters are down the left hand side, the test register letter is left blank.

Here is the actual code below. To run this yourself you can use the online Applesoft BASIC emulator here. Copy and paste the code into there and then change line ‘1010 CLS’ to ‘1010 HOME’ (you can also just remove line 1010 completely). This will start running on the known stop DKX and you can see how it traces through the diagonal board.

Note that this code is very slow! Running on Orwell, which has a 1Mhz clock, it would take about 4.8 months to do a full Bombe run. The real Bombe does a run in about 20 minutes. My C code version, running on an Intel i5 at 2.2Ghz takes about 10 seconds.

 

100 REM "ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
110 R1$ = "EKMFLGDQVZNTOWYHXUSPAIBRCJ UWYGADFPVZBECKMTHXSLRINQOJ" : REM I
120 R2$ = "AJDKSIRUXBLHWTMCQGZNPYFVOE AJPCZWRLFBDKOTYUQGENHXMIVS" : REM II
130 R3$ = "BDFHJLCPRTXVZNYEIWGAKMUSQO TAGBPCSDQEUFVNZHYIXJWLRKOM" : REM III
140 R4$ = "ESOVPZJAYQUIRHXLNFTGKDCMWB HZWVARTNLGUPXQCEJMBSKDYOIF" : REM IV 
150 R5$ = "VZBRGITYUPSDNHLXAWMJQOFECK QCYLXWENFTZOSMVJUDKGIARPHB" : REM V

200 UB$ = "YRUHQSLDPXNGOKMIEBFZCWVJAT" : REM Reflector B
210 UC$ = "FVPJIAOYEDRZXWGCTKUQSBNMHL" : REM Reflector C

230 RF$ = " " : REM Bombe Reflector
240 D1$ = " " : REM Top Bombe drum
250 D2$ = " " : REM Middle Bombe drum
260 D3$ = " " : REM Bottom Bombe drum
270 DIM DO(2) : REM Current drum offsets
280 D = 0: REM Drum offset from ENIGMA rotor 

300 DIM SO$(12) : REM Scramblers relative offsets
310 DIM SC$(12) : REM Scramblers connections

400 DIM DB(25,25) : REM Letters array
410 L1$ = "" : REM Diagonal board letter 1
420 L2$ = "" : REM Diagonal board letter 2

500 ID$ = "ZZZ" : REM Indicator drums

600 UT = 0 : REM Untraced voltages

650 D1 = 0 : REM Drum 1 counter
660 D2 = 0 : REM Drum 2 counter
670 D3 = 0 : REM Drum 3 counter

700 ML = 0 : REM Number of menu letters
710 DIM ML$(12) : REM Menu letters array
720 DIM MC(12,5) : REM Menu connections array

800 IV = 0 : REM Input voltage letter
810 TR = 0 : REM Test menu letter

900 CS$ = "ZZZ" : REM Scrambler offset 
910 SV = 1 : REM Scrambler value
920 V$ = "" : REM Scrambler character
930 OD = 0 : REM Offset current scrambler drum
940 CD$ = "" : REM Current scrambler rotor
950 L$ = "" : REM Scrambler drum letter
960 L = 0 : REM Scrambler drum letter value
970 SL = 0 : REM Scramblers connected output letter
980 VC = 0 : REM Test register voltage count

1100 REM ---------- Main program ----------
1010 CLS
1020 REM Bombe setup
1030 PRINT "BOMBE SETUP DATA..." : PRINT
1040 GOSUB 10000

1050 PRINT "TOP DRUM: "; : CD$ = D1$ : GOSUB 1600
1060 PRINT "MIDDLE DRUM: "; : CD$ = D2$ : GOSUB 1600
1070 PRINT "BOTTOM DRUM: "; : CD$ = D3$ : GOSUB 1600
1080 PRINT "REFLECTOR: "; : GOSUB 1700

1090 PRINT "SCRAMBLERS: "
1100 GOSUB 1800 : REM Print scramblers
1110 PRINT 

1170 PRINT "NUMBER OF MENU LETTERS: "; : PRINT ML

1180 PRINT "MENU LETTERS: "
1190 FOR I = 0 TO ML - 1
1200 PRINT ML$(I); : PRINT ":";
1210 FOR J = 0 TO 5 
1220 IF MC(I, J) <> 0 THEN PRINT MC(I, J); : PRINT " ";
1230 NEXT J
1240 PRINT
1250 NEXT I
1260 PRINT 

1300 PRINT "INPUT VOLTAGE: "; : PRINT CHR$(IV+65);
1310 PRINT
1320 PRINT "INPUT STECKER LETTER: "; : PRINT CHR$(TR+65);
1330 PRINT

1350 PRINT "INDICATOR START: "; : PRINT ID$
1360 IF ID$ = "ZZZ" THEN GOTO 1450
1370 PRINT "OFFSETTING SCRAMBLERS:"
1380 FOR K = 0 TO 12
1390 IF LEN(SO$(K)) = 0 THEN GOTO 1440
1400 CS$ = SO$(K)
1410 GOSUB 2800 : REM Offset drums
1420 SO$(K) = CS$
1430 NEXT K

1440 GOSUB 1800 : REM Print scramblers
1450 GOSUB 5000 : REM Solve subroutine.
1460 END

1600 REM ---------- Print drum number subroutine ----------
1610 IF CD$ = R1$ THEN PRINT "I"
1620 IF CD$ = R2$ THEN PRINT "II"
1630 IF CD$ = R3$ THEN PRINT "III"
1640 IF CD$ = R4$ THEN PRINT "IV"
1650 IF CD$ = R5$ THEN PRINT "V"
1660 RETURN

1700 REM ---------- Print reflector subroutine ----------
1710 IF RF$ = UB$ THEN PRINT "B"
1720 IF RF$ = UC$ THEN PRINT "C"
1730 RETURN

1800 REM ---------- Print scrambler subroutine ----------
1810 FOR K = 0 TO 12
1820 PRINT SO$(K); : PRINT ":"; : PRINT SC$(K);
1830 IF K = 4 THEN PRINT : GOTO 1860
1840 IF K = 9 THEN PRINT : GOTO 1860 
1850 PRINT " ";
1860 NEXT K
1870 RETURN

2000 REM ---------- Scrambler subroutine ----------
2005 IF P2 = 1 THEN PRINT "INPUT: "; : PRINT CHR$(SV+64); 
2010 CD$ = D3$
2020 OD = ASC(MID$(CS$, 3, 1)) - 65
2030 D = DO(2)
2040 GOSUB 2400 : REM Calculate scrambler offset
2050 GOSUB 2500 : REM Forward through rotor

2060 CD$ = D2$
2070 OD = ASC(MID$(CS$, 2, 1)) - 65
2080 D = DO(1)
2090 GOSUB 2400 : REM Calculate scrambler offset
2100 GOSUB 2500 : REM Forward through rotor

2110 CD$ = D1$
2120 OD = ASC(MID$(CS$, 1, 1)) - 65
2130 D = DO(0)
2140 GOSUB 2400 : REM Calculate scrambler offset
2150 GOSUB 2500 : REM Forward through rotor

2160 V$ = MID$(RF$, SV, 1)
2170 SV = ASC(V$) - 64 
2175 IF P2 = 1 THEN PRINT V$;

2180 CD$ = D1$
2190 OD = ASC(MID$(CS$, 1, 1)) - 65
2200 D = DO(0)
2210 GOSUB 2400 : REM Calculate scrambler offset
2220 GOSUB 2600 : REM Back through rotor

2230 CD$ = D2$
2240 OD = ASC(MID$(CS$, 2, 1)) - 65
2250 D = DO(1)
2260 GOSUB 2400 : REM Calculate scrambler offset
2270 GOSUB 2600 : REM Back through rotor

2280 CD$ = D3$
2290 D = DO(2)
2300 OD = ASC(MID$(CS$, 3, 1)) - 65
2310 GOSUB 2400 : REM Calculate scrambler offset
2320 GOSUB 2600 : REM Back through rotor

2325 IF P2 = 1 THEN PRINT " OUTPUT: "; : PRINT V$ 
2330 RETURN

2350 REM ---------- Wrap scrambler offset subroutine ----------
2360 IF SV < 1 THEN SV = SV + 26 : GOTO 2360
2370 IF SV > 26 THEN SV = SV - 26 : GOTO 2370
2380 RETURN

2400 REM ---------- Scrambler offset subroutine ----------
2410 REM SV = letter, OD = drum offset, 25 = Z ring, D = bombe drum offset
2420 SV = SV + OD - 25 - D
2430 GOSUB 2350 : REM Handle wapping
2440 RETURN

2500 REM ---------- Forward through scrambler subroutine ----------
2510 V$ = MID$(CD$, SV, 1)
2520 REM SV = letter, OD = drum offset, 25 = Z ring, D = Bombe drum offset 
2530 SV = (ASC(V$) - 64) - OD + 25 + D
2540 GOSUB 2350 : REM Handle wapping
2550 V$ = CHR$(SV+64) 
2555 IF P2 = 1 THEN PRINT V$;
2560 RETURN

2600 REM ---------- Back through scrambler subroutine ----------
2610 V$ = MID$(CD$, SV + 27, 1)
2620 REM SV = letter, OD = drum offset, 25 = Z ring, D = Bombe drum offset 
2630 SV = (ASC(V$) - 64) - OD + 25 + D
2640 GOSUB 2350 : REM Handle wapping
2650 V$ = CHR$(SV+64)
2655 IF P2 = 1 THEN PRINT V$;
2660 RETURN

2700 REM ---------- Move drums subroutine ----------
2710 FOR I = 0 TO 12
2720 IF LEN(SO$(I)) = 0 THEN GOTO 2760
2730 CS$ = SO$(I)
2740 GOSUB 3000
2750 SO$(I) = CS$
2760 NEXT I
2770 RETURN

2800 REM ---------- Set scrambler offset subroutine ----------
2810 L = ASC(LEFT$(CS$, 1))
2820 L = L + (26 - (ASC(LEFT$(ID$, 1)) - 64))
2830 IF L < 65 THEN L = L + 26 
2840 IF L > 90 THEN L = L - 26 
2850 L$ = CHR$(L) 
2860 CS$ = L$ + RIGHT$(CS$, 2)

2870 L = ASC(MID$(CS$, 2, 1))
2880 L = L + (26 - (ASC(MID$(ID$, 2, 1)) - 64))
2890 IF L < 65 THEN L = L + 26
2900 IF L > 90 THEN L = L - 26
2910 L$ = CHR$(L)
2920 CS$ = LEFT$(CS$, 1) + L$ + RIGHT$(CS$, 1)

2930 L = ASC(RIGHT$(CS$, 1))
2940 L = L + (26 - (ASC(RIGHT$(ID$, 1)) - 64))
2950 IF L < 65 THEN L = L + 26
2960 IF L > 90 THEN L = L - 26
2970 L$ = CHR$(L)
2980 CS$ = LEFT$(CS$,2) + L$
2990 RETURN

3000 REM ---------- Increment scrambler offset subroutine ----------
3010 L$ = LEFT$(CS$, 1)
3020 L = ASC(L$)
3030 L = L + 1 
3040 IF L > 90 THEN L = 65 
3050 L$ = CHR$(L) 
3060 CS$ = L$ + RIGHT$(CS$, 2)
3070 D1 = D1 + 1
3080 IF D1 < 26 THEN GOTO 3300
3090 D1 = 0

3100 L$ = MID$(CS$, 2, 1)
3110 L = ASC(L$)
3120 L = L + 1
3130 IF L > 90 THEN L = 65
3140 L$ = CHR$(L)
3150 CS$ = LEFT$(CS$, 1) + L$ + RIGHT$(CS$, 1)
3160 D2 = D2 + 1
3170 IF D2 < 26 THEN GOTO 3300
3180 D2 = 0

3200 L$ = RIGHT$(CS$, 1)
3210 L = ASC(L$)
3220 L = L + 1
3230 IF L > 90 THEN L = 65
3240 L$ = CHR$(L)
3250 CS$ = LEFT$(CS$,2) + L$
3260 D3 = D3 + 1
3270 IF D3 < 26 THEN GOTO 3300
3280 D3 = 0
3300 RETURN

3500 REM ---------- Decrement indicator drums subroutine ----------
3510 L$ = LEFT$(ID$, 1)
3520 L = ASC(L$)
3530 L = L - 1 
3540 IF L < 65 THEN L = 90 
3550 L$ = CHR$(L) 
3560 ID$ = L$ + RIGHT$(ID$, 2)
3570 IF L$ <> "Z" THEN GOTO 3800

3600 L$ = MID$(ID$, 2, 1)
3610 L = ASC(L$)
3620 L = L - 1
3630 IF L < 65 THEN L = 90
3640 L$ = CHR$(L)
3650 ID$ = LEFT$(ID$, 1) + L$ + RIGHT$(ID$, 1)
3660 IF L$ <> "Z" THEN GOTO 3800

3700 L$ = RIGHT$(ID$, 1)
3710 L = ASC(L$)
3720 L = L - 1
3730 IF L < 65 THEN L = 90
3740 L$ = CHR$(L)
3750 ID$ = LEFT$(ID$,2) + L$
3800 RETURN

4000 REM ---------- Diagonal board subroutine ----------
4010 IF DB(ASC(L1$) - 65, ASC(L2$) - 65) = 0 THEN GOSUB 4100
4020 IF L1$ = L2$ THEN GOTO 4050
4030 T$ = L1$ : L1$ = L2$: L2$ = T$
4040 IF DB(ASC(L1$) - 65, ASC(L2$) - 65) = 0 THEN GOSUB 4100
4050 RETURN

4100 REM ---------- Set value subroutine ---------- 
4120 FOR I2 = 0 TO ML 
4130 IF L1$ = ML$(I2) THEN GOTO 4200
4140 NEXT I2
4150 DB(ASC(L1$) - 65, ASC(L2$) - 65) = 2
4160 RETURN
4200 DB(ASC(L1$) - 65, ASC(L2$) - 65) = -1
4205 UT = UT + 1
4210 RETURN

4300 REM ---------- Print diagonal board ----------
4310 PRINT;
4320 PRINT " ";: FOR I1 = 0 TO 25
4330 PRINT CHR$(I1+65);
4340 NEXT I1 : PRINT
4350 FOR I1 = 0 TO 25
4360 FOR J1 = 0 TO 25
4370 IF J1 = 0 THEN GOTO 4440
4380 IF DB(I1,J1) = 0 THEN PRINT " ";
4382 IF DB(I1,J1) = 1 THEN PRINT "|";
4384 IF DB(I1,J1) = -1 THEN PRINT "x";
4385 IF DB(I1,J1) = 2 THEN PRINT "o";
4390 NEXT J1
4400 PRINT
4410 NEXT I1
4420 PRINT
4430 RETURN
4440 IF I1 = TR THEN PRINT " "; : GOTO 4380
4450 PRINT CHR$(I1+65); : GOTO 4380

4500 REM ---------- Print test register ----------
4510 PRINT "TEST REGISTER: " 
4520 FOR I1 = 0 TO 25
4530 PRINT CHR$(I1+65); 
4540 NEXT I1 : PRINT
4550 FOR I2 = 0 TO 25
4560 IF DB(TR,I2) = 1 THEN PRINT "|";
4570 IF DB(TR,I2) <> 1 THEN PRINT " ";
4580 NEXT I2
4590 RETURN

4700 REM ---------- Clear diagonal board ----------
4710 FOR I1 = 0 TO 25
4720 FOR J1 = 0 TO 25
4730 DB(I1,J1) = 0
4740 NEXT J1
4760 NEXT I1
4770 UT = 0 : REM Clear the untraced count
4780 RETURN

5000 REM ---------- Solve subroutine ----------
5010 PRINT CHR$(7)
5020 GOSUB 4700 : REM Clear diagonal board
5030 L1$ = CHR$(IV + 65) : L2$ = CHR$(TR + 65) : GOSUB 4000

5050 GOSUB 2700 : REM Increment drums
5060 GOSUB 3500 : REM Decrement indicator 
5070 PRINT :PRINT : PRINT "INDICATOR: "; : PRINT ID$
5080 IF ID$ = "ZZZ" THEN GOTO 5500
5090 IF P1 = 1 THEN PRINT "SCRAMBLERS: "
5100 IF P1 = 1 THEN GOSUB 1800 : REM Print scramblers

5110 FOR I = 0 TO ML - 1 : REM For each menu letter
5115 IF UT = 0 THEN GOTO 5210 : REM Loop until no voltage untraced
5120 IF P1 = 1 THEN PRINT
5130 IF P1 = 1 THEN PRINT "CHECKING LETTER: "; : PRINT ML$(I)
5135 IF P1 = 1 THEN PRINT "Untraced: "; : PRINT UT
5140 IF P3 = 1 THEN GOSUB 4300 : REM Print diagonal board
5160 FOR J = 0 TO 25 : REM Check each voltage on this letter
5170 IF DB(ASC(ML$(I))-65,J) = -1 THEN GOSUB 6000
5180 NEXT J
5190 NEXT I
5200 GOTO 5110

5210 REM Check test register
5260 VC = 0
5270 FOR I2 = 0 TO 25
5280 IF DB(TR,I2) = 1 THEN VC = VC + 1
5290 NEXT I2
5300 IF VC < 26 GOTO 5400 : REM A stop!
5310 GOTO 5020 : REM All voltages traced

5400 PRINT : PRINT "STOP" : PRINT CHR$(7)
5410 PRINT "INDICATOR: "; : PRINT ID$
5420 GOSUB 4500 : PRINT : REM Print test register
5430 REM STOP
5440 GOTO 5020

5500 PRINT "BOMB RUN COMPLETE" 
5510 PRINT CHR$(7)
5520 END

6000 REM ---------- Trace voltage ----------
6010 DB(ASC(ML$(I))-65, J) = 1 : UT = UT - 1
6020 FOR K = 0 TO 5
6030 REM For each connected scrambler 
6040 IF MC(I,K) = 0 THEN RETURN
6050 REM Setup the scrambler
6060 CS$ = SO$(MC(I,K)-1)
6070 SV = J + 1 : REM Input letter
6080 GOSUB 2000 : REM Through scrambler
6090 IF LEFT$(SC$((MC(I,K)-1)),1) <> ML$(I) THEN GOTO 6100
6095 SL = ASC(RIGHT$(SC$((MC(I,K)-1)),1))-65 : GOTO 6110
6100 SL = ASC(LEFT$(SC$((MC(I,K)-1)),1))-65
6110 IF DB(SL,SV-1)=-1 THEN GOTO 6180
6120 IF DB(SL,SV-1)=1 GOTO 6180
6140 REM Feed into diagonal board
6150 L1$ = CHR$(SL+65)
6160 L2$ = CHR$(SV-1+65)
6170 GOSUB 4000 : REM Diagonal board
6180 NEXT K
6190 RETURN

6200 REM ---------- Print debugging subroutine ----------
6220 PRINT MC(I,K); : PRINT" ";
6230 GOSUB 6500 : PRINT":";
6240 PRINT CHR$(J+65); : PRINT"-"; 
6250 PRINT CHR$(SV+64); : PRINT" ";
6260 PRINT CHR$(SL+65); : PRINT 
6270 RETURN

6500 REM ---------- Print drum ----------
6510 FOR K1 = 1 TO 3
6520 L = ASC(MID$(CS$,K1,1))
6530 L = L - DO(K1-1)
6540 IF L < 65 THEN L = L + 26
6550 IF L > 90 THEN L = L - 26 
6560 PRINT CHR$(L);
6570 NEXT K1
6580 PRINT " ";
6590 RETURN

10000 REM ---------- Setup subroutine ----------

10010 D1$ = R2$ : DO(0) = 1 : REM Rotor 2
10020 D2$ = R5$ : DO(1) = 3 : REM Rotor 5
10030 D3$ = R3$ : DO(2) = 1 : REM Rotor 3

10040 RF$ = UB$ : REM Reflector B

10050 SO$(0) = "ZZK"
10060 SO$(1) = "ZZE"
10070 SO$(2) = "ZZF"
10080 SO$(3) = "ZZN"
10090 SO$(4) = "ZZM"
10100 SO$(5) = "ZZG"
10110 SO$(6) = "ZZP"
10120 SO$(7) = "ZZB"
10130 SO$(8) = "ZZJ"
10140 SO$(9) = "ZZI"
10150 SO$(10) = "ZZL"
10160 SO$(11) = "ZZO"
10170 SO$(12) = "ZZA"

10200 SC$(0) = "UE"
10210 SC$(1) = "EG"
10220 SC$(2) = "GR"
10230 SC$(3) = "RA"
10240 SC$(4) = "AS"
10250 SC$(5) = "SV"
10260 SC$(6) = "EV"
10270 SC$(7) = "EN"
10280 SC$(8) = "HZ"
10290 SC$(9) = "RZ"
10300 SC$(10) = "GR"
10310 SC$(11) = "GL"
10320 SC$(12) = "SW" 

10330 IL = 0 : REM Input letter value - A
10340 TR = 6 : REM Test register value - G

10350 ID$ = "EKX" : REM Indicator offset

10360 P1 = 1 : REM Debug printing 0 = off
10370 P2 = 0 : REM Enigma printing 0 = off
10380 P3 = 0 : REM Diagonal board printing 0 = off

10400 ML$(0) = "U"
10410 ML$(1) = "E"
10420 ML$(2) = "G"
10430 ML$(3) = "R"
10440 ML$(4) = "A"
10450 ML$(5) = "S"
10460 ML$(6) = "V"
10470 ML$(7) = "N"
10480 ML$(8) = "H"
10490 ML$(9) = "Z"
10500 ML$(10) = "L"
10510 ML$(11) = "W" 
10520 ML = 12 : REM Number of menu letters

10530 FOR I = 0 TO ML - 1 : REM Menu letter connections
10540 FOR J = 0 TO 5 : REM Connections for letter
10550 READ S
10560 MC(I, J) = S
10570 NEXT J
10580 NEXT I

10600 DATA 1, 0, 0, 0, 0, 0 : REM 'U'
10610 DATA 1, 2, 7, 8, 0, 0 : REM 'E'
10620 DATA 2, 3, 11, 12, 0, 0 : REM 'G'
10630 DATA 3, 4, 10, 11, 0, 0 : REM 'R'
10640 DATA 4, 5, 0, 0, 0, 0 : REM 'A'
10650 DATA 5, 6, 0, 0, 0, 0 : REM 'S'
10660 DATA 6, 7, 0, 0, 0, 0 : REM 'V'
10670 DATA 8, 0, 0, 0, 0, 0 : REM 'N'
10680 DATA 9, 0, 0, 0, 0, 0 : REM 'H'
10690 DATA 9, 10, 0, 0, 0, 0 : REM 'Z'
10700 DATA 12, 0, 0, 0, 0, 0 : REM 'L'
10710 DATA 13, 0, 0, 0, 0, 0 : REM 'W'

20000 RETURN


Viewing all articles
Browse latest Browse all 18

Trending Articles