You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

vm.go 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package intcode
  2. // VirtualMachine is an IntCode virtual machine.
  3. type VirtualMachine struct {
  4. ip int
  5. rb int
  6. parameterModes uint8
  7. relativeModes uint8
  8. Memory []int
  9. Halted bool
  10. Input chan int
  11. Output chan int
  12. }
  13. // NewVirtualMachine creates a new IntCode virtual machine, initialised to the given slice of memory.
  14. // The caller is responsible for initialising the VM's I/O channels if required.
  15. func NewVirtualMachine(memory []int) *VirtualMachine {
  16. vm := &VirtualMachine{
  17. ip: 0,
  18. Memory: memory,
  19. Halted: false,
  20. }
  21. return vm
  22. }
  23. // Clone returns a deep copy of this VM, with newly allocated memory and I/O channels.
  24. func (vm *VirtualMachine) Clone() *VirtualMachine {
  25. memory := make([]int, len(vm.Memory))
  26. copy(memory, vm.Memory)
  27. return &VirtualMachine{
  28. ip: vm.ip,
  29. rb: vm.rb,
  30. parameterModes: vm.parameterModes,
  31. relativeModes: vm.relativeModes,
  32. Memory: memory,
  33. Halted: vm.Halted,
  34. Input: make(chan int, 1),
  35. Output: make(chan int, 1),
  36. }
  37. }
  38. // arg Returns the value of the given argument for the current instruction.
  39. func (vm *VirtualMachine) arg(pos int) *int {
  40. mask := uint8(1) << uint8(pos)
  41. if vm.parameterModes&mask == mask {
  42. // Parameter mode - the value of the argument is just treated as an int
  43. for vm.ip+1+pos > len(vm.Memory) {
  44. vm.Memory = append(vm.Memory, make([]int, 1024)...)
  45. }
  46. return &vm.Memory[vm.ip+1+pos]
  47. } else if vm.relativeModes&mask == mask {
  48. // Relative mode - the value of the argument is treated as a memory offset from the relative base
  49. for vm.ip+1+pos > len(vm.Memory) || vm.rb+vm.Memory[vm.ip+1+pos] > len(vm.Memory) {
  50. vm.Memory = append(vm.Memory, make([]int, 1024)...)
  51. }
  52. return &vm.Memory[vm.rb+vm.Memory[vm.ip+1+pos]]
  53. } else {
  54. // Position mode - the value of the argument is treated as a memory offset from the start of the memory
  55. for vm.ip+1+pos > len(vm.Memory) || vm.Memory[vm.ip+1+pos] > len(vm.Memory) {
  56. vm.Memory = append(vm.Memory, make([]int, 1024)...)
  57. }
  58. return &vm.Memory[vm.Memory[vm.ip+1+pos]]
  59. }
  60. }
  61. // Run repeatedly executes instructions until the VM halts.
  62. func (vm *VirtualMachine) Run() {
  63. for !vm.Halted {
  64. instruction := vm.Memory[vm.ip]
  65. opcode := instruction % 100
  66. vm.parameterModes = 0
  67. vm.relativeModes = 0
  68. mask := uint8(1)
  69. for i := instruction / 100; i > 0; i /= 10 {
  70. mode := i % 10
  71. if mode == 1 {
  72. vm.parameterModes = vm.parameterModes | mask
  73. } else if mode == 2 {
  74. vm.relativeModes = vm.relativeModes | mask
  75. }
  76. mask = mask << 1
  77. }
  78. opcodes[opcode](vm)
  79. }
  80. if vm.Output != nil {
  81. close(vm.Output)
  82. }
  83. }
  84. // Reset resets the memory to the given slice, and all other state back to its original value.
  85. func (vm *VirtualMachine) Reset(memory []int) {
  86. copy(vm.Memory, memory)
  87. // We may previously have expanded our own memory, reset that to zero.
  88. for i := len(memory); i < len(vm.Memory)-1; i++ {
  89. vm.Memory[i] = 0
  90. }
  91. vm.ip = 0
  92. vm.rb = 0
  93. vm.Halted = false
  94. }