Posted on

Intro words

I've started port of Microsoft Basic V2 for developing computer Cerberus 2100.

It's totally open project that will be available for manual builds or for manufacturing by companies(there're no limitations). It have TWO main CPUs - Z80 and 65C02.

If Basic interpreter for Z80 CPU already exists(it was creates for prev. iteration of this computer - Cerberus 2080) - BBC Basic by Dean Belfield (aka Break Into Program). But 6502 part was without basic and I've tried make port.

editor

I had no 6502 experience before but I've tried and when I've got running and stable working in emulator basic - I've got no power to stop myself and started extending it.

executed code

Issues started

I've added CLS, LOCATE, low resolution graphics(PSET and LINE) and some other extensions and when I've started adding tiles(or user defined graphics) support - I've found that after adding additional command basic freezes on parsing code.

I've found that token table limited with indexed addressing(using register Y - just 255 bytes maximum, including ending zero-byte).

I had no plans extend basic to spaceship but I've wished have possibility add some tokens for simplifying usage for end users. So I've found solution and patched basic - about it in next part of article.

About fix

I will talk around my fork of this repo.

I think this method will be acceptable for any basic for 6502 that based on Microsoft Basic.

Important point to understand - I don't want limitless tokens, I just need a bit more but this method allows use twice larger table(up to 512 bytes).

Parser code

It located in program.s file. Most simple way locate it - label L2496. Here located iny instruction - increments register Y. This register used for reading token name table.

In nearest code we'll see:

        sec
        sbc     TOKEN_NAME_TABLE,y

It's used for comparing current character from input buffer and from token table.

What we should do? Introduce variable that will store flag of usage additional page of tokens and swap tables when increment will overflow register.

iny instruction will be replaced with call of this subroutine:

increment_token_list:
         iny
         bne @keep_same

         php
         pha

         lda EXTRA_TABLE_FLAG
         eor #$ff
         sta EXTRA_TABLE_FLAG

         pla
         plp
 @keep_same:
         rts

and code:

        sec
        sbc     TOKEN_NAME_TABLE,y

will become:

         bit EXTRA_TABLE_FLAG
         bmi @extra

         sec 
         sbc TOKEN_NAME_TABLE, y
         jmp @notuse
 @extra:
         sec
         sbc TOKEN_NAME_TABLE+$100, y 
 @notuse:

For stable work this routine we need just a bit more - single preparation. In routine labeled L248C we should found dey instruction(just before stx TXTPTR) and store Y value(it will be here $ff) to our EXTRA_TABLE_FLAG variable.

Just because in L2496 we'll call our increment routine and Y register will be zero and we'll switch to our initial token page.

Our next station is label L24DB. It's linked routine that looks for next token in our table.

iny will be replaced with increment_token_list call. But what we see next? Some kind of black magic with MATHTBL+28+1. It looks awful but honestly it's just TOKEN_NAME_TABLE - 1 address.

So, we should replace this LDA MATHTBL+28+1,y with routine that will read byte from our token name tables with offset -1 and LDA TOKEN_NAME_TABLE,y with direct reading.

;; Reads byte from one of the tokens table
 read_token_byte:
         bit EXTRA_TABLE_FLAG
         bmi @extra

         lda TOKEN_NAME_TABLE, y
         rts
 @extra:
         lda TOKEN_NAME_TABLE+$100, y
         rts

 ;; Reads prev. byte from token table
 read_token_prev_byte:
         bit EXTRA_TABLE_FLAG
         bmi @extra

         lda TOKEN_NAME_TABLE-1, y
         rts
 @extra:
         lda TOKEN_NAME_TABLE+$ff, y
         rts

After replacement increment and couple of load statement this part of code will look like:

L24DB:
         jsr     increment_token_list
         jsr     read_token_prev_byte
         bpl     L24DB
         jsr     read_token_byte
         bne     L2498
        ....

Now it parses, but LIST command crashes. And fix for it will be dead simple.

Look for label named L25F2 and just before it store Y register to our EXTRA_TABLE_FLAG variable(for selecting on first increment our initial table start).

Next you'll see couple times this lines of code(labels L25F5 and L25FD):

        iny
        lda TOKEN_NAME_TABLE,y

They should be replaced with:

        jsr     increment_token_list
        jsr     read_token_byte

Well, now we got working parser and list command.

And everything executes.

But how to add myown token to Basic?

If you want add your own command to basic you should use keyword_rts macros and add your new keyword to tokens.s file. Just before count_tokens.

For example it can look like:

...
		keyword_rts "CLS", clear_screen ;; Just call our BIOS/KERNEL code for cleaning screen
		keyword_rts "LOCATE", LOCATE
		keyword_rts "PSET", PSET
		keyword_rts "LINE", DRAW_LINE
		keyword_rts "TILEDEF", DEF_TILE
		keyword_rts "TILE", TILE
		count_tokens
...

Usually it's just classical assembly routine. If you'll need work with parameters - you can easily find required routines in other tokens code(for example, GETBYT - eval current argument to number that will be for 0 to 255, CHKCOM - check that next will be comma).

Simple commands can look like:

VER:
        lda     #<commit    ;; Here located string contains that basic built
        ldy     #>commit    ;; from commit XXXXXX
        jmp     GOSTROUT2

Happy hacking! Use it for good of all beings!