Execute Computer/Zero: Difference between revisions

Created Nim solution.
(Add Go)
(Created Nim solution.)
Line 1,930:
Round: 4 Move: coop Player: 6 Computer: 6
Round: 5 Move: defe Player: 6 Computer: 9</pre>
 
=={{header|Nim}}==
We don’t use an assembly language but define the memory values using functions named NOP, LDA, STA, etc. To be able to use Uniform Function Call Syntax (i.e. call without parentheses),
all these functions have an argument, even NOP and STP.
 
In the last example, “Prisoners”, an input is expected from the user. We defined a function “enterValue” which stores the input value into the byte at address referenced by the PC then increments the PC by one. We have chosen the same player strategy as the one used by the Lua program and, of course, we get the same result.
 
<syntaxhighlight lang="Nim">import std/strformat
 
type
OpCode = enum opNOP, opLDA, opSTA, opADD, opSUB, opBRZ, opJMP, opSTP
Instruction = byte
Address = 0..31
# Definition of a computer zero.
Computer = object
mem: array[Address, byte] # Memory.
pc: Address # Program counter.
acc: byte # Accumulator.
Program = tuple
name: string # Name of the program.
code: seq[Instruction] # List of instructions.
 
proc split(inst: Instruction): tuple[code: OpCode, address: Address] =
## Split an instruction into an opCode and a value.
(OpCode((inst and 0b11100000u8) shr 5), Address(inst and 0b00011111u8))
 
proc toInst(opCode: OpCode; address: int): Instruction =
## Build an instruction with given opcode and value.
if address notin Address.low..Address.high:
raise newException(ValueError, "address out of range.")
byte(opCode) shl 5 or byte(address)
 
# Functions to build instructions.
func NOP(n: int): Instruction = toInst(opNOP, n)
func LDA(n: int): Instruction = toInst(opLDA, n)
func STA(n: int): Instruction = toInst(opSTA, n)
func ADD(n: int): Instruction = toInst(opADD, n)
func SUB(n: int): Instruction = toInst(opSUB, n)
func BRZ(n: int): Instruction = toInst(opBRZ, n)
func JMP(n: int): Instruction = toInst(opJMP, n)
func STP(n: int): Instruction = toInst(opSTP, n)
 
proc reset(comp: var Computer) =
## Reset the computer state.
comp.mem.reset()
comp.pc = 0
comp.acc = 0
 
proc load(comp: var Computer; program: Program) =
## Load a program into a computer.
comp.reset()
var idx = 0
for inst in program.code:
comp.mem[idx] = inst
inc idx
 
proc run(comp: var Computer) =
## Run a computer.
## The execution starts or resumes at the instruction
## at current PC, with current value in the accumulator.
while true:
let (opCode, address) = comp.mem[comp.pc].split()
inc comp.pc
case opCode
of opNOP:
discard
of opLDA:
comp.acc = comp.mem[address]
of opSTA:
comp.mem[address] = comp.acc
of opADD:
comp.acc += comp.mem[address]
of opSUB:
comp.acc -= comp.mem[address]
of opBRZ:
if comp.acc == 0:
comp.pc = address
of opJMP:
comp.pc = address
of opSTP:
break
 
proc enterValue(comp: var Computer; value: byte) =
## Enter a value into memory at address given by the PC,
## then increment the PC.
comp.mem[comp.pc] = value
inc comp.pc
 
 
# Programs are built using the functions NOP, LDA, STA, etc.
# To be able to use Uniform Function Call Syntax (i.e. call without parentheses),
# all these functions have an argument, even NOP and STP.
const
Prog1 = (name: "2+2",
code: @[LDA 3, ADD 4, STP 0, 2, 2])
Prog2 = (name: "7*8",
code: @[LDA 12, ADD 10, STA 12, LDA 11, SUB 13, STA 11, BRZ 8, JMP 0,
LDA 12, STP 0, 8, 7, 0, 1])
Prog3 = (name: "Fibonacci",
code: @[LDA 14, STA 15, ADD 13, STA 14, LDA 15, STA 13, LDA 16, SUB 17,
BRZ 11, STA 16, JMP 0, LDA 14, STP 0, 1, 1, 0,
8, 1])
Prog4 = (name: "List",
code: @[LDA 13, ADD 15, STA 5, ADD 16, STA 7, NOP 0, STA 14, NOP 0,
BRZ 11, STA 15, JMP 0, LDA 14, STP 0, LDA 0, 0, 28,
1, 0, 0, 0, 6, 0, 2, 26,
5, 20, 3, 30, 1, 22, 4, 24])
Prog5 = (name: "Prisoner",
code: @[NOP 0, NOP 0, STP 0, NOP 0, LDA 3, SUB 29, BRZ 18, LDA 3,
STA 29, BRZ 14, LDA 1, ADD 31, STA 1, JMP 2, LDA 0, ADD 31,
STA 0, JMP 2, LDA 3, STA 29, LDA 1, ADD 30, ADD 3, STA 1,
LDA 0, ADD 30, ADD 3, STA 0, JMP 2, 0, 1, 3])
 
var computer: Computer
 
for prog in [Prog1, Prog2, Prog3, Prog4]:
computer.load prog
computer.run()
echo &"Result for {prog.name}: {computer.acc}"
 
# "Prog5" is different as it stops and waits for an input from the user.
# Input is stored at address 3 (current PC value) and scores are stored at addresses 0 and 1.
 
type Action = enum cooperate, defect
 
echo &"\nResult for {Prog5.name}:"
computer.load Prog5
computer.run()
 
for round in 1..5:
let action = Action(round and 1)
computer.enterValue(byte(action))
computer.run()
echo &"Round {round} Action: {action:9} Player: {computer.mem[0]} Computer: {computer.mem[1]}"
</syntaxhighlight>
 
{{out}}
<pre>Result for 2+2: 4
Result for 7*8: 56
Result for Fibonacci: 55
Result for List: 6
 
Result for Prisoner:
Round 1 Action: defect Player: 0 Computer: 3
Round 2 Action: cooperate Player: 3 Computer: 3
Round 3 Action: defect Player: 3 Computer: 6
Round 4 Action: cooperate Player: 6 Computer: 6
Round 5 Action: defect Player: 6 Computer: 9
</pre>
 
=={{header|Phix}}==
256

edits