trunk/src/mess/drivers/h8.c
| r23870 | r23871 | |
| 2 | 2 | |
| 3 | 3 | Heathkit H8 |
| 4 | 4 | |
| 5 | | 12/05/2009 Skeleton driver. |
| 5 | 2009-05-12 Skeleton driver. |
| 6 | 6 | |
| 7 | | STATUS: |
| 8 | | - It runs, keyboard works, you can enter data |
| 7 | This system uses Octal rather than the usual hexadecimal. |
| 9 | 8 | |
| 10 | | TODO: |
| 11 | | - Proper artwork |
| 12 | | - Fix interrupts |
| 13 | | - Seems to crash when GO pressed (needs a subject-matter expert to test) |
| 14 | | - Add load/dump facility (cassette port) |
| 9 | STATUS: |
| 10 | It runs, keyboard works, you can enter data. |
| 15 | 11 | |
| 12 | Meaning of LEDs: |
| 13 | PWR = power is turned on |
| 14 | MON = controls should work |
| 15 | RUN = CPU is running (not halted) |
| 16 | ION = Interrupts are enabled |
| 17 | |
| 18 | Pasting: |
| 19 | 0-F : as is |
| 20 | + : ^ |
| 21 | - : V |
| 22 | MEM : - |
| 23 | ALTER : = |
| 24 | |
| 25 | Addresses must have all 6 digits entered. |
| 26 | Data must have all 3 digits entered. |
| 27 | System has a short beep for each key, and a slightly longer beep |
| 28 | for each group of 3 digits. The largest number allowed is 377 (=0xFF). |
| 29 | |
| 30 | Test Paste: |
| 31 | -041000=123 245 333 144 255 366 077=-041000 |
| 32 | Now press up-arrow to confirm the data has been entered. |
| 33 | |
| 34 | Official test program from pages 4 to 8 of the operator's manual: |
| 35 | -040100=076 002 062 010 040 006 004 041 170 040 021 013 040 016 011 176 |
| 36 | 022 043 023 015 302 117 040 016 003 076 377 315 053 000 015 302 |
| 37 | 131 040 005 302 112 040 076 062 315 140 002 076 062 315 053 000 |
| 38 | 076 062 315 140 002 303 105 040 377 262 270 272 275 377 222 200 |
| 39 | 377 237 244 377 272 230 377 220 326 302 377 275 272 271 271 373 |
| 40 | 271 240 377 236 376 362 236 376 362 236 376 362 R6=040100=4 |
| 41 | |
| 42 | TODO: |
| 43 | - Cassette (coded but not working) |
| 44 | |
| 16 | 45 | ****************************************************************************/ |
| 17 | 46 | |
| 18 | 47 | #include "emu.h" |
| 19 | 48 | #include "cpu/i8085/i8085.h" |
| 49 | #include "machine/i8251.h" |
| 50 | #include "imagedev/cassette.h" |
| 20 | 51 | #include "sound/beep.h" |
| 52 | #include "sound/wave.h" |
| 21 | 53 | #include "h8.lh" |
| 22 | 54 | |
| 23 | 55 | |
| r23870 | r23871 | |
| 26 | 58 | public: |
| 27 | 59 | h8_state(const machine_config &mconfig, device_type type, const char *tag) |
| 28 | 60 | : driver_device(mconfig, type, tag), |
| 29 | | m_maincpu(*this, "maincpu"), |
| 30 | | //m_cass(*this, "cassette"), |
| 31 | | m_beep(*this, "beeper") |
| 61 | m_maincpu(*this, "maincpu"), |
| 62 | m_uart(*this, "uart"), |
| 63 | m_cass(*this, "cassette"), |
| 64 | m_beep(*this, "beeper") |
| 32 | 65 | { } |
| 33 | 66 | |
| 34 | | required_device<cpu_device> m_maincpu; |
| 35 | | //required_device<cassette_image_device> m_cass; |
| 36 | | required_device<beep_device> m_beep; |
| 37 | | DECLARE_READ8_MEMBER(h8_f0_r); |
| 38 | | DECLARE_WRITE8_MEMBER(h8_f0_w); |
| 39 | | DECLARE_WRITE8_MEMBER(h8_f1_w); |
| 67 | DECLARE_READ8_MEMBER(portf0_r); |
| 68 | DECLARE_WRITE8_MEMBER(portf0_w); |
| 69 | DECLARE_WRITE8_MEMBER(portf1_w); |
| 40 | 70 | DECLARE_WRITE8_MEMBER(h8_status_callback); |
| 41 | 71 | DECLARE_WRITE_LINE_MEMBER(h8_inte_callback); |
| 72 | DECLARE_READ_LINE_MEMBER(rxdata_callback); |
| 73 | DECLARE_WRITE_LINE_MEMBER(txdata_callback); |
| 74 | TIMER_DEVICE_CALLBACK_MEMBER(h8_irq_pulse); |
| 75 | TIMER_DEVICE_CALLBACK_MEMBER(h8_c); |
| 76 | TIMER_DEVICE_CALLBACK_MEMBER(h8_p); |
| 77 | private: |
| 42 | 78 | UINT8 m_digit; |
| 43 | 79 | UINT8 m_segment; |
| 44 | 80 | UINT8 m_irq_ctl; |
| 45 | | UINT8 m_ff_b; |
| 81 | bool m_ff_b; |
| 82 | UINT8 m_cass_data[4]; |
| 83 | bool m_cass_state; |
| 46 | 84 | virtual void machine_reset(); |
| 47 | | TIMER_DEVICE_CALLBACK_MEMBER(h8_irq_pulse); |
| 85 | required_device<cpu_device> m_maincpu; |
| 86 | required_device<i8251_device> m_uart; |
| 87 | required_device<cassette_image_device> m_cass; |
| 88 | required_device<beep_device> m_beep; |
| 48 | 89 | }; |
| 49 | 90 | |
| 50 | 91 | |
| r23870 | r23871 | |
| 59 | 100 | m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, ASSERT_LINE, 0xcf); |
| 60 | 101 | } |
| 61 | 102 | |
| 62 | | READ8_MEMBER( h8_state::h8_f0_r ) |
| 103 | READ8_MEMBER( h8_state::portf0_r ) |
| 63 | 104 | { |
| 64 | 105 | // reads the keyboard |
| 65 | 106 | |
| r23870 | r23871 | |
| 89 | 130 | return data; |
| 90 | 131 | } |
| 91 | 132 | |
| 92 | | WRITE8_MEMBER( h8_state::h8_f0_w ) |
| 133 | WRITE8_MEMBER( h8_state::portf0_w ) |
| 93 | 134 | { |
| 94 | 135 | // this will always turn off int10 that was set by the timer |
| 95 | 136 | // d0-d3 = digit select |
| r23870 | r23871 | |
| 101 | 142 | m_digit = data & 15; |
| 102 | 143 | if (m_digit) output_set_digit_value(m_digit, m_segment); |
| 103 | 144 | |
| 104 | | output_set_value("mon_led",(data & 0x20) ? 0 : 1); |
| 105 | | m_beep->set_state((data & 0x80) ? 0 : 1); |
| 145 | output_set_value("mon_led", !BIT(data, 5)); |
| 146 | m_beep->set_state(!BIT(data, 7)); |
| 106 | 147 | |
| 107 | 148 | m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); |
| 108 | 149 | m_irq_ctl &= 0xf0; |
| 109 | | if (data & 0x40) m_irq_ctl |= 1; |
| 110 | | if (~data & 0x10) m_irq_ctl |= 2; |
| 150 | if (BIT(data, 6)) m_irq_ctl |= 1; |
| 151 | if (!BIT(data, 4)) m_irq_ctl |= 2; |
| 111 | 152 | } |
| 112 | 153 | |
| 113 | | WRITE8_MEMBER( h8_state::h8_f1_w ) |
| 154 | WRITE8_MEMBER( h8_state::portf1_w ) |
| 114 | 155 | { |
| 115 | 156 | //d7 segment dot |
| 116 | 157 | //d6 segment f |
| r23870 | r23871 | |
| 127 | 168 | |
| 128 | 169 | static ADDRESS_MAP_START(h8_mem, AS_PROGRAM, 8, h8_state) |
| 129 | 170 | ADDRESS_MAP_UNMAP_HIGH |
| 130 | | AM_RANGE(0x0000, 0x03ff) AM_ROM |
| 171 | AM_RANGE(0x0000, 0x0fff) AM_ROM |
| 131 | 172 | AM_RANGE(0x2000, 0x9fff) AM_RAM |
| 132 | 173 | ADDRESS_MAP_END |
| 133 | 174 | |
| 134 | 175 | static ADDRESS_MAP_START( h8_io, AS_IO, 8, h8_state) |
| 135 | 176 | ADDRESS_MAP_UNMAP_HIGH |
| 136 | 177 | ADDRESS_MAP_GLOBAL_MASK(0xff) |
| 137 | | AM_RANGE(0xf0, 0xf0) AM_READWRITE(h8_f0_r,h8_f0_w) |
| 138 | | AM_RANGE(0xf1, 0xf1) AM_WRITE(h8_f1_w) |
| 139 | | //AM_RANGE(0xf8, 0xf8) load and dump data port |
| 140 | | //AM_RANGE(0xf9, 0xf9) load and dump control port |
| 141 | | //AM_RANGE(0xfa, 0xfa) console data port |
| 142 | | //AM_RANGE(0xfb, 0xfb) console control port |
| 178 | AM_RANGE(0xf0, 0xf0) AM_READWRITE(portf0_r,portf0_w) |
| 179 | AM_RANGE(0xf1, 0xf1) AM_WRITE(portf1_w) |
| 180 | AM_RANGE(0xf8, 0xf8) AM_DEVREADWRITE("uart", i8251_device, data_r, data_w) |
| 181 | AM_RANGE(0xf9, 0xf9) AM_DEVREADWRITE("uart", i8251_device, status_r, control_w) |
| 182 | // optional connection to a serial terminal @ 600 baud |
| 183 | //AM_RANGE(0xfa, 0xfa) AM_DEVREADWRITE("uart1", i8251_device, data_r, data_w) |
| 184 | //AM_RANGE(0xfb, 0xfb) AM_DEVREADWRITE("uart1", i8251_device, status_r, control_w) |
| 143 | 185 | ADDRESS_MAP_END |
| 144 | 186 | |
| 145 | 187 | /* Input ports */ |
| r23870 | r23871 | |
| 157 | 199 | PORT_START("X1") |
| 158 | 200 | PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8 LOAD") PORT_CODE(KEYCODE_8) PORT_CHAR('8') |
| 159 | 201 | PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9 DUMP") PORT_CODE(KEYCODE_9) PORT_CHAR('9') |
| 160 | | PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("+") PORT_CODE(KEYCODE_EQUALS) |
| 161 | | PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) |
| 162 | | PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("* CANCEL") PORT_CODE(KEYCODE_Q) |
| 163 | | PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("// ALTER RST") PORT_CODE(KEYCODE_W) |
| 164 | | PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("# MEM RTM") PORT_CODE(KEYCODE_E) |
| 165 | | PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("@ REG") PORT_CODE(KEYCODE_R) |
| 202 | PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("+") PORT_CODE(KEYCODE_UP) PORT_CHAR('^') |
| 203 | PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("-") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V') |
| 204 | PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CANCEL") PORT_CODE(KEYCODE_ESC) PORT_CHAR('Q') |
| 205 | PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ALTER") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') |
| 206 | PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MEM") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') |
| 207 | PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R') |
| 166 | 208 | INPUT_PORTS_END |
| 167 | 209 | |
| 168 | 210 | void h8_state::machine_reset() |
| r23870 | r23871 | |
| 170 | 212 | m_beep->set_frequency(H8_BEEP_FRQ); |
| 171 | 213 | output_set_value("pwr_led", 0); |
| 172 | 214 | m_irq_ctl = 1; |
| 215 | m_cass_state = 1; |
| 216 | m_cass_data[0] = 0; |
| 217 | m_cass_data[1] = 0; |
| 218 | m_cass_data[2] = 0; |
| 219 | m_cass_data[3] = 0; |
| 173 | 220 | } |
| 174 | 221 | |
| 175 | 222 | WRITE_LINE_MEMBER( h8_state::h8_inte_callback ) |
| 176 | 223 | { |
| 177 | 224 | // operate the ION LED |
| 178 | | output_set_value("ion_led",(state) ? 0 : 1); |
| 225 | output_set_value("ion_led", !state); |
| 179 | 226 | m_irq_ctl &= 0x7f | ((state) ? 0 : 0x80); |
| 180 | 227 | } |
| 181 | 228 | |
| r23870 | r23871 | |
| 186 | 233 | a int20 (output of 2nd flipflop) will occur after 4 M1 steps, to pause the running program. |
| 187 | 234 | But, all of this can only occur if bit 5 of port F0 is low. */ |
| 188 | 235 | |
| 189 | | UINT8 state = (data & I8085_STATUS_M1) ? 0 : 1; |
| 190 | | UINT8 c,a = (m_irq_ctl & 0x80) ? 1 : 0; |
| 236 | bool state = (data & I8085_STATUS_M1) ? 0 : 1; |
| 237 | bool c,a = (m_irq_ctl & 0x80) ? 1 : 0; |
| 191 | 238 | |
| 192 | 239 | if (m_irq_ctl & 2) |
| 193 | 240 | { |
| 194 | 241 | if (!state) // rising pulse to push data through flipflops |
| 195 | 242 | { |
| 196 | | c=m_ff_b^1; // from /Q of 2nd flipflop |
| 197 | | m_ff_b=a; // from Q of 1st flipflop |
| 243 | c = ~m_ff_b; // from /Q of 2nd flipflop |
| 244 | m_ff_b = a; // from Q of 1st flipflop |
| 198 | 245 | if (c) |
| 199 | 246 | m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, ASSERT_LINE, 0xd7); |
| 200 | 247 | } |
| 201 | 248 | } |
| 202 | 249 | else |
| 203 | 250 | { // flipflops are 'set' |
| 204 | | c=0; |
| 205 | | m_ff_b=1; |
| 251 | c = 0; |
| 252 | m_ff_b = 1; |
| 206 | 253 | } |
| 207 | 254 | |
| 208 | 255 | |
| r23870 | r23871 | |
| 210 | 257 | output_set_value("run_led", state); |
| 211 | 258 | } |
| 212 | 259 | |
| 260 | READ_LINE_MEMBER( h8_state::rxdata_callback ) |
| 261 | {//printf("%X",m_cass_data[2]); |
| 262 | return (bool)m_cass_data[2]; |
| 263 | } |
| 264 | |
| 265 | WRITE_LINE_MEMBER( h8_state::txdata_callback ) |
| 266 | { |
| 267 | m_cass_state = state; |
| 268 | } |
| 269 | |
| 270 | static const i8251_interface uart_intf = |
| 271 | { |
| 272 | DEVCB_DRIVER_LINE_MEMBER(h8_state,rxdata_callback), //rxd_cb |
| 273 | DEVCB_DRIVER_LINE_MEMBER(h8_state,txdata_callback), //txd_cb |
| 274 | DEVCB_NULL, |
| 275 | DEVCB_NULL, |
| 276 | DEVCB_NULL, |
| 277 | DEVCB_NULL, |
| 278 | DEVCB_NULL, |
| 279 | DEVCB_NULL, |
| 280 | DEVCB_NULL |
| 281 | }; |
| 282 | |
| 213 | 283 | static I8085_CONFIG( h8_cpu_config ) |
| 214 | 284 | { |
| 215 | 285 | DEVCB_DRIVER_MEMBER(h8_state, h8_status_callback), /* Status changed callback */ |
| r23870 | r23871 | |
| 218 | 288 | DEVCB_NULL /* SOD changed callback (I8085A only) */ |
| 219 | 289 | }; |
| 220 | 290 | |
| 291 | TIMER_DEVICE_CALLBACK_MEMBER(h8_state::h8_c) |
| 292 | { |
| 293 | m_uart->receive_clock(); |
| 294 | m_uart->transmit_clock(); |
| 295 | m_cass_data[3]++; |
| 296 | |
| 297 | if (m_cass_state) |
| 298 | m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz |
| 299 | else |
| 300 | m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz |
| 301 | } |
| 302 | |
| 303 | TIMER_DEVICE_CALLBACK_MEMBER(h8_state::h8_p) |
| 304 | { |
| 305 | /* cassette - turn 1200/2400Hz to a bit */ |
| 306 | m_cass_data[1]++; |
| 307 | UINT8 cass_ws = (m_cass->input() > +0.03) ? 1 : 0; |
| 308 | |
| 309 | if (cass_ws != m_cass_data[0]) |
| 310 | { |
| 311 | m_cass_data[0] = cass_ws; |
| 312 | m_cass_data[2] = (m_cass_data[1] < 12) ? 1 : 0; |
| 313 | m_cass_data[1] = 0; |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | static const cassette_interface h8_cassette_interface = |
| 318 | { |
| 319 | cassette_default_formats, |
| 320 | NULL, |
| 321 | //(cassette_state) (CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED), |
| 322 | (cassette_state) (CASSETTE_PLAY | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED), |
| 323 | "h8_cass", |
| 324 | NULL |
| 325 | }; |
| 326 | |
| 221 | 327 | static MACHINE_CONFIG_START( h8, h8_state ) |
| 222 | 328 | /* basic machine hardware */ |
| 223 | 329 | MCFG_CPU_ADD("maincpu", I8080, H8_CLOCK) |
| r23870 | r23871 | |
| 225 | 331 | MCFG_CPU_IO_MAP(h8_io) |
| 226 | 332 | MCFG_CPU_CONFIG(h8_cpu_config) |
| 227 | 333 | |
| 228 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("h8_timer", h8_state, h8_irq_pulse, attotime::from_hz(H8_IRQ_PULSE)) |
| 229 | | |
| 230 | 334 | /* video hardware */ |
| 231 | 335 | MCFG_DEFAULT_LAYOUT(layout_h8) |
| 232 | 336 | |
| r23870 | r23871 | |
| 234 | 338 | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 235 | 339 | MCFG_SOUND_ADD("beeper", BEEP, 0) |
| 236 | 340 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00) |
| 341 | MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") |
| 342 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) |
| 343 | |
| 344 | /* Devices */ |
| 345 | MCFG_I8251_ADD("uart", uart_intf) |
| 346 | MCFG_CASSETTE_ADD("cassette", h8_cassette_interface) |
| 347 | MCFG_TIMER_DRIVER_ADD_PERIODIC("h8_c", h8_state, h8_c, attotime::from_hz(4800)) |
| 348 | MCFG_TIMER_DRIVER_ADD_PERIODIC("h8_p", h8_state, h8_p, attotime::from_hz(40000)) |
| 349 | MCFG_TIMER_DRIVER_ADD_PERIODIC("h8_timer", h8_state, h8_irq_pulse, attotime::from_hz(H8_IRQ_PULSE)) |
| 237 | 350 | MACHINE_CONFIG_END |
| 238 | 351 | |
| 239 | 352 | /* ROM definition */ |
| r23870 | r23871 | |
| 251 | 364 | |
| 252 | 365 | /* Driver */ |
| 253 | 366 | |
| 254 | | /* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */ |
| 255 | | COMP( 1977, h8, 0, 0, h8, h8, driver_device, 0, "Heath, Inc.", "Heathkit H8", GAME_NOT_WORKING ) |
| 367 | /* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS, INIT COMPANY FULLNAME FLAGS */ |
| 368 | COMP( 1977, h8, 0, 0, h8, h8, driver_device, 0, "Heath, Inc.", "Heathkit H8", GAME_NOT_WORKING ) |