// buildFunction compiles the given function. func (a *assembler) buildFunction(f *parser.Function) (err error) { nodes := f.Children() name := nodes[0].(*parser.Label) a.debug.SetFunctionStart(cpu.Word(len(a.code)), f.Line(), name.Data) for i := range nodes { switch tt := nodes[i].(type) { case *parser.Comment: /* ignore */ case *parser.Label: a.labels[tt.Data] = cpu.Word(len(a.code)) case *parser.Instruction: err = a.buildInstruction(tt.Children()) default: err = NewBuildError( a.ast.Files[tt.File()], tt.Line(), tt.Col(), "Unexpected node %T. Want Comment, Label, Instruction.", tt, ) } if err != nil { return } } a.debug.SetFunctionEnd(cpu.Word(len(a.code)), nodes[len(nodes)-1].Line()) return }
// Insert loads media from the given file. // Data is expected to be stored in Little Endian format. // // protected is true when the media is considered read-only. func (d *Device) Insert(file string, protected bool) error { if d.state != StateNoMedia { return fmt.Errorf("Media already present.") } d.setState(StateBusy) defer d.setState(StateReady) data, err := ioutil.ReadFile(file) if err != nil { d.setError(ErrBroken) return err } if len(data) != WordCount<<1 { d.setError(ErrBroken) return fmt.Errorf("Invalid file size. Expected %d bytes.", WordCount<<1) } for i := range d.data { d.data[i] = cpu.Word(data[i*2]) | cpu.Word(data[i*2+1])<<8 } d.protected = protected return nil }
// buildNodes compiles the given ast root nodes func (a *assembler) buildNodes(nodes []parser.Node) (err error) { for i := range nodes { switch tt := nodes[i].(type) { case *parser.Comment: /* ignore */ case *parser.Label: a.labels[tt.Data] = cpu.Word(len(a.code)) case *parser.Function: err = a.buildFunction(tt) case *parser.Instruction: err = a.buildInstruction(tt.Children()) default: err = NewBuildError( a.ast.Files[tt.File()], tt.Line(), tt.Col(), "Unexpected node %T. Want Comment, Label, Function or Instruction.", tt, ) } if err != nil { return } } return }
func test(t *testing.T, src string, code []cpu.Word) { var ast parser.AST files, err := path.SourceFiles(src) if err != nil { t.Fatal(err) } for i := range files { err = ast.Parse(files[i], 0) if err != nil { t.Fatal(err) } } ar, err := Assemble(&ast, 0) if err != nil { t.Fatal(err) } if len(code) != len(ar.Code) { t.Fatalf("Code size mismatch. Expected %d, have %d\n%04x\n%04x", len(code), len(ar.Code), code, ar.Code) } for i := range code { if code[i] != cpu.Word(ar.Code[i]) { t.Fatalf("Code mismatch at instruction %d:\nWant: %04x\nHave: %04x", i, code, ar.Code) } } }
// buildData compiles the given data section func (a *assembler) buildData(nodes []parser.Node) (err error) { var r rune nodes = nodes[1:] // Skip 'dat' instruction. for i := range nodes { expr, ok := nodes[i].(*parser.Expression) if !ok { continue } switch tt := expr.Children()[0].(type) { case *parser.String: for _, r = range tt.Data { a.debug.Emit(tt) a.code = append(a.code, cpu.Word(r)) } case parser.NumericNode: num, err := tt.Parse() if err != nil { return err } a.debug.Emit(tt) a.code = append(a.code, num) } } return }
// setError changes the current error and optionally triggers an interrupt message. func (d *Device) setError(err uint32) { atomic.StoreUint32(&d.error, err) id := cpu.Word(atomic.LoadUint32(&d.id)) if id != 0 { d.int(id) } }
// setState changes the current state and optionally triggers an interrupt message. func (d *Device) setState(state uint32) { atomic.StoreUint32(&d.state, state) id := cpu.Word(atomic.LoadUint32(&d.id)) if id != 0 { d.int(id) } }
func (d *DebugInfo) getStartAddr(fileidx int) cpu.Word { for i := range d.SourceMapping { if d.SourceMapping[i].File == fileidx { return cpu.Word(i) } } return 0 }
// Words returns the program code as a sequence of words. func (a *Archive) Words() []cpu.Word { w := make([]cpu.Word, len(a.Code)) for i, v := range a.Code { w[i] = cpu.Word(v) } return w }
func (d *Device) Handler(s *cpu.Storage) { switch s.A { case PollDevice: d.Lock() s.A = cpu.Word(d.state) s.B = cpu.Word(d.error) s.C = cpu.Word(len(d.buf)) d.Unlock() case SetInterrupt: atomic.StoreUint32(&d.id, uint32(s.X)) case Connect: go d.connect(byte(s.X>>8), byte(s.X), byte(s.Y>>8), byte(s.Y), s.Z) case Disconnect: go d.disconnect() case Send: if s.Y == 0 || s.Y > MaxBuffer { d.setError(ErrInvalidBufferSize) return } go d.send(s.Mem[s.X : s.X+s.Y]) case Receive: if s.Y == 0 || s.Y > MaxBuffer { s.B = 0 d.setError(ErrInvalidBufferSize) return } s.A = s.Y sz := cpu.Word(len(d.buf)) if s.A > sz { s.A = sz } go d.receive(s.Mem[s.X : s.X+s.A]) } }
func (d *Device) Init() error { glfw.SetKeyCallback(func(key, state int) { var value cpu.Word if key >= glfw.KeySpecial { switch key { case glfw.KeyBackspace: value = 0x10 case glfw.KeyEnter: value = 0x11 case glfw.KeyInsert: value = 0x12 case glfw.KeyDel: value = 0x13 case glfw.KeyUp: value = 0x80 case glfw.KeyDown: value = 0x81 case glfw.KeyLeft: value = 0x82 case glfw.KeyRight: value = 0x83 case glfw.KeyLshift, glfw.KeyRshift: value = 0x90 case glfw.KeyLctrl, glfw.KeyRctrl: value = 0x91 default: return // Not supported. } } else { value = cpu.Word(key) } d.state[value] = cpu.Word(state) d.buf = append(d.buf, value) // Trigger an interrupt if enabled. if d.id != 0 { d.int(d.id) } }) return nil }
// Parse attempts to process the node's string data as a number. func (n *Char) Parse() (cpu.Word, error) { r, size := utf8.DecodeRuneInString(n.Data) // If the encoding is invalid, utf8.DecodeRune yields (RuneError, 1). // This constitutes an impossible result for correct UTF-8. if r == utf8.RuneError && size == 1 { return 0, errors.New(fmt.Sprintf("Invalid utf8 character literal: %s", n.Data)) } return cpu.Word(r), nil }
func TestEQU(t *testing.T) { doTest(t, `equ SomeValue, 16 equ AnotherValue, 'A' set a, SomeValue add a, AnotherValue `, cpu.Encode(cpu.SET, 0, 0x31), // set a, 16 cpu.Encode(cpu.ADD, 0, 0x1f), // add a, 'A' cpu.Word('A'), ) }
// Parse attempts to process the node's string data as a number. func (n *Number) Parse() (cpu.Word, error) { var v uint64 var err error if len(n.Data) > 2 && n.Data[0] == '0' && n.Data[1] == 'b' { // strconv.ParseUint can't deal with 0b01010101 formatted strings. // So handle these manually. v, err = strconv.ParseUint(n.Data[2:], 2, 64) } else { // Otherwise, just let it figure out if we have octal, decimal or hex values. v, err = strconv.ParseUint(n.Data, 0, 64) } return cpu.Word(v), err }
// buildOperand compiles the given instruction operand. // // The `first` parameter determines if we are parsing the A or B parameter // in something like 'set A, B'. This makes a difference when encoding // small literal numbers. func (a *assembler) buildOperand(argv *[]cpu.Word, symbols *[]parser.Node, node parser.Node, first bool) (val cpu.Word, err error) { switch tt := node.(type) { case *parser.Name: if reg, ok := registers[tt.Data]; ok { return reg, nil } if addr, ok := a.labels[tt.Data]; ok { if !first && (addr == 0xffff || addr <= 0x1e) { return addr + 0x21, nil } *symbols = append(*symbols, tt) *argv = append(*argv, addr) return 0x1f, nil } a.refs[cpu.Word(len(a.code)+1+len(*argv))] = tt *symbols = append(*symbols, tt) *argv = append(*argv, 0) return 0x1f, nil case parser.NumericNode: num, err := tt.Parse() if err != nil { return 0, err } if !first && (num == 0xffff || num <= 0x1e) { return num + 0x21, nil } *symbols = append(*symbols, tt) *argv = append(*argv, num) return 0x1f, nil case *parser.Block: return a.buildBlock(argv, symbols, tt) default: return 0, NewBuildError( a.ast.Files[tt.File()], tt.Line(), tt.Col(), "Unexpected node %T. Want Name, Number or Block.", tt, ) } return }
// translate retrieves the red component for each pixel in // the given column. It then constructs an 8 bit value from // the 8 red components. func translate(img image.Image, x, y int) cpu.Word { var c [8]uint32 c[0], _, _, _ = img.At(x, y+0).RGBA() c[1], _, _, _ = img.At(x, y+1).RGBA() c[2], _, _, _ = img.At(x, y+2).RGBA() c[3], _, _, _ = img.At(x, y+3).RGBA() c[4], _, _, _ = img.At(x, y+4).RGBA() c[5], _, _, _ = img.At(x, y+5).RGBA() c[6], _, _, _ = img.At(x, y+6).RGBA() c[7], _, _, _ = img.At(x, y+7).RGBA() return cpu.Word(((c[0]&1)<<0 | (c[1]&1)<<1 | (c[2]&1)<<2 | (c[3]&1)<<3 | (c[4]&1)<<4 | (c[5]&1)<<5 | (c[6]&1)<<6 | (c[7]&1)<<7) & 0xff) }
func (a *assembler) buildBlockOperand(argv *[]cpu.Word, symbols *[]parser.Node, node parser.Node) (cpu.Word, error) { switch tt := node.(type) { case *parser.Name: if reg, ok := registers[tt.Data]; ok { if reg <= 0x7 { return reg + 0x10, nil } if reg == 0x1b { return 0x1a, nil } return 0, NewBuildError( a.ast.Files[tt.File()], tt.Line(), tt.Col(), "Illegal use of register %q.", tt.Data, ) } if addr, ok := a.labels[tt.Data]; ok { *symbols = append(*symbols, tt) *argv = append(*argv, addr) return 0, nil } a.refs[cpu.Word(len(a.code)+1+len(*argv))] = tt *symbols = append(*symbols, tt) *argv = append(*argv, 0) return 0, nil case parser.NumericNode: num, err := tt.Parse() if err != nil { return 0, err } *symbols = append(*symbols, tt) *argv = append(*argv, num) return 0, nil } return 0, NewBuildError( a.ast.Files[node.File()], node.Line(), node.Col(), "Unexpected node %T. Want Name or Number.", node, ) }
func TestWrite(t *testing.T) { var fdd HMU1440 err := fdd.Open("test.fdd") if err != nil { t.Fatal(err) } defer fdd.Close() var buf [SectorSize]cpu.Word for i := range buf { buf[i] = cpu.Word(i) } if err = fdd.Write(2, buf[:]); err != nil { t.Errorf("write: %v", err) return } }
func (k *Keyboard) Handler(s *cpu.Storage) { switch s.A { case ClearBuffer: k.buf = k.buf[:0] case GetNextKey: s.C = 0 if sz := len(k.buf); sz > 0 { s.C = k.buf[0] copy(k.buf, k.buf[1:]) k.buf = k.buf[:sz-1] } case GetKeyState: s.C = 0 if int(s.B) < len(k.keys) { s.C = cpu.Word(k.keys[s.B] & 1) } case SetInterruptId: k.id = s.B } }
func (v *Vertex) toWords() (a, b cpu.Word) { a = cpu.Word(v.Y)<<8 | cpu.Word(v.X) b = cpu.Word(v.Intensity)<<10 | cpu.Word(v.Color)<<8 | cpu.Word(v.Z) return }
// Encode encodes vidmem cell components into a single word. func Encode(fg, bg, blink, char byte) cpu.Word { return cpu.Word(char&127) | cpu.Word(blink&1)<<7 | cpu.Word(bg&15)<<8 | cpu.Word(fg&15)<<12 }
// buildBlock builds a block expression. func (a *assembler) buildBlock(argv *[]cpu.Word, symbols *[]parser.Node, b *parser.Block) (val cpu.Word, err error) { nodes := b.Children() switch len(nodes) { case 1: switch tt := nodes[0].(type) { case *parser.Name: if reg, ok := registers[tt.Data]; ok { return reg + 0x08, nil } if addr, ok := a.labels[tt.Data]; ok { *symbols = append(*symbols, tt) *argv = append(*argv, addr) return 0x1e, nil } a.refs[cpu.Word(len(a.code)+1+len(*argv))] = tt *symbols = append(*symbols, tt) *argv = append(*argv, 0) return 0x1e, nil case parser.NumericNode: num, err := tt.Parse() if err != nil { return 0, err } *symbols = append(*symbols, tt) *argv = append(*argv, num) return 0x1e, nil default: return 0, NewBuildError( a.ast.Files[tt.File()], tt.Line(), tt.Col(), "Unexpected node %T. Want Name, Number or Block.", tt, ) } case 3: var va, vb cpu.Word if va, err = a.buildBlockOperand(argv, symbols, nodes[0]); err != nil { return } if vb, err = a.buildBlockOperand(argv, symbols, nodes[2]); err != nil { return } if va != 0 { return va, nil } return vb, nil default: err = NewBuildError( a.ast.Files[b.File()], b.Line(), b.Col(), "Unexpected node count. Want 1 or 3. Got %d", len(nodes)) } return }
// Read reads the binary version of a profile into a Profile structure. // // See the documentation on prof.Write for details on the file format. func Read(r io.Reader) (p *Profile, err error) { var size uint32 p = new(Profile) // [1] if err = binary.Read(r, be, &size); err != nil { return } p.Files = make([]asm.FileInfo, size) // [2] for i := range p.Files { if err = binary.Read(r, be, &p.Files[i].StartAddr); err != nil { return } var size uint16 if err = binary.Read(r, be, &size); err != nil { return } d := make([]byte, size) if _, err = r.Read(d); err != nil { return } p.Files[i].Name = string(d) } // [3] if err = binary.Read(r, be, &size); err != nil { return } p.Functions = make([]asm.FuncInfo, size) // [4] for i := range p.Functions { if err = binary.Read(r, be, &p.Functions[i].StartAddr); err != nil { return } if err = binary.Read(r, be, &p.Functions[i].EndAddr); err != nil { return } var line uint32 if err = binary.Read(r, be, &line); err != nil { return } p.Functions[i].StartLine = int(line) if err = binary.Read(r, be, &line); err != nil { return } p.Functions[i].EndLine = int(line) var size uint16 if err = binary.Read(r, be, &size); err != nil { return } d := make([]byte, size) if _, err = r.Read(d); err != nil { return } p.Functions[i].Name = string(d) } // [5] if err = binary.Read(r, be, &size); err != nil { return } p.Data = make([]ProfileData, size) // [6] var d [30]byte for i := range p.Data { if _, err = r.Read(d[:]); err != nil { return } var pd ProfileData pd.Data = cpu.Word(d[0])<<8 | cpu.Word(d[1]) pd.File = int(d[2])<<24 | int(d[3])<<16 | int(d[4])<<8 | int(d[5]) pd.Line = int(d[6])<<24 | int(d[7])<<16 | int(d[8])<<8 | int(d[9]) pd.Col = int(d[10])<<24 | int(d[11])<<16 | int(d[12])<<8 | int(d[13]) pd.Count = uint64(d[14])<<56 | uint64(d[15])<<48 | uint64(d[16])<<40 | uint64(d[17])<<32 | uint64(d[18])<<24 | uint64(d[19])<<16 | uint64(d[20])<<8 | uint64(d[21]) pd.Penalty = uint64(d[22])<<56 | uint64(d[23])<<48 | uint64(d[24])<<40 | uint64(d[25])<<32 | uint64(d[26])<<24 | uint64(d[27])<<16 | uint64(d[28])<<8 | uint64(d[29]) p.Data[i] = pd } p.setInstructionSizes() return }