mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-16 18:30:32 +00:00
Attempt a real slot-by-slot blit.
This commit is contained in:
parent
5d992758f8
commit
94a90b7a89
@ -223,8 +223,9 @@ uint16_t Blitter::get_status() {
|
|||||||
bool Blitter::advance_dma() {
|
bool Blitter::advance_dma() {
|
||||||
if(!height_) return false;
|
if(!height_) return false;
|
||||||
|
|
||||||
not_zero_flag_ = false;
|
|
||||||
if(line_mode_) {
|
if(line_mode_) {
|
||||||
|
not_zero_flag_ = false;
|
||||||
|
|
||||||
// As-yet unimplemented:
|
// As-yet unimplemented:
|
||||||
assert(b_data_ == 0xffff);
|
assert(b_data_ == 0xffff);
|
||||||
|
|
||||||
@ -332,139 +333,140 @@ bool Blitter::advance_dma() {
|
|||||||
draw_ = true;
|
draw_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
busy_ = false;
|
||||||
} else {
|
} else {
|
||||||
// Copy mode.
|
// Copy mode.
|
||||||
|
if(!busy_) {
|
||||||
|
sequencer_.begin();
|
||||||
|
a32_ = 0;
|
||||||
|
b32_ = 0;
|
||||||
|
|
||||||
// Quick hack: do the entire action atomically.
|
y_ = 0;
|
||||||
sequencer_.begin();
|
x_ = 0;
|
||||||
a32_ = 0;
|
loop_index_ = -1;
|
||||||
b32_ = 0;
|
write_phase_ = WritePhase::Starting;
|
||||||
|
not_zero_flag_ = 0;
|
||||||
|
busy_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
y_ = 0;
|
const auto next = sequencer_.next();
|
||||||
x_ = 0;
|
|
||||||
int loop_index_ = -1;
|
|
||||||
write_phase_ = WritePhase::Starting;
|
|
||||||
|
|
||||||
while(true) {
|
// If this is the start of a new iteration, check for end of line,
|
||||||
const auto next = sequencer_.next();
|
// or of blit, and pick an appropriate mask for A based on location.
|
||||||
|
if(next.second != loop_index_) {
|
||||||
|
transient_a_mask_ = x_ ? 0xffff : a_mask_[0];
|
||||||
|
|
||||||
// If this is the start of a new iteration, check for end of line,
|
// Check whether a complete row was completed in the previous iteration.
|
||||||
// or of blit, and pick an appropriate mask for A based on location.
|
// If so then add modulos.
|
||||||
if(next.second != loop_index_) {
|
if(!x_ && y_) {
|
||||||
transient_a_mask_ = x_ ? 0xffff : a_mask_[0];
|
pointer_[0] += modulos_[0] * channel_enables_[0] * direction_;
|
||||||
|
pointer_[1] += modulos_[1] * channel_enables_[1] * direction_;
|
||||||
|
pointer_[2] += modulos_[2] * channel_enables_[2] * direction_;
|
||||||
|
pointer_[3] += modulos_[3] * channel_enables_[3] * direction_;
|
||||||
|
}
|
||||||
|
|
||||||
// Check whether a complete row was completed in the previous iteration.
|
++x_;
|
||||||
// If so then add modulos.
|
if(x_ == width_) {
|
||||||
if(!x_ && y_) {
|
transient_a_mask_ &= a_mask_[1];
|
||||||
pointer_[0] += modulos_[0] * channel_enables_[0] * direction_;
|
x_ = 0;
|
||||||
pointer_[1] += modulos_[1] * channel_enables_[1] * direction_;
|
++y_;
|
||||||
pointer_[2] += modulos_[2] * channel_enables_[2] * direction_;
|
if(y_ == height_) {
|
||||||
pointer_[3] += modulos_[3] * channel_enables_[3] * direction_;
|
sequencer_.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
++x_;
|
|
||||||
if(x_ == width_) {
|
|
||||||
transient_a_mask_ &= a_mask_[1];
|
|
||||||
x_ = 0;
|
|
||||||
++y_;
|
|
||||||
if(y_ == height_) {
|
|
||||||
sequencer_.complete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++loop_index_;
|
|
||||||
}
|
}
|
||||||
|
++loop_index_;
|
||||||
|
}
|
||||||
|
|
||||||
using Channel = BlitterSequencer::Channel;
|
using Channel = BlitterSequencer::Channel;
|
||||||
switch(next.first) {
|
switch(next.first) {
|
||||||
case Channel::A:
|
case Channel::A:
|
||||||
a_data_ = ram_[pointer_[0] & ram_mask_];
|
a_data_ = ram_[pointer_[0] & ram_mask_];
|
||||||
pointer_[0] += direction_;
|
pointer_[0] += direction_;
|
||||||
continue;
|
return true;
|
||||||
case Channel::B:
|
case Channel::B:
|
||||||
b_data_ = ram_[pointer_[1] & ram_mask_];
|
b_data_ = ram_[pointer_[1] & ram_mask_];
|
||||||
pointer_[1] += direction_;
|
pointer_[1] += direction_;
|
||||||
continue;
|
return true;
|
||||||
case Channel::C:
|
case Channel::C:
|
||||||
c_data_ = ram_[pointer_[2] & ram_mask_];
|
c_data_ = ram_[pointer_[2] & ram_mask_];
|
||||||
pointer_[2] += direction_;
|
pointer_[2] += direction_;
|
||||||
continue;
|
return true;
|
||||||
case Channel::None:
|
case Channel::None:
|
||||||
continue;
|
return false;
|
||||||
case Channel::Write: break;
|
case Channel::Write: break;
|
||||||
case Channel::FlushPipeline:
|
case Channel::FlushPipeline:
|
||||||
// HACK. REMOVE ONCE NON-BLOCKING.
|
posit_interrupt(InterruptFlag::Blitter);
|
||||||
posit_interrupt(InterruptFlag::Blitter);
|
height_ = 0;
|
||||||
height_ = 0;
|
busy_ = false;
|
||||||
// END HACK.
|
|
||||||
|
|
||||||
if(write_phase_ == WritePhase::Full) {
|
if(write_phase_ == WritePhase::Full) {
|
||||||
ram_[write_address_ & ram_mask_] = write_value_;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
a32_ = (a32_ << 16) | (a_data_ & transient_a_mask_);
|
|
||||||
b32_ = (b32_ << 16) | b_data_;
|
|
||||||
|
|
||||||
uint16_t a, b;
|
|
||||||
|
|
||||||
// The barrel shifter shifts to the right in ascending address mode,
|
|
||||||
// but to the left otherwise.
|
|
||||||
if(!one_dot_) {
|
|
||||||
a = uint16_t(a32_ >> shifts_[0]);
|
|
||||||
b = uint16_t(b32_ >> shifts_[1]);
|
|
||||||
} else {
|
|
||||||
// TODO: there must be a neater solution than this.
|
|
||||||
a = uint16_t(
|
|
||||||
(a32_ << shifts_[0]) |
|
|
||||||
(a32_ >> (32 - shifts_[0]))
|
|
||||||
);
|
|
||||||
|
|
||||||
b = uint16_t(
|
|
||||||
(b32_ << shifts_[1]) |
|
|
||||||
(b32_ >> (32 - shifts_[1]))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t output =
|
|
||||||
apply_minterm<uint16_t>(
|
|
||||||
a,
|
|
||||||
b,
|
|
||||||
c_data_,
|
|
||||||
minterms_);
|
|
||||||
|
|
||||||
if(exclusive_fill_ || inclusive_fill_) {
|
|
||||||
// Use the fill tables nibble-by-nibble to figure out the filled word.
|
|
||||||
uint16_t fill_output = 0;
|
|
||||||
int ongoing_carry = fill_carry_;
|
|
||||||
const int type_mask = exclusive_fill_ ? (1 << 5) : 0;
|
|
||||||
for(int c = 0; c < 16; c += 4) {
|
|
||||||
const int total_index = (output & 0xf) | (ongoing_carry << 4) | type_mask;
|
|
||||||
fill_output |= ((fill_values[total_index >> 3] >> ((total_index & 7) * 4)) & 0xf) << c;
|
|
||||||
ongoing_carry = (fill_carries[total_index >> 5] >> (total_index & 31)) & 1;
|
|
||||||
output >>= 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
output = fill_output;
|
|
||||||
fill_carry_ = ongoing_carry;
|
|
||||||
}
|
|
||||||
|
|
||||||
not_zero_flag_ |= output;
|
|
||||||
|
|
||||||
switch(write_phase_) {
|
|
||||||
case WritePhase::Full:
|
|
||||||
ram_[write_address_ & ram_mask_] = write_value_;
|
ram_[write_address_ & ram_mask_] = write_value_;
|
||||||
[[fallthrough]];
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case WritePhase::Starting:
|
a32_ = (a32_ << 16) | (a_data_ & transient_a_mask_);
|
||||||
write_phase_ = WritePhase::Full;
|
b32_ = (b32_ << 16) | b_data_;
|
||||||
write_address_ = pointer_[3];
|
|
||||||
write_value_ = output;
|
|
||||||
pointer_[3] += direction_;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
default: break;
|
uint16_t a, b;
|
||||||
|
|
||||||
|
// The barrel shifter shifts to the right in ascending address mode,
|
||||||
|
// but to the left otherwise.
|
||||||
|
if(!one_dot_) {
|
||||||
|
a = uint16_t(a32_ >> shifts_[0]);
|
||||||
|
b = uint16_t(b32_ >> shifts_[1]);
|
||||||
|
} else {
|
||||||
|
// TODO: there must be a neater solution than this.
|
||||||
|
a = uint16_t(
|
||||||
|
(a32_ << shifts_[0]) |
|
||||||
|
(a32_ >> (32 - shifts_[0]))
|
||||||
|
);
|
||||||
|
|
||||||
|
b = uint16_t(
|
||||||
|
(b32_ << shifts_[1]) |
|
||||||
|
(b32_ >> (32 - shifts_[1]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t output =
|
||||||
|
apply_minterm<uint16_t>(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c_data_,
|
||||||
|
minterms_);
|
||||||
|
|
||||||
|
if(exclusive_fill_ || inclusive_fill_) {
|
||||||
|
// Use the fill tables nibble-by-nibble to figure out the filled word.
|
||||||
|
uint16_t fill_output = 0;
|
||||||
|
int ongoing_carry = fill_carry_;
|
||||||
|
const int type_mask = exclusive_fill_ ? (1 << 5) : 0;
|
||||||
|
for(int c = 0; c < 16; c += 4) {
|
||||||
|
const int total_index = (output & 0xf) | (ongoing_carry << 4) | type_mask;
|
||||||
|
fill_output |= ((fill_values[total_index >> 3] >> ((total_index & 7) * 4)) & 0xf) << c;
|
||||||
|
ongoing_carry = (fill_carries[total_index >> 5] >> (total_index & 31)) & 1;
|
||||||
|
output >>= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output = fill_output;
|
||||||
|
fill_carry_ = ongoing_carry;
|
||||||
|
}
|
||||||
|
|
||||||
|
not_zero_flag_ |= output;
|
||||||
|
|
||||||
|
switch(write_phase_) {
|
||||||
|
case WritePhase::Full:
|
||||||
|
ram_[write_address_ & ram_mask_] = write_value_;
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
|
case WritePhase::Starting:
|
||||||
|
write_phase_ = WritePhase::Full;
|
||||||
|
write_address_ = pointer_[3];
|
||||||
|
write_value_ = output;
|
||||||
|
pointer_[3] += direction_;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default: assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +215,8 @@ class Blitter: public DMADevice<4, 4> {
|
|||||||
} write_phase_;
|
} write_phase_;
|
||||||
int y_, x_;
|
int y_, x_;
|
||||||
uint16_t transient_a_mask_;
|
uint16_t transient_a_mask_;
|
||||||
|
bool busy_ = false;
|
||||||
|
int loop_index_ = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user