Jump to content
IGNORED

Finished (probably) version of our first TI game!


Krool885

Recommended Posts

Hi everyone!

 

Me and a friend have gotten into TI99 assembly recently and we just finished our first game. It's just snake but we don't think it's a terrible version at least...

 

I've attached the binary for use on FinalGROM99, Classic99 etc. and the source code. I'd love for someone to take a look over our (very) amateurish code and give us a few tips, as we're kinda shit and we plan to do some other projects in the future with hopefully better code.

 

The forums been pretty useful so far, so thanks!

 

Snake.asm Snake.bin

  • Like 24
  • Thanks 1
Link to comment
Share on other sites

Did someone ASK for a code review?? :)

 

Nice use of graphics and sound. I wasn't expecting game options and the like from a first title!

 

Initial impressions of the code is good! You took some care with it.

 

Things like the equates you could store in a separate file and COPY it in, but not a big deal (just saves some copy and paste to new projects).

 

I probably would not use byte variables as often - there is a cost in CPU time and memory space to use them (you save a byte of storage, but every access by address costs you two extra bytes of ROM and an extra 8 cycles just to read the address). But, I also think that's a personal choice as most of the time you are not starved for bytes or cycles.

 

For the initializations at the start, you could get around it by storing the constant in the high byte of R0 instead of using R0LB (so, LI R0,78*256 // MOVB R0,@NM1 -- don't even need to do the math yourself that way!)

 

Other style stuff.. most code uses R0,R1,R2 to pass values to functions, you are using R8 and up. Not at all a problem, just convention and not even that strict.

 

I don't see anything wrong with it at all, honestly. It's documented, cleanly written, clearly tested, you did all your own hardware interface, it's pretty solid!

 

  • Like 10
Link to comment
Share on other sites

Very nice! Surprisingly complete, effective graphics, some sound, all in compact code. Nice use of subroutines. I like the low-level approach with custom VDP and CRU routines.

 

As for coding tips:

  • Documentation is essential to make sense of any assembly code, e.g. what do the subroutines do.
  • You can use BLWP/RTWP subroutines (with their own register workspaces) instead of BL/RT subroutines, so you don't need to worry about clobbering registers. You might then even keep all your variables in registers, which makes for simpler code (although the naming isn't as explicit).
  • You could consider the xdt assembler. It supports long names and macros, to work at a slightly higher level of abstraction.
  • Like 4
Link to comment
Share on other sites

2 hours ago, Krool885 said:

Hi everyone!

 

Me and a friend have gotten into TI99 assembly recently and we just finished our first game. It's just snake but we don't think it's a terrible version at least...

 

I've attached the binary for use on FinalGROM99, Classic99 etc. and the source code. I'd love for someone to take a look over our (very) amateurish code and give us a few tips, as we're kinda shit and we plan to do some other projects in the future with hopefully better code.

 

The forums been pretty useful so far, so thanks!

 

Snake.asm 23.87 kB · 13 downloads Snake.bin 3.36 kB · 12 downloads

I don't know nothing about assembly coding, but i do know i'm addicted to snake games, on pretty much any platform.

Can never have too many snake games IMO. This one is pretty good 🙂.

 

The speed goes from 1-3, and then P for some reason?

Edited by jrhodes
  • Like 1
Link to comment
Share on other sites

3 minutes ago, jrhodes said:

I don't know nothing about assembly coding, but i do know i'm addicted to snake games, on pretty much any platform.

Can never have too many snake games IMO. This one is pretty good 🙂.

 

The speed goes from 1-3, and then P for some reason?

P is for progressive lol, as in your speed increases after each apple pickup.

  • Like 3
  • Thanks 2
Link to comment
Share on other sites

11 hours ago, Krool885 said:

Me and a friend have gotten into TI99 assembly recently and we just finished our first game.

 

Nice work to both of you!  Only a small number of people who start any game actually finish, let alone in assembly, and double let alone on a retro computer system.

 

11 hours ago, Krool885 said:

It's just snake ...

 

Nothing wrong with snake, and a very good choice for cutting your teeth and learning.  You chose wisely.

 

11 hours ago, Krool885 said:

... as we're kinda shit and we plan to do some other projects in the future with hopefully better code.

 

Eh, that's how it goes with your first few projects.  I don't know anyone who has been coding for any amount of time, who does not look back on their first projects and cringe a little.  It only gets better from here on. ;)

 

11 hours ago, Krool885 said:

I'd love for someone to take a look over our (very) amateurish code and give us a few tips ...

 

Random thoughts as I glance through the code:

 

* It is better than my first assembly programs.

 

* You wrap all of your VDP routines with `LIMI 0` and `LIMI 2`, except this one:

* R8=ADDR, R10=CHAR
VDPWRITE      MOVB @R8LB,@VDPWA      * Send low byte of VDP RAM write address
              ORI  R8,>4000          * Set read/write bits 14 and 15 to write (01)
              MOVB R8,@VDPWA         * Send high byte of VDP RAM write address

              MOVB @R10LB,@VDPWD
              RT

 

It is usually better to manage allowing interrupts (`LIMI 2` and `LIMI 0`) in a single place in your code, rather than scattering it all over.  The start or end of the game-loop is a good place.  Also, in this program you do not seem to be using any part of the console ISR, so you do not need to enable interrupts at all.  You could just do `LIMI 0` at the start of the program and not worry about it.

 

* You do this in a few places to convert row,col into a VDP name table offset:

* R8=COL, R9=ROW, R10=CHAR
CHRWRITE      LIMI 0
              LI R4,32
              LI R5,0
              MPY R9,R4
              A R8,R5      

 

Any time you find yourself multiply or dividing by powers of two, a shift will almost always be faster.  The above could be written as:

* R8=COL, R9=ROW, R10=CHAR
CHRWRITE      LIMI 0
              SLA R9,5    * Multiply ROW by 32
              A R8,R9     * Add the COL

 

This will save you two load-immediate instructions, and a very expensive multiply instruction.  This will leave the result in R9 instead of R5, so adjust other code accordingly.

 

* To set a register to zero, instead of `LI Rn,0`, use `CLR Rn` (n being a register number).  Same for setting to all ones: `SETO Rn` (instead of something like `LI Rn,>FFFF`)

 

* To increment a register: `INC Rn`, to increment by two: `INCT Rn` (instead of `AI Rn,2`)

 

* You might want to consider using the VDP interrupt to keep your game-loop timed to the frame rate.  This will help prevent tearing (updating the screen during the active display), and also gives you a nice way to control animations and game speed.  1/60th of a second is a good time-slice for most things.

 

* It is hard to represent any kind of program organization in assembly due to the nature of the formatting (no block indentation, etc.) so extra comment lines to help indicate what is happening can really help keep your place in the code (in 6 months it will not look so familiar to you).  Self-documenting code is a fallacy, IMO.

 

* You have a lot of code the manage and display the score, since you are storing the score as a single digits, 0-9, in individual bytes.  The function names hint at Binary Coded Decimal (BCD), but this is not actually BCD, and it just makes things harder.  It is better to keep the score in a single 16-bit value so it is easy to work with (add points, compare to high-score, etc.) and just convert the binary number to single digits when the score needs to be displayed.  Something like this is the basic idea, and sets up R1 and R2 for VDP display of the digits.

 

* Somewhere in code, along with other constants and variables.
...
TEN    DATA 10                * Number 10
...


*********************************************************************
*
* Unsigned Integer to ASCII - minimal version
*
* Converts a binary number in a 16-bit register into an ASCII representation,
* one digit at a time, by repeatedly dividing the number by 10 until there are
* no more digits.
*
* The tens digit is isolated every time through the loop, so the number is
* converted from right-to-left, hence passing a pointer to the END of the
* buffer to receive the converted ASCII digits.
*
* The buffer needs to be large enough to hold the converted value, which for a
* 16-bit value will never be larger than five digits (65535).
*
* R1 - Pointer to the *END* of a buffer to store ASCII digits.
* R2 - Destroyed.
* R3 - Destroyed.
* R4 - Value to convert, destroyed.
*
* Returns:
* R1 - Points to start of converted number, one ASCII digit per byte.
* R2 - Length of ASCII number.
*
I2A    CLR  R2                * Length counter
I2A1   CLR  R3                * Clear the high word of the dividend
       DIV  @TEN,R3           * Divide R3:R4 by 10 (R3=Q, R4=R)
       AI   R4,48             * Adjust remainder (will be 0..9) to ASCII digit
       MOVB @R4LB,*R1         * Move ASCII digit to the buffer
       DEC  R1                * Adjust buffer pointer to next digit location
       INC  R2                * Count the digit
       MOV  R3,R4             * Move quotient into R4 for the next digit
       JNE  I2A1
       B    *R11
*// I2A

 

Edited by matthew180
  • Like 4
Link to comment
Share on other sites

Congratulazioni per questa versione di Snake! Anche fino ai tempi del mitico Nokia 3310, sono sempre stato attratto da questo videogioco! Grazie per questa conversione per l'amato Ti99, attendo ulteriori sviluppi e ovviamente nuovi progetti da parte tua! Grazie per tutto il tempo dedicato allo sviluppo! 👍

Link to comment
Share on other sites

Hi Lily and Robyn!

Great work on your first assembly language game! You have a great career ahead of you in retrocoding, haha. 

 

My only tip for you is to use the DORG (Dummy ORiGin) to generate your variable labels without having to calculate the offsets all by hand.  It does not generate any object code, so you can use BYTE, DATA or BSS directives to specify how much size should be allocated to each variable.  The DORG needs to outside of your AORG section.  You should also generate a listing file so and check your variable offsets to make sure you haven't exceeded the fast RAM memory space.

 


       IDT  'SNAKE'

       DORG >8320
HDCOL  BYTE 0
HDROW  BYTE 0
TLCOL  BYTE 0
TLROW  BYTE 0
SNDIR  BYTE 0
WAITHI BYTE 0
WAITLO BYTE 0
SNDIR2 BYTE 0
SCR1   BYTE 0
SCR2   BYTE 0
SCR3   BYTE 0
NM1    BYTE 0
NM2    BYTE 0
NM3    BYTE 0
HSCR1  BYTE 0
HSCR2  BYTE 0
HSCR3  BYTE 0
CSCR1  BYTE 0
CSCR2  BYTE 0
CSCR3  BYTE 0
PSET   BYTE 0

       DORG >837D
VDPCHR BYTE 0
VDPROW BYTE 0
VDPCOL BYTE 0
* The BYTE values don't matter, no data is being written

       AORG >6000
...

 

You could also combine WAITHI and WAITLO into a single word "WAIT   DATA 0", but make sure it is on an even address by putting an "EVEN" directive on the previous line.  Then instead of this:

       LI R0,>0001
       MOVB @R0LB,@WAITHI
       LI R0,>00A0
       MOVB @R0LB,@WAITLO

You could do this:

       LI R0,>01A0
       MOV R0,@WAIT

Hope that is helpful.  Looking forward to seeing what else you can make.

  • Like 5
Link to comment
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...