Re: Binary Logic (UPDATE: 4-Bit CPU simulation)
Posted: Wed Mar 04, 2015 12:23 am
Ah, then it's just zaubara then. Wonder if he was the RDF.
www.factorio.com
https://forums.factorio.com/
So basically a printer which spits out 2 different items according to the ROM data? Nice!NotABiter wrote:BTW, I'm currently working on a "hi-res" display (Nx2 "pixels" in each tile - I haven't actually measured what N is yet but is probably in the 2.x range -- it's just the density at which items are placed on belts -- it was inspired by your 7-segment display) along with "driver logic" and a "character generator ROM", and it's the ROM that I'm trying to create and import first as a blueprint. The ROM should hold 768 bytes (96 character definitions) of "full-speed" data in just 96x72 tiles, and support reading a full 64 bits (one 8x8 pixel character) worth of data every "cycle" (a bit longer than one fast-inserter time). (I figured out how to store 8 bits of full-speed ROM data using just 4 chests and 4 inserters + poles/wires.)
Yes, but just a small time, then I've run a small server on my own for some friends and me. I never actively contributed to the RDF... I haven't finished my CPU build and somehow I lost interest in playing Minecraft when all this unfinished buggy adventure stuff came in and most redstoners were building farms and guns, but if you're interested, you can see some of my builds herePatric20878 wrote:Hey guys, I'm interested in knowing: as you've (both you?) built CPU's in Minecraft, were you ever part of The Redstone Foundation server long ago?
Here's a simple bit of test code that shows the general idea (with lots of comments added):zaubara wrote:How do you input the circuit you exporting the blueprint from?
Code: Select all
final Vector<Entity> entities = new Vector<Entity>();
final SmartInserter inserter1 = new SmartInserter(); // make a new smart inserter
inserter1.dir = UP; // grab from next tile down, put to next tile up
inserter1.set_tile_pos(0, 2); // this position is relative to mouse cursor when placing blue print in game
entities.add(inserter1); // add smart-inserter to list of entities that will be generated
inserter1.add_filter(COAL); // only grab coal...
inserter1.add_filter(STONE); // ...or stone
final SmallElectricPole pole1 = new SmallElectricPole(); // make a new electric pole
pole1.set_tile_pos(2, 1);
entities.add(pole1);
connect(RED, inserter1, pole1); // use red wire to connect the inserter to the electric pole
inserter1.add_condition(RED, IRON_ORE, GREATER_THAN, 100); // set red wire condition on the smart inserter
inserter1.add_condition(LOGISTICS, STONE, LESS_THAN, 47);
final RequesterChest rc = new RequesterChest(); // make a new requester chest
rc.set_tile_pos(0, 0);
entities.add(rc);
rc.add_request_filter(COPPER_PLATE, 99); // set it to request 99 copper...
rc.add_request_filter(IRON_ORE, 100); // ...and 100 iron ore
check(entities); // run the constraint checker
out(entities); // generate the blueprint, sends the blueprint string to stdout
Code: Select all
final Vector<Entity> entities = new Vector<Entity>();
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
Entity e;
switch (y) {
case 0: e = new BasicInserter(); break;
case 1: e = new FastInserter(); break;
case 2: e = new LongHandedInserter(); break;
case 3: e = new SmartInserter(); break;
default:
throw null;
}
switch (x) {
case 0: e.dir = UP; break;
case 1: e.dir = RIGHT; break;
case 2: e.dir = DOWN; break;
case 3: e.dir = LEFT; break;
default:
throw null;
}
e.set_tile_pos(x * 2, y * 2);
entities.add(e);
}
}
check(entities);
out(entities);
Code: Select all
// 0 is encoded as 4 rather than 0 so completion of a read can always be detected
//
static int encode_bits(int value) {
if (value == 0) {
return 4;
} else if (value >= 1 && value <= 3) {
return value;
}
throw new RuntimeException();
}
final static String[] item_encoding = new String[]{
IRON_ORE, // 0
IRON_PLATE, // 1
IRON_GEAR_WHEEL, // 2
IRON_STICK, // 3
COPPER_ORE, // 4
COPPER_PLATE, // 5
COPPER_CABLE, // 6
GREEN_WIRE, // 7
RED_WIRE, // 8
BASIC_BELT, // 9
BASIC_UNDERGROUND, // 10
BASIC_SPLITTER, // 11
RAW_WOOD, // 12
WOOD, // 13
STONE, // 14
STONE_BRICK, // 15
COAL, // 16
STEEL_PLATE, // 17
ELECTRONIC_CIRCUIT, // 18
REGULAR_MAGAZINE, // 19
BASIC_INSERTER, // 20
FAST_INSERTER, // 21
LONG_HANDED_INSERTER, // 22
SMART_INSERTER, // 23
ACTIVE_PROVIDER_CHEST, // 24
IRON_CHEST, // 25
PASSIVE_PROVIDER_CHEST, // 26
REQUESTER_CHEST, // 27
SMART_CHEST, // 28
STEEL_CHEST, // 29
STORAGE_CHEST, // 30
WOODEN_CHEST // 31
};
public static void main(String[] _) {
final Vector<Entity> entities = new Vector<Entity>();
final Net address = new Net("address[3:0]");
final Net enable_address = new Net("enable:address[6:4]");
final Net data = new Net("data");
final String address_item_type = IRON_ORE;
final int X_END = 96;
final int Y_END = 72;
final int POLE_SPACING = 6;
// requester chests and smart inserters
//
final Entity[][] map = new Entity[X_END + POLE_SPACING][Y_END + POLE_SPACING];
for (int bx = 0; bx < X_END/3; bx++) {
final int tx = bx * 3;
for (int by = 0; by < Y_END/3; by++) {
final int ty = by * 3;
final int char_x = bx >> 1; // 0..15
final int char_y = by >> 2; // 0..5
final int char_code = 32 + char_y * 16 + char_x; // 32..127
// bytes in char are organized as follows (0 being top, 7 being bottom):
// 04
// 15
// 26
// 37
//
final int bi = (bx & 1) * 4 + (by & 3);
// bits within a (4 box / 4 inserter) cell are organized as follows:
// 0:1 4:5
// 2:3 6:7
//
for (int i = 0; i < 4; i++) {
int dx = 0;
int dy = 0;
final int b = font.get_byte(char_code, bi);
final int bits;
switch (i) {
case 0:
bits = b;
break;
case 1:
bits = b >> 2;
dy = 2;
break;
case 2:
bits = b >> 4;
dx = 2;
break;
default:
bits = b >> 6;
dx = dy = 2;
break;
}
final RequesterChest rc = new RequesterChest();
rc.set_net(GREEN, data);
map[tx + dx][ty + dy] = rc;
rc.add_request_filter(item_encoding[bi * 4 + i], encode_bits(bits & 3));
}
for (int i = 0; i < 4; i++) {
final SmartInserter si = new SmartInserter();
int dx = 1;
int dy = 1;
switch (i) {
case 0:
si.dir = RIGHT;
dy = 0;
break;
case 1:
si.dir = DOWN;
dx = 2;
break;
case 2:
si.dir = LEFT;
dy = 2;
break;
default:
si.dir = UP;
dx = 0;
break;
}
map[tx + dx][ty + dy] = si;
si.set_net(RED, address);
si.set_net(GREEN, enable_address);
si.add_condition(RED, address_item_type, EQUALS, char_code & 0xf);
si.add_condition(GREEN, address_item_type, EQUALS, 8 + (char_code >> 4));
}
}
}
// medium electric poles
//
boolean xeven = false;
for (int tx = 1; tx < X_END + POLE_SPACING; tx += POLE_SPACING) {
xeven = !xeven;
boolean yeven = false;
for (int ty = 1; ty < Y_END + POLE_SPACING; ty += POLE_SPACING) {
yeven = !yeven;
// place medium pole
//
final MediumElectricPole p = new MediumElectricPole();
map[tx][ty] = p;
// wire up enable:address networks to smart inserters around pole
//
for (int dx = 1 - POLE_SPACING; dx <= POLE_SPACING; dx++) {
final int x = tx + dx;
if (x >= 0 && x < X_END) {
for (int dy = 1 - POLE_SPACING; dy <= POLE_SPACING; dy++) {
final int y = ty + dy;
if (y >= 0 && y < Y_END) {
final Entity e = map[x][y];
if (xeven == yeven) {
// wire up enable:address networks to smart inserters around pole
//
if (e instanceof SmartInserter) {
connect(RED, p, e);
connect(GREEN, p, e);
}
} else {
// wire up data network to requester chests
//
if (e instanceof RequesterChest) {
connect(GREEN, p, e);
}
}
}
}
}
}
// wire up to adjacent poles in same networks
//
if (tx - POLE_SPACING >= 0 && ty - POLE_SPACING >= 0) {
final Entity p2 = map[tx - POLE_SPACING][ty - POLE_SPACING];
if (xeven == yeven) connect(RED, p, p2);
connect(GREEN, p, p2);
}
}
}
// build up entities list
//
for (int tx = 0; tx < X_END + POLE_SPACING; tx++) {
for (int ty = 0; ty < Y_END + POLE_SPACING; ty++) {
final Entity e = map[tx][ty];
if (e != null) {
e.set_tile_pos(tx, ty);
entities.add(e);
}
}
}
check(entities);
out(entities);
}
The thought has most definitely crossed my mind.zaubara wrote:Why not make an HDL compiler and generate the complete CPU?
Actually I'm thinking 4 or more different items (for more colors). The ROM data only contains on/off data, but that doesn't mean I can't provide foreground/background color data when I tell the whole machine to spit out the next character. (Strikeout, underline, italics and bold are also options I might include, but I haven't thought them through yet to see how hard they would be.) Also, my current plan is to treat my font as proportional (leaving out redundant blank columns) to make maximum use of my belt space. (In fact if that ends up not working out I'll have to fix my font to not all be left-justified as it is now.)zaubara wrote:So basically a printer which spits out 2 different items according to the ROM data? Nice!
Both code and hints are above. Another hint: My dynamic logic is "negative" logic, not "positive" logic. I.e., it's not about what's in the boxes that are wired up, it's about what's not in the boxes (because it's currently in-flight in the hands of inserters). My whole CPU design is pretty much based on dynamic-negative logic because it's very fast -- results pop out in game-tick time, not in box-to-box inserter-movement time.zaubara wrote:8 bits in 4 chests, ok, but 4 inserters? How?
What's the difference between SHLC and ROL? (If the difference is that SHLC makes the Z flag work across multiple nibbles and ROL does not, I think that ROL could just be dropped as it's not going to be a very commonly used instruction.)zaubara wrote:NOP, ADD, ADDI, ADDC, SUB, SUBI, SUBC, AND, ANDI, OR, ORI, XOR, XORI, SHL, SHLC, ROL, SHR, SHRC, ROR, ASR, CMP, CMPI, CPC, JMP, JMPR, JZ, JZR, JNZ, JNZR, JCS, JCSR, JLS, JLSR, JGT, JGTR, CALL, RET, LDI, MOV, IN, INR, OUT, OUTR, OUTI
zaubara wrote:But I am not quite sure what you mean by keeping the Z flag over a synthesized operation (ex 8 bit addition), to be more precise, what impact on the design it has, I can see the need for this...
Code: Select all
Z: R7 * R6 * R5 * R4 * R3 * R2 * R1 * R0 * Z Previous value remains unchanged when the result is zero; cleared otherwise.
Code: Select all
Z: R7 * R6 * R5 * R4 * R3 * R2 * R1 * R0 Set if the result is $00; cleared otherwise.
Code: Select all
CP r0, r4 ; Z set iff: r0 == r4
CPC r1, r5 ; Z set iff: r0 == r4 && r1 === r5
CPC r2, r6 ; Z set iff: r0 == r4 && r1 === r5 && r2 == r6
CPC r3, r7 ; Z set iff: r0 == r4 && r1 === r5 && r2 == r6 && r3 == r7
BREQ someplace ; branches iff: r0 == r4 && r1 === r5 && r2 == r6 && r3 == r7
I can't say that it's "common" in the case of the Z flag because so few architectures even bother to handle the Z flag correctly. It's of course extremely common in the case of the carry flag (I'm not even aware of a counter-example in any real hardware architecture), and it makes a lot of sense for an architecture to handle Z the same (what kind of instruction sequence is needed) way as it handles carry.zaubara wrote:Somehow i have to reset the Z flag prior to such an operation (or vice versa keep it during it), is it a common way to have the Carry-less instructions (ADD, SUB, ...) also resetting the Z flag (to not use an extra operation dedicated to reset the flag)?
Code: Select all
// wire up enable:address networks to smart inserters around pole
//
for (int dx = 1 - POLE_SPACING; dx <= POLE_SPACING; dx++) {
final int x = tx + dx;
if (x >= 0 && x < X_END) {
for (int dy = 1 - POLE_SPACING; dy <= POLE_SPACING; dy++) {
final int y = ty + dy;
if (y >= 0 && y < Y_END) {
final Entity e = map[x][y];
if (xeven == yeven) {
// wire up enable:address networks to smart inserters around pole
//
if (e instanceof SmartInserter) {
connect(RED, p, e);
connect(GREEN, p, e);
}
} else {
// wire up data network to requester chests
//
if (e instanceof RequesterChest) {
connect(GREEN, p, e);
}
}
}
}
}
}
Code: Select all
if (xeven == yeven) {
p.set_net(RED, address);
p.set_net(GREEN, enable_address);
} else {
p.set_net(GREEN, data);
}
Code: Select all
auto_wire_nonpoles(entities, errors);
Code: Select all
check(entities, errors);
export(entities, errors);
Code: Select all
final Net address = new Net("address[3:0]", Fig.BLUE[3]);
final Net enable_address = new Net("enable:address[6:4]", Fig.RED[3]);
final Net data = new Net("data", Fig.GREEN[3]);