Jump to content
IGNORED

Booting from Cartridge


Recommended Posts

I wonder if someone could point me in the direction of a book, or better yet the section of a book which describes how to write a programme that will boot from cartridge. I am sure there is material about this in 'Mapping The Atari' for instance, but that is a rather dense volume to flick through speculatively! Obviously as an ASM novice I am only looking at the most BASIC 8K cartridges and not yet the more elaborate cartridge banking schemes. That is something I definitely want to look at - but quite a bit further down the road.

 

My reason for the interest is I would like to use cartridge as a 'transfer medium' to move self-booting programmes from PC to the A8, without at this point having to worry about learning the intricacies of how to write a boot loader for a disk-based programme. The UltimateCart makes it super easy to load up a cartridge image in this way and if the programme is self-booting then I can switch BASIC off and have a great deal more room in memory to play with.

Link to comment
Share on other sites

If it really is easy then page 103 of Mapping should be enough to get that going.

 

Excellent, many thanks 1050!!!! That material looks like it covers exactly what I am after.

 

Going from what is said there, how and what sections of code are run by a cartridge is determined by the last six bytes of the 8K chunk of memory that is your cartridge image. When a cartridge is inserted and the machine is switched on - assuming we are talking about the standard 'left' slot - that 8k of data are copied to the region in the Atari's memory between address $A000 and $BFFF and those last six bytes occupy addresses $BFFA to $BFFF. $BFFA and $BFFB are the low and high bytes denoting where your cartridge's code begins - which will usually be set of $00 and $A0 respectively assuming you start at the first piece of memory available. I would think $BFFC is set to 0 automatically by the machine to say that a cartridge is indeed present. $BFFD determines whether to boot a disk and whether to move the point of execution to the address in memory where the initialization data of the cartridge begins; so the 8 bits for 'no disk boot, start cartridge' would be 00000100. The last two bytes $BFFE and $BFFD are the low and high bytes which denote exactly where in the cartridge's 8K chunk of memory that initialization code begins - so again usually $00 and $A0 respectively.

 

If I have got that right then it seems quite a bit easier than I was expecting - at least in respect to the simplest 8k type of cartridge image. The next question I have is, how do you tell your assembler programme to actually produce code in that specific 8k chunk of memory? My first guess would be something along the lines of:

ORG  $A000

...programme goes here...

ORG  $BFFA
.BYTE  $00  ;low byte cartridge code start address
.BYTE  $A0  ;high byte cartridge code start address
.BYTE  $00  ;set by Atari to 0 when cartridge inserted
.BYTE  $04  ;option bytes with bits 00000100 telling Atari to initialize and run code in cartridge
.BYTE  $00  ;low byte cartridge initialization code start address
.BYTE  $A0  ;high byte cartridge initialization code start address
Edited by morelenmir
Link to comment
Share on other sites

It's Rom, nothing is copied to Ram unless the cartridge program does that itself.

 

Note that with banked carts that can swap the area containing those flags/vectors @ $BFFA, you need to have those provisioned in every bank that can provide those bytes since the active bank is only guaranteed on an initial power on operation but not coldstarts that occur without removing power.

 

It's up to the programmer whether to run the program from Rom or have it Ram-based and copied there, or do both. But Ram-based will usually mean more than 16K required.

The earliest carts like Asteroids and Star Raiders would work with 8K Ram, it soon increased to 16K. Many later ones esp XEGS branded, require 40K or more to work.

Link to comment
Share on other sites

It's Rom, nothing is copied to Ram unless the cartridge program does that itself.

 

Note that with banked carts that can swap the area containing those flags/vectors @ $BFFA, you need to have those provisioned in every bank that can provide those bytes since the active bank is only guaranteed on an initial power on operation but not coldstarts that occur without removing power.

 

It's up to the programmer whether to run the program from Rom or have it Ram-based and copied there, or do both. But Ram-based will usually mean more than 16K required.

The earliest carts like Asteroids and Star Raiders would work with 8K Ram, it soon increased to 16K. Many later ones esp XEGS branded, require 40K or more to work.

 

That is one of the concepts that I really struggle with, but I think I understand what you are saying Rybags. The code is not 'copied' to this address range. Instead you can access the code by referring to these addresses in actual RAM and this is somehow translated to the memory positions holding code that physically remain on the cartridge chips. However, the code on the cartridge can copy some part of itself to RAM addresses that are physically on the RAM of the motherboard.

 

What I don't quite understand is how the memory range physically on the cartridge is 'overlayed' on to the RAM of the motherboard without copying it. Perhaps that doesn't matter though.

Link to comment
Share on other sites

The architecture of the computer is such that there's flexibility, the MMU from a combination of address, state of PORTB which programs can change, and whether certain conditions on PBI and cartridge ports means that Ram, Rom, IO space, empty space can potentially exist almost anywhere.

 

It doesn't matter greatly, not much more than an overview of the machine is needed to be able to program for it.

Link to comment
Share on other sites

I wonder if someone could point me in the direction of a book, or better yet the section of a book which describes how to write a programme that will boot from cartridge. I am sure there is material about this in 'Mapping The Atari' for instance, but that is a rather dense volume to flick through speculatively! Obviously as an ASM novice I am only looking at the most BASIC 8K cartridges and not yet the more elaborate cartridge banking schemes. That is something I definitely want to look at - but quite a bit further down the road.

 

Use "atari-cartridge.cfg" configuration with cc65 :-)

Link to comment
Share on other sites

 

Use "atari-cartridge.cfg" configuration with cc65 :-)

 

That's a good point sanny, although not perhaps in the way you mean!!! I will be using MADS, so... Do you know how I go about specifying that assembler outputs an 8k chunk of data which represents the $A000-$BFFF cartridge data space?

 

Presumably I could start with an 'ORG $0' command and ensure my programme does not go beyond $2000 with the last 8bytes defined as mentioned before. I think the '-f' switch will cause MADS to emit a solid block of data as a *.BIN file. Then, when wrapped in a *.CAR file with the appropriate header that 8K chunk will be presented by the UltimateCart as a cartridge and the Atari will automatically relocate the starting byte to $A000? That is just a guess at the process though.

Link to comment
Share on other sites

	opt h-	; no header

	org $A000	; cartridge start address
			
	opt f+	; turn on fill mode


	[your code]


	.align $BFFA,$FF

	[put 6 byte cartridge header here]


MANY thanks jon!!! I was all set to start a thread specifically on how to go about getting assemblers to make cartridge images, but I think you post has cleared up most of my questions!!!

 

I do have a couple of points about the MADS-specific stuff though. I notice you use the '.align' directive rather than another 'ORG' statement to carry the assembler forward to the final six bytes (for some reason I has started thinking there were eight bytes!). Would 'ORG $BFFA' not work? I also assumed that switches like 'opt f+' (I did not know about 'opt h-') would be supplied as a command line argument to MADS from WUDSN. Instead It looks like you can just embed them in the actual ASM code and the assembler will obey?

Link to comment
Share on other sites

Yes, you can probably use ORG. You'd have to test it and see.

 

All the OPTs are listed in the Assembly Control section of the English manual:

 

http://mads.atari8.info/mads_eng.html#_opt

 

Excellent! From that document it seems the 'opt' directives are specifically for use inside the code itself, therefore more in common with a #pragma directive in C/C++ than a command-line switch.

 

 

One other quick note...

 

After the OS inits, it checks the 8000-9FFF area for a cartridge first. This means a 16K program should pay attention to what's in $9FFC. A diagnostic cart (one that inits the system itself) won't have this issue.

 

I think I can understand what you mean Bryan. I couldn't quite get my head around the similar description in 'Mapping The Atari' but considering it in terms of a 16k cartridge helps a lot.

 

My next question is how do you go about programming code intended for a cartridge on the actual hardware? I would think you cannot give the ASM editor equivalent of 'ORG $A000' ('*=$A000' perhaps?) and have it build as the ASM editor cartridge itself is occupying that space in memory... Bit of a Kobayashi Maru!

Edited by morelenmir
Link to comment
Share on other sites

Cartridge end bytes are so similar to file header info they can be treated as the same thing.

Instead of INIT and RUN vectors the addresses are compiled as .WORDS at the end of

cartridge code instead.

 

* =$BFFA

.WORD RUN

.BYTE Non_zero

.BYTE Option

.WORD INIT

 

Needs translating to MADS of course and I too have not gone there as of yet.

 

Function of MMU is to switch ram off and map the cartridge data directly to

the cartridge address locations. Of course if you want to believe in

smoke and mirrors then that's how it's done.

 

When the assembler is set to compile code to a file it can be sent there

instead of into memory where for example Mac/65 cart is already existing.

A minor amount of work on the file with a hex editor and you have a burn

file for an eprom of your choice.

Link to comment
Share on other sites

Excellent! From that document it seems the 'opt' directives are specifically for use inside the code itself, therefore more in common with a #pragma directive in C/C++ than a command-line switch.

 

I think I can understand what you mean Bryan. I couldn't quite get my head around the similar description in 'Mapping The Atari' but considering it in terms of a 16k cartridge helps a lot.

 

My next question is how do you go about programming code intended for a cartridge on the actual hardware? I would think you cannot give the ASM editor equivalent of 'ORG $A000' ('*=$A000' perhaps?) and have it build as the ASM editor cartridge itself is occupying that space in memory... Bit of a Kobayashi Maru!

 

The A8 always treats the cartridge port as 2 8K banks. There's a select for 8000-9FFF and A000-BFFF. The LEFT/XL/XE cartridge has the capability of occupying both spaces, effectively giving you a 16K cartridge, but you still need to be aware that the OS will look for an 8K cartridge at 8000-9FFF first.

 

If you assemble for a 16K image and the values are already okay to fail the 8K cartridge test, then there's nothing else you need to do. If not, then you have to rearrange things a bit or jump over a few reserved bytes. I believe as long as 9FFC != 0, you're okay (I'm away from my computer, but I can look it up later).

Link to comment
Share on other sites

IMHO: If you use .org for the cartridge control bytes, you may end up with something you don't want. At least some assemblers will add the binary file header every time they see a .org.

 

Just a suggestion for debugging/developing your code. You may want to lower RAMTOP below the $A000 area and use DOS to load the binary into the cart region. A development cycle something like assemble file to file, lower RAMTOP, go to DOS, binary load the file, use DOS command RUN at $A000 or where ever your code is supposed to start.

 

The binary load will strip all the multiple .orgs you may have in your code and the binary headers to give you a clean copy of what the cart will look like in ROM<except it will be in RAM of course. There are a few bugs, like clear screen may wipe out the bottom of your RAM/CART image under the older OS. Anyway, makes for a reasonable development cycle.

Link to comment
Share on other sites

IMHO: If you use .org for the cartridge control bytes, you may end up with something you don't want. At least some assemblers will add the binary file header every time they see a .org.

 

Just a suggestion for debugging/developing your code. You may want to lower RAMTOP below the $A000 area and use DOS to load the binary into the cart region. A development cycle something like assemble file to file, lower RAMTOP, go to DOS, binary load the file, use DOS command RUN at $A000 or where ever your code is supposed to start.

 

The binary load will strip all the multiple .orgs you may have in your code and the binary headers to give you a clean copy of what the cart will look like in ROM<except it will be in RAM of course. There are a few bugs, like clear screen may wipe out the bottom of your RAM/CART image under the older OS. Anyway, makes for a reasonable development cycle.

 

An excellent workflow - especially if you are trying to programme from the Atari itself, which is my ultimate aim.

 

In regards starting out with cartridge programming I have followed I think all the suggestions I have received so far and come up with the following, very short programme:

		    opt h-
		    ICL "Atari 8-Bit Memory Constants.asm"		;INCLUDE ATARI MEMORY LABEL DEFINITIONS
				
		    org $A000
		    opt f+
				
START_PROGRAMME     lda #$FF
		    sta COLOR1
		
		    lda #$00     ;load the acumulator with 0
		    sta SDMCTL   ;store 0 in SDMCTL which should stop ANTIC.
				
		    lda #DISPLAY_LIST&255   
		    sta SDLSTL              
		    lda #DISPLAY_LIST/256
		    sta SDLSTL+1
				
		    lda #$22
		    sta SDMCTL
				
END_PROGRAM	    jmp END_PROGRAM				
			
		                          ;(192 scan lines available)									;
DISPLAY_LIST        .byte $70,$70,$70     ;Discard 24 scan lines
		    .byte $42	          ;8 scan lines		= 40 bytes / 1 character per byte = 40 characters per line
		    .word DISPLAY_MEMORY
		    .byte $08		  ;8 scan lines		= 10 bytes / 4 pixels per byte 	  = 40 pixels per line
		    .byte $03		  ;10 scan lines	= 40 bytes / 1 character per byte = 40 characters per line
		    .byte $08		  ;8 scan lines		= 10 bytes / 4 pixels per byte 	  = 40 pixels per line
		    .byte $06		  ;8 scan lines		= 20 bytes / 1 character per byte = 20 characters per line
		    .byte $08		  ;8 scan lines		= 10 bytes / 4 pixels per byte 	  = 40 pixels per line
		    .byte $07		  ;16 scan lines	= 20 bytes / 1 character per byte = 20 characters per line
		    .byte $08		  ;8 scan lines		= 10 bytes / 4 pixels per byte    = 40 pixels per line
					  ;= 99 scan lines (94 scan lines lines remaining)
		    .byte $41
		    .word DISPLAY_LIST

DISPLAY_MEMORY	    .sb "Custom Display List.                    "
		    .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
		    .sb "Custom Display List.                    "
		    .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	            .sb "CUSTOM DISPLAY LIST."
		    .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
		    .sb "CUSTOM DISPLAY LIST."
		    .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00				
				
CARTRIDGE_OPTIONS   .align $BFFA, $FF
				
		    .word START_PROGRAMME
		    .byte $0
		    .byte $4
		    .word START_PROGRAMME

Obviously this is very simple. My intention was to create a new display list which defines a line of each type of plain text graphics, separated by a line of pixel graphics. The text 'Custom Display List', previously loaded in to the memory location pointed to by the new display list should then be displayed in each type of text graphics.

 

At least that was my intention. If I compile this as a *.XEX, then rename it as a *.BIN and under Altirra use 'Attach Cartridge' all seems well. The virtual machine boots, but I get this screen:

 

post-31546-0-65286000-1473031689_thumb.jpg - Not what was hoping for!

 

Would one of you chaps be able to tell me where I am going wrong? Aside form the new cartridge booting process I thought I had all this display list and screen memory stuff down pat when I first started properly learning Atari ASM three or four years ago. However it seems I have forgotten something over the intervening years - something which I have no doubt is both ridiculously simple and glaringly obvious!!!

 

Update:

 

I seem to have worked this out. If I create my own custom character set with 'Atari Font Maker' name 'character set.fnt', include it in the WUDSN project and add to the main body of the programme the lines:

LDA #CHARACTER_SET/256
STA CHBAS

further along followed by:

CHARACTER_SET		.align $A400, $00
			ins "character set.fnt"

All is well!. I guess using the cartridge boot method in some way overwrites the built in character set. Unless of course the character set is in the BASIC ROM?

Edited by morelenmir
Link to comment
Share on other sites

You can set CHBAS to $E0 to use the ROM character set.

 

Interesting... That works perfectly Xuel - many thanks!!!

 

Doing a little more investigation I find if I comment out all the cartridge specific code and just compile this as a *.XEX, when dropped on Altirra the programme displayes the correct text - therefore the correct character set. However if I restore the cartridge code I get the old problem. Perhaps this because CHBAS is set to '0' and the display machinery is interpreting the contents of PAGE 0 as characters?

 

Therefore I guess the question is why does CHBAS get set properly when I boot as a *.XEX - even with BASIC turned off - yet is not set when booting from a cartridge?

Link to comment
Share on other sites

The problem is that your cartridge is running its mainline code off of the initialization vector, so it is taking control of the system before the Screen Editor (E:) has been opened. It would be better to have your cartridge init vector pointing at an RTS and run all of your existing code off of the cartridge run vector.

 

Also, let's fix up that display init code:

    lda #$00                 ;load the accumulator with 0
    sta SDMCTL               ;store 0 in SDMCTL to disable display DMA
    sta DMACTL               ;shut display DMA off immediately instead of waiting for vblank

    sei                      ;suspend VBI stage 2 so it can't copy half the dlist ptr
    lda #DISPLAY_LIST&255
    sta SDLSTL              
    lda #DISPLAY_LIST/256
    sta SDLSTL+1
    cli                      ;resume VBI stage 2
				
    lda #$22
    sta SDMCTL

  • Like 2
Link to comment
Share on other sites

 

The problem is that your cartridge is running its mainline code off of the initialization vector, so it is taking control of the system before the Screen Editor (E:) has been opened. It would be better to have your cartridge init vector pointing at an RTS and run all of your existing code off of the cartridge run vector.

 

Also, let's fix up that display init code:

    lda #$00                 ;load the accumulator with 0
    sta SDMCTL               ;store 0 in SDMCTL to disable display DMA
    sta DMACTL               ;shut display DMA off immediately instead of waiting for vblank

    sei                      ;suspend VBI stage 2 so it can't copy half the dlist ptr
    lda #DISPLAY_LIST&255
    sta SDLSTL              
    lda #DISPLAY_LIST/256
    sta SDLSTL+1
    cli                      ;resume VBI stage 2
				
    lda #$22
    sta SDMCTL

 

That is fascinating material phaeron - and I have never heard of either using 'DMACTL' or the 'sei' command in that way. Annoyingly neither 'Atari Roots' nor 'De Re Atari' cover that material - which is annoying as it sounds like a very powerful technique.

 

The distinction between 'initialization' and 'run' addresses is something I have never understood. From what you are saying I guess it would be possible to use it like the constructor in a C++ class and then return control to the system after you have done programme specific things like setting variables and so on. Again that is a very useful technique and something the books - those I have read at least - don't cover at all. One useful thing that could go in to the initialization section is the 'CLC' and 'CLD' command that must start all programmes?

Link to comment
Share on other sites

Think of it this way: INIT code is executed before the OS does its thing, while RUN code is executed after. So if you're relying on anything the OS does, you either have to do it in your RUN code, duplicate the code in your INIT code, or have your INIT code call the parts of the OS ROM you need.

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