screen.cpp 13.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * screen.cpp
 *
 *  Created on: 26/03/2015
 *      Author: raster
 */

#include "llscreen.hh"
#include "screen.hh"
#include "emulator.hh"
#include "computer.hh"
12
#include "keyboard.hh"
13 14 15 16

class Screen *screen;

/* Registers the screen surface where the Spectrum will put the picture,
17 18
 * prepares the palette and creates two arrays (translate and translate2)
 * that gives the memory address for each scan */
19 20

Screen::Screen() {
21
	// int resx,resy;
22 23 24 25 26
	int bucle, bucle2, bucle3, bucle4, bucle5;

	// we prepare the scanline transform arrays

	bucle5 = 0;
27 28 29
	for (bucle = 0; bucle < 3; bucle++) {
		for (bucle2 = 0; bucle2 < 8; bucle2++) {
			for (bucle3 = 0; bucle3 < 8; bucle3++) {
30
				for (bucle4 = 0; bucle4 < 32; bucle4++) {
31
					this->translate[bucle5]  = 147456 + bucle * 2048 + bucle2 * 32 + bucle3 * 256 + bucle4;
32 33 34
					this->translate2[bucle5] = 153600 + bucle * 256 + bucle2 * 32 + bucle4;
					bucle5++;
				}
35 36 37
			}
		}
	}
38 39
	this->tstados_counter = 0;

40 41 42 43
	this->border      = 0;
	this->currline    = 0;
	this->currpix     = 0;
	this->flash       = 0;
44
	this->screen_snow = false;
45
	this->bus_value   = 255;
46 47
	int height;
	int width;
48 49 50

	switch (ordenador->zaurus_mini) {
	case 0:
51 52
		this->init_line     = 0;
		this->next_line     = 640;
53
		this->next_scanline = 640;
54 55 56 57 58 59
		this->first_line    = 40;
		this->last_line     = 278;
		this->first_column  = 12;
		this->next_pixel    = 1;
		this->jump_pixel    = 16;
		width  = 640;
60
		height = 480;
61
		break;
62

63
	case 1:
64 65
		this->init_line     = 80;
		this->next_line     = 160;
66
		this->next_scanline = 160;
67 68 69 70 71 72
		this->first_line    = 40;
		this->last_line     = 348;
		this->first_column  = 12;
		this->next_pixel    = 1;
		this->jump_pixel    = 8;
		width  = 480;
73
		height = 240;
74
		break;
75

76
	case 2:
77 78
		this->init_line     = 480 * 640;
		this->next_line     = -(307202);
79
		this->next_scanline = -1;
80 81 82 83 84 85
		this->first_line    = 40;
		this->last_line     = 278;
		this->first_column  = 24;
		this->next_pixel    = 480;
		this->jump_pixel    = 7680;
		width  = 480;
86
		height = 640;
87
		break;
88

89
	case 3:
90 91
		this->init_line     = 0;
		this->next_line     = 0;
92
		this->next_scanline = 0;
93 94 95 96 97 98
		this->first_line    = 40;
		this->last_line     = 278;
		this->first_column  = 24;
		this->next_pixel    = 1;
		this->jump_pixel    = 4;
		width  = 480;
99
		height = 240;
100 101 102
		break;
	}

103
	this->last_column = 160;
104 105
	this->p_translt   = this->translate;
	this->p_translt2  = this->translate2;
106 107

	this->contador_flash = 0;
108 109
	this->pixancho       = 224;
	this->pixalto        = 312; // values for 48K mode
110

111
	this->ulaplus     = false;
112
	this->ulaplus_reg = 0;
113
	this->int_counter = 0;
114 115 116

	llscreen->set_paletes(ordenador->bw);

Sergio Costas's avatar
Sergio Costas committed
117
	this->base_pixel = llscreen->memory;
118 119
	this->max_pixel  = this->base_pixel + (height * width);
	this->pixel      = this->base_pixel + this->init_line;
120 121
}

122
void Screen::set_memory_pointers() {
123 124 125 126
	static unsigned int rom, ram;

	// assign the offset for video page

127 128 129 130 131
	if (ordenador->mport1 & 0x08) {
		ordenador->video_offset = 32768; // page 7
	} else {
		ordenador->video_offset = 0;     // page 5
	}
132 133
	// assign ROMs and, if in special mode, RAM for the whole blocks

134
	if ((ordenador->current_mode == MODE_P3)) {
135 136 137
		if (ordenador->mport2 & 0x01) {                      // +2A/+3 special mode
			ordenador->page48k = 0;                          // no 48K ROM paged in
			ram = (unsigned int) (ordenador->mport1 & 0x06); // bits 1&2
138 139 140 141 142 143 144
			switch (ram) {
			case 0:
				ordenador->block0 = ordenador->memoria + 65536;
				ordenador->block1 = ordenador->memoria + 65536;
				ordenador->block2 = ordenador->memoria + 65536;
				ordenador->block3 = ordenador->memoria + 65536;
				break;
145

146 147 148 149 150 151
			case 2:
				ordenador->block0 = ordenador->memoria + 131072;
				ordenador->block1 = ordenador->memoria + 131072;
				ordenador->block2 = ordenador->memoria + 131072;
				ordenador->block3 = ordenador->memoria + 131072;
				break;
152

153 154 155 156 157 158
			case 4:
				ordenador->block0 = ordenador->memoria + 131072;
				ordenador->block1 = ordenador->memoria + 131072;
				ordenador->block2 = ordenador->memoria + 131072;
				ordenador->block3 = ordenador->memoria + 65536;
				break;
159

160 161 162 163 164 165
			case 6:
				ordenador->block0 = ordenador->memoria + 131072;
				ordenador->block1 = ordenador->memoria + 163840;
				ordenador->block2 = ordenador->memoria + 131072;
				ordenador->block3 = ordenador->memoria + 65536;
				break;
166

167 168 169 170
			default:
				printf("FullRAM desconocido\n");
			}
			return;
171
		} else { // ROMs for +2A/+3 normal mode
172
			rom = 0;
173
			if (ordenador->mport1 & 0x10) {
174
				rom++;
175 176
			}
			if (ordenador->mport2 & 0x04) {
177
				rom += 2;
178
			}
179
			// assign the first block pointer to the right block bank
180
			ordenador->block0  = ordenador->memoria + (16384 * rom);
Sergio Costas's avatar
Sergio Costas committed
181
			ordenador->page48k = (rom == 3) ? 1 : 0; // 48K ROM is in ROM page 3
182
		}
183 184
	} else if (ordenador->current_mode == MODE_48K) {
		ordenador->block0  = ordenador->memoria;
185
		ordenador->page48k = 1;
186
	} else { // ROMs for 128K/128Kspa/+2 mode
187
		if (ordenador->mport1 & 0x10) {
188
			ordenador->block0  = ordenador->memoria + 16384;
189 190
			ordenador->page48k = 1;
		} else {
191
			ordenador->block0  = ordenador->memoria;
192 193 194 195 196 197
			ordenador->page48k = 0;
		}
	}

	// RAMs for 128K/+2 mode, and +2A/+3 in normal mode

198 199
	ordenador->block1 = ordenador->memoria + 131072;        // page 5 minus 16384
	ordenador->block2 = ordenador->memoria + 65536;         // page 2 minus 32768
200

201 202
	ram = 1 + ((unsigned int) (ordenador->mport1 & 0x07));  // RAM page for block3 plus 1
	ordenador->block3 = ordenador->memoria + (16384 * ram); // page n minus 49152
203 204
}

205
uint8_t Screen::get_bus_value(int tstates) {
206
	return this->bus_value;
207 208
}

209
/* Paints the spectrum screen during the TSTADOS tstates that the Z80 used
210
 * to execute last instruction */
211

212
void Screen::show_screen(int tstados) {
213
	static uint8_t temporal, ink, paper, fflash, tmp2;
214
	static int     loop;
215

216
	if ((procesador.I >= 0x40) && (procesador.I <= 0x7F)) {
217 218 219 220
		this->screen_snow = true;
	} else {
		this->screen_snow = false;
	}
221
	this->tstados_counter2    += tstados;
222 223 224 225
	ordenador->cicles_counter += tstados;

	fflash = 0; // flash flag

226
	for (loop = 0; loop < tstados; loop++) {
227
		if (this->int_counter > 0) {
228
			this->int_counter--;
229 230
			if (this->int_counter == 0) {
				Z80free_INTserved(&procesador);
231
			}
232
		}
233
		this->currpix  = (this->tstados_counter + this->first_column + this->offset + this->hoffset) % this->pixancho;
234
		this->currline = (this->tstados_counter + this->first_column + this->offset + this->hoffset) / this->pixancho;
235
		if ((this->currline > this->first_line) && (this->currpix == 0)) {
236 237
			this->pixel += this->next_line;
		}
238 239
		this->bus_value = 0xFF;
		ordenador->memcontended_zone = 0; // no contention here
240
		if (((this->tstados_counter + this->offset) % 4) == 0) {
241
			if (((this->tstados_counter + this->offset + this->offset2) < this->tstate_contention) || ((this->tstados_counter + this->offset + this->offset2) >= this->tstate_contention2) || (((this->tstados_counter + this->offset + this->offset2) % this->pixancho) >= 128)) {
242
				// is border
243
				if (this->ulaplus) {
244
					this->paint_pixels(255, this->border + 24, 0); // paint 8 pixels with BORDER color
245
				} else {
246
					this->paint_pixels(255, this->border, 0);      // paint 8 pixels with BORDER color
247
				}
248
			} else {
249
				// is user area. We search for ink and paper colours
250
				this->paint_pixels(this->user_pixels, this->user_ink, this->user_paper);
251
			}
252 253
		}

254
		if ((this->tstados_counter >= this->tstate_contention) && (this->tstados_counter < this->tstate_contention2)) {
255
			int p;
256
			if (((this->tstados_counter - this->tstate_contention) % this->pixancho) < 128) {
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
				int zone = ((this->tstados_counter - this->tstate_contention) % this->pixancho) % 8;
				switch (zone) {
				case 0:
				case 5:
					ordenador->memcontended_zone = 6 - zone;
					break;

				case 1:
				case 3:
					ordenador->memcontended_zone = 6 - zone;
					p = *this->p_translt;
					if ((!this->screen_snow) || (rand() % 8)) {
						this->bus_value = ordenador->memoria[p + ordenador->video_offset];
					} else {
						this->bus_value = ordenador->memoria[p + ordenador->video_offset - 1];
					}
					this->p_translt++;
					this->user_pixels = this->bus_value;
					break;

				case 2:
				case 4:
					ordenador->memcontended_zone = 6 - zone;
					p = *this->p_translt2;
					this->bus_value = ordenador->memoria[p + ordenador->video_offset]; // attributes
					this->p_translt2++;
					ink   = this->bus_value & 0x07;                                    // ink colour
					paper = (this->bus_value >> 3) & 0x07;                             // paper colour
					if (this->ulaplus) {
						tmp2   = 0x10 + ((this->bus_value >> 2) & 0x30);
						ink   += tmp2;
						paper += 8 + tmp2;
					} else {
						if (this->bus_value & 0x40) { // bright flag?
							ink   += 8;
							paper += 8;
293
						}
294 295 296 297 298
						fflash = this->bus_value & 0x80; // flash flag
						if ((fflash) && (this->flash)) {
							// paint_pixels (temporal, paper, ink);	// if FLASH, invert PAPER and INK
							this->user_ink   = paper;
							this->user_paper = ink;
299
						} else {
300 301 302
							// paint_pixels (temporal, ink, paper);
							this->user_ink   = ink;
							this->user_paper = paper;
303
						}
304 305
					}
					break;
306
				}
307 308 309
				if (this->offset_p3 != 0) {
					ordenador->memcontended_zone = (15 - (zone + this->offset_p3)) % 8;
				}
310 311 312
			}
		}

Sergio Costas's avatar
Sergio Costas committed
313
		if (this->tstados_counter == this->tstates_screen) {
314
			this->currline = 0;
315 316
			if (osd->get_time() != 0) {
				uint8_t lines;
317
				string  text;
318
				text = osd->get_text(lines);
319
				llscreen->print_string(text, -1, -lines, 12, 0);
320
			} else {
321
				keyboard->tab_extended = false;
322
				keyboard->esc_again    = false;
323 324 325 326 327
			}

			llscreen->do_flip();

			ordenador->interr = 1;
328
			this->int_counter = 32;
329
			Z80free_INT(&procesador, ordenador->bus_empty());
330
			ordenador->cicles_counter = 0;
331 332
			this->pixel      = this->base_pixel + this->init_line;
			this->p_translt  = this->translate;
333 334
			this->p_translt2 = this->translate2;
			this->contador_flash++;
Sergio Costas's avatar
Sergio Costas committed
335
			if (this->contador_flash >= 16) {
336
				this->flash          = 1 - this->flash;
337 338
				this->contador_flash = 0;
			}
Sergio Costas's avatar
Sergio Costas committed
339
			this->tstados_counter = 0;
340 341
		} else {
			this->tstados_counter++;
342 343 344 345 346
		}
	}
}

/* PAINT_PIXELS paints one byte with INK color for 1 bits and PAPER color
347
 * for 0 bits, and increment acordingly the pointer PIXEL */
348

349 350
void Screen::paint_pixels(uint8_t octet, uint8_t ink, uint8_t paper) {
	static int           bucle, valor;
351 352 353
	static unsigned int *p;
	static unsigned char mask;

354
	if (((this->currpix >= (this->last_column))) || (this->currline < this->first_line) || (this->pixel >= this->max_pixel)) {
355
		return;
356
	}
357 358 359 360

	mask = 0x80;
	for (bucle = 0; bucle < 8; bucle++) {
		valor = (octet & mask) ? (int) ink : (int) paper;
361 362
		llscreen->paint_one_pixel(valor, this->pixel);
		if ((ordenador->zaurus_mini != 1) && (ordenador->zaurus_mini != 3)) {
363
			if (ordenador->dblscan) {
364
				llscreen->paint_one_pixel(valor, this->pixel + this->next_scanline);
365
			} else {
366
				llscreen->paint_one_pixel(0, this->pixel + this->next_scanline);
367 368
			}
		}
369 370 371
		this->pixel += this->next_pixel;
		if ((ordenador->zaurus_mini != 1) && (ordenador->zaurus_mini != 3)) {
			llscreen->paint_one_pixel(valor, this->pixel);
372
			if (ordenador->dblscan) {
373
				llscreen->paint_one_pixel(valor, this->pixel + this->next_scanline);
374
			} else {
375
				llscreen->paint_one_pixel(0, this->pixel + this->next_scanline);
376
			}
377
			this->pixel += this->next_pixel;
378 379 380 381 382 383 384 385
		}
		mask = ((mask >> 1) & 0x7F);
	}
}

void Screen::reset(uint8_t model) {
	this->ulaplus = false;
	switch (model) {
386
	case MODE_48K: // 48K
387 388 389
		this->pixancho          = 224;
		this->pixalto           = 312;
		this->pixborde_top      = 64;
390
		this->tstate_contention = 14335;
391 392 393 394
		this->offset            = 2;
		this->offset2           = -4;
		this->offset_p3         = 0;
		this->hoffset           = 0;
395
		break;
396

397 398 399
	case MODE_128K:
	case MODE_P2:
	case MODE_128K_SPA:
400 401 402
		this->pixborde_top      = 63;
		this->pixancho          = 228;
		this->pixalto           = 311;
403
		this->tstate_contention = 14361;
404 405 406 407
		this->offset            = 0;
		this->offset2           = 0;
		this->offset_p3         = 0;
		this->hoffset           = 4;
408
		break;
409

410
	case MODE_P3:
411 412 413
		this->pixborde_top      = 63;
		this->pixancho          = 228;
		this->pixalto           = 311;
414
		this->tstate_contention = 14359;
415 416 417 418
		this->offset            = 2;
		this->offset2           = 0;
		this->offset_p3         = 6;
		this->hoffset           = 4;
419
		break;
420
	}
Sergio Costas's avatar
Sergio Costas committed
421
	printf("Reset\n");
422
	this->tstados_counter     = 0;
Sergio Costas's avatar
Sergio Costas committed
423
	ordenador->cicles_counter = 0;
424 425
	this->pixel      = this->base_pixel + this->init_line;
	this->p_translt  = this->translate;
Sergio Costas's avatar
Sergio Costas committed
426
	this->p_translt2 = this->translate2;
427 428
	this->currpix    = 0;
	this->currline   = 0;
429

430
	this->tstate_contention2 = this->tstate_contention + 192 * this->pixancho;
431
	this->tstates_screen     = this->pixancho * this->pixalto - 1;
432 433 434 435 436 437 438
}

void Screen::set_ulaplus_register(uint8_t reg) {
	this->ulaplus_reg = reg;
}

void Screen::set_ulaplus_value(uint8_t Value) {
439
	if ((this->ulaplus_reg & 0xC0) == 0x40) { // mode
440 441 442 443
		this->ulaplus = (Value & 0x01) == 1 ? true : false;
		return;
	}
	if (this->ulaplus_reg < 0x40) { // register set mode
444
		llscreen->set_palete_entry(this->ulaplus_reg, Value, ordenador->bw);
445 446 447 448
	}
}

uint8_t Screen::read_ulaplus_value() {
449
	if ((this->ulaplus_reg & 0xC0) == 0x40) { // mode
450 451 452 453 454 455 456
		return (this->ulaplus ? 1 : 0);
	}
	if (this->ulaplus_reg < 0x40) { // register set mode
		return (llscreen->get_palete_entry(this->ulaplus_reg));
	}
	return 0;
}