1 // license:BSD-3-Clause
2 // copyright-holders:David Haywood
3 
4 /*
5 
6  todo: sometimes sprites get left onscreen (xain)
7 
8 */
9 #include "emu.h"
10 #include "cedar_magnet_sprite.h"
11 
12 
13 DEFINE_DEVICE_TYPE(CEDAR_MAGNET_SPRITE, cedar_magnet_sprite_device, "cedmag_sprite", "Cedar Sprite")
14 
15 
cedar_magnet_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)16 cedar_magnet_sprite_device::cedar_magnet_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
17 	: device_t(mconfig, CEDAR_MAGNET_SPRITE, tag, owner, clock)
18 	, cedar_magnet_board_interface(mconfig, *this, "spritecpu", "ram")
19 	, m_sprite_ram_bankdev(*this, "sp_sub_ram")
20 	, m_pio(*this, "z80pio%u", 0U)
21 {
22 }
23 
cedar_magnet_sprite_sub_ram_map(address_map & map)24 void cedar_magnet_sprite_device::cedar_magnet_sprite_sub_ram_map(address_map &map)
25 {
26 // these are 8x SIEMENS HYB 41256-15 AA - 262,144 bit DRAM (32kbytes)
27 // these are on the sprite board memory sub-board
28 	map(0x00000, 0x3ffff).ram().share("ram");
29 }
30 
exzisus_hack_r(offs_t offset)31 u8 cedar_magnet_sprite_device::exzisus_hack_r(offs_t offset)
32 {
33 	//printf("exzisus_hack_r\n");
34 	int pc = m_cpu->pc();
35 
36 	// exzisus has startup code outside of the first 0x400 bytes
37 	// but the main cpu only transfers 0x400 bytes of the code to the other banks?!
38 	if ((pc >= 0x3e0) && (pc <= 0x800))
39 	{
40 		return m_ram[0x400 + offset];
41 	}
42 	else
43 	{
44 		return m_ram[0x400 + offset + (m_pio2_pb_data & 0x3) * 0x10000];
45 	}
46 
47 }
48 
49 
cedar_magnet_sprite_map(address_map & map)50 void cedar_magnet_sprite_device::cedar_magnet_sprite_map(address_map &map)
51 {
52 	map(0x00000, 0x0ffff).m("sp_sub_ram", FUNC(address_map_bank_device::amap8));
53 
54 	map(0x00400, 0x007ff).r(FUNC(cedar_magnet_sprite_device::exzisus_hack_r));
55 }
56 
cedar_magnet_sprite_io(address_map & map)57 void cedar_magnet_sprite_device::cedar_magnet_sprite_io(address_map &map)
58 {
59 	map.global_mask(0xff);
60 
61 	map(0xc0, 0xc3).rw("z80pio0", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
62 	map(0xc4, 0xc7).rw("z80pio1", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
63 	map(0xc8, 0xcb).rw("z80pio2", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
64 
65 	map(0x80, 0x80).w(FUNC(cedar_magnet_sprite_device::sprite_port80_w));
66 	map(0x84, 0x84).w(FUNC(cedar_magnet_sprite_device::sprite_port84_w));
67 
68 	map(0x88, 0x88).w(FUNC(cedar_magnet_sprite_device::sprite_port88_w)); // increasing values // upper address?
69 
70 	map(0x8c, 0x8c).w(FUNC(cedar_magnet_sprite_device::sprite_port8c_w)); // written after 88 (possible data upload?)
71 
72 	map(0x9c, 0x9c).w(FUNC(cedar_magnet_sprite_device::sprite_port9c_w)); // ?
73 
74 }
75 
do_blit()76 void cedar_magnet_sprite_device::do_blit()
77 {
78 //  printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
79 //  printf("~~~~~~~~~~~~~~~~~ drawing sprite with x:%02x y:%02x code:%04x size:%02x unk:%02x\n", m_loweraddr, m_upperaddr, (m_spritecodehigh << 8) | m_spritecodelow, m_spritesize, m_pio0_pb_data);
80 //  printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
81 
82 	int ysize = 0;
83 	int xsize = 0;
84 	int erase = 0;
85 
86 	// bit 0x80 is always set
87 	if ((m_spritesize & 0x7f) == 0x00)
88 		ysize = xsize = 8;
89 
90 	if ((m_spritesize & 0x7f) == 0x01)
91 		ysize = xsize = 16;
92 
93 	if ((m_spritesize & 0x7f) == 0x02)
94 		ysize = xsize = 32;
95 
96 	if ((m_spritesize & 0x7f) == 0x03)
97 		ysize = xsize = 64;
98 
99 	// m_spritesize
100 	// m_pio0_pb_data
101 
102 	int source = (m_spritecodehigh << 8) | m_spritecodelow;
103 
104 	if (source == 0)
105 		erase = 1;
106 
107 	source &= ~0x3f;
108 
109 	for (int y = 0; y < ysize; y++)
110 	{
111 		for (int x = 0; x < xsize; x++)
112 		{
113 			int xpos = (m_loweraddr + x);
114 			int ypos = (m_upperaddr + y);
115 
116 			u8 data = m_ram[source + ((m_uppersprite & 0x3) * 0x10000)];
117 
118 			if (!(m_pio0_pb_data & 0x02))
119 				data = machine().rand();
120 
121 			source++;
122 
123 			xpos &= 0xff;
124 
125 			// without this some sprites incorrectly wraparound on the volcano table.
126 			if (!erase)
127 			{
128 				if (!(m_pio0_pb_data & 0x40))
129 				{
130 					if (xpos >= 0xff-64)
131 						continue;
132 				}
133 				else
134 				{
135 					if (xpos < 64)
136 						continue;
137 				}
138 			}
139 
140 			//if ((ypos >= 0) && (ypos < 0x100))
141 			ypos &= 0xff;
142 
143 			{
144 				int offset = (ypos * 256) + xpos;
145 
146 				if (erase == 1)
147 				{
148 					m_framebuffer[offset] = 0;
149 				}
150 				else
151 				{
152 					if (data) m_framebuffer[offset] = data;
153 				}
154 			}
155 		}
156 	}
157 }
158 
sprite_port80_w(u8 data)159 void cedar_magnet_sprite_device::sprite_port80_w(u8 data)
160 {
161 	m_spritecodelow = data;
162 //  printf("%s:sprite numlow / trigger %02x\n", machine().describe_context().c_str(), data);
163 
164 	do_blit();
165 }
166 
sprite_port84_w(u8 data)167 void cedar_magnet_sprite_device::sprite_port84_w(u8 data)
168 {
169 	m_spritecodehigh = data;
170 	m_high_write = 1;
171 //  printf("%s:sprite numhigh %02x\n", machine().describe_context().c_str(), data);
172 }
173 
sprite_port88_w(u8 data)174 void cedar_magnet_sprite_device::sprite_port88_w(u8 data)
175 {
176 // frequent
177 //  printf("%s:sprite_y_coordinate %02x\n", machine().describe_context().c_str(), data);
178 	m_upperaddr = data;
179 }
180 
pio2_pa_w(u8 data)181 void cedar_magnet_sprite_device::pio2_pa_w(u8 data)
182 {
183 // frequent
184 //  printf("%s:sprite_x_coordinate %02x\n", machine().describe_context().c_str(), data);
185 	m_loweraddr = data;
186 }
187 
sprite_port8c_w(u8 data)188 void cedar_magnet_sprite_device::sprite_port8c_w(u8 data)
189 {
190 	int address = (m_upperaddr << 8) | m_loweraddr;
191 	m_framebuffer[address] = data;
192 	if (data!=0x00) printf("sprite_port8c_w write %04x %02x\n", address, data);
193 }
194 
195 // possible watchdog?
sprite_port9c_w(u8 data)196 void cedar_magnet_sprite_device::sprite_port9c_w(u8 data)
197 {
198 //  printf("%s:sprite_port9c_w %02x\n", machine().describe_context().c_str(), data);
199 }
200 
device_add_mconfig(machine_config & config)201 void cedar_magnet_sprite_device::device_add_mconfig(machine_config &config)
202 {
203 	z80_device &spritecpu(Z80(config, "spritecpu", 4000000));
204 	spritecpu.set_addrmap(AS_PROGRAM, &cedar_magnet_sprite_device::cedar_magnet_sprite_map);
205 	spritecpu.set_addrmap(AS_IO, &cedar_magnet_sprite_device::cedar_magnet_sprite_io);
206 
207 	Z80PIO(config, m_pio[0], 4000000/2);
208 //  m_pio[0]->out_int_callback().set_inputline("maincpu", INPUT_LINE_IRQ0);
209 	m_pio[0]->in_pa_callback().set(FUNC(cedar_magnet_sprite_device::pio0_pa_r));
210 	m_pio[0]->out_pa_callback().set(FUNC(cedar_magnet_sprite_device::pio0_pa_w));
211 //  m_pio[0]->in_pb_callback().set(FUNC(cedar_magnet_sprite_device::pio0_pb_r));
212 	m_pio[0]->out_pb_callback().set(FUNC(cedar_magnet_sprite_device::pio0_pb_w));
213 
214 	Z80PIO(config, m_pio[1], 4000000/2);
215 //  m_pio[1]->out_int_callback().set_inputline("maincpu", INPUT_LINE_IRQ0);
216 //  m_pio[1]->in_pa_callback().set(FUNC(cedar_magnet_sprite_device::pio1_pa_r));
217 	m_pio[1]->out_pa_callback().set(FUNC(cedar_magnet_sprite_device::pio1_pa_w));
218 //  m_pio[1]->in_pb_callback().set(FUNC(cedar_magnet_sprite_device::pio1_pb_r));
219 	m_pio[1]->out_pb_callback().set(FUNC(cedar_magnet_sprite_device::pio1_pb_w));
220 
221 	Z80PIO(config, m_pio[2], 4000000/2);
222 //  m_pio[2]->out_int_callback().set_inputline("maincpu", INPUT_LINE_IRQ0);
223 //  m_pio[2]->in_pa_callback().set(FUNC(cedar_magnet_sprite_device::pio2_pa_r));
224 	m_pio[2]->out_pa_callback().set(FUNC(cedar_magnet_sprite_device::pio2_pa_w));
225 //  m_pio[2]->in_pb_callback().set(FUNC(cedar_magnet_sprite_device::pio2_pb_r));
226 	m_pio[2]->out_pb_callback().set(FUNC(cedar_magnet_sprite_device::pio2_pb_w));
227 
228 
229 	ADDRESS_MAP_BANK(config, m_sprite_ram_bankdev).set_map(&cedar_magnet_sprite_device::cedar_magnet_sprite_sub_ram_map).set_options(ENDIANNESS_LITTLE, 8, 18, 0x10000);
230 }
231 
232 
pio0_pa_r()233 u8 cedar_magnet_sprite_device::pio0_pa_r()
234 {
235 //  actually read
236 //  printf("%s: pio0_pa_r\n", machine().describe_context().c_str());
237 	return 0x00;
238 }
239 
240 
pio0_pa_w(u8 data)241 void cedar_magnet_sprite_device::pio0_pa_w(u8 data)
242 {
243 	m_spritesize = data;
244 }
245 
pio0_pb_w(u8 data)246 void cedar_magnet_sprite_device::pio0_pb_w(u8 data)
247 {
248 	m_pio0_pb_data = data;
249 	//printf("%s: pio0_pb_w %02x\n", machine().describe_context().c_str(), data);
250 }
251 
pio1_pa_w(u8 data)252 void cedar_magnet_sprite_device::pio1_pa_w(u8 data)
253 {
254 	//printf("%s: pio1_pa_w %02x\n", machine().describe_context().c_str(), data);
255 }
256 
pio1_pb_w(u8 data)257 void cedar_magnet_sprite_device::pio1_pb_w(u8 data)
258 {
259 	//printf("%s: pio1_pb_w %02x\n", machine().describe_context().c_str(), data);
260 }
261 
262 
pio2_pb_w(u8 data)263 void cedar_magnet_sprite_device::pio2_pb_w(u8 data)
264 {
265 	// this feels like a hack
266 	// the game writes here when creating the sprite list so that it can access the correct gfd data
267 	// however the actual LIST data is always in bank 0 (it can't be in any other bank, those areas are occupied by actual gfx)
268 	if (m_high_write)
269 	{
270 		m_uppersprite = data & 0x03;
271 		m_high_write = 0;
272 		return;
273 
274 	}
275 
276 	m_pio2_pb_data = data;
277 	//printf("%s: ******************************************* BANK? **** pio2_pb_w %02x\n", machine().describe_context().c_str(), data);
278 	// yes, it ends up banking the ram right out from under itself during startup execution...
279 	// during this time the main cpu is waiting in a loop, after which it copies the startup code again, and reboots it.
280 	m_sprite_ram_bankdev->set_bank(data & 0x03);
281 }
282 
283 
device_start()284 void cedar_magnet_sprite_device::device_start()
285 {
286 	m_framebuffer = make_unique_clear<u8[]>(0x10000);
287 	save_pointer(NAME(m_framebuffer), 0x10000);
288 }
289 
290 
device_reset()291 void cedar_magnet_sprite_device::device_reset()
292 {
293 	halt_assert();
294 	m_sprite_ram_bankdev->set_bank(0);
295 	m_pio2_pb_data = 0x00;
296 	m_spritesize = 0xff;
297 }
298 
draw(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect,int palbase)299 u32 cedar_magnet_sprite_device::draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int palbase)
300 {
301 //  printf("-----------------------------------------------------------------------------------------------------------\n");
302 //  printf("--------------------------------------------- FRAME -------------------------------------------------------\n");
303 //  printf("-----------------------------------------------------------------------------------------------------------\n");
304 
305 	int count = 0;
306 
307 //  if (!(m_m_spritesize & 0x40))
308 //      return 0;
309 
310 	for (int y = 0; y < 256; y++)
311 	{
312 		uint16_t *const dst = &bitmap.pix((y) & 0xff);
313 
314 		for (int x = 0; x < 256; x++)
315 		{
316 			u8 pix = m_framebuffer[count];
317 			count++;
318 
319 			if (pix) dst[(x) & 0xff] = pix + palbase * 0x100;
320 		}
321 	}
322 
323 	return 0;
324 }
325