2014-03-05 01:42:51 +00:00
|
|
|
package inst
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/zellyn/go6502/asm/context"
|
|
|
|
"github.com/zellyn/go6502/asm/expr"
|
|
|
|
"github.com/zellyn/go6502/asm/lines"
|
|
|
|
"github.com/zellyn/go6502/opcodes"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Type int
|
|
|
|
|
|
|
|
const (
|
|
|
|
TypeUnknown Type = iota
|
|
|
|
|
|
|
|
TypeNone // No type (eg. just a label, just a comment, empty, ignored directive)
|
|
|
|
TypeMacroStart // Start of macro definition
|
|
|
|
TypeMacroEnd // End of macro definition
|
|
|
|
TypeMacroCall // Macro invocation
|
|
|
|
TypeMacroExit // Macro early exit
|
|
|
|
TypeIfdef // Ifdef start
|
|
|
|
TypeIfdefElse // Ifdef else block
|
|
|
|
TypeIfdefEnd // Ifdef end
|
|
|
|
TypeInclude // Include a file
|
2014-05-31 19:55:36 +00:00
|
|
|
TypeData // Data
|
2014-03-05 01:42:51 +00:00
|
|
|
TypeBlock // Block storage
|
|
|
|
TypeOrg // Where to store assembled code
|
|
|
|
TypeTarget // Target address to use for jumps, labels, etc.
|
|
|
|
TypeSegment // Segments: bss/code/data
|
|
|
|
TypeEqu // Equate
|
|
|
|
TypeOp // An actual asm opcode
|
|
|
|
TypeEnd // End assembly
|
2014-06-03 15:46:49 +00:00
|
|
|
TypeSetting // An on/off setting toggle
|
2014-03-05 01:42:51 +00:00
|
|
|
)
|
|
|
|
|
2014-05-31 19:55:36 +00:00
|
|
|
// Variants for "TypeData" instructions.
|
|
|
|
const (
|
|
|
|
DataBytes = iota // Data: expressions, but forced to one byte per
|
|
|
|
DataMixed // Bytes or words (LE), depending on individual expression widths
|
|
|
|
DataWordsLe // Data: expressions, but forced to one word per, little-endian
|
|
|
|
DataWordsBe // Data: expressions, but forced to one word per, big-endian
|
|
|
|
DataAscii // Data: from ASCII strings, high bit clear
|
|
|
|
DataAsciiFlip // Data: from ASCII strings, high bit clear, except last char
|
2014-06-13 00:39:48 +00:00
|
|
|
DataAsciiHi // Data: from ASCII strings, high bit set
|
2014-05-31 19:55:36 +00:00
|
|
|
DataAsciiHiFlip // Data: from ASCII strings, high bit set, except last char
|
2014-06-04 15:35:31 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Variants for "TypeEqu" instructions.
|
|
|
|
const (
|
|
|
|
EquNormal = iota
|
|
|
|
EquPageZero
|
2014-05-31 19:55:36 +00:00
|
|
|
)
|
|
|
|
|
2014-03-05 01:42:51 +00:00
|
|
|
type I struct {
|
|
|
|
Type Type // Type of instruction
|
|
|
|
Label string // Text of label part
|
|
|
|
MacroArgs []string // Macro args
|
|
|
|
Command string // Text of command part
|
|
|
|
TextArg string // Text of argument part
|
|
|
|
Exprs []*expr.E // Expression(s)
|
|
|
|
Data []byte // Actual bytes
|
|
|
|
WidthKnown bool // Do we know how many bytes this instruction takes yet?
|
2014-08-08 22:55:33 +00:00
|
|
|
Width uint16 // width in bytes
|
2014-03-05 01:42:51 +00:00
|
|
|
Final bool // Do we know the actual bytes yet?
|
|
|
|
Op byte // Opcode
|
|
|
|
Mode opcodes.AddressingMode // Opcode mode
|
|
|
|
ZeroMode opcodes.AddressingMode // Possible ZP-option mode
|
|
|
|
ZeroOp byte // Possible ZP-option Opcode
|
|
|
|
Value uint16 // For Equates, the value
|
2014-05-31 19:55:36 +00:00
|
|
|
DeclaredLine uint16 // Line number listed in file
|
|
|
|
Line *lines.Line // Line object for this line
|
|
|
|
Addr uint16 // Current memory address
|
|
|
|
AddrKnown bool // Whether the current memory address is known
|
|
|
|
Var int // Variant of instruction type
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (i I) TypeString() string {
|
|
|
|
switch i.Type {
|
|
|
|
case TypeNone:
|
|
|
|
return "-"
|
|
|
|
case TypeMacroStart:
|
|
|
|
return "macro"
|
|
|
|
case TypeMacroEnd:
|
|
|
|
return "endm"
|
|
|
|
case TypeMacroCall:
|
|
|
|
return "call " + i.Command
|
|
|
|
case TypeMacroExit:
|
|
|
|
return "exitm"
|
|
|
|
case TypeIfdef:
|
|
|
|
return "if"
|
|
|
|
case TypeIfdefElse:
|
|
|
|
return "else"
|
|
|
|
case TypeIfdefEnd:
|
|
|
|
return "endif"
|
|
|
|
case TypeInclude:
|
|
|
|
return "inc"
|
|
|
|
case TypeData:
|
2014-05-31 19:55:36 +00:00
|
|
|
switch i.Var {
|
|
|
|
case DataMixed:
|
|
|
|
return "data"
|
|
|
|
case DataBytes:
|
|
|
|
return "data/b"
|
|
|
|
case DataWordsLe:
|
|
|
|
return "data/wle"
|
|
|
|
case DataWordsBe:
|
|
|
|
return "data/wbe"
|
|
|
|
case DataAscii, DataAsciiHi, DataAsciiFlip, DataAsciiHiFlip:
|
|
|
|
return "data/b"
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unknown data variant: %d", i.Var))
|
|
|
|
}
|
2014-03-05 01:42:51 +00:00
|
|
|
case TypeBlock:
|
|
|
|
return "block"
|
|
|
|
case TypeOrg:
|
|
|
|
return "org"
|
|
|
|
case TypeTarget:
|
|
|
|
return "target"
|
|
|
|
case TypeSegment:
|
|
|
|
return "seg"
|
|
|
|
case TypeEqu:
|
|
|
|
return "="
|
|
|
|
case TypeEnd:
|
|
|
|
return "end"
|
2014-06-03 15:46:49 +00:00
|
|
|
case TypeSetting:
|
|
|
|
return "set"
|
2014-03-05 01:42:51 +00:00
|
|
|
case TypeOp:
|
|
|
|
modeStr := "?"
|
|
|
|
switch i.Mode {
|
|
|
|
case opcodes.MODE_IMPLIED:
|
|
|
|
modeStr = "imp"
|
|
|
|
case opcodes.MODE_ABSOLUTE:
|
|
|
|
modeStr = "abs"
|
|
|
|
case opcodes.MODE_INDIRECT:
|
|
|
|
modeStr = "ind"
|
|
|
|
case opcodes.MODE_RELATIVE:
|
|
|
|
modeStr = "rel"
|
|
|
|
case opcodes.MODE_IMMEDIATE:
|
|
|
|
modeStr = "imm"
|
|
|
|
case opcodes.MODE_ABS_X:
|
|
|
|
modeStr = "absX"
|
|
|
|
case opcodes.MODE_ABS_Y:
|
|
|
|
modeStr = "absY"
|
|
|
|
case opcodes.MODE_ZP:
|
|
|
|
modeStr = "zp"
|
|
|
|
case opcodes.MODE_ZP_X:
|
|
|
|
modeStr = "zpX"
|
|
|
|
case opcodes.MODE_ZP_Y:
|
|
|
|
modeStr = "zpY"
|
|
|
|
case opcodes.MODE_INDIRECT_Y:
|
|
|
|
modeStr = "indY"
|
|
|
|
case opcodes.MODE_INDIRECT_X:
|
|
|
|
modeStr = "indX"
|
|
|
|
case opcodes.MODE_A:
|
|
|
|
modeStr = "a"
|
|
|
|
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s/%s", i.Command, modeStr)
|
|
|
|
}
|
|
|
|
return "?"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i I) String() string {
|
|
|
|
switch i.Type {
|
|
|
|
case TypeInclude:
|
|
|
|
return fmt.Sprintf("{inc '%s'}", i.TextArg)
|
2014-06-03 15:46:49 +00:00
|
|
|
case TypeSetting:
|
|
|
|
return fmt.Sprintf("{set %s %s}", i.Command, i.TextArg)
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
s := "{" + i.TypeString()
|
|
|
|
if i.Label != "" {
|
|
|
|
s += fmt.Sprintf(" '%s'", i.Label)
|
|
|
|
}
|
2014-05-08 00:44:03 +00:00
|
|
|
if i.TextArg != "" {
|
2014-06-16 15:24:42 +00:00
|
|
|
s += fmt.Sprintf(` "%s"`, i.TextArg)
|
2014-05-08 00:44:03 +00:00
|
|
|
}
|
2014-03-05 01:42:51 +00:00
|
|
|
if len(i.MacroArgs) > 0 {
|
|
|
|
ma := fmt.Sprintf("%#v", i.MacroArgs)[8:]
|
|
|
|
s += " " + ma
|
|
|
|
}
|
|
|
|
if len(i.Exprs) > 0 {
|
|
|
|
exprs := []string{}
|
|
|
|
for _, expr := range i.Exprs {
|
|
|
|
exprs = append(exprs, expr.String())
|
|
|
|
}
|
|
|
|
s += " " + strings.Join(exprs, ",")
|
|
|
|
}
|
|
|
|
return s + "}"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute attempts to finalize the instruction.
|
2014-08-08 22:55:33 +00:00
|
|
|
func (i *I) Compute(c context.Context, final bool) (bool, error) {
|
2014-03-05 01:42:51 +00:00
|
|
|
if i.Type == TypeEqu || i.Type == TypeTarget || i.Type == TypeOrg {
|
2014-08-08 22:55:33 +00:00
|
|
|
return i.computeMustKnow(c, final)
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
2014-08-08 22:55:33 +00:00
|
|
|
if err := i.computeLabel(c, final); err != nil {
|
2014-03-05 01:42:51 +00:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if i.Final {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
switch i.Type {
|
|
|
|
case TypeOp:
|
2014-08-08 22:55:33 +00:00
|
|
|
return i.computeOp(c, final)
|
2014-03-05 01:42:51 +00:00
|
|
|
case TypeData:
|
2014-08-08 22:55:33 +00:00
|
|
|
return i.computeData(c, final)
|
2014-03-05 01:42:51 +00:00
|
|
|
case TypeBlock:
|
2014-08-08 22:55:33 +00:00
|
|
|
return i.computeBlock(c, final)
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Everything else is zero-width
|
|
|
|
i.WidthKnown = true
|
2014-08-08 22:55:33 +00:00
|
|
|
i.Width = 0
|
2014-03-05 01:42:51 +00:00
|
|
|
i.Final = true
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2014-05-08 23:44:08 +00:00
|
|
|
// FixLabels attempts to turn .1 into LAST_LABEL.1, etc.
|
|
|
|
func (i *I) FixLabels(labeler context.Labeler) error {
|
|
|
|
macroCall := i.Line.GetMacroCall()
|
2014-07-23 15:13:53 +00:00
|
|
|
macroLocals := i.Line.GetMacroLocals()
|
2014-05-08 23:44:08 +00:00
|
|
|
parent := labeler.IsNewParentLabel(i.Label)
|
2014-07-23 15:13:53 +00:00
|
|
|
newL, err := labeler.FixLabel(i.Label, macroCall, macroLocals)
|
2014-05-08 23:44:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return i.Errorf("%v", err)
|
|
|
|
}
|
|
|
|
i.Label = newL
|
|
|
|
if parent {
|
|
|
|
labeler.SetLastLabel(i.Label)
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, e := range i.Exprs {
|
2014-07-23 15:13:53 +00:00
|
|
|
if err := e.FixLabels(labeler, macroCall, macroLocals, i.Line); err != nil {
|
2014-03-05 01:42:51 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// computeLabel attempts to compute equates and label values.
|
2014-08-08 22:55:33 +00:00
|
|
|
func (i *I) computeLabel(c context.Context, final bool) error {
|
2014-03-05 01:42:51 +00:00
|
|
|
if i.Label == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if i.Type == TypeEqu {
|
|
|
|
panic("computeLabel should not be called for equates")
|
|
|
|
}
|
|
|
|
|
|
|
|
addr, aok := c.GetAddr()
|
|
|
|
if !aok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
lval, lok := c.Get(i.Label)
|
|
|
|
if lok && addr != lval {
|
2014-05-05 03:51:58 +00:00
|
|
|
return i.Errorf("Trying to set label '%s' to $%04x, but it already has value $%04x", i.Label, addr, lval)
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
c.Set(i.Label, addr)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-08-08 22:55:33 +00:00
|
|
|
func (i *I) computeData(c context.Context, final bool) (bool, error) {
|
2014-03-05 01:42:51 +00:00
|
|
|
if len(i.Data) > 0 {
|
|
|
|
i.WidthKnown = true
|
2014-08-08 22:55:33 +00:00
|
|
|
i.Width = uint16(len(i.Data))
|
2014-03-05 01:42:51 +00:00
|
|
|
i.Final = true
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
allFinal := true
|
|
|
|
data := []byte{}
|
|
|
|
var width uint16
|
|
|
|
for _, e := range i.Exprs {
|
2014-05-31 19:55:36 +00:00
|
|
|
var w uint16
|
|
|
|
switch i.Var {
|
|
|
|
case DataMixed:
|
|
|
|
w = e.Width()
|
|
|
|
case DataBytes:
|
|
|
|
w = 1
|
|
|
|
case DataWordsLe, DataWordsBe:
|
|
|
|
w = 2
|
|
|
|
}
|
2014-03-05 01:42:51 +00:00
|
|
|
width += w
|
2014-05-05 03:51:58 +00:00
|
|
|
val, labelMissing, err := e.CheckedEval(c, i.Line)
|
2014-03-05 01:42:51 +00:00
|
|
|
if err != nil && !labelMissing {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if labelMissing {
|
|
|
|
allFinal = false
|
|
|
|
if final {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
}
|
2014-05-31 19:55:36 +00:00
|
|
|
switch i.Var {
|
|
|
|
case DataMixed:
|
|
|
|
switch w {
|
|
|
|
case 1:
|
|
|
|
data = append(data, byte(val))
|
|
|
|
case 2:
|
|
|
|
data = append(data, byte(val), byte(val>>8))
|
|
|
|
}
|
|
|
|
case DataBytes:
|
2014-03-05 01:42:51 +00:00
|
|
|
data = append(data, byte(val))
|
2014-05-31 19:55:36 +00:00
|
|
|
case DataWordsLe:
|
2014-03-05 01:42:51 +00:00
|
|
|
data = append(data, byte(val), byte(val>>8))
|
2014-05-31 19:55:36 +00:00
|
|
|
case DataWordsBe:
|
|
|
|
data = append(data, byte(val>>8), byte(val))
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("Unknown data variant handed to computeData: %d", i.Var))
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
}
|
2014-08-08 22:55:33 +00:00
|
|
|
i.Width = width
|
2014-03-05 01:42:51 +00:00
|
|
|
i.WidthKnown = true
|
|
|
|
if allFinal {
|
|
|
|
i.Data = data
|
|
|
|
i.Final = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return i.Final, nil
|
|
|
|
}
|
|
|
|
|
2014-08-08 22:55:33 +00:00
|
|
|
func (i *I) computeBlock(c context.Context, final bool) (bool, error) {
|
2014-05-05 03:51:58 +00:00
|
|
|
val, err := i.Exprs[0].Eval(c, i.Line)
|
2014-03-05 01:42:51 +00:00
|
|
|
if err == nil {
|
|
|
|
i.Value = val
|
|
|
|
i.WidthKnown = true
|
|
|
|
i.Final = true
|
2014-08-08 22:55:33 +00:00
|
|
|
i.Width = val
|
2014-03-05 01:42:51 +00:00
|
|
|
} else {
|
2014-08-08 22:55:33 +00:00
|
|
|
if final {
|
2014-05-05 03:51:58 +00:00
|
|
|
return false, i.Errorf("block storage with unknown size")
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return i.Final, nil
|
|
|
|
}
|
|
|
|
|
2014-08-08 22:55:33 +00:00
|
|
|
func (i *I) computeMustKnow(c context.Context, final bool) (bool, error) {
|
2014-03-05 01:42:51 +00:00
|
|
|
i.WidthKnown = true
|
2014-08-08 22:55:33 +00:00
|
|
|
i.Width = 0
|
2014-03-05 01:42:51 +00:00
|
|
|
i.Final = true
|
2014-05-05 03:51:58 +00:00
|
|
|
val, err := i.Exprs[0].Eval(c, i.Line)
|
2014-03-05 01:42:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
i.Value = val
|
|
|
|
switch i.Type {
|
|
|
|
case TypeTarget:
|
|
|
|
return false, errors.New("Target not implemented yet.")
|
|
|
|
case TypeOrg:
|
|
|
|
c.SetAddr(val)
|
|
|
|
case TypeEqu:
|
|
|
|
c.Set(i.Label, val)
|
|
|
|
// Don't handle labels.
|
|
|
|
return true, nil
|
|
|
|
}
|
2014-08-08 22:55:33 +00:00
|
|
|
if err := i.computeLabel(c, final); err != nil {
|
2014-03-05 01:42:51 +00:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2014-08-08 22:55:33 +00:00
|
|
|
func (i *I) computeOp(c context.Context, final bool) (bool, error) {
|
2014-03-05 01:42:51 +00:00
|
|
|
// If the width is not known, we better have a ZeroPage alternative.
|
|
|
|
if !i.WidthKnown && (i.ZeroOp == 0 || i.ZeroMode == 0) {
|
2014-07-31 23:33:10 +00:00
|
|
|
if i.Line.Context != nil && i.Line.Context.Parent != nil {
|
|
|
|
fmt.Println(i.Line.Context.Parent.Sprintf("foo"))
|
|
|
|
}
|
|
|
|
panic(i.Sprintf("Reached computeOp for '%s' with no ZeroPage alternative: %#v [%s]", i.Command, i, i.Line.Text()))
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
// An op with no args would be final already, so we must have an Expression.
|
|
|
|
if len(i.Exprs) == 0 {
|
|
|
|
panic(fmt.Sprintf("Reached computeOp for '%s' with no expressions", i.Command))
|
|
|
|
}
|
2014-05-05 03:51:58 +00:00
|
|
|
val, labelMissing, err := i.Exprs[0].CheckedEval(c, i.Line)
|
2014-03-05 01:42:51 +00:00
|
|
|
// A real error
|
|
|
|
if !labelMissing && err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if labelMissing {
|
|
|
|
// Don't know, do care.
|
|
|
|
if final {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Already know enough.
|
|
|
|
if i.WidthKnown {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do we know the width, even though the value is unknown?
|
|
|
|
if i.Exprs[0].Width() == 1 {
|
|
|
|
i.WidthKnown = true
|
2014-08-08 22:55:33 +00:00
|
|
|
i.Width = 2
|
2014-03-05 01:42:51 +00:00
|
|
|
i.Op, i.Mode = i.ZeroOp, i.ZeroMode
|
|
|
|
i.ZeroOp, i.ZeroMode = 0, 0
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Okay, we have to set the width: since we don't know, go wide.
|
|
|
|
i.WidthKnown = true
|
2014-08-08 22:55:33 +00:00
|
|
|
i.Width = 3
|
2014-03-05 01:42:51 +00:00
|
|
|
i.ZeroOp, i.ZeroMode = 0, 0
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we got here, we got an actual value.
|
|
|
|
|
|
|
|
// We need to figure out the width
|
|
|
|
if !i.WidthKnown {
|
|
|
|
if val < 0x100 {
|
2014-08-08 22:55:33 +00:00
|
|
|
i.Width = 2
|
2014-03-05 01:42:51 +00:00
|
|
|
i.Op = i.ZeroOp
|
|
|
|
i.Mode = i.ZeroMode
|
|
|
|
} else {
|
2014-08-08 22:55:33 +00:00
|
|
|
i.Width = 3
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// It's a branch
|
|
|
|
if i.Mode == opcodes.MODE_RELATIVE {
|
|
|
|
curr, ok := c.GetAddr()
|
|
|
|
if !ok {
|
|
|
|
if final {
|
2014-05-05 03:51:58 +00:00
|
|
|
return false, i.Errorf("cannot determine current address for '%s'", i.Command)
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
// Found both current and target addresses
|
|
|
|
offset := int32(val) - (int32(curr) + 2)
|
|
|
|
if offset > 127 {
|
2014-05-31 19:55:36 +00:00
|
|
|
return false, i.Errorf("%s cannot jump forward %d (max 127) from $%04x to $%04x", i.Command, offset, curr+2, val)
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
if offset < -128 {
|
2014-05-31 19:55:36 +00:00
|
|
|
return false, i.Errorf("%s cannot jump back %d (max -128) from $%04x to $%04x", i.Command, offset, curr+2, val)
|
2014-03-05 01:42:51 +00:00
|
|
|
}
|
|
|
|
val = uint16(offset)
|
|
|
|
}
|
|
|
|
|
|
|
|
i.WidthKnown = true
|
|
|
|
i.Final = true
|
|
|
|
i.ZeroOp = 0
|
|
|
|
i.ZeroMode = 0
|
|
|
|
|
2014-08-08 22:55:33 +00:00
|
|
|
switch i.Width {
|
2014-03-05 01:42:51 +00:00
|
|
|
case 2:
|
|
|
|
// TODO(zellyn): Warn if > 0xff
|
|
|
|
i.Data = []byte{i.Op, byte(val)}
|
|
|
|
case 3:
|
|
|
|
i.Data = []byte{i.Op, byte(val), byte(val >> 8)}
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("computeOp reached erroneously for '%s'", i.Command))
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
2014-05-05 03:51:58 +00:00
|
|
|
|
|
|
|
func (i I) Errorf(format string, a ...interface{}) error {
|
|
|
|
return i.Line.Errorf(format, a...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i I) Sprintf(format string, a ...interface{}) string {
|
|
|
|
return i.Line.Sprintf(format, a...)
|
|
|
|
}
|