(( L.O.V.E. FORTH - RPN Assembler - completed with all op-codes by Homer Seywerd changes/additions also placed in the public domain - support for CODE and ;CODE - virtual vocabulary *** An example provided to illustrate the programming of a traditional RPN assembler in Forth. Serious assembly language programmers should use the LOVE Forth standard assembler interface instead. The standard assembler interface allows the use of Borland's , Microsoft's or Isaacson's assemblers to produce object code for LOVE Forth. *** )) ( ASSEMBLER SOURCE ) : 8* 8 * ; ( N -- N) : 6+ 6 + ; ( N -- N) : OFF ( addr-- ) 0 SWAP ! ; : ON ( addr-- ) 1 SWAP ! ; : 3C, C, C, C, ; VARIABLE <#> VARIABLE VARIABLE VARIABLE VARIABLE VARIABLE VARIABLE VARIABLE VARIABLE VARIABLE VARIABLE INTER VARIABLE : A, CS:C, ; : ! SP@ ! ; : ? @ SP@ - 2- 2/ ; : ?ERR1 ABORT" ADDRESS OUT OF RANGE" ; : ?ERR2 ABORT" IMMEDIATE DATA VALUE NOT ALLOWED" ; : ?ERR3 ABORT" ILLEGAL OPERAND" ; : ?ERR4 ABORT" DESTINATION ADDRESS MISSING" ; : ERR5 1 ABORT" REGISTER MISMATCH" ; : ERR3 1 ?ERR3 ; 0 CONSTANT DIRECT 1 CONSTANT IMMED 2 CONSTANT REG8 3 CONSTANT REG16 4 CONSTANT INDEXED 5 CONSTANT SEGREG HEX : DREG CREATE 3C, DOES> DUP C@ DUP FF = IF DROP ELSE DUP ! ! THEN 1+ DUP C@ ! 1+ C@ ! <#> @ ?ERR2 @ 4 = IF ? 0> IF ! THEN THEN ; : SREG CREATE 3C, DOES> DUP C@ DUP FF = IF DROP ELSE ! THEN 1+ DUP C@ ! 1+ C@ ! @ 4 = IF ? 0> IF ! THEN THEN ; 0 2 0 SREG AL 0 2 0 DREG AL, 0 3 1 SREG AX 0 3 1 DREG AX, 1 2 0 SREG CL 1 2 0 DREG CL, 1 3 1 SREG CX 1 3 1 DREG CX, 2 2 0 SREG DL 2 2 0 DREG DL, 2 3 1 SREG DX 2 3 1 DREG DX, 3 2 0 SREG BL 3 2 0 DREG BL, 3 3 1 SREG BX 3 3 1 DREG BX, 4 2 0 SREG AH 4 2 0 DREG AH, 4 3 1 SREG SP 4 3 1 DREG SP, 5 2 0 SREG CH 5 2 0 DREG CH, 5 3 1 SREG BP 5 3 1 DREG BP, 6 2 0 SREG DH 6 2 0 DREG DH, 6 3 1 SREG SI 6 3 1 DREG SI, 7 2 0 SREG BH 7 2 0 DREG BH, 7 3 1 SREG DI 7 3 1 DREG DI, 0 5 -1 SREG ES 0 5 -1 DREG ES, 1 5 -1 SREG CS 1 5 -1 DREG CS, 2 5 -1 SREG SS 2 5 -1 DREG SS, 3 5 -1 SREG DS 3 5 -1 DREG DS, 0 4 -1 SREG [BX+SI] 0 4 -1 DREG [BX+SI], 0 4 -1 SREG [SI+BX] 0 4 -1 DREG [SI+BX], 1 4 -1 SREG [BX+DI] 1 4 -1 DREG [BX+DI], 1 4 -1 SREG [DI+BX] 1 4 -1 DREG [DI+BX], 2 4 -1 SREG [BP+SI] 2 4 -1 DREG [BP+SI], 2 4 -1 SREG [SI+BP] 2 4 -1 DREG [SI+BP], 3 4 -1 SREG [BP+DI] 3 4 -1 DREG [BP+DI], 3 4 -1 SREG [DI+BP] 3 4 -1 DREG [DI+BP], 4 4 -1 SREG [SI] 4 4 -1 DREG [SI], 5 4 -1 SREG [DI] 5 4 -1 DREG [DI], 6 4 -1 SREG [BP] 6 4 -1 DREG [BP], 7 4 -1 SREG [BX] 7 4 -1 DREG [BX], : ?W @ ; : ?TD @ ; : ?TS @ ; : ?RD @ ; : ?RS @ ; : ?OD @ ; : ?OS @ ; : +D @ 2* + ; : +W ?W + ; : +RD ?RD + ; : +RS ?RS + ; : MOD1 3F AND 040 OR ; : MOD2 3F AND 080 OR ; : MOD3 3F AND 0C0 OR ; : RESET <#> OFF OFF OFF OFF OFF OFF OFF OFF OFF OFF ! INTER OFF ; : DSET ?TS INDEXED = IF ON THEN ; : Dt ON ; : BIG? ABS -80 AND 0= NOT ; : +S ( instr,word--instr) FF80 AND ?DUP IF FF80 = ELSE -1 THEN ?W 0= NOT AND IF 2+ OFF THEN ; : ASMHERE DPCODE @ ; ( for revectoring) : OFFSET8, ASMHERE 1+ - DUP ABS 07F > ?ERR1 A, ; : OFFSET16, ASMHERE 2+ - CS:, ; : DISP, @ IF ?OS ELSE ?OD THEN DUP IF DUP ABS 07F > IF SWAP MOD2 A, CS:, ELSE SWAP MOD1 A, A, THEN ELSE DROP DUP 7 AND 6 = IF MOD1 A, 0 A, ELSE A, THEN THEN ; : 1MI CREATE C, DOES> C@ A, RESET ; : 2MI CREATE C, DOES> C@ A, OFFSET8, RESET ; : 3MI CREATE C, DOES> C@ +W A, RESET ; : 4MI CREATE 3C, C, ( PUSH POP) DOES> ?TS CASE DIRECT OF 2+ DUP C@ A, 1+ C@ 6 + A, CS:, ENDOF REG16 OF 1+ C@ +RS A, ENDOF INDEXED OF 2+ DUP C@ A, DSET 1+ C@ +RS DISP, ENDOF SEGREG OF C@ ?RS 8* + A, ENDOF ERR3 ENDCASE RESET ; : 6MI CREATE C, C, DOES> DUP C@ 2 AND IF ?TD ?TS ELSE ?TS ?TD THEN REG16 = IF ON ELSE OFF THEN REG16 = IF ( dx) 1+ C@ +W A, ELSE ( dir) C@ +W A, A, THEN RESET ; : 5MI CREATE 3C, ( JMP CALL) DOES> ?TS CASE DIRECT OF INTER @ IF 2+ C@ A, CS:, CS:, ( intersegment) ELSE C@ ( intraseg) SWAP ASMHERE - 2- SWAP ( disp,op--) DDUP 1 AND SWAP BIG? NOT AND IF 2+ A, A, ( short jmp) ELSE A, 1- CS:, ( jm/cl) THEN THEN ENDOF REG16 OF INTER @ ?ERR3 FF A, 1+ C@ +RS MOD3 A, ENDOF INDEXED OF DSET FF A, 1+ C@ +RS INTER @ IF 8 + THEN DISP, ENDOF ERR3 ENDCASE RESET ; : 7MI CREATE 3C, C, C, DOES> ?TS IMMED = IF 1+ DUP 2+ C@ 0= ?ERR2 ?TD REG8 = ?TD REG16 = OR IF ?RD IF DUP 1+ C@ +W OVER 3 + C@ IF 2 PICK +S THEN A, C@ MOD3 +RD A, ELSE 2+ C@ +W A, ( AL/AX) THEN ELSE DUP 1+ C@ +W OVER 3 + C@ IF 2 PICK +S THEN A, ?TD CASE DIRECT OF C@ 6+ A, SWAP CS:, ENDOF INDEXED OF DSET C@ +RD DISP, ENDOF ERR3 ENDCASE THEN ?W IF CS:, ELSE A, THEN ELSE DSET ?TS DIRECT = IF Dt THEN C@ +D +W A, ?TD CASE REG8 OF ?TS CASE ( REG8 is dest) DIRECT OF ?RD 8* 6+ A, CS:, ENDOF REG8 OF ?RS 8* +RD MOD3 A, ENDOF INDEXED OF ?RD 8* +RS DISP, ENDOF ERR3 ENDCASE ENDOF REG16 OF ?TS CASE DIRECT OF ?RD 8* 6+ A, CS:, ENDOF REG16 OF ?RS 8* +RD MOD3 A, ENDOF INDEXED OF ?RD 8* +RS DISP, ENDOF ERR3 ENDCASE ENDOF INDEXED OF ?TS CASE REG8 OF ?RS 8* +RD DISP, ENDOF REG16 OF ?RS 8* +RD DISP, ENDOF ERR3 ENDCASE ENDOF DIRECT OF ?TS CASE REG8 OF ?RS 8* 6+ A, CS:, ENDOF REG16 OF ?RS 8* 6+ A, CS:, ENDOF ERR3 ENDCASE ENDOF ERR3 ENDCASE THEN RESET ; : 8MI CREATE C, C, DOES> DUP 1+ C@ +W A, ?TS CASE DIRECT OF C@ 6+ A, CS:, ENDOF REG8 OF C@ +RS MOD3 A, ENDOF REG16 OF C@ +RS MOD3 A, ENDOF INDEXED OF DSET C@ +RS DISP, ENDOF ERR3 ENDCASE RESET ; : 9MI CREATE C, C, DOES> DUP 1+ C@ @ + ?TS 1 > IF 2+ ( CL reg) ELSE ROT DROP ( 1) THEN A, ?TD CASE DIRECT OF C@ 6+ A, CS:, ENDOF REG8 OF C@ MOD3 +RD A, ENDOF REG16 OF C@ MOD3 +RD A, ENDOF INDEXED OF DSET C@ +RD DISP, ENDOF ERR3 ENDCASE RESET ; : 10MI CREATE C, C, ( AAD AAM ) DOES> DUP C@ SWAP 1+ C@ A, A, RESET ; : 11MI CREATE C, C, ( INC DEC ) DOES> ?TS CASE DIRECT OF FE +W A, 1+ C@ 6+ A, CS:, ENDOF REG8 OF 0FE A, 1+ C@ MOD3 +RS A, ENDOF REG16 OF C@ +RS A, ENDOF INDEXED OF DSET 0FE +W A, 1+ C@ +RS DISP, ENDOF ERR3 ENDCASE RESET ; : MOV DSET ?TD CASE DIRECT OF ?TS CASE REG8 OF ?RS IF 088 A, ?RS 8* 6+ A, CS:, ELSE 0A2 +W A, CS:, THEN ENDOF REG16 OF ?RS IF 089 A, ?RS 8* 6+ A, CS:, ELSE 0A2 +W A, CS:, THEN ENDOF SEGREG OF 08C A, ?RS 8* 6+ A, CS:, ENDOF IMMED OF C6 +W A, 6 A, SWAP CS:, ?W IF CS:, ELSE A, THEN ENDOF ERR3 ENDCASE ENDOF REG8 OF ?TS CASE DIRECT OF ?RD IF 08A A, ?RD 8* 6+ A, CS:, ELSE 0A0 +W A, CS:, THEN ENDOF IMMED OF 0B0 +RD A, A, ENDOF REG8 OF Dt 88 +D A, ?RD 8* +RS MOD3 A, ENDOF REG16 OF ERR5 ENDOF INDEXED OF 88 +D +W A, ?RD 8* +RS DISP, ENDOF ERR3 ENDCASE ENDOF REG16 OF ?TS CASE DIRECT OF ?RD IF 08B A, ?RD 8* 6+ A, CS:, ELSE 0A0 +W A, CS:, THEN ENDOF IMMED OF 0B8 +RD A, CS:, ENDOF REG16 OF Dt 88 +W +D A, ?RD 8* +RS MOD3 A, ENDOF INDEXED OF 088 +D +W A, ?RD 8* +RS DISP, ENDOF SEGREG OF 08C A, ?RS 8* +RD MOD3 A, ENDOF ERR3 ENDCASE ENDOF INDEXED OF ?TS CASE IMMED OF 0C6 +W A, ?RD DISP, ?W IF CS:, ELSE A, THEN ENDOF REG8 OF 088 +D +W A, ?RS 8* +RD DISP, ENDOF REG16 OF 088 +D +W A, ?RS 8* +RD DISP, ENDOF SEGREG OF 08C A, ?RS 8* +RD DISP, ENDOF ERR3 ENDCASE ENDOF SEGREG OF ?TS CASE DIRECT OF 08E A, ?RD 8* 6+ A, CS:, ENDOF REG16 OF 08E A, ?RD 8* +RS MOD3 A, ENDOF INDEXED OF 08E A, ?RD 8* +RS DISP, ENDOF ERR3 ENDCASE ENDOF ERR3 ENDCASE RESET ; : XCHG DSET ?TD CASE DIRECT OF ?TS REG16 = IF 90 +RS A, ELSE ERR3 THEN ENDOF REG8 OF 86 +W A, ?TS CASE DIRECT OF ?RD 8* 6+ A, CS:, ENDOF REG8 OF ?RD 8* +RS MOD3 A, ENDOF INDEXED OF ?RD 8* +RS DISP, ENDOF ERR3 ENDCASE ENDOF REG16 OF 86 +W A, ?TS CASE DIRECT OF ?RD 8* 6+ A, CS:, ENDOF REG16 OF ?RD 8* +RS MOD3 A, ENDOF INDEXED OF ?RD 8* +RS DISP, ENDOF ERR3 ENDCASE ENDOF ERR3 ENDCASE RESET ; : TEST ?TS IMMED = IF ?TD DUP REG8 = SWAP REG16 = OR IF ?RD IF F6 +W A, ?RD MOD3 A, ELSE A8 +W A, THEN ELSE F6 +W A, ?TD CASE DIRECT OF 6 A, SWAP CS:, ENDOF INDEXED OF ?RD DISP, ENDOF ERR3 ENDCASE THEN ?W IF CS:, ELSE A, THEN ELSE ?TD REG8 < ?TD REG16 > OR ?ERR3 84 +W A, DSET ?TS CASE DIRECT OF ?RD 8* 6+ A, CS:, ENDOF REG8 OF ?RD 8* +RS MOD3 A, ENDOF REG16 OF ?RD 8* +RS MOD3 A, ENDOF INDEXED OF ?RD 8* +RS DISP, ENDOF ERR3 ENDCASE THEN RESET ; : INT CD A, A, RESET ; : 16MI CREATE C, ( RET ) DOES> C@ DUP INTER @ IF 8 + THEN A, 1 AND 0= IF CS:, THEN RESET ; 37 1MI AAA 1 3C 80 38 38 7MI CMP ( INT ) D5 0A 10MI AAD A6 3MI CMPS CE 1MI INTO D4 0A 10MI AAM 99 1MI CWD CF 1MI IRET 3F 1MI AAS 27 1MI DAA 77 2MI JA 1 14 80 10 10 7MI ADC 2F 1MI DAS 73 2MI JAE 1 04 80 00 00 7MI ADD 08 48 11MI DEC 72 2MI JB 0 24 80 20 20 7MI AND F6 30 8MI DIV 76 2MI JBE 9A 10 E8 5MI CALL ( ESC ) E3 2MI JCXZ 98 1MI CBW F4 1MI HLT 74 2MI JE F8 1MI CLC F6 38 8MI IDIV 7F 2MI JG FC 1MI CLD F6 28 8MI IMUL 7D 2MI JGE FA 1MI CLI EC E4 6MI IN 7C 2MI JL F5 1MI CMC 00 40 11MI INC 7E 2MI JLE EA 20 E9 5MI JMP 75 2MI JNZ ( E2 2MI LOOP ) 76 2MI JNA 70 2MI JO E1 2MI LOOPE 72 2MI JNAE 7A 2MI JP E0 2MI LOOPNE 73 2MI JNB 7A 2MI JPE E0 2MI LOOPNZ 77 2MI JNBE 7B 2MI JPO E1 2MI LOOPZ 75 2MI JNE 78 2MI JS ( MOV ) 7E 2MI JNG 74 2MI JZ A4 3MI MOVS 7C 2MI JNGE 9F 1MI LAHF F6 20 8MI MUL 7D 2MI JNL 0 0 0 0 C2 7MI LDS F6 18 8MI NEG 7F 2MI JNLE 0 0 0 0 8A 7MI LEA 90 1MI NOP 71 2MI JNO 0 0 0 0 C1 7MI LES F6 10 8MI NOT 7B 2MI JNP F0 1MI LOCK 0 0C 80 08 08 7MI OR 79 2MI JNS AC 3MI LODS EE E6 6MI OUT 0 8F 58 07 4MI POP D0 08 9MI ROR 1 2C 80 28 28 7MI SUB 9D 1MI POPF 9E 1MI SAHF ( TEST ) 30 FF 50 06 4MI PUSH D0 38 9MI SAR 9B 1MI WAIT 9C 1MI PUSHF 1 1C 80 18 18 7MI SBB ( XCHG ) D0 10 9MI RCL AE 3MI SCAS D7 1MI XLAT D0 18 9MI RCR ( SEG ) 0 34 80 30 30 7MI XOR F3 1MI REP D0 20 9MI SAL C2 16MI +RET F3 1MI REPE D0 20 9MI SHL F2 1MI REPNE D0 28 9MI SHR F2 1MI REPNZ F9 1MI STC F3 1MI REPZ FD 1MI STD C3 16MI RET FB 1MI STI D0 00 9MI ROL AA 3MI STOS : BYTE OFF OFF ; : WORD ON ON ; : # <#> ON ON ; : , DEPTH 1 < ?ERR4 OFF ; : ES: 26 A, ; : CS: 2E A, ; : SS: 36 A, ; : DS: 3E A, ; : FAR INTER ON ; ( AGAIN and REPEAT are two byte relative jumps ) ( All of the other logical tests are one byte relative jumps ) ( e.g., max decimal one byte jump is +/- 127 bytes ) : ERR1? - DUP ABS 7F > ?ERR1 ; ( VERY IMPORTANT NOTE: MUST have count in CX REG !!! ) ( CX max can be 1 THRU 65535 <$FFFF> ) : DO DPCODE @ RESET ; ( Will set CX=1 to exit upon reaching LOOP ) ( E.G., A, B CMP IF= LEAVE THEN ) : LEAVE B9 A, 1 CS:, RESET ; ( Will first decrement CX and then JMP to DO if CX is not 0 ) : LOOP E2 A, DPCODE @ 1+ ERR1? A, RESET ; ( The following are used after: A, B CMP ) ( A or B can be any MEM, REG, or # NO.; byte or word ) : IF= 75 A, DPCODE @ 0 A, RESET ; ( IF A = B ) : IF< 7D A, DPCODE @ 0 A, RESET ; ( IF A < B ) : IFU< 73 A, DPCODE @ 0 A, RESET ; ( IF A U< B ) : IF0< 79 A, DPCODE @ 0 A, RESET ; ( IF SF = 1 ) : IF-OVERFLOW 71 A, DPCODE @ 0 A, RESET ; ( If overflow flag 1) : ELSE EB A, DUP DPCODE @ SWAP ERR1? SWAP CS:C! DPCODE @ 0 A, RESET ; : THEN DUP 1+ DPCODE @ SWAP ERR1? SWAP CS:C! RESET ; : BEGIN DPCODE @ RESET ; : AGAIN E9 A, DPCODE @ 2+ - CS:, RESET ; ( Use after: A, B CMP e.g., repeat UNTIL A=B ) : UNTIL= 75 A, DPCODE @ 1+ ERR1? A, RESET ; : UNTIL0< 79 A, DPCODE @ 1+ ERR1? A, RESET ; : UNTIL< 7D A, DPCODE @ 1+ ERR1? A, RESET ; : UNTILU< 73 A, DPCODE @ 1+ ERR1? A, RESET ; ( next word can be followed by "not" ) : UNTIL-OVERFLOW 71 A, DPCODE @ 1+ ERR1? A, RESET ; ( ovrflo = 1 ) : WHILE= IF= ; ( example: BEGIN...A, B CMP WHILE=...REPEAT ) : WHILE< IF< ; : WHILE0< IF0< ; : WHILEU< IFU< ; : WHILE-OVERFLOW IF-OVERFLOW ; ( can be followed by "not" ) ( Use "non" to invert logical operators, e.g., IF= non ) ( or UNTIL< non means repeat until A is not less than B ) ( Lower case is used to avoid conflict with opcode word NOT ) : non DPCODE @ 2- DUP CS:C@ 1- SWAP CS:C! ; : REPEAT SWAP AGAIN THEN ; ( Assembler TEST Logical Operators ) ( Use after A, B TEST since logic is reversed ) : IFTEST IF= non ; : UNTILTEST UNTIL= non ; : WHILETEST WHILE= non ; : IFNOTEST IF= ; : UNTILNOTEST UNTIL= ; : WHILENOTEST WHILE= ; ( Do not compile two not's together. Do not compile IFTEST non) ( as two separate words.) ( WORD LODS BX, AX MOV WORD [BX] JMP ) : NEXT-JMP AD A, 8B A, D8 A, FF A, 27 A, RESET ; : APUSH-JMP 50 A, NEXT-JMP ; DECIMAL : C; RESET ?CSP SMUDGE VDROP ; : END-CODE C; ; : LABEL ( define assembler label ( -- ) ( this takes advantage of L.O.V.E Forth characteristic that: CONSTANT does not add anything to the code segment ) SMUDGE ( previous code word ) CS:HERE CONSTANT SMUDGE ( for C; to unsmudge) ; : ;CODE ?CSP COMPILE <;CODE> DPCODE @ TS:, [COMPILE] [ ALSO ASSEMBLER RESET ; IMMEDIATE : CODE !CSP CREATE: DPCODE @ TS:HERE TS:BODY> TS:! ALSO ASSEMBLER RESET SMUDGE ;