Page 5 of 15
Re: Neolithic Tiny Basic
Posted: Fri Feb 14, 2025 9:04 am
by barnacle
Code: Select all
isalpha:
pha ; (optional)
ora #$20 ; xlate upper to lower
clc
adc #$85 ; 'z' should be $ff now
cmp #$e6 ; only 7-bit ASCII alphas should set carry
pla ; (optional)
rts
Kudos, Mike, looks like you nailed it. That may be the canonical 'isalpha' - may I steal it please?
Thanks,
Neil
Re: Neolithic Tiny Basic
Posted: Fri Feb 14, 2025 4:10 pm
by barrym95838
Absolutely! Everything I post here is free to any good home that'll take it.
Re: Neolithic Tiny Basic
Posted: Fri Feb 14, 2025 6:49 pm
by barnacle
My thanks!
Once I've got the current issues with for/next sorted (can't decide whether searching for a matching next has any variables it needs to keep between recursive iterations) I'll drop it into the code and test it for real.
Neil
Re: Neolithic Tiny Basic
Posted: Sat Feb 15, 2025 6:41 pm
by barnacle
Dropped it in, it doesn't appear to have broken anything!
Meanwhile, a little macro abuse. For reasons I don't know, my assembler doesn't have an override (that I can find) that will let it generate three-byte absolute,x instructions when the address is less than $100. Since I use the x register as a pointer to the local stack, I need that ability whenever I store local variables on the stack.
In most cases, zero page addresses can be used, or shared, for local variables without overlap, but there are half a dozen routines which need to be called recursively and have more than one local 16-bit variable. Pushing and popping works well enough for a single variable, but is a pain once you get past there. It's also handy to be able to refer to a named variable... hence:
Code: Select all
; stack looks like:
; caller param 1 hi ; 0x0105,x
; caller param 2 lo ; 0x0104,x
; return address hi
; return address lo
; pushed x ; stack pointer copied to x
; param 1 hi ; 0x0100,x
; param 1 lo ; 0x00ff,x
; param 2 hi ; 0x00fe,x
; param 2 lo ; 0x00fd,x
; ... ...
LDAZXHI macro p1
db 0xbd
db lo (0x100 - ((p1 - 1) * 2))
db hi (0x100 - ((p1 - 1) * 2))
endm
LDAZXLO macro p1
db 0xbd
db lo (0xff - ((p1 - 1) * 2))
db hi (0xff - ((p1 - 1) * 2))
endm
STAZXHI macro p1
db 0x9d
db lo (0x100 - ((p1 - 1) * 2))
db hi (0x100 - ((p1 - 1) * 2))
endm
STAZXLO macro p1
db 0x9d
db lo (0xff - ((p1 - 1) * 2))
db hi (0xff - ((p1 - 1) * 2))
endm
And in use:
Code: Select all
PUSH $1234 ; caller private
PUSH $5678 ; caller parameter
jsr test
pla
pla
pla
pla
test:
first_p equ 1
second_p equ 2
phx
tsx
PUSH $9ABC ; first_p
PUSH $DEF0 ; second_p
lda CALLER_LO,x
lda CALLER_HI,x
LDAZXHI first_p
LDAZXLO first_p
LDAZXHI second_p
LDAZXLO second_p
lda #0
STAZXHI first_p
STAZXLO first_p
STAZXHI second_p
STAZXLO second_p
plx
plx
plx
plx
plx
rts
And a chunk of generated code
Code: Select all
PUSH $DEF0 ; second_p
ec0d : a9de > lda # hi $DEF0
ec0f : 48 > pha
ec10 : a9f0 > lda # lo $DEF0
ec12 : 48 > pha
ec13 : bd0401 lda CALLER_LO,x
ec16 : bd0501 lda CALLER_HI,x
LDAZXHI first_p
ec19 : bd > db 0xbd
ec1a : 00 > db lo (0x100 - ((first_p - 1) * 2))
ec1b : 01 > db hi (0x100 - ((first_p - 1) * 2))
LDAZXLO first_p
ec1c : bd > db 0xbd
ec1d : ff > db lo (0xff - ((first_p - 1) * 2))
ec1e : 00 > db hi (0xff - ((first_p - 1) * 2))
LDAZXHI second_p
ec1f : bd > db 0xbd
ec20 : fe > db lo (0x100 - ((second_p - 1) * 2))
ec21 : 00 > db hi (0x100 - ((second_p - 1) * 2))
LDAZXLO second_p
ec22 : bd > db 0xbd
ec23 : fd > db lo (0xff - ((second_p - 1) * 2))
ec24 : 00 > db hi (0xff - ((second_p - 1) * 2))
ec25 : a900 lda #0
STAZXHI first_p
ec27 : 9d > db 0x9d
ec28 : 00 > db lo (0x100 - ((first_p - 1) * 2))
ec29 : 01 > db hi (0x100 - ((first_p - 1) * 2))
(Just a chunk from the middle, for brevity, but to show all accesses)
Neil
Re: Neolithic Tiny Basic
Posted: Sat Feb 15, 2025 8:33 pm
by barnacle
Hmm, dunno why I called it LDAZXHI/LO. Perhaps LDAAXHI/LO...
Neil
Re: Neolithic Tiny Basic
Posted: Sat Feb 15, 2025 11:26 pm
by barrym95838
Using "negative" stack addressing can be useful but should be done with care, lest an untimely interrupt fires to stomp on your unprotected parameters.
Re: Neolithic Tiny Basic
Posted: Sun Feb 16, 2025 6:35 am
by barnacle
Yeah, I know. There aren't any interrupts running on this system, but if there were I don't think it's an issue: the local variables are all pushed onto the stack and popped off on exit, so (a) calls from within the routine work and (b) an interrupt happening at any time can use the stack safely; things carry on as normal when it returns.
Code: Select all
for:
; recursive, so allocate local storage on stack
paramto equ 1
for_name equ 2 ; only one byte but allocate two
this_line equ 3
this_where equ 4
where equ 5
phx ; save incoming stack frame
tsx ; grab the new stack frame
; int16_t paramto; // to
phx
phx
; char name; // and which variable is the counter?
phx
phx
; uint16_t this_line; // the line we start at
phx
phx
; char * this_where; // the place we start at
phx
phx
; where -= 4;
phy ; where is in y:a on entry
pha
sec
LDAZXLO where
sbc #4
STAZXLO where
bcs for_1
INCZXHI where
for_1: ...
And a big pile of plx to clean the stack up on exit.
The issue in general is that using the stack this way temporarily uses a huge amount of it; I may have to start again and use separate call and variable stacks. We'll see how deep the stack gets in practice.
As an aside: when this runs, I'm going to have to go through it line by line and rationalise the variable naming. I had hoped that this method would allow me to use the same variable name in different places by re-using equates, but nope, they can only be used once. Hmm... must not get distracted by writing an assembler
Neil
Re: Neolithic Tiny Basic
Posted: Sun Feb 16, 2025 7:28 am
by GARTHWILSON
It can be done, even re-using the same variable names for local variables. I tell about it in the middle of the page at http://wilsonminesco.com/stacks/loc_vars.html .
Re: Neolithic Tiny Basic
Posted: Sun Feb 16, 2025 8:25 am
by barnacle
Ah, I had thought from the documentation (AS65:
http://www.kingswood-consulting.co.uk/assemblers/) that 'equ' and 'set' did the same thing. But they don't...
complains about a redefinition, while
doesn't, and it appears to generate the expected code. Thanks for that!
The list of macros is increasing: I have a feeling I may rewrite (once it's working) so that many or most of the common usages use macros rather than discrete code. It's easier to maintain the 1:1 comparison with the C code that way, though it will likely need another pass to optimise, which I'm doing as I go along at the moment.
Code: Select all
; high and low byte versions of
LDAZXHI
STAZXHI
STYZXHI
ADCZXHI
SBCZXHI
STZZXHI
INCZXHI
DECZXHI
CMPZXHI
Neil
edit: I think there could still be issues if a global variable has the same name as a local, though that's a potential wherever and generally to be frowned upon.
Re: Neolithic Tiny Basic
Posted: Mon Feb 17, 2025 1:00 am
by BigDumbDinosaur
Using "negative" stack addressing can be useful but should be done with care, lest an untimely interrupt fires to stomp on your unprotected parameters.
All the more reason to move up to the 65C816.
Stack (and direct page) contretemps arising from an “untimely” interrupt are readily avoided when operating the 816 in native mode. Using the stack as a scratchpad is far easier, especially if direct page is temporarily relocated to the stack. In other words, why shovel snow by hand when a snowplow is readily available?
Re: Neolithic Tiny Basic
Posted: Mon Feb 17, 2025 3:39 am
by barrym95838
If you are honestly asking "Why", then you're missing a key reason for the existence of this forum, BDD. I could try to explain in further detail, but your well-worn preference to frequently promote an antiquated MPU over a slightly more antiquated MPU is beyond this youngster's ability to influence. The entire thread (up to now) has been exclusively about a 65C02 tiny BASIC, and your well-intentioned opinions are not universally unwelcome, but are also (sadly) not universally prescriptive.
Re: Neolithic Tiny Basic
Posted: Mon Feb 17, 2025 5:36 am
by barnacle
Well, why spend all that time trying to figure out the crossword when you could have all the answers tomorrow anyway?
Yes, I have railed against the 65c02's lack of a stack frame but that is not in itself sufficient to move to a different processor. I want to know whether _I_ can solve the problems in implementing my program using the hardware available to me in the 65c02. Converting my (working) C code using a 16-bit wide processor would, I am certain, be a lot simpler than it is with the 65c02 but that's not the issue at hand.
On the other hand, it's nice to know what options are available if I ever decide to use the 65c816 but it is not currently on my roadmap. At current progress rates, I can see it being at least a year before this project is finished, if ever.
Also: I don't see it as being difficult, or indeed risky, to use the stack for local storage. The need for the macros was to handle an assembler deficiency (they all produce similar three-byte instructions which can't otherwise be generated) and the benefit is that I can access named variables on the stack.
I don't see a real risk from interrupts of this approach. At all times the stack pointer is below the data in use, whether it's positioned by simply pushing (as I do here) or by manipulating the stack pointer directly (which is shorter than pushing only when you need more than four 16-bit variables).
The issue isn't one of 8-bit versus 16-bit, but the lack a stack frame pointer which could be indexed from. Allocate space for the variables, copy the stack pointer to the frame pointer (having saved the frame pointer because something above you might be using it) and all the variables, including those passed from the caller, are positive offsets from the frame pointer. But it doesn't have it, so the fun is working around that. Which is what I'm doing with X - not the only approach, but it works.
Neil
Re: Neolithic Tiny Basic
Posted: Mon Feb 17, 2025 7:38 am
by barnacle
Oh bother! I have been deceived by my assembler... it thinks there's an STY abs,x instruction and assembles it without warning:
which is of course the opcode for STY abs... a minor rethink of my plans is in order!
Neil
Re: Neolithic Tiny Basic
Posted: Thu Feb 20, 2025 9:38 am
by barnacle
Well we're getting closer, but not quite there yet
Neil
Re: Neolithic Tiny Basic
Posted: Sun Feb 23, 2025 8:52 pm
by barnacle
Finally... nested for-next.
Unfortunately it revealed a couple of further errors:
- print with no parameters should just print an empty line; it doesn't
- expression, as used by print, should be able to parse '(q*10)+r' but it throws an error (or print isn't dealing correctly with the open bracket)
Slowly, we progress...
Neil