X68000 development, a chronicle of quarter arsed failure

Started by kamiboy, June 17, 2013, 07:08:41 AM

Previous topic - Next topic

kamiboy



So, the quarter assed master of laziness has decided to dabble a bit in X68000 game coding while his arse is on that sepulchral couch anyway.

Knowing myself I'll run out of that magical steam of enthusiasm in a few short weeks and abandon yet another pile of quarter completed failure of potential in the long chain that I call life.

But until then I'll prolly reverse engineer some arcane knowledge out of this handsome tall monolith called the X68000 and perhaps someone else will one day find the discoveries useful, so they will be documented here.

For the very earliest discoveries look at my posts in this thread.

For the record most of my knowledge stems from the following sites:

http://daifukkat.su/wiki/index.php/X68000

http://datacrystal.romhacking.net/wiki/Sharp_X68000

The rest is by using the debug features of the XM6 Pro emulator in addition to good old fashioned intuition.

kamiboy



Sirs, it gives me waves of tantric release to announce the completion of the first tiny ant sized step on the long road to maybe, perhaps someday have something quasi worthy of note.

As far as first steps are concerned this one falls somewhere between the discovery of fire and the launching of Sputnik.

After an utterly, abysmally shameful number of programming errors, more than I'll ever be forced to admit, even under the pain of torture, or even death, I have finally come far enough to put a single humble sprite loaded from from your everyday bitmap onto the screen. It even works on the very hardware itself as you may witness from the celebratory image below.



Yes, indeed our friend Arthur, from the Genesis port of the game is there on the screen, green with envy over the superior circumstances enjoyed by his cousin in the X68000 port.

It took too long to even take this micro leap. I would chalk it up to me being rusty but then again I was always a slapdash coder. In any regard I now have a routine that can load sprites of arbitrary size from a bitmap and store them in a useful local struct format. The loading routine takes care of subdividing the loaded sprite into 16x16 chunks, with each 8x8 being organized in that funny way required by the X68000 and even makes sure to build the pallets in an more or less intelligent manner so as to avoid wasting precious pallet slots.

Now that it works the idea is to fork out the sprite from bitmap loading routine and turn it into a tool that can be used to generate a series of easier to load sprite files from a bitmap. Once I get that done I'll share the code as no it no doubt will save someone out there the hassle of having to reinvent this wheel.

Things to note is that the bitmap has to be 24 bit and the dimensions have to be divisible by 4.

eidis

 Awesome work Kamiboy !!!

Just think of the possibilities ! One day there might even be a quality Sonic The Hedgehog port for the X68000. Keep up the good work !

Eidis
X68000 personal computer is called, "X68K" or "no good good" is called, is the PC that are loved by many people today.

kamiboy

I am not much for ports personally. I mean, Sonic the Hedgehog will never be better than it is on the Genesis because it was designed for that system, so it should be played on that platform. I am sort of a purist in that regard when opportunity permits.

I am very keen, however, on seeing new games made for older platforms. All these indie developers are wasting their time developing faux retro games for the PC laden heavy with nostalgia. Why not make honest to god 8/16-bit games for the actual hardware? Money? I spit on your dirty indie money!

In addition so many, many, many, many, many homebrew faux Mega Man titles floating about on the internet. Leaving alone the question of how many are any good, as it is likely to be none, may we ask how many actually run on the NES hardware?

Meanwhile Konami is busy handing paychecks to overseas outsourcers so that they can wipe themselves after taking irreversible shits on franchises like Castlevania and no one seems to care.

Why if I had talent, and motivation, or friends with either I would set about making a Castlevania inspired game for the X68000. Not a clone or parody either mind you, like all those indies do with their faux retro games. No, a genuine inspired game in the truest sense of the word, as in something that neither looks nor plays anything like Castlevania but in playing it it evokes the same sort of emotions.

And what is this obsession with Mega Man anyway. As a guy who grew up worshiping the series and only discovering Castlevania in recent years I will happily concede that the latter is the superior series design wise. Where are all the Castlevania imitators, or even inspired games?

I count only one series that fits the bill, From Software's Souls series. The tragic irony of course is that all the people who love that series do not even realize how closely it is related to classic Castlevania in terms of design philosophy.

Anyway, if I by some miracle do not burn out on this project I'll certainly would want it to be Castlevania inspired. While we are on the subject. Anyone know a good pixel artist who might want to work on this in their spare time?

It always helps to keep motivated if you are two. I'll be making a design doc that I can forward to like minded individuals.




Anywaste, I've rambled too long without giving out any development details. Currently I am making slow progress on planning out the finer points regarding resource management.

You have all this memory, pallet space, PCG, sprite registers etc. that you need to juggle in an intelligent manner which quickly gets very complicated. If you go about it the wrong way you'll have to rewrite everything further down the road when you realize the error of your ways. Hell, that always happens anyway.

Of course once you get that sorted out the bigger problem of asset management comes in. Having a systems that can load level geometry, sprite graphics and even animations is fine until you realize that you need something to generate all that data unless you want to do it all by hand.

That is where I suspect I'll burn out. I don't much care for making graphical editors. In fact I barely know how. I've only ever once made a window based app and that was in C# about 6 years ago. A sorry little thing it was too.

Fortunately before that becomes relevant there is another, much more fun hurdle to pass. Collision detection and AI. Oh, such fun times ahead.

eidis

 Hi Kamiboy !

Take a look at Tools directory in the HDD image v3

Get it here:
http://nfggames.com/X68000/Games/X68000v3.zip

The following tools might be of interest to you:

C Compiler Pro
Canvas Pro
Magic Palette
Matier
Prism
Sprite Editor
Staff Pro
Wet Paint

This post might also give you some valuable info:

Converting BMP to X68000 PIC and vice versa
http://nfggames.com/forum2/index.php?topic=4814.msg31571;topicseen#msg31571

Ganbare and keep the scene alive !
Eidis
X68000 personal computer is called, "X68K" or "no good good" is called, is the PC that are loved by many people today.

kamiboy

Actually what I meant was not a tool to generate the graphics but tools to generate the data that relates graphics to game objects.

Like, say, you a giant BMP file full of sprites of different sorts. You need some sort of data file that tells the game that, hey, here, at these coordinates are the images making up such and such frame of a this and that animation.

You need this for every resource you'll be using in the game whether it is level layout, music, sound effects etc.

Let me tell you, making that stuff manually by hand quickly gets very tedious. One can get by if only wanting to create a test level of a few platforms with a single sprite jumping around, but anything more complicated than that and you need to create proper editors for your game.

I see some of those tools allow you to make animated sprites, which is awesome if you are an artist, which I am not. Of course even if you are an artist you need to be privy to how the program's file format works so as to be able to load them into other programs, such as your game.

In fact the most ideal situation is exactly to make all sprites from scratch using that program since they give you the correct aspect ratio when displayed in-game.

neko68k

I follow your logic. You should make a conversion step in your build though. So in addition to making the data and the associated uncooked format(let me suggest xml and pcx) but you should also wind up with a cooked version to use in your game rather than converting at run-time. Something with, say, all the frames of an animation in the swizzled format ready to load to GVRAM with a small header for width/height of your frame, number of frames, etc. Basically as fast and simple as possible to load. Remember, we have 10mhz and aiming for more than 2MB of RAM is kind of limiting your audience.

Also keep in mind, if you are using native x68000 tools that often the docs that come with it tell you the file format. Likewise many come with source or source is available. On the other hand, using many of the native tools is difficult and I wouldn't do it personally :)

lydux

Maybe we could imagine a simple generic and adapted file format, then develop an x68000 library and a special convertion tool that we will integrate into the toolchain ? It could be a good start for expanding this thing.

Personnally Neko, I like the xml+pcx idea !

To be honest, I've never really dealt with the GVRAM and sprite register. You guys already know more than me on this point. So I'm listening carrefully ! :)

kamiboy

I have mused over the intermittent file format myself, but for what I am doing right now 10mhz and 2mb more than suffice. I'll save that as the last step once everything else is taken care of.

I am not too keen on XML files to be honest. For one thing, I would need to write a xml parser, which I rather not waste time on, and xml files take up too much space for the information that they provide. Lastly the only advantage of xml is that they are human readable. This only makes them an advantage as long as you are making data manually.

If you have a level editor you has better save things in a binary format so they can be read directly into appropriate game structs byte for byte.

Currently the only thing I have is a bitmap that I want to dump all the used game graphics into. That will be loaded into RAM permanently and then loaded into sprite registers and sprite data as needed.

It might prove to be too memory hungry but I can always fix that further down the line.

The problem with trying to be too efficient with memory is that if you allocate and deallocate all over the place you will prolly end up with fragmentation.

Right now I plan on using static arrays with max sizes for each asset type as a beginning. It imposes severe limitations but I think the reduction of complexity makes it well worth it.

Nothing is set in stone at this juncture though, I am still just planning things out as I make progress.

BlueBMW

If anything were to be ported... I say Outrun!  Why did the 68k not get outrun??  We got Space Harrier and Hang On...  can you imagine Outrun woth some excellent FM and Midi tunes???!

lydux

Kamiboy,

You really should not have to worry about memory fragmentation on such an old 68000 based system ! Thanks that you have common memory management functions within newlib, but remember that most games you have played on the x68000 (and like many others similar systems) are purelly assembler. No malloc things here !

You can consider those functions as sdk tools to speed-up your development, not something you absolutly have to do each time you need to store datas.

To let you know : on startup, my newlib port will fix heap size to 64KB. Memory allocations should reside within this area and it's a common value for x68000 application. Of course, it's possible to break this limit as needed, and this is transparent for you. But each time you will break it, newlib will reserve some extra memory space for itself as allocated block header. This is how you could cause heavy fragmentation. And I'll not talk about memory block alignment...

Definitivelly, uninitialized data is the way you should go (as you plan to do : static arrays).

kamiboy

Static arrays work fine, but sometimes you need to allocate dynamic arrays for the sake of efficiency.

For an example, right now I plan on loading all game graphics at startup and keeping them in main memory throughout the entire game.

Now, if I ended up having a lot of graphics I may at some point need to revise that strategy in favour of loading only graphics that are shared across all levels permenantly and in addition load level specific graphics as we start each level.

Since each piece of graphic can be of different size I will need to deallocate all the previous level's graphics and then load the next level's graphics, even if they may have a few images shared.

Anyway, to avoid fragmentation one could manage ones own memory. Like at startup allocate a giant 1.5mb chunk and then do with it as one pleases. Then there is no need to deallocate, only one has to choose the offsets one dedicates to certain assets in an intelligent manner.

I think that is much easier to do as the last phase of dvelopment when one knows exactly how large each asset pool is.

For now I stick to static arrays, so much easier to deal with.

lydux

Quote
Anyway, to avoid fragmentation one could manage ones own memory. Like at startup allocate a giant 1.5mb chunk and then do with it as one pleases. Then there is no need to deallocate, only one has to choose the offsets one dedicates to certain assets in an intelligent manner.
Yeah, that's what I was thinking about when I wrote my last post... Newlib is good for simple text based application, not really for games. Plus, it creates huges executable... That's mainly why I don't maintain it.
Instead I use a smaller but efficient micro-libc with minimalistic memory management and asm optimized common functions, but it's only for bare metal application actually (SRAM based).
I'll try to make a human68k version and a repository on my github page next week. Sure it'll interest you !

kamiboy

Yeah the huge executables are a bit of a problem. Those 50~60K will all be loaded into memory and reduce memory available to do actual work by that much.

At the end all I'll need is for some IOCS and DOS calls to be available and I am happy. For now though having printf at least is a necessity for debugging.

kamiboy

Well gentlemen, while I am stuck in a quagmire of data structures and pointless struct renaming until I can go from having a single sprite on screen to having the same only in a more structure way I have a request.

If any of you fine folk is privy to how exactly the rendering "windows" or planes work I would appreciate a primer.

So far as I can gather, and I might be wrong, if you pick the 512x512@256 colour mode what you get is a visible resolution on screen of 256x256. I assume, or I've gathered from Akumajou, that the actual rendering area is 512x512 and what is displayed on screen is a 256x256 window inside this that you control using the scroll function call, or just manually by changing the relevant memory area that stores said coordinates.

I am talking about the BG's here, I think at least. Bear with me I looked at this stuff a few weeks ago and my memory is not so fresh. Maybe the sprite layer works the same, I do not know. I think Akumajo renders level geometry to one 512x512 BG layer while showing enemies and such on top by way of sprites. Correct me if I am wrong.

Anywaste, would appreciate a good layman's explanation


kamiboy

By the by, while you fine gentlemen ponder my previous question I might as well make you aware of this:

http://www.gamedev.net/classifieds/item/2131-seek-likeminded-individuals-for-castlevania-inspired-endeavour/

That is correct, to help keep me motivated long enough to get this thing off of the ground I am recruiting for this grand X68000 endeavour. If you know anyone who might be interested and could be of any help then let them know.

It is a tall order, I know, but a man can dream now, can't he?

Edit:

If anyone knows of a better place to turn to for recruiting for a X68000 project then do let us know. These obsolete platform game projects are unfortunately always a result of festering nostalgia so finding people willing to work on an obscure platform is nigh on impossible.

neko68k

Disclaimer: My memory is hazy too, I may be mistaken. You understand the drawable/viewable area correctly. Also IIRC the scroll wraps around the edges of the buffer, which is pretty handy. Additionally, I think the text layer is planar. 4 bits in 4 slightly different memory areas to build an index into palette 0 and it's always 1024x1024. It can be treated as an extra graphics layer or you can print text on it with the appropriate functions. Akumajou does what you think it does. I'm still surprised how many games I've seen that don't really use the hardware. OTOH the raster scroll they used to emulate the road in Hang-On is pretty damned clever. :D

lydux

I know you want to talk about BG but here is what I can tell about Text and Graphic :

First is the TVRAM, which is a frame buffer mostly used by human68k and games as static display informations (scores, live, credits, etc...). 512KB of VRAM starting at address 0xe00000 up to 0xe7ffff, 16 bits word access, and as Neko68k said, is planar. Planes are accessible on each 128KB boundary, so there are 4 and are 1024 x 1024 x 1 bpp :
- Plane 0 : 0xe00000 --> 0xe1ffff
- Plane 1 : 0xe20000 --> 0xe3ffff
- Plane 2 : 0xe40000 --> 0xe5ffff
- Plane 3 : 0xe60000 --> 0xe7ffff

Next is the GVRAM, which is also 512KB, starting at 0xc00000 up to 0xc7ffff, this area is 512x512. RAM can be splitted or merged to obtains more or less layers, and de facto, more or less colors. This behavior is configurable by writing specific value to the video controller (VC) register R0 located at address 0xe82400, bits 1~0 (COL) :
- 00 : 4 layers, each 4bpp :
         * layer 0 : 0xc00000 --> 0xc7ffff
         * layer 1 : 0xc80000 --> 0xcfffff
         * layer 2 : 0xd00000 --> 0xd7ffff
         * layer 3 : 0xd80000 --> 0xdfffff
- 01 : 2 layers, each 8bpp
         * layer 0 : 0xc00000 --> 0xc7ffff
         * layer 1 : 0xc80000 --> 0xcfffff
- 10 : undefined mode
- 11 : 1 single layer at full 16bpp (65536 colors)
         * layer 0 : 0xc00000 --> 0xc7ffff
There is also an extra mode to merge all layers into a single 1024x1024x4bpp area (I think this is the one used by SX-Window) which is done by setting bit 2 (SIZ) of video controller register R0.
When using multiple layers, you can define a display priority upon each layer. This is done by writing the desire layer number into the video controller register R1 located at address 0xe82500 :
- Bit 7~6 (GP3) : Graphic layer number for priority level 3
- Bit 5~4 (GP2) : Graphic layer number for priority level 2
- Bit 3~2 (GP1) : Graphic layer number for priority level 1
- Bit 1~0 (GP0) : Graphic layer number for priority level 0
Level 0 has the highest priority.

As for example, in 4 layers mode, writing value 0xe4 (11100100) to this register will set layer 0 to the highest priority then layer 1, then 2 and finally layer 3.

This same register is also use to setup the priority between TVRAM, GVRAM and sprites :
- Bit 13~12 (SP) : Sprite priority level
- Bit 11~10 (TV) : TVRAM priority level
- Bit 9~8 (GV) : GVRAM priority level
Valid values for each bits are 0, 1 or 2.

Of course, avoid writing same priority level to different bits...

You can also enable or disable any video part using video controller register R2 (0xe82600) :
- Bit 6 (SON) : Sprite enable
- Bit 5 (TON) : TVRAM enable
- Bit 4 (GS4) : GVRAM enable when configured as 1024x1024x4bpp mode
- Bit 3 (GS3) : GVRAM layer 3 enable (in 512x512)
- Bit 2 (GS2) : GVRAM layer 2 enable
- Bit 1 (GS1) : GVRAM layer 1 enable
- Bit 0 (GS0) : GVRAM layer 0 enable
Write a 1 to enable, or 0 to disable.


I don't know yet, but seems there is also some bits on the video controller registers that deals with the color transparency...

About the viewable area and the drawing area : it's controlled by the CRTC, from register R10 up to register R19. You have to write the wanted RAM position within those registers as a 16 bits word :
- 0xe80014 (R10) : TVRAM X position
- 0xe80016 (R11) : TVRAM Y position
- 0xe80018 (R12) : GVRAM layer 0 X position (X0)
- 0xe8001A (R13) : GVRAM layer 0 Y position (Y0)
- 0xe8001C (R14) : GVRAM layer 1 X position (X1)
- 0xe8001E (R15) : GVRAM layer 1 Y position (Y1)
- 0xe80020 (R16) : GVRAM layer 2 X position (X2)
- 0xe80022 (R17) : GVRAM layer 2 Y position (Y2)
- 0xe80024 (R18) : GVRAM layer 3 X position (X3)
- 0xe80026 (R19) : GVRAM layer 3 Y position (Y3)


Also, a word about sprite controller : seems it's possible to split its RAM to create up to 2 tilemapped background layers (while keeping some part for sprites). I don't know more, but you have probably already found it.


Well... I'm sorry, this is really quick explanations, maybe wrong, low level programming and I don't know if there are some function call facilities (I guess so). But I hope that'll help a bit !

kamiboy

Thanks a bunch guys, that gives me plenty to digest.

So there are BG layers, Sprite layers and GV + TV RAM at disposal.

That sure is a lot of resources. So GV RAM is basically just a frame buffer that you can write pixels to at leisure, sort of like mode 13h in DOS, correct?

Rendering stuff to it manually must be slow and since there seems not to be any way to scroll it using hardware I imagine few games use it extensively.

I suspect Akumajo uses it for manually rendering special effects such as rotation effects and maybe some of those extra background layers in that cave level, but that is just a hunch. I think I'll stick to BG and sprites for now.

neko68k

you scroll gvram and tvram with the last set of registers lydux posted.

kamiboy

The deuce, how did I miss that?

Well that just made them each much, much more useful.

I could render static level geometry there for an example instead of the BG layers.

The possibilities are endless, I'll need to experiment when my engine grows quite a bit more sophisticated.

lydux

And don't forget that you have a DMA controller for speed up memory transfert !
Or you can setup the GVRAM as double 256 colors layers, enable the display for one while drawing on the other (it's slow, but possible). Then flip the enable bits on each vblank. A kind of double buffering. (Not tested, but I guess it should work)

neko68k

A good example of GVRAM double buffering is the intro of Phalanx. When it zooms in on the space station, that is double buffered.

kamiboy



I was fired yesterday effective immediately! You know what that means, more time to dedicate to making sweet, sweet game coding love to the X68000, so come here, Compact, you old gal.

Lady luck, that wanton harlot, has lifted her skirt for me once again. A week or more of coding and I should have something jumping around on the screen, huzzah!

It is a great day for the society.

kamiboy



Gentlemen, once again I find myself forced to stoop very low. I must humbly bow my head, bowe it so low as to place it upon the very filth below my feet. I most impudently ask that those of you of a higher station draw upon your superior talents and faculties to compensate for my lowly deficiencies. It is shameful, but it most be done, in the name of X68000 homebrew, it most be gamanned.

I have two issues that needs to be addressed urgently. Foremost and most urgent. I need to be able to code a loop that only repeats once every 1/60th of a second. I am sure you all know how that would be useful.

Contrary to what I thought the tiny bit of code which I received that was supposed to wait for vblank does not seem to be functioning. Inserting that into my game loop I thought I would get me an automatic 60 fps cyce but that is not so. It seems my engine is running currently at around 240fps.

Secondly, how the deuce do you access a time keeper with a sub second granularity on the X68000?

Currently all the timer keeper functions that I can find return time in seconds. I need miliseconds at the very least for the sake of fps calculations.

So in short, given the loop below, how would you deign to force it to cycle only 60 times per second.

while(true)
{
// Please insert code that limits update to 60 loops per second.
}

Omakase shimasu.

lydux

Quote
I have two issues that needs to be addressed urgently. Foremost and most urgent. I need to be able to code a loop that only repeats once every 1/60th of a second. I am sure you all know how that would be useful.
Agree, a wonderfull number !

Quote
Contrary to what I thought the tiny bit of code which I received that was supposed to wait for vblank does not seem to be functioning. Inserting that into my game loop I thought I would get me an automatic 60 fps cyce but that is not so. It seems my engine is running currently at around 240fps.
Agree as well, and that's totally normal as I said you total shit ! :D
In fact I haven't tested the code, and was wrong on the mfp bit, and condition... Really sorry !
Here is a corrected one, with clarifications and tested this time :

#include <stdio.h>
#include <stdint.h>
#include <dos.h>

/* MFP address */
#define MFP_BASE  0xe88000

struct MFP
{
  uint16_t gpdr;
  uint16_t aer;
  uint16_t ddr;
  uint16_t iera;
  uint16_t ierb;
  uint16_t ipra;
  uint16_t iprb;
  uint16_t isra;
  uint16_t isrb;
  uint16_t imra;
  uint16_t imrb;
  uint16_t vr;
  uint16_t tacr;
  uint16_t tbcr;
  uint16_t tcdcr;
  uint16_t tadr;
  uint16_t tbdr;
  uint16_t tcdr;
  uint16_t tddr;
  uint16_t scr;
  uint16_t ucr;
  uint16_t rsr;
  uint16_t tsr;
  uint16_t udr;
};
#define mfp ((*(volatile struct MFP *)MFP_BASE))

#define GPIP_ALARM    (1 << 0)
#define GPIP_EXPON    (1 << 1)
#define GPIP_POWSW    (1 << 2)
#define GPIP_OPMIRQ   (1 << 3)
#define GPIP_VDISP    (1 << 4)
#define GPIP_CRTC     (1 << 6)
#define GPIP_HSYNC    (1 << 7)

static void inline wait_for_vblank (void)
{
  /* CRTC under vdisp */
  while (mfp.gpdr & GPIP_VDISP);
}

static void inline wait_for_vdisp (void)
{
  /* CRTC under vblank */
  while (!(mfp.gpdr & GPIP_VDISP));
}

int main (int argc, char **argv)
{
  int i = 0;

  _dos_super (0);

  while (1)
  {
    wait_for_vblank ();
    i++;
    if ((i % 60) == 0)
    {
      printf ("Tick !\n");
      i = 0;
    }
    wait_for_vdisp ();
  }

  return 0;
}

As you can see, the bit on mfp GPDR register can be used in 2 manners.

Now, I will show you a more complex code on how to use the CRTC VDISP/VBLANK interrupt line to automatically trigger a sub routine. This is one of the second method I've already told you about when dealing with the vertical blanking period. You might find it more usefull to perform some tricks...
Also, note it's a bit faster than the previous one :

#include <stdio.h>
#include <stdint.h>
#include <dos.h>

/* MFP address */
#define MFP_BASE  0xe88000

struct MFP
{
  uint16_t gpdr;      /* General Purpose I/O line Data Register (used as interrupt sources on x68000) */
  uint16_t aer;       /* Active Edge Register (The signal edge the interrupt will be trigerred on) */
  uint16_t ddr;       /* Data Direction Register (only input on x68000) */
  uint16_t iera;      /* Interrupt Enable Register A */
  uint16_t ierb;      /* Interrupt Enable Register B */
  uint16_t ipra;      /* Interrupt Pending Register A */
  uint16_t iprb;      /* Interrupt Pending Register B */
  uint16_t isra;      /* Interrupt In-Service Register A */
  uint16_t isrb;      /* Interrupt In-Service Register B */
  uint16_t imra;      /* Interrupt Mask Register A */
  uint16_t imrb;      /* Interrupt Mask Register B */
  uint16_t vr;        /* Vector Register */
  uint16_t tacr;
  uint16_t tbcr;
  uint16_t tcdcr;
  uint16_t tadr;
  uint16_t tbdr;
  uint16_t tcdr;
  uint16_t tddr;
  uint16_t scr;
  uint16_t ucr;
  uint16_t rsr;
  uint16_t tsr;
  uint16_t udr;
};
#define mfp ((*(volatile struct MFP *)MFP_BASE))

/* Bits signification on registers GPDR, AER and DDR */
#define DR_ALARM    0
#define DR_EXPON    1
#define DR_POWSW    2
#define DR_OPMIRQ   3
#define DR_VDISP    4
#define DR_CRTC     6
#define DR_HSYNC    7

/* Bits signification on interrupt registers A (IERA, IPRA, ISRA and IMRA) */
#define IA_TIMERB   0
#define IA_TXERR    1
#define IA_TXEMPTY  2
#define IA_RXERR    3
#define IA_RXFULL   4
#define IA_TIMERA   5
#define IA_CRTC     6
#define IA_HSYNC    7

/* Bits signification on interrupt registers B (IERB, IPRB, ISRB and IMRB) */
#define IB_ALARM    0
#define IB_EXPON    1
#define IB_POWSW    2
#define IB_OPMIRQ   3
#define IB_TIMERD   4
#define IB_TIMERC   5
#define IB_VDISP    6
#define IB_GPIP5    7

/* MFP vectors (CPU user vectors) */
#define MFP_VEC_ALARM             64    /* RTC Alarm interrupt */
#define MFP_VEC_EXPON             65    /* Expansion card power off */
#define MFP_VEC_POWSW             66    /* Front power switch */
#define MFP_VEC_OPMIRQ            67    /* OPM interrupt */
#define MFP_VEC_TIMERD            68    /* MFP internal timer D */
#define MFP_VEC_TIMERC            69    /* MFP internal timer C */
#define MFP_VEC_VDISP             70    /* CRTC VDisp interrupt */
#define MFP_VEC_RTC_CLOCK         71    /* RTC clock */
#define MFP_VEC_TIMERB            72    /* MFP internal timer B */
#define MFP_VEC_USART_TX_ERROR    73    /* MFP internal USART transmitter error */
#define MFP_VEC_USART_TX_EMPTY    74    /* MFP internal USART transmitter buffer empty */
#define MFP_VEC_USART_RX_ERROR    75    /* MFP internal USART receiver error */
#define MFP_VEC_USART_RX_EMPTY    76    /* MFP internal USART receiver buffer empty */
#define MFP_VEC_TIMERA            77    /* MFP internal timer A */
#define MFP_VEC_CRTC              78    /* CRTC interrupt (?) */
#define MFP_VEC_HSYNC             79    /* CRTC VSync interrupt */

/* GCC and m68k specific. */
#define ISR  __attribute__((__interrupt_handler__))

/* CPU interrupts managment */
#define DISABLE_ALL_INTERRUPTS() asm("ori.w #0x0700, %sr")
#define ENABLE_ALL_INTERRUPTS() asm("andi.w #0xf8ff, %sr")

/* Registers bits access helper */
#define bset(reg,bit)   (reg |= (1<<bit))
#define bclr(reg,bit)   (reg &= ~(1<<bit))

/* This interrupt sub routine (ISR) will occur each time
* the CRTC enter VBLANK period. */
static void ISR crtc_vblank_handler (void)
{
  static int i = 0;

  /* Avoid entering another ISR while we are here */
  DISABLE_ALL_INTERRUPTS ();

  i++;
  if ((i % 60) == 0)
  {
    printf ("Tick !\n");
    i = 0;
  }

  /* Allow all possible interrupts to trigger */
  ENABLE_ALL_INTERRUPTS ();
}

int main (int argc, char **argv)
{
  void (*old_vblank_isr)(void);
  uint16_t usp;
  /* Save some MFP register state */
  uint8_t old_aer;
  uint8_t old_ierb;
  uint8_t old_imrb;

  /* Switch to supervisor mode */
  usp = _dos_super (0);

  /* Assign our VBLANK interrupt handler */
  old_vblank_isr = _dos_intvcs (MFP_VEC_VDISP, crtc_vblank_handler);

  /*
   * By default, MFP GPIP are set as input (interrupt source).
   * The VDISP interrupt line is masked and not enabled.
   */
  old_aer = mfp.aer;
  old_ierb = mfp.ierb;
  old_imrb = mfp.imrb;

  /* First, make sure it trigger on falling edge :
   *    falling edge = entering VBLANK
   *    rising edge = entering VDISP                  */
  bclr (mfp.aer, DR_VDISP);

  /* Now unmask VDISP interrupt source, and enable it */
  bset (mfp.imrb, IB_VDISP);
  bset (mfp.ierb, IB_VDISP);

  /* Main loop */
  int running = 1;
  while (running)
  {
    /* Main code here */
  }
 
  /* Reset MFP registers to their original states */
  mfp.ierb = old_ierb;
  mfp.imrb = old_imrb;
  mfp.aer = old_aer;

  /* Reassign the original VDISP handler */
  _dos_intvcs (MFP_VEC_VDISP, old_vblank_isr);

  /* Reenter user mode */
  _dos_super (usp);

  return 0;
}


Be warned ! It's incomplete as it miss the main loop code, it will never exit correctly and the handler will stay resident in memory. So it will continue displaying "Tick !" even after killing the process by pressing CTRL+C !
I've tried to comment it as best as I could right now on most important part, but it might be difficult to understand so feel free to ask for clarification.

Quote
Secondly, how the deuce do you access a time keeper with a sub second granularity on the X68000?
It's possible, and with a pretty nice resolution. This is also MFP related (it has 4 internal timers), so some part of my previous code will remain usefull. I will explain this later, so please be patient.


Have fun !  ;)

kamiboy



ストラクチャー get!

Gentlemen, I am awash in a sultan's harem worth of debauchery as I announce that the much adjourned establishment of a solid founding structure is finally complete.

It is a rather ham-fisted little thing too, inflexible to a fault and horrendously inefficient. Alas, beggars cannot be choosers.

Obviously there is now a system to render level geometry to graphical layer 0 and also takes care of scrolling said layer in tandem with the camera moving about.

There is a primitive collision detection system that can only handle dynamic objects against static level geometry. There is a animation system which currently, and presumably forever will require you to painstakingly hand generate all the coordinates for every frame of every animation and manually assign them to the designated game object.

Actually currently all data needs to be hand generated thus because there are no editors to generate the necessary relational data that the engine needs in order to be anything more than a miserable pile of code.

What needs to be done? Well everything that a 2D action-platformer is expected to have. Disregarding pedestrian requirements such as a scrolling background and some sort of music soundtrack in addition to sound effect there is plenty more on the todo list.

We need enemies for one thing, as well as a list of character actions in order to overcome said enemies.

Well that wraps up this devlog update. I have attached the source and binary of the game engine in its current form which consists only of our friend Arthur with his circumstances scarcely improved. Sure he can run and jump around now, but he is confined to an absurd boxed world. Like Sisyphus he is doomed to run and jump around eternally to no avail.

Fret not brave Arthur! Your valor cannot be contained by such an rectangular bastille, one day you shall pierce those walls and break free.




Now for credits, those go out to all the fine gentlemen who have chipped in with their encyclopedic knowledge of the obscure X68000 platform. Especially master Lydux whose insight into that black box is now the stuff of internet legends.

Master Lydux, one day I will surpass you, such as is the fate of the pupil to slay the master as his final act of learning.

caius

Quote from: kamiboy on July 18, 2013, 02:40:07 AM

Master Lydux, one day I will surpass you, such as is the fate of the pupil to slay the master as his final act of learning.

That's impossible, you can always try but at the end you will only hurt yourself.... ;D

Joking apart, good job!I tried it in the emulator and it didn't work, I'm gonna to launch it on the real thing.

kamiboy

Actually I have not tested it on the real thing. To get it to work you need to put the executble and sprite.bmp in the same folder. Also whatever media you launch it from must not be write protected because it tries to generate and dump level and sprite data to said media.

For me it runs fine on XM6 PRO. A pro tip, if you are launching off of a xdf file then make sure to close the disk file editor before mounting the image. If you dont it will be mounted as read only and the program will shit the bed.

lydux

Yeah ! Excellent Kamiboy !! :) Good job !

I've tested it quickly on XM6, but I don't have a joypad.

A word after looking into your source code : as expected, you do not use very much the libc and better rely on doscalls/iocscalls. I would do the same... This newlib port is just useless and huge. I think I'll drop it for something more dedicated to games developpement.
I do also have to implement FLOAT.X support into gcc, but that's an hard point...

Quote
Master Lydux, one day I will surpass you, such as is the fate of the pupil to slay the master as his final act of learning.
I wish you'll do... Until then, try DMA ! ;)

kamiboy

Master Lydux, if you wish to work on anything regarding the X68000 development platform the let me humbly suggest some sort of a debugger.

Let me tell you, debugging using printf gets cumbersome very fast. I know you've talked before about using a serial connection to the actual hardware for debug, but I think using XM6 Pro is more elegant if at all possible.

Float.x is really unnecessary for my purpose at least. I am used to doing everything using integers, and that works the best for a 2D game. In fact I cannot think of a use for floating point math in such a game at all, but you never know. Maybe it might be useful for rotation effects, but then you can do that using fixed point math which is much faster.

Actually I do not have a joypad either, I use a program that emulates a joypad via the keyboard. It works great. It is called key to joy I believe.

What is DMA?




Lastly a technical question.  It seems I have managed to attract another hopeful to this project. A composer. Now, the trouble is that I am positively clueless when it comes to sound hardware.

Disregarding the optional midi route and sticking to the internal FM hardware, what would be the best option for composing tunes for said hardware on, say, an ordinary PC platform which then can be played back on the actual hardware in some way?

If I undetstand things right some of the X68000 games are using different sorts of sound libraries to generate their tunes.

Would using any such library be the recommended route? If so which one? It is crucial that whatever format the music is to be written in is well documented in english at the very least. Having composing and playback software available on ordinary PC platforms would be a plus.

So far I've recommended the composer to research those MDX files that the X68000 music player, and its windows port, use. But I am not even sure whether that is a viable option for game development or not. Any input is appreciated.

lydux

Quote
Master Lydux, if you wish to work on anything regarding the X68000 development platform the let me humbly suggest some sort of a debugger.
I do already have multiple solutions for remotly debugging C programs, but none of them are easy for use right now as they are GDB command line only, and require some human68k internal knowledges. However, I have an idea and started to write an implementation some days ago (since you got into x68000 programming in fact ! As I was sure you'll ask for this soon or another).

I don't know XM6 Pro, but I know Neko68k managed to get a kind of serial line loopback when he wrote his linux<->ppp<->human68k tutorial. If he could explain how, it will be applicable for my debugger stub as well.

Quote
Float.x is really unnecessary for my purpose at least. I am used to doing everything using integers, and that works the best for a 2D game. In fact I cannot think of a use for floating point math in such a game at all, but you never know. Maybe it might be useful for rotation effects, but then you can do that using fixed point math which is much faster.

Ok, I know you're not fan of, but let me show you a bit of assembler. Here is a damn simple c function :

int test (int a, int b)
{
  return a * b;
}


After compilation (with optimization), here is the resulting assembler code :

test:
  movel %sp@(8),%sp@-
  movel %sp@(8),%sp@-
  jsr   __mulsi3
  addql #8,%sp
  rts

"JSR" stands for "Jump to Sub Routine", which is an instruction that will call another function. In our case, a function called "__mulsi3" that will take 2 parameters ("a" and "b"), and will return a result into the register "d0".

So, what the hell is that function "__mulsi3" ?
In fact, the m68000 is a silly processor that is just unable to do complex arithmetic ! And simply multiplying 2 integers already reach the limit. Normally, this is the responsability of another device called... FPU !
Fortunatly, using some logics and register tricks, it's possible to perform arithmetic operations without an FPU. But that's highly CPU time cost !
Here is the purpose of "__mulsi3", which is an arithmetic emulation function provided internally by GCC that'll multiply 2 integers number using registers tricks.
That's good enough for you if you don't have an FPU, but what about others ? This is where FLOAT.X comes in mind. That package provides additionnal doscalls like functions for arithmetic and floating point operations : "__IMUL" is one of them, and is an equivalent to "__mulsi3".
This way :
- FLOAT2.X will rely on the mc68000 registers.
- FLOAT3.X will rely on an mc68881 equipped board.
- FLOAT4.X will rely on the embedded FPU on X68030.
But every of these 3 packages will provide an "__IMUL" function accessible using the same way.

So, having GCC supporting FLOAT.X will be transparent for you, will reduce your executable size, and will make your program run faster on FPU equipped x68000 (you can even think about 3D). So will make everyone happy !  ;D

Quote
What is DMA?
Really ? "Direct Memory Access". A way to perform memory to memory transfert without using the CPU, but using the DMAC instead. This will highly improve your game engine speed when transfering tiles or filling TV or GV ram from main memory. A very fast "memcpy" if you prefer.
Try looking for these "_DMA####" iocscalls.


About sound, all I can say is that the ADPCM is an OKI MSM6258, which use the 4 bits dialogic adpcm algorithm. I know the "SoX" program can convert many pcm formats to another one, including dialogic. For playing samples, I guess you can use the iocscall "_ADPCMOUT".
Link : http://sox.sourceforge.net/


As for FM... Well, I just suck with this... I just know MDX is highly CPU and memory consumming.
Someone else maybe ?

kamiboy

Ah, yes, DMA. I know what it is. And I did look into it a bit as it would indeed be useful for tile transfers, unfortunately I could not find any good documentation on its inner workings, so I put it on the back burner. DMA transfers and such are relevant in the optimization steps, which can wait.  But if there are any good primers I would be curious To have a read.

I believe I saw something about chained link transfers which sounds very useful if it is what I think it is, which is to say initiating a queue of different transfers with one call.

I am sorry to hear that you are not well versed in the FM hardware, I was banking on your help. If you know anyone who might be privy please let them know that their assistance is sorely needed.

neko68k

@lydux
I used the com0com driver for my serial interface. Basically it creates two virtual COM ports that are linked, by default, as a serial null modem. I point the linux VM at one and XM6 at the other one. Very simple.

@kamiboy
FM is primarily programmed using MML of one flavor or another, then compiled. MXDRV and Z-Music are by far the most common. As lydux said though, they can be very CPU intensive but it really depends on how complex your songs are.

BlueBMW

Interesting.. so FM sound generation requires more cpu power?  Is this why Mahou Daisakusen seems to run faster when MIDI is used rather than FM?

kamiboy

Well, that is certainly bizarre. The whole idea of having dedicated sound hardware is to play and forget, sort to say. I thought you would just upload a program to the chip somewhere in memory along with perhaps some sound data and then just give the chip a kick to have it commence chewing on the code and defecating sweet, sweet chiptune music, directly into your ear canal.

Now for the hundred ducat question, is any of these MML implementations well documented in English? Or documented at all?

I need to quickly establish a viable route for our composer to make tunes in some sort of program to whatever he composed being made to play on the X68000 when the time comes.

Deuce I would love to be able to read Japanese right now. The Danish education system did us all a huge disservice by making the German language a mandatory part of the elementary school curriculum.

Verdammt noch mal! Who the heck needs to speak German anyway? Blast ye, they should have thought us Japanese. Sunk us temple deep in Kanji lessons. I spit on their lack of foresight.

lydux

Quote
Well, that is certainly bizarre. The whole idea of having dedicated sound hardware is to play and forget, sort to say. I thought you would just upload a program to the chip somewhere in memory along with perhaps some sound data and then just give the chip a kick to have it commence chewing on the code and defecating sweet, sweet chiptune music, directly into your ear canal.
That's the way SEGA goes with the Genesis/Megadrive as well as many arcade pcb : sound is heavily cpu intensive, so it's performed by a dedicated Z80 that'll order to an FM generator and PCM chip. Unfortunatly, that's not our case... IMHO, maybe the worst design idea on the x68000.

Quote
Now for the hundred ducat question, is any of these MML implementations well documented in English? Or documented at all?
Well... Seems MML implementations are some kind standardized. But it needs knowledge about the target soundchip.
Website in english about MML (but not especially X68000) : http://woolyss.com/chipmusic-mml.php
There is also the "mxdrv.txt" files inside the punigrammer manual (japanese, as always).
And, I found this site recently (MML files are readable.) : http://mmltalks.appspot.com

Maybe a good start ?

Quote from: neko68k
I used the com0com driver for my serial interface. Basically it creates two virtual COM ports that are linked, by default, as a serial null modem. I point the linux VM at one and XM6 at the other one. Very simple.
Ok, will give a try. Thank you !

neko68k

While MML is somewhat standardized, like lydux said, there are specific features for specific chips. Unfortunately, none of the X68000 supported formats are documented in english. See the following:
http://www16.atwiki.jp/mxdrv/
http://www.z-z-z.jp/zmusic/

also check the MXDRV compiler, with docs:
http://nfggames.com/X68000/Mirrors/x68pub/x68tools/SOUND/MXDRV/NOTE085.LZH

kamiboy

#38


That is right, Jay from Game Chasers, now they are artists, and a better one than me at that.

In any regard gentlemen, tis time for yet another update. You see gents, I recently unearthed a file from the very deepest Mariana trench like crevices of my hard drive. A forgotten little thing, beset by many a year's worth of digital dust. Indeed its discovery did provide me with a  hydrothermal geyser of immediate pleasure.

No, gentlemen, it was not porn that I found, though I wish it was. Rather instead it was a simple tile based level editor that I once upon a time whipped up for a personal proof of concept project while I worked for a small mobile game making company in Denmark. Come to think of it that job was my closest ever brush with that desired for game design position that has always eluded me. I've since been hopelessly stuck in a dead end position with only a peripheral connection to video games, curse you lady luck and your coquettish frivolity!

But I digress. This tiny little forgotten level editor was buried inside a zip file somewhere amid the Minos labyrinth of folders in the center of my hard drive.

As with everything that I ever make it is a inflexible little thing, albeit not so much that it cannot with some effort be made work as the level editor of choice for this project.

There were a few details that needed sorting out first of course. Chief among them was the fact that I, for some reason, had chosen to make the maximum size of levels created with said editor 127x127.

An odd number that only makes sense if the level dimensions were being stored in a signed byte. Knowing my byte pushing self I imagine it very well was. Unfortunately this ancient limitation severely hampered the utility of the level editor.

Furthermore nestled inside that zip was only the lone executable for the level editor, with no source code to complete the ensemble.

With the way my game engine works the horizontal dimensions of a level needs to be a power of 2. This, as any old school coder would instantly realize, is done for the sake of efficiency when looking up level data.

Ideally it should be possible to create levels up to a maximum size of 256x256. If that sounds a bit small consider that these are not pixels but level blocks, or cells, with each being 8x8 pixels in size. Consider then also that the visible screen is 256x256 which means that a 256x256 cell sized level can span 8 screens deep and 8 screens high.

These dimensions are not exactly amazing, but beggars cannot be choosers, the level editor is all I have to work with.

So it was that I was beset by the crazy idea of trying to edit the binary of the editor in an attempt to change the 127 dimension limitation to 256. So it was that I set on on a quest of desperation and did a search in the binary for that magic hexadecimal number 0x7F.

For each one I found I changed it to 0xFF, launched the editor and tried to make a level larger than 127. It took a few dozen attempts which mostly netted me crashes or no change in behavior before I finally found the two magic bytes that did the trick.

As a bonus of the way the editor is written the change actually made it so that you can create levels of any size you want now because changing the numbers actually broke the limiters of the editor, so huzzah. Alas the editor might be able to create levels of an arbitrary size, but it cannot load back any level larger than 256 because level dimensions are saved in a pair of bytes, so better not make anything too crazy.

Anywaste, I changed the level loader to load levels created with the level editor rather than levels made by hand.

If you think the updated game binary still features a very sparse level design then let me explain. Firstly I am no artist sirs, I excel at game design, not level design. Furthermore, the game engine is currently very slow. If you add too many details to the levels the framerate will catch a halberd with its face.

In other news I took the liberty to add background capability to the renderer. I am actually not using the specific background scrolling hardware of the X68000, rather the current single background plane is the second graphic plane.

There is a reason for this. Firstly since I already had a system to render level geometry to the first graphic plane it took a minimal amount of work to extend that to also render another level geometry to the second graphic plane. This way one can create the background using the level editor which is pretty spiffy.

Unfortunately the hardware background layers work differently from the the graphic planes so I have not bothered with them yet. Perhaps I will never bother with them and keep the game to one parallax background, we'll see.

The trouble is background layers use sprite segments for their graphic which makes them harder to deal with, while doing so also takes away available sprite segments available for the game.

I've tried to examine what other X68000 games do and Akumajo at the very least seems to be making heavy use of  graphic planes for its background while the background planes are only sparsely used. Background planes are faster though, so perhaps they should not be rules out off hand.

Anywaste, all in good time gentlemen. Next we need to develop some character abilities apart from running and jumping, and then adding some dangers such as enemies and obstacles to give the character a raison d'etre.

Pretty soon I will have to start looking at sound, especially music as well. I need to figure out a way for our composer to create MDX files. Before that I will of course need to figure out how I even play those damned files using the terminate and stay resident MDX driver.

Unfortunately all the documentation is in Japanese, and to add insult to injury the ones found in zip files are in a strangely formatted doc file that nothing I have will recognize nor open.

Maybe someone who can open these docs could copy paste their Japanese text into a compatible modern document and provide them to me so I can start using google translate to decipher them.

Anyhow, the project in its current form, including level editor and source are attached.

neko68k

christ man, tl;dr. learn to get to the point :P

So called "DOC" files are just plain text with Shift-JIS encoding.