fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/chipset/e6560.c *
7 * Created: 2020-04-18 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2020 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <stdlib.h>
24#include <stdio.h>
25
26#include "e6560.h"
27
28
29void e6560_init (e6560_t *vic)
30{
31 unsigned i;
32
33 vic->hsync_ext = NULL;
34 vic->hsync = NULL;
35
36 vic->vsync_ext = NULL;
37 vic->vsync = NULL;
38
39 vic->sound_ext = NULL;
40 vic->sound = NULL;
41
42 for (i = 0; i < 64; i++) {
43 vic->memmap[i] = NULL;
44 }
45
46 vic->colram = NULL;
47
48 vic->w = 260;
49 vic->h = 261;
50
51 vic->frame = 0;
52
53 vic->ptr = vic->buf;
54
55 vic->snd_enable = 0;
56 vic->snd_rem = 0;
57
58 vic->snd_clk_inp = 1000000;
59 vic->snd_clk_out = 44100;
60 vic->snd_clk_cnt = 0;
61
62 vic->snd_buf_cnt = 0;
63 vic->snd_buf_max = 256;
64}
65
66void e6560_free (e6560_t *vic)
67{
68 if (vic->sound != NULL) {
69 vic->sound (vic->sound_ext, vic->snd_buf, 0, 1);
70 }
71}
72
73void e6560_set_hsync_fct (e6560_t *vic, void *ext, void *fct)
74{
75 vic->hsync_ext = ext;
76 vic->hsync = fct;
77}
78
79void e6560_set_vsync_fct (e6560_t *vic, void *ext, void *fct)
80{
81 vic->vsync_ext = ext;
82 vic->vsync = fct;
83}
84
85void e6560_set_sound_fct (e6560_t *vic, void *ext, void *fct)
86{
87 vic->sound_ext = ext;
88 vic->sound = fct;
89}
90
91void e6560_set_memmap (e6560_t *vic, unsigned pa, unsigned pn, const unsigned char *ptr)
92{
93 while ((pa < 64) && (pn > 0)) {
94 vic->memmap[pa] = ptr;
95
96 pa += 1;
97 pn -= 1;
98
99 if (ptr != NULL) {
100 ptr += 256;
101 }
102 }
103}
104
105void e6560_set_colram (e6560_t *vic, const unsigned char *ptr)
106{
107 vic->colram = ptr;
108}
109
110void e6560_set_pal (e6560_t *vic, int val)
111{
112 if (val) {
113 vic->w = 284;
114 vic->h = 312;
115 }
116 else {
117 vic->w = 260;
118 vic->h = 261;
119 }
120}
121
122void e6560_set_clock (e6560_t *vic, unsigned long val)
123{
124 vic->snd_clk_inp = val;
125}
126
127void e6560_set_srate (e6560_t *vic, unsigned long val)
128{
129 if (val == 0) {
130 return;
131 }
132
133 vic->snd_clk_out = val;
134 vic->snd_clk_cnt = 0;
135}
136
137static
138void vic_sound_start (e6560_t *vic)
139{
140 unsigned i;
141
142 vic->snd_buf_cnt = 0;
143 vic->snd_rem = 2 * vic->snd_clk_out;
144 vic->snd_clk_cnt = 0;
145
146 if (vic->sound == NULL) {
147 return;
148 }
149
150 for (i = 0; i < vic->snd_buf_max; i++) {
151 vic->snd_buf[i] = 0x8000;
152 }
153
154 vic->sound (vic->sound_ext, vic->snd_buf, vic->snd_buf_max, 0);
155 vic->sound (vic->sound_ext, vic->snd_buf, vic->snd_buf_max, 0);
156}
157
158static
159void vic_sound_stop (e6560_t *vic)
160{
161 if (vic->sound != NULL) {
162 vic->sound (vic->sound_ext, vic->snd_buf, vic->snd_buf_cnt, 1);
163 }
164
165 vic->snd_buf_cnt = 0;
166}
167
168static
169void vic_sound_enable (e6560_t *vic, unsigned chn, unsigned char val)
170{
171 e6560_chn_t *c;
172
173 c = vic->chn + (chn & 3);
174
175 if (val & 0x80) {
176 vic->snd_enable |= 1 << chn;
177
178 c->div = (128 - ((val + 1) & 0x7f)) << (7 - chn);
179 c->cnt = c->div;
180 c->smp = -64;
181
182 if (vic->snd_rem == 0) {
183 vic_sound_start (vic);
184 }
185 }
186 else {
187 vic->snd_enable &= ~(1 << chn);
188
189 c->smp = 0;
190 }
191}
192
193static
194void vic_sound_out (e6560_t *vic)
195{
196 int val;
197 uint16_t tmp;
198 e6560_chn_t *chn;
199
200 if (vic->snd_rem == 0) {
201 return;
202 }
203
204 vic->snd_clk_cnt += vic->snd_clk_out;
205
206 if (vic->snd_clk_cnt < vic->snd_clk_inp) {
207 return;
208 }
209
210 vic->snd_clk_cnt -= vic->snd_clk_inp;
211
212 chn = vic->chn;
213
214 val = (chn[0].smp + chn[1].smp + chn[2].smp + chn[3].smp);
215 val *= ((vic->reg[14] & 15) + 1) * 4;
216 tmp = val;
217
218 vic->snd_buf[vic->snd_buf_cnt++] = tmp ^ 0x8000;
219
220 if (vic->snd_buf_cnt >= vic->snd_buf_max) {
221 if (vic->sound != NULL) {
222 vic->sound (vic->sound_ext, vic->snd_buf, vic->snd_buf_max, 0);
223 }
224
225 vic->snd_buf_cnt = 0;
226 }
227
228 if (--vic->snd_rem == 0) {
229 vic_sound_stop (vic);
230 }
231}
232
233static
234void vic_sound_clock (e6560_t *vic)
235{
236 unsigned i;
237 e6560_chn_t *chn;
238
239 if (vic->snd_enable == 0) {
240 return;
241 }
242
243 vic->snd_rem = 2 * vic->snd_clk_out;
244
245 for (i = 0; i < 4; i++) {
246 if (~vic->snd_enable & (1 << i)) {
247 continue;
248 }
249
250 chn = vic->chn + i;
251
252 if (--chn->cnt > 0) {
253 continue;
254 }
255
256 chn->cnt = chn->div;
257 chn->smp = -chn->smp;
258
259 if (i == 3) {
260 if (vic->snd_lfsr & 1) {
261 vic->snd_lfsr = (vic->snd_lfsr >> 1) ^ 0x80000057;
262 chn->smp = -chn->smp;
263 }
264 else {
265 vic->snd_lfsr = vic->snd_lfsr >> 1;
266 }
267 }
268 }
269}
270
271static
272void vic_update_line (e6560_t *vic)
273{
274 vic->reg[3] = (vic->reg[3] & 0x7f) | ((vic->y & 1) << 7);
275 vic->reg[4] = vic->y >> 1;
276}
277
278static
279void vic_update_colmap (e6560_t *vic)
280{
281 vic->reverse = (vic->reg[15] >> 3) & 1;
282
283 vic->colmap1[vic->reverse] = 0;
284 vic->colmap1[vic->reverse ^ 1] = (vic->reg[15] >> 4) & 0x0f;
285
286 vic->colmap2[0] = (vic->reg[15] >> 4) & 0x0f;
287 vic->colmap2[1] = vic->reg[15] & 0x07;
288 vic->colmap2[2] = 0;
289 vic->colmap2[3] = (vic->reg[14] >> 4) & 0x0f;
290}
291
292static
293void vic_update_vaddr (e6560_t *vic)
294{
295 vic->vbase = (vic->reg[5] << 6) & 0x3c00;
296 vic->vbase |= (vic->reg[2] << 2) & 0x200;
297 vic->cbase = (vic->reg[5] << 10) & 0x3c00;
298}
299
300unsigned char e6560_get_reg (e6560_t *vic, unsigned long addr)
301{
302 if ((addr == 3) || (addr == 4)) {
303 vic_update_line (vic);
304 }
305
306 if (addr < 16) {
307 return (vic->reg[addr]);
308 }
309
310 return (0xaa);
311}
312
313void e6560_set_reg (e6560_t *vic, unsigned long addr, unsigned char val)
314{
315 if (addr >= 16) {
316 return;
317 }
318
319 vic->reg[addr] = val;
320
321 if (addr == 0) {
322 vic->start_x = (val & 0x7f) << 2;
323 }
324
325 if (addr == 1) {
326 vic->start_y = (val & 0xff) << 1;
327 }
328
329 if ((addr == 2) || (addr == 5)) {
330 vic_update_vaddr (vic);
331 }
332
333 if (addr == 3) {
334 vic->line_shift = 3 + (val & 1);
335 vic->line_cnt = 1 << vic->line_shift;
336 }
337
338 if ((addr == 14) || (addr == 15)) {
339 vic_update_colmap (vic);
340 }
341
342 if ((addr >= 10) && (addr <= 13)) {
343 vic_sound_enable (vic, addr - 10, vic->reg[addr]);
344 }
345}
346
347void e6560_reset (e6560_t *vic)
348{
349 unsigned i;
350
351 for (i = 0; i < 16; i++) {
352 vic->reg[i] = 0;
353 }
354
355 vic->x = 0;
356 vic->y = 0;
357
358 vic->start_x = 0;
359 vic->start_y = 0;
360
361 vic->col = 0;
362 vic->row = 0;
363 vic->col_cnt = 0;
364 vic->row_cnt = 0;
365
366 vic->line = 0;
367 vic->line_cnt = 8;
368 vic->line_shift = 3;
369
370 vic->vbase = 0;
371 vic->cbase = 0;
372
373 vic->addr1 = 0;
374 vic->addr2 = 0;
375
376 vic->next = 0;
377
378 vic->ptr = vic->buf;
379
380 vic->colmap1[0] = 0;
381 vic->colmap1[1] = 0;
382
383 for (i = 0; i < 4; i++) {
384 vic->colmap2[i] = 0;
385 }
386
387 vic->snd_enable = 0;
388 vic->snd_lfsr = 1;
389 vic->snd_clk_cnt = 0;
390
391 for (i = 0; i < 4; i++) {
392 vic->chn[i].cnt = 0;
393 vic->chn[i].div = 0;
394 vic->chn[i].smp = 0;
395 }
396}
397
398static
399unsigned char vic_fetch_data (e6560_t *vic, unsigned addr)
400{
401 const unsigned char *ptr;
402
403 ptr = vic->memmap[(addr & 16383) >> 8];
404
405 if (ptr != NULL) {
406 return (ptr[addr & 0xff]);
407 }
408
409 return (0);
410}
411
412static
413unsigned char vic_fetch_color (e6560_t *vic, unsigned addr)
414{
415 if (vic->colram == NULL) {
416 return (0);
417 }
418
419 return (vic->colram[addr & 0x3ff]);
420}
421
422static
423void e6560_clock_char (e6560_t *vic)
424{
425 unsigned i;
426 unsigned addr;
427 unsigned char val, col;
428 unsigned char *map;
429
430 if ((vic->col_cnt == 0) || (vic->row_cnt == 0)) {
431 /* in border */
432
433 val = vic->reg[15] & 7;
434
435 for (i = 0; i < 4; i++) {
436 *(vic->ptr++) = val;
437 }
438
439 return;
440 }
441
442 if (vic->next == 0) {
443 /* fetch character code */
444
445 vic->chr = vic_fetch_data (vic, vic->vbase + vic->addr2);
446 vic->color = vic_fetch_color (vic, vic->vbase + vic->addr2);
447
448 vic->next = 1;
449 vic->addr2 += 1;
450
451 return;
452 }
453
454 /* update one line of one character */
455
456 addr = vic->cbase + (vic->chr << vic->line_shift) + vic->line;
457 val = vic_fetch_data (vic, addr);
458
459 vic->next = 0;
460 vic->col += 1;
461 vic->col_cnt -= 1;
462
463 if (vic->color & 0x08) {
464 map = vic->colmap2;
465 map[2] = vic->color & 7;
466
467 for (i = 0; i < 4; i++) {
468 col = map[(val >> 6) & 3];
469 *(vic->ptr++) = col;
470 *(vic->ptr++) = col;
471 val <<= 2;
472 }
473 }
474 else {
475 map = vic->colmap1;
476 map[vic->reverse] = vic->color & 7;
477
478 for (i = 0; i < 8; i++) {
479 *(vic->ptr++) = map[(val >> 7) & 1];
480 val <<= 1;
481 }
482 }
483}
484
485void e6560_clock (e6560_t *vic)
486{
487 if (vic->snd_rem > 0) {
488 vic_sound_clock (vic);
489 vic_sound_out (vic);
490 }
491
492 if (vic->x == vic->start_x) {
493 vic->col_cnt = vic->reg[2] & 0x7f;
494 }
495
496 e6560_clock_char (vic);
497
498 vic->x += 4;
499
500 if (vic->x < vic->w) {
501 return;
502 }
503
504 if (vic->hsync != NULL) {
505 vic->hsync (vic->hsync_ext, vic->y, vic->w, vic->buf);
506 }
507
508 vic->x = 0;
509 vic->next = 0;
510 vic->ptr = vic->buf;
511 vic->col = 0;
512 vic->col_cnt = 0;
513
514 vic->line += 1;
515
516 if (vic->line >= vic->line_cnt) {
517 vic->line = 0;
518
519 if (vic->row_cnt > 0) {
520 vic->row += 1;
521 vic->row_cnt -= 1;
522 }
523
524 vic->addr1 = vic->addr2;
525 }
526 else {
527 vic->addr2 = vic->addr1;
528 }
529
530 vic->y += 1;
531
532 if (vic->y == vic->start_y) {
533 vic->row = 0;
534 vic->line = 0;
535 vic->row_cnt = (vic->reg[3] >> 1) & 0x3f;
536 vic->addr1 = 0;
537 vic->addr2 = 0;
538 }
539
540 if (vic->y < vic->h) {
541 return;
542 }
543
544 if (vic->vsync != NULL) {
545 vic->vsync (vic->vsync_ext);
546 }
547
548 vic->y = 0;
549 vic->row = 0;
550 vic->line = 0;
551 vic->row_cnt = 0;
552 vic->addr1 = 0;
553 vic->addr2 = 0;
554 vic->frame += 1;
555}