From 8d6a0f8fd33d8a57d3ef43db4f3a36ae4827b16d Mon Sep 17 00:00:00 2001 From: Bradford Lamson-Scribner Date: Sat, 30 May 2020 17:06:14 -0600 Subject: [PATCH] internal/vm: start playing with op executions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- internal/vm/exec_funcs.go | 49 ++++++++++++++++++++++++++ internal/vm/opcodes.go | 73 ++++++++++++++++++++------------------- internal/vm/vm.go | 44 +++++++++++++++++++++++ 3 files changed, 131 insertions(+), 35 deletions(-) create mode 100644 internal/vm/exec_funcs.go diff --git a/internal/vm/exec_funcs.go b/internal/vm/exec_funcs.go new file mode 100644 index 0000000..b36b3a9 --- /dev/null +++ b/internal/vm/exec_funcs.go @@ -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 +} diff --git a/internal/vm/opcodes.go b/internal/vm/opcodes.go index 8667a47..4ee105e 100644 --- a/internal/vm/opcodes.go +++ b/internal/vm/opcodes.go @@ -2,7 +2,6 @@ package vm import ( "errors" - "fmt" ) // 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 } -// 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 -} - -func exec0xC6(a *Appleone, o op) error { - fmt.Println("Implement me") - return nil -} - -func todo(a *Appleone, o op) error { - fmt.Println("implement me") - return nil +func (o op) getAddr(a *Appleone) (uint16, error) { + switch o.addrMode { + // TODO: will these ever apply here? + // case accumulator: + // + // case implied: + // + case absolute: + return a.nextDWord(), nil + case absoluteXIndexed: + return a.nextDWord() + uint16(a.cpu.x), nil + case absoluteYIndexed: + return a.nextDWord() + uint16(a.cpu.y), nil + case immediate: + return a.cpu.pc - 1, nil + case indirect: + return uint16(a.nextWord()), nil + case indirectXIndexed: + addr := (uint16(a.nextWord()) + uint16(a.cpu.x)) & 0xFF + return a.littleEndianToUint16(a.mem[addr+1], a.mem[addr]), nil + case indirectYIndexed: + addr := uint16(a.nextWord()) + val := a.littleEndianToUint16(a.mem[addr+1], a.mem[addr]) + return val + uint16(a.cpu.y), nil + case relative: + return a.cpu.pc - 1, nil + case zeroPage: + return uint16(a.nextWord()) & 0xFF, nil + case zeroPageXIndexed: + return (uint16(a.nextWord()) + uint16(a.cpu.x)) & 0xFF, 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 @@ -90,9 +93,9 @@ var opcodes = map[uint8]op{ // absolute DEC oper CE 3 6 // absolute,X DEC oper,X DE 3 7 0xC6: newOp("DEC", 0xC6, 2, zeroPage, exec0xC6), - 0xD6: newOp("DEC", 0xD6, 2, zeroPageXIndexed, todo), - 0xCE: newOp("DEC", 0xCE, 3, absolute, todo), - 0xDE: newOp("DEC", 0xDE, 3, absoluteXIndexed, todo), + 0xD6: newOp("DEC", 0xD6, 2, zeroPageXIndexed, exec0xC6), + 0xCE: newOp("DEC", 0xCE, 3, absolute, exec0xC6), + 0xDE: newOp("DEC", 0xDE, 3, absoluteXIndexed, exec0xC6), // INC Increment Memory by One // addressing assembler opc bytes cyles diff --git a/internal/vm/vm.go b/internal/vm/vm.go index b0fd353..e439a9d 100644 --- a/internal/vm/vm.go +++ b/internal/vm/vm.go @@ -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 func (a *Appleone) pushWordToStack(b byte) { a.mem[StackBottom+uint16(a.cpu.sp)] = b @@ -62,3 +66,43 @@ func (a *Appleone) popStackDWord() uint16 { h := a.popStackWord() 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 +}