Login
You're viewing the social.afront.org public feed.
  • Stylusstylus
    Apr 24, 2026, 2:44 AM

    The BASIC dialect we're using here is derived from Tom Pittman's, and it uses an interpreted bytecode called TBIL to do most of the work of interpreting BASIC.

    As I think I mentioned elsewhere, I made a TBIL code generator for the asl assembler and successfully rebuilt the TBIL code.

    Now, this bears fruit: It made it say to add the SAY command. A few posts above, I was sending phoneme numbers as numbers.

    But now, I wrote a program that turns the phoneme list into a series of TBIL parsing instructions, so that you can write

    10 SAY "HE H3 L L O"
    20 SAY"K UH1 M P Y1 IU U T E R"
    30 END

    Comma and period are used for the two pause
    codes, rather than PA0 and PA1.

    It takes about 450 bytes of (ROM) code to parse the phonemes.

    I've been using this phoneme dictionary to look up words:

    mirrors.apple2.org.za/Apple%20

    doing English to phoneme translation would be Much Harder (TM)

    💬 1🔄 1⭐ 3

Replies

  • Stylusstylus
    Apr 24, 2026, 2:49 AM

    If an initial character has a lot of alternatives, they're checked in a subroutine:

        BC SAYNOTA "A"
    J SAYDOA ; ['AH2', 'AW1', 'AH1', 'AE1', 'AW2', 'A1', 'A2', 'AY', 'AH', 'AE', 'AW', 'A']
    SAYNOTA:

    later, SAYDOA checks starting with the 2nd character:

        bc SAYNOTAH2 "H2"
    lb 8
    j SAYDO
    SAYNOTAH2:
    ; ... etc, ending with the base case for the lone letter 'A':
    SAYNOTAW:

    lb 32
    j SAYDO

    If an initial character has exactly two alternatives, it checks them each explicitly:

        bc SAYNOTDT "DT"
    lb 4
    j SAYDO
    SAYNOTDT:

    bc SAYNOTD "D"
    lb 30
    j SAYDO
    SAYNOTD:

    If it has more, but few enough alternatives that you can BC across them (BC can jump up to +32 bytes), it has a first-character test but then inlines the other tests:

        BC SAYNOTT "T"
    bc SAYNOTTHV "HV"
    lb 56
    j SAYDO
    SAYNOTTHV:

    bc SAYNOTTH "H"
    lb 57
    j SAYDO
    SAYNOTTH:

    lb 42
    j SAYDO

    SAYNOTT:

    The argument to lb is the phoneme number, lb pushes it onto the operand stack as a byte.

    💬 1🔄 0⭐ 0
  • Stylusstylus
    Apr 24, 2026, 2:56 AM

    A benefit of assembling the 6800 code & the TBIL code from a single file is that you can refer to the address of 6800 code ("SAYCODE") from TBIL code:

        LN SAYCODE
    LN 0x3F
    DS
    US
    SS

    US is the TBIL opcode that implements the USR() function, which loads AB and X with some values and then jumps to an address (SAYCODE). Here, the arguments are 003F and 003F (duplicated by DS). The result is popped with SS.

    The alternative is allocating an entire TBIL opcode for the 6800 implementation of SAY. I didn't do this, but (especially if the opcode did the equivalent of j SAYDO) it might save ~120 bytes of TBIL code.

    Because I'm working with an obscene 32KiB of ROM I don't care about this :)

    💬 1🔄 0⭐ 1
  • Stylusstylus
    Apr 24, 2026, 6:19 PM

    Let's stick a ~20kB TTS dictionary in this thing.

    This required adding two TBIL opcodes (TALK, followed by a sequence of phonemes; and JX, 16-bit jump)

    1 REM SIMULATED 6800 + TINYBASIC + VOTRAX SC-01A + TTS DICTIONARY
    10 SAYE "HELLO"
    15 SAY "K UH1 M P Y1 IU U T E R"
    20 SAYE "I LIKE CHEESE. IT IS GREAT."
    30 SAYE "SOMEWHERE OVER THE RAINBOW"
    999 END

    A terminal session showing the program listing. When run, it speaks the text:

hello computer
I like cheese. it is great.
Somewhere over the rainbow.
    💬 1🔄 1⭐ 2
  • Stylusstylus
    Apr 24, 2026, 6:34 PM

    Someone had coded up a Python version of the votrax emulator (github.com/Echoshard/Votrax-Py) that used nltk & g2p_en to convert from English text to phonemes.

    I took a modified version of the XKCD list, added my own (terrible! broken!) stemming, and then turned it into a big ugly bunch of parse tables in TBIL.

    For instance, when we have previously parsed the initial "S", we arrive here and sequentially check for each word that starts with S:

    ESAYDOS:
    bc ESAYNOTSOMEWHERE "OMEWHERE"
    FCB $28, $1f, $15, $0c, $2d, $00, $ab ; ['S', 'AH1', 'M', 'W', 'EH3', 'R']
    jx ESAYMORE
    ESAYNOTSOMEWHERE:

    bc ESAYNOTSOMEBODI "OMEBODI"
    FCB $28, $1f, $24, $0c, $0e, $15, $1e, $3c, $a9 ; ['S', 'AH', 'M', 'B', 'AH1', 'D', 'E1', 'Y']
    jx ESAYMORE
    ESAYNOTSOMEBODI:

    My simulator's execution rate is not accurate to anything; when it's not waiting for input it's "as fast as possible". so for all I know, this approach is not fast enough on real hardware and I should have more levels of single letter checks. This would manifest as long pauses between words when running on hardware. If so, a 2nd single-letter lookup would be the obivous remedy...

    💬 0🔄 0⭐ 0