commit:39d9edd8256c7b02c27210e9d40b561914e3f6bd
author:Chip
committer:Chip
date:Sun Mar 30 14:50:13 2025 -0500
parents:593fbb89f6ab7d41df9eee7f1c849aa2eb3e4a91
More opcodes and usage info
diff --git a/asm89.pas b/asm89.pas
line changes: +18/-4
index 174d18f..244ad96
--- a/asm89.pas
+++ b/asm89.pas
@@ -68,13 +68,27 @@ begin
     CloseOmf(f);
 end;
 
+procedure PrintUsage;
+begin
+    Writeln('Usage: asm89 <input.asm> [output.obj]');
+    Writeln('');
+    Writeln('The output object file is optional. If it is not specified, the output file');
+    Writeln('Is the input file with the .obj extension');
+end;
+
+procedure FatalError(const message: string; const print_usage: boolean);
+begin
+    Writeln(message);
+    Writeln('');
+    if print_usage then PrintUsage;
+    Halt(1);
+end;
+
 begin
     Writeln('asm89 compiler v', VERSION);
     Writeln('');
-    if ParamCount < 1 then begin
-        Writeln('You must specify an input file');
-        Halt(1);
-    end;
+    if ParamCount < 1 then
+        FatalError('You must specify an input file', true);
     infile := ParamStr(1);
     if ParamCount < 2 then begin
         outfile := ChangeExtension(ParamStr(1), 'obj');

diff --git a/examples/scrfuzz.asm b/examples/scrfuzz.asm
line changes: +2/-3
index acc7d08..00ee022
--- a/examples/scrfuzz.asm
+++ b/examples/scrfuzz.asm
@@ -5,7 +5,6 @@
 _ScreenFuzz:
 	lpd ga, [pp].4  ; pp.4 is the input buffer address
 	movi ix, 0
-1:	inc [ga+ix+]
+loop:	inc [ga+ix+]
 	andi ix, 07ffh
-	jmp 1-
-
+	jmp loop

diff --git a/omf.pas b/omf.pas
line changes: +1/-1
index 90ca13f..c301d82
--- a/omf.pas
+++ b/omf.pas
@@ -162,7 +162,7 @@ begin
     Inc(rec.head.length);
     rec.data[rec.head.length] := cclass;
     Inc(rec.head.length);
-    for i := 0 to comment_len do begin
+    for i := 1 to comment_len do begin
         rec.data[rec.head.length] := Byte(comment[i]);
         Inc(rec.head.length);
     end;

diff --git a/opcode.pas b/opcode.pas
line changes: +165/-7
index 4778963..e0a5537
--- a/opcode.pas
+++ b/opcode.pas
@@ -57,15 +57,53 @@ Operand = record
 end;
 
 InstructionRBP = (
-    rbpReg,    { RbP Encodes a register }
-    rbpBit,    { RbP Encodes a bit position }
-    rbpPtrReg, { RbP encodes a pointer register }
+    rbpReg,    { RbP Encodes a register (rrr) }
+    rbpBit,    { RbP Encodes a bit position (bbb) }
+    rbpPtrReg, { RbP encodes a pointer register (PPP) }
     rbpNone,   { RbP encodes nothing (0b000) }
     rbpHlt,    { RbP encodes 0b001 }
     rbpSintr,  { RbP encodes 0b010 }
     rbpJmp     { RbP encodes 0b100 }
 );
 InstructionWidth = (widByte, widWord);
+{ |        low order byte         |        high order byte        | }
+{ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | }
+{ |   r/b/P   |  W B  |  A A  | W |        OPCODE         |  MM   | }
+{                                                                   }
+{ r/b/P = Register, Bit, or Pointer/Register Select                 }
+{     RRR         bbb            PPP                                }
+{     000 - GA    000 - bit 0    000 - GA                           }
+{     001 - GB    001 - bit 1    001 - GB                           }
+{     010 - GC    010 - bit 2    010 - GC                           }
+{     011 - GA    011 - bit 3    100 - TP                           }
+{     100 - GA    100 - bit 4                                       }
+{     101 - GA    101 - bit 5                                       }
+{     110 - GA    110 - bit 6                                       }
+{     111 - GA    111 - bit 7                                       }
+{                                                                   }
+{ WB = Number of immediate/displacement value bytes  (EncWB)        }
+{     00 - Reserved              (wReserved)                        }
+{     01 - 1 byte                (wOne)                             }
+{     10 - 2 bytes (word)        (wTwo)                             }
+{     11 - TSL instruction only  (wTSL)                             }
+{                                                                   }
+{ AA = Memory Address Mode                                          }
+{     00 - Base address only                                        }
+{     01 - Base address + 8 bit offset                              }
+{     10 - Base address + index register                            }
+{     11 - Base address + index register, post-increment            }
+{                                                                   }
+{ W = Memory Data Width                                             }
+{     0 - 1 byte  (widByte)                                         }
+{     1 - 2 bytes (widWord)                                         }
+{                                                                   }
+{ OPCODE = Opcode, see below                                        }
+{                                                                   }
+{ MM = Base Memory Address Select                                   }
+{     00 - GA                                                       }
+{     01 - GB                                                       }
+{     10 - GC                                                       }
+{     11 - PP                                                       }
 InstructionEncoding = record
     { these parts are matched against }
     m: Mnemonic;
@@ -73,13 +111,13 @@ InstructionEncoding = record
     o2: OperandType;
     o3: OperandType;
     { these parts are encoded when the above fields match }
-    op: Byte;
+    op: Byte;  { pre-shifted into the high 6-bits }
     w: InstructionWidth;
     wb: EncWB;
     rbp: InstructionRBP;
 end;
 
-const IEnc: array[0..18] of InstructionEncoding = (
+const IEnc: array[0..29] of InstructionEncoding = (
     (m: mADD;   o1: OpRegister; o2: OpMemory;    o3: OpAbsent;
         op: $A0;   w: widWord;     wb: wReserved;   rbp: rbpReg),
 
@@ -122,19 +160,139 @@ const IEnc: array[0..18] of InstructionEncoding = (
     (m: mANDBI; o1: OpMemory;   o2: OpImmediate; o3: OpAbsent;
         op: $C8;   w: widByte;     wb: wOne;        rbp: rbpNone),
 
+    (m: mANDI;  o1: OpRegister; o2: OpImmediate; o3: OpAbsent;
+        op: $28;   w: widWord;     wb: wTwo;        rbp: rbpReg),
+
+    (m: mANDI;  o1: OpMemory;   o2: OpImmediate; o3: OpAbsent;
+        op: $C8;   w: widWord;     wb: wTwo;        rbp: rbpNone),
+
     (m: mCALL;  o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
         op: $9C;   w: widWord;     wb: wLocation;   rbp: rbpJmp),
 
+    (m: mCLR;   o1: OpMemory;   o2: OpBit;       o3: OpAbsent;
+        op: $F8;   w: widByte;   wb: wReserved;   rbp: rbpBit),
+
+    (m: mDEC;   o1: OpRegister; o2: OpAbsent;    o3: OpAbsent;
+        op: $3C;   w: widByte;   wb: wReserved;   rbp: rbpReg),
+
+    (m: mDEC;   o1: OpMemory;   o2: OpAbsent;    o3: OpAbsent;
+        op: $EC;   w: widWord;   wb: wReserved;   rbp: rbpNone),
+
+    (m: mDECB;  o1: OpMemory;   o2: OpAbsent;    o3: OpAbsent;
+        op: $EC;   w: widByte;   wb: wReserved;   rbp: rbpNone),
+
     (m: mHLT;   o1: OpAbsent;   o2: OpAbsent;    o3: OpAbsent;
         op: $48;   w: widByte;     wb: wReserved;   rbp: rbpHlt),
 
+    (m: mINC;   o1: OpRegister; o2: OpAbsent;    o3: OpAbsent;
+        op: $38;   w: widByte;     wb: wReserved;   rbp: rbpReg),
+
+    (m: mINC;   o1: OpMemory;   o2: OpAbsent;    o3: OpAbsent;
+        op: $E8;   w: widWord;     wb: wReserved;   rbp: rbpNone),
+
     (m: mJBT;   o1: OpMemory;   o2: OpBit;       o3: OpLocation;
         op: $BC;   w: widByte;     wb: wLocation;   rbp: rbpBit),
 
+    (m: mJMCE;  o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $B0;   w: widByte;     wb: wLocation;   rbp: rbpNone),
+
+    (m: mJMCNE; o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $B4;   w: widByte;     wb: wLocation;   rbp: rbpNone),
+
     (m: mJMP;   o1: OpLocation; o2: OpAbsent;    o3: OpAbsent;
-        op: $20;   w: widWord;     wb: wTwo;        rbp: rbpJmp),
+        op: $20;   w: widWord;     wb: wLocation;   rbp: rbpJmp), { AUG shows w changing for 8/16-bit displacement }
+
+    (m: mJNBT;  o1: OpMemory;   o2: OpBit;       o3: OpLocation;
+        op: $B8;   w: widByte;     wb: wLocation;   rbp: rbpBit),
+
+    (m: mJNZ;   o1: OpRegister; o2: OpLocation;  o3: OpAbsent;
+        op: $40;   w: widByte;     wb: wLocation;   rbp: rbpReg),
+
+    (m: mJNZ;   o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $E0;   w: widWord;     wb: wLocation;   rbp: rbpNone),
+
+    (m: mJNZB;  o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $E0;   w: widByte;     wb: wLocation;   rbp: rbpNone),
+
+    (m: mJZ;    o1: OpRegister; o2: OpLocation;  o3: OpAbsent;
+        op: $44;   w: widByte;     wb: wLocation;   rbp: rbpReg),
+
+    (m: mJZ;    o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $E4;   w: widWord;     wb: wLocation;   rbp: rbpNone),
+
+    (m: mJZB;   o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $E4;   w: widByte;     wb: wLocation;   rbp: rbpNone),
+
+    (m: mLCALL; o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $9C;   w: widWord;     wb: wTwo;        rbp: rbpJmp),
+
+    (m: mLJBT;  o1: OpMemory;   o2: OpBit;       o3: OpLocation;
+        op: $BC;   w: widByte;     wb: wTwo;        rbp: rbpBit),
+
+    (m: mLJMCE;  o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $B0;   w: widByte;     wb: wTwo;        rbp: rbpNone),
+
+    (m: mLJMCNE; o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $B4;   w: widByte;     wb: wTwo;        rbp: rbpNone),
+
+    (m: mLJMP;   o1: OpLocation; o2: OpAbsent;    o3: OpAbsent;
+        op: $20;   w: widWord;     wb: wTwo;        rbp: rbpJmp), { AUG shows w changing for 8/16-bit displacement }
+
+    (m: mLJNBT;  o1: OpMemory;   o2: OpBit;       o3: OpLocation;
+        op: $B8;   w: widByte;     wb: wTwo;        rbp: rbpBit),
+
+    (m: mLJNZ;   o1: OpRegister; o2: OpLocation;  o3: OpAbsent;
+        op: $40;   w: widByte;     wb: wTwo;        rbp: rbpReg),
+
+    (m: mLJNZ;   o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $E0;   w: widWord;     wb: wTwo;        rbp: rbpNone),
+
+    (m: mLJNZB;  o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $E0;   w: widByte;     wb: wTwo;        rbp: rbpNone),
+
+    (m: mLJZ;    o1: OpRegister; o2: OpLocation;  o3: OpAbsent;
+        op: $44;   w: widByte;     wb: wTwo;        rbp: rbpReg),
+
+    (m: mLJZ;    o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $E4;   w: widWord;     wb: wTwo;        rbp: rbpNone),
+
+    (m: mLJZB;   o1: OpMemory;   o2: OpLocation;  o3: OpAbsent;
+        op: $E4;   w: widByte;     wb: wTwo;        rbp: rbpNone),
+
+    (m: mLPD;    o1: OpPointer;  o2: OpMemory;    o3: OpAbsent;
+        op: $88;   w: widWord;     wb: wReserved;   rbp: rbpPtrReg),
+
+    (m: mLPDI;   o1: OpPointer;  o2: OpImmediate; o3: OpAbsent;
+        op: $08;   w: widWord;     wb: wTwo;        rbp: rbpPtrReg),
+
+    (m: mMOV;    o1: OpMemory;   o2: OpRegister;  o3: OpAbsent;
+        op: $84;   w: widWord;     wb: wReserved;   rbp: rbpReg),
+
+    (m: mMOV;    o1: OpRegister; o2: OpMemory;    o3: OpAbsent;
+        op: $80;   w: widWord;     wb: wReserved;   rbp: rbpReg),
+
+    (m: mMOV;    o1: OpMemory;   o2: OpMemory;    o3: OpAbsent;
+        op: $90;   w: widWord;     wb: wReserved;   rbp: rbpNone),
+    { MOV M, M is encoded as a pair of instructions. Each one encodes a memory
+      register and optional offset. The second opcode is $CC. }
+
+    (m: mMOVB;   o1: OpMemory;   o2: OpRegister;  o3: OpAbsent;
+        op: $84;   w: widByte;     wb: wReserved;   rbp: rbpReg),
+
+    (m: mMOVB;   o1: OpRegister; o2: OpMemory;    o3: OpAbsent;
+        op: $80;   w: widByte;     wb: wReserved;   rbp: rbpReg),
+
+    (m: mMOVB;    o1: OpMemory;   o2: OpMemory;    o3: OpAbsent;
+        op: $90;   w: widByte;     wb: wReserved;   rbp: rbpNone),
+    { MOVB M, M works just like MOV with a different W field. }
+
+    (m: mMOVI;   o1: OpRegister; o2: OpImmediate; o3: OpAbsent;
+        op: $30;   w: widWord;     wb: wTwo;        rbp: rbpReg),
+    
+    (m: MMOVI;   o1: OpMemory;   o2: OpImmediate; o3: OpAbsent;
+        op: $4C;   w: widWord;     wb: wTwo;        rbp: rbpNone),
 
-    (m: mSINTR; o1: OpAbsent;   o2: OpAbsent;    o3: OpAbsent;
+    (m: mSINTR;  o1: OpAbsent;   o2: OpAbsent;    o3: OpAbsent;
         op: $00;   w: widByte;     wb: wReserved;   rbp: rbpSintr)
 );
 

diff --git a/parse.pas b/parse.pas
line changes: +15/-5
index 799a792..01b6673
--- a/parse.pas
+++ b/parse.pas
@@ -153,6 +153,13 @@ begin
     ParseIntLiteral := Word(j);
 end;
 
+function ParseNumericExpression(const s: String; var i: Word): Boolean;
+begin
+    { TODO: actually parse expressions }
+    i := ParseIntLiteral(s);
+    ParseNumericExpression := true;
+end;
+
 function ParseMnemonic(const m: String): Mnemonic;
 var i: Mnemonic;
 begin
@@ -235,16 +242,19 @@ var i: Byte;
     l: String;
 begin
     i := 1;
+    { First try an identifier }
     l := ToUpCase(ConsumeIdentifier(a, i));
     if i < Length(a) + 1 then begin
         Writeln('Malformed location? `', a, '`');
         Halt(1);
     end;
-    if NOT FindSymbol(l, o.loc) then begin
-        Writeln('Symbol not found: ', l);
-        Halt(1);
+    if FindSymbol(l, o.loc) then begin
+        o.Kind := OpLocation;
+        Exit;
     end;
-    o.Kind := OpLocation;
+    { Then maybe it's a numeric expression }
+    if ParseNumericExpression(a, o.loc) then
+        o.Kind := OpLocation;
 end;
 
 procedure ParseMemory(a: String; var o: Operand);
@@ -289,7 +299,7 @@ begin
     else if a[i] = '+' then begin
         Inc(i);
         ConsumeWhitespace(a, i);
-        reg := ConsumeRegister(a, i);
+        reg := ToUpCase(ConsumeRegister(a, i));
         if reg <> 'IX' then
             Exit;
         ConsumeWhitespace(a, i);