# Re: 6809 / 6702 puzzle

From: Rhialto <rhialto_at_falu.nl>
Date: Wed, 25 Apr 2012 22:22:33 +0200
Message-ID: <20120425202233.GA21377@falu.nl>
Here is a copy of the C version of the dongle check routine. Dave wrote
it and I added some extra experiments.

It is still based on the table that lists the successive values that are
read from \$EFE0, and ignores writes, but I have found now, I think,
which writes matter and which don't.

If we take the output from the Basic version that I ran on Ruud's
SuperPET:

seed: 214                                     hex:D6
stb 128 asl 214 b^= 214 sta 145 asl 214  214      D6 D6
stb  64 asl 214 b^= 214 sta 133 asl 198  198      D6 C6
stb  32 asl 198 b^= 198 sta   3 asl 212  212      C6 D4
stb  16 asl 212 b^= 212 sta 113 asl  84   84      D4 54
stb   8 asl  84 b^=  84 sta 225 asl   6    6      54 06
stb   4 asl   6 b^=   6 sta 209 asl  39   39      06 27
stb   2 asl  39 b^=  39 sta  67 asl  51   51      27 33

^^^^^^^

I already noticed that the value read from \$EFE0 only changed after the
value written to it in the middle "sta" column.

Well, this value is guaranteed to be always odd(1), and all other writes
are guaranteed to be even(2).

Proof of (2) is easy: the writes are the shifting bit, 128...2, which
doesn't go on to 1, so these are always even.
The results of the asl instruction is also always even.

Proof of (1) is not too difficult either. If you trace the creation of
the value, you see that at \$9860 A is read from the dongle. That value
is made odd by ORing with \$11. This value is stored in both input
locations for the later MUL instruction.

An odd value multiplied with an odd value is always odd.

The low byte of the product (odd!) is stored back as one of the factors.
The high byte is ORed with the low byte, and therefore will also be
odd, and is stored as the other factor.

The hi|lo byte is stored back into \$EFE0.

So, by induction, all values stored in this way are odd.

I tested the theory with a small basic program which pokes all values
from 0..255 into the dongle and reads it out. And indeed it kept
returning the same value twice and changed the value only after an odd
poke.

-Olaf.
--
___ Olaf 'Rhialto' Seibert  -- There's no point being grown-up if you
\X/ rhialto/at/xs4all.nl    -- can't be childish sometimes. -The 4th Doctor

// ; The basis upon which I am working is as follows:-
// ;
// ; The "LDA [<\$06,S]" instruction at address 9860 appears to only be executed once for each call of the subroutine (i.e.
// ; right at the beginning). I am assuming that the codes returned from the 6702 chip will start at some defined value when
// ; the power is applied (or the reset operated) and will then procede in a cyclic manner. The schematic diagram for the
// ; 6702 board shows the reset signal is used - hence the 6702 device must have some internal state registers of some description.
// ;
// ; I am not sure what role the writing of data has to the 6702; but I am assuming that it will produce a sequence of 8-bit numbers
// ; which must be unique - the sequence of which must be pre-determined. I assume that the initial instruction at address 9860 would
// ; effectively identify where in the sequence the 6702 chip is when the subroutine is entered. It would be logical then to assume
// ; that the 6702 response to READING will then be deterministic.
// ;
// ; We should then only require to look for one specific solution to obtain a working system (I hope)!
// ;
// ; As the writes to the 6702 can be ignored (i.e. the "STB [<\$06,S]" instruction at address 986B and the
// ; "ASL [<\$06,S]" instructions at addresses 987C and 9887) we can then safely concentrate on the 6702 responses to software
// ; reads. As the 'big loop' is executed seven times, and there are two instructions within the loop which read data from
// ; the 6702, this amounts to 1+(7*2) = 15 data bytes which would be read from the 6702 to satisfy the Waterloo Language software.
// ;
// ; I would then assume (rightly or wrongly) that if the 6702 protection subroutine was entered for a subsequent time, that returning
// ; the same set of numbers would (again) satisy the software.

// ; Version :
// ;    Date :
// ;  Author :
// ;  Reason :
// ;
// ; Version : 2.00
// ;    Date : 10th April 2012
// ;  Author : David Roberts
// ;  Reason : Major re-write!
// ;
// ; Version : 1.00
// ;    Date : 9th April 2012
// ;  Author : David Roberts
// ;  Reason : First creation.

// *******************
// ***             ***
// ***  Libraries  ***
// ***             ***
// *******************

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <assert.h>

// *********************
// ***               ***
// ***  Global data  ***
// ***               ***
// *********************

unsigned char LOOKUP_TABLE[ 15 ] = { 	// Indexed as 0 to 14.
#if 1
214,
214, 214,
214, 198,
198, 212,
212,  84,
84,   6,
6,  39,
39,  51,
#else
0x04,
0x91,
0x3C,
0xAC,
0xC7,
0xF0,
0xE3,
0x36,
0x5A,
0xBB,
0xC3,
0x6A,
0x75,
0xA3,
0x3E,
#endif
};

unsigned char LOOKUP_INDEX;       	// Index to the above table.

// *******************************************************************
// ***                                                             ***
// ***  Function to return a code from the simulated 6702 device.  ***
// ***                                                             ***
// *******************************************************************

unsigned char Read6702( void ) {

assert( LOOKUP_INDEX <= 14 );

return LOOKUP_TABLE[ LOOKUP_INDEX++ ]; // And move on to the next code for the next call.

} // End of function Read6702

// **************************
// ***                    ***
// ***  More global data  ***
// ***                    ***
// **************************

int Best, BestNow, BigLoops;      	// Internal statistics.

unsigned char A;                  	// 6809 accumulator A.
unsigned char B;                  	// 6809 accumulator B.
unsigned char X;                  	// 6809 register X.
unsigned char Y;                  	// 6809 register Y.

unsigned short D;                 	// Used within the simulator for the MUL instruction at address 9873.

unsigned char S0, S1, S4, S5;     	// These reflect memory locations "0,S" , "1,S" , "4,S" and "5,S" respectively.

unsigned char StoreA;             	// Stack storage for the A accumulator.
unsigned char StoreX;             	// Temp. storage for the X register.

unsigned char TABLE[ 8 ] = {      	// Data values following the "BSR" instruction at address 9895. Indexed as 0 to 7.
0x10,
0x80,
0x22,
0x00,
0x40,
0x00,
0x04,
0xA8
};

unsigned char XSTACK[ 7 ];        	// Stack storage. Indexed as 0 to 6.

// ************************************
// ***                              ***
// ***  Main function of interest!  ***
// ***                              ***
// ************************************

int X6702( void ) {

LOOKUP_INDEX = 0;               	// Start at the beginning of the lookup table. This table occurs just after the BSR

X = 6;                          	// 9852: TFR S,X 	; My simulation implementation anyhow!

// Irrelevant instructions here for the purpose of cracking the 6702...
// 9854: LEAS -14,S	; Allocate working space for subroutine.
// 9856: LDA #\$F0	; }
// 9858: STA 6,S	;  }
// 985A: DEC 6,S	;   } Evil way of setting 6,S and 7,S to the
// 985C: STA 7,S	;  }  address of the 6702 chip (\$EFE0).
// 985E: ASL 7,S	; }

A = Read6702();                 	// 9860: LDA [<\$06,S]	; Get a code value from the 6702. <<<<<<<<<<<<<<<<<<<<<<<<<

A |= 0x11;                      	// 9863: ORA #\$11	; make sure it is odd

S4 = A;                         	// 9865: STA \$04,S	; for multiplication: first time it will be A squared
S5 = A;                         	// 9867: STA \$05,S	; note that A is odd, so the product is odd too.

B = 0x40;                       	// 9869: LDB #\$40

do {                            	// Big loop starting at address 986B.

BestNow++;

BigLoops++;

//printf("Start big loop (B = 0x%02X).\n",B);

B <<= 1;                      	// 986B: LSLB

S1 = B;                       	// 986C: STB \$01,S

// Write6702( B );            	// 986E: STB [<\$06,S]	; Can ignore for the purpose of this simulation.
// 								; the written value is always EVEN (\$80 \$40 \$20 \$10 \$08 \$04 \$02)

A = S4; B = S5;               	// 9871: LDD \$04,S	; Loads A and B in one go.

//printf("%02x * %02x -> ", A, B);
D = A*B;                      	// 9873: MUL		; A:B = A * B.
A = ((D >> 8) & 0xFF);        	//			; A is hi byte of result.
B = ((D >> 0) & 0xFF);        	//			; B is lo byte of result.
//printf("%02x %02x -> ", A, B);

S5 = B;                       	// 9874: STB \$05,S	; lo byte is always odd

A |= S5;                      	// 9876: ORA \$05,S	; hi |= lo, so hi will now always be odd too.

S4 = A;                       	// 9878: STA \$04,S
//printf("%02x %02x\n", S4, S5);

B ^= 0xD7;                    	// 987A: EORB #\$D7

// Write6702( Read6702() << 1 );	// 987C: ASL [<\$06,S]	; Can ignore for the purpose of this simulation.
// 								; the written value is always EVEN

B ^= Read6702();              	// 987F: EORB [<\$06,S]	; Get a code value from the 6702. <<<<<<<<<<<<<<<<<<<<<<<<<

A = S5;                       	// 9882: LDA \$05,S	; hi | lo from multiplication, so ODD.

// Write6702( A );               	// 9884: STA [<\$06,S]	; Can ignore for the purpose of this simulation.
// 								; the written value is always ODD

// Write6702( Read6702() << 1 ); 	// 9887: ASL [<\$06,S]	; Can ignore for the purpose of this simulation.
// 								; the written value is always EVEN

A = Read6702();               	// 988A: LDA [\$06,S]	; Get a code value from the 6702. <<<<<<<<<<<<<<<<<<<<<<<<<

S0 = A;                       	// 988D: STA ,S

assert( X <= 6 );

// ; The way I think this works (which seems to have got us all confused) is as follows. I often wondered why 14 bytes
// ; of stack were allocated for this subroutine but only 8 appear to be used (0,S through 7,S). This leaves 8,S upwards
// ; either unused - or used by something we have not discovered yet!
// ;
// ; The "STB ,-X" instruction at address 988F pre-decrements register X and stores the value of register B into the
// ; allocated stack memory. The new (already decremented) value of X is then stored away for later use at address 98B4.
// ; This code is in the "outer loop".
// ;
// ; The "inner loop" then works 'up' the stack checking the value just stored ***AND*** all the previous values stored
// ; from the last iterations of the "outer loop". Hence the reason that the instruction "LDA ,X+" at address 98A8 can iterate
// ; more than once - because multiple values have been stored in this area of the stack from the iteration of the outer
// ; loop.
// ;
// ; On the first   iteration of the outer loop, one   value  will have been stored and the inner loop will iterate once.
// ; On the second  iteration of the outer loop, two   values will have been stored and the inner loop will iterate twice.
// ; On the third   iteration of the outer loop, three values will have been stored and the inner loop will iterate three times.
// ; On the fourth  iteration of the outer loop, four  values will have been stored and the inner loop will iterate four  times.
// ; On the fifth   iteration of the outer loop, five  values will have been stored and the inner loop will iterate five  times.
// ; On the sixth   iteration of the outer loop, six   values will have been stored and the inner loop will iterate six   times.
// ; On the seventh iteration of the outer loop, seven valuea will have been stored and the inner loop will iterate seven times.

XSTACK[ X-- ] = B;            	// 988F: STB ,-X	; Store the value of B on the stack and move X down in memory.
//printf("XSTACK = %02X %02X %02X %02X %02X %02X %02X).\n",XSTACK[0],XSTACK[1],XSTACK[2],XSTACK[3],XSTACK[4],XSTACK[5],XSTACK[6]);

StoreX = X;                   	// 9891: STX \$02,S	; Save the (new) lower value of X for later use.

B = S1;                       	// 9893: LDB \$01,S

// 9895: BSR >\$989F	; Not required for simulation...

// ; Discontinuity in listing. This address (just following the BSR) is occupied by a look-up table of 8 (?) bytes.
// ; The called subroutine picks up the return address from the stack and uses it to index the look-up table. The
// ; called subroutine never returns back to the instruction stream following the BSR.

Y = 0;                        	// 989F: LDY ,S++	; Well, a hacked version of it!
//                  	; Start at the beginning of TABLE[]

do {                          	// Small loop starting at address 98A2.

BestNow++;

//printf("Start small loop (S1=0x%02X TABLE[Y]=0x%02X B=0x%02x).\n",S1,TABLE[Y],B);

A |= S0;                    	// 98A2: ORA ,S		; same as A = S0 !!

assert( Y <= 7 );

A &= TABLE[ Y ];            	// 98A4: ANDA ,Y	; Index TABLE[] value.

StoreA = A;                 	// 98A6: PSHS A		; Save A on the stack at this point (for use at 98AC).

A = XSTACK[ ++X ];          	// 98A8: LDA ,X+	; Recover a value stored in the stack and bump up the index.

A &= TABLE[ Y++ ];          	// 98AA: ANDA ,Y+	; Index the same TABLE[] value as above and bump up the table index for next time.

//printf("Check Y=%d TABLE[Y]=%02X S0=%02X A=0x%02X StoreA=0x%02X.\n",Y-1,TABLE[Y-1],S0,A,StoreA);

A -= StoreA;                	// 98AC: SUBA ,S+	; Is the 6702 returning the correct codes?

if( A != 0x00 ) {           	// 98AE: BNE >\$98BA	; Branch taken if the answer is no!

//@@@ printf("6702 check FAIL 0x%02X.\n",A);

return A;       		// *** NO NO NO NO ***

} // End if

S1 <<= 1;                   	// 98B0: ASL \$01,S

} while( S1 != 0x00 );        	// 98B2: BNE >\$98A2

X = StoreX;                   	// 98B4: LDX \$02,S	; Recover the 'reduced' value of X indexing into the stack.

B >>= 2;                      	// 98B6: LSRB
// 98B7: LSRB

} while( B != 0x00 );           	// 98B8: BNE >\$986B

// ****************
// ***          ***
// ***  WHOOP!  ***
// ***          ***
// ****************

BestNow = 1000000; // !!!

//printf("!!! 6702 check PASS !!!\n");

return A | B;

} // End of function X6702

void print_table()
{
int i;

for( i=0; i<15; i++ )
printf("%02X ",LOOKUP_TABLE[i]);
printf("\n");
}

int extend(int howmany, int restore_it)
{
if (howmany <= 0)
return 0;

howmany--;

unsigned char save[3];

memcpy(save, LOOKUP_TABLE, 3);
memmove(LOOKUP_TABLE + 1, LOOKUP_TABLE + 3, sizeof(LOOKUP_TABLE)-3);

int first, last;
int fail = 1;
int copy = LOOKUP_TABLE[1];

/* for (first = 0; first < 256; first++) */
for (first = copy; first <= copy; first++) {
LOOKUP_TABLE[0] = first;

BigLoops = 0;
int i = X6702(); // Call the 6702 test function.
if (i == 0) {
printf("Success with pre-pended value %02x\n", first);
print_table();
fail = 0;
break;
}
if (BigLoops >= 7) {
printf("Pre-pended value %02x got 7 loops.\n", first);
for (last = 0; last < 256*256; last++) {
LOOKUP_TABLE[13] = last >> 8;
LOOKUP_TABLE[14] = last & 0xFF;
BigLoops = 0;
i = X6702(); // Call the 6702 test function.
if (i == 0) {
if (howmany <= 0) {
printf("extend: ");
print_table();
fail = 0;
goto end;
} else {
fail = extend(howmany, restore_it);
}
}
}
}
}

end:
if (restore_it) {
memmove(LOOKUP_TABLE + 3, LOOKUP_TABLE + 1, sizeof(LOOKUP_TABLE)-3);
memcpy(LOOKUP_TABLE, save, 3);
}

return fail;
}

int keep_extending()
{
while (!extend(1, 0)) {
print_table();
}
}

void test_next(int done)
{
int i, last;

for (i = 1 + done * 2 + 2; i < sizeof(LOOKUP_TABLE); i++)
LOOKUP_TABLE[i] = 0;

for (last = 0; last < 256; last++) {
LOOKUP_TABLE[1 + done * 2    ] = LOOKUP_TABLE[1 + done * 2 - 1 ];
LOOKUP_TABLE[1 + done * 2 + 1] = last & 0xFF;
BigLoops = 0;
i = X6702(); // Call the 6702 test function.
if (i == 0) {
printf("done=%d ", done); print_table();
if (extend(1, 1) == 0) {
print_table();
LOOKUP_TABLE[1 + done * 2    ] = 0;
LOOKUP_TABLE[1 + done * 2 + 1] = 0;
return;
}
} else if (BigLoops > done + 1) {
/* Need to check BigLoops for 1 more than you'd think, since it is
* incremented before the checks are done, rather than after.
*/
test_next(done + 1);
}
}
}

// **************
// ***        ***
// ***  MAIN  ***
// ***        ***
// **************

int main( int argc, char* argv[] ) {

int  i;
long j;
int  k;

argc = argc;
argv = argv;

printf("Hello from %s...\n",argv[0]);

#if 0

j = 0;
srand( time(0) ); // Randomise things a bit!
Best = 0;

do {

for( i=0; i<15; i++ ) {
LOOKUP_TABLE[ i ] = (rand() >> 6) & 0xFF;
} // End for i

j++;
if( j == 100000000L ) {
j = 0;
printf("Working...\n");
} // End if

BestNow = 0;
BigLoops = 0;

k = X6702();

if( BestNow > Best ) {

printf("\n");
printf("Best has gone from %2d to %2d. BigLoops=%2d\n",Best,BestNow,BigLoops);

Best = BestNow;

printf("\n");
printf("\aPartial solution.\n");
printf("\n");

for( i=0; i<15; i++ )
printf("%02X ",LOOKUP_TABLE[i]);

printf("\n");

} // End if

} while( k != 0 );

printf("\a\n");
printf("The answer to life, the universe and everything 6702 is...\n");
printf("\n");

for( i=0; i<15; i++ )
printf("%02X ",LOOKUP_TABLE[i]);

printf("\n");

return 0;

#elif 0
int last;

for (last = 0; last < 256; last++) {
LOOKUP_TABLE[14] = last & 0xFF;
i = X6702(); // Call the 6702 test function.
if (i == 0) {
printf("Success with appended value %04x\n", last);
print_table();
return i;
}
}
#elif 0
printf("extend returned %d\n", extend(1, 1));
#elif 0
int first, last;

for (first = 0; first < 256; first++) {
LOOKUP_TABLE[0] = first;

BigLoops = 0;
i = X6702(); // Call the 6702 test function.
if (i == 0) {
printf("Success with pre-pended value %02x\n", first);
print_table();
//return i;
}
if (BigLoops >= 7) {
printf("Pre-pended value %02x got 7 loops.\n", first);
for (last = 0; last < 256*256; last++) {
LOOKUP_TABLE[13] = last >> 8;
LOOKUP_TABLE[14] = last & 0xFF;
BigLoops = 0;
i = X6702(); // Call the 6702 test function.
if (i == 0) {
printf("Success with appended value %04x\n", last);
print_table();
//return i;
}
}
}
}
#elif 0
int first;

for (first = 0xD6; first < 0xD7; first++) {
//if ((first | 0x11) != first) continue;
memset(LOOKUP_TABLE, 0, sizeof(LOOKUP_TABLE));
LOOKUP_TABLE[0] = first;
printf("Seed value %02x...\n", first);

test_next(0);

}
#elif 0
keep_extending();
#else

i = X6702(); // Call the 6702 test function.

printf("Answer returned from the call of X6702() is %d.\n",i);

j = 0L;
k = 0;

return i;

#endif

} // End of function main

Message was sent through the cbm-hackers mailing list