Building a Cartridge Header in 64tass, Confused

Programming the 6502 microprocessor and its relatives in assembly and other languages.
load81
Posts: 71
Joined: 16 Nov 2018

Building a Cartridge Header in 64tass, Confused

Post by load81 »

So, I'm building a cartridge header file in 64tass for a project I'm working on, and it's not quite doing what I expect. If do something like this:

Code: Select all

	*=$0000
	(cart header bytes)

	*=$8000
	.addr start				; Start of execution
	.addr reset				; <RUN/STOP> + <RESTORE>
	.text "CBM80"				; Autostart "Magic Number"
Everything past the cartridge header is filled with $00's. Obviously, my desired output is for the assembler to not produce any intervening null bytes. I've tried using the -n flag to suppress this behavior and it's not working. Perhaps I'm using the wrong flag, or there is an assembler directive I've missed in the documentation.

The exact flags I'm using are:

Code: Select all

64tass -a -n -b --m6502 infile.asm -o outfile.bin 
I've worked around the problem by building the cartridge header and the main program separately. I then use the oldest Linux/Unix trick in the book: cat header.bin program.bin > program.crt. This works, and that's what my Makefile is doing now, but I feel like I've missed a more appropriate solution and it's bugging me. Suggestions?

FYI, I'm deliberately not using VICE's cartconv program for this purpose, because it's not quite fine-grained enough for what I want. So, for my purposes, building it directly in the assembler is the correct approach.
DerTrueForce
Posts: 483
Joined: 04 Jun 2016
Location: Australia

Re: Building a Cartridge Header in 64tass, Confused

Post by DerTrueForce »

Is the cartridge header supposed to be at address $0000, as seen by the 6502? Because that's what you're telling the assembler to set up there.
Or have I entirely missed what this header data is actually for?
load81
Posts: 71
Joined: 16 Nov 2018

Re: Building a Cartridge Header in 64tass, Confused

Post by load81 »

DerTrueForce wrote:
Is the cartridge header supposed to be at address $0000, as seen by the 6502? Because that's what you're telling the assembler to set up there.
Or have I entirely missed what this header data is actually for?
That was a point of confusion for me too, at first. Starting at offset $0000 is correct.

The CPU never sees the header bytes, they're entirely for the benefit of the emulator. Without a header, the emulator will not know how to treat the cartridge hardware itself. Here is a tutorial with examples targeting a different assembler. Note the header bytes start at $0000 and the ROM part, including the "CBM80" auto-start signature, start at $8000 which is what the CPU actually sees.

That tutorial shows what I'm trying to do, I just can't get it to work the way I expect in 64tass.
DerTrueForce
Posts: 483
Joined: 04 Jun 2016
Location: Australia

Re: Building a Cartridge Header in 64tass, Confused

Post by DerTrueForce »

By the looks of that tutorial, the value of the program counter is entirely irrelevant to the header itself. Those examples look to me like they're using a behaviour of their assemblers that is not standardized.
The most assembler-agnostic method of doing that(while still keeping it within the assembler) is to set the program counter once, to $8000 minus however many bytes the header requires, so that the start of the rom contents starts with the program counter at $8000.
All that said, I think you are probably already doing it in the most correct manner. The cartridge contents and that header data are logically seperate entities, so I think generating them separately and then combining them is probably more correct than doing both in the one source file.
User avatar
8BIT
Posts: 1787
Joined: 30 Aug 2002
Location: Sacramento, CA
Contact:

Re: Building a Cartridge Header in 64tass, Confused

Post by 8BIT »

I believe what you are looking for is the .logical directive.

You source will look something like this:

Code: Select all

	*= $0000
	.text "C64 CARTRIDGE   "
	.byte $00,$00 ;header length
	.byte $00,$40 ;header length
	.word $0001 ;version
	.word $0000 ;crt type
	.byte $00 ;exrom line
	.byte $01 ;game line
	.byte $00,$00,$00,$00,$00,$00 ;unused
	.text "CRT TITLE"
	.fill $0040 - *, $00 ; pad title with $00
	;chip packets
	.text "CHIP"  
	.byte $00,$00,$20,$10 ;chip length
	.byte $00,$00 ;chip type
	.byte $00,$00 ;bank
	.byte $80,$00 ;address
	.byte $20,$00 ;length

;ROM part - this will be loaded at address $8000 so all label and branch targets need to start at $8000
	.logical $8000
	(begin source)
You may need to tweak this a little, but it should get you where you want to be.

Daryl
Please visit my website -> https://sbc.rictor.org/
load81
Posts: 71
Joined: 16 Nov 2018

Re: Building a Cartridge Header in 64tass, Confused

Post by load81 »

8BIT wrote:
I believe what you are looking for is the .logical directive.
Based upon the documentation, that appears to be the case!
8BIT wrote:

Code: Select all

   .text "CRT TITLE"
   .fill $0040 - *, $00 ; pad title with $00
I was fighting with .text format / fill, too. You accidentally solved a second issue I was having with the null padded cart title field. Thanks!
User avatar
8BIT
Posts: 1787
Joined: 30 Aug 2002
Location: Sacramento, CA
Contact:

Re: Building a Cartridge Header in 64tass, Confused

Post by 8BIT »

I'm happy I could help.

Best of luck with your project!

Daryl
Please visit my website -> https://sbc.rictor.org/
beethead
Posts: 9
Joined: 03 Dec 2019

Re: Building a Cartridge Header in 64tass, Confused

Post by beethead »

8BIT wrote:

Code: Select all

	.text "CRT TITLE"
	.fill $0040 - *, $00 ; pad title with $00
That's interesting because I thought I was being innovative with my own assembler. This is my variation on the same directive/code.

Code: Select all

.byte ("CRT TITLE") pad $40; for space padded ..pad $40($20)
That pads after the text, use -$40 to pad before the text. Alternatively for the lazy person who can't count and overruns the text field, "trunc" can be substituted for "pad".
load81
Posts: 71
Joined: 16 Nov 2018

Re: Building a Cartridge Header in 64tass, Confused

Post by load81 »

beethead wrote:
8BIT wrote:

Code: Select all

	.text "CRT TITLE"
	.fill $0040 - *, $00 ; pad title with $00
That's interesting because I thought I was being innovative with my own assembler. This is my variation on the same directive/code.

Code: Select all

.byte ("CRT TITLE") pad $40; for space padded ..pad $40($20)
That pads after the text, use -$40 to pad before the text. Alternatively for the lazy person who can't count and overruns the text field, "trunc" can be substituted for "pad".
Oh, neat. Hmm... @8BIT, by "my own assembler" do you mean "my own code [in 6502 assembly]" in 64tass or otherwise, or have you written your own assembler proper as well as code for it? If so, which one? I'd like to have a look at it.

Yeah, I got the padding to work just fine. After some testing, it seems the .logical directive has screwed up the way at least some of my labels are computed. I'm sure it's the right approach generally and that I'm just doing something wrong. I'll circle back to it, there are other issues I'm working through with the codebase I'm disassembling and working through.
User avatar
8BIT
Posts: 1787
Joined: 30 Aug 2002
Location: Sacramento, CA
Contact:

Re: Building a Cartridge Header in 64tass, Confused

Post by 8BIT »

load81 wrote:
Oh, neat. Hmm... @8BIT, by "my own assembler" do you mean "my own code [in 6502 assembly]" in 64tass or otherwise, or have you written your own assembler proper as well as code for it? If so, which one? I'd like to have a look at it.
I think you meant this for Beethead.
Quote:
Yeah, I got the padding to work just fine. After some testing, it seems the .logical directive has screwed up the way at least some of my labels are computed. I'm sure it's the right approach generally and that I'm just doing something wrong. I'll circle back to it, there are other issues I'm working through with the codebase I'm disassembling and working through.
My understanding is any label you define will keep that definition. This is good for zero page and system calls. Any label assigned an address by the assembler (from its internal PC), should get assigned the logical address. That way JSR/JMP works within the assembled code. Branches use offsets so they are not affected. Also, if you put data tables in your code, then do not use the *= directive to place your table(s). If you need page aligned tables, then you'll need to figure out a way to do that. 64TASS might have a .page align type directive.

If you need more help, please post some code examples. I do not use 64TASS, but I used to use the original TASS, which is similar.

Daryl
Please visit my website -> https://sbc.rictor.org/
load81
Posts: 71
Joined: 16 Nov 2018

Re: Building a Cartridge Header in 64tass, Confused

Post by load81 »

Quote:
I think you meant this for Beethead.
Yep, I did. I was tired. My bad.

Here is what I've come up with and my trial and error process that got me there...

I dispensed with *=$0000 and switched to .logical instead. At first, it didn't work. The 64tass documentation shows the two directives used together. That didn't work. I tried it in a few places, right after the .logical keyword for starters, then at the end of my code. Nothing worked. The resulting file was larger than expected, but not by a ton. Even a simple jsr [label to absolute address] wouldn't work.

This is what I tried was:

Code: Select all

*=$0000
(cartridge header)
(chip packet)
.logical $8000
.word start	    ; $8000
.word nmi_reset   ; $8074
.text "CBM80"	  ; auto-start signature
(main code)
.here
Eventually, I hit on abandoning *=$8000 entirely, and just went with .logical $0000 and .logical $8000, that did work as long as I placed the .here statements at the end of the segments.

The idea of named sections of code appealed to me. I'm reverse engineering a game after all. I have only a rough idea where the code ends and where sprite and bitmap data begin. When I figure out the dividing line, it will be nice to be able to mark it as data; if only as an aid to the reader.

What I ended up with, after some trial and error was...

Code: Select all

cart_header=$0000
.dsection cart_header
;.cerror * > $50, "Header too large."

main=$8000
.desection main

.section cart_header
.logical cart_header
(cartridge header)
(chip packet)
.here

.section main
.logical main
.word start
.word nmi_reset
.addr start		;$8009
.addr nmi_reset  ;$8074
.text "CBM80"	 ;auto-start signature

(program code and data)
.here
Maybe that's redundant, or I am otherwise "doing it wrong." I'm open to criticism. Eventually, I want to work out a generic macro to handle cartridge headers and chip packets. I'll add a section for data to clearly demark it from the main program when I get that far.
load81
Posts: 71
Joined: 16 Nov 2018

Re: Building a Cartridge Header in 64tass, Confused

Post by load81 »

Now I more fully understand why I ran into the issue I was hitting, from Section 4.1 of the 64tass manual:
Quote:
Two counters are used while assembling. The compile offset is where the data and code ends up in memory (or in image file). The program counter is what labels get set to and what the special star label refers to
I was failing to fully appreciate the distinction between .logical and the *= assembler directives.
User avatar
BitWise
In Memoriam
Posts: 996
Joined: 02 Mar 2004
Location: Berkshire, UK
Contact:

Re: Building a Cartridge Header in 64tass, Confused

Post by BitWise »

If the output is a binary file which consists of a 80 byte cartridge header followed by the ROM image then you could start with the origin set to $8000-$50 (*=$7FB0).
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs
User avatar
8BIT
Posts: 1787
Joined: 30 Aug 2002
Location: Sacramento, CA
Contact:

Re: Building a Cartridge Header in 64tass, Confused

Post by 8BIT »

BitWise wrote:
If the output is a binary file which consists of a 80 byte cartridge header followed by the ROM image then you could start with the origin set to $8000-$50 (*=$7FB0).
That would certainly follow the KISS mantra. Nice solution!

Daryl
Please visit my website -> https://sbc.rictor.org/
load81
Posts: 71
Joined: 16 Nov 2018

Re: Building a Cartridge Header in 64tass, Confused

Post by load81 »

I worked out a series of generic macros for handling cartridge headers and chip packets. I'm sure both of them could stand improvement, but I have a sold working first draft. I have some lookup tables and error flags, but this is the "meat" of the macro.

Code: Select all

cart_header	.macro title="", hardware="generic", exrom=false, game=false
				.include "encodings.s"					; defines ASCII

				.enc "ascii"
				.text "C64 CARTRIDGE   "				 ; Magic value
				.enc "none"

				.word >< $0000							   ; Cartridge header length, >< not needed but it matches the rest.
				.word >< $0040							   ; ""
				.word CRT_FORMAT_VER[1.0]				 ; Version of the .crt file format.
				.word CARTRIDGE_HARDWARE[\hardware]	; cartridge hardware
				.byte \exrom								  ; EXROM line
				.byte \game								   ; GAME line
				.fill 6,0									  ; "reserved for future use"

				.enc "none"
				.text \title
			 
				.fill $0040 - *, $00					; Pad title with null-bytes.
			.endm
Now, I just invoke cart_header in and pass parameters and the cartridge header gets built exactly the way I want it to. I'm sure there are about a dozen ways this macro could be improved. The only "gotcha" is, when I turn -Wall on (enable all warnings) I get the following warning...
Quote:
note: original location in an expanded macro was herecan't get integer value of bool 'false' -Wstrict-bool
This is strange, because boolean values are just aliases for 1/0 true and false. Even after having read the -Wstrict-bool flag details, I'm not sure why this happening. Can someone comment on this?
Post Reply