@@ -4,6 +4,7 @@ import android.util.Log
4
4
import com.emerjbl.ultra8.chip8.graphics.Chip8Font
5
5
import com.emerjbl.ultra8.chip8.graphics.SimpleGraphics
6
6
import com.emerjbl.ultra8.chip8.input.Chip8Keys
7
+ import com.emerjbl.ultra8.chip8.machine.Chip8.State
7
8
import com.emerjbl.ultra8.chip8.sound.Chip8Sound
8
9
import com.emerjbl.ultra8.chip8.sound.Pattern
9
10
import java.nio.ByteBuffer
@@ -12,6 +13,7 @@ import java.util.Random
12
13
import kotlin.math.pow
13
14
import kotlin.time.TimeSource
14
15
16
+
15
17
private const val EXEC_START : Int = 0x200
16
18
private const val FONT_START : Int = 0x000
17
19
private const val HIRES_FONT_START : Int = 0x100
@@ -58,18 +60,33 @@ sealed class Halt(val pc: Int) {
58
60
}
59
61
}
60
62
63
+ private fun stateFromProgram (program : ByteArray , font : Chip8Font ) = State ().apply {
64
+ font.lo.copyInto(mem, FONT_START )
65
+ font.hi.copyInto(mem, HIRES_FONT_START )
66
+ program.copyInto(mem, EXEC_START )
67
+ }
68
+
61
69
/* * All state of a running Chip8 Machine. */
62
70
class Chip8 (
63
71
private val keys : Chip8Keys ,
64
72
private val sound : Chip8Sound ,
65
73
private val font : Chip8Font ,
66
74
timeSource : TimeSource ,
67
- program : ByteArray
75
+ private val state : State ,
68
76
) {
77
+
78
+ constructor (
79
+ keys: Chip8Keys ,
80
+ sound: Chip8Sound ,
81
+ font: Chip8Font ,
82
+ timeSource: TimeSource ,
83
+ program: ByteArray
84
+ ) : this (keys, sound, font, timeSource, stateFromProgram(program, font))
85
+
69
86
/* * Collect all Chip8 state in one place. Convenient for eventual save/restore. */
70
87
class State (
71
88
/* * Registers V0-VF. */
72
- internal val v : IntArray = intArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
89
+ val v : IntArray = intArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
73
90
/* *
74
91
* Special HP flag registers.
75
92
*
@@ -80,76 +97,62 @@ class Chip8(
80
97
*
81
98
* These *should* be persisted, but we don't currently do that.
82
99
**/
83
- internal val hp : IntArray = intArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
100
+ val hp : IntArray = intArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
84
101
/* * Call stack. */
85
- internal val stack : IntArray = IntArray (64),
102
+ val stack : IntArray = IntArray (64),
86
103
/* *
87
104
* Machine memory.
88
105
*
89
106
* The original machine implementation is 4k, but Chip-XO can address up to 64k.
90
107
* For now, we just support the highest.
91
108
**/
92
- internal val mem : ByteArray = ByteArray (65536),
109
+ val mem : ByteArray = ByteArray (65536),
93
110
/* * Index register I. */
94
- internal var i : Int = 0 ,
111
+ var i : Int = 0 ,
95
112
/* * Stack pointer. */
96
- internal var sp : Int = 0 ,
113
+ var sp : Int = 0 ,
97
114
/* * Program counter. */
98
- internal var pc : Int = 0x200 ,
115
+ var pc : Int = 0x200 ,
99
116
/* * Chip-XO target plane for drawing (0-3). */
100
- internal var targetPlane : Int = 0x1
117
+ var targetPlane : Int = 0x1 ,
118
+
119
+ /* * Graphics buffer is an important part of state, too. */
120
+ val gfx : SimpleGraphics = SimpleGraphics ()
101
121
) {
102
122
/* *
103
123
* A read-only view of the Chip8 state.
104
124
*
105
125
* A given instance wraps an actual live machine state, so it will change as the machine does;
106
126
* it's not a static copy.
107
127
*/
108
- interface View {
109
- val v: IntBuffer
110
- val hp: IntBuffer
111
- val stack: IntBuffer
112
- val mem: ByteBuffer
113
- val i: Int
114
- val sp: Int
115
- val pc: Int
116
- val targetPlane: Int
128
+ class View (private val state : State ) {
129
+ val v = IntBuffer .wrap(state.v).asReadOnlyBuffer()
130
+ val hp = IntBuffer .wrap(state.hp).asReadOnlyBuffer()
131
+ val stack = IntBuffer .wrap(state.stack).asReadOnlyBuffer()
132
+ val mem = ByteBuffer .wrap(state.mem).asReadOnlyBuffer()
133
+ val i: Int get() = state.i
134
+ val sp: Int get() = state.sp
135
+ val pc: Int get() = state.pc
136
+ val targetPlane: Int get() = state.targetPlane
137
+
138
+ /* * Create a deep copy of the entire state. */
139
+ fun clone () = State (
140
+ state.v.clone(),
141
+ state.hp.clone(),
142
+ state.stack.clone(),
143
+ state.mem.clone(),
144
+ i,
145
+ sp,
146
+ pc,
147
+ targetPlane,
148
+ state.gfx.clone(),
149
+ )
117
150
}
118
-
119
- val stateView = object : View {
120
- override val v: IntBuffer = IntBuffer .wrap(this @State.v).asReadOnlyBuffer()
121
- override val hp: IntBuffer = IntBuffer .wrap(this @State.hp).asReadOnlyBuffer()
122
- override val stack: IntBuffer = IntBuffer .wrap(this @State.stack).asReadOnlyBuffer()
123
- override val mem: ByteBuffer = ByteBuffer .wrap(this @State.mem).asReadOnlyBuffer()
124
- override val i: Int
125
- get() = this @State.i
126
- override val sp: Int
127
- get() = this @State.sp
128
- override val pc: Int
129
- get() = this @State.pc
130
- override val targetPlane: Int
131
- get() = this @State.targetPlane
132
- }
133
-
134
- /* * Create a deep copy of the entire state. */
135
- fun clone () = State (
136
- v.clone(), hp.clone(), stack.clone(), mem.clone(),
137
- i, sp, pc, targetPlane,
138
- )
139
-
140
151
}
141
152
142
- private val gfx : SimpleGraphics = SimpleGraphics ( )
153
+ val stateView = State . View (state )
143
154
144
- fun nextFrame (frame : SimpleGraphics .Frame ? ): SimpleGraphics .Frame = gfx.nextFrame(frame)
145
-
146
-
147
- private val state = State ().apply {
148
- font.lo.copyInto(mem, FONT_START )
149
- font.hi.copyInto(mem, HIRES_FONT_START )
150
- program.copyInto(mem, EXEC_START )
151
- }
152
- val stateView = state.stateView
155
+ fun nextFrame (frame : SimpleGraphics .Frame ? ): SimpleGraphics .Frame = state.gfx.nextFrame(frame)
153
156
154
157
private val random: Random = Random ()
155
158
private val timer: Chip8Timer = Chip8Timer (timeSource)
@@ -166,7 +169,7 @@ class Chip8(
166
169
0xE0 -> gfx.clear()
167
170
168
171
0xEE -> {
169
- if (sp < 0 ) return Halt .StackUnderflow (pc - 2 )
172
+ if (sp < 1 ) return Halt .StackUnderflow (pc - 2 )
170
173
pc = stack[-- sp]
171
174
}
172
175
0 commit comments