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