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.'