package hal import ( "fmt" "strings" ) type ProgrammedInstruction struct { Instruction *Instruction Operand float64 } func (pi *ProgrammedInstruction) Execute(module *Module) error { if pi.Instruction.ExecuteWithOperand != nil { module.debug("Operand: %f", pi.Operand) return pi.Instruction.ExecuteWithOperand(module, pi.Operand) } else if pi.Instruction.Execute != nil { return pi.Instruction.Execute(module) } else { return fmt.Errorf("instruction is not implemented") } } type Instruction struct { Name string ExecuteWithOperand func(module *Module, operand float64) error Execute func(module *Module) error } func FindInstructionByName(name string) *Instruction { for _, instruction := range instructions { if instruction.Name == strings.ToUpper(name) { return instruction } } return nil } var instructions = []*Instruction{ InstructionStart, InstructionStop, InstructionIn, InstructionOut, InstructionLoad, InstructionLoadNum, InstructionStore, InstructionJumpNeg, InstructionJumpPos, InstructionJumpNull, InstructionJump, InstructionAddNum, InstructionSubNum, InstructionMulNum, InstructionDivNum, InstructionAdd, InstructionSub, InstructionMul, InstructionDiv, InstructionLoadInd, InstructionStoreInd, InstructionDumpReg, InstructionDumpProg, } var InstructionStart = &Instruction{ Name: "START", Execute: func(module *Module) error { // actually not required module.isStopped = false return nil }, } var InstructionStop = &Instruction{ Name: "STOP", Execute: func(module *Module) error { module.isStopped = true return nil }, } var InstructionOut = &Instruction{ Name: "OUT", ExecuteWithOperand: func(module *Module, operand float64) error { index := int64(operand) if _, ok := module.IO[index]; !ok { return fmt.Errorf("IO[%d] is not registered", index) } return module.IO[index].Write(module.accumulator) }, } var InstructionIn = &Instruction{ Name: "IN", ExecuteWithOperand: func(module *Module, operand float64) (err error) { index := int64(operand) if _, ok := module.IO[index]; !ok { return fmt.Errorf("IO[%d] is not registered", index) } module.accumulator, err = module.IO[index].Read() return }, } var InstructionLoad = &Instruction{ Name: "LOAD", ExecuteWithOperand: func(module *Module, operand float64) error { module.accumulator = module.mmu.Read(Address(operand)) return nil }, } var InstructionLoadNum = &Instruction{ Name: "LOADNUM", ExecuteWithOperand: func(module *Module, operand float64) error { module.accumulator = operand return nil }, } var InstructionStore = &Instruction{ Name: "STORE", ExecuteWithOperand: func(module *Module, operand float64) error { module.mmu.Write(Address(operand), module.accumulator) return nil }, } func newJumpInstruction(name string, doJump func(accumulator float64) bool) *Instruction { return &Instruction{ Name: name, ExecuteWithOperand: func(module *Module, operand float64) error { index := int64(operand) if _, ok := module.programStorage[index]; !ok { return fmt.Errorf("index %d does not exist in program storage", index) } if doJump(module.accumulator) { return module.setProgramCounter(index) } return nil }, } } var InstructionJumpNeg = newJumpInstruction("JUMPNEG", func(accumulator float64) bool { return accumulator < 0 }) var InstructionJumpPos = newJumpInstruction("JUMPPOS", func(accumulator float64) bool { return accumulator > 0 }) var InstructionJumpNull = newJumpInstruction("JUMPNULL", func(accumulator float64) bool { return accumulator == 0 }) var InstructionJump = newJumpInstruction("JUMP", func(accumulator float64) bool { return true }) var addInstr = func(accumulator, value float64) float64 { return accumulator + value } var subInstr = func(accumulator, value float64) float64 { return accumulator - value } var mulInstr = func(accumulator, value float64) float64 { return accumulator * value } var divInstr = func(accumulator, value float64) float64 { return accumulator / value } func newMathNumInstruction(name string, operation func(accumulator, value float64) float64) *Instruction { return &Instruction{ Name: name, ExecuteWithOperand: func(module *Module, operand float64) error { module.accumulator = operation(module.accumulator, operand) return nil }, } } var InstructionAddNum = newMathNumInstruction("ADDNUM", addInstr) var InstructionSubNum = newMathNumInstruction("SUBNUM", subInstr) var InstructionMulNum = newMathNumInstruction("MULNUM", mulInstr) var InstructionDivNum = newMathNumInstruction("DIVNUM", divInstr) func newMathInstruction(name string, operation func(accumulator, register float64) float64) *Instruction { return &Instruction{ Name: name, ExecuteWithOperand: func(module *Module, operand float64) error { param := module.mmu.Read(Address(operand)) module.accumulator = operation(module.accumulator, param) return nil }, } } var InstructionAdd = newMathInstruction("ADD", addInstr) var InstructionSub = newMathInstruction("SUB", subInstr) var InstructionMul = newMathInstruction("MUL", mulInstr) var InstructionDiv = newMathInstruction("DIV", divInstr) var InstructionLoadInd = &Instruction{ Name: "LOADIND", ExecuteWithOperand: func(module *Module, operand float64) error { module.mmu.Read(Address(module.register[int(operand)])) return nil }, } var InstructionStoreInd = &Instruction{ Name: "STOREIND", ExecuteWithOperand: func(module *Module, operand float64) error { module.mmu.Write(Address(module.register[int(operand)]), module.accumulator) return nil }, } var InstructionDumpReg = &Instruction{ Name: "DUMPREG", Execute: func(module *Module) error { for index, content := range module.register { module.debug("%d:\t%d", index, content) } return nil }, } var InstructionDumpProg = &Instruction{ Name: "DUMPPROG", Execute: func(module *Module) error { for index, content := range module.programStorage { module.debug("%d\t%s\t%d", index, content.Instruction.Name, content.Operand) } return nil }, }