Author Topic: [TUTO] How to make "perfect" grayscale in Axe using multiple interrupts!  (Read 4876 times)

0 Members and 1 Guest are viewing this topic.

Offline pimathbrainiac

  • Occasionally I make projects
  • Members
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1731
  • Rating: +136/-23
  • dagaem
    • View Profile
In the tradition of ThePenguin77 and Hayleia, I am making my grayscale EPIC!!!

So... Wabbit does not like my code :P, but it works on my calc, so hopefully it will work on yours!

Attached is the source (84+), to make it 83+, calculate the values necessary as shown in the tutorial. Speaking of which, here is my explaination/tutorial!:

First: let's start with the interrupt speeds:

Speed // 83+ // 84+
0 // 560 Hz // 512 Hz
2 // 248 Hz // 228 Hz
4 // 170 Hz // 146 Hz
6 // 118 Hz // 108 Hz

With multiple interrupts running at multiple speeds, you can have more data points than just one interrupt!

The problem: how do you make the data points sync up?

Here's how (set up for a basic RPG engine with only one 12*8 tilemap) (please excuse the direct cut-and-paste from graph link):

Code: [Select]
.CC

51.000üE
0.000üF
108.000üJ
0.000üT
0üX
0üY
0üA
0üB

J/*EüF
0.211üK
0.474üI
0.740üL


FnOff

ClrDrawõõ

[010101010101010101010101]üGDB1
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]

[0000000000000000]üPic1
[FFFFFFFFFFFFFFFF]

For(Z,0,11)
For(Á,0,7)
Pt-On((Z*8),(Á*8),(8*{(Á*12)+Z+GDB1})+Pic1)õ
End
End

fnInt(DG,0)
fnInt(DH,6)
fnInt(DI,4)
fnInt(DJ,2)
Pause 200
While 1
If (getKey(54))
E-0.100üE
If (E÷49.000)
65.000üE
End
J/*EüF
0.000üT
Pause 200
End
If (getKey(55))
E+0.100üE
If (Eù66.000)
50.000üE
End
J/*EüF
0.000üT
Pause 200
End
getKey(3)-getKey(2)üA
getKey(1)-getKey(4)üB
FnOff
X+AüX
Y+BüY
ClrDraw
Pt-On((X/2),(Y/2),8+Pic1)
FnOn
EndIf getKey(15)
LnReg õ
Return

Lbl DG
T+KüT
If (TùF)
DispGraphõ
0.000üT
End
Return

Lbl DH
T+1.000üT
If (TùF)
DispGraphõ
0.000üT
End
Return

Lbl DI
T+LüT
If (TùF)
DispGraphõ
0.000üT
End
Return

Lbl DJ
T+IüT
If (TùF)
DispGraphõ
0.000üT
End
Return

Let's run through that code block by block:

Code: [Select]
51.000üE
0.000üF
108.000üJ
0.000üT
0üX
0üY
0üA
0üB

J/*EüF
0.211üK
0.474üI
0.740üL

Variable declarations. Notice how almost everything is an 8.8 fixed point number. Why? Because of the way the interrupts will work. (basically, the 108Hz interrupt is the main one, (1 is added to the counter, T, every time it is called), and K, I, and L are the values added to the counter every time a different interrupt is called). K, I, and L are J/value, where value is the Hz for the other interrupts. J is the Interrupt 6 value. Change these to be what is needed for the 83+ models (round to 3 decimal places) using the calculation I just said. F is the target for the counter, and E is the target framerate.

Code: [Select]
[010101010101010101010101]üGDB1
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]
[010101010101010101010101]

[0000000000000000]üPic1
[FFFFFFFFFFFFFFFF]

Same old stuff for an RPG (tilemap and sprites)

Code: [Select]
For(Z,0,11)
For(Á,0,7)
Pt-On((Z*8),(Á*8),(8*{(Á*12)+Z+GDB1})+Pic1)õ
End
End

Drawing the back buffer once (just as an example, put this in a subroutine if you wish)

Code: [Select]
fnInt(DG,0)
fnInt(DH,6)
fnInt(DI,4)
fnInt(DJ,2)

Setting up the interrupts

Code: [Select]
Pause 200
While 1
If (getKey(54))
E-0.100üE
If (E÷49.000)
65.000üE
End
J/*EüF
0.000üT
Pause 200
End
If (getKey(55))
E+0.100üE
If (Eù66.000)
50.000üE
End
J/*EüF
0.000üT
Pause 200
End
getKey(3)-getKey(2)üA
getKey(1)-getKey(4)üB
FnOff
X+AüX
Y+BüY
ClrDraw
Pt-On((X/2),(Y/2),8+Pic1)
FnOn
EndIf getKey(15)
LnReg õ
Return

The main body of the code. There are adjustments using 2nd and mode (1/10 hz adjustments), and the ability to move a block around the screen.



Now for the interrupts:

Code: [Select]
Lbl DG
T+KüT
If (TùF)
DispGraphõ
0.000üT
End
Return

Lbl DH
T+1.000üT
If (TùF)
DispGraphõ
0.000üT
End
Return

Lbl DI
T+LüT
If (TùF)
DispGraphõ
0.000üT
End
Return

Lbl DJ
T+IüT
If (TùF)
DispGraphõ
0.000üT
End
Return

Here's what they do: They add their respective values to the counter, and then they see if the counter is greater than or equal to the target counter. If so, the the screen is drawn, and the counter is reset



Long story short: The faster interrupts are fractions of the longer interrupt, and they add to the counter smaller amounts for accuracy. Because there are more times the counter is incremented, the frame rate is more accurate.



I hope this helps! And may the grayscale be with you! ;D
« Last Edit: March 25, 2013, 02:58:07 pm by pimathbrainiac »
I am Bach.

Offline Streetwalrus

  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3821
  • Rating: +80/-8
    • View Profile
You know that they'll be run at random, right ?

Offline pimathbrainiac

  • Occasionally I make projects
  • Members
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1731
  • Rating: +136/-23
  • dagaem
    • View Profile
You know that they'll be run at random, right ?

What do you mean? They'll run the amount of times per second that they should be called.
I am Bach.

Offline ralphdspam

  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 841
  • Rating: +38/-1
  • My name is actually Matt.
    • View Profile
This is technique is similar to the one I posted in Heylia's thread, but I used an 8.16 counted with the 560Hz interrupt.

I'm not sure is the multiple interrupt routine would work, though.  560, 248, 170, and 118 are all modes of the same hardware interrupt timer.  I don't think you can run then simultaneously. 
However, You can ask Runer112 to implement control of the second hardware timer, which runs up to 1120Hz.
« Last Edit: March 25, 2013, 04:46:01 pm by ralphdspam »
ld a, 0
ld a, a

Offline Hayleia

  • Programming Absol
  • Coder Of Tomorrow
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3367
  • Rating: +393/-7
    • View Profile
Heylia
D:
Also, yeah, I think I once asked if several interrupts could be triggered at once, and I was answered that no, that the best I could do was to put all the routines in one interrupt :(
Maybe it was changed since, don't know ?
I own: 83+ ; 84+SE ; 76.fr ; CX CAS ; Prizm ; 84+CSE
Sorry if I answer with something that seems unrelated, English is not my primary language and I might not have understood well. Sorry if I make English mistakes too.

click here to know where you got your last +1s

Offline Streetwalrus

  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3821
  • Rating: +80/-8
    • View Profile
Nope, unfortunately the part that allows selecting a routine depending on what triggered the interrupt is broken on the hardware side.
Looking at ASM in 28 days, you'll learn that you can actually have four routines that will be run randomly when an interrupt occurs. Or have 4x the same one, which is done most of the time (and that's prolly what Axe does).

Theoretically, pimath's method is very good, but practically it does not work. Only the DJ routine will be run since it's the last one to be registered, at frequency 2. Which means that it becomes Hayleia method with random garbage added to it. :P Sorry to break your pride pimath, TI's hardware really sucks.
« Last Edit: March 26, 2013, 01:47:17 pm by Streetwalker »

Offline pimathbrainiac

  • Occasionally I make projects
  • Members
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1731
  • Rating: +136/-23
  • dagaem
    • View Profile
Dangit. I had the theory down, but I didn't know what you just said. Thanks for pointing out my flaws.
I am Bach.