1 // license:BSD-3-Clause
2 // copyright-holders:Wilbert Pol
3 /*
4   Implementation for Sharp sm8500 cpu. There is hardly any information available
5   on this cpu. Currently we've only found documentation on the microcontroller
6   parts of the cpu, but nothing on the cpu itself.
7 
8   Through looking at binary data we have attempted to figure out the opcodes for
9   this cpu, and made educated guesses on the number of cycles for each instruction.
10 
11   Code by Wilbert Pol
12 
13 
14 There is some internal ram for the main cpu registers. They are offset by an index value.
15 The address is (PS0 & 0xF8) + register number. It is not known what happens when PS0 >= F8.
16 The assumption is that F8 to 107 is used, but it might wrap around instead.
17 The registers also mirror out to main RAM, appearing at 0000 to 000F regardless of where
18 they are internally.
19 
20 */
21 
22 #include "emu.h"
23 #include "sm8500.h"
24 #include "sm8500d.h"
25 #include "debugger.h"
26 
27 
28 DEFINE_DEVICE_TYPE(SM8500, sm8500_cpu_device, "sm8500", "Sharp SM8500")
29 
30 
31 static constexpr uint8_t sm8500_b2w[8] = {
32 		0, 8, 2, 10, 4, 12, 6, 14
33 };
34 
35 
sm8500_cpu_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)36 sm8500_cpu_device::sm8500_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
37 	: cpu_device(mconfig, SM8500, tag, owner, clock)
38 	, m_program_config("program", ENDIANNESS_BIG, 8, 16, 0)
39 	, m_dma_func(*this)
40 	, m_timer_func(*this)
41 	, m_PC(0), m_IE0(0), m_IE1(0), m_IR0(0), m_IR1(0)
42 	, m_SYS(0), m_CKC(0), m_clock_changed(0)
43 	, m_SP(0)
44 	, m_PS0(0)
45 	, m_PS1(0), m_IFLAGS(0), m_CheckInterrupts(0), m_halted(0), m_icount(0), m_program(nullptr), m_oldpc(0)
46 {
47 }
48 
memory_space_config() const49 device_memory_interface::space_config_vector sm8500_cpu_device::memory_space_config() const
50 {
51 	return space_config_vector {
52 		std::make_pair(AS_PROGRAM, &m_program_config),
53 	};
54 }
55 
56 
get_sp()57 void sm8500_cpu_device::get_sp()
58 {
59 	m_SP = m_program->read_byte(0x1d);
60 	if (m_SYS & 0x40) m_SP |= ( m_program->read_byte(0x1c) << 8 );
61 }
62 
63 
mem_readbyte(uint32_t offset) const64 uint8_t sm8500_cpu_device::mem_readbyte( uint32_t offset ) const
65 {
66 	offset &= 0xffff;
67 	if ( offset < 0x10)
68 	{
69 		return m_register_ram[offset + (m_PS0 & 0xF8)];
70 	}
71 
72 	return m_program->read_byte( offset );
73 }
74 
75 
mem_writebyte(uint32_t offset,uint8_t data)76 void sm8500_cpu_device::mem_writebyte( uint32_t offset, uint8_t data )
77 {
78 	uint8_t i;
79 	offset &= 0xffff;
80 	if (offset < 0x10)
81 	{
82 		m_register_ram[offset + (m_PS0 & 0xF8)] = data;
83 	}
84 
85 	m_program->write_byte( offset, data );
86 
87 	switch (offset)
88 	{
89 		case 0x10: m_IE0 = data; break;
90 		case 0x11: m_IE1 = data; break;
91 		case 0x12: m_IR0 = data; break;
92 		case 0x13: m_IR1 = data; break;
93 		case 0x19: m_SYS = data; break;
94 		case 0x1a: m_CKC = data; break;
95 		case 0x1c:
96 		case 0x1d: get_sp(); break;
97 		case 0x1e: m_PS0 = data;
98 				for (i = 0; i < 16; i++)    // refresh register contents in debugger
99 				{
100 					m_program->write_byte(i, mem_readbyte(i));
101 				}
102 				break;
103 		case 0x1f: m_PS1 = data; break;
104 	}
105 }
106 
107 
device_start()108 void sm8500_cpu_device::device_start()
109 {
110 	m_program = &space(AS_PROGRAM);
111 
112 	m_dma_func.resolve_safe();
113 	m_timer_func.resolve_safe();
114 
115 	save_item(NAME(m_PC));
116 	save_item(NAME(m_IE0));
117 	save_item(NAME(m_IE1));
118 	save_item(NAME(m_IR0));
119 	save_item(NAME(m_IR1));
120 	save_item(NAME(m_SYS));
121 	save_item(NAME(m_CKC));
122 	save_item(NAME(m_clock_changed));
123 	save_item(NAME(m_SP));
124 	save_item(NAME(m_PS0));
125 	save_item(NAME(m_PS1));
126 	save_item(NAME(m_IFLAGS));
127 	save_item(NAME(m_CheckInterrupts));
128 	save_item(NAME(m_halted));
129 	save_item(NAME(m_oldpc));
130 	save_pointer(NAME(m_register_ram),0x108);
131 
132 	// Register state for debugger
133 	state_add(SM8500_PC, "PC", m_PC ).callimport().callexport().formatstr("%04X");
134 	state_add(SM8500_SP, "SP", m_SP ).callimport().callexport().formatstr("%04X");
135 	state_add(SM8500_PS, "PS", m_PS0 ).callimport().callexport().formatstr("%04s");
136 	state_add(SM8500_SYS, "SYS", m_SYS ).callimport().callexport().formatstr("%04X");
137 	state_add(SM8500_RR0, "RR0", m_PC ).callimport().callexport().formatstr("%04s");
138 	state_add(SM8500_RR2, "RR2", m_PC ).callimport().callexport().formatstr("%04s");
139 	state_add(SM8500_RR4, "RR4", m_PC ).callimport().callexport().formatstr("%04s");
140 	state_add(SM8500_RR6, "RR6", m_PC ).callimport().callexport().formatstr("%04s");
141 	state_add(SM8500_RR8, "RR8", m_PC ).callimport().callexport().formatstr("%04s");
142 	state_add(SM8500_RR10, "RR10", m_PC ).callimport().callexport().formatstr("%04s");
143 	state_add(SM8500_RR12, "RR12", m_PC ).callimport().callexport().formatstr("%04s");
144 	state_add(SM8500_RR14, "RR14", m_PC ).callimport().callexport().formatstr("%04s");
145 	state_add(STATE_GENPC, "GENPC", m_PC).formatstr("%8s").noshow();
146 	state_add(STATE_GENPCBASE, "CURPC", m_PC).formatstr("%8s").noshow();
147 	state_add(STATE_GENFLAGS, "GENFLAGS", m_PS1).formatstr("%8s").noshow();
148 
149 	set_icountptr(m_icount);
150 }
151 
152 
state_string_export(const device_state_entry & entry,std::string & str) const153 void sm8500_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
154 {
155 	switch (entry.index())
156 	{
157 		case SM8500_PS:
158 			str = string_format("%04X", ( m_PS0 << 8 ) | m_PS1 );
159 			break;
160 
161 		case SM8500_RR0:
162 			str = string_format("%04X", mem_readword( 0x00 ) );
163 			break;
164 
165 		case SM8500_RR2:
166 			str = string_format("%04X", mem_readword( 0x02 ) );
167 			break;
168 
169 		case SM8500_RR4:
170 			str = string_format("%04X", mem_readword( 0x04 ) );
171 			break;
172 
173 		case SM8500_RR6:
174 			str = string_format("%04X", mem_readword( 0x06 ) );
175 			break;
176 
177 		case SM8500_RR8:
178 			str = string_format("%04X", mem_readword( 0x08 ) );
179 			break;
180 
181 		case SM8500_RR10:
182 			str = string_format("%04X", mem_readword( 0x0a ) );
183 			break;
184 
185 		case SM8500_RR12:
186 			str = string_format("%04X", mem_readword( 0x0c ) );
187 			break;
188 
189 		case SM8500_RR14:
190 			str = string_format("%04X", mem_readword( 0x0e ) );
191 			break;
192 
193 		case STATE_GENFLAGS:
194 			str = string_format("%c%c%c%c%c%c%c%c",
195 				m_PS1 & FLAG_C ? 'C' : '.',
196 				m_PS1 & FLAG_Z ? 'Z' : '.',
197 				m_PS1 & FLAG_S ? 'S' : '.',
198 				m_PS1 & FLAG_V ? 'V' : '.',
199 				m_PS1 & FLAG_D ? 'D' : '.',
200 				m_PS1 & FLAG_H ? 'H' : '.',
201 				m_PS1 & FLAG_B ? 'B' : '.',
202 				m_PS1 & FLAG_I ? 'I' : '.' );
203 			break;
204 	}
205 }
206 
207 
device_reset()208 void sm8500_cpu_device::device_reset()
209 {
210 	for (auto & elem : m_register_ram)
211 	{
212 		elem = 0;
213 	}
214 
215 	m_PC = 0x1020;
216 	m_clock_changed = 0;
217 	m_CheckInterrupts = 0;
218 	m_halted = 0;
219 	m_IFLAGS = 0;
220 	mem_writeword(0x10, 0);                 // IE0, IE1
221 	mem_writeword(0x12, 0);                 // IR0, IR1
222 	mem_writeword(0x14, 0xffff);            // P0, P1
223 	mem_writeword(0x16, 0xff00);            // P2, P3
224 	mem_writebyte(0x19, 0);                 // SYS
225 	mem_writebyte(0x1a, 0);                 // CKC
226 	mem_writebyte(0x1f, 0);                 // PS1
227 	mem_writebyte(0x2b, 0xff);              // URTT
228 	mem_writebyte(0x2d, 0x42);              // URTS
229 	mem_writebyte(0x5f, 0x38);              // WDTC
230 }
231 
232 
233 #define PUSH_BYTE(X)    m_SP--; \
234 			if ( ( m_SYS & 0x40 ) == 0 ) m_SP &= 0xFF; \
235 			mem_writebyte( m_SP, X );
236 
237 
take_interrupt(uint16_t vector)238 void sm8500_cpu_device::take_interrupt(uint16_t vector)
239 {
240 	/* Get regs from ram */
241 	get_sp();
242 	m_SYS = m_program->read_byte(0x19);
243 	m_PS1 = m_program->read_byte(0x1f);
244 	/* Push PC */
245 	PUSH_BYTE( m_PC & 0xFF );
246 	PUSH_BYTE( m_PC >> 8 );
247 	/* Push PS1 */
248 	PUSH_BYTE( m_PS1 );
249 	/* Clear I flag */
250 	m_PS1 &= ~ 0x01;
251 	/* save regs to ram */
252 	m_program->write_byte(0x1f, m_PS1);
253 	m_program->write_byte(0x1d, m_SP&0xFF);
254 	if (m_SYS&0x40) m_program->write_byte(0x1c, m_SP>>8);
255 	/* Change PC to address stored at "vector" */
256 	m_PC = mem_readword( vector );
257 }
258 
259 
process_interrupts()260 void sm8500_cpu_device::process_interrupts()
261 {
262 	if ( m_CheckInterrupts )
263 	{
264 		int irqline = 0;
265 		while( irqline < 11 )
266 		{
267 			if ( m_IFLAGS & ( 1 << irqline ) )
268 			{
269 				m_halted = 0;
270 				m_IE0 = m_program->read_byte(0x10);
271 				m_IE1 = m_program->read_byte(0x11);
272 				m_IR0 = m_program->read_byte(0x12);
273 				m_IR1 = m_program->read_byte(0x13);
274 				m_PS0 = m_program->read_byte(0x1e);
275 				m_PS1 = m_program->read_byte(0x1f);
276 				switch( irqline )
277 				{
278 				case WDT_INT:
279 					take_interrupt( 0x101C );
280 					break;
281 				case ILL_INT:
282 				case NMI_INT:
283 					take_interrupt( 0x101E );
284 					break;
285 				case DMA_INT:
286 					m_IR0 |= 0x80;
287 					if ( BIT( m_IE0, 7) && BIT( m_PS1, 0) )
288 					{
289 						take_interrupt( 0x1000 );
290 					}
291 					break;
292 				case TIM0_INT:
293 					m_IR0 |= 0x40;
294 					if ( BIT( m_IE0, 6) && BIT( m_PS1, 0) )
295 					{
296 						take_interrupt( 0x1002 );
297 					}
298 					break;
299 				case EXT_INT:
300 					m_IR0 |= 0x10;
301 					if ( BIT( m_IE0, 4) && ( ( m_PS0 & 0x07 ) < 7 ) && BIT( m_PS1, 0) )
302 					{
303 						take_interrupt( 0x1006 );
304 					}
305 					break;
306 				case UART_INT:
307 					m_IR0 |= 0x08;
308 					if ( BIT( m_IE0, 3) && ( ( m_PS0 & 0x07 ) < 6 ) && BIT( m_PS1, 0) )
309 					{
310 						take_interrupt( 0x1008 );
311 					}
312 					break;
313 				case LCDC_INT:
314 					m_IR0 |= 0x01;
315 					if ( BIT( m_IE0, 0) && ( ( m_PS0 & 0x07 ) < 5 ) && BIT( m_PS1, 0) )
316 					{
317 						take_interrupt( 0x100E );
318 					}
319 					break;
320 				case TIM1_INT:
321 					m_IR1 |= 0x40;
322 					if ( BIT( m_IE1, 6) && ( ( m_PS0 & 0x07 ) < 4 ) && BIT( m_PS1, 0) )
323 					{
324 						take_interrupt( 0x1012 );
325 					}
326 					break;
327 				case CK_INT:
328 					m_IR1 |= 0x10;
329 					if ( BIT( m_IE1, 4) && ( ( m_PS0 & 0x07 ) < 3 ) && BIT( m_PS1, 0) )
330 					{
331 						take_interrupt( 0x1016 );
332 					}
333 					break;
334 				case PIO_INT:
335 					m_IR1 |= 0x04;
336 					if ( BIT( m_IE1, 2) && ( ( m_PS0 & 0x07 ) < 2 ) && BIT( m_PS1, 0) )
337 					{
338 						take_interrupt( 0x101A );
339 					}
340 					break;
341 				}
342 				m_IFLAGS &= ~ ( 1 << irqline );
343 				m_program->write_byte(0x12, m_IR0);
344 				m_program->write_byte(0x13, m_IR1);
345 			}
346 			irqline++;
347 		}
348 	}
349 }
350 
351 
create_disassembler()352 std::unique_ptr<util::disasm_interface> sm8500_cpu_device::create_disassembler()
353 {
354 	return std::make_unique<sm8500_disassembler>();
355 }
356 
357 
execute_run()358 void sm8500_cpu_device::execute_run()
359 {
360 	do
361 	{
362 		int     mycycles = 0;
363 		uint8_t   r1,r2;
364 		uint16_t  s1,s2;
365 		uint32_t  d1,d2;
366 		uint32_t  res;
367 
368 		debugger_instruction_hook(m_PC);
369 		m_oldpc = m_PC;
370 		process_interrupts();
371 		if ( !m_halted ) {
372 			uint8_t op = mem_readbyte( m_PC++ );
373 			m_SYS = m_program->read_byte(0x19);
374 			m_PS0 = m_program->read_byte(0x1e);
375 			m_PS1 = m_program->read_byte(0x1f);
376 			get_sp();
377 			switch( op )
378 			{
379 #include "sm85ops.h"
380 			}
381 			if (m_SYS&0x40) m_program->write_byte(0x1c,m_SP>>8);
382 			m_program->write_byte(0x1d,m_SP&0xFF);
383 			mem_writebyte(0x1e,m_PS0); // need to update debugger
384 			m_program->write_byte(0x1f,m_PS1);
385 		} else {
386 			mycycles = 4;
387 			m_dma_func( mycycles );
388 		}
389 		m_timer_func( mycycles );
390 		m_icount -= mycycles;
391 	} while ( m_icount > 0 );
392 }
393 
394 
execute_set_input(int inptnum,int state)395 void sm8500_cpu_device::execute_set_input( int inptnum, int state )
396 {
397 	m_IR0 = m_program->read_byte(0x12);
398 	m_IR1 = m_program->read_byte(0x13);
399 	if ( state == ASSERT_LINE )
400 	{
401 		m_IFLAGS |= ( 0x01 << inptnum );
402 		m_CheckInterrupts = 1;
403 		switch( inptnum )
404 		{
405 			case DMA_INT:   m_IR0 |= 0x80; break;
406 			case TIM0_INT:  m_IR0 |= 0x40; break;
407 			case EXT_INT:   m_IR0 |= 0x10; break;
408 			case UART_INT:  m_IR0 |= 0x08; break;
409 			case LCDC_INT:  m_IR0 |= 0x01; break;
410 			case TIM1_INT:  m_IR1 |= 0x40; break;
411 			case CK_INT:    m_IR1 |= 0x10; break;
412 			case PIO_INT:   m_IR1 |= 0x04; break;
413 		}
414 	}
415 	else
416 	{
417 		m_IFLAGS &= ~( 0x01 << inptnum );
418 		switch( inptnum )
419 		{
420 			case DMA_INT:   m_IR0 &= ~0x80; break;
421 			case TIM0_INT:  m_IR0 &= ~0x40; break;
422 			case EXT_INT:   m_IR0 &= ~0x10; break;
423 			case UART_INT:  m_IR0 &= ~0x08; break;
424 			case LCDC_INT:  m_IR0 &= ~0x01; break;
425 			case TIM1_INT:  m_IR1 &= ~0x40; break;
426 			case CK_INT:    m_IR1 &= ~0x10; break;
427 			case PIO_INT:   m_IR1 &= ~0x04; break;
428 		}
429 		if ( 0 == m_IFLAGS )
430 		{
431 			m_CheckInterrupts = 0;
432 		}
433 	}
434 	m_program->write_byte(0x12, m_IR0);
435 	m_program->write_byte(0x13, m_IR1);
436 }
437