Jump to content
IGNORED

classic99 questions


dhe

Recommended Posts

1 hour ago, TheBF said:

I can't figure this out.

 

I get this debug warning:

 

Warning: PC >A072 reading VDP with LIMI 2

 

When this code runs:


******************************************************
* Warning: PC >A072 reading VDP with LIMI 2
******************************************************
A070  A072  a    *R2+,R1      * THIS IS A POINTER. but the code runs
A072  0646  dect R6           * THIS IS CODE THAT RUNS  
A074  C584  mov  R4,*R6         
A076  02A4  stwp R4             
A078  A118  a    *R8,R4         
A07A  045A  b    *R10         * branch to Forth interpreter

* The Forth interpreter when the above is interpreted 
838A  C239  mov  *R9+,R8      * POP Instruction pointer (A070) to R8 w/autoinc
838C  C178  mov  *R8+,R5      * move contents (A072) of R8 to R5, w/autoinc
838E  0455  b    *R5          * branch to the code address (A072) in R5

Explanation Note:

The code at >A072 is computing the address of a "workspace" variable. ie: a variable that is referenced with the WP register so that difference contexts can have the same variable name but with unique values.  I got the same results when a simple variable ie: a routine that simply returned an address to R4,  occupied this address space >A070.

...

There seems to be no side effects except the error message.  I see no problems on real hardware but I don't have the tools to see the CPU in such detail on the TI-99.

Didn't we go over this in another thread some time ago?

 

So first off - assume that the reported PC might be late - it's reporting the program counter when the access happens... there's a good chance the instruction was already parsed and the PC incremented. Looking at the code, we can see that >A072 is unlikely, but >A070 looks pretty likely.

 

So is it right? Is the interrupt mask 2 when VDP is read at >A070? That's all the warning is telling you.

 

"There seems to be no side effects"... when you access the VDP with interrupts enabled, then 60 times a second there is a chance that the console interrupt will take control and potentially corrupt your VDP address. If your code takes a full millisecond to execute, then pure chance says that 15 times out of 16 it will run just fine. The nature of software when the VDP address unexpectedly changes means that there's a good chance you won't even notice as the end user about half the time, so now we're saying you won't notice at least 30 times out of 32. Most access blocks are faster than a millisecond, meaning the odds of actually being hit are even smaller. But race conditions are bad, and eventually, they always get hit.

 

The warning is in the emulator because it's bad practice, and you probably shouldn't do it. But if you do do it, then don't blame the emulator if it breaks - whether it breaks in emulation or on hardware. ;)

 

Of course, as implemented the warning isn't perfect. It doesn't check whether the VDP interrupt bit is enabled in the VDP for instance. It probably could also check the console interrupt control flags - I think if you turn off all the built in routines it's probably okay. But it's just safer to protect your VDP access. For a sense of what happens when you don't respect the interrupt, go look at some of the ColecoVision threads. On that machine the VDP interrupt is non-maskable, so pretty much every new programmer runs into strange corruption issues, and ends up working around them with sleeps and fragile balancing acts, hoping to win the race every frame.

 

But it's just a warning.

 

Link to comment
Share on other sites

3 hours ago, Tursi said:

Didn't we go over this in another thread some time ago?

 

So first off - assume that the reported PC might be late - it's reporting the program counter when the access happens... there's a good chance the instruction was already parsed and the PC incremented. Looking at the code, we can see that >A072 is unlikely, but >A070 looks pretty likely.

 

So is it right? Is the interrupt mask 2 when VDP is read at >A070? That's all the warning is telling you.

 

"There seems to be no side effects"... when you access the VDP with interrupts enabled, then 60 times a second there is a chance that the console interrupt will take control and potentially corrupt your VDP address. If your code takes a full millisecond to execute, then pure chance says that 15 times out of 16 it will run just fine. The nature of software when the VDP address unexpectedly changes means that there's a good chance you won't even notice as the end user about half the time, so now we're saying you won't notice at least 30 times out of 32. Most access blocks are faster than a millisecond, meaning the odds of actually being hit are even smaller. But race conditions are bad, and eventually, they always get hit.

 

The warning is in the emulator because it's bad practice, and you probably shouldn't do it. But if you do do it, then don't blame the emulator if it breaks - whether it breaks in emulation or on hardware. ;)

 

Of course, as implemented the warning isn't perfect. It doesn't check whether the VDP interrupt bit is enabled in the VDP for instance. It probably could also check the console interrupt control flags - I think if you turn off all the built in routines it's probably okay. But it's just safer to protect your VDP access. For a sense of what happens when you don't respect the interrupt, go look at some of the ColecoVision threads. On that machine the VDP interrupt is non-maskable, so pretty much every new programmer runs into strange corruption issues, and ends up working around them with sleeps and fragile balancing acts, hoping to win the race every frame.

 

But it's just a warning.

 

We did talk about it and I changed all my VDP routines and they never make trouble now.

This is different. 

 

What seems to be happening is somehow the DATA at >A070 is executing even though it is NOT code. It is DATA, a pointer to the next address. This is a function of indirect threaded code. 

And because the DATA looks like an Add instruction that auto increments R2,  it will randomly hit the VDP ports as R2 increments along through the memory.

BUT... it should never be executed. It is not code. :) It should never be executed.

 

So I can't figure out how to tell is it me or you. :) 

My sense is that if I had it wrong then the entire Forth system would collapse into a pile of schist because the whole thing is running by using this indirect threading mechanism.

 

Link to comment
Share on other sites

5 hours ago, Asmusr said:

What is the value of R2 at >A070?

It's random.  I am not using it at that point.

It is for VDP routines typically. 

 

But if I put one these workspace variables in a loop it will just increment endlessly.

Link to comment
Share on other sites

Okay, I understand what you are saying, but you didn't provide enough of the software for us to analyze the problem.

 

Put a breakpoint at >A070, and then you can see a trace of exactly how it got there. That usually solves the question. :)

 

  • Like 2
Link to comment
Share on other sites

4 hours ago, Tursi said:

Okay, I understand what you are saying, but you didn't provide enough of the software for us to analyze the problem.

 

Put a breakpoint at >A070, and then you can see a trace of exactly how it got there. That usually solves the question. :)

 

I put it there. :)   

The Forth compiler lays down a pointer to code as the first word in a routine. In the case of an assembler routine the pointer points to the next 16 bit word which where the machine code is.

In this case the pointer address >A072 happens to also be the instruction:  A *R2+,R1

I will get you the entire code and a memory dump as well. Standby...

 

 

Link to comment
Share on other sites

>A072 is not that instruction in the screenshot you posted...

 

Show me a screenshot on the breakpoint. >A070 is the address causing the warning, that you didn't expect to be executing. How did the PC get there, is the question I think needs to be answered.

 

Link to comment
Share on other sites

I think that you are getting confused by the details being imprecise, and letting your knowledge of the software make you disregard the evidence.

 

Based on what you posted, the CPU is /executing/ >A070, which happens to contain your pointer which happens to be >A072. But it's not /supposed/ to be executing >A070, so we need to disregard the fact that it points to the correct code in the very next instruction, and see why it's executing >A070.

 

Either that or I'm missing information. :)

 

  • Like 1
Link to comment
Share on other sites

I set the breakpoint at >A070 and pressed enter. This of course cause the "ok" prompt to be processed and so a call to my VSBW was needed.

Here is the full dump with comments to explain some internals to Camel99 Forth.

 

You can see a second example of the pointer to code at A080 in the dictionary structure.

It is a wicked mix of code and data so I have no idea how you would differentiate them.

 

Originally I had the two pieces of code swapped in memory and so my console was reading the cursor variable all the time and the "pointer"/code at >A070 ran continuously and I could see it traverse the memory in the heat map.

 

But as mentioned, it is not code. It is data.  I have not checked every assembler word in the system but they all live from A000 to A97E so a lot of the pointers will look like variations of the add instruction. I have watched some of the others execute so it's consistent.

 

And if it just a side effect of this weird language in Classic99 that's fine. I am just always unsure of my homemade compiler. 

A758  06C0  swpb R0                * piece of VDP write address setup
A75A  0300  limi >0000             * disable interrupts
      0000
A75E  D800  movb R0,@>8c02
      8C02
A762  06C0  swpb R0
A764  D800  movb R0,@>8c02
      8C02
A768  045B  b    *R11             * return from VDP write setup
A830  06D6  swpb *R6              * character is on the stack
A832  C836  mov  *R6+,@>8c00      * write to VDP
      8C00
A836  0300  limi >0002            * Just like Tursi said :)
      0002
A83A  C136  mov  *R6+,R4          * refill top of stack cache register
A83C  045A  b    *R10             * return from VSBW
838A  C239  mov  *R9+,R8          * Forth interpreter  *IP+ -> W
838C  C178  mov  *R8+,R5          * *W+ -> temp
838E  0455  b    *R5              * branch *temp
A070  A072  a    *R2+,R1          * !!!NOT CODE!! POINTER TO CODE
>  A072  0646  dect R6            * make room on stack
A074  C584  mov  R4,*R6           * push TOS cache onto stack. R4 now free to use
A076  02A4  stwp R4               * get the WP register into R4
A078  A118  a    *R8,R4           * *R8 holds offset of workspace variable. Add to WP
A07A  045A  b    *R10             * branch to Interpreter, >838A through R10
* next forth word definition in "dictionary"
A07C  A069  a    @>0005(R9),R1    * DATA: link to previous Forth word
      0005                        * DATA: IMMEDIATE BYTE, STRING LENGTH
A080  444F  szc  R15,*R1          * TEXT: "DO"
A082  5641  szcb R1,*R9           * TEXT: "VA"
A084  52FF  szcb *R15+,R11        * TEXT: "R", FF (alignment byte)
A086  A088  a    R8,R2            * DATA: Pointer to code (like A070)
A088  0646  dect R6               * make room on stack
A08A  C584  mov  R4,*R6           * push TOS register
A08C  C108  mov  R8,R4            * Get address of a variable -> TOS

 

CAMELFORTHA070.png

Link to comment
Share on other sites

Quote

 


You can see a second example of the pointer to code at A080 in the dictionary structure.

It is a wicked mix of code and data so I have no idea how you would differentiate them.
 

 

 

Well, Classic99 won't disassemble data statements, only statements the CPU has executed, so that helps. ;)
(edit: well, if they are above the cursor, anyway ;) )

 

Your breakpoint is on /access/ to >A070, not execution... put no prefix at all on it.

 

That said, the execution trace clearly shows that >A070 was executed as an instruction. You need to be starting your function at >A072.

 

From the code that's posted, I guess these are the lines in question:

838A  C239  mov  *R9+,R8          * Forth interpreter  *IP+ -> W
838C  C178  mov  *R8+,R5          * *W+ -> temp
838E  0455  b    *R5              * branch *temp

So in >838A, R9 was B284 -- we can't see what is there, but we know that it dumped it into R8 and must have got >AA86

We then moved from AA86 into R5, then jumped to it. 

 

We can see that R5 is indeed >A070.

 

Assuming the first two statements are correct, I think you probably meant to have one more indirection there:

mov *R8+,R5     * *W+ -> temp
mov *r5,r5      * get function pointer from temp
b *r5           * and branch to it

 

Edited by Tursi
Link to comment
Share on other sites

4 minutes ago, Tursi said:

 

 

 

Well, Classic99 won't disassemble data statements, only statements the CPU has executed, so that helps. ;)

 

Your breakpoint is on /access/ to >A070, not execution... put no prefix at all on it.

 

That said, the execution trace clearly shows that >A070 was executed as an instruction. You need to be starting your function at >A072.

 

From the code that's posted, I guess these are the lines in question:


838A  C239  mov  *R9+,R8          * Forth interpreter  *IP+ -> W
838C  C178  mov  *R8+,R5          * *W+ -> temp
838E  0455  b    *R5              * branch *temp

So in >838A, R9 was B284 -- we can't see what is there, but we know that it dumped it into R8 and must have got >AA86

We then moved from AA86 into R5, then jumped to it. 

 

We can see that R5 is indeed >A070.

 

Assuming the first two statements are correct, I think you probably meant to have one more indirection there:


mov *R8+,R5     * *W+ -> temp
mov *r5,r5      * get function pointer from temp
b *r5           * and branch to it

 

 

 

Your breakpoint is on /access/ to >A070, not execution... put no prefix at all on it.

When I put no prefix on >A070 it does not break. 

But when I set it for >A072 , No prefix, it stops.  

 

Here is the code that was running. It is a low level routine to write a character to the screen.

The thing it is doing at the break point is getting the address of VCOL, the video column "workspace" variable.

 

I don't think another indirection will work.  This is the same interpreter used by FbForth and TurboForth and is a direct translation of the formula written up by Brad Rodriguez in moving forth. The 9900 is almost the perfect instruction set to write his example so it's pretty straightforward.

 

 In the screen capture you can see the "list" of addresses that are run by the interpreter.

They begin at >B280

839E  is the address of "enter"  a list of addresses

A812  is the address of VPUT, assembler primitive to put a char on the screen

AA86 is the address of VCOL, which we are trying to understand.

 

I also decompiled (EMIT) so we could verify that the second address is the workspace variable in question.

 

Just tell me when to stop. :)  I am enjoying the discussion but everything works fine.

 

 

 

 

DECOMPILE-EMIT.png

Link to comment
Share on other sites

21 minutes ago, TheBF said:

I don't think another indirection will work.  This is the same interpreter used by FbForth and TurboForth and is a direct translation of the formula written up by Brad Rodriguez in moving forth. The 9900 is almost the perfect instruction set to write his example so it's pretty straightforward.

Well, it's pretty clear in your screenshot. R5 contains >A070, and your last executed instruction that uses it is B *R5. The trace shows that >A070 was executed, it even tells you how many cycles it took. You insist that >A070 is not supposed to be executed, it's supposed to be a pointer to the function at >A072. I know how to read the debugger I wrote, and I know 9900 assembly. ;)

 

I also know this particular trap is easy to get screwed up on, as I just spent a full month fighting it in my music player. The 9900 doesn't do indirection intuitively, that extra lookup is needed.

 

Anyway, I've explained the debug message. I've analyzed your code. But until you try the suggestion that's enough work on my side. ;)

 

Link to comment
Share on other sites

Just now, Tursi said:

Well, it's pretty clear in your screenshot. R5 contains >A070, and your last executed instruction that uses it is B *R5. The trace shows that >A070 was executed, it even tells you how many cycles it took. You insist that >A070 is not supposed to be executed, it's supposed to be a pointer to the function at >A072. I know how to read the debugger I wrote, and I know 9900 assembly. ;)

 

I also know this particular trap is easy to get screwed up on, as I just spent a full month fighting it in my music player. The 9900 doesn't do indirection intuitively, that extra lookup is needed.

 

Anyway, I've explained the debug message. I've analyzed your code. But until you try the suggestion that's enough work on my side. ;)

 

I believe you completely.  

I will build a new one right now.

Link to comment
Share on other sites

I have read that there are only two hard problems in computer science.

Memory, naming and off by one errors.

 

This problem was of course me.

Forth cross-compilers are very manual.  A few of the key routines must be used before they are coded.

This means the programmer has to fill in their missing addresses later. (resolve forward references)

 

I was off by one memory word...

 

All the code generated by the compiler was correct. The human adjusted part... not so much. :) 

 

I guess I shouldn't feel too bad about having my a*s handed to me by the Harmless Lion. 

 

Thanks again Tursi for showing me the error of my ways.

 

(Now that the system is not running those extra nonsense cycles I gave it, it's even faster. )

 

 

  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...

I need to do two more things, to my classic99 configuration to really make me happy, and I was wondering if anyone had done these things already, and could point me in the right direction.

1) For XB, I'd really like to run Winfried Winklers XB3,

2) I'd like to replace GROM0 with Gary/OPS's SOB GROM.

 

Thanks,

Dano

  • Like 1
Link to comment
Share on other sites

You can't permanently replace GROM0 without recompiling Classic99 (or hex-editting it), so you would have to create cartridge images in Classic99.ini for every combination of GROM0 + cartridge you wanted to run. I suppose that's something for the future - configurable base system - but it's a ways out. ;)

 

XB3, if it doesn't work by just drag and drop onto the window, you would need to know the memory layout and create a Classic99.ini entry for it. I'm assuming it doesn't have any new hardware - I haven't seen it myself so I can't provide you that magic.

 

 

Link to comment
Share on other sites

Loading files from disk is the same in Classic99 as on the real machine. There are examples in the manual for various types of files.

 

Loading the DSK image itself - Select Disk, then the disk device you want to load (ie: DSK1), then the first line of the popup there will take you to a dialog, section 6.5 in the manual covers it.

 

Without more specifics it's hard to give a more detailed answer. 

Link to comment
Share on other sites

  • 4 months later...

Hi Tursi,

  With the release of TICodEd there is a reason why you might want to write to FIAD on the PC side, an execute the files in Classic99.

  Before I get all Assumptiony, I know the TI is really dumb, and really has no knowledge of file system states, except for what's in the PAB at any given moment.

   But if using Classic99, is there any 'statey' information stored while it is running, or will a reset back to the TI title screen be enough to render write to FIAD on the PC side harmless?

 

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