mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-09 00:37:27 +00:00
Starts to make some attempt at classic II modes.
This commit is contained in:
parent
4a42de4f18
commit
4b024c5787
@ -56,6 +56,22 @@ VideoBase::VideoBase() :
|
||||
crt_(CyclesPerLine - 1, 1, Outputs::Display::Type::NTSC60, Outputs::Display::InputDataType::Red4Green4Blue4) {
|
||||
crt_.set_display_type(Outputs::Display::DisplayType::RGB);
|
||||
crt_.set_visible_area(Outputs::Display::Rect(0.097f, 0.1f, 0.85f, 0.85f));
|
||||
|
||||
// Establish the shift lookup table for NTSC -> RGB output.
|
||||
for(int c = 0; c < 256; c++) {
|
||||
const auto top_nibble = c >> 4;
|
||||
|
||||
// Otherwise, check for descending disagreements.
|
||||
ntsc_shift_lookup_[c] = 4;
|
||||
int mask = 0x10;
|
||||
while(mask) {
|
||||
if((top_nibble & mask) != (c & mask)) break;
|
||||
mask >>= 1;
|
||||
--ntsc_shift_lookup_[c];
|
||||
}
|
||||
|
||||
ntsc_shift_lookup_[c] &= 3;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBase::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
@ -197,6 +213,7 @@ void VideoBase::output_row(int row, int start, int end) {
|
||||
|
||||
if(start == start_of_pixels) {
|
||||
// 640 is the absolute most number of pixels that might be generated
|
||||
// printf("Begin\n");
|
||||
next_pixel_ = pixels_ = reinterpret_cast<uint16_t *>(crt_.begin_data(640, 2));
|
||||
|
||||
// YUCKY HACK. I do not know when the IIgs fetches its super high-res palette
|
||||
@ -232,6 +249,12 @@ void VideoBase::output_row(int row, int start, int end) {
|
||||
case Apple::II::GraphicsMode::DoubleText:
|
||||
next_pixel_ = output_double_text(next_pixel_, window_start, window_end, row);
|
||||
break;
|
||||
case Apple::II::GraphicsMode::LowRes:
|
||||
next_pixel_ = output_low_resolution(next_pixel_, window_start, window_end, row);
|
||||
break;
|
||||
case Apple::II::GraphicsMode::HighRes:
|
||||
next_pixel_ = output_high_resolution(next_pixel_, window_start, window_end, row);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
@ -243,6 +266,7 @@ void VideoBase::output_row(int row, int start, int end) {
|
||||
}
|
||||
|
||||
if(end_of_period == start_of_right_border) {
|
||||
// printf("Output\n");
|
||||
crt_.output_data((start_of_right_border - start_of_pixels) * CyclesPerTick, next_pixel_ ? size_t(next_pixel_ - pixels_) : 1);
|
||||
next_pixel_ = pixels_ = nullptr;
|
||||
}
|
||||
@ -405,3 +429,115 @@ uint16_t *VideoBase::output_super_high_res(uint16_t *target, int start, int end,
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
uint16_t *VideoBase::output_low_resolution(uint16_t *target, int start, int end, int row) {
|
||||
const int row_shift = row&4;
|
||||
const uint16_t row_address = get_row_address(row);
|
||||
for(int c = start; c < end; c++) {
|
||||
ntsc_shift_ <<= 14;
|
||||
|
||||
const uint8_t source = ram_[row_address + c] >> row_shift;
|
||||
|
||||
// Convulve input as a function of odd/even row.
|
||||
if((start + c)&1) {
|
||||
ntsc_shift_ |= ((source & 4) >> 2) * 0x2222;
|
||||
ntsc_shift_ |= ((source & 8) >> 3) * 0x1111;
|
||||
ntsc_shift_ |= ((source & 1) >> 0) * 0x0888;
|
||||
ntsc_shift_ |= ((source & 2) >> 1) * 0x0444;
|
||||
} else {
|
||||
ntsc_shift_ |= ((source & 1) >> 0) * 0x2222;
|
||||
ntsc_shift_ |= ((source & 2) >> 1) * 0x1111;
|
||||
ntsc_shift_ |= ((source & 4) >> 2) * 0x0888;
|
||||
ntsc_shift_ |= ((source & 8) >> 3) * 0x0444;
|
||||
}
|
||||
|
||||
// TODO: initial state?
|
||||
target = output_shift(target, (c * 14) & 3);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
uint16_t *VideoBase::output_high_resolution(uint16_t *target, int start, int end, int row) {
|
||||
const uint16_t row_address = get_row_address(row);
|
||||
for(int c = start; c < end; c++) {
|
||||
ntsc_shift_ <<= 14;
|
||||
|
||||
uint8_t source = ram_[row_address + c];
|
||||
|
||||
// HACK: bit reverse, for now.
|
||||
// Because I'd forgotten which order the Apple II serialises bits in, and have predicated too much upon it.
|
||||
source = ((source >> 6) & 0x01) | ((source >> 4) & 0x02) | ((source >> 2) & 0x04) | ((source >> 0) & 0x08) | ((source << 2) & 0x10) | ((source << 4) & 0x20) | ((source << 6) & 0x40);
|
||||
|
||||
// TODO: can do this in two multiplies, I think.
|
||||
const uint16_t doubled_source =
|
||||
((source&0x01) * (0x0003 >> 0)) +
|
||||
((source&0x02) * (0x000c >> 1)) +
|
||||
((source&0x04) * (0x0030 >> 2)) +
|
||||
((source&0x08) * (0x00c0 >> 3)) +
|
||||
((source&0x10) * (0x0300 >> 4)) +
|
||||
((source&0x20) * (0x0c00 >> 5)) +
|
||||
((source&0x40) * (0x3000 >> 6));
|
||||
|
||||
// Just append new bits, doubled up (and possibly delayed).
|
||||
// TODO: I can kill the conditional here.
|
||||
if(source & high_resolution_mask_ & 0x80) {
|
||||
ntsc_shift_ |= ((ntsc_shift_ >> 1) & 0x2000) | (doubled_source >> 1);
|
||||
} else {
|
||||
ntsc_shift_ |= doubled_source;
|
||||
}
|
||||
|
||||
// TODO: initial state?
|
||||
target = output_shift(target, (c * 14) & 3);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
uint16_t *VideoBase::output_shift(uint16_t *target, int phase) const {
|
||||
constexpr uint8_t rolls[4][16] = {
|
||||
{
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
|
||||
},
|
||||
{
|
||||
0x0, 0x8, 0x1, 0x9, 0x2, 0xa, 0x3, 0xb,
|
||||
0x4, 0xc, 0x5, 0xd, 0x6, 0xe, 0x7, 0xf
|
||||
},
|
||||
{
|
||||
0x0, 0x4, 0x8, 0xc, 0x1, 0x5, 0x9, 0xd,
|
||||
0x2, 0x6, 0xa, 0xe, 0x3, 0x7, 0xb, 0xf
|
||||
},
|
||||
{
|
||||
0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe,
|
||||
0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xd, 0xf
|
||||
},
|
||||
};
|
||||
|
||||
#define OutputPixel(offset) {\
|
||||
const auto phase_offset = ntsc_shift_lookup_[(ntsc_shift_ >> offset) & 0xff]; \
|
||||
const auto raw_bits = (ntsc_shift_ >> (offset + phase_offset)) & 0x0f; \
|
||||
target[13 - offset] = appleii_palette[rolls[(phase + 13 - offset - phase_offset)&3][raw_bits]]; \
|
||||
}
|
||||
//#define OutputPixel(offset) {\
|
||||
// target[13 - offset] = appleii_palette[(ntsc_shift_ >> offset) & 0xf]; \
|
||||
//}
|
||||
|
||||
OutputPixel(13);
|
||||
OutputPixel(12);
|
||||
OutputPixel(11);
|
||||
OutputPixel(10);
|
||||
OutputPixel(9);
|
||||
OutputPixel(8);
|
||||
OutputPixel(7);
|
||||
OutputPixel(6);
|
||||
OutputPixel(5);
|
||||
OutputPixel(4);
|
||||
OutputPixel(3);
|
||||
OutputPixel(2);
|
||||
OutputPixel(1);
|
||||
OutputPixel(0);
|
||||
|
||||
#undef OutputPixel
|
||||
|
||||
return target + 14;
|
||||
}
|
||||
|
@ -87,9 +87,32 @@ class VideoBase: public Apple::II::VideoSwitches<Cycles> {
|
||||
uint16_t *output_double_text(uint16_t *target, int start, int end, int row) const;
|
||||
uint16_t *output_char(uint16_t *target, uint8_t source, int row) const;
|
||||
|
||||
uint16_t *output_low_resolution(uint16_t *target, int start, int end, int row);
|
||||
|
||||
uint16_t *output_high_resolution(uint16_t *target, int start, int end, int row);
|
||||
|
||||
// Super high-res per-line state.
|
||||
uint8_t line_control_;
|
||||
uint16_t palette_[16];
|
||||
|
||||
// Lookup tables and state to assist in the IIgs' mapping from NTSC to RGB.
|
||||
//
|
||||
// My understanding of the real-life algorithm is: maintain a four-bit buffer.
|
||||
// Fill it in a circular fashion. Ordinarily, output the result of looking
|
||||
// up the RGB mapping of those four bits of Apple II output (which outputs four
|
||||
// bits per NTSC colour cycle), commuted as per current phase. But if the bit
|
||||
// being inserted differs from that currently in its position in the shift
|
||||
// register, hold the existing output for three shifts.
|
||||
//
|
||||
// From there I am using the following:
|
||||
|
||||
// Maps from the most recent eight bits of Apple II output to how far back
|
||||
// into history the graphics system should look for output.
|
||||
uint8_t ntsc_shift_lookup_[256];
|
||||
int ntsc_shift_ = 0;
|
||||
|
||||
/// Outputs the lowest 14 bits from @c ntsc_shift_, mapping to RGB.
|
||||
uint16_t *output_shift(uint16_t *target, int phase) const;
|
||||
};
|
||||
|
||||
class Video: public VideoBase {
|
||||
|
Loading…
x
Reference in New Issue
Block a user