1 // license:BSD-3-Clause
2 // copyright-holders:F. Ulivi
3 // ********************************************************************************
4 // * HP "hybrid" processor disassembler
5 // ********************************************************************************
6 
7 #include "emu.h"
8 #include "hphybrid_dasm.h"
9 #include "hphybrid_defs.h"
10 
addr_2_str(std::ostream & stream,uint16_t addr,bool indirect)11 void hp_hybrid_disassembler::addr_2_str(std::ostream &stream, uint16_t addr , bool indirect)
12 {
13 	if (m_flags & HP_HYBRID_DASM_HAS_15BITS) {
14 		addr &= 0x7fff;
15 	}
16 
17 	util::stream_format(stream, "$%04x" , addr);
18 
19 	// Common registers
20 	switch (addr) {
21 	case HP_REG_A_ADDR:
22 		stream << "(A)";
23 		break;
24 
25 	case HP_REG_B_ADDR:
26 		stream << "(B)";
27 		break;
28 
29 	case HP_REG_P_ADDR:
30 		stream << "(P)";
31 		break;
32 
33 	case HP_REG_R_ADDR:
34 		stream << "(R)";
35 		break;
36 
37 	case HP_REG_R4_ADDR:
38 		stream << "(R4)";
39 		break;
40 
41 	case HP_REG_R5_ADDR:
42 		stream << "(R5)";
43 		break;
44 
45 	case HP_REG_R6_ADDR:
46 		stream << "(R6)";
47 		break;
48 
49 	case HP_REG_R7_ADDR:
50 		stream << "(R7)";
51 		break;
52 
53 	case HP_REG_IV_ADDR:
54 		stream << "(IV)";
55 		break;
56 
57 	case HP_REG_PA_ADDR:
58 		stream << "(PA)";
59 		break;
60 
61 	case HP_REG_DMAPA_ADDR:
62 		stream << "(DMAPA)";
63 		break;
64 
65 	case HP_REG_DMAMA_ADDR:
66 		stream << "(DMAMA)";
67 		break;
68 
69 	case HP_REG_DMAC_ADDR:
70 		stream << "(DMAC)";
71 		break;
72 
73 	case HP_REG_C_ADDR:
74 		stream << "(C)";
75 		break;
76 
77 	case HP_REG_D_ADDR:
78 		stream << "(D)";
79 		break;
80 
81 	default:
82 		// EMC registers
83 		if (m_flags & HP_HYBRID_DASM_HAS_EMC) {
84 			if (m_flags & HP_HYBRID_DASM_HAS_15BITS) {
85 				switch (addr) {
86 				case HP_REG_AR1_ADDR & 0x7fff:
87 					stream << "(Ar1)";
88 					break;
89 
90 				case (HP_REG_AR1_ADDR & 0x7fff) + 1:
91 					stream << "(Ar1_2)";
92 					break;
93 
94 				case (HP_REG_AR1_ADDR & 0x7fff) + 2:
95 					stream << "(Ar1_3)";
96 					break;
97 
98 				case (HP_REG_AR1_ADDR & 0x7fff) + 3:
99 					stream << "(Ar1_4)";
100 					break;
101 				}
102 			} else {
103 				switch (addr) {
104 				case HP_REG_AR1_ADDR:
105 					stream << "(Ar1)";
106 					break;
107 
108 				case HP_REG_AR1_ADDR + 1:
109 					stream << "(Ar1_2)";
110 					break;
111 
112 				case HP_REG_AR1_ADDR + 2:
113 					stream << "(Ar1_3)";
114 					break;
115 
116 				case HP_REG_AR1_ADDR + 3:
117 					stream << "(Ar1_4)";
118 					break;
119 				}
120 			}
121 			switch (addr) {
122 			case HP_REG_AR2_ADDR:
123 				stream << "(Ar2)";
124 				break;
125 
126 			case HP_REG_AR2_ADDR + 1:
127 				stream << "(Ar2_2)";
128 				break;
129 
130 			case HP_REG_AR2_ADDR + 2:
131 				stream << "(Ar2_3)";
132 				break;
133 
134 			case HP_REG_AR2_ADDR + 3:
135 				stream << "(Ar2_4)";
136 				break;
137 
138 			case HP_REG_SE_ADDR:
139 				stream << "(SE)";
140 				break;
141 
142 			case HP_REG_R25_ADDR:
143 				stream << "(R25)";
144 				break;
145 
146 			case HP_REG_R26_ADDR:
147 				stream << "(R26)";
148 				break;
149 
150 			case HP_REG_R27_ADDR:
151 				stream << "(R27)";
152 				break;
153 
154 			default:
155 				break;
156 			}
157 		}
158 		// AEC registers
159 		if (m_flags & HP_HYBRID_DASM_HAS_AEC) {
160 			switch (addr) {
161 			case HP_REG_R32_ADDR:
162 				stream << "(R32)";
163 				break;
164 
165 			case HP_REG_R33_ADDR:
166 				stream << "(R33)";
167 				break;
168 
169 			case HP_REG_R34_ADDR:
170 				stream << "(R34)";
171 				break;
172 
173 			case HP_REG_R35_ADDR:
174 				stream << "(R35)";
175 				break;
176 
177 			case HP_REG_R36_ADDR:
178 				stream << "(R36)";
179 				break;
180 
181 			case HP_REG_R37_ADDR:
182 				stream << "(R37)";
183 				break;
184 
185 			default:
186 				break;
187 			}
188 		}
189 		break;
190 	}
191 
192 	if (indirect) {
193 		stream << ",I";
194 	}
195 }
196 
param_none(std::ostream & stream,offs_t pc,uint16_t opcode)197 void hp_hybrid_disassembler::param_none(std::ostream &stream, offs_t pc , uint16_t opcode)
198 {
199 }
200 
param_loc(std::ostream & stream,offs_t pc,uint16_t opcode)201 void hp_hybrid_disassembler::param_loc(std::ostream &stream, offs_t pc , uint16_t opcode)
202 {
203 	uint16_t base;
204 	uint16_t off;
205 
206 	if (opcode & 0x0400) {
207 		if (m_flags & HP_HYBRID_DASM_ABS_MODE) {
208 			// Current page, absolute mode
209 			base = (pc & 0xfc00) | 0x200;
210 		} else {
211 			// Current page, relative mode
212 			base = pc;
213 		}
214 	} else {
215 		// Base page
216 		base = 0;
217 	}
218 
219 	off = opcode & 0x3ff;
220 	if (off & 0x200) {
221 		off -= 0x400;
222 	}
223 
224 	addr_2_str(stream, base + off , (opcode & 0x8000) != 0);
225 }
226 
param_addr32(std::ostream & stream,offs_t pc,uint16_t opcode)227 void hp_hybrid_disassembler::param_addr32(std::ostream &stream, offs_t pc , uint16_t opcode)
228 {
229 	addr_2_str(stream, opcode & 0x1f , (opcode & 0x8000) != 0);
230 }
231 
param_skip(std::ostream & stream,offs_t pc,uint16_t opcode)232 void hp_hybrid_disassembler::param_skip(std::ostream &stream, offs_t pc , uint16_t opcode)
233 {
234 	uint16_t off = opcode & 0x3f;
235 	if (off & 0x20) {
236 		off -= 0x40;
237 	}
238 	addr_2_str(stream , pc + off , false);
239 }
240 
param_skip_sc(std::ostream & stream,offs_t pc,uint16_t opcode)241 void hp_hybrid_disassembler::param_skip_sc(std::ostream &stream, offs_t pc , uint16_t opcode)
242 {
243 	param_skip(stream, pc, opcode);
244 
245 	if (opcode & 0x80) {
246 		if (opcode & 0x40) {
247 			stream << ",S";
248 		} else {
249 			stream << ",C";
250 		}
251 	}
252 }
253 
param_ret(std::ostream & stream,offs_t pc,uint16_t opcode)254 void hp_hybrid_disassembler::param_ret(std::ostream &stream, offs_t pc , uint16_t opcode)
255 {
256 	int off = opcode & 0x3f;
257 
258 	if (off & 0x20) {
259 		off -= 0x40;
260 	}
261 
262 	util::stream_format(stream , "%d" , off);
263 	if (opcode & 0x40) {
264 		stream << ",P";
265 	}
266 }
267 
param_n16(std::ostream & stream,offs_t pc,uint16_t opcode)268 void hp_hybrid_disassembler::param_n16(std::ostream &stream, offs_t pc , uint16_t opcode)
269 {
270 	util::stream_format(stream , "%u" , (opcode & 0xf) + 1);
271 }
272 
param_reg_id(std::ostream & stream,offs_t pc,uint16_t opcode)273 void hp_hybrid_disassembler::param_reg_id(std::ostream &stream, offs_t pc , uint16_t opcode)
274 {
275 	addr_2_str(stream, opcode & 7, false);
276 
277 	if (opcode & 0x80) {
278 		stream << ",D";
279 	} else {
280 		stream << ",I";
281 	}
282 }
283 
284 const hp_hybrid_disassembler::dis_entry_t hp_hybrid_disassembler::dis_table_common[] = {
285 	// *** BPC Instructions ***
286 	{0xffff , 0x0000 , "NOP" , &hp_hybrid_disassembler::param_none , 0 },
287 	{0x7800 , 0x0000 , "LDA" , &hp_hybrid_disassembler::param_loc , 0 },
288 	{0x7800 , 0x0800 , "LDB" , &hp_hybrid_disassembler::param_loc , 0 },
289 	{0x7800 , 0x1000 , "CPA" , &hp_hybrid_disassembler::param_loc , 0 },
290 	{0x7800 , 0x1800 , "CPB" , &hp_hybrid_disassembler::param_loc , 0 },
291 	{0x7800 , 0x2000 , "ADA" , &hp_hybrid_disassembler::param_loc , 0 },
292 	{0x7800 , 0x2800 , "ADB" , &hp_hybrid_disassembler::param_loc , 0 },
293 	{0x7800 , 0x3000 , "STA" , &hp_hybrid_disassembler::param_loc , 0 },
294 	{0x7800 , 0x3800 , "STB" , &hp_hybrid_disassembler::param_loc , 0 },
295 	{0x7800 , 0x4000 , "JSM" , &hp_hybrid_disassembler::param_loc , STEP_OVER },
296 	{0x7800 , 0x4800 , "ISZ" , &hp_hybrid_disassembler::param_loc , 0 },
297 	{0x7800 , 0x5000 , "AND" , &hp_hybrid_disassembler::param_loc , 0 },
298 	{0x7800 , 0x5800 , "DSZ" , &hp_hybrid_disassembler::param_loc , 0 },
299 	{0x7800 , 0x6000 , "IOR" , &hp_hybrid_disassembler::param_loc , 0 },
300 	{0x7800 , 0x6800 , "JMP" , &hp_hybrid_disassembler::param_loc , 0 },
301 	{0x7fe0 , 0x7000 , "EXE" , &hp_hybrid_disassembler::param_addr32 , 0 },
302 	{0xffc0 , 0x7400 , "RZA" , &hp_hybrid_disassembler::param_skip , 0 },
303 	{0xffc0 , 0x7C00 , "RZB" , &hp_hybrid_disassembler::param_skip , 0 },
304 	{0xffc0 , 0x7440 , "RIA" , &hp_hybrid_disassembler::param_skip , 0 },
305 	{0xffc0 , 0x7C40 , "RIB" , &hp_hybrid_disassembler::param_skip , 0 },
306 	{0xffc0 , 0x74c0 , "SDS" , &hp_hybrid_disassembler::param_skip , 0 },
307 	{0xffc0 , 0x7500 , "SZA" , &hp_hybrid_disassembler::param_skip , 0 },
308 	{0xffc0 , 0x7D00 , "SZB" , &hp_hybrid_disassembler::param_skip , 0 },
309 	{0xffc0 , 0x7540 , "SIA" , &hp_hybrid_disassembler::param_skip , 0 },
310 	{0xffc0 , 0x7D40 , "SIB" , &hp_hybrid_disassembler::param_skip , 0 },
311 	{0xffc0 , 0x7480 , "SFS" , &hp_hybrid_disassembler::param_skip , 0 },
312 	{0xffc0 , 0x7580 , "SFC" , &hp_hybrid_disassembler::param_skip , 0 },
313 	{0xffc0 , 0x75c0 , "SDC" , &hp_hybrid_disassembler::param_skip , 0 },
314 	{0xffc0 , 0x7c80 , "SSS" , &hp_hybrid_disassembler::param_skip , 0 },
315 	{0xffc0 , 0x7d80 , "SSC" , &hp_hybrid_disassembler::param_skip , 0 },
316 	{0xffc0 , 0x7cc0 , "SHS" , &hp_hybrid_disassembler::param_skip , 0 },
317 	{0xffc0 , 0x7dc0 , "SHC" , &hp_hybrid_disassembler::param_skip , 0 },
318 	{0xff00 , 0x7600 , "SLA" , &hp_hybrid_disassembler::param_skip_sc , 0 },
319 	{0xff00 , 0x7e00 , "SLB" , &hp_hybrid_disassembler::param_skip_sc , 0 },
320 	{0xff00 , 0x7700 , "RLA" , &hp_hybrid_disassembler::param_skip_sc , 0 },
321 	{0xff00 , 0x7f00 , "RLB" , &hp_hybrid_disassembler::param_skip_sc , 0 },
322 	{0xff00 , 0xf400 , "SAP" , &hp_hybrid_disassembler::param_skip_sc , 0 },
323 	{0xff00 , 0xfc00 , "SBP" , &hp_hybrid_disassembler::param_skip_sc , 0 },
324 	{0xff00 , 0xf500 , "SAM" , &hp_hybrid_disassembler::param_skip_sc , 0 },
325 	{0xff00 , 0xfd00 , "SBM" , &hp_hybrid_disassembler::param_skip_sc , 0 },
326 	{0xff00 , 0xf600 , "SOC" , &hp_hybrid_disassembler::param_skip_sc , 0 },
327 	{0xff00 , 0xf700 , "SOS" , &hp_hybrid_disassembler::param_skip_sc , 0 },
328 	{0xff00 , 0xfe00 , "SEC" , &hp_hybrid_disassembler::param_skip_sc , 0 },
329 	{0xff00 , 0xff00 , "SES" , &hp_hybrid_disassembler::param_skip_sc , 0 },
330 	{0xffff , 0xf020 , "TCA" , &hp_hybrid_disassembler::param_none , 0 },
331 	{0xffff , 0xf820 , "TCB" , &hp_hybrid_disassembler::param_none , 0 },
332 	{0xffff , 0xf060 , "CMA" , &hp_hybrid_disassembler::param_none , 0 },
333 	{0xffff , 0xf860 , "CMB" , &hp_hybrid_disassembler::param_none , 0 },
334 	{0xff80 , 0xf080 , "RET" , &hp_hybrid_disassembler::param_ret , STEP_OUT },
335 	{0xfff0 , 0xf100 , "AAR" , &hp_hybrid_disassembler::param_n16 , 0 },
336 	{0xfff0 , 0xf900 , "ABR" , &hp_hybrid_disassembler::param_n16 , 0 },
337 	{0xffff , 0xf14f , "CLA" , &hp_hybrid_disassembler::param_none , 0 },
338 	{0xfff0 , 0xf140 , "SAR" , &hp_hybrid_disassembler::param_n16 , 0 },
339 	{0xffff , 0xf94f , "CLB" , &hp_hybrid_disassembler::param_none , 0 },
340 	{0xfff0 , 0xf940 , "SBR" , &hp_hybrid_disassembler::param_n16 , 0 },
341 	{0xfff0 , 0xf180 , "SAL" , &hp_hybrid_disassembler::param_n16 , 0 },
342 	{0xfff0 , 0xf980 , "SBL" , &hp_hybrid_disassembler::param_n16 , 0 },
343 	{0xfff0 , 0xf1c0 , "RAR" , &hp_hybrid_disassembler::param_n16 , 0 },
344 	{0xfff0 , 0xf9c0 , "RBR" , &hp_hybrid_disassembler::param_n16 , 0 },
345 	// *** IOC Instructions ***
346 	{0xffff , 0x7110 , "EIR" , &hp_hybrid_disassembler::param_none , 0 },
347 	{0xffff , 0x7118 , "DIR" , &hp_hybrid_disassembler::param_none , 0 },
348 	{0xffff , 0x7120 , "DMA" , &hp_hybrid_disassembler::param_none , 0 },
349 	{0xffff , 0x7128 , "PCM" , &hp_hybrid_disassembler::param_none , 0 },
350 	{0xffff , 0x7138 , "DDR" , &hp_hybrid_disassembler::param_none , 0 },
351 	{0xff78 , 0x7160 , "PWC" , &hp_hybrid_disassembler::param_reg_id , 0 },
352 	{0xff78 , 0x7168 , "PWD" , &hp_hybrid_disassembler::param_reg_id , 0 },
353 	{0xff78 , 0x7960 , "PBC" , &hp_hybrid_disassembler::param_reg_id , 0 },
354 	{0xff78 , 0x7968 , "PBD" , &hp_hybrid_disassembler::param_reg_id , 0 },
355 	{0xff78 , 0x7170 , "WWC" , &hp_hybrid_disassembler::param_reg_id , 0 },
356 	{0xff78 , 0x7178 , "WWD" , &hp_hybrid_disassembler::param_reg_id , 0 },
357 	{0xff78 , 0x7970 , "WBC" , &hp_hybrid_disassembler::param_reg_id , 0 },
358 	{0xff78 , 0x7978 , "WBD" , &hp_hybrid_disassembler::param_reg_id , 0 },
359 	// *** END ***
360 	{0 , 0 , nullptr , nullptr , 0 }
361 };
362 const hp_hybrid_disassembler::dis_entry_t hp_hybrid_disassembler::dis_table_ioc16[] = {
363 	// *** IOC-16 instructions ***
364 	{0xffff , 0x7100 , "SDO" , &hp_hybrid_disassembler::param_none , 0 },
365 	{0xffff , 0x7108 , "SDI" , &hp_hybrid_disassembler::param_none , 0 },
366 	{0xffff , 0x7140 , "DBL" , &hp_hybrid_disassembler::param_none , 0 },
367 	{0xffff , 0x7148 , "CBL" , &hp_hybrid_disassembler::param_none , 0 },
368 	{0xffff , 0x7150 , "DBU" , &hp_hybrid_disassembler::param_none , 0 },
369 	{0xffff , 0x7158 , "CBU" , &hp_hybrid_disassembler::param_none , 0 },
370 	// *** END ***
371 	{0 , 0 , nullptr , nullptr , 0 }
372 };
373 const hp_hybrid_disassembler::dis_entry_t hp_hybrid_disassembler::dis_table_emc[] = {
374 	// *** EMC Instructions ***
375 	{0xffff , 0x7200 , "MWA" , &hp_5061_3001_disassembler::param_none , 0 },
376 	{0xffff , 0x7220 , "CMY" , &hp_5061_3001_disassembler::param_none , 0 },
377 	{0xffff , 0x7260 , "CMX" , &hp_5061_3001_disassembler::param_none , 0 },
378 	{0xffff , 0x7280 , "FXA" , &hp_5061_3001_disassembler::param_none , 0 },
379 	{0xfff0 , 0x7300 , "XFR" , &hp_5061_3001_disassembler::param_n16 , 0 },
380 	{0xffff , 0x7340 , "NRM" , &hp_5061_3001_disassembler::param_none , 0 },
381 	{0xfff0 , 0x7380 , "CLR" , &hp_5061_3001_disassembler::param_n16 , 0 },
382 	{0xffff , 0x73c0 , "CDC" , &hp_5061_3001_disassembler::param_none , 0 },
383 	{0xffff , 0x7a00 , "FMP" , &hp_5061_3001_disassembler::param_none , 0 },
384 	{0xffff , 0x7a21 , "FDV" , &hp_5061_3001_disassembler::param_none , 0 },
385 	{0xffff , 0x7b00 , "MRX" , &hp_5061_3001_disassembler::param_none , 0 },
386 	{0xffff , 0x7b21 , "DRS" , &hp_5061_3001_disassembler::param_none , 0 },
387 	{0xffff , 0x7b40 , "MRY" , &hp_5061_3001_disassembler::param_none , 0 },
388 	{0xffff , 0x7b61 , "MLY" , &hp_5061_3001_disassembler::param_none , 0 },
389 	{0xffff , 0x7b8f , "MPY" , &hp_5061_3001_disassembler::param_none , 0 },
390 	// *** END ***
391 	{0 , 0 , nullptr , nullptr , 0 }
392 };
393 const hp_hybrid_disassembler::dis_entry_t hp_hybrid_disassembler::dis_table_aec[] = {
394 	// *** Undocumented AEC instructions of 5061-3001 ***
395 	{0xffff , 0x7026 , "CIM" , &hp_5061_3001_disassembler::param_none , 0 },
396 	{0xffff , 0x7027 , "SIM" , &hp_5061_3001_disassembler::param_none , 0 },
397 	// *** END ***
398 	{0 , 0 , nullptr , nullptr , 0 }
399 };
400 
disassemble_table(uint16_t opcode,offs_t pc,const dis_entry_t * table,std::ostream & stream)401 offs_t hp_hybrid_disassembler::disassemble_table(uint16_t opcode , offs_t pc , const dis_entry_t *table , std::ostream &stream)
402 {
403 	const dis_entry_t *p;
404 
405 	for (p = table; p->m_op_mask; p++) {
406 		if ((opcode & p->m_op_mask) == p->m_opcode) {
407 			stream << p->m_mnemonic << " ";
408 			(this->*(p->m_param_fn))(stream , pc , opcode);
409 			return 1 | p->m_dasm_flags | SUPPORTED;
410 		}
411 	}
412 
413 	return 0;
414 }
415 
disassemble(std::ostream & stream,offs_t pc,const data_buffer & opcodes,const data_buffer & params)416 offs_t hp_hybrid_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
417 {
418 	uint16_t opcode = opcodes.r16(pc);
419 	offs_t res;
420 
421 	res = disassemble_table(opcode, pc, dis_table_common, stream);
422 
423 	if (res == 0 && (m_flags & HP_HYBRID_DASM_HAS_15BITS) == 0) {
424 		res = disassemble_table(opcode, pc, dis_table_ioc16, stream);
425 	}
426 	if (res == 0 && (m_flags & HP_HYBRID_DASM_HAS_EMC) != 0) {
427 		res = disassemble_table(opcode, pc, dis_table_emc, stream);
428 	}
429 	if (res == 0 && (m_flags & HP_HYBRID_DASM_HAS_AEC) != 0) {
430 		res = disassemble_table(opcode, pc, dis_table_aec, stream);
431 	}
432 
433 	if (res == 0) {
434 		// Unknown opcode
435 		stream << "???";
436 		res = 1 | SUPPORTED;
437 	}
438 
439 	return res;
440 }
441 
hp_hybrid_disassembler(unsigned flags)442 hp_hybrid_disassembler::hp_hybrid_disassembler(unsigned flags)
443 	: m_flags(flags)
444 {
445 }
446 
hp_5061_3011_disassembler(bool relative_mode)447 hp_5061_3011_disassembler::hp_5061_3011_disassembler(bool relative_mode)
448 	: hp_hybrid_disassembler(relative_mode ? 0 : HP_HYBRID_DASM_ABS_MODE)
449 {
450 }
451 
hp_5061_3001_disassembler(bool relative_mode)452 hp_5061_3001_disassembler::hp_5061_3001_disassembler(bool relative_mode)
453 	: hp_hybrid_disassembler(HP_HYBRID_DASM_HAS_EMC |
454 							 HP_HYBRID_DASM_HAS_AEC |
455 							 (relative_mode ? 0 : HP_HYBRID_DASM_ABS_MODE))
456 {
457 }
458 
hp_09825_67907_disassembler(bool relative_mode)459 hp_09825_67907_disassembler::hp_09825_67907_disassembler(bool relative_mode)
460 	: hp_hybrid_disassembler(HP_HYBRID_DASM_HAS_15BITS |
461 							 HP_HYBRID_DASM_HAS_EMC |
462 							 (relative_mode ? 0 : HP_HYBRID_DASM_ABS_MODE))
463 {
464 }
465 
opcode_alignment() const466 u32 hp_hybrid_disassembler::opcode_alignment() const
467 {
468 	return 1;
469 }
470