summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/monty.cpp
blob: 85a5d6bb6503f1d0606f5f7285637a189dc78b3d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// license:BSD-3-Clause
// copyright-holders:Andrew Gardner
/***************************************************************************

    2015-05-08 Skeleton driver for Ritam Monty Plays Scrabble BRAND crossword game

    Scrabble computer that allows you play a game of Scrabble by yourself (or you
    can play with up to 3 players). Has a built-in 12,000 vocabulary, expandable
    to 44,000 by way of 2 expansion modules each containing 16,000 more obscure words.
    You can use the included 'score cards' (which look like little Scrabble boards),
    or you can use a real Scrabble board and tiles to play.  Also note, Monty
    apparently originally came with a little pen.

    This game was later upgraded by Ritam to Master Monty which had 24,000 words
    built-in (expandable to a total of 56,000 with the same 2 expansion modules).
    Two variations on Master Monty have been seen: one looks exactly the same as the
    Monty but the electronics on the inside have been upgraded.  The later version
    is blue and says Master Monty at the top.  Both of these versions are hand-upgraded
    by adding chips and wires to the inside of the game.

    TODO:
    - Need instructions
    - Proper SED1503F emulation (it's simulated in-driver for now)
    - When it wants tiles, put 64 into FD1B (monty), 7D1B (mmonty) and press
      Enter.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "sound/spkrdev.h"
#include "video/sed1520.h"
#include "screen.h"
#include "speaker.h"


class monty_state : public driver_device
{
public:
	monty_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_sed0(*this, "sed1520_0")
		, m_writeUpper(false)
		, m_halt(0)
	{
		for (auto & elem : m_pixels)
			elem = 0xff000000;
	}

	DECLARE_WRITE_LINE_MEMBER(key_pressed);

	void monty(machine_config &config);
	void mmonty(machine_config &config);

protected:
	DECLARE_WRITE8_MEMBER(sound_w);
	DECLARE_WRITE8_MEMBER(ioDisplayWrite_w);
	DECLARE_WRITE8_MEMBER(ioCommandWrite0_w);
	DECLARE_WRITE8_MEMBER(ioCommandWrite1_w);
	DECLARE_WRITE_LINE_MEMBER(halt_changed);

	// screen updates
	uint32_t lcd_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect);
	SED1520_UPDATE_CB(screen_update);

	void mmonty_mem(address_map &map);
	void monty_io(address_map &map);
	void monty_mem(address_map &map);

private:
	required_device<cpu_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_device<sed1520_device> m_sed0;     // TODO: This isn't actually a SED1520, it's a SED1503F
	//required_device<sed1520_device> m_sed1;   // TODO: Also, there are 2 SED1503Fs on the board - one is flipped upside down

	// Test
	uint8_t m_writeUpper;
	uint32_t m_pixels[42*32];
	bool m_sound_sw;
	bool m_dirty;
	int m_halt;
};


void monty_state::monty_mem(address_map &map)
{
	map(0x0000, 0xbfff).rom();
	//AM_RANGE(0x4000, 0x4000) // The main rom checks to see if another program is here on startup
	map(0xf800, 0xffff).ram();
}

void monty_state::mmonty_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	//AM_RANGE(0xc000, 0xc000) // The main rom checks to see if another program is here on startup
	map(0x8000, 0xffff).rom();
	map(0x7800, 0x7fff).ram();
}


void monty_state::monty_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).w(this, FUNC(monty_state::ioCommandWrite0_w));
	map(0x01, 0x01).w(this, FUNC(monty_state::sound_w));
	map(0x02, 0x02).w(this, FUNC(monty_state::ioCommandWrite1_w));
	map(0x80, 0xff).w(this, FUNC(monty_state::ioDisplayWrite_w));

	// 7 reads from a bit shifted IO port
	map(0x01, 0x01).portr("X1");
	map(0x02, 0x02).portr("X2");
	map(0x04, 0x04).portr("X3");
	map(0x08, 0x08).portr("X4");
	map(0x10, 0x10).portr("X5");
	map(0x20, 0x20).portr("X6");
	map(0x40, 0x40).portr("X7");
}


// Input ports
static INPUT_PORTS_START( monty )
	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Left") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, monty_state, key_pressed)
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X7")
	PORT_BIT( 0xFF, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


WRITE8_MEMBER( monty_state::sound_w )
{
	m_sound_sw ^= 1;
	m_speaker->level_w(m_sound_sw);
}


WRITE8_MEMBER( monty_state::ioCommandWrite0_w )
{
	//printf("(%04x) Command Port 0 write : %02x\n", m_maincpu->pc(), data);
	m_writeUpper = false;
}


WRITE8_MEMBER( monty_state::ioCommandWrite1_w )
{
	//if (data == 0xfe)
	//    printf("---\n");

	//printf("(%04x) Command Port 1 write : %02x\n", m_maincpu->pc(), data);
	m_writeUpper = true;
}


WRITE8_MEMBER( monty_state::ioDisplayWrite_w )
{
	m_dirty = true;
	// Offset directly corresponds to sed1503, DD RAM address (offset 0x7f may be special?)
	//printf("(%04x) %02x %02x\n", m_maincpu->pc(), offset, data);

	uint8_t x = offset & 0x3f;
	uint8_t y = (BIT(offset, 6) + (m_writeUpper ? 2 : 0)) << 3;

	// Skip the controller and write straight to the LCD    (pc=134f)
	for (int i = 0; i < 8; i++)
	{
		// Pixel color
		if (x < 42)
			m_pixels[(y*42) + x] = BIT(data, i) ? 0xffffffff : 0xff000000;

		y++;
	}
}


WRITE_LINE_MEMBER( monty_state::halt_changed )
{
	m_halt = state;
}


WRITE_LINE_MEMBER( monty_state::key_pressed )
{
	if (!state && m_halt)
		m_maincpu->set_input_line(INPUT_LINE_RESET, PULSE_LINE);
}


uint32_t monty_state::lcd_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect)
{
	if (!m_dirty)
		return 1;

	uint8_t x,y,z;
	m_dirty = false;
	for (y = 0; y < 32; y++)
	{
		for (z = 0; z < 8; z++)
		{
			for (x = 0; x < 5; x++)
			{
				bitmap.pix32(y, x+z*6) = m_pixels[y*42 + z*5 + x];
			}
			bitmap.pix32(y, 5+z*6) = 0; // space between letters
		}
		bitmap.pix32(y, 48) = m_pixels[y*42 + 40];
		bitmap.pix32(y, 49) = m_pixels[y*42 + 41];
	}

	return 0;
}


SED1520_UPDATE_CB(monty_state::screen_update)
{
	// TODO: Not really a SED1520 - there are two SED1503s
	return 0x00;
}


// TODO: Additional machine definition - Master Monty has a different memory layout
MACHINE_CONFIG_START(monty_state::monty)
	// Basic machine hardware
	MCFG_CPU_ADD("maincpu", Z80, 3580000)       // Ceramic resonator labeled 3.58MT
	MCFG_CPU_PROGRAM_MAP(monty_mem)
	MCFG_CPU_IO_MAP(monty_io)
	MCFG_Z80_SET_HALT_CALLBACK(WRITELINE(monty_state, halt_changed))

	// Video hardware
	MCFG_SCREEN_ADD("screen", LCD)
	MCFG_SCREEN_REFRESH_RATE(50)
	MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) // Not accurate
	MCFG_SCREEN_SIZE(50, 32)    // Two SED1503s (42x16 pixels) control the top and bottom halves
	MCFG_SCREEN_VISIBLE_AREA(0, 50-1, 0, 32-1)
	MCFG_SCREEN_UPDATE_DRIVER(monty_state, lcd_update)

	/* sound hardware */
	MCFG_SPEAKER_STANDARD_MONO("mono")
	MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0)
	MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50)

	// LCD controller interfaces
	MCFG_SED1520_ADD("sed1520_0", UPDATE(monty_state, screen_update))
MACHINE_CONFIG_END

MACHINE_CONFIG_START(monty_state::mmonty)
	monty(config);
	MCFG_CPU_MODIFY( "maincpu" )
	MCFG_CPU_PROGRAM_MAP(mmonty_mem)
MACHINE_CONFIG_END


// ROM definitions
ROM_START( monty )
	ROM_REGION(0xc000, "maincpu", 0)
	ROM_LOAD( "monty_main.bin",    0x0000, 0x4000, CRC(720b4f55) SHA1(0106eb88d3fbbf25a745b9b6ee785ba13689d095) )   // 27128
	ROM_LOAD( "monty_module1.bin", 0x4000, 0x4000, CRC(2725d8c3) SHA1(8273b9779c0915f9c7c43ea4fb460f43ce036358) )   // 27128
	ROM_LOAD( "monty_module2.bin", 0x8000, 0x4000, CRC(db672e47) SHA1(bb14fe86df06cfa4b19625ba417d1a5bc8eae155) )   // 27128
ROM_END

ROM_START( mmonty )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "master_monty_main.bin", 0x0000, 0x8000, CRC(bb5ef4d4) SHA1(ba2c759e429f8740df419f9abb60832eddfba8ab) )   // 27C256
	ROM_LOAD( "monty_module1.bin",     0x8000, 0x4000, CRC(2725d8c3) SHA1(8273b9779c0915f9c7c43ea4fb460f43ce036358) )   // 27128
	ROM_LOAD( "monty_module2.bin",     0xc000, 0x4000, CRC(db672e47) SHA1(bb14fe86df06cfa4b19625ba417d1a5bc8eae155) )   // 27128
ROM_END


// Drivers
//    YEAR  NAME     PARENT  COMPAT   MACHINE    INPUT   STATE         INIT  COMPANY   FULLNAME                 FLAGS
COMP( 1980, monty,   0,      0,       monty,     monty,  monty_state,  0,    "Ritam",  "Monty Plays Scrabble",  MACHINE_NOT_WORKING )
COMP( 1982, mmonty,  0,      0,       mmonty,    monty,  monty_state,  0,    "Ritam",  "Master Monty",          MACHINE_NOT_WORKING )