trunk/src/mess/drivers/amaztron.c
| r0 | r243823 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:hap |
| 3 | /*************************************************************************** |
| 4 | |
| 5 | Coleco Amaze-A-Tron, by Ralph Baer |
| 6 | * TMS1100 MCU, labeled MP3405(die label too) |
| 7 | |
| 8 | This is an electronic board game with a selection of 8 maze games, |
| 9 | most of them for 2 players. A 5x5 playing grid and four markers are |
| 10 | required to play. Refer to the official manual for more information. |
| 11 | |
| 12 | ***************************************************************************/ |
| 13 | |
| 14 | #include "emu.h" |
| 15 | #include "cpu/tms0980/tms0980.h" |
| 16 | #include "sound/speaker.h" |
| 17 | |
| 18 | #include "amaztron.lh" |
| 19 | |
| 20 | // master clock is a single stage RC oscillator: R=33K?, C=100pf, |
| 21 | // according to the TMS 1000 series data manual this is around 350kHz |
| 22 | #define MASTER_CLOCK (350000) |
| 23 | |
| 24 | |
| 25 | class amaztron_state : public driver_device |
| 26 | { |
| 27 | public: |
| 28 | amaztron_state(const machine_config &mconfig, device_type type, const char *tag) |
| 29 | : driver_device(mconfig, type, tag), |
| 30 | m_maincpu(*this, "maincpu"), |
| 31 | m_button_matrix(*this, "IN"), |
| 32 | m_speaker(*this, "speaker") |
| 33 | { } |
| 34 | |
| 35 | required_device<cpu_device> m_maincpu; |
| 36 | required_ioport_array<6> m_button_matrix; |
| 37 | required_device<speaker_sound_device> m_speaker; |
| 38 | |
| 39 | UINT16 m_r; |
| 40 | UINT16 m_o; |
| 41 | |
| 42 | DECLARE_READ8_MEMBER(read_k); |
| 43 | DECLARE_WRITE16_MEMBER(write_o); |
| 44 | DECLARE_WRITE16_MEMBER(write_r); |
| 45 | |
| 46 | void leds_update(); |
| 47 | |
| 48 | virtual void machine_start(); |
| 49 | }; |
| 50 | |
| 51 | |
| 52 | |
| 53 | /*************************************************************************** |
| 54 | |
| 55 | I/O |
| 56 | |
| 57 | ***************************************************************************/ |
| 58 | |
| 59 | void amaztron_state::leds_update() |
| 60 | { |
| 61 | for (int i = 0; i < 2; i++) |
| 62 | if (m_r >> (i + 8) & 1) |
| 63 | output_set_digit_value(i, m_o); |
| 64 | } |
| 65 | |
| 66 | READ8_MEMBER(amaztron_state::read_k) |
| 67 | { |
| 68 | UINT8 k = 0; |
| 69 | |
| 70 | // read selected button rows |
| 71 | for (int i = 0; i < 6; i++) |
| 72 | { |
| 73 | if (m_r >> i & 1) |
| 74 | k |= m_button_matrix[i]->read(); |
| 75 | } |
| 76 | |
| 77 | // the 5th row is tied to K4+K8 |
| 78 | if (k & 0x10) k |= 0xc; |
| 79 | return k & 0xf; |
| 80 | } |
| 81 | |
| 82 | WRITE16_MEMBER(amaztron_state::write_r) |
| 83 | { |
| 84 | // R0-R5: input mux |
| 85 | // R6,R7: lamps |
| 86 | output_set_lamp_value(0, data >> 6 & 1); |
| 87 | output_set_lamp_value(1, data >> 7 & 1); |
| 88 | |
| 89 | // R8,R9: select digit |
| 90 | m_r = data; |
| 91 | leds_update(); |
| 92 | |
| 93 | // R10: speaker out |
| 94 | m_speaker->level_w(data >> 10 & 1); |
| 95 | } |
| 96 | |
| 97 | WRITE16_MEMBER(amaztron_state::write_o) |
| 98 | { |
| 99 | // O0-O6: digit segments |
| 100 | // O7: N/C |
| 101 | m_o = data & 0x7f; |
| 102 | leds_update(); |
| 103 | } |
| 104 | |
| 105 | |
| 106 | |
| 107 | /*************************************************************************** |
| 108 | |
| 109 | Inputs |
| 110 | |
| 111 | ***************************************************************************/ |
| 112 | |
| 113 | static INPUT_PORTS_START( amaztron ) |
| 114 | PORT_START("IN.0") // R0 |
| 115 | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Button 1") |
| 116 | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Button 6") |
| 117 | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Button 11") |
| 118 | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Button 16") |
| 119 | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Button 21") |
| 120 | |
| 121 | PORT_START("IN.1") // R1 |
| 122 | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Button 2") |
| 123 | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Button 7") |
| 124 | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Button 12") |
| 125 | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Button 17") |
| 126 | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Button 22") |
| 127 | |
| 128 | PORT_START("IN.2") // R2 |
| 129 | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Button 3") |
| 130 | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Button 8") |
| 131 | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Button 13") |
| 132 | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Button 18") |
| 133 | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Button 23") |
| 134 | |
| 135 | PORT_START("IN.3") // R3 |
| 136 | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Button 4") |
| 137 | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_NAME("Button 9") |
| 138 | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Button 14") |
| 139 | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Button 19") |
| 140 | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Button 24") |
| 141 | |
| 142 | PORT_START("IN.4") // R4 |
| 143 | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Button 5") |
| 144 | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_NAME("Button 10") |
| 145 | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Button 15") |
| 146 | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Button 20") |
| 147 | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Button 25") |
| 148 | |
| 149 | PORT_START("IN.5") // R5 |
| 150 | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Game Select") |
| 151 | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("Game Start") |
| 152 | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) |
| 153 | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) |
| 154 | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) |
| 155 | INPUT_PORTS_END |
| 156 | |
| 157 | |
| 158 | |
| 159 | /*************************************************************************** |
| 160 | |
| 161 | Machine Config |
| 162 | |
| 163 | ***************************************************************************/ |
| 164 | |
| 165 | void amaztron_state::machine_start() |
| 166 | { |
| 167 | m_r = 0; |
| 168 | m_o = 0; |
| 169 | |
| 170 | save_item(NAME(m_r)); |
| 171 | save_item(NAME(m_o)); |
| 172 | } |
| 173 | |
| 174 | |
| 175 | static MACHINE_CONFIG_START( amaztron, amaztron_state ) |
| 176 | |
| 177 | /* basic machine hardware */ |
| 178 | MCFG_CPU_ADD("maincpu", TMS1100, MASTER_CLOCK) |
| 179 | MCFG_TMS1XXX_READ_K_CB(READ8(amaztron_state, read_k)) |
| 180 | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(amaztron_state, write_o)) |
| 181 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(amaztron_state, write_r)) |
| 182 | |
| 183 | MCFG_DEFAULT_LAYOUT(layout_amaztron) |
| 184 | |
| 185 | /* no video! */ |
| 186 | |
| 187 | /* sound hardware */ |
| 188 | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 189 | MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) |
| 190 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) |
| 191 | MACHINE_CONFIG_END |
| 192 | |
| 193 | |
| 194 | |
| 195 | /*************************************************************************** |
| 196 | |
| 197 | Game driver(s) |
| 198 | |
| 199 | ***************************************************************************/ |
| 200 | |
| 201 | ROM_START( amaztron ) |
| 202 | ROM_REGION( 0x0800, "maincpu", 0 ) |
| 203 | ROM_LOAD( "tms1100nll_mp3405", 0x0000, 0x0800, CRC(9cbc0009) SHA1(17772681271b59280687492f37fa0859998f041d) ) |
| 204 | |
| 205 | ROM_REGION( 867, "maincpu:mpla", 0 ) |
| 206 | ROM_LOAD( "tms1100_amaztron_mpla.pla", 0, 867, CRC(03574895) SHA1(04407cabfb3adee2ee5e4218612cb06c12c540f4) ) |
| 207 | ROM_REGION( 365, "maincpu:opla", 0 ) |
| 208 | ROM_LOAD( "tms1100_amaztron_opla.pla", 0, 365, CRC(f3875384) SHA1(3c256a3db4f0aa9d93cf78124db39f4cbdc57e4a) ) |
| 209 | ROM_END |
| 210 | |
| 211 | |
| 212 | CONS( 1979, amaztron, 0, 0, amaztron, amaztron, driver_device, 0, "Coleco", "Amaze-A-Tron", GAME_SUPPORTS_SAVE ) |
trunk/src/mess/drivers/comp4.c
| r0 | r243823 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:hap |
| 3 | /*************************************************************************** |
| 4 | |
| 5 | Milton Bradley Comp IV |
| 6 | * TMC0904NL CP0904A (die labeled 4A0970D-04A) |
| 7 | |
| 8 | This is small tabletop Mastermind game; a code-breaking game where the player |
| 9 | needs to find out the correct sequence of colours (numbers in our case). |
| 10 | It is known as Logic 5 in Europe, and as Pythaligoras in Japan. |
| 11 | |
| 12 | Press the R key to start, followed by a set of unique numbers and E. |
| 13 | Refer to the official manual for more information. |
| 14 | |
| 15 | |
| 16 | TODO: |
| 17 | - MCU clock is unknown |
| 18 | |
| 19 | ***************************************************************************/ |
| 20 | |
| 21 | #include "emu.h" |
| 22 | #include "cpu/tms0980/tms0980.h" |
| 23 | |
| 24 | #include "comp4.lh" |
| 25 | |
| 26 | // master clock is unknown, the value below is an approximation |
| 27 | #define MASTER_CLOCK (250000) |
| 28 | |
| 29 | |
| 30 | class comp4_state : public driver_device |
| 31 | { |
| 32 | public: |
| 33 | comp4_state(const machine_config &mconfig, device_type type, const char *tag) |
| 34 | : driver_device(mconfig, type, tag), |
| 35 | m_maincpu(*this, "maincpu"), |
| 36 | m_button_matrix(*this, "IN") |
| 37 | { } |
| 38 | |
| 39 | required_device<cpu_device> m_maincpu; |
| 40 | required_ioport_array<3> m_button_matrix; |
| 41 | |
| 42 | UINT16 m_o; |
| 43 | |
| 44 | UINT16 m_display_state; |
| 45 | UINT8 m_display_decay[0x10]; |
| 46 | |
| 47 | DECLARE_READ8_MEMBER(read_k); |
| 48 | DECLARE_WRITE16_MEMBER(write_o); |
| 49 | DECLARE_WRITE16_MEMBER(write_r); |
| 50 | |
| 51 | TIMER_DEVICE_CALLBACK_MEMBER(display_decay_tick); |
| 52 | void display_update(); |
| 53 | |
| 54 | virtual void machine_start(); |
| 55 | }; |
| 56 | |
| 57 | |
| 58 | /*************************************************************************** |
| 59 | |
| 60 | LED Display |
| 61 | |
| 62 | ***************************************************************************/ |
| 63 | |
| 64 | // The device strobes the outputs very fast, it is unnoticeable to the user. |
| 65 | // To prevent flickering here, we need to simulate a decay. |
| 66 | |
| 67 | // decay time, in steps of 1ms |
| 68 | #define DISPLAY_DECAY_TIME 25 |
| 69 | |
| 70 | void comp4_state::display_update() |
| 71 | { |
| 72 | for (int i = 0; i < 0x10; i++) |
| 73 | { |
| 74 | // turn on powered segments |
| 75 | if (m_display_state >> i & 1) |
| 76 | m_display_decay[i] = DISPLAY_DECAY_TIME; |
| 77 | |
| 78 | // send to output |
| 79 | output_set_lamp_value(i, (m_display_decay[i] != 0) ? 1 : 0); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | TIMER_DEVICE_CALLBACK_MEMBER(comp4_state::display_decay_tick) |
| 84 | { |
| 85 | // slowly turn off unpowered segments |
| 86 | for (int i = 0; i < 0x10; i++) |
| 87 | if (!(m_display_state >> i & 1) && m_display_decay[i]) |
| 88 | m_display_decay[i]--; |
| 89 | |
| 90 | display_update(); |
| 91 | } |
| 92 | |
| 93 | |
| 94 | |
| 95 | /*************************************************************************** |
| 96 | |
| 97 | I/O |
| 98 | |
| 99 | ***************************************************************************/ |
| 100 | |
| 101 | READ8_MEMBER(comp4_state::read_k) |
| 102 | { |
| 103 | UINT8 k = 0; |
| 104 | |
| 105 | // read selected button rows |
| 106 | for (int i = 0; i < 3; i++) |
| 107 | if (m_o >> (i+1) & 1) |
| 108 | k |= m_button_matrix[i]->read(); |
| 109 | |
| 110 | return k; |
| 111 | } |
| 112 | |
| 113 | WRITE16_MEMBER(comp4_state::write_r) |
| 114 | { |
| 115 | // LEDs: |
| 116 | // R4 R9 |
| 117 | // R10! R8 |
| 118 | // R2 R7 |
| 119 | // R1 R6 |
| 120 | // R0 R5 |
| 121 | m_display_state = data; |
| 122 | display_update(); |
| 123 | } |
| 124 | |
| 125 | WRITE16_MEMBER(comp4_state::write_o) |
| 126 | { |
| 127 | // O0: LEDs common (always writes 1) |
| 128 | // O1-O3: input mux |
| 129 | // other bits: N/C |
| 130 | m_o = data; |
| 131 | } |
| 132 | |
| 133 | |
| 134 | |
| 135 | /*************************************************************************** |
| 136 | |
| 137 | Inputs |
| 138 | |
| 139 | ***************************************************************************/ |
| 140 | |
| 141 | static INPUT_PORTS_START( comp4 ) |
| 142 | PORT_START("IN.0") // O1 |
| 143 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("R") |
| 144 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1") |
| 145 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4") |
| 146 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7") |
| 147 | |
| 148 | PORT_START("IN.1") // O2 |
| 149 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0") |
| 150 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2") |
| 151 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5") |
| 152 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8") |
| 153 | |
| 154 | PORT_START("IN.2") // O3 |
| 155 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("E") |
| 156 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3") |
| 157 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6") |
| 158 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9") |
| 159 | INPUT_PORTS_END |
| 160 | |
| 161 | |
| 162 | |
| 163 | /*************************************************************************** |
| 164 | |
| 165 | Machine Config |
| 166 | |
| 167 | ***************************************************************************/ |
| 168 | |
| 169 | void comp4_state::machine_start() |
| 170 | { |
| 171 | // zerofill |
| 172 | m_display_state = 0; |
| 173 | memset(m_display_decay, 0, sizeof(m_display_decay)); |
| 174 | |
| 175 | m_o = 0; |
| 176 | |
| 177 | // register for savestates |
| 178 | save_item(NAME(m_display_state)); |
| 179 | save_item(NAME(m_display_decay)); |
| 180 | |
| 181 | save_item(NAME(m_o)); |
| 182 | } |
| 183 | |
| 184 | |
| 185 | static MACHINE_CONFIG_START( comp4, comp4_state ) |
| 186 | |
| 187 | /* basic machine hardware */ |
| 188 | MCFG_CPU_ADD("maincpu", TMS0970, MASTER_CLOCK) |
| 189 | MCFG_TMS1XXX_READ_K_CB(READ8(comp4_state, read_k)) |
| 190 | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(comp4_state, write_o)) |
| 191 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(comp4_state, write_r)) |
| 192 | |
| 193 | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", comp4_state, display_decay_tick, attotime::from_msec(1)) |
| 194 | |
| 195 | MCFG_DEFAULT_LAYOUT(layout_comp4) |
| 196 | |
| 197 | /* no video! */ |
| 198 | |
| 199 | /* no sound! */ |
| 200 | MACHINE_CONFIG_END |
| 201 | |
| 202 | |
| 203 | |
| 204 | /*************************************************************************** |
| 205 | |
| 206 | Game driver(s) |
| 207 | |
| 208 | ***************************************************************************/ |
| 209 | |
| 210 | ROM_START( comp4 ) |
| 211 | ROM_REGION( 0x0400, "maincpu", 0 ) |
| 212 | ROM_LOAD( "tmc0904nl_cp0904a", 0x0000, 0x0400, CRC(6233ee1b) SHA1(738e109b38c97804b4ec52bed80b00a8634ad453) ) |
| 213 | |
| 214 | ROM_REGION( 782, "maincpu:ipla", 0 ) |
| 215 | ROM_LOAD( "tms0970_default_ipla.pla", 0, 782, CRC(e038fc44) SHA1(dfc280f6d0a5828d1bb14fcd59ac29caf2c2d981) ) |
| 216 | ROM_REGION( 860, "maincpu:mpla", 0 ) |
| 217 | ROM_LOAD( "tms0970_comp4_mpla.pla", 0, 860, CRC(ee9d7d9e) SHA1(25484e18f6a07f7cdb21a07220e2f2a82fadfe7b) ) |
| 218 | ROM_REGION( 352, "maincpu:opla", 0 ) |
| 219 | ROM_LOAD( "tms0970_comp4_opla.pla", 0, 352, CRC(a0f887d1) SHA1(3c666663d484d5bed81e1014f8715aab8a3d489f) ) |
| 220 | ROM_REGION( 157, "maincpu:spla", 0 ) |
| 221 | ROM_LOAD( "tms0970_comp4_spla.pla", 0, 157, CRC(e5bddd90) SHA1(4b1c6512c70e5bcd23c2dbf0c88cd8aa2c632a10) ) |
| 222 | ROM_END |
| 223 | |
| 224 | |
| 225 | CONS( 1977, comp4, 0, 0, comp4, comp4, driver_device, 0, "Milton Bradley", "Comp IV", GAME_SUPPORTS_SAVE | GAME_NO_SOUND_HW ) |
trunk/src/mess/drivers/hh_tms1k.c
| r243822 | r243823 | |
| 13 | 13 | #include "cpu/tms0980/tms0980.h" |
| 14 | 14 | #include "sound/speaker.h" |
| 15 | 15 | |
| 16 | | #include "amaztron.lh" |
| 17 | 16 | #include "ebball.lh" |
| 18 | | #include "comp4.lh" |
| 19 | | #include "simon.lh" |
| 20 | | #include "tc4.lh" |
| 21 | 17 | |
| 22 | 18 | |
| 23 | 19 | class hh_tms1k_state : public driver_device |
| r243822 | r243823 | |
| 26 | 22 | hh_tms1k_state(const machine_config &mconfig, device_type type, const char *tag) |
| 27 | 23 | : driver_device(mconfig, type, tag), |
| 28 | 24 | m_maincpu(*this, "maincpu"), |
| 29 | | m_inp_matrix(*this, "IN"), |
| 30 | | m_speaker(*this, "speaker"), |
| 31 | | m_display_maxy(1), |
| 32 | | m_display_maxx(0), |
| 33 | | m_display_wait(50) |
| 25 | // m_button_matrix(*this, "IN"), |
| 26 | m_speaker(*this, "speaker") |
| 34 | 27 | { } |
| 35 | 28 | |
| 36 | 29 | required_device<cpu_device> m_maincpu; |
| 37 | | optional_ioport_array<7> m_inp_matrix; // max 7 |
| 38 | | optional_device<speaker_sound_device> m_speaker; |
| 39 | | |
| 40 | | UINT16 m_r; |
| 41 | | UINT16 m_o; |
| 42 | | UINT16 m_inp_mux; |
| 30 | // required_ioport_array<3> m_button_matrix; |
| 31 | required_device<speaker_sound_device> m_speaker; |
| 43 | 32 | |
| 44 | | int m_display_maxy; |
| 45 | | int m_display_maxx; |
| 46 | | int m_display_wait; |
| 47 | | |
| 48 | | UINT32 m_display_state[0x20]; |
| 49 | | UINT32 m_display_cache[0x20]; |
| 50 | | UINT8 m_display_decay[0x20][0x20]; |
| 51 | | UINT16 m_7seg_mask[0x20]; |
| 52 | | |
| 53 | | TIMER_DEVICE_CALLBACK_MEMBER(display_decay_tick); |
| 54 | | bool index_is_7segled(int index); |
| 55 | | void display_update(); |
| 56 | | |
| 57 | | UINT8 read_inputs(int columns); |
| 58 | | |
| 59 | | // game-specific handlers |
| 60 | | void amaztron_display(); |
| 61 | | DECLARE_READ8_MEMBER(amaztron_read_k); |
| 62 | | DECLARE_WRITE16_MEMBER(amaztron_write_o); |
| 63 | | DECLARE_WRITE16_MEMBER(amaztron_write_r); |
| 64 | | |
| 65 | | void tc4_display(); |
| 66 | | DECLARE_READ8_MEMBER(tc4_read_k); |
| 67 | | DECLARE_WRITE16_MEMBER(tc4_write_o); |
| 68 | | DECLARE_WRITE16_MEMBER(tc4_write_r); |
| 69 | | |
| 70 | | DECLARE_READ8_MEMBER(comp4_read_k); |
| 71 | | DECLARE_WRITE16_MEMBER(comp4_write_o); |
| 72 | | DECLARE_WRITE16_MEMBER(comp4_write_r); |
| 73 | | |
| 74 | | DECLARE_READ8_MEMBER(simon_read_k); |
| 75 | | DECLARE_WRITE16_MEMBER(simon_write_o); |
| 76 | | DECLARE_WRITE16_MEMBER(simon_write_r); |
| 77 | | |
| 78 | 33 | virtual void machine_start(); |
| 79 | 34 | }; |
| 80 | 35 | |
| 81 | 36 | |
| 82 | | void hh_tms1k_state::machine_start() |
| 83 | | { |
| 84 | | // zerofill |
| 85 | | memset(m_display_state, 0, sizeof(m_display_state)); |
| 86 | | memset(m_display_cache, 0, sizeof(m_display_cache)); |
| 87 | | memset(m_display_decay, 0, sizeof(m_display_decay)); |
| 88 | | memset(m_7seg_mask, 0, sizeof(m_7seg_mask)); |
| 89 | | |
| 90 | | m_o = 0; |
| 91 | | m_r = 0; |
| 92 | | m_inp_mux = 0; |
| 93 | | |
| 94 | | // register for savestates |
| 95 | | save_item(NAME(m_display_maxy)); |
| 96 | | save_item(NAME(m_display_maxx)); |
| 97 | | save_item(NAME(m_display_wait)); |
| 98 | | |
| 99 | | save_item(NAME(m_display_state)); |
| 100 | | save_item(NAME(m_display_cache)); |
| 101 | | save_item(NAME(m_display_decay)); |
| 102 | | save_item(NAME(m_7seg_mask)); |
| 103 | | |
| 104 | | save_item(NAME(m_o)); |
| 105 | | save_item(NAME(m_r)); |
| 106 | | save_item(NAME(m_inp_mux)); |
| 107 | | } |
| 108 | | |
| 109 | | |
| 110 | | /*************************************************************************** |
| 111 | | |
| 112 | | Helper Functions |
| 113 | | |
| 114 | | ***************************************************************************/ |
| 115 | | |
| 116 | | |
| 117 | | // The device strobes the outputs very fast, it is unnoticeable to the user. |
| 118 | | // To prevent flickering here, we need to simulate a decay. |
| 119 | | |
| 120 | | |
| 121 | | void hh_tms1k_state::display_update() |
| 122 | | { |
| 123 | | UINT32 active_state[0x20]; |
| 124 | | |
| 125 | | for (int y = 0; y < m_display_maxy; y++) |
| 126 | | { |
| 127 | | active_state[y] = 0; |
| 128 | | |
| 129 | | for (int x = 0; x < m_display_maxx; x++) |
| 130 | | { |
| 131 | | // turn on powered segments |
| 132 | | if (m_display_state[y] >> x & 1) |
| 133 | | m_display_decay[y][x] = m_display_wait; |
| 134 | | |
| 135 | | // determine active state |
| 136 | | int ds = (m_display_decay[y][x] != 0) ? 1 : 0; |
| 137 | | active_state[y] |= (ds << x); |
| 138 | | } |
| 139 | | } |
| 140 | | |
| 141 | | // on difference, send to output |
| 142 | | for (int y = 0; y < m_display_maxy; y++) |
| 143 | | if (m_display_cache[y] != active_state[y]) |
| 144 | | { |
| 145 | | if (m_7seg_mask[y] != 0) |
| 146 | | output_set_digit_value(y, active_state[y] & m_7seg_mask[y]); |
| 147 | | |
| 148 | | const int mul = (m_display_maxx <= 10) ? 10 : 100; |
| 149 | | for (int x = 0; x < m_display_maxx; x++) |
| 150 | | output_set_lamp_value(y * mul + x, active_state[y] >> x & 1); |
| 151 | | } |
| 152 | | |
| 153 | | memcpy(m_display_cache, active_state, sizeof(m_display_cache)); |
| 154 | | } |
| 155 | | |
| 156 | | TIMER_DEVICE_CALLBACK_MEMBER(hh_tms1k_state::display_decay_tick) |
| 157 | | { |
| 158 | | // slowly turn off unpowered segments |
| 159 | | for (int y = 0; y < m_display_maxy; y++) |
| 160 | | for (int x = 0; x < m_display_maxx; x++) |
| 161 | | if (!(m_display_state[y] >> x & 1) && m_display_decay[y][x] != 0) |
| 162 | | m_display_decay[y][x]--; |
| 163 | | |
| 164 | | display_update(); |
| 165 | | } |
| 166 | | |
| 167 | | |
| 168 | | UINT8 hh_tms1k_state::read_inputs(int columns) |
| 169 | | { |
| 170 | | UINT8 k = 0; |
| 171 | | |
| 172 | | // read selected input rows |
| 173 | | for (int i = 0; i < columns; i++) |
| 174 | | if (m_inp_mux >> i & 1) |
| 175 | | k |= m_inp_matrix[i]->read(); |
| 176 | | |
| 177 | | return k; |
| 178 | | } |
| 179 | | |
| 180 | | |
| 181 | | |
| 182 | | /*************************************************************************** |
| 183 | | |
| 184 | | Minidrivers (I/O, Inputs, Machine Config) |
| 185 | | |
| 186 | | ***************************************************************************/ |
| 187 | | |
| 188 | | /*************************************************************************** |
| 189 | | |
| 190 | | Coleco Amaze-A-Tron, by Ralph Baer |
| 191 | | * TMS1100 MCU, labeled MP3405(die label too) |
| 192 | | |
| 193 | | This is an electronic board game with a selection of 8 maze games, |
| 194 | | most of them for 2 players. A 5x5 playing grid and four markers are |
| 195 | | required to play. Refer to the official manual for more information. |
| 196 | | |
| 197 | | ***************************************************************************/ |
| 198 | | |
| 199 | | |
| 200 | | void hh_tms1k_state::amaztron_display() |
| 201 | | { |
| 202 | | m_display_maxy = 3; |
| 203 | | m_display_maxx = 8; |
| 204 | | |
| 205 | | // R8,R9: select digit |
| 206 | | for (int y = 0; y < 2; y++) |
| 207 | | { |
| 208 | | m_7seg_mask[y] = 0x7f; |
| 209 | | m_display_state[y] = (m_r >> (y + 8) & 1) ? m_o : 0; |
| 210 | | } |
| 211 | | |
| 212 | | // R6,R7: lamps -> lamp20, lamp21 |
| 213 | | m_display_state[2] = m_r >> 6 & 3; |
| 214 | | |
| 215 | | display_update(); |
| 216 | | } |
| 217 | | |
| 218 | | READ8_MEMBER(hh_tms1k_state::amaztron_read_k) |
| 219 | | { |
| 220 | | UINT8 k = read_inputs(6); |
| 221 | | |
| 222 | | // the 5th column is tied to K4+K8 |
| 223 | | if (k & 0x10) k |= 0xc; |
| 224 | | return k & 0xf; |
| 225 | | } |
| 226 | | |
| 227 | | WRITE16_MEMBER(hh_tms1k_state::amaztron_write_r) |
| 228 | | { |
| 229 | | // R0-R5: input mux |
| 230 | | m_inp_mux = data & 0x3f; |
| 231 | | |
| 232 | | // R10: speaker out |
| 233 | | m_speaker->level_w(data >> 10 & 1); |
| 234 | | |
| 235 | | // other bits: |
| 236 | | m_r = data; |
| 237 | | amaztron_display(); |
| 238 | | } |
| 239 | | |
| 240 | | WRITE16_MEMBER(hh_tms1k_state::amaztron_write_o) |
| 241 | | { |
| 242 | | // O0-O6: digit segments |
| 243 | | // O7: N/C |
| 244 | | m_o = data & 0x7f; |
| 245 | | amaztron_display(); |
| 246 | | } |
| 247 | | |
| 248 | | |
| 249 | | static INPUT_PORTS_START( amaztron ) |
| 250 | | PORT_START("IN.0") // R0 |
| 251 | | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Button 1") |
| 252 | | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Button 6") |
| 253 | | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Button 11") |
| 254 | | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Button 16") |
| 255 | | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Button 21") |
| 256 | | |
| 257 | | PORT_START("IN.1") // R1 |
| 258 | | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Button 2") |
| 259 | | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Button 7") |
| 260 | | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Button 12") |
| 261 | | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Button 17") |
| 262 | | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Button 22") |
| 263 | | |
| 264 | | PORT_START("IN.2") // R2 |
| 265 | | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Button 3") |
| 266 | | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Button 8") |
| 267 | | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Button 13") |
| 268 | | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Button 18") |
| 269 | | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Button 23") |
| 270 | | |
| 271 | | PORT_START("IN.3") // R3 |
| 272 | | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Button 4") |
| 273 | | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_NAME("Button 9") |
| 274 | | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Button 14") |
| 275 | | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Button 19") |
| 276 | | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Button 24") |
| 277 | | |
| 278 | | PORT_START("IN.4") // R4 |
| 279 | | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Button 5") |
| 280 | | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_NAME("Button 10") |
| 281 | | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Button 15") |
| 282 | | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Button 20") |
| 283 | | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Button 25") |
| 284 | | |
| 285 | | PORT_START("IN.5") // R5 |
| 286 | | PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Game Select") |
| 287 | | PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("Game Start") |
| 288 | | PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) |
| 289 | | PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) |
| 290 | | PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) |
| 37 | static INPUT_PORTS_START( ebball ) |
| 291 | 38 | INPUT_PORTS_END |
| 292 | 39 | |
| 293 | 40 | |
| 294 | | static MACHINE_CONFIG_START( amaztron, hh_tms1k_state ) |
| 295 | 41 | |
| 296 | | /* basic machine hardware */ |
| 297 | | MCFG_CPU_ADD("maincpu", TMS1100, 350000) // RC osc. R=33K?, C=100pf -> ~350kHz |
| 298 | | MCFG_TMS1XXX_READ_K_CB(READ8(hh_tms1k_state, amaztron_read_k)) |
| 299 | | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(hh_tms1k_state, amaztron_write_o)) |
| 300 | | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(hh_tms1k_state, amaztron_write_r)) |
| 301 | | |
| 302 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 303 | | |
| 304 | | MCFG_DEFAULT_LAYOUT(layout_amaztron) |
| 305 | | |
| 306 | | /* no video! */ |
| 307 | | |
| 308 | | /* sound hardware */ |
| 309 | | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 310 | | MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) |
| 311 | | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) |
| 312 | | MACHINE_CONFIG_END |
| 313 | | |
| 314 | | |
| 315 | | |
| 316 | | |
| 317 | 42 | /*************************************************************************** |
| 318 | 43 | |
| 319 | | Coleco Total Control 4 |
| 320 | | * TMS1400NLL MP7334-N2 (die labeled MP7334) |
| 44 | Machine Config |
| 321 | 45 | |
| 322 | | This is a head to head electronic tabletop LED-display sports console. |
| 323 | | One cartridge(Football) was included with the console, the other three were |
| 324 | | sold separately. Gameplay has emphasis on strategy, read the official manual |
| 325 | | on how to play. Remember that you can rotate the view in MESS: rotate left |
| 326 | | for Home(P1) orientation, rotate right for Visitor(P2) orientation. |
| 327 | | |
| 328 | | Cartridge socket: |
| 329 | | 1 N/C |
| 330 | | 2 9V+ |
| 331 | | 3 power switch |
| 332 | | 4 K8 |
| 333 | | 5 K4 |
| 334 | | 6 K2 |
| 335 | | 7 K1 |
| 336 | | 8 R9 |
| 337 | | |
| 338 | | The cartridge connects pin 8 with one of the K-pins. |
| 339 | | |
| 340 | | Available cartridges: |
| 341 | | - Football (K8, confirmed) |
| 342 | | - Hockey (K4?) |
| 343 | | - Soccer (K2?) |
| 344 | | - Basketball (K1?) |
| 345 | | |
| 346 | | |
| 347 | | TODO: |
| 348 | | - pin configuration of other carts is guessed |
| 349 | | - softlist for the cartridges? |
| 350 | | - offsensive players leds are supposed to look brighter |
| 351 | | - MCU clock is unknown |
| 352 | | |
| 353 | 46 | ***************************************************************************/ |
| 354 | 47 | |
| 355 | | void hh_tms1k_state::tc4_display() |
| 48 | void hh_tms1k_state::machine_start() |
| 356 | 49 | { |
| 357 | | m_display_maxy = 10; |
| 358 | | m_display_maxx = 9; |
| 359 | | |
| 360 | | // R5,7,8,9 are 7segs |
| 361 | | for (int y = 0; y < m_display_maxy; y++) |
| 362 | | if (y >= 5 && y <= 9 && y != 6) |
| 363 | | m_7seg_mask[y] = 0x7f; |
| 364 | | |
| 365 | | // update current state (note: R6 as extra column!) |
| 366 | | for (int y = 0; y < m_display_maxy; y++) |
| 367 | | m_display_state[y] = (m_r >> y & 1) ? (m_o | (m_r << 2 & 0x100)) : 0; |
| 368 | | |
| 369 | | display_update(); |
| 370 | 50 | } |
| 371 | 51 | |
| 372 | | READ8_MEMBER(hh_tms1k_state::tc4_read_k) |
| 373 | | { |
| 374 | | UINT8 k = read_inputs(6); |
| 375 | 52 | |
| 376 | | // read from cartridge |
| 377 | | if (m_inp_mux & 0x200) |
| 378 | | k |= m_inp_matrix[6]->read(); |
| 379 | | |
| 380 | | return k; |
| 381 | | } |
| 382 | | |
| 383 | | WRITE16_MEMBER(hh_tms1k_state::tc4_write_o) |
| 384 | | { |
| 385 | | // O0-O7: leds/7segment |
| 386 | | m_o = data; |
| 387 | | tc4_display(); |
| 388 | | } |
| 389 | | |
| 390 | | WRITE16_MEMBER(hh_tms1k_state::tc4_write_r) |
| 391 | | { |
| 392 | | // R10: speaker out |
| 393 | | m_speaker->level_w(data >> 10 & 1); |
| 394 | | |
| 395 | | // R0-R5: input mux |
| 396 | | // R9: to cartridge slot |
| 397 | | m_inp_mux = data & 0x23f; |
| 398 | | |
| 399 | | // R6: led column 8 |
| 400 | | // +other: select leds |
| 401 | | m_r = data; |
| 402 | | tc4_display(); |
| 403 | | } |
| 404 | | |
| 405 | | |
| 406 | | static INPUT_PORTS_START( tc4 ) |
| 407 | | PORT_START("IN.0") // R0 |
| 408 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Pass/Shoot Button 3") // right |
| 409 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Pass/Shoot Button 1") // left |
| 410 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Pass/Shoot Button 2") // middle |
| 411 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 D/K Button") |
| 412 | | |
| 413 | | PORT_START("IN.1") // R1 |
| 414 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) |
| 415 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) |
| 416 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) |
| 417 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) |
| 418 | | |
| 419 | | PORT_START("IN.2") // R2 |
| 420 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) |
| 421 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) |
| 422 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) |
| 423 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) |
| 424 | | |
| 425 | | PORT_START("IN.3") // R3 |
| 426 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) PORT_PLAYER(2) |
| 427 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) PORT_PLAYER(2) |
| 428 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_PLAYER(2) |
| 429 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_PLAYER(2) |
| 430 | | |
| 431 | | PORT_START("IN.4") // R4 |
| 432 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_PLAYER(2) |
| 433 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_PLAYER(2) |
| 434 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_PLAYER(2) |
| 435 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_PLAYER(2) |
| 436 | | |
| 437 | | PORT_START("IN.5") // R5 |
| 438 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Pass/Shoot Button 3") // right |
| 439 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Pass/Shoot Button 1") // left |
| 440 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Pass/Shoot Button 2") // middle |
| 441 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 D/K Button") |
| 442 | | |
| 443 | | PORT_START("IN.6") // R9 |
| 444 | | PORT_CONFNAME( 0x0f, 0x08, "Cartridge") |
| 445 | | PORT_CONFSETTING( 0x01, "Basketball" ) |
| 446 | | PORT_CONFSETTING( 0x02, "Soccer" ) |
| 447 | | PORT_CONFSETTING( 0x04, "Hockey" ) |
| 448 | | PORT_CONFSETTING( 0x08, "Football" ) |
| 449 | | INPUT_PORTS_END |
| 450 | | |
| 451 | | static MACHINE_CONFIG_START( tc4, hh_tms1k_state ) |
| 452 | | |
| 453 | | /* basic machine hardware */ |
| 454 | | MCFG_CPU_ADD("maincpu", TMS1400, 450000) // approximation - RC osc. R=27.3K, C=100pf, but unknown RC curve |
| 455 | | MCFG_TMS1XXX_READ_K_CB(READ8(hh_tms1k_state, tc4_read_k)) |
| 456 | | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(hh_tms1k_state, tc4_write_o)) |
| 457 | | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(hh_tms1k_state, tc4_write_r)) |
| 458 | | |
| 459 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 460 | | |
| 461 | | MCFG_DEFAULT_LAYOUT(layout_tc4) |
| 462 | | |
| 463 | | /* no video! */ |
| 464 | | |
| 465 | | /* sound hardware */ |
| 466 | | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 467 | | MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) |
| 468 | | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) |
| 469 | | MACHINE_CONFIG_END |
| 470 | | |
| 471 | | |
| 472 | | |
| 473 | | /*************************************************************************** |
| 474 | | |
| 475 | | Milton Bradley Comp IV |
| 476 | | * TMC0904NL CP0904A (die labeled 4A0970D-04A) |
| 477 | | |
| 478 | | This is small tabletop Mastermind game; a code-breaking game where the player |
| 479 | | needs to find out the correct sequence of colours (numbers in our case). |
| 480 | | It is known as Logic 5 in Europe, and as Pythaligoras in Japan. |
| 481 | | |
| 482 | | Press the R key to start, followed by a set of unique numbers and E. |
| 483 | | Refer to the official manual for more information. |
| 484 | | |
| 485 | | |
| 486 | | TODO: |
| 487 | | - MCU clock is unknown |
| 488 | | |
| 489 | | ***************************************************************************/ |
| 490 | | |
| 491 | | READ8_MEMBER(hh_tms1k_state::comp4_read_k) |
| 492 | | { |
| 493 | | return read_inputs(3); |
| 494 | | } |
| 495 | | |
| 496 | | WRITE16_MEMBER(hh_tms1k_state::comp4_write_r) |
| 497 | | { |
| 498 | | // leds: |
| 499 | | // R4 R9 |
| 500 | | // R10! R8 |
| 501 | | // R2 R7 |
| 502 | | // R1 R6 |
| 503 | | // R0 R5 |
| 504 | | m_display_maxx = 11; |
| 505 | | m_display_state[0] = data; |
| 506 | | display_update(); |
| 507 | | } |
| 508 | | |
| 509 | | WRITE16_MEMBER(hh_tms1k_state::comp4_write_o) |
| 510 | | { |
| 511 | | // O0: leds common (always writes 1) |
| 512 | | // O1-O3: input mux |
| 513 | | // other bits: N/C |
| 514 | | m_inp_mux = data >> 1 & 7; |
| 515 | | } |
| 516 | | |
| 517 | | |
| 518 | | static INPUT_PORTS_START( comp4 ) |
| 519 | | PORT_START("IN.0") // O1 |
| 520 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("R") |
| 521 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1") |
| 522 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4") |
| 523 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7") |
| 524 | | |
| 525 | | PORT_START("IN.1") // O2 |
| 526 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0") |
| 527 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2") |
| 528 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5") |
| 529 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8") |
| 530 | | |
| 531 | | PORT_START("IN.2") // O3 |
| 532 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("E") |
| 533 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3") |
| 534 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6") |
| 535 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9") |
| 536 | | INPUT_PORTS_END |
| 537 | | |
| 538 | | |
| 539 | | |
| 540 | | static MACHINE_CONFIG_START( comp4, hh_tms1k_state ) |
| 541 | | |
| 542 | | /* basic machine hardware */ |
| 543 | | MCFG_CPU_ADD("maincpu", TMS0970, 250000) // approximation - unknown freq |
| 544 | | MCFG_TMS1XXX_READ_K_CB(READ8(hh_tms1k_state, comp4_read_k)) |
| 545 | | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(hh_tms1k_state, comp4_write_o)) |
| 546 | | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(hh_tms1k_state, comp4_write_r)) |
| 547 | | |
| 548 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 549 | | |
| 550 | | MCFG_DEFAULT_LAYOUT(layout_comp4) |
| 551 | | |
| 552 | | /* no video! */ |
| 553 | | |
| 554 | | /* no sound! */ |
| 555 | | MACHINE_CONFIG_END |
| 556 | | |
| 557 | | |
| 558 | | /*************************************************************************** |
| 559 | | |
| 560 | | Milton Bradley Simon, created by Ralph Baer |
| 561 | | |
| 562 | | Revision A hardware: |
| 563 | | * TMS1000 (has internal ROM), DS75494 lamp driver |
| 564 | | |
| 565 | | Newer revisions have a smaller 16-pin MB4850 chip instead of the TMS1000. |
| 566 | | This one has been decapped too, but we couldn't find an internal ROM. |
| 567 | | It is possibly a cost-reduced custom ASIC specifically for Simon. |
| 568 | | |
| 569 | | Other games assumed to be on similar hardware: |
| 570 | | - Pocket Simon, but there's a chance it only exists with MB4850 chip |
| 571 | | - Super Simon (TMS1100) |
| 572 | | |
| 573 | | ***************************************************************************/ |
| 574 | | |
| 575 | | |
| 576 | | |
| 577 | | |
| 578 | | |
| 579 | | /*************************************************************************** |
| 580 | | |
| 581 | | I/O |
| 582 | | |
| 583 | | ***************************************************************************/ |
| 584 | | |
| 585 | | READ8_MEMBER(hh_tms1k_state::simon_read_k) |
| 586 | | { |
| 587 | | return read_inputs(4); |
| 588 | | } |
| 589 | | |
| 590 | | WRITE16_MEMBER(hh_tms1k_state::simon_write_r) |
| 591 | | { |
| 592 | | // R4-R8 go through an 75494 IC first: |
| 593 | | // R4 -> 75494 IN6 -> green lamp |
| 594 | | // R5 -> 75494 IN3 -> red lamp |
| 595 | | // R6 -> 75494 IN5 -> yellow lamp |
| 596 | | // R7 -> 75494 IN2 -> blue lamp |
| 597 | | m_display_maxx = 4; |
| 598 | | m_display_state[0] = data >> 4 & 0xf; |
| 599 | | display_update(); |
| 600 | | |
| 601 | | // R8 -> 75494 IN0 -> speaker |
| 602 | | m_speaker->level_w(data >> 8 & 1); |
| 603 | | |
| 604 | | // R0,R1,R2,R9: input mux |
| 605 | | // R3: GND |
| 606 | | // other bits: N/C |
| 607 | | m_inp_mux = (data & 7) | (data >> 6 & 8); |
| 608 | | } |
| 609 | | |
| 610 | | WRITE16_MEMBER(hh_tms1k_state::simon_write_o) |
| 611 | | { |
| 612 | | // N/C |
| 613 | | } |
| 614 | | |
| 615 | | |
| 616 | | static INPUT_PORTS_START( simon ) |
| 617 | | PORT_START("IN.0") // R0 |
| 618 | | PORT_CONFNAME( 0x07, 0x02, "Game Select") |
| 619 | | PORT_CONFSETTING( 0x02, "1" ) |
| 620 | | PORT_CONFSETTING( 0x01, "2" ) |
| 621 | | PORT_CONFSETTING( 0x04, "3" ) |
| 622 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) |
| 623 | | |
| 624 | | PORT_START("IN.1") // R1 |
| 625 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Green Button") |
| 626 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Red Button") |
| 627 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Yellow Button") |
| 628 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("Blue Button") |
| 629 | | |
| 630 | | PORT_START("IN.2") // R2 |
| 631 | | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Start") |
| 632 | | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Last") |
| 633 | | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Longest") |
| 634 | | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) |
| 635 | | |
| 636 | | PORT_START("IN.3") // R9 |
| 637 | | PORT_CONFNAME( 0x0f, 0x01, "Skill Level") |
| 638 | | PORT_CONFSETTING( 0x02, "1" ) |
| 639 | | PORT_CONFSETTING( 0x04, "2" ) |
| 640 | | PORT_CONFSETTING( 0x08, "3" ) |
| 641 | | PORT_CONFSETTING( 0x01, "4" ) |
| 642 | | INPUT_PORTS_END |
| 643 | | |
| 644 | | |
| 645 | | static MACHINE_CONFIG_START( simon, hh_tms1k_state ) |
| 646 | | |
| 647 | | /* basic machine hardware */ |
| 648 | | MCFG_CPU_ADD("maincpu", TMS1000, 350000) // RC osc. R=33K, C=100pf -> ~350kHz |
| 649 | | MCFG_TMS1XXX_READ_K_CB(READ8(hh_tms1k_state, simon_read_k)) |
| 650 | | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(hh_tms1k_state, simon_write_o)) |
| 651 | | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(hh_tms1k_state, simon_write_r)) |
| 652 | | |
| 653 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 654 | | |
| 655 | | MCFG_DEFAULT_LAYOUT(layout_simon) |
| 656 | | |
| 657 | | /* no video! */ |
| 658 | | |
| 659 | | /* sound hardware */ |
| 660 | | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 661 | | MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) |
| 662 | | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) |
| 663 | | MACHINE_CONFIG_END |
| 664 | | |
| 665 | | |
| 666 | | |
| 667 | | |
| 668 | | |
| 669 | | /*************************************************************************** |
| 670 | | |
| 671 | | Entex Baseball |
| 672 | | * TMS1000NLP MP0914 (die labeled MP0914A) |
| 673 | | |
| 674 | | ***************************************************************************/ |
| 675 | | |
| 676 | | // inputs |
| 677 | | static INPUT_PORTS_START( ebball ) |
| 678 | | INPUT_PORTS_END |
| 679 | | |
| 680 | | // machine config |
| 681 | 53 | static MACHINE_CONFIG_START( ebball, hh_tms1k_state ) |
| 682 | 54 | |
| 683 | 55 | /* basic machine hardware */ |
| r243822 | r243823 | |
| 701 | 73 | |
| 702 | 74 | ***************************************************************************/ |
| 703 | 75 | |
| 704 | | |
| 705 | | ROM_START( amaztron ) |
| 706 | | ROM_REGION( 0x0800, "maincpu", 0 ) |
| 707 | | ROM_LOAD( "tms1100nll_mp3405", 0x0000, 0x0800, CRC(9cbc0009) SHA1(17772681271b59280687492f37fa0859998f041d) ) |
| 708 | | |
| 709 | | ROM_REGION( 867, "maincpu:mpla", 0 ) |
| 710 | | ROM_LOAD( "tms1100_amaztron_mpla.pla", 0, 867, CRC(03574895) SHA1(04407cabfb3adee2ee5e4218612cb06c12c540f4) ) |
| 711 | | ROM_REGION( 365, "maincpu:opla", 0 ) |
| 712 | | ROM_LOAD( "tms1100_amaztron_opla.pla", 0, 365, CRC(f3875384) SHA1(3c256a3db4f0aa9d93cf78124db39f4cbdc57e4a) ) |
| 713 | | ROM_END |
| 714 | | |
| 715 | | |
| 716 | | |
| 717 | | |
| 718 | | ROM_START( tc4 ) |
| 719 | | ROM_REGION( 0x1000, "maincpu", 0 ) |
| 720 | | ROM_LOAD( "tms1400nll_mp7334", 0x0000, 0x1000, CRC(923f3821) SHA1(a9ae342d7ff8dae1dedcd1e4984bcfae68586581) ) |
| 721 | | |
| 722 | | ROM_REGION( 867, "maincpu:mpla", 0 ) |
| 723 | | ROM_LOAD( "tms1100_default_mpla.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) ) |
| 724 | | ROM_REGION( 557, "maincpu:opla", 0 ) |
| 725 | | ROM_LOAD( "tms1400_tc4_opla.pla", 0, 557, CRC(3b908725) SHA1(f83bf5faa5b3cb51f87adc1639b00d6f9a71ad19) ) |
| 726 | | ROM_END |
| 727 | | |
| 728 | | |
| 729 | 76 | ROM_START( ebball ) |
| 730 | 77 | ROM_REGION( 0x0400, "maincpu", 0 ) |
| 731 | 78 | ROM_LOAD( "mp0914", 0x0000, 0x0400, CRC(3c6fb05b) SHA1(b2fe4b3ca72d6b4c9bfa84d67f64afdc215e7178) ) |
| r243822 | r243823 | |
| 737 | 84 | ROM_END |
| 738 | 85 | |
| 739 | 86 | |
| 740 | | ROM_START( comp4 ) |
| 741 | | ROM_REGION( 0x0400, "maincpu", 0 ) |
| 742 | | ROM_LOAD( "tmc0904nl_cp0904a", 0x0000, 0x0400, CRC(6233ee1b) SHA1(738e109b38c97804b4ec52bed80b00a8634ad453) ) |
| 743 | | |
| 744 | | ROM_REGION( 782, "maincpu:ipla", 0 ) |
| 745 | | ROM_LOAD( "tms0970_default_ipla.pla", 0, 782, CRC(e038fc44) SHA1(dfc280f6d0a5828d1bb14fcd59ac29caf2c2d981) ) |
| 746 | | ROM_REGION( 860, "maincpu:mpla", 0 ) |
| 747 | | ROM_LOAD( "tms0970_comp4_mpla.pla", 0, 860, CRC(ee9d7d9e) SHA1(25484e18f6a07f7cdb21a07220e2f2a82fadfe7b) ) |
| 748 | | ROM_REGION( 352, "maincpu:opla", 0 ) |
| 749 | | ROM_LOAD( "tms0970_comp4_opla.pla", 0, 352, CRC(a0f887d1) SHA1(3c666663d484d5bed81e1014f8715aab8a3d489f) ) |
| 750 | | ROM_REGION( 157, "maincpu:spla", 0 ) |
| 751 | | ROM_LOAD( "tms0970_comp4_spla.pla", 0, 157, CRC(e5bddd90) SHA1(4b1c6512c70e5bcd23c2dbf0c88cd8aa2c632a10) ) |
| 752 | | ROM_END |
| 753 | | |
| 754 | | |
| 755 | | ROM_START( simon ) |
| 756 | | ROM_REGION( 0x0400, "maincpu", 0 ) |
| 757 | | ROM_LOAD( "tms1000.u1", 0x0000, 0x0400, CRC(9961719d) SHA1(35dddb018a8a2b31f377ab49c1f0cb76951b81c0) ) |
| 758 | | |
| 759 | | ROM_REGION( 867, "maincpu:mpla", 0 ) |
| 760 | | ROM_LOAD( "tms1000_simon_mpla.pla", 0, 867, CRC(52f7c1f1) SHA1(dbc2634dcb98eac173ad0209df487cad413d08a5) ) |
| 761 | | ROM_REGION( 365, "maincpu:opla", 0 ) |
| 762 | | ROM_LOAD( "tms1000_simon_opla.pla", 0, 365, CRC(2943c71b) SHA1(bd5bb55c57e7ba27e49c645937ec1d4e67506601) ) |
| 763 | | ROM_END |
| 764 | | |
| 765 | | |
| 766 | | |
| 767 | | |
| 768 | | CONS( 1979, amaztron, 0, 0, amaztron, amaztron, driver_device, 0, "Coleco", "Amaze-A-Tron", GAME_SUPPORTS_SAVE ) |
| 769 | | CONS( 1981, tc4, 0, 0, tc4, tc4, driver_device, 0, "Coleco", "Total Control 4", GAME_SUPPORTS_SAVE ) |
| 770 | | |
| 771 | 87 | CONS( 1979, ebball, 0, 0, ebball, ebball, driver_device, 0, "Entex", "Baseball (Entex)", GAME_SUPPORTS_SAVE | GAME_NOT_WORKING ) |
| 772 | | |
| 773 | | CONS( 1977, comp4, 0, 0, comp4, comp4, driver_device, 0, "Milton Bradley", "Comp IV", GAME_SUPPORTS_SAVE | GAME_NO_SOUND_HW ) |
| 774 | | CONS( 1978, simon, 0, 0, simon, simon, driver_device, 0, "Milton Bradley", "Simon (Rev. A)", GAME_SUPPORTS_SAVE ) |
trunk/src/mess/drivers/simon.c
| r0 | r243823 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:hap |
| 3 | /*************************************************************************** |
| 4 | |
| 5 | Milton Bradley Simon, created by Ralph Baer |
| 6 | |
| 7 | Revision A hardware: |
| 8 | * TMS1000 (has internal ROM), DS75494 lamp driver |
| 9 | |
| 10 | Newer revisions have a smaller 16-pin MB4850 chip instead of the TMS1000. |
| 11 | This one has been decapped too, but we couldn't find an internal ROM. |
| 12 | It is possibly a cost-reduced custom ASIC specifically for Simon. |
| 13 | |
| 14 | Other games assumed to be on similar hardware: |
| 15 | - Pocket Simon, but there's a chance it only exists with MB4850 chip |
| 16 | - Super Simon (TMS1100) |
| 17 | |
| 18 | ***************************************************************************/ |
| 19 | |
| 20 | #include "emu.h" |
| 21 | #include "cpu/tms0980/tms0980.h" |
| 22 | #include "sound/speaker.h" |
| 23 | |
| 24 | #include "simon.lh" // clickable |
| 25 | |
| 26 | // master clock is a single stage RC oscillator: R=33K, C=100pf, |
| 27 | // according to the TMS 1000 series data manual this is around 350kHz |
| 28 | #define MASTER_CLOCK (350000) |
| 29 | |
| 30 | |
| 31 | class simon_state : public driver_device |
| 32 | { |
| 33 | public: |
| 34 | simon_state(const machine_config &mconfig, device_type type, const char *tag) |
| 35 | : driver_device(mconfig, type, tag), |
| 36 | m_maincpu(*this, "maincpu"), |
| 37 | m_button_matrix(*this, "IN"), |
| 38 | m_speaker(*this, "speaker") |
| 39 | { } |
| 40 | |
| 41 | required_device<cpu_device> m_maincpu; |
| 42 | required_ioport_array<4> m_button_matrix; |
| 43 | required_device<speaker_sound_device> m_speaker; |
| 44 | |
| 45 | UINT16 m_r; |
| 46 | |
| 47 | DECLARE_READ8_MEMBER(read_k); |
| 48 | DECLARE_WRITE16_MEMBER(write_o); |
| 49 | DECLARE_WRITE16_MEMBER(write_r); |
| 50 | |
| 51 | virtual void machine_start(); |
| 52 | }; |
| 53 | |
| 54 | |
| 55 | /*************************************************************************** |
| 56 | |
| 57 | I/O |
| 58 | |
| 59 | ***************************************************************************/ |
| 60 | |
| 61 | READ8_MEMBER(simon_state::read_k) |
| 62 | { |
| 63 | UINT8 k = 0; |
| 64 | |
| 65 | // read selected button rows |
| 66 | for (int i = 0; i < 4; i++) |
| 67 | { |
| 68 | const int ki[4] = { 0, 1, 2, 9 }; |
| 69 | if (m_r >> ki[i] & 1) |
| 70 | k |= m_button_matrix[i]->read(); |
| 71 | } |
| 72 | |
| 73 | return k; |
| 74 | } |
| 75 | |
| 76 | WRITE16_MEMBER(simon_state::write_r) |
| 77 | { |
| 78 | // R4-R8 go through an 75494 IC first: |
| 79 | // R4 -> 75494 IN6 -> green lamp |
| 80 | // R5 -> 75494 IN3 -> red lamp |
| 81 | // R6 -> 75494 IN5 -> yellow lamp |
| 82 | // R7 -> 75494 IN2 -> blue lamp |
| 83 | for (int i = 0; i < 4; i++) |
| 84 | output_set_lamp_value(i, data >> (4 + i) & 1); |
| 85 | |
| 86 | // R8 -> 75494 IN0 -> speaker |
| 87 | m_speaker->level_w(data >> 8 & 1); |
| 88 | |
| 89 | // R0,R1,R2,R9: input mux |
| 90 | // R3: GND |
| 91 | // other bits: N/C |
| 92 | m_r = data; |
| 93 | } |
| 94 | |
| 95 | WRITE16_MEMBER(simon_state::write_o) |
| 96 | { |
| 97 | // N/C |
| 98 | } |
| 99 | |
| 100 | |
| 101 | |
| 102 | /*************************************************************************** |
| 103 | |
| 104 | Inputs |
| 105 | |
| 106 | ***************************************************************************/ |
| 107 | |
| 108 | static INPUT_PORTS_START( simon ) |
| 109 | PORT_START("IN.0") // R0 |
| 110 | PORT_CONFNAME( 0x07, 0x02, "Game Select") |
| 111 | PORT_CONFSETTING( 0x02, "1" ) |
| 112 | PORT_CONFSETTING( 0x01, "2" ) |
| 113 | PORT_CONFSETTING( 0x04, "3" ) |
| 114 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) |
| 115 | |
| 116 | PORT_START("IN.1") // R1 |
| 117 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Green Button") |
| 118 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Red Button") |
| 119 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Yellow Button") |
| 120 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("Blue Button") |
| 121 | |
| 122 | PORT_START("IN.2") // R2 |
| 123 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Start") |
| 124 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Last") |
| 125 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Longest") |
| 126 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) |
| 127 | |
| 128 | PORT_START("IN.3") // R9 |
| 129 | PORT_CONFNAME( 0x0f, 0x01, "Skill Level") |
| 130 | PORT_CONFSETTING( 0x02, "1" ) |
| 131 | PORT_CONFSETTING( 0x04, "2" ) |
| 132 | PORT_CONFSETTING( 0x08, "3" ) |
| 133 | PORT_CONFSETTING( 0x01, "4" ) |
| 134 | INPUT_PORTS_END |
| 135 | |
| 136 | |
| 137 | |
| 138 | /*************************************************************************** |
| 139 | |
| 140 | Machine Config |
| 141 | |
| 142 | ***************************************************************************/ |
| 143 | |
| 144 | void simon_state::machine_start() |
| 145 | { |
| 146 | m_r = 0; |
| 147 | save_item(NAME(m_r)); |
| 148 | } |
| 149 | |
| 150 | |
| 151 | static MACHINE_CONFIG_START( simon, simon_state ) |
| 152 | |
| 153 | /* basic machine hardware */ |
| 154 | MCFG_CPU_ADD("maincpu", TMS1000, MASTER_CLOCK) |
| 155 | MCFG_TMS1XXX_READ_K_CB(READ8(simon_state, read_k)) |
| 156 | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(simon_state, write_o)) |
| 157 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(simon_state, write_r)) |
| 158 | |
| 159 | MCFG_DEFAULT_LAYOUT(layout_simon) |
| 160 | |
| 161 | /* no video! */ |
| 162 | |
| 163 | /* sound hardware */ |
| 164 | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 165 | MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) |
| 166 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) |
| 167 | MACHINE_CONFIG_END |
| 168 | |
| 169 | |
| 170 | |
| 171 | /*************************************************************************** |
| 172 | |
| 173 | Game driver(s) |
| 174 | |
| 175 | ***************************************************************************/ |
| 176 | |
| 177 | ROM_START( simon ) |
| 178 | ROM_REGION( 0x0400, "maincpu", 0 ) |
| 179 | ROM_LOAD( "tms1000.u1", 0x0000, 0x0400, CRC(9961719d) SHA1(35dddb018a8a2b31f377ab49c1f0cb76951b81c0) ) |
| 180 | |
| 181 | ROM_REGION( 867, "maincpu:mpla", 0 ) |
| 182 | ROM_LOAD( "tms1000_simon_mpla.pla", 0, 867, CRC(52f7c1f1) SHA1(dbc2634dcb98eac173ad0209df487cad413d08a5) ) |
| 183 | ROM_REGION( 365, "maincpu:opla", 0 ) |
| 184 | ROM_LOAD( "tms1000_simon_opla.pla", 0, 365, CRC(2943c71b) SHA1(bd5bb55c57e7ba27e49c645937ec1d4e67506601) ) |
| 185 | ROM_END |
| 186 | |
| 187 | |
| 188 | CONS( 1978, simon, 0, 0, simon, simon, driver_device, 0, "Milton Bradley", "Simon (Rev. A)", GAME_SUPPORTS_SAVE ) |
trunk/src/mess/drivers/tc4.c
| r0 | r243823 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:hap |
| 3 | /*************************************************************************** |
| 4 | |
| 5 | Coleco Total Control 4 |
| 6 | * TMS1400NLL MP7334-N2 (die labeled MP7334) |
| 7 | |
| 8 | This is a head to head electronic tabletop LED-display sports console. |
| 9 | One cartridge(Football) was included with the console, the other three were |
| 10 | sold separately. Gameplay has emphasis on strategy, read the official manual |
| 11 | on how to play. Remember that you can rotate the view in MESS: rotate left |
| 12 | for Home(P1) orientation, rotate right for Visitor(P2) orientation. |
| 13 | |
| 14 | Cartridge socket: |
| 15 | 1 N/C |
| 16 | 2 9V+ |
| 17 | 3 power switch |
| 18 | 4 K8 |
| 19 | 5 K4 |
| 20 | 6 K2 |
| 21 | 7 K1 |
| 22 | 8 R9 |
| 23 | |
| 24 | The cartridge connects pin 8 with one of the K-pins. |
| 25 | |
| 26 | Available cartridges: |
| 27 | - Football (K8, confirmed) |
| 28 | - Hockey (K4?) |
| 29 | - Soccer (K2?) |
| 30 | - Basketball (K1?) |
| 31 | |
| 32 | |
| 33 | TODO: |
| 34 | - pin configuration of other carts is guessed |
| 35 | - softlist for the cartridges? |
| 36 | - offsensive players leds are supposed to look brighter |
| 37 | - MCU clock is unknown |
| 38 | |
| 39 | ***************************************************************************/ |
| 40 | |
| 41 | #include "emu.h" |
| 42 | #include "cpu/tms0980/tms0980.h" |
| 43 | #include "sound/speaker.h" |
| 44 | |
| 45 | #include "tc4.lh" |
| 46 | |
| 47 | // The master clock is a single stage RC oscillator: R=27.3K, C=100pf. |
| 48 | // TMS1400 RC curve is unknown, so let's do an approximation until it is. |
| 49 | #define MASTER_CLOCK (450000) |
| 50 | |
| 51 | |
| 52 | class tc4_state : public driver_device |
| 53 | { |
| 54 | public: |
| 55 | tc4_state(const machine_config &mconfig, device_type type, const char *tag) |
| 56 | : driver_device(mconfig, type, tag), |
| 57 | m_maincpu(*this, "maincpu"), |
| 58 | m_button_matrix(*this, "IN"), |
| 59 | m_speaker(*this, "speaker") |
| 60 | { } |
| 61 | |
| 62 | required_device<cpu_device> m_maincpu; |
| 63 | required_ioport_array<7> m_button_matrix; |
| 64 | required_device<speaker_sound_device> m_speaker; |
| 65 | |
| 66 | UINT16 m_r; |
| 67 | UINT16 m_o; |
| 68 | |
| 69 | UINT16 m_display_state[0x10]; |
| 70 | UINT16 m_display_cache[0x10]; |
| 71 | UINT8 m_display_decay[0x100]; |
| 72 | |
| 73 | DECLARE_READ8_MEMBER(read_k); |
| 74 | DECLARE_WRITE16_MEMBER(write_o); |
| 75 | DECLARE_WRITE16_MEMBER(write_r); |
| 76 | |
| 77 | TIMER_DEVICE_CALLBACK_MEMBER(display_decay_tick); |
| 78 | bool index_is_7segled(int index); |
| 79 | void display_update(); |
| 80 | |
| 81 | virtual void machine_start(); |
| 82 | }; |
| 83 | |
| 84 | |
| 85 | |
| 86 | /*************************************************************************** |
| 87 | |
| 88 | LED Display |
| 89 | |
| 90 | ***************************************************************************/ |
| 91 | |
| 92 | // The device strobes the outputs very fast, it is unnoticeable to the user. |
| 93 | // To prevent flickering here, we need to simulate a decay. |
| 94 | |
| 95 | // decay time, in steps of 1ms |
| 96 | #define DISPLAY_DECAY_TIME 50 |
| 97 | |
| 98 | inline bool tc4_state::index_is_7segled(int index) |
| 99 | { |
| 100 | // R5,7,8,9 are 7segs |
| 101 | return (index >= 5 && index <= 9 && index != 6); |
| 102 | } |
| 103 | |
| 104 | void tc4_state::display_update() |
| 105 | { |
| 106 | UINT16 active_state[0x10]; |
| 107 | |
| 108 | for (int i = 0; i < 0x10; i++) |
| 109 | { |
| 110 | // update current state (note: R6 as extra column!) |
| 111 | m_display_state[i] = (m_r >> i & 1) ? (m_o | (m_r << 2 & 0x100)) : 0; |
| 112 | |
| 113 | active_state[i] = 0; |
| 114 | |
| 115 | for (int j = 0; j < 0x10; j++) |
| 116 | { |
| 117 | int di = j << 4 | i; |
| 118 | |
| 119 | // turn on powered segments |
| 120 | if (m_display_state[i] >> j & 1) |
| 121 | m_display_decay[di] = DISPLAY_DECAY_TIME; |
| 122 | |
| 123 | // determine active state |
| 124 | int ds = (m_display_decay[di] != 0) ? 1 : 0; |
| 125 | active_state[i] |= (ds << j); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | // on difference, send to output |
| 130 | for (int i = 0; i < 0x10; i++) |
| 131 | if (m_display_cache[i] != active_state[i]) |
| 132 | { |
| 133 | if (index_is_7segled(i)) |
| 134 | output_set_digit_value(i, active_state[i] & 0x7f); |
| 135 | |
| 136 | for (int j = 0; j < 9; j++) |
| 137 | output_set_lamp_value(i*10 + j, active_state[i] >> j & 1); |
| 138 | } |
| 139 | |
| 140 | memcpy(m_display_cache, active_state, sizeof(m_display_cache)); |
| 141 | } |
| 142 | |
| 143 | TIMER_DEVICE_CALLBACK_MEMBER(tc4_state::display_decay_tick) |
| 144 | { |
| 145 | // slowly turn off unpowered segments |
| 146 | for (int i = 0; i < 0x100; i++) |
| 147 | if (!(m_display_state[i & 0xf] >> (i>>4) & 1) && m_display_decay[i]) |
| 148 | m_display_decay[i]--; |
| 149 | |
| 150 | display_update(); |
| 151 | } |
| 152 | |
| 153 | |
| 154 | |
| 155 | /*************************************************************************** |
| 156 | |
| 157 | I/O |
| 158 | |
| 159 | ***************************************************************************/ |
| 160 | |
| 161 | READ8_MEMBER(tc4_state::read_k) |
| 162 | { |
| 163 | UINT8 k = 0; |
| 164 | |
| 165 | // read selected button rows |
| 166 | for (int i = 0; i < 6; i++) |
| 167 | if (m_r >> i & 1) |
| 168 | k |= m_button_matrix[i]->read(); |
| 169 | |
| 170 | // read from cartridge |
| 171 | if (m_r & 0x200) |
| 172 | k |= m_button_matrix[6]->read(); |
| 173 | |
| 174 | return k; |
| 175 | } |
| 176 | |
| 177 | WRITE16_MEMBER(tc4_state::write_o) |
| 178 | { |
| 179 | // O0-O7: leds/7segment |
| 180 | m_o = data; |
| 181 | display_update(); |
| 182 | } |
| 183 | |
| 184 | WRITE16_MEMBER(tc4_state::write_r) |
| 185 | { |
| 186 | // R10: speaker out |
| 187 | m_speaker->level_w(data >> 10 & 1); |
| 188 | |
| 189 | // R0-R5: input mux |
| 190 | // R6: led column 8 |
| 191 | // R9: to cartridge slot |
| 192 | // +other: select leds |
| 193 | m_r = data & 0x3ff; |
| 194 | display_update(); |
| 195 | } |
| 196 | |
| 197 | |
| 198 | |
| 199 | /*************************************************************************** |
| 200 | |
| 201 | Inputs |
| 202 | |
| 203 | ***************************************************************************/ |
| 204 | |
| 205 | static INPUT_PORTS_START( tc4 ) |
| 206 | PORT_START("IN.0") // R0 |
| 207 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Pass/Shoot Button 3") // right |
| 208 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Pass/Shoot Button 1") // left |
| 209 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Pass/Shoot Button 2") // middle |
| 210 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 D/K Button") |
| 211 | |
| 212 | PORT_START("IN.1") // R1 |
| 213 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) |
| 214 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) |
| 215 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) |
| 216 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) |
| 217 | |
| 218 | PORT_START("IN.2") // R2 |
| 219 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) |
| 220 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) |
| 221 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) |
| 222 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) |
| 223 | |
| 224 | PORT_START("IN.3") // R3 |
| 225 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) PORT_PLAYER(2) |
| 226 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) PORT_PLAYER(2) |
| 227 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_PLAYER(2) |
| 228 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_PLAYER(2) |
| 229 | |
| 230 | PORT_START("IN.4") // R4 |
| 231 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_PLAYER(2) |
| 232 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_PLAYER(2) |
| 233 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_PLAYER(2) |
| 234 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_PLAYER(2) |
| 235 | |
| 236 | PORT_START("IN.5") // R5 |
| 237 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Pass/Shoot Button 3") // right |
| 238 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Pass/Shoot Button 1") // left |
| 239 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Pass/Shoot Button 2") // middle |
| 240 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 D/K Button") |
| 241 | |
| 242 | PORT_START("IN.6") // R9 |
| 243 | PORT_CONFNAME( 0x0f, 0x08, "Cartridge") |
| 244 | PORT_CONFSETTING( 0x01, "Basketball" ) |
| 245 | PORT_CONFSETTING( 0x02, "Soccer" ) |
| 246 | PORT_CONFSETTING( 0x04, "Hockey" ) |
| 247 | PORT_CONFSETTING( 0x08, "Football" ) |
| 248 | INPUT_PORTS_END |
| 249 | |
| 250 | |
| 251 | |
| 252 | /*************************************************************************** |
| 253 | |
| 254 | Machine Config |
| 255 | |
| 256 | ***************************************************************************/ |
| 257 | |
| 258 | void tc4_state::machine_start() |
| 259 | { |
| 260 | // zerofill |
| 261 | memset(m_display_state, 0, sizeof(m_display_state)); |
| 262 | memset(m_display_cache, 0, sizeof(m_display_cache)); |
| 263 | memset(m_display_decay, 0, sizeof(m_display_decay)); |
| 264 | |
| 265 | m_r = 0; |
| 266 | m_o = 0; |
| 267 | |
| 268 | // register for savestates |
| 269 | save_item(NAME(m_display_state)); |
| 270 | save_item(NAME(m_display_cache)); |
| 271 | save_item(NAME(m_display_decay)); |
| 272 | |
| 273 | save_item(NAME(m_r)); |
| 274 | save_item(NAME(m_o)); |
| 275 | } |
| 276 | |
| 277 | |
| 278 | static MACHINE_CONFIG_START( tc4, tc4_state ) |
| 279 | |
| 280 | /* basic machine hardware */ |
| 281 | MCFG_CPU_ADD("maincpu", TMS1400, MASTER_CLOCK) |
| 282 | MCFG_TMS1XXX_READ_K_CB(READ8(tc4_state, read_k)) |
| 283 | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(tc4_state, write_o)) |
| 284 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(tc4_state, write_r)) |
| 285 | |
| 286 | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", tc4_state, display_decay_tick, attotime::from_msec(1)) |
| 287 | |
| 288 | MCFG_DEFAULT_LAYOUT(layout_tc4) |
| 289 | |
| 290 | /* no video! */ |
| 291 | |
| 292 | /* sound hardware */ |
| 293 | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 294 | MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) |
| 295 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) |
| 296 | MACHINE_CONFIG_END |
| 297 | |
| 298 | |
| 299 | |
| 300 | /*************************************************************************** |
| 301 | |
| 302 | Game driver(s) |
| 303 | |
| 304 | ***************************************************************************/ |
| 305 | |
| 306 | ROM_START( tc4 ) |
| 307 | ROM_REGION( 0x1000, "maincpu", 0 ) |
| 308 | ROM_LOAD( "tms1400nll_mp7334", 0x0000, 0x1000, CRC(923f3821) SHA1(a9ae342d7ff8dae1dedcd1e4984bcfae68586581) ) |
| 309 | |
| 310 | ROM_REGION( 867, "maincpu:mpla", 0 ) |
| 311 | ROM_LOAD( "tms1100_default_mpla.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) ) |
| 312 | ROM_REGION( 557, "maincpu:opla", 0 ) |
| 313 | ROM_LOAD( "tms1400_tc4_opla.pla", 0, 557, CRC(3b908725) SHA1(f83bf5faa5b3cb51f87adc1639b00d6f9a71ad19) ) |
| 314 | ROM_END |
| 315 | |
| 316 | |
| 317 | CONS( 1981, tc4, 0, 0, tc4, tc4, driver_device, 0, "Coleco", "Total Control 4", GAME_SUPPORTS_SAVE ) |
trunk/src/osd/windows/window.c
| r243822 | r243823 | |
| 120 | 120 | // PROTOTYPES |
| 121 | 121 | //============================================================ |
| 122 | 122 | |
| 123 | | static void winwindow_video_window_destroy(win_window_info *window); |
| 124 | 123 | |
| 125 | | static unsigned __stdcall thread_entry(void *param); |
| 126 | | static int complete_create(win_window_info *window); |
| 127 | 124 | static void create_window_class(void); |
| 128 | | static void set_starting_view(int index, win_window_info *window, const char *view); |
| 129 | 125 | |
| 130 | | static void constrain_to_aspect_ratio(win_window_info *window, RECT *rect, int adjustment); |
| 131 | | static void get_min_bounds(win_window_info *window, RECT *bounds, int constrain); |
| 132 | | static void get_max_bounds(win_window_info *window, RECT *bounds, int constrain); |
| 133 | | static void update_minmax_state(win_window_info *window); |
| 134 | | static void minimize_window(win_window_info *window); |
| 135 | | static void maximize_window(win_window_info *window); |
| 136 | | |
| 137 | | static void adjust_window_position_after_major_change(win_window_info *window); |
| 138 | | static void set_fullscreen(win_window_info *window, int fullscreen); |
| 139 | | |
| 140 | | |
| 141 | 126 | // temporary hacks |
| 142 | 127 | #if LOG_THREADS |
| 143 | 128 | struct mtlog |
| r243822 | r243823 | |
| 213 | 198 | fatalerror("Failed to create window thread ready event\n"); |
| 214 | 199 | |
| 215 | 200 | // create a thread to run the windows from |
| 216 | | temp = _beginthreadex(NULL, 0, thread_entry, NULL, 0, (unsigned *)&window_threadid); |
| 201 | temp = _beginthreadex(NULL, 0, win_window_info::thread_entry, NULL, 0, (unsigned *)&window_threadid); |
| 217 | 202 | window_thread = (HANDLE)temp; |
| 218 | 203 | if (window_thread == NULL) |
| 219 | 204 | fatalerror("Failed to create window thread\n"); |
| r243822 | r243823 | |
| 272 | 257 | { |
| 273 | 258 | win_window_info *temp = win_window_list; |
| 274 | 259 | win_window_list = temp->m_next; |
| 275 | | winwindow_video_window_destroy(temp); |
| 260 | temp->destroy(); |
| 261 | global_free(temp); |
| 262 | |
| 276 | 263 | } |
| 277 | 264 | |
| 278 | 265 | // kill the drawers |
| r243822 | r243823 | |
| 659 | 646 | // (main thread) |
| 660 | 647 | //============================================================ |
| 661 | 648 | |
| 662 | | void winwindow_video_window_create(running_machine &machine, int index, win_monitor_info *monitor, const osd_window_config *config) |
| 649 | void win_window_info::create(running_machine &machine, int index, win_monitor_info *monitor, const osd_window_config *config) |
| 663 | 650 | { |
| 664 | 651 | win_window_info *window, *win; |
| 665 | 652 | |
| r243822 | r243823 | |
| 690 | 677 | |
| 691 | 678 | // set the specific view |
| 692 | 679 | windows_options &options = downcast<windows_options &>(machine.options()); |
| 693 | | set_starting_view(index, window, options.view(index)); |
| 680 | window->set_starting_view(index, options.view(index)); |
| 694 | 681 | |
| 695 | 682 | // remember the current values in case they change |
| 696 | 683 | window->m_targetview = window->m_target->view(); |
| r243822 | r243823 | |
| 720 | 707 | } |
| 721 | 708 | } |
| 722 | 709 | else |
| 723 | | window->m_init_state = complete_create(window) ? -1 : 1; |
| 710 | window->m_init_state = window->complete_create() ? -1 : 1; |
| 724 | 711 | |
| 725 | 712 | // handle error conditions |
| 726 | 713 | if (window->m_init_state == -1) |
| r243822 | r243823 | |
| 734 | 721 | // (main thread) |
| 735 | 722 | //============================================================ |
| 736 | 723 | |
| 737 | | static void winwindow_video_window_destroy(win_window_info *window) |
| 724 | void win_window_info::destroy() |
| 738 | 725 | { |
| 739 | 726 | win_window_info **prevptr; |
| 740 | 727 | |
| r243822 | r243823 | |
| 742 | 729 | |
| 743 | 730 | // remove us from the list |
| 744 | 731 | for (prevptr = &win_window_list; *prevptr != NULL; prevptr = &(*prevptr)->m_next) |
| 745 | | if (*prevptr == window) |
| 732 | if (*prevptr == this) |
| 746 | 733 | { |
| 747 | | *prevptr = window->m_next; |
| 734 | *prevptr = this->m_next; |
| 748 | 735 | break; |
| 749 | 736 | } |
| 750 | 737 | |
| 751 | 738 | // destroy the window |
| 752 | | if (window->m_hwnd != NULL) |
| 753 | | SendMessage(window->m_hwnd, WM_USER_SELF_TERMINATE, 0, 0); |
| 739 | if (m_hwnd != NULL) |
| 740 | SendMessage(m_hwnd, WM_USER_SELF_TERMINATE, 0, 0); |
| 754 | 741 | |
| 755 | 742 | // free the render target |
| 756 | | window->machine().render().target_free(window->m_target); |
| 743 | machine().render().target_free(m_target); |
| 757 | 744 | |
| 745 | // FIXME: move to destructor |
| 758 | 746 | // free the lock |
| 759 | | osd_lock_free(window->m_render_lock); |
| 747 | osd_lock_free(m_render_lock); |
| 760 | 748 | |
| 761 | | // free the window itself |
| 762 | | global_free(window); |
| 763 | 749 | } |
| 764 | 750 | |
| 765 | 751 | |
| r243822 | r243823 | |
| 905 | 891 | // (main thread) |
| 906 | 892 | //============================================================ |
| 907 | 893 | |
| 908 | | static void set_starting_view(int index, win_window_info *window, const char *view) |
| 894 | void win_window_info::set_starting_view(int index, const char *view) |
| 909 | 895 | { |
| 910 | | const char *defview = downcast<windows_options &>(window->machine().options()).view(); |
| 896 | const char *defview = downcast<windows_options &>(machine().options()).view(); |
| 911 | 897 | int viewindex; |
| 912 | 898 | |
| 913 | 899 | assert(GetCurrentThreadId() == main_threadid); |
| r243822 | r243823 | |
| 917 | 903 | view = defview; |
| 918 | 904 | |
| 919 | 905 | // query the video system to help us pick a view |
| 920 | | viewindex = window->m_target->configured_view(view, index, video_config.numscreens); |
| 906 | viewindex = m_target->configured_view(view, index, video_config.numscreens); |
| 921 | 907 | |
| 922 | 908 | // set the view |
| 923 | | window->m_target->set_view(viewindex); |
| 909 | m_target->set_view(viewindex); |
| 924 | 910 | } |
| 925 | 911 | |
| 926 | 912 | |
| r243822 | r243823 | |
| 1037 | 1023 | // (window thread) |
| 1038 | 1024 | //============================================================ |
| 1039 | 1025 | |
| 1040 | | INLINE int wnd_extra_width(win_window_info *window) |
| 1026 | int win_window_info::wnd_extra_width() |
| 1041 | 1027 | { |
| 1042 | 1028 | RECT temprect = { 100, 100, 200, 200 }; |
| 1043 | | if (window->m_fullscreen) |
| 1029 | if (m_fullscreen) |
| 1044 | 1030 | return 0; |
| 1045 | | AdjustWindowRectEx(&temprect, WINDOW_STYLE, window->win_has_menu(), WINDOW_STYLE_EX); |
| 1031 | AdjustWindowRectEx(&temprect, WINDOW_STYLE, win_has_menu(), WINDOW_STYLE_EX); |
| 1046 | 1032 | return rect_width(&temprect) - 100; |
| 1047 | 1033 | } |
| 1048 | 1034 | |
| r243822 | r243823 | |
| 1053 | 1039 | // (window thread) |
| 1054 | 1040 | //============================================================ |
| 1055 | 1041 | |
| 1056 | | INLINE int wnd_extra_height(win_window_info *window) |
| 1042 | int win_window_info::wnd_extra_height() |
| 1057 | 1043 | { |
| 1058 | 1044 | RECT temprect = { 100, 100, 200, 200 }; |
| 1059 | | if (window->m_fullscreen) |
| 1045 | if (m_fullscreen) |
| 1060 | 1046 | return 0; |
| 1061 | | AdjustWindowRectEx(&temprect, WINDOW_STYLE, window->win_has_menu(), WINDOW_STYLE_EX); |
| 1047 | AdjustWindowRectEx(&temprect, WINDOW_STYLE, win_has_menu(), WINDOW_STYLE_EX); |
| 1062 | 1048 | return rect_height(&temprect) - 100; |
| 1063 | 1049 | } |
| 1064 | 1050 | |
| r243822 | r243823 | |
| 1069 | 1055 | // (window thread) |
| 1070 | 1056 | //============================================================ |
| 1071 | 1057 | |
| 1072 | | static unsigned __stdcall thread_entry(void *param) |
| 1058 | unsigned __stdcall win_window_info::thread_entry(void *param) |
| 1073 | 1059 | { |
| 1074 | 1060 | MSG message; |
| 1075 | 1061 | |
| r243822 | r243823 | |
| 1141 | 1127 | case WM_USER_FINISH_CREATE_WINDOW: |
| 1142 | 1128 | { |
| 1143 | 1129 | win_window_info *window = (win_window_info *)message.lParam; |
| 1144 | | window->m_init_state = complete_create(window) ? -1 : 1; |
| 1130 | window->m_init_state = window->complete_create() ? -1 : 1; |
| 1145 | 1131 | dispatch = FALSE; |
| 1146 | 1132 | break; |
| 1147 | 1133 | } |
| r243822 | r243823 | |
| 1165 | 1151 | // (window thread) |
| 1166 | 1152 | //============================================================ |
| 1167 | 1153 | |
| 1168 | | static int complete_create(win_window_info *window) |
| 1154 | int win_window_info::complete_create() |
| 1169 | 1155 | { |
| 1170 | 1156 | RECT monitorbounds, client; |
| 1171 | 1157 | int tempwidth, tempheight; |
| r243822 | r243823 | |
| 1175 | 1161 | assert(GetCurrentThreadId() == window_threadid); |
| 1176 | 1162 | |
| 1177 | 1163 | // get the monitor bounds |
| 1178 | | monitorbounds = window->m_monitor->position_size(); |
| 1164 | monitorbounds = m_monitor->position_size(); |
| 1179 | 1165 | |
| 1180 | 1166 | // create the window menu if needed |
| 1181 | | if (downcast<windows_options &>(window->machine().options()).menu()) |
| 1167 | if (downcast<windows_options &>(machine().options()).menu()) |
| 1182 | 1168 | { |
| 1183 | | if (win_create_menu(window->machine(), &menu)) |
| 1169 | if (win_create_menu(machine(), &menu)) |
| 1184 | 1170 | return 1; |
| 1185 | 1171 | } |
| 1186 | 1172 | |
| 1187 | 1173 | // create the window, but don't show it yet |
| 1188 | | window->m_hwnd = win_create_window_ex_utf8( |
| 1189 | | window->m_fullscreen ? FULLSCREEN_STYLE_EX : WINDOW_STYLE_EX, |
| 1174 | m_hwnd = win_create_window_ex_utf8( |
| 1175 | m_fullscreen ? FULLSCREEN_STYLE_EX : WINDOW_STYLE_EX, |
| 1190 | 1176 | "MAME", |
| 1191 | | window->m_title, |
| 1192 | | window->m_fullscreen ? FULLSCREEN_STYLE : WINDOW_STYLE, |
| 1177 | m_title, |
| 1178 | m_fullscreen ? FULLSCREEN_STYLE : WINDOW_STYLE, |
| 1193 | 1179 | monitorbounds.left + 20, monitorbounds.top + 20, |
| 1194 | 1180 | monitorbounds.left + 100, monitorbounds.top + 100, |
| 1195 | 1181 | NULL,//(win_window_list != NULL) ? win_window_list->m_hwnd : NULL, |
| 1196 | 1182 | menu, |
| 1197 | 1183 | GetModuleHandle(NULL), |
| 1198 | 1184 | NULL); |
| 1199 | | if (window->m_hwnd == NULL) |
| 1185 | if (m_hwnd == NULL) |
| 1200 | 1186 | return 1; |
| 1201 | 1187 | |
| 1202 | 1188 | // set window #0 as the focus window for all windows, required for D3D & multimonitor |
| 1203 | | window->m_focus_hwnd = win_window_list->m_hwnd; |
| 1189 | m_focus_hwnd = win_window_list->m_hwnd; |
| 1204 | 1190 | |
| 1205 | 1191 | // set a pointer back to us |
| 1206 | | SetWindowLongPtr(window->m_hwnd, GWLP_USERDATA, (LONG_PTR)window); |
| 1192 | SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this); |
| 1207 | 1193 | |
| 1208 | 1194 | // skip the positioning stuff for -video none */ |
| 1209 | 1195 | if (video_config.mode == VIDEO_MODE_NONE) |
| 1210 | 1196 | return 0; |
| 1211 | 1197 | |
| 1212 | 1198 | // adjust the window position to the initial width/height |
| 1213 | | tempwidth = (window->m_win_config.width != 0) ? window->m_win_config.width : 640; |
| 1214 | | tempheight = (window->m_win_config.height != 0) ? window->m_win_config.height : 480; |
| 1215 | | SetWindowPos(window->m_hwnd, NULL, monitorbounds.left + 20, monitorbounds.top + 20, |
| 1216 | | monitorbounds.left + tempwidth + wnd_extra_width(window), |
| 1217 | | monitorbounds.top + tempheight + wnd_extra_height(window), |
| 1199 | tempwidth = (m_win_config.width != 0) ? m_win_config.width : 640; |
| 1200 | tempheight = (m_win_config.height != 0) ? m_win_config.height : 480; |
| 1201 | SetWindowPos(m_hwnd, NULL, monitorbounds.left + 20, monitorbounds.top + 20, |
| 1202 | monitorbounds.left + tempwidth + wnd_extra_width(), |
| 1203 | monitorbounds.top + tempheight + wnd_extra_height(), |
| 1218 | 1204 | SWP_NOZORDER); |
| 1219 | 1205 | |
| 1220 | 1206 | // maximum or minimize as appropriate |
| 1221 | | if (window->m_startmaximized) |
| 1222 | | maximize_window(window); |
| 1207 | if (m_startmaximized) |
| 1208 | maximize_window(); |
| 1223 | 1209 | else |
| 1224 | | minimize_window(window); |
| 1225 | | adjust_window_position_after_major_change(window); |
| 1210 | minimize_window(); |
| 1211 | adjust_window_position_after_major_change(); |
| 1226 | 1212 | |
| 1227 | 1213 | // show the window |
| 1228 | | if (!window->m_fullscreen || window->m_fullscreen_safe) |
| 1214 | if (!m_fullscreen || m_fullscreen_safe) |
| 1229 | 1215 | { |
| 1230 | 1216 | // finish off by trying to initialize DirectX; if we fail, ignore it |
| 1231 | | window->m_renderer = draw.create(window); |
| 1232 | | if (window->m_renderer->create()) |
| 1217 | m_renderer = draw.create(this); |
| 1218 | if (m_renderer->create()) |
| 1233 | 1219 | return 1; |
| 1234 | | ShowWindow(window->m_hwnd, SW_SHOW); |
| 1220 | ShowWindow(m_hwnd, SW_SHOW); |
| 1235 | 1221 | } |
| 1236 | 1222 | |
| 1237 | 1223 | // clear the window |
| 1238 | | dc = GetDC(window->m_hwnd); |
| 1239 | | GetClientRect(window->m_hwnd, &client); |
| 1224 | dc = GetDC(m_hwnd); |
| 1225 | GetClientRect(m_hwnd, &client); |
| 1240 | 1226 | FillRect(dc, &client, (HBRUSH)GetStockObject(BLACK_BRUSH)); |
| 1241 | | ReleaseDC(window->m_hwnd, dc); |
| 1227 | ReleaseDC(m_hwnd, dc); |
| 1242 | 1228 | return 0; |
| 1243 | 1229 | } |
| 1244 | 1230 | |
| r243822 | r243823 | |
| 1258 | 1244 | if (window != NULL) |
| 1259 | 1245 | { |
| 1260 | 1246 | assert(GetCurrentThreadId() == window_threadid); |
| 1261 | | update_minmax_state(window); |
| 1247 | window->update_minmax_state(); |
| 1262 | 1248 | } |
| 1263 | 1249 | |
| 1264 | 1250 | // handle a few messages |
| r243822 | r243823 | |
| 1360 | 1346 | { |
| 1361 | 1347 | RECT *rect = (RECT *)lparam; |
| 1362 | 1348 | if (video_config.keepaspect && !(GetAsyncKeyState(VK_CONTROL) & 0x8000)) |
| 1363 | | constrain_to_aspect_ratio(window, rect, wparam); |
| 1349 | window->constrain_to_aspect_ratio(rect, wparam); |
| 1364 | 1350 | InvalidateRect(wnd, NULL, FALSE); |
| 1365 | 1351 | break; |
| 1366 | 1352 | } |
| r243822 | r243823 | |
| 1380 | 1366 | // handle maximize |
| 1381 | 1367 | if (cmd == SC_MAXIMIZE) |
| 1382 | 1368 | { |
| 1383 | | update_minmax_state(window); |
| 1369 | window->update_minmax_state(); |
| 1384 | 1370 | if (window->m_ismaximized) |
| 1385 | | minimize_window(window); |
| 1371 | window->minimize_window(); |
| 1386 | 1372 | else |
| 1387 | | maximize_window(window); |
| 1373 | window->maximize_window(); |
| 1388 | 1374 | break; |
| 1389 | 1375 | } |
| 1390 | 1376 | return DefWindowProc(wnd, message, wparam, lparam); |
| r243822 | r243823 | |
| 1435 | 1421 | |
| 1436 | 1422 | // fullscreen set |
| 1437 | 1423 | case WM_USER_SET_FULLSCREEN: |
| 1438 | | set_fullscreen(window, wparam); |
| 1424 | window->set_fullscreen(wparam); |
| 1439 | 1425 | break; |
| 1440 | 1426 | |
| 1441 | 1427 | // minimum size set |
| 1442 | 1428 | case WM_USER_SET_MINSIZE: |
| 1443 | | minimize_window(window); |
| 1429 | window->minimize_window(); |
| 1444 | 1430 | break; |
| 1445 | 1431 | |
| 1446 | 1432 | // maximum size set |
| 1447 | 1433 | case WM_USER_SET_MAXSIZE: |
| 1448 | | maximize_window(window); |
| 1434 | window->maximize_window(); |
| 1449 | 1435 | break; |
| 1450 | 1436 | |
| 1451 | 1437 | // set focus: if we're not the primary window, switch back |
| r243822 | r243823 | |
| 1514 | 1500 | // (window thread) |
| 1515 | 1501 | //============================================================ |
| 1516 | 1502 | |
| 1517 | | static void constrain_to_aspect_ratio(win_window_info *window, RECT *rect, int adjustment) |
| 1503 | void win_window_info::constrain_to_aspect_ratio(RECT *rect, int adjustment) |
| 1518 | 1504 | { |
| 1519 | | win_monitor_info *monitor = window->winwindow_video_window_monitor(rect); |
| 1520 | | INT32 extrawidth = wnd_extra_width(window); |
| 1521 | | INT32 extraheight = wnd_extra_height(window); |
| 1505 | win_monitor_info *monitor = winwindow_video_window_monitor(rect); |
| 1506 | INT32 extrawidth = wnd_extra_width(); |
| 1507 | INT32 extraheight = wnd_extra_height(); |
| 1522 | 1508 | INT32 propwidth, propheight; |
| 1523 | 1509 | INT32 minwidth, minheight; |
| 1524 | 1510 | INT32 maxwidth, maxheight; |
| r243822 | r243823 | |
| 1541 | 1527 | { |
| 1542 | 1528 | case WMSZ_BOTTOM: |
| 1543 | 1529 | case WMSZ_TOP: |
| 1544 | | window->m_target->compute_visible_area(10000, propheight, pixel_aspect, window->m_target->orientation(), propwidth, propheight); |
| 1530 | m_target->compute_visible_area(10000, propheight, pixel_aspect, m_target->orientation(), propwidth, propheight); |
| 1545 | 1531 | break; |
| 1546 | 1532 | |
| 1547 | 1533 | case WMSZ_LEFT: |
| 1548 | 1534 | case WMSZ_RIGHT: |
| 1549 | | window->m_target->compute_visible_area(propwidth, 10000, pixel_aspect, window->m_target->orientation(), propwidth, propheight); |
| 1535 | m_target->compute_visible_area(propwidth, 10000, pixel_aspect, m_target->orientation(), propwidth, propheight); |
| 1550 | 1536 | break; |
| 1551 | 1537 | |
| 1552 | 1538 | default: |
| 1553 | | window->m_target->compute_visible_area(propwidth, propheight, pixel_aspect, window->m_target->orientation(), propwidth, propheight); |
| 1539 | m_target->compute_visible_area(propwidth, propheight, pixel_aspect, m_target->orientation(), propwidth, propheight); |
| 1554 | 1540 | break; |
| 1555 | 1541 | } |
| 1556 | 1542 | |
| 1557 | 1543 | // get the minimum width/height for the current layout |
| 1558 | | window->m_target->compute_minimum_size(minwidth, minheight); |
| 1544 | m_target->compute_minimum_size(minwidth, minheight); |
| 1559 | 1545 | |
| 1560 | 1546 | // clamp against the absolute minimum |
| 1561 | 1547 | propwidth = MAX(propwidth, MIN_WINDOW_DIM); |
| r243822 | r243823 | |
| 1566 | 1552 | propheight = MAX(propheight, minheight); |
| 1567 | 1553 | |
| 1568 | 1554 | // clamp against the maximum (fit on one screen for full screen mode) |
| 1569 | | if (window->m_fullscreen) |
| 1555 | if (m_fullscreen) |
| 1570 | 1556 | { |
| 1571 | 1557 | maxwidth = rect_width(&monitor->position_size()) - extrawidth; |
| 1572 | 1558 | maxheight = rect_height(&monitor->position_size()) - extraheight; |
| r243822 | r243823 | |
| 1577 | 1563 | maxheight = rect_height(&monitor->usuable_position_size()) - extraheight; |
| 1578 | 1564 | |
| 1579 | 1565 | // further clamp to the maximum width/height in the window |
| 1580 | | if (window->m_win_config.width != 0) |
| 1581 | | maxwidth = MIN(maxwidth, window->m_win_config.width + extrawidth); |
| 1582 | | if (window->m_win_config.height != 0) |
| 1583 | | maxheight = MIN(maxheight, window->m_win_config.height + extraheight); |
| 1566 | if (m_win_config.width != 0) |
| 1567 | maxwidth = MIN(maxwidth, m_win_config.width + extrawidth); |
| 1568 | if (m_win_config.height != 0) |
| 1569 | maxheight = MIN(maxheight, m_win_config.height + extraheight); |
| 1584 | 1570 | } |
| 1585 | 1571 | |
| 1586 | 1572 | // clamp to the maximum |
| r243822 | r243823 | |
| 1588 | 1574 | propheight = MIN(propheight, maxheight); |
| 1589 | 1575 | |
| 1590 | 1576 | // compute the visible area based on the proposed rectangle |
| 1591 | | window->m_target->compute_visible_area(propwidth, propheight, pixel_aspect, window->m_target->orientation(), viswidth, visheight); |
| 1577 | m_target->compute_visible_area(propwidth, propheight, pixel_aspect, m_target->orientation(), viswidth, visheight); |
| 1592 | 1578 | |
| 1593 | 1579 | // compute the adjustments we need to make |
| 1594 | 1580 | adjwidth = (viswidth + extrawidth) - rect_width(rect); |
| r243822 | r243823 | |
| 1630 | 1616 | // (window thread) |
| 1631 | 1617 | //============================================================ |
| 1632 | 1618 | |
| 1633 | | static void get_min_bounds(win_window_info *window, RECT *bounds, int constrain) |
| 1619 | void win_window_info::get_min_bounds(RECT *bounds, int constrain) |
| 1634 | 1620 | { |
| 1635 | 1621 | INT32 minwidth, minheight; |
| 1636 | 1622 | |
| 1637 | 1623 | assert(GetCurrentThreadId() == window_threadid); |
| 1638 | 1624 | |
| 1639 | 1625 | // get the minimum target size |
| 1640 | | window->m_target->compute_minimum_size(minwidth, minheight); |
| 1626 | m_target->compute_minimum_size(minwidth, minheight); |
| 1641 | 1627 | |
| 1642 | 1628 | // expand to our minimum dimensions |
| 1643 | 1629 | if (minwidth < MIN_WINDOW_DIM) |
| r243822 | r243823 | |
| 1646 | 1632 | minheight = MIN_WINDOW_DIM; |
| 1647 | 1633 | |
| 1648 | 1634 | // account for extra window stuff |
| 1649 | | minwidth += wnd_extra_width(window); |
| 1650 | | minheight += wnd_extra_height(window); |
| 1635 | minwidth += wnd_extra_width(); |
| 1636 | minheight += wnd_extra_height(); |
| 1651 | 1637 | |
| 1652 | 1638 | // if we want it constrained, figure out which one is larger |
| 1653 | 1639 | if (constrain) |
| r243822 | r243823 | |
| 1658 | 1644 | test1.top = test1.left = 0; |
| 1659 | 1645 | test1.right = minwidth; |
| 1660 | 1646 | test1.bottom = 10000; |
| 1661 | | constrain_to_aspect_ratio(window, &test1, WMSZ_BOTTOMRIGHT); |
| 1647 | constrain_to_aspect_ratio(&test1, WMSZ_BOTTOMRIGHT); |
| 1662 | 1648 | |
| 1663 | 1649 | // then constrain with no width limit |
| 1664 | 1650 | test2.top = test2.left = 0; |
| 1665 | 1651 | test2.right = 10000; |
| 1666 | 1652 | test2.bottom = minheight; |
| 1667 | | constrain_to_aspect_ratio(window, &test2, WMSZ_BOTTOMRIGHT); |
| 1653 | constrain_to_aspect_ratio(&test2, WMSZ_BOTTOMRIGHT); |
| 1668 | 1654 | |
| 1669 | 1655 | // pick the larger |
| 1670 | 1656 | if (rect_width(&test1) > rect_width(&test2)) |
| r243822 | r243823 | |
| 1680 | 1666 | } |
| 1681 | 1667 | |
| 1682 | 1668 | // get the window rect |
| 1683 | | GetWindowRect(window->m_hwnd, bounds); |
| 1669 | GetWindowRect(m_hwnd, bounds); |
| 1684 | 1670 | |
| 1685 | 1671 | // now adjust |
| 1686 | 1672 | bounds->right = bounds->left + minwidth; |
| r243822 | r243823 | |
| 1694 | 1680 | // (window thread) |
| 1695 | 1681 | //============================================================ |
| 1696 | 1682 | |
| 1697 | | static void get_max_bounds(win_window_info *window, RECT *bounds, int constrain) |
| 1683 | void win_window_info::get_max_bounds(RECT *bounds, int constrain) |
| 1698 | 1684 | { |
| 1699 | 1685 | RECT maximum; |
| 1700 | 1686 | |
| 1701 | 1687 | assert(GetCurrentThreadId() == window_threadid); |
| 1702 | 1688 | |
| 1703 | 1689 | // compute the maximum client area |
| 1704 | | window->m_monitor->refresh(); |
| 1705 | | maximum = window->m_monitor->usuable_position_size(); |
| 1690 | m_monitor->refresh(); |
| 1691 | maximum = m_monitor->usuable_position_size(); |
| 1706 | 1692 | |
| 1707 | 1693 | // clamp to the window's max |
| 1708 | | if (window->m_win_config.width != 0) |
| 1694 | if (m_win_config.width != 0) |
| 1709 | 1695 | { |
| 1710 | | int temp = window->m_win_config.width + wnd_extra_width(window); |
| 1696 | int temp = m_win_config.width + wnd_extra_width(); |
| 1711 | 1697 | if (temp < rect_width(&maximum)) |
| 1712 | 1698 | maximum.right = maximum.left + temp; |
| 1713 | 1699 | } |
| 1714 | | if (window->m_win_config.height != 0) |
| 1700 | if (m_win_config.height != 0) |
| 1715 | 1701 | { |
| 1716 | | int temp = window->m_win_config.height + wnd_extra_height(window); |
| 1702 | int temp = m_win_config.height + wnd_extra_height(); |
| 1717 | 1703 | if (temp < rect_height(&maximum)) |
| 1718 | 1704 | maximum.bottom = maximum.top + temp; |
| 1719 | 1705 | } |
| 1720 | 1706 | |
| 1721 | 1707 | // constrain to fit |
| 1722 | 1708 | if (constrain) |
| 1723 | | constrain_to_aspect_ratio(window, &maximum, WMSZ_BOTTOMRIGHT); |
| 1709 | constrain_to_aspect_ratio(&maximum, WMSZ_BOTTOMRIGHT); |
| 1724 | 1710 | else |
| 1725 | 1711 | { |
| 1726 | | maximum.right -= wnd_extra_width(window); |
| 1727 | | maximum.bottom -= wnd_extra_height(window); |
| 1712 | maximum.right -= wnd_extra_width(); |
| 1713 | maximum.bottom -= wnd_extra_height(); |
| 1728 | 1714 | } |
| 1729 | 1715 | |
| 1730 | 1716 | // center within the work area |
| 1731 | | RECT work = window->m_monitor->usuable_position_size(); |
| 1717 | RECT work = m_monitor->usuable_position_size(); |
| 1732 | 1718 | bounds->left = work.left + (rect_width(&work) - rect_width(&maximum)) / 2; |
| 1733 | 1719 | bounds->top = work.top + (rect_height(&work) - rect_height(&maximum)) / 2; |
| 1734 | 1720 | bounds->right = bounds->left + rect_width(&maximum); |
| r243822 | r243823 | |
| 1742 | 1728 | // (window thread) |
| 1743 | 1729 | //============================================================ |
| 1744 | 1730 | |
| 1745 | | static void update_minmax_state(win_window_info *window) |
| 1731 | void win_window_info::update_minmax_state() |
| 1746 | 1732 | { |
| 1747 | 1733 | assert(GetCurrentThreadId() == window_threadid); |
| 1748 | 1734 | |
| 1749 | | if (!window->m_fullscreen) |
| 1735 | if (!m_fullscreen) |
| 1750 | 1736 | { |
| 1751 | 1737 | RECT bounds, minbounds, maxbounds; |
| 1752 | 1738 | |
| 1753 | 1739 | // compare the maximum bounds versus the current bounds |
| 1754 | | get_min_bounds(window, &minbounds, video_config.keepaspect); |
| 1755 | | get_max_bounds(window, &maxbounds, video_config.keepaspect); |
| 1756 | | GetWindowRect(window->m_hwnd, &bounds); |
| 1740 | get_min_bounds(&minbounds, video_config.keepaspect); |
| 1741 | get_max_bounds(&maxbounds, video_config.keepaspect); |
| 1742 | GetWindowRect(m_hwnd, &bounds); |
| 1757 | 1743 | |
| 1758 | 1744 | // if either the width or height matches, we were maximized |
| 1759 | | window->m_isminimized = (rect_width(&bounds) == rect_width(&minbounds) || |
| 1745 | m_isminimized = (rect_width(&bounds) == rect_width(&minbounds) || |
| 1760 | 1746 | rect_height(&bounds) == rect_height(&minbounds)); |
| 1761 | | window->m_ismaximized = (rect_width(&bounds) == rect_width(&maxbounds) || |
| 1747 | m_ismaximized = (rect_width(&bounds) == rect_width(&maxbounds) || |
| 1762 | 1748 | rect_height(&bounds) == rect_height(&maxbounds)); |
| 1763 | 1749 | } |
| 1764 | 1750 | else |
| 1765 | 1751 | { |
| 1766 | | window->m_isminimized = FALSE; |
| 1767 | | window->m_ismaximized = TRUE; |
| 1752 | m_isminimized = FALSE; |
| 1753 | m_ismaximized = TRUE; |
| 1768 | 1754 | } |
| 1769 | 1755 | } |
| 1770 | 1756 | |
| r243822 | r243823 | |
| 1775 | 1761 | // (window thread) |
| 1776 | 1762 | //============================================================ |
| 1777 | 1763 | |
| 1778 | | static void minimize_window(win_window_info *window) |
| 1764 | void win_window_info::minimize_window() |
| 1779 | 1765 | { |
| 1780 | 1766 | RECT newsize; |
| 1781 | 1767 | |
| 1782 | 1768 | assert(GetCurrentThreadId() == window_threadid); |
| 1783 | 1769 | |
| 1784 | | get_min_bounds(window, &newsize, video_config.keepaspect); |
| 1785 | | SetWindowPos(window->m_hwnd, NULL, newsize.left, newsize.top, rect_width(&newsize), rect_height(&newsize), SWP_NOZORDER); |
| 1770 | get_min_bounds(&newsize, video_config.keepaspect); |
| 1771 | SetWindowPos(m_hwnd, NULL, newsize.left, newsize.top, rect_width(&newsize), rect_height(&newsize), SWP_NOZORDER); |
| 1786 | 1772 | } |
| 1787 | 1773 | |
| 1788 | 1774 | |
| r243822 | r243823 | |
| 1792 | 1778 | // (window thread) |
| 1793 | 1779 | //============================================================ |
| 1794 | 1780 | |
| 1795 | | static void maximize_window(win_window_info *window) |
| 1781 | void win_window_info::maximize_window() |
| 1796 | 1782 | { |
| 1797 | 1783 | RECT newsize; |
| 1798 | 1784 | |
| 1799 | 1785 | assert(GetCurrentThreadId() == window_threadid); |
| 1800 | 1786 | |
| 1801 | | get_max_bounds(window, &newsize, video_config.keepaspect); |
| 1802 | | SetWindowPos(window->m_hwnd, NULL, newsize.left, newsize.top, rect_width(&newsize), rect_height(&newsize), SWP_NOZORDER); |
| 1787 | get_max_bounds(&newsize, video_config.keepaspect); |
| 1788 | SetWindowPos(m_hwnd, NULL, newsize.left, newsize.top, rect_width(&newsize), rect_height(&newsize), SWP_NOZORDER); |
| 1803 | 1789 | } |
| 1804 | 1790 | |
| 1805 | 1791 | |
| r243822 | r243823 | |
| 1809 | 1795 | // (window thread) |
| 1810 | 1796 | //============================================================ |
| 1811 | 1797 | |
| 1812 | | static void adjust_window_position_after_major_change(win_window_info *window) |
| 1798 | void win_window_info::adjust_window_position_after_major_change() |
| 1813 | 1799 | { |
| 1814 | 1800 | RECT oldrect, newrect; |
| 1815 | 1801 | |
| 1816 | 1802 | assert(GetCurrentThreadId() == window_threadid); |
| 1817 | 1803 | |
| 1818 | 1804 | // get the current size |
| 1819 | | GetWindowRect(window->m_hwnd, &oldrect); |
| 1805 | GetWindowRect(m_hwnd, &oldrect); |
| 1820 | 1806 | |
| 1821 | 1807 | // adjust the window size so the client area is what we want |
| 1822 | | if (!window->m_fullscreen) |
| 1808 | if (!m_fullscreen) |
| 1823 | 1809 | { |
| 1824 | 1810 | // constrain the existing size to the aspect ratio |
| 1825 | 1811 | newrect = oldrect; |
| 1826 | 1812 | if (video_config.keepaspect) |
| 1827 | | constrain_to_aspect_ratio(window, &newrect, WMSZ_BOTTOMRIGHT); |
| 1813 | constrain_to_aspect_ratio(&newrect, WMSZ_BOTTOMRIGHT); |
| 1828 | 1814 | } |
| 1829 | 1815 | |
| 1830 | 1816 | // in full screen, make sure it covers the primary display |
| 1831 | 1817 | else |
| 1832 | 1818 | { |
| 1833 | | win_monitor_info *monitor = window->winwindow_video_window_monitor(NULL); |
| 1819 | win_monitor_info *monitor = winwindow_video_window_monitor(NULL); |
| 1834 | 1820 | newrect = monitor->position_size(); |
| 1835 | 1821 | } |
| 1836 | 1822 | |
| 1837 | 1823 | // adjust the position if different |
| 1838 | 1824 | if (oldrect.left != newrect.left || oldrect.top != newrect.top || |
| 1839 | 1825 | oldrect.right != newrect.right || oldrect.bottom != newrect.bottom) |
| 1840 | | SetWindowPos(window->m_hwnd, window->m_fullscreen ? HWND_TOPMOST : HWND_TOP, |
| 1826 | SetWindowPos(m_hwnd, m_fullscreen ? HWND_TOPMOST : HWND_TOP, |
| 1841 | 1827 | newrect.left, newrect.top, |
| 1842 | 1828 | rect_width(&newrect), rect_height(&newrect), 0); |
| 1843 | 1829 | |
| 1844 | 1830 | // take note of physical window size (used for lightgun coordinate calculation) |
| 1845 | | if (window == win_window_list) |
| 1831 | if (this == win_window_list) |
| 1846 | 1832 | { |
| 1847 | 1833 | win_physical_width = rect_width(&newrect); |
| 1848 | 1834 | win_physical_height = rect_height(&newrect); |
| r243822 | r243823 | |
| 1856 | 1842 | // (window thread) |
| 1857 | 1843 | //============================================================ |
| 1858 | 1844 | |
| 1859 | | static void set_fullscreen(win_window_info *window, int fullscreen) |
| 1845 | void win_window_info::set_fullscreen(int fullscreen) |
| 1860 | 1846 | { |
| 1861 | 1847 | assert(GetCurrentThreadId() == window_threadid); |
| 1862 | 1848 | |
| 1863 | 1849 | // if we're in the right state, punt |
| 1864 | | if (window->m_fullscreen == fullscreen) |
| 1850 | if (m_fullscreen == fullscreen) |
| 1865 | 1851 | return; |
| 1866 | | window->m_fullscreen = fullscreen; |
| 1852 | m_fullscreen = fullscreen; |
| 1867 | 1853 | |
| 1868 | 1854 | // kill off the drawers |
| 1869 | | window->m_renderer->destroy(); |
| 1870 | | global_free(window->m_renderer); |
| 1871 | | window->m_renderer = NULL; |
| 1855 | m_renderer->destroy(); |
| 1856 | global_free(m_renderer); |
| 1857 | m_renderer = NULL; |
| 1872 | 1858 | |
| 1873 | 1859 | // hide ourself |
| 1874 | | ShowWindow(window->m_hwnd, SW_HIDE); |
| 1860 | ShowWindow(m_hwnd, SW_HIDE); |
| 1875 | 1861 | |
| 1876 | 1862 | // configure the window if non-fullscreen |
| 1877 | 1863 | if (!fullscreen) |
| 1878 | 1864 | { |
| 1879 | 1865 | // adjust the style |
| 1880 | | SetWindowLong(window->m_hwnd, GWL_STYLE, WINDOW_STYLE); |
| 1881 | | SetWindowLong(window->m_hwnd, GWL_EXSTYLE, WINDOW_STYLE_EX); |
| 1882 | | SetWindowPos(window->m_hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| 1866 | SetWindowLong(m_hwnd, GWL_STYLE, WINDOW_STYLE); |
| 1867 | SetWindowLong(m_hwnd, GWL_EXSTYLE, WINDOW_STYLE_EX); |
| 1868 | SetWindowPos(m_hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| 1883 | 1869 | |
| 1884 | 1870 | // force to the bottom, then back on top |
| 1885 | | SetWindowPos(window->m_hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); |
| 1886 | | SetWindowPos(window->m_hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); |
| 1871 | SetWindowPos(m_hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); |
| 1872 | SetWindowPos(m_hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); |
| 1887 | 1873 | |
| 1888 | 1874 | // if we have previous non-fullscreen bounds, use those |
| 1889 | | if (window->m_non_fullscreen_bounds.right != window->m_non_fullscreen_bounds.left) |
| 1875 | if (m_non_fullscreen_bounds.right != m_non_fullscreen_bounds.left) |
| 1890 | 1876 | { |
| 1891 | | SetWindowPos(window->m_hwnd, HWND_TOP, window->m_non_fullscreen_bounds.left, window->m_non_fullscreen_bounds.top, |
| 1892 | | rect_width(&window->m_non_fullscreen_bounds), rect_height(&window->m_non_fullscreen_bounds), |
| 1877 | SetWindowPos(m_hwnd, HWND_TOP, m_non_fullscreen_bounds.left, m_non_fullscreen_bounds.top, |
| 1878 | rect_width(&m_non_fullscreen_bounds), rect_height(&m_non_fullscreen_bounds), |
| 1893 | 1879 | SWP_NOZORDER); |
| 1894 | 1880 | } |
| 1895 | 1881 | |
| 1896 | 1882 | // otherwise, set a small size and maximize from there |
| 1897 | 1883 | else |
| 1898 | 1884 | { |
| 1899 | | SetWindowPos(window->m_hwnd, HWND_TOP, 0, 0, MIN_WINDOW_DIM, MIN_WINDOW_DIM, SWP_NOZORDER); |
| 1900 | | maximize_window(window); |
| 1885 | SetWindowPos(m_hwnd, HWND_TOP, 0, 0, MIN_WINDOW_DIM, MIN_WINDOW_DIM, SWP_NOZORDER); |
| 1886 | maximize_window(); |
| 1901 | 1887 | } |
| 1902 | 1888 | } |
| 1903 | 1889 | |
| r243822 | r243823 | |
| 1905 | 1891 | else |
| 1906 | 1892 | { |
| 1907 | 1893 | // save the bounds |
| 1908 | | GetWindowRect(window->m_hwnd, &window->m_non_fullscreen_bounds); |
| 1894 | GetWindowRect(m_hwnd, &m_non_fullscreen_bounds); |
| 1909 | 1895 | |
| 1910 | 1896 | // adjust the style |
| 1911 | | SetWindowLong(window->m_hwnd, GWL_STYLE, FULLSCREEN_STYLE); |
| 1912 | | SetWindowLong(window->m_hwnd, GWL_EXSTYLE, FULLSCREEN_STYLE_EX); |
| 1913 | | SetWindowPos(window->m_hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| 1897 | SetWindowLong(m_hwnd, GWL_STYLE, FULLSCREEN_STYLE); |
| 1898 | SetWindowLong(m_hwnd, GWL_EXSTYLE, FULLSCREEN_STYLE_EX); |
| 1899 | SetWindowPos(m_hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| 1914 | 1900 | |
| 1915 | 1901 | // set topmost |
| 1916 | | SetWindowPos(window->m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); |
| 1902 | SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); |
| 1917 | 1903 | } |
| 1918 | 1904 | |
| 1919 | 1905 | // adjust the window to compensate for the change |
| 1920 | | adjust_window_position_after_major_change(window); |
| 1906 | adjust_window_position_after_major_change(); |
| 1921 | 1907 | |
| 1922 | 1908 | // show ourself |
| 1923 | | if (!window->m_fullscreen || window->m_fullscreen_safe) |
| 1909 | if (!m_fullscreen || m_fullscreen_safe) |
| 1924 | 1910 | { |
| 1925 | 1911 | if (video_config.mode != VIDEO_MODE_NONE) |
| 1926 | | ShowWindow(window->m_hwnd, SW_SHOW); |
| 1927 | | window->m_renderer = draw.create(window); |
| 1928 | | if (window->m_renderer->create()) |
| 1912 | ShowWindow(m_hwnd, SW_SHOW); |
| 1913 | m_renderer = draw.create(this); |
| 1914 | if (m_renderer->create()) |
| 1929 | 1915 | exit(1); |
| 1930 | 1916 | } |
| 1931 | 1917 | |
| 1932 | 1918 | // ensure we're still adjusted correctly |
| 1933 | | adjust_window_position_after_major_change(window); |
| 1919 | adjust_window_position_after_major_change(); |
| 1934 | 1920 | } |
| 1935 | 1921 | |
| 1936 | 1922 | #if (USE_QTDEBUG) |