Jump to content
IGNORED

PM multiplexor


Heaven/TQA

Recommended Posts

eg I have a timer of value 5, it goes 5,4,3,2,1,IRQ,5,4,3,2,1,IRQ2 ? but If I want the 2nd one to be 10 will putting 10 into AUDF4 at any point during that 2nd countdown change the internal countdown or will it only fetch my 10 when it hits IRQ2.

 

My tinkerings led me to conclude that the value 10 would have to be loaded during the time before IRQ1.. When the timer elapses for IRQ 1 then the value 10 is loaded into the counter from the AUDF4 register and begins counting down.. So you have to in 'IRQ0' you have to load the delta value you need for the time between IRQ1 and IRQ2..

 

In that form it's not that useful for rasters in a real world scenario, with DLIs showing up and an undetermined workload per interrupt and other delays, not if you want to keep it bang on line accurate..

 

It's exactly the same as the CIAs, except there's no force load, only by STIMER in this case..

 

That's how it seemed to run on Altirra at least, which I've heard heralded here for its cycle accurate timer emulation, and how I understand it from the docs as well.. If the realities any different I'd love to know exactly how it's different..

Edited by andym00
Link to comment
Share on other sites

Yeah, that's what I thought. Sorry Analmux I just wasn't getting from what you were saying, if you meant it did or didn't need something to reset it because I'd asked an either/or question (where the first option was it did work just putting a new value in) and you said yes that's correct but then your explanation made it sound the other way again. It must just be my tired brain today that nothing is making sense.

 

 

 

Pete

Link to comment
Share on other sites

Look here, I already gave a short explanation:

http://www.atariage.com/forums/topic/149957-timer-irqs-as-dlis-tutorial/page__view__findpost__p__1829748

 

(Update:)

 

Setting up the timer IRQs involves the following: (suppose we use timer 4)

 

(1) Initialize timer (f.e. start at scanline 64) : Here we use STIMER

(2) Activate a timer pattern, f.e. at scanline 64, 96, 176, 178, 180, (next frame) 64 (= 376), 96, etc.

 

Compute the deltas: 96-64=32, 176-96=80,178-176=2, 180-178=2, 64-180==376-180=196:

List of deltas: 32, 80, 2, 2, 196, 32, 80, etc. : 32+80+2+2+196=312

Beware: AUDF4+1 is the real delta, so the list of AUDFs is 31,79,1,1,195,...

thus AUDF4_Step1+AUDF4_Step2+AUDF4_Step3+AUDF4_Step4+AUDF4_Step5+5=312

 

For initializing, we first need to be sure to start at scanline 64. We can do this by a DLI.

 

So, suppose this is part of the DLI code:

 sei
lda <IRQTask1
sta $fffe
lda >IRQTask1
sta $ffff
lda #31
sta audf4
sta stimer
lda #4
sta irqen ; enables timer 4 interrupt
cli
ExecuteIRQTask1
jmp ($fffe)
...

 

Suppose timer4 is the only IRQ for now, and we do direct programming of IRQ vector $fffe

 

Now, during IRQTask1 we do

 lda #0 ; this resets the IRQ flag
sta irqen
lda #4
sta irqen
lda #79
sta audf4
... ; other stuff you want to do here
lda <NewIrqAddress
sta $fffe
lda >NewIrqAddress
sta $ffff

 

This triggers the relative timing for IRQTask2, and activates its run address.

 

For IRQTask2, the value #79 is replaced by #1

For IRQTask3, the value #1 is replaced by #1 (actually: no change needed here)

For IRQTask4, the value #1 is replaced by #195 etc. etc.

Edited by analmux
Link to comment
Share on other sites

OK, despite reminding me how to write an IRQ handler ;) You're saying that it's fine to just put new values in AUDF4 and it will reload them when you do so, whereas tests andym00 (and I) has done show it doesn't. You said in your last post that it doesn't reload to its maximum (AUDF4) until it hits 0, so once its hit 0 when does it do the reload? if it's the next cycle or so you're screwed but from your code it takes some time else it would ignore what you put into AUDF4 in that irq handler so meaning if your last gap was 50 scanlines you wouldn't be able to change that if you wanted one in 3 lines, you'd end up with an unwanted one in 50 lines time and then another 3 later.

 

 

Am I getting this totally wrong because I'm getting two different methods from 2 people.

 

 

*edit*

 

In your post you linked to above you have part of the logic of it as this

 

The next timer IRQ is on line n+50, thus (n+50)-n = 50, subtract 1 = 49, store into $D206 (AUDF4)

Then do pokey init (LDA 0 STA $D20F LDA 3 STA $D20F), and then a countdown timer reset (STA $D209 (="STIMER")).

Redefine timer4 IRQ vector

...wait for next interrupt...

again 49 into $D206, pokey init, countdown timer init, redefine timer4 vector

...wait for next interrupt...

now we need to 'close the circle'. scanline n+100+212 (=n+312) is the same as scanline n, but then 1 frame later.

store 211 into $D206, etc.

 

Which includes "pokey init" and "countdown timer init" after each timer trigger.

 

This seems to be the contention where I'm getting lost. If it IS necessary to do those (which it seems to be) then the question goes back to the original one of "does doing all that timer and pokey init stuff every irq kill any chance of having music running?"

 

Pete

Edited by PeteD
Link to comment
Share on other sites

Look here, I already gave a short explanation:

http://www.atariage.com/forums/topic/149957-timer-irqs-as-dlis-tutorial/page__view__findpost__p__1829748

 

(Update:)

 

Setting up the timer IRQs involves the following: (suppose we use timer 4)

 

(1) Initialize timer (f.e. start at scanline 64) : Here we use STIMER

(2) Activate a timer pattern, f.e. at scanline 64, 96, 176, 178, 180, (next frame) 64 (= 376), 96, etc.

 

Compute the deltas: 96-64=32, 176-96=80,178-176=2, 180-178=2, 64-180==376-180=196:

List of deltas: 32, 80, 2, 2, 196, 32, 80, etc. : 32+80+2+2+196=312

Beware: AUDF4+1 is the real delta, so the list of AUDFs is 31,79,1,1,195,...

thus AUDF4_Step1+AUDF4_Step2+AUDF4_Step3+AUDF4_Step4+AUDF4_Step5+5=312

 

For initializing, we first need to be sure to start at scanline 64. We can do this by a DLI.

 

So, suppose this is part of the DLI code:

 sei
lda <IRQTask1
sta $fffe
lda >IRQTask1
sta $ffff
lda #31
sta audf4
sta stimer
lda #4
sta irqen ; enables timer 4 interrupt
cli
ExecuteIRQTask1
jmp ($fffe)
...

 

Suppose timer4 is the only IRQ for now, and we do direct programming of IRQ vector $fffe

 

Now, during IRQTask1 we do

 lda #0 ; this resets the IRQ flag
sta irqen
lda #4
sta irqen
lda #79
sta audf4
... ; other stuff you want to do here
lda <NewIrqAddress
sta $fffe
lda >NewIrqAddress
sta $ffff

 

This triggers the relative timing for IRQTask2, and activates its run address.

 

For IRQTask2, the value #79 is replaced by #1

For IRQTask3, the value #1 is replaced by #1 (actually: no change needed here)

For IRQTask4, the value #1 is replaced by #195 etc. etc.

 

Right, that's exactly what I understood..

So, no way to recover from a DLI knocking you out, or unexpected workload in an IRQ.. Not without precomputing the deltas taking into account how the irqs intersect DLI, and then computing the workload of each interrupt and adjusting deltas correctly..

Link to comment
Share on other sites

OK, despite reminding me how to write an IRQ handler ;) You're saying that it's fine to just put new values in AUDF4 and it will reload them when you do so, whereas tests andym00 (and I) has done show it doesn't. You said in your last post that it doesn't reload to its maximum (AUDF4) until it hits 0, so once its hit 0 when does it do the reload? if it's the next cycle or so you're screwed but from your code it takes some time else it would ignore what you put into AUDF4 in that irq handler so meaning if your last gap was 50 scanlines you wouldn't be able to change that if you wanted one in 3 lines, you'd end up with an unwanted one in 50 lines time and then another 3 later.

 

 

Am I getting this totally wrong because I'm getting two different methods from 2 people.

 

 

Pete

 

 

No.. Look at his code.. He jumps straight into the first interrupt in his list.. Check the code.. He gets the delta times he expects, which will work fine if your just doing colour splits or anything that doesn't care where your position is exactly on the screen.. But since the sprite stuff needs to examine the raster line on the fly to determine if it should continue loading more sprites without waiting for the next interrupt it can't work like that, since it has to figure out where the next raster should be once it's finished whatever needs to be done..

 

You can do it all with deltas and never look at your raster line, but it means calculating the overhead of any DLIs that might fire, plus computing the workload of the interrupt at the scanline, then calculating a true stand-alone delta value.. Which is going to be a bugger load of work i think..

Link to comment
Share on other sites

I'm lost lol

 

Code, understood, simple as you can get irq setup and handler.

 

Deltas, of course, it's the only way to use a timer to emulate a raster interrupt.

 

STIMER to reload the timer, that's me gone lol I've tested code and it seems to need it, you said earlier you needed it too, even Analmux included it in his post he links to.

 

So, I'm back to the question again of. If you don't reset the timers using STIMER to force them to load from the AUDF registers, does it ignore anything you put in AUDFn until it hits 0? If so and you've just had a delta of 40 and you want one of 20, wont you just get one at 40 instead and THEN one at 20? Not scanlines but delta lines.

 

 

Pete

Link to comment
Share on other sites

but I STILL don't get if it's possible to change AUDF4 when it's already counting down. lol

 

You can't.. It only take effect once the current timer value has elapsed..

 

Well that's the way I've been understanding it, but in that case "see previous post". It's quite possible that as I've said, my tired brain just isn't processing this and if so just tell me to go to bed or something lol But I don't see how you can get a "scanline" gap of 30 then 20 lines if after 30 the timer gets reloaded with 30 again. You've got to put the AUDFn value in somewhere and if the only place to do that is when it's already hit 0 and reloaded, how do you get it where you want?

 

 

Pete

Link to comment
Share on other sites

There's no easy way to know for sure your code will execute exactly where you want it just by going, I want one in 3 lines time so store this value, but I STILL don't get if it's possible to change AUDF4 when it's already counting down. lol

In the meantime I gave you the answer already two times. ;)

Of course you can change the value, at any desired moment.

Indeed the link may be not 100% accurate, I admit.

In the examples above I only do it right at the start of an IRQ already being executed.

 

And, to make it clear another time: INIT procedure is only needed ONCE. A write to STIMER is only needed ONCE, not every IRQ!!!!!!!

 

...and if the emu gives another result, it could be the fault of the emu. There are other cases when the emu doesn't do the right thing in this context.

Link to comment
Share on other sites

So, I'm back to the question again of. If you don't reset the timers using STIMER to force them to load from the AUDF registers, does it ignore anything you put in AUDFn until it hits 0?

So let's turn 180 degrees. Why the hell would you need to redefine the timer itself, when you're only waiting for the next interrupt to come. This one will already occur at a desired moment in time.

Link to comment
Share on other sites

OK, let's explain it in again another way:

 

Check out the following list of steps in time

 

-DLI (done by cpu)
-Initialize 1st delta: Write delta1-1 to AUDF4
-Reset timer at desired moment: Write to STIMER
-Enable Timer IRQ
-Execute 1st IRQ (done by cpu)
-Initialize 2nd delta: Write delta2-1 to AUDF4
-do your IRQ stuff (colour cycling)
-Timer counts down in the meantime
(done internally by pokey: in the meantime the
timer (thus NOT AUDF4 itself, but the internal
timer register inside Pokey chip) will start
to countdown)
-Wait for (next) IRQ (done by cpu, do other stuff in meantime)
-IRQ (done by cpu: will happen when timer4 has counted down to zero)
-Initialize 3rd delta: Write delta3-1 to AUDF4
-do your IRQ stuff
-Timer counts down in the meantime
-Wait for IRQ
-IRQ (done by cpu: will happen when timer4 has counted down to zero)
-Initialize 4rd delta: Write delta4-1 to AUDF4
...

 

Remember: We have enough time to redefine the AUDF4 values correctly.

 

Comments:

-writing to AUDFn will not affect state of TIMERn, when TIMERn is already downcounting

-writing to AUDFn is always possible; as simple as LDA #value STA AUDFn

-TIMERn will be (re)loaded with value in AUDFn after reaching 0

 

....etc.

 

Now I don't understand how to explain this any more clear.

Link to comment
Share on other sites

No, your explanations have been fine, just I couldn't get my head around the reloading of TIMERn from AUDFn when TIMERn is ALREADY running.

 

 

This is how I thought it worked

 

 

Say I want IRQs to trigger at line 10, 20,80,90


-DLI (done by cpu)
-Initialize 1st delta: Write delta1-1 to AUDF4   say we're on line 0 and want like 10  so  9?
-Reset timer at desired moment: Write to STIMER
-Enable Timer IRQ

10,9,8,7,6,5,4,3,2,1
-Execute 1st IRQ (done by cpu)
-Initialize 2nd delta: Write delta2-1 to AUDF4		now want line 20  so 20-10 =10 -1   9 again, no change
-do your IRQ stuff (colour cycling)
-Timer counts down in the meantime
(done internally by pokey: in the meantime the
timer (thus NOT AUDF4 itself, but the internal
timer register inside Pokey chip) will start
to countdown)
 9,8,7,6,5,4,3,2,1
 
-Execute 2nd IRQ (done by cpu)
-Initialize 2nd delta: Write delta2-1 to AUDF4		now want line 80  so 80-20 =60 -1   59
-do your IRQ stuff (colour cycling)
-Timer counts down in the meantime
(done internally by pokey: in the meantime the
timer (thus NOT AUDF4 itself, but the internal
timer register inside Pokey chip) will start
to countdown)
9,8,7,6,5,4,3,2,1

-Execute 3rd IRQ (done by cpu)   Now on line 30 not line 80 because by the time we write AUDFn above the timer is already reloaded and counting down?


 

 

Pete

Link to comment
Share on other sites

So, now I've got my head around it (I think). Basically what I need to do is either find the 1st previous DLI to the 1st scanline I want a change, set the 2nd delta and hang around with wsync or similar till the 1st line I actually want a change and do a pseudo IRQ there which sets the next delta (which is reaaaaly the one after next). OR DLI at the top of the screen, do the same thing there and kind of waste an "IRQ" (or at least its pseudo brother which sets the "one after next" AUDFn).

 

Whichever, I've got working code now. Instead of trying to think about it when too tired I just wrote it from scratch instead of messing the the more complex Fist screen code I'd got going. Barring a bug which also wasn't helping that seemed to do the trick but it still doesn't seem right in my head lol

 

Sorry for all the confusion but please never take questions from me the wrong way, I won't be telling you you're wrong, just trying to get the fullest picture possible (eg when in that one post where Analmux linked to a post which said each irq did reset timers etc, then in the body of the same post didn't do that). As I said, just tell me to go to bed ;)

 

 

Pete

Link to comment
Share on other sites

Pete,

 

The only way to be 100% clear is a detailed & tested example.

 

There's no such thing as 'timer underrun' like you seem to think.

 

The timer value written to AUDFn will affect the length of the NEXT cycle. Not the cycle itself.

Writing to AUDFn ONLY defines this length, and it won't interrupt the TIMERn.

 

In general (when not using STIMER) we have the following scheme:

 

-Write LENGTH1 to AUDFn
-In the meantime TIMERn is already counting down
-...
-TIMERn reaches 0
-TIMERn will reload, and takes the value AUDFn is holding at that moment
-At the same moment the IRQ will happen
-Timer will count down from LENGTH1, thus THIS cycle lasts for LENGTH1 steps
-In the meantime we write LENGTH2 to AUDFn, for again the NEXT cycle
-...
-TIMERn reaches 0
etc.

Link to comment
Share on other sites

Pete,

 

The only way to be 100% clear is a detailed & tested example.

 

There's no such thing as 'timer underrun' like you seem to think.

 

The timer value written to AUDFn will affect the length of the NEXT cycle. Not the cycle itself.

Writing to AUDFn ONLY defines this length, and it won't interrupt the TIMERn.

 

In general (when not using STIMER) we have the following scheme:

 

-Write LENGTH1 to AUDFn
-In the meantime TIMERn is already counting down
-...
-TIMERn reaches 0
-TIMERn will reload, and takes the value AUDFn is holding at that moment
-At the same moment the IRQ will happen
-Timer will count down from LENGTH1, thus THIS cycle lasts for LENGTH1 steps
-In the meantime we write LENGTH2 to AUDFn, for again the NEXT cycle
-...
-TIMERn reaches 0
etc.

 

Yup, all understood (well, to my tired brain anyway), coded and tested, see above :) Thanks again for persisting with telling me and not just telling me to dig a hole and settle down in it ;)

 

Pete

Link to comment
Share on other sites

Don't apologise dude, totally my fault for not getting what's an easy concept in the 1st place. The code I had originally was almost right and probably the bug in it was throwing me way off track and I thought rather than write more code when tired or mess up what I'd already got even more, I'd just ask, but I should have waited till I was awake :)

 

 

Pete

Link to comment
Share on other sites

What's the shortest distance it's possible to reassign two sprites in though...?

 

For getting two of the sample players next to each other, the HPOS write should be 3 color clocks before it occurs (1.5 CPU cycles). So if you put player #0 at color clock 39 and then again at 47, then you need to finish STA HPOS by color clock 44. Haven't played with GRAFn but horizontal re-use is more useful and easier for replicating same shapes.

Link to comment
Share on other sites

So this multiplexer changes the Dispay List to alter DLI positions to where it needs to change player/missile positions? I use fixed Display List since some of the stuff I do require color changes, work Antic Mode 4, etc. I use tables to detect what zone the sprite is begins and ends at, and check if zone has been used by a sprite already.

 

Tempest is done in mode4? ;)

 

yeah... it was easy to do...

 

VBL is running the sorter and while the mapper is preparing all necessary stuff for the DLIs I setup the DLI bits in this easy displaylist... I could have had used blank lines but well... I wanted to have gfx there...

 

and for insiders... nobody realised for ages that the Taquart logo was drawn by a c64 scener... ;)

 

Actually Tempest was done in mode 15, but still used fixed zones. I am working with several projects that are using Antic 4 that also require color changes in areas of the screen. How my table method works is a table of 256 bytes will indicate the zones from the vertical positions, 000...0011...1122...,etc. In areas where we don't want sprites to appear, I put in a value more than 128 that simply skips the draw + DLI setup by using a BMI instruction.

 

To indicate if the player number has a sprite using it, I just use a small USED table, turn bits on (1011 will indicate player 1,2 & 4 has been used)

 

looks something like this

 

LDY SpriteNum

LDA VertPos, Y

TAX

LDA TabZone,X

STA TopZone

CLC

TXA

ADC SpriteSize, Y

TAX

LDA TabZone,X

STA BtmZone

LDY TopZone

LDA UseTable,Y

LDY BtmZone

ORA UseTable,Y

ASL

BCS CheckPlyr2

(do player 1 stuff + set DLI)

LDA UseTable,Y

ORA #1

STA UseTable,Y

CheckPly2

ASL

BCS CheckPlyr3

(do player 2 stuff + set DLI)

LDA UseTable,Y

ORA #2

STA UseTable,Y

CheckPlyr3

...

 

This does work if we assume the sprite will only cross one zone at the most and is just a brief example of how I did it.

 

What was tough was trying to get it do complete everything at the beginning of the VBI before the scan line reaches the first zone. It took me some tweaking before it worked right.

Edited by peteym5
Link to comment
Share on other sites

  • 3 months later...

Got a little bored over the post-christmas period and instead of heading down to a gym like any sane person would I instead began thinking about A8 plexers and colourful sprites again, and thanks to Phaeron making some fixes in Altirra (the Pot stuff and the SBX implementation) I found myself thinking far too much about multiplexers again, especially using missles to underlay players as a potential source of some additional colour..

Still very 2600'ish, but amazing how much difference a little colour makes to them.. So I wrote a little test from scratch today with some other idea to improve things performance wise, and..

post-3913-12620192056_thumb.png

It's 64 sprites, though one has gone awol somewhere due to some errant code throwing stuff around memory randomly at the moment.. But, it's not as useful as I thought it would be really.. Something DropZone like would be doable.. Just wondering if there's any point in continuing with such an approach ?

 

My problem is that with this method, and the single line height character screen on, the CPU is borderline out off puff with the 64 sprites here :( So there's no cpu time left for anything else with this many.. Plus it make the handling of overloads butt ugly without some stupidly expensive checks.. And to be honest, you could render these faster in software I reckon since over half of this time in here is simply moving data around.. The sort, and irq preparation are trivial in time, it's just the clearing, the copying, and the nasty nasty or'ing of missles together that pulls it down completely..

 

Plus the priority of the missles against the players makes for some massively weird artifacts when multiple PMs are overlapped, and I as far as a I can tell there's no PRIOR setting that puts a missile at the same priority as it's respective player, unless 5th player mode is disabled I assume ? But then they assume the player colour, which defeats the object of this exercise.. But the overlap priority oddness wouldn't be a show stopped I guess..

 

This time I went to the lengths of making the entire system double-buffered, but just can't bring myself to use it at 25hz because 25hz just feels like a cop-out on 8bit machines where everything should be at 50/60hz even though driving at at 25/50hz would make for something rather special with all that time..

 

Anyway, it was just a quick experiment over the last few days, partly because it was fun to use Altirra once again in the absence of real hardware here, and I just had some ideas I wanted to try out.. But..

 

Oh well.. Scratch that then I guess, and on to the next idea from the list.. Maybe it's time to start looking at 2600'like kernels to reduce the amount of data copying that's taking place and see where we end up then :) I hope Altirras got DCP implemented ;)

 

Here's an exe if anyone's interested, PAL only though, only tested under Altirra and there are very likely to be issues on other emulators..

whatamess.zip

Edited by andym00
  • Like 1
Link to comment
Share on other sites

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...