1 // license:BSD-3-Clause
2 // copyright-holders:Stefan Jokisch
3 /***************************************************************************
4 
5     video/liberatr.c
6 
7   Functions to emulate the video hardware of the machine.
8 
9    Liberator's screen is 256 pixels by 256 pixels.  The
10      round planet in the middle of the screen is 128 pixels
11      tall by 96 equivalent (192 at double pixel rate).  The
12      emulator needs to account for the aspect ratio of 4/3
13      from the arcade video system in order to make the planet
14      appear round.
15 
16 ***************************************************************************/
17 
18 #include "emu.h"
19 #include "includes/liberatr.h"
20 
21 
22 #define NUM_PENS    (0x18)
23 
24 
25 
26 
bitmap_xy_w(uint8_t data)27 void liberatr_state::bitmap_xy_w(uint8_t data)
28 {
29 	m_videoram[(*m_ycoord << 8) | *m_xcoord] = data & 0xe0;
30 }
31 
32 
bitmap_xy_r()33 uint8_t liberatr_state::bitmap_xy_r()
34 {
35 	return m_videoram[(*m_ycoord << 8) | *m_xcoord];
36 }
37 
38 
bitmap_w(offs_t offset,uint8_t data)39 void liberatr_state::bitmap_w(offs_t offset, uint8_t data)
40 {
41 	uint8_t x, y;
42 
43 	m_bitmapram[offset] = data;
44 
45 	offset += 3;
46 	x = (offset & 0x3f) << 2;
47 	y = offset >> 6;
48 
49 	data = data & 0xe0;
50 
51 	m_videoram[(y << 8) | x | 0] = data;
52 	m_videoram[(y << 8) | x | 1] = data;
53 	m_videoram[(y << 8) | x | 2] = data;
54 	m_videoram[(y << 8) | x | 3] = data;
55 }
56 
57 
58 /********************************************************************************************
59   liberatr_init_planet()
60 
61   The data for the planet is stored in ROM using a run-length type of encoding.  This
62   function does the conversion to the above structures and then a smaller
63   structure which is quicker to use in real time.
64 
65   Its a multi-step process, reflecting the history of the code.  Not quite as efficient
66   as it might be, but this is not realtime stuff, so who cares...
67  ********************************************************************************************/
68 
init_planet(planet & liberatr_planet,uint8_t * planet_rom)69 void liberatr_state::init_planet(planet &liberatr_planet, uint8_t *planet_rom)
70 {
71 	uint16_t longitude;
72 
73 	const uint8_t *latitude_scale = memregion("user1")->base();
74 	const uint8_t *longitude_scale = memregion("user2")->base();
75 
76 	/* for each starting longitude */
77 	for (longitude = 0; longitude < 0x100; longitude++)
78 	{
79 		uint8_t i, latitude, start_segment, segment_count;
80 		uint8_t *buffer;
81 
82 		planet_frame frame;
83 		planet_frame_line *line = nullptr;
84 
85 		uint16_t total_segment_count = 0;
86 
87 		/* for each latitude */
88 		for (latitude = 0; latitude < 0x80; latitude++)
89 		{
90 			uint8_t segment, longitude_scale_factor, latitude_scale_factor, color, x=0;
91 			uint8_t x_array[32], color_array[32], visible_array[32];
92 
93 			/* point to the structure which will hold the data for this line */
94 			line = &frame.lines[latitude];
95 
96 			latitude_scale_factor = latitude_scale[latitude];
97 
98 			/* for this latitude, load the 32 segments into the arrays */
99 			for (segment = 0; segment < 0x20; segment++)
100 			{
101 				uint16_t length, planet_data, address;
102 
103 				/*
104 				   read the planet picture ROM and get the
105 				   latitude and longitude scaled from the scaling PROMS
106 				*/
107 				address = (latitude << 5) + segment;
108 				planet_data = (planet_rom[address] << 8) | planet_rom[address + 0x1000];
109 
110 				color  =  (planet_data >> 8) & 0x0f;
111 				length = ((planet_data << 1) & 0x1fe) + ((planet_data >> 15) & 0x01);
112 
113 
114 				/* scale the longitude limit (adding the starting longitude) */
115 				address = longitude + ( length >> 1 ) + ( length & 1 );     /* shift with rounding */
116 				visible_array[segment] = (( address & 0x100 ) ? 1 : 0);
117 				if (address & 0x80)
118 				{
119 					longitude_scale_factor = 0xff;
120 				}
121 				else
122 				{
123 					address = ((address & 0x7f) << 1) + (((length & 1) || visible_array[segment]) ? 0 : 1);
124 					longitude_scale_factor = longitude_scale[address];
125 				}
126 
127 				x_array[segment] = (((uint16_t)latitude_scale_factor * (uint16_t)longitude_scale_factor) + 0x80) >> 8;  /* round it */
128 				color_array[segment] = color;
129 			}
130 
131 			/*
132 			   determine which segment is the western horizon and
133 			     leave 'segment' indexing it.
134 			*/
135 			for (segment = 0; segment < 0x1f; segment++)    /* if not found, 'segment' = 0x1f */
136 				if (visible_array[segment]) break;
137 
138 			/* transfer from the temporary arrays to the structure */
139 			line->max_x = (latitude_scale_factor * 0xc0) >> 8;
140 			if (line->max_x & 1)
141 				line->max_x += 1;               /* make it even */
142 
143 			/*
144 			   as part of the quest to reduce memory usage (and to a lesser degree
145 			     execution time), stitch together segments that have the same color
146 			*/
147 			segment_count = 0;
148 			i = 0;
149 			start_segment = segment;
150 
151 			do
152 			{
153 				color = color_array[segment];
154 				while (color == color_array[segment])
155 				{
156 					x = x_array[segment];
157 					segment = (segment+1) & 0x1f;
158 					if (segment == start_segment)
159 						break;
160 				}
161 
162 				line->color_array[i] = color;
163 				line->x_array[i] = (x > line->max_x) ? line->max_x : x;
164 				i++;
165 				segment_count++;
166 			} while ((i < 32) && (x <= line->max_x));
167 
168 			total_segment_count += segment_count;
169 			line->segment_count = segment_count;
170 		}
171 
172 		/* now that the all the lines have been processed, and we know how
173 		   many segments it will take to store the description, allocate the
174 		   space for it and copy the data to it.
175 		*/
176 		buffer = auto_alloc_array(machine(), uint8_t, 2*(128 + total_segment_count));
177 
178 		liberatr_planet.frames[longitude] = buffer;
179 
180 		for (latitude = 0; latitude < 0x80; latitude++)
181 		{
182 			uint8_t last_x;
183 
184 
185 			line = &frame.lines[latitude];
186 			segment_count = line->segment_count;
187 			*buffer++ = segment_count;
188 			last_x = 0;
189 
190 			/* calculate the bitmap's x coordinate for the western horizon
191 			   center of bitmap - (the number of planet pixels) / 4 */
192 			*buffer++ = (m_screen->width() / 2) - ((line->max_x + 2) / 4);
193 
194 			for (i = 0; i < segment_count; i++)
195 			{
196 				uint8_t current_x = (line->x_array[i] + 1) / 2;
197 
198 				*buffer++ = line->color_array[i];
199 				*buffer++ = current_x - last_x;
200 
201 				last_x = current_x;
202 			}
203 		}
204 	}
205 }
206 
207 
208 /***************************************************************************
209 
210   Start the video hardware emulation.
211 
212 ***************************************************************************/
213 
video_start()214 void liberatr_state::video_start()
215 {
216 	// for each planet in the planet ROMs
217 	init_planet(m_planets[0], &memregion("gfx1")->base()[0x2000]);
218 	init_planet(m_planets[1], &memregion("gfx1")->base()[0x0000]);
219 
220 	save_item(NAME(m_planet_select));
221 }
222 
223 
get_pens(pen_t * pens)224 void liberatr_state::get_pens(pen_t *pens)
225 {
226 	offs_t i;
227 
228 	for (i = 0; i < NUM_PENS; i++)
229 	{
230 		uint8_t r,g,b;
231 
232 		/* handle the hardware flip of the bit order from 765 to 576 that
233 		   hardware does between vram and color ram */
234 		static const offs_t penmap[] = { 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
235 									0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
236 									0x10, 0x12, 0x14, 0x16, 0x11, 0x13, 0x15, 0x17 };
237 
238 		uint8_t data = m_colorram[i];
239 
240 		/* scale it from 0x00-0xff */
241 		r = ((~data >> 3) & 0x07) * 0x24 + 3;  if (r == 3)  r = 0;
242 		g = ((~data >> 0) & 0x07) * 0x24 + 3;  if (g == 3)  g = 0;
243 		b = ((~data >> 5) & 0x06) * 0x24 + 3;  if (b == 3)  b = 0;
244 
245 		pens[penmap[i]] = rgb_t(r, g, b);
246 	}
247 }
248 
249 
draw_planet(bitmap_rgb32 & bitmap,pen_t * pens)250 void liberatr_state::draw_planet(bitmap_rgb32 &bitmap, pen_t *pens)
251 {
252 	uint8_t const *buffer = m_planets[m_planet_select].frames[*m_planet_frame];
253 
254 	/* for each latitude */
255 	for (uint8_t latitude = 0; latitude < 0x80; latitude++)
256 	{
257 		/* grab the color value for the base (if any) at this latitude */
258 		uint8_t const base_color = m_base_ram[latitude >> 3] ^ 0x0f;
259 
260 		uint8_t const segment_count = *buffer++;
261 		uint8_t x = *buffer++;
262 		uint8_t const y = 64 + latitude;
263 
264 		/* run through the segments, drawing its color until its x_array value comes up. */
265 		for (uint8_t segment = 0; segment < segment_count; segment++)
266 		{
267 			uint8_t color = *buffer++;
268 			uint8_t segment_length = *buffer++;
269 
270 			if ((color & 0x0c) == 0x0c)
271 				color = base_color;
272 
273 			for (uint8_t i = 0; i < segment_length; i++, x++)
274 				bitmap.pix(y, x) = pens[color];
275 		}
276 	}
277 }
278 
279 
draw_bitmap(bitmap_rgb32 & bitmap,pen_t * pens)280 void liberatr_state::draw_bitmap(bitmap_rgb32 &bitmap, pen_t *pens)
281 {
282 	for (offs_t offs = 0; offs < 0x10000; offs++)
283 	{
284 		uint8_t const data = m_videoram[offs];
285 
286 		uint8_t const y = offs >> 8;
287 		uint8_t const x = offs & 0xff;
288 
289 		if (data)
290 			bitmap.pix(y, x) = pens[(data >> 5) | 0x10];
291 	}
292 }
293 
294 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)295 uint32_t liberatr_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
296 {
297 	pen_t pens[NUM_PENS];
298 	get_pens(pens);
299 
300 	bitmap.fill(rgb_t::black(), cliprect);
301 	draw_planet(bitmap, pens);
302 	draw_bitmap(bitmap, pens);
303 
304 	return 0;
305 }
306