I'm surprised nobody noticed something about
SKIP in my example of SMC.
This:
Code:
// SKIP
CODE SKIP ( A1 L1 C -- A2 L2 )
0= NOT # LDA, // LOAD OPCODE F0 ( BEQ )
' SCAN @ 3 + @ STA,
' SCAN @ 5 + JMP, END-CODE
can be streamlined to this:
Code:
CODE SKIP ( A1 L1 C -- A2 L2 )
0= NOT # LDA, // LOAD OPCODE F0 ( BEQ )
' SCAN @ 2+ JMP, END-CODE
whartung wrote:
As a rule, I tend to not like code that doesn't do what the source code says it does. It's not a universal truth, it's just a guideline I tend to favor.
In my example,
SCAN does exactly what the source code says it does. Storing the type of branch in the code simply sets it back to doing what the source code says because
SKIP changes it slightly and it's not too difficult to see what
SKIP does.
Imagine if you will a hypothetical extension to the 6510 processor ( the one in the C64 ). An extra flag is added, the branch invert flag. When this flag is cleared all branches work normally. When this flag is set all branches operate in the opposite sense. With such a processor,
SCAN would clear the branch invert flag.
SKIP would set it then jump into
SCAN to the instruction after the clear branch invert instruction. This version of
SCAN and
SKIP would not be SMC. My version of
SCAN and
SKIP uses SMC to accomplish the same code reuse on the 6510 that would be possible on the hypothetical extended 6510.
I'm not thinking about SMC that twists the code beyond all recognition, rather to allow better code reuse as with
SCAN and
SKIP or to work around limitations such as what
NEXT does to work around the 6502 ( and 6510 ) not having a double indirect jump.
I realize that my version, with SMC, would not work in ROM. Here is a version I might use in a ROM based Forth for the 65C02 or even a cartridge for the C64 to keep the size down. It depends on how badly I would need to save memory ( to fit the system in ROM ).
Code:
// SCAN
HEX
CODE SCAN ( AD1 N1 C -- AD2 N2 )
DEY,
N 6 + STY, 0 # LDY, // SETUP NEEDS Y TO BE ZERO
3 # LDA, SETUP JSR,
AHEAD,
BEGIN,
N 4 + INC,
0= IF, N 5 + INC, THEN,
N 2+ LDA,
0= IF, N 3 + DEC, THEN,
N 2+ DEC,
CS-SWAP THEN,
N 2+ LDA, N 3 + ORA,
0= NOT WHILE,
N 4 + )Y LDA, N EOR, .A ASL,
0= NOT IF, 0FF # LDA, THEN, // BE SURE RESULT IN ACCUMULATOR IS 0 OR FF
N 6 + EOR, // SCAN INVERTS THE RESULT, SKIP DOES NOT
0= NOT UNTIL, // SO THE TEST IS REVERSED
THEN,
DEX, DEX,
N 4 + LDA, 0 ,X STA,
N 5 + LDA, 1 ,X STA,
N 2+ LDA, PHA,
N 3 + LDA,
PUSH JMP, END-CODE
CODE SKIP ( A1 L1 C -- A2 L2 )
// SKIP HAS NO PARAMETER FIELD. IT'S CFA POINTS ONE BYTE INTO SCAN
// AVOIDING THE DEY, INSTRUCTION
-2 ALLOT
' SCAN @ 1+ , END-CODE
Notice the extra clock cycles added to the loop.
I thought of another version to keep the size down that doesn't use SMC and shouldn't incur as many extra cycles in the loop as this one does, but the SMC version reads easier. This other version wasn't pretty. Trust me.
[Edit: I forgot to remove the screen number ( and comment line ) in this example the first time. Oops. Yes, my Forth system has source in blocks. ]