Re: [stella] Atari 7800

Subject: Re: [stella] Atari 7800
From: Eckhard Stolberg <Eckhard_Stolberg@xxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 18 Nov 1998 16:35:48 +0100
At 12:40 17.11.98 -0600, you wrote:

>>Bruce Tomlin posted a Pascal version of the validation routine to RGVC
>>about two years ago. I think I still have this message somewhere and could
>>send it to you, if you want it.
>
>Absolutely!  That would be awesome!

I'm not sure, if you still need this, since someone else already
posted a C version, but here is Bruce Tomlin's messege from RGVC.


Ciao, Eckhard Stolberg
============================================================================
Okay, here's the finished Turbo Pascal program to validate an Atari 7800
cartridge checksum.  It expects a 64k dump file of the entire cartridge
address space, and if anyone ever comes up with the reverse algorithm, it
should automatically use it when the bytes from $FF80-$FFF7 are all $FF. 

Of course if you ever make a cartridge of your own after the reverse
algorithm is discovered, you will want to set $FFF9 to $F7 (or $F3 if you
don't want the way-cool Atari logo to show on startup) to minimize the
time it takes to validate the checksum.  The reset vector *MUST* point
into a checksummed region, and of course you don't want the checksum to go
down into a bank-selected region. 

One important thing to note is that the 13 bits in the MSB end of the
result which are cleared before comparison *will be* different between the
result of the cartridge and checksum functions.  This may have been due to
a short-cut to make the reverse algorithm computable within a reasonable
amount of time. 

PROGRAM Crypt78;

{ $R+}

TYPE
    Blk120 = ARRAY[0..$77+1] OF Byte;
    Blk256 = ARRAY[0..$FF] OF Byte;

CONST
   hex : String[16] = ('0123456789ABCDEF');
(*
   A_cart : Blk120 = ($02,$E0,$77,$88,$A6,$1B,$03,$39, { from Monitor Cart }
                      $DE,$FC,$51,$11,$DD,$B9,$A9,$9B,
                      $12,$B6,$44,$CD,$DC,$6C,$BD,$06,
                      $AD,$FE,$7E,$56,$FA,$37,$B3,$58,
                      $6A,$85,$B4,$88,$39,$57,$8F,$ED,
                      $42,$A6,$88,$BE,$7A,$BC,$A2,$CD,
                      $A8,$07,$08,$DE,$8B,$93,$BF,$3A,
                      $74,$A1,$DC,$7E,$03,$9C,$AD,$3F,
                      $0F,$33,$E6,$F6,$CF,$83,$79,$2B,
                      $A1,$89,$9E,$6C,$DC,$56,$B1,$19,
                      $48,$C7,$00,$A0,$F9,$08,$6F,$C2,
                      $52,$71,$7D,$24,$A3,$9A,$B4,$99,
                      $1B,$5C,$6E,$18,$A8,$5A,$46,$DF,
                      $03,$91,$6E,$1E,$E6,$2C,$6C,$D2,
                      $0A,$C6,$93,$05,$8A,$CC,$4E,$B0,$FF);
*)
   A_ROM  : Blk120 = ($09,$CA,$C9,$C6,$B4,$12,$08,$1B,
                      $60,$58,$81,$4B,$86,$01,$D8,$BF,
                      $D9,$25,$A0,$7B,$DC,$32,$79,$84,
                      $3B,$7C,$BC,$2F,$E2,$E2,$FA,$8D,
                      $0A,$00,$3B,$C5,$EC,$AF,$2D,$8A,
                      $CD,$06,$93,$6A,$A5,$14,$46,$77,
                      $C4,$6A,$B2,$53,$36,$EF,$8C,$CE,
                      $0C,$A2,$68,$71,$D3,$73,$E8,$F7,
                      $6D,$06,$B5,$20,$EF,$23,$47,$0C,
                      $51,$55,$C8,$FE,$F4,$58,$C4,$3F,
                      $20,$A7,$67,$38,$B0,$76,$E2,$C4,
                      $D8,$05,$63,$F8,$3C,$58,$3B,$2D,
                      $22,$CC,$88,$B3,$71,$8F,$1D,$80,
                      $0A,$87,$BD,$A1,$59,$23,$E9,$70,
                      $E2,$D3,$EC,$46,$68,$80,$42,$39,$EA);

   A_munge : ARRAY[0..255+8] OF Byte =
                     ($C7,$65,$AB,$CA,$EE,$F7,$83,$09,
                      $E1,$D0,$92,$67,$62,$B6,$72,$55,
                      $8E,$91,$DC,$C5,$81,$BE,$78,$20,
                      $59,$B7,$E6,$3D,$06,$45,$AF,$C8,
                      $08,$31,$38,$D1,$FB,$73,$84,$A9,
                      $17,$FC,$34,$87,$A3,$94,$FA,$90,
                      $B8,$ED,$CE,$3B,$5B,$0A,$43,$D9,
                      $F3,$53,$82,$B3,$0D,$6D,$5A,$60,
                      $9D,$51,$A7,$B9,$11,$10,$BC,$E4,
                      $7F,$80,$41,$E7,$E3,$F6,$56,$26,
                      $35,$EC,$D6,$DF,$0C,$7F,$F4,$9E,
                      $AC,$52,$46,$EF,$CF,$BF,$A2,$3F,
                      $A4,$13,$15,$97,$4A,$1C,$B0,$42,
                      $8C,$B1,$05,$58,$80,$18,$77,$2B,
                      $02,$3E,$A8,$49,$1A,$6A,$CB,$6E,
                      $0B,$8A,$EB,$F1,$4F,$14,$79,$8B,
                      $D8,$9F,$9B,$57,$19,$F8,$2A,$2D,
                      $76,$0E,$E8,$2E,$4B,$F9,$07,$03,
                      $DE,$93,$16,$7E,$D4,$E5,$B2,$F0,
                      $7D,$7A,$DA,$D2,$A1,$CC,$1D,$E0,
                      $5E,$23,$A0,$95,$22,$1E,$36,$85,
                      $FE,$1F,$39,$AA,$89,$96,$AD,$0F,
                      $2F,$C0,$47,$27,$5D,$24,$EA,$C3,
                      $A5,$F5,$21,$5F,$1B,$40,$8F,$AE,
                      $74,$25,$DD,$C1,$7C,$CD,$A6,$70,
                      $D7,$33,$7B,$2C,$75,$BB,$86,$99,
                      $BD,$54,$9A,$6C,$63,$32,$48,$4C,
                      $8D,$BA,$5C,$61,$C4,$4E,$29,$37,
                      $12,$C6,$98,$9C,$D5,$69,$6B,$E2,
                      $04,$4D,$E9,$C2,$88,$3A,$DB,$64,
                      $01,$44,$6F,$B5,$F2,$30,$28,$FD,
                      $50,$71,$3C,$B4,$66,$68,$C9,$D3,
                      $CA,$83,$C7,$AB,$F7,$65,$09,$EE);

VAR
   A_1800 : Blk256;
   A_1900 : ARRAY[0..7] OF Blk120;
   A_2000 : Blk120;
   A_1A00 : Blk120;
   A_cart : Blk120;
   A_mem  : ARRAY[$4000..$FFFF] OF Byte;
   fname  : String;
   f      : File;


PROCEDURE Write1Hex(n: Word);

BEGIN
   n := n AND 15;
   Write(Copy(hex,n+1,1));
END;


PROCEDURE Write2Hex(n: Word);

BEGIN
   Write1Hex(n SHR 4);
   Write1Hex(n);
END;


PROCEDURE Dump1800;

VAR
   i : Integer;

BEGIN
   FOR i := 0 TO $FF DO BEGIN
      Write2Hex(A_1800[i]);
           IF (i AND 15)=7  THEN Write('  ')
      ELSE IF (i AND 15)=15 THEN WriteLn
      ELSE Write(' ');
   END;
   WriteLn;
END;


PROCEDURE Dump120(VAR {read only} ary: Blk120);

VAR
   i : Integer;

BEGIN
   FOR i := 0 TO $77 DO BEGIN
      Write2Hex(ary[i]);
           IF (i AND 15)=7  THEN Write('  ')
      ELSE IF (i AND 15)=15 THEN WriteLn
      ELSE Write(' ');
   END;
   WriteLn;
   WriteLn;
END;


PROCEDURE ReadBlock(addr,len: Word; buf: Pointer);

BEGIN
   Seek(f,addr);
   BlockRead(f,buf^,len);
END;


PROCEDURE BigRot;

VAR
   i,j,n,carry : Integer;

BEGIN
   A_1900[0,0] := 0;

   FOR i := 0 TO 6 DO BEGIN
      carry := 0;
      FOR j := $78 DOWNTO 0 DO BEGIN
         n := A_1900[i,j] * 2 + carry;
         A_1900[i+1,j] := n AND 255;
         carry := n SHR 8;
      END;
   END;
END;


PROCEDURE BigSub(i,j: Integer);

VAR
   x,n,borrow : Integer;

BEGIN
   borrow := 0;
   FOR x := $78 DOWNTO 0 DO BEGIN
      n := A_1800[i+x] - A_1900[j,x] - borrow;
      A_1800[i+x] := n AND 255;
      borrow := (n SHR 8) AND 1;
   END;

   x := -1;
   WHILE borrow<>0 DO BEGIN
      n := A_1800[i+x] - borrow;
      A_1800[i+x] := n;
      borrow := (n SHR 8) AND 1;
      x := x - 1;
   END;
END;


FUNCTION BigCmp(i,j: Integer): Boolean;

VAR
   x : Integer;

BEGIN
   x := 0;
   WHILE A_1800[i+x] = A_1900[j,x] DO
      x := x + 1;
   BigCmp := (A_1800[i+x] > A_1900[j,x]);
END;


PROCEDURE BigDiv;

VAR
   i,j : Integer;

BEGIN
   BigRot;

   A_1800[0] := 0;
   FOR i := 0 TO $78 DO
      FOR j := 7 DOWNTO 0 DO
         IF BigCmp(i,j) THEN
            BigSub(i,j);
END;


PROCEDURE BigAdd(i,j: Integer);

VAR
   x,n,carry : Integer;

BEGIN
   carry := 0;
   FOR x := $78 DOWNTO 0 DO BEGIN
      n := A_1800[i+x] + A_1900[j,x] + carry;
      A_1800[i+x] := n AND 255;
      carry := n SHR 8;
   END;

   x := -1;
   WHILE carry<>0 DO BEGIN
      n := A_1800[i+x] + carry;
      A_1800[i+x] := n AND 255;
      carry := n SHR 8;
      x := x - 1;
   END;
END;


PROCEDURE BigMul;

VAR
   i,j : Integer;

BEGIN
   BigRot;

   FOR i := $F0 DOWNTO 0 DO
      A_1800[i] := 0;

   FOR i := $77 DOWNTO 0 DO BEGIN
      FOR j := 0 TO 7 DO BEGIN
         IF (A_2000[i] AND (1 SHL j))<>0 THEN
            BigAdd(i+1,j);
      END;
   END;
END;


PROCEDURE MungeChecksum;

VAR
   i : Integer;

BEGIN
   WriteLn('MungeChecksum');

   FOR i := 0 TO $77 DO
      A_1900[0,i+1] := A_cart[i];

   A_2000 := A_cart;
   BigMul;

   FOR i := 0 TO $77 DO
      A_1900[0,i+1] := A_ROM[i];
   BigDiv;

   FOR i := 0 TO $77 DO
      A_2000[i] := A_1800[i+$79];

   Dump120(A_2000);

   A_2000[0] := A_2000[0] AND 7;
   A_2000[4] := 0;
END;


PROCEDURE X_23FF(page: Word; munge: Word; VAR a,carry: Integer);

VAR
   i,n  : Integer;
   addr : Word;

BEGIN
   addr := page SHL 8;

   FOR i := 0 TO 255 DO BEGIN
      n := a + A_1800[i] + carry;
      carry := (n SHR 8) AND 1;
      n := (n AND 255) + A_mem[addr+i] + carry;
      a := A_munge[munge + (n AND 255)];
      A_1800[i] := a AND 255;
      carry := (n SHR 8) AND 1;
   END;
END;


PROCEDURE ROLWork(VAR carry: Integer);

VAR
   i,n : Integer;

BEGIN
   FOR i := 0 TO 255 DO BEGIN
      n := (A_1800[i] SHL 1) + carry;
      A_1800[i] := n AND 255;
      carry := n SHR 8;
   END;
END;


PROCEDURE MungeCart;

VAR
   i       : Integer;
   a,carry : Integer;
   p       : Integer;

BEGIN
   WriteLn('MungeCart');

   p := A_mem[$FFF9] AND $F0;

   FOR i := 0 TO 255 DO
      IF i IN [$80..$F8] THEN A_1800[i] := 0
                         ELSE A_1800[i] := A_mem[$FF00+i];

   a := 0;
   carry := 1;

   FOR i := p TO $FE DO BEGIN
      X_23FF(i,0,a,carry);
      carry := 0;
   END;

   carry := 1;
   ROLWork(carry);
   ROLWork(carry);

   FOR i := $FE DOWNTO p DO BEGIN
      X_23FF(i,8,a,carry);
      carry := 1;
   END;

   {Dump1800;}

   FOR i := 0 TO $77 DO
      A_1A00[i] := A_1800[i] XOR A_1800[i+$50] XOR A_1800[i+$88];

   Dump120(A_1A00);

   A_1A00[0] := A_1A00[0] AND 7;
   A_1A00[4] := 0;
END;


VAR
   i : Integer;


BEGIN
   WriteLn;
   FillChar(A_1800,SizeOf(A_1800),#0);
   FillChar(A_1900,SizeOf(A_1900),#0);
   FillChar(A_2000,SizeOf(A_2000),#0);

   IF ParamCount=0 THEN fname := 'HATTRICK.BIN'
                   ELSE fname := ParamStr(1);
   Assign(f,fname);
   Reset(f,1);
   ReadBlock($FF80,SizeOf(A_cart),@A_cart);
   ReadBlock($4000,SizeOf(A_mem) ,@A_mem);
   Close(f);

   MungeChecksum;
   MungeCart;

   i := 0;
   WHILE (i<=$77) AND (A_1A00[i]=A_2000[i]) DO
      i := i + 1;

   IF i=$78 THEN WriteLn('Cartridge checksum for ',fname,' is valid!')
            ELSE WriteLn('Cartridge checksum for ',fname,' is not valid!');
   WriteLn;
END.



--
Archives (includes files) at http://www.biglist.com/lists/stella/archives/
Unsub & more at http://www.biglist.com/lists/stella/

Current Thread