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 ¶ms)
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