2021-07-22 22:43:07 +00:00
|
|
|
|
//
|
|
|
|
|
// Blitter.cpp
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 22/07/2021.
|
|
|
|
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include "Blitter.hpp"
|
|
|
|
|
|
2021-09-23 22:13:51 +00:00
|
|
|
|
#include "Minterms.h"
|
|
|
|
|
|
2021-07-22 22:43:07 +00:00
|
|
|
|
//#define NDEBUG
|
|
|
|
|
#define LOG_PREFIX "[Blitter] "
|
|
|
|
|
#include "../../Outputs/Log.hpp"
|
|
|
|
|
|
|
|
|
|
using namespace Amiga;
|
|
|
|
|
|
|
|
|
|
void Blitter::set_control(int index, uint16_t value) {
|
2021-09-21 03:08:26 +00:00
|
|
|
|
if(index) {
|
2021-09-26 22:16:00 +00:00
|
|
|
|
line_mode_ = (value & 0x0001);
|
2021-09-26 23:18:12 +00:00
|
|
|
|
one_dot_ = value & 0x0002;
|
|
|
|
|
line_direction_ = (value >> 2) & 7;
|
|
|
|
|
line_sign_ = (value & 0x0040) ? -1 : 1;
|
|
|
|
|
|
|
|
|
|
direction_ = one_dot_ ? uint32_t(-1) : uint32_t(1);
|
2021-09-29 02:11:58 +00:00
|
|
|
|
inclusive_fill_ = (value & 0x0008);
|
|
|
|
|
exclusive_fill_ = (value & 0x0010);
|
|
|
|
|
fill_carry_ = (value & 0x0004);
|
2021-09-21 03:08:26 +00:00
|
|
|
|
} else {
|
2021-09-23 22:30:35 +00:00
|
|
|
|
minterms_ = value & 0xff;
|
2021-09-23 22:38:37 +00:00
|
|
|
|
channel_enables_[3] = value & 0x100;
|
2021-09-24 00:42:31 +00:00
|
|
|
|
channel_enables_[2] = value & 0x200;
|
2021-09-23 22:38:37 +00:00
|
|
|
|
channel_enables_[1] = value & 0x400;
|
|
|
|
|
channel_enables_[0] = value & 0x800;
|
2021-09-21 03:08:26 +00:00
|
|
|
|
}
|
|
|
|
|
shifts_[index] = value >> 12;
|
2021-07-22 22:43:07 +00:00
|
|
|
|
LOG("Set control " << index << " to " << PADHEX(4) << value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Blitter::set_first_word_mask(uint16_t value) {
|
|
|
|
|
LOG("Set first word mask: " << PADHEX(4) << value);
|
2021-09-26 22:42:08 +00:00
|
|
|
|
a_mask_[0] = value;
|
2021-07-22 22:43:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Blitter::set_last_word_mask(uint16_t value) {
|
|
|
|
|
LOG("Set last word mask: " << PADHEX(4) << value);
|
2021-09-26 22:42:08 +00:00
|
|
|
|
a_mask_[1] = value;
|
2021-07-22 22:43:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Blitter::set_size(uint16_t value) {
|
2021-08-10 11:17:01 +00:00
|
|
|
|
width_ = (width_ & ~0x3f) | (value & 0x3f);
|
|
|
|
|
height_ = (height_ & ~0x3ff) | (value >> 6);
|
|
|
|
|
LOG("Set size to " << std::dec << width_ << ", " << height_);
|
2021-09-23 22:13:51 +00:00
|
|
|
|
|
|
|
|
|
// Current assumption: writing this register informs the
|
|
|
|
|
// blitter that it should treat itself as about to start a new line.
|
2021-07-22 22:43:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Blitter::set_minterms(uint16_t value) {
|
|
|
|
|
LOG("Set minterms " << PADHEX(4) << value);
|
2021-08-10 11:17:01 +00:00
|
|
|
|
minterms_ = value & 0xff;
|
2021-07-22 22:43:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Blitter::set_vertical_size(uint16_t value) {
|
|
|
|
|
LOG("Set vertical size " << PADHEX(4) << value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Blitter::set_horizontal_size(uint16_t value) {
|
|
|
|
|
LOG("Set horizontal size " << PADHEX(4) << value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Blitter::set_modulo(int channel, uint16_t value) {
|
|
|
|
|
LOG("Set modulo size " << channel << " to " << PADHEX(4) << value);
|
2021-09-23 22:30:35 +00:00
|
|
|
|
|
|
|
|
|
// Convert by sign extension.
|
|
|
|
|
modulos_[channel] = uint32_t(int16_t(value) >> 1);
|
2021-07-22 22:43:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Blitter::set_data(int channel, uint16_t value) {
|
|
|
|
|
LOG("Set data " << channel << " to " << PADHEX(4) << value);
|
2021-09-24 02:05:59 +00:00
|
|
|
|
|
|
|
|
|
// Ugh, backed myself into a corner. TODO: clean.
|
|
|
|
|
switch(channel) {
|
|
|
|
|
case 0: a_ = value; break;
|
|
|
|
|
case 1: b_ = value; break;
|
|
|
|
|
case 2: c_ = value; break;
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
2021-07-22 22:43:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t Blitter::get_status() {
|
2021-09-26 22:16:00 +00:00
|
|
|
|
LOG("Returned status of " << (height_ ? 0x8000 : 0x0000));
|
|
|
|
|
return height_ ? 0x8000 : 0x0000;
|
2021-07-22 22:43:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-10 11:17:01 +00:00
|
|
|
|
bool Blitter::advance() {
|
2021-09-16 23:56:28 +00:00
|
|
|
|
if(!height_) return false;
|
|
|
|
|
|
2021-09-21 03:08:26 +00:00
|
|
|
|
if(line_mode_) {
|
|
|
|
|
//
|
|
|
|
|
// Line mode.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
// Bluffer's guide to line mode:
|
|
|
|
|
//
|
|
|
|
|
// In Bresenham terms, the following registers have been set up:
|
|
|
|
|
//
|
|
|
|
|
// [A modulo] = 4 * (dy - dx)
|
|
|
|
|
// [B modulo] = 4 * dy
|
|
|
|
|
// [A pointer] = 4 * dy - 2 * dx, with the sign flag in BLTCON1 indicating sign.
|
|
|
|
|
//
|
|
|
|
|
// [A data] = 0x8000
|
|
|
|
|
// [Both masks] = 0xffff
|
|
|
|
|
// [A shift] = x1 & 15
|
|
|
|
|
//
|
|
|
|
|
// [B data] = texture
|
|
|
|
|
// [B shift] = bit at which to start the line texture (0 = LSB)
|
|
|
|
|
//
|
|
|
|
|
// [C and D pointers] = word containing the first pixel of the line
|
|
|
|
|
// [C and D modulo] = width of the bitplane in bytes
|
|
|
|
|
//
|
|
|
|
|
// height = number of pixels
|
|
|
|
|
//
|
|
|
|
|
// If ONEDOT of BLTCON1 is set, plot only a single bit per horizontal row.
|
|
|
|
|
//
|
|
|
|
|
// BLTCON1 quadrants are (bits 2–4):
|
|
|
|
|
//
|
|
|
|
|
// 110 -> step in x, x positive, y negative
|
|
|
|
|
// 111 -> step in x, x negative, y negative
|
|
|
|
|
// 101 -> step in x, x negative, y positive
|
|
|
|
|
// 100 -> step in x, x positive, y positive
|
|
|
|
|
//
|
|
|
|
|
// 001 -> step in y, x positive, y negative
|
|
|
|
|
// 011 -> step in y, x negative, y negative
|
|
|
|
|
// 010 -> step in y, x negative, y positive
|
|
|
|
|
// 000 -> step in y, x positive, y positive
|
|
|
|
|
//
|
|
|
|
|
// So that's:
|
|
|
|
|
//
|
2021-09-26 23:18:12 +00:00
|
|
|
|
// * bit 4 = x [=1] or y [=0] major;
|
2021-09-21 03:08:26 +00:00
|
|
|
|
// * bit 3 = 1 => major variable negative; otherwise positive;
|
|
|
|
|
// * bit 2 = 1 => minor variable negative; otherwise positive.
|
|
|
|
|
|
2021-09-29 01:39:09 +00:00
|
|
|
|
//
|
|
|
|
|
// Implementation below is heavily based on the documentation found
|
|
|
|
|
// at https://github.com/niklasekstrom/blitter-subpixel-line/blob/master/Drawing%20lines%20using%20the%20Amiga%20blitter.pdf
|
2021-09-30 02:19:17 +00:00
|
|
|
|
//
|
2021-09-29 01:39:09 +00:00
|
|
|
|
|
2021-09-30 02:19:17 +00:00
|
|
|
|
int error = int16_t(pointer_[0] << 1) >> 1; // TODO: what happens if line_sign_ doesn't agree with this?
|
2021-09-29 01:39:09 +00:00
|
|
|
|
bool draw_ = true;
|
2021-09-26 23:18:12 +00:00
|
|
|
|
while(height_--) {
|
|
|
|
|
|
2021-09-29 01:39:09 +00:00
|
|
|
|
if(draw_) {
|
2021-09-30 02:49:35 +00:00
|
|
|
|
// TODO: patterned lines. Unclear what to do with the bit that comes out of b.
|
|
|
|
|
// Probably extend it to a full word?
|
2021-09-29 01:39:09 +00:00
|
|
|
|
c_ = ram_[pointer_[3] & ram_mask_];
|
|
|
|
|
ram_[pointer_[3] & ram_mask_] =
|
|
|
|
|
apply_minterm<uint16_t>(a_ >> shifts_[0], b_, c_, minterms_);
|
|
|
|
|
draw_ &= !one_dot_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr int LEFT = 1 << 0;
|
|
|
|
|
constexpr int RIGHT = 1 << 1;
|
|
|
|
|
constexpr int UP = 1 << 2;
|
|
|
|
|
constexpr int DOWN = 1 << 3;
|
|
|
|
|
int step = (line_direction_ & 4) ?
|
|
|
|
|
((line_direction_ & 1) ? LEFT : RIGHT) :
|
|
|
|
|
((line_direction_ & 1) ? UP : DOWN);
|
|
|
|
|
|
|
|
|
|
if(error < 0) {
|
|
|
|
|
error += modulos_[1];
|
|
|
|
|
} else {
|
|
|
|
|
step |=
|
|
|
|
|
(line_direction_ & 4) ?
|
|
|
|
|
((line_direction_ & 2) ? UP : DOWN) :
|
|
|
|
|
((line_direction_ & 2) ? LEFT : RIGHT);
|
|
|
|
|
|
|
|
|
|
error += modulos_[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(step & LEFT) {
|
|
|
|
|
--shifts_[0];
|
|
|
|
|
if(shifts_[0] == -1) {
|
|
|
|
|
--pointer_[3];
|
|
|
|
|
}
|
|
|
|
|
} else if(step & RIGHT) {
|
2021-09-28 02:06:00 +00:00
|
|
|
|
++shifts_[0];
|
|
|
|
|
if(shifts_[0] == 16) {
|
|
|
|
|
++pointer_[3];
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-29 01:39:09 +00:00
|
|
|
|
shifts_[0] &= 15;
|
|
|
|
|
|
|
|
|
|
if(step & UP) {
|
|
|
|
|
pointer_[3] -= modulos_[2];
|
|
|
|
|
draw_ = true;
|
|
|
|
|
} else if(step & DOWN) {
|
|
|
|
|
pointer_[3] += modulos_[2];
|
|
|
|
|
draw_ = true;
|
|
|
|
|
}
|
2021-09-26 23:18:12 +00:00
|
|
|
|
}
|
2021-09-21 03:08:26 +00:00
|
|
|
|
} else {
|
|
|
|
|
// Copy mode.
|
2021-09-29 02:11:58 +00:00
|
|
|
|
|
2021-09-30 06:42:59 +00:00
|
|
|
|
// Quick hack: do the entire action atomically.
|
2021-09-23 22:13:51 +00:00
|
|
|
|
for(int y = 0; y < height_; y++) {
|
|
|
|
|
for(int x = 0; x < width_; x++) {
|
2021-09-23 22:38:37 +00:00
|
|
|
|
if(channel_enables_[0]) {
|
2021-09-24 02:05:59 +00:00
|
|
|
|
a32_ = (a32_ << 16) | ram_[pointer_[0] & ram_mask_];
|
|
|
|
|
a_ = uint16_t(a32_ >> shifts_[0]);
|
2021-09-23 22:38:37 +00:00
|
|
|
|
pointer_[0] += direction_;
|
2021-09-24 02:05:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 22:38:37 +00:00
|
|
|
|
if(channel_enables_[1]) {
|
2021-09-24 02:05:59 +00:00
|
|
|
|
b32_ = (b32_ << 16) | ram_[pointer_[1] & ram_mask_];
|
|
|
|
|
b_ = uint16_t(b32_ >> shifts_[1]);
|
2021-09-23 22:38:37 +00:00
|
|
|
|
pointer_[1] += direction_;
|
2021-09-24 02:05:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 22:38:37 +00:00
|
|
|
|
if(channel_enables_[2]) {
|
2021-09-24 02:05:59 +00:00
|
|
|
|
c_ = ram_[pointer_[2] & ram_mask_];
|
2021-09-23 22:38:37 +00:00
|
|
|
|
pointer_[2] += direction_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(channel_enables_[3]) {
|
2021-09-30 02:49:35 +00:00
|
|
|
|
uint16_t a_mask = 0xffff;
|
|
|
|
|
if(x == 0) a_mask &= a_mask_[0];
|
|
|
|
|
if(x == width_ - 1) a_mask &= a_mask_[1];
|
2021-09-26 22:42:08 +00:00
|
|
|
|
|
2021-09-23 22:38:37 +00:00
|
|
|
|
ram_[pointer_[3] & ram_mask_] =
|
2021-09-29 02:11:58 +00:00
|
|
|
|
apply_minterm<uint16_t>(
|
|
|
|
|
a_ & a_mask, // TODO: is this properly-placed?
|
|
|
|
|
b_,
|
|
|
|
|
c_,
|
|
|
|
|
minterms_);
|
2021-09-24 02:05:59 +00:00
|
|
|
|
|
2021-09-23 22:38:37 +00:00
|
|
|
|
pointer_[3] += direction_;
|
|
|
|
|
}
|
2021-09-23 22:13:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 22:38:37 +00:00
|
|
|
|
pointer_[0] += modulos_[0] * channel_enables_[0];
|
|
|
|
|
pointer_[1] += modulos_[1] * channel_enables_[1];
|
|
|
|
|
pointer_[2] += modulos_[2] * channel_enables_[2];
|
|
|
|
|
pointer_[3] += modulos_[3] * channel_enables_[3];
|
2021-09-23 22:13:51 +00:00
|
|
|
|
}
|
2021-09-21 03:08:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-17 01:01:37 +00:00
|
|
|
|
height_ = 0;
|
2021-09-16 23:56:28 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
2021-07-22 22:43:07 +00:00
|
|
|
|
}
|