Page 1 of 2
Building a Cartridge Header in 64tass, Confused
Posted: Sun May 24, 2020 12:57 pm
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.
Re: Building a Cartridge Header in 64tass, Confused
Posted: Sun May 24, 2020 1:14 pm
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?
Re: Building a Cartridge Header in 64tass, Confused
Posted: Sun May 24, 2020 1:26 pm
by load81
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.
Re: Building a Cartridge Header in 64tass, Confused
Posted: Sun May 24, 2020 1:54 pm
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.
Re: Building a Cartridge Header in 64tass, Confused
Posted: Sun May 24, 2020 4:08 pm
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
Re: Building a Cartridge Header in 64tass, Confused
Posted: Sun May 24, 2020 7:25 pm
by load81
I believe what you are looking for is the .logical directive.
Based upon the documentation, that appears to be the case!
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!
Re: Building a Cartridge Header in 64tass, Confused
Posted: Sun May 24, 2020 7:54 pm
by 8BIT
I'm happy I could help.
Best of luck with your project!
Daryl
Re: Building a Cartridge Header in 64tass, Confused
Posted: Tue May 26, 2020 3:56 pm
by beethead
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".
Re: Building a Cartridge Header in 64tass, Confused
Posted: Tue May 26, 2020 4:49 pm
by load81
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.
Re: Building a Cartridge Header in 64tass, Confused
Posted: Tue May 26, 2020 6:46 pm
by 8BIT
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.
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
Re: Building a Cartridge Header in 64tass, Confused
Posted: Wed May 27, 2020 2:22 am
by load81
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.
Re: Building a Cartridge Header in 64tass, Confused
Posted: Wed May 27, 2020 12:34 pm
by load81
Now I more fully understand why I ran into the issue I was hitting, from
Section 4.1 of the
64tass manual:
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.
Re: Building a Cartridge Header in 64tass, Confused
Posted: Wed May 27, 2020 1:18 pm
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).
Re: Building a Cartridge Header in 64tass, Confused
Posted: Wed May 27, 2020 10:54 pm
by 8BIT
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
Re: Building a Cartridge Header in 64tass, Confused
Posted: Fri May 29, 2020 11:58 pm
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...
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?