Jump to content
IGNORED

Problems with Stella, Z26, and DASM


Recommended Posts

I have been writing simple programs for the 2600 that are smaller than 2K and 4K. since they are smaller than 2K, they will not run under an emulator. How can i make these programs 2k? Is there some kind of switch/option that i can use with DASM or Z26 to make them run/assemble to 2K or 4K?

 

thank you for any help you might give me!

Link to comment
Share on other sites

I have been writing simple programs for the 2600 that are smaller than 2K and 4K. since they are smaller than 2K, they will not run under an emulator. How can i make these programs 2k? Is there some kind of switch/option that i can use with DASM or Z26 to make them run/assemble to 2K or 4K?

 

thank you for any help you might give me!

You can use the ORG statement to tell DASM where the cartridge (ROM image) should start, and then you can use another ORG statement to tell DASM where the cartridge should end. The full cartridge area is 4K, but you can also create ROM images that fill up only half of that, or 2K. Even if your actual code is less than 2K, you still need to create either a 2K or 4K ROM image. (Or, if you use some kind of bankswitching, you can create ROM images that are more than 4K, but we won't get into that, since you're specifically asking about *smaller* ROM images.)

 

A skeleton for a 4K ROM image might look like this in DASM:

 

		 PROCESSOR 6502

	 INCLUDE "VCS.H"
	 INCLUDE "MACRO.H"

	 ORG $F000

; code and data here

	 ORG $FFFA

NMI_VECTOR
	 WORD vector_1

BOOT_VECTOR
	 WORD vector_2

BRK_VECTOR
	 WORD vector_3

 

For a 2K ROM image, it might look like this instead:

 

		 PROCESSOR 6502

	 INCLUDE "VCS.H"
	 INCLUDE "MACRO.H"

	 ORG $F800

; code and data here

	 ORG $FFFA

NMI_VECTOR
	 WORD vector_1

BOOT_VECTOR
	 WORD vector_2

BRK_VECTOR
	 WORD vector_3

 

The actual addresses in the ORG statements are flexible, due to a combination of the 6507 processor and how the Atari 2600 connects the cartridge ROM, the TIA chip, and the RIOT chips to the address pins of the 6507 processor.

 

The 6507 processor has only 13 address pins-- A00 through A12 (A13 through A15 are undefined)-- so the 6507 can "see" only 8K of address space. If we write the memory locations in binary, they range from %xxx0000000000000 through %xxx1111111111111 (where "x" indicates a bit that isn't connected to an address pin). In hex, that would be $0000 through $1FFF, if we assume that the undefined bits are all 0s. But they could actually be anything, because the 6507 can't "see" them anyway, therefore the following address ranges map to the same locations as far as the 6507 can tell:

 

$0000 through $1FFF

$2000 through $3FFF

$4000 through $5FFF

$6000 through $7FFF

$8000 through $9FFF

$A000 through $BFFF

$C000 through $DFFF

$E000 through $FFFF

 

Furthermore, the TIA and RIOT chips are connected to certain address pins, such that the 6507 sees them at certain memory locations-- which we won't get into right now, other than to say that they're located within the bottom half of the 8K address space (although they don't actually take up all of the lower 4K of the address space).

 

On the other hand, the cartridge slot is connected to the address pins in such a way that the 6507 sees it as taking up the top half of the 8K address space, as follows:

 

$0000 through $0FFF = TIA and RIOT

$1000 through $1FFF = cartridge slot

 

$2000 through $2FFF = TIA and RIOT

$3000 through $3FFF = cartridge slot

 

etc., up to

 

$E000 through $EFFF = TIA and RIOT

$F000 through $FFFF = cartridge slot

 

Now, the 6502 processor assumes that the top six bytes of the full 64K address space are used for three vectors-- the NMI vector ($FFFA and $FFFB), the boot or "reset" vector ($FFFC and $FFFD), and the BRK vector ($FFFE and $FFFF). The 6507 processor doesn't have non-maskable interrupts, so the NMI vector has no meaning to it, which is how some bankswitching schemes are able to use the NMI vector bytes for bankswitching hotspots. And even though the 6507 processor can't tell the difference between address $1FFF and address $FFFF, the 6507 processor is a member of the 6502 family, so it makes sense to think of the three vectors as being at $FFFA through $FFFF-- in which case, we would think of the cartridge slot as being at $F000 through $FFFF. But you'll see a lot of games use $1000 through $1FFF as the addresses of the cartridge slot.

 

The 2K cartridges don't connect to the A11 address pin (and both the 4K and the 2K cartridges assume that the A12 address pin is 1), therefore the 6507 processor will see a 2K cartridge as being "doubled up" in the 4K cartridge slot. That means you can code a 2K cartridge as if it resides within any of the following address ranges, because they are all the "same" as far as the 6507 processor can tell:

 

$1000 through $17FF = 2K cartridge

$1800 through $1FFF = 2K cartridge

 

$3000 through $37FF = 2K cartridge

$3800 through $3FFF = 2K cartridge

 

etc., up to

 

$F000 through $F7FF = 2K cartridge

$F800 through $FFFF = 2K cartridge

 

Again, since the three vectors of the 6502 processor are at $FFFA through $FFFF, it makes sense to think of a 2K cartridge as being at $F800 through $FFFF-- but where you actually code it is up to you.

 

Now, if your program is less than 2K, you can start it and end it anywhere within the 2K ROM image-- other than in the boot vector or BRK vector, that is-- as long as you have the boot vector point to the address of the startup code in your program. The startup code can be whatever you want it to be, but the Atari 2600 powers up in a random state, therefore it's normal to have the startup code initialize the TIA registers and the 128 bytes of RAM.

 

So let's suppose that you have 1K of actual code, located in contiguous addresses, and that you're coding the 2K cartridge as if it resides at $F800 through $FFFF. Then the following would be okay:

 

		 PROCESSOR 6502

	 INCLUDE "VCS.H"
	 INCLUDE "MACRO.H"

	 ORG $F800

; an unused area

	 ORG $F900

START
; your 1K of code goes here, followed by

; another unused area, then

	 ORG $FFFC

BOOT_VECTOR
	 WORD START

BRK_VECTOR
	 WORD START

 

Does this help at all?

 

Michael

Link to comment
Share on other sites

Yes, thank you very much. Lets say that my program looks like this:

processor 6502

include "vcs.h"

include "macro.h"

ORG $F800

START

 

; code less than 1k

 

ORG $FFFA

 

word vector_1

word vector_2

word vector_3

 

is this how it should look if i had less than 1k of code? will this still run on z26 or stella?

 

you were very helpful :)

Link to comment
Share on other sites

Yes, tho in that example the START tag is not used (you called it vector_2). This address can exist anywhere...it doesn't have to exist at the start of rom. It indicates where to go when the cartridge boots. Normally, this should be a loop that clears all ram (additionally, a check can be done prior to this to detect if the cartridge is running on a 7800...as that console powers up with known values existing in ram).

Link to comment
Share on other sites

or should code start at $FBFF(since it is 1k or less) or $FE6A (since it is only 400 bytes) :?:

 

The binary itself must be at least 2k AFAIK. It doesn't matter if the space is occupied with anything in the source code (Dasm will automatically fill unused areas with $FF's). The first rom ORG should indicate a 2k boundry address in which the 13th bit is set (as described above). The last 6 bytes hold the vectors... + $7FA from the origin in a 2k image, + $FFA from the origin in a 4k image.

 

EDIT: forgot the NMI vector (does anybody even use this?)

Edited by Nukey Shay
Link to comment
Share on other sites

Yes, thank you very much. Lets say that my program looks like this:

processor 6502

include "vcs.h"

include "macro.h"

ORG $F800

START

 

; code less than 1k

 

ORG $FFFA

 

word vector_1

word vector_2

word vector_3

 

is this how it should look if i had less than 1k of code? will this still run on z26 or stella?

 

you were very helpful :)

Yes, that should work-- except you don't want to say

	word vector_1
word vector_2
word vector_3

I just used "vector_1" through "vector_3" as examples, because they can be lo-byte/hi-byte vectors to any three addresses. But as I said, the NMI vector has no significance to the 6507 processor, since the 6507 processor doesn't have the ability to do NMI interrupts. Thus, you really only need to use the last two vectors. However, you don't actually need to use the BRK vector-- the very last vector-- but if you know how, it's possible to create a BRK (or IRQ) interrupt routine, in which case you may want to point the BRK vector to that routine. Otherwise, it's a good idea to point the BRK vector to the boot vector, so that if your program accidentally runs into a BRK command, it will jump back to the boot routine.

 

So your program would look like this:

 

	 processor 6502
 include "vcs.h"
 include "macro.h"
 ORG $F800
START; <-- I'm assuming your boot routine starts here

; code less than 1k

 ORG $FFFA

word START
word START
word START

 

Michael

Link to comment
Share on other sites

EDIT: forgot the NMI vector (does anybody even use this?)

I've seen it specified in some disassemblies, where all three vectors are set the same:

 

		 ORG $FFFA
	 word START
	 word START
	 word START

 

It has no relevance to the 6507 processor, but I've been curious about playing Atari 2600 games on an Atari 7800, since the Atari 7800 *does* have NMI interrupts. Does that mean you could create an NMI routine in an Atari 2600 game that's being played on an Atari 7800, or does the Atari 7800 switch over to a 6507 processor to run Atari 2600 games?

 

Michael

Link to comment
Share on other sites

Ok... this is starting to make sense more! Thank you for all of your guyes help! I just have one more question that doesent have to do with this directly. I noticed that in some of the 2600 101 tutorials before the ORG command there is a SEG. What does this do?

 

Thanks

That's short for "segment." I haven't looked at the code you're referring to, but I'm going to guess that it's using a SEG.U command? SEG.U creates an *uninitialized* segment, meaning DASM doesn't try to output any values or code for that segment of memory-- it just "defines" it so to speak. This is handy for defining variables, especially RAM variables that are located outside of the cartridge memory range (i.e., in the Atari 2600's zero-page RAM, although they could also be in expansion RAM that's included in the cartridge-- e.g., in Superchip RAM).

 

For example, if you open up the "VCS.H" file, and chop out all of the comments, you'll be left with this:

 

VERSION_VCS			 = 105

 IFNCONST TIA_BASE_ADDRESS
TIA_BASE_ADDRESS		= 0
 ENDIF

 IFNCONST TIA_BASE_READ_ADDRESS
TIA_BASE_READ_ADDRESS = TIA_BASE_ADDRESS
 ENDIF

 IFNCONST TIA_BASE_WRITE_ADDRESS
TIA_BASE_WRITE_ADDRESS = TIA_BASE_ADDRESS
 ENDIF

 SEG.U TIA_REGISTERS_WRITE
 ORG TIA_BASE_WRITE_ADDRESS

VSYNC		   ds 1
VBLANK		  ds 1
WSYNC		   ds 1
RSYNC		   ds 1
NUSIZ0		  ds 1
NUSIZ1		  ds 1
COLUP0		  ds 1
COLUP1		  ds 1
COLUPF		  ds 1
COLUBK		  ds 1

etc.

 

The first line simply defines a constant called "VERSION_VCS" and sets it to a value of 105, to identify that this is version 1.05 of the "VCS.H" file.

 

The next three lines say "IFNCONST TIA_BASE_ADDRESS" (i.e., if constant "TIA_BASE_ADDRESS" is *not* defined somewhere else within the program-- "If Not CONSTant"), then set it equal to 0 (i.e., to address $0000). That's because the TIA chip is connected to the 6507's address pins in such a way that the 6507 processor "sees" it at address $0000-- except that, like the cartridge slot, the TIA chip ends up being "mirrored" throughout the 64K address space. That means you can use any of the mirrors, and you'll still be addressing the TIA. At least one bankswitching scheme uses the beginning of the 64K address space for bankswitching purposes, in which case you can't address the TIA at those addresses or you'll end up switching banks-- so that bankswitching scheme uses the TIA mirror that's at address $40 (or $0040). DASM lets you set a constant called "TIA_BASE_ADDRESS" to specify where you'll be addressing the TIA registers, in case you want to write a program that uses that bankswitching scheme.

 

Likewise, the six lines after that let you define constants for the "TIA_BASE_READ_ADDRESS" and for the "TIA_BASE_WRITE_ADDRESS," so you can read the TIA "read registers" at one set of addresses, and write to the TIA "write registers" at another set of addresses. Normally, the TIA read and write registers are thought of as starting at $00 (or $0000), so if you haven't defined those two constants in your program, DASM will automatically set them equal to 0 (or $0000).

 

The next line-- "SEG.U TIA_REGISTERS_WRITE"-- says that we want to create an uninitialized segment that we'll call "TIA_REGISTERS_WRITE." Since it's an *uninitialized* segment, DASM won't create any actual code for it.

 

The next line-- "ORG TIA_BASE_WRITE_ADDRESS"-- says the uninitialized "TIA_REGISTERS_WRITE" segment will start at the address indicated by the "TIA_BASE_WRITE_ADDRESS" constant. Assuming we haven't set that constant to anything in particular, DASM will set it to 0 ($0000), in which case the uninitialized segment will start at $0000.

 

The next group of lines are the real "meat"-- they define where the TIA write registers are, what they'll be known as within the program, and how many bytes will be reserved for each one. For example, if the "TIA_REGISTERS_WRITE" segment is starting at address $0000, then the "VSYNC ds 1" line will define 1 byte at $0000, and associate it with the label "VSYNC." The next line-- "VBLANK ds 1"-- will define 1 byte at $0001, and associate it with the label "VBLANK."

 

You can use this same technique to create variables in zero-page RAM, as in this example:

 

		 SEG.U My_Zero_Page_RAM_Variables
	 ORG $80; or $0080, the start of zero-page RAM
An_8_Byte_Array	  DS 8
A_1_Byte_Variable	DS 1
A_2_Byte_Pointer	 DS 2

 

In this example, the label "An_8_Byte_Array" will be associated with address $80 (or $0080).

 

The label "A_1_Byte_Variable" will be associated with address $88 (or $0088), because 8 bytes were set aside for "An"8_Byte_Array," starting at $0080, therefore the next available address is $0088, see?

 

Now, I said that 8 bytes were "set aside" for "An_8_Byte_Array," but that doesn't stop you from using other labels for those bytes, if you define the other labels somewhere else in your program.

 

Another way to do this same thing would like this:

 

An_8_Byte_Array	  = $80
A_1_Byte_Variable	= $88
A_2_Byte_Pointer	 = $89

 

However, this second method can be tedious and tricky, because you have to keep track of where you want the next label to go, it's a hassle to relocate the variables, and it's easier to make a mistake as you locate or relocate the variables. For example, suppose you typed this by mistake:

 

An_8_Byte_Array	  = $80
A_1_Byte_Variable	= $81
A_2_Byte_Pointer	 = $82

 

Oops, you actually wanted to use addresses $80 through $87 for "An_8_Byte_Array," but the above lines will make "A_1_Byte_Variable" and "A_2_Byte_Pointer" overlap with "An_8_Byte_Array."

 

Anyway, if you want to declare space for variables using the "DS" statement, then you need to also use the "SEG.U" statement if the variables are going to be outside of the 4K cartridge area, otherwise DASM will create the binary file too large. For example:

 

		 ORG $80
An_8_Byte_Array	  DS 8
A_1_Byte_Variable	DS 1
A_2_Byte_Pointer	 DS 2

	 ORG $F000
START
; my program goes here...

	 ORG $FFFC
	 WORD START
	 WORD START

 

The above code will cause DASM to create a binary file that's 65408 bytes long (starting at $0080 and ending at $FFFF), which is not good. On the other hand, the following is okay:

 

		 SEG.U My_Variables
	 ORG $80
An_8_Byte_Array	  DS 8
A_1_Byte_Variable	DS 1
A_2_Byte_Pointer	 DS 2

	 SEG My_Program
	 ORG $F000
START
; my program goes here...

	 ORG $FFFC
	 WORD START
	 WORD START

 

This will create an uninitialized segment starting at $0080, so DASM won't output anything for that segment, but then it will create an initialized segment starting at $F000--- so DASM will create a binary file that's 4K (from $F000 through $FFFF).

 

If you want to know all of the options and commands that are available with DASM, there's a text file named "DASM.TXT" that comes with DASM, which describes them all.

 

I hope this wasn't too confusing. :)

 

Michael

Edited by SeaGtGruff
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...