Jump to content

Airshack

+AtariAge Subscriber
  • Content Count

    1,549
  • Joined

  • Last visited

Posts posted by Airshack


  1. Why are you REFing the VSMW? You implement it right here.

    Thanks for the fast feedback. I was getting "Undefined symbol" from the Assembler when I referenced the subroutine VSMW, so I added the REF and that error went away.

     

    Obviously, I've done something wrong here. This thread is all about learning so basically I don't quite understand what that REF did?

     

    Did I import a routine from the EA ROM by the same name?

     

    I'll have to research this all in the AM. Thanks.

     

    -j


  2. Okay, so here are the VDP functions I promised. If you are reading this to learn assembly, then I *highly recommend* you type in this code instead of just copy and pasting. I know it sucks, but when you type each instruction you are forced to look at it in detail and it helps drive home what is going on. Also, you get more familiar with actually writing code, which is what you are trying to do anyway. Personally I always learn something or resolve a problem by typing in the examples.

     

    Matthew

     

     

    Matthew,

     

    I was able to type in the VDP routines and get them to Assemble along with some code:.

     

    DEF VDPRTN * VDP Functions from matthew180 @ AtariAge

    * from AtariAge post 24 May 2010 - 14 May 2010

    REF VSMW

    * REF We're going to "roll our own" = better, faster, smaller code; no E/A cart required

    * The floating point routines in the E/A & XB carts are horribly slow. Primarily due to setting the WP

    * to the lower 8K of the expansion RAM which is 8-bit; every access to registers will cause 8 wait

    * states (since 16-bit registers always access 2 bytes) <=== Best reason to avoid

     

    * VDP Memory Map - VDP RAM is accessed by the following four decoded address lines or "ports":

    VDPRD EQU >8800 * VDP Read Data

    VDPSTA EQU >8802 * VDP Read Status

    VDPWD EQU >8C00 * VDP Write Data

    VDPWA EQU >8C02 * VDP Read/Write Address

     

    * EQUates are not assembly opcodes, they're compiler directives used to make labels for constant memory values

    WRKSP EQU >8300 * Workspace in beginning area of 16-bit fast "CPU RAM"

    RoLB EQU WRKSP+1 * Register-0 LSB

     

    VDPRTN LIMI 0 * disable interrupt because the ISRs use the heck out of CPU RAM

    * which would otherwise trash out game: destroy Registers and Variables

     

    LWPI WRKSP * CPU RAM

    * NOTE: All "immediate" instructions for TMS9900 require a numeric value

    * and not a memory or regiser operands.

    ***********************************************************************************************************************

    * PROGRAMMING CONVENTIONS *

    ***********************************************************************************************************************

    * Register Use

    * -------- -----------------------------------------------------------------------------------------

    * R0-R2 always used for VDP interaction

    * R3-R6 general purpose

    * R7 used when calling the random number routine (think tombstone city code)

    * R8-R9 just extra registers

    * R10 a pseudo stack number for calling a few levels of sub routines

    * R11 BL uses this register to save the current PC value so a subroutine may return with B *R11

    * R12 ued by CRU instructions and needed for keyboard and joystick checking

    * R13-R15 extra unless you want to use BLWP which uses them

    *

    ***********************************************************************************************************************

     

    ******************************

    * Code below is used to test and play around with the "home brew" VDP Routines: VSBW, VSMW, VMBW, VSBR, VMBR, VWTR

    *

     

    MAINLP LI R0,>0000

    LI R1,>2300 * fills the screen with pound sign manaully, one position at a time

    LI R2,768

    MOVB @RoLB,@VDPWA

    ORI R0,>4000

    MOVB R0,@VDPWA

     

    POUND MOVB R1,@VDPWD

    DEC R2

    JNE POUND

     

    LI R2,>FFFF * delay 1

    DEL1 DEC R2

    JNE DEL1

     

     

    LI R0,>0000

    LI R1,>2000 * clears the screen using VSMW, inserting 768 spaces

    LI R2,768

    CLS BL @VSMW * calls VSMW as a common workspace subroutine

     

    LI R2,>FFFF * delay 2

    DEL2 DEC R2

    JNE DEL2

     

    JMP MAINLP

     

    END

     

     

     

    ***********************************************************************************************************************

    * SUBROUTINE CODE BEGINS *

    ***********************************************************************************************************************

     

    ***********************************************************************************************************************

    * VDP SINGLE BYTE W R I T E

    *

    * follows EA Register conventions: R0 = Write address in VDP RAM

    * R1 = data in MSB of R1 will be sent to VDP RAM

    *

    * Modifies: R0 can be restored with: ANDI R0,>3FFF

    *

    * Avoids: BLWP ==> no 32bytes CPU RAM wasted, no slow 8-bit RAM used, avoids BLWP 26-clock cycles

    *

    VSBW MOVB @RoLB, @VDPWA * send the LSB of VDP RAM address to VDP address reg, 1st sent is always LSB

    ORI R0, >4000 * sets bits 14/15 to = 01 which signals a WRITE once sent to VDP address reg

    * ( assumes 00 is ALWAYS in bits 14/15 of R0 )

    MOVB R0,@VDPWA * send high byte of VDP RAM address and write signal bits to VDP address reg

    MOVB R1,@VDPWD * sends a byte of data to the VDP RAM

    B *R11 * return

    *//VSBW

     

    ***********************************************************************************************************************

    * VDP SINGLE BYTE M U L T I P L E -- W R I T E -- same BYTE of data to multiple concurrent locations in VDP RAM

    *

    * follows EA conventions: R0 = Write address in VDP RAM

    * R1 = MSB of R1 sent to VDP RAM

    *

    * R0 Starting write address in VDP RAM

    * R1 data in MSB of R1 will be sent to VDP RAM

    * R2 # of times to write MSB of R1 to VDP RAM

    *

    * Modifies: R0 can be restored with: ANDI R0,>3FFF

    *

    VSMW MOVB @RoLB,@VDPWA * send LSB of VDP RAM address to VDP address reg, 1st sent is always LSB

    ORI R0,>4000 * sets bits 14/15 to = 01 which signals a WRITE once sent to VDP address reg

    * ( assumes 00 is ALWAYS in bits 14/15 of R0 )

    MOVB R0,@VDPWA * send MSB of VDP RAM address and write signal bits to VDP address reg

    VSMWLP MOVB R1,@VDPWD * sends a byte of data to the VDP RAM

    DEC R2 * decreases the loop counter

    JNE VSMWLP

    B *R11 * return

    *// VSMW

     

     

    ***********************************************************************************************************************

    * VDP Multiple Byte W R I T E ( writing BLOCKS OF DATA from address in CPU RAM to address in VDP RAM )

    *

    * R0 starting Write To address in VDP RAM

    * R1 starting data location address in CPU RAM

    * R2 number of bytes we're sending from CPU RAM to VDP RAM

    *

    * Modifies: R0, restore R0 with ANDI R0,>3FFF

    *

    VMBW MOVB @R0LB,@VDPWA *send LSB of VDP RAM write address to VDP address reg, 1st sent is always LSB

    ORI R0,>4000 *sets read/write bits 14/15 to (01) to signal WRITE

    * ( assumes 00 is ALWAYS in bits 14/15 of R0 )

    MOVB R0,@VDPWA *send MSB of VDP RAM write address and write signal bits to VDP address reg

    VMBWLP MOVB @R1+,@VDPWD *write byte from CPU RAM to VDP RAM

    DEC R2 * decrease loop counter

    JNE VMBWLP

    B *R11 * return

    *//VMBW

     

     

    ***********************************************************************************************************************

    * VDP SINGLE Byte R E A D ( reads VDP RAM data byte into Register-1's MSB )

    *

    * R0 contains the address in VDP RAM from where the data will be read

    * R1 MSB of R1 will receive the data from the hardwired VDP data bus

    *

    VSBR MOVB @RoLB,@VDPWA * send LSB of VDP RAM read address to VDP address register, 1st sent always LSB

    MOVB R0,@VDPWA * send MSB of VDP RAM read address to VDP address register

    * ( assumes read/write bits 14/15 are already 00 which signals a READ

    MOVB @VDPRD,R1 * read VDP RAM into R1

    B *R11 * return

    *//VSBR

     

     

    ***********************************************************************************************************************

    * VDP M U L T I P L E Byte R E A D ( reads VDP RAM data bytes into CPU RAM identified by Register-1's MSB )

    *

    * R0 contains the starting read address in VDP RAM

    * R1 contains the starting write address in CPU RAM

    * R2 number of bytes to read from VDP RAM to CPU RAM

    *

    VMBR MOVB @RoLB,@VDPWA * send LSB of VDP RAM read address to VDP address register, 1st sent always LSB

    MOVB R0,@VDPWA * send MSB of VDP RAM read address to VDP address register

    * ( assumes read/write bits 14/15 are already 00 which signals a READ

    VMBRLP MOVB @VDPRD,*R1+ * read byte from VDP RAM to CPU RAM address in R1, then inc R1

    DEC R2 * decrease loop counter

    JNE VMBRLP

    B *R11 * return

    *//VMBR

     

     

    ***********************************************************************************************************************

    * VDP Write to a VDP REGISTER (from Workspace Register Zero)

    *

    * R0 MSB contains the VDP Register to WRITE to

    * R0 LSB contains the value to write to the VDP Register

    *

    VWTR MOVB @RoLB,@VDPWA * send LSB (data value) to be written to the LSB of VDP address register

    ORI R0,>8000 * signal VDP Register Write operation bits 14/15 = (10)

    * * assumes bits 14/15 are ALWAYS (00)

    MOVB R0,@VDPWA * send VDP register number and WRITE signal to MSB of VDP r/w address

    B *R11

    *//VWTR

     

     

     

    Yet was unable to get the main program to execute the BL to @VSMW?

     

    ​Made that line a comment and everything ran (without clearing the screen of course).

     

    asm994a assembled everything error free yet Classic99 gave me " ERROR CODE 0D" when I attempted to run it?

     

    ​- James


  3. Okay, so here are the VDP functions I promised. If you are reading this to learn assembly, then I *highly recommend* you type in this code instead of just copy and pasting. I know it sucks, but when you type each instruction you are forced to look at it in detail and it helps drive home what is going on. Also, you get more familiar with actually writing code, which is what you are trying to do anyway. Personally I always learn something or resolve a problem by typing in the examples.

    I put this code at the bottom of my programs, just above the character definitions I posed before is a good place.

    
    

     

    This type-it-in approach works well for me too. This is my homework assignment for the evening.

     

    This reminds me of learning by typing code in from the pages of Creative Computing in the 80s.


  4. I have used ALL of the XB compilers and not a single one can you convert and compile with any speed, it is even slower than Assembly to do that as all the extra steps.

     

    XB is good for what it is designed for which is quickly type in a program and test as you go with the interpreter.

     

    Why are you even writing in XB if your goal is Assembly?

    I mean talk about doing something totally unnecessary to end up someplace else.

    Why not just write it in Assembly in the first place?

    Or for that matter C that is 100% designed around that idea in the first place.

    I'm a big fan of the XB256/BASIC Compiler package because it allowed my to increase the speed of my XB code 20-times. That's nowhere near the speed of Assembly code but still pretty good.

     

    Actually, a twenty times performance enhancement with zero knowledge of Assembly Language programming and zero required investment in TI-99/4 architecture knowledge is extremely impressive.

     

    The XB256 BASIC compiler workflow has also served as a nice bridge between BASIC and learning Assembly Language programming.

     

    It prepared me (a TI novice) by Introducing me to:

     

    A. Using an Assembler on Threaded Assembly code.

    B. Using modern PC based editors to edit code.

    C Learning the differences between assembly source code and an object file.

    D. Converting obj files to bin for FlashROM99

    E. Exposure to Graphics II Mode and the TI's enhanced character and sprite capabilities.

    F. Exposure to multiple screen swapping.

    G. Memory management issues beyond what BASIC coding alone offers.

     

    Etc...

     

     

    Compiled BASIC opened up the entire Assembly paradigm for me. That's a big deal and may be why others may want to use it, even if "your goal is Assembly."

     

    As I learn more about system architecture and Assembly programming I consider my time spent with XB256 Compiled BASIC as invaluable. It rocked my world to see my BASIC code spring to life at speeds well beyond anything (known to me) short of Assembly Language itself.

     

    Best Part: The cost of entry for this non TI programmer elite -- minimal!

     

    "Why not just write it in Assembly in the first place?"

     

    Hopefully I've provided an answer. Using the compiler also motivated me to learn more and dig deeper into the TI-99/4A hobby.

     

    That's something?

     

    Hopefully this post can bring you down to my level for a moment. What may seem "unnecessary" to you was fascinating for me.


  5. Okay, the last thing we need to cover on the VDP is the eight write-only registers. Here they are:

    <<deleted ASCII images >>
    REG 0: M3

    REG 1: M1

    REG 1: M2

     

    These three bits control the video mode, which can be Graphics I, Graphics II, Multicolor, and Text mode. The mode is set like this:

    M1 M2 M3
    0  0  0   Graphics I mode
    0  0  1   Graphics II mode
    0  1  0   Multicolor mode
    1  0  0   Text mode
    

    Why they used 3-bits to represent 4 modes I have no idea (they could have used just 2 bits. Maybe there were originally going to be more than 4 modes...)

     

    I wanted to know specifics behind these modes (before moving along) so I referenced the E/A Manual: 21.2 Graphics Mode, 21.3 Multicolor Mode, 21.4 Text Mode, 21.5 Bit Map Mode

     

    Q1: What on earth did they have in mind for Multicolor Mode?

     

    Q2: Is it safe to assume Multicolor Mode is mostly avoided for game programming?

     

    Q3: Is Graphics Mode simply the same as what's available when coding in Extended BASIC?

     

    Q4: Is it ever advantageous to swap modes in the same program: Graphics/Text, Bit Map/Text, Graphics/Multicolor? Any examples in the wild?

     

    Q5: Is Bit Map mode simply Graphics mode with background/foreground color information available for the 8 individual character rows vs per entire character, and no Auto-Sprites?

     

    ​Q6: Are Auto-Sprites only a good deal for XB programs?

     

     


  6. This post with its diagram really cleared up some questions I had from the earlier conversation/post:

     



    So, how does the VDP know we are setting up a read or write address if there is only one memory mapped port "VDP set read/write address" at >8C02? The answer is, the VDP looks at the upper two bits of the 2nd address byte we send. Since the VDP address register is 14-bits, the 1st byte (8-bits) plus 6-bits from the 2nd byte are used to form the address. The two most significant bits of the 2nd byte we send inform the VDP that this is a read or write address:
    |               2nd byte                |               1st byte                |
    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
    | t0 | t1 | a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9 | a10| a11| a12| a13|
    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
    |  type   |                   14-bit VDP address register                       |
    
    Here is the truth table for the two "type" bits:

    00 : Setting up a read address
    01 : Setting up a write address
    10 : Writing to a write-only register
    11 : Illegal / undefined

    Here is the code to set up a read address:
    WRKSP  EQU  >8300             * Where we will set the WP
    R0LB   EQU  WRKSP+1           * Memory address were R0's LSB will be
    
    . . .
           LWPI WRKSP
    . . .
    
           LI   R0,384            * R0 contains the address we want to set up
           MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
           ANDI R0,>3FFF          * Set read/write bits 0 and 1 to read (00)
           MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
    
    A few things to understand here. First, I'm using the fact that the CPU's general purpose registers are memory-based and I'm using a memory-to-memory move to send the low byte. This could be done a lot of different ways, and something common you will see is this:
           SWPB R0
           MOVB R0,@VDPWA
           SWPB R0
           ANDI R0,>3FFF
           MOVB R0,@VDPWA
    
    Remember, we have to send the LSB to the VDP first. The SWPB (SWaP Bytes) instruction will do just that, swap the register's low and high bytes. So the LBS of the address we want to set up is now in the MSB of R0, and sent to the VDP. Then the second SWPB puts the bytes back to their original form and the MSB of the address we want to set up is sent. Personally I don't really like this method, but it works and you might see it in code out there in the wild. There were other reasons the SWPB was used that have to do with timing, but that's a longer story.

    The ANDI R0,>3FFF instruction masks out the upper two bits and makes sure they are zero, which indicates to the VDP that we are setting up a read address. If we assume that the programmer will never set a VDP address greater than 14-bits, then the upper two bits will always be zero and we can remove this instruction. This is what I personally do since it is quicker and the VDP functions we are developing will be used a lot in games. Thus, my version of setting up a VDP read address can be reduced to two instructions and assumes the VDP address to set up has been loaded into R0:
           MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
           MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
    

    Setting a write address is exactly the same except we make sure that the two "type" bits are 01 by using an ORI (OR Immediate) instruction to set the bits. Actually this will not work if most significant bit of R0 was already 1, but I assume again that the programmer will not load a set up address into R0 that is greater than the 14-bits used by the VDP:
           MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
           ORI  R0,>4000          * Set read/write bits 0 and 1 to write (01)
           MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
    

    At this point you are ready to rock. You can start reading and writing bytes to the VRAM. The only thing we did not do is specifically set a graphics mode and fix up the various tables in VRAM by writing to the VDP's write-only registers. But, since this post is already long enough, that will come in another post.

    Next I'll present a complete set of VDP routines that use the same calling convention (using R0, R1, and R2) as the routines available in the E/A or XB cartridges. We'll even add a new routine of our own that makes VRAM initialization fast and easy.

    Matthew

     

     

    I was struggling with this earlier since I didn't realize you were assuming >00 in the two most significant bits. Now it's clear! Much cleaner than the double SWBP technique. I'm looking forward to moving along to the VDP routines post.

     

    It seems (to me) the slight thread creep may have cut you off on the Character Definition (12-14 May 2010) post conversation? Did you intend to simply show the DATA format without going into how to load the character set into CPU RAM? I was expecting a short example.

     

    **
    * Standard Character Set 1 - "Space" 8x8
    *
    * NOTE: This data will increasethe size of our executable and uses CPU RAM!
    * When run from cartridge it eats up part of our 8K (unless we do paging).
    SCS1
    DATA >0000,>0000,>0000,>0000 ; 0 >00
    DATA >7C82,>AA82,>BA44,>3800 ; 1 >01
    DATA >7C92,>92FE,>BA44,>3800 ; 2 >02................????


  7. It's misleading to say that using any of these addresses will "enable" the VDP. You don't need to do anything special to "enable" it.

     

    I appreciate and understand what you're saying about the enable analogy. Since this thread is basically Assembly Language Preschool for Dummies, I believe the "enable" analogy was used to help me grasp something I failed to reach. I'll be sure to not take it literally.


  8. TODAY's DEBRIEF & LESSONS LEARNED SUMMARY:

     

    1. The 99/4A designers decided to wire the 9918A's 8-bit bus to the MSB of the 9900's data bus. That means any "word" read or write (MOV instruction) will transfer the MSB or the register or memory to/from the 9918A. Since the 9918A is only physically wired to the MSB of the data bus, it will only ever see the MSB.

     

    2. The 9900 address >8C00 will enable the 9918A, and the MSB of the 9900 data bus will be transfered to the 9918A.

     

    3. You have to send the low-byte of the VDP address first, then the high byte (that's just the way the 9918A works).

     

    To be clear:

    • All writes to the VDP are byte writes—The first byte written to VDPWA is assumed to be the LSB of the VRAM address.

     

     

    4. ...there is one instance when it's important, and that's when addressing memory mapped devices that are auto-incrementing their internal address. Like the VDP. Since it increments the address on both a read and a write, you can't have the VDPWD and VDPRD decoded on successive bytes. If they were, the read-before-write concept in the TMS 9900 would increment the address when fetching the word that contains the byte that shouldn't change and again increment it when storing the word that contains the byte we want to store.

     

    5. When you use the MOVB instruction, the 9900 will operate on whatever memory byte is addressed. However, when one or both of the operands of the MOVB instruction is a register, the MSB of the register will always be read or written. MOVB *always* works with the MSB of any register operands

     

    6. The ISR *will* communicate with the VDP, so if you are in the middle of writing to the VDP when the ISR happens, then all bets are off as to the VDP's internal memory address register. So, only enable the ISR with LIMI 2 when you know it is OK for the ISR to talk to the VDP. If you need to talk to the VDP then you must disable the ISR with LIMI 0.

     

    7. Tursi plans to port over ALL Colecovision games to the TI-99/4A.

     

    Thanks to all for this initial barrage of feedback.


  9. Games just seems to be something people get excited about. People like to see things happen, so getting something on the screen and moving is a big motivator and can be very rewarding.

    A significant point many Assembly Language books miss.


  10. If you leave the interrupt mask off, then the ISR doesn't run.

     

     

    I can't see offhand why that program would do anything unusual in that case though...

     

    How about that same question but reversing everything:If you leave the interrupt mask on (LIMI 2), the code will start doing funny things like changing the character foreground and background colors? Not sure why?

    • Like 1

  11.  

    • SWPB is not a console routine. It is a TMS9900 instruction. It just happens to be slower than some other instructions and slower than it needs to be to give the VDP time to recover.

    ...lee

     

    Thanks again Lee!

     

     

    To be clear

    • Ampersand is ‘&’, not ‘@’.

    ...lee

     

    Clearly! ;p


  12. It's not correct that MOVB only transfers even-to-even or odd-to-odd. MOVB can also transfer odd to even or even to odd.

     

    Damn! There goes my understanding of what's happening!

     

    To be clear:

    • All writes to the VDP are byte writes—The first byte written to VDPWA is assumed to be the LSB of the VRAM address. Matthew's code always writes the first byte to VDPWA from the LSB of the relevant workspace register—and, it is always his first write by his design. It just happens to be odd by his plan. If he had written the first byte of the register, it would have been even to odd.
    • SWPB is not a console routine. It is a TMS9900 instruction. It just happens to be slower than some other instructions and slower than it needs to be to give the VDP time to recover.
    • Ampersand is ‘&’, not ‘@’.

    ...lee

     

    Here's the golden nugget I was searching for: "The first byte written to VDPWA is assumed to be the LSB of the VRAM address."

     

    Thanks guys! I think I get it now -- again!


  13. The 11 May 2010 entry begins with a little sample code:

     

     

    I'm posting this mostly as a response to help Owen get past some of the initial hurdles of AL programming and also as maybe a "starter kit" for anyone else who wants to get going with AL on the 99/4A. I will be presenting the information and programming style that I like to use, but of course there are always other ways and other people will have their own methods and madness. I'm not interested in arguing styles and such in this thread, so please let's not go down that road.

    There are *basically* (but not only) two types of AL programs formats you can write on the TI, programs that run from the Editor/Assembler (EA) menu 3 option, commonly known as EA3. These types of programs require a linking phase to finalize the memory references before execution. Because of this, this kind of code is "relocatable" and the loader can place the program just about anywhere in memory that it will fit.

    The second type of program is one meant to be run from a cartridge ROM. These programs are set up differently and have a few more restrictions as to defining labels and such. For now we will stick with EA3 types of programs.

    This is the basic skeleton:

           DEF  START
    
    * Typically EQUates, DATA, and BYTE defintions for variables
    * will be at the head of the program, with longer data sets
    * places at the bottom of the code.
    
    
    * Program execution starts here
    START  LIMI 2
    
    LP9999 JMP  LP9999
    
           END
    
    This program will do nothing but execute an endless loop. However, interrupts were enabled with LIMI 2, so the FCTN= combination can be used to reset the console.

    This line:

    DEF START

    Is an assembler directive, which means it will be read and used by the assembler. It is *not* assembly language and does not cause any code to be created. This directive is used to place a label in the REF/DEF table that the linker uses to load and find programs. This entry is saying that there will be a label called "START" and that is where our program will begin execution. "START" could be anything we want that is 1 to 6 characters in length.

    Let's add something that will actually do something for us like set the VDP to Graphics Mode I, clear the screen, and say "Hello World". I think this is the basic entry level program that most people try to start with.

    *Note*
    Sometimes you will see a $-2 or such used in code. The assembler users the $ to represent the current location and will subtract (or add) the specified number of bytes to the current location when generating an address. This is usually used as a short cut for short loops to avoid using a label. I do not currently recommend using this method because I have found that Asm994a gets it wrong, and as of right now that is the only Windows based assembler I know of. If you are going to stick 100% with the E/A, then go ahead, but don't be surprised if you try to assemble with Asm994a and your code does not work.

          DEF  START
    
    * VDP Memory Map
    *
    VDPRD  EQU  >8800             * VDP read data
    VDPSTA EQU  >8802             * VDP status
    VDPWD  EQU  >8C00             * VDP write data
    VDPWA  EQU  >8C02             * VDP set read/write address
    
    * Workspace
    WRKSP  EQU  >8300             * Workspace
    R0LB   EQU  WRKSP+1           * R0 low byte reqd for VDP routines
    
    * Program execution starts here
    START  LIMI 0
           LWPI WRKSP
    
    
           LIMI 2
    LP9999 JMP  LP9999
    
           END
    
    First notice that there are no REF statements for the normal VDP routines. This is because I don't like using the console based routines for a couple of reasons:

    1. They are slow since they were designed to save ROM space instead of being fast to execute
    2. They use BLWP which is slow and designed primarily for context switching in a multi-tasking system like TI's mini computers where the TMS9900 CPU was designed to be used
    3. The routines use a workspace in 8-bit RAM! This in of itself should be a sin!

    Also, I don't like to use the GLP routines because they require a workspace pointer in the 256 bytes of scratch pad RAM. Since there is only 256 bytes of 16-bit RAM in the machine, it is highly prized memory and I don't like some other console routines chewing up 32 bytes of that RAM. Anyway, the VDP routines provided in the E/A cartridge are slow, slow, and slow, and I'm a speed freak.

    What we set up in the code is an equate (EQU) to the memory address where the VDP is mapped in the 99/4A's address space. Equates are assembler directives that simply let us use a label instead of a number. The assembler will do a search and replace on the labels, so any place you see VDPRD, for example, will be replaced with >8800.

    We have also changed the first instruction to LIMI 0, which will disable the VDP interrupt, which in turn disables the console ISR. While the VDP interrupt is a nice thing to have to use as a sixtieth of a second timer in a game, it triggers the console's one and only interrupt service routine which we usually don't want running (it tries to do too much IMO.) So, we shut it off. We can still use the VDP interrupt, we just have to poll for it, which is not too bad.

    The next thing we do is LWPI (load workspace pointer immediate), which sets the workspace pointer to >8300, which is the first address in the 16-bit scratchpad RAM. Since the registers in the 9900 CPU are memory-based, you absolutely positively always want to keep the workspace pointer set to an address in that 16-bit RAM (unless you really want to kill performance, in which case you should just use XB... icon_winking.gif )

    Finally you will note an equate called R0LB. This creates a label that gives us a convenient way to access the low byte of R0 without using SWPB. It comes in handy when dealing with the VDP as you will see in the routines we set up.

    Now let's do something, like clear the screen:

           DEF  START
    
    * VDP Memory Map
    *
    VDPRD  EQU  >8800             * VDP read data
    VDPSTA EQU  >8802             * VDP status
    VDPWD  EQU  >8C00             * VDP write data
    VDPWA  EQU  >8C02             * VDP set read/write address
    
    * Workspace
    WRKSP  EQU  >8300             * Workspace
    R0LB   EQU  WRKSP+1           * R0 low byte reqd for VDP routines
    
    
    * Program execution starts here
    START  LIMI 0
           LWPI WRKSP
    
           CLR  R0                * Set the VDP address to zero
           MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
           ORI  R0,>4000          * Set read/write bits 14 and 15 to write (01)
           MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
    
           LI   R1,>2000          * Set high byte to 32 (>20)
           LI   R2,768            * Set every screen tile name to >20
    CLS    MOVB R1,@VDPWD         * Write byte to VDP RAM
           DEC  R2
           JNE  CLS
    
           LIMI 2
    LP9999 JMP  LP9999
    
           END
    
    For now we are doing things directly, but soon we will make a subroutine to encapsulate the VDP reading and writing. One of the most important things to understand about the 9918A VDP is that it maintains an internal address register that auto-increments any time a byte of data is written-to or read-from the VDP. This is handy since setting the VDP's address register takes two writes to the VDP write-to-register port. You can see that above. R0 is loaded with the address we want to set. In this case we are going to set up the address of zero, since I know the VDP name table defaults to memory location zero, we are setting up the VDP address to write bytes to the VDP RAM used to display the screen. A little later we will set the name table location directly so we know without a doubt where it is located in the VDP RAM.

    The other thing to know about the VDP registers is that they are read only (except for the status registers.) So the only way to know for sure where the various VDP tables are located is to set them.

    So, the address is set up and written to the VDP. The VDP's internal address register is 14-bits since it has to reference up to 16K, so it takes two 1-byte transfers to load the address register. After that, any data we write (or read) to the VDP will cause the address to auto-increment after the write (or read.)

    Next we load R1 with the byte we want to write to the VDP. When writing from registers to memory mapped devices, the MSB of the register will always be transferred. Thus, the low byte of R1 really does not matter, but we use >00 to help keep things clear. Since we are clearing the screen, the pattern for tile (character) 32 is already defined as space thanks to the console boot-up process (ASCII 32, or >20 in hex.) We set R2 to 768 to keep track of how many bytes we have written to the VDP. We count *down* to take advantage of the fact that the 9900 CPU will compare R2 to 0 after the DEC instruction and we can use that to control the JNE (jump if not equal) instruction. So we are saying, Jump if R2 is not 0.

    Finally we re-enable interrupts and cause an infinite loop. Something else to note. You *MUST* disable interrupts when reading or writing to the VDP because the console ISR also reads and writes the VDP and will mess up the address you had set up. And since your program has no way to know that it was possibly interrupted, your VDP accesses will be all messed up. But, we don't need the console ISR, so we will just leave it disabled until the end, if we ever enable it at all.

    Okay, that's is for this installment. Next I'll add code for a full VDP mode setup so things are exactly where we want them, and we can set up the VDP subroutines.

    Matthew

     

     

    Today I had the opportunity to play with the code and see if I could figure everything out.

     

    One issue I had was not knowing that MOVB is an even-to-even or odd-to-odd byte operation.

     

    Here's my code I've over-commented in an attempt to capture Matthews first lesson along with some things I've learned while studying:

     

    Intro to Assembly Language for the TI Home COmputer by Ralph Molesworth

    Fundamentals of TI-99/4A Assembly Language by M.S.Morley

    TI's Editor Assembler Manual (as a reference only)

    Compute!'s Beginner's Guide to Assembly Language on the TI-99/4A by M. L. Lottrup

     

    DEF LESON1

    * no REF statements because VDP routines are slow: Console routines are designed to save ROM, not run quickly
    * BLWP is for context switching TI 990 type multi-tasking operations
    * NOTE: TI-99/4A was not initially designed to use this 16-bit CPU
    * the console routines use 8-bit RAM for their workspace! BAD TI!
    * the 99/4 has but 256 bytes of 16-bit RAM
    * the VDP routines in E/A cartridge are super slow - avoid them
    * GPL routines are slow and use up 32 bytes of 16-bit RAM
    * E/A cartridge copies code from its GROM into low memory starting
    * at >2000 so they cost you expansion RAM which is unnecessary

    * VDP Memory Map - VDP RAM is accessed by the following four decoded address lines:
    VDPRD EQU >8800 * VDP Read Data
    VDPSTA EQU >8802 * VDP Read Status
    VDPWD EQU >8C00 * VDP Write Data
    VDPWA EQU >8C02 * VDP Read/Write Address
    * VDP RAM is not directly addressable from the CPU, you MUST go through the VDP

    * Workspace
    WRKSP EQU >8300 * Workspace in beginning area of 16-bit fast "CPU RAM"
    * * TI-99/4A only has 256 BYTES of CPU RAM! Range: >8300->83FF

    RoLB EQU WRKSP+1 * R0 low byte
    * gives us access to LSB of R0 w/o using the slow console routine SWPB

    EDIT: SWPB is not a console routine but a TMS9900 instruction which happens to be slower than it needs to be.
    * console routines are designed to save ROM space, not for speed

    * program execution begins here
    LESON1 LIMI 0 * You MUST disable VDP interrupts any time you write/read with VDP
    * because console ISRs will mess with your VDP addressing since they
    * read and write to the VDP on their own
    * NOTE: Whenever disabled we can still use it by polling.

    LWPI WRKSP * ALWAYS set workspace pointer to fast CPU RAM (unless context shifting)

    MAINLP CLR R0 * we'll use R0 to set up the VDP write address, R0 now = >0000

    MOVB @RoLB,@VDPWA * send LSB of R0 to LSB of VDP RAM read/write address (00000000 ==> >8C02+1)
    * 16-bit VDP R/W Address uses 14-bits (13-0) to address 16K of memory

    * * NOTE: Since @RoLB is an ODD address the MOVB will move that LSB to the
    * LSB @VDPWA, or @VDPWA+1. MOVB transfers even-to-even or odd-to-odd

     

    ​MOVB @R0LB,@VDPWA * sends LSB of R0 to VDP Write Address Register LSB

    * when loading the VDP Address Register you MUST send the LSB first

    ​ * this is simply a function of how the 9918A works in the TI-99/4A

    * * NOTE: With E/A the Screen Image Table default range is >0000->02FF
    * this table is divided into three sections of 256 bytes each.
    *
    ORI R0,>4000 * sets MSB R0 (future read/write bits 14 and 15) to signal a write (01)
    * LSB does not matter since it is not used in the next MOVB
    * R0: 0000 0000 XXXX XXXX
    * >4000: 0100 0000 0000 0000
    * Result: 0100 0000 XXXX XXXX

    MOVB R0,@VDPWA * send MSB of R0 to MSB of 16-bit VDP RAM write address

    * the second write to VDP Write Address Register ALWAYS writes to the MS-Byte
    * a MOVB from a Register ALWAYS sends the MS-Byte

    * NOTE: >0000->02FF is the default range for the screen image table

    * NOTE: VDP internal register auto increments after a VDP read or write.

    * Setting the VDP address register takes TWO writes (MOVB) to the VDP
    * write-to-register port.
    * Subsequent writes in this program will only take ONE write due to
    * the initial setup along with auto-increment

    LI R1,>4000 * sets MSB to "@" code >40
    LI R2,768 * Loop counter set in R2 to 768 = number of tiles on a 32*24 screen

    ATFILL MOVB R1,@VDPWD * write "@" character in MSB R1 to the VDP Write Data resister
    * write address value in @VDPWA auto-increments with each write/read
    DEC R2 * decrease loop counter in R2
    JNE ATFILL * Jump up to CLS if R2 is not zero

    LI R2,>FFFF
    DELAY1 DEC R2
    JNE DELAY1

    CLR R0
    MOVB @RoLB,@VDPWA * sets up LSB of VDP Write Address Register to beginning of Screen
    ORI R0,>4000
    MOVB R0,@VDPWA * sets MSB

    LI R1,>2000 * >20 = BLANK character
    LI R2,768

    CLS MOVB R1,@VDPWD * write blank character in MSB R1 to the VDP Write Data Register
    DEC R2
    JNE CLS

    LIMI 2 * enable interrupt, allows ALT+= to halt program execution
    LI R2,>FFFF *delay for clear screen
    DELAY2 DEC R2
    JNE DELAY2
    LIMI 0 * disable interrupt, don't want ISRs messing with my VDP writing

    JMP MAINLP
    able to
    END

     

     

    Once I figured out that MOVB @RoLB,@VDPWA was working on the LSB of the VDP Write Address Register @VDPWA, I was able to understand everything.

     

    ​Edit:

     

    My code was modified to fill the screen with the "@" character and then clear it out because I wasn't sure matthew's screen clearing code was actually working on my machine. I'm new to the whole E/A process so I wasn't sure if the blank screen was a sign that the code worked or maybe just locked up and somehow was displaying a blank screen. The drawing and clearing of @-symbols made the status of the running code obvious.

     

    Question for the TI faithful:

     

    If you leave the interrupt mask off (LIMI 0), the code will start doing funny things like changing the character foreground and background colors? Not sure why?

     

    If you leave the interrupt mask on (LIMI 2), the code will start doing funny things like changing the character foreground and background colors? Not sure why?EDIT:

     

    Obviously, this shows that the console's ISRs are changing things around somehow. The specifics are what haunt me.


  14. I'm finally getting around to commenting on this thread from matthew180 which he started back in 2010.

     

    Hopefully we can reignite this excellent Assembly Language tutorial/conversation.

     

     

    I'm posting this mostly as a response to help Owen get past some of the initial hurdles of AL programming and also as maybe a "starter kit" for anyone else who wants to get going with AL on the 99/4A

     

    I knew I'd want a better workflow and a modern set of tools for my "starter kit" before this BASIC programmer began climbing Mount Assembler.

     

    Here's what I came up with thanks to the suggestions of many on AtariAge:

     

    Editor: IntelliJ IDEA - Has a nice TMS9900 Assembly Language plug-in which also works with TI BASIC

     

    https://www.jetbrains.com/idea/

     

    IntelliJ Plugin (xdt99 IDEA) for TI BASIC and Assembly: https://github.com/endlos99

    *** Thanks to Ralph Benzinger

     

    The xdt99-mode and xdt99 IDEA plugins provide editor support for writing assembly and TI Extended BASIC programs using the GNU Emacs and the IntelliJ IDEA development environments, respectively. Plugin features include syntax highlighting, navigation, and semantic renaming, among others.

     

    Cross Assembler: Asm994A (part of the Win994a simulator for Windows)

     

    http://www.99er.net/win994a.shtml

    *** Thanks to Cory Burr

     

    AtariAge thread on using asm994a: http://atariage.com/forums/topic/229206-using-asm994a/

     

     

    Emulator: Classic99

     

    http://harmlesslion.com/

    *** Thanks to Tursi

     

     

     

     

     

     

     

     

     

     

    Cross-Assembler: Downloaded


  15.  

    Yes, the F18A... The winner got back to me, and says he does have a 4A, and does intend to put it to use. He turned down full market value, so I assume he is serious. Which is good to hear.

     

    The more the merrier.

     

    [email protected]

    He did express to me a concern that he'd destroy his TI attempting the install. I assured him it was quite easy.

    • Like 1

  16. I listened to one Pod cast and they mentioned RXB but not me or RXB by name just descriptions.

     

    They just said there was this cool thing being done in XB by a guy and talked about how the TI is being supported currently.

     

    Very uninformative and not very impressive to not even know names or things that have names.

    Hey Rich,

     

    I find this very unusual as well. He's programs usually offer many details as he seems to do an excellent job preparing for his own Atari Podcast.

     

    You shouldn't take any of this personal. He's probably just immersed in the Atari and not too interested in anything else.

    • Like 1

  17. I listened to one Pod cast and they mentioned RXB but not me or RXB by name just descriptions.

     

    They just said there was this cool thing being done in XB by a guy and talked about how the TI is being supported currently.

     

    Very uninformative and not very impressive to not even know names or things that have names.

    The strange part is, I spoke with Randy (the Antic Atari Podcaster whom appears on several different podcasts, not just TI specific), and he seemed quite interested in what was going on at FestWest.

     

    I can understand the lack of TI/FestWest details on his mainstay Podcast Antic, yet completely miss why he didn't offer more on this general interest retro computing program?

     

    Yep, I don't get it.

    • Like 1
×
×
  • Create New...