diff options
Diffstat (limited to 'compiler/z80/raz80asm.pas')
-rw-r--r-- | compiler/z80/raz80asm.pas | 2390 |
1 files changed, 2390 insertions, 0 deletions
diff --git a/compiler/z80/raz80asm.pas b/compiler/z80/raz80asm.pas new file mode 100644 index 0000000000..90c8787d95 --- /dev/null +++ b/compiler/z80/raz80asm.pas @@ -0,0 +1,2390 @@ +{ + Copyright (c) 1998-2008 by Carl Eric Codere and Peter Vreman + + Does the parsing for the Z80 styled inline assembler. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + **************************************************************************** +} +Unit raz80asm; + +{$i fpcdefs.inc} + + Interface + + uses + cclasses, + globtype, + rasm,raz80, + aasmbase,cpubase; + + type + tasmtoken = ( + AS_NONE,AS_LABEL,AS_LLABEL,AS_STRING,AS_INTNUM, + AS_REALNUM,AS_COMMA,AS_LPAREN, + AS_RPAREN,AS_COLON,AS_DOT,AS_PLUS,AS_MINUS,AS_STAR, + AS_SEPARATOR,AS_ID,AS_REGISTER,AS_OPCODE,AS_CONDITION,AS_SLASH,AS_DOLLAR, + AS_HASH,AS_LSBRACKET,AS_RSBRACKET,AS_LBRACKET,AS_RBRACKET, + AS_EQUAL, + {------------------ Assembler directives --------------------} + AS_DEFB,AS_DEFW,AS_END, + {------------------ Assembler Operators --------------------} + AS_TYPE,AS_SIZEOF,AS_VMTOFFSET,AS_MOD,AS_SHL,AS_SHR,AS_NOT,AS_AND,AS_OR,AS_XOR,AS_NOR,AS_AT, + AS_RELTYPE, // common token for relocation types + {------------------ Target-specific directive ---------------} + AS_TARGET_DIRECTIVE + ); + tasmkeyword = string[10]; + + const + { These tokens should be modified accordingly to the modifications } + { in the different enumerations. } + firstdirective = AS_DEFB; + lastdirective = AS_END; + token2str : array[tasmtoken] of tasmkeyword=( + '','Label','LLabel','string','integer', + 'float',',','(', + ')',':','.','+','-','*', + ';','identifier','register','opcode','condition','/','$', + '#','{','}','[',']', + '=', + 'defb','defw','END', + 'TYPE','SIZEOF','VMTOFFSET','%','<<','>>','!','&','|','^','~','@','reltype', + 'directive'); + + type + { input flags for BuildConstSymbolExpression } + tconstsymbolexpressioninputflag = ( + cseif_needofs, + cseif_isref, + cseif_startingminus, + { allows using full reference-like syntax for constsymbol expressions, + for example: + Rec.Str[5] -> Rec.Str+5 } + cseif_referencelike + ); + tconstsymbolexpressioninputflags = set of tconstsymbolexpressioninputflag; + { output flags for BuildConstSymbolExpression } + tconstsymbolexpressionoutputflag = ( + cseof_isseg, + cseof_is_farproc_entry, + cseof_hasofs + ); + tconstsymbolexpressionoutputflags = set of tconstsymbolexpressionoutputflag; + + { tz80reader } + + tz80reader = class(tasmreader) + actasmcond : TAsmCond; + actasmpattern_origcase : string; + actasmtoken : tasmtoken; + prevasmtoken : tasmtoken; + inexpression : boolean; + procedure SetupTables; + procedure GetToken; + function consume(t : tasmtoken):boolean; + procedure RecoverConsume(allowcomma:boolean); + procedure AddReferences(dest,src : tz80operand); + function is_locallabel(const s:string):boolean; + function is_asmopcode(const s: string):boolean; + Function is_asmdirective(const s: string):boolean; + function is_register(const s:string):boolean; + function is_condition(const s:string):boolean; + function is_targetdirective(const s: string):boolean; + procedure BuildRecordOffsetSize(const expr: string;out offset:tcgint;out size:tcgint; out mangledname: string; needvmtofs: boolean; out hastypecast: boolean); + procedure BuildConstSymbolExpression(in_flags: tconstsymbolexpressioninputflags;out value:tcgint;out asmsym:string;out asmsymtyp:TAsmsymtype;out size:tcgint;out out_flags:tconstsymbolexpressionoutputflags); + function BuildConstExpression:longint; + function BuildRefConstExpression(out size:tcgint;startingminus:boolean=false):longint; + procedure BuildConstantOperand(oper: tz80operand); + procedure BuildReference(oper : tz80operand); + procedure BuildOperand(oper: tz80operand;istypecast:boolean); + procedure BuildOpCode(instr:TZ80Instruction); + procedure handleopcode; + procedure ConvertCalljmp(instr : tz80instruction); + function Assemble: tlinkedlist;override; + end; + + + Implementation + + uses + { helpers } + cutils, + { global } + globals,verbose, + systems, + { aasm } + cpuinfo,aasmtai,aasmdata,aasmcpu, + { symtable } + symconst,symbase,symtype,symsym,symtable,symdef,symutil, + { parser } + scanner,pbase, + procinfo, + rabase,rautils, + cgbase,cgutils,cgobj + ; + + +{***************************************************************************** + tz80reader +*****************************************************************************} + + + procedure tz80reader.SetupTables; + var + i: TAsmOp; + begin + iasmops:=TFPHashList.create; + for i:=firstop to lastop do + iasmops.Add(upper(std_op2str[i]),Pointer(PtrInt(i))); + end; + + + procedure tz80reader.GetToken; + var + len: Integer; + srsym : tsym; + srsymtable : TSymtable; + can_be_condition : Boolean; + begin + c:=scanner.c; + { certain instructions can have a condition, as an operand. We need to set this flag, + because 'C' can be either a register, or a condition, depending on the context } + can_be_condition:=(actasmtoken=AS_OPCODE) and (actopcode in [A_JP,A_JR,A_CALL,A_RET]); + { save old token and reset new token } + prevasmtoken:=actasmtoken; + actasmtoken:=AS_NONE; + { reset } + actasmpattern:=''; + { while space and tab , continue scan... } + while c in [' ',#9] do + c:=current_scanner.asmgetchar; + { get token pos } + if not (c in [#10,#13,'{',';','/','(']) then + current_scanner.gettokenpos; + { Local Label, Label, Directive, Prefix or Opcode } + if firsttoken and not(c in [#10,#13,'{',';','/','(']) then + begin + firsttoken:=FALSE; + len:=0; + { directive } + if c = '.' then + begin + inc(len); + actasmpattern[len]:=c; + { Let us point to the next character } + c:=current_scanner.asmgetchar; + while c in ['A'..'Z','a'..'z','0'..'9','_','$'] do + begin + inc(len); + actasmpattern[len]:=c; + c:=current_scanner.asmgetchar; + end; + actasmpattern[0]:=chr(len); + { must be a directive } + if is_asmdirective(actasmpattern) then + exit; + if is_targetdirective(actasmpattern) then + begin + actasmtoken:=AS_TARGET_DIRECTIVE; + exit; + end; + Message1(asmr_e_not_directive_or_local_symbol,actasmpattern); + end; + { only opcodes, global and local labels are allowed now. } + while c in ['A'..'Z','a'..'z','0'..'9','_','@'] do + begin + inc(len); + actasmpattern[len]:=c; + c:=current_scanner.asmgetchar; + end; + actasmpattern[0]:=chr(len); + actasmpattern_origcase:=actasmpattern; + { Label ? } + if c = ':' then + begin + { Local label ? } + if is_locallabel(actasmpattern) then + actasmtoken:=AS_LLABEL + else + actasmtoken:=AS_LABEL; + { let us point to the next character } + c:=current_scanner.asmgetchar; + firsttoken:=true; + exit; + end; + { Opcode ? } + if is_asmopcode(upper(actasmpattern)) then + begin + uppervar(actasmpattern); + exit; + end; + { End of assemblerblock ? } + if upper(actasmpattern) = 'END' then + begin + actasmtoken:=AS_END; + exit; + end; + message1(asmr_e_unknown_opcode,actasmpattern); + actasmtoken:=AS_NONE; + end + else { else firsttoken } + { Here we must handle all possible cases } + begin + case c of + '.' : { possiblities : - local label reference , such as in jmp @local1 } + { - field of object/record } + { - directive. } + begin + if (prevasmtoken in [AS_ID,AS_RPAREN]) then + begin + c:=current_scanner.asmgetchar; + actasmtoken:=AS_DOT; + exit; + end; + actasmpattern:=c; + c:=current_scanner.asmgetchar; + while c in ['A'..'Z','a'..'z','0'..'9','_','$'] do + begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + end; + if is_asmdirective(actasmpattern) then + exit; + if is_targetdirective(actasmpattern) then + begin + actasmtoken:=AS_TARGET_DIRECTIVE; + exit; + end; + { local label references and directives } + { are case sensitive } + actasmtoken:=AS_ID; + exit; + end; + + { identifier, register, prefix or directive } + '_','A'..'Z','a'..'z': + begin + len:=0; + while c in ['A'..'Z','a'..'z','0'..'9','_','$'] do + begin + inc(len); + actasmpattern[len]:=c; + c:=current_scanner.asmgetchar; + end; + actasmpattern[0]:=chr(len); + actasmpattern_origcase:=actasmpattern; + uppervar(actasmpattern); + {$ifdef x86} + { only x86 architectures have instruction prefixes } + + { Opcode, can only be when the previous was a prefix } + If is_prefix(actopcode) and is_asmopcode(actasmpattern) then + Begin + uppervar(actasmpattern); + exit; + end; + {$endif x86} + { check for end which is a reserved word unlike the opcodes } + if actasmpattern = 'END' then + begin + actasmtoken:=AS_END; + exit; + end; + if actasmpattern = 'TYPE' then + begin + actasmtoken:=AS_TYPE; + exit; + end; + if actasmpattern = 'SIZEOF' then + begin + actasmtoken:=AS_SIZEOF; + exit; + end; + if actasmpattern = 'VMTOFFSET' then + begin + actasmtoken:=AS_VMTOFFSET; + exit; + end; + if can_be_condition and is_condition(actasmpattern) then + begin + actasmtoken:=AS_CONDITION; + exit; + end; + if is_register(actasmpattern) then + begin + actasmtoken:=AS_REGISTER; + exit; + end; + { if next is a '.' and this is a unitsym then we also need to + parse the identifier } + if (c='.') then + begin + searchsym(actasmpattern,srsym,srsymtable); + if assigned(srsym) and + (srsym.typ=unitsym) and + (srsym.owner.symtabletype in [staticsymtable,globalsymtable]) and + srsym.owner.iscurrentunit then + begin + actasmpattern:=actasmpattern+c; + c:=current_scanner.asmgetchar; + while c in ['A'..'Z','a'..'z','0'..'9','_','$'] do + begin + actasmpattern:=actasmpattern + upcase(c); + c:=current_scanner.asmgetchar; + end; + end; + end; + actasmtoken:=AS_ID; + exit; + end; + + //'%' : { register or modulo } + // handlepercent; + + '1'..'9': { integer number } + begin + len:=0; + while c in ['0'..'9'] do + Begin + inc(len); + actasmpattern[len]:=c; + c:=current_scanner.asmgetchar; + end; + actasmpattern[0]:=chr(len); + actasmpattern:=tostr(ParseVal(actasmpattern,10)); + actasmtoken:=AS_INTNUM; + exit; + end; + '0' : { octal,hexa,real or binary number. } + begin + actasmpattern:=c; + c:=current_scanner.asmgetchar; + case upcase(c) of + 'B': { binary } + Begin + c:=current_scanner.asmgetchar; + while c in ['0','1'] do + Begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + end; + actasmpattern:=tostr(ParseVal(actasmpattern,2)); + actasmtoken:=AS_INTNUM; + exit; + end; + 'D': { real } + Begin + c:=current_scanner.asmgetchar; + { get ridd of the 0d } + if (c in ['+','-']) then + begin + actasmpattern:=c; + c:=current_scanner.asmgetchar; + end + else + actasmpattern:=''; + while c in ['0'..'9'] do + Begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + end; + if c='.' then + begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + while c in ['0'..'9'] do + Begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + end; + if upcase(c) = 'E' then + begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + if (c in ['+','-']) then + begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + end; + while c in ['0'..'9'] do + Begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + end; + end; + actasmtoken:=AS_REALNUM; + exit; + end + else + begin + Message1(asmr_e_invalid_float_const,actasmpattern+c); + actasmtoken:=AS_NONE; + end; + end; + 'X': { hexadecimal } + Begin + c:=current_scanner.asmgetchar; + while c in ['0'..'9','a'..'f','A'..'F'] do + Begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + end; + actasmpattern:=tostr(ParseVal(actasmpattern,16)); + actasmtoken:=AS_INTNUM; + exit; + end; + '1'..'7': { octal } + begin + actasmpattern:=actasmpattern + c; + while c in ['0'..'7'] do + Begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + end; + actasmpattern:=tostr(ParseVal(actasmpattern,8)); + actasmtoken:=AS_INTNUM; + exit; + end; + else { octal number zero value...} + Begin + actasmpattern:=tostr(ParseVal(actasmpattern,8)); + actasmtoken:=AS_INTNUM; + exit; + end; + end; { end case } + end; + + '&' : + begin + c:=current_scanner.asmgetchar; + actasmtoken:=AS_AND; + end; + + '''' : { char } + begin + actasmpattern:=''; + repeat + c:=current_scanner.asmgetchar; + case c of + '\' : + begin + { copy also the next char so \" is parsed correctly } + actasmpattern:=actasmpattern+c; + c:=current_scanner.asmgetchar; + actasmpattern:=actasmpattern+c; + end; + '''' : + begin + c:=current_scanner.asmgetchar; + break; + end; + #10,#13: + Message(scan_f_string_exceeds_line); + else + actasmpattern:=actasmpattern+c; + end; + until false; + actasmpattern:=EscapeToPascal(actasmpattern); + actasmtoken:=AS_STRING; + exit; + end; + + '"' : { string } + begin + actasmpattern:=''; + repeat + c:=current_scanner.asmgetchar; + case c of + '\' : + begin + { copy also the next char so \" is parsed correctly } + actasmpattern:=actasmpattern+c; + c:=current_scanner.asmgetchar; + actasmpattern:=actasmpattern+c; + end; + '"' : + begin + c:=current_scanner.asmgetchar; + break; + end; + #10,#13: + Message(scan_f_string_exceeds_line); + else + actasmpattern:=actasmpattern+c; + end; + until false; + actasmpattern:=EscapeToPascal(actasmpattern); + actasmtoken:=AS_STRING; + exit; + end; + + //'$' : + // begin + // handledollar; + // exit; + // end; + + '#' : + begin + actasmtoken:=AS_HASH; + c:=current_scanner.asmgetchar; + exit; + end; + + '[' : + begin + actasmtoken:=AS_LBRACKET; + c:=current_scanner.asmgetchar; + exit; + end; + + ']' : + begin + actasmtoken:=AS_RBRACKET; + c:=current_scanner.asmgetchar; + exit; + end; + + '{' : + begin + {$ifdef arm} + // the arm assembler uses { ... } for register sets + // but compiler directives {$... } are still allowed + c:=current_scanner.asmgetchar; + if c<>'$' then + actasmtoken:=AS_LSBRACKET + else + begin + current_scanner.skipcomment(false); + GetToken; + end; + {$else arm} + current_scanner.skipcomment(true); + GetToken; + {$endif arm} + exit; + end; + + {$ifdef arm} + '}' : + begin + actasmtoken:=AS_RSBRACKET; + c:=current_scanner.asmgetchar; + exit; + end; + + '=' : + begin + actasmtoken:=AS_EQUAL; + c:=current_scanner.asmgetchar; + exit; + end; + {$endif arm} + + ',' : + begin + actasmtoken:=AS_COMMA; + c:=current_scanner.asmgetchar; + exit; + end; + + '<' : + begin + actasmtoken:=AS_SHL; + c:=current_scanner.asmgetchar; + if c = '<' then + c:=current_scanner.asmgetchar; + exit; + end; + + '>' : + begin + actasmtoken:=AS_SHL; + c:=current_scanner.asmgetchar; + if c = '>' then + c:=current_scanner.asmgetchar; + exit; + end; + + '|' : + begin + actasmtoken:=AS_OR; + c:=current_scanner.asmgetchar; + exit; + end; + + '^' : + begin + actasmtoken:=AS_XOR; + c:=current_scanner.asmgetchar; + exit; + end; + + + '(' : + begin + c:=current_scanner.asmgetchar; + if c='*' then + begin + current_scanner.skipoldtpcomment(true); + GetToken; + end + else + actasmtoken:=AS_LPAREN; + exit; + end; + + ')' : + begin + actasmtoken:=AS_RPAREN; + c:=current_scanner.asmgetchar; + exit; + end; + + ':' : + begin + actasmtoken:=AS_COLON; + c:=current_scanner.asmgetchar; + exit; + end; + + '+' : + begin + actasmtoken:=AS_PLUS; + c:=current_scanner.asmgetchar; + exit; + end; + + '-' : + begin + actasmtoken:=AS_MINUS; + c:=current_scanner.asmgetchar; + exit; + end; + + '*' : + begin + actasmtoken:=AS_STAR; + c:=current_scanner.asmgetchar; + exit; + end; + + '/' : + begin + c:=current_scanner.asmgetchar; + if c='/' then + begin + current_scanner.skipdelphicomment; + GetToken; + end + else + actasmtoken:=AS_SLASH; + exit; + end; + + '!', '~' : + begin + actasmtoken:=AS_NOT; + c:=current_scanner.asmgetchar; + exit; + end; + + '@' : { possiblities : - local label reference , such as in jmp @local1 } + { - @Result, @Code or @Data special variables. } + begin + actasmpattern:=c; + c:=current_scanner.asmgetchar; + while c in ['A'..'Z','a'..'z','0'..'9','_','@','$','&','?'] do + begin + actasmpattern:=actasmpattern + c; + c:=current_scanner.asmgetchar; + end; + actasmpattern_origcase:=actasmpattern; + uppervar(actasmpattern); + actasmtoken:=AS_ID; + exit; + end; + + #13,#10: + begin + current_scanner.linebreak; + c:=current_scanner.asmgetchar; + firsttoken:=TRUE; + actasmtoken:=AS_SEPARATOR; + exit; + end; + + ';' : + begin + c:=current_scanner.asmgetchar; + firsttoken:=TRUE; + actasmtoken:=AS_SEPARATOR; + exit; + end; + + else + current_scanner.illegal_char(c); + end; + end; + end; + + + function tz80reader.consume(t: tasmtoken): boolean; + begin + Consume:=true; + if t<>actasmtoken then + begin + Message2(scan_f_syn_expected,token2str[t],token2str[actasmtoken]); + Consume:=false; + end; + repeat + gettoken; + until actasmtoken<>AS_NONE; + end; + + + procedure tz80reader.RecoverConsume(allowcomma: boolean); + begin + while not (actasmtoken in [AS_SEPARATOR,AS_END]) do + begin + if allowcomma and (actasmtoken=AS_COMMA) then + break; + Consume(actasmtoken); + end; + end; + + + procedure tz80reader.AddReferences(dest, src: tz80operand); + + procedure AddRegister(reg:tregister;scalefactor:byte); + begin + if reg=NR_NO then + exit; + if (dest.opr.ref.base=NR_NO) and (scalefactor=1) then + begin + dest.opr.ref.base:=reg; + exit; + end; + if dest.opr.ref.index=NR_NO then + begin + dest.opr.ref.index:=reg; + dest.opr.ref.scalefactor:=scalefactor; + exit; + end; + if dest.opr.ref.index=reg then + begin + Inc(dest.opr.ref.scalefactor,scalefactor); + exit; + end; + Message(asmr_e_multiple_index); + end; + + var + tmplocal: TOprRec; + segreg: TRegister; + begin + case dest.opr.typ of + OPR_REFERENCE: + begin + case src.opr.typ of + OPR_REFERENCE: + begin + AddRegister(src.opr.ref.base,1); + AddRegister(src.opr.ref.index,src.opr.ref.scalefactor); + Inc(dest.opr.ref.offset,src.opr.ref.offset); + Inc(dest.opr.constoffset,src.opr.constoffset); + dest.haslabelref:=dest.haslabelref or src.haslabelref; + dest.hasproc:=dest.hasproc or src.hasproc; + dest.hasvar:=dest.hasvar or src.hasvar; + if assigned(src.opr.ref.symbol) then + begin + if assigned(dest.opr.ref.symbol) then + Message(asmr_e_cant_have_multiple_relocatable_symbols); + dest.opr.ref.symbol:=src.opr.ref.symbol; + end; + if assigned(src.opr.ref.relsymbol) then + begin + if assigned(dest.opr.ref.relsymbol) then + Message(asmr_e_cant_have_multiple_relocatable_symbols); + dest.opr.ref.relsymbol:=src.opr.ref.relsymbol; + end; + if dest.opr.ref.refaddr=addr_no then + dest.opr.ref.refaddr:=src.opr.ref.refaddr; + end; + OPR_LOCAL: + begin + tmplocal:=src.opr; + if dest.opr.ref.base<>NR_NO then + begin + if tmplocal.localindexreg=NR_NO then + begin + tmplocal.localindexreg:=dest.opr.ref.base; + tmplocal.localscale:=0; + end + else if tmplocal.localindexreg=dest.opr.ref.base then + tmplocal.localscale:=Min(tmplocal.localscale,1)+1 + else + Message(asmr_e_multiple_index); + end; + if dest.opr.ref.index<>NR_NO then + begin + if tmplocal.localindexreg=NR_NO then + begin + tmplocal.localindexreg:=dest.opr.ref.index; + tmplocal.localscale:=dest.opr.ref.scalefactor; + end + else if tmplocal.localindexreg=dest.opr.ref.index then + tmplocal.localscale:=Min(tmplocal.localscale,1)+Min(dest.opr.ref.scalefactor,1) + else + Message(asmr_e_multiple_index); + end; + Inc(tmplocal.localconstoffset,dest.opr.constoffset); + Inc(tmplocal.localsymofs,dest.opr.ref.offset); + dest.opr:=tmplocal; + end; + else + internalerror(2018030701); + end; + end; + OPR_LOCAL: + begin + case src.opr.typ of + OPR_REFERENCE: + begin + if src.opr.ref.base<>NR_NO then + begin + if dest.opr.localindexreg=NR_NO then + begin + dest.opr.localindexreg:=src.opr.ref.base; + dest.opr.localscale:=0; + end + else if dest.opr.localindexreg=src.opr.ref.base then + dest.opr.localscale:=Min(dest.opr.localscale,1)+1 + else + Message(asmr_e_multiple_index); + end; + if src.opr.ref.index<>NR_NO then + begin + if dest.opr.localindexreg=NR_NO then + begin + dest.opr.localindexreg:=src.opr.ref.index; + dest.opr.localscale:=src.opr.ref.scalefactor; + end + else if dest.opr.localindexreg=src.opr.ref.index then + dest.opr.localscale:=Min(dest.opr.localscale,1)+Min(src.opr.ref.scalefactor,1) + else + Message(asmr_e_multiple_index); + end; + Inc(dest.opr.localconstoffset,src.opr.constoffset); + Inc(dest.opr.localsymofs,src.opr.ref.offset); + end; + OPR_LOCAL: + Message(asmr_e_no_local_or_para_allowed); + else + internalerror(2018030703); + end; + end; + else + internalerror(2018030702); + end; + end; + + + function tz80reader.is_locallabel(const s: string): boolean; + begin + is_locallabel:=(length(s)>1) and (s[1]='@'); + end; + + + function tz80reader.is_asmopcode(const s: string):boolean; + begin + actcondition:=C_None; + actopcode:=tasmop(PtrUInt(iasmops.Find(s))); + if actopcode<>A_NONE then + begin + actasmtoken:=AS_OPCODE; + is_asmopcode:=true; + end + else + is_asmopcode:=false; + end; + + + function tz80reader.is_asmdirective(const s: string): boolean; + var + i : tasmtoken; + hs : string; + begin + hs:=lower(s); + for i:=firstdirective to lastdirective do + if hs=token2str[i] then + begin + actasmtoken:=i; + is_asmdirective:=true; + exit; + end; + is_asmdirective:=false; + end; + + + function tz80reader.is_register(const s:string):boolean; + begin + is_register:=false; + actasmregister:=std_regnum_search(lower(s)); + if actasmregister<>NR_NO then + begin + is_register:=true; + actasmtoken:=AS_REGISTER; + end; + end; + + + function tz80reader.is_condition(const s: string): boolean; + var + condstr: string; + cond: TAsmCond; + begin + is_condition:=false; + actasmcond:=C_None; + condstr:=lower(s); + for cond in TAsmCond do + if (cond<>C_None) and (cond2str[cond]=condstr) then + begin + is_condition:=true; + actasmtoken:=AS_CONDITION; + actasmcond:=cond; + exit; + end; + end; + + + function tz80reader.is_targetdirective(const s: string): boolean; + begin + result:=false; + end; + + procedure tz80reader.BuildRecordOffsetSize(const expr: string; out + offset: tcgint; out size: tcgint; out mangledname: string; + needvmtofs: boolean; out hastypecast: boolean); + var + s: string; + Begin + offset:=0; + size:=0; + mangledname:=''; + hastypecast:=false; + s:=expr; + while (actasmtoken=AS_DOT) do + begin + Consume(AS_DOT); + if actasmtoken in [AS_ID,AS_REGISTER] then + begin + s:=s+'.'+actasmpattern; + consume(actasmtoken); + end + else + begin + Consume(AS_ID); + RecoverConsume(true); + break; + end; + end; + if not GetRecordOffsetSize(s,offset,size,mangledname,needvmtofs,hastypecast) then + Message(asmr_e_building_record_offset); + end; + + procedure tz80reader.BuildConstSymbolExpression( + in_flags: tconstsymbolexpressioninputflags; out value: tcgint; out + asmsym: string; out asmsymtyp: TAsmsymtype; out size: tcgint; out + out_flags: tconstsymbolexpressionoutputflags); + var + tempstr,expr,hs,mangledname : string; + parenlevel : longint; + l,k : tcgint; + hasparen, + errorflag, + needvmtofs : boolean; + prevtok : tasmtoken; + hl : tasmlabel; + hssymtyp : Tasmsymtype; + def : tdef; + sym : tsym; + srsymtable : TSymtable; + hastypecast : boolean; + Begin + { reset } + value:=0; + asmsym:=''; + asmsymtyp:=AT_DATA; + size:=0; + out_flags:=[]; + errorflag:=FALSE; + tempstr:=''; + expr:=''; + if cseif_startingminus in in_flags then + expr:='-'; + inexpression:=TRUE; + parenlevel:=0; + sym:=nil; + needvmtofs:=FALSE; + Repeat + { Support ugly delphi constructs like: [ECX].1+2[EDX] } + if (cseif_isref in in_flags) and (actasmtoken=AS_LBRACKET) then + break; + if (cseif_referencelike in in_flags) and + (actasmtoken in [AS_LBRACKET,AS_RBRACKET]) then + case actasmtoken of + AS_LBRACKET: + begin + Consume(AS_LBRACKET); + if (length(expr)>0) and + not (expr[length(expr)] in ['+','-']) then + expr:=expr+'+'; + expr:=expr+'['; + end; + AS_RBRACKET: + begin + Consume(AS_RBRACKET); + expr:=expr+']'; + end; + else + ; + end; + Case actasmtoken of + AS_LPAREN: + Begin + Consume(AS_LPAREN); + expr:=expr + '('; + inc(parenlevel); + end; + AS_RPAREN: + Begin + { Keep the AS_PAREN in actasmtoken, it is maybe a typecast } + if parenlevel=0 then + break; + Consume(AS_RPAREN); + expr:=expr + ')'; + dec(parenlevel); + end; + AS_SHL: + Begin + Consume(AS_SHL); + expr:=expr + '<'; + end; + AS_SHR: + Begin + Consume(AS_SHR); + expr:=expr + '>'; + end; + AS_SLASH: + Begin + Consume(AS_SLASH); + expr:=expr + '/'; + end; + AS_MOD: + Begin + Consume(AS_MOD); + expr:=expr + '%'; + end; + AS_STAR: + Begin + Consume(AS_STAR); + if (cseif_isref in in_flags) and (actasmtoken=AS_REGISTER) then + break; + expr:=expr + '*'; + end; + AS_PLUS: + Begin + Consume(AS_PLUS); + if (cseif_isref in in_flags) and ((actasmtoken=AS_REGISTER) or (actasmtoken=AS_LBRACKET)) then + break; + expr:=expr + '+'; + end; + AS_MINUS: + Begin + Consume(AS_MINUS); + expr:=expr + '-'; + end; + AS_AND: + Begin + Consume(AS_AND); + expr:=expr + '&'; + end; + AS_NOT: + Begin + Consume(AS_NOT); + expr:=expr + '~'; + end; + AS_XOR: + Begin + Consume(AS_XOR); + expr:=expr + '^'; + end; + AS_OR: + Begin + Consume(AS_OR); + expr:=expr + '|'; + end; + AS_INTNUM: + Begin + expr:=expr + actasmpattern; + Consume(AS_INTNUM); + end; +{$ifdef i8086} + AS_SEG: + begin + include(out_flags,cseof_isseg); + Consume(actasmtoken); + if actasmtoken<>AS_ID then + Message(asmr_e_seg_without_identifier); + end; +{$endif i8086} + AS_VMTOFFSET{, + AS_OFFSET}: + begin + {if (actasmtoken = AS_OFFSET) then + begin + include(in_flags,cseif_needofs); + include(out_flags,cseof_hasofs); + end + else} + needvmtofs:=true; + Consume(actasmtoken); + if actasmtoken<>AS_ID then + Message(asmr_e_offset_without_identifier); + end; + AS_SIZEOF, + AS_TYPE: + begin + l:=0; + hasparen:=false; + Consume(actasmtoken); + if actasmtoken=AS_LPAREN then + begin + hasparen:=true; + Consume(AS_LPAREN); + end; + if actasmtoken<>AS_ID then + Message(asmr_e_type_without_identifier) + else + begin + tempstr:=actasmpattern; + Consume(AS_ID); + if actasmtoken=AS_DOT then + begin + BuildRecordOffsetSize(tempstr,k,l,mangledname,false,hastypecast); + if mangledname<>'' then + { procsym } + Message(asmr_e_wrong_sym_type); + if hastypecast then + + end + else + begin + asmsearchsym(tempstr,sym,srsymtable); + if assigned(sym) then + begin + case sym.typ of + staticvarsym, + localvarsym, + paravarsym : + l:=tabstractvarsym(sym).getsize; + typesym : + l:=ttypesym(sym).typedef.size; + else + Message(asmr_e_wrong_sym_type); + end; + end + else + Message1(sym_e_unknown_id,tempstr); + end; + end; + str(l, tempstr); + expr:=expr + tempstr; + if hasparen then + Consume(AS_RPAREN); + end; + //AS_PTR : + // begin + // { Support ugly delphi constructs like <constant> PTR [ref] } + // break; + // end; + AS_STRING: + begin + l:=0; + case Length(actasmpattern) of + 1 : + l:=ord(actasmpattern[1]); + 2 : + l:=ord(actasmpattern[2]) + ord(actasmpattern[1]) shl 8; + 3 : + l:=ord(actasmpattern[3]) + + Ord(actasmpattern[2]) shl 8 + ord(actasmpattern[1]) shl 16; + 4 : + l:=ord(actasmpattern[4]) + ord(actasmpattern[3]) shl 8 + + Ord(actasmpattern[2]) shl 16 + ord(actasmpattern[1]) shl 24; + else + Message1(asmr_e_invalid_string_as_opcode_operand,actasmpattern); + end; + str(l, tempstr); + expr:=expr + tempstr; + Consume(AS_STRING); + end; + AS_ID: + begin + hs:=''; + hssymtyp:=AT_DATA; + def:=nil; + tempstr:=actasmpattern; + prevtok:=prevasmtoken; + { stop parsing a constant expression if we find an opcode after a + non-operator like "db $66 mov eax,ebx" } + if (prevtok in [AS_ID,AS_INTNUM,AS_RPAREN]) and + is_asmopcode(actasmpattern) then + break; + consume(AS_ID); + if (tempstr='@CODE') or (tempstr='@DATA') then + begin + if asmsym='' then + begin + asmsym:=tempstr; + asmsymtyp:=AT_SECTION; + end + else + Message(asmr_e_cant_have_multiple_relocatable_symbols); + end + else if SearchIConstant(tempstr,l) then + begin + str(l, tempstr); + expr:=expr + tempstr; + end + else + begin + if is_locallabel(tempstr) then + begin + CreateLocalLabel(tempstr,hl,false); + hs:=hl.name; + hssymtyp:=AT_FUNCTION; + end + else + if SearchLabel(tempstr,hl,false) then + begin + hs:=hl.name; + hssymtyp:=AT_FUNCTION; + end + else + begin + asmsearchsym(tempstr,sym,srsymtable); + if assigned(sym) then + begin + case sym.typ of + staticvarsym : + begin + hs:=tstaticvarsym(sym).mangledname; + def:=tstaticvarsym(sym).vardef; + end; + localvarsym, + paravarsym : + begin + Message(asmr_e_no_local_or_para_allowed); + end; + procsym : + begin + if Tprocsym(sym).ProcdefList.Count>1 then + Message(asmr_w_calling_overload_func); + hs:=tprocdef(tprocsym(sym).ProcdefList[0]).mangledname; +{$ifdef i8086} + if is_proc_far(tprocdef(tprocsym(sym).ProcdefList[0])) + and not (po_interrupt in tprocdef(tprocsym(sym).ProcdefList[0]).procoptions) then + include(out_flags,cseof_is_farproc_entry) + else + exclude(out_flags,cseof_is_farproc_entry); +{$endif i8086} + hssymtyp:=AT_FUNCTION; + end; + typesym : + begin + if not(ttypesym(sym).typedef.typ in [recorddef,objectdef]) then + Message(asmr_e_wrong_sym_type); + size:=ttypesym(sym).typedef.size; + end; + fieldvarsym : + begin + tempstr:=upper(tdef(sym.owner.defowner).GetTypeName)+'.'+tempstr; + end; + else + Message(asmr_e_wrong_sym_type); + end; + end + else + Message1(sym_e_unknown_id,tempstr); + end; + { symbol found? } + if hs<>'' then + begin + if asmsym='' then + begin + asmsym:=hs; + asmsymtyp:=hssymtyp; + end + else + Message(asmr_e_cant_have_multiple_relocatable_symbols); + if (expr='') or (expr[length(expr)]='+') then + begin + { don't remove the + if there could be a record field } + if actasmtoken<>AS_DOT then + delete(expr,length(expr),1); + end + else + //if (cseif_needofs in in_flags) then + // begin + // if (prevtok<>AS_OFFSET) then + // Message(asmr_e_need_offset); + // end + //else + Message(asmr_e_only_add_relocatable_symbol); + end; + if (actasmtoken=AS_DOT) or + (assigned(sym) and + is_normal_fieldvarsym(sym)) then + begin + BuildRecordOffsetSize(tempstr,l,size,hs,needvmtofs,hastypecast); + if hs <> '' then + hssymtyp:=AT_FUNCTION + else + begin + str(l, tempstr); + expr:=expr + tempstr; + end + end + else if (actasmtoken<>AS_DOT) and + assigned(sym) and + (sym.typ=typesym) and + (ttypesym(sym).typedef.typ in [recorddef,objectdef]) then + begin + { just a record type (without being followed by dot) + evaluates to 0. Ugly, but TP7 compatible. } + expr:=expr+'0'; + end + else + begin + if (expr='') or (expr[length(expr)] in ['+','-','/','*']) then + delete(expr,length(expr),1); + end; + if (actasmtoken=AS_LBRACKET) and + assigned(def) and + (def.typ=arraydef) then + begin + consume(AS_LBRACKET); + l:=BuildConstExpression; + if l<tarraydef(def).lowrange then + begin + Message(asmr_e_constant_out_of_bounds); + l:=0; + end + else + l:=(l-tarraydef(def).lowrange)*tarraydef(def).elesize; + str(l, tempstr); + expr:=expr + '+' + tempstr; + consume(AS_RBRACKET); + end; + end; + { check if there are wrong operator used like / or mod etc. } + if (hs<>'') and not(actasmtoken in [AS_MINUS,AS_PLUS,AS_COMMA,AS_SEPARATOR,AS_END,AS_RBRACKET]) then + Message(asmr_e_only_add_relocatable_symbol); + end; + //AS_ALIGN, + //AS_DB, + //AS_DW, + //AS_DD, + //AS_DQ, + AS_END, + AS_RBRACKET, + AS_SEPARATOR, + AS_COMMA, + AS_COLON: + break; + else + begin + { write error only once. } + if not errorflag then + Message(asmr_e_invalid_constant_expression); + { consume tokens until we find COMMA or SEPARATOR } + Consume(actasmtoken); + errorflag:=TRUE; + end; + end; + Until false; + { calculate expression } + if not ErrorFlag then + value:=CalculateExpression(expr) + else + value:=0; + { no longer in an expression } + inexpression:=FALSE; + end; + + + function tz80reader.BuildConstExpression: longint; + var + l,size : tcgint; + hs : string; + hssymtyp : TAsmsymtype; + out_flags : tconstsymbolexpressionoutputflags; + begin + BuildConstSymbolExpression([],l,hs,hssymtyp,size,out_flags); + if hs<>'' then + Message(asmr_e_relocatable_symbol_not_allowed); + BuildConstExpression:=l; + end; + + + function tz80reader.BuildRefConstExpression(out size: tcgint; + startingminus: boolean): longint; + var + l : tcgint; + hs : string; + hssymtyp : TAsmsymtype; + in_flags : tconstsymbolexpressioninputflags; + out_flags : tconstsymbolexpressionoutputflags; + begin + in_flags:=[cseif_isref]; + if startingminus then + include(in_flags,cseif_startingminus); + BuildConstSymbolExpression(in_flags,l,hs,hssymtyp,size,out_flags); + if hs<>'' then + Message(asmr_e_relocatable_symbol_not_allowed); + BuildRefConstExpression:=l; + end; + + + procedure tz80reader.BuildConstantOperand(oper: tz80operand); + var + l,size : tcgint; + tempstr : string; + tempsymtyp : tasmsymtype; + cse_out_flags : tconstsymbolexpressionoutputflags; + begin + if not (oper.opr.typ in [OPR_NONE,OPR_CONSTANT]) then + Message(asmr_e_invalid_operand_type); + BuildConstSymbolExpression([cseif_needofs],l,tempstr,tempsymtyp,size,cse_out_flags); + if tempstr<>'' then + begin + oper.opr.typ:=OPR_SYMBOL; + oper.opr.symofs:=l; + oper.opr.symbol:=current_asmdata.RefAsmSymbol(tempstr,tempsymtyp); + oper.opr.symseg:=cseof_isseg in cse_out_flags; + oper.opr.sym_farproc_entry:=cseof_is_farproc_entry in cse_out_flags; + end + else + if oper.opr.typ=OPR_NONE then + begin + oper.opr.typ:=OPR_CONSTANT; + oper.opr.val:=l; + end + else + inc(oper.opr.val,l); + end; + + + procedure tz80reader.BuildReference(oper: tz80operand); + var + scale : byte; + k,l,size : tcgint; + tempstr,hs : string; + tempsymtyp : tasmsymtype; + code : integer; + hreg : tregister; + GotStar,GotOffset,HadVar, + GotPlus,Negative,BracketlessReference : boolean; + hl : tasmlabel; + hastypecast: boolean; + tmpoper: tz80operand; + cse_in_flags: tconstsymbolexpressioninputflags; + cse_out_flags: tconstsymbolexpressionoutputflags; + begin + if actasmtoken=AS_LPAREN then + begin + Consume(AS_LPAREN); + BracketlessReference:=false; + end + else + BracketlessReference:=true; + if not(oper.opr.typ in [OPR_LOCAL,OPR_REFERENCE]) then + oper.InitRef; + GotStar:=false; + GotPlus:=true; + GotOffset:=false; + Negative:=false; + Scale:=0; + repeat + if GotOffset and (actasmtoken<>AS_ID) then + Message(asmr_e_invalid_reference_syntax); + + Case actasmtoken of + AS_ID, { Constant reference expression OR variable reference expression } + AS_VMTOFFSET: + Begin + if not GotPlus then + Message(asmr_e_invalid_reference_syntax); + GotStar:=false; + GotPlus:=false; + if (actasmtoken = AS_VMTOFFSET) or + (SearchIConstant(actasmpattern,l) or + SearchRecordType(actasmpattern)) then + begin + l:=BuildRefConstExpression(size,negative); + if size<>0 then + oper.SetSize(size,false); + negative:=false; { "l" was negated if necessary } + GotPlus:=(prevasmtoken=AS_PLUS); + GotStar:=(prevasmtoken=AS_STAR); + case oper.opr.typ of + OPR_LOCAL : + begin + if GotStar then + Message(asmr_e_invalid_reference_syntax); + Inc(oper.opr.localsymofs,l); + end; + OPR_REFERENCE : + begin + if GotStar then + oper.opr.ref.scalefactor:=l + else + Inc(oper.opr.ref.offset,l); + end; + else + internalerror(2019050715); + end; + end + else + Begin + if negative and not oper.hasvar then + Message(asmr_e_only_add_relocatable_symbol) + else if oper.hasvar and not GotOffset and + (not negative or assigned(oper.opr.ref.relsymbol)) then + Message(asmr_e_cant_have_multiple_relocatable_symbols); + HadVar:=oper.hasvar and GotOffset; + tempstr:=actasmpattern; + Consume(AS_ID); + { typecasting? } + if (actasmtoken=AS_LPAREN) and + SearchType(tempstr,l) then + begin + oper.hastype:=true; + oper.typesize:=l; + Consume(AS_LPAREN); + BuildOperand(oper,true); + Consume(AS_RPAREN); + end + else + if is_locallabel(tempstr) then + begin + CreateLocalLabel(tempstr,hl,false); + oper.InitRef; + oper.haslabelref:=true; + if not negative then + begin + oper.opr.ref.symbol:=hl; + oper.hasvar:=true; + end + else + oper.opr.ref.relsymbol:=hl; +{$ifdef i8086} + if oper.opr.ref.segment=NR_NO then + oper.opr.ref.segment:=NR_CS; +{$endif i8086} + end + else + if oper.SetupVar(tempstr,GotOffset) then + begin + { convert OPR_LOCAL register para into a reference base } + if (oper.opr.typ=OPR_LOCAL) and + AsmRegisterPara(oper.opr.localsym) then + oper.InitRefConvertLocal + else + begin +{$ifdef x86_64} + if actasmtoken=AS_WRT then + begin + if (oper.opr.typ=OPR_REFERENCE) then + begin + Consume(AS_WRT); + Consume(AS___GOTPCREL); + if (oper.opr.ref.base<>NR_NO) or + (oper.opr.ref.index<>NR_NO) or + (oper.opr.ref.offset<>0) then + Message(asmr_e_wrong_gotpcrel_intel_syntax); + if tf_no_pic_supported in target_info.flags then + Message(asmr_e_no_gotpcrel_support); + oper.opr.ref.refaddr:=addr_pic; + oper.opr.ref.base:=NR_RIP; + end + else + message(asmr_e_invalid_reference_syntax); + end; +{$endif x86_64} + end; + end + else + Message1(sym_e_unknown_id,tempstr); + { record.field ? } + if actasmtoken=AS_DOT then + begin + BuildRecordOffsetSize(tempstr,l,k,hs,false,hastypecast); + if (hs<>'') then + Message(asmr_e_invalid_symbol_ref); + case oper.opr.typ of + OPR_LOCAL : + inc(oper.opr.localsymofs,l); + OPR_REFERENCE : + inc(oper.opr.ref.offset,l); + else + internalerror(2019050716); + end; + if hastypecast then + oper.hastype:=true; + oper.SetSize(k,false); + end; + if GotOffset then + begin + if oper.hasvar and (oper.opr.ref.base=current_procinfo.framepointer) then + begin + if (oper.opr.typ=OPR_REFERENCE) then + oper.opr.ref.base:=NR_NO; + oper.hasvar:=hadvar; + end + else + begin + if oper.hasvar and hadvar then + Message(asmr_e_cant_have_multiple_relocatable_symbols); + { should we allow ?? } + end; + end; + end; + GotOffset:=false; + end; + + AS_PLUS : + Begin + Consume(AS_PLUS); + Negative:=false; + GotPlus:=true; + GotStar:=false; + Scale:=0; + end; + + AS_DOT : + Begin + { Handle like a + } + Consume(AS_DOT); + Negative:=false; + GotPlus:=true; + GotStar:=false; + Scale:=0; + end; + + AS_MINUS : + begin + Consume(AS_MINUS); + Negative:=true; + GotPlus:=true; + GotStar:=false; + Scale:=0; + end; + + AS_STAR : { Scaling, with eax*4 order } + begin + Consume(AS_STAR); + hs:=''; + l:=0; + case actasmtoken of + AS_ID, + AS_LPAREN : + l:=BuildConstExpression; + AS_INTNUM: + Begin + hs:=actasmpattern; + Consume(AS_INTNUM); + end; + AS_REGISTER : + begin + case oper.opr.typ of + OPR_REFERENCE : + begin + if oper.opr.ref.scalefactor=0 then + begin + if scale<>0 then + begin + oper.opr.ref.scalefactor:=scale; + scale:=0; + end + else + Message(asmr_e_wrong_scale_factor); + end + else + Message(asmr_e_invalid_reference_syntax); + end; + OPR_LOCAL : + begin + if oper.opr.localscale=0 then + begin + if scale<>0 then + begin + oper.opr.localscale:=scale; + scale:=0; + end + else + Message(asmr_e_wrong_scale_factor); + end + else + Message(asmr_e_invalid_reference_syntax); + end; + else + internalerror(2019050719); + end; + end; + else + Message(asmr_e_invalid_reference_syntax); + end; + if actasmtoken<>AS_REGISTER then + begin + if hs<>'' then + val(hs,l,code); + case oper.opr.typ of + OPR_REFERENCE : + oper.opr.ref.scalefactor:=l; + OPR_LOCAL : + oper.opr.localscale:=l; + else + internalerror(2019050717); + end; + if l>9 then + Message(asmr_e_wrong_scale_factor); + end; + GotPlus:=false; + GotStar:=false; + end; + + AS_REGISTER : + begin + hreg:=actasmregister; + + Consume(AS_REGISTER); + + if not((GotPlus and (not Negative)) or + GotStar) then + Message(asmr_e_invalid_reference_syntax); + { this register will be the index: + 1. just read a * + 2. next token is a * + 3. base register is already used } + case oper.opr.typ of + OPR_LOCAL : + begin + if (oper.opr.localindexreg<>NR_NO) then + Message(asmr_e_multiple_index); + oper.opr.localindexreg:=hreg; + if scale<>0 then + begin + oper.opr.localscale:=scale; + scale:=0; + end; + end; + OPR_REFERENCE : + begin + if (GotStar) or + (actasmtoken=AS_STAR) or + (oper.opr.ref.base<>NR_NO) then + begin + if (oper.opr.ref.index<>NR_NO) then + Message(asmr_e_multiple_index); + oper.opr.ref.index:=hreg; + if scale<>0 then + begin + oper.opr.ref.scalefactor:=scale; + scale:=0; + end; + end + else + begin + oper.opr.ref.base:=hreg; +{$ifdef x86_64} + { non-GOT based RIP-relative accesses are also position-independent } + if (oper.opr.ref.base=NR_RIP) and + (oper.opr.ref.refaddr<>addr_pic) then + oper.opr.ref.refaddr:=addr_pic_no_got; +{$endif x86_64} + end; + end; + else + internalerror(2019050718); + end; + GotPlus:=false; + GotStar:=false; + end; + + //AS_OFFSET : + // begin + // Consume(AS_OFFSET); + // GotOffset:=true; + // end; + + AS_TYPE, + AS_NOT, + AS_STRING, + AS_INTNUM, + AS_LPAREN : { Constant reference expression } + begin + if not GotPlus and not GotStar then + Message(asmr_e_invalid_reference_syntax); + cse_in_flags:=[cseif_needofs,cseif_isref]; + if GotPlus and negative then + include(cse_in_flags,cseif_startingminus); + BuildConstSymbolExpression(cse_in_flags,l,tempstr,tempsymtyp,size,cse_out_flags); + { already handled by BuildConstSymbolExpression(); must be + handled there to avoid [reg-1+1] being interpreted as + [reg-(1+1)] } + negative:=false; + + if tempstr<>'' then + begin + if GotStar then + Message(asmr_e_only_add_relocatable_symbol); + if not assigned(oper.opr.ref.symbol) then + begin + oper.opr.ref.symbol:=current_asmdata.RefAsmSymbol(tempstr,tempsymtyp); +{$ifdef i8086} + if cseof_isseg in cse_out_flags then + begin + if not (oper.opr.ref.refaddr in [addr_fardataseg,addr_dgroup]) then + oper.opr.ref.refaddr:=addr_seg; + end + else if (tempsymtyp=AT_FUNCTION) and (oper.opr.ref.segment=NR_NO) then + oper.opr.ref.segment:=NR_CS; +{$endif i8086} + end + else + Message(asmr_e_cant_have_multiple_relocatable_symbols); + end; + case oper.opr.typ of + OPR_REFERENCE : + begin + if GotStar then + oper.opr.ref.scalefactor:=l + else if (prevasmtoken = AS_STAR) then + begin + if scale<>0 then + scale:=l*scale + else + scale:=l; + end + else + begin + Inc(oper.opr.ref.offset,l); + Inc(oper.opr.constoffset,l); + end; + end; + OPR_LOCAL : + begin + if GotStar then + oper.opr.localscale:=l + else if (prevasmtoken = AS_STAR) then + begin + if scale<>0 then + scale:=l*scale + else + scale:=l; + end + else + Inc(oper.opr.localsymofs,l); + end; + else + internalerror(2019050714); + end; + GotPlus:=(prevasmtoken=AS_PLUS) or + (prevasmtoken=AS_MINUS); + if GotPlus then + negative := prevasmtoken = AS_MINUS; + GotStar:=(prevasmtoken=AS_STAR); + end; + + //AS_LBRACKET : + // begin + // if (GotPlus and Negative) or GotStar then + // Message(asmr_e_invalid_reference_syntax); + // tmpoper:=Tz80Operand.create; + // BuildReference(tmpoper); + // AddReferences(oper,tmpoper); + // tmpoper.Free; + // GotPlus:=false; + // GotStar:=false; + // end; + + AS_RPAREN : + begin + if GotPlus or GotStar or BracketlessReference then + Message(asmr_e_invalid_reference_syntax); + + Consume(AS_RPAREN); + + + + if actasmtoken=AS_LPAREN then + begin + tmpoper:=Tz80Operand.create; + BuildReference(tmpoper); + AddReferences(oper,tmpoper); + tmpoper.Free; + end; + break; + end; + + AS_SEPARATOR, + AS_END, + AS_COMMA: + begin + if not BracketlessReference then + begin + Message(asmr_e_invalid_reference_syntax); + RecoverConsume(true); + end; + break; + end; + + else + Begin + Message(asmr_e_invalid_reference_syntax); + RecoverConsume(true); + break; + end; + end; + until false; + end; + + + procedure tz80reader.BuildOperand(oper: tz80operand; istypecast: boolean); + + procedure AddLabelOperand(hl:tasmlabel); + begin + if (oper.opr.typ=OPR_NONE) and + is_calljmp(actopcode) then + begin + oper.opr.typ:=OPR_SYMBOL; + oper.opr.symbol:=hl; + end + else + begin + oper.InitRef; + oper.opr.ref.symbol:=hl; + oper.haslabelref:=true; + end; + end; + + var + l: tcgint; + tsize: tcgint; + expr: string; + hl: tasmlabel; + begin + repeat + case actasmtoken of + //AS_OFFSET, + AS_SIZEOF, + AS_VMTOFFSET, + AS_TYPE, + AS_NOT, + AS_STRING, + AS_PLUS, + AS_MINUS, +// AS_LPAREN, + AS_INTNUM : + begin + case oper.opr.typ of + OPR_REFERENCE : + begin + l := BuildRefConstExpression(tsize); + if tsize<>0 then + oper.SetSize(tsize,false); + inc(oper.opr.ref.offset,l); + inc(oper.opr.constoffset,l); + end; + OPR_LOCAL : + begin + l := BuildConstExpression; + inc(oper.opr.localsymofs,l); + inc(oper.opr.localconstoffset,l); + end; + + OPR_NONE, + OPR_CONSTANT : + BuildConstantOperand(oper); + else + Message(asmr_e_invalid_operand_type); + end; + end; + + AS_LPAREN: + begin + BuildReference(oper); + end; + + AS_ID : { A constant expression, or a Variable ref. } + Begin + { Label or Special symbol reference? } + if actasmpattern[1] = '@' then + Begin + if actasmpattern = '@RESULT' then + Begin + oper.SetupResult; + Consume(AS_ID); + expr:='result'; + end + else + if (actasmpattern = '@CODE') or (actasmpattern = '@DATA') then + begin + Message(asmr_w_CODE_and_DATA_not_supported); + Consume(AS_ID); + end + else + { Local Label } + begin + CreateLocalLabel(actasmpattern,hl,false); + Consume(AS_ID); + AddLabelOperand(hl); + end; + end + else + { support result for delphi modes } + if (m_objpas in current_settings.modeswitches) and (actasmpattern='RESULT') then + begin + oper.SetUpResult; + Consume(AS_ID); + expr:='result'; + end + { probably a variable or normal expression } + { or a procedure (such as in CALL ID) } + else + Begin + { is it a constant ? } + if SearchIConstant(actasmpattern,l) then + Begin + case oper.opr.typ of + OPR_REFERENCE : + begin + l := BuildRefConstExpression(tsize); + if tsize<>0 then + oper.SetSize(tsize,false); + inc(oper.opr.ref.offset,l); + inc(oper.opr.constoffset,l); + end; + + OPR_LOCAL : + begin + l := BuildRefConstExpression(tsize); + if tsize<>0 then + oper.SetSize(tsize,false); + inc(oper.opr.localsymofs,l); + inc(oper.opr.localconstoffset,l); + end; + OPR_NONE, + OPR_CONSTANT : + BuildConstantOperand(oper); + else + Message(asmr_e_invalid_operand_type); + end; + end + else + { Check for pascal label } + if SearchLabel(actasmpattern,hl,false) then + begin + Consume(AS_ID); + AddLabelOperand(hl); + end + else + { is it a normal variable ? } + Begin + expr:=actasmpattern; + Consume(AS_ID); + + + { typecasting? } + if SearchType(expr,l) then + begin + oper.hastype:=true; + oper.typesize:=l; + case actasmtoken of + AS_LPAREN : + begin + { Support Type([Reference]) } + Consume(AS_LPAREN); + BuildOperand(oper,true); + { Delphi also supports Type(Register) and + interprets it the same as Type([Register]). } + if (oper.opr.typ = OPR_REGISTER) then + { This also sets base to the register. } + oper.InitRef; + Consume(AS_RPAREN); + end; + //AS_LBRACKET : + // begin + // { Support Var.Type[Index] } + // { Convert @label.Byte[1] to reference } + // if oper.opr.typ=OPR_SYMBOL then + // oper.initref; + // end; + else + ; + end; + end + else + begin + if not oper.SetupVar(expr,false) then + Begin + { not a variable, check special variables.. } + if expr = 'SELF' then + begin + oper.SetupSelf; + expr:='self'; + end + else + begin + Message1(sym_e_unknown_id,expr); + expr:=''; + end; + end; + { indexed access to variable? } + //if actasmtoken=AS_LBRACKET then + // begin + // { ... then the operand size is not known anymore } + // oper.size:=OS_NO; + // BuildReference(oper); + // end; + end; + end; + end; + end; + + AS_REGISTER : { Register, a variable reference or a constant reference } + begin + Consume(AS_REGISTER); + + { Simple register } + if (oper.opr.typ <> OPR_NONE) then + Message(asmr_e_syn_operand); + oper.opr.typ:=OPR_REGISTER; + oper.opr.reg:=actasmregister; + oper.SetSize(tcgsize2size[reg_cgsize(oper.opr.reg)],true); + end; + + AS_SEPARATOR, + AS_END, + AS_COMMA: + begin + break; + end; + + else + begin + Message(asmr_e_syn_operand); + RecoverConsume(true); + break; + end; + end; + until false; + end; + + + procedure tz80reader.BuildOpCode(instr: TZ80Instruction); + var + operandnum: Integer; + begin + instr.opcode:=actopcode; + operandnum:=1; + Consume(AS_OPCODE); + { Zero operand opcode ? } + if actasmtoken in [AS_SEPARATOR,AS_END] then + exit; + { Condition (e.g. 'NC' in 'JP NC, label') } + if actasmtoken=AS_CONDITION then + begin + instr.condition:=actasmcond; + Consume(AS_CONDITION); + if actasmtoken in [AS_SEPARATOR,AS_END] then + exit; + if actasmtoken=AS_COMMA then + Consume(AS_COMMA); + end; + { Read Operands } + repeat + case actasmtoken of + { End of asm operands for this opcode } + AS_END, + AS_SEPARATOR : + break; + + { Operand delimiter } + AS_COMMA : + begin + { should have something before the comma } + if instr.operands[operandnum].opr.typ=OPR_NONE then + Message(asmr_e_syntax_error); + if operandnum >= max_operands then + Message(asmr_e_too_many_operands) + else + Inc(operandnum); + Consume(AS_COMMA); + end; + else + BuildOperand(instr.Operands[operandnum] as tz80operand,false); + end; + until false; + instr.ops:=operandnum; + end; + + + procedure tz80reader.handleopcode; + var + instr: TZ80Instruction; + begin + instr:=TZ80Instruction.create(TZ80Operand); + BuildOpcode(instr); + with instr do + begin + //CheckNonCommutativeOpcodes; + //AddReferenceSizes; + //SetInstructionOpsize; + //CheckOperandSizes; + ConcatInstruction(curlist); + end; + instr.Free; + end; + + + procedure tz80reader.ConvertCalljmp(instr : tz80instruction); + var + newopr : toprrec; + begin + if instr.Operands[1].opr.typ=OPR_REFERENCE then + begin + newopr.typ:=OPR_SYMBOL; + newopr.symbol:=instr.Operands[1].opr.ref.symbol; + newopr.symofs:=instr.Operands[1].opr.ref.offset; + if (instr.Operands[1].opr.ref.base<>NR_NO) or + (instr.Operands[1].opr.ref.index<>NR_NO) then + Message(asmr_e_syn_operand); + instr.Operands[1].opr:=newopr; + end; + end; + + + function tz80reader.Assemble: tlinkedlist; + var + hl: tasmlabel; + begin + Message1(asmr_d_start_reading,'Z80'); + firsttoken:=TRUE; + { sets up all opcode and register tables in uppercase } + if not _asmsorted then + begin + SetupTables; + _asmsorted:=TRUE; + end; + curlist:=TAsmList.Create; + + { we might need to know which parameters are passed in registers } + if not parse_generic then + current_procinfo.generate_parameter_info; + + { start tokenizer } + gettoken; + { main loop } + repeat + case actasmtoken of + AS_LLABEL: + Begin + if CreateLocalLabel(actasmpattern,hl,true) then + ConcatLabel(curlist,hl); + Consume(AS_LLABEL); + end; + + AS_LABEL: + Begin + if SearchLabel(upper(actasmpattern),hl,true) then + begin + if hl.is_public then + ConcatPublic(curlist,actasmpattern_origcase); + ConcatLabel(curlist,hl); + end + else + Message1(asmr_e_unknown_label_identifier,actasmpattern); + Consume(AS_LABEL); + end; + + AS_END: + begin + break; { end assembly block } + end; + + AS_SEPARATOR: + begin + Consume(AS_SEPARATOR); + end; + + AS_OPCODE: + begin + HandleOpCode; + end; + + else + begin + Message(asmr_e_syntax_error); + RecoverConsume(false); + end; + end; + until false; + { check that all referenced local labels are defined } + checklocallabels; + { Return the list in an asmnode } + assemble:=curlist; + Message1(asmr_d_finish_reading,'Z80'); + end; + + +{***************************************************************************** + Initialize +*****************************************************************************} + +const +{ asmmode_z80_att_info : tasmmodeinfo = + ( + id : asmmode_z80_gas; + idtxt : 'GAS'; + casmreader : tz80attreader; + );} + + asmmode_z80_standard_info : tasmmodeinfo = + ( + id : asmmode_standard; + idtxt : 'STANDARD'; + casmreader : tz80reader; + ); + +initialization +// RegisterAsmMode(asmmode_z80_att_info); + RegisterAsmMode(asmmode_z80_standard_info); +end. |