mirror of
https://github.com/ForthHub/FIG-Forth.git
synced 2024-06-04 16:29:34 +00:00
Ragsdale's 6502 assember article, and the Datatronic version.
This commit is contained in:
parent
b58a30b879
commit
61ad01df29
BIN
FD-V03N5.pdf
Normal file
BIN
FD-V03N5.pdf
Normal file
Binary file not shown.
629
asm.doc
Normal file
629
asm.doc
Normal 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
264
asm.scr
Normal 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
|
Loading…
Reference in New Issue
Block a user