Omnimaga

Calculator Community => TI Calculators => Axe => Topic started by: Hayleia on October 03, 2012, 11:16:42 am

Title: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Hayleia on October 03, 2012, 11:16:42 am
(please tell me where I made typos, I am sure I made some :P)

With my recent work on TinyCraft, people have been asking me how I make my grayscale so beautiful. So here is a tutorial!
(what do you mean by "you stole thepenguin's sentence" ? :P)

So one day, I read thepenguin's tutorial (http://ourl.ca/9538/182589) to see if I could get something to work in Axe on a 83+BE without Full speed and I understood that I had to refresh the screen 50-60 times per second.

Now, How would you do this in Axe ? With a loop that runs exactly at 50 Hz ? Not really, for two reasons:
- maybe your calc will have beautiful grey at 50 Hz but others may hae it beautiful at 52 Hz
- maybe the loop will run at 50 Hz on your calc but will run at 48 Hz on others

So you need interrupts. If you don't know how interrupts work, check out Hot Dog's tutorial (http://ourl.ca/11099).
Problem: the slowest interrupts still runs at a higher frequency than 100 Hz, there is no interrupt at 50 Hz. But who told you that the screen was actually refreshed every time the interrupt runs ? ;)

The speeds for the interrupts are as follow (according to Hot Dog):

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

So:
-with speed 0, we would need to refresh the screen only 1 time every ten runs, because 50<560/10<60
-with speed 6, we would need to refresh the screen only 1 time every 2 runs, because 50<118/2<60
-etc

Let's make a try with speed 0 for example. Then, it will be easy to get it to work with arbitrary speeds ;)
So for educationnal reasons, we are going to make a stupid and unoptimized program that will include beautiful grey. Note that I don't code so bad in real life :P
Let's draw for example two rectangles, one black, the other one grey.

.AA
ClrDrawrr
Rect(0,0,8,8,L3)
Rect(0,8,8,8,L6)

Now we need to set up the interrupt. We use speed 0 so we need to refresh the screen 1 time every 10 runs. We need a counter that will tell the calculator if it should refresh or not. So the routine used as an interrupt should look like this.

Lbl DG
T+1?T
If T=10
 0?T
 DispGraphr
End
Return

Another solution would be this one

Lbl DG
T+1?T
!If T^10
 DispGraphr
End
Return

And now, the only thing we need is to set this interrupt on to refresh the screen automatically ;)

.AA
FnOff
ClrDrawrr
Rect(0,0,8,8,L3)
Rect(0,8,8,8,L6)
0?T
fnInt(DG,0)
Repeat getKey(0)
End
LnReg
Return

Lbl DG
T+1?T
!If T^10
 DispGraphr
End
Return

If you want a little more optimised version of this, here it is

.AA
FnOff
ClrDrawrr
Rect(0,,8,,L3)
Rect(0?T,8,,,L6)
fnInt(DG,0)
While 1
EndIf getKey(0)
LnReg
Return

Lbl DG
!If T++^10
 DispGraphr
End
Return

If you want a faster-than-light or a smaller-than-0-bytes version of this, ask Runer112.

Now, the only thing that is missing in that tutorial is "how to make a calibration screen ?", but I am sure you can do this without my help. You just need to make a menu that will select between 4 values and each value gives an interupt speed and a timer-max.

Notes:
I made an example with 3 levels of grey but it works with 4 levels too ;)
See TinyCraft for example.

Disadvantages of this method:
-First of all, the screen is automatically refreshed. This means two bad things: this will make you forget your good habit to make "DispGraph"s inside your loops, and sometimes, you only want to refresh after it cleared all and redraw all, but now you can't choose anymore where the refreshing will happen
-For some reason, using the Text() command on a regular 83+ (not SE) kills the interrupt (but everything else in the program runs fine). To avoid problems, you can use Jacobly's TEXT Axiom or simply avoid the Text() command when the interrupt is running (disable it before using Text() then put it back for example, or prerender all the text in your menus instead of putting the Text() commands in the loop).
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: thepenguin77 on October 03, 2012, 02:05:48 pm
So I guess the question we all have to ask is, how well does it work?

The best way to simulate a real calculator would be to go into wabbitemu and set the grayscale on steady freq at ~63Hz with 4 shades. (From my experience, 63 is average with the upperbound at 66 and lower at 59).

For comparison, here's chess at 63Hz with 4 shades:
(http://img.removedfromgame.com/imgs/chess_example.gif)
Where 173 is the correct setting.

If you can match that with axe, I'll be impressed :D

Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Hayleia on October 03, 2012, 03:50:20 pm
If you can match that with axe, I'll be impressed :D
I get exactly this on both my 83+BE (with interrupt 4) and my 84+SE (with interrupt 0), with just the little "scrolling glitch line", as you say :D
However, the grey is not that perfect on my sister's calc, whatever the interrupt I choose :-\

edit: here is what I get with Wabbitemu set at 68 Hz and 5 shades with this program ;)
.AA
FnOff
ClrDrawrr
Fill(L3+252,252,255)
Fill(L6+252+252,252,255)
fnInt(DG,0)
Pause 200
While 1
EndIf getKey(0)
LnReg
Return

Lbl DG
!If T++^10
 DispGraphr
End
Return
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Sorunome on October 03, 2012, 07:40:15 pm
wow, nice tutorial!
Quote
If you want a faster-than-light or a smaller-than-0-bytes version of this, ask Runer112.
I LOL'd at that one XD
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: thepenguin77 on October 03, 2012, 10:24:56 pm
here is what I get with Wabbitemu set at 68 Hz and 5 shades with this program ;)

That counts, I'm impressed.
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Runer112 on October 04, 2012, 02:34:29 pm
here is what I get with Wabbitemu set at 68 Hz and 5 shades with this program ;)

That counts, I'm impressed.


I have to comment though, I don't think it works that well on the majority of calculators. Basically this method is the same as your method thepenguin77, except it uses the 560/512Hz first hardware timer interrupt instead of a 10922Hz crystal timer interrupt. Tuning this is about 20x rougher, so it's really just luck if one tuning setting looks close to perfect.


For instance, this is about what pure dark gray looks like at the best tuning setting on the luckier of my two calculators:
(http://img.removedfromgame.com/imgs/not%20exactly%20perfect%20gray_.gif)

Change the tuning setting by just one in either direction and I get something that looks approximately like this:
(http://img.removedfromgame.com/imgs/definitely%20not%20perfect%20gray.gif)
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Sorunome on October 04, 2012, 05:32:16 pm
I guess i have a unlucky calc then, coz greyscale never really worked out for me D:
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Hayleia on October 05, 2012, 01:16:29 am
I have to comment though, I don't think it works that well on the majority of calculators. Basically this method is the same as your method thepenguin77, except it uses the 560/512Hz first hardware timer interrupt instead of a 10922Hz crystal timer interrupt. Tuning this is about 20x rougher, so it's really just luck if one tuning setting looks close to perfect.
Yes, of course my method doesn't give perfect greyscale. I only get this on my two calcs but my sister's calc doesn't have this beautiful glitch line (it has several of them if you see what I mean :P)
The real advantage of my method though is that the grey is constant (I mean that if you go from a loop to another, there is no "no grey" time) and that you can get acceptable grey even in 6 MHz :)
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Streetwalrus on March 19, 2013, 06:53:09 am
Necropost. :P

Quote
!If T++^10
Why don't you just do DS(T,10) ?

Anyway this method is awesome, but smooth scrolling a tilemap with 4 level gray looks crappy. I'll stick with three.
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Hayleia on March 19, 2013, 11:55:40 am
Necropost. :P

Quote
!If T++^10
Why don't you just do DS(T,10) ?
Because a tutorial is meant to be easy to understand, and once you understood the concepts, you can optimize yourself ;)

Anyway this method is awesome, but smooth scrolling a tilemap with 4 level gray looks crappy. I'll stick with three.
Thanks :)
But I don't think a smooth scrolling tilemapper looks that crappy with 4 levels, I do this in TinyCraft and I am not so disturbed. Or maybe this is because I am the coder and I am now used to seeing my greyscale tilemapper :P
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Streetwalrus on March 21, 2013, 05:27:01 am
Or maybe I'm just scrolling too fast. :P Syncing scrolling to LCD updates with While and Stop actually makes it looks a lot better. But also incredibly slow. :S
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: ralphdspam on March 21, 2013, 10:41:04 am
Does Axe still draw the screen twice on the grayscale routine?  If so, that might give you sub-optimal results.
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: leafy on March 21, 2013, 10:46:27 am
No, Axe only draws once each frame, but four frames for each full "greyscale loop." Of course, that hardly matters in an interrupt.
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Streetwalrus on March 21, 2013, 03:12:43 pm
So it updates the whole screen four times each time I call dispgraphrr ? Anyways I tried to calibrate it and now it looks a lot less blurry when scrolling and less flickery when standing still. ;)
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Ki1o on March 21, 2013, 04:20:44 pm
How would you go about allowing moving objects on the screen with the interrupt running?  Would you have to somehow sync the refresh rate with the amount of frames? (Sorry if I worded that incorrectly)
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: ralphdspam on March 21, 2013, 05:02:37 pm
How would you go about allowing moving objects on the screen with the interrupt running?  Would you have to somehow sync the refresh rate with the amount of frames? (Sorry if I worded that incorrectly)
You don't necessarily have to sync with the interrupts, but it would be good practice to use it to keep a constant framerate.  
The Stop command halts the CPU until an interrupt occurs.  Remember that a frame is not drawn every interrupt in this case.  You could call a piece of code each time the screen is drawn, but you have to keep your code short to avoid interrupting on itself.  Instead, you can set a variable each time the screen is drawn, then have a piece of main code that waits for the variable to be reset.  



I wrote the following code to have more precision in screen drawing.  The 8.16 fixed point number allows the screen display rate to alternate between whole numbers.  While you will be able to make the screen better, it will still not be as clean as the faster crystal timer interrupts.

Code: [Select]
:.AXEGRAY
:
:ClrDrawrr
:Rect(48,0,48,64)
:Rect(24,0,24,64)r
:Rect(72,0,24,64)r
:
:1→G
:0→H
:9→S
:0→T
:FnInt(DG,0)
:Fix 0
:Fix 5
:
:While 1
:If getKey(4)
:If T>(T+256→T)
:S+1→S
:End
:End
:If getKey(3)
:T+1→T
:If T=0
:S+1→S
:End
:End
:If getKey(1)
:If T<(T-256→T)
:S-1→S
:End
:End
:If getKey(2)
:T-1→T
:If T=EFFFF
:S-1→S
:End
:End
:Text(0,0,S►Hex)
:Text(16,0,T►Hex)
:Pause 100
:EndIf getKey(15)
:LnReg
:Fix 4
:Return
:
:Lbl DG
:G-1→G
:If G=0
:DispGraphrr
:If H<(H+T→H)
:G+1→G
:End
:G+S→G
:End
:Return

EDIT: Wait... This code crashes in Wabbit.  It works fine on my calculator, though.
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Hayleia on March 22, 2013, 02:19:44 am
How would you go about allowing moving objects on the screen with the interrupt running?
There is ralphdspam's solution, but you can just also be sure that the drawing commands are always close enough to the erasing commands so that the interrupt "never" occurs between them.

For example, lets say that your program is 7 balls moving on the screen.
Don't do this:
Erase all screen
For(7)
 Calculate coordinates of ball
 Draw ball
End

In this case, the interrupt can occur before all the balls are drawn.
This would be better:
For(7)
 Erase ball
 Calculate new coordinates
 Draw ball
End


Now, at worst, there will be one ball missing on the screen, but that is only at one frame, it will be back at the next frame.
The best would be this:
For(7)
 Calculate new coordinates
 Erase ball
 Draw ball
End

There is almost absolutely no chance that the interrupt occurs between erase and draw ;)
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: ralphdspam on March 22, 2013, 02:21:57 pm
There is almost absolutely no chance that the interrupt occurs between erase and draw ;)

You could also temporarily disable interrupts before the erase and enable them after the draw.  That way, you will never display a half-drawn screen.

EDIT: fixed quote formatting
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Streetwalrus on March 22, 2013, 02:26:07 pm
You could call a piece of code each time the screen is drawn, but you have to keep your code short to avoid interrupting on itself.
Actually you can encapsulate your interrupt with FnOff and FnOn to avoid that. Then put what you want in. The main reason why an interrupt should be short is speed. The longer it is, the more lag you create in your main prog.

Also I've compared 3 vs 4 levels and 3 looks clean when scrolling while 4 looks really blurry. Maybe it's because I can't calibrate it correctly. ???
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: ralphdspam on March 22, 2013, 02:52:43 pm
Also I've compared 3 vs 4 levels and 3 looks clean when scrolling while 4 looks really blurry. Maybe it's because I can't calibrate it correctly. ???
What you are seing is most likely an artifact of the grayscale mask.  How fast are you scrolling in relationship to the interrupts?  Have you tried scrolling at faster or slower rates?
Title: Re: [TUTO] How to make "beautiful greyscale" in Axe using interrupts
Post by: Streetwalrus on March 22, 2013, 03:44:59 pm
I'm scrolling a bit faster than the interrupt. Syncing it doesn't fix it so I think it's the calibration because 3 level looks perfect at the same speed.