internal/vm: start playing with op executions

So far the pattern feels a tiny bit weird but also has been nice to work
with. As you can see, the op now contains it’s instruction execution func
which can be called directly from the op.
This commit is contained in:
Bradford Lamson-Scribner 2020-05-30 17:06:14 -06:00
parent 2bd748fccd
commit 8d6a0f8fd3
3 changed files with 131 additions and 35 deletions

49
internal/vm/exec_funcs.go Normal file
View File

@ -0,0 +1,49 @@
package vm
import "fmt"
func todo(a *Appleone, o op) error {
fmt.Println("implement me")
return nil
}
// interrupt, N Z C I D V
// push PC+2, push SR - - - 1 - -
func exec0x00(a *Appleone, o op) error {
// set processer status flag to BRK
a.cpu.ps = flagBreak
a.pushDWordToStack(a.cpu.pc + 1)
a.pushWordToStack(a.cpu.ps)
a.cpu.ps |= flagDisableInterrupts
a.cpu.pc = uint16(a.mem[0xFFFF])<<8 | uint16(a.mem[0xFFFE])
return nil
}
// pull SR, pull PC N Z C I D V
// from stack
func exec0x40(a *Appleone, o op) error {
a.cpu.ps = a.popStackWord()
a.cpu.pc = a.popStackDWord()
return nil
}
// M - 1 -> M N Z C I D V
// + + - - - -
func exec0xC6(a *Appleone, o op) error {
addr, err := o.getAddr(a)
if err != nil {
return err
}
b := a.mem[addr]
b--
a.mem[addr] = b
a.setZeroIfNeeded(b)
a.setNegativeIfOverflow(b)
return nil
}

View File

@ -2,7 +2,6 @@ package vm
import ( import (
"errors" "errors"
"fmt"
) )
// op represents an operation. It includes the name of the op, it's 8 bit hexidecimal // op represents an operation. It includes the name of the op, it's 8 bit hexidecimal
@ -34,37 +33,41 @@ func opByCode(b byte) (op, error) {
return o, nil return o, nil
} }
// interrupt, N Z C I D V func (o op) getAddr(a *Appleone) (uint16, error) {
// push PC+2, push SR - - - 1 - - switch o.addrMode {
func exec0x00(a *Appleone, o op) error { // TODO: will these ever apply here?
// set processer status flag to BRK // case accumulator:
a.cpu.ps = flagBreak //
// case implied:
a.pushDWordToStack(a.cpu.pc + 1) //
a.pushWordToStack(a.cpu.ps) case absolute:
return a.nextDWord(), nil
a.cpu.ps |= flagDisableInterrupts case absoluteXIndexed:
a.cpu.pc = uint16(a.mem[0xFFFF])<<8 | uint16(a.mem[0xFFFE]) return a.nextDWord() + uint16(a.cpu.x), nil
case absoluteYIndexed:
return nil return a.nextDWord() + uint16(a.cpu.y), nil
} case immediate:
return a.cpu.pc - 1, nil
// pull SR, pull PC N Z C I D V case indirect:
// from stack return uint16(a.nextWord()), nil
func exec0x40(a *Appleone, o op) error { case indirectXIndexed:
a.cpu.ps = a.popStackWord() addr := (uint16(a.nextWord()) + uint16(a.cpu.x)) & 0xFF
a.cpu.pc = a.popStackDWord() return a.littleEndianToUint16(a.mem[addr+1], a.mem[addr]), nil
return nil case indirectYIndexed:
} addr := uint16(a.nextWord())
val := a.littleEndianToUint16(a.mem[addr+1], a.mem[addr])
func exec0xC6(a *Appleone, o op) error { return val + uint16(a.cpu.y), nil
fmt.Println("Implement me") case relative:
return nil return a.cpu.pc - 1, nil
} case zeroPage:
return uint16(a.nextWord()) & 0xFF, nil
func todo(a *Appleone, o op) error { case zeroPageXIndexed:
fmt.Println("implement me") return (uint16(a.nextWord()) + uint16(a.cpu.x)) & 0xFF, nil
return nil case zeroPageYIndexed:
return (uint16(a.nextWord()) + uint16(a.cpu.y)) & 0xFF, nil
default:
return 0, errors.New("unkown addressing mode")
}
} }
// opcodes represent all of the Apple 1 opcodes available. Each 8 bit opcode is mapped to a corresponding // opcodes represent all of the Apple 1 opcodes available. Each 8 bit opcode is mapped to a corresponding
@ -90,9 +93,9 @@ var opcodes = map[uint8]op{
// absolute DEC oper CE 3 6 // absolute DEC oper CE 3 6
// absolute,X DEC oper,X DE 3 7 // absolute,X DEC oper,X DE 3 7
0xC6: newOp("DEC", 0xC6, 2, zeroPage, exec0xC6), 0xC6: newOp("DEC", 0xC6, 2, zeroPage, exec0xC6),
0xD6: newOp("DEC", 0xD6, 2, zeroPageXIndexed, todo), 0xD6: newOp("DEC", 0xD6, 2, zeroPageXIndexed, exec0xC6),
0xCE: newOp("DEC", 0xCE, 3, absolute, todo), 0xCE: newOp("DEC", 0xCE, 3, absolute, exec0xC6),
0xDE: newOp("DEC", 0xDE, 3, absoluteXIndexed, todo), 0xDE: newOp("DEC", 0xDE, 3, absoluteXIndexed, exec0xC6),
// INC Increment Memory by One // INC Increment Memory by One
// addressing assembler opc bytes cyles // addressing assembler opc bytes cyles

View File

@ -36,6 +36,10 @@ func (a *Appleone) step() {
} }
} }
func (a *Appleone) littleEndianToUint16(big, little uint8) uint16 {
return uint16(a.mem[big])<<8 | uint16(a.mem[little])
}
// pushWordToStack pushes the given word (byte) into memory and sets the new stack pointer // pushWordToStack pushes the given word (byte) into memory and sets the new stack pointer
func (a *Appleone) pushWordToStack(b byte) { func (a *Appleone) pushWordToStack(b byte) {
a.mem[StackBottom+uint16(a.cpu.sp)] = b a.mem[StackBottom+uint16(a.cpu.sp)] = b
@ -62,3 +66,43 @@ func (a *Appleone) popStackDWord() uint16 {
h := a.popStackWord() h := a.popStackWord()
return (uint16(h) << 8) | uint16(l) return (uint16(h) << 8) | uint16(l)
} }
// nextWord returns the next byte in memory
func (a *Appleone) nextWord() uint8 {
return a.mem[a.cpu.pc-1]
}
// nextDWord returns the next two bytes (double word)
func (a *Appleone) nextDWord() uint16 {
return a.littleEndianToUint16(a.mem[a.cpu.pc-1], a.mem[a.cpu.pc-2])
}
func (a *Appleone) setZeroIfNeeded(word uint8) {
a.clearZero()
if word == 0 {
a.setZero()
}
}
func (a *Appleone) setZero() {
a.cpu.ps |= flagZero
}
func (a *Appleone) clearZero() {
a.cpu.sp &^= flagZero
}
func (a *Appleone) setNegativeIfOverflow(word uint8) {
a.clearNegative()
if word > 127 {
a.setNegative()
}
}
func (a *Appleone) setNegative() {
a.cpu.ps |= flagZero
}
func (a *Appleone) clearNegative() {
a.cpu.sp &^= flagZero
}