Jump to content
  • 0
JohnGill

Hello World assembly beating me!

Question

Hi all, sorry to keep bugging you with these noob assembly questions. I'm sure I've set all the registers correctly, I've been wracking my brains to work out what I've done wrong with this seemingly very simple bit of code but I can't fathom it out.

It compiles fine in CBM PRG studio, and I can load it up into emu r37, but when I sys $2C00 it just clears the screen and returns straight back to READY at the top of the screen in basic.

Help! I'm hoping someone can spot my noob mistake! 
 

Untitled-1.png

Share this post


Link to post
Share on other sites

14 answers to this question

Recommended Posts

  • 0

Why are you trying to load this at $2C00? Have you tried running your code from $0801 instead? I'm not familiar with this assembler, so I don't know if you are doing the right SYS command to run this. Have you tried running in debug mode, with -debug? You can check to make sure your code is in memory where you are expecting it.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
  • 0

Well, this is probably pretty easy. You're dropping a string right into the middle of your code, and I'll bet CBM PRG studio isn't politely shuffling that to the end of your program like a segmented assembler would. I don't actually know anything about CBM PRG studio, and there didn't appear to be any online documentation, but the author seems insistent that his program is not an assembler, so I'm guessing it doesn't have the smarts to work that out, by design. ¯\_(ツ)_/¯

Try moving that string to some point after the rts instruction at the end there.

 

 

If that's not it, then I agree with @SlithyMatt that the most suspect thing is that *=$2C00, which is setting the instruction pointer to $2C00 and will, under most assemblers, cause it to begin emitting instructions and addresses as if the following portion of the file began at that address.

There are a bunch of things to check to follow this rabbit hole.

Firstly, open your .prg file in a hex editor of some sort (I frequently go to https://hexed.it online for these purposes) and check that the first two bytes are $00 and $2C, in that order (this is $2C00 in little-endian order, which is what the X16 expects). If you're loading the file either through the -prg command-line option or with LOAD"FILENAME.PRG",8,1, then these bytes need to be correct or else the program is being loaded somewhere else, meaning you'll need to change your *=$2C00 to match wherever CBM PRG studio is indicating the file should be loaded.

Worst case, if you open the file and discover the starting bytes are your assembly instructions (A9 10 8D 22 9F A9 00 8D 20 9F 8D 21 9F...), then CBM PRG studio is not placing ANY header at all and you'll want to manually add one with a byte statement, probably byte $00, $2C or byte $00 $2C, depending on the specifics of its syntax.

If you're loading with LOAD"FILENAME.PRG",8,0 or LOAD"FILENAME.PRG", instead, the file will always be loaded into $0801 with a LOAD command. (That is, unless you're trying to poke memory to clobber this behavior, but to my knowledge that technique is completely undocumented on the X16 and probably subject to change with future revisions to the kernal, so I wouldn't personally try that).

The next thing to do would be to run the debugger with the -debug command-line option, in addition to whatever other command-line options you're currently using. Load the file, and then press F12. This should pause the emulator and bring up a panel like this:

x16-debugger.png.ef3d0edea647d72588d12f93fe13ff6b.png

From here, type d2c00 and press enter. This is similar to how the monitor worked on the C64, 'd' is a prefix meaning you want to change the address inspected by the disassembly view in the top-left of the debugging panel. At this point, the disassembly view should be showing your assembly instructions, at $2C00. If you don't see your assembly there, the code got loaded somewhere else, and we need to go back to the beginning and figure out why it didn't get loaded where you think it did.

But if your code is there, you can now press F9 to set a breakpoint at $2C00. Debugger panel breakpoints will pause the entire emulator at the point of execution. I love them. ❤️ So at this point, press F5 to close the debugger panel and resume emulation. Try doing that SYS$2C00 call, and the emulator should automatically pop open the debug panel again, this time paused immediately before executing the instruction at $2C00. From here, you can inspect the state of the machine and step through your code, instruction by instruction, with F11 (step-into). Alternatively, you can step with F10 (step-over), the difference is that F10 will run a jsr and all the code that executes after it, until immediately after the matching rts instruction is executed.

If you need to inspect memory instead of disassembled code, you can use the prefix 'm' instead of 'd', to change the region of memory shown in the lower 2/3rds of the debug panel.

 

And if you've gotten all the way to the end here, then yeah, I wrote basically all of this before seeing that string being dropped right in the middle of your assembly code. Rather than discard it all, I guess I'll just own up to my own failed spot check and hope the rest of this ends up being useful for someone at some point.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
  • 0

Matt, Stephen, many thanks for your responses.

I had no reason to choose $2C00 specifically, I just thought I'd choose a higher area of memory to keep clear of any basic I might type in to test it with.

I've moved the text string to the end of the code, and changed the header code to $0801, and checked in a hex editor that it compiles with the correct 2 header bytes, which it does.

I then loaded it up and peeked at $0801- , expecting to see the same bytes as the ones that followed the $01 $08 header, but got completely different numbers so it doesn't appear to be loading into $0801. Obviously, it's still not working when I sys $0801.

Any ideas on what I should check next?


 

Untitled-1.png

Untitled-2.png

Untitled-3.png

Share this post


Link to post
Share on other sites
  • 0

Your assembler is not building the PRG properly. I would suggest using ca65 instead: https://github.com/cc65/cc65

PRG studio is specifically for creating programs for Commodore computers, and while the X16 has some similarity to the VIC-20 and the C64, it is not the same. The whole cc65 suite (including the ca65 assembler) has support specifically for the X16, and it is the assembler that the dev team actually uses to build the ROM.

If you are building your program to be loaded at $0801, you don't need the ",8,1", it will be automatically loaded to the correct location.

What you should see at $0801 is the tokenized BASIC for "800 SYS2061" and the executable machine code should start at $080d, with a JMP to your actual code starting at $0810. And like Stephen and I both said, using the built-in debugger is the way to go. Printing and peeking is a lot harder. Just run with -debug and hit F12. Then you should be able to see your code by typing in "d 810".

  • Thanks 1

Share this post


Link to post
Share on other sites
  • 0

Thanks matt.

I'm still finding my way around the assembly compiling  world (can you tell!!) - I had just about got my head around how to produce PRG files with basic and assembly all in one file from the CBM PRG studio app, but I guess it'll be better in the long run if I do the sensible thing and port my brain over to ca65 😉

I found the download zip option on the github page, and downloaded and unzipped the cc65-master.zip. There's a ton of folders in there, and I've been rummaging around trying to find the ca65 assembler but I can't seem to find any "ca65.exe" or something to actually run..  I feel really bad asking you noob question after noob question, so I googled "how do I compile assembly using ca65". Then wished I hadn't!  I waded through pages and pages on cc65.org and stackoverflow, but they just baffled the pants off me - I had no frame of reference for anything they were talking about, I had no idea where to even begin.

I'm thinking now perhaps ca65 is for "proper" programmers who understand what the hell libs, includes and makefiles are, and maybe I'm biting off more than I can chew - I'm going to go batter my head against CBM PRG studio for a while longer - at least I know I can output code from that -  I've got the -debug monitor thing working, and that's really handy, Stephen's tip for adding a break point so you can look at what the code is doing step by step is brilliant.  Thanks so much for your help, sorry I'm a bit useless! 😄 

 

 

Share this post


Link to post
Share on other sites
  • 0

You need to compile using the Makefile, then it will be in the bin directory. Or, you can download a precompiled release

  • Thanks 1

Share this post


Link to post
Share on other sites
  • 0
11 hours ago, StephenHorn said:

Well, this is probably pretty easy. You're dropping a string right into the middle of your code, and I'll bet CBM PRG studio isn't politely shuffling that to the end of your program like a segmented assembler would. I don't actually know anything about CBM PRG studio, and there didn't appear to be any online documentation, but the author seems insistent that his program is not an assembler, so I'm guessing it doesn't have the smarts to work that out, by design. ¯\_(ツ)_/¯

Try moving that string to some point after the rts instruction at the end there.

 

 

If that's not it, then I agree with @SlithyMatt that the most suspect thing is that *=$2C00, which is setting the instruction pointer to $2C00 and will, under most assemblers, cause it to begin emitting instructions and addresses as if the following portion of the file began at that address.

There are a bunch of things to check to follow this rabbit hole.

Firstly, open your .prg file in a hex editor of some sort (I frequently go to https://hexed.it online for these purposes) and check that the first two bytes are $00 and $2C, in that order (this is $2C00 in little-endian order, which is what the X16 expects). If you're loading the file either through the -prg command-line option or with LOAD"FILENAME.PRG",8,1, then these bytes need to be correct or else the program is being loaded somewhere else, meaning you'll need to change your *=$2C00 to match wherever CBM PRG studio is indicating the file should be loaded.

Worst case, if you open the file and discover the starting bytes are your assembly instructions (A9 10 8D 22 9F A9 00 8D 20 9F 8D 21 9F...), then CBM PRG studio is not placing ANY header at all and you'll want to manually add one with a byte statement, probably byte $00, $2C or byte $00 $2C, depending on the specifics of its syntax.

If you're loading with LOAD"FILENAME.PRG",8,0 or LOAD"FILENAME.PRG", instead, the file will always be loaded into $0801 with a LOAD command. (That is, unless you're trying to poke memory to clobber this behavior, but to my knowledge that technique is completely undocumented on the X16 and probably subject to change with future revisions to the kernal, so I wouldn't personally try that).

The next thing to do would be to run the debugger with the -debug command-line option, in addition to whatever other command-line options you're currently using. Load the file, and then press F12. This should pause the emulator and bring up a panel like this:

x16-debugger.png.ef3d0edea647d72588d12f93fe13ff6b.png

From here, type d2c00 and press enter. This is similar to how the monitor worked on the C64, 'd' is a prefix meaning you want to change the address inspected by the disassembly view in the top-left of the debugging panel. At this point, the disassembly view should be showing your assembly instructions, at $2C00. If you don't see your assembly there, the code got loaded somewhere else, and we need to go back to the beginning and figure out why it didn't get loaded where you think it did.

But if your code is there, you can now press F9 to set a breakpoint at $2C00. Debugger panel breakpoints will pause the entire emulator at the point of execution. I love them. ❤️ So at this point, press F5 to close the debugger panel and resume emulation. Try doing that SYS$2C00 call, and the emulator should automatically pop open the debug panel again, this time paused immediately before executing the instruction at $2C00. From here, you can inspect the state of the machine and step through your code, instruction by instruction, with F11 (step-into). Alternatively, you can step with F10 (step-over), the difference is that F10 will run a jsr and all the code that executes after it, until immediately after the matching rts instruction is executed.

If you need to inspect memory instead of disassembled code, you can use the prefix 'm' instead of 'd', to change the region of memory shown in the lower 2/3rds of the debug panel.

 

And if you've gotten all the way to the end here, then yeah, I wrote basically all of this before seeing that string being dropped right in the middle of your assembly code. Rather than discard it all, I guess I'll just own up to my own failed spot check and hope the rest of this ends up being useful for someone at some point.

these -debug tips are great, thanks very much Stephen. I've managed to get my code loading into the correct place and doing what it should do -  loops 12 times, finds the "0" and rts's. But the two sta's to vera_data0 are putting nothing on screen, so I've got somewhere to start debugging! 

  • Like 1

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

I can appreciate that cc65 may seem a bit arcane if you're not used to command line programming workflows, and especially so if you downloaded its source code and first need to compile that. Myself, I found it convenient to download their Windows snapshot on https://cc65.github.io/. The Windows snapshot comes already built into executables. They can be found in the /bin/ folder of wherever you extract the snapshot zip file. The main one we care about is cl65.exe. It's smart enough on its own to interpret a text file full of assembly instructions, and it's the program that spits out the final .prg. You can tell it to assemble a bunch of code into a .prg file by calling:

cl65.exe -o print.prg -DC64 --cpu 65C02 print.asm

If you open this in a hex editor, you'll probably see the expected $01 $08, but then the stuff immediately after that is probably going to be a mystery. This is because cc65 creates "auto-launching" programs. I've previously explained how that gobbledy-gook works elsewhere, it turns out! https://github.com/commanderx16/x16-emulator/wiki/(ASM-Programming)-Creating-"auto-launching"-programs

The benefit of "auto-launching" programs is that you don't need to use SYS to call them. You can just use RUN, like you were starting a BASIC program, and the X16 will do the rest. If you checked out my link to the X16 github wiki (or just do LIST right after your LOAD), you'll see it's because the file is beginning with a 1-line BASIC program that does the SYS command on your behalf. Very slick technique, in my opinion. But if you're looking to write bespoke assembly functions to call from BASIC programs, pretty useless to you.

3 hours ago, SlithyMatt said:

Your assembler is not building the PRG properly.

Oh, you're just saying that because CBM PRG studio didn't add the additional header for making an "auto-launching" program, the way cc65 would. From the screenshot John provided of the hex dump of his file, it looks good to me. Proper file header for loading at $0801, the jmp instruction appears to point to the proper memory address, the lda for the string also appears to be referring to the proper location.

Then again, though, maybe you have a point. I made a clone of John's file, based on the screenshot he provided from his hex editor and tried loading it myself. BASIC is clobbering the first two bytes of memory to $08 $08 for some reason, deleting the lda $10 at the start of the program. I don't know why it's doing this. At a guess, I'm thinking it's scanning through memory to find what appears to be the first valid line of "BASIC" code, and then clobbering $0801 to point at that memory address. $0808 does suspiciously appear to be the first location in memory that would map to a BASIC token.

Edit: I should add, I tried renaming the file from a .prg to a .seq, in the hopes of avoiding this behavior, but no dice. BASIC still clobbered the first two bytes starting at $0801. This probably means $0801 is special, not just in the sense that it's the start of where BASIC stores programs, but also in the sense that BASIC enforces specific requirements on the contents of memory starting at $0801.

Edited by StephenHorn
  • Thanks 1

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

If my guess is correct and BASIC is scanning memory for the start of a BASIC program, then you'll have to add the auto-launching BASIC header to your program as well, John. See my link: https://github.com/commanderx16/x16-emulator/wiki/(ASM-Programming)-Creating-"auto-launching"-programs. You'd add that header immediately after the $01 $08 file header.

Edited by StephenHorn

Share this post


Link to post
Share on other sites
  • 0
3 hours ago, StephenHorn said:

I can appreciate that cc65 may seem a bit arcane if you're not used to command line programming workflows, and especially so if you downloaded its source code and first need to compile that. Myself, I found it convenient to download their Windows snapshot on https://cc65.github.io/. The Windows snapshot comes already built into executables. They can be found in the /bin/ folder of wherever you extract the snapshot zip file. The main one we care about is cl65.exe. It's smart enough on its own to interpret a text file full of assembly instructions, and it's the program that spits out the final .prg. You can tell it to assemble a bunch of code into a .prg file by calling:

cl65.exe -o print.prg -DC64 --cpu 65C02 print.asm

If you open this in a hex editor, you'll probably see the expected $01 $08, but then the stuff immediately after that is probably going to be a mystery. This is because cc65 creates "auto-launching" programs. I've previously explained how that gobbledy-gook works elsewhere, it turns out! https://github.com/commanderx16/x16-emulator/wiki/(ASM-Programming)-Creating-"auto-launching"-programs

The benefit of "auto-launching" programs is that you don't need to use SYS to call them. You can just use RUN, like you were starting a BASIC program, and the X16 will do the rest. If you checked out my link to the X16 github wiki (or just do LIST right after your LOAD), you'll see it's because the file is beginning with a 1-line BASIC program that does the SYS command on your behalf. Very slick technique, in my opinion. But if you're looking to write bespoke assembly functions to call from BASIC programs, pretty useless to you.

Oh, you're just saying that because CBM PRG studio didn't add the additional header for making an "auto-launching" program, the way cc65 would. From the screenshot John provided of the hex dump of his file, it looks good to me. Proper file header for loading at $0801, the jmp instruction appears to point to the proper memory address, the lda for the string also appears to be referring to the proper location.

Then again, though, maybe you have a point. I made a clone of John's file, based on the screenshot he provided from his hex editor and tried loading it myself. BASIC is clobbering the first two bytes of memory to $08 $08 for some reason, deleting the lda $10 at the start of the program. I don't know why it's doing this. At a guess, I'm thinking it's scanning through memory to find what appears to be the first valid line of "BASIC" code, and then clobbering $0801 to point at that memory address. $0808 does suspiciously appear to be the first location in memory that would map to a BASIC token.

Edit: I should add, I tried renaming the file from a .prg to a .seq, in the hopes of avoiding this behavior, but no dice. BASIC still clobbered the first two bytes starting at $0801. This probably means $0801 is special, not just in the sense that it's the start of where BASIC stores programs, but also in the sense that BASIC enforces specific requirements on the contents of memory starting at $0801.

thanks again for this Stephen - I think cc65 is a bit beyond me for the time being.

I've made it this far with CBM Prg studio so I'll stick with what I know. Using the hex editor and -debug tricks, I've worked out how to get the routine into the emulator and working. Only issue now is that it's displaying all the character 1 set characters...! I don't suppose you know how to switch to the secondary character set with the lower case letters? 😉 

Untitled-1.png

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

For various reasons, I've never bothered to swap the character set through assembly. But if you PRINT CHR$($0E) as a statement in BASIC before running your program, you can get the kernal to swap in the shifted PETSCII font that has both upper- and lower-case letters. (Doing this work in assembly could be much more involved-- if the character sets are not always kept in VRAM, then you'd need to copy them over, and I don't know where the kernal keeps the font data.)

Edit: And swapping back to uppercase-only is PRINT CHR$($8E).

Edited by StephenHorn
  • Thanks 1

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)
12 hours ago, JohnGill said:

thanks again for this Stephen - I think cc65 is a bit beyond me for the time being.

I've made it this far with CBM Prg studio so I'll stick with what I know. Using the hex editor and -debug tricks, I've worked out how to get the routine into the emulator and working. Only issue now is that it's displaying all the character 1 set characters...! I don't suppose you know how to switch to the secondary character set with the lower case letters? 😉 

 

There is a system function listed in the programmer's reference called screen_set_charset to do exactly this. LDA #3 and then JSR $FF62 will switch to PET upper/lower.

Edited by togster510
additional info
  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
  • 0

My 2 cents worth... do NOT load assembly code directly to $801, unless you have a SYS statement built in to the program.  Instead, either start your program with a SYS statement, or load to a higher address (such as $2000) like you're already doing. Ideally, you should change the top of BASIC pointers to protect some memory for your program, but I'm not going to get into that here.

I use 64TASS for 6502 and 65816 assembly. I'ts simple to use,doesn't require any complicated setup, and the whole program is 1 file. Just drop the EXE in the same directory as your assembly files and either assemble from the command line or create batch files for each project. 


Actually, there's a simple trick you can use, as long as you don't modify any BASIC lines once you've loaded your program. 

Start your program like this:

* = $801
; 10 SYS $810
DB $0D, $08, $0A, $00, $9E, $20, $24, $30, $38, $31, $30, $00, $00, $00, $00

... your code goes here

If you look at the anatomy of a BASIC program, it looks like this:
2 bytes: the start address of the next line of code. 
2 bytes: the line number (in 16 bit binary format)
n bytes: BASIC tokens (any value >= 128) or ASCII characters (values < 128)
00 end of line
... repeat for each line
00 end of program

Here's what it looks like in memory:

image.png.e04c7146cc5a2872731e65e42883bff4.png

So the DB line is actually:
0C 08 - the next line of code starts at $80C. Since this is the last line, $80C is just zeros.
0A 00 - This line number is 10. (You could make this anything you wanted, as long as it's a 16-bit integer.)
9E - SYS statement
20 - space
24 - ASCII $
38 31 30 - ASCII "810"
00 - end of line
00 00 - if you count the bytes, this should be location $80D. The 00 00 means "this is the end of the program."

If always start stand-alone ML programs with that sequence, you can then always start your programs with a simple RUN statement. Keeps things nice and simple.
 

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)
1 hour ago, TomXP411 said:

 

HI John - Taking in the comments above I've re-cut your code a little to work with CBM Prg Studio:

; 10 SYS (2064)

*=$0801

target TGT_C64

vera_bankstride = $9F22
vera_hibyte = $9f21
vera_lobyte = $9f20
vera_data0 = $9f23


        BYTE    $0E, $08, $0A, $00, $9E, $20, $28,  $32, $30, $36, $34, $29, $00, $00, $00

YourCode
        lda #%00010000
        sta vera_bankstride
        lda #$00
        sta vera_lobyte
        sta vera_hibyte

colour = #$0f

        ldx #$00
        ldy colour
loop
        lda string,x
        beq loopend
        sta vera_data0
        sty vera_data0
        inx
        bne loop
loopend
        rts

string
        text 'hello world'
        byte 0

 

Output is:

image.png.79f65dd4f3f5da324f0ee2481f4d16e3.png

 

Also CBM PRG Studio has a little helper piece for adding the basic line:

image.png.c9295f953c04cf30bf9d9c3919ab0837.png

 

image.png.5949b3f45c564b4221e5584ff005dedc.png

image.thumb.png.20b30f0053c513f328cdd0b66a233b19.png

Edited by Geehaf
  • Like 2

Share this post


Link to post
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.

Guest
Answer this question...

×   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.


×
×
  • Create New...

Important Information

Please review our Terms of Use