Jump to content

Photo

Assembly on the 99/4A


516 replies to this topic

#501 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 253 posts
  • Location:The Great White North

Posted Sun Jul 16, 2017 8:34 PM

Thanks Tursi!



#502 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 253 posts
  • Location:The Great White North

Posted Sun Jul 16, 2017 8:42 PM

 I'd suggest setting a breakpoint on it (that becomes asm, right?) 

BTW  I have to smile at this comment.  :) 

 

Actually ... it is ASM and it becomes machine code... instantly.

 

I know it's hard for the world to believe you can make an assembler 1 instruction at a time with all the instructions backwards.  :grin: 

 

B

 

PS I know you already know this, I am just smiling here at your double checking in case this Forth nut has some other weird stuff going on.



#503 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 253 posts
  • Location:The Great White North

Posted Mon Jul 17, 2017 8:13 AM

. I'd suggest setting a breakpoint on it (that becomes asm, right?) and just stepping through the first invocation to see what the CPU is actually doing.

 

So I ran the routine with a breakpoint in the debugger and it seemed to worked perfectly.  Of course it did! :-)

 

More research needed...



#504 Airshack OFFLINE  

Airshack

    Moonsweeper

  • 419 posts
  • Location:Phoenix, AZ

Posted Tue Jul 18, 2017 5:54 PM

I'm trying to get Magellan to export data to the same format as Matthew's Viewport example:

 

* -- Map Row 0 (of 40) -- [ 'MAP6' is 88 * 40 ]
MAP6 DATA >696E,>6E69,>6960,>6060 ; * Each table row = 8 tile identifiers: >69, >6E, >6E, >69, >69, etc...
DATA >6060,>6060,>6060,>6060 ; * - each tile in the table is described by one 16-bit word
DATA >6060,>6068,>6868,>6868 ; * - the table has 11 DATA rows per Map Row
DATA >6868,>6860,>6060,>6060 ; * - therefor each Map Row has 8 * 11 = 88 Tiles (per MAP row)
DATA >6060,>6060,>6868,>6868 ; *
DATA >686F,>6F6F,>6F68,>6868 ; * In this example code the MAP is 88 Tiles wide * 40 Map rows
DATA >6868,>6868,>6860,>6060 ;
DATA >6868,>6868,>6868,>6060 ; * The Origin (0,0) of the Map is top-left corner of the MAP
DATA >6060,>6060,>6060,>6060 ;
DATA >6060,>6060,>6060,>6060 ;
DATA >6060,>6060,>6060,>6060 ;
* -- Map Row 1 (of 40)--
DATA >6B69,>6B70,>7060,>6060 ; * <==== 8 tiles for row 1/40
DATA >6060,>6068,>6868,>6868 ;
DATA >6868,>6868,>6868,>6868 ;
DATA >6868,>6860,>6060,>6060 ;
DATA >6060,>6060,>6868,>6868 ;
DATA >686A,>6A6A,>6A68,>6868 ;
DATA >6868,>6868,>6860,>6060 ;
DATA >6868,>6868,>6868,>6868 ;
DATA >6868,>6868,>6868,>6868 ;
DATA >6860,>6060,>6068,>6868 ;
DATA >6868,>6860,>6060,>6060 ; * 88 tiles TOTAL for row 1/40
etc....

 

I'm having difficulty using the Export feature of Magellan. Any good documentation out there?



#505 Asmusr OFFLINE  

Asmusr

    River Patroller

  • 2,357 posts
  • Location:Denmark

Posted Wed Jul 19, 2017 2:31 AM

I'm trying to get Magellan to export data to the same format as Matthew's Viewport example:

 

I'm having difficulty using the Export feature of Magellan. Any good documentation out there?

 

What's for format you're looking for? 

 

I don't believe there's anymore documentation than the bit that's included.



#506 Airshack OFFLINE  

Airshack

    Moonsweeper

  • 419 posts
  • Location:Phoenix, AZ

Posted Wed Jul 19, 2017 7:11 AM

 
What's for format you're looking for? 
 
I don't believe there's anymore documentation than the bit that's included.


Well... I believe I can work with what Magellan produces after all. It does seem strange how Export menu items work though.

Ex. The BASIC and XB Export functions don't seem to export anything? I remember Sinphaltimus finding a resolution for this issue last year? I forgot the fix. Probably needing to check a Magellan thread.

#507 Airshack OFFLINE  

Airshack

    Moonsweeper

  • 419 posts
  • Location:Phoenix, AZ

Posted Wed Jul 19, 2017 7:47 AM

 

What's for format you're looking for? 

 

I don't believe there's anymore documentation than the bit that's included.

 

I was just looking for a Map data arrangement similar to the ViewPort code.

 

 

 

 

Turns out that's Magellan's default IF No Compression is selected in the Export Settings menu.



#508 Airshack OFFLINE  

Airshack

    Moonsweeper

  • 419 posts
  • Location:Phoenix, AZ

Posted Fri Jul 21, 2017 2:01 AM

Attached File  DSK9.zip   26.17KB   1 downloadsThat's how you learn. If you don't struggle through something, you won't understand it or get better. Your knowledge comes from your failures as much as your success.

My code was designed to be a viewport scroller. Although you can use it as a basis for further development, there is a *lot* more that needs to go into it to facilitate a game.

You should spend some time understand the code and where/how things work. Try changing something and see what happens. Look at the sprite code, look at the coordinate to offset translations, etc. I try to comment enough to explain what is going on, but I would be very surprised if you didn't have any questions.

Matthew

 

 

I've invested the time to understand matthew's viewport code from page-4 of this thread. Here's my twist on the whole thing along with comments clarifying what may seem obvious to experienced TI Assembly programmers. The attached Viewport code is over-commented to suit my needs and to encapsulate answers to questions, thread comments, etc:

 

Attached File  ViewportFD.zip   5.67KB   2 downloads

 

This version of the viewport code is American Football themed.

 

Added feature: I was able to allow for the addition of diagonal joystick moves by removing two JMP PLY10s and adding a JMP PLY03..

 

Whenever a Left or Right move is detected the code now ALSO checks for Up and DOWN which equals a diagonal move.

*      Test the joystick via the Communications Register Unit (CRU)
*                               Com Register Unit on TI-99/4A reads: Keyboard keys, Joysticks, Cassette i/o
*                                  - Interfaces with TMS9900 via TMS9901 Programmable Systems Interface (PSI)
*                                       - uses 12 lines of the address bus (3-14), does not use the data bus
*                                           - addresses 4,096 Input lines and 4,096 output lines
*                                                    - CRUIN line, CRUOUT line, CRUCLK line

*               Note: Joystick lines are tied high(1), and when a contact is made, they are pulled low.
*                                       - Making a contact with the joystick connects that contact to 0V & sets CRU bit.
*
       LI   R12,>0006         * Base CRU address for joystick 1 is placed into R12 for following Test Bit Operation
*                                    -   0000 0000 0000 0110
*                                    -    ^^^ ^^^^ ^^^^ ^    <== bits 3-14 yield >0000 ????

       TB   1                 * \\\\\\\\\\ LEFT MOVE CHECK \\\\\\\\\\ Jump if LEFT-Joy1 position not selected

*                                   TB 1 = Test Bit 1; Reads CRU bit/line 1 relative to the CRU base address in R12
*                                           The Equal Status Bit is set to the value of CRU line level, which gets
*                                           set '1' when the joystick line goes to 0V.
*                                       E/A Manual, 9.5, p.156
*                                           Reads the digital input bit on the CRU at the address specified
*                                           by the sum of the signed displacement (1) + contents of R12, bits 3-14,
*                                           and set the equal status bit (third MSbit) of Status Reg to the value read


       JEQ  PLY02             * Jump when Left not selected, test Right next
       MOVB @SLTNAM,R4        * R4 loaded with the Sprite address for the Left moving player Sprite's Top/Left Tile
*                                    - Player Sprites are four tiles in size
       DEC  R3                * R3 contains P1LOC (top/left)value; updates R3 held tile counter one position less/left
       DEC  R5                * R5 contains P1LOC+88(bottom/left); DEC R5 to update player1 Sprite's bottom-LEFT tile
       MOV  R3,R6             * R6 updates to hold a copy of P1LOC tile counter for the top/LEFT tile
*                                   - Since this is a LEFT move it's important to use LEFT side Sprite tiles
*                                     for when we later check move legality.
       DEC  R1                * R1 contains @P1MX value; this updates R1 held P1MX value
       JLT  PLY99             * Left side horizonal bound check; IF P1MX < 0 THEN No_Updating RETURN
*                                   - Player1 cannot move left beyond column 0
*                                   - Nothing has been updated above except temp. values held in registers
*       JMP  PLY10             * PLY10 is where we test the moves before making them permanent/executing them in memory
*                                   - R1= top/LefT P1MX; R3= P1LOC offset; R4 = char; R5 = bottom/LEFT; R6= P1LOC top/LT
        JMP  PLY03

PLY02  TB   2                 * ////////// RIGHT MOVE CHECK   CRU Bit 2 //////////
       JEQ  PLY03
       MOVB @SRTNAM,R4        * Sprite name
       INC  R3                * R3 contains P1LOC value; updates R3 held tile counter to one position right
       INCT R5                * R5 contains P1LOC+88; adds TWO so R5 now holds counter for Player1 Sprite bottow/RIGHT
*                                   - had to add TWO here since R5 = Sprite bottom/RIGHT for validity checking
       INC  R6                * R6 contains Sprite top/right tile offset; this updates it one tile to the right
       INCT R1                * R1 contains P1MX value; adds TWO so R1 now holds counter for Player1 Sprite top/RIGHT
*                                   - right moves dictate right side Sprite tile values here for move validity checks
       C    R1,@MAPW          * Upper horizontal bound check; see if right side of sprite exceeds right edge of map
       JGT  PLY99             * There is no arithmetic >= so make two checks
       JEQ  PLY99             * PLY99 RETURNs without moving
       DEC  R1                * R1 Readjusted to real (left/top) tile counter location, X + 1

*       JMP  PLY10             * R1= P1MX; R3= P1LOC offset; R4= char; R5= bottom/RIGHT; R6= top/RIGHT
*                               edited to go to PLY03

PLY03  TB   3                 * DOWN MOVE CHECK --- moving down DECREASES P1MY --- Row 0 of the Map is at the bottom
       JEQ  PLY04
       MOVB @SDNNAM,R4        * Sprite name for down
       A    @MAPW,R3          * Adjust the location of P1LOC counter to a row lower
       A    @MAPW,R5          * R5 holds bottom/left tile offset; update to a row lower
       MOV  R5,R6
       INC  R6                * R6 now holds bottom/right sprite tile offset
       INCT R2                * Move down, Y + 2 for bound check
       C    R2,@MAPH          * Upper vertical bound check; compare P1MY to Map Height
       JGT  PLY99             * There is no arithmetic >= so make two checks
       JEQ  PLY99             * RETURN w/o moving
       DEC  R2                * Re-adjust to real location, Y + 1
       JMP  PLY10             * R2= P1MY updated; R3= P1LOC offset; R4 = char; R5 = BOTTOM/Left; R6= BOTTOM/right

PLY04  TB   4                 * UP MOVE CHECK  --- moving up INCREASES P1MY --- Row 39 of the Map is on top
       JEQ  PLY10
       MOVB @SUPNAM,R4        * Sprite name for up
       S    @MAPW,R3          * Adjust the location
       MOV  R3,R5
       S    @MAPW,R6          * R6 was top/right tile which has been update to a row above
       DEC  R2                * Move up, Y - 1
       JLT  PLY99             * Lower vertical bound check 0, If fails RETURN w/o moving
*                             * R2= P1MY updated; R3 = P1LOC offset; R4 = char; R5 = TOP/left; R6 = TOP/right
PLY10
*
*   PLY10 Checks to make sure the requested Player1 move is to a 'Legal' tile such as a path or bridge
*

Attached File  DSK9.zip   26.17KB   1 downloads   <===== File "F2O" runs w/ E/A

 

- J


Edited by Airshack, Fri Jul 21, 2017 3:36 PM.


#509 Airshack OFFLINE  

Airshack

    Moonsweeper

  • 419 posts
  • Location:Phoenix, AZ

Posted Sat Jul 22, 2017 7:15 PM

Yes it is, but the Beryl thread is 18 pages and it can be hard to dig the details out of a thread like that. Anyway, I really didn't mean for you to post answers, I was mostly proposing questions that you need to have thought about and worked out before you can write code to execute the ideas. Sorry, I'll try to be less vague in the future.

The 32K and disk requirement affect how your structure your code, as does a cart vs. EA3 style program. The code I wrote is EA3 and will need to be modified to run from a cart. Also, with a cart you can use 100% of the low 8K, which would make a good place to store map data, and possibly sound. However, sound data only needs to be read a few bytes at a time in sequential order, unlike the map, so the VDP RAM is also a good place for sound data. An EA3 program has to tread lightly in the low 8K, at least until it is loaded and running, since the REF/DEF table and utilities are in the low 8K. Once you are running, that RAM can be used 100% if you do not intend to return to the E/A cart (which I don't think you do.)

Either way, it makes a difference in how things are coded and set up. Also, if you are going to have a bank-switched cart, that also affects program organization.

I think I'm going to post a few skeleton programs for people to use as a general reference:

1. EA3, bare bones
2. EA3, VSYNC based FSM
3. Cart, bare bones
4. Cart, VSYNC based FSM

Of course none of these will have REF dependencies (which you can't use in a cart anyway.)

Matthew


Hey Matthew and AtariAger TI Gurus,

I was wondering about how things may have changed over the past year with respect to programming in Assembly for Cartridge vs. EA. Specifically, how has the advent of the FlashROM/FlashGROM99/NanoPEB/SAMS impacted things, if at all, since this thread began back in 2010 - pre-Flash/Nano Anything?

Why wouldn't one want to write for Cart v EA? I suspect the balance is tipped towards Cart development with all the new hardware, though modern disk replacements (GoTech, Lotharek, etc) tip the scale back towards disk use code.

It's confusing at this point as I'm not fully appreciative of one route over the other.

With the development of JediMatt's external 32k, NanoPEB w/32k expansion, and the proliferation of emulation, I can't really imagine wanting to write code without the 32k expansion in mind. Seems to me most current day TI users will most likely be using systems some form of the 32k expansion RAM. Any good arguments with this conclusion out there?

Big Picture Question: How/why does EA3 code need to be modified to run from a Cart?

Apparently EA3 cod tramples all over the lower 8K?

What's the advantage to EA3 code over Cart based coding?

-j

#510 --- Ω --- OFFLINE  

--- Ω ---

    --- Ω ---

  • 10,194 posts
  • TI-99/4A Fanatic
  • Location:In the den playing with my FinalGROM 99!

Posted Sat Jul 22, 2017 7:51 PM

Airshack,

   You bring up some very good questions.  Things have changed quite a bit over the last year, and I believe it WILL have an impact on program development and even the audience for programs.

 

    Having a full fledged TI capable of doing anything other than the basics can be costly.  I believe that cost was a major factor in attracting and retaining TI users.  Now with the inexpensive 32K device by Matt and the inexpensive FinalGROM 99 by Ralf, we are seeing more people return to the TI world, because now, with those two items, people can now do what 90% of what people with a P-Box usually did, which was play games.

 

    It's not just being able to do what a P-Box user could do either, the FinalGROM99 is just hands down FASTER than loading stuff from disk drives (real or emulated).  And now with the banking and loading features of the FG99, I believe a new class of program is on the horizon, so there may be even less incentive for people to get a full-blown system.  

 

   Personally,  I've not used my P-Box much lately :_(, right now it's unplugged with Matt's 32K  plugged into the side and the FG99 plugged into the front.



#511 Lee Stewart ONLINE  

Lee Stewart

    River Patroller

  • 3,240 posts
  • Location:Silver Run, Maryland

Posted Sat Jul 22, 2017 10:26 PM

...
Big Picture Question: How/why does EA3 code need to be modified to run from a Cart?

Apparently EA3 cod tramples all over the lower 8K?

What's the advantage to EA3 code over Cart based coding?

 

The E/A cartridge copies ALC utilities, stored in the cartridge, to the lower 8 KiB (>2000 – >2675) and uses the upper end (>3F38 – >3FFF) for the REF/DEF table, which can grow down from there as programs are loaded.  This leaves ~6300 bytes for EA3 ALC programming.  If your EA3 program is relocatable, the E/A loader will try to load it in the upper 24 KiB starting at the bottom at >A000.  If there is no room there, it will try to load it at >2676 in low RAM.

 

To write programs for cartridge space, you must AORG (absolute origin) it to >6000 – >7FFF.  There must also be a proper header at the low end so the cartridge will start.  Since it is ROM, you cannot write to it except for bank-switching, which obviously does not store anything.

 

Programmers writing EA3 programs can and do use any part of RAM to write information for use by the program.  All of those writes must be changed to write explicitly to RAM.  Any self-modifying code must either be dispensed with or copied to RAM and run from there.  For example in fbForth 2.0, I had to modify the MG GPLLNK/DSRLNK routines to run in cartridge space, by putting all registers and other writable addresses, residing within the routines, into RAM space.

 

EA3 code  does not need to worry about switching banks as cartridge-based programs must if they are larger than 8 KiB.

 

Others will add much more, I am sure, but this is a start.

 

...lee


Edited by Lee Stewart, Sat Jul 22, 2017 10:32 PM.


#512 Asmusr OFFLINE  

Asmusr

    River Patroller

  • 2,357 posts
  • Location:Denmark

Posted Sun Jul 23, 2017 2:41 AM

EA3 programming, or single bank cartridge programming if you want it to run an unexpanded console, is definitely easier to work with than a bank switched cart. One reason is that we don't have a really good toolbox for working with multi-bank carts. xdt99 takes you some of the way but you still need to do something extra to pad and combine the banks, for instance. Another reason is that bank switching is kind of a pain to work with, especially if your program is running in the switched banks. For those reasons I would usually start up a project using EA3 and postpone thinking about bank switching until I start to run out of memory. At that point it might have been easier to think about bank switching all along, but that's preferable to having hurdles to overcome at the beginning of the project.

 

The bank switched carts I have worked with have loaded the program code from the cart into RAM at startup and from that point only used the cart to retrieve static data for graphics, sound, etc. This is quite easy to work with compared to bank switching on an unexpanded console where you only have 256 bytes of CPU RAM available. With the model I have used you still need the code to copy the program into RAM, and you need a script to combine the various bank files into a complete cartridge image that you load into an emulator (I never develop directly on the TI), but you don't need to worry about trampoline routines etc.

 

The FG99 with its RAM mode opens up a new possibility for bank switched carts on an unexpanded console. In this mode you have a 4K window of bank switched ROM and a 4K window of independently bank switched RAM. If you can limit your main program code to 4K you can run it from one of the ROM banks and then switch in RAM banks with data or code as needed (or vice versa). But note that this mode is not supported in emulators yet, and your program would only work on an FG99.



#513 Airshack OFFLINE  

Airshack

    Moonsweeper

  • 419 posts
  • Location:Phoenix, AZ

Posted Tue Jul 25, 2017 6:43 PM

 
The FG99 with its RAM mode opens up a new possibility for bank switched carts on an unexpanded console. In this mode you have a 4K window of bank switched ROM and a 4K window of independently bank switched RAM. If you can limit your main program code to 4K you can run it from one of the ROM banks and then switch in RAM banks with data or code as needed (or vice versa). But note that this mode is not supported in emulators yet, and your program would only work on an FG99.



The RAM mode w/ FG99 seems worthy of its own topic thread. Has anyone utilized this capability?

#514 Airshack OFFLINE  

Airshack

    Moonsweeper

  • 419 posts
  • Location:Phoenix, AZ

Posted Tue Jul 25, 2017 6:57 PM

That's actually not so unusual! icon_smile.gif Many of Nintendo's NES titles run entirely from vertical blank (see the thread here on the Mario Bros source, for instance).

However, I've been thinking about it since reading Matthew's post this morning. At first, I was thinking that I'd write a contrary view to give both sides, but after a lot of thought, and indeed my current experience with the TI Farmer port, I think I agree. For a game, you are better off disabling interrupts and not using any console functions. For one, it frees up the entire scratchpad RAM for you (at only 256 bytes, it's silly to 'reserve' parts of it, especially if you aren't using the ISR code and just wanted the interrupt itself).

Since most TI games run with interrupts disabled, and do LIMI 2/LIMI 0 when they are ready to deal with the ISR, you can easily replace that with a check of the VDP status bit yourself.. if it's set, then a blank has occurred and you can process your end of screen details. If it's not, then there was nothing to do yet, carry on. You gain even more doing it this way in that the ISR function no longer wastes half your blanking time deciding whether to do anything. icon_wink.gif

So although this is indeed a new stance for me, I also agree. ASM titles on the TI are better off NOT using the console interrupt code. icon_wink.gif The benefits far outweigh the small inconvenience of checking it yourself.


Tursi,

Do you continue to hold this position?

Apparently there's a situation where VPD status bit in question VSYNC is masked. Wondering if a few misses here and there of the status bit is a significant enough issue?

Later in this thread Matthew has a change of heart regarding this VDP status bit methodology.

-j

#515 Asmusr OFFLINE  

Asmusr

    River Patroller

  • 2,357 posts
  • Location:Denmark

Posted Yesterday, 3:08 PM

Tursi,

Do you continue to hold this position?

Apparently there's a situation where VPD status bit in question VSYNC is masked. Wondering if a few misses here and there of the status bit is a significant enough issue?

Later in this thread Matthew has a change of heart regarding this VDP status bit methodology.

-j

 

There are two issues here: 1. Whether you should use the ISR or roll your own. 2. If you roll your own, whether you should poll the VDP status register or the CRU interrupt bit.

 

Re 2: Polling the CRU bit is believed to be more accurate, and it's just as easy as polling the VDP status, so I switched to the CRU bit a while ago.

 

Re 1: I have never used the ISR because I like to be in full control of the machine's limited resources (RAM and CPU). And since you can't run with interrupts enabled all the time anyway, it's just as easy to place calls to your own "interrupt" routine in your code instead of "LIMI 2, LIMI 0". The "interrupt" routine polls the CRU bit an returns immediately if there is no interrupt, otherwise it continues to play sound etc. But actually in most of my games the main loop is faster than the time between two interrupts, so instead of an interrupt routine, waiting for the interrupt and calling the sound player is just part of the main loop.



#516 PeteE OFFLINE  

PeteE

    Star Raider

  • 53 posts
  • Location:Beaverton, OR

Posted Yesterday, 6:01 PM

I agree with Asmusr.  My research led me to avoid using the ISR, avoid polling the VDP status, and instead read the CRU interrupt line.  In my main game loop, I call this VSYNC routine once per loop, which keeps the frame rate locked to VDP refresh rate 60Hz (NTSC) or 50Hz (PAL).

; Reading the VDP INT bit from the CRU doesn't clear the status register, so it should be safe to poll.
; The CRU bit appears to get updated even with interrupts disabled (LIMI 0)
; Modifies R12
VSYNC  MOVB @VDPSTA,R12      ; Clear interrupt first so we catch the edge
       CLR R12
!      TB 2                  ; CRU Address bit 0002 - VDP INT
       JEQ -!                ; Loop until set
       MOVB @VDPSTA,R12      ; Clear interrupt flag manually since we polled CRU
       RT

This doesn't save sprite collision or 5th sprite number, so if you need those you'll have to add it yourself.

 

EDIT: Changing from "LI R12,>0004" and "TB 0", and saved two bytes.


Edited by PeteE, Yesterday, 11:26 PM.


#517 Tursi OFFLINE  

Tursi

    River Patroller

  • 4,691 posts
  • Location:BUR

Posted Yesterday, 10:26 PM

Tursi,

Do you continue to hold this position?

Apparently there's a situation where VPD status bit in question VSYNC is masked. Wondering if a few misses here and there of the status bit is a significant enough issue?

Later in this thread Matthew has a change of heart regarding this VDP status bit methodology.

-j

 

Yes, I still hold the position of not using the console ISR for all but the simplest assembly programs. You don't miss any sets if you leave the interrupt enabled on the VDP and the CRU and just stay in LIMI 0 mode. Then you can simply test CRU bit 2 for the presence of the interrupt signal and take action when it's set - no need to ever poll the status word.

 

Ever, of course, is used in the limited sense where if you know better, you are not beholden to this statement. Which was always and forever true but someone always wants to call out the exceptions. ;)






0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users