AMIGA alive

AMIGA alive
Showing posts with label c coding. Show all posts
Showing posts with label c coding. Show all posts

Sunday, November 24, 2024

AMIGA alive "Quick Clips"

"Quick clips" are a number of quickly recorded and little edited videos of what you might call "everyday Amiga issues" - installing a software package, fixing some C code, transcoding a file from one format to another, and the likes.

So far, these "quick clips" can be found on the YouTube channel:

Rewriting AMOS code in C

Some "live" examples of how to translate AMOS code to C code. 

https://www.youtube.com/watch?v=_jRAXVnV6cg

How to run AmigaAMP 3 on AmigaOS 3.0

With a little bit of manual effort, AmigaAMP 3 is nearly fully usable on OS3.0.

https://www.youtube.com/watch?v=bTu3J9nA8tk

Speech synthesis with "say"

Some experimentation, and a little story, with AmigaOS' "say", translator.library, and narrator.device.

https://www.youtube.com/watch?v=4e4m2Yqt51A

How to sleep() with AmigaOS

How to substitute an eventually missing POSIX sleep() function in AmigaOS. 

https://www.youtube.com/watch?v=yBYEGYFmmhA

 

...with more to come. Is there anything specific you'd like to see? Leave a comment - here, or on one of the videos' pages.

"Quick Clips" full playlist:

https://www.youtube.com/playlist?list=PLFqkmsX-uEeIA7uBhdPvHLV1QCO27hU5l


Subscriptions to the channel are highly appreciated. :-)

Thanks for watching!


Friday, November 8, 2024

A little odd: TAG_DONE and TAG_END

Sometimes one might get confused when to use TAG_DONE in AmigaOS C-code, and when to use TAG_END. The answer is very simple: They're one and the same. But it turns out there might be a little bit of an unknown story to these two widely used tokens.

TAG_DONE showed up before TAG_END, in NDK1.3, along with struct TagItem, but not as part of the system includes, and the TagItem system wasn't used in system functions.

Or was it? This is a section of code from the file Read-Me1.3/A2024Docs/OpenA2024.c, which is part of NDK1.3.

#define NONEWINCLUDES       1   /* don't have 1.3 includes yet  */

#if NONEWINCLUDES       /* Some additional definitions  */

/********** from intuition/screens.h ************/

#define NS_EXTENDED     0x1000  /* NewScreen.Extension is valid     */

struct TagItem
{
    ULONG   ti_Tag;     /* identifies the type of this item */
    ULONG   ti_Data;    /* type-specific data, can be a pointer */
};

/* ---- system tag values ----------------------------- */
#define TAG_DONE   (0L) /* terminates array of TagItems. ti_Data unused */
#define TAG_IGNORE (1L) /* ignore this item, not end of array       */
#define TAG_MORE   (2L) /* ti_Data is pointer to another array of TagItems
             * note that this tag terminates the current array
             */

Note the condition "#if NONEWINCLUDES". It looks as if these definitions were part of some "new includes" that weren't part of the distribution (yet?). 

There appears to be a nice little oddity here:

NS_EXTENDED (and TagItem) can't be found in intuition/screens.h, while other sections of the code (not shown here) can. The code contains a copy of NewScreen structure definition, only extended with an extra field, type TagItem pointer. It then creates an instance of that NewScreen structure, with type NS_EXTENDED, and calls OpenScreen() with that structure. There's no mentioning of NS_EXTENDED, or a TagItem pointer field in the Autodocs of OpenScreen() in this version of the NDK.

Does that mean that AmigaOS1.3 already had an (almost) undocumented feature, that would only be made (really) public with AmigaOS2?

TAG_END appears in NDK2.0, in new system include file utility/tagitem.h, as a clone of TAG_DONE, along with struct TagItem etc.:

NDK2.0-4/include/utility/tagitem.h

#define TAG_DONE   (0L) /* terminates array of TagItems. ti_Data unused */
#define TAG_END TAG_DONE

This NDK's version of intuition/screens.h also defines an ExtNewScreen structure that has the extra TagItem pointer field. 

Looking at the usage of TAG_DONE and TAG_END, across all files that are supplied with NDK2.0, there seems to be no preference. Maybe TAG_END was really just introduced to avoid interruptions in workflow, because many developers would intuitively think, and type, "end" instead of "done"?

Another tiny oddity is a change that appears in NDK3.1. It has a slightly different definition of TAG_END: It's still identical, but for some unexplainable reason, TAG_END isn't bound to TAG_DONE anymore. Instead, a comment explains why both use the same value.

Includes\&Libs/include_h/utility/tagitem.h

#define TAG_DONE (0L) /* terminates array of TagItems. ti_Data unused */
#define TAG_END (0L) /* synonym for TAG_DONE */

We'll probably never find out what happened, precisely.
But that's ok. Fortunately, TAG_DONE and TAG_END are either synonyms, or simply the same. ;-)



Sunday, June 26, 2022

AADevLog #4 - AROS and AmigaOS text editor

Publishing zx81paint on AmiNet led to me reviewing a lot of old C code, and creating an ASCII text editor is certainly an essential project for every C coder.

Actually it started a long time ago: In the late nineties, I was impressed by text editors like Microsoft's "Frontpage" and some MacOS programs, which were among the first to show "tabbed" documents, have integrated shell functions (e.g. recursive search), and some other features I hadn't seen before in GUI text editors. So my idea of a program called "proWEB" was born, an HTML editor with advanced functions like sophisticated TABLE creation, FRAME management, etc. Well, time has passed, web development has changed, my preferences have changed, operating systems have changed, and so on. But the idea of a great text editor, according to my personal taste, has remained. In it's first early incarnation for AmigaOS, "proWEB" didn't really get far. There was a second incarnation for AROS which got considerably further, but I was a bit over-ambitious, and under-educated in software architecture, so that one slowly but steadily came to a halt, too. 

Nowadays, having created a lot of smaller programs, slowly developing a usable concept of a program's architecture, and with some experience with C compilers (vbcc is my favourite) gained, I resurrected this old project again.

Among the first steps of resurrection was to find a nice short name you can easily type into your shell - so now "proWEB" has become: "reED", preferably spoken (and certainly typed in as) "reed", which is a reference to a) my name, b) editing, and c) Phragmites Australis. :-)

To my surprise it took me just a day to make all code adjustments from AROS' gcc to AmigaOS' vbcc, including lots of stylistic improvements, and I got a working executable. The code now doesn't use any special compiler features, and should work with gcc, probably SAS-C, etc. just as it does with vbcc. There were a couple of minor differences between AmigaOS and AROS that needed to be taken care of - for example my old AROS system used a font called "ttcourier" instead of "courier".

"reED" running on AmigaOS3.9 in 2022!

Looking at this old code from my "modern" perspective, it's quite a mess, a lot of things can be done much simpler. But "reED" is more sophisticated than I remembered: It has it's own GUI layout engine, it has a massive menu (which I use more like a notepad, many functions not yet implemented), uses ASL file requesters, it can edit multiple documents, it can mark/cut/copy/paste - and it has actual tabs with keyboard control! Colors might be a bit harsh, though. ;-)

I'm not sure if it makes any sense to devote lots of energy to this project - but it's a sort-of success even at the current stage of development. AmigaOS is great, AROS is great, and why not support both at the same time, it's really easy. (Well, gcc will do, but we still need an updated vbcc for AROS to make it perfect.) And in the end, AROS is just AmigaOS, so it just makes a lot of sense.

Let's see what other old (or new) stuff I can dig out from my harddrive... there are a couple of games I have to seriously review and rework...

Thanks for reading, stay healthy!

* * *

Click here for an overview of all AADevLog articles

Monday, January 31, 2022

AADevLog #3 - A paint program for the ZX81

The Sinclair ZX81 is a wonderful little machine. It's limitations create both a simple, fun environment to play with, and a challenge for developers.

About four decades ago, I started learning about computers with a ZX81. Type in stuff on a minimalist keyboard, print characters to a simple 32x24 characters monochrome screen, don't worry about colors or sound, because they're just not there. The ZX81 has very little memory, and the user interface is almost non-existent, so you're done pretty quickly. It's great, but also very limited in terms of sophisticated tools - there's just no extra room for such things, and you're having a hard time entering and editing the required data on the ZX81 anyway.

About three decades ago, I started using an Amiga. The Amiga, in comparison to the ZX81, is a highly capable computer. It has plenty of room, tons of screen size, a proper keyboard, several standard I/O ports, and colors and sound on top of it. Well, obviously it became my main computing platform.

About two weeks ago I got myself a ZX81 again. Oh what pleasures. Nostalgia galore. And that great experience of typing in little things which create immediate results.

Of course I quickly ran into some of the aforementioned limitations, so I decided to create some ZX81 support software for the Amiga: a paint program to paint nice game intro screens.

First painting with zx81paint.

It's called "zx81paint" and runs on Amigas with OS2.0 or higher. Currently it can't load or save files, but a first painting was created successfully. 

Due to the simplicity of the ZX81, the data that needs to be handled is also very simple, so there are little to no critical issues. For example the ZX81 picture itself is stored as a fixed-size array of bytes filled with ZX81 character set codes. Most of the development time is spent on putting together a pleasant and useful user interface. With the data and program structure in place, functions like line-drawing, flood fill, etc. can easily be added.

File loading and saving is currently missing - and might be one of the more challenging aspects of this little project. Several file formats used by emulators come to mind, but it would also be nice to have something that can directly be played back to the ZX81's tape-in ("EAR") jack.

There's no fixed feature list for "zx81paint", but an early release version will be put on AmiNet in a few days.

Thanks for reading, c u next time!

* * *

Click here for an overview of all AADevLog articles

Wednesday, December 22, 2021

AADevLog #2 - Sprites, tiles, scrolling... a game!

Another project I've been working on is slowly taking shape, and there was quite some headache involved to get it going.

In my pursuit of creating... a game! ...I've arrived at VSprites, tiles, and scrolling. The Amiga is a technical marvel, but it has it's pitfalls. So you read the docs, set up your default, low-res PAL display "View", create a "ViewPort", adjust ViewPort offsets, re-read the docs, adjust the ViewPort offsets properly, read the docs again, re-adjust... and it still behaves weirdly. At some point you try a high-res display mode. Either way, in the end result some VSprites are missing, or the scroll offset is wrong. Well, after hours and hours of experimenting, it turns out that ViewPort->RxOffset and ViewPort->RyOffset are indeed the correct variables that need to be changed to move around a larger source bitmap, but they get interpreted differently by different Kickstart/OS/chipset versions - it looks as if KS3.0 on AGA chipset, and KS3.1 SetPatch'd by OS3.9 on AGA chipset, might be messing up VSprites when scrolling, and earlier KS versions always scroll by low-res pixels, regardless of actual display (high) resolution. As a quick fix I'm focusing on what is probably still the most common Amiga configuration: Amiga 500, OCS chipset, Kickstart 1.3. I'll work on compatibility or maybe a separate version of the game later.

Basic screen mockup, to get an idea of object's sizes, and screen layout

And a "bug" in the docs was discovered: When using VSprites, you "reserve" some hardware sprites from VSprite usage by setting/clearing bits in GelsList->sprRsrvd. The docs give contradictory information about what "reserve" actually means - bit set or clear? It has now been proven and confirmed that bits need to be set to allow VSprite usage of that hardware sprite, and bits need to be cleared to "reserve" the corresponding hardware sprite from VSprite usage. Einstein-alike.

Anyway - after overcoming these obstacles it's beginning to look like... a game!

ViewPorts are a great thing, by the way. There are a couple of limitations, but whenever you run low on colors, need a different origin for your drawing coordinates, or maybe are thinking about an alternative to a dual-playfield, a new ViewPort might solve the problem.

So now it's got a scrollable bitmap, separate status bar, sprites, tiles, game map files, and a rudimentary "physics" engine that allows sideways movement and drops. That's a huuuge step forward!

No, that's not the... game! Just an early tileset.

Linux IFF picture viewer xiffview, which was created as a side-product of game development, now really comes in handy. I use it regularly, it has become part of my makefiles, and I add features as I need them.

When writing software that's handles data for different architectures, you have to pay attention to a lot of details. The reversed endianness (MSB on m68k vs. LSB on x86) has been mentioned many times. Also different compilers on different CPU architectures may have different data storage types - an "int" on a modern x86 machine is not be the same size as an "int" on Amiga. I encountered this just recently, when adding some code to xiffview that writes Amiga UWORD values to a text file, using something like fprintf("0x%04x", value). The UWORDs written had too many digits, too large for an Amiga 16-bit number. It looks like a bug on first sight, but the Linux x86 compiler is simply made to handle larger numbers (by default) than an Amiga C compiler. The solution is simple: you have to explicitly cast the value to a 16-bit data type, like this: fprintf("0x%04x", (short int) value).

Thanks for reading, c u next time!

* * *

Click here for an overview of all AADevLog articles

 

Tuesday, October 5, 2021

AADevLog #1 - xiffview 0.4, CLI 3d-printing, and a little bit of research

Over the last few months I've looked into several different subjects with no particular focus, but I got some results nontheless.

You might have seen the latest release of xiffview (v0.4), my little IFF ILBM picture viewer for Linux. It's a side product, created to simplify development for the Amiga, but I have a couple of further ideas to turn it into a really useful tool, e.g. add cropping, bitplane manipulation, etc. Anyway, for now it has learned something about palettes and sprites - see AmiNet readme file.

Another side product of some hardware changes happening in my workplace is a CLI controlled .gcode 3d-printing program written in quite Amiga-ish C code, which I call "reprint". It's currently very beta, and for Linux, but it successfully sends g-code to the printer - maybe another brick in building 3d-printing support for the Amiga? Some day I'll try to wire up something, then recompile "reprint" for Amiga, and keep my fingers crossed. ;-)

And then there's this little question that's been nagging me for years: when writing C code, is it good practice to copy stuff to local vars?

Here's what I mean by that: I have a tendency to create a large struct that holds my program's data, settings, pointers, etc., then access that data as members of that struct, e.g. in (inner) loops. I always wonder if this creates a complicated set of CPU instructions when compiled to assembler, esp. when accessing "deep" members - thus if it would be better to copy member data from global struct to a local variable, then use that variable e.g. in (inner) loops.

For single access this is obviously not an issue - but what about repeatedly accessing such member data? So I did some actual testing.

Looking at this piece of repeatedly struct-member-accessing (pseudo) C code...

for (loops) { n = some_pointer->some_member.some_number; }

...and comparing it to this piece of (pseudo) C code, which first copies to local var, ...

my_n = some_pointer->some_member.some_number;
for (loops) { n = my_n; }

...one would probably assume it's obvious the latter version is faster, because the code inside the loop looks vastly simpler. On the other hand someone somewhere suggested it wouldn't make a (noticeable) difference due to compiler optimization.

After a few test runs (pretty much using the pseudo code above), I think it can indeed be considered good practice to copy to local var. Results vary strongly, esp. on my 8-core Linux machine, but on the Amiga the latter version of the code is near-consistently about 25% faster than the former. With the struct member inside the loop, the compiler, or someone else, obviously didn't see we're accessing the same data over and over again. There are probably a hundred things to keep in mind, e.g. loop-size vs. CPU cache, size of struct, location of member, amount of data, compiler abilities, etc., - but it looks as if copying data to local var should indeed be preferred. Under certain circumstances it executes faster, and in most cases it's much more readable and saves a lot of typing.

What do you think? Any experience, opinion?

C u next time!

* * *

Click here for an overview of all AADevLog articles