\ BBL Post-Fix assembler This assembler is almost a direct steal of the public domain assembler written by Michael Perry. You may NOT sell this assembler. If you pass it on, you must leave the credit to Michael Perry intact! Michael Perry 1125 Bancroft Way Berkeley California 94702 (415) 644-3421 This variant of it was written by Roedy Green Canadian Mind Products #11 - 3856 Sunset Street Burnaby BC Canada V5G 1T3 \ Load screen 2 999 THRU \ General Notes ;S To create and assembler language definition, use the defining word CODE. It must be terminated with either END-CODE or ;C How the assembler operates is a very interesting example of the power of CREATE DOES> Basically the instructions are categorized and a defining word is created for each category. When the nmemonic for the instruction is interpreted, it compiles itself. Here is the code for an equivalent to SWAP CODE MY-SWAP ( a b -- b a ) DX POP AX POP BX PUSH CX PUSH CX DX MOV BX AX MOV NEXT END-CODE \ General Notes ;S Before you go writing your own code words, you had better have a good look at BBLASM.DOC where the register conventions are explained. Briefly CX:BX is top element of the stack. DS:SI is the interpretive pointer. DS: rarely points anywhere useful to you. Use CS: or ES: SS:SP is stack pointer SS:BP is return stack pointer DI is kept zero. The system will crash if it isn't. DX:AX are scratch regs ES: is a scratch reg Direction bit in flags register for strings is kept 0. \ sample code ;S AX BX MOV means move AX into BX 5 # AX ADD means add immediate data 5 to reg AX 0 [BX] AX SUB means subtract value in memory location pointed to by BX from AX AX 9 [BX+SI] SUB means subtract AX from the value in the memory location 9 past the sum of BX and SI CS: XXX #) BX AND means AND BX with the contents of memory location CS:xxx leaving the result in BX # 1 AX SHL means shift register AX left one bit CL AX SHL means shift register AX left by count in CL 7 # AX TEST means test AX with the immediate mask 7. BYTE 7 # 0 [BX] TEST means test contents of memory pointed to by BX with immediate data 7. \ sample code ;S XXX [BX] AX LEA get address of XXX+BX into AX ( only offset portion LSW of XXX is ) ( used. ) XXX MSW # DX MOV if XXX is a VARIABLE get SEG part of its pfa into DX XXX LSW # AX MOV if XXX is a VARIABLE get OFFSET part of its pfa into AX ES: XXX #) AX MOV if XXX is a VARIABLE whose pfa SEG is in ES this will get the LSW of its contents into AX ' XXX JMP start executing the code at XXX_cfa ' XXX JE start executing the code at XXX_cfa For more examples see the instructions themselves \ WARNINGS ;S This assembler has almost NO error checking. It just keeps on lumbering along generating garbage. Until you learn the ropes I strongly suggest using a debugger such as Periscope to disassemble all code you create. There are lots of nasty surprises and that is the only way I know to learn the pitfalls. Ordinary assemblers keep track of # verses #) for you. You have to be explicit in this post-fix assembler. 1 AX MOV is illegal! You need 1 # AX MOV Between CODE and END-CODE you are EXECUTING not COMPILING. Thus you use ' rather than ['] \ WARNINGS continued ;S The assembler is postfix. The source comes before the destination opposite to MASM. Numbers or addresses must be followed by # for immediate data or #) to indicate a direct address. Index registers must always be preceeded by a constant even if the constant is 0. There are special rules on shift instructions and TEST that might catch you off guard. JMP and JE have tricky rules. You also need to put in your own segment overrides. MASM does this for you. Nearly every memory reference will need a CS: or ES: override. \ WARNINGS continued ;S Make sure you fully understand the difference between XXX JMP and XXX #) JMP. Normally you want XXX JMP. Words such as BL IF THEN BEGIN END are in both the ASSEMBLER and FORTH vocabularies. Be very careful about the search order or you will get the wrong version. The assembler versions have totally different effects. Order is very important. 1 [BX] is correct, [BX] 1 is not. 1 # AX TEST is correct AX 1 # TEST is not. \ Notes to people who wish to modify the assembler ;S Read the iAPX 88 book by Intel published by Reston Publishing if you want to how op codes, registers, addressing modes etc are encoded. This book is a little more useful than The 8086 Book by Russell Rector and George Alexy Osborne/McGraw Hill for this purpose because all the variants of an op code are gathered together on one page. It is not as useful for understanding what instructions do however. It also has 8088 instruction timings, -- something The 8086 Book lacks. Another good book is the PD70108/70116 Low-Power CMOS Microprocessors User's manual published by NEC electronics on the V20/V30 chip. The legal beavers at Intel and NEC forced NEC to use non-standard mnemonics, but other than that the book is concise and clear and sometimes free. \ ASSEMBLER CODE FORTH DEFINITIONS DECIMAL 3000 VOC-SIZE ! ( nfas only not bodies ) VOCABULARY ASSEMBLER ( not immediate ) ONLY FORTH ALSO ASSEMBLER DEFINITIONS : CODE ( -- starts assembler definition ) CREATE UNCREATE ( build nfa and empty cfa ) SMUDGE ( make name temporarily invisible ) ASSEMBLER ; ' CODE FORTH DEFINITIONS ALIAS CODE \ ;CODE ASSEMBLER DEFINITIONS : ;CODE ( -- : used in defining words with actions ) ( written in assembler ) ?CSP ( colon stack depth same as when started? ) COMPILE (;CODE) ( will pop Rstack into IP ) HEREC TOKEN, ( redirect high level to low level code ) ( we are about to write ) [COMPILE] [ ( stop compilation so things like AX POP ) ( will be executed at compile time ) ( don't UNSMUDGE yet. Wait for END-CODE ) ASSEMBLER ; IMMEDIATE ' ;CODE FORTH DEFINITIONS ALIAS ;CODE \ END-CODE ASSEMBLER DEFINITIONS : END-CODE ( -- : ends assembler definition ) UNSMUDGE ( reveal definition ) FORTH ; ' END-CODE ALIAS ;C \ 8086 Assembler Register Definitions ;S On the 8086, register names are cleverly defined constants. The value returned by registers and by modes such as #) containsboth mode and register information. The instructions use the mode information to decide how many arguments exist, and what toassemble. Like many CPUs, the 8086 uses many 3 bit fields in its opcodes This makes octal ( base 8 ) natural for describing the registers \ 8086 Assembler Register Definitions OCTAL : REG ( mode index -- ) 11 Q* SWAP 1000 Q* OR CONSTANT ; : REGS ( Count mode -- ) SWAP 0 DO DUP I REG LOOP DROP ; ( 0 1 2 3 4 5 6 7 ) 10 0 REGS AL CL DL BL AH CH DH BH 10 1 REGS AX CX DX BX SP BP SI DI 10 2 REGS [BX+SI] [BX+DI] [BP+SI] [BP+DI] [SI] [DI] [BP] [BX] 4 2 REGS [SI+BX] [DI+BX] [SI+BP] [DI+BP] ( synonyms ) 4 3 REGS ES CS SS DS 2 4 REGS # #) DECIMAL ;S e.g SP is defined as constant 1044 octal mode:0:index:index 1 octal digit per field # is immediate data #) is direct address \ Addressing Modes OCTAL : MD ( mode -- : builds a word to test mode ) CREATE 1000 Q* W, DOES> UW@ SWAP 7000 AND = ; 1 MD R16? ( n -- flag : true if 16-bit reg AX BX etc ) 2 MD [RX]? ( n -- flag : true if [BX+SI] [BX] etc ) 3 MD SEG? ( n -- flag : true if seg reg CS ES etc ) DECIMAL \ words to interrogate addressing modes MEM? REG? ACC? OCTAL : MEM? ( n -- f : true if memory reference ) DUP [RX]? SWAP #) = OR ; : REG? ( n -- f : true if any reg 8 or 16 bit ) 7000 AND 2000 < ; : ACC? ( n -- f : true if reg is AX or AL ) DUP AL = SWAP AX = OR ; HEX : BIG? ( N -- F : True if won't fit in one byte ) ( 7 bit signed numbers from -128 to +127 will fit ) ( numbers with all 0 or all 1s in bits 15..7 will fit ) ( can ignore seg MSW part ) 0FF80 AND ?DUP IF 0FF80 <> ELSE FALSE THEN ; DECIMAL \ masking words RLOW RMID OCTAL : RLOW ( n1 -- n2 : mask off all but low register field) 7 AND ; : RMID ( n1 -- n2 : mask off all but middle register field ) 70 AND ; DECIMAL DECIMAL \ SIZE WORD BYTE VARIABLE SIZE SIZE ON ( SIZE true for 16 bit, false for 8 bit. ) : BYTE ( -- : set size to 8 bit ) SIZE OFF ; \ e.g. BYTE LODS instead of LODSB : WORD ( -- : set size to 16 bit ) SIZE ON ; \ e.g. WORD LODS instead of LODSW \ nothing at all to do with usual FORTH WORD \ BYTE, WORD, ' C,C ALIAS BYTE, ( c -- ) \ compile 8 bits following the code field ' W,C ALIAS WORD, ( n -- ) \ compile 16 bits following the code field \ OP, OPW, OPSIZE, : OP, ( N OP -- ) OR BYTE, ; ( for efficiency. OR two numbers and assemble ) : OPW, ( OP MR -- ) R16? 1 AND OP, ; ( assemble opcode with W field set for size of register. ) : OPSIZE, ( N OP -- ) SIZE @ 1 AND OP, ; ( assemble opcode with W field set for size of data. ) : W/B, ( n f -- ) IF WORD, ELSE BYTE, THEN ; ( if flag is true compiles 16 bit else 8 bit value ) \ OPR, LOGICAL B/L? OCTAL : OPRR, ( MR1 MR2 -- ) RMID SWAP RLOW OR 300 OP, ; ( assemble register to register instruction. ) VARIABLE LOGICAL ( true while assembling logical instructions. ) : B/L? ( n -- f ) BIG? LOGICAL @ OR ; ( true if offset is BIG or assembling a logical instruction ) DECIMAL \ Addressing MEM, OCTAL : MEM, ( DISP MR RMID -- ) ( handles memory reference modes. It takes a displacement,) ( a mode/register, and a register, and encodes and assembles) ( them. ) OVER #) = ( direct address ) IF RMID 6 OP, DROP WORD, ELSE RMID OVER RLOW OR -ROT [BP] = OVER 0= AND IF SWAP 100 OP, BYTE, ( reg-reg ) ELSE SWAP OVER BIG? ( 16 bit displacement ) IF 200 OP, WORD, ELSE OVER 0= ( no regs ) IF BYTE, DROP ELSE 100 OP, BYTE, ( 8 bit displacement ) THEN THEN THEN THEN ; DECIMAL \ Addressing WMEM, R/M, : WMEM, ( DISP MEM REG OP -- ) OVER OPW, MEM, ; ( uses MEM, after packing the register size into the opcode ) : R/M, ( MR REG -- ) OVER REG? IF OPRR, ELSE MEM, THEN ; ( assembles either a register to register or a register to or from memory mode. ) \ WR/SM, INTER FAR OCTAL : WR/SM, ( R/M R OP -- ) 2 PICK DUP REG? IF OPW, OPRR, ELSE DROP OPSIZE, MEM, THEN ; ( assembles either a register mode with size field, or a memory mode with size from SIZE. Default is 16 bit. Use BYTE for 8 bit size. ) VARIABLE INTER ( true if inter-segment far jump, call, or return. ) : FAR ( -- ) INTER ON ; ( Usage: FAR JMP FAR CALL FAR RET ) DECIMAL \ Defining Words to Generate Op Codes OCTAL : 1MI CREATE C, DOES> C@ BYTE, WORD ; ( define one byte constant instructions. e.g. CLD CWD ) : 2MI CREATE C, DOES> C@ BYTE, 12 BYTE, WORD ; ( define ascii adjust instructions. e.g. AAD ) ( op code followed by HEX 0A ) : 3MI CREATE C, DOES> C@ BYTE, HEREC - 1- BYTE, WORD ; ( define branch instructions, with one byte offset. ) ( 074 3MI JE ) ( op code for ' XXX JE -- no modifier after the address ! ) ( also handles LOOP LOOPE JCXZ ) DECIMAL \ Defining Words to Generate Op Codes OCTAL : 4MI CREATE C, DOES> C@ BYTE, MEM, WORD ; ( define LDS, LEA, LES instructions. ) DECIMAL ;S 8D 4MI LEA op code for LEA ' XXX #) BX LEA LEA BX,XXX_cfa ' XXX [BX] AX LEA LEA AX,XXX_cfa[BX] 0 [BX] AX LEA LEA AX,[BX] you cannot have immediate data or a register as the source \ Defining Words to Generate Op Codes OCTAL : 5MI CREATE C, DOES> C@ OPSIZE, WORD ; DECIMAL ;S define string instructions. 0AC 5MI LODS opcode for LODS used WORD LODS instead of LODSW or BYTE LODS instead of LODSB alternatively you can use 0AD 1MI LODSW 0AC 1MI LODSB opcode for LODSW allows LODSB instead of BYTE LODS allows LODSW instead of WORD LODS \ 7MI defines MUL DIV Op Codes OCTAL : 7MI CREATE C, DOES> C@ 366 WR/SM, WORD ; ( define multiply and divide instructions. ) DECIMAL ;S 30 7MI DIV BX DIV DIV BX 2 [BX] DIV DIV WORD PTR [BX+2] BYTE 2 [BX] DIV DIV BYTE PTR [BX+2] XXX #) DIV DIV XXX_pfa Note that immediate data is not allowed The AX or DX registers are implied and are not mentioned \ 8MI defines IN OUT Op Codes OCTAL : 8MI CREATE C, DOES> C@ SWAP R16? 1 AND OR SWAP # = IF BYTE, BYTE, ELSE 10 OR BYTE, THEN WORD ; ( define input and output instructions. ) DECIMAL ;S E4 8MI IN 6 # AL IN IN AL,6 6 # AX IN IN AX,6 DX AL IN IN AL,DX DX AX IN IN AX,DX \ 9MI Generate Op Codes for INC DEC OCTAL : 9MI CREATE C, DOES> C@ OVER R16? IF 100 OR SWAP RLOW OP, ELSE 376 WR/SM, THEN WORD ; ( define increment/decrement instructions. ) DECIMAL ;S 00 9MI INC 08 9MI DEC Special short forms for INC/DEC 16 bit reg BX INC INC BX 2 [BX] INC INC WORD PTR [BX+2] BYTE 2 [BX] INC INC BYTE PTR [BX+2] XXX #) INC INC XXX_pfa \ 10MI to Generate shift Op Codes such as SHL OCTAL : 10MI CREATE C, DOES> C@ ( CL AX op ) OVER REG? IF ROT CL = IF ( CL AX case ) 322 ELSE ROT DROP ( rid of 1 ) 320 THEN ELSE ( CL/1# 0 [BX] case ) >R >R >R ( get rid of 0 [BX] op ) CL = IF ( CL 0 [BX] case ) R> R> R> 322 ELSE ( 1 # 0 [BX] case ) DROP R> R> R> 320 THEN THEN WR/SM, WORD ; ( define shift/rotate instructions. ) DECIMAL \ Notes on 10MI to Generate shift Op Codes such as SHL ;S 20 10MI SHL 1 # AX SHL SHL AX,1 CL AX SHL SHL AX,CL CL 0 [BX] SHL SHL [BX],CL 5 # AX SHL SHL AX,5 <--- later will be added for NEC \ JMPF OCTAL : JMPF ( dest -- : FAR JMP ) DUP MSW IF ( XXX JMP - store absolute address ) 352 BYTE, ,C ( 4 bytes offset:seg ) ELSE ( mem/reg JMP ) 377 BYTE, 50 ( reg field ) R/M, THEN INTER OFF WORD ; DECIMAL \ JMP OCTAL : JMP ( dest -- ) INTER @ IF JMPF ELSE DUP MSW IF ( XXX JMP - convert to disp16 ) HEREC - 3 - DUP BIG? OVER 1+ BIG? OR ( relative to byte after either jmp l h or jmp l ) IF ( long JMP ) 351 BYTE, WORD, ELSE ( short XXX JMP ) 353 BYTE, 1+ BYTE, THEN ELSE ( mem/reg JMP ) 377 BYTE, 40 ( reg field ) R/M, THEN THEN WORD ; DECIMAL \ CALLF OCTAL : CALLF ( dest -- : FAR CALL ) DUP MSW IF ( XXX CALL - store absolute address ) 232 BYTE, ,C ( 4 bytes offset:seg ) ELSE ( mem/reg CALL ) 377 BYTE, 30 ( reg field ) R/M, THEN INTER OFF WORD ; DECIMAL \ CALL OCTAL : CALL ( dest -- ) INTER @ IF CALLF ELSE DUP MSW ( We can distinguish XXX from the code for anything ) ( else because its MSW seg portion will be non-zero ) ( and the seg part of all other codes is 0. ) IF ( XXX CALL - convert to disp16 ) 350 BYTE, HEREC - 2- WORD, ELSE ( mem/reg CALL ) 377 BYTE, 20 ( reg field ) R/M, THEN THEN WORD ; DECIMAL \ CALL and JMP ;S CALL JMP work the same way except CALL lacks a short disp. FAR CALL or CALLF and FAR JMP or JMPF do intersegment transfers XXX JMP JMP XXX code is at XXX XXX #) JMP JMP [XXX] address of code is at XXX BX JMP JMP BX address of code is in BX 0 [BX] JMP JMP [BX] address of code is in Ram pointed to by BX ' XXX JMP JMP XXX_cfa code is at XXX_cfa XXX [BX] JMP JMP XXX_pfa [BX] address is in RAM. Does not mean start executing BX bytes past XXX \ 12MI Generate PUSH and POP Op codes OCTAL : 12MI ( define pushes and pops. ) ( e.g. 8F 07 58 12MI POP ) ( op codes for 2 [BX] POP ES POP AX POP ) ( immediate data not allowed ) CREATE C, C, C, DOES> OVER REG? IF C@ SWAP RLOW OP, ELSE 1+ OVER SEG? IF C@ RLOW SWAP RMID OP, ELSE COUNT SWAP C@ BYTE, MEM, THEN THEN WORD ; DECIMAL ;S special op codes are needed to handle the SEG regs. later # 88 PUSH will be added for the NEC \ RET +RET FAR RET RETF FAR +RET +RETF OCTAL : RETF ( -- ) 313 BYTE, INTER OFF ; : +RETF ( n -- ) 312 BYTE, WORD, INTER OFF ; : RET ( -- ) INTER @ IF RETF ELSE 303 BYTE, THEN ; : +RET ( n -- ) INTER @ IF +RETF ELSE 302 BYTE, WORD, THEN ; DECIMAL ;S RET RET 4 +RET RET 4 FAR RET RETF RETF RETF \ 13MI arithmetic and logical Op Codes ( define arithmetic and logical instructions. ) OCTAL : 13MI CREATE C, C, DOES> COUNT >R C@ LOGICAL ! DUP REG? IF OVER REG? IF R> OVER OPW, SWAP OPRR, ELSE OVER MEM? IF R> 2 OR WMEM, ELSE ( # ) NIP DUP ACC? IF R> 4 OR OVER OPW, R16? W/B, ELSE OVER B/L? OVER R16? 2DUP AND -ROT 1 AND SWAP NOT 2 AND OR 200 OP, SWAP RLOW 300 OR R> OP, W/B, THEN THEN THEN ELSE ( MEM ) ROT DUP REG? IF R> WMEM, ELSE ( # ) DROP 2 PICK B/L? DUP NOT 2 AND 200 OR OPSIZE, -ROT R> MEM, SIZE @ AND W/B, THEN THEN WORD ; DECIMAL \ Notes on 13MI arithmetic and logical ops e.g. AND SUB ;S 2 20 13MI AND 0 28 13MI SUB 2=Logical no 8bit sign extend of immediate ops 0=arithmetic special short forms can be used when dest is AX or AL AX BX AND AND AX,BX CX BX AND AND BX,CX CL BL AND AND BL,CL 1 # AX AND AND AX,1 1 # AL AND AND AL,1 1 # BX AND AND BX,1 1 # 7 [BX] AND AND WORD PTR [BX+7],1 BYTE 1 # 0 [BX] AND AND BYTE PTR [BX],1 2 [BX] CX AND AND CX,[BX+2] 2 [BX] CL AND AND CL,[BX+2] XXX #) CX AND AND CX,XXX_pfa \ TEST OCTAL : TEST ( source dest -- ) DUP REG? IF OVER REG? IF ( reg reg ) 204 OVER OPW, SWAP OPRR, ELSE OVER MEM? IF ( mem reg ) 204 WMEM, ELSE ( # reg ) NIP DUP ACC? IF ( # AX/AL ) 250 OVER OPW, R16? W/B, ELSE ( # RX ) 366 OVER OPW, DUP RLOW 300 OP, R16? W/B, THEN THEN THEN ELSE ( ? mem ) ROT DUP REG? IF ( was reg mem ) 204 WMEM, ELSE ( was # mem ) DROP 366 OPSIZE, 0 MEM, SIZE @ W/B, THEN THEN WORD ; DECIMAL \ Notes on TEST ;S 01 # AX TEST TEST AX,01 01 # AL TEST TEST AL,01 01 # BX TEST TEST BX,01 BX CX TEST TEST CX,BX 1 # 7 [BX] TEST TEST WORD PTR [BX+7],1 BYTE 1 # 0 [BX] TEST TEST BYTE PTR [BX],1 2 [BX] CX TEST TEST CX,WORD PTR [BX+2] 2 [BX] CL TEST TEST CL,BYTE PTR [BX+2] CX 2 [BX] TEST TEST CX, WORD PTR [BX+2] CL 2 [BX] TEST TEST CL,BYTE PTR [BX+2] special short forms exist for AX AL and with immediate data Any immediate data must be shown as the source rather than the destination. \ Instructions ESC INT HEX : ESC ( source ext-opcode -- ) RLOW 0D8 OP, R/M, WORD ; : INT ( N -- ) DUP 3 = IF DROP 0CC BYTE, ( can be done with 1 byte ) ELSE 0CD BYTE, BYTE, THEN WORD ; DECIMAL ;S 0 [BX] ESC ESC [BX] ' XXX #) ESC ESC XXX_cfa ESC cannot be used with a plain register or immediate data 3 INT INT 3 \ Instructions XCHG HEX : XCHG ( MR1source MR2dest -- ) DUP REG? IF DUP AX = IF ( dest was AX ) OVER REG? ( source a reg too? ) IF ( BX AX style ) DROP RLOW 90 OP, ELSE ( [BX] AX style ) 86 WR/SM, THEN ELSE ( dest reg but not AX ) OVER AX = ( source = AX ) IF NIP RLOW 90 OP, ELSE 86 WR/SM, THEN THEN ELSE ROT 86 WR/SM, THEN WORD ; DECIMAL \ Notes on XCHG ;S BX AX XCHG XCHG BX AX BX XCHG XCHG BX BX CX XCHG XCHG CX,BX CX 0 [BX] XCHG XCHG [BX],CX 0 [BX] CX XCHG XCHG [BX],CX XCHG has short form if dest register is AX and source is reg. When one operand is Memory it must be the destination not the source. This assembler will swap the operands if necessary to automatically care of these two cases. \ Instructions CS: DS: ES: SS: SEG HEX : SEG ( SEG -- ) RMID 26 OP, ; : CS: CS SEG ; : DS: DS SEG ; : ES: ES SEG ; : SS: SS SEG ; DECIMAL ;S use of segment override AX CS: 1 [BX] MOV MOV AX,CS:[BX+1] The override can come anywhere before the op-code \ Instructions HEX : MOV ( Source Dest ) DUP SEG? IF 8E BYTE, R/M, ELSE DUP REG? IF OVER #) = OVER ACC? AND IF A0 SWAP OPW, DROP WORD, ELSE OVER SEG? IF SWAP 8C BYTE, OPRR, ELSE OVER # = IF NIP DUP R16? SWAP RLOW OVER 8 AND OR B0 OP, W/B, ELSE 8A OVER OPW, R/M, THEN THEN THEN ELSE ( MEM ) ROT DUP SEG? IF 8C BYTE, MEM, ELSE DUP # = IF DROP C6 OPSIZE, 0 MEM, SIZE @ W/B, ELSE OVER #) = OVER ACC? AND IF A2 SWAP OPW, DROP WORD, ELSE 88 OVER OPW, R/M, THEN THEN THEN THEN THEN WORD ; DECIMAL \ Notes an MOV ;S Place the Source before the destination -- this is the opposite of MASM Version 4 conventions!! Watch out AX BX MOV MOV BX,AX 2 # AL MOV MOV AL,2 2 #) AX MOV MOV AX,[2] AX 2 [BX] MOV MOV [BX+2],AX AX ' XXX >BODY #) MOV MOV XXX_PFA,AX AX 2 #) MOV MOV [2],AX CS AX MOV MOV AX,CS CS: 0 #) AX MOV MOV AX,CS:[0] Note CS: means seg override, and CS is just the reg. \ Instructions HEX 37 1MI AAA D5 2MI AAD D4 2MI AAM 3F 1MI AAS 0 10 13MI ADC 0 00 13MI ADD 2 20 13MI AND ( CALL ) 98 1MI CBW F8 1MI CLC FC 1MI CLD FA 1MI CLI F5 1MI CMC 0 38 13MI CMP A6 5MI CMPS 99 1MI CWD 27 1MI DAA 2F 1MI DAS 08 9MI DEC 30 7MI DIV ( ESC ) F4 1MI HLT 38 7MI IDIV 28 7MI IMUL E4 8MI IN 00 9MI INC ( INT ) 0CE 1MI INTO DECIMAL \ Instructions HEX 0CF 1MI IRET 77 3MI JA 73 3MI JAE 72 3MI JB 76 3MI JBE E3 3MI JCXZ 74 3MI JE 7F 3MI JG 7D 3MI JGE 7C 3MI JL 7E 3MI JLE ( JMP ) 75 3MI JNE 71 3MI JNO 79 3MI JNS 70 3MI JO 7A 3MI JPE 7B 3MI JPO 78 3MI JS 9F 1MI LAHF C5 4MI LDS 8D 4MI LEA C4 4MI LES F0 1MI LOCK 0AC 5MI LODS E2 3MI LOOP E1 3MI LOOPE E0 3MI LOOPNE DECIMAL \ Instructions HEX ( MOV ) 0A4 5MI MOVS 20 7MI MUL 18 7MI NEG 90 1MI NOP 10 7MI NOT 2 08 13MI OR E6 8MI OUT 8F 07 58 12MI POP 9D 1MI POPF 0FF 36 50 12MI PUSH 9C 1MI PUSHF 10 10MI RCL 18 10MI RCR F2 1MI REP F2 1MI REPNZ F3 1MI REPZ ( RET ) 00 10MI ROL 8 10MI ROR 9E 1MI SAHF 38 10MI SAR 0 18 13MI SBB 0AE 5MI SCAS ( SEG ) 20 10MI SHL 28 10MI SHR F9 1MI STC FD 1MI STD FB 1MI STI 0AA 5MI STOS 0 28 13MI SUB ( TEST ) 9B 1MI WAIT ( XCHG ) D7 1MI XLAT 2 30 13MI XOR ( +RET ) DECIMAL \ alias string Instructions HEX 0A4 1MI MOVSB 0AE 1MI SCASB 0AA 1MI STOSB A6 1MI CMPSB 0AC 1MI LODSB 0A5 1MI MOVSW 0AF 1MI SCASW 0AB 1MI STOSW A7 1MI CMPSW 0AD 1MI LODSW DECIMAL \ NEC-V20 instructions ;S To come: bit and nibble instructions and immediate SHL and PUSH BL ROL4 XXX #) ROL4 BL ROR4 XXX #) ROR4 3 # BL INS BL CL EXT 3 # BL EXT 4 # PUSH PUSHR 3 # BX SET1 3 # BL SET1 3 # XXX #) SET1 BYTE 3 # XXX #) SET1 CL BX SET1 CL BL SET1 CL XXX #) SET1 BYTE CL XXX #) SET13 # BX CLR1 3 # BL CLR1 3 # XXX #) CLR1 BYTE 3 # XXX #) CLR1 CL BX CLR1 CL BL CLR1 CL XXX #) CLR1 BYTE CL XXX #) CLR13 # BX NOT1 3 # BL NOT1 3 # XXX #) NOT1 BYTE 3 # XXX #) NOT1 CL BX NOT1 CL BL NOT1 CL XXX #) NOT1 BYTE CL XXX #) NOT13 # BX TEST1 3 # BL TEST1 3 # XXX #) TEST1 BYTE 3 # XXX #) TEST1 CL BX TEST1 CL BL TEST1 CL XXX #) TEST1 BYTE CL XXX #) TEST1 \ ?CONDITION : ?CONDITION ( true -- ) TRUE <> ( must be exact -1 ) ABORT" Conditionals not paired properly" ; \ Structured Conditionals : ?>MARK ( -- f addr ) TRUE HEREC 0 BYTE, ; ( assembler version of forward mark. ) : ?>RESOLVE ( f addr -- ) HEREC OVER 1+ - SWAP C! ?CONDITION ; ( assembler version of forward resolve. ) : ?<MARK ( -- f addr ) TRUE HEREC ; ( assembler version of backward mark. ) : ?<RESOLVE ( f addr -- ) HEREC 1+ - BYTE, ?CONDITION ; ( assembler version of backward resolve. ) \ 0= OV ALWAYS etc conditionals HEX 75 CONSTANT 0= 74 CONSTANT 0<> 79 CONSTANT 0< 78 CONSTANT 0>= 7D CONSTANT < 7C CONSTANT >= 7F CONSTANT <= 7E CONSTANT > 73 CONSTANT U< 72 CONSTANT U>= 77 CONSTANT U<= 76 CONSTANT U> 71 CONSTANT OV EB CONSTANT ALWAYS ( op code for JMP ) E3 CONSTANT CX0<> 74 CONSTANT <> 75 CONSTANT = DECIMAL ;S These conditional test words leave the opcodes of conditional branches to be used by the structured conditional words. 0= is opcode for JE -- opposite of meaning i.e. jmp to bypass IF clause. For example, 5 # CX CMP 0< IF AX BX ADD ELSE AX BX SUB THEN \ Subtle conditional Notes ;S You may be wondering why there are both < and 0< conditionals. < corresponds to JGE and 0< correponds to JNS. < is the one normally used after an arithmetic operation because it is accurate even if the result overflowed. 0< is the one used if you wish to test the result after the overflow information is lost. If there has been no overflow then both give the same results. \ Structured Conditionals HEX : IF BYTE, ?>MARK ; : THEN ?>RESOLVE ; : ELSE ALWAYS IF ( not the FORTH IF ) 2SWAP THEN ; : BEGIN ?<MARK ; : UNTIL BYTE, ?<RESOLVE ; : AGAIN ALWAYS UNTIL ; : WHILE IF ; : REPEAT 2SWAP AGAIN THEN ; : DO HEREC ; ( LOOP is machine instr ) DECIMAL \ BE VERY CAREFUL NOT TO CONFUSE WITH THE FORTH EQUIVALENTS \ THIS CAN HAPPEN AFTER AN ERROR WHEN THE CONTEXT IS \ AUTOMATICALLY RESTORED TO FORTH RATHER THAN ASSEMBLER. \ Examples of use of structured conditionals. ;S 5 # CX CMP 0< IF ( executes if CX < 5 ) AX BX ADD ELSE ( executes if CX >= 5 ) AX BX SUB THEN BEGIN ( code executes until low bit of AX is 0 ) ... 1 # AX TEST 0= UNTIL BEGIN ... ( loops endlessly ) AGAIN \ Examples of use of structured conditionals. ;S BEGIN ( code executes as long as low bit of AX is 0 ) ... 1 # AX TEST 0<> WHILE ... REPEAT 9 # CX MOV DO ... ( perform this code 9 times with CX=9,8,...1 ) LOOP \ Macros NEXT POPCB POPDX PUSHCB PUSHDX \ Because : rather than CODE used, these words \ will assemble the code rather than execute it. : NEXT ( inline inner interpreter ) LODSW AX JMP ; : POPCB CX POP BX POP ; : POPDA DX POP AX POP ; : PUSHCB BX PUSH CX PUSH ; : PUSHDA AX PUSH DX PUSH ; \ Last Screen FORTH DEFINITIONS