Ragsdale's 6502 assember article, and the Datatronic version.

This commit is contained in:
Lars Brinkhoff 2018-05-08 08:47:49 +02:00
parent b58a30b879
commit 61ad01df29
3 changed files with 893 additions and 0 deletions

BIN
FD-V03N5.pdf Normal file

Binary file not shown.

629
asm.doc Normal file
View File

@ -0,0 +1,629 @@
6502 FORTH ASSEMBLER by W. Ragsdale
Introduction
This article should further polarize the attitudes of those outside the
growing community of FORTH users. Some will be fascinated by a label-
less, macro assembler whose source code is only 96 lines long! Others
will be repelled by reverse Polish syntax and the absence of labels.
The author immodestly claims that this is the best FORTH assembler
ever distributed. It is the only assembler that detects all errors in op-code
generation and conditional structuring. It is released to the public
domain as a defense mechanism. Three good 6502 assemblers were
submitted to the FORTH Interest Group but each had some lack. Rather
than merge and edit for publication, the author chose to publish his
with all the submitted features plus several more.
Imagine having an assembler in 1300 bytes of object code with:
1. User macros (like IF, UNTIL,) definable at any time.
2. Literal values expressed in any numeric base, alterable at
any time.
3. Expressions using any resident computation capability.
4. Nested control structures without labels with error control.
5. Assembler source itself in a portable high level language.
Overview
FORTH is provided with a machine language assembler to create
execution procedures that would be time inefficient, if written as
colon-definitions. It is intended that "code" be written similarly to high
level, for clarity of expression. Functions may be written first in high
level, tested, and then re-coded into assembly, with a minimum of
restructuring.
The Assembly Process
Code assembly consists of interpreting with the ASSEMBLER vocabulary
as CONTEXT. Thus each word in the input stream will be matched
according to the FORTH practice of searching CONTEXT first, and then
CURRENT.
ASSEMBLER (now CONTEXT)
FORTH (chained to ASSEMBLER)
user's (CURRENT if one exists)
FORTH (chained to user's vocabulary)
try for literal number
else, do error abort.
The above sequence is the usual action of FORTH's text interpreter,
which remains in control during assembly.
During assembly of CODE definitions, FORTH continues interpretation of
each word encountered in the input stream (not in the compile mode).
These assembler words specify operands, address modes, and op-
codes. At the conclusion of the CODE definition, a final error check
verifies correct completion by "unsmudging" the definition's name, to
make it available for dictionary searches.
Run-Time, Assembly-Time
One must be careful to understand at what time a particular word
definition executes. During assembly, each assembler word interpreted
executes. Its function at that instant is called 'assembling' or assembly-
time'. This function may involve op-code generation, address calculation,
mode selection, and so forth.
The later execution of the generated code is called 'run-time'. This
distinction is particularly important with the conditionals. At assembly
time each such word (that is, IF, UNTIL, BEGIN, etc.) itself 'runs' to
produce machine code which will later execute at what is labeled 'run-
time' when its named code definition is used.
An Example
As a practical example, here's a simple call to the system monitor
(KIM-1 only), via the NMI address vector (using the BRK op-code).
CODE MON (exit to monitor)
BRK, NEXT JMP, END-CODE
The word CODE is first encountered and executed by FORTH. CODE
builds the following name "MON" into a dictionary header and calls
ASSEMBLER as the CONTEXT vocabulary.
The "(" is next found in the FORTH and executed to skip until ")". This
method skips over comments. Note that the name after CODE and the
")" after "(" must be on the same text line.
Op-Codes
BRK, is next found in the assembler as the op-code. When BRK,
executes, it assembles the byte value 00 (zero) into the dictionary as
the op-code for "break to monitor" via "NMI".
Many assembler word's names end in ",". The significance of this is:
1. The comma shows the conclusion of a logical grouping that
would be one line of classical assembly source code.
2. compiles Into the dictionary; thus a comma implies the
point at which code is generated.
3. The "," distinguishes op-codes from possible HEX numbers
ADC and ADD.
Next
FORTH executes your word definitions under control of the address
interpreter, named NEXT. This short code routine moves execution from
one definition, to the next. At the end of your code definition, you must
return control to NEXT or else to code which returns to NEXT.
Return of Control
Most 6502 systems can resume execution after a break, since the
monitor (KIM-1 only) saves the CPU register contents. Therefore, we
must return control to FORTH after a return from the monitor. NEXT is a
constant that supplies the machine address of FORTH's address
interpreter ($8115 for 64FORTH). Here it is the operand for JMP,. As
JMP, executes, it assembles a machine code jump to the address of
NEXT from the assembly time stack value.
Security
Numerous tests are made within the assembler for user errors:
1. All parameters used in CODE definitions must be removed.
2. Conditionals must be properly nested and paired.
3. Address modes and operands must be allowed for the
op-codes.
These tests are accomplished by checking the stack position (in CSP) at
the creation of the definition name and comparing it with the position at
END-CODE. Legality of address modes and operands is insured by
means of a bit mask associated with each operand.
Remember that if an error occurs during assembly, END-CODE never
executes. The result is that the "smudged" condition of the definition
name remains in the "smudged" condition and will not be found during
dictionary searches.
The user should be aware that one error not trapped is referencing a
definition in the wrong vocabulary:
i.e., 0= of ASSEMBLER when you want
0= of FORTH
Summary (KIM-1 only)
The object code of our example is:
3059 83 4D 4F CE CODE MON
305D 4D 30 link field
305F 61 30 code field
3061 00 BRK
3062 4C 42 02 JMP NEXT
Op-Codes, revisited
The bulk of the assembler consists of dictionary entries for each
op-code. The 6502 one mode op-codes are:
BRK, CLC, CLD, CLI, CLV,
DEX, DEY, INX, INY, NOP,
PHA, PHP, PLA, PLP, RTI,
RTS, SEC, SED, SEI, TAX,
TAY, TSX, TXS, TXA,
When any of these are executed, the corresponding op-code byte is
assembled into the dictionary.
The multi-mode op-codes are:
ADC, AND, CMP, EOR, LDA,
ORA, SBC, STA, ASL, DEC,
INC, LSR, ROL, ROR, STX,
CPX, CPY, LDX, LDY, STY,
JSR, JMP, BIT,
These usually take an operand, which must already be on the stack.
An address mode may also be specified. If none is given the op-code
uses z-page or absolute addressing. The address modes are described by:
SYMBOL MODE OPERAND
.A accumulator none
# immediate 8 bits only
,X indexed X z-page or absolute
,Y indexed Y z-page or absolute
X) indexed indirect X z-page only
)Y indirect indexed Y z-page only
) indirect absolute only
none memory z-page or absolute
Examples
Here are examples of FORTH vs. a conventional assembler. Note that
the operand comes first, followed by any mode modifier, and then the
op-code mnemonic. This makes best use of the stack at assembly time.
Also, each assembler word is set off by blanks, as is required for all
FORTH source text.
FORTH ASSEMBLER CONVENTIONAL ASSEMBLER
.A ROL, ROL A
1 # LDY, LDY #1
DATA ,X STA, STA DATA,X
DATA ,Y CMP, CMP DATA,Y
06 X) ADC, ADC (06,X)
POINT )Y STA, STA (POINT),Y
VECTOR ) JMP, JMP (VECTOR)
( .A distinguishes from the HEX number 0A )
The word DATA and VECTOR specify machine addresses. in the case of
" 06 )X ADC, " the operand memory address $0006 was given directly.
This is occasionally done if the usage of a value does not justify
devoting the dictionary space to a symbolic value.
6502 Conventions
Stack Addressing
The data stack is located in z-page usually addressed by "Z-PAGE,X".
The stack starts near $009E (64FORTH is at $0060) and grows down-
ward. The X index register is the data stack pointer. Thus, incrementing
X by two removes a data stack value; decrementing X twice makes
room for one new data stack value.
Sixteen-bit values are placed on the stack according to the 6502
convention; the low byte is at low memory, with the high byte following.
This allows "indexed, indirect X" directly off a stack value.
The bottom and second stack values are referenced often enough that
the support words BOT and SEC are included. Using:
BOT LDA, assembles LDA (0,X) and
SEC ADC, assembles ADC (2,X)
BOT leaves 0 on the stack and sets the address mode to X. SEC leaves
2 on the stack also setting the address mode to X.
Here is a pictorial representation of the stack in z-page:
[ ]
-------------------------
[ sec high ]
[ sec low ]
-------------------------
[ bot high ]
[ bot low ] <-- X offset above $0000
-------------------------
Here is an example of code to "or" to the accumulator four bytes on
the stack:
BOT LDA, LDA (0,X)
BOT 1+ ORA, ORA (1,X)
SEC ORA, ORA (2,X)
SEC 1+ ORA, ORA (3,X)
To obtain the 14-th byte on the stack:
BOT 13 + LDA, LDA (13,X)
Return Stack
The FORTH Return Stack is located in the 6502 machine stack in
page 1. It starts at $01FE and builds downward. No lower bound is set or
check as Page 1 has sufficient capacity for all (non-recursive)
applications.
By 6502 convention the CPU's register points to the next free byte
below the bottom of the return stack. The byte order follows the
convention of low significance byte at the lower address.
Return stack values may be obtained by: PLA, PLA, which will pull the
low byte and then the high byte from the return stack. To operate on
arbitrary bytes, the method is:
1. Save X in XSAVE.
2. Execute TSX, to bring the S register to X.
3. Use RP) to address the lowest byte of the return stack. Offset
the value to address higher bytes. (Address mode is
automatically set to ,X)
4. Restore X from XSAVE.
As an example, this definition non-destructively tests that the second
item on the return stack (also the machine stack) is zero.
CODE IS-IT ( zero ? )
XSAVE STX, ( save current value of X register )
TSX, ( setup for return stack access )
RP) 2+ LDA,
RP) 3 + ORA,
0= IF, INY, ( if zero bump Y to one )
ENDIF,
TYA,
PHA, ( save result on stack )
XSAVE LDX, ( restore return stack pointer )
PUSH JMP, ( go push a boolean from stack )
END-CODE ( terminate the CODE definition )
[ ]
[ Return Stack ]
-------------------------
[ high byte ] second
RP) = $0101,X ---> [ low byte ] item
-------------------------
[ high byte ] bottom
[ low byte ] item
-------------------------
S ---> [ free byte ]
FORTH Registers
Several FORTH registers are available only at the assembly level and
have been given names that return their memory addresses. They are:
IP Address of the Interpretive Pointer, Specifying the next
FORTH address which will be interpreted by next.
W Address of the Interpretive Pointer, specifying the next
definition just interpreted by NEXT.
UP User Pointer containing address of the base of the user area.
N A utility area in z-page from N-1 through N+7.
CPU Registers
When FORTH execution leaves NEXT to execute a CODE definition,
the following conventions apply:
1. The Y index register is zero. It may be freely used.
2. The Z index register defines the low byte of the bottom data
stack item relative to machine address $0000.
3. The CPU stack pointer S points one byte below the bottom
return stack item. Executing PLA, will pull this byte to the
accumulator.
4. The accumulator may be freely used.
5. The processor is in binary mode and must be returned in
that mode.
XSAVE
XSAVE is a byte buffer in z-page, for temporary storage of the X register.
Typical usage, with a call which will change X, is:
CODE DEMO
XSAVE STX, ( save current value of X )
USER'S JSR, ( Go to a user's routine )
XSAVE LDX, ( restore value of X register )
NEXT JMP, ( return to FORTH )
END-CODE ( terminate the CODE definition )
When absolute memory registers are required, use the 'N Area' in the
base (zero) page. These registers may be used as pointers for indexed/
indirect addressing or for temporary values. As an example of use, see
CMOVE in the "fig MODEL" installation manual.
The assembler word N returns the base address (64FORTH=$0068).
The N area spans 9 bytes, from N-1 to N+7. Conventionally, N-1
holds one byte and N, N+2, N+4, N+6 are pairs which may hold 16 bit
values. See SETUP for help on moving values to the N area.
It is very important to note that many FORTH procedures use N. Thus, N
may only be used within a single code definition. Never expect that a
value will remain there, outside a single definition.
CODE DEMO HEX
6 # LDA,
N 1 - STA, ( setup a counter )
BEGIN,
8001 BIT, ( tickle a port in KIM-1 )
N 1 - DEC, ( decrement the counter )
0= UNTIL, ( loop till counter = zero )
NEXT JMP, ( return to FORTH )
END-CODE ( complete the definition )
SETUP
Often we wish to move stack values to the N area. The subroutine
SETUP has been provided for this purpose...Upon entering SETUP the
accumulator specifies the quantity of 16-bit values to be moved to the
N area. That is, A may be 1, 2, 3, or 4, only:
3 # LDA, ( setup to move three values )
SETUP JSR, ( move 3 16 bit values to N area )
stack before N after stack after
H high H
G low bot-G
F F
E E
D D
sec-> C C
B B
bot-> A N--> A
Control Flow
FORTH discards the usual convention of assembler labels. Instead, two
replacements are used. First, each FORTH definition name is
permanently included in the dictionary, This allows procedures to be
located and executed by name at any time as well as compiled within
other definitions.
Secondly, within a code definition, executing flow is controlled by
label-less branching according to "structured programming". This
method is identical to the form used in colon-definitions. Branch
calculations are done at assembly time by temporary stack values
placed by the control words:
BEGIN, UNTIL, IF, ELSE, ENDIF,
( THEN, is used in some assemblers in place of ENDIF, )
Here again, the assembler words end with a comma to indicate that
code is being produced and to clearly differentiate from the high-level
form.
One major difference occurs! High-level flow is controlled by run-time
boolean values on the data stack. Assembly flow is instead controlled by
processor status bits. The programmer must indicate which status bit to
test, just before a conditional branching word (IF, and UNTIL,).
Examples are:
PORT LDA,
0= IF, ( read port, if equal to zero do )
<function A> ( <function A> )
ENDIF,
PORT LDA,
0= NOT IF, ( read port, if not equal to zero )
<function A> ( do <function A> )
ENDIF,
The conditional specifiers for 6502 are:
CS test carry set C=1 in processor status
CS NOT test carry clear C=0
0< byte less than zero N=1
0< NOT test positive N=0
0= equal to zero Z=1
0= NOT test not equal zero Z=0
OVS overflow set V=1 ( added to 64FORTH )
OVS NOT overflow clear V=0 ( added to 64FORTH )
Conditional Looping
A conditional loop is formed at assembler level by placing the portion
to be repeated between BEGIN, and UNTIL,:
6 # LDA,
N STA, ( define loop counter in N )
BEGIN,
PORT DEC, ( repeated action )
N DEC,
0= UNTIL, ( N reaches zero )
First, the byte at address N is loaded with the value 6. The beginning of
the loop is marked (at assembly time) by BEGIN,. Memory at PORT is
decremented, then the loop counter N is decremented. Of course, the
CPU updates its status register as N is decremented. Finally, a test for
Z=1 is made; if N hasn't reached zero, execution returns to BEGIN,.
When N reaches zero (after executing PORT DEC, 6 times) execution
continues ahead after UNTIL,. Note that BEGIN, generates no machine
code, but is only an assembly time locator.
Conditional Execution
Paths of execution may be chosen at assembly in a similar fashion as
done in colon-definitions. In this case, the branch is chosen based on a
processor status condition code.
PORT LDA,
0= IF,
<function A> ( executed if PORT is zero )
ENDIF,
( then continue on with rest )
In this example, the accumulator is loaded from PORT. The zero status
is tested if set (Z=1). If so, the code (for zero set) is executed. Whether
the zero status is set or not, execution will resume at ENDIF,.
The conditional branching also allows a specific action for the false
case. Here we see the addition of the ELSE, part.
PORT LDA,
0= IF,
<function A> ( executed if PORT is zero )
ELSE,
<function B> ( executed if PORT is not zero )
ENDIF,
( then continue on with rest )
The test of PORT will select one of two execution paths, before
resuming execution after ENDIF,. The next example increments N based
on bit D7 of PORT:
PORT LDA,
0< IF,
N DEC, ( if D7=1, decrement N )
ELSE,
N INC, ( if D7=0, increment N )
ENDIF,
( continue ahead )
Conditional Nesting
Conditionals may be nested according to the conventions of structured
programming. That is, each conditional sequence begun (IF, BEGIN,)
must be terminated (ENDIF, UNTIL,) before the next earlier conditional
is terminated. An ELSE, must pair with the immediately preceeding IF,.
BEGIN, <code always executed>
CS IF, <code if carry set>
ELSE, <code if carry clear>
ENDIF,
0= NOT UNTIL, ( loop till condition flag is non-zero )
<code that continues onward>
Next is an error that the assembler security will reveal.
BEGIN, PORT LDA,
0= IF, BOT INC,
0= UNTIL, ENDIF,
The UNTIL, will not complete the pending BEGIN, since the immediately
preceeding IF, is not completed. An error trap will occur at UNTIL,
saying "conditionals not paired".
Return of Control, revisited
When concluding a code definition, several common stack manipulations
often are needed. These functions are already In the nucleus, so we
may share their use just by knowing their return points. Each of these
returns control to NEXT.
POP Remove one 16-bit stack value.
POPTWO Remove two 16-bit stack values.
PUSH Add two bytes to the data stack.
PUT Write two bytes to the data stack, over the
present bottom of the stack.
Our next example complements a byte in memory. The bytes' address
is on the stack when INVERT is executed.
CODE INVERT ( a memory byte ) HEX
BOT X) LDA, ( fetch byte addressed by stack )
FF # EOR, ( complement the accumulator )
BOT X) STA, ( replace result in memory )
POP JMP, ( discard pointer from stack )
END-CODE ( and return to next )
A new stack value may result from a code definition. We could program
placing it on the stack by:
CODE ONE ( put 1 on the stack )
DEX,
DEX, ( make room on the data stack )
1 # LDA, ( get a 1 in accumulator )
BOT STA, ( store low byte )
BOT 1+ STA, ( high byte stored from Y since=zero )
NEXT JMP, ( return to FORTH )
END-CODE
A simpler version could use PUSH:
CODE ONE
1 # LDA,
PHA, ( push low byte to machine stack )
TYA, ( clear accumulator, high byte=zero )
PUSH JMP, ( go push to data stack )
END-CODE
The convention for PUSH and PUT is:
1. push the low byte onto the machine stack.
2. leave the high byte in accumulator.
3. jump to PUSH or PUT.
PUSH will place the two bytes as the new bottom of the data stack.
PUT will over-write the present bottom of the stack with the two bytes.
Failure to push exactly one byte on the machine stack will disrupt
execution upon usage!!
Fooling Security
Occasionally we wish to generate unstructured code. To accomplish
this, we can control the assembly time security checks for our purpose.
First, we must note the parameters utilized by the control structures at
assembly time. The notation below is taken from the assembly glossary.
The --- indicates assembly time execution, and separate input stack
values from the output stack values of the words execution.
BEGIN, ==> --- addrB 1
UNTIL, ==> addrB 1 cc ---
IF, ==> cc --- addrI 2
ELSE, ==> addrI 2 --- addrE 2
ENDIF, ==> addrI 2 ---
or addrE 2 ---
The address values indicate the machine location of the corresponding
'B'EGIN, 'I'F, or 'E'LSE,. cc represents the condition code to select the
processor status bit referenced. The digit 1 or 2 is tested for condition
pairing.
The general method of security control is to drop off the check digit an
manipulate the addresses at assembly time. The security against errors
is less, but the programmer is usually paying intense attention to detail
during this effort.
To generate the equivalent of the high level:
BEGIN <a> WHILE <b> REPEAT
We write in assembly:
BEGIN, DROP ( the check digit 1, leaving addrB )
<a>
CS IF, ( leaves addrI and digit 2 )
<b>
ROT ( bring addrB to bottom
JMP, ( to addrB of BEGIN, )
ENDIF, ( complete false forward branch from IF, )
It is essential to write the assembly time stack on paper, and run
through the assembly steps, to be sure that the check digits are
dropped and re-inserted at the correct points and addresses are
correctly available.
NOTE: The ASSEMBLER glossary is included in the main glossary at
the end of this manual.

264
asm.scr Normal file
View File

@ -0,0 +1,264 @@
SCR # 20
0 ( VIC-FORTH 6502 ASSEMBLER )
1 FORTH DEFINITIONS DECIMAL
2
3 VOCABULARY ASSEMBLER IMMEDIATE
4
5 ASSEMBLER DEFINITIONS HEX
6
7 0 VARIABLE MODE
8
9 : MODE! ( N -- ) MODE ! ;
10
11 : ABS! ( -- ) 2 MODE! ;
12
13 : END-CODE
14 CURRENT @ CONTEXT ! ?CSP BASE ! SMUDGE ;
15 -->
SCR # 21
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 : ERR ( N -- ) ABS! ERROR ;
3
4 : NZPAG? ( N -- N F )
5 DUP 0FF00 AND ;
6
7 : # ( N -- N )
8 NZPAG? IF 9 ERR THEN 3 MODE! ;
9
10 : ) ( -- )
11 9 MODE! ;
12
13 : ,X ( N -- N )
14 NZPAG? IF 4 MODE! ELSE 8 MODE! THEN ;
15 -->
SCR # 22
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 : ,Y ( N -- N )
3 NZPAG? IF 5 MODE! ELSE 0A MODE! THEN ;
4
5 : X) ( N -- N )
6 NZPAG? IF 9 ERR THEN 6 MODE! ;
7
8 : )Y ( N -- N )
9 NZPAG? IF 9 ERR THEN 7 MODE! ;
10
11 : .A ( -- )
12 0 MODE! ;
13 -->
14
15
SCR # 23
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 : IMPLIED <BUILDS C, DOES> C@ C, ;
3
4 00 IMPLIED BRK, 18 IMPLIED CLC, D8 IMPLIED CLD,
5 58 IMPLIED CLI, B8 IMPLIED CLV, CA IMPLIED DEX,
6 88 IMPLIED DEY, E8 IMPLIED INX, C8 IMPLIED INY,
7 EA IMPLIED NOP, 48 IMPLIED PHA, 08 IMPLIED PHP,
8 68 IMPLIED PLA, 28 IMPLIED PLP, 40 IMPLIED RTI,
9 60 IMPLIED RTS, 38 IMPLIED SEC, F8 IMPLIED SED,
10 78 IMPLIED SEI, AA IMPLIED TAX, A8 IMPLIED TAY,
11 BA IMPLIED TSX, 8A IMPLIED TXA, 9A IMPLIED TXS,
12 98 IMPLIED TYA,
13
14 : JSR ( ADR -- ) 20 C, , ;
15 -->
SCR # 24
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 0 VARIABLE OPCOD -2 ALLOT
3
4 6DFF , 6965 , 797D , 7161 , FF75 , FFFF ,
5 252D , 3D29 , 2139 , 3531 , FFFF , 0E0A ,
6 FF06 , FFFF , FFFF , FF16 , FFFF , 242C ,
7 FFFF , FFFF , FFFF , FFFF , CDFF , C9C5 ,
8 D9DD , D1C1 , FFD5 , FFFF , E4EC , FFE0 ,
9 FFFF , FFFF , FFFF , CCFF , C0C4 , FFFF ,
10 FFFF , FFFF , FFFF , C6CE , DEFF , FFFF ,
11 D6FF , FFFF , 4DFF , 4945 , 595D , 5141 ,
12 FF55 , FFFF , E6EE , FEFF , FFFF , F6FF ,
13 FFFF , 4CFF , FFFF , FFFF , FFFF , 6CFF ,
14 FFFF , A5AD , BDA9 , A1B9 , B5B1 , FFFF ,
15 -->
SCR # 25
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 AEFF , A2A6 , BEFF , FFFF , FFFF , FFB6 ,
3 FFFF , A5AD , BDA9 , A1B9 , B5B1 , FFFF ,
4 AEFF , A2A6 , BEFF , FFFF , FFFF , FFB6 ,
5 A4AC , BCA0 , FFFF , B4FF , FFFF , 4E4A ,
6 FF46 , FF5E , FFFF , FF56 , FFFF , 050D ,
7 1D09 , 0119 , 1511 , FFFF , 2E2A , FF26 ,
8 FF3E , FFFF , FF36 , 6AFF , 666E , 7EFF ,
9 FFFF , 76FF , FFFF , EDFF , E9E5 , F9FD ,
10 F1E1 , FFF5 , FFFF , 858D , 9DFF , 8199 ,
11 9591 , FFFF , 8EFF , FF86 , FFFF , FFFF ,
12 FFFF , FF96 , 848C , FFFF , FFFF , 94FF ,
13 FFFF ,
14
15 -->
SCR # 26
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 : I@
3 MODE @ SWAP 0B * OPCOD + + C@ ;
4
5 : PUTC
6 I@ C, MODE @ IF
7 MODE @ 1 = MODE @ 4 = MODE @ 5 =
8 MODE @ 9 = OR OR OR
9 IF , ELSE C, THEN THEN
10 ABS! ;
11
12 -->
13
14
15
SCR # 27
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 : IS
3 <BUILDS C, DOES> C@
4 BEGIN DUP I@ 0FF = WHILE
5 MODE @ 8 = IF 4 MODE! ELSE
6 MODE @ 0A = IF 5 MODE ! ELSE
7 MODE @ 2 = IF 1 MODE! ELSE
8 3 ERR THEN
9 THEN
10 THEN
11 REPEAT
12 MODE @ 2 = IF OVER NZPAG?
13 IF 1 MODE! THEN DROP THEN
14 PUTC ;
15 -->
SCR # 28
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 00 IS ADC, 01 IS AND, 02 IS ASL, 03 IS BIT,
3 04 IS CMP, 05 IS CPX, 06 IS CPY, 07 IS DEC,
4 08 IS EOR, 09 IS INC, 0A IS JMP, 0B IS LDA,
5 0C IS LDX, 0D IS LDY, 0E IS LSR, 0F IS ORA,
6 10 IS ROL, 11 IS ROR, 12 IS SBC, 13 IS STA,
7 14 IS STX, 15 IS STY,
8
9 : NOT ( N1 -- N2 )
10 20 XOR ;
11
12 90 CONSTANT CC B0 CONSTANT CS 50 CONSTANT VC
13 70 CONSTANT VS 30 CONSTANT MI 10 CONSTANT PL
14 F0 CONSTANT EQ D0 CONSTANT NE
15 -->
SCR # 29
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 : IF,
3 NOT C, 0 C, HERE ;
4
5 : THEN,
6 HERE OVER - SWAP 1- C! ;
7
8 : ELSE,
9 CLV, VS IF, SWAP THEN, ;
10
11 : BEGIN,
12 HERE ;
13
14 -->
15
SCR # 30
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 : UNTIL,
3 NOT C, HERE 1+ - C, ;
4
5 : AGAIN,
6 JMP, ;
7
8 : WHILE,
9 IF, ;
10
11 : REPEAT,
12 SWAP JMP, THEN, ;
13
14 -->
15
SCR # 31
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 ' LIT 13 + ( A059 ) CONSTANT PUT
3 ' LIT 11 + ( A057 ) CONSTANT PUSH
4 ' SP@ 1 + ( A3B0 ) CONSTANT PUSH0A
5 ' (DO) 0E + ( A170 ) CONSTANT POP
6 ' (DO) 0C + ( A16E ) CONSTANT POPTWO
7 ' AND 9 + ( A377 ) CONSTANT BINARY
8 ' EXECUTE NFA 11 - ( A086 ) CONSTANT SETUP
9 ' LIT 18 + ( A05E ) CONSTANT NEXT
10 84 CONSTANT IP
11 87 CONSTANT W
12 7C CONSTANT N
13 8B CONSTANT XSAVE
14 89 CONSTANT UP
15 -->
SCR # 32
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 : BOT 0 ,X ;
3
4 : SEC 2 ,X ;
5
6 : R 0101 ,X ;
7
8 FORTH DEFINITIONS
9
10 : ;CODE
11 ?CSP COMPILE (;CODE) [COMPILE] [
12 BASE @ HEX !CSP [COMPILE] ASSEMBLER
13 ASSEMBLER ABS! ; IMMEDIATE
14 -->
15
SCR # 33
0 ( VIC-FORTH 6502 ASSEMBLER )
1
2 : CODE
3 ?EXEC [COMPILE] ASSEMBLER
4 ASSEMBLER ABS! CREATE BASE @ HEX
5 !CSP ; IMMEDIATE
6
7 FORTH DECIMAL
8
9
10
11
12
13
14
15