Re: PET WordCraft tape port dongle

From: Rhialto <rhialto_at_falu.nl>
Date: Tue, 22 Apr 2025 18:31:10 +0200
Message-ID: <aAfETomjwePLeUbN_at_falu.nl>
On Mon 21 Apr 2025 at 13:53:49 +0200, Didier Derny wrote:
> later the dongle was officially removed the software existed in 2 version
> the hard disk version (not protected) and the floppy version protected

Interesting, I have not found such a version yet.

(I found most versions in a file named Images.zip which was linked from
Ruud on one of the vintage computer sites, but I didn't take note of the
exact source unfortunately).

> it's probably easier with modern logic analyser able to capture large event
> the one I was using was limited to 256 captures...
> but it was connected by IEEE488 to CBM3000 with a software
>  to trace the execution

What I did was run them in Vice, and set a breakpoint on reading $E811.
I knew from the page which was linked upthread that that would be
cassette #1's read line, i.e. data in for the shift register. That
interrupted WordCraft at the right place. I examined the code around it
ant it turns out that it obfuscates the checking code, but now it was
running so I could read it. After the check it gets obfuscated again.

There was some self-modifying code that puts the value which is received
from the dongle in some other place, and around there a EOR instruction
also refers back to the dongle code. I didn't analyze that further but
it is likely a tamper detection. There could be well another detection
against tampering with the tamper detection.

Different WordCraft versions have mostly the same code but at different
addresses. A small difference is if they load 7 or 15 bits from the
dongle. I also found 2 different "master" programs to make copies of
WordCraft disks. One of them required a dongle (with any number except
zero) and could change the serial number in the copy. But so far I have
no hints that that was used for anything else than displaying it on
start-up. The other program was marked "Commodore B.V." so it was from a
Dutch dealer or even Commodore NL BV.

There are also variations in the dialog for selecting a printer driver.
There may have been separate versions for Letter QUality printers.

And there is something with networking. You can select an address for
the current computer, and choose between two modes (which both meant
nothing to me).

Here are the notes I collected about the dongle code:


Dongle on cassette port

E840 VIA port B bit 3 tape data out
                    4 tape #2 motor
E84D VIA IFR $10 CB1 input from cassette #2

E810 PIA1 port A bit 4 & 5: cassette #1 & #2 sense
E811      CRA  CA1: cassette #1 read line
E813      CRB  CB2 tape #1 motor

load:  Cassette port Sense
clockIn: Cassette port Write
clockEnable:  Cassette port Motor
dataIn: Cassette port Read

Graham lashed up a 74LS165 8 bit shift register on a piece of Veroboard wired to a connector on the tape cassette port - the PET had two such ports. The logic was simple: the software would waggle a clock line to the shift register and shift the 8 bit data in on another line. The shift register lines were tied randomly to ground or 5V providing 255 possible values (excluding zero!) This was really simple stuff but we felt it was enough - especially if the whole thing could be potted in epoxy resin so no-one could see what was going on. By the time we left Graham's place the PET was on his kitchen table with the wires and Veroboard dangling out of the back - so we called it a "dongle". 

void setup() {
  pinMode (led,OUTPUT);
  pinMode (load,OUTPUT);
  pinMode (clockEnable,OUTPUT);
  pinMode (clockIn,OUTPUT);
  pinMode (dataIn,INPUT);

// **********************************************************
void readDongle() {
  String s = "";                                                // Used to hold dongle number for output as a string of bits: e.g. 10101010
  byte dongle = 0;                                              // Dongle number read from 74LS165

  digitalWrite(load, LOW);                                      // Pulse load pin
  delayMicroseconds(5);
  digitalWrite(load, HIGH);
  delayMicroseconds(5);

  digitalWrite(clockIn, HIGH);
  digitalWrite(clockEnable, LOW);                               // Has no effect because pin 15 on the 74LS165 (CLK INH) is tied low
  dongle = shiftIn(dataIn, clockIn, LSBFIRST);
  digitalWrite(clockEnable, HIGH);                              // Has no effect because pin 15 on the 74LS165 (CLK INH) is tied low

  dongleOut (dongle);                                           // Reflect the dongle number on the 12-way connector

  if (dongle <   2) s = "0" + s;                                // Add leading zeros
  if (dongle <   4) s = "0" + s;
  if (dongle <   8) s = "0" + s;
  if (dongle <  16) s = "0" + s;
  if (dongle <  32) s = "0" + s;
  if (dongle <  64) s = "0" + s;
  if (dongle < 128) s = "0" + s;
  s = s + String(dongle, BIN);

  display2 ("DONGLE NO:", s);                                   // Display dongle number
  delay (1000);
  oled_clearDisplay();                                          // Clear the display after each dongle read
  oled_display();                                               // Send memory buffer to OLED
  delay (250);
}



.C:20d4  78          SEI
.C:20d5  20 AA 21    JSR $21AA	decode dongle code
.C:20d8  4C 9D 21    JMP $219D	-> 20f5
				-- decoded from here
.C:20da        21
				pulse tape data out ->0 -> 1
.C:20db  A9 D7       LDA #$D7	%1101 0111 port A tape data out: 0
.C:20dd  8D 40 E8    STA $E840
.C:20e0  A9 DF       LDA #$DF	%1101 1111 port A tape data out: 1
.C:20e2  8D 40 E8    STA $E840	VIA PORT B
.C:20e5  60          RTS

(C:$2169) d 20e6
.C:20e6  A9 38       LDA #$38	%0011 1000
.C:20e8  8D 11 E8    STA $E811	CRA CA1 active low, irq off, DDR active
.C:20eb  8E 10 E8    STX $E810	DDRA  X=$3F  set cassette senses to output?
.C:20ee  A9 3C       LDA #$3C   %0011 1100  make PORTA active
.C:20f0  8D 11 E8    STA $E811  CRA  CA1 active low
.C:20f3  D0 EB       BNE $20E0  always

.C:20f5  78          SEI	do the whole dongle reading thing
.C:20f6  A2 01       LDX #$01
.C:20f8  86 03       STX $03
.C:20fa  86 7E       STX $7E	what bit value an active transition is (initially 1)
.C:20fc  86 04       STX $04
.C:20fe  86 80       STX $80
.C:2100  CA          DEX
.C:2101  86 7D       STX $7D
.C:2103  86 7F       STX $7F
.C:2105  AD 11 E8    LDA $E811	PIA 1 CRA
.C:2108  AD 4C E8    LDA $E84C	VIA PCR
.C:210b  29 EF       AND #$EF
.C:210d  8D 4C E8    STA $E84C	VIA PCR
.C:2110  A9 30       LDA #$30
.C:2112  2D 10 E8    AND $E810  PIA 1 PORT A
.C:2115  C9 30       CMP #$30
.C:2117  D0 68       BNE $2181
.C:2119  A2 3F       LDX #$3F
.C:211b  20 E6 20    JSR $20E6  senses output, tape data 1
.C:211e  A9 30       LDA #$30
.C:2120  8D 10 E8    STA $E810  PIA 1 PORT A
.C:2123  20 E0 20    JSR $20E0
.C:2126  A9 00       LDA #$00
.C:2128  8D 10 E8    STA $E810  PIA 1 PORT A
.C:212b  A9 30       LDA #$30
.C:212d  8D 10 E8    STA $E810  PIA 1 PORT A

.C:2130  06 04       ASL $04    bits^2 from cass #2  <7F 04>
.C:2132  26 7F       ROL $7F
.C:2134  06 03       ASL $03	bits^2 from cass #1  <7D 03>
.C:2136  26 7D       ROL $7D
.C:2138  B0 42    /--BCS $217C  if an 1 comes out we have all bits
.C:213a  20 DB 20 |  JSR $20DB	pulse tape data out signal
.C:213d  AD 11 E8 |  LDA $E811	PIA 1 CRA
.C:2140  10 0E    |/-BPL $2150		positive when CA1 had no active transition (cass 1 read line)
.C:2142  49 02     | EOR #$02	active transition switch
.C:2144  8D 11 E8  | STA $E811	PIA 1 CRA
.C:2147  AD 10 E8  | LDA $E810  PIA 1 PORT A/DDR A
.C:214a  A5 7E     | LDA $7E
.C:214c  49 01     | EOR #$01	toggle which value is represented by active transn.
.C:214e  85 7E     | STA $7E	bits from cass #1
.C:2150  AD 4D E8  ->LDA $E84D	VIA IFR
.C:2153  29 10    |  AND #$10	CB1 interrupt bit, input from cassette #2
.C:2155  F0 0C     /-BEQ $2163  when input from cassette #2 inactive
.C:2157  4D 4C E8  | EOR $E84C	VIA PCR
.C:215a  8D 4C E8  | STA $E84C	VIA PCR
.C:215d  A5 80     | LDA $80
.C:215f  49 01     | EOR #$01
.C:2161  85 80     | STA $80	bits from cass #2
.C:2163  A5 04     ->LDA $04
.C:2165  05 80    |  ORA $80
.C:2167  85 04    |  STA $04
.C:2169  A5 03       LDA $03
.C:216b  05 7E       ORA $7E	bits from cass #1
.C:216d  85 03       STA $03    bits^2 from cass #1
.C:216f  4C 30 21    JMP $2130

.C:2172  84 03    |  STY $03    4 -> 3    copy data from dongle #2 to #1
.C:2174  A9 00    |  LDA #$00
.C:2176  85 04       STA $04              and wipe its source
.C:2178  A5 7F       LDA $7F    7F -> 7D
.C:217a  85 7D    |  STA $7D
                  |		have got all bits from the shift register
.C:217c  A2 0F    \->LDX #$0F
.C:217e  20 E6 20    JSR $20E6  senses output, tape data 1
.C:2181  A4 7D       LDY $7D	do something with input from cass #1 <7D 03>
.C:2183  8C 67 0F    STY $0F67	MODIFYING CODE! : EOR #$D2
.C:2186  98          TYA
.C:2187  49 D2       EOR #$D2   $7D expected to be #$D2? (hi)
.C:2189  D0 06     /-BNE $2191
.C:218b  A5 03     | LDA $03
.C:218d  49 10     | EOR #$10
.C:218f  F0 09     | BEQ $219A  $03 expected to be #$10? (lo)
.C:2191  A4 04     ->LDY $04
.C:2193  D0 DD       BNE $2172	a dongle present in cass #2?
                                check failed
.C:2195  A9 02       LDA #$02   JAM opcode
.C:2197  8D 47 10    STA $1047	MODIFYING CODE!  get there when you press STOP
.C:219a  4C AA 21    JMP $21AA		-- re-encrypt the dongle code
.C:219d  20 F5 20    JSR $20F5		-- do the reading thing again
					-- decode up to here
.C:21a0  D0 57       BNE $21F9
.C:21a2  A4 1C       LDY $1C
.C:21a4  8C D6 22    STY $22D6
.C:21a7  4C F9 21    JMP $21F9

.C:21aa  48          PHA		decode dongle code
.C:21ab  A2 C5       LDX #$C5
.C:21ad  8A          TXA		A=C5
.C:21ae  18          CLC
.C:21af  69 FE       ADC #$FE		A=X-2=C3
.C:21b1  5D DA 20    EOR $20DA,X	Decode some code?, high 219f
.C:21b4  9D DA 20    STA $20DA,X	The code that does the checking...
.C:21b7  CA          DEX
.C:21b8  D0 F3       BNE $21AD
.C:21ba  58          CLI
.C:21bb  68          PLA
.C:21bc  60          RTS

...

.C:2284  A9 00       LDA #$00
.C:2286  8D F0 FF    STA $FFF0		8096 version
.C:2289  A2 FF       LDX #$FF
.C:228b  78          SEI
.C:228c  9A          TXS
.C:228d  20 16 FD    JSR $FD16
.C:2290  A9 93       LDA #$93
.C:2292  8D 6F 02    STA $026F
.C:2295  A9 83       LDA #$83
.C:2297  8D 70 02    STA $0270
.C:229a  A9 02       LDA #$02
.C:229c  85 9E       STA $9E
.C:229e  4C 1E FD    JMP $FD1E
.C:22a1  C9 F0       CMP #$F0
.C:22a3  F0 30       BEQ $22D5
.C:22a5  A2 1F       LDX #$1F
.C:22a7  A9 00       LDA #$00
.C:22a9  9D 1F 03    STA $031F,X
.C:22ac  CA          DEX
.C:22ad  10 FA       BPL $22A9
.C:22af  A2 21       LDX #$21
.C:22b1  9D 01 04    STA $0401,X
.C:22b4  CA          DEX
.C:22b5  10 FA       BPL $22B1
.C:22b7  8E 21 03    STX $0321
.C:22ba  A5 80       LDA $80
.C:22bc  C9 FC       CMP #$FC
.C:22be  D0 04       BNE $22C4
.C:22c0  A9 30       LDA #$30
.C:22c2  85 80       STA $80
.C:22c4  B1 30       LDA ($30),Y
.C:22c6  C9 60       CMP #$60
.C:22c8  F0 0B       BEQ $22D5
.C:22ca  C9 2C       CMP #$2C
.C:22cc  D0 13       BNE $22E1
.C:22ce  C8          INY
.C:22cf  06 80       ASL $80
.C:22d1  06 80       ASL $80
.C:22d3  D0 E5       BNE $22BA
.C:22d5  A9 00       LDA #$00	<- constant modified by dongle code above
.C:22d7  85 1C       STA $1C
.C:22d9  A9 01       LDA #$01
.C:22db  8D D6 22    STA $22D6
.C:22de  6C 32 00    JMP ($0032)

-Olaf.
-- 
___ Olaf 'Rhialto' Seibert                            <rhialto/at/falu.nl>
\X/ There is no AI. There is just someone else's work.           --I. Rose

Received on 2025-04-22 18:00:01

Archive generated by hypermail 2.4.0.