FORTH FACTORY#

6502 DISASSEMBLER#

by JOHN MATTES

see also 6502 Disassembler

Note: the below has been published in ANTIC 3/84 - with errors, preventing the code from running. Errata has been published in the following issue. Corrections were on the 1st screen (line 10 and 13) and the last one (1st line). Applied below. Full code (tested with APX Forth and adapted for pasting into Altirra and the Forth editor) maintained here: https://github.com/BartGo/forth-atari/blob/main/DISASM.4TH

To run: 

' WORD-TO-CHECK DIS

Result for words C@ and @ shown below.

APX Forth looks good (someone please verify): No InterWiki reference defined in properties for Wiki called "data"!  

Antic Forth (1.4S / Team Atari) seems to be in trouble (also, does not empty the stack): 

No InterWiki reference defined in properties for Wiki called "data"!. This sequence puts the parameter field address of C@ on the stack and starts disassembly. The result looks like:

13FB  LDA X) 0
13FD  STA ,X 0
13FF  STY  ,X 1
1401 JMP   .....  F47      OK

Note that the address O,X points to the byte on the bottom of the data stack (it grows down!) and 1,X is the next byte up. F47 is the address of the Forth procedure NEXT, which passes execution to the next Forth word.

The first instruction loads the accumulator with the byte which was at the 16-bit address on the "top" (physically at the bottom) of the stack. The second instruction at 13FF stores the contents of the Y register (which you can count on being zero) into the high order byte on "top" of the stack. Thus the address on the "top" of the stack is replaced by the byte which was (and still is) stored at that address.

A word of warning: DISASSEMBLE will disassemble anything! It does not try to stop you from disassembling data, Forth code or even machine code starting at the wrong point. However, you can easily detect a listing of gibberish. The listing will tend to be long (over a screen), the addresses will be all over the place and rarely used instructions will pop up frequently.


Listing: DISASM.4TH

SCR #30
   0 ( FORTH DISASSEMBLER ANTIC 3/84 )
   1 HEX 0 VARIABLE MULTIMODE -2 ALLOT
   2 1D , 1E , 3C , 3D , 3E , 5C ,
   3 5D , 5E , 7D , 7E , 9C , 9D ,
   4 9E , BC , BD , BE , DC , DD ,
   5 DE , FC , FD , FE ,
   6
   7 : SEARCH 1 + 0 DO OVER OVER I 2
   8     * + C@ - DUP 0= IF DROP
   9     DROP DROP I 1 LEAVE ELSE
  10     0 < IF DROP DROP I 0 LEAVE
  11     ENDIF ENDIF LOOP ;
  12
  13 0 VARIABLE POINTER
  14
  15 -->SCR #31
   0 : STRING ( COMPILE TEXT )
   1     BL BLK @ IF BLK @ BLOCK ELSE
   2     TIB @ ENDIF IN @ + SWAP
   3     ENCLOSE IN +! OVER - >R +
   4     HERE R CMOVE R> ALLOT ;
   5
   6 0 VARIABLE MULTINAME -2 ALLOT
   7
   8 STRING ORAASLBITANDROLJMPEORLSR
   9 STRING ADCRORSTYSTASTXLDYLDALDX
  10 STRING CPYCMPDECCPXSBCINC
  11
  12 : CHKMODE DROP DUP 2 * '
  13     MULTIMODE + @ POINTER @ C@ -
  14     4 /MOD SWAP IF SWAP 1+ SWAP
  15     ENDIF ;   -->SCR #32
   0 0 VARIABLE ONEMODE -2 ALLOT
   1 2C00 , 2C08 , 280A , 3010 ,
   2 2C18 , 1020 , 2C28 , 282A ,
   3 3030 , 2C38 , 2C40 , 2C48 ,
   4 284A , 3050 , 2C58 , 2C60 ,
   5 2C68 , 286A , 246C , 3070 ,
   6 2C78 , 2C88 , 2C8A , 3090 ,
   7 2096 , 2C98 , 2C9A , 14A0 ,
   8 14A2 , 2CA8 , 2CAA , 30B0 ,
   9 20B6 , 2CB8 , 2CBA , 04BE ,
  10 2CC8 , 2CCA , 30D0 , 2CD8 ,
  11 2CE8 , 2CEA , 30F0 , 2CF8 ,
  12 00FF , ( 00FF IS A DUMMY )
  13
  14
  15 -->SCR #33
   0 0 VARIABLE ONENAME -2 ALLOT
   1 STRING BRKPHPASLBPLCLCJSRPLPROL
   2 STRING BMISECRTIPHALSRBVCCLIRTS
   3 STRING PLARORJMPBVSSEIDEYTXABCC
   4 STRING STXTYATXSLDYLDXTAYTAXBCS
   5 STRING LDXCLVTSXLDXINYDEXBNECLD
   6 STRING INXNOPBEQSED???
   7
   8 0 VARIABLE MODE -2 ALLOT
   9 STRING ,X,Y,X)Y..##0PX).Y().AIMRE
  10
  11 0 VARIABLE LENGTH -2 ALLOT
  12 2 C, 2 C, 1 C, 1 C, 2 C, 1 C,
  13 1 C, 1 C, 1 C, 2 C, 0 C, 0 C,
  14 1 C, 0 C,
  15 -->SCR #34
   0 : PRINTNAME SPACE SWAP 3 * +
   1             3 TYPE 2 SPACES ;
   2
   3 : PRINTMODE 2 * ' MODE + 2 TYPE
   4             2 SPACES ;
   5
   6 : PRINTADD POINTER @ C@ DUP 20
   7     = OVER 40 = OR OVER 4C = OR
   8     OVER 60 = OR SWAP 6C = OR
   9     SWAP ' LENGTH + C@ 1 POINTER
  10     +! POINTER @ OVER POINTER +!
  11     OVER 0= IF DROP DROP ELSE
  12     OVER 1 = IF C@ . DROP ELSE
  13     @ 0 D. DROP ENDIF ENDIF ;
  14
  15 -->
SCR #35
   0 : DISASSEMBLE POINTER ! CR
   1   BEGIN CR
   2     POINTER @ DUP 0 D. 2 SPACES
   3     C@ ' ONEMODE 2D SEARCH
   4       IF ( FOUND ) DUP ' ONENAME
   5          PRINTNAME 2 * 1+ '
   6          ONEMODE + C@ 4 /
   7       ELSE ( NOT ) DROP POINTER @
   8          C@ ' MULTIMODE 16 SEARCH
   9          CHKMODE CHKMODE CHKMODE
  10          SWAP ' MULTINAME
  11          PRINTNAME ENDIF
  12     DUP PRINTMODE PRINTADD
  13     ?TERMINAL OR
  14   UNTIL ;
  15 : DIS DISASSEMBLE ;

DESCRIPTION OF DISASSEMBLER#

 

Step Stack Instruction Comment
OPadd (2) Address of current opcode
1 POINTER! Store OPadd in POINTER
3 CR Start a new line.
4 BEGIN CR Start a loop with a new line.
6 OPadd (11) POINTER@ Fetch the opcode address.
7 OPadd (9) DUP
8 O (9) O Print the address (double precision -
9 D. to avoid negative addresses!)
10 2 SPACES Leave 2 spaces
11 OP (14) C@ Fetch the opcode.
12 OMad (14) ' ONEMODE Calculate the start address for ONEMODE table.
13 2D (14) 2D ONEMODE is 2D (45) entries long.
14 I (20)(27) SEARCH For OP in ONEMODE table. Leave -
f (15) Index and flag.
15 IF Test f. False part starts Step 26.
16 I (18) DUP TRUE part (f=1),i.e., OP on ONEMODE table.
17 ONad (18) ' ONENAME Start address for ONENAME table.
18 PRINTNAME Type the mnemonic
20 2I + 1 (23) 2 * 1+
22 OMad (23) ' ONEMODE Start address of ONEMODE table.
23 OMad+2I+1 (24) + Adress of MODE for entry I.
24 MODE (25) C@ Fetch MODE.
25 MODE/4 (41) 4 / True part jumps to Step 38.
26 ELSE FALSE part OP not in ONEMODE
27 DROP The index left by SEARCH (Step 14).
28 OP POINTER @ c @ Prepare to search MULTIMODE -
29 MMad ' MULTIMODE table for opcode. Table is
30 16 16 16 (hex) entries long.
31 I (32) SEARCH For OP. Leave Index and flag.
f (32) f is not used in this case
32 J (33) CHKMODE Check whether MODE for entry -
MODE/4 (33) I in MULTIMODE table is -
33 J (34) CHKMODE divisible by 4. If it is -
MODE/4 (34) return J = I otherwise J = I + 1.
34 J (35) CHKMODE It may be necessary to -
MODE/4 (41) increment I twice (3 CHKMODEs).
35 J (37) SWAP
36 MNad (37) ' MULTINAME Start address for MULTINAME table
37 PRINTNAME Start address for MULTINMAE table
38 ENDIF terminates the IF at line 15.
39 MODE/4 (40) DUP
40 PRINTMODE Print address mode mnemonics
41 f (43) PRINTADD Print the address part of theinstruction and update the pointer. f = 1 indicates a jump instruction (finish).
42 f (43) ?TERMINAL f = 1 indicates a key is pressed (finish).
43 f (44) OR
44 UNTIL Jump to BEGIN (step 4) if f = 0
45 ; END

'John Mattes, from Syndney, Australia, is an electrical engineer who has worked in telecommunications for 20 years. He says he is "absorbed" in using Forth with his Atari 800.'