Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
vera.c
Go to the documentation of this file.
1 /* The Xemu project.
2  Copyright (C)2016-2019 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
3 
4  This is the Commander X16 emulation. Note: the source is overcrowded with comments by intent :)
5  That it can useful for other people as well, or someone wants to contribute, etc ...
6 
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20 
21 #include "xemu/emutools.h"
22 #include "xemu/emutools_files.h"
23 #include "vera.h"
24 #include "xemu/cpu65.h"
25 #include "sdcard.h"
26 #include "commander_x16.h"
27 
28 
29 #define DEBUGVERA DEBUGPRINT
30 //#define DEBUGVERA DEBUG
31 //#define DEBUGVERA(...)
32 
33 #define SCANLINES_TOTAL 525
34 #define SCANLINE_START_VSYNC 492
35 #define SCANLINE_STOP_VSYNC 495
36 
37 #define MODE_NTSC_COMPOSITE 2
38 // 4 bits of interrupt info
39 #define INTERRUPT_REGISTER_MASK 15
40 #define VRAM_SIZE 0x20000
41 #define UART_CLOCK 25000000
42 #define UART_DEFAULT_BAUD_RATE 1000000
43 
44 #define VSYNC_IRQ 1
45 #define RASTER_IRQ 2
46 #define SPRITE_IRQ 4
47 #define UART_IRQ 8
48 
49 
50 static struct { // current_port variable selects one of these
51  Uint8 regspace[3]; // used as "storage only", ie to be able to read back the registers by CPU, nothing more
52  int addr;
53  int inc;
54 } dataport[2];
55 static int current_port; // current dataport in use for CPU VERA registers 0/1/2
56 
57 static struct {
58  Uint8 regspace[0x10];
62  int counter_map, counter_tile_row; // Xemu specific things!
63 } layer[2];
64 
65 static struct {
66  int index;
68  int changed;
69 } border;
70 
71 static struct {
72  Uint8 regspace[0x10];
74  int hscale, vscale;
75  int mode, field;
76  int irqline;
78  int is_mono_mode; // a value takes chromakill and NTSC mode to be true to use the mono mode for real
79  int hwidth; // this is an Xemu specific field, calculated to be the number of VGA pixels to be processed in a line, based on hstart..hend, also chopping of overflow etc
80 } composer;
81 
82 static struct {
83  Uint32 colours[0x100];
84  Uint32 all[4096];
86  Uint8 regspace[0x200];
87 } palette;
88 
89 static struct {
92  int spi_busy; // not so much used currently ...
95 } serial;
96 
97 static Uint8 ien_reg, isr_reg;
98 // VRAM is actually only 128K. However this is a kind of optimization:
99 // And it's much easier to handle things like "is the character set now in ROM or RAM?" since we need only offset in VMEM, no matter RAM/ROM or even setting to undecoded area, etc ...
100 static Uint8 vram[1 * 1024 * 1024];
101 
102 
103 // This table is taken from Mist's X16 emulator, since I have no idea what the default palette is ...
104 static const Uint16 x16paldata[] = {
105  0x0000,0xfff,0x800,0xafe,0xc4c,0x0c5,0x00a,0xee7,0xd85,0x640,0xf77,0x333,0x777,0xaf6,0x08f,0xbbb,0x000,0x111,0x222,0x333,0x444,0x555,0x666,0x777,0x888,0x999,0xaaa,0xbbb,0xccc,0xddd,0xeee,0xfff,0x211,0x433,0x644,0x866,0xa88,0xc99,0xfbb,0x211,0x422,0x633,0x844,0xa55,0xc66,0xf77,0x200,0x411,0x611,0x822,0xa22,0xc33,0xf33,0x200,0x400,0x600,0x800,0xa00,0xc00,0xf00,0x221,0x443,0x664,0x886,0xaa8,0xcc9,0xfeb,0x211,0x432,0x653,0x874,0xa95,0xcb6,0xfd7,0x210,0x431,0x651,0x862,0xa82,0xca3,0xfc3,0x210,0x430,0x640,0x860,0xa80,0xc90,0xfb0,0x121,0x343,0x564,0x786,0x9a8,0xbc9,0xdfb,0x121,0x342,0x463,0x684,0x8a5,0x9c6,0xbf7,0x120,0x241,0x461,0x582,0x6a2,0x8c3,0x9f3,0x120,0x240,0x360,0x480,0x5a0,0x6c0,0x7f0,0x121,0x343,0x465,0x686,0x8a8,0x9ca,0xbfc,0x121,0x242,0x364,0x485,0x5a6,0x6c8,0x7f9,0x020,0x141,0x162,0x283,0x2a4,0x3c5,0x3f6,0x020,0x041,0x061,0x082,0x0a2,0x0c3,0x0f3,0x122,0x344,0x466,0x688,0x8aa,0x9cc,0xbff,0x122,0x244,0x366,0x488,0x5aa,0x6cc,0x7ff,0x022,0x144,0x166,0x288,0x2aa,0x3cc,0x3ff,0x022,0x044,0x066,0x088,0x0aa,0x0cc,0x0ff,0x112,0x334,0x456,0x668,0x88a,0x9ac,0xbcf,0x112,0x224,0x346,0x458,0x56a,0x68c,0x79f,0x002,0x114,0x126,0x238,0x24a,0x35c,0x36f,0x002,0x014,0x016,0x028,0x02a,0x03c,0x03f,0x112,0x334,0x546,0x768,0x98a,0xb9c,0xdbf,0x112,0x324,0x436,0x648,0x85a,0x96c,0xb7f,0x102,0x214,0x416,0x528,0x62a,0x83c,0x93f,0x102,0x204,0x306,0x408,0x50a,0x60c,0x70f,0x212,0x434,0x646,0x868,0xa8a,0xc9c,0xfbe,0x211,0x423,0x635,0x847,0xa59,0xc6b,0xf7d,0x201,0x413,0x615,0x826,0xa28,0xc3a,0xf3c,0x201,0x403,0x604,0x806,0xa08,0xc09,0xf0b
106 };
107 
108 static XEMU_INLINE void uart_set_baud_divisor ( int div )
109 {
110  serial.uart_baud_divisor = div;
111  serial.uart_baud_rate = (UART_CLOCK) / (div + 1);
112  DEBUGVERA("UART: %d bps, divisor = %d" NL, serial.uart_baud_rate, serial.uart_baud_divisor);
113 }
114 
115 
116 
117 
118 static XEMU_INLINE void UPDATE_IRQ ( void )
119 {
120  // leave any upper bits as-is, used by other IRQ sources than VERA, in the emulation of VIAs for example
121  // This works at the 65XX emulator level, that is a single irqLevel variable is though to be boolean as zero (no IRQ) or non-ZERO (IRQ request),
122  // but, on other component level we use it as bit fields from various IRQ sources. That is, we save emulator time to create an 'OR" logic for
123  // various interrupt sources.
124  cpu65.irqLevel = (cpu65.irqLevel & ~INTERRUPT_REGISTER_MASK) | (ien_reg & isr_reg);
125 }
126 
127 #if 0
128 static XEMU_INLINE void SET_IRQ ( int irq_type )
129 {
130  isr_reg |= irq_type;
131  UPDATE_IRQ();
132 }
133 
134 static XEMU_INLINE void CLEAR_IRQ ( int irq_type )
135 {
136  isr_reg &= ~irq_type;
137  UPDATE_IRQ();
138 }
139 #endif
140 
141 
142 // Updates a palette enty based on the "addr" in palette-memory
143 // Also gives "monochrome" version if it's needed
144 // Also, updates the border colour if the changed x16 colour index is used for the border
145 static void update_palette_entry ( int addr )
146 {
147  addr &= 0x1FE;
148  int index = addr >> 1;
149  Uint32 colour = *((XEMU_UNLIKELY(composer.is_mono_mode) ? palette.all_mono : palette.all) + (palette.regspace[addr] + ((palette.regspace[addr + 1] & 0xF) << 8)));
150  //palatte[index] = XEMU_LIKELY(index) ? colour : 0; // transparent, if index is zero
151  palette.colours[index] = colour;
152  if (index == border.index) {
153  border.colour = colour;
154  border.changed = 1;
155  }
156 }
157 
158 
159 static XEMU_INLINE void update_all_palette_entries ( void )
160 {
161  for (int a = 0; a < 0x200; a += 2)
162  update_palette_entry(a);
163 }
164 
165 
166 
167 static void write_layer_register ( int ln, int reg, Uint8 data )
168 {
169  switch (reg) {
170  case 0x0:
171  layer[ln].enabled = data & 1;
172  layer[ln].mode = data >> 5;
173  if (layer[ln].mode >= 5) // bitmap mode
174  layer[ln].hscroll &= 0xFF;
175  else
176  layer[ln].hscroll = (layer[ln].hscroll & 0xFF) | (layer[ln].bitmap_palette_offset << 8);
177  DEBUGVERA("VERA: LAYER-%d#0 register write: enabled=%d mode=%d" NL, ln, layer[ln].enabled, layer[ln].mode);
178  data &= 0x80 | 0x40 | 0x20 | 0x01;
179  break;
180  case 0x1:
181  layer[ln].tileh = (data & 32) ? 1 : 0;
182  layer[ln].tilew = (data & 16) ? 1 : 0;
183  layer[ln].maph = (data >> 2) & 3;
184  layer[ln].mapw = data & 3;
185  DEBUGVERA("VERA: LAYER-%d#1 register write: tilew=%d tileh=%d mapw=%d maph=%d" NL, ln, layer[ln].tilew, layer[ln].tileh, layer[ln].mapw, layer[ln].maph);
186  data &= 0xFF - 0x80 - 0x40;
187  break;
188  case 0x2: // MAP base, bits 9 downto 2
189  layer[ln].map_base = (layer[ln].map_base & 0x3FC00) | (data << 2);
190  DEBUGVERA("VERA: LAYER-%d#2 register write: map_base=$%05X" NL, ln, layer[ln].map_base);
191  break;
192  case 0x3: // MAP base, bits 17 downto 10
193  layer[ln].map_base = (layer[ln].map_base & 0x3FC) | (data << 10);
194  DEBUGVERA("VERA: LAYER-%d#3 register write: map_base=$%05X" NL, ln, layer[ln].map_base);
195  break;
196  case 0x4: // TILE base, bits 9 downto 2
197  layer[ln].tile_base = (layer[ln].tile_base & 0x3FC00) | (data << 2);
198  DEBUGVERA("VERA: LAYER-%d#4 register write: tile_base=$%05X" NL, ln, layer[ln].tile_base);
199  break;
200  case 0x5: // TILE base, bits 17 downto 10
201  layer[ln].tile_base = (layer[ln].tile_base & 0x3FC) | (data << 10);
202  DEBUGVERA("VERA: LAYER-%d#5 register write: tile_base=$%05X" NL, ln, layer[ln].tile_base);
203  break;
204  case 0x6:
205  if (layer[ln].mode >= 5) // in bitmap modes ...
206  layer[ln].hscroll = data;
207  else
208  layer[ln].hscroll = data | (layer[ln].bitmap_palette_offset << 8);
209  DEBUGVERA("VERA: LAYER-%d#6 register write: hscroll=%d" NL, ln, layer[ln].hscroll);
210  break;
211  case 0x7:
212  data &= 0xF;
213  layer[ln].bitmap_palette_offset = data; // only used in bitmap modes, but we always update it for reasons above ... [meaning of these bit can change!]
214  if (layer[ln].mode < 5) // not biitmap modes ...
215  layer[ln].hscroll = (layer[ln].hscroll & 0xFF) | (data << 8);
216  DEBUGVERA("VERA: LAYER-%d#7 register write: hscroll=%d" NL, ln, layer[ln].hscroll);
217  break;
218  case 0x8:
219  layer[ln].vscroll = (layer[ln].vscroll & 0xF00) | data;
220  DEBUGVERA("VERA: LAYER-%d#8 register write: vscroll=%d" NL, ln, layer[ln].vscroll);
221  break;
222  case 0x9:
223  data &= 0xF;
224  layer[ln].vscroll = (layer[ln].vscroll & 0xFF) | (data << 8);
225  DEBUGVERA("VERA: LAYER-%d#2 register write: vscroll=%d" NL, ln, layer[ln].vscroll);
226  break;
227  default:
228  DEBUGVERA("VERA: LAYER-%d#%d register write: ?UNKNOWN_REGISTER?=?%d?" NL, ln, reg & 0xF, data);
229  data = 0xFF; // FIXME: unused register returns with 0xFF?
230  break;
231  }
232  layer[ln].regspace[reg] = data;
233 }
234 
235 
236 static void recalculate_hwidth ( void )
237 {
238  if (composer.mode == 0) {
239  composer.hwidth = 0; // output disabled. We do this by faking hwidth is zero
240  } else if (composer.hstart < 640 && composer.hstart < composer.hstop) {
241  composer.hwidth = composer.hstop - composer.hstart;
242  if (composer.hstop > 640)
243  composer.hwidth -= (composer.hstop - 640);
244  } else
245  composer.hwidth = 0;
246  DEBUGVERA("VERA: %s() hstart=%d,hstop=%d,out_mode=%d -> hwidth=%d" NL, __func__, composer.hstart, composer.hstop, composer.mode, composer.hwidth);
247  if (XEMU_UNLIKELY(composer.hwidth < 0))
248  FATAL("hwidth becomes %d in %s() even after adjusing!", composer.hwidth, __func__);
249 }
250 
251 
252 
253 static void write_composer_register ( int reg, Uint8 data )
254 {
255  switch (reg) {
256  case 0: {
257  composer.chromakill = data & 4;
258  composer.mode = data & 3;
259  int is_mono_mode = (composer.mode == MODE_NTSC_COMPOSITE) && (data & 4);
260  if (is_mono_mode != composer.is_mono_mode) {
261  composer.is_mono_mode = is_mono_mode;
262  update_all_palette_entries();
263  }
264  DEBUGVERA("VERA: COMPOSER #0 register write: chroma_kill=%d output_mode=%d MONO_MODE=%d" NL, composer.chromakill ? 1 : 0, composer.mode, composer.is_mono_mode ? 1 : 0);
265  data = composer.field | (data & 7); // also use the interlace bit field [0x80 or 0x00] when register is used
266  }
267  recalculate_hwidth();
268  break;
269  case 1:
270  composer.hscale = data;
271  break;
272  case 2:
273  composer.vscale = data;
274  break;
275  case 3:
276  if (data != border.index) {
277  border.index = data;
278  border.colour = palette.colours[data];
279  border.changed = 1;
280  }
281  break;
282  case 4:
283  composer.hstart = (composer.hstart & 0xFF00) | data;
284  recalculate_hwidth();
285  break;
286  case 5:
287  composer.hstop = (composer.hstop & 0xFF00) | data;
288  recalculate_hwidth();
289  break;
290  case 6:
291  composer.vstart = (composer.vstart & 0xFF00) | data;
292  recalculate_hwidth();
293  break;
294  case 7:
295  composer.vstop = (composer.vstop & 0xFF00) | data;
296  recalculate_hwidth();
297  break;
298  case 8:
299  composer.hstart = (composer.hstart & 0xFF) | ((data & 3) << 8);
300  composer.hstop = (composer.hstop & 0xFF) | ((data & 12) << 6);
301  composer.vstart = (composer.vstart & 0xFF) | ((data & 16) << 4);
302  composer.vstop = (composer.vstop & 0xFF) | ((data & 32) << 3);
303  recalculate_hwidth();
304  data &= 0xFF - 0x80 - 0x40;
305  break;
306  case 9:
307  composer.irqline = (composer.irqline & 0xFF00) | data;
308  //for (int a = 0; a < 3; a++)
309  // composer.irqline_next = composer.irqline + 1;
310  break;
311  case 10:
312  data &= 1;
313  composer.irqline = (composer.irqline & 0xFF) | (data << 8);
314  //composer.irqline_next = composer.irqline + 1;
315  break;
316  default:
317  data = 0xFF; // FIXME: unused register returns with 0xFF?
318  break;
319  }
320  composer.regspace[reg] = data;
321 }
322 
323 
324 static void write_vmem_through_dataport ( int port, Uint8 data )
325 {
326  int addr = dataport[port].addr;
327  dataport[port].addr = (addr + dataport[port].inc) & 0xFFFFF;
328  // OK, let's see, what is about to be written ...
329  DEBUGVERA("VERA: writing VMEM at $%05X with data $%02X" NL, addr, data);
330  if (XEMU_LIKELY(addr < VRAM_SIZE)) {
331  vram[addr] = data;
332  return;
333  }
334  if (XEMU_UNLIKELY(addr < 0xF0000)) {
335  return; // writes between the "gap" of end of VRAM and begin of registers etc area does nothing
336  }
337  switch ((addr >> 12) & 0xF) {
338  case 0:
339  write_composer_register(addr & 0xF, data);
340  return;
341  case 1:
342  addr &= 0x1FF;
343  palette.regspace[addr] = data;
344  update_palette_entry(addr);
345  return;
346  case 2:
347  write_layer_register(0, addr & 0xF, data);
348  return;
349  case 3:
350  write_layer_register(1, addr & 0xF, data);
351  return;
352  case 4:
353  // FIXME: sprite registers to be implemented ...
354  return;
355  case 5:
356  // FIXME: sprite attributes to be implemented ...
357  return;
358  case 6:
359  // FIXME: audio registers to be implemented ...
360  return;
361  case 7:
362  if ((addr & 1) == 0)
363  serial.spi_received_data = sdcard_spi_transfer(data);
364  else {
365  data &= 1;
366  serial.spi_select = data;
368  }
369  return;
370  case 8:
371  // FIXME: UART does not emulated, just mostly the BAUD RATE setting / reading back ...
372  switch (addr & 3) {
373  case 0:
374  return; // we do not store, we can't TX yet, not emulated ...
375  case 1:
376  return; // not a writable register at all ...
377  case 2:
378  uart_set_baud_divisor((serial.uart_baud_divisor & 0xFF00) | data );
379  return;
380  case 3:
381  uart_set_baud_divisor((serial.uart_baud_divisor & 0x00FF) | (data << 8));
382  return;
383  }
384  return;
385  default:
386  DEBUGVERA("UNKNOWN_WRITE: address $%X" NL, addr);
387  return;
388  }
389 }
390 
391 
392 static Uint8 read_vmem_through_dataport ( int port )
393 {
394  int addr = dataport[port].addr;
395  dataport[port].addr = (addr + dataport[port].inc) & 0xFFFFF;
396  // OK, let's see, wwhat is about to read ...
397  if (XEMU_LIKELY(addr < VRAM_SIZE)) {
398  return vram[addr];
399  }
400  if (XEMU_UNLIKELY(addr < 0xF0000)) {
401  return 0xFF; // writes between the "gap" of end of VRAM and begin of registers etc area does nothing
402  }
403  switch ((addr >> 12) & 0xF) {
404  case 0:
405  return composer.regspace[addr & 0xF];
406  case 1:
407  return palette.regspace[addr & 0x1FF];
408  case 2:
409  return layer[0].regspace[addr & 0xF];
410  case 3:
411  return layer[1].regspace[addr & 0xF];
412  case 4:
413  // FIXME: sprite registers to be implemented ...
414  return 0xFF;
415  case 5:
416  // FIXME: sprite attributes to be implemented ...
417  return 0xFF;
418  case 6:
419  // FIXME: audio registers to be implemented ...
420  return 0xFF;
421  case 7:
422  if ((addr & 1) == 0)
423  return serial.spi_received_data; // READ of REG0: data received
424  else
425  return serial.spi_select; // READ of REG1: we never use the BUSY flag ...
426  case 8:
427  // FIXME: UART does not emulated, just mostly the BAUD RATE setting / reading back ...
428  switch (addr & 3) {
429  case 0:
430  return 0xFF; // received data, we have no ...
431  case 1:
432  return 0; // return with always non-busy on TX and RXFIFO empty
433  case 2:
434  return serial.uart_baud_divisor & 0xFF;
435  case 3:
436  return serial.uart_baud_divisor >> 8;
437  }
438  return 0xFF;
439  default:
440  DEBUGVERA("UNKNOWN_READ: address $%X" NL, addr);
441  return 0xFF;
442  }
443 }
444 
445 
446 
447 
449 {
450  //increment = (1 << ((data >> 4) - 1)) & 0xFFFF;
451  DEBUGVERA("VERA: writing register %d with data $%02X" NL, reg & 7, data);
452  switch (reg & 7) {
453  case 0:
454  dataport[current_port].regspace[0] = data;
455  dataport[current_port].addr = (dataport[current_port].addr & 0xFFF00) | data;
456  break;
457  case 1:
458  dataport[current_port].regspace[1] = data;
459  dataport[current_port].addr = (dataport[current_port].addr & 0xF00FF) | (data << 8);
460  break;
461  case 2:
462  dataport[current_port].regspace[2] = data;
463  dataport[current_port].inc = (1 << ((data >> 4) - 1)) & 0xFFFF;
464  dataport[current_port].addr = (dataport[current_port].addr & 0x0FFFF) | ((data & 0xF) << 16);
465  break;
466  case 3:
467  write_vmem_through_dataport(0, data);
468  break;
469  case 4:
470  write_vmem_through_dataport(1, data);
471  break;
472  case 5:
473  if (XEMU_UNLIKELY(data & 0x80))
474  vera_reset();
475  else
476  current_port = data & 1;
477  break;
478  case 6:
479  ien_reg = data & INTERRUPT_REGISTER_MASK;
480  UPDATE_IRQ();
481  break;
482  case 7:
483  isr_reg &= (~data) & INTERRUPT_REGISTER_MASK;
484  UPDATE_IRQ();
485  break;
486  }
487 }
488 
489 
491 {
492  switch (reg & 7) {
493  case 0:
494  return dataport[current_port].regspace[0];
495  case 1:
496  return dataport[current_port].regspace[1];
497  case 2:
498  return dataport[current_port].regspace[2];
499  case 3:
500  return read_vmem_through_dataport(0);
501  case 4:
502  return read_vmem_through_dataport(1);
503  case 5:
504  return current_port; // only give back dataport. We always reports back "no reset" state, though we DO it on write, within zero cycles ;-P
505  case 6:
506  return ien_reg;
507  case 7:
508  return isr_reg & ien_reg; // it seems from the doc, that only the 'effective' bits are set, ie if also that source is enabled
509  }
510  return 0xFF; // some C compilers cannot figure out, that all possible cases ARE handled above, and gives a warning without this ... :(
511 }
512 
513 
514 #if 0
515 static Uint32 *pixel;
516 static int scanline;
517 //static int map_base; //, tile_base;
518 static int tile_row;
519 static int map_counter;
520 #endif
521 
522 
523 
524 int vera_dump_vram ( const char *fn )
525 {
526  return dump_stuff(fn, vram, VRAM_SIZE);
527 }
528 
529 
530 #if 0
531 void vera_vsync ( void )
532 {
533  int tail;
534  pixel = xemu_start_pixel_buffer_access(&tail);
535  scanline = 0;
536  //map_base = (L1REGS[2] << 2) | (L1REGS[3] << 10);
537  //tile_base = (L1REGS[4] << 2) | (L1REGS[5] << 10);
538  map_counter = 0;
539  tile_row = 0;
540 #if 0
541  fprintf(stderr, "L1: EN=%d MODE=%d MAP_BASE=%d TILE_BASE=%d TILEH=%d TILEW=%d MAPH=%d MAPW=%d "
542  "HSTOP=%d HSTART=%d VSTOP=%d VSTART=%d OUT_MODE=%d "
543  "IEN=%d\n",
544  L1REGS[0] & 1, L1REGS[0] >> 5, layer[0].map_base, layer[0].tile_base,
545  (L1REGS[1] >> 5) & 1, // TILEH
546  (L1REGS[1] >> 4) & 1, // TILEW
547  (L1REGS[1] >> 2) & 3, // MAPH
548  L1REGS[1] & 3, // MAPW
549  DCREGS[5] | (((DCREGS[8] >> 2) & 3) << 8), // HSTOP
550  DCREGS[4] | ((DCREGS[8] & 3) << 8), // HSTART
551  DCREGS[7] | (((DCREGS[8] >> 5) & 1) << 8), // VSTOP
552  DCREGS[6] | (((DCREGS[8] >> 4) & 1) << 8), // VSTART
553  DCREGS[0] & 3,
554  ien_reg
555  );
556 #endif
557 // isr_reg |= 1;
558 // UPDATE_IRQ();
559  SET_IRQ(VSYNC_IRQ);
560 }
561 #endif
562 
563 
564 static void render_text_c16 ( int ln, Uint8 *op, int active_size )
565 {
566  Uint8 *vp = vram + layer[ln].map_base + layer[ln].counter_map;
567  for (;;) {
568  Uint8 chr = vram[layer[ln].tile_base + (*vp << 3) + layer[ln].counter_tile_row];
569  vp++;
570  Uint8 fg = *vp & 0xF, bg = *vp >> 4 ;
571  for (int b = 128; b; b >>= 1) {
572  *op++ = (chr & b) ? fg : bg;
573  active_size--;
574  // TODO: later optimization: allow to check only after the bit loop ...
575  // Yes, it can cause emit too much pixel, but it can be handled later with border
576  // rendering at the end, which will cover it. And it's faster not to check this
577  // condition inside this loop at least (also it's more likely that the loop can be unrolled for more speed ...)
578  // This will also rewquire to make output buffer at least 7 bytes larger not to overflow in this case if scrolled by pixel resolution
579  if (XEMU_UNLIKELY(active_size == 0)) {
580  if (layer[ln].counter_tile_row == 7) {
581  layer[ln].counter_tile_row = 0;
582  layer[ln].counter_map += 1 << (layer[ln].mapw + 6); // FIXME: CHECK: is this correct?
583  } else
584  layer[ln].counter_tile_row++;
585  return;
586  }
587  }
588  vp++;
589  }
590 }
591 
592 
593 static void render_bitmap_c256 ( int ln, Uint8 *op, int active_size )
594 {
595  Uint8 *vp = vram + layer[ln].tile_base + layer[ln].counter_map; // FIXME looks ugly to use counter_map for tile ...
596  memcpy(op, vp, active_size);
597  layer[ln].counter_map += active_size; // FIXME: ???
598 }
599 
600 
601 
602 // CURRENTLY a hard coded case, for 16 colour text mode using layer-1 only (what is the default used one by the KERNAL/BASIC at least)
603 int vera_render_line ( void )
604 {
605  static Uint32 *pixel;
606  static Uint8 vera_line[640]; // 256-colour pixel values in X16 colour space. it will be rendered later into native SDL stuff
607  static int vera_line_is_border = -1;
608  static int scanline = 0;
609  if (XEMU_UNLIKELY(scanline == 0)) {
610  layer[0].counter_map = 0;
611  layer[1].counter_map = 0;
612  layer[0].counter_tile_row = 0;
613  layer[1].counter_tile_row = 0;
614  int tail; // not so much used, it should be zero
615  pixel = xemu_start_pixel_buffer_access(&tail);
616  if (XEMU_UNLIKELY(tail))
617  FATAL("Texture TAIL is not zero");
618  }
619  if (XEMU_LIKELY(scanline < 480)) { // actual screen content as VGA signal, can be still border (thus 'inactive'), etc ...
620  if (XEMU_LIKELY(scanline < composer.vstop && scanline >= composer.vstart && composer.hwidth)) {
621  if (vera_line_is_border != border.index) {
622  memset(vera_line, 640, border.index); // first, fill with border (ie "display is inactive") colour
623  vera_line_is_border = border.index;
624  }
625  for (int ln = 0; ln < 2; ln++)
626  if (layer[ln].enabled) {
627  vera_line_is_border = -1;
628  switch (layer[ln].mode) {
629  case 0:
630  render_text_c16(ln, vera_line + composer.hstart, composer.hwidth);
631  break;
632  case 7:
633  render_bitmap_c256(ln, vera_line + composer.hstart, composer.hwidth);
634  break;
635  default:
636  fprintf(stderr, "Unimplemented video mode #%d on layer#%d" NL, layer[ln].mode, ln);
637  break;
638  }
639  }
640  // Finally, fill the texture with our line, now already with SDL-specific colour values
641  for (int x = 0; x < 640; x++)
642  *pixel++ = palette.colours[vera_line[x]];
643  } else {
644  // Otherwise (composer does not active in the visible line), it's just border, so no layers, no sprites, etc ...
645  for (int x = 0; x < 640; x++)
646  *pixel++ = border.colour;
647  }
648  }
649  // Manage end of full frame and vsync IRQ handling
650  if (XEMU_UNLIKELY(scanline == SCANLINES_TOTAL - 1)) {
651  scanline = 0;
652  } else {
653  scanline++;
655  isr_reg |= VSYNC_IRQ;
657  isr_reg &= ~VSYNC_IRQ;
658  }
659  // Check and deal with raster IRQ condition [for the next line ... so our line based emulation is more OK]
660  if (XEMU_UNLIKELY(scanline == composer.irqline))
661  isr_reg |= RASTER_IRQ;
662  else
663  isr_reg &= ~RASTER_IRQ;
664  // Then, update the IRQ status
665  UPDATE_IRQ();
666  return scanline;
667 #if 0
668  //fprintf(stderr, "TILE BASE is: $%X" NL, layer[0].tile_base);
669  //layer[0].tile_base = 0xF800; // HACK! FIXME
670  //DEBUGPRINT("tile base=$%X" NL, layer[0].tile_base);
671  if (layer[BITMAP_LAYER].mode == 7) {
672  Uint8 *v = vram + layer[BITMAP_LAYER].tile_base + map_counter;
673  for (int x = 0; x < 640; x++)
674  *pixel++ = palette.colours[*v++];
675  } else {
676  Uint8 *v = vram + layer[TEXT_LAYER].map_base + map_counter;
677  for (int x = 0; x < 80; x++) {
678  Uint8 chr = vram[layer[TEXT_LAYER].tile_base + ((*v++) << 3) + tile_row];
679  Uint32 fg = palette.colours[*v & 0xF];
680  Uint32 bg = palette.colours[(*v++) >> 4 ];
681  for (int b = 128; b; b >>= 1) {
682  *pixel++ = (chr & b) ? fg : bg;
683  }
684  }
685  }
686  if (isr_reg & 1) {
687  //isr_reg &= ~1;
688  //UPDATE_IRQ();
689  CLEAR_IRQ(VSYNC_IRQ);
690  }
691  scanline++;
692  if (layer[BITMAP_LAYER].mode == 7) {
693  map_counter += 640;
694  return (scanline >= 480);
695  } else {
696  if (tile_row == 7) {
697  tile_row = 0;
698  // map_base += 256 - 80*2;
699  map_counter += 256;
700  return (scanline >= 480);
701  } else {
702  tile_row++;
703  // map_base -= 80 * 2;
704  return 0;
705  }
706  }
707  // return !scanline;
708 #endif
709 }
710 
711 
712 void vera_reset ( void )
713 {
714  current_port = 0;
715  memset(dataport, 0, sizeof dataport);
716  // Serial stuffs
717  serial.spi_select = 0;
718  sdcard_spi_select(serial.spi_select);
719  serial.spi_received_data = 0xFF;
720  uart_set_baud_divisor((UART_CLOCK / UART_DEFAULT_BAUD_RATE) - 1);
721  // Make sure, that we clear (maybe there was some during the reset request) + disable interrupts ...
722  ien_reg = 0;
723  isr_reg = 0;
724  UPDATE_IRQ();
725  // Populate the default palette for VERA after reset.
726  border.index = 0; // update palette step below will take it account then ...
727  for (int a = 0, b = 0; a < 0x100;) {
728  palette.regspace[b++] = x16paldata[a ];
729  palette.regspace[b ] = x16paldata[a++] >> 8;
730  update_palette_entry(b++);
731  }
732  // Clear all registers
733  // This is needed, as those write functions decodes the meanings to structs
734  // It's more easy,quick to solve this way, rather than to init those decoded stuffs by "hand",
735  // also would result in code duplication more or less ...
736  for (int a = 0; a < 0x10; a++) {
737  write_layer_register(0, a, 0);
738  write_layer_register(1, a, 0);
739  write_composer_register(a, 0);
740  }
741 }
742 
743 
744 
745 // This function must be called only ONCE, before other vera... functions!
746 void vera_init ( void )
747 {
748  // Generate full system palette [4096 colours]
749  // Yeah, it's 4096 * 4 * 2 bytes of memory (*2 because of chroma killed mono version as well), but
750  // that it's still "only" 32Kbytes, nothing for a modern PC, but then we have the advantage to get SDL formatted
751  // DWORD available for any colour, without any conversion work in run-time!
752  for (int r = 0, index = 0; r < 0x10; r++)
753  for (int g = 0; g < 0x10; g++)
754  for (int b = 0; b < 0x10; b++, index++) {
755  // 0xFF = alpha channel
756  // "*17" => 255/15 = 17.0 Thus we can reach full brightness on 4 bit RGB component values
757  palette.all[index] = SDL_MapRGBA(sdl_pix_fmt, r * 17, g * 17, b * 17, 0xFF);
758  // About CHROMAKILL:
759  // this is not the right solution too much, but anyway
760  // RGB -> mono conversion would need some gamma correction and whatever black magic still ...
761  // the formula is taken from wikipedia/MSDN, BUT as I noted above, it's maybe incorrect because of the lack of gamma correction
762  // FIXME: we would need to see HDL implmenetation of VERA to be sure what it uses ...
763  //int mono = ((0.2125 * r) + (0.7154 * g) + (0.0721 * b)) * 17;
764  int mono = ((0.299 * (float)r) + (0.587 * (float)g) + (0.114 * (float)b)) * 17.0;
765  palette.all_mono[index] = SDL_MapRGBA(sdl_pix_fmt, mono, mono, mono, 0xFF);
766  }
767  SDL_free(sdl_pix_fmt); // we don't need this any more
768  sdl_pix_fmt = NULL;
769  // Clear all of vram
770  memset(vram, 0, sizeof vram);
771  // Reset VERA
772  vera_reset();
773 }
spi_received_data
Uint8 spi_received_data
Definition: vera.c:90
commander_x16.h
vstop
int vstop
Definition: vera.c:73
vera.h
serial
int serial
Definition: primo.c:142
index
int index
Definition: vera.c:66
vscroll
int vscroll
Definition: vera.c:60
UART_CLOCK
#define UART_CLOCK
Definition: vera.c:41
emutools.h
sdcard_spi_transfer
Uint8 sdcard_spi_transfer(Uint8 data)
Definition: sdcard.c:39
INTERRUPT_REGISTER_MASK
#define INTERRUPT_REGISTER_MASK
Definition: vera.c:39
MODE_NTSC_COMPOSITE
#define MODE_NTSC_COMPOSITE
Definition: vera.c:37
field
int field
Definition: vera.c:75
colour
Uint32 colour
Definition: vera.c:67
hscroll
int hscroll
Definition: vera.c:60
vera_dump_vram
int vera_dump_vram(const char *fn)
Definition: vera.c:524
DEBUGVERA
#define DEBUGVERA
Definition: vera.c:29
uart_baud_rate
int uart_baud_rate
Definition: vera.c:94
vera_render_line
int vera_render_line(void)
Definition: vera.c:603
sdl_pix_fmt
SDL_PixelFormat * sdl_pix_fmt
Definition: emutools.c:80
enabled
int enabled
Definition: vera.c:61
fn
const char * fn
Definition: roms.c:42
maph
int maph
Definition: vera.c:61
XEMU_INLINE
#define XEMU_INLINE
Definition: emutools_basicdefs.h:126
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
spi_select
int spi_select
Definition: vera.c:91
Uint32
uint32_t Uint32
Definition: fat32.c:49
is_mono_mode
int is_mono_mode
Definition: vera.c:78
dump_stuff
int dump_stuff(const char *fn, void *mem, int size)
Definition: commander_x16.c:307
Uint8
uint8_t Uint8
Definition: fat32.c:51
hwidth
int hwidth
Definition: vera.c:79
inc
int inc
Definition: vera.c:53
hstart
int hstart
Definition: vera.c:73
counter_tile_row
int counter_tile_row
Definition: vera.c:62
tilew
int tilew
Definition: vera.c:61
emutools_files.h
x
int x
Definition: console.c:27
SCANLINE_START_VSYNC
#define SCANLINE_START_VSYNC
Definition: vera.c:34
map_base
int map_base
Definition: vera.c:59
xemu_start_pixel_buffer_access
Uint32 * xemu_start_pixel_buffer_access(int *texture_tail)
Definition: emutools.c:1153
bitmap_palette_offset
int bitmap_palette_offset
Definition: vera.c:60
changed
int changed
Definition: vera.c:68
VSYNC_IRQ
#define VSYNC_IRQ
Definition: vera.c:44
vera_reset
void vera_reset(void)
Definition: vera.c:712
mapw
int mapw
Definition: vera.c:61
chromakill
int chromakill
Definition: vera.c:77
hscale
int hscale
Definition: vera.c:74
XEMU_LIKELY
#define XEMU_LIKELY(__x__)
Definition: emutools_basicdefs.h:124
mode
int mode
Definition: vera.c:61
vera_init
void vera_init(void)
Definition: vera.c:746
NL
#define NL
Definition: fat32.c:37
compress_sd_image.r
def r
Definition: compress_sd_image.py:76
SCANLINE_STOP_VSYNC
#define SCANLINE_STOP_VSYNC
Definition: vera.c:35
tileh
int tileh
Definition: vera.c:61
cpu65.h
vera_read_cpu_register
Uint8 vera_read_cpu_register(int reg)
Definition: vera.c:490
all
Uint32 all[4096]
Definition: vera.c:84
counter_map
int counter_map
Definition: vera.c:62
SCANLINES_TOTAL
#define SCANLINES_TOTAL
Definition: vera.c:33
RASTER_IRQ
#define RASTER_IRQ
Definition: vera.c:45
addr
int addr
Definition: vera.c:52
colours
Uint32 colours[0x100]
Definition: vera.c:83
Uint16
uint16_t Uint16
Definition: fat32.c:50
regspace
Uint8 regspace[3]
Definition: vera.c:51
spi_busy
int spi_busy
Definition: vera.c:92
vstart
int vstart
Definition: vera.c:73
hstop
int hstop
Definition: vera.c:73
UART_DEFAULT_BAUD_RATE
#define UART_DEFAULT_BAUD_RATE
Definition: vera.c:42
sdcard.h
all_mono
Uint32 all_mono[4096]
Definition: vera.c:85
FATAL
#define FATAL(...)
Definition: xep128.h:117
tile_base
int tile_base
Definition: vera.c:59
sdcard_spi_select
void sdcard_spi_select(int select)
Definition: sdcard.c:29
XEMU_UNLIKELY
#define XEMU_UNLIKELY(__x__)
Definition: emutools_basicdefs.h:125
vera_write_cpu_register
void vera_write_cpu_register(int reg, Uint8 data)
Definition: vera.c:448
scanline
int scanline
Definition: vic6561.c:52
uart_baud_divisor
int uart_baud_divisor
Definition: vera.c:93
vscale
int vscale
Definition: vera.c:74
irqline
int irqline
Definition: vera.c:76
VRAM_SIZE
#define VRAM_SIZE
Definition: vera.c:40