v50 Steam/Premium information for editors
- v50 information can now be added to pages in the main namespace. v0.47 information can still be found in the DF2014 namespace. See here for more details on the new versioning policy.
- Use this page to report any issues related to the migration.
This notice may be cached—the current version can be found here.
Editing User:Vasiln/Goblin Logic 3
Jump to navigation
Jump to search
Warning: You are not logged in.
Your IP address will be recorded in this page's edit history.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.
Latest revision | Your text | ||
Line 1: | Line 1: | ||
==Optimization: Opcodes revisited== | ==Optimization: Opcodes revisited== | ||
+ | |||
Right now, we have seven operations codes to spare: 0000, 1010, 1011, 1100, 1101, 1110, and 1111; and 15 arguments for XOR; and 8 arguments for bit shift or bit rotate. Way more than we need. At the same time, we don't have nearly enough memory. We could do a couple of things: we could add instructions, so that for instance it wouldn't take two instructions to add two numbers. We could also switch it up to a 3-bit instruction and a 5-bit address. | Right now, we have seven operations codes to spare: 0000, 1010, 1011, 1100, 1101, 1110, and 1111; and 15 arguments for XOR; and 8 arguments for bit shift or bit rotate. Way more than we need. At the same time, we don't have nearly enough memory. We could do a couple of things: we could add instructions, so that for instance it wouldn't take two instructions to add two numbers. We could also switch it up to a 3-bit instruction and a 5-bit address. | ||
Line 15: | Line 16: | ||
Two of these need specifying. | Two of these need specifying. | ||
− | + | 100xxxxx Register operations | |
+ | |||
*10000000 XOR a and b -> a | *10000000 XOR a and b -> a | ||
*10000001 Add: XOR a and b -> a, write 9-bit carry word to buffer, XOR a and carry word->a, write 8-bit carry word to buffer, with carry(8)=output of first XOR(8) OR output of second XOR(8) | *10000001 Add: XOR a and b -> a, write 9-bit carry word to buffer, XOR a and carry word->a, write 8-bit carry word to buffer, with carry(8)=output of first XOR(8) OR output of second XOR(8) | ||
Line 26: | Line 28: | ||
*10001000-10011111 Reserved (24 choices!) | *10001000-10011111 Reserved (24 choices!) | ||
− | + | 101xxyyy Bit shifting register operations | |
+ | |||
*10100yyy Arithmetic right shift 0-7 units (y units) | *10100yyy Arithmetic right shift 0-7 units (y units) | ||
*10101yyy Arithmetic left shift 0-7 units (y units) | *10101yyy Arithmetic left shift 0-7 units (y units) | ||
Line 43: | Line 46: | ||
===A note on the notion of "Turing complete"=== | ===A note on the notion of "Turing complete"=== | ||
+ | |||
To be Turing complete, a computer has to have four things: a NAND, a NOR, a jump if, and infinite memory. | To be Turing complete, a computer has to have four things: a NAND, a NOR, a jump if, and infinite memory. | ||
Line 54: | Line 58: | ||
==Optimization: Memory Addressing Revisited== | ==Optimization: Memory Addressing Revisited== | ||
+ | |||
We spent a lot of time optimizing our adder, and ended up with a parallel add/look-ahead that was very fast-- less than half the time of our parallel ripple, and far faster than a full adder. What I forgot to mention is that there was a lot of stupid delay built into the add just by virtue of requiring two memory accesses (two XOR operations). We've been assuming that a single goblin will address all of our bits in a particular bit location, but that takes a lot of time: trip the hatch takes 100 ticks just to get him started, and with 32 bits to address, we're probably looking at something like 30 moves to make it to our addressable bit-- so we've got around 400 ticks just to read memory/buffer, followed by 100 ticks of latency for our memory. | We spent a lot of time optimizing our adder, and ended up with a parallel add/look-ahead that was very fast-- less than half the time of our parallel ripple, and far faster than a full adder. What I forgot to mention is that there was a lot of stupid delay built into the add just by virtue of requiring two memory accesses (two XOR operations). We've been assuming that a single goblin will address all of our bits in a particular bit location, but that takes a lot of time: trip the hatch takes 100 ticks just to get him started, and with 32 bits to address, we're probably looking at something like 30 moves to make it to our addressable bit-- so we've got around 400 ticks just to read memory/buffer, followed by 100 ticks of latency for our memory. | ||
Line 126: | Line 131: | ||
==Optimization: Memory Latency== | ==Optimization: Memory Latency== | ||
+ | |||
So far, we've only been linking our operations to the true part of memory. We've been using FALSE and NOT TRUE interchangeably. But they're not quite interchangeable-- not really. Consider the following two circuits: | So far, we've only been linking our operations to the true part of memory. We've been using FALSE and NOT TRUE interchangeably. But they're not quite interchangeable-- not really. Consider the following two circuits: | ||
Line 147: | Line 153: | ||
==Cycle Manager== | ==Cycle Manager== | ||
+ | |||
Every cycle of our computer follows a certain flowchart. Let's spell it out. | Every cycle of our computer follows a certain flowchart. Let's spell it out. | ||
Line 171: | Line 178: | ||
==Special Memory: Getting stuff done== | ==Special Memory: Getting stuff done== | ||
+ | |||
So far, our computer doesn't do anything. I mean, it can compute anything we throw at it, but it doesn't kill goblins or irrigate our farms or anything like that. What we need are special bytes: output and input. | So far, our computer doesn't do anything. I mean, it can compute anything we throw at it, but it doesn't kill goblins or irrigate our farms or anything like that. What we need are special bytes: output and input. | ||
===Output=== | ===Output=== | ||
+ | |||
Output bytes look like any other kind of byte (yeah, I'm going to stop showing you that same picture of a memory bit now.) The only difference is that the pressure plate in the memory is linked to more than just r/w functions-- it's linked to existing devices. For instance, a bridge could raise when the goblin moves into TRUE position and lower when the goblin moves off of TRUE position. | Output bytes look like any other kind of byte (yeah, I'm going to stop showing you that same picture of a memory bit now.) The only difference is that the pressure plate in the memory is linked to more than just r/w functions-- it's linked to existing devices. For instance, a bridge could raise when the goblin moves into TRUE position and lower when the goblin moves off of TRUE position. | ||
Line 207: | Line 216: | ||
I told you-- we were going to do a lot more bit shifts than add-with-carries. | I told you-- we were going to do a lot more bit shifts than add-with-carries. | ||
+ | ===Input=== | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
Input's harder. At first, you wouldn't think so. Just link a lever to write to true, right? And another to write to false? | Input's harder. At first, you wouldn't think so. Just link a lever to write to true, right? And another to write to false? | ||
Line 269: | Line 227: | ||
====Clock bits==== | ====Clock bits==== | ||
+ | |||
A perfect example of this is a clock. Clocks are great input-- for instance, if we want a doomsday program to constantly check the clock until a certain time is reached, then open the magma floodgates, we need clock-input. You never want to write to a clock. But clocks change all the time. | A perfect example of this is a clock. Clocks are great input-- for instance, if we want a doomsday program to constantly check the clock until a certain time is reached, then open the magma floodgates, we need clock-input. You never want to write to a clock. But clocks change all the time. | ||
− | + | ### | |
− | + | ###<# | |
− | + | ##h#h# | |
− | + | ##^#^## | |
− | + | #hd#dh# | |
− | + | ##^#^## | |
− | + | #h#h## | |
− | + | #<### | |
− | + | ### | |
Here we have an incrementing memory cell. It's the perfect design for a clock. Somewhere, we have a repeater toggling this memory, any any incrementation ripples through the next bits. If clock(0) toggles every 200 ticks-- well, if clock(0) toggles every 200 ticks, then by the time we've run a single instruction, clock(0) won't be the same anymore. But if clock(0) toggles every 1200 ticks (that is, daily), we want to be able to grab a correct value-- not yesterday's value, not tomorrow's value, not some random value. I mean, what if clock(0) has toggled when we read it, but it has created a carry that hasn't yet percolated through clock(7) when we read it? It'll look like we went back in time. | Here we have an incrementing memory cell. It's the perfect design for a clock. Somewhere, we have a repeater toggling this memory, any any incrementation ripples through the next bits. If clock(0) toggles every 200 ticks-- well, if clock(0) toggles every 200 ticks, then by the time we've run a single instruction, clock(0) won't be the same anymore. But if clock(0) toggles every 1200 ticks (that is, daily), we want to be able to grab a correct value-- not yesterday's value, not tomorrow's value, not some random value. I mean, what if clock(0) has toggled when we read it, but it has created a carry that hasn't yet percolated through clock(7) when we read it? It'll look like we went back in time. | ||
Line 289: | Line 248: | ||
In the case of the clock, what we want to do is block access to the entire circuit whenever a clock updates. That includes clock(7), which might be incrementing much later (let's be conservative, and say 1400 ticks later). We're going to institute a guard for the begin door, which is normally triggered whenever it's time to read or write to memory. | In the case of the clock, what we want to do is block access to the entire circuit whenever a clock updates. That includes clock(7), which might be incrementing much later (let's be conservative, and say 1400 ticks later). We're going to institute a guard for the begin door, which is normally triggered whenever it's time to read or write to memory. | ||
− | + | #### | |
− | + | rddc | |
− | + | #### | |
Leftmost door is linked to the thing that normally says that writes are allowed to memory. Rightmost door is linked to our actual write to clock. And continuation leads to an iterated delay circuit. (This breaks our rules, but again, since the doors will never close until continuation is reached, only open, it's okay.) | Leftmost door is linked to the thing that normally says that writes are allowed to memory. Rightmost door is linked to our actual write to clock. And continuation leads to an iterated delay circuit. (This breaks our rules, but again, since the doors will never close until continuation is reached, only open, it's okay.) | ||
− | + | ### ### | |
− | + | rhc rhc | |
− | + | ### ### | |
− | Continuation of clock guard trips the hatch; 100 ticks later, the 1st delay goblin runs to continuation of delay1, which trips the hatch on delay2; 100 ticks later, delay2 goblins runs over our second continuation, which opens the door to clock(0). We'll need to institute further iterated delays to read clock(2-7) | + | Continuation of clock guard trips the hatch; 100 ticks later, the 1st delay goblin runs to continuation of delay1, which trips the hatch on delay2; 100 ticks later, delay2 goblins runs over our second continuation, which opens the door to clock(0). We'll need to institute further iterated delays to read clock(2-7). |
This is bad. It means reading the clock is slow. First, we have to wait for the clock to change; then, we have to wait for all possible changes to percolate. (We could build a look-ahead unit for our clock to make this faster, but let's not go there.) Can this be optimized? Probably. Should it be optimized? A dwarf has only so much energy to devote to problems. (In other words, I don't yet know what the ideal solution is.) | This is bad. It means reading the clock is slow. First, we have to wait for the clock to change; then, we have to wait for all possible changes to percolate. (We could build a look-ahead unit for our clock to make this faster, but let's not go there.) Can this be optimized? Probably. Should it be optimized? A dwarf has only so much energy to devote to problems. (In other words, I don't yet know what the ideal solution is.) | ||
Line 308: | Line 267: | ||
execute your lever throw, in the time between possible r/w. (There'll be plenty of time. Believe me.) This means instituting a similar delay, with a similar manager, to writes to input from lever (or, preferably, pressure plate, because we want everything to reset automatically), rather than to reads from input by computer. The mechanisms are the same. | execute your lever throw, in the time between possible r/w. (There'll be plenty of time. Believe me.) This means instituting a similar delay, with a similar manager, to writes to input from lever (or, preferably, pressure plate, because we want everything to reset automatically), rather than to reads from input by computer. The mechanisms are the same. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ===Input/Output=== | |
− | |||
− | === | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
You might wonder how you can build a bridge that you can operate with a lever, but that still plays nice with your computer-- so that you can open it or close it, but your computer can too. This is input/output. Instead of linking your lever directly to the bridge, you link it to an input bit. Then, you treat that input bit as an output bit, and link it to your bridge. Now your computer can operate the bridge, you can operate the bridge, and your computer can even tell if you pulled the lever while it wasn't looking. | You might wonder how you can build a bridge that you can operate with a lever, but that still plays nice with your computer-- so that you can open it or close it, but your computer can too. This is input/output. Instead of linking your lever directly to the bridge, you link it to an input bit. Then, you treat that input bit as an output bit, and link it to your bridge. Now your computer can operate the bridge, you can operate the bridge, and your computer can even tell if you pulled the lever while it wasn't looking. | ||
Line 382: | Line 283: | ||
===Feedback: Memory multiplexing=== | ===Feedback: Memory multiplexing=== | ||
+ | |||
We do. But there's one more really cool thing we could with i/o. It doesn't even involve creating any latency. | We do. But there's one more really cool thing we could with i/o. It doesn't even involve creating any latency. | ||
Line 401: | Line 303: | ||
Prefer input bytes? I can't imagine why, but multiplex those instead! Hell, we could even have a split multiplex. By multiplexing a few data cells as well, we could maintain a large bank of variables to pass between memory spaces, while still keeping sufficient multiplexed instruction space. | Prefer input bytes? I can't imagine why, but multiplex those instead! Hell, we could even have a split multiplex. By multiplexing a few data cells as well, we could maintain a large bank of variables to pass between memory spaces, while still keeping sufficient multiplexed instruction space. | ||
− | |||
− |