You still need to tell me where to start the "permanent" SdramCache COG.
Sorry - I thought that was clear. In this particular case your RAM caching cog will need to be started by Catalyst (i.e. in Catalyst_XMM_SD_Loader_Input.spin). Catalyst performs the first phase of the load - which is to load the entire file to be executed into XMM RAM (apart from the first 31k which it loads into Hub RAM, and then executes as a normal SPIN program).
The program that gets loaded into the first 31k is the target, which loads all the other plugins, then finally loads and executes yet another loader - one which knows how to load the program back again from XMM RAM (Catalina_HUB_XMM_Loader_Input.spin).
Finally, the XMM Hub Loader converts itself into the kernel.
Your mission ... should you choose to accept it << insert Mission Impossible music here >> ... is to ensure that at each transition of control - i.e. from Catalyst to the target to the Hub Loader, to the kernel - cog 6 is never stopped.
By the way, the biggest cog-monster at the moment is the hires VGA display on the Morpheus (it takes 4 cogs just to generate the VGA signal!) - but even on that platform there are still plenty of cogs left to do useful things.
The full color tile thread looks like it might end up using 4 cogs as well. (I haven't looked at adding this to catalina yet, as the full color thread is now on its 4th iteration, but eventually it might stabilise).
Kye has a keyboard/mouse in one cog.
So - 4 for vga, one for key/mouse and one for the ram driver/C driver.
Hmm - would that leave two left? One of those would probably be the sd card driver, but with loadable cogjects, you could potentially load up a cog on the fly to do sd card access?
I've still got work to do with the color video driver, but one thing that is becoming clear is that if you want the nice graphics then there is no code space left in hub. Which kind of leaves few other choices besides catalina in external memory.
Another thing to come out of the full color thread is that if you want 160x120 you can just put the pixels in hub ram, but once you go to 256x240 and have tiles, then rebuilding tiles from within code might end up needing quite a bit of code. Only catalina is likely to be able to deliver that.
So - no code requests for the moment from me, but there might be some down the track.
Addit: 27th November
Having completed the Movie driver code (160x120), and also worked extensively with the 256x240 code building GUI primitives, I am now getting a feel for a wishlist of Catalina cog plugins. I hope this list isn't too long for Santa:
1) 160x120 vga color driver by Kye
2) 256x240 vga tile driver by Baggers
3) Kye's sd fat32 driver
4) Kye's combo mouse/keyboard in one cog
and lower priority but still would be very useful:
5) Adding more latches to the dracblade (this is complicated because the code has to be integrated into the XMM driver, and put the XMM driver on hold temporarily while bytes are sent/received).
6) Tim Moore's multi serial port in one cog code (eg Pullmoll's dual driver with bigger buffers).
I am still trying to understand how to build cog drivers. Each of the above has some extensive Spin code, but this can be replicated in C. Talking to the cog usually involves sending commands as single letters. I read the tutorial but it seems there is one step you have to do before doing anything, and that is to determine how many bytes the existing cog code has, and how much is free, and will the tiny extra catalina code take up too much space.
Will the Spin glue code end up being replicated in C? If so, is it a matter of taking the Spin code and translating it one line at a time into C?
I got distracted with the video driver code for the last week - suddenly it went from a few colors to 64 colors to smaller pixels. And then it was possible to play movies.
I'm going to post the movie code because it is so simple and because it highlights something I think is important:
PUB Movie(n)
fat.openfile(string("taz.pmv"),"R") ' 160x120 per frame, saved as a binary file
repeat i from 1 to n ' number of frames
fat.readdata(pix.displaypointer,19200)
fat.closefile
This is one of those 'standing on the shoulders of giants' moments because I didn't really do anything special here.
Kye released the sd card driver code some time ago. I had some earlier code that read bytes one at a time and it was slow, and I replaced it with a function he had already coded = the fat.readdata. This puts reads n bytes and puts them in a designated location.
In amongst some more recent code he released for the video driver is a function that returns the pointer where the graphics display is located - pix.displaypointer.
So - by combining these two together, reading a movie frame started off as quite a large amount of slow code with many variables and gradually was reduced down to this one line
fat.readdata(pix.displaypointer,19200)
Today I went back to the Catalina IDE. I've actually been adding tabs to the code, one for building static images in various resolutions for different drivers, and one for movies. So these could potentially become things you could easily add to your C program too.
The above illustrates something I think is important. Kye releases some very clever code which I do not understand. Baggers and Potatohead release some other code. Then along comes a dummy like me and I'm able to stitch those bits of code together and do something.
I'd like to be able to do the same thing in C.
Why? Well, fundamentally, these wonderful eye candy programs are nothing but that, because they use up all the hub ram and leave nothing for code. Only XMM C can solve this problem.
But, try as I might, I simply cannot get my head around plugins.
To my simple way of thinking, what I notice in the compilation process is that various drivers get wrapped up together. What those bits of code are is a mystery to me, because I don't really have access to their inner workings.
This is very different to the way one builds, say, a movie player, as I was able to open all the relevant objects in the Prop Tool, and have them side by side. When I started that project, I didn't actually know there was a routine that returned the location of the ram buffer. But by scrolling through the code, I was able to find it and also to experiment with it and reduce the code.
I am thinking of a way of doing something similar in the catalina IDE - maybe having the ability to open up all the driver code in a separate tab and being able to see how it works.
Why? Well this might seem heretical, but I might want to edit it, because it might not do what I want. For example, there might be a nifty driver for a keyboard, and another for a mouse, and I might want to combine the two. Then I'd like to share that on the Obex.
So - a fundamental concept I still don't quite understand. On the obex are objects that are generally a hybrid of spin and pasm. Is there an equivalent in C - objects that are a hybrid of C and Pasm? If so, could you pull them up in another tab alongside the main C program, just to get a feel of what they can do. And if not, are the C drivers just pasm drivers but with some clever glue code included somewhere behind the scenes?
But having said that, I'm going to go racing off in a new direction. In any IDE, I think it is really useful to have a quick 'hello world' demo. We have that with compilation to local hub ram using payload. Further, this is immensely useful, as one can code simple functions and test them out quickly as add-ons to the 'hello world' program. Even just simple things like getting to know C instructions.
However, once you have those functions written, I'd like to add them to proper big programs and that means XMM.
The problem is that many of the things I'd like to try out don't have the drivers yet (sorry about the huge list of requests above!). Eg - it would be cool, having demonstrated a movie player in Spin, to quickly put one together for C. I'll bet it would be quicker, for a start.
XMM and high res video is different to existing code in that there is no hub ram left, so that guides program development possibly down a different path.
In an ideal world all drivers would all be written.
So, what I am thinking is a generic way of adding custom cog code. The idea is that you have 2k of cog code, and you have tested it already with its associated spin driver. You then take the spin code and translate it to C. You take the cog code and simplify it to the very simplest interface to Spin/C code, which I think would be one Long. Define bit 31 as a flag to send or receive data, bits 24-30 as a command letter (A,B,C,D etc), and the other 3 bytes are for data.
Say I have my custom code. I can load it into the cog in several ways, either from Kyedos, or as part of the C load process, or from within C code. Regardless, it is now sitting in a cog and running and waiting for a command.
In the C program is all the glue code that makes it simple to interface. Thinking XMM, things become simpler, because I might load up a video driver and tell it that it has a buffer in hub from location 4000 to location 23200. Because I know that in my code, I can then load in another cog binary, and I can tell it that it has access to hub buffer location at 23200 to 24200. I can even calculate that in my program.
The clever bit about XMM is that there is no complicated and unknown sized spin/c code in between those buffer locations. So the compiler does not have to worry about that at all.
Where this leads to is a very simple concept - for 8 cogs you just need 8 fixed locations in hub ram for communication to C. Say you pick the top 8 longs in hub ram (are they free?). Cog 6 is running some code. I know what that code is because I loaded it earlier from my own C program from a binary on the sd card. I also know how to talk to that code using a variety of C functions, because I copied and pasted them from a C library. So the one long interface might be hidden to me with some more complex C code eg void New_Frame(), or I might choose to talk to the cog directly. In any case, I put one long in that fixed hub location, set bit 31 to high, put a command 7 bit ascii character in there, and put in 3 data bytes. The cog is waiting for a command, goes off and processes it, and then indicates it has finished by setting bit 31 low.
I'm not even sure you need to register the cogs in the catalina registry.
It could lead to some different ways of coding. For a start, a demo bit of code would be even simpler than the plugin demo. The very simplest demo would be scanning a hub location for a byte where bit 31 is high, when it gets one, it simply sets that byte low. That is only a few pasm instructions.
But it can be more complex too. With XMM there is no cost to having lots of redundant code. So one could have a whole lot of C code that is the equivalent of the SD driver spin code, and a whole lot of video support code as well. One could load in just the 2k binary into a cog for the sd card, use it for a while, then throw it away and load the video driver 2k binary into the same cog. You could emulate having 50 cogs if you like, by loading and reloading code all the time. If you ran out of cogs and were really desperate, you could disable the keyboard while reading the sd card. Or blank the video while reading the sd card.
I'd like to give this idea a try by taking some existing combo spin/pasm code, changing the spin to C, modifying the pasm so it listens for commands (some is already like this, eg Cluso's ram driver, and Kye's sd card driver), and then producing a C 'copy and paste' function, and an associated 2k binary blob of code.
So my request is - could I have 8 longs somewhere in a fixed location in hub ram?
So my request is - could I have 8 longs somewhere in a fixed location in hub ram?
Hi Dr_Acula,
Your post raises some points that will take me more time to address than I have now (I'm at work at the moment - I will answer in more detail later).
However, the main thing you seem to be asking for is exactly what the registry is - i.e. 8 longs reserved in high Hub RAM to be used for communications between cogs.
The Catalina registry just adds some frills to the basic "8 longs" concept because one long is not usually sufficient for practical communications purposes, and also because you should not have to rely on hardcoded knowledge of which cog is currently runnning what.
I documented the registry in the plugin tutorial document - but form your comments, obviously not quite as well as I believed I had!
Yes, please excuse my ignorance with respect to the plugin tutorial and registry.
I'm in the process of trying to simplify it down to single button presses in an IDE, and in doing so, I need to understand the inner workings in order to make it possible for someone to use who does not understand the inner workings.
I think I will have an option on the IDE that puts two richtextboxes side by side. One will have the C code. And one will have the PASM code. I envisage debugging both at the same time. I'd like a "Simple Plugin" button that pastes the appropriate C code into the C window, and also the appropriate PASM code, much like the New button pastes a complete working 'Hello World' into the code window.
Behind the scenes, the PASM code will be compiled separately (I've yet to work out exactly how, maybe with some absolute minimalist spin code that we know the length of so it is possible to strip it out and have just the cog binary). Then download that binary code using xmodem to the sd card as a .bin file - or maybe .cog or something. Then download the C program and run it. I'd like the C code to provide all the methods to talk to that cog, and to be able to load that cog and unload it as required.
In pseudo code, I'm trying to think of the absolute simplest pasm code to demonstrate this:
start
get value from hub location x
if bit 31 is low then
or with 10000000_00000000_00000000_10101010 ' new data and flag set
put new value back to hub location x
endif
goto start
But, like you say, if this is already done that would be great.
I'll read the tutorial again to work out the confusing bits. I presume this technique is only going to be applicable to XMM solutions?
Maybe I'll write the IDE code with the expectation that it is possible to have a demo program, and that can be added later? There are some things I need to do, eg write code for the binary blob download, and write all the New/Save/Load/Download code for the PASM IDE.
In amongst some more recent code he released for the video driver is a function that returns the pointer where the graphics display is located - pix.displaypointer.
This type of facility will be available in Catalina 2.8. I have added the Parallax Graphics object, and that also required me to expose the bitmap location (which was not required for the Text objects). So a C programs can now not only use graphics, but also maniuplate the bitmap directly - which means that doing in Catalina what you did in SPIN (i.e. to show movies) will also be just a couple of lines of C code.
Today I went back to the Catalina IDE. I've actually been adding tabs to the code, one for building static images in various resolutions for different drivers, and one for movies. So these could potentially become things you could easily add to your C program too.
The above illustrates something I think is important. Kye releases some very clever code which I do not understand. Baggers and Potatohead release some other code. Then along comes a dummy like me and I'm able to stitch those bits of code together and do something.
I'd like to be able to do the same thing in C.
You already can. It's just different with C than with SPIN. With SPIN you almost always have to distribute your programs as source, since every user will have to modify the code to suit their particular platform. With Catalina you would use libraries to achieve the same thing, and these can be distributed in compiled form, since one of my main goals with Catalina is to make all C code (including libraries) completely platform independent. All the platform dependent differences are wrapped up in the Catalina plugins and targets, which form a Hardware Abstraction Layer (HAL) in much the same way an operating system kernel does.
The targets are mainly about differences inherent in using the different memory models, and the plugins are mainly about hardware differences, or to allow for code that needs to be implemented in PASM (e.g. for speed).
But it takes a different mindset to use this technique than to use SPIN, and requires you to "factor out" memory model and platform dependencies into the targets and the various plugins.
But, try as I might, I simply cannot get my head around plugins.
To my simple way of thinking, what I notice in the compilation process is that various drivers get wrapped up together. What those bits of code are is a mystery to me, because I don't really have access to their inner workings.
You should ignore the complexity of the standard targets, and concentrate on the explanation (and working example) given in the Catalina Unplugged thread - much simpler!
This is very different to the way one builds, say, a movie player, as I was able to open all the relevant objects in the Prop Tool, and have them side by side. When I started that project, I didn't actually know there was a routine that returned the location of the ram buffer. But by scrolling through the code, I was able to find it and also to experiment with it and reduce the code.
I am thinking of a way of doing something similar in the catalina IDE - maybe having the ability to open up all the driver code in a separate tab and being able to see how it works.
Why? Well this might seem heretical, but I might want to edit it, because it might not do what I want. For example, there might be a nifty driver for a keyboard, and another for a mouse, and I might want to combine the two. Then I'd like to share that on the Obex.
The style of programming you are talking about is well suited to hobbyist use, but (as I mentioned before) C requires a different style and mindset - it is not really conducive to this type of "interactive" programming. Attempts in the past to make it so have mostly been unsuccessful (do you remember Borland's "Turbo Pascal" product? - this was a great success and is still going strong in various new incarnations such as "Feee Pascal"). But "Turbo C", which tried to do the same thing for C really didn't really ever make it - not because the product was bad, but because C is not so well suited this type of programming).
To program well in C you need to design your program up front to be well structured and modular. It is not a language well suited to "discovering" functionality by reading code, and then "tweaking" here and there to make it work.
While C is a useful language to have available on the Propeller, and is also a necessary language for professional use, I don't claim (as I once did) that C is a good choice for a hobbyist language - for precisely this reason. I'm so used to professional programming, it took me a while to realize that the needs of hobbyist programmers is different.
So - a fundamental concept I still don't quite understand. On the obex are objects that are generally a hybrid of spin and pasm. Is there an equivalent in C - objects that are a hybrid of C and Pasm? If so, could you pull them up in another tab alongside the main C program, just to get a feel of what they can do. And if not, are the C drivers just pasm drivers but with some clever glue code included somewhere behind the scenes?
As you point out - most OBEX objects are a combination of PASM and SPIN. Often, the real complex stuff is done in PASM, and the "glue" or "interface" code is done in SPIN.
In C, much more of the code is done in C itself (quite often, all of it). In that sense, C is more like a platform independent assembly language - and if PASM was the only language available for programming the Propeller, then professionals might still use the chip, but hobbyists would not do so.
But even so, not everything can be done in C (just as not everything can be done in SPIN). But the consequences of the HAL architecture are that there should really be two distinct types of Catalina C object:
if the functionality is platform dependent it would be a C program, or a C library
if the functionality must be implemented in PASM, or is platform or hardware dependent, it would be a plugin.
While the HAL simplifies many things, it can complicate the place where the C program and the plugins have to be tied together (i.e. the target). This means that if loading the plugin requires knowledge of the memory model, then a target that shows how it must be loaded and initialized should also be provided.
Michael park is doing some work on Homespun that may simplify the way targets are currently constructed - and I also have some ideas of my own that I will implement when I get time. At the moment they are a bit mesy, and are not really well suited to the type of "plug and play" which you would need.
But having said that, I'm going to go racing off in a new direction. In any IDE, I think it is really useful to have a quick 'hello world' demo. We have that with compilation to local hub ram using payload. Further, this is immensely useful, as one can code simple functions and test them out quickly as add-ons to the 'hello world' program. Even just simple things like getting to know C instructions.
Good idea. But be aware that few people learn C as a first language. Teaching C is generally more difficult than teaching other languages.
However, once you have those functions written, I'd like to add them to proper big programs and that means XMM.
The problem is that many of the things I'd like to try out don't have the drivers yet (sorry about the huge list of requests above!). Eg - it would be cool, having demonstrated a movie player in Spin, to quickly put one together for C. I'll bet it would be quicker, for a start.
XMM and high res video is different to existing code in that there is no hub ram left, so that guides program development possibly down a different path.
In an ideal world all drivers would all be written.
In an ideal world the Propeller would have more Hub RAM!
So, what I am thinking is a generic way of adding custom cog code. The idea is that you have 2k of cog code, and you have tested it already with its associated spin driver. You then take the spin code and translate it to C. You take the cog code and simplify it to the very simplest interface to Spin/C code, which I think would be one Long. Define bit 31 as a flag to send or receive data, bits 24-30 as a command letter (A,B,C,D etc), and the other 3 bytes are for data.
Say I have my custom code. I can load it into the cog in several ways, either from Kyedos, or as part of the C load process, or from within C code. Regardless, it is now sitting in a cog and running and waiting for a command.
In the C program is all the glue code that makes it simple to interface. Thinking XMM, things become simpler, because I might load up a video driver and tell it that it has a buffer in hub from location 4000 to location 23200. Because I know that in my code, I can then load in another cog binary, and I can tell it that it has access to hub buffer location at 23200 to 24200. I can even calculate that in my program.
It's not quite that easy - as Jazzed is finding out with his 32mb SdRam driver, the XMM load process really gets in the way here. The load process Catalina has to go through to get a program that may consist of several megabytes into a chip with (essentially) only 32kb of RAM would make doing a double reverse sumersault with a triple pike and twist into a glass of water look easy!
The clever bit about XMM is that there is no complicated and unknown sized spin/c code in between those buffer locations. So the compiler does not have to worry about that at all.
Where this leads to is a very simple concept - for 8 cogs you just need 8 fixed locations in hub ram for communication to C. Say you pick the top 8 longs in hub ram (are they free?). Cog 6 is running some code. I know what that code is because I loaded it earlier from my own C program from a binary on the sd card. I also know how to talk to that code using a variety of C functions, because I copied and pasted them from a C library. So the one long interface might be hidden to me with some more complex C code eg void New_Frame(), or I might choose to talk to the cog directly. In any case, I put one long in that fixed hub location, set bit 31 to high, put a command 7 bit ascii character in there, and put in 3 data bytes. The cog is waiting for a command, goes off and processes it, and then indicates it has finished by setting bit 31 low.
I'm not even sure you need to register the cogs in the catalina registry.
As I mentioned before, what you are describing is the Catalina registry.
It could lead to some different ways of coding. For a start, a demo bit of code would be even simpler than the plugin demo. The very simplest demo would be scanning a hub location for a byte where bit 31 is high, when it gets one, it simply sets that byte low. That is only a few pasm instructions.
But it can be more complex too. With XMM there is no cost to having lots of redundant code. So one could have a whole lot of C code that is the equivalent of the SD driver spin code, and a whole lot of video support code as well. One could load in just the 2k binary into a cog for the sd card, use it for a while, then throw it away and load the video driver 2k binary into the same cog. You could emulate having 50 cogs if you like, by loading and reloading code all the time. If you ran out of cogs and were really desperate, you could disable the keyboard while reading the sd card. Or blank the video while reading the sd card.
I'd like to give this idea a try by taking some existing combo spin/pasm code, changing the spin to C, modifying the pasm so it listens for commands (some is already like this, eg Cluso's ram driver, and Kye's sd card driver), and then producing a C 'copy and paste' function, and an associated 2k binary blob of code.
So my request is - could I have 8 longs somewhere in a fixed location in hub ram?
As described above, you don't need anything other than the registry.
What you are describing is essentially the process of integrating a new plugin into Catalina - i.e. you would be taking off from where I finished up in the Catalina Unplugged tutorial (i.e. with a plugin that can be loaded but which doesn't do much) and actually adding some useful functionality - written in PASM - that can be peformed by the plugin and which can then called from C. That would be great!
By the way, I've just been through exactly this process with the Parallax "graphics" plugin - you may want to wait till Catalina 2.8 is released, and then compare the modified graphics Catalina plugin with the original Parallax graphics OBEX object. The code changes to the object itself were quite trivial - but (as I hinted before) figuring out how to load it - especially in an XMM - environment is still quite tricky, because this particular object has to interact with another object (i.e. the TV driver) via Hub RAM.
I have read and reread the .pdf. We are very much on the same wavelength. I would have designed it from scratch in a very similar way. The registry is perfect for this.
Ok, now to the practical stuff.
First - not everything is in the .pdf. The meaty stuff is in the readme files. I found those and read them.
So - practical things. I unzipped the zip to a folder c:\program files\propeller\plugins And once it was unzipped I read the readme files and I think it needs to be in a different folder.
There are two sub folders - custom and custom_demo
In custom the readme has an instruction
catalina -T "C:\Program Files\Catalina\custom" test_leds.c
so I think that I need to move the custom directory to c:\program files\catalina\custom
but in the custom_demo folder the readme says
It is assumed to be installed in the following
directory: C:\Program Files\Catalina\custom
So do I copy all the custom_demo files to custom?
I'm not so good with command line programs. So I've automated the IDE so it does things like the startup batch file including the path as part of the compilation. One click compile and all that.
I want to stick to the catalina conventions, and I'm a bit confused about where the C plugin programs actually reside. In demos, or custom or custom_demos. I guess custom for the moment.
I'll get there. Please don't mind my rambling.
First step is to get the unchanged LMM demo to compile and download.
I have read and reread the .pdf. We are very much on the same wavelength. I would have designed it from scratch in a very similar way. The registry is perfect for this.
Ok, now to the practical stuff.
First - not everything is in the .pdf. The meaty stuff is in the readme files. I found those and read them.
Bottom of page 3 (right after the install instructions!): In each of these directories (and their subdirectories) you will find README.txt files, which contain detailed information in addition to what is contained in this tutorial :smilewinkgrin:
So - practical things. I unzipped the zip to a folder c:\program files\propeller\plugins And once it was unzipped I read the readme files and I think it needs to be in a different folder.
My mistake - I should have specified that you unzip the folders into the default Catalina folder (i.e. C:\Program Files\Catalina), leaving you with:
C:\Program Files\Catalina\custom
and
C:\Program Files\Catalina\custom_demo (note that the document says custom_demos- but that's a typo)
Thanks for that. Just got a few things working - making good progress.
A general question - do you think there ever might be a need for a slow simple XMM version using a serial ram chip. I know it would run at least 10x slower, but nothing could beat it for simplicity. It would be easy enough to solder that chip onto a demoboard for instance. And then people could get a taste for XMM and if it was too slow, could move to other ram solutions?
Another question. With pasm code, there are the 'included' ones and the 'plugin' ones. Is it possible to treat an included one as a plugin?
By way of demonstration, say you booted up with the LORES vga driver, print a signon message, and is it then possible to load the HIRES driver into the same cogs? Then print another message? Then wipe that and load a graphics driver?
I think you already have pasm objects for each of those - if this can be done what might the code look like?
Addit: I'm confused again.
I need some help understanding LMM and XMM. In the demo folder are three files ending in .s These appear to be pieces of a pasm program that get built into a single program, and I see similar code in Catalina_LMM.spin. What builds what? Is the .spin program created from the .s programs, or vice versa? Which one do you edit to add your code?
But maybe that is going off on a tangent I don't need to look at. LMM looks very complicated, and I presume there is complexity because you have a program running in hub ram and somehow you need to get some binary data into a cog.
The one that interests me more is XMM. In a general sort of way, this should be simpler because once XMM code is running (ie a C program), the first thing it could do is wipe virtually all the hub free and the program would keep running.
I'm not entirely sure how an XMM program gets data into a cog, but I presume the steps involve reading data off an sd card into low hub ram, then running a coginit instruction and then somehow returning control to the XMM program.
I'm thinking of a plugin that is much simpler than the three .s programs. Do you really need to have all the LMM jump tables in order to run a plugin?
For instance, the simple concept I'm trying to build is a tiny pasm program that accepts a value, adds one, and returns it. For this, I need some pasm code. I might also need the compiler to 'write' some pasm code to tell it where the registry is - I'm not sure about that. Maybe I can just write my pasm code so it always talks to registry 6. Or maybe a config file is linked to my code.
In any case, my pasm program might only be 6 lines long.
I know the generic concept is that the plugin layer is hidden from the user, and is platform independent, and that is all fine, but I have to be blunt and say that the problem I have is that I have this long wish list of plugins that do not exist yet. They only exist in the obex for spin. So - either I request that they get written, or I write them myself. I'd like to do the latter because I think that will be the simplest solution.
So, what is the absolute minimum cog code one could have that can talk to C? Just give me one service if necessary - eg service 4. Do you need all that LMM jump code?
As I think XMM is the only place that loading and unloading cog code is going to work, is it possible to think about minimalist cog binary blobs of code for XMM only:
1) Does a binary cogject need to be compiled as part of a catalina compile or can it be compiled completely independently?
2) Is it possible to load 2k of binary data from an sd card into hub ram and return control to the C program?
3) Is it possible to move that 2k of binary from hub ram to a cog and return control to the C program?
4) Can the C program have code that registers the new cog (or does that registration have to come from within the cog code itself)
If it is possible to do 1 to 4, then you ought to be able to talk to the cog code using Catalina_Common.
But to answer some of the above myself, I see things like
' wait till cog has registered
repeat until long[registry][cog] >> 24 == PTYPE
and that suggests that the cog itself is doing the registering. If so, does this not use valuable cog code space? ie all this
entry
cogid t1 ' get ...
shl t1,#2 ' ... our ...
add t1,par ' ... registry block
rdlong rqstptr,t1 ' register ...
and rqstptr,low_24 ' ... this ...
wrlong zero,rqstptr ' ... plugin ...
mov t2,#PTYPE ' ... as ...
shl t2,#24 ' ... the ...
or t2,rqstptr ' ... appropriate ...
wrlong t2,t1 ' ... type
I've gone round in circles a few times, but a common problem is when existing obex code is packed tight and there is one long free. So you start adding the catalina stuff, and it runs out of space, then you delete some of the working cog code and then nothing works and then you delete another bit and then you try to find a bit that is least important, and finally you give up because it is too hard to actually debug anything. This is where I get stuck anyway.
that can go. If something is illegal then it crashes. Don't pass illegal things.
And the service requests are great, but you might only need one so we don't need the table for the moment
' table of service entry points:
'
svc_table
long _Service_1 ' Service 1 entry point
long _Service_2 ' Service 2 entry point
long _Service_3 ' Service 3 entry point
long _Service_4 ' Service 4 entry point
and we probably only need one of these three
' some handy common service exit points:
'
err_service
neg rslt,#1 ' set result to -1 on error
end_service
mov t1,rqstptr ' return result ...
add t1,#4 ' ... in second long ...
wrlong rslt,t1 ' ... of request block
done_service
wrlong zero,rqstptr ' clear request block
jmp #get_service ' process next service request
and for a super simple demo, replace this with something that just doubles the long or adds a constant
_Service_4
and rqst,low_24 ' lower 24 bits is ...
rdlong t1,rqst ' ... address of block ...
add rqst,#4 ' ... containing two longs ...
rdlong t2,rqst ' ... of data
mov t3,CNT ' wait ...
add t3,t2 ' ... specified ...
waitcnt t3,t2 ' ... time
or dira,t1 ' set direction as outputs
andn outa,t1 ' turn outputs off
neg rslt,#4 ' return -4
jmp #end_service
and maybe some of these are not needed
zero long 0 ' handy value (zero)
low_24 long $00FFFFFF ' handy value (lower 24 bits)
rqstptr long 0 ' request address
rqst long 0 ' service request
rslt long 0 ' service result
t1 long 0 ' temporary variable
t2 long 0 ' temporary variable
t3 long 0 ' temporary variable
'
param long 0 ' saved initialization data
So maybe that can all be reduced to something that fits on one screen so the code is as simple as possible.
And then I need to think about doing this in XMM rather than LMM as the demo is written for LMM.
I hope all this makes sense. Sorry about rambling on, but hopefully it is more useful to talk through the problem in more detail rather than just saying "it won't work".
Thanks for that. Just got a few things working - making good progress.
A general question - do you think there ever might be a need for a slow simple XMM version using a serial ram chip. I know it would run at least 10x slower, but nothing could beat it for simplicity. It would be easy enough to solder that chip onto a demoboard for instance. And then people could get a taste for XMM and if it was too slow, could move to other ram solutions?
I was going to do precisely that for the C3 - but when I had the time I didn't have the board. Now I'd have to say it's not high on my list of priorities, but I would be happy for some one else to do it.
By way of demonstration, say you booted up with the LORES vga driver, print a signon message, and is it then possible to load the HIRES driver into the same cogs? Then print another message? Then wipe that and load a graphics driver?
I need some help understanding LMM and XMM. In the demo folder are three files ending in .s These appear to be pieces of a pasm program that get built into a single program, and I see similar code in Catalina_LMM.spin. What builds what? Is the .spin program created from the .s programs, or vice versa? Which one do you edit to add your code?
The .s files are compiled assembly language versions of C programs. This is sometimes usefl (e.g. when building libraries) and can be done using the command line option -S. For example:
But maybe that is going off on a tangent I don't need to look at. LMM looks very complicated, and I presume there is complexity because you have a program running in hub ram and somehow you need to get some binary data into a cog.
The one that interests me more is XMM. In a general sort of way, this should be simpler because once XMM code is running (ie a C program), the first thing it could do is wipe virtually all the hub free and the program would keep running.
Correct - the Hub RAM is available for other purposes (e.g. graphics dfisplay buffers). However, keep in mind that the -x2 (SMALL) mode also uses Hub RAM for data and stack space, and the -x5 (LARGE) mode uses Hub RAM for stack space.
It would be possible to run the stack and all data space in XMM RAM, but the results would be very slow. This is similar to the "serial SRAM" option - i.e. I have little interest in it myself, but others may find a use for it.
I'm not entirely sure how an XMM program gets data into a cog, but I presume the steps involve reading data off an sd card into low hub ram, then running a coginit instruction and then somehow returning control to the XMM program.
This is covered in the Catalina Reference Manual, page 83.
I'm thinking of a plugin that is much simpler than the three .s programs. Do you really need to have all the LMM jump tables in order to run a plugin?
No - the jump tables are only required to be included by one object, and they are automatically included by the appropriate target startup file (e.g. lmm_progbeg.s). Actually, one day I will get rid of them altogether.
For instance, the simple concept I'm trying to build is a tiny pasm program that accepts a value, adds one, and returns it. For this, I need some pasm code. I might also need the compiler to 'write' some pasm code to tell it where the registry is - I'm not sure about that. Maybe I can just write my pasm code so it always talks to registry 6. Or maybe a config file is linked to my code.
In any case, my pasm program might only be 6 lines long.
Have a look at any of the ".e" files in the 'source\lib\catalina' library - they are all hand-coded, self-contained LMM pasm programs that implement specific library functions.
I know the generic concept is that the plugin layer is hidden from the user, and is platform independent, and that is all fine, but I have to be blunt and say that the problem I have is that I have this long wish list of plugins that do not exist yet. They only exist in the obex for spin. So - either I request that they get written, or I write them myself. I'd like to do the latter because I think that will be the simplest solution.
So, what is the absolute minimum cog code one could have that can talk to C? Just give me one service if necessary - eg service 4. Do you need all that LMM jump code?
For a plugin, see Generic_Plugin.spin in the custom folder (and remove everything except Service_1. For only one service, the serice request decoding logic can also be removed).
For an LMM PASM subroutine, see any of the .e files in source\lib\catalina.
As I think XMM is the only place that loading and unloading cog code is going to work, is it possible to think about minimalist cog binary blobs of code for XMM only:
No, you can also load and unload from Hub RAM in a simple LMM programs - see complex_test.c in custom_demo. However, I agree loading from SD card is probably only feasible in XMM programs.
I've gone round in circles a few times, but a common problem is when existing obex code is packed tight and there is one long free. So you start adding the catalina stuff, and it runs out of space, then you delete some of the working cog code and then nothing works and then you delete another bit and then you try to find a bit that is least important, and finally you give up because it is too hard to actually debug anything. This is where I get stuck anyway.
I've never found a program yet that cannot be reduced in size by one long without affecting functionality. Logically then, with enough work all programs can be reduced in size to zero .
that can go. If something is illegal then it crashes. Don't pass illegal things.
And the service requests are great, but you might only need one so we don't need the table for the moment
' table of service entry points:
'
svc_table
long _Service_1 ' Service 1 entry point
long _Service_2 ' Service 2 entry point
long _Service_3 ' Service 3 entry point
long _Service_4 ' Service 4 entry point
and we probably only need one of these three
' some handy common service exit points:
'
err_service
neg rslt,#1 ' set result to -1 on error
end_service
mov t1,rqstptr ' return result ...
add t1,#4 ' ... in second long ...
wrlong rslt,t1 ' ... of request block
done_service
wrlong zero,rqstptr ' clear request block
jmp #get_service ' process next service request
and for a super simple demo, replace this with something that just doubles the long or adds a constant
_Service_4
and rqst,low_24 ' lower 24 bits is ...
rdlong t1,rqst ' ... address of block ...
add rqst,#4 ' ... containing two longs ...
rdlong t2,rqst ' ... of data
mov t3,CNT ' wait ...
add t3,t2 ' ... specified ...
waitcnt t3,t2 ' ... time
or dira,t1 ' set direction as outputs
andn outa,t1 ' turn outputs off
neg rslt,#4 ' return -4
jmp #end_service
and maybe some of these are not needed
zero long 0 ' handy value (zero)
low_24 long $00FFFFFF ' handy value (lower 24 bits)
rqstptr long 0 ' request address
rqst long 0 ' service request
rslt long 0 ' service result
t1 long 0 ' temporary variable
t2 long 0 ' temporary variable
t3 long 0 ' temporary variable
'
param long 0 ' saved initialization data
So maybe that can all be reduced to something that fits on one screen so the code is as simple as possible.
And then I need to think about doing this in XMM rather than LMM as the demo is written for LMM.
Yes - I've not looked at your code in detail but everything is correct in theory.
I hope all this makes sense. Sorry about rambling on, but hopefully it is more useful to talk through the problem in more detail rather than just saying "it won't work".
[/QUOTE]
I ramble enough, so I can't really complain if others do!
Just to clarify, I'll be mainly using the small model for rapid testing of code fragments, and the XMM model for the 'real' program.
Now I understand the LMM vs XMM a bit better. If plugin ones are pure PASM - nothing complicated, nothing LMM, that makes things so much simpler. I've certainly used LMM eg in the CP/M emulation, but I think there is an even better model to test out, and that is to load and reload cogs with a wide variety of code fragments.
I'll check out the .e
Fantastic to hear all the pieces of the jigsaw can be done.
When I said 'only one long free', that is only partly true, in that yes, I can think of at least three obex objects that are chock full, but on the other hand, each does have its own internal comms system to its spin drivers. So in reality, one would be replacing those with the ones that talk to the catalina registry. It does still mean that one needs code absolutely as small as possible - ie as efficiently as the existing spin interface code (which varies depending on who wrote it).
Great that cogjects (is that the name??) can be compiled independently.
Just to double check - you can do the registration from within C. Does that mean one might need to call a tiny delay function in C when you load a cog, just to make sure it has actually loaded before you start talking to it?
This commented out line in the complex test - I presume you can do the same with XMM_DUM
// _register_plugin(cog, LMM_DUM);
Re It would be possible to run the stack and all data space in XMM RAM
Ballpark figure for graphics buffers might be 20k. So there is 12k left.
The stack would be fine in hub - is that likely to ever be more than 1k? Possibly less?
But what about data space. Do you mean things like lists of data that one has in C. Or do you mean things like array space.
In XMM, if I dimension myarray[5000], is that stored in hub or external memory?
Ok, what I think I need to do is try to create the smallest pasm program I can. Then post it here and see if anything has been left out.
Just to clarify, I'll be mainly using the small model for rapid testing of code fragments, and the XMM model for the 'real' program.
Now I understand the LMM vs XMM a bit better. If plugin ones are pure PASM - nothing complicated, nothing LMM, that makes things so much simpler. I've certainly used LMM eg in the CP/M emulation, but I think there is an even better model to test out, and that is to load and reload cogs with a wide variety of code fragments.
I'll check out the .e
Fantastic to hear all the pieces of the jigsaw can be done.
When I said 'only one long free', that is only partly true, in that yes, I can think of at least three obex objects that are chock full, but on the other hand, each does have its own internal comms system to its spin drivers. So in reality, one would be replacing those with the ones that talk to the catalina registry. It does still mean that one needs code absolutely as small as possible - ie as efficiently as the existing spin interface code (which varies depending on who wrote it).
Great that cogjects (is that the name??) can be compiled independently.
Just to double check - you can do the registration from within C. Does that mean one might need to call a tiny delay function in C when you load a cog, just to make sure it has actually loaded before you start talking to it?
Yes, you can register it from C. I doubt a delay would ever be necessary, but you can certainly put a small one in if you're worried.
This commented out line in the complex test - I presume you can do the same with XMM_DUM
// _register_plugin(cog, LMM_DUM);
Actually, the symbol is misleading - LMM_DUM is just the symbolic name of the plugin type - I settled on the LMM_ prefix before XMM came along - the name of the plugin type will be the same in all cases (i.e. LMM, EMM, SMM, XMM).
The line is commented out merely because the plugin registers itself - I intended it to show how to do it from C if that were not the case.
But what about data space. Do you mean things like lists of data that one has in C. Or do you mean things like array space.
When using the SMALL XMM memory model (-x2), all data is in Hub RAM.
When using the LARGE XMM memory model (-x5), local variables are in Hub RAM, but variables declared with file scope (see below) are in XMM RAM, as are all dynamically allocated variables.
In XMM, if I dimension myarray[5000], is that stored in hub or external memory?
As above, with the SMALL XMM memory model, this array will be in Hub RAM. With the LARGE XMM memory model, this array will be in XMM RAM if it is declared with file scope, or in Hub RAM if declared at the local level.
That's probably confusing - here is an example:
int myarray_1[5000]; <-- in the LARGE XMM model, this will be in XMM RAM (global, file scope)
int main() {
int myarray_2[5000]; <-- this will always be in Hub RAM (on the stack, local scope)
int *my_array_3 = malloc(5000); <-- in the LARGE XMM model, this will be in XMM RAM
}
Here is a reference you can look up on file scope. This is important in Catalina, since plugins can generally only access Hub RAM (i.e. they do not have access to XMM RAM) - this means that all variables used to communicate with plugins must be in Hub RAM. When using XMM, this can be done by making such variables "local" to the main function (and passing a pointer to them to anything outside that scope - when you see the new graphics plugin you'll see a working example of this - the graphics plugin works in both LMM and XMM modes, but it need some special "smarts" (which are all internal and invisible to the user) to make things work properly when used in XMM mode.
So for big data arrays keep those in xmm. And for variables that are being passed back and forth to cogs, keep those in hub. I'd anticipate lots of comments in my code to make it clear why things are being done the way they are.
Ok, this is an awful thing to do to a working program - even 'do_stuff' has gone, but is this the smallest pasm plugin one could have?
The spin part is the same. The 'entry' part - I gather that could be done in C?
get the service
if nothing, try again
if something, shift right to get the number
jump to end service and process it
Can it be simplified any further? You could maybe not bother getting the number but one would usually be passing a few numbers.
end_service - does that return in the second long?
To compile this as a standalone file and make a binary, do I need to put Catalina_Common in the same working directory?
{{
''=============================================================================
'' Catalina_Plugin - A Generic Plugin for Catalina.
''
'' This code is intended just as an example of how to construct a Plugin.
''
'' Since this Plugin does nothing, it has no platform-dependent code. If it
'' did have any platform dependencies (or dependencies on other plugins) then
'' it would not be called "Catalina_Plugin.spin" and live in the target
'' directory - instead, it would be called "Catalina_Plugin_Input.spin" and
'' live in the "input" subdirectory (and be mentioned in the Catalina.input
'' file in that directory). Then it would be re-created on every compile with
'' the command line parameters defined appropriately, and you could use
'' constructs like:
''
'' #ifdef PLATFORM
'' ... platform dependent code ...
'' #endif
''
'' History:
'' 2.8 - Initial version
''
''=============================================================================
}}
CON
'
' Note that the following definitions are only for use in this program - they
' must be defined again (usually in a C header file) for use from a C program
'
' Specify the type of this plugin (This plugin is a dummy):
'
PTYPE = common#LMM_DUM
'
' Specify the size of a Hub RAM block to be allocated for this plugin
' (this is an example only - a plugins may not need a Hub RAM block):
'
BLOCK_SIZE = 100
'
' Specify the services that this Plugin wil accept (examples only):
'
'
Service_1 = 1 << 24
Service_2 = 2 << 24
Service_3 = 3 << 24
Service_4 = 4 << 24
'
SERV_MAX = 4 ' maximum service request that will be accepted
'
' Define the services accepted by this plugin (examples only):
'
'name: Service_1
'code: 1
'type: initialization request
'data: initialization data (loaded but never used)
'rslt: -1
'name: Service_2
'code: 2
'type: short service request
'data: outputs to turn on (can only turn on up to 24 outputs)
'rslt: -2
'name: Service_3
'code: 3
'type: long service request
'data: outputs to turn on (can turn on all 32 outputs)
'rslt: -3
'name: Service_4
'code: 4
'type: long service request
'data: outputs to turn off, and a delay to wait before doing so
' (can turn off all 32 outputs)
'rslt: -4
'
OBJ
'
' Include common definitions:
'
common : "Catalina_Common"
PUB start (registry, parameter) : ok | request, cog
'
' Start - this will be called to start the plugin. A parameter is accepted,
' and this parameter is passed to the plugin once it has been started
' (using Service_1 - this is just done as an example).
'
' Returns zero on error, or cog + 1
'
cog := cognew(@entry, registry)
if cog => 0
' wait till cog has registered
repeat until long[registry][cog] >> 24 == PTYPE
' send the parameter to the cog (via Service_1)
common.SendInitializationData(cog, Service_1<<24, parameter)
' wait for the service request to complete
common.WaitForRequest(cog)
ok := cog + 1
else
ok := 0
DAT
org 0
entry
' cogid t1 ' get ...
' shl t1,#2 ' ... our ...
' add t1,par ' ... registry block
' rdlong rqstptr,t1 ' register ...
' and rqstptr,low_24 ' ... this ...
' wrlong zero,rqstptr ' ... plugin ...
' mov t2,#PTYPE ' ... as ...
' shl t2,#24 ' ... the ...
' or t2,rqstptr ' ... appropriate ...
' wrlong t2,t1 ' ... type
get_service
rdlong rqst,rqstptr wz ' any service requests?
' if_z jmp #do_stuff ' no - do other stuff!
mov t1,rqst ' yes - get ...
shr t1,#24 ' ... service ...
' cmp t1,#SERV_MAX wz,wc ' ... request code
' if_a jmp #err_service ' illegal service request
jmp #end_service ' return value
' add t1,#(svc_table-1) ' fetch ...
' movs :table,t1 ' ... service ...
' nop ' ... (nop required here!) ...
':table mov t2,0-0 ' ... address
' jmp t2 ' jump to service
'
' table of service entry points:
'
'svc_table
' long _Service_1 ' Service 1 entry point
' long _Service_2 ' Service 2 entry point
' long _Service_3 ' Service 3 entry point
' long _Service_4 ' Service 4 entry point
' ... etc ...
'
' we can do other stuff while waiting for a service request if we need to:
'
'do_stuff
' nop ' example
' jmp #get_service
'
' some handy common service exit points:
'
'err_service
' neg rslt,#1 ' set result to -1 on error
end_service
mov t1,rqstptr ' return result ...
add t1,#4 ' ... in second long ...
wrlong rslt,t1 ' ... of request block
done_service
wrlong zero,rqstptr ' clear request block
jmp #get_service ' process next service request
'---------------------------------- Services -----------------------------------
'
' _Service_1 - example only - just accept a 32 bit configuration parameter.
' The value is loaded, but never actually used for anything.
'
'_Service_1
' mov t1,rqstptr ' fetch ...
' add t1,#4 ' ... the ...
' rdlong param,t1 ' ... parameter
' neg rslt,#1 ' return -1
' jmp #end_service
'
' _Service_2 - example only - turn on the specfied output(s).
' Since this is a short request, the data is the lower 24 bits of
' the actual request - so we can only turn ON the first 24 outputs.
' If we need more than this, we have to use a service that accepts
' a long request - such as Service_3.
'
'_Service_2
' and rqst,low_24 ' lower 24 bits is data
' or dira,rqst ' set direction as outputs
' or outa,rqst ' turn outputs on
' neg rslt,#2 ' return -2
' jmp #end_service
'
' _Service_3 - example only - turn on the specified output(s).
' Since this is a long request, the data is pointed to by
' the lower 24 bits of the actual request (since this
' must be in Hub RAM, 24 bits is always enough) and this
' means we can turn ON all 32 outputs.
'
'_Service_3
' and rqst,low_24 ' lower 24 bits is ...
' rdlong t1,rqst ' ... address of data
' or dira,t1 ' set direction as outputs
' or outa,t1 ' turn outputs on
' neg rslt,#3 ' return -3
' jmp #end_service
'
' _Service_4 - example only - turn off the specified output(s) after the
' specified delay.
' Since this is a long request, the data is pointed to by
' the lower 24 bits of the actual request (since this
' must be in Hub RAM, 24 bits is always enough) and this
' means we can turn OFF all 32 outputs.
'
'_Service_4
' and rqst,low_24 ' lower 24 bits is ...
' rdlong t1,rqst ' ... address of block ...
' add rqst,#4 ' ... containing two longs ...
' rdlong t2,rqst ' ... of data
' mov t3,CNT ' wait ...
' add t3,t2 ' ... specified ...
' waitcnt t3,t2 ' ... time
' or dira,t1 ' set direction as outputs
' andn outa,t1 ' turn outputs off
' neg rslt,#4 ' return -4
' jmp #end_service
'
'---------------------------------- Storage ------------------------------------
'
zero long 0 ' handy value (zero)
low_24 long $00FFFFFF ' handy value (lower 24 bits)
rqstptr long 0 ' request address
rqst long 0 ' service request
rslt long 0 ' service result
t1 long 0 ' temporary variable
t2 long 0 ' temporary variable
t3 long 0 ' temporary variable
'
param long 0 ' saved initialization data
'
fit $1f0
{{
+------------------------------------------------------------------------------------------------------------------------------+
¦ TERMS OF USE: MIT License ¦
+------------------------------------------------------------------------------------------------------------------------------¦
¦Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation ¦
¦files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, ¦
¦modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software¦
¦is furnished to do so, subject to the following conditions: ¦
¦ ¦
¦The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.¦
¦ ¦
¦THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE ¦
¦WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR ¦
¦COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ¦
¦ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ¦
+------------------------------------------------------------------------------------------------------------------------------+
}}
RossH i read that you are (nearly?) finished with the Parllax Graphics Object and i thought when are you planing to release it.
I could really need it by now
RossH i read that you are (nearly?) finished with the Parllax Graphics Object and i thought when are you planing to release it.
I could really need it by now
Please let me know.
Greetings from Austria
Wuut
Hi wuut,
Yes it is finished and it is 100% functionally equivalent to the Parallax Graphics OBEX object - I'm just not happy with the speed of it yet.
When is is used from an LMM program, it is faster than the SPIN version - but I anticipate it will mostly get used from an XMM program in order to maximize the amount of Hub RAM available to use as graphics buffers. Unfortunately, at the moment when you use it from XMM RAM it is significantly slower than the SPIN version.
I think I can improve its performance over time, but I know you need it ASAP so I will release an "alpha" version tomorrow.
So for big data arrays keep those in xmm. And for variables that are being passed back and forth to cogs, keep those in hub. I'd anticipate lots of comments in my code to make it clear why things are being done the way they are.
Yes - if there is a reason a variable has to have local scope, it is worthwhile adding a note to it.
You may be tempted to try and pass the request block address directly - but in general you cannot since you first need to know the cog - which of course you don't know until you have started the plugin! So you have a "chicken and egg" situation.
The spin part is the same. The 'entry' part - I gather that could be done in C?
Not quite. You have removed the plugin registering itself, but the SPIN code does not register it. A plugin must be registered where it is started, since that is the only place that knows the cog it was started in. In this case the plugin is intended to be started from SPIN, so you need to register it in SPIN. Also, you no longer need the parameter, so your SPIN Start method should look as follows:
PUB start (registry) : ok | request, cog
'
' Start - this will be called to start the plugin.
' Returns zero on error, or cog + 1
'
cog := cognew(@entry, registry)
if cog => 0
common#Register (cog, LMM_DUM)
ok := cog + 1
else
ok := 0
get the service
if nothing, try again
if something, shift right to get the number
jump to end service and process it
Can it be simplified any further? You could maybe not bother getting the number but one would usually be passing a few numbers.
end_service - does that return in the second long?
Yes, in general a result is returned in the second long of the request block, and the fact that the service is complete is indicated by zeroing the first long in the rquest block.
To compile this as a standalone file and make a binary, do I need to put Catalina_Common in the same working directory?
No - this program should be put in the targetdirectory (i.e. custom). Call it something new like Catalina_Min_Plugin.spin. Since we modified the Start method, in the same directory you will need to update the target file (in the custom\input subdirectory) called lmm_default_Input.spin.
Here is what that file would look like if you called your new plugin Catalina_Min_Plugin.spin, and decided to use the symbol MIN_PLUGIN to enble it (i.e. by adding -D MIN_PLUGIN to the catalina compilation command):
{{
'-------------------------------------------------------------------------------
' !!! IMPORTANT NOTE !!!
' ======================
' If the name of this file does not end in "_Input.spin", then this
' is not the original file - it is a processed version that will get
' overwritten by the next compile. Do not change this file - instead,
' locate the original version (normally in the 'input' subdirectory of
' the Catalina target directory) and modify that version of this file.
'-------------------------------------------------------------------------------
'
' LMM_default - an LMM target for Catalina LMM Programs.
'
' The following symbols affect the kernel and plugins:
'
' PLUGIN - load and start the Generic Plugin
' MIN_PLUGIN - load and start a minimal plugin
'
' Version 2.8 - initial version
'
'-------------------------------------------------------------------------------
}}
CON
'
' Set these to suit the platform by modifying "Catalina_Common"
' That is where the PIN definitions for all platforms are defined.
'
_clkmode = Common#CLOCKMODE
_xinfreq = Common#XTALFREQ
_stack = Common#STACKSIZE
'
OBJ
'
' The following object MUST be loaded ...
'
Catalina : "Catalina" ' The program to run
'
' If you have additional objects that must be available at run-time, put them
' here (i.e. between Catalina and Catalina_Common) ...
'
'
' The following object MUST be loaded ...
'
Common : "Catalina_Common" ' Common Definitions
'
' The following object MUST be loaded ...
'
Kernel : "Catalina_LMM" ' The Catalina Kernel
'
' The following objects are optional:
'
#ifdef PLUGIN
PLUGIN : "Catalina_Plugin" ' Generic Plugin
#endif
'
#ifdef MIN_PLUGIN
PLUGIN : "Catalina_Min_Plugin" ' Minimum Plugin
#endif
'
'
' Start - This is the main initialization routine - it
' initializes all plugins and then start the Kernel.
'
PUB Start : ok | cog, ALLOC, PLUGIN_BLOCK
' Set up the Registry
Common.InitializeRegistry
' Point to just below the Registry area - we allocate any
' dedicated data blocks downwards from here in Hub RAM
ALLOC := Common#REQUESTS
ok := TRUE
#ifdef PLUGIN
if ok
' allocate a data block for our PLUGIN
PLUGIN_BLOCK := ALLOC - PLUGIN#BLOCK_SIZE
ALLOC := PLUGIN_BLOCK
' start the Plugin - in this cae, the parameter we pass is
' the address of an approporiately sized block of Hub RAM,
' allocated specifically for this Plugin to use - this is
' plugin-dependent, and is intended only as an example.
ok := Plugin.Start(Common#REGISTRY, ALLOC)
#endif
#ifdef MIN_PLUGIN
if ok
' start the Plugin
ok := Plugin.Start(Common#REGISTRY)
#endif
if ok
' Plugins now loaded- start the LMM by giving it the address
' of the top of RAM (to use as the initial SP) and also the base
' address of the Catalina program to execute. The address of the
' initial SP is just below any Hub RAM we allocated to our plugins.
Kernel.Run (Common#REGISTRY, Common#REQUESTS, Catalina.Base, ALLOC)
else
' what?
I'll need to take this one step at a time as it is very confusing.
1)
entry
' cogid t1 ' get ...
' shl t1,#2 ' ... our ...
' add t1,par ' ... registry block
' rdlong rqstptr,t1 ' register ...
' and rqstptr,low_24 ' ... this ...
' wrlong zero,rqstptr ' ... plugin ...
' mov t2,#PTYPE ' ... as ...
' shl t2,#24 ' ... the ...
' or t2,rqstptr ' ... appropriate ...
' wrlong t2,t1 ' ... type
So the first four lines must be included. What about the other six? What do they do?
2)
The common definitions. I gather these are things like which pins are used for the sd card.
If we are building an absolute minimalist plugin that is to be compiled to a separate binary file, and which will sit on an sd card to be loaded as needed into an XMM program, how many of those common definitions do you actually need?
eg - if our independent binary just adds one to a value and returns it, does it need to know what vga pins are being used? Ok, yes, down the track it will, but there are so many things to understand here that I'd like to take it one step at a time.
I tried removing common : "Catalina_Common" and it got unhappy about PTYPE = common#LMM_DUM not being defined. So I guess there is some link between our minimalist binary and the definitions.
The tiny plugin looks so simple in itself, but it is referencing the common definitions and that is full of #ifdefs, and thus I presume needs homespun or BST to compile. I know this is going slightly off on a tangent, but is it possible to build a simple binary that is just one program with no references to other programs and which has no ifdefs and thus could be compiled with the proptool.
Or are there so many common variables one has to keep in one place that this would not be sensible?
I'll need to take this one step at a time as it is very confusing.
1)
entry
' cogid t1 ' get ...
' shl t1,#2 ' ... our ...
' add t1,par ' ... registry block
' rdlong rqstptr,t1 ' register ...
' and rqstptr,low_24 ' ... this ...
' wrlong zero,rqstptr ' ... plugin ...
' mov t2,#PTYPE ' ... as ...
' shl t2,#24 ' ... the ...
' or t2,rqstptr ' ... appropriate ...
' wrlong t2,t1 ' ... type
So the first four lines must be included. What about the other six? What do they do?
The first four lines calculate the address of this plugins given the registry address (passed as par) and the cog it is running in.
The next two lines ensure the first long of the request block is zero, so the plugin won't accidentally execute an erroneous command if that location has not already been cleared (which may be the case if we are loading a new plugin in the same cog that a previous plugin was using).
The last four lines update the registry with the type of plugin running in this cog (i.e. "registering" itself).
The common definitions. I gather these are things like which pins are used for the sd card.
If we are building an absolute minimalist plugin that is to be compiled to a separate binary file, and which will sit on an sd card to be loaded as needed into an XMM program, how many of those common definitions do you actually need?
eg - if our independent binary just adds one to a value and returns it, does it need to know what vga pins are being used? Ok, yes, down the track it will, but there are so many things to understand here that I'd like to take it one step at a time.
I tried removing common : "Catalina_Common" and it got unhappy about PTYPE = common#LMM_DUM not being defined. So I guess there is some link between our minimalist binary and the definitions.
The tiny plugin looks so simple in itself, but it is referencing the common definitions and that is full of #ifdefs, and thus I presume needs homespun or BST to compile. I know this is going slightly off on a tangent, but is it possible to build a simple binary that is just one program with no references to other programs and which has no ifdefs and thus could be compiled with the proptool.
You are probably looking at the Catalina_Common_Input.spin file and wondering how the symbols used in the #define statements get set if you don't want to use Catalina (e.g. you just want to compile a SPIN file using Homespun).
This file can be processed into an output version (i.e. Catalina_Common.spin) which is left in the target directory, and which is suitable for use on the DRACBLADE by Homespun by executing a command such as:
You only need to do the catbind command once. Also, note the specification of the target directory on the Homespun command line. If you don't want to do this, you can instead manually edit Catalina_Common_Input.spin into a suitable version for your own use and copy it to your local directory - I do this sometimes when testing.
Or are there so many common variables one has to keep in one place that this would not be sensible?
I find keeping everything platform dependent in one file very useful - but then I routinely test programs on up to 10 different propeller platforms, whereas I suppose most other people use only one.
Because it adds complexity and complexity means I don't understand it. Sorry about pulling your perfectly good code to bits!
I would not have found the catbind command but that has done something to the file, though it still has a number of #ifdefs all through it.
I'm trying to think in a very simplistic way, and in the common file are a huge number of constants that I don't think I need. Hidden in amongst those are some important ones, but I can't find them because there is too much other information.
The super simple plugin that just adds one to a long is not going to be application specific. Once I have reduced the code to this I'll put that in the IDE as the simplest example, then I can start building it back up again, adding things and also adding these to the IDE, and that will no doubt involve adding some platform specific code.
But for the moment, I'd just like a tiny binary file that ideally is one single .spin file, with no dependencies and can be edited on the prop tool.
Something is wrong with my path definitions I think. It failed to compile and catalina_common is in the target directory (see the last few lines).
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and Settings\Administrator>cd c:\program files
C:\Program Files>cd catalina
C:\Program Files\catalina>use_catalina
===================
SETTING UP CATALINA
===================
CATALINA_DEFINE = [default]
CATALINA_INCLUDE = [default]
CATALINA_LIBRARY = [default]
CATALINA_TARGET = [default]
CATALINA_LCCOPT = [default]
CATALINA_TEMPDIR = [default]
LCCDIR = [default]
C:\Program Files\catalina>catbind -p -D DRACBLADE
C:\Program Files\catalina>cd custom
C:\Program Files\catalina\custom>dir
Volume in drive C has no label.
Volume Serial Number is C42D-53B6
Directory of C:\Program Files\catalina\custom
30/11/2010 11:06 PM <DIR> .
30/11/2010 11:06 PM <DIR> ..
11/10/2010 07:51 PM 669 catalina_default.s
03/11/2010 10:54 PM 38,626 Catalina_LMM.spin
13/11/2010 05:51 PM 11,534 Catalina_Plugin.spin
30/11/2010 11:22 PM 11,731 Catalina_Plugin_Short.spin
30/11/2010 04:36 PM <DIR> input
31/10/2010 05:27 PM 4,000 lmm_progbeg.s
27/09/2009 10:55 AM 343 lmm_progend.s
13/11/2010 09:22 PM 2,895 README.txt
7 File(s) 69,798 bytes
3 Dir(s) 10,307,862,528 bytes free
C:\Program Files\catalina\custom>homespun Catalina_Plugin_Short.spin -L "C:\Program Files\Catalina\custom"
Homespun Spin Compiler 0.27
parsing Catalina_Plugin_Short.spin
Error: Catalina_Plugin_Short.spin (80, 13): File not found
common : "Catalina_Common"
^
C:\Program Files\catalina\custom>cd..
C:\Program Files\catalina>cd target
C:\Program Files\catalina\target>dir catalina_common.*
Volume in drive C has no label.
Volume Serial Number is C42D-53B6
Directory of C:\Program Files\catalina\target
01/12/2010 12:13 PM 36,881 Catalina_Common.spin
1 File(s) 36,881 bytes
0 Dir(s) 10,307,883,008 bytes free
C:\Program Files\catalina\target>
I copied the common file to the local directory. Now it gives a different error:
The Catalina_Common.spin file in the standard target directory (called target) will not have LMM_DUM defined - that is only in the custom target directory (called custom).
By the way, all the catbind program does when you specify the -p option (p for preprocess) is preprocess the various input files to include the appropriate #defines from the command line so that they will compile.
LMM_VMM = 0 ' main LMM (single threaded)
LMM_HMI = 1 ' HMI Drivers (Kbd/Mouse/Screen)
LMM_LIB = 2 ' Utility library
LMM_FLA = 3 ' Floating Point Library A
LMM_FLB = 4 ' Floating Point Library B
LMM_RTC = 5 ' Real-Time Clock
LMM_FIL = 6 ' File System
LMM_SIO = 7 ' Serial I/O
LMM_DUM = 8 ' Dummy Plugin
' Plugin types - the Catalina Kernel generally attempts to locate plugins
' by type, not by the cog they happen to be running on. The first 8 values
' are reserved. New plugin types should start from 8 onwards.
I'll try putting that into my custom spin file and remove the dependency and see if any other errors come up. BRB
Addit:
For this code in the 'entry' section
mov t2,#PTYPE ' ... as ...
shl t2,#24 ' ... the ...
is it possible to move it while it is a constant. Would that save one line of code?
PTYPE = 8 << 24
More experiments. By moving everything into one spin program (see at the bottom of this post) it is possible to see the flow of the program more easily, and also to quickly try to compile/edit/recompile in the proptool. I've added the registry constants, and commented out the link to the common file.
In doing so, I can start to see how this works. But the compiler gives an error on this line
PUB start (registry, parameter) : ok | request, cog
And the reason is that 'registry' is now a constant. So I'll need to pick another value (I changed it to 'registryvalue' and it now compiles to a 220 byte program). But this brings up another question. If you have a total binary pasm object, how can you have any spin at all? How can you have PUB start (registry, parameter) : ok | request, cog. Surely that implies that the spin interpreter is running?
I was thinking one answer is that you build the pasm code linked to some spin code to determine variables, then discard that (? a fixed length) and just take the pasm DAT. But this does not make sense when you have spin methods like
PUB SendInitializationData(to_cog, data_1, data_2)
Advice on this question would be most appreciated.
{{
''=============================================================================
'' Catalina_Plugin - A Generic Plugin for Catalina.
''
'' This code is intended just as an example of how to construct a Plugin.
''
'' Since this Plugin does nothing, it has no platform-dependent code. If it
'' did have any platform dependencies (or dependencies on other plugins) then
'' it would not be called "Catalina_Plugin.spin" and live in the target
'' directory - instead, it would be called "Catalina_Plugin_Input.spin" and
'' live in the "input" subdirectory (and be mentioned in the Catalina.input
'' file in that directory). Then it would be re-created on every compile with
'' the command line parameters defined appropriately, and you could use
'' constructs like:
''
'' #ifdef PLATFORM
'' ... platform dependent code ...
'' #endif
''
'' History:
'' 2.8 - Initial version
''
''=============================================================================
}}
CON
'
' Note that the following definitions are only for use in this program - they
' must be defined again (usually in a C header file) for use from a C program
'
' Specify the type of this plugin (This plugin is a dummy):
'
'PTYPE = common#LMM_DUM
PTYPE = 8 ' dummy plugins are 8 (0-7 are reserved, 9 and up are for future use)
'
' Specify the size of a Hub RAM block to be allocated for this plugin
' (this is an example only - a plugins may not need a Hub RAM block):
'
BLOCK_SIZE = 100
'
' Specify the services that this Plugin wil accept (examples only):
'
'
Service_1 = 1 << 24
Service_2 = 2 << 24
Service_3 = 3 << 24
Service_4 = 4 << 24
'
SERV_MAX = 4 ' maximum service request that will be accepted
' Registry definitions. The Catalina registry provides a place where all
' plugins can be found, and a request block for sending requests to each
' plugin. The registry location must be known to all plugins. To use the
' registry, each plugin finds its own cog number, and then registers by
' putting its plugin type in the top 8 bits of the long at
' long[REGISTRY][plugin_cog]
' The bottom 24 bits of that long point to the request block the plugin
' must use to receive requests and return results. Each request block
' is two longs - the first long is the request, which may be a 'short'
' request (if the request and all its parameters can fit into a single
' long, or the address of a 'long' parameter block elsewhere in RAM. The
' plugin must know how to interpret these requests. When complete, the
' plusing zeroes the first long of the request block, and may either
' return the result in the second long of the request block (typical for
' 'short' requests, but also used for some 'long' requests), or in the
' 'long' parameter block.
'
COGSTORE = $7EFC ' long used for interacting with cog used to store command line
REGISTRY_END = COGSTORE ' this location is fairly arbitrary.
REGISTRY = REGISTRY_END - 4 * 1 * 8
REQUESTS = REGISTRY - 4 * 2 * 8
'
' Define the services accepted by this plugin (examples only):
'
'name: Service_1
'code: 1
'type: initialization request
'data: initialization data (loaded but never used)
'rslt: -1
'name: Service_2
'code: 2
'type: short service request
'data: outputs to turn on (can only turn on up to 24 outputs)
'rslt: -2
'name: Service_3
'code: 3
'type: long service request
'data: outputs to turn on (can turn on all 32 outputs)
'rslt: -3
'name: Service_4
'code: 4
'type: long service request
'data: outputs to turn off, and a delay to wait before doing so
' (can turn off all 32 outputs)
'rslt: -4
'
OBJ
'
' Include common definitions:
'
' common : "Catalina_Common"
PUB start (registry, parameter) : ok | request, cog
'
' Start - this will be called to start the plugin. A parameter is accepted,
' and this parameter is passed to the plugin once it has been started
' (using Service_1 - this is just done as an example).
'
' Returns zero on error, or cog + 1
'
cog := cognew(@entry, registry)
if cog => 0
' wait till cog has registered
repeat until long[registry][cog] >> 24 == PTYPE
' send the parameter to the cog (via Service_1)
SendInitializationData(cog, Service_1<<24, parameter)
' wait for the service request to complete
WaitForRequest(cog)
ok := cog + 1
else
ok := 0
PUB SendInitializationData(to_cog, data_1, data_2)
'
' this routine can be called to send initialization data
' to a cog - note that once initialization is complete,
' SPIN methods such as this cannot be used - requests
' must be done in PASM. Also note that since most
' plugins trigger off the first long of the request,
' it is important to initialize that long second.
'
long[REQUESTS][2 * to_cog + 1] := data_2
long[REQUESTS][2 * to_cog] := data_1
PUB WaitForRequest(to_cog)
' wait for a request to be processed
repeat while long[REQUESTS][2 * to_cog] <> 0
DAT
org 0
entry
cogid t1 ' get ...
shl t1,#2 ' ... our ...
add t1,par ' ... registry block
rdlong rqstptr,t1 ' register ...
and rqstptr,low_24 ' ... this ...
wrlong zero,rqstptr ' ... plugin ...
mov t2,#PTYPE ' ... as ...
shl t2,#24 ' ... the ...
or t2,rqstptr ' ... appropriate ...
wrlong t2,t1 ' ... type
get_service
rdlong rqst,rqstptr wz ' any service requests?
if_z jmp get_service ' nothing so try again
' if_z jmp #do_stuff ' no - do other stuff!
mov t1,rqst ' yes - get ...
shr t1,#24 ' ... service ...
' cmp t1,#SERV_MAX wz,wc ' ... request code
' if_a jmp #err_service ' illegal service request
jmp #end_service ' return value
' add t1,#(svc_table-1) ' fetch ...
' movs :table,t1 ' ... service ...
' nop ' ... (nop required here!) ...
':table mov t2,0-0 ' ... address
' jmp t2 ' jump to service
'
' table of service entry points:
'
'svc_table
' long _Service_1 ' Service 1 entry point
' long _Service_2 ' Service 2 entry point
' long _Service_3 ' Service 3 entry point
' long _Service_4 ' Service 4 entry point
' ... etc ...
'
' we can do other stuff while waiting for a service request if we need to:
'
'do_stuff
' nop ' example
' jmp #get_service
'
' some handy common service exit points:
'
'err_service
' neg rslt,#1 ' set result to -1 on error
end_service
mov t1,rqstptr ' return result ...
add t1,#4 ' ... in second long ...
wrlong rslt,t1 ' ... of request block
done_service
wrlong zero,rqstptr ' clear request block
jmp #get_service ' process next service request
'---------------------------------- Services -----------------------------------
'
' _Service_1 - example only - just accept a 32 bit configuration parameter.
' The value is loaded, but never actually used for anything.
'
'_Service_1
' mov t1,rqstptr ' fetch ...
' add t1,#4 ' ... the ...
' rdlong param,t1 ' ... parameter
' neg rslt,#1 ' return -1
' jmp #end_service
'
' _Service_2 - example only - turn on the specfied output(s).
' Since this is a short request, the data is the lower 24 bits of
' the actual request - so we can only turn ON the first 24 outputs.
' If we need more than this, we have to use a service that accepts
' a long request - such as Service_3.
'
'_Service_2
' and rqst,low_24 ' lower 24 bits is data
' or dira,rqst ' set direction as outputs
' or outa,rqst ' turn outputs on
' neg rslt,#2 ' return -2
' jmp #end_service
'
' _Service_3 - example only - turn on the specified output(s).
' Since this is a long request, the data is pointed to by
' the lower 24 bits of the actual request (since this
' must be in Hub RAM, 24 bits is always enough) and this
' means we can turn ON all 32 outputs.
'
'_Service_3
' and rqst,low_24 ' lower 24 bits is ...
' rdlong t1,rqst ' ... address of data
' or dira,t1 ' set direction as outputs
' or outa,t1 ' turn outputs on
' neg rslt,#3 ' return -3
' jmp #end_service
'
' _Service_4 - example only - turn off the specified output(s) after the
' specified delay.
' Since this is a long request, the data is pointed to by
' the lower 24 bits of the actual request (since this
' must be in Hub RAM, 24 bits is always enough) and this
' means we can turn OFF all 32 outputs.
'
'_Service_4
' and rqst,low_24 ' lower 24 bits is ...
' rdlong t1,rqst ' ... address of block ...
' add rqst,#4 ' ... containing two longs ...
' rdlong t2,rqst ' ... of data
' mov t3,CNT ' wait ...
' add t3,t2 ' ... specified ...
' waitcnt t3,t2 ' ... time
' or dira,t1 ' set direction as outputs
' andn outa,t1 ' turn outputs off
' neg rslt,#4 ' return -4
' jmp #end_service
'
'---------------------------------- Storage ------------------------------------
'
zero long 0 ' handy value (zero)
low_24 long $00FFFFFF ' handy value (lower 24 bits)
rqstptr long 0 ' request address
rqst long 0 ' service request
rslt long 0 ' service result
t1 long 0 ' temporary variable
t2 long 0 ' temporary variable
t3 long 0 ' temporary variable
'
param long 0 ' saved initialization data
'
fit $1f0
{{
+------------------------------------------------------------------------------------------------------------------------------+
¦ TERMS OF USE: MIT License ¦
+------------------------------------------------------------------------------------------------------------------------------¦
¦Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation ¦
¦files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, ¦
¦modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software¦
¦is furnished to do so, subject to the following conditions: ¦
¦ ¦
¦The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.¦
¦ ¦
¦THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE ¦
¦WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR ¦
¦COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ¦
¦ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ¦
+------------------------------------------------------------------------------------------------------------------------------+
}}
If you have a total binary pasm object, how can you have any spin at all? How can you have PUB start (registry, parameter) : ok | request, cog. Surely that implies that the spin interpreter is running?
I was thinking one answer is that you build the pasm code linked to some spin code to determine variables, then discard that (? a fixed length) and just take the pasm DAT. But this does not make sense when you have spin methods like
PUB SendInitializationData(to_cog, data_1, data_2)
Advice on this question would be most appreciated.
The SPIN interpreter always runs during initialization, so you can use SPIN to do the job for any plugins loaded at that point. After that (i.e. once Catalina has been launched) you can also load a pure binary "blob" - essentially just the DAT section of the plugin - but naturally you cannot use any of the SPIN methods since there is no longer a SPIN interpreter running. This is when you either have to have a self-registering plugin, or do it from C after loading the plugin into a spare cog.
You need to study the "complex_test.c" example in more depth, since it does both these things - i.e. it loads the plugin once during initialization, and then the C the program repeatedly unloads it and then loads it again from a binary blob. You need to study not only the program itself, but also the build file, because that is what generates the binary array which contains the "blob" which is the plugin DAT section.
you can also load a pure binary "blob" - essentially just the DAT section of the plugin
Yes, that is what I want to do.
Can you point me to the actual DAT section? See below, this is complex_test.c, this all seems fairly straightforward, but what is it actually loading, and how was that binary blob created? plugin_array.h is a blank file.
and so is that being created from catalina_plugin.spin in the custom directory. That is the file I am editing, and it is the file that has all the extra spin code. How does that get converted to just the DAT pasm bit?
/******************************************************************************\
* Complex program to test the Generic Plugin *
* *
* This program simply toggles some outputs by calling *
* services provided by the Generic Plugin - the OUTPUTS *
* symbol specifies which output pins will be toggled *
* *
* This program is more complex than the simple test, since *
* it unloads and reloads the plugin after every 10 toggles *
* *
\******************************************************************************/
/*
* include the definition of the services provided by the generic plugin:
*/
#include "generic_plugin.h"
/*
* include the generic plugin itself (since we will load it dynamically):
*/
#include "plugin_array.h"
/*
* include the definitions of some useful utility functions:
*/
#include "utilities.h"
/*
* define which pins to toggle (Pin 0 is a debug LED on HYDRA & HYBRID):
*/
#define OUTPUTS 0x00000001
/*
* use_plugin - call various Generic Plugin services (iterate count times):
*/
void use_plugin(int count) {
int i;
// define a buffer for the plugin to use
char buffer[BUFFER_SIZE];
// Call Service_1 to initialize the plugin
//
// This is just an example of how we might configure the plugin - a
// plugin that is intended to be permanently loaded would usually be
// initialized once on startup using a reserved section of Hub RAM.
// That method would suit something like a video driver, which is
// permanently loaded, and where we only need to communicate with
// the driver via registry requests - but if we wanted to interact
// with the plugin directly from C then we can also initialize it
// from within our C program ...
Service_1(buffer);
// Call Service_2 to turn on the outputs - this service uses a
// "short" request, but we can use it to turn on 24 outputs ...
Service_2(OUTPUTS);
// wait 1/2 second ...
wait(500);
// toggle the outputs 10 times ...
for (i = 0; i < count; i++) {
// Call Service_4 to wait for a specified delay, then turn off
// the ouputs - this service uses a "long" request, and allows
// us to pass two parameters ...
Service_4(OUTPUTS, _clockfreq()/2);
// wait 500 msec
wait(500);
// Call Service_3 to turn on the outputs - this service uses a
// "long" request, so we can turn on all 32 outputs ...
Service_3(OUTPUTS);
}
}
void main() {
unsigned reg;
int cog;
// get the address of the registry (so we can pass it to the plugin)
reg = _registry();
// see if the generic plugin is already loaded
cog = _locate_plugin(LMM_DUM);
if (cog >= 0) {
// the plugin is loaded, so we can use it straight away ...
use_plugin(10);
// now unregister the plugin
_unregister_plugin(cog);
// now stop the plugin
_cogstop(cog);
}
// now keep loading, using and unloading the plugin -
// wait 5 seconds between each load/use/unload cycle
while (1) {
// wait for 5 seconds between cycles
wait(5000);
// load the generic plugin from our binary copy - we use the _coginit
// function, passing the address of the registry as the parameter ...
cog = _coginit((int)reg>>2, (int)catalina_plugin_array>>2, ANY_COG);
if (cog > 0) {
// now we can register the plugin (so various functions can find it)
// NOTE that we don't need to do this if the plugin registers itself:
// _register_plugin(cog, LMM_DUM);
// now use the plugin services
use_plugin(10);
// now unregister the plugin
_unregister_plugin(cog);
// now stop the plugin
_cogstop(cog);
}
else {
// failed to load plugin
}
}
}
The spinc command is what builds the binary blob from the binary compiled by Homespun. If your blob is empty, something went wrong. Please post the output of running build.bat.
So that is presumably the plugin. What I can't understand is how this plugin can contain both spin and pasm code.
Maybe I understood this wrong, but I thought a plugin was a binary file that loaded into a cog and the support code was in C, not in spin. But the above is a hybrid of spin and pasm, so what is running the spin part if you don't have the spin interpreter running on that second reload?
Maybe I understood this wrong, but I thought a plugin was a binary file that loaded into a cog and the support code was in C, not in spin. But the above is a hybrid of spin and pasm, so what is running the spin part if you don't have the spin interpreter running on that second reload?
You are right - a plugin is just a PASM program, which can be represented as a pure binary "blob". Creating such a blob from a SPIN file (so that it can be loaded from C) is the job of the spinc program.
But plugins are typically loaded and started during the program initialization phase by the Catalina target, so each one contains a Start method intended to be called by that target. A plugin may also contain other SPIN methods, but these methods can only to be used by the target - they cannot be used once the C program starts, since the target is terminated once the Catalina kernel is started.
Sometimes (e.g. as is the case in the complex_test.c program) there will be several ways the same plugin can be loaded and started - i.e. once from the SPIN target using the Start method in the plugin itself, then again later from C.
If the plugin does not register and initialize itself, then this will have to be explicitly done in both places - the difference is that the SPIN target can use SPIN methods in the plugin (as well as the SPIN support methods contained in Catalina_Common_Input.spin intended for just this purpose), but the C program will have to do it all from C using the C support functions defined in catalina_plugin.h.
To summarize - the guts of each plugin is always a pure PASM program which is represented as a DAT block to a SPIN program, or as a binary array to a C program. Everything else you see is just frills intended to make the plugin easier to load and initialize.
The sort of plugin I am interested in is most definitely not to be loaded at startup. It will be loaded some time later by the C program.
Hence, my understanding is that it cannot have any supporting Spin because the spin interpreter is no longer running.
The complex demo example loads both at startup and then is being reloaded. I need to understand more fully what is going on, because I suspect that the first load is doing the registration.
For a pasm cog load, done by C from within the C program, there is no ability to do registration with spin code, is that right?
but the C program will have to do it all from C using the C support functions defined in catalina_plugin.h.
It is great that such functions have already been written. I think I understand those from the C side of things.
But I still cannot understand the pasm part. To my way of thinking, if you have a combination of spin and pasm, and you load that at startup, that is fine, but if you unregister that cog, then reregister it, spinc will need to somehow strip off the spin part and just produce the pasm binary.
Is this what spinc does?
and, how do you pass values to code in a cog when the code was written expecting to be passed variables from spin?
To summarize - the guts of each plugin is always a pure PASM program which is represented as a DAT block to a SPIN program, or as a binary array to a C program.
Is there an example of how to create just the guts.
The complex demo example loads both at startup and then is being reloaded. I need to understand more fully what is going on, because I suspect that the first load is doing the registration.
No, this plugin self-registers. The SPIN code then shows how you might initialize such a plugin, but initialization is something different. Some plugins will need initialization, others won't.
but the C program will have to do it all from C using the C support functions defined in catalina_plugin.h.
It is great that such functions have already been written. I think I understand those from the C side of things.
But I still cannot understand the pasm part. To my way of thinking, if you have a combination of spin and pasm, and you load that at startup, that is fine, but if you unregister that cog, then reregister it, spinc will need to somehow strip off the spin part and just produce the pasm binary.
and, how do you pass values to code in a cog when the code was written expecting to be passed variables from spin?
Most commonly, I simply replace all references to VAR variables with offsets into a common data block, and then pass the plugin the data block at runtime. For example, instead of
VAR
LONG var1
LONG var2
DAT
...
rdlong r1,@var1
wrlong r1,@var2
...
[/QUOTE]
I might have code like:
CON
var1 = 0 ' offset of var1
var2 = 4 ' offset of var2
DAT
... code to accept a data block pointer (into blk_ptr) ...
mov tmp,blk_ptr
add tmp,#var1
rdlong r1,tmp
mov tmp,blk_ptr
add tmp,#var2
wrlong r1,tmp
...
This example makes it look onerous and inefficient - but in fact it usually isn't.
To summarize - the guts of each plugin is always a pure PASM program which is represented as a DAT block to a SPIN program, or as a binary array to a C program.
Is there an example of how to create just the guts.
Yes - that is precisely what complex_test.c is intended to illustrate. I could make it simpler by using a simpler plugin, but the mechanics of it would remain the same.
I'll take your word that this can be done, but I'd still like to be able to do this myself.
To explain another way. Here is the source program
common : "Catalina_Common"
PUB start (registry, parameter) : ok | request, cog
'
' Start - this will be called to start the plugin. A parameter is accepted,
' and this parameter is passed to the plugin once it has been started
' (using Service_1 - this is just done as an example).
'
' Returns zero on error, or cog + 1
'
cog := cognew(@entry, registry)
if cog => 0
' wait till cog has registered
repeat until long[registry][cog] >> 24 == PTYPE
' send the parameter to the cog (via Service_1)
common.SendInitializationData(cog, Service_1<<24, parameter)
' wait for the service request to complete
common.WaitForRequest(cog)
ok := cog + 1
else
ok := 0
DAT
org 0
and then it goes on with the pasm part.
If you compile that to a pure bit of pasm, ie something that fits into a C array, then surely the Pub start has to go? This is spin, right?
So if it is not included, should it be possible to write a shorter version of the program that does not have the PUB start at all.
Wait till it is registered - you say that is very quick anyway.
Send initialisation data - sent from C, right?
Wait for request - C does that?
and the DAT section has self registering code.
So if I compile that, where is the DAT section? At the beginning. Or n bytes after the beginning, ie after the compiled spin code.
Perhaps it might help to explain more what I want to do. See the attached board - I just finished soldering this up and testing it.
This has a parallel port, and it also has an LCD display. But in simple terms they can just be considered 16 generic digital outputs and 8 generic inputs.
Unfortunately any driver for these uses the same 12 pins as the external ram chip. And if a program is running in Catalina in external ram, those 12 pins are spoken for.
I have taken a look at the xmm driver for catalina, and it might be possible to use some #ifdefs to add specific dracblade driver code.
BUT - a) this makes your code more complicated and b) one might run out of cog space and c) even if one doesn't run out, you might want to use this cog space for some code you write in the future.
There is a comment you made earlier which I found interesting talking about locks. I gather this is a way of making sure cogs don't fight for pins.
So I am wondering if one could add a simple one line #ifdef for the dracblade to the xmm driver that tests for a lock periodically, or between each C instruction, or when a timer clicks over?
Then load a cog with, say, an LCD driver binary, or a Parallel Port binary. Then from within C, send an instruction and a byte (should be possible with one of the 4 instructions you can use). The cog goes off and sends a message via locks, or maybe via some other mechanism, that it wants control of the lower 12 propeller lines.
Eventually, the C XMM driver tests for this and hands over control. The LCD driver cog sends out its byte, then unlocks the XMM driver. C then keeps running.
The idea is that these bits of cog code would be quite small and simple. The don't need any supporting spin and they can be loaded and reloaded many times via the C program.
More importantly, it means I won't be bugging you so often for changes to code, because I'll be able to write my own code (hopefully!).
I'll take your word that this can be done, but I'd still like to be able to do this myself.
To explain another way. Here is the source program
common : "Catalina_Common"
PUB start (registry, parameter) : ok | request, cog
'
' Start - this will be called to start the plugin. A parameter is accepted,
' and this parameter is passed to the plugin once it has been started
' (using Service_1 - this is just done as an example).
'
' Returns zero on error, or cog + 1
'
cog := cognew(@entry, registry)
if cog => 0
' wait till cog has registered
repeat until long[registry][cog] >> 24 == PTYPE
' send the parameter to the cog (via Service_1)
common.SendInitializationData(cog, Service_1<<24, parameter)
' wait for the service request to complete
common.WaitForRequest(cog)
ok := cog + 1
else
ok := 0
DAT
org 0
and then it goes on with the pasm part.
If you compile that to a pure bit of pasm, ie something that fits into a C array, then surely the Pub start has to go? This is spin, right?
So if it is not included, should it be possible to write a shorter version of the program that does not have the PUB start at all.
Of course - except you will find with most SPIN compilers that you need at least one PUB method to compile it at all - but it can be just a dummy method.
Perhaps it might help to explain more what I want to do. See the attached board - I just finished soldering this up and testing it.
This has a parallel port, and it also has an LCD display. But in simple terms they can just be considered 16 generic digital outputs and 8 generic inputs.
Unfortunately any driver for these uses the same 12 pins as the external ram chip. And if a program is running in Catalina in external ram, those 12 pins are spoken for.
I have taken a look at the xmm driver for catalina, and it might be possible to use some #ifdefs to add specific dracblade driver code.
BUT - a) this makes your code more complicated and b) one might run out of cog space and c) even if one doesn't run out, you might want to use this cog space for some code you write in the future.
If you make your new code no larger than any of the existing XMM implementations (I think from memory the HX512 (i.e. Hydra and Hybrid) code is currently the largest), then you will be fine - I guarantee to leave at least this much space in the XMM kernel. Beyond that and you may run into problems.
So I am wondering if one could add a simple one line #ifdef for the dracblade to the xmm driver that tests for a lock periodically, or between each C instruction, or when a timer clicks over?
Not between each C instruction - that would make the program far too slow, since locks are hub instructions (and can take 22 cycles) - but in the new multithreading kernel (which is coming in version 2.8) I do something similar to check if I should switch processes after a fixed number of instructions (e.g. every 100 instructions).
Then load a cog with, say, an LCD driver binary, or a Parallel Port binary. Then from within C, send an instruction and a byte (should be possible with one of the 4 instructions you can use). The cog goes off and sends a message via locks, or maybe via some other mechanism, that it wants control of the lower 12 propeller lines.
Eventually, the C XMM driver tests for this and hands over control. The LCD driver cog sends out its byte, then unlocks the XMM driver. C then keeps running.
The idea is that these bits of cog code would be quite small and simple. The don't need any supporting spin and they can be loaded and reloaded many times via the C program.
More importantly, it means I won't be bugging you so often for changes to code, because I'll be able to write my own code (hopefully!).
Comments
Sorry - I thought that was clear. In this particular case your RAM caching cog will need to be started by Catalyst (i.e. in Catalyst_XMM_SD_Loader_Input.spin). Catalyst performs the first phase of the load - which is to load the entire file to be executed into XMM RAM (apart from the first 31k which it loads into Hub RAM, and then executes as a normal SPIN program).
The program that gets loaded into the first 31k is the target, which loads all the other plugins, then finally loads and executes yet another loader - one which knows how to load the program back again from XMM RAM (Catalina_HUB_XMM_Loader_Input.spin).
Finally, the XMM Hub Loader converts itself into the kernel.
Your mission ... should you choose to accept it << insert Mission Impossible music here >> ... is to ensure that at each transition of control - i.e. from Catalyst to the target to the Hub Loader, to the kernel - cog 6 is never stopped.
Ross.
The full color tile thread looks like it might end up using 4 cogs as well. (I haven't looked at adding this to catalina yet, as the full color thread is now on its 4th iteration, but eventually it might stabilise).
Kye has a keyboard/mouse in one cog.
So - 4 for vga, one for key/mouse and one for the ram driver/C driver.
Hmm - would that leave two left? One of those would probably be the sd card driver, but with loadable cogjects, you could potentially load up a cog on the fly to do sd card access?
I've still got work to do with the color video driver, but one thing that is becoming clear is that if you want the nice graphics then there is no code space left in hub. Which kind of leaves few other choices besides catalina in external memory.
Another thing to come out of the full color thread is that if you want 160x120 you can just put the pixels in hub ram, but once you go to 256x240 and have tiles, then rebuilding tiles from within code might end up needing quite a bit of code. Only catalina is likely to be able to deliver that.
So - no code requests for the moment from me, but there might be some down the track.
Addit: 27th November
Having completed the Movie driver code (160x120), and also worked extensively with the 256x240 code building GUI primitives, I am now getting a feel for a wishlist of Catalina cog plugins. I hope this list isn't too long for Santa:
1) 160x120 vga color driver by Kye
2) 256x240 vga tile driver by Baggers
3) Kye's sd fat32 driver
4) Kye's combo mouse/keyboard in one cog
and lower priority but still would be very useful:
5) Adding more latches to the dracblade (this is complicated because the code has to be integrated into the XMM driver, and put the XMM driver on hold temporarily while bytes are sent/received).
6) Tim Moore's multi serial port in one cog code (eg Pullmoll's dual driver with bigger buffers).
I am still trying to understand how to build cog drivers. Each of the above has some extensive Spin code, but this can be replicated in C. Talking to the cog usually involves sending commands as single letters. I read the tutorial but it seems there is one step you have to do before doing anything, and that is to determine how many bytes the existing cog code has, and how much is free, and will the tiny extra catalina code take up too much space.
Will the Spin glue code end up being replicated in C? If so, is it a matter of taking the Spin code and translating it one line at a time into C?
I got distracted with the video driver code for the last week - suddenly it went from a few colors to 64 colors to smaller pixels. And then it was possible to play movies.
I'm going to post the movie code because it is so simple and because it highlights something I think is important:
This is one of those 'standing on the shoulders of giants' moments because I didn't really do anything special here.
Kye released the sd card driver code some time ago. I had some earlier code that read bytes one at a time and it was slow, and I replaced it with a function he had already coded = the fat.readdata. This puts reads n bytes and puts them in a designated location.
In amongst some more recent code he released for the video driver is a function that returns the pointer where the graphics display is located - pix.displaypointer.
So - by combining these two together, reading a movie frame started off as quite a large amount of slow code with many variables and gradually was reduced down to this one line
fat.readdata(pix.displaypointer,19200)
Today I went back to the Catalina IDE. I've actually been adding tabs to the code, one for building static images in various resolutions for different drivers, and one for movies. So these could potentially become things you could easily add to your C program too.
The above illustrates something I think is important. Kye releases some very clever code which I do not understand. Baggers and Potatohead release some other code. Then along comes a dummy like me and I'm able to stitch those bits of code together and do something.
I'd like to be able to do the same thing in C.
Why? Well, fundamentally, these wonderful eye candy programs are nothing but that, because they use up all the hub ram and leave nothing for code. Only XMM C can solve this problem.
But, try as I might, I simply cannot get my head around plugins.
To my simple way of thinking, what I notice in the compilation process is that various drivers get wrapped up together. What those bits of code are is a mystery to me, because I don't really have access to their inner workings.
This is very different to the way one builds, say, a movie player, as I was able to open all the relevant objects in the Prop Tool, and have them side by side. When I started that project, I didn't actually know there was a routine that returned the location of the ram buffer. But by scrolling through the code, I was able to find it and also to experiment with it and reduce the code.
I am thinking of a way of doing something similar in the catalina IDE - maybe having the ability to open up all the driver code in a separate tab and being able to see how it works.
Why? Well this might seem heretical, but I might want to edit it, because it might not do what I want. For example, there might be a nifty driver for a keyboard, and another for a mouse, and I might want to combine the two. Then I'd like to share that on the Obex.
So - a fundamental concept I still don't quite understand. On the obex are objects that are generally a hybrid of spin and pasm. Is there an equivalent in C - objects that are a hybrid of C and Pasm? If so, could you pull them up in another tab alongside the main C program, just to get a feel of what they can do. And if not, are the C drivers just pasm drivers but with some clever glue code included somewhere behind the scenes?
But having said that, I'm going to go racing off in a new direction. In any IDE, I think it is really useful to have a quick 'hello world' demo. We have that with compilation to local hub ram using payload. Further, this is immensely useful, as one can code simple functions and test them out quickly as add-ons to the 'hello world' program. Even just simple things like getting to know C instructions.
However, once you have those functions written, I'd like to add them to proper big programs and that means XMM.
The problem is that many of the things I'd like to try out don't have the drivers yet (sorry about the huge list of requests above!). Eg - it would be cool, having demonstrated a movie player in Spin, to quickly put one together for C. I'll bet it would be quicker, for a start.
XMM and high res video is different to existing code in that there is no hub ram left, so that guides program development possibly down a different path.
In an ideal world all drivers would all be written.
So, what I am thinking is a generic way of adding custom cog code. The idea is that you have 2k of cog code, and you have tested it already with its associated spin driver. You then take the spin code and translate it to C. You take the cog code and simplify it to the very simplest interface to Spin/C code, which I think would be one Long. Define bit 31 as a flag to send or receive data, bits 24-30 as a command letter (A,B,C,D etc), and the other 3 bytes are for data.
Say I have my custom code. I can load it into the cog in several ways, either from Kyedos, or as part of the C load process, or from within C code. Regardless, it is now sitting in a cog and running and waiting for a command.
In the C program is all the glue code that makes it simple to interface. Thinking XMM, things become simpler, because I might load up a video driver and tell it that it has a buffer in hub from location 4000 to location 23200. Because I know that in my code, I can then load in another cog binary, and I can tell it that it has access to hub buffer location at 23200 to 24200. I can even calculate that in my program.
The clever bit about XMM is that there is no complicated and unknown sized spin/c code in between those buffer locations. So the compiler does not have to worry about that at all.
Where this leads to is a very simple concept - for 8 cogs you just need 8 fixed locations in hub ram for communication to C. Say you pick the top 8 longs in hub ram (are they free?). Cog 6 is running some code. I know what that code is because I loaded it earlier from my own C program from a binary on the sd card. I also know how to talk to that code using a variety of C functions, because I copied and pasted them from a C library. So the one long interface might be hidden to me with some more complex C code eg void New_Frame(), or I might choose to talk to the cog directly. In any case, I put one long in that fixed hub location, set bit 31 to high, put a command 7 bit ascii character in there, and put in 3 data bytes. The cog is waiting for a command, goes off and processes it, and then indicates it has finished by setting bit 31 low.
I'm not even sure you need to register the cogs in the catalina registry.
It could lead to some different ways of coding. For a start, a demo bit of code would be even simpler than the plugin demo. The very simplest demo would be scanning a hub location for a byte where bit 31 is high, when it gets one, it simply sets that byte low. That is only a few pasm instructions.
But it can be more complex too. With XMM there is no cost to having lots of redundant code. So one could have a whole lot of C code that is the equivalent of the SD driver spin code, and a whole lot of video support code as well. One could load in just the 2k binary into a cog for the sd card, use it for a while, then throw it away and load the video driver 2k binary into the same cog. You could emulate having 50 cogs if you like, by loading and reloading code all the time. If you ran out of cogs and were really desperate, you could disable the keyboard while reading the sd card. Or blank the video while reading the sd card.
I'd like to give this idea a try by taking some existing combo spin/pasm code, changing the spin to C, modifying the pasm so it listens for commands (some is already like this, eg Cluso's ram driver, and Kye's sd card driver), and then producing a C 'copy and paste' function, and an associated 2k binary blob of code.
So my request is - could I have 8 longs somewhere in a fixed location in hub ram?
Hi Dr_Acula,
Your post raises some points that will take me more time to address than I have now (I'm at work at the moment - I will answer in more detail later).
However, the main thing you seem to be asking for is exactly what the registry is - i.e. 8 longs reserved in high Hub RAM to be used for communications between cogs.
The Catalina registry just adds some frills to the basic "8 longs" concept because one long is not usually sufficient for practical communications purposes, and also because you should not have to rely on hardcoded knowledge of which cog is currently runnning what.
I documented the registry in the plugin tutorial document - but form your comments, obviously not quite as well as I believed I had!
Ross.
I'm in the process of trying to simplify it down to single button presses in an IDE, and in doing so, I need to understand the inner workings in order to make it possible for someone to use who does not understand the inner workings.
I think I will have an option on the IDE that puts two richtextboxes side by side. One will have the C code. And one will have the PASM code. I envisage debugging both at the same time. I'd like a "Simple Plugin" button that pastes the appropriate C code into the C window, and also the appropriate PASM code, much like the New button pastes a complete working 'Hello World' into the code window.
Behind the scenes, the PASM code will be compiled separately (I've yet to work out exactly how, maybe with some absolute minimalist spin code that we know the length of so it is possible to strip it out and have just the cog binary). Then download that binary code using xmodem to the sd card as a .bin file - or maybe .cog or something. Then download the C program and run it. I'd like the C code to provide all the methods to talk to that cog, and to be able to load that cog and unload it as required.
In pseudo code, I'm trying to think of the absolute simplest pasm code to demonstrate this:
start
get value from hub location x
if bit 31 is low then
or with 10000000_00000000_00000000_10101010 ' new data and flag set
put new value back to hub location x
endif
goto start
But, like you say, if this is already done that would be great.
I'll read the tutorial again to work out the confusing bits. I presume this technique is only going to be applicable to XMM solutions?
Maybe I'll write the IDE code with the expectation that it is possible to have a demo program, and that can be added later? There are some things I need to do, eg write code for the binary blob download, and write all the New/Save/Load/Download code for the PASM IDE.
To answer some of the details in your last mega post ...
This type of facility will be available in Catalina 2.8. I have added the Parallax Graphics object, and that also required me to expose the bitmap location (which was not required for the Text objects). So a C programs can now not only use graphics, but also maniuplate the bitmap directly - which means that doing in Catalina what you did in SPIN (i.e. to show movies) will also be just a couple of lines of C code. You already can. It's just different with C than with SPIN. With SPIN you almost always have to distribute your programs as source, since every user will have to modify the code to suit their particular platform. With Catalina you would use libraries to achieve the same thing, and these can be distributed in compiled form, since one of my main goals with Catalina is to make all C code (including libraries) completely platform independent. All the platform dependent differences are wrapped up in the Catalina plugins and targets, which form a Hardware Abstraction Layer (HAL) in much the same way an operating system kernel does.
The targets are mainly about differences inherent in using the different memory models, and the plugins are mainly about hardware differences, or to allow for code that needs to be implemented in PASM (e.g. for speed).
But it takes a different mindset to use this technique than to use SPIN, and requires you to "factor out" memory model and platform dependencies into the targets and the various plugins.
You should ignore the complexity of the standard targets, and concentrate on the explanation (and working example) given in the Catalina Unplugged thread - much simpler!
The style of programming you are talking about is well suited to hobbyist use, but (as I mentioned before) C requires a different style and mindset - it is not really conducive to this type of "interactive" programming. Attempts in the past to make it so have mostly been unsuccessful (do you remember Borland's "Turbo Pascal" product? - this was a great success and is still going strong in various new incarnations such as "Feee Pascal"). But "Turbo C", which tried to do the same thing for C really didn't really ever make it - not because the product was bad, but because C is not so well suited this type of programming).
To program well in C you need to design your program up front to be well structured and modular. It is not a language well suited to "discovering" functionality by reading code, and then "tweaking" here and there to make it work.
While C is a useful language to have available on the Propeller, and is also a necessary language for professional use, I don't claim (as I once did) that C is a good choice for a hobbyist language - for precisely this reason. I'm so used to professional programming, it took me a while to realize that the needs of hobbyist programmers is different.
As you point out - most OBEX objects are a combination of PASM and SPIN. Often, the real complex stuff is done in PASM, and the "glue" or "interface" code is done in SPIN.
In C, much more of the code is done in C itself (quite often, all of it). In that sense, C is more like a platform independent assembly language - and if PASM was the only language available for programming the Propeller, then professionals might still use the chip, but hobbyists would not do so.
But even so, not everything can be done in C (just as not everything can be done in SPIN). But the consequences of the HAL architecture are that there should really be two distinct types of Catalina C object:
- if the functionality is platform dependent it would be a C program, or a C library
- if the functionality must be implemented in PASM, or is platform or hardware dependent, it would be a plugin.
While the HAL simplifies many things, it can complicate the place where the C program and the plugins have to be tied together (i.e. the target). This means that if loading the plugin requires knowledge of the memory model, then a target that shows how it must be loaded and initialized should also be provided.Michael park is doing some work on Homespun that may simplify the way targets are currently constructed - and I also have some ideas of my own that I will implement when I get time. At the moment they are a bit mesy, and are not really well suited to the type of "plug and play" which you would need.
Good idea. But be aware that few people learn C as a first language. Teaching C is generally more difficult than teaching other languages.
In an ideal world the Propeller would have more Hub RAM!
It's not quite that easy - as Jazzed is finding out with his 32mb SdRam driver, the XMM load process really gets in the way here. The load process Catalina has to go through to get a program that may consist of several megabytes into a chip with (essentially) only 32kb of RAM would make doing a double reverse sumersault with a triple pike and twist into a glass of water look easy!
As I mentioned before, what you are describing is the Catalina registry.
As described above, you don't need anything other than the registry.
What you are describing is essentially the process of integrating a new plugin into Catalina - i.e. you would be taking off from where I finished up in the Catalina Unplugged tutorial (i.e. with a plugin that can be loaded but which doesn't do much) and actually adding some useful functionality - written in PASM - that can be peformed by the plugin and which can then called from C. That would be great!
By the way, I've just been through exactly this process with the Parallax "graphics" plugin - you may want to wait till Catalina 2.8 is released, and then compare the modified graphics Catalina plugin with the original Parallax graphics OBEX object. The code changes to the object itself were quite trivial - but (as I hinted before) figuring out how to load it - especially in an XMM - environment is still quite tricky, because this particular object has to interact with another object (i.e. the TV driver) via Hub RAM.
Ross.
I have read and reread the .pdf. We are very much on the same wavelength. I would have designed it from scratch in a very similar way. The registry is perfect for this.
Ok, now to the practical stuff.
First - not everything is in the .pdf. The meaty stuff is in the readme files. I found those and read them.
So - practical things. I unzipped the zip to a folder c:\program files\propeller\plugins And once it was unzipped I read the readme files and I think it needs to be in a different folder.
There are two sub folders - custom and custom_demo
In custom the readme has an instruction
catalina -T "C:\Program Files\Catalina\custom" test_leds.c
so I think that I need to move the custom directory to c:\program files\catalina\custom
but in the custom_demo folder the readme says
It is assumed to be installed in the following
directory: C:\Program Files\Catalina\custom
So do I copy all the custom_demo files to custom?
I'm not so good with command line programs. So I've automated the IDE so it does things like the startup batch file including the path as part of the compilation. One click compile and all that.
I want to stick to the catalina conventions, and I'm a bit confused about where the C plugin programs actually reside. In demos, or custom or custom_demos. I guess custom for the moment.
I'll get there. Please don't mind my rambling.
First step is to get the unchanged LMM demo to compile and download.
C:\Program Files\Catalina\custom
and
C:\Program Files\Catalina\custom_demo (note that the document says custom_demos - but that's a typo)
No - one is the target folder (custom) and the other contains the demo programs that use that target (custom_demo).
You should compile the programs in the custom_demo directory, and the -T option tells Catalina to use the target in the custom directory.
Ross.
A general question - do you think there ever might be a need for a slow simple XMM version using a serial ram chip. I know it would run at least 10x slower, but nothing could beat it for simplicity. It would be easy enough to solder that chip onto a demoboard for instance. And then people could get a taste for XMM and if it was too slow, could move to other ram solutions?
Another question. With pasm code, there are the 'included' ones and the 'plugin' ones. Is it possible to treat an included one as a plugin?
By way of demonstration, say you booted up with the LORES vga driver, print a signon message, and is it then possible to load the HIRES driver into the same cogs? Then print another message? Then wipe that and load a graphics driver?
I think you already have pasm objects for each of those - if this can be done what might the code look like?
Addit: I'm confused again.
I need some help understanding LMM and XMM. In the demo folder are three files ending in .s These appear to be pieces of a pasm program that get built into a single program, and I see similar code in Catalina_LMM.spin. What builds what? Is the .spin program created from the .s programs, or vice versa? Which one do you edit to add your code?
But maybe that is going off on a tangent I don't need to look at. LMM looks very complicated, and I presume there is complexity because you have a program running in hub ram and somehow you need to get some binary data into a cog.
The one that interests me more is XMM. In a general sort of way, this should be simpler because once XMM code is running (ie a C program), the first thing it could do is wipe virtually all the hub free and the program would keep running.
I'm not entirely sure how an XMM program gets data into a cog, but I presume the steps involve reading data off an sd card into low hub ram, then running a coginit instruction and then somehow returning control to the XMM program.
I'm thinking of a plugin that is much simpler than the three .s programs. Do you really need to have all the LMM jump tables in order to run a plugin?
For instance, the simple concept I'm trying to build is a tiny pasm program that accepts a value, adds one, and returns it. For this, I need some pasm code. I might also need the compiler to 'write' some pasm code to tell it where the registry is - I'm not sure about that. Maybe I can just write my pasm code so it always talks to registry 6. Or maybe a config file is linked to my code.
In any case, my pasm program might only be 6 lines long.
I know the generic concept is that the plugin layer is hidden from the user, and is platform independent, and that is all fine, but I have to be blunt and say that the problem I have is that I have this long wish list of plugins that do not exist yet. They only exist in the obex for spin. So - either I request that they get written, or I write them myself. I'd like to do the latter because I think that will be the simplest solution.
So, what is the absolute minimum cog code one could have that can talk to C? Just give me one service if necessary - eg service 4. Do you need all that LMM jump code?
As I think XMM is the only place that loading and unloading cog code is going to work, is it possible to think about minimalist cog binary blobs of code for XMM only:
1) Does a binary cogject need to be compiled as part of a catalina compile or can it be compiled completely independently?
2) Is it possible to load 2k of binary data from an sd card into hub ram and return control to the C program?
3) Is it possible to move that 2k of binary from hub ram to a cog and return control to the C program?
4) Can the C program have code that registers the new cog (or does that registration have to come from within the cog code itself)
If it is possible to do 1 to 4, then you ought to be able to talk to the cog code using Catalina_Common.
But to answer some of the above myself, I see things like
and that suggests that the cog itself is doing the registering. If so, does this not use valuable cog code space? ie all this
I've gone round in circles a few times, but a common problem is when existing obex code is packed tight and there is one long free. So you start adding the catalina stuff, and it runs out of space, then you delete some of the working cog code and then nothing works and then you delete another bit and then you try to find a bit that is least important, and finally you give up because it is too hard to actually debug anything. This is where I get stuck anyway.
So - thinking minimalist
that can go. If something is illegal then it crashes. Don't pass illegal things.
And the service requests are great, but you might only need one so we don't need the table for the moment
and we probably only need one of these three
and for a super simple demo, replace this with something that just doubles the long or adds a constant
and maybe some of these are not needed
So maybe that can all be reduced to something that fits on one screen so the code is as simple as possible.
And then I need to think about doing this in XMM rather than LMM as the demo is written for LMM.
I hope all this makes sense. Sorry about rambling on, but hopefully it is more useful to talk through the problem in more detail rather than just saying "it won't work".
I was going to do precisely that for the C3 - but when I had the time I didn't have the board. Now I'd have to say it's not high on my list of priorities, but I would be happy for some one else to do it.
No - the included ones are LMM PASM, whereas the plugin ones are normal PASM. LMM PASM executes about 1/4 the speed.
Yes.
See the demo custom_demo example complex_test.c
The .s files are compiled assembly language versions of C programs. This is sometimes usefl (e.g. when building libraries) and can be done using the command line option -S. For example:
Correct - the Hub RAM is available for other purposes (e.g. graphics dfisplay buffers). However, keep in mind that the -x2 (SMALL) mode also uses Hub RAM for data and stack space, and the -x5 (LARGE) mode uses Hub RAM for stack space.
It would be possible to run the stack and all data space in XMM RAM, but the results would be very slow. This is similar to the "serial SRAM" option - i.e. I have little interest in it myself, but others may find a use for it.
This is covered in the Catalina Reference Manual, page 83.
No - the jump tables are only required to be included by one object, and they are automatically included by the appropriate target startup file (e.g. lmm_progbeg.s). Actually, one day I will get rid of them altogether.
Have a look at any of the ".e" files in the 'source\lib\catalina' library - they are all hand-coded, self-contained LMM pasm programs that implement specific library functions.
Good!
For a plugin, see Generic_Plugin.spin in the custom folder (and remove everything except Service_1. For only one service, the serice request decoding logic can also be removed).
For an LMM PASM subroutine, see any of the .e files in source\lib\catalina.
No, you can also load and unload from Hub RAM in a simple LMM programs - see complex_test.c in custom_demo. However, I agree loading from SD card is probably only feasible in XMM programs.
Independently.
Yes.
Yes.
Yes.
Yes.
Yes - I use both techniques.
I've never found a program yet that cannot be reduced in size by one long without affecting functionality. Logically then, with enough work all programs can be reduced in size to zero
Yes - I've not looked at your code in detail but everything is correct in theory.
[/QUOTE]
I ramble enough, so I can't really complain if others do!
Now here is something meaty to get my teeth into.
Just to clarify, I'll be mainly using the small model for rapid testing of code fragments, and the XMM model for the 'real' program.
Now I understand the LMM vs XMM a bit better. If plugin ones are pure PASM - nothing complicated, nothing LMM, that makes things so much simpler. I've certainly used LMM eg in the CP/M emulation, but I think there is an even better model to test out, and that is to load and reload cogs with a wide variety of code fragments.
I'll check out the .e
Fantastic to hear all the pieces of the jigsaw can be done.
When I said 'only one long free', that is only partly true, in that yes, I can think of at least three obex objects that are chock full, but on the other hand, each does have its own internal comms system to its spin drivers. So in reality, one would be replacing those with the ones that talk to the catalina registry. It does still mean that one needs code absolutely as small as possible - ie as efficiently as the existing spin interface code (which varies depending on who wrote it).
Great that cogjects (is that the name??) can be compiled independently.
Just to double check - you can do the registration from within C. Does that mean one might need to call a tiny delay function in C when you load a cog, just to make sure it has actually loaded before you start talking to it?
This commented out line in the complex test - I presume you can do the same with XMM_DUM
Re It would be possible to run the stack and all data space in XMM RAM
Ballpark figure for graphics buffers might be 20k. So there is 12k left.
The stack would be fine in hub - is that likely to ever be more than 1k? Possibly less?
But what about data space. Do you mean things like lists of data that one has in C. Or do you mean things like array space.
In XMM, if I dimension myarray[5000], is that stored in hub or external memory?
Ok, what I think I need to do is try to create the smallest pasm program I can. Then post it here and see if anything has been left out.
Yes, you can register it from C. I doubt a delay would ever be necessary, but you can certainly put a small one in if you're worried.
Actually, the symbol is misleading - LMM_DUM is just the symbolic name of the plugin type - I settled on the LMM_ prefix before XMM came along - the name of the plugin type will be the same in all cases (i.e. LMM, EMM, SMM, XMM).
The line is commented out merely because the plugin registers itself - I intended it to show how to do it from C if that were not the case.
Sometimes, yes - all local variables are allocated on the stack, so it may get large for a complex program.
When using the SMALL XMM memory model (-x2), all data is in Hub RAM.
When using the LARGE XMM memory model (-x5), local variables are in Hub RAM, but variables declared with file scope (see below) are in XMM RAM, as are all dynamically allocated variables.
As above, with the SMALL XMM memory model, this array will be in Hub RAM. With the LARGE XMM memory model, this array will be in XMM RAM if it is declared with file scope, or in Hub RAM if declared at the local level.
That's probably confusing - here is an example:
Here is a reference you can look up on file scope. This is important in Catalina, since plugins can generally only access Hub RAM (i.e. they do not have access to XMM RAM) - this means that all variables used to communicate with plugins must be in Hub RAM. When using XMM, this can be done by making such variables "local" to the main function (and passing a pointer to them to anything outside that scope - when you see the new graphics plugin you'll see a working example of this - the graphics plugin works in both LMM and XMM modes, but it need some special "smarts" (which are all internal and invisible to the user) to make things work properly when used in XMM mode.
So for big data arrays keep those in xmm. And for variables that are being passed back and forth to cogs, keep those in hub. I'd anticipate lots of comments in my code to make it clear why things are being done the way they are.
Ok, this is an awful thing to do to a working program - even 'do_stuff' has gone, but is this the smallest pasm plugin one could have?
The spin part is the same. The 'entry' part - I gather that could be done in C?
get the service
if nothing, try again
if something, shift right to get the number
jump to end service and process it
Can it be simplified any further? You could maybe not bother getting the number but one would usually be passing a few numbers.
end_service - does that return in the second long?
To compile this as a standalone file and make a binary, do I need to put Catalina_Common in the same working directory?
RossH i read that you are (nearly?) finished with the Parllax Graphics Object and i thought when are you planing to release it.
I could really need it by now
Please let me know.
Greetings from Austria
Wuut
Hi wuut,
Yes it is finished and it is 100% functionally equivalent to the Parallax Graphics OBEX object - I'm just not happy with the speed of it yet.
When is is used from an LMM program, it is faster than the SPIN version - but I anticipate it will mostly get used from an XMM program in order to maximize the amount of Hub RAM available to use as graphics buffers. Unfortunately, at the moment when you use it from XMM RAM it is significantly slower than the SPIN version.
I think I can improve its performance over time, but I know you need it ASAP so I will release an "alpha" version tomorrow.
Ross.
Yes - if there is a reason a variable has to have local scope, it is worthwhile adding a note to it.
You cannot remove the following lines, which set up the request pointer. Otherwise the plugin will not know the address of it's request block:
You may be tempted to try and pass the request block address directly - but in general you cannot since you first need to know the cog - which of course you don't know until you have started the plugin! So you have a "chicken and egg" situation. Not quite. You have removed the plugin registering itself, but the SPIN code does not register it. A plugin must be registered where it is started, since that is the only place that knows the cog it was started in. In this case the plugin is intended to be started from SPIN, so you need to register it in SPIN. Also, you no longer need the parameter, so your SPIN Start method should look as follows:
[/QUOTE]
Yes, in general a result is returned in the second long of the request block, and the fact that the service is complete is indicated by zeroing the first long in the rquest block.
No - this program should be put in the targetdirectory (i.e. custom). Call it something new like Catalina_Min_Plugin.spin. Since we modified the Start method, in the same directory you will need to update the target file (in the custom\input subdirectory) called lmm_default_Input.spin.
Here is what that file would look like if you called your new plugin Catalina_Min_Plugin.spin, and decided to use the symbol MIN_PLUGIN to enble it (i.e. by adding -D MIN_PLUGIN to the catalina compilation command):
I'll need to take this one step at a time as it is very confusing.
1)
So the first four lines must be included. What about the other six? What do they do?
2)
The common definitions. I gather these are things like which pins are used for the sd card.
If we are building an absolute minimalist plugin that is to be compiled to a separate binary file, and which will sit on an sd card to be loaded as needed into an XMM program, how many of those common definitions do you actually need?
eg - if our independent binary just adds one to a value and returns it, does it need to know what vga pins are being used? Ok, yes, down the track it will, but there are so many things to understand here that I'd like to take it one step at a time.
I tried removing common : "Catalina_Common" and it got unhappy about PTYPE = common#LMM_DUM not being defined. So I guess there is some link between our minimalist binary and the definitions.
The tiny plugin looks so simple in itself, but it is referencing the common definitions and that is full of #ifdefs, and thus I presume needs homespun or BST to compile. I know this is going slightly off on a tangent, but is it possible to build a simple binary that is just one program with no references to other programs and which has no ifdefs and thus could be compiled with the proptool.
Or are there so many common variables one has to keep in one place that this would not be sensible?
The first four lines calculate the address of this plugins given the registry address (passed as par) and the cog it is running in.
The next two lines ensure the first long of the request block is zero, so the plugin won't accidentally execute an erroneous command if that location has not already been cleared (which may be the case if we are loading a new plugin in the same cog that a previous plugin was using).
The last four lines update the registry with the type of plugin running in this cog (i.e. "registering" itself).
I don't know - it depends on the plugin itself
Why remove it?
You are probably looking at the Catalina_Common_Input.spin file and wondering how the symbols used in the #define statements get set if you don't want to use Catalina (e.g. you just want to compile a SPIN file using Homespun).
This file can be processed into an output version (i.e. Catalina_Common.spin) which is left in the target directory, and which is suitable for use on the DRACBLADE by Homespun by executing a command such as:
You only need to do the catbind command once. Also, note the specification of the target directory on the Homespun command line. If you don't want to do this, you can instead manually edit Catalina_Common_Input.spin into a suitable version for your own use and copy it to your local directory - I do this sometimes when testing.
I find keeping everything platform dependent in one file very useful - but then I routinely test programs on up to 10 different propeller platforms, whereas I suppose most other people use only one.
Ross.
Because it adds complexity and complexity means I don't understand it. Sorry about pulling your perfectly good code to bits!
I would not have found the catbind command but that has done something to the file, though it still has a number of #ifdefs all through it.
I'm trying to think in a very simplistic way, and in the common file are a huge number of constants that I don't think I need. Hidden in amongst those are some important ones, but I can't find them because there is too much other information.
The super simple plugin that just adds one to a long is not going to be application specific. Once I have reduced the code to this I'll put that in the IDE as the simplest example, then I can start building it back up again, adding things and also adding these to the IDE, and that will no doubt involve adding some platform specific code.
But for the moment, I'd just like a tiny binary file that ideally is one single .spin file, with no dependencies and can be edited on the prop tool.
Something is wrong with my path definitions I think. It failed to compile and catalina_common is in the target directory (see the last few lines).
I copied the common file to the local directory. Now it gives a different error:
so is LMM_DUM a more recent addition to the common file? I checked in the input directory and the file was created 7th November.
This is the line that is causing the problem so is it possible just to find out what the constant is, and to add it in the spin file for the moment?
My fault - if you are using the custom target, you should first say:
Alternatively, you can specify the target on each catalina/catbind command line - e.g:
The Catalina_Common.spin file in the standard target directory (called target) will not have LMM_DUM defined - that is only in the custom target directory (called custom).
By the way, all the catbind program does when you specify the -p option (p for preprocess) is preprocess the various input files to include the appropriate #defines from the command line so that they will compile.
Ross.
Addit:
For this code in the 'entry' section
is it possible to move it while it is a constant. Would that save one line of code?
More experiments. By moving everything into one spin program (see at the bottom of this post) it is possible to see the flow of the program more easily, and also to quickly try to compile/edit/recompile in the proptool. I've added the registry constants, and commented out the link to the common file.
In doing so, I can start to see how this works. But the compiler gives an error on this line
And the reason is that 'registry' is now a constant. So I'll need to pick another value (I changed it to 'registryvalue' and it now compiles to a 220 byte program). But this brings up another question. If you have a total binary pasm object, how can you have any spin at all? How can you have PUB start (registry, parameter) : ok | request, cog. Surely that implies that the spin interpreter is running?
I was thinking one answer is that you build the pasm code linked to some spin code to determine variables, then discard that (? a fixed length) and just take the pasm DAT. But this does not make sense when you have spin methods like
PUB SendInitializationData(to_cog, data_1, data_2)
Advice on this question would be most appreciated.
The SPIN interpreter always runs during initialization, so you can use SPIN to do the job for any plugins loaded at that point. After that (i.e. once Catalina has been launched) you can also load a pure binary "blob" - essentially just the DAT section of the plugin - but naturally you cannot use any of the SPIN methods since there is no longer a SPIN interpreter running. This is when you either have to have a self-registering plugin, or do it from C after loading the plugin into a spare cog.
You need to study the "complex_test.c" example in more depth, since it does both these things - i.e. it loads the plugin once during initialization, and then the C the program repeatedly unloads it and then loads it again from a binary blob. You need to study not only the program itself, but also the build file, because that is what generates the binary array which contains the "blob" which is the plugin DAT section.
Ross.
Yes, that is what I want to do.
Can you point me to the actual DAT section? See below, this is complex_test.c, this all seems fairly straightforward, but what is it actually loading, and how was that binary blob created? plugin_array.h is a blank file.
In the batch file is this
and so is that being created from catalina_plugin.spin in the custom directory. That is the file I am editing, and it is the file that has all the extra spin code. How does that get converted to just the DAT pasm bit?
The spinc command is what builds the binary blob from the binary compiled by Homespun. If your blob is empty, something went wrong. Please post the output of running build.bat.
Ross.
It all compiles fine.
This is your original file. Maybe I can try to get that working first, and then look at shrinking it down.
Can you explain a bit about what is going on. I see this part compiles what I think is the 'standalone' binary.
So that is presumably the plugin. What I can't understand is how this plugin can contain both spin and pasm code.
Maybe I understood this wrong, but I thought a plugin was a binary file that loaded into a cog and the support code was in C, not in spin. But the above is a hybrid of spin and pasm, so what is running the spin part if you don't have the spin interpreter running on that second reload?
You are right - a plugin is just a PASM program, which can be represented as a pure binary "blob". Creating such a blob from a SPIN file (so that it can be loaded from C) is the job of the spinc program.
But plugins are typically loaded and started during the program initialization phase by the Catalina target, so each one contains a Start method intended to be called by that target. A plugin may also contain other SPIN methods, but these methods can only to be used by the target - they cannot be used once the C program starts, since the target is terminated once the Catalina kernel is started.
Sometimes (e.g. as is the case in the complex_test.c program) there will be several ways the same plugin can be loaded and started - i.e. once from the SPIN target using the Start method in the plugin itself, then again later from C.
If the plugin does not register and initialize itself, then this will have to be explicitly done in both places - the difference is that the SPIN target can use SPIN methods in the plugin (as well as the SPIN support methods contained in Catalina_Common_Input.spin intended for just this purpose), but the C program will have to do it all from C using the C support functions defined in catalina_plugin.h.
To summarize - the guts of each plugin is always a pure PASM program which is represented as a DAT block to a SPIN program, or as a binary array to a C program. Everything else you see is just frills intended to make the plugin easier to load and initialize.
Ross.
The sort of plugin I am interested in is most definitely not to be loaded at startup. It will be loaded some time later by the C program.
Hence, my understanding is that it cannot have any supporting Spin because the spin interpreter is no longer running.
The complex demo example loads both at startup and then is being reloaded. I need to understand more fully what is going on, because I suspect that the first load is doing the registration.
For a pasm cog load, done by C from within the C program, there is no ability to do registration with spin code, is that right?
but the C program will have to do it all from C using the C support functions defined in catalina_plugin.h.
It is great that such functions have already been written. I think I understand those from the C side of things.
But I still cannot understand the pasm part. To my way of thinking, if you have a combination of spin and pasm, and you load that at startup, that is fine, but if you unregister that cog, then reregister it, spinc will need to somehow strip off the spin part and just produce the pasm binary.
Is this what spinc does?
and, how do you pass values to code in a cog when the code was written expecting to be passed variables from spin?
To summarize - the guts of each plugin is always a pure PASM program which is represented as a DAT block to a SPIN program, or as a binary array to a C program.
Is there an example of how to create just the guts.
I hope these questions make sense.
[/QUOTE]
I might have code like:
This example makes it look onerous and inefficient - but in fact it usually isn't. Yes - that is precisely what complex_test.c is intended to illustrate. I could make it simpler by using a simpler plugin, but the mechanics of it would remain the same.
Ross.
I'll take your word that this can be done, but I'd still like to be able to do this myself.
To explain another way. Here is the source program
and then it goes on with the pasm part.
If you compile that to a pure bit of pasm, ie something that fits into a C array, then surely the Pub start has to go? This is spin, right?
So if it is not included, should it be possible to write a shorter version of the program that does not have the PUB start at all.
Wait till it is registered - you say that is very quick anyway.
Send initialisation data - sent from C, right?
Wait for request - C does that?
and the DAT section has self registering code.
So if I compile that, where is the DAT section? At the beginning. Or n bytes after the beginning, ie after the compiled spin code.
Perhaps it might help to explain more what I want to do. See the attached board - I just finished soldering this up and testing it.
This has a parallel port, and it also has an LCD display. But in simple terms they can just be considered 16 generic digital outputs and 8 generic inputs.
Unfortunately any driver for these uses the same 12 pins as the external ram chip. And if a program is running in Catalina in external ram, those 12 pins are spoken for.
I have taken a look at the xmm driver for catalina, and it might be possible to use some #ifdefs to add specific dracblade driver code.
BUT - a) this makes your code more complicated and b) one might run out of cog space and c) even if one doesn't run out, you might want to use this cog space for some code you write in the future.
There is a comment you made earlier which I found interesting talking about locks. I gather this is a way of making sure cogs don't fight for pins.
So I am wondering if one could add a simple one line #ifdef for the dracblade to the xmm driver that tests for a lock periodically, or between each C instruction, or when a timer clicks over?
Then load a cog with, say, an LCD driver binary, or a Parallel Port binary. Then from within C, send an instruction and a byte (should be possible with one of the 4 instructions you can use). The cog goes off and sends a message via locks, or maybe via some other mechanism, that it wants control of the lower 12 propeller lines.
Eventually, the C XMM driver tests for this and hands over control. The LCD driver cog sends out its byte, then unlocks the XMM driver. C then keeps running.
The idea is that these bits of cog code would be quite small and simple. The don't need any supporting spin and they can be loaded and reloaded many times via the C program.
More importantly, it means I won't be bugging you so often for changes to code, because I'll be able to write my own code (hopefully!).
Sounds entirely feasible.
Ross.