English Amiga Board

Go Back   English Amiga Board > Coders > Coders. Asm / Hardware

Thread Tools
Old 17 October 2019, 22:07   #1
Registered User

Join Date: Oct 2017
Location: Sunderland, England
Posts: 2,599
Game Programming Framework

So after a fair bit of thought on what to do next, I've long set my heart on doing some Game programming tutorial streams for the Amiga.

With that said, looking back I made many many mistakes when coding Rygar... things that were just bad habits or because I was under a time constraint to finish the game for the competition.

So what I'd like to do is come up with a set of assembler functions that facilitate making game coding easier, while the implementation will be assembler I'll try and comform to high level languages like C (although I've never written any C before).

So here goes... I've documented (but not written) a couple of functions to set the tone, the idea being writing the actual function will be the assembler tutorial and the hope is that a picture of how to build a game will slowly come across to following...with better games being made for the Amiga.

Here are the off the cuff high level functions (just a brain dump)...I have more to add.

[+] rawHandle = amgLoadAndUnpackRncAsset(*name, bufsize, memtype)
[+] assetHandle = amgParseAsset(rawHandle)

[+] cameraHandle = amgCreateScreenBuffer(XSize, YSize, BitPlanes)
        Allocate screen memory, Creates Bitplane Handles
[+] copperHandle = amgCreateInitalCopperList(cameraHandle,paletteHandle)
        Build a default copper list from a camera handle.
[+] spriteSheetHandle = amgGenerateSpriteSheet(assetHandle, *Buffer, SpriteCutSize, MaxCuts, bool Mask)
        Build a list of pointers to the sprites and their masks	
[+] spriteHandle = amgCreateSprite(spriteSheetHandle)
[+] result = amgSetSpriteHandler(spriteHandle, *Address, State)
[+] result = amgSetPlayerSprite(spriteHandle *Address)
[+] result = amgDestroySprite(spriteHandle)	

[+] spriteAnimHandle = amgCreateSpriteAnimation(spriteHandle, spriteAnimationType)
[+] result = amgAppendSpriteAnimationFrame(spriteAnimHandle,*Handler, Frame, Speed)
[+] result = amgSetSpriteAnimation(spriteAnimHandle, bool [loop|term])

[+] SpriteNumber = amgCastSprite(spriteHandle,initXpos,initYpos,State)
[+] result = amgSetSpriteStatus(spriteHandle, bool State)
[+] result = amgSetSpriteAnimationSpeed(spriteAnimHandle)

[+] tileHandle = amgSetTileSheetAttributes = (assetHandle, sizeX, size Y)
[+] result = amgSetTilePlatform(tileId,tileHandle,spriteHandle,maskId)
[+] result = amgSetTileObstacle(tileId,tileHandle,spriteHandle,maskId)
[+] result = amgSetTileCeiling(tileId,tileHandle,spriteHandle,maskId)

[+] mapHandle  = amgCreateTileMap(rawHandle,xSize,ySize,size)

[+] canvasHandle = amgCreateCanvas(mapHandle, tileHandle)
[+] result = amgSetCanvasLimits(canvasHandle,x1,y1,x2,y2)
[+] result = amgSetCanvasPlayerLimits(canvasHandle,spriteHandle,x1,y1,x2,y2)

[+] amgBlitTileToCamera(canvasHandle,cameraHandle, sourceTile, destTile)
[+] copperList = amgDrawCurrentCameraPosition(canvasHandle,cameraHandle)

[+] copperListHandle = amgSetCanvasMode(canvasHandle,mode)
                        Mode can be:
                                0 = Static
                                1 = Horizontal Bi-Directional
                                2 = Horizontal Right
                                3 = Horizontal Left
                                4 = Vertical Bi-Directional
                                5 = Vertical Down
                                6 = Vertical Up
                                7 = 8 Way
                                10 = Dual Playfield Static
                                11 = Dual Playfield Horizontal Bi-Directional
                                12 = Dual Playfield Horizontal Right
                                13 = Dual Playfield Horizontal Left
                                14 = Dual Playfield Vertical Bi-Directional
                                15 = Dual Playfield Vertical Down
                                16 = Dual Playfield Vertical Up
                                17 = Dual Playfield 8 Way

[+] amgSetCanvasCameraPosition(canvasHandle,cameraHandle,x1,y1,x2,y2)                              ; Can use this to scroll around

And here's the first two functions documented:

        amgLoadAndUnpackRncAsset - Load and unpack file

        rawHandle = amgLoadAndUnpackRncAsset(*name, bufsize, memtype)
        d0                                  a0     d0       d1

        The file 'name' should be a ProPacked file using method 1 or 2.
        Memory of the required type and bufsize in bytes is allocated using AmigaDos
        malloc(), If the supplied bufsize is 0 then the packed file header is read to find
        the number of first before loading and unpacking the file.
        The memtype argument can either by MEMF_CHIP to allocate chip ram, MEMF_FAST to
        allocate Fast Ram or MEMF_ANY to allocate any available ram type, however should
        fast ram be available as well as chip then fast will be used as a priority.

        name - (a0) pointer to null-terminated file name of asset to load.

        bufsize - (d0) buffer size in bytes to allocate

        memtype - (d1) MEMF_CHIP | MEMF_FAST | MEMF_ANY

        rawHandle - (d0) handle code for the loaded asset

        error - (d0) failure to load and unpack the asset will result in any of the following

                        -1 - File not found
                        -2 - Unable to allocate ram type
                        -3 - Unpack failed
                        -4 - Max handles reached

        amgParseAsset - Parse and store the information found in a loaded asset

        assetHandle = amgParseAsset(rawHandle)
        d0                           d0

        The handle supplied should be the result from calling the 'amgLoadAndUnpackRncAsset'
        function.  The supported assets are Protracker Music Modules, 8SVX Sound samples and
        uncompressed Amiga ILBM image files.  Upon successful execution of the function
        a new handle is returned with the requested information parsed based on the found
        asset type.  See the AMG handles structure for more information.

        handle (d0) - A handle number that has previously been returned from

        assetHandle (d0) - Asset handle number.

        error - (d0) failure to parse information found with the associated handle.

                        -1 - Supplied handle not found
                        -2 - No supported Interchangable File Format header could be found (FORM)
                        -3 - Failed to locate Bitmap Header (BMHD)
                        -4 - Failed to locate Body Header (BODY)
                        -5 - Failed to locate Colour Map header (CMAP)
                        -6 - Asset is compressed
                        -7 - Failed to locate Sample header (8SVX)
                        -8 - Failed to locate Module header (MOD)

And here's the handle structures...

Struct handles
        STATUS                  X       byte    (-1 - invlaid, 0 - valid)                                      * AmgLoadAndUnpackRncAsset()
        ADDRESS                 X       long    Pointer in ram to asset                                        * amgLoadAndUnpackRncAsset()
        TYPE                    X       byte    (-1 - undefined, 					       * amgParseAsset()
						  0 - IFF Image, 
						  1 - Music Module, 
						  2 - Sound Sample )    
        IMGIFFSIZE              X       word    Size in bytes of entire                                        * amgParseAsset()
        IMGBODYSIZE             X       long    Size in bytes of body                                          * amgParseAsset()
        IMGCMAPSIZE             X       long    Size in bytes of colour map                                    * amgParseAsset()
        IMGBODYPTR              X       long    Pointer to body data origin                                    * amgParseAsset()
        IMGCMAPPTR              X       long    Pointer to cmap data origin                                    * amgParseAsset()
        IMGCMAPTYPE             X       byte    Type of colour map 0=12bit/1=24bit                             * amgParseAsset()
        IMGWIDTH                X       word    Size in pixels of image width                                  * amgParseAsset()
        IMGDEPTH                X       word    Size in pixels of image depth                                  * amgParseAsset()
        IMGPLANESIZE    	X       byte    Number of bitplanes in the image                               * amgParseAsset()
        IMGBYTEWIDTH    	X       byte    Number of bytes in width of the image                          * amgParseAsset()
        IMGMODULO               X       byte    Image modulo                                                   * amgParseAsset()
        8SXIFFSIZE              X       word    Size in bytes of entire asset                                  * amgParseAsset()
        8SXBODYSIZE             X       long    Size in bytes of body data                                     * amgParseAsset()
        8SXPERIOD               X       word    Sample period                                                  * amgSetAssetData()

result = amgSetAssetData(handle, offset, data)
d0                         d0     d1     d2

result = amgGetAssetData(handle, offset)
d0                         d0     d1

Index to handle data
*ptr = amgIndexToHandle(handle)

So the hope is that I can build on this. As I have a strong Unix/Shell/Python background I tend to operate everything on file handles and OOP so I'm hoping to take those principles into this.

This project might not go very far... but then again, it might really be useful.
It's certainly something I can take at a much slower pace than coding something like Rygar.

Ideas and comments as usual are really welcome.

mcgeezer is offline  
Old 17 October 2019, 22:42   #2
Registered User

Join Date: Dec 2016
Location: England
Posts: 87

You probably don't remember but I asked you about this on Facebook few weeks ago, so glad it looks like it's going ahead!
Spec-Chum is offline  
Old 17 October 2019, 23:15   #3

tolkien's Avatar
Join Date: Oct 2012
Location: Castro Urdiales/Spain
Posts: 731
great! I tick this thread to favourites!
tolkien is offline  
Old 17 October 2019, 23:23   #4
Registered User

Join Date: Dec 2016
Location: England
Posts: 87
Originally Posted by mcgeezer View Post
(although I've never written any C before).
I really doubt a man of your skill will have much issues mate, if I'm honest.

C can be made very asm like, indeed that was what it was designed for, as an almost 1 to 1 correlation with asm.

As an example I've decided to code Scoopex's tutorials into C, so I'm not just copying and pasting. Here's tut4. You can most probably guess the code this produces, as it's virtually identical to Photon's source.

#include <exec/types.h>

#define VPOSR 0xdff004
#define VHPOSR 0xdff006
#define INTENAR 0xdff01c
#define INTENA 0xdff09a
#define COLOR00 0xdff180
#define CIAPRAA 0xbfe001

int main()
	volatile UBYTE *vhposr = (UBYTE *)VHPOSR;
	volatile UBYTE *vposr_lo = (UBYTE *)VPOSR + 1;	// point to low byte
	UWORD *color00 = (UWORD *)COLOR00;
	UWORD *intena = (UWORD *)INTENA;

	// line starting position
	UBYTE yPos = 0xac;

	// line direction
	BYTE yDir = 1;

	// save interrupts

	// disable all interrupts
	*intena = 0x7fff;

	// do main loop until mouse pressed
	while(*(volatile UBYTE *)CIAPRAA & 64)
		// wait for start of frame
		if((*vposr_lo & 1) == 0 && *vhposr == 0x2c)
			*color00 = 0;	// bg black

			// update line y position
			yPos += yDir;

			// bounce line
			if(yPos > 0xf0 || yPos < 0x40)
				yDir = -yDir;

			// do literally nothing until we reach yPos
			while(*vhposr != yPos)
			*color00 = 0xfff;	// bg white

			// now do nothing until we're not on yPos
			while(*vhposr == yPos)
			*color00 = 0x116;	// bg blue

	// restore interrupts
	*intena = oldInt | 0xc000;	
    return 0;
On the other side of the coin, you can use the headers to (to some) make your life a little easier, here's my asm takeover code I converted to C:

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <exec/execbase.h>
#include <hardware/cia.h>
#include <hardware/custom.h>
#include <graphics/gfxbase.h>

extern struct GfxBase *GfxBase;
extern struct Custom custom;
extern struct CIA ciaa;

struct View *OldView;
static UWORD oldInt;
static UWORD oldDMA;

__chip UWORD copperlist[] =
        0x0106, 0x0000,     // AGA - BPLCON3
        0x01fc, 0x0000,     // AGA - Slow FMODE

    	0x0180, 0x0f00,		// Colour00 = red
        0x8001, 0xff00,     // wait until VPos = 0x80
        0x0180, 0x0fff,     // Colour00 = white
        0xd001, 0xff00,		// wait until VPos 0xd0
        0x0180, 0x000f, 	// Colour00 = blue
		0xffff, 0xfffe		// end list

int main()
	// Save original view
	OldView = GfxBase->ActiView;

	// Set clean view

	// Save interrupts and DMA
	oldInt = custom.intenar;
	oldDMA = custom.dmaconr;

	// disable all interrupts
	custom.intena = 0x7fff;

	// disable all DMA
	custom.dmacon = 0x7fff;

	// set cl1 to our copper
	custom.cop1lc = (ULONG)copperlist;
	// enable selected DMA
	custom.dmacon = 0x8280;	// No Blitter or sprites, only copper

	// loop until mouse clicked
	while(ciaa.ciapra & CIAF_GAMEPORT0)
		// do nothing

	// restore interrupts and DMA
	custom.dmacon = oldDMA | 0x8000;
	custom.intena = oldInt | 0x8000;

	// restore orginal copper
	custom.cop1lc = (ULONG)GfxBase->copinit;

	// restore old view

	return 0;

Last edited by Spec-Chum; 17 October 2019 at 23:41.
Spec-Chum is offline  
Old 20 October 2019, 23:57   #5
Registered User

Join Date: Oct 2017
Location: Sunderland, England
Posts: 2,599
So I've implemeted a couple of these functions now and added another few.

The function added is amgUnloadAsset() - it's pretty simple.

        amgUnloadAsset - Release handle and ram held by an asset


        The specified handle is marked with a status of unused within its structure.
	The address and length pointers of the handle are left in tact, these are
	subsequently used to deallocate the ram of the asset using LVOFreeMem().

        handle - (d0) A previously allocated handle using amgLoadAndUnpackRncAsset().



As I've implemented the functions I can post them here.

We have six files in total

main.asm - this has the main calling program.
loader.asm/loader.dat - this is what does the load header, allocate ram, read packed file, unpack file.
handle.asm/handle.dat - this contains the code for managing handles
unpack.asm - RNC unpacker

The main program looks like this, it simply reads the header of an RNC packed file, reads the unpacked size from the header, allocates that amount of ram, loads and unpacks the whole file to that allocated ram, allocates a handle for that asset that has been loaded....then... deallocates the handle and frees ram. This is pretty much a key aspect into managing assets in an Amiga game.

ExecBase:               	equ     4


        basereg data,a4
        near    a4


	bra	MAIN

	CNOP	0,4
        include i/custom.i              ; Custom Register definitions
        include i/cia.i                 ; CIA Hardware definitions
        include 'exec/types.i'
	include 'exec/exec.i'
	include 'exec/exec_lib.i'
	include 'libraries/dos.i'
	include 'libraries/dos_lib.i'
        include 'hardware/blit.i'
        include 'hardware/dmabits.i'	
        include s/unpack.asm
	include s/loader.asm
	include s/handle.asm
; Reserve a4 and a5 for global pointers.
	lea	data,a4					
	lea	CHIPBASE,a5				

; Open the DOS library
	move.l  ExecBase,a6				; handle[d0] = OpenLibrary=(version[d0],libname[a1])
	lea     DOSNAME(a4),a1
	moveq   #0,d0
	jsr     _LVOOpenLibrary(a6)
	move.l	d0,DOSBASE(a4)				; save DOSBASE for file reading

	lea	asset_lower(a4),a0			; rawHandle = amgLoadAndUnpackRncAsset(*name[a0], memtype[d1])
	moveq	#0,d0
	moveq	#MEMF_CHIP,d1
	bsr	amgLoadAndUnpackRncAsset
; d0 has allocated handle number

; now lets unload the asset from memory.
	bsr	amgUnloadAsset				; amgUnloadUnpackedAsset(handle[d0])
	move.l  ExecBase,a6				; Close the DOS library.
	move.l  DOSBASE(a4),a1
	jsr     _LVOCloseLibrary(a6)

; End program
        moveq   #0,d0
; Data section here.


	include s/loader.dat
	include s/handle.dat

DOSBASE:	dc.l	0
DOSNAME:	dc.b	'dos.library',0

asset_lower:	dc.b	"dh1:Rygar/ry_lower.rnc",0		; Packed RNC file to load.


loader.asm looks like this...

mode_old   =1005
mode_new   =1006
; rawHandle = amgLoadAndUnpackRncAsset(*name, memtype)
;    d0                                  a0     d1

	movem.l	d1-d7/a0-a1,-(a7)		; Save Registers
	move.l	d1,d5				; Save Requested Memory Type
	move.l	a0,d6				; Save filename pointer (we'll be needing it twice)
; Open the file for reading
	move.l	a0,d1				; handle[d0] = LVOOpenFile(filename[d1],mode[d2])
	move.l  DOSBASE(a4),a6       
        move.l  #mode_old,d2
        jsr     _LVOOpen(a6)         		
	tst.l	d0				
	bmi	.open_error
; Read the RNC header file
	move.l	d0,d4				; Save handle for LVOClose
	lea	amgRncHeaderBuffer(a4),a0	; Buffer to read RNC header bytes
	move.l	a0,d2				; bytes[d0] = LVORead(handle[d1],buffer[d2],size[d3])		
	moveq	#20,d3				
        jsr     _LVORead(a6)         		
	tst.l	d0				; Was there an error?
	bmi	.header_error
	cmp.l	d0,d3				; Did we read the correct number of bytes?
	bne	.read_error
; Close the file
	move.l	d4,d1				; result = LVOClose(handle[d1])
        move.l  DOSBASE(a4),a6       	
        jsr     _LVOClose(a6)        
; Check the RNC header
	lea	amgRncHeaderBuffer(a4),a0	; Get pointer to the read header
	move.l	(a0),d0
	lsr.l	#8,d0
	cmp.l	#"RNC",d0			; Is it RNC?
	bne	.rnc_error

; Allocate ram based on RNC header	
	move.l	4(a0),d0			; address[d0] = LVOAllocMem(buffsize[d0],MEM_TYPE[d1])
	move.l	d5,d1				
        move.l  ExecBase,a6
        jsr     _LVOAllocMem(a6)
	tst.l	d0
	bmi.s	.alloc_error
	move.l	d0,d4				; Save the allocated buffer origin
; Open the file again and read it.
	move.l	d6,d1				; handle[d0] = LVOOpenFile(filename[d1],mode[d2])
	move.l  DOSBASE(a4),a6       
        move.l  #mode_old,d2
        jsr     _LVOOpen(a6)         		
	tst.l	d0
	bmi.s	.open_error
	move.l	d0,d5				; Save handle for read and close.
; Read the entire file
	move.l	d4,d2				; bytes[d0] = LVORead(handle[d1],buffer[d2],size[d3])
	move.l	#$ffffff,d3		
        jsr     _LVORead(a6)         	
	tst.l	d0
	bmi.s	.read_error
; Close the file
	move.l	d5,d1				; result = LVOClose(handle[d1])
        move.l  DOSBASE(a4),a6       
        jsr     _LVOClose(a6)        

; Allocate a new handle here for the asset that was just read.
	bsr	amgAllocateHandle		; assetHandle[d0], handleOrigin[a0] = amgAllocateHandle()
	tst.l	d0
	bmi.s	.handle_error
	move.l	d0,d1				; Save handle
	move.l	d2,a1
	move.l	d4,HANDLE_STRUCT_ADDRESS(a0)	; Save the address of the asset in ram in the handle
	move.l	4(a1),HANDLE_STRUCT_LENGTH(a0)	; Save the length of the asset in ram in the handle
	move.l	a1,a0				; UnpackSize[d0] = Unpack[source[a0],dest[a0]
	bsr	Unpack
; Verify the unpack size against what was in the header
	lea	amgHandlesStruct(a4),a0
	lsl.w	#4,d1
	cmp.l	HANDLE_STRUCT_LENGTH(a0,d1),d0
	bne.s	.unpack_error
	move.l	d1,d0

	bra	.exit				; All done.
	bra.s	.exit
	bra.s	.exit
	bra.s	.exit
	bra.s	.exit
	bra.s	.exit
	bra.s	.exit
	bra.s	.exit
	movem.l	(a7)+,d1-d7/a0-a1

; d0=handle number
; Now lets release the handle and the memory we just allocated.
	lsr.w	#4,d0
	bsr	amgReleaseHandle		; handleOrigin[a0] = amgReleaseHandle(assetHandle[d0])

	move.l  ExecBase,a6
	jsr     _LVOFreeMem(a6)
loader.dat looks like this

			CNOP	0,4

			ds.w	20

			CNOP	0,4

handle.asm looks like this...

maxHandles:			equ	20		

; Error return codes for handles.

; Handle structure indexes

; Returns new handle number in d0
; Returns pointer to handle structure in a0
	move.l	d7,-(a7)
	lea	amgHandleAllocs(a4),a0
	moveq	#0,d0
	moveq	#maxHandles-1,d7
.loop:	tst.w	(a0)+
	bmi.s	.found
	addq.w	#1,d0
	dbf	d7,.loop
	moveq	#-1,d0
	bra.s	.exit
	subq.w	#2,a0
	move.w	d0,(a0)
	move.l	d0,d7
	lsl.w	#4,d7
	lea	amgHandlesStruct(a4),a0
	add.l	d7,a0
	move.b	d0,(a0)

	move.l	(a7)+,d7

; Frees up a handle previously allocated with amgAllocateHandle()
; INPUT [d0] = handle to release	
	lea	amgHandleAllocs(a4),a0
	move.w	#-1,(a0,d0*2)

	lea	amgHandlesStruct(a4),a0
	lsl.w	#4,d0
	add.l	d0,a0
	move.w	#-1,(a0)
and handle.dat like this...

amgHandleCurrent:	dc.w	0			; Current handle pointer

			rept	maxHandles
			dc.w	-1
			rept	maxHandles
			dc.b	-1			; Status -1/0
			dc.b	-1			; Type (-1 = unallocated)
			dc.l	0			; Address
			dc.l	0			; Length
			dc.b	"HANDLE"		; Padding.

Also included in the archive I have uploaded is the RNC unpack code.

And a link to the source code is here.

For those who want to follow along I will do a video showing setting up the tool chain and debugging the source here, feel free though to give it a try yourself if you want to race ahead.
mcgeezer is offline  
Old 16 December 2021, 04:26   #6
Registered User

Join Date: Jun 2020
Location: Australia
Posts: 10

; d0=handle number
; Now lets release the handle and the memory we just allocated.
	lsr.w	#4,d0
	bsr	amgReleaseHandle		; handleOrigin[a0] = amgReleaseHandle(assetHandle[d0])

	move.l  ExecBase,a6
	jsr     _LVOFreeMem(a6)
I know this post is a little old. Aren't the func params for FreeMem (a1,d0) in asm ?
shoestring is offline  
Old 16 December 2021, 04:29   #7

Pyromania's Avatar
Join Date: Jan 2002
Location: Dallas, TX
Posts: 2,931
Thanx for refreshing this thread, it's the first time I have seen it.
Pyromania is offline  
Old 16 December 2021, 04:41   #8
Registered User

Join Date: Jun 2020
Location: Australia
Posts: 10
Good thing that I bumped it then.

I just couldn't get this to free the right amount of RAM ( obviously too small a value in d0 ) until I moved the proper length of the asset into d0.
shoestring is offline  

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
C# Framework bburtonpa Coders. General 16 21 June 2020 13:27
API2 modern programming framework! AMIGASYSTEM support.Apps 1 21 February 2019 17:05
Haujobb Amiga Framework released stainy Coders. Releases 6 03 February 2019 18:02
Non-game programming - what does that leave me with? TenLeftFingers Coders. General 56 03 February 2016 01:59
Amiga 3D game programming Steve Coders. General 37 17 June 2013 22:15

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

All times are GMT +2. The time now is 15:32.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2022, vBulletin Solutions Inc.
Page generated in 0.07493 seconds with 15 queries