Asmusr Posted July 6, 2020 Share Posted July 6, 2020 What is the value of R2 at >A070? Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 6, 2020 Share Posted July 6, 2020 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. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 7, 2020 Share Posted July 7, 2020 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. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 7, 2020 Share Posted July 7, 2020 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. Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 7, 2020 Share Posted July 7, 2020 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. 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 7, 2020 Share Posted July 7, 2020 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... Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 8, 2020 Share Posted July 8, 2020 >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. Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 8, 2020 Share Posted July 8, 2020 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. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 8, 2020 Share Posted July 8, 2020 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 Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 8, 2020 Share Posted July 8, 2020 (edited) 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 July 8, 2020 by Tursi Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 8, 2020 Share Posted July 8, 2020 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. Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 8, 2020 Share Posted July 8, 2020 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. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 8, 2020 Share Posted July 8, 2020 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. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 8, 2020 Share Posted July 8, 2020 The extra indirection did not work out of the box. Thanks for your patience and all your help. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 8, 2020 Share Posted July 8, 2020 I think my finger error was why it didn't break on >A070. I didn't regain focus on the Forth console window when I hit enter so of course it did nothing. Thanks again for this excellent emulator. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 8, 2020 Share Posted July 8, 2020 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. ) 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 8, 2020 Share Posted July 8, 2020 You're too kind. The important bit is you solved the issue! 2 Quote Link to comment Share on other sites More sharing options...
+dhe Posted July 30, 2020 Author Share Posted July 30, 2020 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 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 30, 2020 Share Posted July 30, 2020 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. Quote Link to comment Share on other sites More sharing options...
+arcadeshopper Posted July 30, 2020 Share Posted July 30, 2020 the only copy of this I have is an uber image.. Quote Link to comment Share on other sites More sharing options...
classicgamer74 Posted July 31, 2020 Share Posted July 31, 2020 Does anyone know how to load dsk files on classic99? Could you walk me through it? thank you!! Quote Link to comment Share on other sites More sharing options...
+arcadeshopper Posted July 31, 2020 Share Posted July 31, 2020 Does anyone know how to load dsk files on classic99? Could you walk me through it? thank you!!Did you read the manual? If not what what is it going to for us to tell you what it says in the manual when you can just read that Sent from my LM-V600 using Tapatalk Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 31, 2020 Share Posted July 31, 2020 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. Quote Link to comment Share on other sites More sharing options...
+dhe Posted December 14, 2020 Author Share Posted December 14, 2020 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? Quote Link to comment Share on other sites More sharing options...
Tursi Posted December 16, 2020 Share Posted December 16, 2020 Lots of people do that, including myself. For FIAD Classic99 only accesses the real disk file on OPEN (and CLOSE, if it was written to), so you are quite safe writing from the PC and reading from the emulator. No need to reset. 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.