1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder
3 /*
4
5 TODO:
6
7 - GR is always 0
8
9 */
10
11 #include "emu.h"
12 #include "includes/newbrain.h"
13 #include "rendlay.h"
14 #include "screen.h"
15
16 #define VERBOSE 0
17 #include "logmacro.h"
18
19 #define NEWBRAIN_VIDEO_RV 0x01
20 #define NEWBRAIN_VIDEO_FS 0x02
21 #define NEWBRAIN_VIDEO_32_40 0x04
22 #define NEWBRAIN_VIDEO_UCR 0x08
23 #define NEWBRAIN_VIDEO_80L 0x40
24
tvl(uint8_t data,int a6)25 void newbrain_state::tvl(uint8_t data, int a6)
26 {
27 /* latch video address counter bits A5-A0 */
28 m_tvl = m_80l ? 0x04 : 0x02;
29
30 /* latch video address counter bit A6 */
31 m_tvl |= a6 << 6;
32
33 /* latch data to video address counter bits A14-A7 */
34 m_tvl |= (data << 7);
35
36 LOG("%s %s TVL %04x\n", machine().time().as_string(), machine().describe_context(), m_tvl);
37 }
38
tvtl_w(uint8_t data)39 void newbrain_state::tvtl_w(uint8_t data)
40 {
41 /*
42
43 bit signal description
44
45 0 RV 1 reverses video over entire field, ie. black on white
46 1 FS 0 generates 128 characters and 128 reverse field characters from 8 bit character code. 1 generates 256 characters from 8 bit character code
47 2 32/_40 0 generates 320 or 640 horizontal dots in pixel graphics mode. 1 generates 256 or 512 horizontal dots in pixel graphics mode
48 3 UCR 0 selects 256 characters expressed in an 8x10 matrix, and 25 lines (max) displayed. 1 selects 256 characters in an 8x8 matrix, and 31 lines (max) displayed
49 4
50 5
51 6 80L 0 selects 40 character line length. 1 selects 80 character line length
52 7
53
54 */
55
56 LOG("%s %s TVTL %02x\n", machine().time().as_string(), machine().describe_context(), data);
57
58 m_rv = BIT(data, 0);
59 m_fs = BIT(data, 1);
60 m_32_40 = BIT(data, 2);
61 m_ucr = BIT(data, 3);
62 m_80l = BIT(data, 6);
63 }
64
video_start()65 void newbrain_state::video_start()
66 {
67 // set timer
68 m_clkint_timer = timer_alloc(TIMER_ID_CLKINT);
69 m_clkint_timer->adjust(attotime::zero, 0, attotime::from_hz(50));
70
71 // state saving
72 save_item(NAME(m_rv));
73 save_item(NAME(m_fs));
74 save_item(NAME(m_32_40));
75 save_item(NAME(m_ucr));
76 save_item(NAME(m_80l));
77 save_item(NAME(m_tvl));
78 }
79
do_screen_update(bitmap_rgb32 & bitmap,const rectangle & cliprect)80 void newbrain_state::do_screen_update(bitmap_rgb32 &bitmap, const rectangle &cliprect)
81 {
82 int columns = m_80l ? 80 : 40;
83 int excess = m_32_40 ? 4 : 24;
84 int gr = 0;
85
86 uint16_t videoram_addr = m_tvl;
87 int rc = 0;
88 uint8_t vsr = 0;
89 uint8_t grsr = 0;
90
91 for (int y = 0; y < 240; y++) {
92 int x = 0;
93
94 for (int sx = 0; sx < columns; sx++) {
95 uint8_t rd = m_ram->pointer()[(videoram_addr + sx) & 0x7fff];
96
97 bool rc3 = BIT(rc, 3);
98 bool txt = !(BIT(rd, 6) || BIT(rd, 5));
99 bool txtq = m_ucr || txt;
100
101 int rc_ = rc & 0x07;
102 if (rc3 && txt) {
103 rc_ = 7;
104 }
105
106 uint16_t charrom_addr = (m_ucr << 11) | (rc_ << 8) | ((BIT(rd, 7) && m_fs) << 7) | (rd & 0x7f);
107 uint8_t crd = m_char_rom->base()[charrom_addr & 0xfff];
108 bool crd0 = BIT(crd, 0);
109
110 bool ldvsr = !(((!rc3 ^ crd0) || gr) || txtq);
111
112 if (!ldvsr && !gr) {
113 vsr = (crd & 0xfe) | (crd0 && txtq);
114 }
115
116 if (!ldvsr && gr) {
117 grsr = rd;
118 }
119
120 for (int i = 0; i < 8; i++) {
121 uint8_t sr = gr ? grsr : vsr;
122 int color = BIT(sr, 7) ^ m_rv;
123
124 bitmap.pix(y, x++) = m_palette->pen(color);
125
126 if (columns == 40) {
127 bitmap.pix(y, x++) = m_palette->pen(color);
128 }
129
130 grsr <<= 1;
131 vsr <<= 1;
132 }
133 }
134
135 if (gr)
136 {
137 // get new data for each line
138 videoram_addr += columns;
139 videoram_addr += excess;
140 }
141 else
142 {
143 rc++;
144
145 if (rc == (m_ucr ? 8 : 10)) {
146 rc = 0;
147
148 // get new data after each character row
149 videoram_addr += columns;
150 videoram_addr += excess;
151 }
152 }
153 }
154 }
155
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)156 uint32_t newbrain_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
157 {
158 if (m_tvp)
159 do_screen_update(bitmap, cliprect);
160 else
161 bitmap.fill(rgb_t::black(), cliprect);
162
163 return 0;
164 }
165
166 /* F4 Character Displayer */
167 static const gfx_layout newbrain_charlayout =
168 {
169 8, 10, /* 8 x 10 characters */
170 256, /* 256 characters */
171 1, /* 1 bits per pixel */
172 { 0 }, /* no bitplanes */
173 /* x offsets */
174 { 0, 1, 2, 3, 4, 5, 6, 7 },
175 /* y offsets */
176 { 0*256*8, 1*256*8, 2*256*8, 3*256*8, 4*256*8, 5*256*8, 6*256*8, 7*256*8, 8*256*8, 9*256*8, 10*256*8, 11*256*8, 12*256*8, 13*256*8, 14*256*8, 15*256*8 },
177 8 /* every char takes 16 x 1 bytes */
178 };
179
180 static GFXDECODE_START( gfx_newbrain )
181 GFXDECODE_ENTRY( "chargen", 0x0000, newbrain_charlayout, 0, 1 )
182 GFXDECODE_END
183
184 /* Machine Drivers */
185
newbrain_video(machine_config & config)186 void newbrain_state::newbrain_video(machine_config &config)
187 {
188 screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER, rgb_t::green()));
189 screen.set_screen_update(FUNC(newbrain_state::screen_update));
190 screen.set_refresh_hz(50);
191 screen.set_size(640, 250);
192 screen.set_visarea(0, 639, 0, 249);
193
194 PALETTE(config, m_palette, palette_device::MONOCHROME);
195
196 GFXDECODE(config, "gfxdecode", m_palette, gfx_newbrain);
197 }
198