Jump to content

Photo

Macros


37 replies to this topic

#1 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • 5,443 posts
  • Location:Georgia, USA

Posted Sat Sep 18, 2010 1:00 AM

Here's a little document I put together about using the 'macro' and 'callmacro' statements, as well as using a 'def' statement to make a 'callmacro' statement look more like a new instruction. I've put the document in a text file so Random Terrain can grab it and adapt it for his bB page if he wants.

Michael



macro
-----

The macro statement lets you define a keyword that represents other programming statements. You can use parameters in a macro's definition by putting numbers inside curly brackets. Use '{1}' for the first parameter, '{2}' for the second parameter, '{3}' for the third parameter, and so on. Use an 'end' statement to terminate a macro's definition.

For example, the following macro defines 'setscreencolors' as a keyword that can be used to set the background color, playfield color, and score color:

   macro setscreencolors
   COLUBK={1}
   COLUPF={2}
   scorecolor={3}
end



callmacro
---------

The callmacro statement lets you call a macro after you've defined it. To pass parameters to the macro, follow the name of the macro with the variable names or values you want the macro to use. Put a space before each parameter, but don't include commas.

For example, the following statement calls the 'setscreencolors' macro that was defined above:

   callmacro setscreencolors $02 $46 $1C

When the batari Basic compiler sees this statement in your program, it replaces the 'callmacro' statement with the macro's code, inserting the parameters in the order they were listed, as follows:

   COLUBK=$02
   COLUPF=$46
   scorecolor=$1C



Combining macro, def, and callmacro
-----------------------------------

Although 'macro' essentially lets you create a new instruction of your own, it doesn't look much like a new instruction with the 'callmacro' keyword in front of it. But you can use 'def' to make a keyword that represents the 'callmacro' statement. For example:

   def scolors=callmacro setscreencolors

   scolors $02 $46 $1C

Now it looks more like a new instruction!



More Examples of Using Macros
-----------------------------

The following example contains a few more macros that could be useful:

   macro setplayercolors
   COLUP0={1}
   COLUP1={2}
end

   def pcolors=callmacro setplayercolors

   macro setplayer0xy
   player0x={1}
   player0y={2}
end

   def p0xy=callmacro setplayer0xy

   macro setplayer1xy
   player1x={1}
   player1y={2}
end

   def p1xy=callmacro setplayer1xy

   player0:
   % 00010000
   % 00101000
   %01000100
   %10000010
   %01000100
   % 00101000
   % 00010000
end

   p0xy 20 40

   player1:
   %11111111
   %10000001
   %10000001
   %10000001
   %10000001
   %10000001
   %11111111
end

   p1xy 50 30

loop

   pcolors $0E $C6
   drawscreen
   goto loop

You can even create macros that contain if-then statements, for-next statements, and other complex code, or call a macro from inside another macro. For example:

   macro movesprite
   if joy0left then {1}x={1}x-1
   if joy0right then {1}x={1}x+1
   if joy0up then {1}y={1}y-1
   if joy0down then {1}y={1}y+1
end

   callmacro movesprite player0

In this example, 'callmacro movesprite' will get replaced by the four if-then statements of the 'movesprite' macro, and '{1}' will get replaced by the name of the sprite, so player0 will get moved around by joystick0. If you change it to 'callmacro movesprite missile1', it will move missile1 around instead of player0.

As you can see, macros can help make it easier for you to write your program code. However, macros could also make it harder for someone else to understand your program. For example, if someone sees 'p0xy 20 40' in your program, first they would need to look through your code to find where you defined 'p0xy' to mean 'callmacro setplayer0xy', and then they would need to look further to find where you defined the code for the 'setplayer0xy' macro. On the other hand, that's not much different than seeing a statement like 'gosub moose_tracks' and then having to look for the 'moose_tracks' subroutine to see what it does.

Attached Files


Edited by SeaGtGruff, Sat Sep 18, 2010 1:02 AM.


#2 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sat Sep 18, 2010 1:27 AM

Thanks. I'll start working on the bB page.

#3 yuppicide OFFLINE  

yuppicide

    I am the Black Knight. Give me your money!

  • 6,933 posts
  • Location:New Jersey

Posted Sat Sep 18, 2010 8:28 AM

Great!

#4 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sun Sep 19, 2010 11:19 AM

It's on the bB page now:

http://www.randomter...ands.html#macro


Can some magic be done with macro and def to make the Nybble me this, Batman! trick so easy a cave man could do it? What would be the best, easiest, least code wasting way to make our own nybble variables? We might get lucky and bB will automatically handle nybble variables in a future update, but until that day comes, looks like macro and def are our only hope for dummy-proof nybble variables.

#5 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sun Sep 19, 2010 11:42 PM

Can some magic be done with macro and def to make the Nybble me this, Batman! trick so easy a cave man could do it? What would be the best, easiest, least code wasting way to make our own nybble variables? We might get lucky and bB will automatically handle nybble variables in a future update, but until that day comes, looks like macro and def are our only hope for dummy-proof nybble variables.

I tried all kinds of things, but since callmacro can only take numbers and can't use variables or rand, I can't create a fake nybble variable that works similar to a real one.

#6 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • Topic Starter
  • 5,443 posts
  • Location:Georgia, USA

Posted Sun Sep 19, 2010 11:56 PM

I tried all kinds of things, but since callmacro can only take numbers and can't use variables or rand, I can't create a fake nybble variable that works similar to a real one.

I'm trying to work out an easy way to do this. I might have something by tomorrow night.

Michael

#7 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Mon Sep 20, 2010 12:14 AM

I'm trying to work out an easy way to do this. I might have something by tomorrow night.

Thanks. I hope you'll have better luck. I think I broke my brain.

#8 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • Topic Starter
  • 5,443 posts
  • Location:Georgia, USA

Posted Mon Sep 20, 2010 1:05 AM

This is the best I've come up with so far. Here's a sample program that defines two macros for setting the two nibbles, and uses some 'def' statements to make it easier to set or read a nibble variable.

   include div_mul.asm

   macro set_lo_nibble
   {1}={1}&$F0
   {1}={1}|{2}
end

   macro set_hi_nibble
   {1}={1}&$0F
   {1}={1}|16*{2}
end

   def gamelevel=a&$0F
   def game_level=callmacro set_lo_nibble a

   def hitpoints=a/16
   def hit_points=callmacro set_hi_nibble a

   rem **************************************************************

   game_level 1
   hit_points 5

loop

   if gamelevel=1 then COLUBK=$02 else COLUBK=$12
   if hitpoints=5 then scorecolor=$0E else scorecolor=$1E

   drawscreen

   goto loop
Notes:

(1) You must include 'div_mul.asm' or it won't compile.

(2) The two macros are 'set_lo_nibble' and 'set_hi_nibble'. They each take two parameters-- the variable you want to set a nibble for, and the value you want to set it to (0 to 15).
Example: 'callmacro set_lo_nibble x 4' will set the lo nibble of variable 'x' to 4, without affecting the hi nibble.
Example: 'callmacro set_hi_nibble y 7' will set the hi nibble of variable 'y' to 7, without affecting the lo nibble.

(3) You don't need a macro to get (read) the value of a nibble, since you can do it easier with a 'def' statement.

(4) You can't use the same "variable" name to get *and* set a nibble, because bB would get confused. So you need to 'def' one variable name for getting (reading) the nibble, and a different one for setting the nibble.
Example: 'def gamelevel=a&$0F' will define 'gamelevel' to be the lo nibble of 'a'. 'gamelevel' is the name you'd use to *read* the nibble.
Example: 'def game_level=callmacro set_lo_nibble a' will define 'game_level' to be the 'callmacro' statement for *setting* the nibble.

(5) To set the nibble, do *not* use an equal sign, just give the name of the 'def' that sets the nibble, followed by a space, followed by the value you want to set it to.
Example: 'game_level 1' will get changed to 'callmacro set_lo_nibble a 1', which will set the lo nibble of 'a' to 1.
Example: 'hit_points 5' will get changed to 'callmacro set_hi_nibble a 5', which will set the hi nibble of 'a' to 5.

(6) To read the value of a nibble, just give the name of the 'def' that reads the nibble.
Example: 'if gamelevel=1 then dosomething' will read the lo nibble of 'a' and compare it to 1.

(7) If you want to increment or decrement a nibble, you'll need to use a temporary variable.
Example:
   temp1=gamelevel+1 : rem * read the lo nibble of 'a', add 1 to it, and store the result in 'temp1'
   game_level temp1 : rem * set the lo nibble of 'a' to the new value
Michael

Edited by SeaGtGruff, Mon Sep 20, 2010 1:06 AM.


#9 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Mon Sep 20, 2010 5:04 AM

Wow, I didn't know you could do things like "{1}={1}|16*{2}". Thanks.

To help me avoid confusion even more, I'd probably adapt it so both def variables have the same names, but will start with the prefix Peek or Poke. Take a look at this:

   include div_mul.asm

   set smartbranching on

   dim rand16 = z

   macro set_lo_nibble
   {1}={1}&$F0
   {1}={1}|{2}
end

   macro set_hi_nibble
   {1}={1}&$0F
   {1}={1}|16*{2}
end

   def Peek_Game_Level=a&$0F
   def Poke_Game_Level=callmacro set_lo_nibble a

   def Peek_Hit_Points=a/16
   def Poke_Hit_Points=callmacro set_hi_nibble a


   scorecolor = $1A : COLUBK = $C4


Loop

   w = w + 1 : if w < 30 then Skip_It_All

   temp5 = (Peek_Game_Level) + 1 : Poke_Game_Level temp5

   temp5 = rand & 15 : Poke_Hit_Points temp5

   score = 0 : w = 0

   temp5 = Peek_Game_Level
   if temp5 > 0 then for temp6 = 1 to temp5 : score=score+1000 : next
   
   temp5 = Peek_Hit_Points
   if temp5 > 0 then for temp6 = 1 to temp5 : score=score+1 : next

Skip_It_All

   drawscreen

   goto Loop

One is a random number and the other adds up until it hits 15, then starts at 0 again. I was surprised to find out that I didn't need an if-then to keep the count from messing up. Once it hits 15, it automatically rolls over to zero.

Since you found a way to have reusable macros, setting up more nybble variables will be easy using def.

Edited by Random Terrain, Mon Sep 20, 2010 5:05 AM.


#10 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Mon Sep 20, 2010 10:02 AM

When you get a chance, check this out and see if I mangled it:

http://www.randomter...#nybblemacrodef

It's the first try, so I probably messed something up.


Thanks.

#11 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • Topic Starter
  • 5,443 posts
  • Location:Georgia, USA

Posted Mon Sep 20, 2010 7:46 PM

It looks okay to me, and I like your idea of just putting PEEK_ and POKE_ in front of the nibble variable name. Why didn't I think of that? :ponder:

Michael




#12 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Mon Sep 20, 2010 7:53 PM

It looks okay to me, and I like your idea of just putting PEEK_ and POKE_ in front of the nibble variable name. Why didn't I think of that? :ponder:

Thanks. If you notice anything later that doesn't sound right, let me know and I'll fix it. Seems like I always discover something days, weeks, or months later.

#13 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • Topic Starter
  • 5,443 posts
  • Location:Georgia, USA

Posted Tue Sep 21, 2010 4:54 AM

It looks okay to me, and I like your idea of just putting PEEK_ and POKE_ in front of the nibble variable name. Why didn't I think of that? Posted Image

Thanks. If you notice anything later that doesn't sound right, let me know and I'll fix it. Seems like I always discover something days, weeks, or months later.

One thing I did notice that didn't sound right-- when setting a nibble, you should be careful to set it to a value between 0 and 15, because after the code does a bitwise AND to clear the nibble that's about to be set, it does a bitwise OR to combine the new nibble value with the whole byte. If you're setting the lo nibble, and the new value is greater than 15, you might end up accidentally changing one of the bits in the hi nibble. This isn't a problem when setting the hi nibble, since the new value gets multiplied by 16 before it's combined with the whole byte. There was one place where you said something about incrementing the old value of the lo nibble and it automatically rolled over from 15 to 0. It would actually go from 15 to 16, which would roll over the lo nibble from 15 to 0, but it would also turn on one of the bits in the hi nibble (bit 4). If that bit was already on anyway, "no harm no foul." But if that bit was originally off, it would get changed. So when you add something to the value of the lo nibble, be sure the new value doesn't exceed 15. The easiest way to do that is to AND the result with 15 ($0F) before you set the lo nibble to the new value.

Michael

#14 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Tue Sep 21, 2010 6:32 AM


It looks okay to me, and I like your idea of just putting PEEK_ and POKE_ in front of the nibble variable name. Why didn't I think of that? Posted Image

Thanks. If you notice anything later that doesn't sound right, let me know and I'll fix it. Seems like I always discover something days, weeks, or months later.

One thing I did notice that didn't sound right-- when setting a nibble, you should be careful to set it to a value between 0 and 15, because after the code does a bitwise AND to clear the nibble that's about to be set, it does a bitwise OR to combine the new nibble value with the whole byte. If you're setting the lo nibble, and the new value is greater than 15, you might end up accidentally changing one of the bits in the hi nibble. This isn't a problem when setting the hi nibble, since the new value gets multiplied by 16 before it's combined with the whole byte. There was one place where you said something about incrementing the old value of the lo nibble and it automatically rolled over from 15 to 0. It would actually go from 15 to 16, which would roll over the lo nibble from 15 to 0, but it would also turn on one of the bits in the hi nibble (bit 4). If that bit was already on anyway, "no harm no foul." But if that bit was originally off, it would get changed. So when you add something to the value of the lo nibble, be sure the new value doesn't exceed 15. The easiest way to do that is to AND the result with 15 ($0F) before you set the lo nibble to the new value.

Thanks. After testing both nybbles again (keeping the other at zero), I see that one side can roll over, but the other can't without messing with the other side.

Before I update the page, is there a simple thing that can be done with the macros that would keep both nibbles between 0 and 15? That way bB users wouldn't have to worry about it.

Edited by Random Terrain, Tue Sep 21, 2010 6:42 AM.


#15 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • Topic Starter
  • 5,443 posts
  • Location:Georgia, USA

Posted Tue Sep 21, 2010 7:31 PM

   macro set_lo_nibble
   {1}={1}&$F0
   {1}={1}|{2}
end

Just change the "set_lo_nibble" macro...

   macro set_lo_nibble
   {1}={1}&$F0
   {2}={2}&$0F
   {1}={1}|{2}
end
... to this.

Michael

#16 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Tue Sep 21, 2010 10:28 PM

   macro set_lo_nibble
   {1}={1}&$F0
   {2}={2}&$0F
   {1}={1}|{2}
end

Thanks. I couldn't get that to compile, so I tried this:

   macro set_lo_nibble
   {1}={1}&$F0
   {1}={1}|({2}&$0F)
end

It compiles and seems to work. I'll use that if you don't see any problems with it.

Is it safe to tell bB users that the nybble variables will always stay between 0 and 15 and the numbers will roll over from 15 to 0 or from 0 to 15 similar to how a normal variable rolls over from 255 to 0 or from 0 to 255?

#17 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • Topic Starter
  • 5,443 posts
  • Location:Georgia, USA

Posted Wed Sep 22, 2010 6:12 PM

   macro set_lo_nibble
   {1}={1}&$F0
   {2}={2}&$0F
   {1}={1}|{2}
end

Thanks. I couldn't get that to compile, so I tried this:

   macro set_lo_nibble
   {1}={1}&$F0
   {1}={1}|({2}&$0F)
end

It compiles and seems to work. I'll use that if you don't see any problems with it.

Is it safe to tell bB users that the nybble variables will always stay between 0 and 15 and the numbers will roll over from 15 to 0 or from 0 to 15 similar to how a normal variable rolls over from 255 to 0 or from 0 to 255?

D'oh! You mean you can't set a literal value to something? I thought 16=16&$0F was a perfectly legitimate statement! :roll: Oh, well, that'll teach me to actually try it out next time before I post it. Posted Image

Funny thing is, I thought about doing it your way, but I wasn't sure if the compiler could handle it. Like I said, I should of actually *tried* it.

Michael

#18 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Wed Sep 22, 2010 7:20 PM

OK, if you don't see any problems, I'll tell bB users that the nybble variables can roll over from 15 to 0 just like a normal variable rolls over from 255 to 0.

#19 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sun Sep 26, 2010 1:21 PM

I ran into a problem. Without bank switching, I can pile up POKE nybble variables until I run out of ROM and everything will work fine, but once I switch over to bank switching, I can't use more than one hi nibble POKE.

#20 theloon ONLINE  

theloon

    Quadrunner

  • 7,548 posts

Posted Sun Sep 26, 2010 2:56 PM

I ran into a problem. Without bank switching, I can pile up POKE nybble variables until I run out of ROM and everything will work fine, but once I switch over to bank switching, I can't use more than one hi nibble POKE.


Just curious, does the new nybble code work as a normal gosub routine with bank-switching?

#21 RevEng OFFLINE  

RevEng

    River Patroller

  • 3,262 posts
  • bit player
  • Location:Canada

Posted Sun Sep 26, 2010 5:52 PM

No, its a macro, so an copy of it gets in-lined wherever you use it.

But the macro in question is this...
   macro set_hi_nibble
   {1}={1}&$0F
   {1}={1}|16*{2}
end

The bb code created by this winds up bank-switching to the 8 bit multiplication subroutines for the multiply by 16.

If RT changes it to the following, an expensive bank switch will be avoided...

   macro set_hi_nibble
   {1}={1}&$0F
   {1}={1}|({2}*16)
end

It also might "fix" the issue, though possibly not the root of the problem.

#22 Random Terrain ONLINE  

Random Terrain

    Visual batari Basic User

  • 24,728 posts
  • Controlled Randomness
    Replay Value
    Nonlinear
  • Location:North Carolina (USA)

Posted Sun Sep 26, 2010 6:05 PM

It also might "fix" the issue, though possibly not the root of the problem.

Nope, still have a blank screen.

#23 RevEng OFFLINE  

RevEng

    River Patroller

  • 3,262 posts
  • bit player
  • Location:Canada

Posted Sun Sep 26, 2010 8:25 PM

Ok, I understand the problem better. In a nutshell...

1. The newer test versions of bB (e.g from this thread) seem to have a broken "* power of 2" detection. They seem to always use the 8 bit multiplication library, even with either one of these simple examples...

a=b*16
a=16*b

2. When your macro is built, it's being built with a fixed return label, so the math routine knows where to jump back to. Unfortunately, when you use the macro more than once it creates a duplicate label in your source.

3. The duplicate label is causing an imcomplete build of your binary, which is giving you the black screen. If you check the size of your binary, I'm betting it's wrong.

Short term workaround...

   macro set_hi_nibble
 asm
   lda {1}
   and #$0f
   sta {1}
   lda {2}
   asl
   asl
   asl
   asl
   ora {1}
   sta {1}
end
end

Edited by RevEng, Sun Sep 26, 2010 8:26 PM.


#24 RevEng OFFLINE  

RevEng

    River Patroller

  • 3,262 posts
  • bit player
  • Location:Canada

Posted Sun Sep 26, 2010 8:48 PM

On closer inspection, I think #1 might be a feature instead of a bug. It seems that "power of 2" detection works for numbers less than 16.

Either way, it's the wrong thing to do in a bankswitched binary. That's a big time penalty to pay for a multiply by 16.

#25 SeaGtGruff OFFLINE  

SeaGtGruff

    Quadrunner

  • Topic Starter
  • 5,443 posts
  • Location:Georgia, USA

Posted Sun Sep 26, 2010 9:27 PM

On closer inspection, I think #1 might be a feature instead of a bug. It seems that "power of 2" detection works for numbers less than 16.

Either way, it's the wrong thing to do in a bankswitched binary. That's a big time penalty to pay for a multiply by 16.

I'd think the compiler should be checking for any power of 2 less than 256-- 2, 4, 8, 16, 32, 64, or 128. Actually, I don't know how often the higher numbers are used, but /16 and *16 ought to be very commonly used, since they're essential for working with nibbles. There's no reason to do them with standard division and multiplication routines, especially if that means having to call a "system subroutine" that has to switch banks in a multi-banked game.

Michael




0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users