FX-CG[UI] and FX-CG[CL] are two pieces of code designed to work together to make developing Prizm addins easier. To this point anything beyond simple unit tests has had to run on the calculator, making testing hard. This project aims to make testing/debugging easier and more accurate.

FX-CG[CL] is a library that you can link against your addin, and with a few compiler options, run the unmodified addin code on your PC. This makes debugging easy, since you can quickly iterate and you get the benefit of all the usual debuggers, profilers, etc.
I've only implemented a few functions at this point, but anything that libfxcg provides is fair game. Some of our "useful routines" poke directly at the hardware, so I'd like to put implementation of those in libfxcg so they can be emulated too.

FX-CG[UI] is the interface to FX-CG[CL], but separate because that allows the addin support library code to be simpler, and it means that future projects in the same vein (such as a true Prizm emulator) can use the same UI. It's implemented in Python on top of Qt, so it can run on just about any platform you could want.
The image at the top of the post is what this UI looks like right now, but I will be adding a clickable keyboard (and probably keyboard bindings for same) that communicates back to FX-CG[CL] for keypress handling.

A few more demo shots:

Testing the screen image transfer protocol. The [CL] implementation of Bdisp_PutDisp_DD does a fast color format conversion and sends the screen image in a simple packet to [UI].

You can save screenshots, and I hope to support animated screenshots as well.

If anybody's wondering how fast this decoupled approach is, the answer is "very". I was getting some absurd framerate out of it with early versions, and CPU usage for both processes is negligible when capped to 20 FPS.

Code
The code for both components is on Bitbucket for your hacking/feature-addition/just-using-it gratification:
https://bitbucket.org/tari/fx-cg-cl
https://bitbucket.org/tari/fx-cg-ui
Just saying again here, great progress!

Not a request, but just saying that it would be amazing if you can virtual link 2 emulated calcs together with the IO commands Very Happy
Amazing work as always, Tari, and how did you manage to keep this one a secret from us for so long? Smile How would people be able to help contribute to this effort, particularly in getting all the routines that write directly to VRAM to work properly?
KermMartian wrote:
Amazing work as always, Tari, and how did you manage to keep this one a secret from us for so long? Smile
I think he's only been working on it this weekend, and he's been talking about it in the channel.
merthsoft wrote:
KermMartian wrote:
Amazing work as always, Tari, and how did you manage to keep this one a secret from us for so long? Smile
I think he's only been working on it this weekend, and he's been talking about it in the channel.
Yeah, basically. I messed with some concepts earlier in the week, but most of the implementation came in the last two days.

KermMartian wrote:
How would people be able to help contribute to this effort, particularly in getting all the routines that write directly to VRAM to work properly?
If people follow the recommendations on the wiki and use GetVRAMAddress() rather than a hard-coded value, there's no problem. It may be possible to do something silly like write a valgrind plugin (or perhaps use libsigsegv) to handle hard-coded addresses, but that then depends on there being nothing useful at the relevant addresses, which puts us at the mercy of the system.

AHelper wrote:
Not a request, but just saying that it would be amazing if you can virtual link 2 emulated calcs together with the IO commands Very Happy
Certainly doable, just have to throw a few more sockets around.
It looks similar to my Prizm simulator Wink
MPoupe wrote:
It looks similar to my Prizm simulator Wink
Yes, quite correct. Smile
I looked at your simulator, but decided it would make more sense to redesign something to be more portable than to try to modify yours to work on non-Windows platforms. As a bonus, it gives me the freedom to set it up so addins can be run without modification.
Tari wrote:
Yes, quite correct. Smile
I looked at your simulator, but decided it would make more sense to redesign something to be more portable than to try to modify yours to work on non-Windows platforms.
Good work !
Tari wrote:
As a bonus, it gives me the freedom to set it up so addins can be run without modification.
My latest simulator also doesn't need to modify addin's source. See CGDoom sources - the source code is truly shared.
Decided to prod a bit at the concept of handling hard-coded addresses, since I was looking at this thread after referring to it in another. Some interesting possibilities present themselves.

I actually looked at libsigsegv, and it doesn't quite do what I want, because it requires that there be memory backing the requested region.

As I was reading that, I remembered that mmap() can take an address at which the target descriptor should be mapped (and Windows' VirtualAlloc() performs similarly). By calling mmap() and not allowing the system to change the mapping address, we can put arbitrary data at fixed addresses. These values can't be updated on-demand, but running a threaded approach may be possible for handling things like hardware registers (or we just ignore that and only really support VRAM).

There will be some systems on which this won't work, though. On 32-bit Windows (without the /3GB switch active), 0x80000000 and above are kernel space, and I believe 32-bit Linux does something similar. On 64-bit systems, the region around 0xA0000000 (which is the one we are most interested in) falls squarely in user space, so memory-mapping is easy in that case.

Ensuring the required memory is available may be tricky, especially if the system does address space randomization. There may be a way to make the linker specifically exclude a memory region from relocations (though I doubt this would be a good solution), or the program would have to re-execute itself (up to some limit) if the required regions aren't available for mapping.
That sounds like a clever and workable approach. That's still probably necessary for older programs, although I've taken your (and AHelper's) nagging to heart in using GetVRAMAddress() liberally in all of my recent programs and published routines.
Can this software also do screen receiving from a connected calculator?
No, and that doesn't really fall under the set of tasks I designed it to perform in any case. If the protocol for doing such is known, I might consider building another utility to do so, though.

Interesting timing with your response here, since I pulled this code back out earlier today and started implementing more features (for now, just the address space reservation for VRAM).
Tari wrote:

Interesting timing with your response here, since I pulled this code back out earlier today and started implementing more features (for now, just the address space reservation for VRAM).


Oh, cool. Smile

Do you know if it can handle the type of getkey that LuaZM uses? I am trying to find a way to get a screenshot of a luazm prog without having to use windows.
I don't know what mechanism LuaZM uses to read keys, so I can't comment on that (though no input is implemented yet, but that'll be one of the next things to come in). I expect to be able to support MMIO emulation for input (which I assume is what it uses).
I think the better approach to that problem would be to modify the LuaZM libraries to be hostable on a native Lua interpreter and simply communicate with this UI module.

Unexpected-productivity edit:
Here's the beginning of a zmg module that knows how to communicate with the UI (as far as the UI has been implemented).
Code:
-- LuaSocket
local socket = require("socket")
-- LuaBitOps
local bit = require("bit")
local band, shl, shr = bit.band, bit.lshift, bit.rshift

local s = socket.connect("localhost", 19756)
local VRAM = {}
for i = 0, 384 * 216 do
    VRAM[i] = 0
end

function fastCopy()
    -- Packet type 0, 0x3cc0f bytes
    local header = "FXCG\x00\x03\xcc\x0fP6\n384 216\n255\n"
    s.send(header)
    -- Screen image
    for px = 0, 384 * 216 do
        local r, g, b
        r = shr(band(VRAM[px], 0xF800), 8)
        g = shr(band(VRAM[px], 0x07E0), 3)
        b = shl(band(VRAM[px], 0x001F), 3)
        payload = payload .. string.char(r, g, b)
    end
    s.send(payload)
end
Mind, this is completely untested and I've never written Lua before this. Looks like it should work, though.
I also started documenting the protocol for communicating with the UI.
Progress continues because this is a prerequisite for testing pLemmings. I got tired of hard-to-trace connection resets and general annoyance debugging socket-based IPC, so I'm now using shared memory. As a side-effect of that change, it becomes exceedingly easy to handle raw keyboard access, since the UI merely has to write the keyboard state to shared memory.

I'll probably need to write a small helper module for Python in C to handle the extremely basic spinlock implementation I'm using to avoid tearing when updating the display, but that shouldn't be a problem, and even if I ignore the mutex it can't cause stability problems.

Code's on bitbucket, BTW.
I'm glad to hear that this (pair of) project(s) continues to progress, not to mention that you have at a contest entry, or at worst simply a cool game for Prizm users. Also, I'm surprised that no one has created a simple Python/C spinlock before.
Also posted this video in the pLemmings thread, since I'm running some skeleton code for that in it.

You'll notice that the UI is much larger than 1:1-- it's currently hard-coded to scale by a factor of 3, but I intend to make it changeable at runtime (just need to add a few menu options, really). Towards the end of the video, I show off the fixed address mapping for VRAM (the pointer is indeed 0xA8000000), which should please those who don't like to use GetVRAMAddress.

Debugging this new shared-memory scheme is actually a joy. It's quite nice to muck with the device state and see it updated in real time.
Could you tell me, how can I compile it in Linux?
Linux support is unfinished, since I've been working on it in Windows thus far. Given you're interested, I'll have a go at making things more portable today (probably use CMake). The big change required will be adding Linux implementations of the functions defined in platform.h:
Code:
void platform_mutex_acquire(mutex_t *mutex);
void platform_mutex_release(mutex_t *mutex);

void *platform_map_shared(const char *file, unsigned offset, size_t size, void *baseaddr);
void platform_unmap_shared(void *baseaddr);

void *platform_map_alloc(void *addr, size_t size);
void platform_map_free(void *addr);

void platform_msleep(unsigned milliseconds);

_msleep can just call usleep() internally, and map/unmap_shared and map_alloc/free can all be implemented with mmap()/munmap(). The mutex functions just need atomic operations, such as GCC's __sync_val_compare_and_swap (or perhaps __sync_lock_test_and_set for slightly simpler code).
I would be interested in a linux version also. Smile
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
» Goto page 1, 2, 3, 4, 5  Next
» View previous topic :: View next topic  
Page 1 of 5
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement