-
Notifications
You must be signed in to change notification settings - Fork 0
/
circuits.cpp
249 lines (201 loc) · 8.68 KB
/
circuits.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#include "circuits.h"
#include <QFont>
#include <QPainter>
#include <QDebug>
#include <stdexcept>
using std::runtime_error;
namespace Circuits {
Blueprint * ROM (int addressBits, int dataBits, ROMDataLSBSide dataLSB, ROMAddress0Side addr0Side, const QVector<quint64> &data, bool omitEmpty) {
const Blueprint::Ink trace = Blueprint::Trace5;
if (omitEmpty && addr0Side == Far)
throw runtime_error("Empty columns can only be omitted if address 0 is set to input side. Sorry.");
// things will go nuts if bit counts are too high but for now just let
// things go nuts. maybe overflow checking some day.
quint64 addresses = 1ULL << addressBits;
int width = 3 + 2 * addresses + 1;
int height = (omitEmpty ? 3 : 1) + 4 * (addressBits - 1) + 2 * dataBits;
qDebug() << "rom will be" << width << "x" << height;
Blueprint *bp = new Blueprint(width, height);
int row, col;
// address inputs
row = height - 1;
for (int a = 0; a < addressBits; ++ a) {
if (a == 0) {
if (omitEmpty) {
bp->set(1, row-2, Blueprint::Not);
bp->set(2, row-2, Blueprint::Write);
bp->set(0, row-1, trace);
bp->set(1, row-1, Blueprint::Read);
bp->set(1, row, Blueprint::Buffer);
bp->set(2, row, Blueprint::Write);
row -= 3;
} else {
bp->set(0, row, Blueprint::Read);
bp->set(1, row, addr0Side == Far ? Blueprint::Buffer : Blueprint::Not);
bp->set(2, row, Blueprint::Write);
row -= 1;
}
} else {
bp->set(1, row-3, Blueprint::Not);
bp->set(2, row-3, Blueprint::Write);
bp->set(0, row-2, trace);
bp->set(1, row-2, Blueprint::Read);
bp->set(1, row-1, Blueprint::Buffer);
bp->set(2, row-1, Blueprint::Write);
row -= 4;
}
}
// crosses, gates, background traces
for (row = 0; row < height; row += 2) {
for (col = 3; col < width - 1; col += 2) {
bp->set(col, row, Blueprint::Cross);
bp->set(col+1, row, trace);
}
if (row < height - 1) {
bool isand = true;
for (col = 3; col < width - 1; col += 2) {
Blueprint::Ink ink = isand ? Blueprint::And : Blueprint::Nor;
bp->set(col, row+1, ink);
bp->set(col+1, row+1, ink);
isand = !isand;
}
}
}
int lastColumn = 0;
// address and data bits
// to do: truncate if data vector smaller than address space
col = (addr0Side == Far ? (width - 2) : (width - 2 * addresses) );
bool isnor = (addr0Side == Far);
for (quint64 address = 0; address < addresses; ++ address) {
quint64 curdata = data.value(address);
if (omitEmpty && curdata == 0)
continue;
// ---- data bits
row = (dataLSB == Top ? 0 : (2 * (dataBits - 1)));
for (int bit = 0; bit < dataBits; ++ bit) {
if (curdata & (1ULL << bit))
bp->set(col, row, Blueprint::Write);
row += (dataLSB == Top ? 2 : -2);
}
// ---- address bits
// - in columns with the nors, the not row gets an R if the bit is 1, otherwise the buffer row gets it.
// - in columns with the ands, the buffer row gets an R if the bit is 1, otherwise the not row gets it.
row = height - 1;
for (int bit = 0; bit < addressBits; ++ bit) {
bool one = (address & (1ULL << bit)) != 0;
// i *could* simplify these to logical expressions, but this is why i suck at vcb.
bool rbuf = isnor ? (one ? false : true) : (one ? true : false);
bool rnot = !rbuf;
// there is no not row for the first bit when !omitEmpty
if (bit == 0 && !omitEmpty) {
/*assert(rbuf);
if (rbuf)*/ bp->set(col, row, Blueprint::Read);
row -= 2;
} else {
if (rbuf) bp->set(col, row, Blueprint::Read);
if (rnot) bp->set(col, row - 2, Blueprint::Read);
row -= 4;
}
}
isnor = !isnor;
// ---- advance
if (col > lastColumn) lastColumn = col;
col += (addr0Side == Far ? -2 : 2);
}
// remove extra columns if requested
if (omitEmpty) {
assert(addr0Side == Near);
for (int col = lastColumn + 1; col < width; ++ col)
for (int row = 0; row < height; ++ row) {
bp->set(col, row, Blueprint::Empty);
}
}
// outputs
for (int bit = 0; bit < dataBits; ++ bit)
bp->set(lastColumn + 1, bit * 2, trace);
return bp;
}
Blueprint * Text (QImage font, QString fontCharset, int kerning, QString text, Blueprint::Ink logicInk, Blueprint::Ink decoOnInk, Blueprint::Ink decoOffInk) {
qDebug().noquote() << fontCharset;
// ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 _.:! +-*/\= "' |^& ()[]<> @~#,?%{}`¬
//constexpr char charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789.!:+-*/\\=()[]|^&_<>\"'@~#,?%{}`\xAC";
//constexpr int charsetlen = sizeof(charset) - 1;
const QByteArray charset = fontCharset.toLatin1();
const int charsetlen = charset.length();
if (font.width() % charsetlen != 0)
throw runtime_error("font image width does not match character set length");
const QByteArray str = (charset.contains('a') ? text.toLatin1() : text.toUpper().toLatin1());
font = font.convertToFormat(QImage::Format_RGB888);
const int charwidth = font.width() / charsetlen;
const int charheight = font.height();
const int bpwidth = (charwidth + 1 + kerning) * str.size();
const int bpheight = charheight;
QVector<QPair<Blueprint::Layer,Blueprint::Ink> > layerInks;
layerInks.append({Blueprint::Logic, logicInk});
layerInks.append({Blueprint::DecoOn, decoOnInk});
layerInks.append({Blueprint::DecoOff, decoOffInk});
Blueprint *bp = new Blueprint(bpwidth, bpheight);
for (int k = 0; k < str.size(); ++ k) {
const char ch = str[k];
const char *chloc = strchr(charset, ch);
if (!chloc) { qDebug() << "skipping undefined character" << ch; continue; }
const int index = chloc - charset;
const int srcx0 = index * charwidth;
const int dstx0 = k * (charwidth + 1 + kerning);
for (auto layerInk : layerInks) {
const Blueprint::Layer layer = layerInk.first;
const Blueprint::Ink ink = layerInk.second;
if (!ink.isValid())
continue;
for (int cy = 0; cy < charheight; ++ cy) {
for (int cx = 0; cx < charwidth; ++ cx) {
const int srcx = srcx0 + cx;
const int srcy = cy;
const int dstx = dstx0 + cx;
const int dsty = cy;
QRgb srcpix = font.pixel(srcx, srcy);
if (qRed(srcpix) < 128) bp->setPixel(layer, dstx, dsty, ink);
}
}
}
}
return bp;
}
Blueprint * Text (QFont font, int fontHeight, QString text, Blueprint::Ink logicInk, Blueprint::Ink decoOnInk, Blueprint::Ink decoOffInk) {
// === generate font image ===
font.setPixelSize(fontHeight);
font.setStyleStrategy(QFont::NoAntialias);
font.setHintingPreference(QFont::PreferFullHinting);
QRect rcText = QFontMetrics(font).boundingRect(text);
QImage textImage(rcText.size(), QImage::Format_RGB888);
{
QPainter p(&textImage);
p.setRenderHint(QPainter::Antialiasing, false);
p.setRenderHint(QPainter::TextAntialiasing, false);
p.fillRect(textImage.rect(), Qt::black);
p.setPen(Qt::white);
p.setFont(font);
p.drawText(textImage.rect(), Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, text);
}
//textImage.save("debug-fontimage.png");
// === now copy it to every layer ===
QVector<QPair<Blueprint::Layer,Blueprint::Ink> > layerInks;
layerInks.append({Blueprint::Logic, logicInk});
layerInks.append({Blueprint::DecoOn, decoOnInk});
layerInks.append({Blueprint::DecoOff, decoOffInk});
Blueprint *bp = new Blueprint(textImage.width(), textImage.height());
for (auto layerInk : layerInks) {
const Blueprint::Layer layer = layerInk.first;
const Blueprint::Ink ink = layerInk.second;
if (!ink.isValid())
continue;
for (int y = 0; y < textImage.height(); ++ y) {
for (int x = 0; x < textImage.width(); ++ x) {
QRgb srcpix = textImage.pixel(x, y);
if (qRed(srcpix) > 128) bp->setPixel(layer, x, y, ink);
}
}
}
return bp;
}
}