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
|
// license:BSD-3-Clause
// copyright-holders:F. Ulivi
//
// *****************************************
// Emulator for HP "hybrid" processor series
// *****************************************
//
// The HP hybrid processor series is composed of a few different models with different
// capabilities. The series was derived from HP's own 2116 processor by translating a
// discrete implementation of the 1960s into a multi-chip module (hence the "hybrid" name).
// This emulator currently supports the 5061-3001, 5061-3011 and 09825-67907 versions.
//
// | *CPU* | *Addr bits* | *Multi-indirect* | *BPC* | *IOC* | *EMC* | *AEC* | *Used in* |
// |-------------+-------------+------------------+-------+-------+-------+-------+-----------|
// | 09825-67907 | 15 | Y | Y | Y | Y | N | HP 9825 |
// | 5061-3001 | 16 (+ext) | N | Y | Y | Y | Y | HP 9845 |
// | 5061-3011 | 16 | N | Y | Y | N | N | HP 64000 |
//
// For this emulator I mainly relied on these sources:
// - "The how they do dat manual": this manual has way more than you will ever want to know
// about the hybrid processors. It often gets to the single transistor detail level..
// - "Reference manual for the CPD N-MOS II Processor": this is an internal HP manual that
// was targeted at the hw/sw user of the processors.
// - http://www.hp9845.net/ website
// - HP manual "Assembly development ROM manual for the HP9845"
// - US Patent 4,075,679 describing the HP9825 system
// - US Patent 4,180,854 describing the HP9845 system
// - Study of disassembly of firmware of HP64000 & HP9845 systems
// - hp9800e emulator (now go9800) for inspiration on implementing EMC instructions
// - A lot of "educated" guessing
#ifndef MAME_CPU_HPHYBRID_HPHYBRID_H
#define MAME_CPU_HPHYBRID_HPHYBRID_H
#pragma once
// Input lines
#define HPHYBRID_IRH 0 // High-level interrupt
#define HPHYBRID_IRL 1 // Low-level interrupt
#define HPHYBRID_INT_LVLS 2 // Levels of interrupt
// I/O addressing space (16-bit wide)
// Addresses into this space are composed as follows:
// b[5..2] = Peripheral address 0..15
// b[1..0] = Register address (IC) 0..3
#define HP_IOADDR_PA_SHIFT 2
#define HP_IOADDR_IC_SHIFT 0
// Compose an I/O address from PA & IC
constexpr unsigned HP_MAKE_IOADDR(unsigned pa , unsigned ic) { return ((pa << HP_IOADDR_PA_SHIFT) | (ic << HP_IOADDR_IC_SHIFT)); }
class hp_hybrid_cpu_device : public cpu_device
{
public:
using stm_delegate = device_delegate<void (uint8_t)>;
using opcode_delegate = device_delegate<void (uint16_t)>;
using int_delegate = device_delegate<uint8_t (offs_t)>;
DECLARE_WRITE_LINE_MEMBER(dmar_w);
DECLARE_WRITE_LINE_MEMBER(halt_w);
DECLARE_WRITE_LINE_MEMBER(status_w);
DECLARE_WRITE_LINE_MEMBER(flag_w);
uint8_t pa_r() const { return m_reg_PA[0]; }
auto pa_changed_cb() { return m_pa_changed_func.bind(); }
void set_relative_mode(bool rela) { m_relative_mode = rela; }
void set_rw_cycles(unsigned read_cycles , unsigned write_cycles) { m_r_cycles = read_cycles; m_w_cycles = write_cycles; }
// Possible combinations:
// 00 No r/w cycle in progress
// 01 Non-ifetch rd cycle
// 05 Ifetch rd cycle
// 09 DMA rd cycle
// 02 Wr cycle
// 0a DMA wr cycle
//
// CYCLE_RAL_MASK is set when access is into register space [0..1f]
enum : uint8_t {
CYCLE_RD_MASK = 0x01,
CYCLE_WR_MASK = 0x02,
CYCLE_IFETCH_MASK = 0x04,
CYCLE_DMA_MASK = 0x08,
CYCLE_RAL_MASK = 0x10
};
// Called at start of each memory access
template <typename... T> void set_stm_cb(T &&... args) { m_stm_func.set(std::forward<T>(args)...); }
// Tap into fetched opcodes
template <typename... T> void set_opcode_cb(T &&... args) { m_opcode_func.set(std::forward<T>(args)...); }
// Acknowledge interrupts
template <typename... T> void set_int_cb(T &&... args) { m_int_func.set(std::forward<T>(args)...); }
protected:
hp_hybrid_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, const XTAL &clock, uint8_t addrwidth);
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
// device_execute_interface overrides
virtual uint32_t execute_min_cycles() const noexcept override { return m_r_cycles; }
virtual uint32_t execute_input_lines() const noexcept override { return 2; }
virtual uint32_t execute_default_irq_vector(int inputnum) const noexcept override { return 0xff; }
virtual void execute_run() override;
virtual void execute_set_input(int inputnum, int state) override;
uint16_t execute_one(uint16_t opcode);
bool execute_one_bpc(uint16_t opcode , uint16_t& next_pc);
// Execute an instruction that doesn't belong to BPC
virtual bool execute_no_bpc(uint16_t opcode , uint16_t& next_pc) = 0;
// Add EMC state
void emc_start();
// Execute EMC instructions
bool execute_emc(uint16_t opcode , uint16_t& next_pc);
// device_memory_interface overrides
virtual space_config_vector memory_space_config() const override;
// device_state_interface overrides
virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
// Different cases of memory access
// See patent @ pg 361
typedef enum {
AEC_CASE_A, // Instr. fetches, non-base page fetches of link pointers, BPC direct non-base page accesses
AEC_CASE_B, // Base page fetches of link pointers, BPC direct base page accesses
AEC_CASE_C, // IOC, EMC & BPC indirect final destination accesses
AEC_CASE_D // DMA accesses
} aec_cases_t;
// do memory address extension
virtual uint32_t add_mae(aec_cases_t aec_case , uint16_t addr);
static uint16_t remove_mae(uint32_t addr);
uint16_t RM(aec_cases_t aec_case , uint16_t addr);
uint16_t RM(uint32_t addr);
virtual bool read_non_common_reg(uint16_t addr , uint16_t& v) = 0;
bool read_emc_reg(uint16_t addr , uint16_t& v);
void WM(aec_cases_t aec_case , uint16_t addr , uint16_t v);
void WM(uint32_t addr , uint16_t v);
virtual bool write_non_common_reg(uint16_t addr , uint16_t v) = 0;
bool write_emc_reg(uint16_t addr , uint16_t v);
uint16_t RIO(uint8_t pa , uint8_t ic);
void WIO(uint8_t pa , uint8_t ic , uint16_t v);
uint16_t fetch();
uint16_t fetch_at(uint32_t addr);
virtual uint16_t get_indirect_target(uint32_t addr);
virtual void enter_isr();
virtual void handle_dma() = 0;
uint16_t get_skip_addr(uint16_t opcode , bool condition) const;
void update_pa();
devcb_write8 m_pa_changed_func;
uint8_t m_last_pa;
opcode_delegate m_opcode_func;
stm_delegate m_stm_func;
int_delegate m_int_func;
int m_icount;
uint32_t m_addr_mask;
uint16_t m_addr_mask_low16;
bool m_relative_mode;
unsigned m_r_cycles;
unsigned m_w_cycles;
bool m_boot_mode;
bool m_forced_bsc_25;
// State of processor
uint16_t m_reg_A; // Register A
uint16_t m_reg_B; // Register B
uint16_t m_reg_P; // Register P
uint16_t m_reg_R; // Register R
uint16_t m_reg_C; // Register C
uint16_t m_reg_D; // Register D
uint16_t m_reg_IV; // Register IV
uint16_t m_reg_W; // Register W
uint8_t m_reg_PA[ HPHYBRID_INT_LVLS + 1 ]; // Stack of register PA (4 bit-long)
uint32_t m_flags; // Flags
uint8_t m_dmapa; // DMA peripheral address (4 bits)
uint16_t m_dmama; // DMA address
uint16_t m_dmac; // DMA counter
uint16_t m_reg_I; // Instruction register
uint32_t m_genpc; // Full PC
uint8_t m_curr_cycle; // Current cycle type
// EMC registers
uint16_t m_reg_ar2[ 4 ]; // AR2 register
uint16_t m_reg_se; // SE register (4 bits)
uint16_t m_reg_r25; // R25 register
uint16_t m_reg_r26; // R26 register
uint16_t m_reg_r27; // R27 register
memory_access<22, 1, -1, ENDIANNESS_BIG>::cache m_cache;
memory_access<22, 1, -1, ENDIANNESS_BIG>::specific m_program;
memory_access< 6, 1, -1, ENDIANNESS_BIG>::specific m_io;
private:
address_space_config m_program_config;
address_space_config m_io_config;
uint32_t get_ea(uint16_t opcode);
void do_add(uint16_t& addend1 , uint16_t addend2);
template<typename T> uint16_t get_skip_addr_sc(uint16_t opcode , T& v , unsigned n);
void check_for_interrupts();
static uint8_t do_dec_shift_r(uint8_t d1 , uint64_t& mantissa);
static uint8_t do_dec_shift_l(uint8_t d12 , uint64_t& mantissa);
uint64_t get_ar1();
void set_ar1(uint64_t v);
uint64_t get_ar2() const;
void set_ar2(uint64_t v);
uint64_t do_mrxy(uint64_t ar);
bool do_dec_add(bool carry_in , uint64_t& a , uint64_t b);
void do_mpy();
};
class hp_5061_3011_cpu_device : public hp_hybrid_cpu_device
{
public:
hp_5061_3011_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock);
protected:
hp_5061_3011_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, const XTAL &clock, uint8_t addrwidth);
// TODO: fix
virtual uint32_t execute_max_cycles() const noexcept override { return 25; }
virtual bool execute_no_bpc(uint16_t opcode , uint16_t& next_pc) override;
virtual bool read_non_common_reg(uint16_t addr , uint16_t& v) override;
virtual bool write_non_common_reg(uint16_t addr , uint16_t v) override;
virtual void handle_dma() override;
// device_disasm_interface overrides
virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
};
class hp_5061_3001_cpu_device : public hp_5061_3011_cpu_device
{
public:
hp_5061_3001_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock);
// Set boot mode of 5061-3001: either normal (false) or as in HP9845 system (true)
void set_9845_boot_mode(bool mode) { m_boot_mode = mode; }
protected:
virtual void device_start() override;
virtual void device_reset() override;
// TODO: fix
virtual uint32_t execute_max_cycles() const noexcept override { return 237; } // FMP 15
virtual bool execute_no_bpc(uint16_t opcode , uint16_t& next_pc) override;
virtual uint32_t add_mae(aec_cases_t aec_case, uint16_t addr) override;
virtual bool read_non_common_reg(uint16_t addr , uint16_t& v) override;
virtual bool write_non_common_reg(uint16_t addr , uint16_t v) override;
virtual void enter_isr() override;
// device_disasm_interface overrides
virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
private:
// Additional state of processor
uint16_t m_reg_aec[ 37 - 32 + 1 ]; // AEC registers R32-R37
};
class hp_09825_67907_cpu_device : public hp_hybrid_cpu_device
{
public:
hp_09825_67907_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock);
protected:
// device-level overrides
virtual void device_start() override;
// device_execute_interface overrides
// TODO: fix
virtual uint32_t execute_max_cycles() const noexcept override { return 237; } // FMP 15
virtual bool execute_no_bpc(uint16_t opcode , uint16_t& next_pc) override;
// device_disasm_interface overrides
virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
virtual bool read_non_common_reg(uint16_t addr , uint16_t& v) override;
virtual bool write_non_common_reg(uint16_t addr , uint16_t v) override;
virtual uint16_t get_indirect_target(uint32_t addr) override;
virtual void handle_dma() override;
private:
void inc_dec_cd(uint16_t& cd_reg , bool increment , bool byte);
};
DECLARE_DEVICE_TYPE(HP_5061_3001, hp_5061_3001_cpu_device)
DECLARE_DEVICE_TYPE(HP_5061_3011, hp_5061_3011_cpu_device)
DECLARE_DEVICE_TYPE(HP_09825_67907 , hp_09825_67907_cpu_device)
#endif // MAME_CPU_HPHYBRID_HPHYBRID_H
|