Jump to content

Recommended Posts

Posted (edited)
19 minutes ago, Lillapojkenpåön said:

Lol, my builds have been failing for a while and my batch script ran the last working bin, so that explains the GRAM card,

still don't know how to use the mode tho.

The MODE statement just sets the video mode to either Color Stack (#1) or Foreground/Background (#2).  In the case of Color Stack, it also sets the color stack background colors.  (You'll need to read up on how the Color Stack works to understand what that is.)


The CONST statement will only assign a constant value to a symbol, GW in your case.  This is similar to a variable, except that it doesn't take any memory, and can never change.  Essentially, the compiler stores the value in its memory and replaces it in the code with the actual number during the compilation process.

 

So, you set the MODE, and then defined a CONST.  Presumably your GW constant holds a value that you intend to give to a BACKTAB card in order to display a GRAM picture you have already loaded.  All you need to do now is "poke" that value into the BACKTAB as you were instructed before, either with the POKE statement or the #BACKTAB array.


Just for reference, here are the different ways you can display a card on the screen:

  • POKE:  puts a value onto the address of one card.
  • #BACKTAB:  assigns a value to one card.
  • SCREEN: loads an pre-defined array or matrix of card data, like an entire screen scene, into the BACKTAB directly.
  • PRINT: loads a string of GROM characters, or a GRAM card into the current cursor location in the screen, or to a given column/row coordinate.

 

That last one is intended to print strings onto the screen, but since strings are just characters, and characters come from GROM, you can manipulate the statement to print other pictures, even from GRAM.

 

    dZ.

Edited by DZ-Jay
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)

I believe this code roughly does what you want (but using SCREEN would be more efficient).

 

  include "../samples/constants.bas"

  MODE SCREEN_FOREGROUND_BACKGROUND
  CONST GW = BG00 + FG_BLUE + BG_TAN
  CONST WE = BG01 + FG_WHITE + BG_PURPLE

  DEFINE 0,2,gfx

  RESTORE level
  FOR y=0 TO 10
    FOR x=0 TO 19
      READ #line
      #BACKTAB(y*20+x)=#line
    NEXT
  NEXT

loop:
  GOTO loop

gfx:
  BITMAP "ooo.ooo."
  BITMAP "ooo.ooo."  
  BITMAP "ooo.ooo."
  BITMAP "........"  
  BITMAP ".ooo.ooo"
  BITMAP ".ooo.ooo"  
  BITMAP ".ooo.ooo"
  BITMAP "........"  

  BITMAP "oooooooo"
  BITMAP "o...o..o"  
  BITMAP "o...oo.o"
  BITMAP "oooooooo"  
  BITMAP "o...oo.o"
  BITMAP "o...o..o"  
  BITMAP "oooooooo"
  BITMAP "........"  

level:
  DATA GW,GW,00,00,00,GW,GW,00,00,GW,GW,00,00,GW,GW,00,00,00,GW,GW
  DATA GW,00,00,00,00,00,GW,00,00,00,00,00,00,GW,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,GW,GW,00,00,00,00,GW,GW,00,00,00,00,00,GW
  DATA GW,GW,00,00,00,00,00,GW,00,00,00,00,GW,00,00,00,00,00,GW,GW
  DATA GW,GW,00,00,00,00,00,GW,00,00,00,00,GW,00,00,00,00,00,GW,GW
  DATA GW,GW,GW,00,00,00,00,GW,00,00,00,00,GW,00,00,00,00,GW,GW,GW
  DATA GW,GW,GW,GW,GW,00,00,GW,GW,00,00,GW,GW,00,00,GW,GW,GW,GW,GW
  DATA GW,GW,GW,GW,GW,00,00,00,00,00,00,00,00,00,00,GW,GW,GW,GW,GW
  DATA GW,GW,00,00,GW,00,WE,00,00,00,00,00,00,WE,00,GW,00,00,GW,GW
  DATA GW,GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW,GW
  DATA GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW

 

shot0002.gif.c23167351cfd99c0bae388370d29bb1e.gif

Edited by carlsson
  • Thanks 1

Share this post


Link to post
Share on other sites

By the way, you need a WAIT statement after MODE or DEFINE for them to take effect.  That's because the video memory can only be updated at specific critical times, and IntyBASIC buffers the changes for you automatically.  The WAIT command, essentially waits for the next video frame to apply the buffered changes.


dZ.

  • Thanks 1

Share this post


Link to post
Share on other sites

Correct, I forgot WAIT in the above example but I suppose a vertical blank happens anyway at some point while filling the BACKTAB. It was not intentional.

Share this post


Link to post
Share on other sites
1 minute ago, carlsson said:

Correct, I forgot WAIT in the above example but I suppose a vertical blank happens anyway at some point while filling the BACKTAB. It was not intentional.

Oh, my comment was to @Lillapojkenpåön, not you, sorry.  I was writing it before you posted.  :)

  • Like 1

Share this post


Link to post
Share on other sites
13 minutes ago, carlsson said:

I believe this code roughly does what you want (but using SCREEN would be more efficient).

 

shot0002.gif.c23167351cfd99c0bae388370d29bb1e.gif

Yup that's pretty much exactly what I've got but alot prettier,

and I'm also trying it with GROM characters after a successful GRAM test,

I understand SCREEN might be faster, but I'm not learning anything by using it, I'll use it later.

I seriously didn't know intellivision could do graphics like this! I'm so hyped!!

 INCLUDE "constants.bas"
 CONST GW =  $0000 + 25 + FG_BLUE  + BG_ORANGE

 MODE SCREEN_FOREGROUND_BACKGROUND
 WAIT

 DEFINE DEF00,1,bitmaps
 WAIT

 DIM offset
 DIM #line

 RESTORE level1
 FOR offset = 0 TO 239
     READ #line
     #BACKTAB(offset) = #line
 NEXT offset
 WAIT


loop:

 WAIT
 GOTO loop

bitmaps:
 BITMAP "########"
 BITMAP "#......#"
 BITMAP "#......#"
 BITMAP "#......#"
 BITMAP "#......#"
 BITMAP "#......#"
 BITMAP "#......#"
 BITMAP "########"

level1:
  DATA GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,GW
  DATA GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW,GW

 

The WAIT command after MODE fixed my first issue

Don't know why the different characters have different colors instead of just being blue?

 

As I said before my other issue was just that the compilation failed because of a missing colon, so the emulator ran an old build, made it seem like changing $0800 to $0000 had no affect.

Share this post


Link to post
Share on other sites

The issue here is that you need 25*8.

 

Have a look at the Intellivision wiki: http://wiki.intellivision.us/index.php?title=STIC

 

In particular this illustration for how each BACKTAB position is constructed in FG/BG mode. You see that GROM/GRAM is bit 11 which happens to equal $0800. Further you see that the background colour is constructed out of bits 9, 10, 12, 13 which explains the background constants $0200 - $3600. Foreground is the three lowest bits which perhaps is easiest to understand. It means the actual card to displayed is made out of the six bits (64 GROM or 64 GRAM) at bit 3-8, which explains why you always need to multiply this number by 8.

 

Stic_fgbg_word.thumb.png.32881f457cd153dd048c58d60db2765d.png

 

In the constants.bas file, BG00 - BG63 hold the values $0800, $0808 etc up to $09F8 which means it both sets the GROM/GRAM bit and does the multiplication by 8 for each card. The corresponding shortcuts for GROM characters are not defined but in theory you could use BG00 - GRAM, or manually calculate the value.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)
9 hours ago, Lillapojkenpåön said:

I understand SCREEN might be faster, but I'm not learning anything by using it, I'll use it later.

I disagree.  Part of being a craftsman is to learn to use the proper tool for the job.  It does you no good to use your hammer to drive screws for the mere notion that "a screwdriver might be better, but I should learn to hammer everything first."

 

In IntyBASIC, to change one card, you "poke" it's value into screen memory; and to paint a scene, you use SCREEN to load the screen data into memory.

 

In the final account, you could replace every single statement in your code with a POKE or a PEEK, because ultimately all you are doing is putting things in memory and reading them back.  However, it would be silly to do so because then there would be no advantage to using IntyBASIC versus, say, Assembly Language, or even directly hexadecimal machine code, and we know that is not really the case.


Anyway, you are free to do as you wish, but I personally would recommend you heed the advise of others on not only learning the mechanics of programming a game, but on how to do it in the most optimally "IntyBASIC way." ;)

 

Of course, if something is not clear, or if you'd like to dive deeper into how things work, just ask -- plenty of people here are willing to help.

 

    dZ.

Edited by DZ-Jay

Share this post


Link to post
Share on other sites

Ofcourse, what I meant was that allmost everything I've learned so far came from me trying to do a loop instead of use SCREEN, which I was able to use by just copying code from examples without knowing what it did, but I want to learn the architecture, and what it copies, from where to where, I had allready put this comment from the intyBASIC manual by my SCREEN statement

'This statement is the fastest way to copy screen data to video

So I intend to use it, the loop is just for learning purposes.

Share this post


Link to post
Share on other sites
Posted (edited)
1 hour ago, Lillapojkenpåön said:

Ofcourse, what I meant was that allmost everything I've learned so far came from me trying to do a loop instead of use SCREEN, which I was able to use by just copying code from examples without knowing what it did, but I want to learn the architecture, and what it copies, from where to where, I had allready put this comment from the intyBASIC manual by my SCREEN statement

'This statement is the fastest way to copy screen data to video

So I intend to use it, the loop is just for learning purposes.

Just to be sure, I would recommend to use SCREEN not only because it is faster, but because it is the language construct to paint a whole screen, and your code would be cleaner and easier to read and understand if you employ it.  :)

 

48 minutes ago, Lillapojkenpåön said:

Thanks! that's perfect, I'm gonna go through that wiki now.

 

By the way, ask about anything that is not clear from there.  The important thing to know is that the BACKTAB data is a 13-bit word composed of various fields that instruct the video controller what to do for that card.  As @carlsson pointed out, some bits are reserved for the foreground color, and some for the card index number.  Understanding the bit-field layout is useful.

 

The card index number is composed of 6-bits, that is, it can take a value from 0 (20 - 1) to 63 (26 - 1).  However, it is shifted three bits to the left, starting at bit #3.  It means that in order to specify the card number, you need to shift the index by three bits, which is equivalent to multiplying it by 23, or 8.

 

Also, as @carlsson suggested, the "Constants.bas" file defines a bunch of useful constants to commonly used numeric values, and among them is the specific values for particular card numbers to use in a BACKTAB word.  BG00 to BG63 hold the pre-computed values to use for GRAM cards, so they represent the card number, shifted three bits to the left, and with the "GRAM" bit set.  In other words,

  BG00 = ( 0 * 8) + $0800
  BG01 = ( 1 * 8) + $0800
  ...
  BG63 = (63 * 8) + $0800

 

They are just convenient to use not only because they save keystrokes, but because reading something like "BG01" in your code tells you precisely what it is, whereas "(1 * 8) + $0800" may be a little obscure.

 

     -dZ.

Edited by DZ-Jay
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)
On 6/30/2021 at 1:18 PM, DZ-Jay said:

They are just convenient to use not only because they save keystrokes, but because reading something like "BG01" in your code tells you precisely what it is, whereas "(1 * 8) + $0800" may be a little obscure.

 

     -dZ.

Thanks! I can't find a way to do text replacement?

The way you would hide bit operations in bB

 

  def m0Up=c{4}

 

  if !m0Up then..

 

 

 

 

Edited by Lillapojkenpåön

Share this post


Link to post
Share on other sites
15 minutes ago, Lillapojkenpåön said:

Thanks! I can't find a way to do text replacement?

The way you would hide bit operations in bB

 

  def m0Up=c{4}

 

  if !m0Up then..

 

 

 

 

You can use the DEF FN statement to define “functions.”  These are essentially “macros” in IntyBASIC.

 

   dZ.

  • Thanks 1

Share this post


Link to post
Share on other sites

Just to be clear, “CONST” will perform text substitution, but applies only to expressions that evaluate to a numeric value.  Thus, what you assign to a CONST symbol must be a valid numeric IntyBASIC.  The compiler will evaluate the expression and replace all instances of the symbol with the results.

 

”DEF FN” also performs text substitution, but it requires a valid IntyBASIC statement or expression.  It is akin to a user macro, which may or may not take arguments, and executes actual code.  The compiler will replace the symbol with its contents, inline, but it will be executed and evaluated at runtime.

 

The compiler does process that macro to ensure it is valid code.

 

   dZ.

  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)
1 hour ago, Lillapojkenpåön said:

Is there a way to DIM the two bytes of a 16 bit variable?

What do you mean by that?

 

If you mean extracting each individual byte into a separate variable, you can use the following expressions:

 

high = (#word / 256)   ' Extract high order byte
low  = (#word AND 255) ' Extract low order byte

Actually, the second one does not need the "AND" term, since assigning a 16-bit variable to an 8-but variable will automatically truncate word to the lower 8-bits.

 

   dZ.

Edited by DZ-Jay
  • Thanks 1

Share this post


Link to post
Share on other sites
6 minutes ago, DZ-Jay said:

What do you mean by that?

    Something like this, to have easy access to the two bytes?

 

    DIM #h

    DIM h1 = #h

    DIM h2 = #h + 1

 

And how come the sprite have to be placed at x8 y8 to show up in the top left corner of the screen instead of x0 y0?

Share this post


Link to post
Share on other sites
Posted (edited)
29 minutes ago, Lillapojkenpåön said:

    Something like this, to have easy access to the two bytes?

 

    DIM #h

    DIM h1 = #h

    DIM h2 = #h + 1

That's what I thought.  Unfortunately, you cannot do it that way, but you can use the expressions I posted above.

Edited by DZ-Jay
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)
33 minutes ago, Lillapojkenpåön said:

 And how come the sprite have to be placed at x8 y8 to show up in the top left corner of the screen instead of x0 y0?

For that to make sense you first need to understand how MOBs positions relate to the display area.  I'll try to explain below.

 

The screen is composed of two graphics "planes":  one is the display area, governed by the BACKTAB, i.e., the background scene; and the other is the "object field," where the MOBs roam.  Each one has its own coordinate system.  You already saw how the BACKTAB is an unidimensional array, and its "coordinate" is the memory address of each element.  In contrast, the MOB object field is a Cartesian plane with the origin at the top-left of the screen.  

 

In reality, the object field is slightly larger than the display area.  While the display is 20 x 12 cards (8x8 pixels each), the object space is actually a whole card wider and taller.  The object field projects 8 pixels to the left to the top of the display area.

 

Here is a good illustration of this:

              +--------------------------------------------+
              |  OBJECT FIELD outer area                   |
              |   +---------------------------------------+|
              |   |  DISPLAYED AREA of object field       ||
              |   |                                       ||
              |   |                                       ||
              |   |                                       ||
              |   |                                       ||
              |   |                                       ||
              |   |                                       ||
              |   |                                       ||
              |   |                                       ||
              |   |                                       ||
              |   |                                       ||
              |   +---------------------------------------+|
              +--------------------------------------------+

 

The reason for this is to allow for MOBs to smoothly enter the display.  If you think about this, it makes perfect sense:  a MOB's position, it's X and Y coordinate, corresponds to its top-left corner.

 

To illustrate why this is important, consider if the MOB were constrained to the display area, it means that its origin (0,0) would place it at the top left of the visible screen.

 

What if you need to move the MOB out of the screen?  On the right-side margin is easy, because the maximum X coordinate is 159, which would put the left-most pixels of the MOB at the very edge of the screen, hiding the rest off view.  However, on the left-side edge it is not that simple, because there are no negative coordinates to move the left-most corner of the MOB off the screen.  If you cannot transition it off the screen, it would just "disappear" abruptly:  one frame it is there, visibly whole, and the next frame, POOF! it's gone.

 

A similar problem occurs transitioning out of the top edge.

 

This is why the object plane projects 8 pixels to the left and to the top of the display area, so that placing a MOB at the origin (0,0), essentially means that it is off screen.  From there, you can transition it smoothly into view one pixel at a time, as necessary.


One implication of this, as you noticed, is that you need to add 8 pixels to each axis when translating from MOB coordinates into BACKTAB column/row coordinates.

 

  dZ.

Edited by DZ-Jay
  • Thanks 1

Share this post


Link to post
Share on other sites
37 minutes ago, Lillapojkenpåön said:

    DIM #h

    DIM h1 = #h

    DIM h2 = #h + 1

8-bit and 16-bit variables are placed in different memory areas, even different RAM chips. I  wonder if it is be possible to do some VARPTR magic to have a 16-bit pointer to another 16-bit variable but I'm not sure what you could achieve by doing that. The copy by value method DZ-Jay suggested will probably do what you want.

Share this post


Link to post
Share on other sites
16 hours ago, carlsson said:

8-bit and 16-bit variables are placed in different memory areas, even different RAM chips. I  wonder if it is be possible to do some VARPTR magic to have a 16-bit pointer to another 16-bit variable but I'm not sure what you could achieve by doing that. The copy by value method DZ-Jay suggested will probably do what you want.

I just wanted some easy to use 8.8 variables with fractional increments of the high byte, but sounds like it would be alot faster to just use two bytes and a little bit of logic to do the same thing.

Share this post


Link to post
Share on other sites
Posted (edited)
1 hour ago, Lillapojkenpåön said:

I just wanted some easy to use 8.8 variables with fractional increments of the high byte, but sounds like it would be alot faster to just use two bytes and a little bit of logic to do the same thing.

Ah, then look in the IntyBASIC manual for how to use fixed-point values.  You use "+." and "-." as the operator for fixed-point addition and subtraction, respectively.

 

However, take note that internally IntyBASIC will reverse the 8.8 bytes as an optimization for integer access.  It also handles the overflow from the fractional byte to integer automatically.  The manual references the following forum:

 

 

    -dZ.

Edited by DZ-Jay

Share this post


Link to post
Share on other sites
Posted (edited)

Posting here from our private conversation, for others to see:

 

If what you want to do is handle fractional speeds and positions using 8Q fixed-point arithmetic, you can do this in two ways:  either manually (which is what you seem to be attempting to do here), or using the "+." and "-." IntyBASIC operators that do it for you.

 

If you do this manually, you'll have to do something like this:

DIM #h   ' Stores the 16-bit 8.8 fixed-point fraction

DEF FN hHigh = (#h / 256)
DEF FN hLow  = (#h AND $00FF)
DEF FN Fixed(hi, lo, scale) = (hi * 256) + ((low / scale) * 256)

' The fractional part is scaled by 256:
'   1.50 = (1 * 256) + ((50 / 100) * 256)
#h = Fixed(1, 50, 100)

' Adding 0.50 to #h:
#h = #h + Fixed(0, 50, 100)

' Adding 1.35 to #h:
#h = #h + Fixed(1, 35, 100)

 

Note that because we are using 8.8 fixed-point values in a binary system, we need to scale the fractions by 256.  That means that 1/2 of 100 (0.50 in Decimal) = 1/2 of 256 ($0080 in Hexadecimal).

 

That's what the "Fixed()" macro does above, and it lets you select your scale, or how many decimal places of precision.  For example, "0.50" is 100 (two decimal places, 102), and "0.3" is 10 (one decimal place, 101).

 

All this complexity can be handled by the compiler for you if you use the special fixed-point arithmetic operators.  You can use them like this:

 

DIM #h   ' Stores the 16-bit 8.8 fixed-point fraction

' Initialize the value as a floating point.
' Note that it doesn't matter if you assign an integer
' initially, because in the reverse notation used by the
' compiler, real number "1.0" is stored identically to
' integer "1" -- that is, the "zero" fraction is in the
' upper byte, and the "one" integer is in the lower byte.
#h = 1.5

' Add 0.5 to #h:
#h = #h +. 0.5

' Add 1.35 to #h:
#h = #h +. 1.35

' Subtract 0.25 from #h:
#h = #h -. 0.25

The IntyBASIC compiler will do the magic for you in converting the fractional values to 8.8 scaled by 256.

 

One thing to note, as mentioned above, is that internally, the generated code stores the value in reverse order:

        Fractional Part                      Integer Part
 ______________________________      ______________________________
/                              \    /                              \
+---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+
| F | F | F | F | F | F | F | F | . | I | I | I | I | I | I | I | I |
+---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+

 

For example:

  1.5 Decimal = $0180 (1 integer, 128 fraction) = [00000001 10000000] (8.8)

+---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | . | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
+---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+
\_______________________________/   \_______________________________/
   0.5 = 128 = $0080 = 1000000            1 = $0001 = 00000001
            Fraction                              Integer

 

Does this make sense?

 

    -dZ.

Edited by DZ-Jay
Edited for clarity and correctness
  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
51 minutes ago, DZ-Jay said:

Does this make sense?

 

    -dZ.

Yup, except I still can't figure out how to add or subtract an 8-bit variable to or from the integer part

 

#h = #h + overlap * 256    'nope

#h = #h + overlap    'nope

#h = #h +. overlap.0    'nope

#h = #h + #overlap2    'nope

Share this post


Link to post
Share on other sites
1 hour ago, Lillapojkenpåön said:

Yup, except I still can't figure out how to add or subtract an 8-bit variable to or from the integer part

 

#h = #h + overlap * 256    'nope

#h = #h + overlap    'nope

#h = #h +. overlap.0    'nope

#h = #h + #overlap2    'nope

 

You do it like this:

DIM #h
DIM var

#h  = 1.5        ' #h = 1.5
var = 1

#h = #h +. var   ' #h = 2.5

 

Internally, the variable goes from $8001 (1.5 in reverse-byte order) to $8002 (2.5 in reverse-byte order).  I have confirmed this in the debugger.

 

   -dZ.

  • Thanks 1

Share this post


Link to post
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...