Jump to content
matthew180

Assembly on the 99/4A

Recommended Posts

9 hours ago, Airshack said:

I was using Asm994a which didn't seem to flag it or provide warning. 

 

As indicated by @FarmerPotato, the assembler (both Assm994a and E/A), upon getting the ‘R’ flag adds to your code

R0     EQU  0
R1     EQU  1
R2     EQU  2
R3     EQU  3
R4     EQU  4
R5     EQU  5
R6     EQU  6
R7     EQU  7
R8     EQU  8
R9     EQU  9
R10    EQU  10
R11    EQU  11
R12    EQU  12
R13    EQU  13
R14    EQU  14
R15    EQU  15

 

The ‘Rn’ EQUates are strictly mnemonics for our eyes. The two mentioned assemblers do not care—R0 – R15 are just numbers and assemble without error where numbers are expected as you noted.

 

...lee

  • Thanks 1

Share this post


Link to post
Share on other sites

How much existing code uses just the register number instead of Rx?

 

Is it worth a mode in which the assembler requires the Rx form?

Share this post


Link to post
Share on other sites
16 minutes ago, BillG said:

How much existing code uses just the register number instead of Rx?

 

A lot:

  • Most (all?) of the disassembled code on Thierry’s site.
  • The disassembled code in Heiner Martin’s TI Intern.
  • The original assembly listing of TI Forth, which also used a significant amount of non-Rn EQUates. For example, the TI Forth workspace was dubbed TEMP0 – TEMP7, UP, SP, W, LINK, CRU, IP, RP, NEXT, corresponding to R0 – R15, respectively).
  • The original source code for the TI-99/4A ROM 0 (I think)
  • . . . and a lot more.

 

16 minutes ago, BillG said:

Is it worth a mode in which the assembler requires the Rx form?

 

I think @FarmerPotato’s suggestion of a warning when the Rn EQUates (and, possibly, any EQUates) are used for immediate operands would be preferable, though it might create more trouble than it is worth.

 

...lee

 

  • Like 1

Share this post


Link to post
Share on other sites

I see now....

 

 C    R8,R9    * the assembler is looking for registers so this is the same as...

 

 C    8,9        * I personally dislike referencing registers here without the ‘R’ simply for beginner readability 

                        * so I always use the ‘R’ as a form of better documenting my code

 

CI    R8,R9   * since the assembler is looking for a register and an immediate, it sees   CI    R8,9

 

I doubt any coder would ever intentionally wish to place an ‘R’ in front of an immediate constant in this way.

 

This ‘typo’ of mine took a while to figure out because as an assembly beginner it was difficult to see without a warning.

 

Thanks for the clarification guys.

 

 

 

 

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Next Question: How do you guys manage an assembly program’s size? Since I don’t fully understand how the assembler parcels out memory, other than with the specific in-code declarations I assign. BASIC has warnings for low memory and a command to let you know how much memory is available. 

 

What’s the quick and dirty for for doing this with an assembly program under say EA3 option? 

 

My current project produces an object code file which Ti99Dir says is of Size 251; disk size I’m using is 720 sectors, used is 254 sectors; free is 466. 

 

Any tricks in Classic99 I can use to check program size or mem available once loaded? 

 

Any pitfalls to avoid or or think about as program size inflates?

Share this post


Link to post
Share on other sites

This is a warning with Ralph's latest xas99:

> bnp.asm <2> 1317 - Warning: Register R9 used as immediate operand

 

IntelliJ IDEA editor will even show the little red squiggle when using the wrong operand type:

ci_r8_r9.png.88bef68f7064feb21378d4233aec486c.png

Edited by PeteE
  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites
45 minutes ago, PeteE said:

This is a warning with Ralph's latest xas99:

> bnp.asm <2> 1317 - Warning: Register R9 used as immediate operand

 

IntelliJ IDEA editor will even show the little red squiggle when using the wrong operand type:

ci_r8_r9.png.88bef68f7064feb21378d4233aec486c.png

Thanks PeteE. Upon your recommendation I’m using the IntelliJ IDEA 2017.1.5 editor with xtd99 IntelliJ Plugin, which provides these red squiggles yet I’ve somehow toggled this error correction option off? It no longer provides these visual queues. Maybe an update turned that off? Damn....now I have to get that working again.

 

I’m using plugin version 1.5.1 updated for IDEA 15.

Share this post


Link to post
Share on other sites
2 hours ago, Airshack said:

Next Question: How do you guys manage an assembly program’s size? Since I don’t fully understand how the assembler parcels out memory, other than with the specific in-code declarations I assign. BASIC has warnings for low memory and a command to let you know how much memory is available.

If you actually get into trouble with memory limitations for your machine code, you have a really big project. The worse problem is buffer space for your data, and this is something you can handle more easily. In many cases you'd take some explicitly defined memory area (e.g. from E000-FFFF).

 

For all my assembly language projects, I never had any concerns about memory space.

 

Remember that machine language is very efficient in space. For instance, assume that your average assembly code line has 4 bytes (lots of them with 2, others with 6, many with 4), a machine program with 8 KiB size (program image) means 2048 lines of code in average, and if you print your source code in two columns with 50 lines, you'll fill more than 20 pages.

  • Thanks 1

Share this post


Link to post
Share on other sites
5 hours ago, Airshack said:

Any tricks in Classic99 I can use to check program size or mem available once loaded?

In Classic99, load your program, bring up the debugger or press scroll lock then press F10 ...accept dialog... Goto Classic99's root folder and open MEMDUMP.BIN with a hex-editor, find and highlight your program, the size should be indicated somewhere on the status bar.

 

That's what I do.:)

  • Thanks 1

Share this post


Link to post
Share on other sites
On 10/31/2019 at 5:47 PM, intvnut said:

You might be slightly faster if you always CLR TOS, and then conditionally SETO, to avoid the extra unconditional JMP.  CLR doesn't modify the flags.  Something like this (guessing at the syntax):

             \  cx ax  bx
             \  R1 R0 tos
CODE WITHIN   ( n  lo  hi -- flag )
               R0  POP,
               R1  POP,
               R0  TOS SUB,
               R0  R1  SUB,
               TOS R1  SUB,
               TOS CLR,
               NC  IF, TOS SETO, ENDIF,
               NEXT,
               ENDCODE

 

A while back Intvnut  gave me this code. (in perfect Forth Assembler I might add) :)

While it worked perfectly it was 6 bytes bigger than the equivalent Forth version although of course faster.

 

I was not sure that this would work but I gave it a try.

By removing the POP macros, which are just  *SP+ <Rx> MOV,  and referencing the stack with indirect addressing I remove 4 byes and so it is worth using it to replace the Forth version in my tiny 8K kernel space. 

 

By way of explanation:

1. TOS is just an alias for R4 and is a "cache" for the tos of stack. (makes the VM look like an accumulator machine)

2. This means the 1st parameter "hi" is in R4 on entry.

3. "lo" is the 1st item on the stack in memory

4. "n" parameter is the 2nd item on the stack in memory.

 

CODE: WITHIN   ( n  lo  hi -- flag )
              *SP  TOS  SUB,
              *SP+ *SP  SUB,
               TOS *SP+ SUB,
               TOS CLR,
               NC IF,  TOS SETO, ENDIF,
               NEXT,
               END-CODE  \ 2 bytes bigger than Forth

 

  • Like 2

Share this post


Link to post
Share on other sites
1 hour ago, TheBF said:

A while back Intvnut  gave me this code. (in perfect Forth Assembler I might add) :)

Whoo hoo!  I eyeballed it correctly.

 

1 hour ago, TheBF said:

 

CODE: WITHIN   ( n  lo  hi -- flag )
              *SP  TOS  SUB,
              *SP+ *SP  SUB,
               TOS *SP+ SUB,
               TOS CLR,
               NC IF,  TOS SETO, ENDIF,
               NEXT,
               END-CODE  \ 2 bytes bigger than Forth

 

 

Very nice.  It seemed like the POP macros were unneeded overhead.  They make the code easier to follow, but direct SP manipulation seems more efficient (space, and hopefully time).

 

  • Like 2

Share this post


Link to post
Share on other sites
12 hours ago, Airshack said:

Next Question: How do you guys manage an assembly program’s size? Since I don’t fully understand how the assembler parcels out memory, other than with the specific in-code declarations I assign. BASIC has warnings for low memory and a command to let you know how much memory is available. 

If you want to be sure, generate a listing file (L with E/A, though you need to write it somewhere. With Classic99 I'd write it as a Windows file with the ?W tag for easier reading. ;) )

 

The listing will give you a good idea how big the program is - just scroll down. If the addresses are relative, then you'll see how many bytes are used. If they are absolute, you'll see the actual last address used. You'll also get a symbol list showing where all your named labels are.

 

  • Like 3
  • Thanks 1

Share this post


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

If you want to be sure, generate a listing file (L with E/A, though you need to write it somewhere. With Classic99 I'd write it as a Windows file with the ?W tag for easier reading. ;) )

 

The listing will give you a good idea how big the program is - just scroll down. If the addresses are relative, then you'll see how many bytes are used. If they are absolute, you'll see the actual last address used. You'll also get a symbol list showing where all your named labels are.

 

That’ll come in handy — thank you! 
 

I’m probably under-utilizing the debug tools with Classic99. I’ve figured out how to watch specific VDP and CPU memory locations, monitor the Register space, and watch the status bits, program counter and other obvious stuff, which is extremely useful — fun.


Setting breakpoints in Classic99 is something I’d like to use. It’s not so obvious. 
 

Maybe I need to examine the readme.txt or something?

Share this post


Link to post
Share on other sites
23 hours ago, Airshack said:

That’ll come in handy — thank you! 
 

I’m probably under-utilizing the debug tools with Classic99. I’ve figured out how to watch specific VDP and CPU memory locations, monitor the Register space, and watch the status bits, program counter and other obvious stuff, which is extremely useful — fun.


Setting breakpoints in Classic99 is something I’d like to use. It’s not so obvious. 
 

Maybe I need to examine the readme.txt or something?

Click the question mark under the breakpoint box, and it will give you a summary of the syntax for each breakpoint type. ;)

 

But yes, reading the manual (help->classic99 manual, or the pdf in the install folder) helps. ;)

 

  • Haha 1

Share this post


Link to post
Share on other sites
11 hours ago, Tursi said:

Click the question mark under the breakpoint box, and it will give you a summary of the syntax for each breakpoint type. ;)

 

But yes, reading the manual (help->classic99 manual, or the pdf in the install folder) helps. ;)

 

I’ll click on that question mark to see if I can figure it out before resorting to RTFM. :)

Share this post


Link to post
Share on other sites

I have a game development situation:

 

In order to avoid using more than four sprites in the same horizontal row, I’ve decided to “drop” a key onto my game map using a background tile vs using a fifth sprite.

 

The game is a vertical scroller with the play area roughly the size of four TI screens vertically.

 

At the beginning of my code I load a Magellan assembly data formatted map into memory. The assembler takes care of where...

 

I have a pointer to this map data memory location called MAPDAT. It turns out the assembler mysteriously assigns the map location (top left tile location) to: >C5B0.

 

In Upper Expansion RAM.

 

@MAPDAT = >C5B0 (50,608)

>C5B0 holds character >8B

 

I’ve used the debugger in Classic99 to verify my map data is in fact loading properly at this location.

 

My scroller is working...

 

Question: Is it possible to overwrite the data located at >C5B0? I’ve previously only used VDP console RAM read/writes. 
 

I guess I need a kickstart on accessing upper expansion RAM. There’s something simple I’m overlooking perhaps?


Obviously the data at >C5B0 and beyond is part of my code listing. It’s not logic — just data. Would modifying this data be considered self-modification of code? 
 

It’s possible I’m over-complicating something easy here. Just late and my progress has halted. Help! 

  • Like 1

Share this post


Link to post
Share on other sites
8 minutes ago, Airshack said:

I have a pointer to this map data memory location called MAPDAT. It turns out the assembler mysteriously assigns the map location (top left tile location) to: >C5B0.

 

Question: Is it possible to overwrite the data located at >C5B0? I’ve previously only used VDP console RAM read/writes. 
 

Obviously the data at >C5B0 and beyond is part of my code listing. It’s not logic — just data. Would modifying this data be considered self-modification of code? 

Obviously you can read from the MAPDAT and move data to the VDP. >C5B0 is CPU RAM. Your program can read and write to MAPDAT (since it's RAM). So yes, you can overwrite the data. It's probably not uncommon to overwrite data (once loaded).
 
No, it's not self-modifying code. You're modifying data, not code. It's probably uncommon to modify code.

 

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, sometimes99er said:

So yes, you can overwrite the data. It's probably not uncommon to overwrite data (once loaded)

So I may then use a simple register-to-memory or memory-to-memory application of MOVB to get the job done? 

 

Seems so much easier than working with the VDP RAM via ports. I don’t know why it’s not working out for me. Zero examples in the EA manual of mem-to-mem and reg-to-mem, and probably failing due to fatigue...zzz. Will try in the AM.

 

LI R0, >D900

MOVB RO,@MAPDAT  * to replace byte at MAPDAT w >D9

 

I think I tried this earlier and received a warning.

 

Will the 8bit data path to upper expansion ram result in MOV operating transparently even though it moves 16bits?

 

 

  • Like 2

Share this post


Link to post
Share on other sites
1 hour ago, Airshack said:

LI R0, >D900

MOVB RO,@MAPDAT  * to replace byte at MAPDAT w >D9

Probably just a typo in your post, but in the second line you use an O instead of a 0 in R0.

  • Like 2

Share this post


Link to post
Share on other sites
1 hour ago, Airshack said:

So I may then use a simple register-to-memory or memory-to-memory application of MOVB to get the job done?

Yep.

1 hour ago, Airshack said:

Seems so much easier than working with the VDP RAM via ports.

Yep. I usually restrain myself to unexpanded console, and then sometimes have to use VDP RAM instead of CPU RAM (ScratchPad is always there). You wrap it up (in code) so there's actually not much to it. Of course one has to do a lot of MOVB to the VDP to get a game going.

1 hour ago, Airshack said:

Will the 8bit data path to upper expansion ram result in MOV operating transparently even though it moves 16bits?

Yep. Overall a MOV is faster than 2 MOVB's, but ... MOVB has "the byte address flexibility" (if you're hitting memory with that size (wanting 8-bits rather than 16) ).
 

Share this post


Link to post
Share on other sites

   What a difference some coaching, some sleep, and a cup of coffee make! Of course my problem was undetectable near bedtime. 

 

   Thanks @sometimes99er & @Asmusr

 

   It’s almost thrilling to simply manipulate expanded RAM without messing around with the VDP portal routines. So this must be what it’s like to develop on a system with no VDP between the CPU and RAM. 

 

   Of course the other retro systems of the era are hampered by: 8-bit registers, fewer registers, less capable registers, no divide and multiply op codes, etc.

 

   I suppose the VDP memory access restrictions aren’t that big of a deal considering the power of the 9900, vs Z80 or 6502. 

 

   Anyway, it’s working now. I have a bridge key on my game map! 

 

   MOV    @MAPDAT, R6     * pointer to beginning of map

   LI        R7,>D900          * Character D9 = bridge key

   MOVB  R7,*R6               * Place key at map origin as a test

 

   My problem last night was I was using the following first line:

 

    LI        R6, @MAPDAT        * then basically corrupting MAPDAT                                         

 

   Caused all sorts of havoc onto my game screen.

  • Like 1

Share this post


Link to post
Share on other sites
5 hours ago, sometimes99er said:

I usually restrain myself to unexpanded console, and then sometimes have to use VDP RAM instead of CPU RAM (ScratchPad is always there).

At this point in my learning cycle I exercise little control over where the assembler is going to locate my code.

 

Big Picture Wise: How may I restrict myself to the unexpanded console RAM? 

Share this post


Link to post
Share on other sites
3 minutes ago, Airshack said:

At this point in my learning cycle I exercise little control over where the assembler is going to locate my code.

 

Big Picture Wise: How may I restrict myself to the unexpanded console RAM? 

The assembler should give you total control of where you put your code.   I'm assuming you have something like this in your assembly program:

       AORG >A000    ; start at upper expansion RAM

* your program code

MAPDAT
	   BYTE .... ; your map data from Magellan
	   BYTE ....

In order to run on an unexpanded console, you need to put your program in cartridge ROM like this:

       AORG >6000      ; start at cartridge ROM

* cartridge header
* your program code

MAPDAT
       BYTE ....  ; your map data from Magellan
       BYTE ....  ; note this is now read-only

Note that you can no longer modify the MAPDAT table at its assembler-defined address in ROM, but you must copy it to the VDP to be displayed and then you can modify it in VDP RAM through the VDP port.  To use the scratchpad for data, I use a group of EQUates to define address of variables, leaving room for the register workspace, and calculate the offsets manually by adding the size of the previous item to the current offset:

WRKSP EQU >8300    ; workspace at top of scratchpad
PLAYER EQU WRKSP+32  ; 32 is the size of WRKSP, player is 2 bytes
ENEMY EQU WRKSP+34   ; 2 bytes after PLAYER, enemy is 2 bytes
KEYLOC EQU WRKSP+36   ; 2 bytes after ENEMY, keyloc is 2 bytes
ETC

Keep in mind the scratchpad is only 256 bytes, so the offset cannot exceed 255... and you either need to avoid using scratchpad data used by the ISR, or keep  interrupts off (LIMI 0) for the duration of your program.

  • Like 1

Share this post


Link to post
Share on other sites
48 minutes ago, PeteE said:

The assembler should give you total control of where you put your code.   I'm assuming you have something like this in your assembly program:

       AORG >A000    ; start at upper expansion RAM

* your program code

MAPDAT
	   BYTE .... ; your map data from Magellan
	   BYTE ....

    Actually, No! I’m not using AORG at all. There’s no absolute addressing of my program code. While studying the EA manual I incorrectly assumed that an omission of AORG would simplify matters. What I believe I am realizing now is I’ve simply chosen to defer control over where my code is stored. 

 

    I was vaguely familiar with the assembler directive AORG from messing around with the mini-memory cartridge. Seems my game project began and has grown wildly without using AORG to manage absolute addressing. This being my first assembly game and all, I was pretty happy to let the assembler do its thing.

 

   It appears AORG is going to be necessary if I want this game to ever run from a cartridge. Correct? 

 

   Also, I’ll need to find another way to place keys upon my game background as ROM map images are obviously impossible to manipulate in the manner I’ve recently implemented.

 

   One thought: If I keep track of where my key objects should be located on the scrollable game map, I can simply plot them over grass tiles, after each viewport redraw. I’m imagining this will create a blinking appearance as the map is continually redrawn, and the key is then replotted. Perhaps it will appear as a nice feature highlighting the key’s location?

Share this post


Link to post
Share on other sites
7 minutes ago, Airshack said:

 It appears AORG is going to be necessary if I want this game to ever run from a cartridge. Correct?

 

Pretty much.

 

...lee

  • Like 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.

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...