mirror of
https://github.com/romychs/Ocean-240.2-Emulator.git
synced 2026-04-21 11:03:21 +03:00
Initial commit
This commit is contained in:
commit
76829b80cc
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.bak
|
||||
*.lst
|
||||
*.tmp
|
||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/z80em.iml" filepath="$PROJECT_DIR$/.idea/z80em.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/z80em.iml
generated
Normal file
9
.idea/z80em.iml
generated
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
21
main.go
Normal file
21
main.go
Normal file
@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//TIP <p>To run your code, right-click the code and select <b>Run</b>.</p> <p>Alternatively, click
|
||||
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.</p>
|
||||
|
||||
func main() {
|
||||
//TIP <p>Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined text
|
||||
// to see how GoLand suggests fixing the warning.</p><p>Alternatively, if available, click the lightbulb to view possible fixes.</p>
|
||||
s := "gopher"
|
||||
fmt.Printf("Hello and welcome, %s!\n", s)
|
||||
|
||||
for i := 1; i <= 5; i++ {
|
||||
//TIP <p>To start your debugging session, right-click your code in the editor and select the Debug option.</p> <p>We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
||||
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.</p>
|
||||
fmt.Println("i =", 100/i)
|
||||
}
|
||||
}
|
||||
27
z80em/core.go
Normal file
27
z80em/core.go
Normal file
@ -0,0 +1,27 @@
|
||||
package z80em
|
||||
|
||||
const OP_HALT = 0x76
|
||||
const OP_LD_B_B = 0x40
|
||||
const OP_LD_A_A = 0x7f
|
||||
const OP_ADD_A_B = 0x80
|
||||
const OP_RET_NZ = 0xc0
|
||||
|
||||
var CYCLE_COUNTS = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
var PARITY_BITS = []bool{
|
||||
true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true,
|
||||
false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false,
|
||||
false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false,
|
||||
true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true,
|
||||
false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false,
|
||||
true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true,
|
||||
true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true,
|
||||
false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false,
|
||||
false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false,
|
||||
true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true,
|
||||
true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true,
|
||||
false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false,
|
||||
true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true,
|
||||
false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false,
|
||||
false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false,
|
||||
true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true,
|
||||
}
|
||||
550
z80em/z80em.go
Normal file
550
z80em/z80em.go
Normal file
@ -0,0 +1,550 @@
|
||||
package z80em
|
||||
|
||||
const SpDefault uint16 = 0xdff0
|
||||
|
||||
// FlagsType - Processor flags
|
||||
type FlagsType struct {
|
||||
S bool
|
||||
Z bool
|
||||
Y bool
|
||||
H bool
|
||||
X bool
|
||||
P bool
|
||||
N bool
|
||||
C bool
|
||||
}
|
||||
|
||||
// StateType - Processor state
|
||||
type StateType struct {
|
||||
A byte
|
||||
B byte
|
||||
C byte
|
||||
D byte
|
||||
E byte
|
||||
H byte
|
||||
L byte
|
||||
APrime byte
|
||||
BPrime byte
|
||||
CPrime byte
|
||||
DPrime byte
|
||||
EPrime byte
|
||||
HPrime byte
|
||||
LPrime byte
|
||||
IX byte
|
||||
IY byte
|
||||
I byte
|
||||
R byte
|
||||
SP uint16
|
||||
PC uint16
|
||||
Flags FlagsType
|
||||
FlagsPrime FlagsType
|
||||
|
||||
IMode byte
|
||||
Iff1 byte
|
||||
Iff2 byte
|
||||
Halted bool
|
||||
DoDelayedDI bool
|
||||
DoDelayedEI bool
|
||||
CycleCounter byte
|
||||
interruptOccurred bool
|
||||
core MemIoRW
|
||||
}
|
||||
|
||||
type MemIoRW interface {
|
||||
// M1MemRead Read byte from memory for specified address
|
||||
M1MemRead(addr uint16) byte
|
||||
// MemRead Read byte from memory for specified address
|
||||
MemRead(addr uint16) byte
|
||||
// MemWrite Write byte to memory to specified address
|
||||
MemWrite(addr uint16, val byte)
|
||||
// IORead Read byte from specified port
|
||||
IORead(port uint16) byte
|
||||
// IOWrite Write byte to specified port
|
||||
IOWrite(port uint16, val byte)
|
||||
}
|
||||
|
||||
type Z80em interface {
|
||||
// Reset CPU to initial state
|
||||
Reset()
|
||||
// RunInstruction Run single instruction, return number of CPU cycles
|
||||
RunInstruction() byte
|
||||
// GetState Get current CPU state
|
||||
GetState() *StateType
|
||||
// SetState Set current CPU state
|
||||
SetState(state *StateType)
|
||||
// setFlagsRegister Set value for CPU flags by specified byte [7:0] = SZYHXPNC
|
||||
setFlagsRegister(flags byte)
|
||||
|
||||
decodeInstruction(opcode byte) byte
|
||||
pushWord(pc uint16)
|
||||
}
|
||||
|
||||
func (s StateType) Reset() {
|
||||
s.A = 0
|
||||
s.R = 0
|
||||
s.SP = SpDefault
|
||||
s.PC = 0
|
||||
s.setFlagsRegister(0)
|
||||
// Interrupts disabled
|
||||
s.IMode = 0
|
||||
s.Iff1 = 0
|
||||
s.Iff2 = 0
|
||||
s.interruptOccurred = false
|
||||
|
||||
// Start not halted
|
||||
s.Halted = false
|
||||
s.DoDelayedDI = false
|
||||
s.DoDelayedEI = false
|
||||
// no cycles
|
||||
s.CycleCounter = 0
|
||||
}
|
||||
|
||||
func (s StateType) GetState() *StateType {
|
||||
return &StateType{
|
||||
A: s.A,
|
||||
B: s.B,
|
||||
C: s.C,
|
||||
D: s.D,
|
||||
E: s.E,
|
||||
H: s.H,
|
||||
L: s.L,
|
||||
APrime: s.APrime,
|
||||
BPrime: s.BPrime,
|
||||
CPrime: s.CPrime,
|
||||
DPrime: s.DPrime,
|
||||
EPrime: s.EPrime,
|
||||
HPrime: s.HPrime,
|
||||
IX: s.IX,
|
||||
IY: s.IY,
|
||||
R: s.R,
|
||||
SP: s.SP,
|
||||
PC: s.PC,
|
||||
Flags: s.Flags,
|
||||
FlagsPrime: s.FlagsPrime,
|
||||
IMode: s.IMode,
|
||||
Iff1: s.Iff1,
|
||||
Iff2: s.Iff2,
|
||||
Halted: s.Halted,
|
||||
DoDelayedDI: s.DoDelayedDI,
|
||||
DoDelayedEI: s.DoDelayedEI,
|
||||
CycleCounter: s.CycleCounter,
|
||||
}
|
||||
}
|
||||
|
||||
func (s StateType) SetState(state *StateType) {
|
||||
s.A = state.A
|
||||
s.B = state.B
|
||||
s.C = state.C
|
||||
s.D = state.D
|
||||
s.E = state.E
|
||||
s.H = state.H
|
||||
s.L = state.L
|
||||
s.APrime = state.APrime
|
||||
s.BPrime = state.BPrime
|
||||
s.CPrime = state.CPrime
|
||||
s.DPrime = state.DPrime
|
||||
s.EPrime = state.EPrime
|
||||
s.HPrime = state.HPrime
|
||||
s.IX = state.IX
|
||||
s.IY = state.IY
|
||||
s.I = state.I
|
||||
s.R = state.R
|
||||
s.SP = state.SP
|
||||
s.PC = state.PC
|
||||
s.Flags = state.Flags
|
||||
s.FlagsPrime = state.FlagsPrime
|
||||
s.IMode = state.IMode
|
||||
s.Iff1 = state.Iff1
|
||||
s.Iff2 = state.Iff2
|
||||
s.Halted = state.Halted
|
||||
s.DoDelayedDI = state.DoDelayedDI
|
||||
s.DoDelayedEI = state.DoDelayedEI
|
||||
s.CycleCounter = state.CycleCounter
|
||||
}
|
||||
|
||||
// New Create new
|
||||
func New(memIoRW MemIoRW) *StateType {
|
||||
return &StateType{
|
||||
A: 0,
|
||||
B: 0,
|
||||
C: 0,
|
||||
D: 0,
|
||||
E: 0,
|
||||
H: 0,
|
||||
L: 0,
|
||||
APrime: 0,
|
||||
BPrime: 0,
|
||||
CPrime: 0,
|
||||
DPrime: 0,
|
||||
EPrime: 0,
|
||||
HPrime: 0,
|
||||
IX: 0,
|
||||
IY: 0,
|
||||
I: 0,
|
||||
|
||||
R: 0,
|
||||
SP: SpDefault,
|
||||
PC: 0,
|
||||
Flags: FlagsType{false, false, false, false, false, false, false, false},
|
||||
FlagsPrime: FlagsType{false, false, false, false, false, false, false, false},
|
||||
IMode: 0,
|
||||
Iff1: 0,
|
||||
Iff2: 0,
|
||||
Halted: false,
|
||||
DoDelayedDI: false,
|
||||
DoDelayedEI: false,
|
||||
CycleCounter: 0,
|
||||
interruptOccurred: false,
|
||||
core: memIoRW,
|
||||
}
|
||||
}
|
||||
|
||||
func (s StateType) RunInstruction() byte {
|
||||
|
||||
// R is incremented at the start of every instruction cycle,
|
||||
// before the instruction actually runs.
|
||||
// The high bit of R is not affected by this increment,
|
||||
// it can only be changed using the LD R, A instruction.
|
||||
// Note: also a HALT does increment the R register.
|
||||
s.R = (s.R & 0x80) | (((s.R & 0x7f) + 1) & 0x7f)
|
||||
|
||||
if !s.Halted {
|
||||
// If the previous instruction was a DI or an EI,
|
||||
// we'll need to disable or enable interrupts
|
||||
// after whatever instruction we're about to run is finished.
|
||||
doingDelayedDi := false
|
||||
doingDelayedEi := false
|
||||
if s.DoDelayedDI {
|
||||
s.DoDelayedDI = false
|
||||
doingDelayedDi = true
|
||||
} else if s.DoDelayedEI {
|
||||
s.DoDelayedEI = false
|
||||
doingDelayedEi = true
|
||||
}
|
||||
|
||||
// Read the byte at the PC and run the instruction it encodes.
|
||||
opcode := s.core.M1MemRead(s.PC)
|
||||
s.decodeInstruction(opcode)
|
||||
|
||||
// HALT does not increase the PC
|
||||
if !s.Halted {
|
||||
s.PC++
|
||||
}
|
||||
|
||||
// Actually do the delayed interrupt disable/enable if we have one.
|
||||
if doingDelayedDi {
|
||||
s.Iff1 = 0
|
||||
s.Iff2 = 0
|
||||
} else if doingDelayedEi {
|
||||
s.Iff1 = 1
|
||||
s.Iff2 = 1
|
||||
}
|
||||
|
||||
// And finally clear out the cycle counter for the next instruction
|
||||
// before returning it to the emulator core.
|
||||
cycleCounter := s.CycleCounter
|
||||
s.CycleCounter = 0
|
||||
return cycleCounter
|
||||
} else { // HALTED
|
||||
// During HALT, NOPs are executed which is 4T
|
||||
s.core.M1MemRead(s.PC) // HALT does a normal M1 fetch to keep the memory refresh active. The result is ignored (NOP).
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
// Simulates pulsing the processor's INT (or NMI) pin
|
||||
//
|
||||
// nonMaskable - true if this is a non-maskable interrupt
|
||||
// data - the value to be placed on the data bus, if needed
|
||||
func (s StateType) interrupt(nonMaskable bool, data byte) {
|
||||
if nonMaskable {
|
||||
// An interrupt, if halted, does increase the PC
|
||||
if s.Halted {
|
||||
s.PC++
|
||||
}
|
||||
|
||||
// The high bit of R is not affected by this increment,
|
||||
// it can only be changed using the LD R, A instruction.
|
||||
s.R = (s.R & 0x80) | (((s.R & 0x7f) + 1) & 0x7f)
|
||||
|
||||
// Non-maskable interrupts are always handled the same way;
|
||||
// clear IFF1 and then do a CALL 0x0066.
|
||||
// Also, all interrupts reset the HALT state.
|
||||
|
||||
s.Halted = false
|
||||
s.Iff2 = s.Iff1
|
||||
s.Iff1 = 0
|
||||
s.pushWord(s.PC)
|
||||
s.PC = 0x66
|
||||
|
||||
s.CycleCounter += 11
|
||||
} else if s.Iff1 != 0 {
|
||||
// An interrupt, if halted, does increase the PC
|
||||
if s.Halted {
|
||||
s.PC++
|
||||
}
|
||||
|
||||
// The high bit of R is not affected by this increment,
|
||||
// it can only be changed using the LD R,A instruction.
|
||||
s.R = (s.R & 0x80) | (((s.R & 0x7f) + 1) & 0x7f)
|
||||
|
||||
s.Halted = false
|
||||
s.Iff1 = 0
|
||||
s.Iff2 = 0
|
||||
|
||||
if s.IMode == 0 {
|
||||
// In the 8080-compatible interrupt mode,
|
||||
// decode the content of the data bus as an instruction and run it.
|
||||
// it's probably a RST instruction, which pushes (PC+1) onto the stack
|
||||
// so we should decrement PC before we decode the instruction
|
||||
s.PC--
|
||||
s.decodeInstruction(data)
|
||||
s.PC++ // increment PC upon return
|
||||
s.CycleCounter += 2
|
||||
} else if s.IMode == 1 {
|
||||
// Mode 1 is always just RST 0x38.
|
||||
s.pushWord(s.PC)
|
||||
s.PC = 0x0038
|
||||
s.CycleCounter += 13
|
||||
} else if s.IMode == 2 {
|
||||
// Mode 2 uses the value on the data bus as in index
|
||||
// into the vector table pointer to by the I register.
|
||||
s.pushWord(s.PC)
|
||||
|
||||
// The Z80 manual says that this address must be 2-byte aligned,
|
||||
// but it doesn't appear that this is actually the case on the hardware,
|
||||
// so we don't attempt to enforce that here.
|
||||
vectorAddress := (uint16(s.I) << 8) | uint16(data)
|
||||
s.PC = uint16(s.core.MemRead(vectorAddress)) | (uint16(s.core.MemRead(vectorAddress+1)) << 8)
|
||||
s.CycleCounter += 19
|
||||
// A "notification" is generated so that the calling program can break on it.
|
||||
s.interruptOccurred = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s StateType) pushWord(pc uint16) {
|
||||
// TODO: Implement
|
||||
panic("not yet implemented")
|
||||
}
|
||||
|
||||
func (s StateType) getOperand(opcode byte) byte {
|
||||
switch opcode & 0x07 {
|
||||
case 0:
|
||||
return s.B
|
||||
case 1:
|
||||
return s.C
|
||||
case 2:
|
||||
return s.D
|
||||
case 3:
|
||||
return s.E
|
||||
case 4:
|
||||
return s.H
|
||||
case 5:
|
||||
return s.L
|
||||
case 6:
|
||||
return s.core.MemRead(uint16(s.H)<<8 | uint16(s.L))
|
||||
default:
|
||||
return s.A
|
||||
}
|
||||
}
|
||||
|
||||
func (s StateType) decodeInstruction(opcode byte) {
|
||||
// Handle HALT right up front, because it fouls up our LD decoding
|
||||
// by falling where LD (HL), (HL) ought to be.
|
||||
if opcode == OP_HALT {
|
||||
s.Halted = true
|
||||
} else if opcode >= OP_LD_B_B && opcode < OP_ADD_A_B {
|
||||
// This entire range is all 8-bit register loads.
|
||||
// Get the operand and assign it to the correct destination.
|
||||
s.load8bit(opcode, s.getOperand(opcode))
|
||||
} else if (opcode >= OP_ADD_A_B) && (opcode < OP_RET_NZ) {
|
||||
// These are the 8-bit register ALU instructions.
|
||||
// We'll get the operand and then use this "jump table"
|
||||
// to call the correct utility function for the instruction.
|
||||
s.alu8bit(opcode, s.getOperand(opcode))
|
||||
} else {
|
||||
// This is one of the less formulaic instructions;
|
||||
// we'll get the specific function for it from our array.
|
||||
s.otherInstructions(opcode)
|
||||
}
|
||||
s.CycleCounter += CYCLE_COUNTS[opcode]
|
||||
}
|
||||
|
||||
func (s StateType) load8bit(opcode byte, operand byte) {
|
||||
switch (opcode & 0x38) >> 3 {
|
||||
case 0:
|
||||
s.B = operand
|
||||
case 1:
|
||||
s.C = operand
|
||||
case 2:
|
||||
s.D = operand
|
||||
case 3:
|
||||
s.E = operand
|
||||
case 4:
|
||||
s.H = operand
|
||||
case 5:
|
||||
s.L = operand
|
||||
case 6:
|
||||
s.core.MemWrite(uint16(s.H)<<8|uint16(s.L), operand)
|
||||
default:
|
||||
s.A = operand
|
||||
}
|
||||
}
|
||||
|
||||
func (s StateType) alu8bit(opcode byte, operand byte) {
|
||||
switch (opcode & 0x38) >> 3 {
|
||||
case 0:
|
||||
s.doAdd(operand)
|
||||
case 1:
|
||||
s.doAdc(operand)
|
||||
case 2:
|
||||
s.doSub(operand)
|
||||
case 3:
|
||||
s.doSbc(operand)
|
||||
case 4:
|
||||
s.doAnd(operand)
|
||||
case 5:
|
||||
s.doXor(operand)
|
||||
case 6:
|
||||
s.doOr(operand)
|
||||
default:
|
||||
s.doCp(operand)
|
||||
}
|
||||
}
|
||||
|
||||
func (s StateType) doAdd(operand byte) {
|
||||
|
||||
}
|
||||
|
||||
func (s StateType) doAdc(operand byte) {
|
||||
|
||||
}
|
||||
|
||||
func (s StateType) doSub(operand byte) {
|
||||
|
||||
}
|
||||
|
||||
func (s StateType) doSbc(operand byte) {
|
||||
|
||||
}
|
||||
|
||||
func (s StateType) doAnd(operand byte) {
|
||||
|
||||
}
|
||||
|
||||
func (s StateType) doXor(operand byte) {
|
||||
|
||||
}
|
||||
|
||||
func (s StateType) doOr(operand byte) {
|
||||
|
||||
}
|
||||
|
||||
func (s StateType) doCp(operand byte) {
|
||||
|
||||
}
|
||||
|
||||
func (s StateType) otherInstructions(opcode byte) {
|
||||
|
||||
}
|
||||
|
||||
// getFlagsRegister return whole F register
|
||||
func (s StateType) getFlagsRegister() byte {
|
||||
return getFlags(&s.Flags)
|
||||
}
|
||||
|
||||
// getFlagsRegister return whole F' register
|
||||
func (s StateType) getFlagsPrimeRegister() byte {
|
||||
return getFlags(&s.FlagsPrime)
|
||||
}
|
||||
|
||||
func getFlags(f *FlagsType) byte {
|
||||
var flags byte = 0
|
||||
if f.S {
|
||||
flags |= 0x80
|
||||
}
|
||||
if f.Z {
|
||||
flags |= 0x40
|
||||
}
|
||||
|
||||
if f.Y {
|
||||
flags |= 0x20
|
||||
}
|
||||
|
||||
if f.H {
|
||||
flags |= 0x10
|
||||
}
|
||||
|
||||
if f.X {
|
||||
flags |= 0x08
|
||||
}
|
||||
|
||||
if f.P {
|
||||
flags |= 0x04
|
||||
}
|
||||
|
||||
if f.N {
|
||||
flags |= 0x02
|
||||
}
|
||||
|
||||
if f.C {
|
||||
flags |= 0x01
|
||||
}
|
||||
return flags
|
||||
|
||||
}
|
||||
|
||||
func (s StateType) setFlagsRegister(flags byte) {
|
||||
setFlags(flags, &s.Flags)
|
||||
}
|
||||
|
||||
func (s StateType) setFlagsPrimeRegister(flags byte) {
|
||||
setFlags(flags, &s.FlagsPrime)
|
||||
}
|
||||
|
||||
func setFlags(flags byte, f *FlagsType) {
|
||||
f.S = flags&0x80 != 0
|
||||
f.Z = flags&0x40 != 0
|
||||
f.Y = flags&0x20 != 0
|
||||
f.H = flags&0x10 != 0
|
||||
f.X = flags&0x08 != 0
|
||||
f.P = flags&0x04 != 0
|
||||
f.N = flags&0x02 != 0
|
||||
f.C = flags&0x01 != 0
|
||||
}
|
||||
|
||||
func (s StateType) updateXYFlags(result byte) {
|
||||
// Most of the time, the undocumented flags
|
||||
// (sometimes called X and Y, or 3 and 5),
|
||||
// take their values from the corresponding bits
|
||||
// of the result of the instruction,
|
||||
// or from some other related value.
|
||||
// This is a utility function to set those flags based on those bits.
|
||||
s.Flags.Y = (result&0x20)>>5 != 0
|
||||
s.Flags.X = (result&0x08)>>3 != 0
|
||||
}
|
||||
|
||||
func getParity(value byte) bool {
|
||||
return PARITY_BITS[value]
|
||||
}
|
||||
|
||||
func (s StateType) PushWord(operand uint16) {
|
||||
// Pretty obvious what this function does; given a 16-bit value,
|
||||
// decrement the stack pointer, write the high byte to the new
|
||||
// stack pointer location, then repeat for the low byte.
|
||||
s.SP--
|
||||
s.core.MemWrite(s.SP, byte((operand&0xff00)>>8))
|
||||
s.SP--
|
||||
s.core.MemWrite(s.SP, byte(operand&0x00ff))
|
||||
}
|
||||
|
||||
func (s StateType) PopWord() uint16 {
|
||||
// Again, not complicated; read a byte off the top of the stack,
|
||||
// increment the stack pointer, rinse and repeat.
|
||||
result := uint16(s.core.MemRead(s.SP))
|
||||
s.SP++
|
||||
result |= uint16(s.core.MemRead(s.SP)) << 8
|
||||
s.SP++
|
||||
return result
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user