|
| | | | M A N U A L
| | | | | SOURCE: qdev_manual.txt 1.06 (11/09/2014) English 1 Getting started 1.1 Official ASCII logo 1.2 Background/history 1.3 About this manual 1.4 Foreign languages 1.5 How to participate 1.6 People involved 2 Installation notes 2.1 System requirements 2.2 Library flavours 2.3 Step by step install 3 Compatibility notes 3.1 LibNIX and nativity 3.2 QCLI extension tips 3.3 QCRT0 startup code 4 Library usage 4.1 Out of the box use 4.2 Build control macros 4.3 Library wide macros 4.4 Own library loader 4.5 Order of CLI things 4.6 Getting help on API 4.7 Coding shortcuts 4.8 How to report a bug 5 Complex subsystems 5.1 File Descriptor Relay 5.1.1 Supported packets 5.1.2 Private MsgPorts 5.1.3 ACTION_LOCK_RECORD 5.1.4 ACTION_WAIT_CHAR 5.1.5 Pipe limitations 5.1.6 Address checks 5.1.7 Relay policy 5.1.8 Rollover files 5.1.9 Status explained 5.2 Link Point 5.2.1 Supported packets 5.2.2 Packet route 5.2.3 Context behaviour 5.2.4 Limitations 5.3 DOS device unmounter 5.3.1 Clean method 5.3.2 Shield method 6 Obviously mumbo-jumbo 6.1 Internal bases 6.2 Cached bases 6.3 IEEE arithmetics 6.4 Modular segments 6.5 A4 register issue 6.5.1 Callbacks/Hooks 6.6 Undocumented routines 6.7 Local Base Support 7 FAQ 7.1 Questions and answers 7.1.1 Package related 7.1.2 Programming
1 Getting started
1.1 Official ASCII logo
# http://qdev.sdf.org/
##
## oooooooooooooooooooo
Powered by ## 8 888
## 8 oooooooooooood"888
## 8 88888888888P" 888
88888888 ######## ####### # # 8 888888888P" 888
88 88 ## ## ## ## ## ## 8 8888888P" 888
88 88 ## ## ######### ## ## 8 88888P" 888
88 88 ## ## ## ## ## 8 888P" by BCD 888
88 88 ## ## ## ## ## 8 8P"oooooooooooo888
88 88 ## ## ## ## ## 888888888888888888
88888888 ######## ######## ########
88 oooooooooooooooooo
88 d ..ooo888888b
88 Quick Development Suite d ..odo88888888888b
88 doooooooooooooooooooooob
88 for the Amiga computer
8
If you feel that mentioning 'qdev' as per program that uses its features is
the right thing to do then feel free to copy this logo and paste it in your
readme or manual. You can get rid of the computer emblem on the right if
you want to. Thanks in advance!
1.2 Background/history
At the beginning there was just darkness... Heh. At the beginning there was
a loader. Not a sophisticated loader. Just a tiny loader. The loader was
told to free the 'startup-sequence' from command line stuff, so that
things could be done low-level. The loader was verbose due to its own
screen which did not interfere with the Workbench screen. This way programs
were free to say whatever they wanted. That meant that whenever an error
occurred the user was to know that something failed badly(unlike in
'startup-sequence' where everything is redirected to dreaded 'NIL:'). It
all was great to a point where loader Creator found out that its creation
is a massive dirt ball and the code is so unreadable that further expansion
would be literally a one small step for man, everything but forward! Then
the idea did spring into mind. Why not to write a specialized library that
helps in building loaders? Creator was given an enlightment! "Holy fuck, i
must write that very special library and split all this mess across objects
and master the API and debug it and write a solid documentation as soon as
possible before i forget everything!" - he said. Yes, he was certainly very
excited. The work began in June 2010 on a sunny day after drinking fresh
orange juice. But something was missing. Yap it was the Nectarine demoscene
radio, so he tuned in and started a chat with Socrates to borrow on some
philosophy and stuff.
The concept was API as you understand it, but also the alternative meaning,
the acronym that expands to: Amiga Portable Independent, which divided the
API into three parts, so that many generic functions could easily be ported
to other platforms. The first group of functions to be implemented were
the ones starting with the letter I like independent(platform independent).
Then after establishing the bare minimum there was a time for A like Amiga.
And at that time due to great coincidence the Creator has met a person who
was also into home computers and did see that great potential in loader, so
he joined the project as a beta cowboy and eventually became human
Debugger! The Debugger was nothing like a computer debugger, he was way
advanced, he could load the code into brain, process it and find a bug
before the Creator was annoyed by the fact that he screwed something
halfway through. The cooperation was all fine and dandy. The library
started to grow rapidly and by the opening of 2011 it had about 70
functions(and a totally broken FDR which Debugger was trying to heal). One
day when breakpoints were all set and the porn was being ripped into XVID
the Debugger noticed that his depraved computer is starting to give him
smoke signals that the movie is definetly too hot for this CPU. It then
yelled through speech synthesis: "It is unfair that i go to hell for your
porn you bastard!" and eventually died from hell's fire ;-) . But that was
just an effect of trauma and a bad dream Debugger had after it happened.
A fitting tribute to poor machine that suffered due to human desire was the
dance of Burnt Chip Dominators at the grave of AMD's finest hardware...
The Debugger was sad, so he started to eat cookies and played Soccer Kid
a lot to kill that awful pain. Meanwhile Creator was studying so many books
on Amiga programming that eventually all progress ceased for a few months
and so he also started to eat cookies. After long pause the Creator was
asked to write a program that would eliminate duplicate lines in the text
files really quick. This is when 'hashlab' was created and unbelievable
thing happened the Debugger also wanted to know which hash routine is the
best, so the forces were joined once again. At that time it was mid 2011.
Now the team has realised that the library is shifting into universal. The
toolbox that had just 5 or 6 different instruments started manifest that
clearly. The goal? Oh yes the loader, faded a bit and the team focused on
functionality. This is the time when these tiny utilities really impacted
the library. The Debugger was an optimist, the Debugger was wrong there
were still too many bugs everywhere! It was no other way than to start
writing tiny progs that would actually put their work to a test. Testsuite
was there for a while, but it was a no-go. Although a good checkpoint in
general, the tools and dirty binaries won in the end. Problems were driving
function splitting and simplifying which caused function population to
grow and when it exceeded 100, the existing program flow monitor was
found too inefficient due to bad implementation. Creator had to go back to
books on assembly and to do freaky experiments with the compiler.
Debugger was still fighting bugs in hash evaluator, so they did not know
what hash will be the best for text filtering purposes. Creator tried to
hire 'pthreads' to switch between books and actually woke up in between
contexts screeming that there is no documentation what so ever! They did
drop current schedule and attempted to work on documentation. Again,
numerous bugs were squashed... "Why did i do that this way, any ideas?"
- Creator on it's own code. "What the fuck is this?" - Debugger overlooking
supposedly fixed code. In the end the team had to study the code all over
again to point out possible exceptions. The time was mercyless and
started to boost. It was practically the end of 2011 and the project was
begging for new extra powers as the team was bored to death and did not
even get back to what was interrupted. A crisis! Oh really? They were
puking all over the place with that longish scroll of autodocs. Then,
surprise. Somehow, the news about the library that is in the works got
spread(well, they were not hiding it but also not talking about it out
loud) and they received an e-mail from a company that basically wanted to
know what is this and if they can possibly put their hands on it. Debugger
was stunned, while Creator thought it is a joke. It was not a joke as it
then did turn out, but the company which heavily praises the Amiga is just
a bad sign... BCD explained briefly what the library is all about and how
much bugs it contains and how shitty this project is in hope that they will
just go away. And they did not.
The polite way of taking care of business was way in the past. Numerous
tricks were played on BCD just to gain access to the source code. In the
end Creator decided that a hobby project is to be a hobby project and no
hoax companies need ever to kill it. The case was closed. But then
question was asked whether it is okay to make this code GNU GPL compliant
in whole? A friend suggested that this would easily demotivate them and
the progress would eventually stop in a blink of an eye. The team took
that seriously and started to think on its own license that does not impose
restrictions on users and programers, but totally forbids corporate use.
This may sound silly, but when one realises that there are parasite
companies all around the world who play dirty and turn valuable ideas into
garbage, then certainly it is no longer that funny. This is also the time
when the part P like portable was sorta neglected. Although some functions
were marked to be portable and at least GNU/Linux hosted OSes did compile
that stuff just fine due to personal reasons that did not get too far.
Debugger definetly took a break in the need to learn to the exams. Creator
was very thankful ("Thank you very much man!") for his time and the effort.
The 2012 was about to begin, the docs were nearly complete, the code shiny,
new batch of functions and tools on their way and the Nectarine radio still
on the air although relays tend to shrink and go AAC. Argh! But overall
Burnt Chip Dominators did what they planned. Well, sort of...
At present 'qdev' hides so much of low-level stuff in its stomach that the
library tends to look complex but fortunately though usage is considered
relatively easy and at the same time tries to address different needs by
the variety of departments. Currently things like: text manipulation,
text/data parsers, I/O tweaks, context enhancing, device interfacing,
virtual files, simple graphics ops, memory clustering, integer conversion,
feature rich debug subsystem, stream handlers, friendly little code
agents, inexclusive task exceptions, resident module creation, file and
directory ops, simplified user dialog creation, and many other things are
either possible with very little input or available out of the box. So how
to describe the 'qdev' today with its quite wide routine spectrum?
This does not sound like a difficult question really, but lets conclude. It
is simply universal and that is most probably what Amiga enthusiasts will
appreciate(BCD is hoping) when coding. Can it truly serve as a seed for
growing fancy, OS friendly loaders? Certainly, it will all resolve in the
year 2013 when the team will enter full-blown post-incarnated Workbench
distro project(yet another hope). And we will let you know about it as soon
as it will become usable. Cheers!
1.3 About this manual
Although this manual was written to document the library usage, it does not
cover every single aspect of it! The reason this file exists is that humans
are not computers and they happen to forget things. BCD is no different, we
have no idea what the future will bring, so we would like to protect basic
library guidelines from being lost in case this piece of software is to be
found after the years of possible abandonment by a fresh person, or by the
people who have simply lost.
As you may have guessed by the TOC, an absolute minimum of information was
served. It may seem insufficient, but really there are AutoDocs and other
papers who try to address issues not mentioned here. Aside detailed library
usage section there are supplementory sections. In 'Complex subsystems' you
will find lots of information on the most fuzzy library code. The other
section namely 'Obviously mumbo-jumbo' reveals some tricky internals that
may be important when interfacing this code with auxiliary code.
We just hope that the time spent on familarizing will be recovered by the
new great ideas and programs of course that the Amiga community will
possibly be given to run on their amazing home computers for another 30
years.
1.4 Foreign languages
We do not intend to translate any papers to our native languages nor other
languages. It is our belief that English language is the most universal of
all World languages, so that with a bit of effort anyone can understand the
contents. We sure accept translations though! If you would like to nativise
the docs prior to your first language then please contact us. We will be
able then to help you formulate what you do not really understand. It does
not matter you are green on coding, the key is you can translate stuff and
that certainly counts!
Please note that we cannot pay you for your work(we are hobbysts afterall),
but we can give a full credit for your contribution. Important! The use of
automatic translation engines as available on the web is strongly
discouraged! It makes no sense at all to put such docs in the distribution.
1.5 How to participate
Feel free to contribute some useful code of your's. The material can be
in Public Domain, copyrighted, licensed under your terms or better GNU GPL.
We will need your consent that you allow us to host your material without
major limitations. We give a choice whether the code falls under the API
and so can be altered by us, or if it must not be altered but a wrapper
should be created instead. In the latter case it will end up in 'supp/'
directory. Before sending us anything please contact us first, so we can
discuss "this and that" including the usefulness factor, typical
application, bottlenecks and the like.
Also, the ideas concerning some extra functionality are welcome. You need
not to be a coder to come up with something that will possibly rule the
universe ;-) . Additionally you can donate some money through PayPal if you
like what we do. Your name will be listed in the CONTRIB file. E-mail us if
you do not want to be mentioned! The cash will be largely used to cover the
development expenses. It is not meant to pay the electricity bill, but to
help in case something breaks badly and requires costly repair(Amigas are
rather old).
1.6 People involved
Currently the cult members (the Burnt Chip Dominators) are following folks:
Anatoliy Kashpirovsky (megacz )
Nicknames are all over the Internet. I like them more than real names as
the computer world appears to be something very artifficial even though
real in the end. My name is another nickname by the way (-: . If you did
read the intro then you know who brought this project to life. I seem
to be the main coder, a person whose crime is to spend hours writing
things and possibly hand them down to Tony who tries to fix bugs i
introduced.
Tony Krutchenko (kowalsky)
Hello. This is Tony speaking. From time to time I do support home
computer fans in various ways. The Amiga is actually a new thing to me,
not because I never used it or something but because I prefer 8 bit
machines. I have joined this project because I see great potential in it.
It is not only due to planned rebirth of `fancy' OS loader but simply
because this library allows basically anyone to be a coder and I like the
idea. When I have got the time I try to debug code thus megacz can
introduce new bugs ;-> .
2 Installation notes
2.1 System requirements
Currently the library comes as a static archive, and is available for Amiga
computers only. All objects were compiled in 68020-68060 compatibility
mode. The FPU is not being used directly thus it is not necessary. In order
to compile anything and link against the library following minimum hardware
requirements must be met:
- Any Amiga with ROM 37.300 and up
- At least 8 MiB of free memory
- CPU of MC68020 or better
Please note that library code often relies on ROM 39.xxx features, so
certain things may not work under earlier OS releases! A comfortable
development setup is more or less this:
- Amiga 1200 with ROM 39.106 and up
- At least 24 MiB of free memory
- CPU of MC68040 or better
As to developer environment Geek Gadgets are necessary. Depenedencies along
with links are listed in the 'README' file. All software is either GNU GPL
or freeware. As you may have guessed you will need GCC compiler and its
GNU friends. If you are really puzzled about GG as to how to configure this
beast or just too lazy to do that you can use 'GG-Lite'(a compact DE) that
was created with 'qdev' in mind.
2.2 Library flavours
This archive comes with three library branches that were compiled with
different compiler flags. They all share the same name, but sit in separate
directories. The major difference lies in code and/or data handling. With
so called STANDARD library no addressing problems will occurr, but such
binaries cannot be made resident. On the other hand with RESIDENT libraries
(including the 32 bit branch) programs can be pure, so that memory can be
used more efficiently, but addressation issues may appear thus coder must
really be careful about what he/she is doing. More details below:
lib/libqdev.a (STANDARD )
This is the standard library. It should work flawlessly with any startup
code around. Problemless operation under any circumstances can just be
expected. If the debug support is required then only this branch will do!
lib/libb/libqdev.a (RESIDENT )
This is the resident/pure branch. Programs can be at max 64k big, since
the addressing mode is only 16 bit wide! Programs that utilise this
flavour may work a bit faster and really have small footprint on memory.
When coding using this and the 32 bit braches remember about A4 register
copy/restore! Yes, you must do it manually!
lib/libb32/libm020/libqdev.a (RESIDENT32)
In theory this build allows 32 bit binaries to be resident. Afaik this
works in 'ixlibrary' model, so it should work for your binaries too. Note
however that such binaries will work on 68020 onwards. This branch was
not evaluated at all. Use at your own risk!
2.3 Step by step install
Installing the library and headers is not really a pain. All you have to do
is to copy them to your GG developer environment and that is it. You can
use your favourite file manager to do that or you can type these at the
shell prompt:
cd libqdev-?.?-bin-m68k/
makedir gg:include/qdev
copy include/qdev/ gg:include/qdev
copy lib/libqdev.a gg:lib/
copy lib/libb/libqdev.a gg:lib/libb/
copy lib/libb32/libm020/libqdev.a gg:lib/libb32/libm020/
One more important thing is the header search path. You have got two
choices. You can either create the CPPFLAGS global environment variable and
place there the path:
setenv CPPFLAGS "-I/gg/include/qdev"
Or you can request headers with 'qdev/' prefix in your code. Both ways are
OK and can be mixed. Alternatively you can do something like this:
cd gg:
makedir qdev-include qdev-include/include
copy include/qdev/ qdev-include/include/
delete all include/qdev
assign add gg: qdev-include
Compiler will find each header thanks to Amiga multiassign facility. At
this point you can compile and link against the library with no special
macros being defined. The default startup code will be used.
But the best part is that you do not really need to install anything!
Surprised? Package was designed in such a way that your projects can be
hosted in it. This way OS API can be used without U**X setup and its
primitives. Also, no knowledge about all special lowlevel macros is
necessary since everything is already prepared. In order to create your own
utility go 'user/template-x.x/' directory. Now copy it under the name of
your choice, modify Makefile and start working on your program. Then just
'make' it.
3 Compatibility notes
3.1 LibNIX and nativity
Due to the fact that GCC alone cannot produce immediate executables, a glue
startup code is necessary. One such startup code is 'crt0.o' which depends
on 'ixemul'. Obviously this is a bloatware in case nothing references its
features because each time such prog is to be started heavy BSD artillery
has to be involved. Fortunately though there is an alternative('ncrt0.o')
which utilises static link library called 'libnix'.
Thanks to LibNIX quick ports of wide variety of software are essentialy
possible and sometimes they even work. But that is not the point. What is
really useful about this library is its startup code that allows pure OS
coding! By principle 'qdev' is one such thing that is meant to be used in
that way thus most of its tools use the glue code at absolute minimum which
guides the program through lowlevel wizardy and leaves at the point where
things are straighten up.
This particulary means that no standard libraries are ever opened. It is
up to programmer to take care of that. This also means that STABS and/or
assembly inline affected code will most likely malfunction leading to a
crash just at startup! Therefore one must avoid linkage against 'libnix.a'
when coding natively or to make sure no objects are STABS/ASM affected nor
they reference features currently disabled in the startup code.
To summarize, when coding using OS API only you generally want your prog to
just takeoff so that you can do the rest your way. In such case use 'qdev'
own library loader feature called ___QDEV_LIBINIT_NOEXTRAS . You will also
need to construct custom command line and pass the arguments and objects in
the right order! I encourage you to go to 'user/template-x.x/' directory
and build the template yourself so you can see where stuff goes.
3.2 QCLI extension tips
QCLI is an extension that allows console only programs to be Workbench
launchable. It does that by testing CLI pointer in the Process structure.
If this pointer is NULL then the environment is considered improper and the
program is being relaunched in true CLI mode. This extension is implemented
as a macro in the 'qdev' library loader so that practically anything can be
Workbench compliant. To activate this feature one should supply macro
___QDEV_LIBINIT_CLIONLY with stream name.
While there is no serious problems with the extension itself there are some
restrictions as to when it is possible to use QCLI. If you did overlook
'ixemul' startup code and its magic helper blocks then you surely noticed
that it does deal with the Workbench startup message internally and does
not export anything like 'WBenchMsg'. And that is the main bottleneck, thus
QCLI and 'mem_getwbstartup()' function will only work with LibNIX startup
code.
3.3 QCRT0 startup code
As of version 1.2 of the package there exists special light-weight startup
code that allows to create standard and resident binaries that (aside of
OS 1.3 compatibility) do not require separate initial objects. Startup
autosenses if certain user objects were compiled resident and if so lets
one know (through a special structure) that he/she should take an action
and relocate this and that.
Due to modularistic nature of QCRT it is programmer who decides what
features will be utilised. This is because it is practically impossible to
guess which feature may be useful and which may become major hindrence in
the process of custom creation (ROM/resetproof objects seem a good example
here). Modules are special functions (usually wrapped in handy macros)
starting with the crt_#? prefix that are to be put in 'main()'.
4 Library usage
4.1 Out of the box use
One of the coolest things about 'qdev' is the ability to utilise the
library out of the box. No need to copy any components to your GG/system.
You can start developing programs as hosted in the 'user/' directory. And
believe it or not it was not our goal it just happened when 'tools/' needed
to be integrated. As a consequence, people who do not really want to get
into technical compiler details are handed preconfigured setup.
As stated earlier 'user/template-x.x/' is a starting point. Of course you
should also visit 'examples/', but with template you can just make a copy
of it and start your own project which will be totally native. One thing
to consider is the use of 'pdksh'. As the makefiles really define paths in
an U**X way and also make use of GNU software, the OS shell may not always
be the right choice.
At this point it is assumed that the GG is fully reliable and all necessary
installation was performed just right(taking notes into account). The
actual out of the box use is very simple. You will need to remeber only few
preparation steps each time you go into compiler and no surprise will ever
Guru in. The template can be built like this:
changetaskpri -10
stack 131072
sh
cd libqdev-x.x-bin-m68k/user/template-x.x/
make
And after the 'make' is complete you will be left in the 'Korn' shell. It
is advised to wrap first three lines in a script that will transfer you
to 'Korn' shell and thus let you safely fiddle with the compiler.
4.2 Build control macros
When we mention build related macros we really mean the contents of
makefiles who are used to construct compiler command line input. All these
things sit in the following files:
tools/Makefile.i
Despite the location all important variables and declarations are here.
With BINARYMODE variable you can determine how binaries are supposed to
be built(S, R, R32) package wide! As to NOSTARTFILES variable it does
include normal compiler option plus the define under the same name. This
is to allow 'qdev' library loader to be used where 'exec' and 'dos' bases
are already initialized. CPU and platform related flags can also be set
in this file.
lib/Makefile.i
Library includor defines everything that is needed to just build the lib.
Note however that the debug variables are also defined in here! With the
DEBUGIO attached somewhere in CFLAGS you can activate detailed program
flow and also activate your debug output. Consult 'qdev_debug.txt' for
more details.
4.3 Library wide macros
Library wide macros are those who trigger some actions in the main header
file. Most of them are explained in the 'lib/Makefile'. Certain macros have
effect on the library code while doing the compile, while other can be used
to change/implement something during own binary compilation. It is strongly
recommended to review the 'qdev.h' file and to actually get familiar with
these macros/triggers before making any use of them by judging on the name!
4.4 Own library loader
As the aim of the project is to nativise the programs as much as possible
we wanted to have macroized library loader that would hide all the library
initialization from programmer. This way one can define dependencies as
macros, store them in a separate file possibly and make just two simple
calls that will init and cleanup stuff. While this is essential when coding
with minimalistic startup in mind it also pays off when custom libraries
need to be opened where the full blown startup does not normally do that.
We know that 'libnix' has that magic library opener, that 'ixemul' can use
autoinits, that you can write your custom init part, but still this is not
as universal as we would like it to be, because it lacks easy exensibility.
Compile time library table creation as opposed to static library table is
superrior. No need to rebuild, no need to prepare special autoinit objects,
no need to edit the code at the C level. And if these are not arguments
then how about reduced binary size, preprocessor based table expandability,
lack of library related statements per base or the gain of extra features
inlined in the loader.
To find out more on this topic see the 'qdev_libloader.txt' quick manual
and also take a look at 'a-pre_xxxlibs.h', the loader itself. Satisfaction
guaranteed or your old way of dealing with things back! Heh.
4.5 Order of CLI things
Creating and compiling native Amiga programs with GNU C is not really that
difficult. Having LibNIX startup code and 'qdev' library loader you can
even create combos! To put it simply you can still use the library like any
other library and be forced to link-in heavy startup code and even heavier
'ixemul' kernel or you can create lightweight proggies with a bit of extra
CLI input who are as native as possible. Consider this listing:
1 /*
2 * template.c (program that does print a text)
3 */
4
5 #define ___QDEV_LIBINIT_NOEXTRAS
6 #define ___QDEV_LIBINIT_SYS 37
7 #define ___QDEV_LIBINIT_DOS 37
8
9 #include <qdev/a-pre_xxxlibs.h>
10
11 int main(void)
12 {
13 if (pre_openlibs())
14 {
15 FPrintf(Output(), "Hi there!\n");
16 }
17
18 pre_closelibs();
19
20 return 0;
21 }
As you can see the code(line 15) is surrounded by the 'qdev' lib. loader.
Such setup has one great advantage over classic library access, as you can
compile it in two ways(using two distinct startups). To produce plain Amiga
executable you would need to type this:
gcc -O -s -nostdlib -nostartfiles -Dnostartfiles /lib/libnix/ncrt0.o \
^^ ^^ ^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
1 2 3 4 5 6
template.c -o template -L/lib -lqdev
^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^
7 8 9
The binary will be something like 800 bytes and free from BSD API layer. Of
course you can compile it the standard way too,
gcc -O -s template.c -o template -lqdev
so that the binary will grow to something like 4 kilos and runtime memory
usage will increase even higher. Notice that our code is by all means 100%
native. It is the Amiga 'FPrintf()' and not the C library 'fprintf()'. With
that being said the first approach seems best suited. Lets now discuss CLI
input as numbered(especially beginners should read this):
1 - This is the code optimization option. Level of 'O' was selected to
build the binary as fast as possible and maintain all code checks.
2 - This option tells the linker to strip all symbols and/or the debug
information.
3 - Quite an important switch! What it does is to discard all automatic
library linkage. It should especially be passed when doing custom
compiles.
4 - This switch implies that no startup code nor its helper options must
be passed to the compiler.
5 - Very special define that plays a role in the 'qdev' library loader.
This must generally be passed whenever doing native compiles!
6 - Custom startup code. In this case this the LibNIX startup code, the
non-resident version. Important! Startup code must always be the
first object!
7 - Code to be compiled(or precompiled objects). At this point order of
these objects depends on what you are doing.
8 - Binary output. You can specify paths to static link libraries along
with libraries afterwards, so that all references will be resolved.
9 - The library itself and/or other libraries preceeded by the search
path. Note that in case of resident compile this should point at
'/lib/libb' - 16bit or '/lib/libb32/libm020' - 32bit.
Once you know how to do it, you will be able to write programs that do not
take that much memory and their robustness will never be limited by some
supervisor code. Consult 'qdev' autodocs on how to implement different
subsystems in case you need 'ixemul' functionalities such as signal
handlers in your native code.
4.6 Getting help on API
API and MACRO autodocs resemble good, old manual page layout very much.
They really lack true MAN markers, thus they are considered clear text with
the addition of platform type separators and FF(Form Feed) per page. Each
such document contains quick TOC, so it is very easy to locate particular
function. All you need is just a text viewer with search feature. While
this method is quite common since the beginning of visual computing there
are better ways.
Due to the fact that we prefer plain text as a base and that doubling same
information multiple times(once per file format) is a waste of storage, we
decided to include a proggy('auto2guide') that can generate AmigaGuide
powered autodocs. Yes, the resulting documents look similar to those NDK
comes with. It is then easy to convert such material to HTML or anything
else(check Aminet for such software).
cd libqdev-?.?-bin-m68k/tools/auto2guide-?.?/
auto2guide //docs/qdev_macros.txt >//docs/qdev_macros.guide
auto2guide //docs/qdev_autodocs.txt >//docs/qdev_autodocs.guide
Warning! AmigaGuide documents may be rendered with the lack of backslash
prefixes. For example, NULL byte that can also be expressed with backslash
and zero ('\0') will only yeld for ('0') in the end... This is in regard to
EXAMPLE section.
Typical function description consists of the following sections: NAME,
SYNOPSIS, FUNCTION, INPUTS, RETURNS, BASES, NOTES, SEE ALSO, EXAMPLE and
BUGS. This type of layout is so intuitive that hopefully no explanation is
necessary(remark, at all times pay attention to NOTES). What may be new to
experienced programmers is section called BASES. It basically gives you a
hint what bases this very function expects to be initialized. Internal or
optional bases are square bracketed - [Base], while indirect bases are
braced by parentesis - (Base).
4.7 Coding shortcuts
At some point we realised that the documentation alone may not be enough in
order to understand how to apply particular function properly. We then came
up with the idea of creating examples(one per function). And despite the
eggheads who mostly think that reading a book equals great understanding,
we think that this is not true for majority of information sources
(including autodocs). Hence a bag of 'examples/' is to be given. Luckily
we did fill it with typical application demos giving you the ability to
quickcode whatever you need just by overlooking the right usage. Examples
that begin with '666' prefix are not directly related to functions.
4.8 How to report a bug
If you think you have found a bug that manifests its existance clearly from
within the library then your feedback is greatly appreciated. But for the
sake of completness please double check your code before reporting and read
all possible docs! As long as you can prove that the faulty element is one
of the sybsystems or single function or that the problem lies clearly in
the library but you are unable to track it down then we just expect code
snippet that triggers the problem and we will try to locate it. It would be
nice though if you could include a dump from your favourite memory watchdog
(Enforcer, CyberGuard, MuForce + MuGuardianAngel, etc) or on the fly
code disassembly of possible problem with offsets.
Of course we accept outputs from other debugging software as long as we can
put our hands on it, but memory related hits are essential. On the other
hand we are totally open for your hints on what it could be that your code
forces library code to malfunction. And remember, all wierd findings can do
only good! As to bugreport itself it is advised to pack everything
in a single text file, but if that seems impossible a file archive
with its own directory will do. The file/directory should be called
'qbug_<nick>_<tv>[.txt|.lha]' where '<nick>' is your nickname and '<tv>' is
the time in seconds since 1st Jan 1978 as a hexadecimal.
Typical bugreport can look like the one presented below. Of course contents
of that report is artifficial as the problem described is not an issue
because documentation states that parser related members are not to be
copied! You can use the 'Ed' in current shell to write the report the quick
way(make sure that your RTC is set to the right time first):
ed qbug_hacker_`rx "say d2x(86400*date(i)+time(s))"`.txt window console:
S: Member 'sd_errors' not copied after doing 'mem_copysmlcb()'!
Hi There!
I think i found a bug in 'mem_copysmlcb()' it does not make a copy of the
errors pointed to by source structure. Check the following code out and
see the output:
---BEGIN code.c---
#include "../gid.h"
int GID_main(void)
{
struct nfo_sml_cb sc;
struct nfo_sml_cb *nsc;
txt_memfill(&sc, 0, sizeof(struct nfo_sml_cb));
sc.sc_sd.sd_dosdevice = "MYDEV0";
sc.sc_sd.sd_handler = "myfilesystem";
sc.sc_sd.sd_hantype = QDEV_NFO_SCANML_HANFS;
sc.sc_sd.sd_device = "mydrive.device";
sc.sc_sd.sd_unit = "\x00\x00\x00\x00\x00";
sc.sc_sd.sd_flags = "\x00\x00\x00\x00\x00";
sc.sc_sd.sd_control = "";
sc.sc_sd.sd_startup = "";
sc.sc_sd.sd_stacksize = 4096;
sc.sc_sd.sd_priority = 0;
sc.sc_sd.sd_globvec = -1;
sc.sc_sd.sd_activate = 1;
sc.sc_sd.sd_errors = "Keyword\n";
QDEV_NFO_SCANML_PREPDE(&sc.sc_de);
if ((nsc = mem_copysmlcb(&sc)))
{
FPrintf(Output(),
"( sc) sd_errors = 0x%08lx\n"
"(nsc) sd_errors = 0x%08lx\n",
(LONG)sc.sc_sd.sd_errors,
(LONG)nsc->sc_sd.sd_errors);
mem_freesmlcb(nsc);
}
return 0;
}
---ENDOF code.c---
---BEGIN output---
(sc) sd_errors = 0x01F5A688
(nsc) sd_errors = 0x01F5A688
---ENDOF output---
The addresses are same! One may think that this will cause illegal memory
reads sooner or later... What do you think?
Cya,
Gregory Hacker
<ESC>sa<RETURN>
<ESC>q<RETURN>
Then just e-mail resulting file as an attachment to us and possibly await
the answer if you expect it. Please note that we may be unable to reply at
once but we will certainly do that at some point. And yet a small remark
concerning privacy. Be aware that the reports will form a library and may
be available to the public, so it is not a good place for private
corespondence really!
5 Complex subsystems
5.1 File Descriptor Relay
File Descriptor Relay initially written as Stream Wrapper in the early days
of SWLoader acted as a global redirector. The key was to eliminate 'NIL:'
redirection, so that programs could actually output messages and the user
could see what is going on during bootup on a separate screen. Aside that,
this magic handler was and still is, able to memorise boot history thanks
to virtual rollover files. While Stream Wrapper was just this and nothing
more File Descriptor Relay is way advanced. For now FDR comes with the
following features:
- Multitarget channels (channel can relay data to virtually unlimited
number of destinations/files).
- Each channel can relay data to built-in, scalable, rollover pseudofile.
- One of the existing channels can be marked default so that 'CONSOLE:'
or '*' call will address it immediately.
- Primitive channel finder which allows to locate channel by client's
address.
- Symbolic channel access control(password) for better reliability in
case of I/O sensitive application.
- Automated client signalling and descriptor closing on channel/relay
termination.
- Simple pipe implementation that affects WaitForChar() from under single
process. Pipe channels and relay channels can coexist!
- Built-in smart ANSI escape sequence stripper that in conjunction with
virtual files can preserve all ANSI while regular streams may or may
not contain it.
- Built-in VCR redirector that can remember and blast delays between I/Os
so that playback with exact timings is possible.
- Built-in control CSI forwarder which acts as a proxy between channel
and the console handler FileHandle.
- Plaintext line formatter(prefixer) with variety of options which can
make each line to begin with some valuable information such as current
time and/or caller address.
- Plaintext device statistics always at hand with simple FGets() until
EOF ('type <DEV>:' from CLI).
As you can see FDR has evolved into quite a machinery giving a programmer
lots of neat possibilites. At present the only program in this package that
makes use of large portion of features is 'amiscreen' which tries to copy
on what 'screen' is doing. For your convinience 'amiserver' is available in
'amiscreen/' directory which is a pure FDR you can start by feeding it with
the device name. This way you can experiment with FDR from shell. Note that
'amiserver' is not needed in order to run 'amiscreen'.
5.1.1 Supported packets
ACTION_FINDINPUT Standard I/O handling. Initializes client space.
ACTION_FINDOUTPUT Standard I/O handling. Initializes client space.
ACTION_FINDUPDATE Standard I/O handling. Initializes client space.
ACTION_END Standard I/O handling. Destroys client space.
ACTION_LOCK_RECORD Raw interface access. See 'dos_getfmfdrelay()'.
ACTION_FREE_RECORD Raw interface access. See 'dos_freefmfdrelay()'.
ACTION_READ Status, pipe and virtual file related 'Read()'.
ACTION_WRITE Relay, pipe and virtual file related 'Write()'.
ACTION_WAIT_CHAR Single tasking 'WaitForChar()' implementation.
ACTION_TIMER_PACKET Special helper packet used in ACTION_WAIT_CHAR.
ACTION_SCREEN_MODE Raw/cooked mode switcher and/or CSI forwarder.
ACTION_CHANGE_SIGNAL Stub. Always returns not OK state for clearance.
ACTION_SEEK Stub. Always returns an OK state for clearance.
ACTION_IS_FILESYSTEM Stub. Always tells that handler is not an FS.
ACTION_DISK_INFO Wrapper. Always redirects this packet to target.
ACTION_DIE Device selftermination. Effective when no clients.
5.1.2 Private MsgPorts
FDR uses so called "private message ports" that can cause soft-interrupt
each time new packet arrives on that very port. Each private message port
is tied to the particular FileHandle as returned by 'Open()' function. This
way it is possible to inject client related pointer(fh_Arg1 typically)
somewhere in the DosPacket and thus be able to access it no matter packet
type. This is absolutely necessary since different packets make different
DosPacket assignments where some of these packets simply lack client data
pointer due to design decision that Commodore engineers did take. Packets
who do not come with client data pointer set are thought to be dispatched
in a separate process. But FDR is meant to be a single tasking handler for
maximum efficiency so...
At this point it may seem it is all straighten up and the handler does not
need to spawn children processes to do the strict console packets cus there
is that magic guard that keeps an eye on each packet and glues client data
pointer and forwards such packets to the real handler message port. Yep
that works great and the overhead is very little, but there are quirks too
unfortunately. One particular flaw of this solution is that you cannot just
take the message port from FDR FileHandle and try to perform direct handler
conversation nor clone such descriptor! Please do not do that or your Miggy
will crash. Instead try the following code,
1 void clone_fdr_fh(LONG fd)
2 {
3 struct ctl_csh_data ct = {0, 0, 0};
4 struct Process *pr = (void *)FindTask(NULL);
5 LONG newfd;
6
7 /*
8 * Make the supplied 'fd' default for this process.
9 */
10 ctl_doconswitch(&ct, fd);
11
12 /*
13 * Now you can access -real- handler message port if
14 * you need to.
15 * {
16 * struct MsgPort *mp = (void *)pr->pr_ConsoleTask;
17 *
18 * ...
19 * }
20 */
21
22 /*
23 * This is a copy of the 'fd', you want to buffer it
24 * and set origin to NULL.
25 */
26 newfd = ct.ct_newcon;
27
28 ct.ct_newcon = NULL;
29
30 /*
31 * Undo Process structure changes having cloned 'fd'
32 * as 'newfd' or NULL in the worst case.
33 */
34 ctl_undoconswitch(&ct);
35
36 return newfd;
37 }
which utilises 'ctl_doconswitch()' which knows how to deal with FDR
FileHandles and can of course be used with other FileHandles too. For your
information, FDR interrupt code lives in a totally separate code segment!
5.1.3 ACTION_LOCK_RECORD
This packet acts as a main FDR structure resolver through special routine
called 'dos_getfmfdrelay()'. This packet also accepts masked signal in
dp_Arg1 so that FDR can be put on hold. It will wait for that signal and if
it did arrive will start processing upcoming packets again. Please use this
feature with extreme care as it is very easy to lockup the OS when handlers
get queried and FDR cannot reply!
5.1.4 ACTION_WAIT_CHAR
FDR implements non-buffered, single context pipes who can utilise the
WaitForChar() 'dos.library' function. This really allows 'console.device'
alike keystroke detection so that programs can be written in such a way
that I/O blockage is greatly reduced. Other than that all existing programs
should be able to direct their requests to FDR instead of 'CON:' for
instance if proxification is necessary.
5.1.5 Pipe limitations
Currently aside lack of buffering the only true limitation is that there
can be unlimited number of clients on the channel, each of which can write
something, but only the very first reader will get the data. The rest of
readers will simply receive EOF. I was questioning myself whether to add
something like "tee read" and eventually found that this feature is not
that important right now.
5.1.6 Address checks
Generally speaking, when a FDR channel is in relay mode you can attach
files expressed literally like 'CON:////myconsole', but that is not all.
You can also pass the addresses of already opened FileHandles without
major limits! This of course does rise a question of system stability when
such an address does not point at valid FileHandle. What can happen then,
will a disasterous memory trashing occurr? The answer is: No. FDR does
check for a valid FileHandle by comparing fh_Func1 against the ROM pointer.
If these do not match then the FileHandle is considered invalid. You will
probably notice single illegal read when 'Enforcer' is turned on, but that
is all.
On the other hand such protection mechanism forbids Output() and all other
custom FileHandles from being used! As to first case this is only good cus
you must not use your Output() from other contexts but there is a problem
with the second case, the custom FileHandles. Fortunately though you can
turn off these address checks by accessing FDR main structure with special
function called 'dos_getfmfdrelay()' and stuffing fm_fdcheck with NULL.
After that you should call 'dos_freefmfdrelay()' to eliminate the lock.
5.1.7 Relay policy
FDR never overwrites files as added to the channel thus you must make sure
that the target already exists before attaching and that it is empty. In
other words all files are always accessed using MODE_OLDFILE mode and
a call to 'Seek()' with OFFSET_END is being performed!
5.1.8 Rollover files
FDR supports so called virtual rollover files. There can be exactly one
such file per channel and 15 of them per process. These virtual files are
totally user definable in the terms of size and are intended to store
information circularily. Primary goal of this was to provide quick rollback
of information that is all relevant in a small scale of time so that no
huge logs are to be unnecessarily produced. This feature is still being
used in the SWLoader to memorise bootlog and will probably be utilised in
the further incarnations.
Please note that FDR virtual rollover files can always be read from the
beginning to EOF only. There is no way to set the cursor to particular
position! Fyi rollover files are not a special FDR feature. You can add
them to your code using 'han_rollifh()' + 'mem_openifh()'/'mem_closeifh()'
calls. Such circularity is also being used in the 'qfill' program to form
a binary pseudo-infinite buffer.
5.1.9 Status explained
FDR tries to be firendly to the user and comes with built-in status that
can be requested with simple 'type' CLI command. Suppose that 'FDR0:' was
started and channel 'mychan' was created. Plus that channel can relay data
to some 'CON:' handler. What you would see by executing 'type FDR0:' is
this:
> run amiserver FDR0:
> echo >"FDR0:mychan df CON:////myconsole"
> type FDR0:
D 001 000 001 256 254 000 "FDR0:"
^ ^^^ ^^^ ^^^ ^^^^^^^^^^^ ^^^^^^^
1 2 3 4 5 6
C 001 000 000:00:00:09 -D -P -T "mychan" ""
^ ^^^ ^^^ ^^^^^^^^^^^^ ^^ ^^ ^^ ^^^^^^^^ ^^
7 8 9 10 11 12 13 14 15
F $0067077F $01B8A898 $00000000 0000000000 +A +B +C "CON:////myconsole"
^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^ ^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^
16 17 18 19 20 21 22 23 24
Each status dump of File Descriptor Relay comes with the 'D' entry at the
beginning that describes the device basic status. Then all the channels
marked with 'C' follow. Each channel holds a list of files marked with 'F'.
Follow the numbers below indicators to get their meaning:
1 - Device entry marker.
2 - Total number of files attached. A sum of entries taken from all
possible channels.
3 - Total number of clients connected. Currently there is a limit of 256
clients. Client space is preallocated! This means that even in low
memory situations clients will still be accepted.
4 - Total number of channels formed. Channel space is not preallocated!
5 - First integer means how many loops to flush(this can be set to 0 if
no abandoned channel removal is needed). Second counts down each
access. Third shows how many abandoned channels were flushed.
6 - DOS device name.
7 - Channel entry marker.
8 - Number of files this channel has to deliver data to. Currently there
are no limits.
9 - Number of clients/callers on this channel right now(at the time of
status creation).
10 - DateStamp indicating how long this channel exists in ddd:hh:mm:ss
format.
11 - Default route indicator so that 'CONSOLE:' or '*' request can be
directed to this channel. By default implicit NULL channel will
accept all such packets.
12 - Is channel password protected indicator. If the channel is set as a
default and is password protected then other channel cannot be set
the default without unmarking current channel first.
13 - Should channel selfterminate and close all files or signal caller on
flush indicator.
14 - Channel name.
15 - Channel prefixation. This is only useful when data to be relayed is
plain text. You can place there selfexpanding options. See autodocs.
16 - File entry marker.
17 - Address of FileHandle to relay to. This should be used when removing
file from the list.
18 - Address of the client who did attach the file. This address is not
being validated at any time so be careful when using signalling!
19 - Signal to send to the client who did attach the file if channel or
FDR goes down. Please note that if this is non-zero then closing the
file has no effect! File closing and signalling are mutually
exclusive.
20 - Number of transport failures per this file. Write errors in other
words.
21 - Should ANSI pass through indicator. This indicator can be in form of
'-A' - no ANSI, '*A' - forward control CSI here and '*V' - VCR file.
Last two cases imply pass the ANSI through.
22 - Should buffered calls be used when relaying. Please note that this
setting can only be effectively changed when no writes were made
yet for this file. Eventually this indicator will change to the '-F'
or '+F' letter in case you requested data flushes. This change does
not impact buffering mode!
23 - Should this file be automatically closed down when channel is to
be removed or FDR goes down indicator.
24 - Short literal file identification. This filename can also be used to
remove it from channel.
Refer to autodocs on how to control FDR from C with 'dos_ctrlfdrelay()' or
simply check the 'dos_addfdrelay()' description for literal options.
5.2 Link Point
Link Point is a simplified filesystem mapper that allows to reflect files
and/or stream handlers as regular files thus making it possible to feed
restrictive software with data from different sources. At the time LP
concept did appear TCP/IP was the main objective as the goal was to allow
Internet radio to be streamed by WiFi capable devices that cannot directly
reach global network by the means of anti-features of the firmware. At
present LP does not offer built-in network support(this was removed) but is
able to do that through external DOS handlers such as 'TCP:' for instance.
LP went through a long mastering stage and for now it is capable of:
- Simulating root filesystem(without directories) with references stored
in the comment fields.
- Maintaining address consistency of the file slots/chain, so that root
container can be refreshed at any point of operation.
- Controlling stream size, taking care of access control, cooperate with
auxiliary programs and correlate client activity with resource usage.
- Utilising user friendly LPX interface, so that all arguments can be
entered along mapping.
- Emulating locks at the bare minimum to make sure that all software can
benefit.
- Emulating 'RHTTP:' and 'HTTP:' devices thanks to 'TCP:'. It is possible
to make these devices OS wide too.
- Protecting from accessing circular objects to avoid deadlocks and race
conditions.
- Locating objects very fast thanks to 32 bit FNV1a hash routine used to
establish file tree.
- Multitasking, so each mapping does the I/O from its own process.
Aside of exporting LP contents over various network programs LP can also be
exported itself through Timo Rossi's AmigaNetFS and hence be used across
other Amigas. You should definetly visit the 'linkpoint/' directory for a
standalone program that comes with a nice set of tips and tricks.
5.2.1 Supported packets
ACTION_LOCATE_OBJECT Allocates client space and grants an object lock.
ACTION_COPY_DIR Makes a copy of client slot/lock currently given.
ACTION_PARENT Stub. Will always return 0 meaning root directory.
ACTION_SAME_LOCK Checks if two client slots/locks reference device.
ACTION_EXAMINE_OBJECT Requests FIB feed of currently locked object.
ACTION_EXAMINE_NEXT Requests next object taking current FIB pointer.
ACTION_FREE_LOCK Discards the lock and deallocates client space.
ACTION_FINDINPUT Sets up client and accesses mapping for reading.
ACTION_FINDOUTPUT Sets up client and accesses mapping for writing.
ACTION_FINDUPDATE Sets up client and accesses mapping non-exclusive.
ACTION_FH_FROM_LOCK Associates previously located object with mapping.
ACTION_COPY_DIR_FH Copies client slot/lock from legitimate FH.
ACTION_EXAMINE_FH Requests FIB feed of currently opened file(FH).
ACTION_PARENT_FH Grants access to the root using fake volume node.
ACTION_SET_PROTECT Stub that does nothing. An OK will be returned.
ACTION_SET_COMMENT Stub that does nothing. An OK will be returned.
ACTION_SET_DATE Stub that does nothing. An OK will be returned.
ACTION_READ Forwards this packet to mapping's proc. 'Read()'.
ACTION_WRITE Forwards this packet to mapping's proc. 'Write()'.
ACTION_SEEK Forwards this packet to mapping's proc. 'Seek()'.
ACTION_END Terminates mapping association and frees client.
ACTION_IS_FILESYSTEM Depending on global LP setting returns OK or not.
ACTION_INFO Fills InfoData, id_NumSoftErrors == ommited objs.
ACTION_DISK_INFO Fills InfoData, id_NumSoftErrors == ommited objs.
ACTION_CURRENT_VOLUME Returns pointer to fake volume for compatibility.
ACTION_INHIBIT Flushes and/or rebuilds root directory contents.
ACTION_WRITE_PROTECT Write protects all mappings, these in use too.
ACTION_DIE Tells the handler to go down if clients are gone.
5.2.2 Packet route
As the LP handles mappings in separate contexts to avoid I/O stalls that
may be caused by different handlers, certain client packets such as:
ACTION_FIND#?, ACTION_FH_FROM_LOCK, ACTION_READ, ACTION_WRITE, ACTION_SEEK
and ACTION_END must be forwarded to the corresponding subprocess. There is
no direct client-mapping communication since it would be impossible then to
implement effective parent-child synchronisation. Note however that only
the queries are being forwarded, replies are point-to-point though. Below
is a quick diagram that explains how callers communicate with the LP and
how LP deals with requests.
+--------+ +--------+ +--------+
| | ACTION_#? | | ACTION_#? | | REPLY
| CLIENT | ----------> | LP | - - - - - > | MAP PR | - - - - - ,
| | all | 2| fwd | 3|
| | +--------+ +--------+ |
| | |
| | DIRECT REPLY | |
| | <---------------'
| 1| < - - - - - - - - - - - - - - - - - - - - - - - - - - - '
+--------+
As long as packets are not to be forwarded or an error was detected by the
LP a direct reply will be attempted otherwise mapping's process will have
to reply.
5.2.3 Context behaviour
In LP all lock related packets are isolated from mappings in the way that
obtaining lock does not imply target access on a file level. This is
considered healthy, because no subprocess need to be created in order to
examine the object. On the other hand LP does peek the target on a lock
level directly, which may not always be the right thing to do. Fortunately
though most lock interfaces are well behaved and do not block. As to stream
handlers they mostly lack lock interface completly, but the risk of
blockage is still there since they may be waiting for an event and thus
be unable to process new packets at that time... One simple solution to the
latter problem is to use 'wrapmount' program that is able to disable lock
related packets migration entirely.
When talking about mapping subprocesses they can only be created by the
supply of ACTION_FIND#? and ACTION_FH_FROM_LOCK packets and terminated on
ACTION_END arrival. The only other thing that is able to terminate the
subprocess is 'timer.device'. The use of timer was introduced so that dead
mappings can be flushed off the system in a piecewise way thus protecting
from resource exhaustion. It all works by renewing timer requests every
packet received. If the delay is too big then private termination signal
hits the subprocess and brings it down along with resource deallocation. LP
gets then notified about that through client packet, so that it will
not forward any new packets from that client but will reply with an
ERROR_OBJECT_WRONG_TYPE.
Auxiliary CLI process creation and synchronisation is a bit trickier. All
CLI processes get started in the main LP process, but at that time they are
told to wait until mapping subprocess is up and running. This in turn makes
it dead simple to handle error cases since the legitimate CLI entry is not
yet executing. The execution starts when mapping subprocess gains access to
the target file. It then sends synchronisation signal which gives former
CLI command a spin. The tricky part is that even though the CLI command was
initiated by the LP it now depends on mapping subprocess as it alters the
client slot! What that means is CLI process must die first so that mapping
process can effectively die too. But note that when mapping subprocess is
complete and the CLI process is still executing the first one is considered
a dead body only. Fortunately all gets sorted out on CLI completion.
In conclusion, such a design allows CLI command to be "shared" across
mappings so that stream splitting is essentially possible. In major, LP was
designed with that in mind and believe it or not it works. All you need
is just a multiclient pipe handler such as 'fifo-handler' and some sort
of stream agent like 'qfill' for instance.
5.2.4 Limitations
Currently there is no support for strict console packets and its uncertain
if they will ever be implemented. Although comfortable handler aliasation
would be available this way its use is rather questionable. Also direct
filesystem manipulation is not possible at this time. You really need to
alter the source filesystem/buffer and refresh the contents of the LP. You
can of course implement some sort of notification so the refreshing is
automatic. The most disturbing limitation is the length of comments who
currently store filenames of the targets. We consider a switch to larger
buffer but that will have to wait as there are more important things to
work on right now.
5.3 DOS device unmounter
Sometimes there is a deep need to unmount or remount the volume or handler
in order to change some mount entry parameters. AmigaOS handlers are known
to implement special packet called ACTION_DIE which in conjunction with
'assign' command allows to dismount the volume or handler quite safely and
aside memory leaks that cannot be avoided this seems to work. On the other
hand it proves completly useless when handler or filesystem are in use or
code does not allow ACTION_DIE. So what does a typical Amiga fan do? Of
course, he/she tries to ACTION_INHIBIT the volume maybe and to kick device
node from the list.
Chances are that your filesystem does support volume changes and that it
did detach from medium, but the process is still running. The worst thing
that can be done is to strip device node only. This is because even though
you cannot access this volume anymore, the process is fully in touch with
the partition. The real danger here is that after mounting such volume
again there will be now two processes that are unaware of each other and
who are tied to single partition. Caboom, you loose!
For that reason 'ctl_devunmount()' was created. This single function allows
to get rid of the volume/device or handler in a way that the process behind
it terminates in peace no matter if it supports ACTION_DIE or not. All this
is fully synchronised and can only happen when handler does not process any
packets thus data integrity is to be cared about. For user utility see the
'fsmount' tool.
5.3.1 Clean method
This subsystem can be used in a way described at the very beginning of this
section. The difference is that you do not need to use separate tools to
send the ACTION_DIE packet and then to strip the volume and device nodes.
All this is fully automatic. Generally this method is considered to be the
OS friendly way of dismounting devices, read: clean, but devices may not
always be able to go down.
5.3.2 Shield method
In case when a filesystem or handler refuse to go down 'ctl_devunmount()'
can attempt so called forcible unmounting. The simpliest way of doing this
would be to just freeze the process and strip device node, but clearly that
is too dirty and would eventually cause lockups. Such that all processes
tied to the handler would simply hang. There exists also a risk of data
integrity loss from partition point of view because you never know what is
filesystem doing at the time when you take away CPU from it. Fortunately
there is a better way. Instead of keeping a zombie in memory we can tell
the process to just exit as if it ended the execution.
This of course does not solve synchronisation problem, but look how nice
instead of murder, euthanasia ;-) . So having that as a replacement it is
time to detect when handler's activity ceases. It is not easy though, but
hopefully 'ctl_devunmount()' does that and when it detects that handler is
'Wait()'ing then it attempts to make sure its message port is empty and if
so then it converts it a little bit so that packets instead of migrating
to the handler go to stub handler where they get replied with an error so
that programs cannot hang.
Next step it does is to locate all the children of the main process and put
them all to sleep. Then it injects short assembly inline into their bodies
which is responsible for transferring them to exit and wakes them up. They
do disappear instantly and the OS deallocates their explicit resources such
as memory list entries, segments, TCB an its members. Next step is to
strip volume and device nodes and try to deallocate them. At this point
handler does not exist anymore and the possible partition is shielded and
ready to be mounted again without the risk of data disintegration.
6 Obviously mumbo-jumbo
6.1 Internal bases
Yes, [un]fortunately some of the 'qdev' library objects define internal
library bases, so that they are optional thus do not require explicit
declaration anywhere in the code. This also makes it possible to use
resident library branches without problems. The following table shows what
functions initiate internal library instance:
---------------------------------------------------------------------------
| Function | Shared library | Library base |
---------------------------------------------------------------------------
| crt_createargv() | exec.library | SysBase |
| crt_destroyargv() | exec.library | SysBase |
| crt_exitmethod() | exec.library | SysBase |
| crt_freeinstance() | exec.library | SysBase |
| crt_initmethod() | exec.library | SysBase |
| crt_newinstance() | exec.library | SysBase |
| ctl_addidcmphandler | exec.library | SysBase |
| ctl_openconscreen() | diskfont.library | DiskfontBase |
| ctl_rearrangecon() | intuition.library | IntuitionBase C |
| | mathffp.library | MathBase C |
| | exec.library | SysBase |
| ctl_remidcmphandler | exec.library | SysBase |
| ctl_zoomifycon() | intuition.library | IntuitionBase C |
| | mathffp.library | MathBase C |
| | exec.library | SysBase |
| dos_dopacket() | exec.library | SysBase |
| han_binaryifh | exec.library | SysBase |
| han_rollifh() | exec.library | SysBase |
| han_rwifh() | exec.library | SysBase |
| han_termifh | exec.library | SysBase |
| mem_cooperate | exec.library | SysBase |
| mem_grabqarea | exec.library | SysBase |
| mem_loadpicture() | guigfx.library | GuiGFXBase |
| mem_obtainhotvec() | exec.library | SysBase |
| mem_resolvehotvec() | exec.library | SysBase |
| mem_signalsafe() | exec.library | SysBase |
| nfo_getsystime() | timer.device | TimerBase |
| | exec.library | SysBase |
| nfo_typeofgfxmem() | cybergraphics.library | CyberGfxBase |
| ___ctl_animsubtask() | exec.library | SysBase |
| ___ctl_devhandler() | exec.library | SysBase |
| ___ctl_drawingsubtask()| exec.library | SysBase |
| ___ctl_idcmphandler() | exec.library | SysBase |
| ___ctl_layerbackfill() | graphics.library | GfxBase C |
| ___ctl_osdsubtask() | intuition.library | IntuitionBase C |
| | layers.library | LayersBase C |
| ___ctl_realforbid() | exec.library | SysBase |
| ___ctl_realpermit() | exec.library | SysBase |
| ___ctl_redrawperform() | graphics.library | GfxBase C |
| | exec.library | SysBase |
| ___ctl_refreshimage() | exec.library | SysBase |
| ___ctl_putosdtext() | graphics.library | GfxBase C |
| ___ctl_trigcode() | exec.library | SysBase |
| ___ctl_trigperform() | graphics.library | GfxBase C |
| | intuition.library | IntuitionBase C |
| | exec.library | SysBase |
| ___ctl_startanim() | exec.library | SysBase |
| ___ctl_stopanim() | exec.library | SysBase |
| ___ctl_trigsubtask() | exec.library | SysBase |
| ___ctl_xxxviewctrl() | exec.library | SysBase |
| ___ctl_zoomsubtask() | exec.library | SysBase |
| ___dos_clihandler() | exec.library | SysBase |
| ___dos_packetglue() | exec.library | SysBase |
| ___dos_prochandler() | exec.library | SysBase |
| ___dos_vcrdelay() | timer.device | TimerBase |
| ___mem_createpicbmap() | guigfx.library | GuiGFXBase C |
| ___mem_newswitch | exec.library | SysBase |
| ___mem_packethandler() | exec.library | SysBase |
| ___mem_readfd() | exec.library | SysBase |
| ___qdev_prv_initfunc() | exec.library | SysBase |
| ___qdev_prv_putfunc() | exec.library | SysBase |
---------------------------------------------------------------------------
6.2 Cached bases
Aside internal bases, cached global bases are to be found amongst "control"
related functions that may transfer the control outside current context.
Caching is a much better approach than manipulating the A4 register! See
the structures on where the cached pointers are used.
---------------------------------------------------------------------------
| Function | Shared library | Library base |
---------------------------------------------------------------------------
| ctl_addconlogo() | graphics.library | GfxBase |
| ctl_addviewctrl() | graphics.library | GfxBase |
| | intuition.library | IntuitionBase |
| | layers.library | LayersBase |
| ctl_openconscreen() | intuition.library | IntuitionBase |
| | graphics.library | GfxBase |
| | mathffp.library | MathBase |
---------------------------------------------------------------------------
Warning! As of release 1.2 cached bases no longer directly point at global
bases! They are tied to process local bases! To access global base out of
local one use QBASERESOLVE() macro.
6.3 IEEE arithmetics
As the library progressed and started to shift into the native direction,
we did notice that 'gcc' IEEE wrappers are not very pure and the resident
code crashes heavily under some circumstances. We had to workaround this
problem by replacing normal arithmetics by direct calls to Amiga math
libraries. At present only bare minimum(basic) of ops are supported. These
are: MFADD() - Addition, MFSUB() - Subtraction, MFMUL() - Multiplication,
MFDIV() - Division, MFFIX() - Float conversion, MFFLT() - Int. conversion,
MFCMP() - Comparison, MFCLE() - Less/equal helper, MFCGE() - Greater/equal
helper.
By default all arithmetics is being done on 'float's and so initialization
of 'mathieeesingbas.library', which is a part of the ROM is necessary. This
can easily be changed by defining macro called ___QDEV_DOUBLEPREC so that
the very same macros will work on 'double's. But this time you will need
to initialize 'mathieeedoubbas.library' which is a disk based library! At
all times it is desired to use the MFARITH datatype declarator which
defines 'float' or 'double' depending on state of control macro.
Warning! If you must use non-IEEE arithmetics which is possible by defining
___QDEV_NOIEEEMATH then there may be a problem in a matter of float layout!
Compiler('gcc') assumes IEEE(EM) but FFP(ME) format is different thus
immediate variable initialisation will call for wrong results! Value that
is known at compilation time must be passed to/assigned using MFVALUE()
macro! This unfortunately makes it unable to define such a value beyond
function body...
Note! By default all library subsystems who do some arithmetic ops depend
on 'mathffp.library'! This is because 'mathieeesingbas.library' in ROM V40
is broken(division specifically)!
6.4 Modular segments
Modular segments are essentially possible with 'a-pre_xxxseg.h'(see this
header on how to use) macroset. These can act like private libraries or be
the extra code segments that can be scatterloaded inside running code, so
that they may stay in the memory when base code was unloaded. Currently
modular segments are being used as #?.lo[c|g]o containers and interrupt
handler loaders in FDR and device unmounter core. But you can do much
more with it. You can for instance port 'glib' modules with it very easily
as symbol binding is also supported.
Unlike Amiga shared libraries, modular segments do not require complex init
code in order to use them. This is just ideal solution for those who do not
want to learn how a shared library works nor to maintain assembly interface
along with offsets and stuff. Symbol binding allows the API/ABI to be
altered in such a way that function pointers can change their place without
any side effects! Unfortunately there are limitations too. One of them is
the code model, which can only be non-resident.
Modular segments should really be treated like a task private code, but as
long as you write your code in a reentrant way their API can be made public
so that your module qualifies as a shared object. There is just one rule
you must stick to. Do not reference anything from the outside of function
body and all should be just fine. This way modular software interrupts or
public exceptions can easily be implemented.
6.5 A4 register issue
Old Amiga sharks know, but inexpirienced newcomers may not that resident
binary model is somewhat restricted by the lack of automatic A4 register
setup upon new context spawn. This is especially the case when coding using
LibNIX as its resident startup code lacks 'geta4()' subroutine! Please
consult 'qdev_macros.txt' on how to manually deal with A4 register in
your code that is ought to be pure. See the '__LOADA4()' and '__SAVEA4()'
macros. Just to remind you, the A4 register always carries the base address
responsible for global variables.
6.5.1 Callbacks/Hooks
Recently we did discover that it is unsafe to reference indirect ROM code
(the one that gets called through user segment) and global data inside
callbacks attempted in ROM space while the binary is resident. The problem
lies in wrong A4 register contents as you may have guessed but things are a
bit more fuzzy that just saving and restoring that single CPU register. It
turns out to be also a matter of current set of registers that have to be
preserved before the call is to take place. And that may or may not include
just the A4!
One such problematic case is 'InternalLoadSeg()' which takes function table
that provides memory management and file I/O function pointers. All is just
fine if these are direct AllocMem()/FreeMem()/Read() resolved through LVO
against bases. Unfortunately things change if these functions are wrapped
in your code and/or they reference bases defined as global. But it is not
only a case of globals, this is also how GCC produces code per function.
And it may not generate a code to save the registers at all...
It may happen that your callback ditches the registers that the original
callback would not which in turn makes the ROM code bleed really bad.
Happily, there is a cure for that. You can create a driver function with
QDEV_HLP_DFUNC() macro that will take care of register contents. But there
left just one tiny, and nasty at the same time problem. How to pass backed
up A4 register if function does not take some sort of data pointer? You
cannot just buffer it in a global variable!
To solve this problem, as of release 1.1 of the library so called "hot
vector arrays" have been implemented. These are address arrays that get
created at runtime and are stored in tc_MemEntry list of a task or process.
This way you can obtain such array at any stage in your program safely thus
be able to pass and restore the A4 register in the callback. See the API
reference and code in 'example/' directory for more details.
6.6 Undocumented routines
Most of the time you will like to look into API documentation but you may
be unable to find all the routines there. This is because some of them are
almost identical to their prototypes so there is no point to double the
information, although you will find the note that such a function exists.
Below is a table of such routines:
---------------------------------------------------------------------------
| Prototype | Cousin | Description |
---------------------------------------------------------------------------
| cnv_ALtoULONG() | cnv_ALtoLONG() | Singed converter |
| | cnv_ALtoQUAD() | Singed 64bit conv. |
| | cnv_ALtoUQUAD() | Unsigned 64bit c. |
| cnv_AtoULONG() | cnv_AtoLONG() | Singed converter |
| | cnv_AtoQUAD() | Singed 64bit conv. |
| | cnv_AtoUQUAD() | Unsigned 64bit c. |
| cnv_ULONGtoA() | cnv_LONGtoA() | Singed converter |
| | cnv_QUADtoA() | Singed 64bit conv. |
| | cnv_UQUADtoA() | Unsigned 64bit c. |
| cnv_ULONGtoBITS() | cnv_UQUADtoBITS() | 64bit input ver. |
| mem_scanlbl() | mem_scanlblncc() | Skips C comments |
| txt_bstrncat() | txt_bstrncatlc() | Lower case output |
| | txt_bstrncatuc() | Upper case output |
| txt_bstrnpcat() | txt_bstrnpcatlc() | Lower case output |
| | txt_bstrnpcatuc() | Upper case output |
| txt_datdat() | txt_datidat() | Case insensitive |
| txt_fnv128hash() | txt_fnv128ihash() | Case insensitive |
| txt_fnv64hash() | txt_fnv64ihash() | Case insensitive |
| txt_memcmp() | txt_memicmp() | Case insensitive |
| txt_pjw64hash() | txt_pjw64ihash() | Case insensitive |
| txt_quickhash() | txt_quickihash() | Case insensitive |
| txt_strboth() | txt_striboth() | Case insensitive |
| txt_strchr() | txt_strichr() | Case insensitive |
| txt_strncat() | txt_strncatlc() | Lower case output |
| | txt_strncatuc() | Upper case output |
| txt_strnpcat() | txt_strnpcatlc() | Lower case output |
| | txt_strnpcatuc() | Upper case output |
| txt_strpat() | txt_stripat() | Case insensitive |
| txt_strstr() | txt_stristr() | Case insensitive |
---------------------------------------------------------------------------
6.7 Local Base Support
Release 1.2 adds Local Base Support (LBS) which is active for Amiga and
Portable objects. LBS is an extension that allows to modify shared library
jump table per context (locally). In contrast, using 'SetFunction()' it is
possible to alter jump table globally only. LBS inserts just one more
MOVEA.L (An),A6 instruction during compiling before actual JSR which makes
it practically unnoticable (in terms of overhead) starting from 68030 and
up. On 68000 this needs extra 12 clock cycles. This way function calls can
be made using alternate jump table which affects only current process.
LBS consists of two parts: ___QLBS_NT_LOCALBASE and ___QLBS_NT_LOCALVECT.
Local base is nothing more than a Node (an alias) that addresses real base
twice (changable pointer and reference) initially. Local vector/jump table
contains special call entry addresses instead of the function addresses.
This way table can easily be fixed or manipulated without the need to save
each address first. But also thanks to such a design relative and absolute
addressing are possible with custom A6 register setup! By default no local
jump table is ever allocated so function calls are direct (as if LBS was
not there). Local base gets linked from separate object (when needed) that
can be found in the library.
Under normal circumstances (using bases declared globally) coding with LBS
is totally transparent. The programmer does not have to write extra code
nor add extra statements. Note however that when 'qlbs.h' has been included
(always before protos and inlines) and bases are declared function-private
there is a need to use QBASEDECL2() macro! This is because function calls
are always attempted against local base which starts with the 'L_' prefix.
So having 'SysBase' there will be also 'L_SysBase'. It is important to tell
the compiler that it should reference function-private 'L_SysBase' rather
than the global 'L_SysBase' and that is what QBASEDECL2() does.
Let us tell something about key uses. LBS allows one to monitor shared lib.
calls and thus program flow without hacking on the OS! LBS makes it easy to
implement missing shared library functions (this is the case when software
was compiled using newer inlines and launched under earlier OS/library
releases) without the need to touch actual program code. One just allocates
new jump table, imports the contents of a main library jump table, attaches
missing functions, activates it and voila. See the 'mem_xxxjumptable()' and
'mem_xxxjtslot()' functions in autodocs for more details or go straight to
examples.
7 FAQ
7.1 Questions and answers
Of course no nowadays manual can stay without Frequently Asked Questions,
so here they are. We will try to bank all your doubts in here along with
answers, so if you do not understand something feel free to ask. Important!
We reserve the right to make a selection on questions we receive, but as
long as they are project related or do not go off-topic too much they will
appear under this chapter.
7.1.1 Package related
1 Can I wrap selected 'qdev' code in a true Amiga shared library so that
my programs will use memory more efficiently?
Sure you can. Remeber though that this is only possible with resident
branches!
2 How to use 'qdev' own debug facility, there is nothing about this in
the manual?
Debug support was described in a separate article that is availabe
under 'qdev_debug.txt'. Do not forget to visit the 'examples/' too.
3 I see that the 'include/' directory contains some private headers, can
a programmer use them?
Of course, that is their purpose to help one access private structs.
if there is no other, better way.
4 Why is the autodocs and macro layout so weirdly unalphabetical and why
it often lacks functions that do the similar job?
The reason for that is to keep object family in one place, but also
due to API principle that sorts functions in A, P, I order. It is
true that many text related functions such as 'txt_strcmp()' are not
doubly described due to their non-case insensitive twin brother. So
you will not find an entry for 'txt_stricmp()' for example but a note
that such function exists.
5 Why does the 'mss' do animated logo files faster than 'viewlogo'?
This is because 'mss' activates frame optimization in the logo loader
and 'viewlogo' does not. Main reason for that is to have reference
when something is wrong with the optimizer.
6 What do 2 letter acronyms under the 'Stat' entry per remark in change
logs mean?
Currently there are four shorts and they mean: BD - Bug Detected(but
not fixed), BF - Bug Fixed, FA - Feature Added, NO - Notice.
7 I found that this LZW compressor that comes with the library cannot
really crunch binaries why?
It is very simple implementation that does not do any sorting before
compression, thus it is best for compressing bitmaps who often come
in large portions of 0's.
8 Why static link library and not a shared Amiga library, any reason?
First of all this is all-purpose library so converting it into Amiga
library would not be very smart because each, even smallest program
would require to load quite a monster into memory! Secondly, static
link library allows you to bypass functions that other functions need
so the gain is obvious - wrappers can be made really easy.
9 Main library header is huge! Would not it be better to scatter stuff
across N smaller ones?
Yes, that is true. We consider chapter-based-split but nobody knows
when this will happen and how it will be ordered.
10 Bases: SysBase and DOSBase seem to disappear from object when compiled
standalone while using 'a-pre_xxxlibs.h'! What the hell is going on?
Stuff $(NOSTARTFILES) in $(CFLAGS). Sorry about that, but there is no
other, better way to make the loader compatible with fat startups.
11 Why is the date written as (dd/mm/yyyy) instead of standard (d.m.yy) ?
Original date format Commodore programmers introduced allows to store
year as a two digit value only. This sucks and the 'version' command
will show it wrong if its written using four digits. Hence the format
was changed to mitigate this problem.
12 Should one be concerned about the tools this package comes with from
release to release even though their version information is same?
Yes. Each tool is being rebuilt when new package is to be released
which may have impact on bugs found in the previous builds. Please
note that since version 1.1 of the package every tool contains an
extended version information telling library details. As a general
rule you should update all tools you have copied to system drawers
upon new package release.
7.1.2 Programming
1 Virtual files are cool, but they seem not to work after my process is
gone and the other process wants to I/O, what is going on?
Virtual files use private task exception mechanism to do the I/O so
the process that did create them must be up.
2 Is it possible to display graphics on non-console related screen with
'ctl_addconlogof()' function?
Yes, see the 'examples/mem_xxxpicture/' on how to prepare such screen
or window properly.
3 How do I convert each animation frame as cropped from the whole into
Intuition Image format with Personal Paint 7.x?
This is easy, go to 'misc/' directory and install the script that
allows to export Anim brush to other formats, including C headers.
Then when exporting apply zero aligner to first frame, such as this:
'<filename>000.h'.
4 How can i create my own logo easily after i have saved all frames as C
headers that contain Intuition Images?
The easiest way is to copy one of the existing logos, say 'boing'
directory under new name, remove all existing frames, place your own
frames, fix the name and delays in the 'Makefile' and just 'make' it.
5 Are #?.lo[c|g]o files being kept in memory after they were processed
by 'mem_loadpicture()' function?
No, logo segment is being freed, so that the only thing that lives in
memory are remapped bitmaps.
6 Is 'mem_remapbitmap2()' slower than 'mem_remapbitmap()' function and
why?
Yes, 'mem_remapbitmap2()' is slower because it was written totally in
C, plus is is able to convert only 8 pixels at a time. But beneficial
can be the fact that it does not allocate chunky buffer at all!
7 What is that memory clustering and how can this speed up my code and
yet reduce memory fragmentation?
Memory clustering is possible thanks to 'mem_xxxcluster()' function
family. What it does is to allocate a block of memory, divide it into
smaller and equal pieces that can then be requested locally.
8 Can i compress #?.logo files with some executable cruncher such as
PowerPacker, Imploder, CrunchMania, etc and will they still load?
In theory yes, but you will have to apply special 'LoadSeg()' patch
first most probably.
9 I'm not sure how to resolve symbol address in my program as stuffed
with '___QDEV_SEGINIT_BINDS' in my modular segment, what do i do?
This is simple really, look at the example usage in 'a-pre_xxxseg.h'
and right after '___QDEV_SEGINIT_FINDPTR()' pass its result along
with symbol name to '___QDEV_SEGINIT_FINDSYM()'.
10 Can I attach exception handler to remote task and if so, how do I do
that?
This indeed is possible and may be the only way to hack on remote
task. The trick is to pretend for a brief moment that the remote task
is you. Simply, enter forbidden state, backup SysBase->ThisTask,
assign it remote task address, install exception handler then restore
your address and permit task switches. Remember though, that the code
and/or data live in your segment!
As of release 1.2 of the library there is a special subsystem called
'mem_xxxsniffer()' that allows to sniff on message ports of any task
that is running. You can use this code for that purpose regardless of
exception handler being installed already on -this- signal as sniffer
in an extension to the exception.
11 Can I intermix standard and resident code without any side effects?
This is really not recommended unless you know what you are doing.
The simple answer would be: No, you cannot.
12 Help! My code crashes/hits when compiled with '-resident' switch but
all is just fine without it. What the hell is going on?
* Make sure that all objects were compiled with '-resident' and that
you link against 'nrcrt0.o' or other RESIDENT capable startup code
and that all link libraries are resident compliant too!
* Make sure that before accessing global variables or library bases
outside main context A4 register was restored. You will have to do
that in: subtasks/subprocesses, interrupts, private task exceptions,
callbacks or hooks called in ROM or disk loaded library space.
* Try to create a driver function per callback with QDEV_HLP_DFUNC()
macro and see if it helps. Exclude all registers your callback uses
plus A5 and A7. If return type is 'void' and D0 is not used as an
input register include D0.
13 I really miss Amiga-like function names. What can be done about that?
As of 1.1 there is a file called 'qalias.h' which tries to address
this issue. Just include it in your project and voila :-) .
14 What can I do with QCRT startup that I cannot do with other startups?
QCRT was created so that your hands are free from lauchsite detection
and standard or resident code init/kill ops. Plus you can precisely
tell when certain operation will take place in 'main()'. QCRT is also
capable of being utilised in ROMTag affected binaries so the same
binary can be regular executable and ROM module at the same time
because it lacks global/BSS symbols. OS V34 is supported :-) . QCRT
uses special structure that defines cached CPU registers that contain
all you need to do lots of tricks with your binary.
| |
| | | | |
|