Jump to content
SlithyMatt

"Hello, World!" with cc65

Recommended Posts

Quick question from you C64'ers... (I'm from Apple-land):

I found many references to the routines below:

CHROUT = $FFD2
STROUT = $AB1E

But in X16 Programmer's Reference: Commodore 64 API Compatibility

Quote

 

$FFD2: BSOUT – write character

$FFF0: PLOT – read/write cursor position

 

$FFD2 is given a different name BSOUT... why?

Why not use STROUT directly? Is it compatible with X16? It uses a different memory address... it's not ROM?

Is PLOT the routine used to set LOCATE (or HTAB/VTAB)?

Why did the thread's "Hello World" explicitly print a newline? Couldn't the NEWLINE be added to the string? Do you even need it?

   ; print newline
   lda #NEWLINE
   jsr CHROUT
   rts

Quote

$FFCF: BASIN – get character 
$FFE4: GETIN – get character from keyboard

Lastly, does BASIC's GET command use BASIN or GETIN? Is there a keyboard strobe that needs to be reset before reading in the character?

Edited by geek504

Share this post


Link to post
Share on other sites

From what I can tell, CHROUT and BSOUT are simply two different names for the same function.  Some references call it BSOUT, some CHROUT.  I'm not sure what "STROUT" is.  Kernal routines start with "FF" and on the X16 $AB1E would be in RAM, so no code would be there unless your program put something there.

For the other question, it looks like BASIC GET does a lot of things, but when I skimmed it looking for a kernal call, it seems that it calls GETIN, which simply removes and returns a character from the keyboard queue.

Share this post


Link to post
Share on other sites
24 minutes ago, Ender said:

Kernal routines start with "FF" and on the X16 $AB1E would be in RAM

After some Googling, I found that STROUT is found in BASIC ROM on the C64 ($A000-BFFF). It's part of the BASIC PRINT command that outputs a string.

Commander X16 BASIC ROM is found at $C000-FFFF in Bank #4. I need to find out the address of X16's STROUT and how to JSR to that address... maybe that FARJSR subroutine?

Further analysis of X16 BASIC ROM source code, I found the start of STROUT routine in ~\x16-rom-master\basic\code7.s but it doesn't give me an address.

Edited by geek504

Share this post


Link to post
Share on other sites
5 hours ago, geek504 said:

Why did the thread's "Hello World" explicitly print a newline? Couldn't the NEWLINE be added to the string? Do you even need it?

That's a limitation of the ca65 assembler. You can't have an inline control character in a string constant. So, you have to either do an awkward list that has a newline and then a null (.byte "Hello, World!", NEWLINE,0), or just do a newline print in the code.

  • Like 1

Share this post


Link to post
Share on other sites
8 minutes ago, geek504 said:

After some Googling, I found that STROUT is found in BASIC ROM on the C64 ($A000-BFFF). It's part of the BASIC PRINT command that outputs a string.

Commander X16 BASIC ROM is found at $C000-FFFF in Bank #4. I need to find out the address of X16's STROUT and how to JSR to that address... maybe that FARJSR subroutine?

Well, if the address is a spot within the BASIC code, then it's not a vector kernal routine, therefore something not meant to be used by the user.  That said, it looks like it's $CD52 on bank 4 on the X16 (in the source code it's in basic/code7.s).  You can play around with it if you want, but it probably requires a very specific setup to actually use it.

Share this post


Link to post
Share on other sites
7 minutes ago, Ender said:

Well, if the address is a spot within the BASIC code, then it's not a vector kernal routine, therefore something not meant to be used by the user.  That said, it looks like it's $CD52 on bank 4 on the X16 (in the source code it's in basic/code7.s).  You can play around with it if you want, but it probably requires a very specific setup to actually use it.

Just like so... on a C64 which should be equivalent on the X16. How does one call a function in ROM BANK #4?

Quote

; hw.s: example using BASIC ROM routine to output a string

; cl65 hw.s -o hw.prg -C c64-asm.cfg -u __EXEHDR__

LOWERCASE       = $0E
CLRSCR          = $93
WHITE           = $05
LTBLUE          = $9A
STROUT          = $AB1E

        LDA #<msg
        LDY #>msg
        JMP STROUT

msg:
.byte LOWERCASE, CLRSCR, WHITE, "Hello World!", LTBLUE, 0

 

Edited by geek504

Share this post


Link to post
Share on other sites

Oh cool, well if you already have an example of how to use it, then it's worth a try.  You'd just change the definition of STROUT to $CD52, and make sure to change the ROM bank to 4 first.

Share this post


Link to post
Share on other sites

As the X16 will have flashable ROM, it's really a bad idea to try to call code inside the BASIC interpreter. You won't know what build someone is using, but the published Kernal API should be stable, so stick with that.

Share this post


Link to post
Share on other sites
2 minutes ago, SlithyMatt said:

As the X16 will have flashable ROM, it's really a bad idea to try to call code inside the BASIC interpreter. You won't know what build someone is using, but the published Kernal API should be stable, so stick with that.

I'm thinking about saving work, time, and RAM by using pre-made BASIC routines in one's assembly code, e.g. TAN, COS, *, \, PRINT, etc.

Once the BASIC ROM is stable enough, it should be safe to use. To avoid all this headache, maybe a JUMP TABLE would be a nice idea.

Eventually it will be used in my INTEGER BASIC compiler because it doesn't have floating-point math 🤣

Share this post


Link to post
Share on other sites
26 minutes ago, geek504 said:

Eventually it will be used in my INTEGER BASIC compiler because it doesn't have floating-point math 🤣

That's probably your best course of action. Have a nice, reusable piece of code that runs efficiently and then make it available to other languages.

Share this post


Link to post
Share on other sites
52 minutes ago, Ender said:

Oh cool, well if you already have an example of how to use it, then it's worth a try.  You'd just change the definition of STROUT to $CD52, and make sure to change the ROM bank to 4 first.

Yay, it works! How did you figure out the ROM address for STROUT from code7.s? Hex string search or an easier way?

Quote

;
; cl65 -o hw.prg -t cx16 -C cx16-asm.cfg hw.asm
;
; Run with x16emu.exe -prg hw.prg -run -scale 2
;
; hw.s: example using BASIC ROM routine to output a string


    .org $0801                  ; Assembled code should start at $0801
                                ; (where BASIC programs start)
                                ; The real program starts at $0810 = 2064

; 10 SYS 2064
    .byte $0C, $08              ; $080C - pointer to next line of BASIC code
    .byte $0A, $00              ; 2-byte line number ($000A = 10)
    .byte $9E                   ; SYS BASIC token
    .byte $20                   ; [space]
    .byte $32, $30, $36, $34    ; $32="2",$30="0",$36="6",$34="4"
    .byte $00                   ; End of Line
    .byte $00, $00              ; This is address $080C containing
                              ; 2-byte pointer to next line of BASIC code
                              ; ($0000 = end of program)
    .byte $00, $00              ; Padding so code starts at $0810


STROUT          = $CD52        ; BASIC in ROM BANK #4
VIA1        = $9F60        ; PB0-2 = 0-7

    LDA #4
    STA VIA1
    LDA #<msg
    LDY #>msg
    JMP STROUT
    LDA #0
    STA VIA1
    RTS

msg:
.byte "hello world! ", 0

 

Edited by geek504

Share this post


Link to post
Share on other sites
3 hours ago, geek504 said:

Yay, it works! How did you figure out the ROM address for STROUT from code7.s? Hex string search or an easier way?

If you've compiled the rom, it creates .sym files in "build/x16" for each module that has all the labels and their addresses. So if you look in "build/x16/basic.sym" there's one for strout.

Share this post


Link to post
Share on other sites

Personally, I just wrote my own little strout routine that only uses the CHROUT kernal routine at its documented location.  It's only a few instructions long. Any reason you really want to use basic's STROUT?

  • Like 1

Share this post


Link to post
Share on other sites
4 hours ago, desertfish said:

Personally, I just wrote my own little strout routine that only uses the CHROUT kernal routine at its documented location.  It's only a few instructions long. Any reason you really want to use basic's STROUT?

Mainly because I was thinking about economy of code, if it already exists in ROM, why not use it? Floating-point math also comes to mind.

But you're right, it's probably easier and much more compact to write our own PRINT function. I was already certain that a library would exist in HIGH RAM for my other functions such as 32-bit fixed-point math and Woz's 32-bit floating-point math.

The real reason I did it was because it's another way of doing HELLO WORLD and I learned a thing or two doing it!

Share this post


Link to post
Share on other sites

About the floating point math routines, yeah I gladly use the ROM for that.  I happily assumed the Cx16 has a proper documented fixed jump table for them (https://github.com/commanderx16/x16-rom/blob/0ebe2ab1556a0c46df41e5894ccae9b776c8a6a7/fplib/fplib.inc)  but perhaps this is still subject to change for newer ROM versions? I hope not 🙂

On the C64 we jump straight into BASIC ROM somewhere... but these won't change anymore

Share this post


Link to post
Share on other sites

I'm happy to share a preview of my INTEGER BASIC compiler written in Python using Lark for lexer and parser. Below shows the BASIC "hello world" program, followed by the AST, and the compiled code. Note the use of the virtual ABI r0 register to pass the string pointer to the PRINT routine:

spacer.pnghello.thumb.PNG.4c165c270ba342a10808d0492c27fc66.PNG

Hmm... that "CMP #0" in the PRINT routine can now vanish... forgot about it! As a matter of fact, "PHX" and "PLX" should be changed because I replaced the index from X-reg to Y-reg for (indirect), Y addressing mode.

Edited by geek504

Share this post


Link to post
Share on other sites

While we're talking about CC65... 

I'm writing some C code, but I'd like to call $FF50 -- which is the X16's "gettime" extended KERNAL routine.  It dumps time into the first five (or so) pseudo-registers in zero page, and that's useful to me.

But, for the life of me, I can't figure out (or remember) how to call that sub.  So I shove it in a variable, cast that to a function pointer, and call it?  I can't do that, because it's not a C function.  So..... how?

 

Share this post


Link to post
Share on other sites
48 minutes ago, rje said:

While we're talking about CC65... 

I'm writing some C code, but I'd like to call $FF50 -- which is the X16's "gettime" extended KERNAL routine.  It dumps time into the first five (or so) pseudo-registers in zero page, and that's useful to me.

But, for the life of me, I can't figure out (or remember) how to call that sub.  So I shove it in a variable, cast that to a function pointer, and call it?  I can't do that, because it's not a C function.  So..... how?

 

Greg King is the expert on this, but it looks like you can call clock_gettime()

Quote

int __fastcall__ clock_gettime (clockid_t clk_id, struct timespec *tp);

The code for it is here

https://github.com/cc65/cc65/blob/master/libsrc/cx16/gettime.s

  • Thanks 1

Share this post


Link to post
Share on other sites

Good enough, for now... gettime() of course returns the epoch timestamp.  Eventually I would like to call $ff50 directly.

Share this post


Link to post
Share on other sites
58 minutes ago, rje said:

Good enough, for now... gettime() of course returns the epoch timestamp.  Eventually I would like to call $ff50 directly.

Well, you can also set the time 😄  Unfortunately with the emulator, it forgets the date and time every time you close it and open it again.

Quote

int __fastcall__ clock_settime (clockid_t clk_id, const struct timespec *tp);

https://github.com/cc65/cc65/blob/master/libsrc/cx16/settime.s

 

Actually, that seems like a pretty doable feature for the emulator, to set the date and time when you start it to emulate a RTC...

Edited by Ender

Share this post


Link to post
Share on other sites

I was thinking about writing a "localtime()" function or something...  No reason I can't write it myself and submit a Merge Request.  The X16 will probably have a decent ABI, so it's worth at least seeing how easy/difficult it is.

 

Edited by rje

Share this post


Link to post
Share on other sites
38 minutes ago, Ender said:

Actually, that seems like a pretty doable feature for the emulator, to set the date and time when you start it to emulate a RTC.

I assume that people are waiting for "the powers that be" to choose which RTC chip will be mounted in the X16.

Share this post


Link to post
Share on other sites
13 hours ago, Greg King said:

 


#include <peekpoke.h>
...
asm ("jsr CLOCK_GET_DATE_TIME");
year = PEEK(0x02);
month = PEEK(0x03);
...

 

And so on.

Good point... I totally spaced on inline assembly.

Share this post


Link to post
Share on other sites

Has anyone implemented floating point routines in C, since CC65 doesn't support floats?

I've thought about supporting Commodore-style 5-byte floats, e.g.

typedef struct {
   int exponent: 8;
   int sign:     1;
   int ma4:      7;
   int ma3:      8;
   int ma2:      8;
   int ma1:      8;
} Float5;

(or even:
   int exponent :8;
   long mantissa;
)
	

 

Edited by rje

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


×
×
  • Create New...

Important Information

Please review our Terms of Use