Jump to content
IGNORED

SPECTRA2 development thread


retroclouds

Recommended Posts

The last few days I've been working on a second version of my spectra library, called SPECTRA2.

 

In case you do not yet know what SPECTRA is: it's my library for writing arcade games for the TI-99/4A.

It's basically the library I used for writing Pitfall. Check the original manual here.

 

Compared to the original library, I'm taking a different approach this time, kicking out some of the high-level

functions in favour of small routines that can be more easily reused.

 

I'm taking a "runtime library" approach. With a kernel that is always used and some more

high-level functions that you only need to include if required. The goal is to have a max. 2 KB runtime

that is able to handle sound, speech, joystick pulling, VDP communication, timers and sprite movement.

Tutankham is the first game that will be based on the new library. I'll be posting samples as I move along.

 

This is what I have in mind:

*
Reduced stack usage

No need to save all registers. A lot of pushes/pops can be avoided by defining certain registers as "temporary" registers.

This saves CPU cycles, memory and ROM space.

 

*
New scratch-pad memory layout

Taking a more byte oriented approach where possible for saving on scratch pad memory.

A small memory area is reserved for self modifying code that is used to optimize loops

and writes to/from VDP, bank-switch trampoline code, etc.

 

*
Task scheduler

Reduced memory usage for timers. Now use 4 bytes instead of 8 bytes for a timer slot.

It's also possible to have multiple timer tables and the scheduler itself offers some more possibilities.

The task scheduler is at the core of the library and plays an important role.

 

*
Better support for bank-switching

 

*
Support for using "Monitor" OS routines and disk+file I/O support

 

*
Consistent function labels, parameter passing, etc.

Link to comment
Share on other sites

I know that each developer has his own idea of what a library should support.

And most of us have collected or written own assembly language routines.

 

As a developer, what functions would you like to see supported ?

 

Any ideas welcome :)

 

i read many time first user manual, and for me, the main was already included...

of course you could add more advanced routines like graphics operations, scrolling, etc,..

 

and a tutorial with very simple program could be very great for beginner (like me ;) )

Link to comment
Share on other sites

I know that each developer has his own idea of what a library should support.

And most of us have collected or written own assembly language routines.

 

As a developer, what functions would you like to see supported ?

 

Any ideas welcome :)

 

i read many time first user manual, and for me, the main was already included...

of course you could add more advanced routines like graphics operations, scrolling, etc,..

 

and a tutorial with very simple program could be very great for beginner (like me ;) )

 

Yes, the manual by itself only covers the functionality but unfortunately no examples.

This time, I'll be spending more time on writing some tutorials ;)

Link to comment
Share on other sites

Here's a short demo for testing some of the routines in SPECTRA2

 

http://www.youtube.com/watch?v=p8n5WWOn4Zs

 

The demo runs as a 8K cartridge ROM. It does not use the 32K memory expansion, only 256 bytes of scratchpad memory and 16K VDP video memory.

 

The video was recorded with the MESS emulator.

Unfortunately you can't see the full effect, because a lot of frames got skipped while uploading the video to Youtube.

The skull gets blurry on the real deal :ponder:

 

I might use this as the intro screen for my future games :D

 

Below is the source code. I'll be going into details on what these specific calls do, as I move along.

Obviously there is still some room for improvement.

But you get the picture: TMS9900 assembly language is fun :cool:

 

 

********@*****@*********************@**************************
       AORG  >6000
*--------------------------------------------------------------
* Cartridge header
*--------------------------------------------------------------
GRMHDR  BYTE  >AA,1,1,0,0,0
       DATA  PROG
       BYTE  0,0,0,0,0,0,0,0
PROG    DATA  0
       DATA  KERNEL
       BYTE  9
       TEXT  'SPECTRA 2'
*--------------------------------------------------------------
* Include required files
*--------------------------------------------------------------
       COPY  "D:\Projekte\spectra2\tms9900\runlib.a99"
***************************************************************
* Main 
********@*****@*********************@**************************
MAIN    LI    R0,>0B00
       MOVB  R0,@BVR7              ; Set background drop to light yellow
       BL    @VIDOFF               ; Turn screen off
       BL    @SMAG2                ; Sprite magnification 2
       BL    @PUTVRS               ; Set all VDP registers  
       BL    @FILVM
       DATA  >0000,>00,16000       ; Clear VDP memory
       BL    @FILVM
       DATA  >0380,>10,16
       BL    @PUTVM
       DATA  >1000,SKULPT,68*8     ; Load skull sprite patterns into VDP memory
       BL    @PUTVM
       DATA  >0300,SPRITE,12*4     ; Load overlay sprites into VDP memory
       BL    @CPYM
       DATA  SPRITE+48,>834A,17*4  ; Copy skull sprites from ROM to RAM      
       BL    @PUTVM
       DATA  >0808,LETTRS,13*8     ; Load character patterns into VDP memory
       BL    @VIDON                ; Turn screen on
       BL    @PUTVR1               ; Write VDP register #1
       MOV   @TPDAT1,@WTIMER       ; Pointer to timer table
       MOVB  @BDIG+1,@BTIHI        ; Set highest slot to 1
       BL    @MKSLOT
       DATA  >0000,0               ; Need to fix this!
       DATA  >0101,SKULL,>0000     ; Timer 1, runs 60 times a second
       B     @TMGR
TPDAT1  DATA  >838C                 ; Address of timer table

***************************************************************
* Controller - Scroll skull
***************************************************************
SKULL   DECT  RSTACK
       MOV   R11,*RSTACK
       BL    @PUTVM
       DATA  >0330,>834A,68        ; Dump RAMSAT to VDP                
***************************************************************
* Calculate sprite positions for next frame
***************************************************************        
       LI    TMP0,>834A
       CLR   TMP1
       LI    TMP2,16
SKULLA  MOVB  *TMP0,TMP1               
       AI    TMP1,->0200           ; Y=Y-2
       CI    TMP1,>D000            ; Skip Y position >D0
       JNE   SKULLB
       AI    TMP1,->0100           
SKULLB  CI    TMP1,>1900            ; Y position >19 reached ?
       JEQ   SKULLD                ; Yes, remove overlay sprites
       CI    TMP1,>0800            ; Y position >08 reached ?
       JLE   SKULLE                ; Yes, stop scrolling Wunderkind
SKULLC  MOVB  TMP1,*TMP0
       AI    TMP0,4
       DEC   TMP2
       JNE   SKULLA
       B     @M3POPR        
***************************************************************
* Remove overlay sprites
***************************************************************         
SKULLD  DECT  DSTACK
       MOV   TMP0,*DSTACK
       DECT  DSTACK
       MOV   TMP1,*DSTACK
       DECT  DSTACK
       MOV   TMP2,*DSTACK
       BL    @FILVM
       DATA  >0300,>E0,12*4        ; Put overlay sprites off-screen
       MOV   *DSTACK+,TMP2
       MOV   *DSTACK+,TMP1
       MOV   *DSTACK+,TMP0
       JMP   SKULLC
***************************************************************
* Stop scrolling skull and prepare shadow image
***************************************************************           
SKULLE  BL    @PUTVM
       DATA  >0264,TEXT,24         ; Put text 'A RETROCLOUDS PRODUCTION'       
       LI    TMP0,>834A
       LI    TMP1,>0202
       LI    TMP2,16
SKULLF  A     TMP1,*TMP0            ; Prepare RAMSAT for flicker shadow image
       AI    TMP0,4
       DEC   TMP2
       JNE   SKULLF        
       BL    @PUTVM
       DATA  >0400,>834A,17*4      ; Dump RAMSAT again        
       BL    @MKSLOT
       DATA  >0120,WKDELAY,>0000   ; Replace slot
       B     @M3POPR
***************************************************************
* Controller - Small delay
***************************************************************
WKDELAY DECT  RSTACK
       MOV   R11,*RSTACK
       BL    @MKSLOT
       DATA  >0101,WKTOG           ; Toggle SAT pointers
       DATA  >0240,FREEZE,>0000    ; Freeze after 1 second
       MOVB  @BDIG+2,@BTIHI        ; Set highest slot to 2
       B     @M3POPR
***************************************************************
* Controller - Toggle SAT pointer for shadow image
***************************************************************        
WKTOG   DECT  RSTACK
       MOV   R11,*RSTACK        
       LI    TMP0,>0600            ; SAT at >300 ?
       CB    @BVR5,TMP0
       JEQ   WKTOGA                ; Yes, switch to >0400
       MOVB  TMP0,@BVR5            ; No,  switch to >0300
       JMP   WKTOGB
WKTOGA  MOVB  @BDIG+8,@BVR5         ; Switch to >0400
WKTOGB  BL    @PUTVR5               ; Set VDP register 5
       B     @M3POPR
***************************************************************
* Controller - Start allover again
*************************************************************** 
FREEZE  DECT  RSTACK
       MOV   R11,*RSTACK
       MOVB  @BDIG+1,@BTIHI
       BL    @MKSLOT 
       DATA  >0120,WKEXI2,>0000
       B     @M3POPR 
WKEXI2  JMP   $
       
***************************************************************
* Skull sprite layout
***************************************************************
*  159D
*  26AE
*  37BF
*  48CG   
***************************************************************
* Skull sprites
********@*****@*********************@**************************        
SPRITE  DATA  >9040,>400B           ; Overlay 1
       DATA  >9060,>400B           ; Overlay 2
       DATA  >9080,>400B           ; Overlay 3
       DATA  >90A0,>400B           ; Overlay 4
       DATA  >B040,>400B           ; Overlay 5
       DATA  >B060,>400B           ; Overlay 6
       DATA  >B080,>400B           ; Overlay 7
       DATA  >B0A0,>400B           ; Overlay 8
       DATA  >FE40,>400B           ; Overlay 9
       DATA  >FE60,>400B           ; Overlay A
       DATA  >FE80,>400B           ; Overlay B     
       DATA  >FEA0,>400B           ; Overlay C                                     
       DATA  >9140,>0001           ; Sprite 1
       DATA  >B140,>0401           ; Sprite 2
       DATA  >D140,>0801           ; Sprite 3
       DATA  >F140,>0C01           ; Sprite 4
       DATA  >9160,>1001           ; Sprite 5
       DATA  >B160,>1401           ; Sprite 6
       DATA  >D160,>1801           ; Sprite 7
       DATA  >F160,>1C01           ; Sprite 8
       DATA  >9180,>2001           ; Sprite 9
       DATA  >B180,>2401           ; Sprite A
       DATA  >D180,>2801           ; Sprite B
       DATA  >F180,>2C01           ; Sprite C
       DATA  >91A0,>3001           ; Sprite D
       DATA  >B1A0,>3401           ; Sprite E
       DATA  >D1A0,>3801           ; Sprite F
       DATA  >F1A0,>3C01           ; Sprite G        
       DATA  >D000
***************************************************************
* Skull sprite patterns
********@*****@*********************@**************************         
*-- Sprite 1
SKULPT  DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FEFC,>F8F0,>E0E0
*-- Sprite 2
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FEFE,>FEFE
       DATA  >C0C0,>8080,>8080,>0000
       DATA  >0000,>0000,>0000,>0000
*-- Sprite 3
       DATA  >FEFC,>FCFC,>FCFC,>F8F8
       DATA  >F8F8,>F8F8,>F8F8,>FCFC
       DATA  >0001,>0307,>0F0F,>1F1F
       DATA  >3F3F,>0000,>0000,>0000
*-- Sprite 4
       DATA  >FEFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >0387,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
*-- Sprite 5
       DATA  >FFFF,>FFFE,>FCF8,>F0C0
       DATA  >8000,>0000,>0000,>0000
       DATA  >FFFF,>0000,>0000,>0000
       DATA  >0000,>0000,>0000,>0000
*-- Sprite 6
       DATA  >0000,>0000,>0000,>0000
       DATA  >0000,>0000,>0000,>0000
       DATA  >0001,>0303,>0303,>0303
       DATA  >0303,>0301,>0101,>0100
*-- Sprite 7
       DATA  >FCFE,>FFFF,>FFFF,>FEFC
       DATA  >F8C0,>0000,>0000,>3CFF
       DATA  >0000,>0000,>0103,>0707
       DATA  >0F0F,>1F1F,>1E08,>0000
*-- Sprite 8
       DATA  >FFFE,>FCF0,>E1F3,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >0000,>0040,>8000,>0084
       DATA  >C4FC,>FCFC,>FEFF,>FFFF
*-- Sprite 9
       DATA  >FFFF,>0000,>0000,>0000
       DATA  >0000,>0000,>000F,>3F7F
       DATA  >FFFF,>FF3F,>0F03,>0000
       DATA  >0000,>0000,>00F0,>FCFF
*-- Sprite A
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFF8
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFE
*-- Sprite B
       DATA  >0000,>0000,>80E0,>F0F8
       DATA  >FC7E,>3E02,>0000,>0000
       DATA  >7C00,>0000,>0000,>0000
       DATA  >0101,>0101,>0100,>0000
*-- Sprite C
       DATA  >0000,>0000,>0000,>0000
       DATA  >0808,>0C0F,>1FFF,>FFFF
       DATA  >0000,>0C07,>0301,>0123
       DATA  >131F,>3FFF,>FFFF,>FFFF
*-- Sprite D
       DATA  >FFFF,>FFFF,>FFFF,>FF7F
       DATA  >1F0F,>0703,>0301,>0000
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FF7F
*-- Sprite E
       DATA  >8080,>C0C0,>E0E0,>E0E0
       DATA  >C0C0,>C080,>8080,>0000
       DATA  >7F3F,>1F1F,>0F0F,>0707
       DATA  >0303,>0307,>070F,>0F1F
*-- Sprite F
       DATA  >0000,>000F,>1F3F,>7FFF
       DATA  >FFFF,>FFFF,>FF7F,>1F1F
       DATA  >3F3F,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
*-- Sprite G
       DATA  >1F3F,>3F7F,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF        
*-- Sprite H
       DATA  >FFFF,>FFFF,>FFFF,>FFFF        
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF
       DATA  >FFFF,>FFFF,>FFFF,>FFFF                
***************************************************************
* Pattern definitions for text
********@*****@*********************@**************************        
LETTRS  DATA  >0008,>1C14,>223E,>4200   ; A (1)
       DATA  >007E,>2226,>7C28,>2600   ; R (2)
       DATA  >003C,>4078,>404C,>7000   ; E (3)
       DATA  >007E,>1010,>1010,>1000   ; T (4)
       DATA  >001E,>3262,>4266,>3C00   ; O (5)
       DATA  >001E,>3260,>4066,>3C00   ; C (6)
       DATA  >0010,>1030,>2020,>3C00   ; L (7)
       DATA  >0012,>3222,>624E,>7A00   ; U (
       DATA  >007C,>2222,>263C,>7800   ; D (9)
       DATA  >001E,>2240,>3C04,>3800   ; S (10)
       DATA  >007E,>2224,>3820,>2000   ; P (11)
       DATA  >0038,>0010,>1010,>1000   ; I (12)
       DATA  >0002,>4262,>725E,>4600   ; N (13)
***************************************************************
* Text 'A RETROCLOUDS PRODUCTION'
********@*****@*********************@**************************           
TEXT    BYTE  1,0,2,3,4,2,5,6,7,5,8,9,10,0,11,2,5,9,8,6,4,12,5,13
       END

  • Like 1
Link to comment
Share on other sites

Ahh, you know I'm a sucker for demo effects in 8K cartridges. Very cool ! :thumbsup:

 

And I like the implementation. Thanks for the nice source.

 

I better go do something about my next demo. It's been a while.

 

:cool:

 

Thanks :)

 

Yeah, I wanted to do something with many "magnified" sprites.

 

On a sidenote: Could imagine having some horizontal space shooter game where you have a boss spaceship with about 30 16x16 sprites.

Your ship being another 2 sprites (for multiple colors). Bullets/bombs could be normal tiles. ok, I'm getting carried away now :ponder:

Link to comment
Share on other sites

On a sidenote: Could imagine having some horizontal space shooter game where you have a boss spaceship with about 30 16x16 sprites.

Your ship being another 2 sprites (for multiple colors). Bullets/bombs could be normal tiles. ok, I'm getting carried away now :ponder:

Yeah, knowing the hardware and dreaming up stuff that would have blown your socks off in the heyday. I love that. :D

Edited by sometimes99er
Link to comment
Share on other sites

Glad to BE back. I'm not technically "back" just yet though.... Still have another week here in Key West and then a week in Georgia playing--- then I have to go to the studio for a week.... But on the bright side--- I'll pretty much have 3 weeks completely off before I have to go out for another month. :) Perhaps we can finish Calimari Carl and get some Beryl work done in that 3 week window. :)

Link to comment
Share on other sites

scratch-pad memory usage

 

I've been thinking a lot about this library lately and what I would like it to become.

 

I see SPECTRA2 more as a miniature operating system for running games. It will help the processing of input (joystick, keyboard) and

output (video, sound and speech). A core concept is the use of timers for running multiple tasks.

Ofcourse it will also include some utility functions that help in the communication with the VDP, setting up timers etc.

 

Therefore I have decided that I won't be including many super duper high-level VDP tricks in the library.

Why? Because I think such high-level routines put a lot of restrictions on the programmer.

What I like about assembly language is that -as a programmer- you have full control. So I don't want SPECTRA2 to put too many constraints

on the programmer. Instead it should offer a basic structure, help master basic functions and leave the game programmer the freedom

to do what he needs to do, concentrate on the game :)

 

The main focus is that it should support games that run from the cartridge space on a bare TI-99/4A console.

In that case we only have 256 bytes of CPU RAM (scratch-pad memory) to our disposal. If you think about it as a mini-os, then it's

clear it also needs some memory for its own. I've set the limit (including register workspace) to be 64 bytes. That is 25% of the

available memory. Is that a good value? I don't know, so let's go through the details.

 

Address         Description				Size (bytes)
=======		=================================	============
>3200	  A	Register Workspace				32 
>8320	  B 	VDP shadow registers			   	 8
>8328     C	Columns per row				 	 1
>8329	  D	Copy of VDP status byte				 1
>832A	  E	Current Y position				 1
>832B	  F	Current X position				 1
>832C	  G	Address of timer table				 2
>832E	  H	Highest slot in use				 1
>832F	  I	Internal counter for timers			 1
>8330	  J	Address of sound table				 2
>8332	  K	Virtual game keyboard flags		 	 2
>8334	  L	Mini-os status flags				 1
>8835	  M	Loop code + return from stack		        12
==================================================================
Total bytes							65

 

Wait a minute, that's 65 bytes instead of 64! True, so we'll have to squeeze out 1 more byte.

Highest candidate on the list for now is [C]. Considering that about 99% of the games I know

run in 32 columns per row, this is perhaps a value we can get rid of. But let's go through each of the items first.

A) Register workspace

That's obvious right? On the TMS9900 CPU we need a workspace in memory to hold the 16 registers.

 

B) VDP shadow registers

The VDP registers are write-only. Therefore, we keep a copy in memory so we can easily set some of the registers or

set certain bits and then dump them to the VDP all at once. Or one at a time as you require it.

This concept is not new. It was already there in SPECTRA but I have now reduced required memory from 16 bytes to 8 bytes.

 

C) Columns per row

Depends on the video mode (32, 40 or 64). This value can help in calculating the VDP offset based on row and column information.

If this needs to go, it could be replaced with one of the flags in (L) or just assume we only write games in 32 column mode.

 

E and F) Current Y and X position

This is new in spectra2. We'll have some functions you can use to put tiles at specific coordinates without you having to calculate

each individual position.

 

G) Address of timer table

We can now have multiple timer tables and the timer table can also be relocated. This offers more flexibility compared to the original

SPECTRA implementation where we only had a fixed address.

 

H) Highest slot in use

Keeps track of the highest slot used in the timer table. Was already present in SPECTRA. Size is now 1 byte instead of 2 bytes.

That still allows 256 slots, so more than enough if you ask me :-)

 

I) Internal counter for timers

Already had that in SPECTRA. Size is now 1 byte instead of 2 bytes.

 

J) Address of sound table (tune)

The built-in sound player will be compatible with the ISR sound format. The tune can either be in VDP memory or ROM/RAM.

This is controlled by one of the bits in (L).

 

K) Virtual game keyboard flags

Already had the concept of a virtual game keyboard in SPECTRA. The idea is that we have a keyboard (including mapping to joysticks)

and each bit represents a game key. This is ok for arcade games where you only use a few keys (plus redo/back).

Dunno if it will be enough for other types of games though. As any game depends on input I think it's ok to sacrifice 2 bytes.

 

L) Mini-os status flags

New. These status flags keep track of the environment SPECTRA2 is running in. Below are the flags(bits) I can think of right now.

Dunno if 1 byte will be sufficient in the future though.

 

  • Sound table is in VDP memory

  • Speech synthesizer present

  • 32K RAM expansion present

  • NTSC/PAL console

  • 40 columns mode

  • 64 columns mode

 

M) Loop code + return from stack

This is new in SPECTRA2. It contains some machine code that is copied from ROM on library startup.

We can easily modify the machine code during runtime by overwriting some of the opcodes.

 

Here's the code that is copied on startup. I'll be going into detail once I covered spectra2 register usage. Perhaps we can squeeze out some more bytes here.

What I can say is that -among others- the tight loop is used for reading/writing to the VDP.

 

The "return from stack" code is there for speed reasons and because I want to implement some kind of routine for calling subroutines accross 8K banks.

By fiddling with the "return from stack" code, subroutines could become bank-aware.

 
* ; Tight loop
       DATA  >0000                 ; \ M3LOOP: opcode+operands set by caller
       DATA  >0606                 ; |         DEC R6 (TMP2)
       DATA  >16FD                 ; /         JNE M3LOOP
       DATA  >045B                 ; /         B   *R11
* ; Return from stack
       DATA  >C2FD                 ; \ M3POPR: MOV *RSTACK+,R11
       DATA  >045B                 ; /         B   *R11

This is still a work-in-progress, but I think we've got the basic scratch-pad memory structure going. I still have a lot of questions

at this time. For one I want to be able to call "MONITOR" and GPL functions. Not sure if we're on the safe with the current setup.

 

In fact, we haven't talked about register and stack-usage yet. That will be for one of the next posts ;)

Link to comment
Share on other sites

Very nice.

 

How about dropping the VDP shadow registers and functions, and simply supply a good bunch of equates ? Wouldn't it save a few bytes both in RAM and ROM ?

 

:cool:

 

Thanks. Yes, it would save 8 bytes in RAM. On the other hand I could lose some of the flexibility for the subroutines I have in mind.

Considering that we only have 256 bytes to play with, I'd say being able to save 8 bytes is a lot.

Thinking about this more I could reclaim a lot of the flexibility I had in mind by clever register usage.

 

I've already decided that I'll drop the columns per row value. It saves 1 byte and a few bits in the mini-os status byte :)

So, this means I could save 9 bytes in total.

 

That doesn't mean the mini-os memory usage will be 9 bytes less. What I didn't talk about yesterday is that I'll need 1 more byte for the sound player. I also don't know if I'll need 1 additional byte for a random seed. I know we had a thread about this on Atariage just a while ago. I'll have to do some research on that topic.

 

I've also been thinking on using some of the "monitor" & GPL routines that are in the 99/4A system roms. In that case I'll have to

make my memory layout "compatible". That certainly looks like a challenge.

 

Did you know, that there is a coincidence check routine burried in the system rom that allows you to do coincidence detection between sprites/sprites, sprite/tiles, tiles/tiles. Implementing a coincidence detection routine would take quite some ROM space.

That is why I'm looking in using this one.

 

More to follow.....

 

 

On a sidenote: Writing and sharing ideas on spectra2 really helps me focus on what is important ;)

Link to comment
Share on other sites

Filip--- this is pretty exciting man... Re-reading this thread is really eye opening!!! 256 bytes of RAM..... Holy cow man..... :) Imagine today's programmers for modern machines being told they can only use 256 bytes of scratchpad.... Ha!

 

Your clever scheme here is pretty awesome.... To see how you put it all

together.... Truly cool man... :)

Link to comment
Share on other sites

Filip--- this is pretty exciting man... Re-reading this thread is really eye opening!!! 256 bytes of RAM..... Holy cow man..... :) Imagine today's programmers for modern machines being told they can only use 256 bytes of scratchpad.... Ha!

 

Your clever scheme here is pretty awesome.... To see how you put it all

together.... Truly cool man... :)

 

thanks :)

 

I hope to finish the scratch-pad memory setup soon. Once that is done I'll be able to "convert" my Tutankham source code for using the SPECTRA2 calls.

Link to comment
Share on other sites

I've added a wiki page on ninerpedia for keeping track of the spectra2 documentation.

Ofcourse this Atariage thread will remain the main thread for sharing and discussing further developments.

 

But it's kinda difficult keeping a changing documentation in a forum format, that's why I chose the wiki format: It allows others to join and make corrections and enhancements.

 

From time to time I'll collect the changes in the forum posts and update the wiki accordingly.

 

You can check it out here.

 

Still learning how to format a wiki, so bare with me people :D

Link to comment
Share on other sites

scratch-pad memory usage (update 1)

 

ok, I have a major update on scratch-pad memory usage people :)

 

I've been tinkering with the register usage and I removed the VDP shadow register feature.

Due to this scratch-pad memory requirement has dropped from 65 bytes to 50 bytes.

That means we're now using 19.5% instead of 25% of the available scratch-pad memory.

 

Here are the updates:

 

Address         Description				Size (bytes)
=======		=================================	============
>8300	  A	Register Workspace				32 
>8320	  G	Address of timer table				 2
>8322	  J	Address of sound table				 2
>8324	  L	Mini-os status flags				 2
>8326	  M	Loop code + return from stack		        12
==================================================================
Total bytes							50

 

A) Register workspace

That's obvious right? On the TMS9900 CPU we need a workspace in memory to hold the 16 registers.

 

B) VDP shadow registers

Dropped this feature. We should be able to manage without it.
(8 bytes saved)

 

C) Columns per row

Dropped this feature. We'll definitely manage without it.
(1 byte saved)

 

E and F) Current Y and X position

We'll use a general purpose register.
(2 bytes saved)

 

G) Address of timer table

We can now have multiple timer tables and the timer table can also be relocated. This offers more flexibility compared to the original

SPECTRA implementation where we only had a fixed address.

 

H) Highest slot in use and I) Internal counter for timers

We'll use a general purpose register.
(2 bytes saved)

 

J) Address of sound table (tune)

The built-in sound player will be compatible with the ISR sound format. The tune can either be in VDP memory or ROM/RAM.

This is controlled by one of the bits in (L).

 

K) Virtual game keyboard flags

We'll use a general purpose register.
(2 bytes saved)

 

L) Mini-os status flags

I've decided to use 2 bytes instead of 1 byte for the status flags.
(1 byte lost)

 

M) Loop code + return from stack

This is new in SPECTRA2. It contains some machine code that is copied from ROM on library startup.

We can easily modify the machine code during runtime by overwriting some of the opcodes.

 

You'll notice that I moved quite a few values into the general purpose registers.

Considering we have 16 registers in 1 workspace to our disposal, we might as well use them.

 

I'll be going into the details on register usage soon :)

Link to comment
Share on other sites

I like it. :)

 

How about offering 2 or 3 sound table pointers. Then I could start one sound table with music (I could choose to use channel 1 and 2), and then have other sound tables (using channel 3). Independent music and game soundeffects !?

 

Well, the sound system I have in there is still very primitive to say the least.

That could also have to do with the fact that my music/sound skills are like zero, so perhaps I have neglected that area a bit :)

I'll for sure consider it. For now I'm happy I'm kinda compatible with the ISR sound routine.

 

Yeah, that makes you wanna take a closer look at the Colecovision BIOS. The sound player they have in there really kicks a****

Link to comment
Share on other sites

register usage

 

So let's talk registers now. Below you find the overview on how the registers will be used in spectra2.

 

!important!

There is a comprehensive set of equates available for addressing all of the below registers. It is strongly advised you use these equates where possible.

It'll allow register reorganization without you having to modify your program big time.

There are also equates available for accessing the high/low bytes of any of the mentioned registers.

 

 

registers       Purpose				
=========	============================================================
R0-R3      A    General purpose registers
R4-R7      B    Temporary registers
R8         C    Return stack and data stack pointer
R9         D    Address of return routine (M3POPR)
R10        E    Highest slot in use + Timer counter
R11        F    Subroutine return address
R12        G    Virtual game keyboard flags or keycode
R13        H    Copy of VDP status byte and internal counter for sound player
R14        I    YX coordinates of cursor
R15        J    VDP write address
=============================================================================

 

A) General purpose registers (R0-R3)

You can use these registers in your subroutines, but if you change any of these registers you'll have to push them on the stack first.

Ofcourse upon subroutine exit you'll have to pop the old values of the stack. There are subroutines to help you with that.

If your subroutine is called as a timer, then R0-R2 will contain information that identifies the slot, target tick count, etc.

Hence the requirement for pushing/popping if you change R0-R2.

Also R0-R3 are the registers to use if you are going to implement nested subroutines.

 

 

B) Temporary registers (R4-R7)

Use these registers as you please. They are considered temporary and they can (and will) be overwritten if you call any of the

built-in functions. Nonetheless, most of the time you are safe to use R4-R7 in favour of the (A) registers.

It'll save you the need to push & pop.

Please use the TMP0..TMP3 equates instead of R4-R7. The registers may be reorganized at a later time. But you are safe if you use TMP0..TMP3

 

 

C) Stack pointer (R8)

Pointer to both subroutine return stack and data stack. The stack pointer is set by the spectra2 initialisation routine.

For debugging purposes you could set the data stack equate to another register, e.g. R7

 

 

D) Address of return routine (R9)

The address is is set by the spectra2 initialisation routine. It points to the M3POPR machine code in scratch-pad memory.

For exiting a nested subroutine a "B *R9" is to be used.

Benefit: opcode size is only 2 bytes instead of 6 bytes when using "B @>xxxx". Will also be used when a return accross ROM memory banks is required.

 

 

E) Highest slot in use & internal counter for timers (R10)

The high byte of R10 keeps track of the highest slot used in the timer table.

The low byte of R10 is the timer tick counter and is updated every 1/50th or 1/60th of a second.

 

 

F) Subroutine return address (R11)

Same use as in Editor/Assembler. Contains the return address when doing "BL xxxx"

 

 

G) Virtual game keyboard flags (R12)

Already had the concept of a virtual game keyboard in SPECTRA1. The idea is that we have a keyboard (including mapping to joysticks) and each bit represents a game key. Most likely there will be an additional keyboard scan routine that can hold the typed key and a status flag in R12.

 

 

H) Copy of VDP status register and internal counter for sound list (R13)

The high byte of R13 contains a copy of the VDP status register. This byte is automatically updated by the timer manager.

The low byte of R13 contains an internal counter used by the sound player.

 

 

I) Current Y and X position (R14)

This is new in spectra2. We'll have some functions you can use to put tiles & text at specific coordinates without you having to calculate each individual position.

 

 

J) VDP write address (R15)

Contains the address of the VDP data window. The address is set by the spectra2 initialisation routine.

This register is heavily used by the built-in functions responsible for VDP communication.

Link to comment
Share on other sites

scratch-pad memory usage (update 2)

 

Well, this isn't really a big update. Just updating the documentation.

 

Guess that the memory layout pretty much has stabilized. At least for a basic installation.

By that I mean that each game or program using spectra2 must use the range >8300 - >8331 as described.

I call this range "memory area 1".

 

There might be a small "memory area 2" in the future. This setting will be controlled by one of the flags in (D).

Reason for that is that some of the routines may require some additional pointers for doing some stuff.

However this area will be optional and depends on the features you use.

 

I'm trying to get some modular structure in place without bloating memory if the feature's aren't switched on.

We'll see, these are just all ideas for now :D

 

spectra2_scrpad_memlayout.png

 

A) Register workspace

That's obvious right? On the TMS9900 CPU we need a workspace in memory to hold the 16 registers.

 

B) Address of timer table

We can now have multiple timer tables and the timer table can also be relocated. This offers more flexibility compared to the original

SPECTRA implementation where we only had a fixed address.

 

C) Address of sound table (tune)

The built-in sound player will be compatible with the ISR sound format. The tune can either be in VDP memory or ROM/RAM.

This is controlled by one of the bits in (D).

 

D) Mini-os status flags

These flags will be used for controlling various features of the library. This is what I have in mind:

 
* Bit   Description
* ===================================================================
* 15	Sound player tune source        1=ROM/RAM       0=VDP MEMORY
* 14	free
* 13	free
* 12	Keyboard mode                   1=real          0=virtual
* 11	free
* 10	Timer has hyperthread           1=yes           0=no	
* 09	free
* 08	free
* 07	free
* 06	free
* 05	free
* 04	free
* 03	free
* 02    Speech synth present            1=yes           0=no
* 01	VDP PAL version                 1=yes           0=no
* 00	32K memory expansion		1=yes	        0=no

 

E) Loop code + return from stack

This is new in SPECTRA2. It contains some machine code that is copied from ROM on library startup.

We can easily modify the machine code during runtime by overwriting some of the opcodes.
Edited by retroclouds
Link to comment
Share on other sites

Timers

 

Next to the VDP stuff the most important feature for me are the timers.

 

If you are in to writing games, then you'll face the situation where you have multiple actions

that must run on certain intervals. That could be moving a sprite, waiting for keyboard input, doing some game logic, etc.

 

For dealing with that I wrote a loop that continuously checks a flag in the VDP status register.

The flag itself is set by the TMS9918 video display processor. It gets set 50 or 60 times a second depending if we're on PAL or NTSC.

 

From the TMS9918 VDP programmers's guide:

Interrupt Flag (F)

The F flag in the Status Register is set equal to 1 at the end of the raster scan of the last line of

the active display, just before the Backdrop color at the bottom of the screen begins. It is reset

to a 0 after the Status Register is read or whenever the VDP is externally reset (hardware

reset). If the Interrupt Enable bit located in VDP Register 1 is active (1), then the VDP interrupt

output line (INT) will be active (0) whenever the F status flag is 1.

NOTE ¯

The Status Register needs to be read frame-by-frame in order to clear the interrupt

and receive the new interrupt for the next frame.

 

If set, we loop over a table in memory that hold pointers of the subroutines to execute, some internal counter data, etc.

When the interval matches it executes the matching subroutine.

This for sure is nothing I invented. The concept was not uncommon on several 80's game machines.

At least not on Colecovision and MSX that is :)

 

If you want to read more about how my implementation works, then check out the documentation for spectra1.

What I'm doing for spectra2 is to reduce memory usage and add some new features.

 

What I like the most about timers is that they allow me to "chop up" a game in several small building blocks (subroutines).

I can develop them independently. It's also possible to make them run more often (=faster animation) or slow animations down by letting

them run less often. Once you get the hang of it, you can also chain timers and do some funky stuff on the fly.

 

Sounds great right? Well, yes up to a certain extend it does. It does have some backdraws though.

And that's what I wanna talk about today:

 

1) Due to the fact that the subroutines are basically triggered by the VDP, it means that -in best case- they can only run 50 or 60 times a second.

I know, compared to Extended Basic that is still a lot. But we're talking assembly language here people.

We're on the bare metal and want to run as fast a possible.

 

2) Due to the nature of the beast, all timers are called during the VDP read/write memory window.

That's ok, because in 99.5% we want to do something with VDP memory anyway (dump patterns, read screen, update, sprites, ....)

The bad thing is that part of the VDP memory window may already be used up, if you need to do some kind of calculations or handle

complex game logic first.

 

So to deal with that, I want the possibility to call a subroutine in between VDP interrupts.

Obviously such subroutine will not be able to communicate with VDP memory because we'll be outside the VDP read/write memory window.

But you'll still be able to do other stuff, like control sound generators, let the speech synthesizer talk, read the keyboard, etc.

 

I'll have to find a way to control how often such subroutine is called. At first I was thinking about using some kind of dummy timer slot.

But that is wasting memory if we don't want to use this feature. So most likely the pointer and counter data will be in "memory area 2"

and the feature itself can be turned on/off by a flag.

 

Thinking about it; wonder if I could make use of the timer in the TMS9901? Then again, I might also just enhance the main timer loop for handling this.

 

That's it for today people. Any thought on this ?

Edited by retroclouds
Link to comment
Share on other sites

  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...