summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/sm510/sm510op.cpp
blob: b8cc68de21f31e4e591c718dbfdde1f94dce06e2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
// license:BSD-3-Clause
// copyright-holders:hap

// shared opcode handlers

#include "emu.h"
#include "sm510.h"


// internal helpers

inline u8 sm510_base_device::ram_r()
{
	int bmh = (m_sbm) ? (1 << (m_datawidth-1)) : 0; // from SBM
	u8 address = (bmh | m_bm << 4 | m_bl) & m_datamask;
	return m_data->read_byte(address) & 0xf;
}

inline void sm510_base_device::ram_w(u8 data)
{
	int bmh = (m_sbm) ? (1 << (m_datawidth-1)) : 0; // from SBM
	u8 address = (bmh | m_bm << 4 | m_bl) & m_datamask;
	m_data->write_byte(address, data & 0xf);
}

void sm510_base_device::pop_stack()
{
	m_pc = m_stack[0] & m_prgmask;
	for (int i = 0; i < m_stack_levels-1; i++)
		m_stack[i] = m_stack[i+1];
}

void sm510_base_device::push_stack()
{
	for (int i = m_stack_levels-1; i >= 1; i--)
		m_stack[i] = m_stack[i-1];
	m_stack[0] = m_pc;
}

void sm510_base_device::do_branch(u8 pu, u8 pm, u8 pl)
{
	// set new PC(Pu/Pm/Pl)
	m_pc = ((pu << 10 & 0xc00) | (pm << 6 & 0x3c0) | (pl & 0x03f)) & m_prgmask;
}

inline u8 sm510_base_device::bitmask(u16 param)
{
	// bitmask from immediate opcode param
	return 1 << (param & 3);
}



// instruction set

// RAM address instructions

void sm510_base_device::op_lb()
{
	// LB x: load BM/BL with 4-bit immediate value (partial)

	// SM510 WIP..
	// bm and bl(low) are probably ok!
	m_bm = (m_bm & 4) | (m_op & 3);
	m_bl = (m_op >> 2 & 3);

	// bl(high) is still unclear, official doc is confusing
	u8 hi = 0;
	switch (m_bl)
	{
		case 0: hi = 0; break;
		case 1: hi = 3; break;
		case 2: hi = 3; break;
		case 3: hi = 3; break;
	}
	m_bl |= (hi << 2 & 0xc);
}

void sm510_base_device::op_lbl()
{
	// LBL xy: load BM/BL with 8-bit immediate value
	m_bl = m_param & 0xf;
	m_bm = (m_param & m_datamask) >> 4;
}

void sm510_base_device::op_sbm()
{
	// SBM: set BM high bit for next opcode - handled in execute_one()
}

void sm510_base_device::op_exbla()
{
	// EXBLA: exchange BL with ACC
	u8 a = m_acc;
	m_acc = m_bl;
	m_bl = a;
}

void sm510_base_device::op_incb()
{
	// INCB: increment BL, skip next on overflow
	m_bl = (m_bl + 1) & 0xf;
	m_skip = (m_bl == 0);
}

void sm510_base_device::op_decb()
{
	// DECB: decrement BL, skip next on overflow
	m_bl = (m_bl - 1) & 0xf;
	m_skip = (m_bl == 0xf);
}


// ROM address instructions

void sm510_base_device::op_atpl()
{
	// ATPL: load Pl(PC low bits) with ACC
	m_pc = (m_pc & ~0xf) | m_acc;
}

void sm510_base_device::op_rtn0()
{
	// RTN0: return from subroutine
	pop_stack();
}

void sm510_base_device::op_rtn1()
{
	// RTN1: return from subroutine, skip next
	op_rtn0();
	m_skip = true;
}

void sm510_base_device::op_t()
{
	// T xy: jump(transfer) within current page
	m_pc = (m_pc & ~0x3f) | (m_op & 0x3f);
}

void sm510_base_device::op_tl()
{
	// TL xyz: long jump
	do_branch(m_param >> 6 & 3, m_op & 0xf, m_param & 0x3f);
}

void sm510_base_device::op_tml()
{
	// TML xyz: long call
	push_stack();
	do_branch(m_param >> 6 & 3, m_op & 3, m_param & 0x3f);
}

void sm510_base_device::op_tm()
{
	// TM x: indirect subroutine call, pointers(IDX) are in page 0
	m_icount--;
	push_stack();
	u8 idx = m_program->read_byte(m_op & 0x3f);
	do_branch(idx >> 6 & 3, 4, idx & 0x3f);
}



// Data transfer instructions

void sm510_base_device::op_exc()
{
	// EXC x: exchange ACC with RAM, xor BM with x
	u8 a = m_acc;
	m_acc = ram_r();
	ram_w(a);
	m_bm ^= (m_op & 3);
}

void sm510_base_device::op_bdc()
{
	// BDC: enable LCD bleeder current with C
	m_bc = (m_c != 0);
}

void sm510_base_device::op_exci()
{
	// EXCI x: EXC x, INCB
	op_exc();
	op_incb();
}

void sm510_base_device::op_excd()
{
	// EXCD x: EXC x, DECB
	op_exc();
	op_decb();
}

void sm510_base_device::op_lda()
{
	// LDA x: load ACC with RAM, xor BM with x
	m_acc = ram_r();
	m_bm ^= (m_op & 3);
}

void sm510_base_device::op_lax()
{
	// LAX x: load ACC with immediate value, skip any next LAX
	if ((m_op & ~0xf) != (m_prev_op & ~0xf))
		m_acc = m_op & 0xf;
}

void sm510_base_device::op_ptw()
{
	// PTW: output W latch
	m_write_s(0, m_w, 0xff);
}

void sm510_base_device::op_wr()
{
	// WR: shift 0 into W
	m_w = m_w << 1 | 0;
	update_w_latch();
}

void sm510_base_device::op_ws()
{
	// WR: shift 1 into W
	m_w = m_w << 1 | 1;
	update_w_latch();
}


// I/O instructions

void sm510_base_device::op_kta()
{
	// KTA: input K to ACC
	m_acc = m_read_k(0, 0xff) & 0xf;
}

void sm510_base_device::op_atbp()
{
	// ATBP: output ACC to BP(internal LCD backplate signal)
	m_bp = ((m_acc & 1) != 0);
}

void sm510_base_device::op_atx()
{
	// ATX: output ACC to X
	m_x = m_acc;
}

void sm510_base_device::op_atl()
{
	// ATL: output ACC to L
	m_l = m_acc;
}

void sm510_base_device::op_atfc()
{
	// ATFC: output ACC to Y
	m_y = m_acc;
}

void sm510_base_device::op_atr()
{
	// ATR: output ACC to R
	if (m_r != (m_acc & 3))
	{
		m_r = m_acc & 3;
		m_write_r(0, m_r, 0xff);
	}
}


// Arithmetic instructions

void sm510_base_device::op_add()
{
	// ADD: add RAM to ACC
	m_acc = (m_acc + ram_r()) & 0xf;
}

void sm510_base_device::op_add11()
{
	// ADD11: add RAM and carry to ACC and carry, skip next on carry
	m_acc += ram_r() + m_c;
	m_c = m_acc >> 4 & 1;
	m_skip = (m_c == 1);
	m_acc &= 0xf;
}

void sm510_base_device::op_adx()
{
	// ADX x: add immediate value to ACC, skip next on carry except if x = 10
	m_acc += (m_op & 0xf);
	m_skip = ((m_op & 0xf) != 10 && (m_acc & 0x10) != 0);
	m_acc &= 0xf;
}

void sm510_base_device::op_coma()
{
	// COMA: complement ACC
	m_acc ^= 0xf;
}

void sm510_base_device::op_rot()
{
	// ROT: rotate ACC right through carry
	u8 c = m_acc & 1;
	m_acc = m_acc >> 1 | m_c << 3;
	m_c = c;
}

void sm510_base_device::op_rc()
{
	// RC: reset carry
	m_c = 0;
}

void sm510_base_device::op_sc()
{
	// SC: set carry
	m_c = 1;
}


// Test instructions

void sm510_base_device::op_tb()
{
	// TB: skip next if B(beta) pin is set
	m_skip = (m_read_b() != 0);
}

void sm510_base_device::op_tc()
{
	// TC: skip next if no carry
	m_skip = !m_c;
}

void sm510_base_device::op_tam()
{
	// TAM: skip next if ACC equals RAM
	m_skip = (m_acc == ram_r());
}

void sm510_base_device::op_tmi()
{
	// TMI x: skip next if RAM bit is set
	m_skip = ((ram_r() & bitmask(m_op)) != 0);
}

void sm510_base_device::op_ta0()
{
	// TA0: skip next if ACC is clear
	m_skip = !m_acc;
}

void sm510_base_device::op_tabl()
{
	// TABL: skip next if ACC equals BL
	m_skip = (m_acc == m_bl);
}

void sm510_base_device::op_tis()
{
	// TIS: skip next if 1S(gamma flag) is clear, reset it after
	m_skip = !m_1s;
	m_1s = false;
}

void sm510_base_device::op_tal()
{
	// TAL: skip next if BA pin is set
	m_skip = (m_read_ba() != 0);
}

void sm510_base_device::op_tf1()
{
	// TF1: skip next if divider F1(d14) is set
	m_skip = ((m_div & 0x4000) != 0);
}

void sm510_base_device::op_tf4()
{
	// TF4: skip next if divider F4(d11) is set
	m_skip = ((m_div & 0x0800) != 0);
}


// Bit manipulation instructions

void sm510_base_device::op_rm()
{
	// RM x: reset RAM bit
	ram_w(ram_r() & ~bitmask(m_op));
}

void sm510_base_device::op_sm()
{
	// SM x: set RAM bit
	ram_w(ram_r() | bitmask(m_op));
}


// Melody control instructions

void sm510_base_device::op_pre()
{
	// PRE x: melody ROM pointer preset
	m_melody_address = m_param;
	m_melody_step_count = 0;
}

void sm510_base_device::op_sme()
{
	// SME: set melody enable
	m_melody_rd |= 1;
}

void sm510_base_device::op_rme()
{
	// RME: reset melody enable
	m_melody_rd &= ~1;
}

void sm510_base_device::op_tmel()
{
	// TMEL: skip next if rest signal is set, reset it
	m_skip = ((m_melody_rd & 2) != 0);
	m_melody_rd &= ~2;
}


// Special instructions

void sm510_base_device::op_skip()
{
	// SKIP: no operation
}

void sm510_base_device::op_cend()
{
	// CEND: stop clock (halt the cpu and go into low-power mode)
	m_halt = true;
}

void sm510_base_device::op_idiv()
{
	// IDIV: reset divider
	m_div = 0;
}

void sm510_base_device::op_dr()
{
	// DR: reset divider low 8 bits
	m_div &= 0x7f;
}

void sm510_base_device::op_dta()
{
	// DTA: transfer divider low 4 bits to ACC
	m_acc = m_div >> 11 & 0xf;
}

void sm510_base_device::op_illegal()
{
	logerror("%s unknown opcode $%02X at $%04X\n", tag(), m_op, m_prev_pc);
}