1 // license:BSD-3-Clause
2 // copyright-holders:Peter Trauner,Antoine Mine
3 /*****************************************************************************
4  *
5  *   saturn.c
6  *   portable saturn emulator interface
7  *   (hp calculators)
8  *
9  *
10  *****************************************************************************/
11 
12 #include "emu.h"
13 #include "saturn.h"
14 
15 #include "debugger.h"
16 
17 
18 //#define VERBOSE 1
19 #include "logmacro.h"
20 
21 
22 #define R0 0
23 #define R1 1
24 #define R2 2
25 #define R3 3
26 #define R4 4
27 #define A 5
28 #define B 6
29 #define C 7
30 #define D 8
31 #define I 9 // invalid
32 
33 
34 // Hardware status bits
35 #define XM 1 // external Modules missing
36 #define SB 2 // Sticky bit
37 #define SR 4 // Service Request
38 #define MP 8 // Module Pulled
39 
40 
41 
42 DEFINE_DEVICE_TYPE(SATURN, saturn_device, "saturn_cpu", "Hewlett-Packard Saturn")
43 
44 
saturn_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)45 saturn_device::saturn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
46 	: cpu_device(mconfig, SATURN, tag, owner, clock)
47 	, m_program_config("program", ENDIANNESS_LITTLE, 8, 20, 0)
48 	, m_out_func(*this)
49 	, m_in_func(*this)
50 	, m_reset_func(*this)
51 	, m_config_func(*this)
52 	, m_unconfig_func(*this)
53 	, m_id_func(*this)
54 	, m_crc_func(*this)
55 	, m_rsi_func(*this)
56 	, m_pc(0), m_oldpc(0), m_p(0), m_out(0), m_carry(0), m_decimal(0), m_st(0), m_hst(0)
57 	, m_nmi_state(0), m_irq_state(0), m_irq_enable(0), m_in_irq(0), m_pending_irq(0)
58 	, m_sleeping(0), m_monitor_id(0), m_monitor_in(0)
59 	, m_icount(0)
60 	, m_debugger_temp(0)
61 {
62 }
63 
memory_space_config() const64 device_memory_interface::space_config_vector saturn_device::memory_space_config() const
65 {
66 	return space_config_vector{ std::make_pair(AS_PROGRAM, &m_program_config) };
67 }
68 
get_nonstandard_mnemonics_mode() const69 bool saturn_device::get_nonstandard_mnemonics_mode() const
70 {
71 	// Needs to become configurable live
72 	return false;
73 }
74 
75 
create_disassembler()76 std::unique_ptr<util::disasm_interface> saturn_device::create_disassembler()
77 {
78 	return std::make_unique<saturn_disassembler>(this);
79 }
80 
81 
82 /***************************************************************
83  * include the opcode macros, functions and tables
84  ***************************************************************/
85 
86 #include "satops.ipp"
87 #include "sattable.ipp"
88 
89 /*****************************************************************************
90  *
91  *      Saturn CPU interface functions
92  *
93  *****************************************************************************/
94 
device_start()95 void saturn_device::device_start()
96 {
97 	space(AS_PROGRAM).cache(m_cache);
98 	space(AS_PROGRAM).specific(m_program);
99 
100 	m_out_func.resolve_safe();
101 	m_in_func.resolve_safe(0);
102 	m_reset_func.resolve_safe();
103 	m_config_func.resolve_safe();
104 	m_unconfig_func.resolve_safe();
105 	m_id_func.resolve_safe(0);
106 	m_crc_func.resolve_safe();
107 	m_rsi_func.resolve_safe();
108 
109 	memset(m_reg, 0, sizeof(m_reg));
110 	memset(m_d, 0, sizeof(m_d));
111 	m_pc = 0;
112 	m_oldpc = 0;
113 	memset(m_rstk, 0, sizeof(m_rstk));
114 	m_out = 0;
115 	m_carry = 0;
116 	m_decimal = 0;
117 	m_st = 0;
118 	m_hst = 0;
119 	m_nmi_state = 0;
120 	m_irq_state = 0;
121 	m_irq_enable = 0;
122 	m_in_irq = 0;
123 	m_pending_irq = 0;
124 	m_sleeping = 0;
125 	m_p = 0;
126 
127 	save_item(NAME(m_reg[R0]));
128 	save_item(NAME(m_reg[R1]));
129 	save_item(NAME(m_reg[R2]));
130 	save_item(NAME(m_reg[R3]));
131 	save_item(NAME(m_reg[R4]));
132 	save_item(NAME(m_reg[A]));
133 	save_item(NAME(m_reg[B]));
134 	save_item(NAME(m_reg[C]));
135 	save_item(NAME(m_reg[D]));
136 	save_item(NAME(m_d));
137 	save_item(NAME(m_pc));
138 	save_item(NAME(m_oldpc));
139 	save_item(NAME(m_rstk));
140 	save_item(NAME(m_out));
141 	save_item(NAME(m_carry));
142 	save_item(NAME(m_st));
143 	save_item(NAME(m_hst));
144 	save_item(NAME(m_nmi_state));
145 	save_item(NAME(m_irq_state));
146 	save_item(NAME(m_irq_enable));
147 	save_item(NAME(m_in_irq));
148 	save_item(NAME(m_pending_irq));
149 	save_item(NAME(m_sleeping));
150 
151 	// TODO: Register state
152 	state_add( SATURN_PC, "PC", m_pc ).formatstr("%5X");
153 	state_add( SATURN_D0, "D0", m_d[0] ).formatstr("%5X");
154 	state_add( SATURN_D1, "D1", m_d[1] ).formatstr("%5X");
155 	state_add( SATURN_A, "A", m_debugger_temp ).formatstr("%19s");
156 	state_add( SATURN_B, "B", m_debugger_temp ).formatstr("%19s");
157 	state_add( SATURN_C, "C", m_debugger_temp ).formatstr("%19s");
158 	state_add( SATURN_D, "D", m_debugger_temp ).formatstr("%19s");
159 	state_add( SATURN_R0, "R0", m_debugger_temp ).formatstr("%19s");
160 	state_add( SATURN_R1, "R1", m_debugger_temp ).formatstr("%19s");
161 	state_add( SATURN_R2, "R2", m_debugger_temp ).formatstr("%19s");
162 	state_add( SATURN_R3, "R3", m_debugger_temp ).formatstr("%19s");
163 	state_add( SATURN_R4, "R4", m_debugger_temp ).formatstr("%19s");
164 	state_add( SATURN_P, "P", m_p).formatstr("%1X");
165 	state_add( SATURN_OUT, "OUTP", m_out).formatstr("%3X");
166 	state_add( SATURN_CARRY, "Carry", m_carry).formatstr("%1X");
167 	state_add( SATURN_ST, "ST", m_st).formatstr("%4X");
168 	state_add( SATURN_HST, "HST", m_hst).formatstr("%1X");
169 	state_add( SATURN_RSTK0, "RSTK0", m_rstk[0]).formatstr("%5X");
170 	state_add( SATURN_RSTK1, "RSTK1", m_rstk[1]).formatstr("%5X");
171 	state_add( SATURN_RSTK2, "RSTK2", m_rstk[2]).formatstr("%5X");
172 	state_add( SATURN_RSTK3, "RSTK3", m_rstk[3]).formatstr("%5X");
173 	state_add( SATURN_RSTK4, "RSTK4", m_rstk[4]).formatstr("%5X");
174 	state_add( SATURN_RSTK5, "RSTK5", m_rstk[5]).formatstr("%5X");
175 	state_add( SATURN_RSTK6, "RSTK6", m_rstk[6]).formatstr("%5X");
176 	state_add( SATURN_RSTK7, "RSTK7", m_rstk[7]).formatstr("%5X");
177 	state_add( SATURN_IRQ_STATE, "IRQ", m_debugger_temp).formatstr("%4s");
178 	state_add( SATURN_SLEEPING, "sleep", m_sleeping).formatstr("%1X");
179 
180 	state_add( STATE_GENPC, "GENPC", m_pc ).noshow();
181 	state_add( STATE_GENPCBASE, "CURPC", m_pc ).noshow();
182 	state_add( STATE_GENFLAGS, "GENFLAGS", m_debugger_temp).formatstr("%2s").noshow();
183 
184 	set_icountptr(m_icount);
185 }
186 
state_string_export(const device_state_entry & entry,std::string & str) const187 void saturn_device::state_string_export(const device_state_entry &entry, std::string &str) const
188 {
189 #define Reg64Data(s) s[15],s[14],s[13],s[12],s[11],s[10],s[9],s[8],s[7],s[6],s[5],s[4],s[3],s[2],s[1],s[0]
190 #define Reg64Format "%x %x%x%x%x%x%x%x %x%x%x %x%x%x%x%x"
191 
192 	switch (entry.index())
193 	{
194 		case SATURN_A:
195 			str = string_format( Reg64Format, Reg64Data(m_reg[A]) );
196 			break;
197 
198 		case SATURN_B:
199 			str = string_format( Reg64Format, Reg64Data(m_reg[B]) );
200 			break;
201 
202 		case SATURN_C:
203 			str = string_format( Reg64Format, Reg64Data(m_reg[C]) );
204 			break;
205 
206 		case SATURN_D:
207 			str = string_format( Reg64Format, Reg64Data(m_reg[D]) );
208 			break;
209 
210 		case SATURN_R0:
211 			str = string_format( Reg64Format, Reg64Data(m_reg[R0]) );
212 			break;
213 
214 		case SATURN_R1:
215 			str = string_format( Reg64Format, Reg64Data(m_reg[R1]) );
216 			break;
217 
218 		case SATURN_R2:
219 			str = string_format( Reg64Format, Reg64Data(m_reg[R2]) );
220 			break;
221 
222 		case SATURN_R3:
223 			str = string_format( Reg64Format, Reg64Data(m_reg[R3]) );
224 			break;
225 
226 		case SATURN_R4:
227 			str = string_format( Reg64Format, Reg64Data(m_reg[R4]) );
228 			break;
229 
230 		case SATURN_IRQ_STATE:
231 			str = string_format( "%c%c%c%i", m_in_irq?'S':'.', m_irq_enable?'e':'.', m_pending_irq?'p':'.', m_irq_state );
232 			break;
233 
234 		case STATE_GENFLAGS:
235 			str = string_format( "%c%c", m_decimal?'D':'.', m_carry ? 'C':'.' );
236 			break;
237 	}
238 }
239 
240 
state_import(const device_state_entry & entry)241 void saturn_device::state_import(const device_state_entry &entry)
242 {
243 	switch (entry.index())
244 	{
245 		case SATURN_A:
246 			IntReg64(m_reg[A], m_debugger_temp);
247 			break;
248 
249 		case SATURN_B:
250 			IntReg64(m_reg[B], m_debugger_temp);
251 			break;
252 
253 		case SATURN_C:
254 			IntReg64(m_reg[C], m_debugger_temp);
255 			break;
256 
257 		case SATURN_D:
258 			IntReg64(m_reg[D], m_debugger_temp);
259 			break;
260 
261 		case SATURN_R0:
262 			IntReg64(m_reg[R0], m_debugger_temp);
263 			break;
264 
265 		case SATURN_R1:
266 			IntReg64(m_reg[R1], m_debugger_temp);
267 			break;
268 
269 		case SATURN_R2:
270 			IntReg64(m_reg[R2], m_debugger_temp);
271 			break;
272 
273 		case SATURN_R3:
274 			IntReg64(m_reg[R3], m_debugger_temp);
275 			break;
276 
277 		case SATURN_R4:
278 			IntReg64(m_reg[R4], m_debugger_temp);
279 			break;
280 	}
281 }
282 
state_export(const device_state_entry & entry)283 void saturn_device::state_export(const device_state_entry &entry)
284 {
285 	switch (entry.index())
286 	{
287 		case SATURN_A:
288 			m_debugger_temp = Reg64Int(m_reg[A]);
289 			break;
290 
291 		case SATURN_B:
292 			m_debugger_temp = Reg64Int(m_reg[B]);
293 			break;
294 
295 		case SATURN_C:
296 			m_debugger_temp = Reg64Int(m_reg[C]);
297 			break;
298 
299 		case SATURN_D:
300 			m_debugger_temp = Reg64Int(m_reg[D]);
301 			break;
302 
303 		case SATURN_R0:
304 			m_debugger_temp = Reg64Int(m_reg[R0]);
305 			break;
306 
307 		case SATURN_R1:
308 			m_debugger_temp = Reg64Int(m_reg[R1]);
309 			break;
310 
311 		case SATURN_R2:
312 			m_debugger_temp = Reg64Int(m_reg[R2]);
313 			break;
314 
315 		case SATURN_R3:
316 			m_debugger_temp = Reg64Int(m_reg[R3]);
317 			break;
318 
319 		case SATURN_R4:
320 			m_debugger_temp = Reg64Int(m_reg[R4]);
321 			break;
322 	}
323 }
324 
325 
device_reset()326 void saturn_device::device_reset()
327 {
328 	m_pc=0;
329 	m_sleeping = 0;
330 	m_irq_enable = 0;
331 	m_in_irq = 0;
332 }
333 
334 
saturn_take_irq()335 void saturn_device::saturn_take_irq()
336 {
337 	m_in_irq = 1;       /* reset by software, using RTI */
338 	m_pending_irq = 0;
339 	m_icount -= 7;
340 	saturn_push(m_pc);
341 	m_pc=IRQ_ADDRESS;
342 
343 	LOG("SATURN takes IRQ ($%04x)\n", m_pc);
344 
345 	standard_irq_callback(SATURN_IRQ_LINE);
346 }
347 
execute_run()348 void saturn_device::execute_run()
349 {
350 	do
351 	{
352 		m_oldpc = m_pc;
353 
354 		debugger_instruction_hook(m_pc);
355 
356 		if ( m_sleeping )
357 		{
358 			/* advance time when sleeping */
359 			m_icount -= 100;
360 		}
361 		else
362 		{
363 			/* takes irq */
364 			if ( m_pending_irq && (!m_in_irq) )
365 				saturn_take_irq();
366 
367 			/* execute one instruction */
368 			saturn_instruction();
369 		}
370 
371 	} while (m_icount > 0);
372 }
373 
374 
execute_set_input(int inputnum,int state)375 void saturn_device::execute_set_input(int inputnum, int state)
376 {
377 	switch (inputnum)
378 	{
379 		case SATURN_NMI_LINE:
380 			if ( state == m_nmi_state ) return;
381 			m_nmi_state = state;
382 			if ( state != CLEAR_LINE )
383 			{
384 				LOG("SATURN set_nmi_line(ASSERT)\n");
385 				m_pending_irq = 1;
386 			}
387 			break;
388 
389 		case SATURN_IRQ_LINE:
390 			if ( state == m_irq_state ) return;
391 			m_irq_state = state;
392 			if ( state != CLEAR_LINE && m_irq_enable )
393 			{
394 				LOG("SATURN set_irq_line(ASSERT)\n");
395 				m_pending_irq = 1;
396 			}
397 			break;
398 
399 		case SATURN_WAKEUP_LINE:
400 			if (m_sleeping && state==1)
401 			{
402 				LOG("SATURN set_wakeup_line(ASSERT)\n");
403 				standard_irq_callback(SATURN_WAKEUP_LINE);
404 				m_sleeping = 0;
405 			}
406 			break;
407 	}
408 }
409 
410 
IntReg64(Saturn64 r,int64_t d)411 void saturn_device::IntReg64(Saturn64 r, int64_t d)
412 {
413 	int i;
414 	for (i=0; i<16; i++)
415 		r[i] = (d >> (4*i)) & 0xf;
416 }
417 
418 
Reg64Int(Saturn64 r)419 int64_t saturn_device::Reg64Int(Saturn64 r)
420 {
421 	int64_t x = 0;
422 	int i;
423 	for (i=0; i<16; i++)
424 		x |= (int64_t) r[i] << (4*i);
425 	return x;
426 }
427