Close

Macros

A project log for A SUBLEQ CPU

Yet Another Minimalistic One Instruction CPU SUBLEQ means SUBtraction jump on Less than or EQual to zero.

agp.cooperagp.cooper 06/17/2017 at 00:570 Comments

Macros Part 1

Coding directly in SUBLEQ is necessary initially but is very tedious. Macros are the way to go.

There are a couple of software packages that are suitable:

and others.

I did not like the syntax of M4 and NASM. Here is a NASM example:

/* macro TRUE_BRANCH: correctly handles underflow */ 
%define TRUE_BRANCH(b,GEZ,LTZ) \
          T T ? \
          b T TEST_GEZ \
          T T LTZ \
TEST_GEZ: P T GEZ \
          P T LTZ

// Examples:
TRUE_BRANCH(x,xGEZ,xLTZ)
Which produces:
%line 1+1 macros.nasm
/* macro TRUE_BRANCH: correctly handles underflow */
%line 8+1 macros.nasm

// Examples:
T T ? x T TEST_GEZ T T xLTZ TEST_GEZ: P T xGEZ P T xLTZ

Okay lets try the %macro:

/* macro TRUE_BRANCH: correctly handles underflow */ 
%macro TRUE_BRANCH 3
          T T ? 
          %1 T TEST_GEZ 
          T T %3 
TEST_GEZ: T T %2 
          T T %3
%endmacro

// Examples:
TRUE_BRANCH(x,xGEZ,xLTZ)

Which produces:

%line 1+1 macros.nasm
/* macro TRUE_BRANCH: correctly handles underflow */
%line 9+1 macros.nasm

// Examples:
 T T ?
%line 11+0 macros.nasm
 x T TEST_GEZ
 T T xLTZ
TEST_GEZ: T T xGEZ
 T T xLTZ
Not pretty and not easy to suppress the %Line comments (i.e. .nolist ?).

The C Pre-Processer

The C pre-processors cpp, mcpp and gpp are very similar, but gpp stands out as it honours new lines in its standard mode and emits less commentary.

Here is gpp:

/* macro TRUE_BRANCH: correctly handles underflow */ 
#define TRUE_BRANCH(b,GEZ,LTZ) \
          T T ? \
          b T TEST_GEZ \
          T T LTZ \
TEST_GEZ: P T GEZ \
          P T LTZ

For:

TRUE_BRANCH(x,xGEZ,xLTZ)

Which gets expanded to this:

          T T ? 
          x T TEST_GEZ 
          T T xLTZ 
TEST_GEZ: P T xGEZ 
          P T xLTZ
The problem with cpp and mcpp is that they do not honour the newline, this is what you get:
#line 1 "C:/AlanX/subleq/Macros/macros.inc"
#line 10 "C:/AlanX/subleq/Macros/macros.inc"
T T ? x T TEST_GEZ T T xLTZ TEST_GEZ: P T xGEZ P T xLTZ

I have to add a new line marker ("nl") and then use an AWK script to convert it to a real new line:

/* macro TRUE_BRANCH: correctly handles underflow */ 
#define TRUE_BRANCH(b,GEZ,LTZ) nl\
          T T ? nl\
          b T TEST_GEZ nl\
          T T LTZ nl\
TEST_GEZ: P T GEZ nl\
          P T LTZ

// Examples:
TRUE_BRANCH(x,xGEZ,xLTZ)
Now I get:
#line 1 "C:/AlanX/subleq/Macros/macros.inc"
#line 10 "C:/AlanX/subleq/Macros/macros.inc"

 T T ? 
 x T TEST_GEZ 
 T T xLTZ 
TEST_GEZ: P T xGEZ 
 P T xLTZ
Which is pretty close.

Here is the AWK script:

awk "{ gsub(/nl/,\"\n\"); print }" macros.tmp >macros.sub

It was an absolute headache working out how to get AWK to work with Windows for this one liner!

Macros Part 2

The purpose for this log is to record my macros:

( SUBLEQ MACROS )
( System defaults )
#define system() \
  ( DEFAULTS ) \
  @INPUT -1 \
  @OUTPUT -2 \
  @HALT 0 \
  @Z -9 \
  @T -10 \
  @P -11 \
  @N -12 \
  @SP -13 \
Z Z ? ; Z=0 \ T T ? ; T=0 \ -1 P ? ; P=1 \ 1 N ? ; N=-1 \ 32 SP ? ; SP=-32 \ ( FREE VARIABLES ) \ @a -14 \ @b -15 \ @c -16 \ @d -17 \ @e -18 \ @f -19 \ @g -20 \
( MACRO VARIABLES ) \ @t -21 ; Macro temporary variable \ @w -22 ; Word width test \ @TOP_OF_FREE_RAM -33 \ ( POINTER TO POINTER COPY EQUATES ) \ @PPC -32 \ @NDATA -23 \ @SRC -32 \ @DST -28 \ @RTN -24
( Load Pointer to Pointer Copy to RAM ) #define LoadPPC() \ ( POINTER TO POINTER COPY EQUATES ) \ @NDATA -23 \ @SRC -32 \ @DST -28 \ @RTN -24 \ ( POINTER TO POINTER COPY ROUTINE LINES ) \ @PPC_L1 -32 ; SRC \ @PPC_L2 -31 ; NDATA \ @PPC_L3 -30 ; ? (=PPC_L4) \ @PPC_L4 -29 ; NDATA \ @PPC_L5 -28 ; DST \ @PPC_L6 -27 ; ? (=PPC_L7) \ @PPC_L7 -26 ; T \ @PPC_L8 -25 ; T \ @PPC_L9 -24 ; RTN \ ( COPY ROM CODE TO RAM - THE HARD WAY ) \ PPC_L1 PPC_L1 ? \ T T ? \ PPC_D1 T ? \ T PPC_L1 ? \ PPC_L2 PPC_L2 ? \ T T ? \ PPC_D2 T ? \ T PPC_L2 ? \ PPC_L3 PPC_L3 ? \ T T ? \ PPC_D3 T ? \ T PPC_L3 ? \ PPC_L4 PPC_L4 ? \ T T ? \ PPC_D4 T ? \ T PPC_L4 ? \ PPC_L5 PPC_L5 ? \ T T ? \ PPC_D5 T ? \ T PPC_L5 ? \ PPC_L6 PPC_L6 ? \ T T ? \ PPC_D6 T ? \ T PPC_L6 ? \ PPC_L7 PPC_L7 ? \ T T ? \ PPC_D7 T ? \ T PPC_L7 ? \ PPC_L8 PPC_L8 ? \ T T ? \ PPC_D8 T ? \ T PPC_L8 ? \ PPC_L9 PPC_L9 ? \ T T ? \ PPC_D9 T ? \ T PPC_L9 ? \
T T JMP ; Jump over data section \ ( POINTER TO POINTER COPY CODE TO BE MOVED ) \ .PPC_D1 SRC \ .PPC_D2 NDATA \ .PPC_D3 PPC_L4 \ .PPC_D4 NDATA \ .PPC_D5 DST \ .PPC_D6 PPC_L7 \ .PPC_D7 T \ .PPC_D8 T \ .PPC_D9 RTN \ JMP: T T ? ( Macro jmp: jump to address c ) #define jmp(c) \ T T c ; T=0 ( Macro sub: b=b-a ) #define sub(a,b) \ a b ? ; b=b-a ( Macro add: b=b+a ) #define add(a,b) \ T T ? ; T=0 \ a T ? ; T=-a \ T b ? ; b=b+a ( Macro copy: copy or move a to b ) #define copy(a,b) \ T T ? ; T=0 \ b b ? ; b=0 \ a T ? ; T=-a \ T b ? ; b=a ( Macro not: b=~a ) #define not(a,b) \ T T ? ; T=0 \ b b ? ; b=0 \ a b ? ; b=-a \ P b ? ; b=~a ( ~a=-a-1 )

( Macro shl: a=a+a ) #define shl(a) \ T T ? ; T=0 \ a T ? ; T=-a \ T a ? ; a=a+a ( Macro newsub: c=b-a ) #define newsub(a,b,c) \ T T ? ; T=0 \ b T ? ; T=-b \ c c ? ; c=0 \ T c ? ; c=b \ a c ? ; c=b-a

( Macro newadd: c=b+a ) #define newadd(a,b,c) \ T T ? ; T=0 \ b T ? ; T=-b \ c c ? ; c=0 \ T c ? ; c=b \ T T ? ; T=0 \ a T : ; T=-a \ T c ? ; c=b+a
( Macro inc: a=a+1 ) #define inc(a) \ N a ? ; a++, N=-1 ( Macro dec: a=a-1 ) #define inc(a) \ P a ? ; a++, P=-1
( Macro JGEZ: jump to c if b >= 0 ) #define JGEZ(b,c) \ T T ? ; T=0 \ b T c ; T=-b ( Macro JLEZ: jump to c if b <= 0 ) #define JLEZ(b,c) \ Z b c ; b=b ( Macro branch ) #define branch(b,GTZ,EQZ,LTZ) \ Z b JMP \ Z Z GTZ \ JMP: b Z EQZ \ Z Z LTZ ( Macro true_test: correctly handles underflow ) #define true_test(b,GEZ,LTZ,MIN) \ T T ? \ b T TEST_GEZ \ T T LTZ \ TEST_GEZ: T T GEZ \ T T MIN ( Macro loop: for a=a to b, will execute at least once ) #define loop(a,b,LOOP) \ N a ? ; a++, N=-1 \

T T ? ; until a>b \ a Z ? \ Z T ? \ Z Z ? \ b T LOOP ; goto LOOP ( Macro xor: c=b^a ) #define xor(a,b,c) \ c c ? ; c=0 \ w w ? ; w=0, used to test word width \ loop: \ ( c=c<<1 ) \ shl(c) \ ( TEST 1 a<0 AND b>=0 ) \ ; Test a<0 \ true_test(a,TEST3,TEST2,TEST2) \ TEST2: \ ; Test b>=0 \ true_test(b,SETBIT,TEST3,TEST3) \ TEST3: \ ( a>=0 AND b<0 ) \ ; Test b<0 \ true_test(b,NEXT,TEST4,TEST4) \ TEST4: \ ; Test a>=0 \ true_test(a,SETBIT,NEXT,NEXT) \ SETBIT: \ N c ? ; c++ \ NEXT: \ shl(a) ; a=a<<1 \ shl(b) ; b=b<<1 \ ( loop: w=w<<1+1 - avoid underflow bug! ) \ shl(w) \ add(N,w) \ w T loop ( Macro and: c=b&a ) #define and(a,b,c) \ c c ? ; c=0 \ w w ? ; w=0, used to test word width \ loop: \ ( c=c<<1 ) \ shl(c) \ ( TEST 1 a<0 AND b<0 ) \ ; Test a<0 \ true_test(a,NEXT,TEST2,TEST2) \ TEST2: \ ; Test b<0 \ true_test(b,SETBIT,NEXT,NEXT) \ SETBIT: \ N c ? ; c++ \ NEXT: \ shl(a) ; a=a<<1 \ shl(b) ; b=b<<1 \ ( loop: w=w<<1+1 - avoid underflow bug! ) \ shl(w) \ add(N,w) \ w T loop ( Macro or: c=b|a ) #define or(a,b,c) \ c c ? ; c=0 \ w w ? ; w=0, used to test word width \ loop: \ ( c=c<<1 ) \ shl(c) \ ( TEST 1 A<0 OR B<0 ) \ ; Test b<0 \ true_test(a,TEST2,SETBIT,SETBIT) \ TEST2: \ ; Test b<0 \ true_test(b,NEXT,SETBIT,SETBIT) \ SETBIT: \ N c ? ; c++ \ NEXT: \ shl(a) ; a=a<<1 \ shl(b) ; b=b<<1 \ ( loop: w=w<<1+1 - avoid underflow bug! ) \ shl(w) \ add(N,w) \ w T loop ( Macro nor: c=~(b|a) ) #define nor(a,b,c) \ or(a,b,t) \ not(c,t) ( Macro nand: c=~(b&a) ) #define nand(a,b,c) \ and(a,b,t) \ not(t,c) ( Macro ppc: pointer to pointer copy, [a] -> c -> [b] ) #define ppc(a,b,c) \ copy(a,SRC) \ copy(b,DST) \ copy(rtn,RTN) \ jmp(PPC_L1) \ NDATA c rtn:?

( Macro PtrRead: pointer read, [a] -> c ) #define pr(a,c) \ copy(a,SRC) \ copy(a,DST) \ copy(rtn,RTN) \ jmp(PPC_L1) \ NDATA c rtn:?

( Macro PtrWrite: pointer write, c -> [a] ) #define PtrWrite(a,c) \ NDATA NDATA ? \ c NDATA ? \ copy(a,DST) \ copy(rtn,RTN) \ jmp(PPC_L4) \ T T rtn:?

( Macro push: SP--, [SP]=a ) #define push(a) \ dec(SP) \ PtrWrite(SP,a) ( Macro pop: a=[SP]; SP++ ) #define push(a) \ PtrRead(SP,a) \ inc(SP)

TBC ...

AlanX


Discussions