Passwords


Introduction

Ever wanted to know where a password from Demeter, say
UR3F UR3F UR4F 423R UR3F UR3F UR3F UR3F UR3F UR3F URS4 8
comes from... or what it means? I did.

Part 0: Password map

This password map comes from http://www.braingames.getput.com/mog/passwords.html, but I enhanced it and fixed some bugs.
UR3F UR3F UR4F 423R UR3F UR3F UR3F UR3F UR3F UR3F URS4 8
EB00 EB01 EB02 EB03 EB04 EB05 EB06 EB07 EB08 EB09 EB0A EB0B EB0C EB0D EB0E EB0F
U R 3 F U R 3 F U R 4 F 4 2 3 R
0 at0 au1 --- ah3 cu0 --- ch2 kt3 --- kh1 ea0 ea5 va2 va7 ep4 vp1
1 at1 au2 --- ct0 cu1 --- ch3 ku0 --- kh2 ea1 ea6 va3 ep0 ep5 vp2
2 at2 au3 ah0 ct1 cu2 --- kt0 ku1 --- kh3 ea2 ea7 va4 ep1 ep6 vp3
3 at3 --- ah1 ct2 cu3 ch0 kt1 ku2 --- xxx ea3 va0 va5 ep2 ep7 vp4
4 au0 --- ah2 ct3 --- ch1 kt2 ku3 kh0 xxx ea4 va1 va6 ep3 vp0 vp5
---
EB10 EB11 EB12 EB13 EB14 EB15 EB16 EB17 EB18 EB19 EB1A EB1B EB1C EB1D EB1E EB1F
U R 3 F U R 3 F U R 3 F U R 3 F
0 vp6 w1r w10 w2w w22 --- w4c w41 w5m --- w60 w7w w72 --- w9c w91
1 vp7 w1c w11 w2m w2r w30 w4w w42 --- w6r w61 w7m w8r w80 w9w w92
2 xxx w1w w12 --- w2c w31 w4m w5r w50 w6c w62 --- w8c w81 w9m war
3 xxx w1m w2r w20 w2w w32 --- w5c w51 w6w w7r w70 w8w w82 --- wac
4 xxx --- w2c w21 w2m w4r w40 w5w w52 w6m w7c w71 w8m w8r w90 waw
---
EB20 EB21 EB22 EB23 EB24 EB25 EB26 EB27 EB28 EB29 EB2A EB2B EB2C
U R 3 F U R 3 F U R S 4 8
0 wam arr mag crs oar hal lmp rng pit brw ch0 c00 c10
1 wa0 car --- grk shs cdl vas bib sab slt ch1 c01 c11
2 wa1 rof --- nkl dll arm pdt hrp dag ssh ch2 c02 c12
3 wa2 fir --- crn rob cpt ear trg fea gsh ch3 c03 c13
4 --- min --- hmt bll hlm brt trs shd xxx ch4 c04 c14
atX Arrows (tens) bit X
auX Arrows (units) bit X
ah0 Arrows (hundreds) bit X
ctX Coins (tens) bit X
cuX Coins (units) bit X
chX Coins (hundreds) bit X
ktX Keys (tens) bit X
kuX Keys (units) bit X
khX Keys (hundreds) bit X
eaX Experience Aphrodite bit X. Experience of 0 means full life when an enemy is shattered. Supposed to be a fixed value of (???)
vaX Maximum vitality Aphrodite bit X. Initially at 8. Increases by 8 for every great key picked up.
epX Experience Popolon bit X
vpX Maximum vitality Popolon bit X
wXr World X rod
wXc World X cape
wXw World X holy water
wXm World X map
wXX World X state bit X
arr Arrows
car Ceramic arrows
rof Rolling fire
fir Fire
min Mines
mag Magnifying glass
crs Cross
grk Great Key
nkl Necklace
crn Crown
hmt Helmet
oar Oar
shs Shoes
dll Doll
rob Robe
bll Bell
hal Halo
cdl Candle
arm Armor
cpt Carpet
hlm Helm
lmp Lamp
vas Vase
pdt Pendant
ear Earrings
brt bracelet
rng Ring
bib Bible
hrp Harp
trg Triangle
trs Trumpet shell
pit Pitcher
sab Saber
dag Dagger
fea Feather
shd Bronze Shield
brw Bread and water
slt Salt
ssh Silver shield
gsh Golden shield
ch0 0: Aphrodite active
1: Popolon active
ch1 0: Popolon cannot be returned to life using Death's shrine
1: Popolon can be returned to life using Death's shrine
ch2 0: Aphrodite cannot be returned to life using Death's shrine
1: Aphrodite can be returned to life using Death's shrine
ch3 0: Popolon is dead (overruled if Popolon is active)
1: Popolon is alive
ch4 0: Aphrodite is dead (overruled if Aphrodite is active)
1: Aphrodite is alive
cXX Checksum is discussed elsewhere on this page

Part 1: Decoding the characters

Grab you favourite hex viewer and use it on the ROM. The first interesting part is:

00005670 10 04 1C 1F 08 03 11 05 22 06 0C 15 18 0A 01 ........"...... 00005680 1A 1B 1E 12 20 0F 07 19 17 21 0B 14 02 09 0E 1D .... ....!...... 00005690 13 16 23 0D 24 00 ..#.$ .
Then apply the following mapping:
from: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 to : ! 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

The ROM then becomes:
00005670 10 04 1C 1F 08 03 11 05 22 06 0C 15 18 0A 01 F3RU72G4X5BKN90 00005680 1A 1B 1E 12 20 0F 07 19 17 21 0B 14 02 09 0E 1D PQTHVE6OMWAJ18DS 00005690 13 16 23 0D 24 00 ILYCZ !

The '!' is special. I use it as a symbol for a blank (sort of space). As in the special password:

THIS IS A PASS WORD JJJJ JJJK

(Use left and right arrow for a blank.)

Do you spot the infamous 'UR3F' in reverse? Spot on! This is a lookup table to help Demeter decode its own memory dump so to say.

Believe me when I say that the 128kB of MoG code consists of 16 'pages' of each 8kB. As Z80 can only address 64kB of memory, not all MoG code can be addressed without a hassle. This is the memory layout:

(slot 0) 0x0000-0x3FFF: BIOS (slot 1) 0x4000-0x7FFF: 0x4000-0x5FFF: MoG ROM page 0 (ROM 0x0000-0x1FFFF) (depending on which page was actually loaded) 0x6000-0x7FFF: MoG ROM page 1 (ROM 0x02000-0x03FFF) 0x6000-0x7FFF: MoG ROM page 4 (ROM 0x08000-0x09FFF) 0x6000-0x7FFF: MoG ROM page 7 (ROM 0x0E000-0x0FFFF) 0x6000-0x7FFF: MoG ROM page 10 (ROM 0x14000-0x15FFF) 0x6000-0x7FFF: MoG ROM page 13 (ROM 0x1A000-0x1BFFF) (slot 2) 0x8000-0xBFFF: (depending on which page was actually loaded) 0x8000-0x9999: MoG ROM page 2 (ROM 0x04000-0x05FFF) 0x8000-0x9999: MoG ROM page 5 (ROM 0x0A000-0x0BFFF) 0x8000-0x9999: MoG ROM page 8 (ROM 0x10000-0x11FFF) 0x8000-0x9999: MoG ROM page 11 (ROM 0x16000-0x17FFF) 0x8000-0x9999: MoG ROM page 14 (ROM 0x1C000-0x1DFFF) (depending on which page was actually loaded) 0xA000-0xBFFF: MoG ROM page 3 (ROM 0x06000-0x07FFF) 0xA000-0xBFFF: MoG ROM page 6 (ROM 0x0C000-0x0DFFF) 0xA000-0xBFFF: MoG ROM page 9 (ROM 0x12000-0x13FFF) 0xA000-0xBFFF: MoG ROM page 12 (ROM 0x18000-0x19FFF) 0xA000-0xBFFF: MoG ROM page 15 (ROM 0x1E000-0x1FFFF) (slot 3) 0xC000-0xFFFF: RAM

Since the start of lookup table of 'UR3F' is at ROM 0x5671 (ROM page 2), it will be in the code between 0x8000 and 0x9FFF. The mapping is as straight forward as can be seen, so assembly code should be checked for 0x8000 + (0x5671-0x4000) = 0x9671. This address will be in the ROM code as 0x71 0x96. This brings us to:
000057E0 21 00 EB 11 40 EB !...@. 000057F0 06 2B D5 0E 00 11 71 96 0C 1A BE 28 03 13 18 F8 .+....q....(.... 00005800 78 E6 03 57 79 92 3D D1 12 23 13 10 E5 C9 x..Wy.=..#....

With a disassembly program and with mapping from ROM 0x4000-0x5FFFF to addressable memory 0x8000-0x9FFFF:
(See http://www.zilog.com/docs/z80/um0080.pdf for a complete reference guide of the instructions. It's from the makers of the processor of the MSX (the Z in Z80 stands for Zilog).
ADDR CODE OP ARG(S) ; PSEUDO/C-CODE + COMMENTS ============================================================== ; Initialisation. ; If you use "MAZE OFGA LIUS" as a password, 0xEB00 and on would read: ; 0xEB00: 17 0b 24 0f 19 10 11 0b 16 13 1f 1d 00 00 00 00 /* 0 - 15 */ ; 0xEB10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 /* 16 - 31 */ ; 0xEB20: 00 00 00 00 00 00 00 00 00 00 00 /* 32 - 42 */ ; And 0xEB40 would read afterwards: ; 0xEB40: 14 17 22 14 13 fe 05 19 1d 1d 02 1e 25 26 27 28 ; 0xEB50: 25 26 27 28 25 26 27 28 25 26 27 28 25 26 27 28 ; 0xEB60: 25 26 27 28 25 26 27 28 25 26 27 0x97EA 2100EB.. LD HL,0EB00h ; [00] HL = 0xEB00; /* Start of encoded Demeter password. */ 0x97ED 1140EB.. LD DE,0EB40h ; [01] DE = 0xEB40; /* Start of decoded Demeter password. */ 0x97F0 062B.... LD B,02Bh ; [02] B = 43; /* Password length is 43 (excluding checksum). */ ; Start of multiple loops. 0x97F2 D5...... PUSH DE ; [03] /* Store location of where to store the to be decoded Demeter character. */ 0x97F3 0E00.... LD C,000h ; [04] C = 0; /* This is character 0 in the lookup table. */ 0x97F5 117196.. LD DE,09671h ; [05] DE = &lookup_table; /* We start searching at the beginning of the lookup table. */ ; Lookup the Cth (1-based) encoded Demeter password character in the lookup table. 0x97F8 0C...... INC C ; [06] ++C; /* This is the next character in the lookup table. */ 0x97F9 1A...... LD A,(DE) ; [07] A = *DE; /* A is the Cth (1-based) value in the lookup table. */ 0x97FA BE...... CP (HL) ; [08] A - *HL; /* Set flags with outcome A - *HL. */ 0x97FB 2803.... JR Z,003h ; [09] if (A - *HL == 0) goto 0x9800; /* Break if character was found in lookup table. */ 0x97FD 13...... INC DE ; [10] ++DE; /* Increase pointer into UR3F lookup table. */ 0x97FE 18F8.... JR 0F8h ; [11] goto 0x97F8; /* Try next character in lookup table. */ ; By now, an encoded Demeter password character was matched against a character in the lookup table. ; Meaningful registers: ; HL: address of current encoded Demeter password character. ; DE: address of current lookup table character. ; C : (*DE) is the Cth (1-based) character in the lookup table. ; B : Number of characters still to decode. 0x9800 78...... LD A,B ; [12] A = B; /* A is number of characters still to decode + 1. */ 0x9801 E603.... AND 003h ; [13] A &= 0x3; /* A = B % 4; */ 0x9803 57...... LD D,A ; [14] D = A; /* D = B % 4; */ 0x9804 79...... LD A,C ; [15] A = C; /* A is the position (1-based) of the current encoded Demeter password character in the lookup table. */ 0x9805 92...... SUB D ; [16] A -= D; /* A is made smaller (by at most 3) according to position of encoded character in its group of four. */ 0x9806 3D...... DEC A ; [17] --A; /* A is made 0-based. */ 0x9807 D1...... POP DE ; [18] /* Retrieve location of where to store the current decoded Demeter character. */ 0x9808 12...... LD (DE),A ; [19] *DE = A; /* Fill location with decoded Demeter character. */ 0x9809 23...... INC HL ; [20] ++HL; /* Set location of next encoded character. */ 0x980A 13...... INC DE ; [21] ++DE; /* Set location of next computed position. */ 0x980B 10E5.... DJNZ 0E5h ; [22] if (--B != 0) goto 0x97F2; ; End of multiple loops. 0x980D C9...... RET ; [23] return; /* Finished looking up all characters. */

In example (with password="MAZEOFGALIUS"):

Now, we understand completely what is at 0xEB40 and how it gets there from 0xEB00.

The complete lookup table
Base 2 Decimal Hexadecimal Position 0 Position 1 Position 2 Position 3
00000 00x00UR3F
00001 10x017UR3
00010 20x0227UR
00011 30x03G27U
00100 40x044G27
00101 50x05X4G2
00110 60x065X4G
00111 70x07B5X4
01000 80x08KB5X
01001 90x09NKB5
01010100x0A9NKB
01011110x0B09NK
01100120x0CP09N
01101130x0DQP09
01110140x0ETQP0
01111150x0FHTQP
10000160x10VHTQ
10001170x11EVHT
10010180x126EVH
10011190x1306EV
10100200x14M06E
10101210x15WM06
10110220x16AWM0
10111230x17JAWM
11000240x181JAW
11001250x1981JA
11010260x1AD81J
11011270x1BSD81
11100280x1CISD8
11101290x1DLISD
11110300x1EYLIS
11111310x1FCYLI
100000320x20ZCYL
100001330x21 ZCY
100010340x22 ZC
100011350x23 Z
100100360x24
100101370x25!
100110380x26 !
100111390x27 !
101000400x28 !

Part 2: The checksum

The checksum code is in the ROM starts at 0x580E:
0x980Eh 1100EB.. LD DE,0EB00h ; DE = 0xEB00; /* Start of encoded Demeter password. */ 0x9811h 2E00.... LD L,000h ; L = 0; /* Set checksum to 0. */ 0x9813h 062B.... LD B,02Bh ; B = 43; /* Password length is 43 (excluding checksum). */ 0x9815h 1A...... LD A,(DE) ; A = *DE; /* An encoded character. */ 0x9816h 85...... ADD A,L ; A += L; /* Add checksum so far. */ 0x9817h 6F...... LD L,A ; L = A; /* Save new checksum. */ 0x9818h 13...... INC DE ; ++DE; /* Set location of next encoded character. */ 0x9819h 10FA.... DJNZ 0FAh ; if (--B != 0) goto 0x9815; /* Process next character. */ ; By now, L holds the 8 bit checksum of the /encoded/ characters. ; Basically a checksum of [0xEB00 .. 0xEB2A]. 0x981Bh 5D...... LD E,L ; E = L; /* Copy checksum. */ 0x981Ch 212BEB.. LD HL,0EB2Bh ; HL = 0x0E2B; /* Location of entered checksum byte (nybble actually) number 1. */ 0x981Fh 7E...... LD A,(HL) ; A = *HL; /* Get first entered checksum nybble. */ 0x9820h 3D...... DEC A ; --A; /* Correct off by one error. */ 0x9821h 07...... RLCA ; /* Swap high and low nybble (part 1). */ 0x9822h 07...... RLCA ; /* Swap high and low nybble (part 2). */ 0x9823h 07...... RLCA ; /* Swap high and low nybble (part 3). */ 0x9824h 07...... RLCA ; /* Swap high and low nybble (part 4). */ 0x9825h 23...... INC HL ; ++HL; /* Location of entered checksum byte (nybble actually) number 2. */ 0x9826h 46...... LD B,(HL) ; B = *HL; /* Get first entered checksum nybble. */ 0x9827h 05...... DEC B ; --B; /* Correct off by one error. */ 0x9828h B0...... OR B ; A |= B; /* Form complete checksum byte. */ /* BIG NOTE: if A >= 32, it will influence B's part of the checksum. */ /* BIG NOTE: if B >= 32, it will influence A's part of the checksum. */ 0x9829h BB...... CP E ; A - E; /* Compare entered checksum with computed checksum. */ 0x982Ah C9...... RET ;

The assembly is pretty straight forward: compute eight bit checksum and compare it the encoded checksum characters. In example:

(last characters (=checksum characters): "48". U+R+3+F+ U+R+3+F+ U+R+4+F+ 4+2+3+R+ U+R+3+F+ U+R+3+F+ U+R+3+F+ U+R+3+F+ U+R+3+F+ U+R+3+F+ U+R+S = 31+28+4+16+ 31+28+4+16+ 31+28+ 5+16+ 5+ 3+4+28+ 31+28+4+16+ 31+28+4+16+ 31+28+ 4+16+ 31+28+4+16+ 31+28+4+16+ 31+28+4+16+ 31+28+29 = 840 = 0x348 Checksum computed: 0x348 & 0xFF == 0x48 Checksum entered = "48", converted with lookup table makes 0x05 0x09 High nybble: 0x05 Subtract 1 : 0x04 4x left circular shift: 0x40 Low nybble: 0x09 Subtract 1: 0x08 Combine the nybbles: 0x40 | 0x08 == 0x48 Compare both checksums: 0x48 == 0x48. They match.

Let's do this exercise again, but with password="MAZEOFGALIUS":

M+A+Z+E+ O+F+G+A+ L+I+U+S+ !+!+!+!+ !+!+!+!+ !+!+!+!+ !+!+!+!+ !+!+!+!+ !+!+!+!+ !+!+!+!+ !+!+! = 23+11+36+15+ 25+16+17+11+ 22+19+31+29+ 0+0+0+0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+0+0+0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0+ 0 = 255 = 0xFF Checksum computed: 0xFF & 0xFF == 0xFF Checksum entered = "!!" (two empty spots), converted with lookup table makes 0x00 0x00 High nybble: 0 Subtract 1 : 0xFF (remember: 8 bit integer!) 4x left circular shift: 0xFF Low nybble: 0 Subtract 1: 0xFF (remember: 8 bit integer!) Combine the nybbles: 0xFF | 0xFF == 0xFF Compare both checksums: 0xFF == 0xFF. They match.


Part 3: Bugs or not?


Part 4: Encoding to characters

We already know how Demeter decodes passwords. But how does she encrypt the state to a password/characters?

This again involves the lookup table.

The memory contents from eb40 when at Demeter's:

eb40: 1f 1c 04 10 1f 1c 04 10 1f 1c 05 10 05 03 04 1c .... .... .... .... eb50: 1f 1c 04 10 1f 1c 04 10 1f 1c 04 10 1f 1c 04 10 .... .... .... .... eb60: 1f 1c 04 10 1f 1c 04 10 1f 1c 1d 05 09 .... .... .... .

Interpreting this with our familiar mapping:

eb40: 1f 1c 04 10 1f 1c 04 10 1f 1c 05 10 05 03 04 1c UR3F UR3F UR4F 423R eb50: 1f 1c 04 10 1f 1c 04 10 1f 1c 04 10 1f 1c 04 10 UR3F UR3F UR3F UR3F eb60: 1f 1c 04 10 1f 1c 04 10 1f 1c 1d 05 09 UR3F UR3F URS4 8
So eb40 must be special.

We can decode UR3F to decimals by using the lookup table again. For readability sake, we replaced the hexdigits with the hexdigits from the lookup table.

eb40: 00 00 00 00 00 00 00 00 00 00 06 00 04 03 00 02 UR3F UR3F UR4F 423R eb50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 UR3F UR3F UR3F UR3F eb60: 00 00 00 00 00 00 00 00 00 00 1d UR3F UR3F URS

The memory contents eb00:

eb00: 00 00 00 00 00 00 00 00 00 00 06 00 04 03 00 02 eb10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 eb20: 00 00 00 00 00 00 00 00 00 00 1d

It seems eb00 and eb40 are related! The relation is:

lookup_table = { /* DEC HEX CHAR */ 0x10, 0x04, 0x1c, 0x1f, /* 00..03 00..03 F3RU */ 0x08, 0x03, 0x11, 0x05, /* 04..07 04..07 72G4 */ 0x22, 0x06, 0x0c, 0x15, /* 08..11 08..0B X5BK */ 0x18, 0x0a, 0x01, 0x1a, /* 12..15 0C..0F N90P */ 0x1b, 0x1e, 0x12, 0x20, /* 16..19 10..13 QTHV */ 0x0f, 0x07, 0x19, 0x17, /* 20..23 14..17 E6OM */ 0x21, 0x0b, 0x14, 0x02, /* 24..27 18..1B WAJ1 */ 0x09, 0x0e, 0x1d, 0x13, /* 28..31 1C..1F 8DSI */ 0x16, 0x23, 0x0d, 0x24, /* 32..35 20..23 LYCZ */ 0, 0, 0, 0, /* 36..39 24..27 */ 0 /* 40 28 ! */ }; eb40[i] = lookup_table[eb00[i] + (3 - i%4)]

Subroutines:
;; Subroutine for ;; DE += A; ;; Input: ;; A, D, E ;; Thrashed registers: ;; A T413Bh 83...... .... ADD A,E ; A += E; /* Note: affects carry flag. */ T413Ch 5F...... _... LD E,A ; E = A; /* */ T413Dh D0...... .... RET NC ; if (no carry) return; /* */ T413Eh 14...... .... INC D ; ++D; /* */ T413Fh C9...... .... RET ; return; /* */ ;; Subroutine for ;; A = *(0x9671 + A + (B % 4)); ;; (or A = lookup_table[A + (B % 4)];) ;; Thrashed registers: ;; D, E T9664h 57...... W... LD D,A ; D = A; /* */ T9665h 78...... x... LD A,B ; A = B; /* */ T9666h E603.... .... AND 003h ; A &= 0x3; /* */ T9668h 82...... .... ADD A,D ; A += D; /* */ T9669h 117196.. .q.. LD DE,09671h ; DE = 0x9671; /* Note: 0x9671 is the lookup table. */ T966Ch CD3B41.. .;A. CALL T413BH ; DE += A; /* */ T966Fh 1A...... .... LD A,(DE) ; A = *(DE); /* */ T9670h C9...... .... RET ; return; /* */ ;; Subroutine for computing the character password value. (This value ;; will be stored in 0xEB2A.) ;; ;; Thrashed register(s): ;; B, D, E, H, L ;; ;; Output register(s): ;; A T96A3h 2A58E0.. *X.. LD HL,(TE058H); H = *(0xE059); L = *(0xE058); /* */ ;; 0xE058 1: Aphrodite is available (???) ;; 0xE059 1: Popolon is available (???) T96A6h ED5B5AE0 .[Z. LD DE,(TE05AH); D = *(0xE05B); E = *(0xE05A); /* */ ;; 0xE05A: 1: Aphrodite can be revived in Death's shrine. ;; 0xE05B: 1: Popolon can be revived in Death's shrine. T96AAh 7C...... |... LD A,H ; A = H; /* */ T96ABh 07...... .... RLCA ; carry = A >> 7; A <<= 1; A + carry; /* Rotate left via carry. */ T96ACh B5...... .... OR L ; A |= L; /* */ T96ADh 47...... G... LD B,A ; B = A; /* */ T96AEh 7B...... {... LD A,E ; A = E; /* */ T96AFh A7...... .... AND A ; A &= A; /* Test for A == 0. */ T96B0h 2802.... (... JR Z,002h ; if (A == 0) goto 096B4H; /* */ T96B2h CBD0.... .... SET 2,B ; B |= 4; /* Aphrodite can be revived using Death's shrine. */ T96B4h 7A...... z... LD A,D ; A = D; T96B5h A7...... .... AND A ; A &= A; /* Test for A == 0. */ T96B6h 2802.... (... JR Z,002h ; if (A == 0) goto 096BAH; /* */ T96B8h CBD8.... .... SET 3,B ; B |= 8; /* Popolon can be revived using Death's shrine. */ T96BAh 3A01E5.. :... LD A,(TE501H) ; A = *(0xE501); /* 0xE501 even: Aphrodite active, odd: Popolon active. */ T96BDh A7...... .... AND A ; A &= A; /* Test for A == 0. */ T96BEh 2802.... (... JR Z,002h ; if (A == 0) goto 096C2H; /* */ T96C0h CBE0.... .... SET 4,B ; B |= 16; /* Popolon is active. */ T96C2h 78...... x... LD A,B ; A = B; T96C3h C9...... .... RET ;; Subroutine for ;; converting a serie of bytes to "lookup table" values (5 bits) ;; Input: ;; T95A9h T95B3h T95BDh ;; CALL1 CALL2 CALL3 ;; A : Number of bytes to convert 6 4 10 ;; C : Increment source by two ;; instead of one 0 1 0 ;; DE: Source address 0xE046 0xE051 0xE063 ;; HL: Destination address 0xEB00 0xEB0A 0xEB11 ;; Thrashed registers: ;; B, B' ;; ;; CALL1: eb00..eb09: 9 full 5 bits + 3 leftover bits = 48 bits = 6 bytes ;; CALL2: eb0a..eb10: 6 full 5 bits + 2 leftover bits = 32 bits = 4 bytes ;; CALL3: eb11..eb20: 16 full 5 bits + 0 leftover bits = 80 bits = 10 bytes ;; ;; CALL1: Number of arrows, coins, keys. ;; CALL2: Experience and maximum vitality. ;; CALL3: World information. ;; ;; Example: ;; 46 47 48 49 4A 4B 4C 4D 4E 4F 50 ;; DE/0xE046: A1 44 19 F8 A7 EE 00 01 01 00 00 ;; 1010000101000100 ;; #####$$$$$^^^^^ ;; becomes ;; 00 01 02 03 04 05 06 07 08 09 0A ;; HL/0xEB00: 14 05 02 01 13 1E 05 07 1D 18 00 ;; 00010100 00000101 00000010 ;; ##### $$$$$ ^^^^^ ;; ;; Idea: ;; * The most signifact 5 bits : 10100 (=0x14) ;; * The next most signifact 5 bits: 00101 (=0x05) ;; * The next most signifact 5 bits: 00010 (=0x02) T96C4h 3200F0.. 2... LD (TF000H),A ; *(0xF000) = A; /* 0xF000 holds A. */ T96C7h 79...... y... LD A,C ; A = C; /* */ T96C8h 3201F0.. 2... LD (TF001H),A ; *(0xF001) = A; /* 0xF001 holds C. */ T96CBh D9...... .... EXX ; /* Switch to shadow. */ T96CCh 0608.... .... LD B,008h ; B' = 8; /* Input width is 8 bits per byte. */ T96CEh D9...... .... EXX ; /* Switch to normal. */ T96CFh 1A...... .... LD A,(DE) ; A = *(DE); /* Start with first byte in source. */ T96D0h 4F...... O... LD C,A ; C = A; /* */ T96D1h 0605.... .... LD B,005h ; B = 5; /* Convert per 5 bits. */ T96D3h B7...... .... OR A ; A |= A; carry = 0; /* Clear carry. */ T96D4h CB11.... .... RL C ; carry_new = C >> 7; C <<= 1; C += carry_old; /* Rotate left via carry. */ T96D6h CB16.... .... RL (HL) ; carry_new = *(HL) >> 7; *(HL) <<= 1; *(HL) += carry_old; /* Rotate left via carry. */ T96D8h D9...... .... EXX ; /* Switch to shadow. */ T96D9h 05...... .... DEC B ; --B'; /* Converted 1 bit ??? */ T96DAh D9...... .... EXX ; /* Switch to normal. */ T96DBh 2014.... .... JR NZ,014h ; if (B' != 0) goto 0x96F1; /* */ ;; B' == 0: Copied/converted a byte. T96DDh 13...... .... INC DE ; ++DE; /* Select next source byte. */ T96DEh 3A01F0.. :... LD A,(TF001H) ; A = *(0xF001); /* 0xF001 contains the backed up C. */ T96E1h A7...... .... AND A ; A &= A; /* Test for A == 0. */ T96E2h 2801.... (... JR Z,001h ; if (A == 0) goto 0x96E5; /* */ ;; A != 0: Skip a source byte. T96E4h 13...... .... INC DE ; ++DE; /* Select next source byte. */ T96E5h 1A...... .... LD A,(DE) ; A = *(DE); /* Load next source byte. / T96E6h 4F...... O... LD C,A ; C = A; /* */ T96E7h D9...... .... EXX ; /* Switch to shadow. */ T96E8h 0608.... .... LD B,008h ; B' = 8; /* Reset input width. */ T96EAh 2100F0.. !... LD HL,0F000h ; HL' = 0xF000; /* Select address of number of bytes to copy/convert. */ T96EDh 35...... 5... DEC (HL) ; Fz = --*(HL'); /* Converted a byte. Sets flag zero. */ T96EEh D9...... .... EXX ; /* Switch to normal. */ T96EFh 2805.... (... JR Z,005h ; if (Fz == 0) goto 0x96F6; /* */ T96F1h 10E0.... .... DJNZ 0E0h ; if (--B != 0) goto 0x96D3; /* Jump unless 5 bits converted. */ T96F3h 23...... #... INC HL ; ++HL; /* Select next output byte. */ T96F4h 18DB.... .... JR 0DBh ; goto 0x96D1; /* Loop. */ ;; *(HL') == 0: All bytes converted. T96F6h 3E01.... >... LD A,001h ; A = 1; /* 1. */ T96F8h 90...... .... SUB B ; A -= B; /* 2. */ T96F9h 2807.... (... JR Z,007h ; if (A == 0) goto 0x9702; /* */ T96FBh ED44.... .D.. NEG ; A = -A; /* 3. */ ;; 1. + 2. + 3.: A = -(1 - B) = B - 1 T96FDh 47...... G... LD B,A ; B = A; /* Number of bits left to (left) shift. */ T96FEh CB26.... .&.. SLA (HL) ; *(HL) <<= 1; /* (Left) shift one bit.*/ T9700h 10FC.... .... DJNZ 0FCh ; if (--B != 0) goto 0x96FE; /* Repeat until shifts done. */ T9702h 23...... #... INC HL ; ++HL; /* */ T9703h C9...... .... RET ; return; /* End of subroutine. */ T95A6h 2100EB.. !... LD HL,0EB00h ; HL = 0xEB00; /* 0xEB00 is the start Demeter's half encoded password (needs only a pass through the lookup table). */ T95A9h 1146E0.. .F.. LD DE,0E046h ; DE = 0xE046; /* 0xE046 is the start of the game state (raw Demeter password). */ T95ACh 3E06.... >... LD A,006h ; A = 6; /* */ T95AEh 0E00.... .... LD C,000h ; C = 0; /* */ T95B0h CDC496.. .... CALL T96C4H ; /* ??? */ T95B3h 1151E0.. .Q.. LD DE,0E051h ; DE = 0xE051; /* 0xE051 is Aphrodite's experience. */ T95B6h 3E04.... >... LD A,004h ; A = 4; /* */ T95B8h 0E01.... .... LD C,001h ; C = 1; /* */ T95BAh CDC496.. .... CALL T96C4H ; /* */ T95BDh 1163E0.. .c.. LD DE,0E063h ; DE = 0xE063; /* */ T95C0h 3E0A.... >... LD A,00Ah ; A = 10; /* */ T95C2h 0E00.... .... LD C,000h ; C = 0; /* */ T95C4h CDC496.. .... CALL T96C4H ; /* */ ;; T95C7h - T95E0h: ;; ;; Copy items from 0xE070 to HL (0xEB21). ;; ;; Memory dump when having all items: ;; e070: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ;; e080: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ;; e090: 01 01 01 01 01 01 01 03 01 01 01 01 ;; ;; This has to be converted to 11111 11111 etc. This is done with SCF ;; and RL. The carry flag is used for whether the input is non-zero. In C-code: ;; carry = (*DE != 0); ;; Effectively the input is /copied/ to the carry flag. ;; ;; This carry flag is /shifted/ in with the RLCA. After five shifts, ;; five input bytes are copied to a 5 bit password value. T95C7h 1170E0.. .p.. LD DE,0E070h ; DE = 0xE070; /* Start of input (items, arrow to be precise). */ T95CAh 0E2C.... .,.. LD C,02Ch ; C = 44; /* There are 44 (pseudo) items to copy. */ T95CCh 0605.... .... LD B,005h ; B = 5; /* Each password value is 5 bits wide. */ T95CEh 3600.... 6... LD (HL),000h ; *(HL) = 0; /* Zero output. */ T95D0h 1A...... .... LD A,(DE) ; A = *(DE); /* */ T95D1h A7...... .... AND A ; A &= A; /* Test for A == 0, reset carry flag. */ T95D2h 2801.... (... JR Z,001h ; if (A == 0) goto 0x95D5; /* */ T95D4h 37...... 7... SCF ; carry = 1; /* Set carry flag. */ T95D5h CB16.... .... RL (HL) ; *(HL) <<= 1; *(HL) += c_old; c_new = *(HL)_old >> 15; /* Rotate left with carry. */ T95D7h 13...... .... INC DE ; ++DE; /* Select next input byte. */ T95D8h 0D...... .... DEC C ; --C; /* One item is converted to password. */ T95D9h 2805.... (... JR Z,005h ; if (C == 0) goto 0x95E0; /* If all items are bitwise copied then break. */ T95DBh 10F3.... .... DJNZ 0F3h ; if (--B != 0) goto 0x95D0; /* Loop unless 5 bits copied. */ T95DDh 23...... #... INC HL ; ++HL; /* Select next output 'byte'. */ T95DEh 18EC.... .... JR 0ECh ; goto 0x95CC; /* Loop. */ ;; 44 bits done. Last 4 bits are (000)01111, but this should be ;; (000)11110. We need one more (left) shift. T95E0h CB26.... .&.. SLA (HL) ; *(HL) <<= 1; /* */ T95E2h 23...... #... INC HL ; ++HL; /* Point to character part of password values. */ T95E3h E5...... .... PUSH HL ; /* Save HL on stack. */ T95E4h CDA396.. .... CALL T96A3H ; /* Compute character password value. */ T95E7h E1...... .... POP HL ; /* Restore saved HL. */ T95E8h 77...... w... LD (HL),A ; *(HL) = A; /* Set character password value. */ T95E9h 2100EB.. !... LD HL,0EB00h ; HL = 0xEB00; /* Start of decoded Demeter password (password values). */ T95ECh 1140EB.. .@.. LD DE,0EB40h ; DE = 0xEB40; /* Start of encoded Demeter password. */ T95EFh 062B.... .+.. LD B,02Bh ; B = 43; /* Password length is 43 (excluding checksum). */ T95F1h 7E...... ~... LD A,(HL) ; A = *(HL); /* Start with first password value. */ T95F2h D5...... .... PUSH DE ; /* Save DE. */ T95F3h CD6496.. .d.. CALL T9664H ; A = *(0x9671 + A + (B % 4)); /* */ T95F6h D1...... .... POP DE ; /* Retrieve DE. */ T95F7h 12...... .... LD (DE),A ; *(DE) = A; /* Save encoded character. */ T95F8h 23...... #... INC HL ; ++HL; /* Goto next character. */ T95F9h 13...... .... INC DE ; ++DE; /* Goto next character. */ T95FAh 10F5.... .... DJNZ 0F5h ; if (--B != 0) goto 0x95F1; /* Loop until all 43 characters are handled. */ T95FCh D5...... .... PUSH DE ; /* Save location of checksum character 1. */ T95FDh CD9596.. .... CALL T9695H ; /* */ T9695h 1140EB.. .@.. LD DE,0EB40h ; DE = 0xEB40; /* Start of encoded Demeter password. */ T9698h 2E00.... .... LD L,000h ; L = 0; /* Init checksum to zero. */ T969Ah 062B.... .+.. LD B,02Bh ; B = 43; /* Password length is 43 (excluding checksum). */ T969Ch 1A...... .... LD A,(DE) ; A = *(DE); /* Get value. */ T969Dh 85...... .... ADD A,L ; A += L; /* Compute new checksum. */ T969Eh 6F...... o... LD L,A ; L = A; /* Save new checksum. */ T969Fh 13...... .... INC DE ; ++DE; /* Goto next character. */ T96A0h 10FA.... .... DJNZ 0FAh ; if (--B != 0) goto 0x969C; /* Loop until all 43 characters are checksumed. */ T96A2h C9...... .... RET ; return; /* Done computing checksum. */ ;; L holds the checksum. ;; Write checksum as 2 password characters. T9600h D1...... .... POP DE ; /* Retrieve location of checksum character 1. */ T9601h 7D...... }... LD A,L ; A = L; /* Retrieve checksum. */ T9602h E6F0.... .... AND 0F0h ; A &= 0xF0; /* Clear low nybble. */ T9604h 0F...... .... RRCA ; /* Swap high and low nybble (part 1). */ T9605h 0F...... .... RRCA ; /* Swap high and low nybble (part 2). */ T9606h 0F...... .... RRCA ; /* Swap high and low nybble (part 3). */ T9607h 0F...... .... RRCA ; /* Swap high and low nybble (part 4). */ T9608h 3C...... <... INC A ; ++A; /* Correct off by one error. */ T9609h 12...... .... LD (DE),A ; *(DE) = A; /* Set checksum character 1. */ T960Ah 13...... .... INC DE ; ++DE; /* Set location of checksum character 2.*/ T960Bh 7D...... }... LD A,L ; A = L; /* Retrieve checksum. */ T960Ch E60F.... .... AND 00Fh ; A &= 0x0F; /* Clear high nybble. */ T960Eh 3C...... <... INC A ; ++A; /* Correct off by one error. */ T960Fh 12...... .... LD (DE),A ; *(DE) = A; /* Set checksum character 2. */

(The usage of) subroutine T9664h explains another strangs feature of the password system. Try entering the following password:

CYLI CYLI CYLI CYLI CYLI CYLI CYLI CYLI CYLI CYLI CYLC 0
(Pretty maxed out as all bits are 1.) Immediately going back to Demeter and asking for password will yield:
CYLI CYLI CSLI CYLI 1YLI CYLI CYLI CYLI CYLI CYLI CLLA 2
Changes:
  1. Character 10 changed from Y to S, from 111112 to 111002.
  2. Character 17 changed from C to 1, from 111112 to 110002.
  3. Character 42 changed from Y to L, from 111112 to 111102.

The call from T95B0h with A=6 and C=0 converts 6 bytes, starting with destination address eb00. 6 bytes equals 48 bits. 48 div 5 equals 9 full 5 bits conversions, leaving 3 (48 mod 5) bits. Because of bitshifting at T96FEh, the most significant bits will be used, so the least signifact will not be used, being 5-3=2 bits. As eb00-eb2a is cleared before T95B0h happens, those least significant 2 bits remain zero. Hence 11111 encoded becomes 11100 after decoding+recoding.

Same holds true for character 17. 4 bytes to convert = 32 bits = 6*5 + 2 bits. 3 bits wasted zeroed.

The third quirk must be due to something else, maybe something with bronze shield and golden shield.

Codes

Here are some codes from my last game.
NG3U 4R3F UR4F 4237 UE2F UR3F UR3F UR3F UH3F NH3F URSE E
7R3T 5R37 UR4F 4237 UE2F UR3F UR3F UR3F UHTF NH3F 7RSE 4
UJ3H UR37 UR4F 423G U863 UR3F UR3F UR3F UHTX NH3F 7RS0 D
603U KR32 UR4F 423G U863 UR3F UR3F UR3F UJT5 NH3F 7RSC 6
EG32 4R3G UR4F 423G
U863 UR3F UR3F UR3F
UJT5 NH2F 7RSB F
HG32 6R3G UR4F 423G
U863 UR3F UR3F UR3F
UJT5 NH2R 7RSD 0
4G3T PR34 UR4F 423G
U863 UR3F UR3F UR3F
U1T5 NH2R 7RSD 3
2H3T VR3G UR4F 423G
U86W VR3F UR3F UR3F
U1T3 QH2R 7RS0 3
9H3R 5R37 UR4F K23G
U8MJ VG3F UR3F UR3F
U1T5 QH2R 7RPE C
7R3M PR3R UR4F K23G
88MJ VG3F UR3F UR3F
UYT5 QH2R 7BS2 B
5035 PR3X UR4F K23G
U8MJ A73F UR3F UR3F
UYT3 QJ2R 7BPE 2
2R3F 5R54 UR4F K23G
U8MJ A73F UR3F UR3F
UYT3 QJ2R 7BSD 8
OG2Q 5R5F UG4F K23G
U8MJ A73F UR3F UR3F
UYT3 QJ2R 7BSE D
9H23 TR9W UR4F P23G
U8MJ YN3Q UR3F UR3F
UYT5 QJ2R 7BP3 7
VS7H 2R9E UR4F V23G
U8MJ Y9SX 2R3F UR3F
UYT5 QJ2R 7BP3 A
XR7V PR9H UR4F V23G
U8MJ Y9SX 2R3F UR3F
UYT5 QJGR 7BS7 4
UBUU TR7H UR4F V23G
U8MJ Y9S0 7R3F UR3F
UYTU CIGV ON47 0
TBRA 4R7E UR4F V23X
U8MJ 79SP WR5F UR3F
UYTK CIGV ONO8 B
TRR4 6R7W UR4F V23X
U8MJ Y9SP WR5F UR3F
UYTP CIQI JNOA 6
TGRF 5RNW UR4F V23X
U8MJ Y9SP WR5F UR3F
UYTP CILI JNOB 0
OG3Q 6REO UR4F M23X
U8MJ Y9SP WL63 UR3F
UYTP CILI JN96 5
VR3F 6RKO UR4F M23X
U8MJ Y9SP WL63 UR3F
UYTP CILI JN46 D
2R7H PRNR UR4F M23X
U8MJ Y9SP WL6F VR3F
UYT4 CILI CNO7 4
KRUO TRNR UR4F 123X
U8MJ Y9SP WLMH VG3F
UYTP CILI CNPB 0
XRUO 6RN3 UR4F 123X
U8MJ Y9SP WLWH VG3F
UYTP CILI CNS9 B
UJRF URVT UR4F 123X
ULWJ C9SP WLWJ VG3F
UYTP CILI CNSC 1
9H3F 6RV6 UR4F 123X
ULWJ C9SP WLWJ V73F
UYT4 CILI CNS4 5
OG2Q 6RV6 UR4F 123X
ULWJ C9SP WLWJ V73F
UYT4 CILI CNS5 D
XR2M URVM UR4F 123X
ULWJ C9SP WLWJ C73F
UYT4 CYLI CNS9 2
UG2Q UR1M UR4F 123B
ULWJ C9SP WLWJ CN3Q
UYTP CYLI CNS8 4
TJ2R 9R8F UR4F 123B
ULWJ C9SP WLWJ CN3Q
UYTI CYLI CNS6 B
KS2U KR8F UR4F 123B
ULWJ C9SP WLWJ CN3Q
UYTI CYLI C8S6 A
KG7E PR1A UR4F 123B
ULWJ C9SP WLWJ CN3X
UYTM CYLI C8S5 7
T07G VR1T UR4F 123B
ULWJ C9SP WLWJ C95X
UYTM CYLI C8S5 F
PS7G 6R1Q UR4F 123B
ULWJ C9SP WLWJ C9DX
UYTM CYLI C8S6 3
PS7A 6R14 UR4F I23B
ULWJ C9SP WLWJ C9SX
2YTI CYLI C893 4
BR75 6RHX UR4F I23B
ULWJ C9SP WLWJ C9SX
2YTI CYLI C844 8
Fake codes:
M4Z3 OFG4 L1OU S1S3
4SYT OH4C KCR4 CK4N
DD1S 4SS3 MBLE C
!!!! !!!! !!!! !!!!                       XXXX XXXX XGXX XXXX
!!!! !!!! !!!! !!!!   ->decode+encode->   UXXX XXXX XXXX XXXX
!!!! !!!! !!!0 0                          XXXX XXXX XXXA 2
CYLI CYLI CYLI CYLI                       CYLI CYLI CSLI CYLI   (Y->S: 11111 -> 11100)
CYLI CYLI CYLI CYLI   ->decode+encode->   1YLI CYLI CYLI CYLI   (C->1: 11111 -> 11000)
CYLI CYLI CYLI C0                         CYLI CYLI CLLA 2      (Y->L: 11111 -> 11110)
MAZE OFGA LITT