English Amiga Board (http://eab.abime.net/index.php)
-   Coders. General (http://eab.abime.net/forumdisplay.php?f=37)
-   -   Maths question, divisions, remainders etc. (http://eab.abime.net/showthread.php?t=59442)

 h0ffman 26 May 2011 15:54

Maths question, divisions, remainders etc.

Hi guys

Bit of a maths question today. Funnily enough relating to scope code again. It's to do with sample rates (or periods! as they are called on the amiga) and time calculations.

This is a section of code from the original protracker scope code..

Code:

```        MOVEQ.L        #0,D1         MOVE.W        ns_period(A2),D1         LSR.W        #1,D1         BEQ.S        ScoNextChan         MOVE.L        #35469,D2         DIVU        D1,D2         EXT.L        D2         ADD.L        D2,D0```
The above section calculates where the sample pointer needs to draw from in the next frame. The 35469 is the magic number which does this.

So, at present all the scopes I've seen or worked on simply draw the samples byte by byte to the screen and uses this calculation to simply move the pointer each frame to where the sample should be. This is not an accurate representation of the wave form, however it does suffice most requirements.

What I want to do is draw the wave at the frequency it is being played at. My idea was to simply divide the 35469 by the pixel width of the scope, then use this value to increment the sample pointer for each pixel. However, I very quickly realised that you cannot get away with this as the remainder from the division needs to be taken into consideration for each pixel iteration.

Code:

```        moveq        #0,d1         MOVE.W        #172,D1  ; period         LSR.W        #1,D1         MOVE.L        #35469/160,D2  ; magic number div by 160 pixels         DIVU        D1,D2```
So the result of this in D2 is \$00310002, so I can assume that I can move forward two bytes to get the next pixel from the sample data, however if I just do that for all 160 pixels, it wont finish at the end because the remainder \$0031 is not being taken into consideration.

So... how do you go about performing this type of calculation in 68k?

On a side note, it doesn't need to be fast, just accurate as it will all get pre-calced into look up tables anyhow.

 Ed Cruse 26 May 2011 16:40

Quote:
 Originally Posted by h0ffman (Post 758141) Hi guys Bit of a maths question today. Funnily enough relating to scope code again. It's to do with sample rates (or periods! as they are called on the amiga) and time calculations. This is a section of code from the original protracker scope code.. Code: ```    MOVEQ.L    #0,D1     MOVE.W    ns_period(A2),D1     LSR.W    #1,D1     BEQ.S    ScoNextChan     MOVE.L    #35469,D2     DIVU    D1,D2     EXT.L    D2     ADD.L    D2,D0``` The above section calculates where the sample pointer needs to draw from in the next frame. The 35469 is the magic number which does this. So, at present all the scopes I've seen or worked on simply draw the samples byte by byte to the screen and uses this calculation to simply move the pointer each frame to where the sample should be. This is not an accurate representation of the wave form, however it does suffice most requirements. What I want to do is draw the wave at the frequency it is being played at. My idea was to simply divide the 35469 by the pixel width of the scope, then use this value to increment the sample pointer for each pixel. However, I very quickly realised that you cannot get away with this as the remainder from the division needs to be taken into consideration for each pixel iteration. Code: ```    moveq    #0,d1     MOVE.W    #172,D1  ; period     LSR.W    #1,D1     MOVE.L    #35469/160,D2  ; magic number div by 160 pixels     DIVU    D1,D2``` So the result of this in D2 is \$00310002, so I can assume that I can move forward two bytes to get the next pixel from the sample data, however if I just do that for all 160 pixels, it wont finish at the end because the remainder \$0031 is not being taken into consideration. So... how do you go about performing this type of calculation in 68k? On a side note, it doesn't need to be fast, just accurate as it will all get pre-calced into look up tables anyhow.

One possible way is to convert to double float, do the calculations, round to an integer, and then convert back to integers. It's slow but it works. You may still be off at the end, it just depends on how well the rounding averages out. I've used this method for scaling the data for plotting purposes.

 h0ffman 26 May 2011 17:14

How do you do a double float in assembly?

 dalton 26 May 2011 18:42

If you're on 020, you can use divu.l with 35469<<16 to get a 16.16 bit fixed point representation. That should be sufficient for 160 pixels.

If you're not on 020, perhaps Bresenham interpolation could be a solution.

 Ed Cruse 27 May 2011 00:19

Quote:
 Originally Posted by h0ffman (Post 758153) How do you do a double float in assembly?

Use the Amiga math libs to do the converting and calculations. The easiest way would be to use the math libs to convert all your integers like 35469 and integers in the data registers, to single or double float. Calculate and then use the math libs to convert back to integers. It's basically a pain. :) It's a lot easier to do it in C.

When I was doing a lot of assembly I had macros to make it a lot easier, I also came up with my own double float constants and wrote a program that would search through my source codes and convert double float constants like 35469.0 into two dc.l. Eventually I got an assembler that new about double float constants. After 20 years of assembly programing I finally wised up and learned C, now I do assembly only when it's necessary.

I can already feel the hate from the pure assembly programers, like I used to be. :)

 h0ffman 27 May 2011 15:22

Thanks for the response Ed, however, i'm more interested in how to solve this problem with assembly. I know that maths lib might be the easiest, but I'd love to know how these kind of problems are resolved when working directly with the CPU.

 Ed Cruse 27 May 2011 16:44

Quote:
 Originally Posted by h0ffman (Post 758299) Thanks for the response Ed, however, i'm more interested in how to solve this problem with assembly. I know that maths lib might be the easiest, but I'd love to know how these kind of problems are resolved when working directly with the CPU.

I know what you mean, I've tried to complish the same thing dealing with the remainder but it's always been very difficult. Seems like there's always some odd combination of things that causes problems, so I end up using double floating point. Recently I did manage to use the remainder method and it was working, but in the normal use of what I was doing I would frequently end up with numbers after the divide that were larger then a long could handle and it messed up my whole plan. In this case speed was important and using double floating point was slow but I ended up using it anyway, I had no choice. My math isn't good enough to figure out how to use remainders reliably to do what I needed. Especially rounding.

I use WinUAE so the emulated fpu is really fast, so when I really need my float points to be fast I compile to use inline FPU code. But that also requires using at least a 68020, but you want 68000. If you could use a 68020 you can also use an assembler that knows fpu commands and use the fpu to convert, calculate, and convert back to integers. From a assembly programmers point of view the fpu is a dream to use and very fast.

 MSL 30 May 2011 16:15

I'm not experienced with asm, so I might not understand your code correctly, but from what it looks like to me is, that you divide D1 with D2, where D2 is a division of 35469/160, which have a remainder.

The math can be rewritten:

A / (B / C) = (A * C) / B -> ( D1 * 160 ) / 35469

Assuming that A * C does not overflow you have removed the imprecision of one division.

 h0ffman 07 June 2011 15:35

Just thought I'd put a quick update here, managed to crack it in the end...

Code:

``` ScopeInitFreq        lea        FreqTable+(scopebytewd*8*2),a0                 moveq        #1,d0                ; current period .nextperiod        move.l        #35469/(scopebytewd*8),d6  ; the magic number!                 moveq        #0,d5                 move.w        #scopebytewd*8-1,d7                 moveq        #0,d2                 moveq        #0,d3                 .test                add.l        d6,d5                                                move.l        d5,d1                 divu        d0,d1                 move.w        d1,d2                 sub.w        d3,d1                 move.w        d1,(a0)+                ; store byte move size                 move.w        d2,d3                                 dbra        d7,.test                 add.w        #1,d0                 cmp.w        #scopemaxperiod,d0                 blo.b        .nextperiod                 RTS```
Simply re-do the calculation incrementing the seed each time. Also managed to get this plugged into the scope drawing routine. You can see it re-sizing the wave depending on the frequency of the sample ! :D

 Photon 24 June 2011 01:03

If you're using The Player to optimize modules, there's a scope routine accurate to 1/262144th of a sample in the latest P61 playroutine by me, P6108, if you enable it it will give you pointer and length for a 50Hz frame for each playing channel. It wraps loops correctly and gives a bunch of zeroes if the channel is quiet or ending.

 pmc 24 June 2011 09:40

@ Photon: going slightly off topic I suppose but not too far I hope - I integrated ThePlayer into my demo shell recently but I didn't manage to get your "super optimised, all singing, all dancing" version to assemble and had to use the latest "old" release of ThePlayer instead.

With your version I got 200+ errors on assembly using Devpac3.18 - was I doing something obviously and stupidly wrong...?

 Photon 24 June 2011 19:35

Differences between assemblers will give errors, basically for all sources "not made for your assembler flavor". The Player source is special in that it uses loads of conditionals and a few macros, and these features might not be supported or need to be written in another way to be used in your assembler. Same thing if someone releases a Devpac source and uses features not found in say, AsmOne or PhxAss.

The most common complaint about The Player sources is the structures macros.

Have you set ASMONE=0, to start with? This attempts to replace AsmOne macros with other flavors.

If there are remaining differences you'll have to reconcile them, or you could rip out the Scope routine+a few vars added for it at the end of the source and see if it talks to P6106.

Or check what it does and modify it to talk to Protracker, that would be ace :)

 h0ffman 25 June 2011 00:58

Hey photn, thanks for the offer, however I've got my scope routine already integrated into the player. Fixed all the original bugs that came with the original source, such as sample offset, loops sample endings. I've recently done a version which draws the sample at the sample rate its playing at. Next up is it see if I can combine all four channels into one massive scope :)

 Lonewolf10 25 June 2011 01:00

Quote:
 Originally Posted by pmc (Post 763000) With your version I got 200+ errors on assembly using Devpac3.18 - was I doing something obviously and stupidly wrong...?
I agree with Photon's reply, but just wish to check that you are not using case-sensitive mode when assembling - that gave me loads of errors when compiling with (an old version of) The Player when I first started using it.
Now I always compile assemble with case-sensitive mode off.

Regards,
Lonewolf10

 pmc 25 June 2011 10:06

Errrm, no - not using case sensitive and had already done all the other obvious stuff.

While Photon was around I thought I'd ask out of curiosity but now I know (rather than just suspecting...) the answer is basically to go though those errors and remedy each one I'm gonna take the approach I'd already decided on: stick with the latest un-Photon'd version of ThePlayer that works for me. :)

 Photon 25 June 2011 22:44

pmc, I'd be interested in differences in error log between P6106 and P6108, so email me them if you want !

h0ffman, hm, read pmc's reply as one of yours, sorry for the confusion. The scope routine would then only be of abstract use for your playroutine, anyway good to hear you fixed it up! And yes, 4 fullres scopes running separately consume a not negligible amount of raster time on OCS, visualizing 4 added together to reduce the amount of plots or similar you use to visualize it is a very good idea :)

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