Jump to content

geek504

Members
  • Content Count

    94
  • Joined

  • Last visited

Posts posted by geek504


  1. My all-time favorite was the Apple ][+ with the 16K Language Card for 80-columns and a 20MB Apple Cider external hard disk. Most of my friends had a C64 and I loved the games but the overall user experience was definitely better with the Apple.

    I was then saving for a Commodore 128 but ended having enough for an Amiga 500. I loved it but now, in retrospect, I'd have preferred the Apple Macintosh IIfx if money was not an issue!

    But of course, the Commander X16 will trump them all... maybe not the IIfx but I like my memory map small... 64KB for the win!


  2. I've been scratching my head on these new control characters, more specifically TAB ($09) and BELL ($7). How does one use it in a PRINT CHR$() command? I even tried "activating" by enabling CHARSET ISO ON/OFF ($F).

    I am trying to figure out the "tab" or "," or actually print 10 spaces in my PRINT function or I need to make use of the KERNAL PLOT routine.

    Quote

    New Control Characters

    This is the set of all supported PETSCII control characters. Descriptions in bold indicate new codes compared to the C64:

    Code     Code
    $00 NULL - $80
    $01 SWAP COLORS COLOR: ORANGE $81
    $02 - $82    
    $03 STOP/RUN $83    
    $04 ATTRIBUTES: UNDERLINE HELP $84
    $05 COLOR: WHITE F1 $85
    $06 ATTRIBUTES: BOLD F3 $86
    $07 BELL F5 $87
    $08 BACKSPACE F7 $88
    $09 TAB F2 $89
    $0A LF F4 $8A
    $0B ATTRIBUTES: ITALICS F6 $8B
    $0C ATTRIBUTES: OUTLINE F8 $8C
    $0D REGULAR/SHIFTED RETURN $8D    
    $0E CHARSET: LOWER/UPPER CASE $8E    
    $0F CHARSET: ISO ON/OFF $8F  

     


  3. 13 hours ago, desertfish said:

    It makes the code generator itself a lot simpler however the generated code is quite inefficient in terms of 6502 assembly....

    @desertfish I started coding my compiler without a stack and while I can say it was efficient, it was just too slow (edit: to code and finish the compiler) and prone to bugs... I decided to implement the stack midway just to get the compiler ready and then re-implement the non-stack improvements later if at all. I'm guessing a rough 10-15% speed improvement and am not sure if it is worth the effort right now. 6502 assembly is inefficient by nature (but very simple to implement) especially if we write proper assembly code to preserve A, X, and Y, wasting bytes and cycles with PHA, PHX, PHY, and PLY, PLX, PLA prior to every subroutine. I do feel that this is still more efficient than cc65's C-stack implementation though.

    I am not worrying too much about maximum efficiency (I don't think we will ever get close to super tight assembly code) because I hope one day in the future we will be able to crank up the X16's MHz to GHz range!

    At least in the emulator scene, we can implement the following 6502 JIT-core to x64 which could bring to a realistic 12GHz!

    https://scarybeastsecurity.blogspot.com/2020/04/clocking-6502-to-15ghz.html?m=1

    beebjit_15GHz.png.e4ed4ecc5eb06833cbbd73b194b1fa16.png


  4. 1 hour ago, desertfish said:

    It no longer uses the software-eval-stack for that.

    Hi! Is this "software-eval-stack" you mention a software-based stack used for mathematical computations based on a RPN-type stack? E.g. 2+3 becomes 2, PUSH, 3, PUSH, +? This is what I am using for my BASIC compiler. If so, it does do a lot of function calling and can be greatly optimized if one bypasses the stack entirely but involves major compiler modifications as you mentioned. This is a sample code from my compiler using macros that greatly improves readability:

    Quote


    10 REM ASSIGNMENT
    20 AX = 3 : REM NEED TO OPTIMIZE
    30 V = ABS(AX + 2)
    35 Q = 1 + 2 * 6 / 3
    40 PRINT "THE END"
    50 END

    start
      line
        10
        comment    REM ASSIGNMENT
      line
        20
        assignment
          AX
          3
        comment    REM NEED TO OPTIMIZE
      line
        30
        assignment
          V
          abs
            add_exp
              AX
              +
              2
      line
        35
        assignment
          Q
          add_exp
            1
            +
            mul_exp
              mul_exp
                2
                *
                6
              /
              3
      line
        40
        print    "THE END"
      line
        50
        end

    Tree('start', [Tree('line', [Token('INT', '10'), Tree('comment', [Token('COMMENT', 'REM ASSIGNMENT')])]), Tree('line', [Token('INT', '20'), Tree('assignment', [Token('VAR_ID', 'AX'), Token('INT', '3')]), Tree('comment', [Token('COMMENT', 'REM NEED TO OPTIMIZE')])]), Tree('line', [Token('INT', '30'), Tree('assignment', [Token('VAR_ID', 'V'), Tree('abs', [Tree('add_exp', [Token('VAR_ID', 'AX'), Token('ADD_OP', '+'), Token('INT', '2')])])])]), Tree('line', [Token('INT', '35'), Tree('assignment', [Token('VAR_ID', 'Q'), Tree('add_exp', [Token('INT', '1'), Token('ADD_OP', '+'), Tree('mul_exp', [Tree('mul_exp', [Token('INT', '2'), Token('MUL_OP', '*'), Token('INT', '6')]), Token('MUL_OP', '/'), Token('INT', '3')])])])]), Tree('line', [Token('INT', '40'), Tree('print', [Token('STRING', '"THE END"')])]), Tree('line', [Token('INT', '50'), Tree('end', [])])])

    .include "macros.inc"
    .include "header.inc"
    .code

    L10:        ; REM ASSIGNMENT
    L20:        PushInt 3
            PullVar AX
            ; REM NEED TO OPTIMIZE
    L30:        PushVar AX
            PushInt 2
            jsr ADD
            jsr ABS
            PullVar V
    L35:        PushInt 1
            PushInt 2
            PushInt 6
            jsr UMUL
            PushInt 3
            jsr UDIV
            jsr ADD
            PullVar Q
    L40:        LoadAddress S0        ; to r0
            jsr PrString
    L50:        rts

    S0:        .asciiz "the end"
    AX:        .res 2
    V:        .res 2
    Q:        .res 2

    .include "io.asm"
    .include "math.asm"
     

    As you can see, line 20 does a PUSH and a PULL to/from stack for a simple AX=3. It could have simply copied over the INT 3 directly into VAR AX. I'm planning in writing a post-compiler optimizer much later. Note that I couldn't use VAR A since A is a reserved keyword in ca65!


  5. On 11/3/2020 at 1:00 AM, Getafix said:

    Instead of .org $22 just use .zeropage.

    Out of curiosity, where does ".zeropage" start reserving space? Hopefully not starting from address $0000 since it is prone to corruption of important areas. For X16 it should start at $0022. I assumed it did but now am worried I may have ZP data corruption.


  6. I am trying to setup a few variables in ZEROPAGE using ca65 and the following does not seem to work...

    Quote

            .org $22    ; start of user ZP

    FPSP:       .byte 0        ; stack pointer = 0
    FPTEMP:     .res 4        ; FP temp
    SFL:        .res FPSTACKSIZE
    SFH:        .res FPSTACKSIZE
    SIL:        .res FPSTACKSIZE
    SIH:        .res FPSTACKSIZE

            .org $0801
        
            .byte $0C, $08
            .byte $0A, $00
            .byte $9E
            .byte $20
            .byte $32, $30, $36, $34
            .byte $00
            .byte $00, $00
            .byte $00, $00              ; Padding so code starts at $0810

            StoreImm $11ff, r0
            jsr PUSH
            StoreImm $2233, r0
            jsr PUSH
            StoreImm $5599, r0
            jsr PUSH
            StoreImm $6677, r0
            jsr PUSH
            StoreImm $2345, r0
            jsr PUSH

            jsr ADD
            jsr ADD
            jsr ADD
            jsr ADD
            jsr PULL
            jsr PrHex16
            PrintNewline

            StoreImm msg, r0
            jsr PrString
    END:        rts
    msg:        .asciiz "the end!"

    <snipped math routines>

    It seems multiple ".org" doesn't work?


  7. On 10/28/2020 at 5:57 PM, geek504 said:

    After checking Microsoft's implmentation as well as Woz's and third-party, I decided to use 16.16 Fixed-Point since most of the INTEGER part is done.

    For those who are curious:

    e = exponent, s = sign, m=mantissa, i = integer (used in fixed-point)

    Microsoft (5-bytes): eeee.eeee | smmm.mmmm | mmmm.mmmm | mmmm.mmmm | mmmm.mmmm

    Woz (4-bytes): seee.eeee | smmm.mmmm | mmmm.mmmm | mmmm.mmmm

    Bishop (4-bytes): siii.iiii | mmmm.mmmm | mmmm.mmmm | mmmm.mmmm

    My version (4-bytes): siii.iiii | iiii.iiii | mmmm.mmmm | mmmm.mmmm

    32-bit float has a 7 significant digits precision

    16-bit float has a 3 significant digits precision

    16-bit fixed mantissa has a 5 s.d.p. plus the significant digits from the INT

    The only major drawback of 16.16 fixed-point: it cannot do very very large or very very small numbers. Use ROM float-math for that!

    Bishop's version is very interesting because the INT part is only -128 to +127 only, BUT any number larger than +127 the fractional part starts to become negligible, i.e. ~0.78% error margin. In order to take advantage of that requires constant checking for the INT value to determine when to use the Bishop float or normal 16-bit INT. Bishop's fixed-point was used to generate fast mandelbrots in the Apple ][.

     


  8. 10 minutes ago, desertfish said:

    Can't you just perform the division using FDIV rom routine?

    The compiler is for Integer BASIC using 16-bit SIGNED INTs. After checking Microsoft's implmentation as well as Woz's and third-party, I decided to use 16.16 Fixed-Point since most of the INTEGER part is done. I just have to integrate the 16-bit MANTISSA part into my FP routines. This "integration" might cost a few execution cycles more but it should work well. Considering that fixed-point is faster to compute than floating-point, it's a small sacrifice. GPUs work like this as well, i.e. they use integer math along with fixed-point math with just the fractional part.

    When the compiler is done, I'll add the use of the ROM's floating-point math for serious work!

    My goal is to have a compiler that produces fast math at the expense of accuracy. It also uses 8-bit sine/cosine tables with 6% error margin. PI will be just 22/7 for fast computations. Let's see what this Frankenstein will look like in the end! In true Woz spirit, it is designed for game creation!

    • Like 1

  9. 2 hours ago, geek504 said:

    On another topic, does anyone have a handy algorithm to convert a fraction into a floating-point mantissa (binary form), for example:

    N=7, D=3 ==> N/D = 7/3 = 2R1

    I want to convert R/D (always less than 1) to the fractional part (mantissa) of a floating/fixed point number:

    R/D = 1/3 = 0.3333 ==> Result in Binary : 0.01010101

    After spending the afternoon going over binary division just for fun, I probably re-invented the wheel. In any case, here is the algorithm in Python form:

    Quote

    # Q16 factor for mantissa

    N = 1
    D = 7
    Q = []

    print("Binary Division: (N) " + "{0:b}".format(N) + "/" + "{0:b}".format(D) + " (D)")

    for i in range(16):

        N = N<<1

        print("Can D=" + "{0:b}".format(D) + " go into N=" + "{0:b}".format(N) + "?")

        if D>N:
            print("No")
            Q.append("0")
        else:
            print("Yes")
            Q.append("1")
            N = N - D
            if N == 0:
                break

        print("N={0:b}".format(N))

    print("Result: ", *Q)

    F = 0.0

    for i in range(16):
        if Q.pop(0) == '1':
            F = F + (1/(2**(i+1)))

    print("Fraction: ", F)
     

    Feel free to comment! Now I have to convert this to 6502 assembly 😵

    Sample run on 1/7 (which happens to be the remainder of the cheap PI value 22/7 = 3.14):

    Quote

    Binary Division: (N) 1/111 (D)
    Can D=111 go into N=10?
    No
    N=10
    Can D=111 go into N=100?
    No
    N=100
    Can D=111 go into N=1000?
    Yes
    N=1
    Can D=111 go into N=10?
    No
    N=10
    Can D=111 go into N=100?
    No
    N=100
    Can D=111 go into N=1000?
    Yes
    N=1
    Can D=111 go into N=10?
    No
    N=10
    Can D=111 go into N=100?
    No
    N=100
    Can D=111 go into N=1000?
    Yes
    N=1
    Can D=111 go into N=10?
    No
    N=10
    Can D=111 go into N=100?
    No
    N=100
    Can D=111 go into N=1000?
    Yes
    N=1
    Can D=111 go into N=10?
    No
    N=10
    Can D=111 go into N=100?
    No
    N=100
    Can D=111 go into N=1000?
    Yes
    N=1
    Can D=111 go into N=10?
    No
    N=10
    Result:  0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0
    Fraction:  0.142852783203125

     


  10. 48 minutes ago, SlithyMatt said:

    BCD is generally faster, and a more scalable process. Also, you can have a running counter that stays in BCD all the time, like a score, that can be quickly rendered as text on screen.

    Faster? A statement like var++ would be a simply matter of using INC and ADC for large numbers. BCD would require checking for the 9 digit and moving to 0 digit instead of A. Of course, 6502 does native BCD and that might be just as fast? BCD also uses more memory but I can see that BCD to string is much easier!

    On another topic, does anyone have a handy algorithm to convert a fraction into a floating-point mantissa (binary form), for example:

    N=7, D=3 ==> N/D = 7/3 = 2R1

    I want to convert R/D (always less than 1) to the fractional part (mantissa) of a floating/fixed point number:

    R/D = 1/3 = 0.3333 ==> Result in Binary : 0.01010101


  11. 7 hours ago, Stefan said:

    If I understand your code it's a repeated subtraction method.

    I've found some other interesting methods, which have worked nicely.

    Thanks for the links... I am always looking for cool 6502 algorithms to include in my support library!

    But, I just wanted to print Hex2Dec for the PRINT function... you're suggesting to first convert hex to BCD and then use BCD to print each digit? Would that be substantially faster than repeated subtraction?

    I confess, I never used BCD in my life. Can anyone illuminate the usefulness of BCD?


  12. 2 minutes ago, SlithyMatt said:

    I see it as no different than having a static variable inside a function in C. If you want to be evil, you can put executable instructions in there and call them, but it's not the greatest idea.

    I remember putting a shell code inside a string array... 😈

    • Haha 1

  13. 1 hour ago, SlithyMatt said:

    It's perfectly OK to declare variables in the code segment, as long as it's outside the path of execution, as it appears here. RAM is RAM, and everything has to go somewhere.

    LOL! As I was reading more webpages on algorithms for integer math and fixed-point math, I came across a text that mentioned that keeping data inside your code is in the realms of "self-modifying code"... woooo... makes us look like uber virus/worm programmers! Not that I ever wrote one during the 80s... 😈


  14. 1 hour ago, Ender said:

    I don't think you can declare variables in the middle of your code like that, at least not with cc65, unless you use segments.  The "PrDec16Tens" and "pad" declarations should be at the end.  From what I can tell, when pad is written to, it's overwriting the first command of PrHex8, changing it from PHA to "BMI $8fd".  Therefore, when you PLA later on the stack gets screwed up.  If you move those declarations to the end it works.

    Ender, thanks a million for your fresh view on the code analysis! You actually found the spot where the problem was and it was totally off my radar! It is possible to declare variable space within one's code from what I've experienced (one can really make a mess if one really wanted to!) The bug was...

    1 hour ago, geek504 said:

    PrDec16Tens:
                .word 1
                .word 10
                .word 100
                .word 1000
                .word 10000
    pad:        .res 0            ; default 0 = no padding

    ... pad like you said BUT it was because I was reserving ZERO byte(s) and thus overwriting PrHex8 as you mentioned. It is supposed to be .res 1 and it runs repeatedly without any problems. The ZERO was the remnant of my previous .byte 0 thinking about having a default value for pad, but the routine always write over this space so reserving was more accurate but forgot to change the 0 to 1.

    I wasted so much time thinking that I was corrupting .A, .X, .Y, or the many r0-r15 or simply bad assembly code. *sigh*

    • Like 1

  15. I am in the process of writing support assembly code for my BASIC compiler and am stuck with a particular BUG in the routine (PrDec16) that converts and prints an UNSIGNED INT into a DECIMAL STRING (There is also a routine, PrSgnDec16, that prints SIGNED INT which relies on it).

    When you compile and run the program, it seems to work BUT the side-effect of the bug is that when you type RUN again, it just clears the screen (it actually prints out something random before it clears). So if I wrote a larger program, it ends up clearing the screen preventing other things to be printed (or it crashed?).

    I spent two full days hungting for this bug but cannot find it! Can anyone see where the mistake is?

    Quote

            .org $0801
        
            .byte $0C, $08
            .byte $0A, $00
            .byte $9E
            .byte $20
            .byte $32, $30, $36, $34
            .byte $00
            .byte $00, $00
            .byte $00, $00              ; Padding so code starts at $0810

    CHROUT        = $FFD2
    PLOT        = $FFF0
    NEWLINE        = $0D
    UPPERCASE    = $8E

    r0    = $02
    r0L    = $02
    r0H    = $03
    r1    = $04
    r1L    = $04
    r1H    = $05
    r2    = $06
    r2L    = $06
    r2H    = $07
    r3    = $08
    r3L    = $08
    r3H    = $09
    r4    = $0a
    r4L    = $0a
    r4H    = $0b
    r5    = $0c
    r5L    = $0c
    r5H    = $0d
    r6    = $0e
    r6L    = $0e
    r6H    = $0f
    r7    = $10
    r7L    = $10
    r7H    = $11
    r8    = $12
    r8L    = $12
    r8H    = $13
    r9    = $14
    r9L    = $14
    r9H    = $15
    r10    = $16
    r10L    = $16
    r10H    = $17
    r11    = $18
    r11L    = $18
    r11H    = $19
    r12    = $1a
    r12L    = $1a
    r12H    = $1b
    r13    = $1c
    r13L    = $1c
    r13H    = $1d
    r14    = $1e
    r14L    = $1e
    r14H    = $1f
    r15    = $20
    r15L    = $20
    r15H    = $21

    ; Loads a 16-bit Word (immediate) to .A (lo-byte) and .X (hi-byte)
    ;
    .macro    LoadWordAX value
        lda #<value
        ldx #>value
    .endmacro

    ; Loads the 16-bit Word value at address to .A (lo-byte) and .X (hi-byte)
    ;
    .macro    LoadAX address
        lda address
        ldx address+1
    .endmacro

    ; Moves the 8-bit Byte from source to dest
    ;
    .macro MoveB source, dest
        lda source
        sta dest
    .endmacro

    ; Moves the 16-bit Word from source (lo,hi) to dest (lo,hi)
    ;
    .macro MoveW source, dest
        MoveB source+0, dest+0
        MoveB source+1, dest+1
    .endmacro

    ; Store the 16-bit Word in AX to address (lo,hi)
    ;
    .macro    StoreAX address
        sta address
        stx address+1
    .endmacro

    ; Store a 16-bit Word (immediate) to address (lo,hi)
    ;
    .macro    StoreImm value, address
        LoadWordAX value
        StoreAX address
    .endmacro

    ; Print a NEWLINE char
    ;
    .macro    PrintNewline
        lda #NEWLINE
        jsr CHROUT
    .endmacro

    START:        
            StoreImm $1234, r0    ; decimal = 4660
            jsr PrHex16        ; input at r0
            PrintNewline
            jsr NEG16        ; input at r0, output at r2
            MoveW r2, r0
            jsr PrHex16        ; prints EDCC (decimal = 60876)
            PrintNewline

            ldy #0            ; pad char, 0=none
            jsr PrSgnDec16        ; ### BUG, prints -4660 OK, but prog corrupted
    ;        jsr PrDec16        ; ### BUG, prints 60876 OK, but prog corrupted

    END:        rts

    ;-----------------------------------------------------------------
    ; FUNCTION: 16-bit Binary Negation (two's complement)
    ; INPUT:    r0
    ;
    ; OUTPUT:   r2 = 0 - r0 = ~r0 + 1 
    ;-----------------------------------------------------------------
    NEG16:      ; 16 bit Binary Negation: r2 = 0 - r0 = ~r0 + 1 (two's complement)
                SEC           ;Ensure carry is set
                LDA #0        ;Load constant zero
                SBC r0L       ;... subtract the least significant byte
                STA r2L       ;... and store the result
                LDA #0        ;Load constant zero again
                SBC r0H       ;... subtract the most significant byte
                STA r2H       ;... and store the result
                RTS

    ;-----------------------------------------------------------------
    ; FUNCTION: Print 16-bit decimal number
    ; INPUT:    r0 = value to print, copied to scratch r11
    ;           .Y  = pad character
    ;           (e.g. '0' #48 or ' ' #32 or #0 for none)
    ;-----------------------------------------------------------------
    PrSgnDec16:
            phy            ; save .Y = padding char
            lda r0H            ; load MSB
                and #$80        ; check for sign bit of MSB
                beq LL1            ; positive, print normally
                jsr NEG16        ; negative, NEG16 result at r2
                lda #'-'        ; print '-' sign
                jsr CHROUT
            MoveW r2, r0        ; load new positive value
    LL1:
            ply            ; restore .Y

    ;-----------------------------------------------------------------
    ; FUNCTION: Print 16-bit decimal number
    ; INPUT:    r0 = value to print, copied to scratch r11
    ;           .Y  = pad character
    ;           (e.g. '0' #48 or ' ' #32 or #0 for none)
    ;
    ; INPUT:    at PrDec16Lp1
    ;           Y=(number of digits)*2-2, e.g. 8 for 5 digits
    ;
    ; OUTPUT:   A,X,Y corrupted
    ;-----------------------------------------------------------------
    PrDec16:    
            STY pad            ; Save new padding character
            MoveW r0, r11
            LDY #8            ; Offset to powers of ten
    PrDec16Lp1:
            LDX #$FF            ; Start with digit=-1
            SEC
    PrDec16Lp2:
            LDA r11L            ; Subtract current tens
            SBC PrDec16Tens+0,Y
                STA r11L
                LDA r11H
                SBC PrDec16Tens+1,Y
                STA r11H
                INX                ; Loop until <0
                BCS PrDec16Lp2
                LDA r11L  ; Add current tens back in
                ADC PrDec16Tens+0,Y
                STA r11L
                LDA r11H
                ADC PrDec16Tens+1,Y
                STA r11H
                TXA                ; Not zero, print it
                BNE PrDec16Digit
                LDA pad            ; pad<>0, use it
                BNE PrDec16Print
                BEQ PrDec16Next
    PrDec16Digit:
                LDX #48                     ; ASC"0", No more zero padding
                STX pad
                ORA #48                     ; ASC"0", Print this digit
    PrDec16Print:
                JSR CHROUT
    PrDec16Next:
                DEY                ; Loop for next digit
                DEY
                BPL PrDec16Lp1
                RTS
    PrDec16Tens:
                .word 1
                .word 10
                .word 100
                .word 1000
                .word 10000
    pad:        .res 0            ; default 0 = no padding

    ;-----------------------------------------------------------------
    ; FUNCTION: Print 8-bit hexadecimal number
    ; INPUT:    .A = value to print
    ;
    ; OUTPUT:   
    ;-----------------------------------------------------------------
    PrHex8:     ; Print value in A in hexadecimal padded with zeros, 
                PHA      ; Save A
                LSR A    ; Move top nybble to bottom nybble
                LSR A
                LSR A
                LSR A
                JSR PrNybble               ; Print this nybble
                PLA                        ; Get A back and print bottom nybble
    PrNybble:
                AND #15                    ; Keep bottom four bits
                CMP #10                    ; If 0-9, jump to print
                BCC PrDigit
                ADC #6                     ; Convert ':' to 'A'
    PrDigit:
                ADC #48        ; ASC"0"
                JSR CHROUT     ; Convert to character and print
                RTS

    ;-----------------------------------------------------------------
    ; FUNCTION: Print 16-bit hexadecimal number
    ; INPUT:    r0 = Word to print
    ;           
    ; OUTPUT:   
    ;-----------------------------------------------------------------
    PrHex16:
            ;pha
            ;txa
                lda r0H
                jsr PrHex8
                lda r0L
            ;pla
                jsr PrHex8
                rts
     

    C:\dev\src>rm test.prg

    C:\dev\src>cl65 -o test.prg -t cx16 -C cx16-asm.cfg test.asm

    C:\dev\src>\x16-r38\x16emu.exe -prg test.prg -run

    test.asm


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


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


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

     


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


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

     

×
×
  • Create New...

Important Information

Please review our Terms of Use