Jump to content
IGNORED

Doing speech in C using TMS9900-GCC


xahmol

Recommended Posts

OK, that is seriously weird. I recompile without me being aware of changing anything, and now it works perfectly on real hardware. Still completely crashes in Classic 99 though.

 

It is actually working in https://js99er.net

See this recording using js99er:

Edited by xahmol
Link to comment
Share on other sites

  

19 hours ago, Tursi said:

But if it is actually /crashing/, that I want to know about.

 

Well, to be precise, it is not the Classic99 application that is crashing, I can still use all controls of the emulator. But the emulated machine within Classic99 is crashing/hanging.

See attached video (and please do not set sound too loud, as the sound is really annoying with a beep that also keeps hanging instead of the speech).

 

In the Classic99 Debugger I see this:

Speech halt triggered.
Speech halt cleared at 68620 cycles.
Speech halt triggered.
Speech halt cleared at 59680 cycles.
Illegal opcode (0008) at address >0012, Bank >0000, DSR >FFFF

 

Can include full log, but before that it is just a lot of loggings about the initialisation file loads and memcopies to VDP.

 

Included also the latest build in a ZIP file causing this (not a finished version, so please do not download/distribute other than investigating this behaviour in Classic99).

 

LudoTI994a-v199-20210328-1617.zip

  • Thanks 1
Link to comment
Share on other sites

I intend to try and fix the routine to watch the FIFO signals in the routine. Probably this evening...

 

But maybe it would be enlightening to know what anything causes execution to run off into illegal instructions... Either the program counter gets corrupted by some attempt to hold the CPU, or IDK... 

 

And I figured out why, for me, the 'Hello' in my example was not emitting, and only the second phrase... I needed a delay after the command to speak, as the talking status doesn't go high until the speech chip has gotten several bytes from the PHROM into the FIFO already. So I was just testing for it to be 'done' too soon. I slapped 3 delay_42() calls in and that solved it to wait until Hello was done before proceeding to the next external phrase.. So I'll try and fix that correctly too. 

 

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

void say_data(const char* addr, int len) {
  SPCHWT = SPCH_CMD_EXT; // say loose data in CPU RAM
  delay_asm_12();
  // Load upto the first 16 bytes
  int i = 16;
  while(i > 0 && len > 0) {
    SPCHWT = *addr++;
    len--;
    i--;
  }
  // Next check for buffer low, and add upto 8 bytes at a time
  while(len > 0) {
    SPEECH_BYTE_BOX = 0;
    while (SPEECH_BYTE_BOX & SPCH_STATUS_LOW == 0) {
      READ_WITH_DELAY();
    }
    // there is room for at least 8 bytes in the FIFO, so send upto 8
    i = 8;
    while(i > 0 && len > 0) {
      SPCHWT = *addr++;
      len--;
      i--;
    }
  }
}

The above code still doesn't work on classic99... works on hardware. But of course on hardware, I can't tell if it is working as intended or falling back on READY assertions.

 

Before I get to the second while loop, things go crazy, and the PC starts decrementing as I single step... It's funny... creates the appearance of the disassembly scrolling backwards... 

 

The code in github has shuffled around a bit to make a speech.h and speech.c that can be used independently of my main and sample data.  https://github.com/jedimatt42/fcmd/tree/jm42/say-toy/example/gcc/say

 

More investigation required. 

 

 

  • Thanks 1
Link to comment
Share on other sites

4 hours ago, jedimatt42 said:

The above code still doesn't work on classic99... works on hardware. But of course on hardware, I can't tell if it is working as intended or falling back on READY assertions.

 

Before I get to the second while loop, things go crazy, and the PC starts decrementing as I single step... It's funny... creates the appearance of the disassembly scrolling backwards... 

 

From just reading the code sample, I couldn't say why. It looks like what I remember the sequence that Parsec uses looks like.

 

"The PC steps backwards" doesn't make much sense though, that would require the Windows side to be corrupted...

 

 

Link to comment
Share on other sites

50 minutes ago, Tursi said:

I only tried Xahmol's copy, but, it runs fine here. It doesn't /sound/ right, but that's to be expected.

 

Are you guys doing something strange launching it? Something weird in config? (Willing to share the Classic99.ini?)

 

https://www.twitch.tv/videos/968681533

 

Weird. Installed it clean from the installation site on your website without any configuration but mapping DSK0.

 

And seriously weird: I just did a clean install with your .41 build. And voila, it works at first run similar to what you see.

But if I run again it crashes. If I run again, it crashes, but this time also with red lines in the screen. If I run it again, it still crashes but with different sounds. If I run it again, different type of crash again.

 

So it seems to be very inconsistent in what it does, with the only consistency is that I can make it run at first try after fresh install but never again after it seems.

 

So, could you have a look and see if you can get it to run more consistenly on your side?

 

Attached my classic99.ini.

But also just tried deleting the .ini and start with a fresh one, same results.

 

But maybe we should also test with a clean binary of Jedimatt42 to avoid that anything I did stupid in the rest of my code being the issue instead of the speech part (although my program does work in Classic99 before adding speech). I could for example imagine that the combination of your libtivgm2 librabry for music that I am also using next to Jedimatt42 speech code might complicate things with speech additionally.

 

 

classic99.ini

Edited by xahmol
Link to comment
Share on other sites

Linking the two probably won't have much impact - the small added latency of the VGM code shouldn't be enough to impact interrupt driven speech.

 

I did run multiple times - first to just try it, then to record it, then to record it again because audio was off... how are you resetting between runs? I used cold reset...

 

I don't have sufficient free time to be setting up compilers right now, so I'm only going to be able to work from compiled code.

 

  • Like 1
Link to comment
Share on other sites

13 minutes ago, Tursi said:

I used cold reset..

 

I used cold reset as well. Strange.

And understand lack of time, so any time so far is very appreciated. No time tonight myself as well, will try more tomorrow.

Otherwise I will just release it as two versions: with and without speech.

Link to comment
Share on other sites

speech-crash.zip

 

Inside speech-crash is the captured disasm.txt from classic99.. plus the TIFILES CRASH EA5 binary, and the source and generated assembly under the objects folder.

 

The tail end follows. A1BE to A1C2 are correct. But I don't know how you go from A1C2 to A1C0 with a dec R2 instruction:

 

(0) A1E0 ci   R2,>0000
(0) A1E4 jgt  >a1b2
(0) A1B2 clr  R1
(0) A1B4 movb R1,@>8320
(0) A1B8 movb @>8320,R1
(0) A1BC mov  R3,R1
(0) A1BE movb *R1+,@>9400
(0) A1C2 dec  R2
(0) A1C0 cb   R0,*R0
(0) A1BE movb *R1+,@>9400
(0) A1BC mov  R3,R1
(0) A1BA c    @>c043,R12

 

A1C0 cb R0,*R0 isn't actually in the code. That is just the parameter >9400 disassembled as the PC went backwards.

 

This looks like it is squarly in the 'say_data' routine, inside L25 by way of L26, meaning it has passed through that code once already:

 

        def     say_data
say_data
        li   r3, >6000
        movb r3, @>9400
* Begin inline assembler code
* 25 "../speech.c" 1
        NOP
        NOP
* 0 "" 2
* End of inline assembler code
        ci   r2, 0
        jlt  L27
        jeq  L27
        mov  r1, r3
        movb *r3+, @>9400
        dec  r2
        ai   r1, >10
L19
        ci   r2, 0
        jeq  L27
        movb *r3+, @>9400
        dec  r2
        c    r3, r1
        jne  L19
        ci   r2, 0
        jeq  L27
L25
        clr  r1
        movb r1, @>8320
        movb @>8320, r1
        mov  r3, r1
        movb *r1+, @>9400
        dec  r2
        mov  r3, r4
        ai   r4, >8
        jmp  L26
L29
        mov  r3, r1
L26
        ci   r2, 0
        jeq  L27
        mov  r1, r3
        movb *r3+, @>9400
        dec  r2
        c    r3, r4
        jne  L29
        ci   r2, 0
        jgt  L25
L27
        b    *r11
        .size   say_data, .-say_data
        even

 

  • Like 1
  • Thanks 2
  • Sad 1
Link to comment
Share on other sites

Fascinating, thanks for all that digging! Unfortunately I have two unplanned things to deal with today and tomorrow, so I may not be able to dig into it until Friday evening... but you do seem to have a solid reproduction.

 

(Edit: have it running in a tight loop on v41, using Xahmol's INI (changed the final jmp to loop)... no odd behaviour so far. I'll let it spin for a while.)

 

(Edit2: You /are/ crashing on 41, right? There were some changes to the halt handling back in January... although undoing those changes didn't make the behaviour happen either.)

Edited by Tursi
  • Like 1
Link to comment
Share on other sites

Yep. I was on v39, but upgraded to v41 last night. Retested just now to make sure.

 

--- 

More: The debug window shows:

 

Detected .\DSK1\CRASH as a TIFILES file
loading 0x27C bytes
Speech halt triggered.
Speech halt cleared at 145880 cycles.
Illegal opcode (0000) at address >2FCA, Bank >0000, DSR >FFFF

I have break on illegal opcode enabled... 

 

The symptom of the crash may be different when I'm not single stepping... I just let it run with no other breakpoints, and the disassembly landed here:

 

   A1BE  D831  movb *R1+,@>9400            (112)
         9400
   2FCA  0000  data >0000                  (10)
>  2FCC  0000  data >0000     

quite the random jump.

 

---

I'm not in a hurry... :) This is all hobby speed...

Edited by jedimatt42
Link to comment
Share on other sites

2 hours ago, Lee Stewart said:

This is probably inconsequential considering the coders here, but all reads (status, in this case) of the speech synthesizer are from scratchpad RAM, right?

 

...lee

Actually, I don't see the reads I would expect... It is possible they are getting optimized out by gcc... there should be BL @>8322  before the movb @>8320 statement.

 

I will review that.

 

Maybe it is still in blast mode ..

Link to comment
Share on other sites

Yep... damn... sorry, it was still in blast-mode... One of the things about gcc that I'm still not great at is preventing the optimizer from deciding my code is meaningless ( cause side-effects ) and just skipping it. 

 

There were places where it didn't skip it, so I was confident the read routine / macro to call was working. 

 

But in this loop, gcc doesn't know about the side-effect in the inline assembly code. I probably should have used an output register instead of a well known address. 

 

I can make my CRASH program work now in classic99... by properly waiting for the buffer low status.

  • Like 1
Link to comment
Share on other sites

CRASH  (the TIFILES EA5 PROGRAM all by its lonesome) this one doesn't crash. should say, poorly, "This is the one"

 

Having problems getting speech to work, and then fixing it feels like something of a right of passage on the 4A. 

 

https://github.com/jedimatt42/fcmd/blob/jm42/say-toy/example/gcc/say/speech.c   <-- updated say_data and support functions.

 

This difference: https://github.com/jedimatt42/fcmd/commit/3e1ed3e19de815c66070d481805d5fb8a48aa176

 

I was playing parsec today, in classic99, cause we know speech works from there, and I needed comforting.. LOL... And I finally noticed things like it is talking and I'm moving my ship, and all the 'enemies' are shooting at me, all simultaneously... Makes this chip even more of a wonder. Today speech is just brute forced by comparison. 

 

I need to break up the play_data routine to take a struct with the data pointers, length and cursor so it can be iteratively called to complete a pattern across cycles in a game loop.

 

Edited by jedimatt42
  • Like 2
  • Thanks 1
Link to comment
Share on other sites

Great! Many thanks! And almost feel guilty my question caused so much work.... just hope that also many others can benefit from it,

 

Will definitely incorporate this new version of the speech routines also in my game (but probably no time untill this weekend).

  • Like 1
Link to comment
Share on other sites

Great! Did a quick recompile, and it now also works in Classic99! Speech sounds better in js99er and on real hardware, but at least now it is functional and does not crash on Classic99.

Can now continue adding some more speech (while being careful not to overdo it, because I can imagine it can quickly irritate if you do too much I think).

 

Thanks all here!

 

Attached an OBS capture from Classic99.

 

 

2021-04-01 09-37-29.mkv

Edited by xahmol
  • Like 1
Link to comment
Share on other sites

3 hours ago, jedimatt42 said:

Today speech is just brute forced by comparison. 

True! It is amazing that sample I did for speech via PythonWizard is resulting in only 1017 bytes of data with the .WAV file it is derived from being 602214 bytes.

With the fidelity of it being amazing versus any of the other retro speech solutions I have encountered.

On my first try not doing anything yet to optimize the original recording or fiddling with the PythonWizard options.

Edited by xahmol
  • Like 4
Link to comment
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.
Note: Your post will require moderator approval before it will be visible.

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.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...