// cmd/9a/a.y from Vita Nuova.
//
//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
//	Portions Copyright © 1997-1999 Vita Nuova Limited
//	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
//	Portions Copyright © 2004,2006 Bruce Ellis
//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
//	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

%{
package main

import (
	"cmd/internal/asm"
	"cmd/internal/obj"
	. "cmd/internal/obj/ppc64"
)
%}

%union
{
	sym *asm.Sym
	lval int64
	dval float64
	sval string
	addr obj.Addr
}

%left	'|'
%left	'^'
%left	'&'
%left	'<' '>'
%left	'+' '-'
%left	'*' '/' '%'
%token	<lval>	LMOVW LMOVB LABS LLOGW LSHW LADDW LCMP LCROP
%token	<lval>	LBRA LFMOV LFCONV LFCMP LFADD LFMA LTRAP LXORW
%token	<lval>	LNOP LEND LRETT LWORD LTEXT LDATA LGLOBL LRETRN
%token	<lval>	LCONST LSP LSB LFP LPC LCREG LFLUSH
%token	<lval>	LREG LFREG LR LCR LF LFPSCR
%token	<lval>	LLR LCTR LSPR LSPREG LSEG LMSR
%token	<lval>	LPCDAT LFUNCDAT LSCHED LXLD LXST LXOP LXMV
%token	<lval>	LRLWM LMOVMW LMOVEM LMOVFL LMTFSB LMA
%token	<dval>	LFCONST
%token	<sval>	LSCONST
%token	<sym>	LNAME LLAB LVAR
%type	<lval>	con expr pointer offset sreg
%type	<addr>	addr rreg regaddr name creg freg xlreg lr ctr textsize
%type	<addr>	imm ximm fimm rel psr lcr cbit fpscr msr mask
%%
prog:
|	prog
	{
		stmtline = asm.Lineno
	}
	line

line:
	LNAME ':'
	{
		$1 = asm.LabelLookup($1);
		if $1.Type == LLAB && $1.Value != int64(asm.PC) {
			yyerror("redeclaration of %s", $1.Labelname)
		}
		$1.Type = LLAB;
		$1.Value = int64(asm.PC);
	}
	line
|	LNAME '=' expr ';'
	{
		$1.Type = LVAR;
		$1.Value = $3;
	}
|	LVAR '=' expr ';'
	{
		if $1.Value != $3 {
			yyerror("redeclaration of %s", $1.Name)
		}
		$1.Value = $3;
	}
|	LSCHED ';'
	{
		nosched = int($1);
	}
|	';'
|	inst ';'
|	error ';'

inst:
/*
 * load ints and bytes
 */
	LMOVW rreg ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW addr ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW regaddr ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVB rreg ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVB addr ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVB regaddr ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
/*
 * load floats
 */
|	LFMOV addr ',' freg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LFMOV regaddr ',' freg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LFMOV fimm ',' freg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LFMOV freg ',' freg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LFMOV freg ',' addr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LFMOV freg ',' regaddr
	{
		outcode(int($1), &$2, 0, &$4);
	}
/*
 * store ints and bytes
 */
|	LMOVW rreg ',' addr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW rreg ',' regaddr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVB rreg ',' addr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVB rreg ',' regaddr
	{
		outcode(int($1), &$2, 0, &$4);
	}
/*
 * store floats
 */
|	LMOVW freg ',' addr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW freg ',' regaddr
	{
		outcode(int($1), &$2, 0, &$4);
	}
/*
 * floating point status
 */
|	LMOVW fpscr ',' freg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW freg ','  fpscr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW freg ',' imm ',' fpscr
	{
		outgcode(int($1), &$2, 0, &$4, &$6);
	}
|	LMOVW fpscr ',' creg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMTFSB imm ',' con
	{
		outcode(int($1), &$2, int($4), &nullgen);
	}
/*
 * field moves (mtcrf)
 */
|	LMOVW rreg ',' imm ',' lcr
	{
		outgcode(int($1), &$2, 0, &$4, &$6);
	}
|	LMOVW rreg ',' creg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW rreg ',' lcr
	{
		outcode(int($1), &$2, 0, &$4);
	}
/*
 * integer operations
 * logical instructions
 * shift instructions
 * unary instructions
 */
|	LADDW rreg ',' sreg ',' rreg
	{
		outcode(int($1), &$2, int($4), &$6);
	}
|	LADDW imm ',' sreg ',' rreg
	{
		outcode(int($1), &$2, int($4), &$6);
	}
|	LADDW rreg ',' imm ',' rreg
	{
		outgcode(int($1), &$2, 0, &$4, &$6);
	}
|	LADDW rreg ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LADDW imm ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LLOGW rreg ',' sreg ',' rreg
	{
		outcode(int($1), &$2, int($4), &$6);
	}
|	LLOGW rreg ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LSHW rreg ',' sreg ',' rreg
	{
		outcode(int($1), &$2, int($4), &$6);
	}
|	LSHW rreg ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LSHW imm ',' sreg ',' rreg
	{
		outcode(int($1), &$2, int($4), &$6);
	}
|	LSHW imm ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LABS rreg ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LABS rreg
	{
		outcode(int($1), &$2, 0, &$2);
	}
/*
 * multiply-accumulate
 */
|	LMA rreg ',' sreg ',' rreg
	{
		outcode(int($1), &$2, int($4), &$6);
	}
/*
 * move immediate: macro for cau+or, addi, addis, and other combinations
 */
|	LMOVW imm ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW ximm ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
/*
 * condition register operations
 */
|	LCROP cbit ',' cbit
	{
		outcode(int($1), &$2, int($4.Reg), &$4);
	}
|	LCROP cbit ',' con ',' cbit
	{
		outcode(int($1), &$2, int($4), &$6);
	}
/*
 * condition register moves
 * move from machine state register
 */
|	LMOVW creg ',' creg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW psr ',' creg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW lcr ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW psr ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW xlreg ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW rreg ',' xlreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW creg ',' psr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVW rreg ',' psr
	{
		outcode(int($1), &$2, 0, &$4);
	}
/*
 * branch, branch conditional
 * branch conditional register
 * branch conditional to count register
 */
|	LBRA rel
	{
		outcode(int($1), &nullgen, 0, &$2);
	}
|	LBRA addr
	{
		outcode(int($1), &nullgen, 0, &$2);
	}
|	LBRA '(' xlreg ')'
	{
		outcode(int($1), &nullgen, 0, &$3);
	}
|	LBRA ',' rel
	{
		outcode(int($1), &nullgen, 0, &$3);
	}
|	LBRA ',' addr
	{
		outcode(int($1), &nullgen, 0, &$3);
	}
|	LBRA ',' '(' xlreg ')'
	{
		outcode(int($1), &nullgen, 0, &$4);
	}
|	LBRA creg ',' rel
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LBRA creg ',' addr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LBRA creg ',' '(' xlreg ')'
	{
		outcode(int($1), &$2, 0, &$5);
	}
|	LBRA con ',' rel
	{
		outcode(int($1), &nullgen, int($2), &$4);
	}
|	LBRA con ',' addr
	{
		outcode(int($1), &nullgen, int($2), &$4);
	}
|	LBRA con ',' '(' xlreg ')'
	{
		outcode(int($1), &nullgen, int($2), &$5);
	}
|	LBRA con ',' con ',' rel
	{
		var g obj.Addr
		g = nullgen;
		g.Type = obj.TYPE_CONST;
		g.Offset = $2;
		outcode(int($1), &g, int(REG_R0+$4), &$6);
	}
|	LBRA con ',' con ',' addr
	{
		var g obj.Addr
		g = nullgen;
		g.Type = obj.TYPE_CONST;
		g.Offset = $2;
		outcode(int($1), &g, int(REG_R0+$4), &$6);
	}
|	LBRA con ',' con ',' '(' xlreg ')'
	{
		var g obj.Addr
		g = nullgen;
		g.Type = obj.TYPE_CONST;
		g.Offset = $2;
		outcode(int($1), &g, int(REG_R0+$4), &$7);
	}
/*
 * conditional trap
 */
|	LTRAP rreg ',' sreg
	{
		outcode(int($1), &$2, int($4), &nullgen);
	}
|	LTRAP imm ',' sreg
	{
		outcode(int($1), &$2, int($4), &nullgen);
	}
|	LTRAP rreg comma
	{
		outcode(int($1), &$2, 0, &nullgen);
	}
|	LTRAP comma
	{
		outcode(int($1), &nullgen, 0, &nullgen);
	}
/*
 * floating point operate
 */
|	LFCONV freg ',' freg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LFADD freg ',' freg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LFADD freg ',' freg ',' freg
	{
		outcode(int($1), &$2, int($4.Reg), &$6);
	}
|	LFMA freg ',' freg ',' freg ',' freg
	{
		outgcode(int($1), &$2, int($4.Reg), &$6, &$8);
	}
|	LFCMP freg ',' freg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LFCMP freg ',' freg ',' creg
	{
		outcode(int($1), &$2, int($6.Reg), &$4);
	}
/*
 * CMP
 */
|	LCMP rreg ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LCMP rreg ',' imm
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LCMP rreg ',' rreg ',' creg
	{
		outcode(int($1), &$2, int($6.Reg), &$4);
	}
|	LCMP rreg ',' imm ',' creg
	{
		outcode(int($1), &$2, int($6.Reg), &$4);
	}
/*
 * rotate and mask
 */
|	LRLWM  imm ',' rreg ',' imm ',' rreg
	{
		outgcode(int($1), &$2, int($4.Reg), &$6, &$8);
	}
|	LRLWM  imm ',' rreg ',' mask ',' rreg
	{
		outgcode(int($1), &$2, int($4.Reg), &$6, &$8);
	}
|	LRLWM  rreg ',' rreg ',' imm ',' rreg
	{
		outgcode(int($1), &$2, int($4.Reg), &$6, &$8);
	}
|	LRLWM  rreg ',' rreg ',' mask ',' rreg
	{
		outgcode(int($1), &$2, int($4.Reg), &$6, &$8);
	}
/*
 * load/store multiple
 */
|	LMOVMW addr ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LMOVMW rreg ',' addr
	{
		outcode(int($1), &$2, 0, &$4);
	}
/*
 * various indexed load/store
 * indexed unary (eg, cache clear)
 */
|	LXLD regaddr ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LXLD regaddr ',' imm ',' rreg
	{
		outgcode(int($1), &$2, 0, &$4, &$6);
	}
|	LXST rreg ',' regaddr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LXST rreg ',' imm ',' regaddr
	{
		outgcode(int($1), &$2, 0, &$4, &$6);
	}
|	LXMV regaddr ',' rreg
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LXMV rreg ',' regaddr
	{
		outcode(int($1), &$2, 0, &$4);
	}
|	LXOP regaddr
	{
		outcode(int($1), &$2, 0, &nullgen);
	}
/*
 * NOP
 */
|	LNOP comma
	{
		outcode(int($1), &nullgen, 0, &nullgen);
	}
|	LNOP rreg comma
	{
		outcode(int($1), &$2, 0, &nullgen);
	}
|	LNOP freg comma
	{
		outcode(int($1), &$2, 0, &nullgen);
	}
|	LNOP ',' rreg
	{
		outcode(int($1), &nullgen, 0, &$3);
	}
|	LNOP ',' freg
	{
		outcode(int($1), &nullgen, 0, &$3);
	}
|	LNOP imm /* SYSCALL $num: load $num to R0 before syscall and restore R0 to 0 afterwards. */
	{
		outcode(int($1), &$2, 0, &nullgen);
	}
/*
 * word
 */
|	LWORD imm comma
	{
		outcode(int($1), &$2, 0, &nullgen);
	}
|	LWORD ximm comma
	{
		outcode(int($1), &$2, 0, &nullgen);
	}
/*
 * PCDATA
 */
|	LPCDAT imm ',' imm
	{
		if $2.Type != obj.TYPE_CONST || $4.Type != obj.TYPE_CONST {
			yyerror("arguments to PCDATA must be integer constants")
		}
		outcode(int($1), &$2, 0, &$4);
	}
/*
 * FUNCDATA
 */
|	LFUNCDAT imm ',' addr
	{
		if $2.Type != obj.TYPE_CONST {
			yyerror("index for FUNCDATA must be integer constant")
		}
		if $4.Type != obj.TYPE_MEM || ($4.Name != obj.NAME_EXTERN && $4.Name != obj.NAME_STATIC) {
			yyerror("value for FUNCDATA must be symbol reference")
		}
 		outcode(int($1), &$2, 0, &$4);
	}
/*
 * END
 */
|	LEND comma
	{
		outcode(int($1), &nullgen, 0, &nullgen);
	}
/*
 * TEXT
 */
|	LTEXT name ',' '$' textsize
	{
		asm.Settext($2.Sym);
		outcode(int($1), &$2, 0, &$5);
		if asm.Pass > 1 {
			lastpc.From3 = new(obj.Addr)
		}
	}
|	LTEXT name ',' con ',' '$' textsize
	{
		asm.Settext($2.Sym);
		outcode(int($1), &$2, int($4), &$7);
		if asm.Pass > 1 {
			lastpc.From3 = new(obj.Addr)
			lastpc.From3.Type = obj.TYPE_CONST
			lastpc.From3.Offset = $4
		}
	}
/*
 * GLOBL
 */
|	LGLOBL name ',' imm
	{
		asm.Settext($2.Sym)
		outcode(int($1), &$2, 0, &$4)
		if asm.Pass > 1 {
			lastpc.From3 = new(obj.Addr)
		}
	}
|	LGLOBL name ',' con ',' imm
	{
		asm.Settext($2.Sym)
		outcode(int($1), &$2, 0, &$6)
		if asm.Pass > 1 {
			lastpc.From3 = new(obj.Addr)
			lastpc.From3.Type = obj.TYPE_CONST
			lastpc.From3.Offset = $4
		}
	}

/*
 * DATA
 */
|	LDATA name '/' con ',' imm
	{
		outcode(int($1), &$2, 0, &$6);
		if asm.Pass > 1 {
			lastpc.From3 = new(obj.Addr)
			lastpc.From3.Type = obj.TYPE_CONST
			lastpc.From3.Offset = $4
		}
	}
|	LDATA name '/' con ',' ximm
	{
		outcode(int($1), &$2, 0, &$6);
		if asm.Pass > 1 {
			lastpc.From3 = new(obj.Addr)
			lastpc.From3.Type = obj.TYPE_CONST
			lastpc.From3.Offset = $4
		}
	}
|	LDATA name '/' con ',' fimm
	{
		outcode(int($1), &$2, 0, &$6);
		if asm.Pass > 1 {
			lastpc.From3 = new(obj.Addr)
			lastpc.From3.Type = obj.TYPE_CONST
			lastpc.From3.Offset = $4
		}
	}
/*
 * RETURN
 */
|	LRETRN	comma
	{
		outcode(int($1), &nullgen, 0, &nullgen);
	}

rel:
	con '(' LPC ')'
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_BRANCH;
		$$.Offset = $1 + int64(asm.PC);
	}
|	LNAME offset
	{
		$1 = asm.LabelLookup($1);
		$$ = nullgen;
		if asm.Pass == 2 && $1.Type != LLAB {
			yyerror("undefined label: %s", $1.Labelname)
		}
		$$.Type = obj.TYPE_BRANCH;
		$$.Offset = $1.Value + $2;
	}

rreg:
	sreg
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1);
	}

xlreg:
	lr
|	ctr

lr:
	LLR
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1);
	}

lcr:
	LCR
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1);	/* whole register */
	}

ctr:
	LCTR
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1);
	}

msr:
	LMSR
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1)
	}

psr:
	LSPREG
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1);
	}
|	LSPR '(' con ')'
	{
		if $3 < 0 || $3 >= 1024 {
			yyerror("SPR/DCR out of range")
		}
		$$ = nullgen;
		$$.Type = obj.TYPE_REG
		$$.Reg = int16($1 + $3);
	}
|	msr

fpscr:
	LFPSCR
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1);
	}

freg:
	LFREG
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1);
	}
|	LF '(' con ')'
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16(REG_F0 + $3);
	}

creg:
	LCREG
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1);
	}
|	LCR '(' con ')'
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16(REG_CR0 + $3);
	}


cbit:	con
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_REG;
		$$.Reg = int16($1);
	}

mask:
	con ',' con
	{
		var mb, me int
		var v uint32

		$$ = nullgen;
		$$.Type = obj.TYPE_CONST;
		mb = int($1);
		me = int($3);
		if(mb < 0 || mb > 31 || me < 0 || me > 31){
			yyerror("illegal mask start/end value(s)");
			mb = 0
			me = 0;
		}
		if mb <= me {
			v = (^uint32(0)>>uint(mb)) & (^uint32(0)<<uint(31-me))
		} else {
			v = (^uint32(0)>>uint(me+1)) & (^uint32(0)<<uint(31-(mb-1)))
		}
		$$.Offset = int64(v);
	}

textsize:
	LCONST
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_TEXTSIZE;
		$$.Offset = int64($1)
		$$.Val = int32(obj.ArgsSizeUnknown);
	}
|	'-' LCONST
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_TEXTSIZE;
		$$.Offset = -int64($2)
		$$.Val = int32(obj.ArgsSizeUnknown);
	}
|	LCONST '-' LCONST
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_TEXTSIZE;
		$$.Offset = int64($1)
		$$.Val = int32($3);
	}
|	'-' LCONST '-' LCONST
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_TEXTSIZE;
		$$.Offset = -int64($2)
		$$.Val = int32($4);
	}

ximm:
	'$' addr
	{
		$$ = $2;
		$$.Type = obj.TYPE_ADDR;
	}
|	'$' LSCONST
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_SCONST;
		$$.Val = $2
	}

fimm:
	'$' LFCONST
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_FCONST;
		$$.Val = $2;
	}
|	'$' '-' LFCONST
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_FCONST;
		$$.Val = -$3;
	}

imm:	'$' con
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_CONST;
		$$.Offset = $2;
	}

sreg:
	LREG
|	LR '(' con ')'
	{
		if $$ < 0 || $$ >= NREG {
			print("register value out of range\n")
		}
		$$ = REG_R0 + $3;
	}

regaddr:
	'(' sreg ')'
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_MEM;
		$$.Reg = int16($2);
		$$.Offset = 0;
	}
|	'(' sreg '+' sreg ')'
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_MEM;
		$$.Reg = int16($2);
		$$.Scale = int16($4);
		$$.Offset = 0;
	}

addr:
	name
|	con '(' sreg ')'
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_MEM;
		$$.Reg = int16($3);
		$$.Offset = $1;
	}

name:
	con '(' pointer ')'
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_MEM;
		$$.Name = int8($3);
		$$.Sym = nil;
		$$.Offset = $1;
	}
|	LNAME offset '(' pointer ')'
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_MEM;
		$$.Name = int8($4);
		$$.Sym = obj.Linklookup(asm.Ctxt, $1.Name, 0);
		$$.Offset = $2;
	}
|	LNAME '<' '>' offset '(' LSB ')'
	{
		$$ = nullgen;
		$$.Type = obj.TYPE_MEM;
		$$.Name = obj.NAME_STATIC;
		$$.Sym = obj.Linklookup(asm.Ctxt, $1.Name, 1);
		$$.Offset = $4;
	}

comma:
|	','

offset:
	{
		$$ = 0;
	}
|	'+' con
	{
		$$ = $2;
	}
|	'-' con
	{
		$$ = -$2;
	}

pointer:
	LSB
|	LSP
|	LFP

con:
	LCONST
|	LVAR
	{
		$$ = $1.Value;
	}
|	'-' con
	{
		$$ = -$2;
	}
|	'+' con
	{
		$$ = $2;
	}
|	'~' con
	{
		$$ = ^$2;
	}
|	'(' expr ')'
	{
		$$ = $2;
	}

expr:
	con
|	expr '+' expr
	{
		$$ = $1 + $3;
	}
|	expr '-' expr
	{
		$$ = $1 - $3;
	}
|	expr '*' expr
	{
		$$ = $1 * $3;
	}
|	expr '/' expr
	{
		$$ = $1 / $3;
	}
|	expr '%' expr
	{
		$$ = $1 % $3;
	}
|	expr '<' '<' expr
	{
		$$ = $1 << uint($4);
	}
|	expr '>' '>' expr
	{
		$$ = $1 >> uint($4);
	}
|	expr '&' expr
	{
		$$ = $1 & $3;
	}
|	expr '^' expr
	{
		$$ = $1 ^ $3;
	}
|	expr '|' expr
	{
		$$ = $1 | $3;
	}
