summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/roland_cm32p.cpp
blob: 2c3da094385a6f2253247035fdd44273a690d443 (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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
// license:BSD-3-Clause
// copyright-holders:Valley Bell
/*************************************************************************************************

    Roland CM-32P driver

    Driver by Valley Bell

    Thanks to KitsuWhooa for the PCB layout diagram and part list.


Notes:
- When booting, it does a basic check by writing values 0..0xFF to 0x108A.
  It then expects (value & 0x03) to be read back from 0x1080 and (value) to be read back from 0x1081/0x1082.
  The routine for doing the check begins at 0x4686.
  Succeeding means it ends up at 0x4801.
- When in test mode, the firmware gets stuck in the loop at 0xBB06, waiting for Interrupt #5 (calls 0x4014) to fire.
  It gets there by calling function 0xBBBB, which writes text (address w@0x40, num characters b@0x43) to the LCD screen.
  This can be worked around by setting b@BB2D = 03.
- The firmware gets also stuck in the loop at 0x7D70, waiting for Interrupt #8 (calls 0x4020).
  This might be related to finding the best free voice?
  You can exit the loop by setting b@7D80 = 00.
- Test Mode shows the results of 4 checks:
    1. SRAM check
    2. MIDI IN/OUT check
    3. PCM ROM and card check
    4. RCC-CPU check
  Errors in check 2 and 3 are "not abnormal".
  In order to make check 2 pass successfully, you need to connect MIDI Out with MIDI In, creating a loopback.
  Check 3 requires the SN-U110-04 PCM card ("Electric Grand & Clavi") to be inserted in order to succeed.
- In order to access the built-in PCM ROM (IC18), the CPU asks the sound chip to read offsets 0x000000 .. 0x07FFFF.
  The PCM card is accessed via offsets 0x080000 .. 0x0FFFFF.
  The additional PCM ROMs are mapped to 0x100000 .. 0x17FFFF (IC19) and 0x200000 .. 0x27FFFF according to the sample table in IC18.
- The sound chip has 32 voices. Voice 0 is reserved by the firmware for reading data from the PCM ROM.
  The firmware allocates voices from back to front, i.e. voice 31 first.
- The "PCM sound chip" itself doesn't seem to support panning.
  Maybe it just has 6 outputs (one for each part, like the U-110) and an additional DSP does the rest.


TODO:
- figure out how "freeing a voice" works - right now the firmware gets stuck when playing the 32th note.
- add PCM card support


PCB Layout
----------

PCM BOARD CM-32P ASSY 79554310 00

|   OUTPUT  | PHONES | INPUT (MT-32) | MIDI IN | MIDI THROUGH |  DC 9V |
|----------------------------------------------------------------------|
|  JK2   JK4   JK3   JKLI    JKRI CN4 JK1I         JK1T          JK5   |
|                                                                      |
|                                                                  SW1 |
|                   ||                  IC2    IC1                     |
|    IC35 IC33 IC34 ||  IC31 IC30                              CN0     |
| CN3               ||<--IC32                                          |
|                                       |----|  |----|    |----|       |
|                                       |    |  |    |    |    |       |
|    IC22  ||  IC24                     |IC10|  |IC9 |    |    |       |
|          ||       IC25      IC4       |    |  |    |    |    |       |
|          ||<--IC23                    |SRAM|  |ROM |    |    |       |
|                                       |----|  |----|    |IC3 |   X1  |
| |----|                                                  |    |       |
| | IC |         |------|                                 |    |       |
| | 17 |         | IC16 |                     IC12        |    |       |
| |----|         |------|                 X2              |----|       |
|                                |------|     IC13                     |
|    IC6  |----||----||----|     | IC15 |                              |
| |-||-|  |    ||    ||    |     |------|     IC14      |------|       |
| | || |  |IC20||IC19||IC18|                            | IC8  |       |
| |-||-|  |    ||    ||    |                            |------|       |
| IC7     |SMPL||SMPL||SMPL|   |------------------|                    |
|         |ROM ||ROM ||ROM |   |        CN5       |                    |
|         |----||----||----|   |                  |       CN1          |
|------------------------------|------------------|--------------------|
                               |                  |

Parts:

(All parts THT unless otherwise noted.)
|-----------------------------------------------------------------------|
| JK1I, JK1T                   | DIN 5                                  |
| JK2, JK4, JKRI, JKLI         | 1/4 Mono Jack                          |
| JK3                          | 1/4 Stereo Jack                        |
| JK5                          | DC Barrel Jack (Centre Negative)       |
| SW1                          | SW PCB DPDT                            |
| IC1                          | 74HC04AP                               |
| IC2                          | TLP552                                 |
| IC3                          | P8098 (CPU)                            |
| IC4                          | M51953AL                               |
| IC6, IC7                     | M5M4464AP-12                           |
| IC8                          | Roland R15239111 / M60012-0141FP (QFP) |
| IC9                          | AM27C512-150DC                         |
| IC10                         | TC5564APL-15                           |
| IC12                         | 74HC00AP                               |
| IC13                         | 74HC02AP                               |
| IC14                         | 74HC32AP                               |
| IC15                         | Roland R15229894 / MB87419 (QFP)       |
| IC16                         | Roland R15229895 / MB87420 (QFP)       |
| IC17                         | Roland P15239126 / 23140-007 (QFP)     |
| IC18                         | Roland R15179970 / MB834000A-20 3B1    |
| IC19                         | Roland R15179971 / MB834000A-20 3B2    |
| IC20                         | Roland R15179972 / HN62304BPE98        |
| IC22                         | PCM56P                                 |
| IC23                         | M5238L                                 |
| IC24                         | HD14052BP                              |
| IC25                         | NJM2082D                               |
| IC30, IC31, IC33, IC34, IC35 | NJM4565DD                              |
| IC32                         | M5207L01                               |
| CN0                          | 7805A                                  |
| CN1                          | LED PCB                                |
| CN3                          | Volume Control PCB                     |
| CN4                          | unpopulated                            |
| CN5                          | PCM Card Slot                          |
| X1                           | Crystal 12MHz                          |
| X2                           | Crystal 32.768MHz                      |
|-----------------------------------------------------------------------|


PCM ROM Tables
--------------
Sample Table (address: 0x00100)
------------
Pos Len Description
00  02  start offset, bits 0-15 (Little Endian)
02  01  start offset, bits 16-18 (bits 0-2)
        PCM card select (bit 3): set for sounds on PCM cards
        ROM bank (bits 4-5):
            0 = IC18
            1 = IC19
            2 = IC20
        loop mode (bits 6-7):
            0 = normal loop
            1 = no loop
            2 = ping-pong (forwards, backwards, forwards, backwards, ...)
03  02  last sample (sample length = last sample + 1)
05  02  loop length (loop start = sample length - loop length)
07  01  ??
08  01  reference note (when played back at 32000 Hz)
09  01  ??
-> 0Ah bytes per entry

Tone List (address: 0x01000)
---------
Pos Len Description
00  0A  sample name
0A  01  tone type
            00 - single
            01 - dual
            02 - detune
            03 - velocity mix
            04 - velocity switch
            05..07 - invalid
            08..0F/10..17/.../78..7F - same as 00..07
            80..FF - rhythm?
0B  01  ??
0C  02  ??
0E  01  ??
0F  01  ??
10  0B  some note IDs (padded with FF)
1B  0C  sample IDs (always one more than number of note IDs, padded with FF)
27  09  ??
30  0B  some note IDs (padded with FF)
3B  0C  sample IDs (always one more than number of note IDs, padded with FF)
47  09  ??
-> 50h bytes per entry

Note: Section 30..4F is only used with tone types 01, 03, 04


CM-32P Firmware Work RAM Layout
-------------------------------
2100..22FF - MIDI data receive buffer

2344..23C1 - Part 1..6 "Patch temporary area" (see manual page 21, 0x15 bytes per partial)
23C4..23D4 - "System area" settings (see manual page 22, master volume, reverb setting, channel assignments)
  23CE-23D3 - Part 1..6 MIDI channel
23D6..25B5 - Part 1..6 instrument data (0x50 bytes per partial, from PCM ROM at 0x1000/0x1050/0x10A0/...)
25B8..2B57 - Part 1..6 sample table data (0xF0 bytes per partial, from PCM ROM at 0x0100/0x010A/0x0114/...)

34DC..34E1 - Part 1..6 Modulation (CC #1, initialized with 0)
34E2..34E7 - Part 1..6 Pitch Bend LSB (initialized with 0)
34E8..34ED - Part 1..6 Pitch Bend MSB (initialized with 64)
34EE..34F3 - Part 1..6 Expression setting (CC #11, initialized with 100)
34F4..34F9 - Part 1..6 Sustain setting (CC #64, initialized with 0)
34FA..34FF - Part 1..6 unused (initialized with 0)
3500..3505 - Part 1..6 RPN LSB (CC #98, initialized with 0xFF)
3506..350B - Part 1..6 RPN MSB (CC #99, initialized with 0xFF)
350C..3511 - Part 1..6 NRPN received (initialized with 0xFF, set to 0 when RPN LSB/MSB is received, set to 0xFF when NRPN is received)
3512..3517 - Part 1..6 ?? (initialized with 0)
3518..351D - Part 1..6 Instrument setting
351E - Reverb Mode
351F - Reverb Time
3521 - ?? (initialized with 1)
352A..3889 - voice memory (32 bytes per block)
 35AA..35C9 - some jump table index for interrupt #8
38DE..394D - more voice memory (32 bytes per block)

397E..3??? - state of playing notes
  3986 - current panning volume, left speaker (00..1F)
  39A6 - current panning volume, right speaker (00..1F)
  39C6 - target panning volume, left speaker (00..1F)
  39E6 - target panning volume, right speaker (00..1F)

3D7C..3E84 - SysEx receive data buffer

Some routine locations
----------------------
0x4014  LCD related interrupt handler
0x401C  serial input (MIDI data) interrupt handler
0x4020  some interrupt handler required while playing notes
0x45CB  Initialization (memory clear + checks), external memory is checked from 0x4679 on
0x5024  decide whether or not Test Mode is entered (normal boot == jump to 0x502A)
0x50F5  MIDI handling code
0x65E8  PCM ROM instrument check
0x6650  PCM card instrument check (Note: assumes that the SN-U110-04 PCM card is inserted)
0x6EA4  play a note (parameters: 0040 - part, 0041 = note pitch, 0042 - velocity)
0xB027  load instrument data from PCM ROM (writes to 23D6 + 50*i)
0xB12B  load instrument sample data from PCM ROM (reads sample IDs from 23D6+1B + 50*i, writes to 25B8 + F0*i)
0xB1A0  load secondary instrument sample data from PCM ROM (reads sample IDs from 23D6+3B + 50h*i, writes to 25B8+50 + F0*i)
0xB1E8  load 1 sample table entry from PCM ROM
0xB316  PCM ROM signature check
0xBBBB  write text to LCD

*/

#include "emu.h"
#include "cpu/mcs96/i8x9x.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/rolandpcm.h"
#include "video/msm6222b.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


// unscramble address: ROM dump offset -> proper (descrambled) offset
#define UNSCRAMBLE_ADDRESS(_offset) \
	bitswap<19>(_offset,18,17,15,14,16,12,11, 7, 9,13,10, 8, 3, 2, 1, 6, 4, 5, 0)
// scramble address: proper offset -> ROM dump offset
#define SCRAMBLE_ADDRESS(_offset) \
	bitswap<19>(_offset,18,17,14,16,15, 9,13,12, 8,10, 7,11, 3, 1, 2, 6, 5, 4, 0)

#define UNSCRAMBLE_DATA(_data) \
	bitswap<8>(_data,1,2,7,3,5,0,4,6)


static INPUT_PORTS_START( cm32p )
	PORT_START("A7")
	PORT_BIT(0x03ff, 0x0000, IPT_DIAL) PORT_NAME("Knob") PORT_SENSITIVITY(50) PORT_KEYDELTA(8) PORT_CODE_DEC(KEYCODE_DOWN) PORT_CODE_INC(KEYCODE_UP)

	PORT_START("SERVICE")   // connected to Port 0 of the P8098 CPU.
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test Switch") PORT_TOGGLE PORT_CODE(KEYCODE_F2) // SW A (checked during boot)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: Check/Tune") PORT_CODE(KEYCODE_B) // SW B
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PCM card inserted") PORT_TOGGLE PORT_CODE(KEYCODE_C)

	PORT_START("SW")    // test switches, accessed by reading from address 0x1300
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: MSB Adj.") PORT_CODE(KEYCODE_1)   // SW 1
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: THD Check") PORT_CODE(KEYCODE_2)  // SW 2
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: PCM Out: String 1") PORT_CODE(KEYCODE_3)  // SW 3
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: PCM Out: Sax 1") PORT_CODE(KEYCODE_4) // SW 4
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: PCM + Long Reverb") PORT_CODE(KEYCODE_5)  // SW 5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: PCM + Short Reverb") PORT_CODE(KEYCODE_6) // SW 6
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: VCA Down Check") PORT_CODE(KEYCODE_7) // SW 7
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: VCA Up Check") PORT_CODE(KEYCODE_8)   // SW 8
INPUT_PORTS_END

class cm32p_state : public driver_device
{
public:
	cm32p_state(const machine_config &mconfig, device_type type, const char *tag);

	void cm32p(machine_config &config);

	void init_cm32p();

protected:
	virtual void machine_start() override;
	virtual void machine_reset() override;

private:
	required_device<i8x9x_device> cpu;
	required_device<mb87419_mb87420_device> pcm;
	required_device<msm6222b_device> lcd;
	required_device<timer_device> midi_timer;
	required_device<ram_device> some_ram;
	required_ioport test_sw;
	required_ioport service_port;

	void mt32_palette(palette_device &palette) const;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void midi_w(u16 data);

	u8 lcd_ctrl_r();
	void lcd_ctrl_w(u8 data);
	void lcd_data_w(u8 data);
	u16 port0_r();
	u8 pcmrom_r(offs_t offset);
	u8 dsp_io_r(offs_t offset);
	void dsp_io_w(offs_t offset, u8 data);
	u8 snd_io_r(offs_t offset);
	void snd_io_w(offs_t offset, u8 data);
	u8 test_sw_r();

	TIMER_DEVICE_CALLBACK_MEMBER(midi_timer_cb);
	TIMER_DEVICE_CALLBACK_MEMBER(samples_timer_cb);

	void cm32p_map(address_map &map);

	u8 midi;
	int midi_pos;
	u8 sound_io_buffer[0x100];
	u8 dsp_io_buffer[0x80];
};

cm32p_state::cm32p_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, cpu(*this, "maincpu")
	, pcm(*this, "pcm")
	, lcd(*this, "lcd")
	, midi_timer(*this, "midi_timer")
	, some_ram(*this, "some_ram")
	, test_sw(*this, "SW")
	, service_port(*this, "SERVICE")
{
}


// screen update function from Roland D-110
uint32_t cm32p_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0;
	u8 const *const data = lcd->render();
	bitmap.fill(0);

	for (u8 y = 0; y < 2; y++)
	{
		for (u8 ra = 0; ra < 9; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = 0; x < 16; x++)
			{
				u8 gfx = 0;
				if (ra < 8)
					gfx = data[x*16 + y*640 + ra];

				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
				*p++ = 0;
			}
		}
	}
	return 0;
}

void cm32p_state::machine_start()
{
	u8 *rom = memregion("maincpu")->base();

	// TODO: The IC8 gate array has an "LCD INT" line that needs to be emulated. Then, the hack can be removed.
	// Note: The hack is not necessary when *not* using test mode.
	rom[0xBB2D] = 0x03; // hack to make test mode not freeze when displaying the LCD text

	// TODO: remove this hack
	rom[0x7D80] = 0x00; // hack to exit some loop waiting for interrupt #8
}

void cm32p_state::machine_reset()
{
	midi_timer->adjust(attotime::from_hz(1));
	midi_pos = 0;
}

void cm32p_state::lcd_ctrl_w(u8 data)
{
	lcd->control_w(data);
}

u8 cm32p_state::lcd_ctrl_r()
{
	// The CM-64 service manual lists "D-110 LCD UNIT" for using PCM test mode, so I assume it works like that.
	// However, the CM-32P firmware doesn't seem to ever read the status.
	return lcd->control_r() >> 7;
}

void cm32p_state::lcd_data_w(u8 data)
{
	lcd->data_w(data);
}

void cm32p_state::midi_w(u16 data)
{
	logerror("midi_out %02x\n", data);
	midi = data;
}

TIMER_DEVICE_CALLBACK_MEMBER(cm32p_state::midi_timer_cb)
{
	const static u8 midi_data[3] = { 0x9a, 0x40, 0x7f };
	midi = midi_data[midi_pos++];
	logerror("midi_in %02x\n", midi);
	cpu->serial_w(midi);
	if(midi_pos < sizeof(midi_data))
		midi_timer->adjust(attotime::from_hz(1250));
}

u16 cm32p_state::port0_r()
{
	return service_port->read();
}

u8 cm32p_state::pcmrom_r(offs_t offset)
{
	const u8* pcm_rom = memregion("pcm")->base();
	return pcm_rom[offset];
}

u8 cm32p_state::dsp_io_r(offs_t offset)
{
	return dsp_io_buffer[offset];
}

void cm32p_state::dsp_io_w(offs_t offset, u8 data)
{
	dsp_io_buffer[offset] = data;
	// do read/write to some external memory, makes the RCC-CPU check pass. (routine at 0x4679)
	switch(offset)
	{
	case 0x04:
		// write to partials?? (written in loop at 0x4375)
		break;
	case 0x06:
		{
			u8* ram = some_ram->pointer();
			offs_t ofs = data;
			ram[0x000 | ofs] = dsp_io_buffer[0x00] & 0x03;
			ram[0x100 | ofs] = dsp_io_buffer[0x01];
			ram[0x200 | ofs] = dsp_io_buffer[0x02];
		}
		break;
	case 0x0A:
		{
			const u8* ram = some_ram->pointer();
			offs_t ofs = data;
			dsp_io_buffer[0x00] = ram[0x000 | ofs];
			dsp_io_buffer[0x01] = ram[0x100 | ofs];
			dsp_io_buffer[0x02] = ram[0x200 | ofs];
		}
		break;
	}
}

u8 cm32p_state::snd_io_r(offs_t offset)
{
	// lots of offset modification magic to achieve the following:
	//  - offsets 00..1F are "sound chip read"
	//  - offsets 20..3F are a readback of what was written to registers 00..1F
	//  - This behaviour is reversed for offset 01/21, which is used for reading the PCM sample tables.
	// All this is just for making debugging easier, as it allows one to check the
	// register state using the Memory Viewer.
	if (offset == 0x01 || offset == 0x21)
		offset ^= 0x20; // remove when PCM data readback via sound chip is confirmed to work
	if (offset < 0x20)
		return pcm->read(offset);
	if (offset < 0x40)
		offset -= 0x20;

	if (offset == 0x01)
	{
		// code for reading from the PCM sample table is at 0xB027
		// The code at 0xB0AC writes to 1411/1F (??), then 1403/02 (bank), then 1409/08/0B/0A (address).
		// It waits a few cycles and at 0xB0F7 it reads the resulting data from 1401.
		offs_t bank = sound_io_buffer[0x03];
		offs_t addr = (sound_io_buffer[0x09] << 0) | (sound_io_buffer[0x0A] << 8) | (sound_io_buffer[0x0B] << 16);
		addr = ((addr >> 6) + 2) & 0x3FFFF;
		addr |= (bank << 16);
		// write actual ROM address to 1440..1443 for debugging
		sound_io_buffer[0x43] = (addr >>  0) & 0xFF;
		sound_io_buffer[0x42] = (addr >>  8) & 0xFF;
		sound_io_buffer[0x41] = (addr >> 16) & 0xFF;
		sound_io_buffer[0x40] = (addr >> 24) & 0xFF;
		return pcmrom_r(addr);
	}
	return sound_io_buffer[offset];
}

void cm32p_state::snd_io_w(offs_t offset, u8 data)
{
	// register map
	// ------------
	// Note: 16-bit words are Little Endian, the firmware writes the odd byte is first
	//  00/01 - ??
	//  02/03 - ROM bank (only bits 11-13 are used, bit 11 = PCM card, bits 12-13 select between IC18/19/20)
	//  04/05 - frequency (2.14 fixed point, 0x4000 = 32000 Hz)
	//  06/07 - volume
	//  08/09 - sample start address, fraction (2.14 fixed point, i.e. 1 byte = 0x4000)
	//  0A/0B - sample start address (high word, i.e. address bits 2..17)
	//  0C/0D - sample end address (high word)
	//  0E/0F - sample loop address (high word)
	//  11/13/15/17 - voice enable mask (11 = least significant 8 bits, 17 = most significant 8 bits)
	//  1A - ??
	//  1F - voice select
	if (offset < 0x20)
		pcm->write(offset, data);
	sound_io_buffer[offset] = data;
}

u8 cm32p_state::test_sw_r()
{
	return test_sw->read();
}

TIMER_DEVICE_CALLBACK_MEMBER(cm32p_state::samples_timer_cb)
{
	// TODO: does this trigger something?
}

void cm32p_state::mt32_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0, 0, 0));
	palette.set_pen_color(1, rgb_t(0, 255, 0));
}

void cm32p_state::cm32p_map(address_map &map)
{
	map(0x1080, 0x10ff).rw(FUNC(cm32p_state::dsp_io_r), FUNC(cm32p_state::dsp_io_w));   // DSP area (writes to 1080..82/86/8C/8D)
	map(0x1100, 0x1100).rw(FUNC(cm32p_state::lcd_ctrl_r), FUNC(cm32p_state::lcd_ctrl_w));
	map(0x1102, 0x1102).w(FUNC(cm32p_state::lcd_data_w));
	map(0x1300, 0x1300).r(FUNC(cm32p_state::test_sw_r));    // test switch state
	map(0x1400, 0x14ff).rw(FUNC(cm32p_state::snd_io_r), FUNC(cm32p_state::snd_io_w));   // sound chip area
	map(0x2000, 0x20ff).rom().region("maincpu", 0x2000);    // init vector @ 2080
	map(0x2100, 0x3fff).ram();  // main RAM
	map(0x4000, 0xffff).rom().region("maincpu", 0x4000);
}

void cm32p_state::cm32p(machine_config &config)
{
	i8x9x_device &maincpu(P8098(config, cpu, 12_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &cm32p_state::cm32p_map);
	maincpu.ach7_cb().set_ioport("A7");
	maincpu.serial_tx_cb().set(FUNC(cm32p_state::midi_w));
	maincpu.in_p0_cb().set(FUNC(cm32p_state::port0_r));

	SPEAKER(config, "lspeaker").front_left();
	SPEAKER(config, "rspeaker").front_right();

	MB87419_MB87420(config, pcm, 32.768_MHz_XTAL);
	pcm->int_callback().set_inputline(cpu, i8x9x_device::EXTINT_LINE);
	pcm->add_route(0, "lspeaker", 1.0);
	pcm->add_route(1, "rspeaker", 1.0);

	RAM(config, some_ram).set_default_size("8K");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update(FUNC(cm32p_state::screen_update));
	screen.set_size(16*6-1, (16*6-1)*3/4);
	screen.set_visarea(0, 16*6-2, 0, (16*6-1)*3/4-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(cm32p_state::mt32_palette), 2);

	MSM6222B_01(config, lcd, 0);

	TIMER(config, midi_timer).configure_generic(FUNC(cm32p_state::midi_timer_cb));

	TIMER(config, "samples_timer").configure_periodic(FUNC(cm32p_state::samples_timer_cb), attotime::from_hz(32000*2));
}

void cm32p_state::init_cm32p()
{
	// Roland did a fair amount of scrambling on the address and data lines.
	// Only the first 0x20 bytes of the ROMs are readable text in a raw dump.
	u8* src = static_cast<u8*>(memregion("pcmorg")->base());
	u8* dst = static_cast<u8*>(memregion("pcm")->base());
	for (offs_t bank_ofs = 0x00; bank_ofs < 0x400000; bank_ofs += 0x080000)
	{
		offs_t dstpos;
		for (offs_t srcpos = 0x00; srcpos < 0x80000; srcpos ++)
		{
			dstpos = UNSCRAMBLE_ADDRESS(srcpos);
			dst[bank_ofs + dstpos] = UNSCRAMBLE_DATA(src[bank_ofs + srcpos]);
		}
	}
}

ROM_START( cm32p )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "cm-32_p__1.0.0.am27c512.7d.ic9",  0x000000, 0x10000, CRC(6f2f6dfd) SHA1(689f77c1d56f923ef1dab7d993a124c47736bc56) ) // "CM-32 P // 1 0 0 <filled bubbles in red marker>" sticker on an AM27C512-150DC eprom @ IC9

	ROM_REGION( 0x400000, "pcmorg", 0 ) // ROMs before descrambling
	ROM_LOAD( "roland__r15179970__mb834000a-20__3b1_aa__8917_r00.45f.ic18", 0x000000, 0x80000, CRC(8e53b2a3) SHA1(4872530870d5079776e80e477febe425dc0ec1df) ) // markings under chip footprint are "MB834000A-20P-G-3B1"
	// 0x080000 .. 0x0FFFFF is reserved for the PCM card
	ROM_LOAD( "roland__r15179971__mb834000a-20__3b2_aa__8919_r02.34f.ic19", 0x100000, 0x80000, CRC(c8220761) SHA1(49e55fa672020f95fd9c858ceaae94d6db93df7d) ) // markings under chip footprint are "MB834000A20P-G-3B2" (including the missing dash, which is a typo on the board silkscreen)
	ROM_LOAD( "roland__r15179972__hn62304bpe98__9d1_japan.3f.ic20", 0x200000, 0x80000, CRC(733c4054) SHA1(9b6b59ab74e5bf838702abb087c408aaa85b7b1f) ) // markings under chip footprint are "HN62304BPE98"
	ROM_REGION( 0x400000, "pcm", ROMREGION_ERASEFF )    // ROMs after descrambling
ROM_END

SYST( 1989, cm32p, 0, 0, cm32p, cm32p, cm32p_state, init_cm32p, "Roland", "CM-32P", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )