This is a GPU (Graphics Processing Unit) component for the Logisim (and Logisim Evolution) digital logic simulator program. It can draw points, lines, ovals/circles, polygons, text, rounded rectangles, arcs, blits, and sprites. It even can do several graphics operations in one clock cycle by running a user specified "program".
If you don't have Logisim, here's a link to the original Logisim: https://sourceforge.net/projects/circuit/files/2.7.x/2.7.1/logisim-generic-2.7.1.jar/download for a JAR file (which would be needed for building this component if you need to) and https://sourceforge.net/projects/circuit/files/latest/download for an executable version. If those links don't work, try: http://www.cburch.com/logisim/. Alternatively, Logisim evolution (Reds-Heig) is available at: https://github.com/logisim-evolution/logisim-evolution. And Kevin Walsh's version of Logisim Evolution is available at: https://github.com/kevinawalsh/logisim-evolution.
If using the JAR version, you would need to do something like the following to run it:
java -jar logisim-generic-2.7.1.jar
(Your filename may vary)
This GPU video screen is a drop-in replacement for the existing LCD Video screens. It
will do the normal LCD screen operations that you might be familiar with. But, if you
set the GPU Select line to 1, 2, or 3, it will do extra GPU functions.
The GPUs RAM and ROM can be loaded by clicking the right mouse button on the GPU
component and selecting the desired Load function on the menu. Then choose the
desired image file. Currently, an image file's first line must be the common Logisim
image file header of "v2.0 raw". The data that would follow must be in hexadecimal.
Comments can be put in by starting the comment with a "#"
A sample conversion program (gpuelem.c) is provided to help in converting
your data to something that can be used with the GPU component. It's partially
meant as a guide, you may choose to use other means of conversion. Each time it is
run, it will output to the file "outhex". You should then append the contents
of outhex to your image file. The number of memory elements that are used is
contained in a comment. Use that value to help determine the memory address to
use to access any subsequent graphical elements that you may choose to append.
Tip: If you want to convert a PNG image file to a sprite with a full alpha channel, you can use pngtopnm from the Netpbm package with the -alpha option to create a PGM file containing the alpha channel (and also use without the -alpha option to get the RGB data in the form of a PPM file).
To write a value to the GPU's RAM, first set the GPU memory address (with GPU Select 1) -
the memory address being on the GPU Data In line.
Then write the desired value (being on the GPU Data In line) to the previously specified
memory address (with GPU Select 2). To do a GPU function (using GPU Select 3) that
requires the use of the GPU's RAM or ROM (ones not in italics in the GPU
Functions table), set the GPU memory address (with GPU Select 1) and then do the GPU
function using GPU Select 3 (value on the GPU Data In line).
By the way, a memory address that is set will be remembered
after subsequent operations, so no need to keep setting it if it doesn't need to be changed.
If the GPU function doesn't require the GPU's RAM or ROM, no need to set an address.
Currently, the GPU's RAM and ROM have a limit of 1048576 (100000 hex) 32-bit elements each.
Even though there is this limit, any address below 800000 hex is considered RAM space and
any address at or above 800000 hex is considered ROM space. The GPU Data Out line will
always (and only) show the value of the RAM or ROM at the specified memory address. By
the way, currently the GPU portion of the video screen in its default configuration doesn't require the Clock line to operate. However, it is likely
that your design will effectively trigger a GPU operation on a clock cycle anyway. You
can set the trigger type if you want, however.
| GPU Select | Function |
|---|---|
| 0 | GPU not used - Behave like normal Video screen. |
| 1 | Set GPU memory address. |
| 2 | Write value to GPU memory. |
| 3 | Do GPU function. |
Instead of using the GPU Select Line (value 1) to set the GPU memory address, you could use the GPU Address pin on the GPU. If the value on the pin isn't floating or error at the time a GPU function is performed, the address will be whatever is on this pin at the time. If the value is floating (perhaps not connected to anything) or an error, the address will be whatever the previous good address was (perhaps set by the GPU Select Line).
Functions in italics don't need any data in the GPU's RAM or ROM. 00 means
the value doesn't matter, but there should be 2 hex characters in that place so the
function code (Byte1) will be in the right bit positions. Byte1, Byte2, Byte3, and
Byte4 together make up a 32 bit value with Byte1 being the most significant.
For functions 3 and 4, "Move to" specifies the start of a line segment
and "Line to" specifies the end point and draws it. Any subsequent "Line to" will use
the last specified point as the start of a new line segment.
For text. there currently are 3 fonts defined (Arial Bold 12, Courier New 12, and Tahoma 11)
and up to 16 user-definable fonts. The 3 defined fonts may depend on your system.
The user-definable fonts can be created by using function 20.
For Text function 8, the font number needs to
be specified. It can be 0, 1, or 2 for the pre-defined fonts, or
4 through 19 (decimal) for the user-defined fonts. The Text Char function 9 uses Courier New 12.
Function 1f can be used to define a linear color gradient. Once specified, it applies
to most drawing functions until it is turned off by setting the On value to 0.
It not only applies to filled objects (which would be expected), but it also
applies to text and lines, for example.
For function 14, the values specified are indexes into a point storage section
of the GPU's memory. The start address of a point storage section is specified by
using the GPU Select Line 1 function (Set GPU memory address).
For function 19, SolidDotDash is the stroke type: 00 for solid lines,
01 for dotted lines, and 02 for dashed lines. Width is the line width.
Function 1c can be used to produce a "rubber band" effect, which is the use of
exclusive OR (XOR) drawing mode to draw an object and then erase it by drawing it again.
You most likely have seen this effect in paint programs, for example, when you draw
a line setting one point and dragging the line to the desired endpoint.
The Set pixel function (11) sets the specified pixel to the current GPU color.
For functions b and d, if the Image # or Sprite # respectively is greater
than 0, use already created blit or sprite respectively.
Currently there is a limit of 17 blit images (One on the fly
blit (#0) and 16 pre-created blit images (#1-#16)). And a limit of 17 sprite images (One
on the fly sprite (#0) and 16 pre-created sprite images (#1-#16)). You can turn off an
individual sprite by setting both it's X position and Y position to 255 (ff).
In general, due to the fact that positions are 1 byte, there can never be negative
positions. Since this can be a problem for blits and sprites, there is a workaround -
the positional alignment can be changed. By using functions 1d and 1e,
the x and y alignment can be set: 00 for the default alignment which is top and/or left,
01 for bottom and/or right, and 02 for center.
Also, because of the previously mentioned 1 byte limitation, there can only be a maximum of a 256x256
pixel video screen. That's why other resolutions aren't supported. Similarly, blit and
sprite images can't be any bigger than 256 pixels width and 256 pixels height.
There are I believe
versions of Logisim Evolution that support 64 bits, which would then allow more bits for
the data, but I figure it will take some time for people to start making 64-bit CPUs, etc.
So for now, these limitations stand.
| Function | Byte1 | Byte2 | Byte3 | Byte4 |
|---|---|---|---|---|
| Clear screen with specified color | 0 | Red | Green | Blue |
| Set current color | 1 | Red | Green | Blue |
| Line | 2 | 00 | X Pos | Y Pos |
| Move to | 3 | 00 | X Pos | Y Pos |
| Line to | 4 | 00 | X Pos | Y Pos |
| Polyline | 5 | 00 | X Pos | Y Pos |
| Polygon (closed) | 6 | 00 | X Pos | Y Pos |
| Filled polygon | 7 | 00 | X Pos | Y Pos |
| Text | 8 | Font # | X Pos | Y Pos |
| Text Char. | 9 | ASCII # | X Pos | Y Pos |
| Create blit src | a | 00 | 00 | Image # |
| Blit | b | Image # | X Pos | Y Pos |
| Create sprite src | c | 00 | 00 | Sprite # |
| Sprite | d | Sprite # | X Pos | Y Pos |
| Sprites on/off | e | 00 | 00 | On Flag |
| Oval / Circle | f | 00 | X Pos | Y Pos |
| Filled Oval / Circle | 10 | 00 | X Pos | Y Pos |
| Set pixel | 11 | 00 | X Pos | Y Pos |
| Run GPU program | 12 | Address (Byte1) | Address (Byte2) | Address (Byte3) |
| End GPU program | 13 | 00 | 00 | 00 |
| LineV | 14 | 00 | Pstart | Pend |
| Arc | 15 | 00 | X Pos | Y Pos |
| Filled Arc | 16 | 00 | X Pos | Y Pos |
| Rounded Rectangle | 17 | 00 | X Pos | Y Pos |
| Filled Rounded Rectangle | 18 | 00 | X Pos | Y Pos |
| Set stroke type | 19 | 00 | SolidDotDash | Width |
| Start double buffer | 1a | 00 | 00 | 00 |
| End double buffer | 1b | 00 | 00 | 00 |
| Set XOR mode for drawing | 1c | Red | Green | Blue |
| Set blit alignment | 1d | Image # | Xalign | Yalign |
| Set sprite alignment | 1e | Sprite # | Xalign | Yalign |
| Gradient paint | 1f | 00 | 00 | On |
| Create user font | 20 | 00 | 00 | Userfont # |
| GPU Reset | ff | 00 | 00 | 00 |
Each line definition is stored as a 32-bit value in memory:

Each polyline/polygon definition is stored as a 32-bit value in memory specifying
the number of points followed by that many 16-bit values (the most significant
16 bits of the 32-bit number are ignored):

Each text string consists of a set of 8-bit values (the most significant 24 bits of each 32-bit number are ignored) which is terminated by a 0. Currently, a string will automatically terminate after 256 characters to avoid an infinite loop in case you forget the 0 terminator.
Each user-definable font consists of a 16-bit value (the most significant 16 bits
of the 32-bit number are ignored) in memory specifying the font style and font size:

followed by the font name which is a string consisting of 8-bit values (the most
significant 24 bits of each 32-bit number are ignored) which is terminated by a 0.
The style can be 0 for plain, 1 for bold, 2 for italic, or
3 for bold-italic.
Each oval/circle is stored as a 32-bit value in memory:

Each gradient paint is stored as 4 32-bit values in memory. A gradient paint
is done by specifying the color at a certain starting point and the color at a
certain ending point. Those points are stored in the first of the 4 values:

The next 2 values of the set of 4 are the starting color and the ending color respectively.
They are of the form:

If the alpha part is 0, that color is fully transparent. If the alpha part is 255 (ff), that color is fully opaque.
The last value of the set of 4 is an 8-bit value (the most significant 24 bits are ignored) which is the cyclic flag
(a value of 0 means the gradient is acyclic, a value of 1 means the gradient is cyclic). An acyclic gradient
means that areas outside of the range from the starting point to the ending point are clamped to the color of the nearest specified point.
A cyclic gradient continues to cycle the gradient colors beyond the range from the starting point to the ending point.
Each blit and sprite image is stored as a 16-bit value (the most significant 16 bits
of the 32-bit number are ignored) containing the resolution:

followed by the image data consisting of Width times Height 32-bit values (no
padding needed for non-power-of-2 widths). The values of width and height should be one less than the actual width and height since a typical resolution
could be 256 and 256 can't be stored in one byte.
In the case of blits, the most significant
8 bits of a pixel element in the image data are ignored:

In the case of sprites, all 32 bits of a pixel element are used:

If the alpha part of the pixel is 0, that pixel is fully transparent. If the alpha
part is 255 (ff), that pixel is fully opaque (fully shown).
Each GPU program is stored as a collection of pairs of 32-bit values. The first of the pair is the GPU Select value, and the last of the pair is the GPU Data In value. The program is terminated by an End GPU program function (13 hex). By the way, a GPU program can call a GPU program, and so on (as long as the stack size limit isn't reached). In general, be careful when computing the addresses of each GPU program - it can be confusing having to think in terms of each GPU operation being two 32-bit values.
Point data for the LineV function is stored as a consecutive list of 16-bit values (the
most significant 16 bits of the 32-bit number are ignored):

Unlike the polygon function, the number of points is not part of this storage.
Each Arc is stored as a 32-bit value in memory:

followed by another 32-bit value (all 32 bits are used - both are 16-bit values. Might need to be careful about your CPU's Endianness if different than x86):

Each Rounded Rectangle is stored as a 32-bit value in memory:

JAR files are included, so there shouldn't be a need to build, but if you need to...
Edit Makefile accordingly (to set filename and location of your Logisim program jar files (exe file will not work)). Also, you must have Java JDK installed - probably needs to be at least version 6. Some versions of Logisim Evolution require version 21.
On Windows (assuming Microsoft Visual Studio and assuming DOS compiling environment set up. Adjust accordingly for other IDEs):
nmake
On Linux:
make -f Makefile.unix
to only compile the default version for (original) Logisim 2.7.1.
Or to compile all versions:
nmake all
Or:
make -f Makefile.unix all
Or to compile a specific version (such as Logisim Evolution 3.9.0):
nmake GPU3.9.0
Or:
make -f Makefile.unix GPU3.9.0
(Refer to the makefile for the various versions that can be compiled)
Since not everyone has either designed their own CPU in Logisim or has access to one, sample circuits are provided which access the GPU without a CPU involved. All the GPU instructions that a CPU would send to the GPU are embedded in the ROMs in the sample circuits. Various versions have been created to work with the different versions of Logisim and therefore the different versions of the GPU component. For some of the faster versions of Logisim, you'll probably need to set the Auto-Tick Frequency rather low to see everything. If you feel there are missing frames (it's skipping things), try lowering the tick frequency. These sample circuits use the file gpu which is automatically loaded into the GPU's ROM.
To use rungpu4d.circ, rungpuhc4d.circ, or rungpuev4d.circ files, you need to load the GPUs RAM with the image file gpu4dram by clicking the right mouse button on the GPU component and selecting "Load RAM" from the menu. These sample circuits also use the file gpu4drom which is automatically loaded into the GPU's ROM.
The sample circuits in the form "rungpu*a.circ" use the alternate way of specifying the GPU memory address (rather than using the GPU Select Line (value 1)).
This table shows the sample files provided along with the version of Logisim they are known to work with. They may work with other versions not mentioned however, but no guarantees. Ev stands for Evolution and hc stands for Kevin Walsh's Holy Cross version of Logisim Evolution.
| File | Logisim Version |
|---|---|
| rungpu.circ | Logisim 2.7.1 |
| rungpuev.circ | Logisim Ev. 3.9.0 and Logisim Ev. 4.0.0 |
| rungpuev2.circ | Logisim Ev. 3.3.1hc |
| rungpuev3.circ | Logisim Ev. 2.13.22 |
| rungpuhc.circ | Logisim Ev. 5.0.4hc |
| rungpuhc2.circ | Logisim Ev. 3.1.0hc |
| rungpuhc3.circ | Logisim Ev. 4.0.4hc |
| rungpuhc4.circ | Logisim Ev. 4.0.0hc and Logisim Ev. 4.0.1hc |
| rungpuhc5.circ | Logisim Ev. 5.0.5hc |
| rungpu4d.circ | Logisim 2.7.1 |
| rungpuev4d.circ | Logisim Ev. 3.9.0 and Logisim Ev. 4.0.0 |
| rungpuhc4d.circ | Logisim Ev. 5.0.4hc |
| rungpuhc4d2.circ | Logisim Ev. 5.0.5hc |
| rungpua.circ | Logisim 2.7.1 |
| rungpueva.circ | Logisim Ev. 3.9.0 and Logisim Ev. 4.0.0 |
| rungpuev2a.circ | Logisim Ev. 3.3.1hc |
| rungpuev3a.circ | Logisim Ev. 2.13.22 |
| rungpuhca.circ | Logisim Ev. 5.0.4hc |
| rungpuhc2a.circ | Logisim Ev. 3.1.0hc |
| rungpuhc3a.circ | Logisim Ev. 4.0.4hc |
| rungpuhc4a.circ | Logisim Ev. 4.0.0hc and Logisim Ev. 4.0.1hc |
| rungpuhc5a.circ | Logisim Ev. 5.0.5hc |
Youtube video (also showing my CPU design): Very Fast and Versatile GPU Component for my Logisim CPU / Computer (Blits, Sprites, Polygons, etc.)
-
A mouse click function could be added, so that when a user clicks somewhere on the video screen, a program could get the location of the click. This would allow for all sorts of things such as clickable menu systems and playable games. However, this goes beyond what a GPU does, so I think for now it's just a thought.
-
A GPU language (with variables, loops, subroutines, etc.) could be created that would compile to "bytecode" (not to be confused with Java bytecode) that the GPU would understand. This would create a truly runnable program capability similar to GLSL. However, I don't really feel this makes much sense to do in the case of this component since the strength of real-life GPUs is their ability to do computations in parallel. This component wouldn't be able to do that, and I don't see any point in creating a pseudo-parallel system.
-
Support for 3d graphics could be added. It probably would be rather rudimentary support, since a truly versatile 3d system would be rather complicated.
-
Support for higher-level graphics primitives such as B-splines and Bézier curves could be added.
-
In terms of tools, a gpuelem on steroids, so to speak, could be created - either an interpreted "script" or a set of C++ classes (with each primitive being an "object" in an object-oriented programming system) that would make GPU data/scene creation easier. In the C++ case, instantiating an object would add the object's data to the GPU RAM file, and using the object's "place" (for example) method would add the necessary GPU function call to the program. Just thinking out loud :-)
-
Support for radial gradient fills using Java's RadialGradientPaint function. However, RadialGradientPaint could be a bit of a hassle if you want to do more than 2 gradient colors, specify a focus point, or specify the cycling method.
-
A sort of global translate function that would translate every primitive after it until it's reset. This way a complex scene could be created and translated as a whole. Would be useful for games, etc.
Mark Craig https://www.youtube.com/MrMcSoftware

