Subversion Repositories Shiroi

Rev

Rev 42 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
7 nishi 1
#pragma once
2
/*
3
    mc6847.h -- Motorola 6847 Video Display Generator
4
 
5
    Do this:
6
	#define CHIPS_IMPL
7
    before you include this file in *one* C or C++ file to create the
8
    implementation.
9
 
10
    Optionally provide the following macros with your own implementation
11
 
12
	CHIPS_ASSERT(c)     -- your own assert macro (default: assert(c))
13
 
14
    EMULATED PINS:
15
 
16
		  +-----------+
17
	    FS <--|           |--> A0
18
	    HS <--|           |...
19
	  (RP) <--|           |--> A12
20
		  |           |
21
	    AG -->|           |
22
	    AS -->|  MC6847   |
23
	INTEXT -->|           |<-- D0
24
	   GM0 -->|           |...
25
	   GM1 -->|           |<-- D7
26
	   GM2 -->|           |
27
	   INV -->|           |
28
	   CSS -->|           |
29
		  +-----------+
30
 
31
    FIXME: documentation
32
 
33
    ## zlib/libpng license
34
 
35
    Copyright (c) 2018 Andre Weissflog
36
    This software is provided 'as-is', without any express or implied warranty.
37
    In no event will the authors be held liable for any damages arising from the
38
    use of this software.
39
    Permission is granted to anyone to use this software for any purpose,
40
    including commercial applications, and to alter it and redistribute it
41
    freely, subject to the following restrictions:
42
	1. The origin of this software must not be misrepresented; you must not
43
	claim that you wrote the original software. If you use this software in a
44
	product, an acknowledgment in the product documentation would be
45
	appreciated but is not required.
46
	2. Altered source versions must be plainly marked as such, and must not
47
	be misrepresented as being the original software.
48
	3. This notice may not be removed or altered from any source
49
	distribution.
50
*/
51
#include <stdint.h>
52
#include <stdbool.h>
53
#include <stddef.h>
54
 
55
#ifdef __cplusplus
56
extern "C" {
57
#endif
58
 
59
/*
60
    Pin definitions
61
 
62
    The 13 address bus pins A0 to A12 are on the same location as the
63
    first 13 CPU address bus pins.
64
 
65
    The data bus pins are on the same location as the CPU's.
66
 
67
    The control pins start at location 40.
68
 
69
    For the synchronization output pins FS (field sync), HS (horizontal sync),
70
    and RP (Row Preset) not only the current state is important, but
71
    also the transitions from active to inactive. To capture these
72
    transitions, two extra 64-bit pin struct members are provided
73
    which store the raising and falling transition state for each
74
    pin bit.
75
 
76
    FS (field sync)         --  The inactive-to-active transition coincides with the
77
				end of the active display area. The active-to-inactive
78
				transition coincides with the trailing edge of the
79
				vertical synchronization pulse.
80
 
81
    HS (horizontal sync)    --  The HS pulse coincides with the horizontal synchronization
82
				pulse furnished to the television receiver by the VDG.
83
				The inactive-to-active transition coincides with the
84
				leading edge of the horizontal synchronization pulse
85
				and the active-to-inactive transition coincides with the
86
				trailing egde.
87
 
88
    RP (row preset)         --  The RP pin will go active every 12 lines to reset
89
				a counter for an optional external character rom.
90
 
91
    Mode select pin functions:
92
 
93
    | AG | AS | INT/EXT | INV | GM2 | GM1 | GM0 | # colors | description
94
    +----+----+---------+-----+-----+-----+-----+----------+------------------------------
95
    |  0 |  0 |    0    |  0  |  -  |  -  |  -  |     2    | internal alphanum
96
    |  0 |  0 |    0    |  1  |  -  |  -  |  -  |     2    | internal alphanum, inverted
97
    |  0 |  0 |    1    |  0  |  -  |  -  |  -  |     2    | external alphanum
98
    |  0 |  0 |    1    |  1  |  -  |  -  |  -  |     2    | external alphanum, inverted
99
    +----+----+---------+-----+-----+-----+-----+----------+------------------------------
100
    |  0 |  1 |    0    |  -  |  -  |  -  |  -  |     8    | semigraphics4 (SG4)
101
    |  0 |  1 |    1    |  -  |  -  |  -  |  -  |     8    | semigraphics6 (SG6)
102
    +----+----+---------+-----+-----+-----+-----+----------+------------------------------
103
    |  1 |  - |    -    |  -  |  0  |  0  |  0  |     4    | 64x64 color graphics 1 (CG1)
104
    |  1 |  - |    -    |  -  |  0  |  0  |  1  |     2    | 128x64 resolution graphics 1 (RG1)
105
    |  1 |  - |    -    |  -  |  0  |  1  |  0  |     4    | 128x64 color graphics 2 (CG2)
106
    |  1 |  - |    -    |  -  |  0  |  1  |  1  |     2    | 128x96 resolution graphics 2 (RG2)
107
    |  1 |  - |    -    |  -  |  1  |  0  |  0  |     4    | 128x96 color graphics 3 (CG3)
108
    |  1 |  - |    -    |  -  |  1  |  0  |  1  |     2    | 128x192 resolution graphics 3 (RG3)
109
    |  1 |  - |    -    |  -  |  1  |  1  |  0  |     4    | 128x192 color graphics 6 (CG6)
110
    |  1 |  - |    -    |  -  |  1  |  1  |  1  |     2    | 256x192 resolution graphics 6 (RG6)
111
 
112
    The CSS pins select between 2 possible color sets.
113
*/
114
 
115
// address bus pins
116
#define MC6847_PIN_A0 (0)
117
#define MC6847_PIN_A1 (1)
118
#define MC6847_PIN_A2 (2)
119
#define MC6847_PIN_A3 (3)
120
#define MC6847_PIN_A4 (4)
121
#define MC6847_PIN_A5 (5)
122
#define MC6847_PIN_A6 (6)
123
#define MC6847_PIN_A7 (7)
124
#define MC6847_PIN_A8 (8)
125
#define MC6847_PIN_A9 (9)
126
#define MC6847_PIN_A10 (10)
127
#define MC6847_PIN_A11 (11)
128
#define MC6847_PIN_A12 (12)
129
 
130
// data bus pins
131
#define MC6847_PIN_D0 (16)
132
#define MC6847_PIN_D1 (17)
133
#define MC6847_PIN_D2 (18)
134
#define MC6847_PIN_D3 (19)
135
#define MC6847_PIN_D4 (20)
136
#define MC6847_PIN_D5 (21)
137
#define MC6847_PIN_D6 (22)
138
#define MC6847_PIN_D7 (23)
139
 
140
// synchronization output pins
141
#define MC6847_PIN_FS (40) // field sync
142
#define MC6847_PIN_HS (41) // horizontal sync
143
#define MC6847_PIN_RP (42) // row preset (not emulated)
144
 
145
// mode-select input pins
146
#define MC6847_PIN_AG (43)     // graphics mode enable
147
#define MC6847_PIN_AS (44)     // semi-graphics mode enable
148
#define MC6847_PIN_INTEXT (45) // internal/external select
149
#define MC6847_PIN_INV (46)    // invert enable
150
#define MC6847_PIN_GM0 (47)    // graphics mode select 0
151
#define MC6847_PIN_GM1 (48)    // graphics mode select 1
152
#define MC6847_PIN_GM2 (49)    // graphics mode select 2
153
#define MC6847_PIN_CSS (50)    // color select pin
154
 
155
// pin bit masks
156
#define MC6847_A0 (1ULL << MC6847_PIN_A0)
157
#define MC6847_A1 (1ULL << MC6847_PIN_A1)
158
#define MC6847_A2 (1ULL << MC6847_PIN_A2)
159
#define MC6847_A3 (1ULL << MC6847_PIN_A3)
160
#define MC6847_A4 (1ULL << MC6847_PIN_A4)
161
#define MC6847_A5 (1ULL << MC6847_PIN_A5)
162
#define MC6847_A6 (1ULL << MC6847_PIN_A6)
163
#define MC6847_A7 (1ULL << MC6847_PIN_A7)
164
#define MC6847_A8 (1ULL << MC6847_PIN_A8)
165
#define MC6847_A9 (1ULL << MC6847_PIN_A9)
166
#define MC6847_A10 (1ULL << MC6847_PIN_A10)
167
#define MC6847_A11 (1ULL << MC6847_PIN_A11)
168
#define MC6847_A12 (1ULL << MC6847_PIN_A12)
169
#define MC6847_D0 (1ULL << MC6847_PIN_D0)
170
#define MC6847_D1 (1ULL << MC6847_PIN_D1)
171
#define MC6847_D2 (1ULL << MC6847_PIN_D2)
172
#define MC6847_D3 (1ULL << MC6847_PIN_D3)
173
#define MC6847_D4 (1ULL << MC6847_PIN_D4)
174
#define MC6847_D5 (1ULL << MC6847_PIN_D5)
175
#define MC6847_D6 (1ULL << MC6847_PIN_D6)
176
#define MC6847_D7 (1ULL << MC6847_PIN_D7)
177
#define MC6847_FS (1ULL << MC6847_PIN_FS)
178
#define MC6847_HS (1ULL << MC6847_PIN_HS)
179
#define MC6847_RP (1ULL << MC6847_PIN_RP)
180
#define MC6847_AG (1ULL << MC6847_PIN_AG)
181
#define MC6847_AS (1ULL << MC6847_PIN_AS)
182
#define MC6847_INTEXT (1ULL << MC6847_PIN_INTEXT)
183
#define MC6847_INV (1ULL << MC6847_PIN_INV)
184
#define MC6847_GM0 (1ULL << MC6847_PIN_GM0)
185
#define MC6847_GM1 (1ULL << MC6847_PIN_GM1)
186
#define MC6847_GM2 (1ULL << MC6847_PIN_GM2)
187
#define MC6847_CSS (1ULL << MC6847_PIN_CSS)
188
#define MC6847_CTRL_PINS (MC6847_AG | MC6847_AS | MC6847_INTEXT | MC6847_INV | MC6847_GM0 | MC6847_GM1 | MC6847_GM2 | MC6847_CSS)
189
 
190
// helper macros to set and extract address and data to/from pin mask
191
 
192
// extract 13-bit address bus from 64-bit pins
193
#define MC6847_GET_ADDR(p) ((uint16_t)(p & 0xFFFFULL))
194
// merge 13-bit address bus value into 64-bit pins
195
#define MC6847_SET_ADDR(p, a) \
43 nishi 196
	{ p = ((p & ~0xFFFFULL) | ((a) & 0xFFFFULL)); }
7 nishi 197
// extract 8-bit data bus from 64-bit pins
43 nishi 198
#define MC6847_GET_DATA(p) ((uint8_t)(((p) & 0xFF0000ULL) >> 16))
7 nishi 199
// merge 8-bit data bus value into 64-bit pins
200
#define MC6847_SET_DATA(p, d) \
201
	{ p = (((p) & ~0xFF0000ULL) | (((d) << 16) & 0xFF0000ULL)); }
202
 
203
// public constants
204
#define MC6847_VBLANK_LINES (13)	// 13 lines vblank at top of screen
205
#define MC6847_TOP_BORDER_LINES (25)	// 25 lines top border
206
#define MC6847_DISPLAY_LINES (192)	// 192 lines visible display area
207
#define MC6847_BOTTOM_BORDER_LINES (26) // 26 lines bottom border
208
#define MC6847_VRETRACE_LINES (6)	// 6 'lines' for vertical retrace
209
#define MC6847_ALL_LINES (262)		// all of the above
210
#define MC6847_DISPLAY_START (MC6847_VBLANK_LINES + MC6847_TOP_BORDER_LINES)
211
#define MC6847_DISPLAY_END (MC6847_DISPLAY_START + MC6847_DISPLAY_LINES)
212
#define MC6847_BOTTOM_BORDER_END (MC6847_DISPLAY_END + MC6847_BOTTOM_BORDER_LINES)
213
#define MC6847_FSYNC_START (MC6847_DISPLAY_END)
214
 
215
// hardware color indices
216
#define MC6847_HWCOLOR_GFX_GREEN (0)
217
#define MC6847_HWCOLOR_GFX_YELLOW (1)
218
#define MC6847_HWCOLOR_GFX_BLUE (2)
219
#define MC6847_HWCOLOR_GFX_RED (3)
220
#define MC6847_HWCOLOR_GFX_BUFF (4)
221
#define MC6847_HWCOLOR_GFX_CYAN (5)
222
#define MC6847_HWCOLOR_GFX_MAGENTA (6)
223
#define MC6847_HWCOLOR_GFX_ORANGE (7)
224
#define MC6847_HWCOLOR_ALNUM_GREEN (8)
225
#define MC6847_HWCOLOR_ALNUM_DARK_GREEN (9)
226
#define MC6847_HWCOLOR_ALNUM_ORANGE (10)
227
#define MC6847_HWCOLOR_ALNUM_DARK_ORANGE (11)
228
#define MC6847_HWCOLOR_BLACK (12)
229
#define MC6847_HWCOLOR_NUM (13)
230
 
231
// pixel width and height of entire visible area, including border
232
#define MC6847_DISPLAY_WIDTH (320)
233
#define MC6847_DISPLAY_HEIGHT (MC6847_TOP_BORDER_LINES + MC6847_DISPLAY_LINES + MC6847_BOTTOM_BORDER_LINES)
234
 
235
// framebuffer width and height
236
#define MC6847_FRAMEBUFFER_WIDTH (512)
237
#define MC6847_FRAMEBUFFER_HEIGHT (MC6847_DISPLAY_HEIGHT)
238
#define MC6847_FRAMEBUFFER_SIZE_BYTES (MC6847_FRAMEBUFFER_WIDTH * MC6847_FRAMEBUFFER_HEIGHT)
239
 
240
// pixel width and height of only the image area, without border
241
#define MC6847_IMAGE_WIDTH (256)
242
#define MC6847_IMAGE_HEIGHT (192)
243
 
244
// horizontal border width
245
#define MC6847_BORDER_PIXELS ((MC6847_DISPLAY_WIDTH - MC6847_IMAGE_WIDTH) / 2)
246
 
247
// the MC6847 is always clocked at 3.579 MHz
248
#define MC6847_TICK_HZ (3579545)
249
 
250
// fixed point precision for more precise error accumulation
251
#define MC6847_FIXEDPOINT_SCALE (16)
252
 
253
// a memory-fetch callback, used to read video memory bytes into the MC6847
254
typedef uint64_t (*mc6847_fetch_t)(uint64_t pins, void* user_data);
255
 
256
// the mc6847 setup parameters
257
typedef struct {
258
	// the CPU tick rate in hz
259
	int tick_hz;
260
	// pointer to an uint8_t framebuffer where video image is written to (must be at least 512*244 bytes)
261
	chips_range_t framebuffer;
262
	// memory-fetch callback
263
	mc6847_fetch_t fetch_cb;
264
	// optional user-data for the fetch callback
265
	void* user_data;
266
} mc6847_desc_t;
267
 
268
// the mc6847 state struct
269
typedef struct {
270
	// last pin state
271
	uint64_t pins;
272
 
273
	// internal counters
274
	int h_count;
275
	int h_sync_start;
276
	int h_sync_end;
277
	int h_period;
278
	int l_count;
279
 
280
	// true during field-sync
281
	bool fs;
282
 
283
	// the fetch callback function
284
	mc6847_fetch_t fetch_cb;
285
	// optional user-data for the fetch-callback
286
	void* user_data;
287
	// pointer to uint8_t buffer where decoded video image is written too
288
	uint8_t* fb;
289
	// hardware colors
290
	uint32_t hwcolors[MC6847_HWCOLOR_NUM];
291
} mc6847_t;
292
 
293
// initialize a new mc6847_t instance
294
void mc6847_init(mc6847_t* vdg, const mc6847_desc_t* desc);
295
// reset a mc6847_t instance
296
void mc6847_reset(mc6847_t* vdg);
297
// tick the mc6847_t instance, this will call the fetch_cb and generate the image
298
uint64_t mc6847_tick(mc6847_t* vdg, uint64_t pins);
299
// prepare mc6847_t snapshot for saving
300
void mc6847_snapshot_onsave(mc6847_t* snapshot);
301
// fixup mc6847_t snapshot after loading
302
void mc6847_snapshot_onload(mc6847_t* snapshot, mc6847_t* sys);
303
 
304
#ifdef __cplusplus
305
} // extern "C"
306
#endif
307
 
308
/*--- IMPLEMENTATION ---------------------------------------------------------*/
309
#ifdef CHIPS_IMPL
310
#include <string.h>
311
#ifndef CHIPS_ASSERT
312
#include <assert.h>
313
#define CHIPS_ASSERT(c) assert(c)
314
#endif
315
 
316
#define _MC6847_CLAMP(x) ((x) > 255 ? 255 : (x))
317
#define _MC6847_RGBA(r, g, b) (0xFF000000 | _MC6847_CLAMP((r * 4) / 3) | (_MC6847_CLAMP((g * 4) / 3) << 8) | (_MC6847_CLAMP((b * 4) / 3) << 16))
318
 
319
void mc6847_init(mc6847_t* vdg, const mc6847_desc_t* desc) {
320
	CHIPS_ASSERT(vdg && desc);
321
	CHIPS_ASSERT(desc->framebuffer.ptr && (desc->framebuffer.size <= MC6847_FRAMEBUFFER_SIZE_BYTES));
322
	CHIPS_ASSERT(desc->fetch_cb);
323
	CHIPS_ASSERT((desc->tick_hz > 0) && (desc->tick_hz < MC6847_TICK_HZ));
324
 
325
	memset(vdg, 0, sizeof(*vdg));
326
	vdg->fb = desc->framebuffer.ptr;
327
	vdg->fetch_cb = desc->fetch_cb;
328
	vdg->user_data = desc->user_data;
329
 
330
	/* compute counter periods, the MC6847 is always clocked at 3.579 MHz,
331
	   and the frequency of how the tick function is called must be
332
	   communicated to the init function
333
 
334
	   one scanline is 228 3.5 MC6847 ticks
335
	*/
336
	int64_t tmp = (228LL * desc->tick_hz * MC6847_FIXEDPOINT_SCALE) / MC6847_TICK_HZ;
337
	vdg->h_period = (int)tmp;
338
	// hsync starts at tick 10 of a scanline
339
	tmp = (10LL * desc->tick_hz * MC6847_FIXEDPOINT_SCALE) / MC6847_TICK_HZ;
340
	vdg->h_sync_start = (int)tmp;
341
	// hsync is 16 ticks long
342
	tmp = (26LL * desc->tick_hz * MC6847_FIXEDPOINT_SCALE) / MC6847_TICK_HZ;
343
	vdg->h_sync_end = (int)tmp;
344
 
345
	/* the default graphics mode color palette
346
 
347
	   the MC6847 outputs three color values:
348
	    - Y' - six level analog luminance
349
	    - phiA - three level analog (U)
350
	    - phiB - three level analog (V)
351
 
352
	    see discussion here: http://forums.bannister.org/ubbthreads.php?ubb=showflat&Number=64986
353
 
354
	    NEW VALUES from here: http://www.stardot.org.uk/forums/viewtopic.php?f=44&t=12503
355
 
356
	    green:      19 146  11
357
	    yellow:    155 150  10
358
	    blue:        2  22 175
359
	    red:       155  22   7
360
	    buff:      141 150 154
361
	    cyan:       15 143 155
362
	    magenta:   139  39 155
363
	    orange:    140  31  11
364
 
365
	    color intensities are slightly boosted
366
	*/
367
	vdg->hwcolors[MC6847_HWCOLOR_GFX_GREEN] = _MC6847_RGBA(19, 146, 11);
368
	vdg->hwcolors[MC6847_HWCOLOR_GFX_YELLOW] = _MC6847_RGBA(155, 150, 10);
369
	vdg->hwcolors[MC6847_HWCOLOR_GFX_BLUE] = _MC6847_RGBA(2, 22, 175);
370
	vdg->hwcolors[MC6847_HWCOLOR_GFX_RED] = _MC6847_RGBA(155, 22, 7);
371
	vdg->hwcolors[MC6847_HWCOLOR_GFX_BUFF] = _MC6847_RGBA(141, 150, 154);
372
	vdg->hwcolors[MC6847_HWCOLOR_GFX_CYAN] = _MC6847_RGBA(15, 143, 155);
373
	vdg->hwcolors[MC6847_HWCOLOR_GFX_MAGENTA] = _MC6847_RGBA(139, 39, 155);
374
	vdg->hwcolors[MC6847_HWCOLOR_GFX_ORANGE] = _MC6847_RGBA(140, 31, 11);
375
	vdg->hwcolors[MC6847_HWCOLOR_ALNUM_GREEN] = _MC6847_RGBA(19, 146, 11);
376
	vdg->hwcolors[MC6847_HWCOLOR_ALNUM_DARK_GREEN] = 0xFF002400;
377
	vdg->hwcolors[MC6847_HWCOLOR_ALNUM_ORANGE] = _MC6847_RGBA(140, 31, 11);
378
	vdg->hwcolors[MC6847_HWCOLOR_ALNUM_DARK_ORANGE] = 0xFF000E22;
379
	vdg->hwcolors[MC6847_HWCOLOR_BLACK] = 0xFF111111;
380
}
381
 
382
void mc6847_reset(mc6847_t* vdg) {
383
	CHIPS_ASSERT(vdg);
384
	vdg->h_count = 0;
385
	vdg->l_count = 0;
386
}
387
 
388
/*
389
    internal character ROM dump from MAME
390
    (ntsc_square_fontdata8x12 in devices/video/mc6847.cpp)
391
*/
392
static const uint8_t _mc6847_font[64 * 12] = {
393
    0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x1A, 0x2A, 0x2A, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x12, 0x12, 0x1C, 0x12, 0x12, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x38, 0x20, 0x20, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x38, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x26, 0x22, 0x22, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x22, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3E,
394
    0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x24, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x10, 0x08, 0x04, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08,
395
    0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x3E, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x14, 0x36, 0x00, 0x36, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1E, 0x20, 0x1C, 0x02, 0x3C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x32, 0x04, 0x08, 0x10, 0x26, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x28, 0x28,
396
    0x10, 0x2A, 0x24, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x3E, 0x1C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x1C, 0x20, 0x20, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C,
397
    0x22, 0x02, 0x04, 0x02, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0C, 0x14, 0x3E, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x3C, 0x02, 0x02, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x04, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00,
398
};
399
 
400
static inline uint8_t _mc6847_border_color(uint64_t pins) {
401
	if(pins & MC6847_AG) {
402
		// a graphics mode, either green or buff, depending on CSS pin
403
		return (pins & MC6847_CSS) ? MC6847_HWCOLOR_GFX_BUFF : MC6847_HWCOLOR_GFX_GREEN;
404
	} else {
405
		// alphanumeric or semigraphics mode, always black
406
		return MC6847_HWCOLOR_BLACK;
407
	}
408
}
409
 
410
static void _mc6847_decode_border(mc6847_t* vdg, uint64_t pins, size_t y) {
411
	uint8_t* dst = &(vdg->fb[y * MC6847_FRAMEBUFFER_WIDTH]);
412
	uint8_t c = _mc6847_border_color(pins);
413
	for(size_t x = 0; x < MC6847_DISPLAY_WIDTH; x++) {
414
		*dst++ = c;
415
	}
416
}
417
 
418
static uint64_t _mc6847_decode_scanline(mc6847_t* vdg, uint64_t pins, size_t y) {
419
	uint8_t* dst = &(vdg->fb[(y + MC6847_TOP_BORDER_LINES) * MC6847_FRAMEBUFFER_WIDTH]);
420
	uint8_t bc = _mc6847_border_color(pins);
421
	void* ud = vdg->user_data;
422
 
423
	// left border
424
	for(size_t i = 0; i < MC6847_BORDER_PIXELS; i++) {
425
		*dst++ = bc;
426
	}
427
 
428
	// visible scanline
429
	if(pins & MC6847_AG) {
430
		// one of the 8 graphics modes
431
		size_t sub_mode = (uint8_t)((pins & (MC6847_GM2 | MC6847_GM1)) / MC6847_GM1);
432
		if(pins & MC6847_GM0) {
433
			/*  one of the 'resolution modes' (1 bit == 1 pixel block)
434
			    GM2|GM1:
435
				00:    RG1, 128x64, 16 bytes per row
436
				01:    RG2, 128x96, 16 bytes per row
437
				10:    RG3, 128x192, 16 bytes per row
438
				11:    RG6, 256x192, 32 bytes per row
439
			*/
440
			size_t dots_per_bit = (sub_mode < 3) ? 2 : 1;
441
			size_t bytes_per_row = (sub_mode < 3) ? 16 : 32;
442
			size_t row_height = (pins & MC6847_GM2) ? 1 : (pins & MC6847_GM1) ? 2 : 3;
443
			uint16_t addr = (y / row_height) * bytes_per_row;
444
			uint8_t fg_color = (pins & MC6847_CSS) ? MC6847_HWCOLOR_GFX_BUFF : MC6847_HWCOLOR_GFX_GREEN;
445
			for(size_t x = 0; x < bytes_per_row; x++) {
446
				MC6847_SET_ADDR(pins, addr++);
447
				pins = vdg->fetch_cb(pins, ud);
448
				uint8_t m = MC6847_GET_DATA(pins);
449
				for(int p = 7; p >= 0; p--) {
450
					uint8_t c = ((m >> p) & 1) ? fg_color : MC6847_HWCOLOR_BLACK;
451
					for(size_t d = 0; d < dots_per_bit; d++) {
452
						*dst++ = c;
453
					}
454
				}
455
			}
456
		} else {
457
			/*  one of the 'color modes' (2 bits per pixel == 4 colors, CSS select
458
			    lower or upper half of palette)
459
			    GM2|GM1:
460
				00: CG1, 64x64, 16 bytes per row
461
				01: CG2, 128x64, 32 bytes per row
462
				10: CG3, 128x96, 32 bytes per row
463
				11: CG6, 128x192, 32 bytes per row
464
			*/
465
			uint8_t color_offset = (pins & MC6847_CSS) ? 4 : 0;
466
			size_t dots_per_2bit = (sub_mode == 0) ? 4 : 2;
467
			size_t bytes_per_row = (sub_mode == 0) ? 16 : 32;
468
			size_t row_height = (pins & MC6847_GM2) ? ((pins & MC6847_GM1) ? 1 : 2) : 3;
469
			uint16_t addr = (y / row_height) * bytes_per_row;
470
			for(size_t x = 0; x < bytes_per_row; x++) {
471
				MC6847_SET_ADDR(pins, addr++);
472
				pins = vdg->fetch_cb(pins, ud);
473
				uint8_t m = MC6847_GET_DATA(pins);
474
				for(int p = 6; p >= 0; p -= 2) {
475
					const uint8_t c = ((m >> p) & 3) + color_offset;
476
					for(size_t d = 0; d < dots_per_2bit; d++) {
477
						*dst++ = c;
478
					}
479
				}
480
			}
481
		}
482
	} else {
483
		//  we're in alphanumeric/semigraphics mode, one cell is 8x12 pixels
484
 
485
		// the vidmem src address and offset into the font data
486
		uint16_t addr = (y / 12) * 32;
487
		uint8_t m; // the pixel bitmask
488
		size_t chr_y = y % 12;
489
		// bit shifters to extract a 2x2 or 2x3 semigraphics 2-bit stack
490
		size_t shift_2x2 = (1 - (chr_y / 6)) * 2;
491
		size_t shift_2x3 = (2 - (chr_y / 4)) * 2;
492
		uint8_t alnum_fg = (pins & MC6847_CSS) ? MC6847_HWCOLOR_ALNUM_ORANGE : MC6847_HWCOLOR_ALNUM_GREEN;
493
		uint8_t alnum_bg = (pins & MC6847_CSS) ? MC6847_HWCOLOR_ALNUM_DARK_ORANGE : MC6847_HWCOLOR_ALNUM_DARK_GREEN;
494
		for(size_t x = 0; x < 32; x++) {
495
			MC6847_SET_ADDR(pins, addr++);
496
			pins = vdg->fetch_cb(pins, ud);
497
			uint8_t chr = MC6847_GET_DATA(pins);
498
			if(pins & MC6847_AS) {
499
				// semigraphics mode
500
				uint8_t fg_color;
501
				if(pins & MC6847_INTEXT) {
502
					/*  2x3 semigraphics, 2 color sets at 4 colors (selected by CSS pin)
503
					    |C1|C0|L5|L4|L3|L2|L1|L0|
504
 
505
					    +--+--+
506
					    |L5|L4|
507
					    +--+--+
508
					    |L3|L2|
509
					    +--+--+
510
					    |L1|L0|
511
					    +--+--+
512
					*/
513
 
514
					// extract the 2 horizontal bits from one of the 3 stacks
515
					m = (chr >> shift_2x3) & 3;
516
					// 2 bits of color, CSS bit selects upper or lower half of color palette
517
					fg_color = ((chr >> 6) & 3) + ((pins & MC6847_CSS) ? 4 : 0);
518
				} else {
519
					/*  2x2 semigraphics, 8 colors + black
520
					    |xx|C2|C1|C0|L3|L2|L1|L0|
521
 
522
					    +--+--+
523
					    |L3|L2|
524
					    +--+--+
525
					    |L1|L0|
526
					    +--+--+
527
					*/
528
 
529
					// extract the 2 horizontal bits from the upper or lower stack
530
					m = (chr >> shift_2x2) & 3;
531
					// 3 color bits directly point into the color palette
532
					fg_color = (chr >> 4) & 7;
533
				}
534
				// write the horizontal pixel blocks (2 blocks @ 4 pixel each)
535
				for(int p = 1; p >= 0; p--) {
536
					uint8_t c = (m & (1 << p)) ? fg_color : MC6847_HWCOLOR_BLACK;
537
					*dst++ = c;
538
					*dst++ = c;
539
					*dst++ = c;
540
					*dst++ = c;
541
				}
542
			} else {
543
				/*  alphanumeric mode
544
				    FIXME: INT_EXT (switch between internal and external font
545
				*/
546
				uint8_t m = _mc6847_font[(chr & 0x3F) * 12 + chr_y];
547
				if(pins & MC6847_INV) {
548
					m = ~m;
549
				}
550
				for(int p = 7; p >= 0; p--) {
551
					*dst++ = m & (1 << p) ? alnum_fg : alnum_bg;
552
				}
553
			}
554
		}
555
	}
556
 
557
	// right border
558
	for(size_t i = 0; i < MC6847_BORDER_PIXELS; i++) {
559
		*dst++ = bc;
560
	}
561
 
562
	return pins;
563
}
564
 
565
uint64_t mc6847_tick(mc6847_t* vdg, uint64_t pins) {
566
 
567
	// output pins will be set each tick
568
	pins &= ~(MC6847_HS | MC6847_FS);
569
 
570
	// horizontal and vertical field sync
571
	vdg->h_count += MC6847_FIXEDPOINT_SCALE;
572
	if((vdg->h_count >= vdg->h_sync_start) && (vdg->h_count < vdg->h_sync_end)) {
573
		// horizontal sync on
574
		pins |= MC6847_HS;
575
		if(vdg->l_count == MC6847_FSYNC_START) {
576
			// switch field sync on
577
			vdg->fs = true;
578
		}
579
	}
580
	if(vdg->fs) {
581
		pins |= MC6847_FS;
582
	}
583
 
584
	// rewind horizontal counter?
585
	if(vdg->h_count >= vdg->h_period) {
586
		vdg->h_count -= vdg->h_period;
587
		vdg->l_count++;
588
		if(vdg->l_count >= MC6847_ALL_LINES) {
589
			// rewind line counter, field sync off
590
			vdg->l_count = 0;
591
			vdg->fs = false;
592
		}
593
		if(vdg->l_count < MC6847_VBLANK_LINES) {
594
			// inside vblank area, nothing to do
595
		} else if(vdg->l_count < MC6847_DISPLAY_START) {
596
			// top border
597
			size_t y = (size_t)(vdg->l_count - MC6847_VBLANK_LINES);
598
			_mc6847_decode_border(vdg, pins, y);
599
		} else if(vdg->l_count < MC6847_DISPLAY_END) {
600
			// visible area
601
			size_t y = (size_t)(vdg->l_count - MC6847_DISPLAY_START);
602
			pins = _mc6847_decode_scanline(vdg, pins, y);
603
		} else if(vdg->l_count < MC6847_BOTTOM_BORDER_END) {
604
			// bottom border
605
			size_t y = (size_t)(vdg->l_count - MC6847_VBLANK_LINES);
606
			_mc6847_decode_border(vdg, pins, y);
607
		}
608
	}
609
	vdg->pins = pins;
610
	return pins;
611
}
612
 
613
void mc6847_snapshot_onsave(mc6847_t* snapshot) {
614
	CHIPS_ASSERT(snapshot);
615
	snapshot->fetch_cb = 0;
616
	snapshot->user_data = 0;
617
	snapshot->fb = 0;
618
}
619
 
620
void mc6847_snapshot_onload(mc6847_t* snapshot, mc6847_t* sys) {
621
	CHIPS_ASSERT(snapshot && sys);
622
	snapshot->fetch_cb = sys->fetch_cb;
623
	snapshot->user_data = sys->user_data;
624
	snapshot->fb = sys->fb;
625
}
626
 
627
#endif // CHIPS_IMPL