You can create a function that is responsible for increasing the program counter, and another function that is responsible for executing the instructions.
Increase program counter
nextPC :: Instruccion
nextPC microprocesador = microprocesador { pC = pC microprocesador + 1 }
The nextPC
function takes a microprocessor and returns the same microprocessor, but with the counter incremented.
Execute instruction
run :: Instruccion -> Instruccion
run = (.) nextPC
The run
function takes one instruction and returns another instruction. What it does is to compose the instruction it receives with the function nextPC
: this way it will first perform the logic of the instruction it receives, and then it will increase the program counter.
This function could also be written like this, if it's easier to see:
run :: Instruccion -> Microprocesador -> Microprocesador
run instruccion microprocesador = nextPC (instruccion microprocesador)
This makes it explicit that what the function receives is an instruction and a microprocessor, and that it executes the received instruction to the received microprocessor, and then increases the program counter with nextPC
.
Define instructions
Since the program counter will be increased by the run
function, you must modify your instructions. For example, the function nop
will do nothing:
nop :: Instruccion
nop microprocesador = microprocesador
It returns what it receives, that is, it acts as the identity function id
, so you can write it directly as:
nop :: Instruccion
nop = id
And the same with the rest of the functions:
lodv :: Int -> Instruccion
lodv valor microprocesador = microprocesador { a = a microprocesador + valor }
swap :: Instruccion
swap microprocesador = microprocesador { b = a microprocesador, a = b microprocesador }
Example
Now you can execute your instructions like:
ghci> let x = Microprocesador "a" [] 0 1 2 ""
ghci> run nop x
Microprocesador {nombre = "a", memory = [], a = 0, b = 1, pC = 3, mensajeDeError = ""}