2016-10-12 23:20:23 +00:00
|
|
|
|
//
|
|
|
|
|
// Video.cpp
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 12/10/2016.
|
|
|
|
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include "Video.hpp"
|
|
|
|
|
|
|
|
|
|
using namespace Oric;
|
|
|
|
|
|
2016-10-13 01:29:21 +00:00
|
|
|
|
VideoOutput::VideoOutput(uint8_t *memory) :
|
|
|
|
|
_ram(memory),
|
|
|
|
|
_frame_counter(0), _counter(0),
|
2016-10-13 01:52:47 +00:00
|
|
|
|
_state(Sync), _cycles_in_state(0),
|
2016-10-13 11:59:11 +00:00
|
|
|
|
_is_graphics_mode(false),
|
2016-10-15 02:39:27 +00:00
|
|
|
|
_character_set_base_address(0xb400),
|
|
|
|
|
_phase(0)
|
2016-10-12 23:20:23 +00:00
|
|
|
|
{
|
2016-10-13 01:42:36 +00:00
|
|
|
|
_crt.reset(new Outputs::CRT::CRT(64*6, 6, Outputs::CRT::DisplayType::PAL50, 1));
|
2016-10-13 01:29:21 +00:00
|
|
|
|
|
|
|
|
|
// TODO: this is a copy and paste from the Electron; factor out.
|
|
|
|
|
_crt->set_rgb_sampling_function(
|
|
|
|
|
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
|
|
|
|
|
"{"
|
|
|
|
|
"uint texValue = texture(sampler, coordinate).r;"
|
|
|
|
|
"texValue >>= 4 - (int(icoordinate.x * 8) & 4);"
|
|
|
|
|
"return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));"
|
|
|
|
|
"}");
|
2016-10-15 02:39:27 +00:00
|
|
|
|
|
|
|
|
|
_crt->set_output_device(Outputs::CRT::Television);
|
2016-10-16 01:43:46 +00:00
|
|
|
|
_crt->set_visible_area(_crt->get_rect_for_area(50, 224, 16 * 6, 40 * 6, 4.0f / 3.0f));
|
2016-10-12 23:20:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-13 01:29:21 +00:00
|
|
|
|
std::shared_ptr<Outputs::CRT::CRT> VideoOutput::get_crt()
|
2016-10-12 23:20:23 +00:00
|
|
|
|
{
|
2016-10-13 01:29:21 +00:00
|
|
|
|
return _crt;
|
2016-10-12 23:20:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VideoOutput::run_for_cycles(int number_of_cycles)
|
|
|
|
|
{
|
2016-10-13 01:29:21 +00:00
|
|
|
|
// Vertical: 0–39: pixels; otherwise blank; 48–53 sync
|
|
|
|
|
// Horizontal: 0–223: pixels; otherwise blank; 256–259 sync
|
|
|
|
|
|
|
|
|
|
while(number_of_cycles--)
|
|
|
|
|
{
|
|
|
|
|
_counter = (_counter + 1)%(312 * 64); // TODO: NTSC
|
2016-10-13 11:59:11 +00:00
|
|
|
|
int h_counter =_counter & 63;
|
|
|
|
|
|
|
|
|
|
if(!h_counter)
|
|
|
|
|
{
|
|
|
|
|
_ink = 0xff;
|
|
|
|
|
_paper = 0x00;
|
2016-10-13 23:34:29 +00:00
|
|
|
|
_use_alternative_character_set = _use_double_height_characters = _blink_text = false;
|
|
|
|
|
set_character_set_base_address();
|
2016-10-15 02:39:27 +00:00
|
|
|
|
_phase += 64;
|
2016-10-13 23:34:29 +00:00
|
|
|
|
|
2016-10-17 02:14:01 +00:00
|
|
|
|
if(!_counter)
|
|
|
|
|
{
|
2016-10-17 12:04:15 +00:00
|
|
|
|
_phase += 128;
|
2016-10-17 02:14:01 +00:00
|
|
|
|
_frame_counter++;
|
|
|
|
|
}
|
2016-10-13 11:59:11 +00:00
|
|
|
|
}
|
2016-10-13 01:29:21 +00:00
|
|
|
|
|
|
|
|
|
State new_state = Blank;
|
2016-10-13 01:42:36 +00:00
|
|
|
|
if(
|
|
|
|
|
(h_counter >= 48 && h_counter <= 53) ||
|
|
|
|
|
(_counter >= 256*64 && _counter <= 259*64)) new_state = Sync;
|
2016-10-15 02:39:27 +00:00
|
|
|
|
else if(h_counter >= 54 && h_counter <= 56) new_state = ColourBurst;
|
2016-10-13 01:42:36 +00:00
|
|
|
|
else if(_counter < 224*64 && h_counter < 40) new_state = Pixels;
|
2016-10-13 01:29:21 +00:00
|
|
|
|
|
|
|
|
|
if(_state != new_state)
|
|
|
|
|
{
|
|
|
|
|
switch(_state)
|
|
|
|
|
{
|
2016-10-15 02:39:27 +00:00
|
|
|
|
case ColourBurst: _crt->output_colour_burst(_cycles_in_state * 6, _phase, 128); break;
|
|
|
|
|
case Sync: _crt->output_sync(_cycles_in_state * 6); break;
|
|
|
|
|
case Blank: _crt->output_blank(_cycles_in_state * 6); break;
|
|
|
|
|
case Pixels: _crt->output_data(_cycles_in_state * 6, 2); break;
|
2016-10-13 01:29:21 +00:00
|
|
|
|
}
|
|
|
|
|
_state = new_state;
|
|
|
|
|
_cycles_in_state = 0;
|
|
|
|
|
if(_state == Pixels) _pixel_target = _crt->allocate_write_area(120);
|
|
|
|
|
}
|
2016-10-13 01:42:36 +00:00
|
|
|
|
_cycles_in_state++;
|
2016-10-13 01:29:21 +00:00
|
|
|
|
|
|
|
|
|
if(new_state == Pixels) {
|
2016-10-13 11:59:11 +00:00
|
|
|
|
uint8_t pixels, control_byte;
|
|
|
|
|
|
2016-10-13 23:34:29 +00:00
|
|
|
|
if(_is_graphics_mode && _counter < 200*64)
|
2016-10-13 01:52:47 +00:00
|
|
|
|
{
|
2016-10-13 11:59:11 +00:00
|
|
|
|
control_byte = pixels = _ram[0xa000 + (_counter >> 6) * 40 + h_counter];
|
2016-10-13 01:52:47 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int address = 0xbb80 + (_counter >> 9) * 40 + h_counter;
|
2016-10-13 11:59:11 +00:00
|
|
|
|
control_byte = _ram[address];
|
2016-10-13 23:34:29 +00:00
|
|
|
|
int line = _use_double_height_characters ? ((_counter >> 7) & 7) : ((_counter >> 6) & 7);
|
2016-10-17 02:14:01 +00:00
|
|
|
|
pixels = _ram[_character_set_base_address + (control_byte&127) * 8 + line];
|
2016-10-13 11:59:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-13 23:34:29 +00:00
|
|
|
|
uint8_t inverse_mask = (control_byte & 0x80) ? 0x77 : 0x00;
|
|
|
|
|
if(_blink_text) inverse_mask ^= (_frame_counter&32) ? 0x77 : 0x00;
|
|
|
|
|
|
2016-10-13 11:59:11 +00:00
|
|
|
|
if((control_byte & 0x7f) >= 32)
|
|
|
|
|
{
|
|
|
|
|
uint8_t colours[2] = {
|
2016-10-13 23:34:29 +00:00
|
|
|
|
(uint8_t)(_paper ^ inverse_mask),
|
|
|
|
|
(uint8_t)(_ink ^ inverse_mask),
|
2016-10-13 11:59:11 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_pixel_target[0] = (colours[(pixels >> 4)&1] & 0x0f) | (colours[(pixels >> 5)&1] & 0xf0);
|
|
|
|
|
_pixel_target[1] = (colours[(pixels >> 2)&1] & 0x0f) | (colours[(pixels >> 3)&1] & 0xf0);
|
|
|
|
|
_pixel_target[2] = (colours[(pixels >> 0)&1] & 0x0f) | (colours[(pixels >> 1)&1] & 0xf0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch(control_byte & 0x7f)
|
|
|
|
|
{
|
|
|
|
|
case 0x00: _ink = 0x00; break;
|
|
|
|
|
case 0x01: _ink = 0x44; break;
|
|
|
|
|
case 0x02: _ink = 0x22; break;
|
|
|
|
|
case 0x03: _ink = 0x66; break;
|
|
|
|
|
case 0x04: _ink = 0x11; break;
|
|
|
|
|
case 0x05: _ink = 0x55; break;
|
|
|
|
|
case 0x06: _ink = 0x33; break;
|
|
|
|
|
case 0x07: _ink = 0x77; break;
|
2016-10-13 23:34:29 +00:00
|
|
|
|
|
|
|
|
|
case 0x08: case 0x09: case 0x0a: case 0x0b:
|
|
|
|
|
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
|
|
|
|
|
_use_alternative_character_set = (control_byte&1);
|
|
|
|
|
_use_double_height_characters = (control_byte&2);
|
|
|
|
|
_blink_text = (control_byte&4);
|
|
|
|
|
set_character_set_base_address();
|
|
|
|
|
break;
|
|
|
|
|
|
2016-10-13 11:59:11 +00:00
|
|
|
|
case 0x10: _paper = 0x00; break;
|
|
|
|
|
case 0x11: _paper = 0x44; break;
|
|
|
|
|
case 0x12: _paper = 0x22; break;
|
|
|
|
|
case 0x13: _paper = 0x66; break;
|
|
|
|
|
case 0x14: _paper = 0x11; break;
|
|
|
|
|
case 0x15: _paper = 0x55; break;
|
|
|
|
|
case 0x16: _paper = 0x33; break;
|
|
|
|
|
case 0x17: _paper = 0x77; break;
|
2016-10-13 23:34:29 +00:00
|
|
|
|
|
|
|
|
|
case 0x18: case 0x19: case 0x1a: case 0x1b:
|
|
|
|
|
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
|
|
|
|
|
_is_graphics_mode = (control_byte & 4);
|
|
|
|
|
_is_sixty_hertz = !(control_byte & 2);
|
|
|
|
|
break;
|
|
|
|
|
|
2016-10-13 11:59:11 +00:00
|
|
|
|
default: break;
|
|
|
|
|
}
|
2016-10-13 23:34:29 +00:00
|
|
|
|
_pixel_target[0] = _pixel_target[1] = _pixel_target[2] = (uint8_t)(_paper ^ inverse_mask);
|
2016-10-13 01:52:47 +00:00
|
|
|
|
}
|
2016-10-13 01:29:21 +00:00
|
|
|
|
_pixel_target += 3;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-12 23:20:23 +00:00
|
|
|
|
}
|