Jump to content
IGNORED

Assembly under emulation


Vorticon

Recommended Posts

Hee, I was going to say! It's not referenced anywhere else, not even in a DEF, so it never appears in the object file.

 

But yeah. Win994a didn't get quite enough development time. it's a shame, because with a few tweaks and bugfixes it would be truly amazing. It's still my assembler of choice, but I carefully study the listing file anytime anything wierd happens (that's not always enough either.)

 

Yeah. It's a great assembler, especially when you consider it was just an 'add on' to the main-course!

 

There are some undocumented features in the assembler. For example, IIRC, it supports conditional assembly and macros; they're just undocumented (and maybe unstable) so we don't know how to use them. If you look at the ASM994A exe in a hex editor you'll find references (IIRC) to IF THEN ELSE et al, so they're actually reserved key-words/directives in ASM994A, hence the full on tilt when encountered in your source code!

 

The IF/THEN/ELSE does work. I've used it in Cortex BASIC for conditional compilation for the TI-99 and my TM990 system. Note that the "IF", "THEN" and "ELSE" need to be indented by two spaces.

 

*************
TM990 EQU 1		 **** Set to 1 to assemble for the TM990
*************		 **** Set to 0 to assemble for the TI-99
IF TM990
ELSE *TI-99
 DEF START	 For the TI-99 so program can be run from E/A option 3.
 REF KSCAN	 TI-99 key scan routine.
 REF DSRLNK	 File I/O operations.
ENDIF

*CRU definitions.
EIA02 EQU >0040	 Main 9902 hardware base address.

*Memory mapped I/O definitions.
IF TM990
VRAMW EQU >E400	 VDP VRAM data write address.
VDPREG EQU >E402	 VDP VRAM address and register access address.
VRAMR EQU >E404	 VDP VRAM data read address.
VDPSTTS EQU >E406	 VDP status read address.
ELSE *TI-99
VRAMW EQU >8C00	 VDP VRAM data write address.
VDPREG EQU >8C02	 VDP VRAM address and register access address.
VRAMR EQU >8800	 VDP VRAM data read address.
VDPSTTS EQU >8802	 VDP status read address.
ENDIF

 

Stuart.

 

That is curious because my, rather, TI's offending code is

*** ELSE ***
   DATA L111F
L1120    DATA >C445,>4C53,>45A0
ELSE    DATA DOCOL,QCOMP,TWO,QPAIRS,COMPIL,BRANCH
   DATA HERE,ZERO,COMMA,SWAP,TWO,ENDIF,TWO
   DATA SEMIS

 

The ELSE is against the left margin.

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

It's the label "ELSE" on line 388 that's crashing it, it must be looking for some kind of conditional assembly operation. (Found by binary search - delete half the lines each time, see if it gets through assembly ;) ).

 

You can't even change it to something like "ELSE2", you have to change the word. Changing the first letter works, though, I tested as 'QELSE' ;) It's not referenced anywhere at all, so removing the label seems to let it build too.

 

It also doesn't like the RORG on line 330 of asmsrc2.a99 (though it tells you sort of why but puts the error on like 333). I had to put a number after it (RORG 0). that is definately not the right answer, since it started counting from 0 again and there was already data at zero. Might want to double check the usage of RORG and DORG in this one and then make sure the Asm994A output is what you expect (use the listing file).

 

Thanks for your input, Tursi!

 

I did discover the problem with RORG. Asm994A is wrong. RORG does not require an operand. However, if I put a label "_rlast equ $" just ahead of the "DORG 0" and then use "RORG _rlast", it gets the right value when it continues.

 

I have no choice regarding ELSE. That is the definition of the eponymous TI Forth word.

 

...lee

 

If I use "else" instead of "ELSE", it works. If the object file stores it as uppercase only, I'm OK. Otherwise, I will have to think of something else. :_(

 

...lee

 

Hm-m-m-m... Forth does not use the Assembly label to find the word. It uses the name in the DATA statement above it. It appears, as you say, Tursi, to be an idle label! :)

 

...lee

 

Yeah. That label will only come into play if you want to write Forth code in assembly. That is to say, pre-compiled Forth code, which is a list of entry points:

 

DATA OVER,DUP,LIT,42,ADD

 

etc...

 

In the case of ELSE, I would imagine pre-compiled Forth words are going to be written in terms of 0BRANCH rather than IF...ELSE...THEN, as that is how they would be compiled (having been translated by the immediate words IF...ELSE etc).

 

Virtually all of the resident Forth vocabulary in TI Forth is defined in ASMSRC and there are no DEFines or REFerences at all---only a few EQUates for the inner and outer interpreters and utilities. The object code is at 7282 bytes, so I have a little room to add to the resident vocabulary and can deal with any modified assembly labels I might need at that time. Of course, I have not studied your TurboForth code well enough to know what additional code I will need when I decide to try to hoist TI Forth into cartridge ROM. :ponder:

Link to comment
Share on other sites

Hi Lee

 

At a guess, you'll need code to:

  • Connect to DSRs (DSRLNK equivalent) (I presume the TI Forth authors relied on EAs DSRLNK)
  • Connect to GPL routines (GPLLNK)
  • Connect to ROM routines (XMLLNK)
  • Scan the keyboard

All in TurboForth, and can be found elsewhere. My main source of information for this low-level stuff was Theirry's web site. Feel free to pillage the TF source for anything that's useful. That's what it's there for! :thumbsup:

 

[Edit: TF only uses GPLLNK at startup (after you select the TF option from the menu) to load the character sets from GROM. After that, it's not used at all. Meh. All that, and the lower case characters are overwritten with true lower case anyway! I wonder if it was worth the effort!]

Link to comment
Share on other sites

Regarding labels and dictionary structure, I used a loose self-imposed convention in TurboForth.

 

Here's the implementation for DUP:

 

duph data droph

data 3

text 'DUP '

dup data $+2

mov *stack+,*stack

b *next

 

So,

  • duph = address of the beginning of this dictionary entry ('h' duph meaning 'header')
  • data droph = pointer to top of previous entry in the dictionary
  • data 3 = number of characters in this dictionary entry (also used for immediate and hidden flags, and also to encode the block number for the WHERE query)
  • DUP = the word name, with a trailing space to pad it to an even number of characters
  • dup = CFA. When assembled, dup is a label that points to the CFA of this word. Thus it can be used in data statements to call DUP.
  • DATA $+2 = Indirect threaded code, so points to the machine code. High level point to DOCOL in scratch-pad
  • b *next = B *R12. At the very start of the TF source you'll see a "next EQU R12" equate. Just helps the source code to be a bit more readable. I've also equated the PC (R3), the data stack (R4) and the return stack (R5) IIRC.

Link to comment
Share on other sites

Hi Lee

 

At a guess, you'll need code to:

  • Connect to DSRs (DSRLNK equivalent) (I presume the TI Forth authors relied on EAs DSRLNK)
  • Connect to GPL routines (GPLLNK)
  • Connect to ROM routines (XMLLNK)
  • Scan the keyboard

All in TurboForth, and can be found elsewhere. My main source of information for this low-level stuff was Theirry's web site. Feel free to pillage the TF source for anything that's useful. That's what it's there for! :thumbsup:

 

[Edit: TF only uses GPLLNK at startup (after you select the TF option from the menu) to load the character sets from GROM. After that, it's not used at all. Meh. All that, and the lower case characters are overwritten with true lower case anyway! I wonder if it was worth the effort!]

 

Mark...

 

Thanks for all this and what you wrote in subsequent posts!

 

The TI Forth authors wrote all their own links (DSRLNK, GPLLNK, XMLLNK, KSCAN, VSBW, VMBW, VSBR, VMBR and VWTR)! Only the Forth boot program (FORTH) relies on E/A's DSRLNK, VMBR, VMBW and VWTR to load the TI Forth system (FORTHSAVE) and set up VDP RAM before it hands off control to TI Forth. One primary reason for all the rewriting of the links was that the authors used E/A's utility space for TI Forth's 5 disk buffers. Once the boot program loads the system, it uses TI Forth's GPLLNK a couple of times before actually starting TI Forth. Once that happens, the E/A utility space is pretty much trashed. Obviously, for a cartridge system, I won't need the boot program to load from disk, so I should not need any of E/A's utilities that have not been written into the TI Forth system's support utilities. Rewriting TI Forth to work in a cartridge will be instructive for sure and, I hope, fun. I will undoubtedly need plenty of assistance from the experts on this forum!

 

...lee

Link to comment
Share on other sites

Regarding labels and dictionary structure, I used a loose self-imposed convention in TurboForth.

 

Here's the implementation for DUP:

 

duph data droph

data 3

text 'DUP '

dup data $+2

mov *stack+,*stack

b *next

 

So,

  • duph = address of the beginning of this dictionary entry ('h' duph meaning 'header')
  • data droph = pointer to top of previous entry in the dictionary
  • data 3 = number of characters in this dictionary entry (also used for immediate and hidden flags, and also to encode the block number for the WHERE query)
  • DUP = the word name, with a trailing space to pad it to an even number of characters
  • dup = CFA. When assembled, dup is a label that points to the CFA of this word. Thus it can be used in data statements to call DUP.
  • DATA $+2 = Indirect threaded code, so points to the machine code. High level point to DOCOL in scratch-pad
  • b *next = B *R12. At the very start of the TF source you'll see a "next EQU R12" equate. Just helps the source code to be a bit more readable. I've also equated the PC (R3), the data stack (R4) and the return stack (R5) IIRC.

 

This will certainly help me understand TF as I move forward to cartridge space; but, for now, at least, I will be trying to change as little of TIF as I can---its structure for a dictionary entry is definitely different from TF's and I do understand that pretty well.

 

...lee

Link to comment
Share on other sites

  • 1 year later...
I have a question concerning the interrupt service routine (ISR) in the console ROM. I tried to get an answer over in my fbForth thread here without much success. Maybe it'll get more traction in this thread.
In the following full excerpt of the console ISR from TI99/4A INTERN, Heiner Martin's comment on line 226 (CLR R8) is "Clear GROM search pointer"; but, the E/A Manual says the GROM search pointer is in the INTWS (at this point in the ISR, the GPL workspace is in use). Which one is correct? And, if the the E/A Manual is correct, why clear GPLWS R8 (>83F0) just before ending interrupt processing?
The reason I ask the question is that I am working on hoisting fbForth into cartridge space and I'm trying to reconcile what the TI programmers of TI Forth did (omitted that "CLR R8" line) with what the earlier TI programmers of the console ROM's ALC did by including it.

Interrupt routine

0900 0300 LIMI >0000            Disable interrupt
0902 0000
0904 02E0 LWPI >83E0            Load GPLWS!
0906 83E0
0908 04CC CLR  12               Clear CRU
090A 23A0 COC  @>0032,14        Cassette interrupt?
090C 0032
090E 1602 JNE  >0914            No, jump
0910 0460 B    @>1404
0912 1404
0914 1F02 TB   >0002
0916 1619 JNE  >094A            Jump, if VDP interrupt
0918 020C LI   12,>0F00         Clear CRU
091A 0F00
091C 1D01 SBO  >0001
091E 1E00 SBZ  >0000
0920 022C AI   12,>0100
0922 0100
0924 028C CI   12,>2000
0926 2000
0928 130E JEQ  >0946            End CRU
092A 1D00 SBO  >0000
092C 9820 CB   @>4000,@>000D    ROM exists
092E 4000
0930 000D
0932 16F5 JNE  >091E            No, next
0934 C0A0 MOV  @>400C,2         Intlnk?
0936 400C
0938 13F2 JEQ  >091E            No, next ROM
093A C002 MOV  2,0
093C C0A2 MOV  @>0002(2),2      Fetch INT address
093E 0002
0940 0692 BL   *2               And execute
0942 C090 MOV  *0,2             Next Int routine
0944 10F9 JMP  >0938
0946 0460 B    @>0AB8           End interrupt from CRU
0948 0AB8
094A 1D02 SBO  >0002            Clear VDP interrupt
094C D060 MOVB @>83C2,1         Fetch interrupt flag byte
094E 83C2
0950 0A11 SLA  1,1              No interrupt permitted
0952 1702 JNC  >0958
0954 0460 B    @>0A84           Then jump
0956 0A84
0958 0A11 SLA  1,1
095A 1846 JOC  >09E8            No sprite move permitted, then jump
095C D320 MOVB @>837A,12        Number sprites
095E 837A
0960 1343 JEQ  >09E8            No sprite end
0962 098C SRL  12,8
0964 0202 LI   2,>8800          VDP RD
0966 8800
0968 0203 LI   3,>8C00          VDP WD
096A 8C00
096C 0208 LI   8,>0780          Sprite motion table
096E 0780
0970 D7E0 MOVB @>83F1,*15       Write address motion table
0972 83F1
0974 D7C8 MOVB 8,*15
0976 04C4 CLR  4
0978 D112 MOVB *2,4             Datas Y velocity
097A 04C6 CLR  6
097C D192 MOVB *2,6             Datas X velocity
097E 0844 SRA  4,4
0980 D152 MOVB *2,5             Auxiliary datas
0982 0845 SRA  5,4
0984 A144 A    4,5
0986 D1D2 MOVB *2,7
0988 0846 SRA  6,4
098A 0847 SRA  7,4
098C A1C6 A    6,7
098E 0228 AI   8,>FB80          Address sprite descriptor table
0990 FB80
0992 D7E0 MOVB @>83F1,*15       Write address
0994 83F1
0996 D7C8 MOVB 8,*15
0998 04C4 CLR  4
099A D112 MOVB *2,4             Fetch position
099C A105 A    5,4
099E 0284 CI   4,>C0FF
09A0 C0FF
09A2 1209 JLE  >09B6
09A4 0284 CI   4,>E000          Compute new position
09A6 E000
09A8 1B06 JH   >09B6
09AA C145 MOV  5,5
09AC 1502 JGT  >09B2
09AE 0224 AI   4,>C000
09B0 C000
09B2 0224 AI   4,>2000
09B4 2000
09B6 04C6 CLR  6
09B8 D192 MOVB *2,6
09BA A187 A    7,6
09BC 0268 ORI  8,>4000          VDP address for writing
09BE 4000
09C0 D7E0 MOVB @>83F1,*15
09C2 83F1
09C4 D7C8 MOVB 8,*15
09C6 D4C4 MOVB 4,*3             Write positions
09C8 0228 AI   8,>0482
09CA 0482
09CC D4C6 MOVB 6,*3
09CE 06C5 SWPB 5
09D0 D7E0 MOVB @>83F1,*15       Write address motion table
09D2 83F1
09D4 D7C8 MOVB 8,*15
09D6 0945 SRL  5,4
09D8 D4C5 MOVB 5,*3             Write auxiliary values
09DA 06C7 SWPB 7
09DC 0947 SRL  7,4
09DE D4C7 MOVB 7,*3
09E0 0228 AI   8,>C002          New address motion table
09E2 C002
09E4 060C DEC  12               Last sprite?
09E6 15C4 JGT  >0970            No, once again
09E8 0A11 SLA  1,1
09EA 183D JOC  >0A66            No sound process jump
09EC D0A0 MOVB @>83CE,2         Number of sound byte
09EE 83CE
09F0 133A JEQ  >0A66            None, then end
09F2 780E SB   14,@>83CE        -1
09F4 83CE
09F6 1637 JNE  >0A66            Not 0, then end
09F8 C0E0 MOV  @>83CC,3         Pointer sound list
09FA 83CC
09FC C14E MOV  14,5
09FE 0915 SRL  5,1              GROM or VDP?
0A00 180A JOC  >0A16            1=VDP, then jump
0A02 06A0 BL   @>0864           Push GROM address on substack
0A04 0864
0A06 0205 LI   5,>0402
0A08 0402
0A0A A14D A    13,5             GROM write address
0A0C D543 MOVB 3,*5             Write GROM address
0A0E D560 MOVB @>83E7,*5
0A10 83E7
0A12 C18D MOV  13,6             Read address
0A14 1007 JMP  >0A24
0A16 0205 LI   5,>8C02          VDPWA
0A18 8C02
0A1A D560 MOVB @>83E7,*5        Write VDP address
0A1C 83E7
0A1E D543 MOVB 3,*5
0A20 0206 LI   6,>8800          VDPRD
0A22 8800
0A24 D216 MOVB *6,8             Fetch byte
0A26 130F JEQ  >0A46            0?
0A28 9220 CB   @>0A9C,8
0A2A 0A9C
0A2C 130A JEQ  >0A42            >FF? Yes,switch to another(well possible)!
0A2E 0988 SRL  8,8              Number
0A30 A0C8 A    8,3              To address
0A32 D816 MOVB *6,@>8400        Load sound process
0A34 8400
0A36 0608 DEC  8                How many bytes?
0A38 16FC JNE  >0A32            Next byte
0A3A 05C3 INCT 3
0A3C D096 MOVB *6,2             Fetch duration
0A3E 1309 JEQ  >0A52
0A40 1009 JMP  >0A54            Go on
0A42 2BA0 XOR  @>0378,14        Change system flags
0A44 0378
0A46 D0D6 MOVB *6,3             Fetch new address
0A48 0202 LI   2,>0100          Sound byte >01
0A4A 0100
0A4C D816 MOVB *6,@>83E7        Complete address
0A4E 83E7
0A50 1001 JMP  >0A54            Once again
0A52 7082 SB   2,2
0A54 C803 MOV  3,@>83CC         New pointer sound list
0A56 83CC
0A58 D802 MOVB 2,@>83CE         Sound byte
0A5A 83CE
0A5C 0285 CI   5,>8C02          From VDP?
0A5E 8C02
0A60 1302 JEQ  >0A66
0A62 06A0 BL   @>0842           POP GROM address from substack
0A64 0842
0A66 0A11 SLA  1,1
0A68 180D JOC  >0A84            No QUIT key, then jump
0A6A 020C LI   12,>0024         Load CRU
0A6C 0024
0A6E 30E0 LDCR @>0012,3
0A70 0012
0A72 0B7C SRC  12,7
0A74 020C LI   12,>0006
0A76 0006
0A78 3605 STCR 5,8              Fetch CRU
0A7A 2560 CZC  @>004C,5         QUIT key?
0A7C 004C
0A7E 1602 JNE  >0A84
0A80 0420 BLWP @>0000           Software reset
0A82 0000
0A84 D82F MOVB @>FC00(15),@>837B    VDP status in copy RAM
0A86 FC00
0A88 837B
0A8A 02E0 LWPI >83C0            INTWS
0A8C 83C0
0A8E 05CB INCT 11               Screen timeout counter
0A90 160B JNE  >0AA8            Not 0

Interrupt level 2:

0A92 D30A MOVB 10,12            VDP register 1
0A94 098C SRL  12,8
0A96 026C ORI  12,>8160         Basis value
0A98 8160
0A9A 024C ANDI 12,>FFBF         Turn off screen
0A9C FFBF
0A9E D820 MOVB @>83D9,@>8C02    Load VDP register
0AA0 83D9
0AA2 8C02
0AA4 D80C MOVB 12,@>8C02
0AA6 8C02
0AA8 02E0 LWPI >83E0            GPLWS
0AAA 83E0
0AAC B80E AB   14,@>8379        VDP interrupt timer (system flags!)
0AAE 8379
0AB0 C320 MOV  @>83C4,12        User defined interrupt
0AB2 83C4
0AB4 1301 JEQ  >0AB8            None, then jump
0AB6 069C BL   *12              Otherwise execute
0AB8 04C8 CLR  8                Clear GROM search pointer
0ABA 02E0 LWPI >83C0            INTWS
0ABC 83C0
0ABE 0380 RTWP                  And end interrupt

 

 

 

...lee

Link to comment
Share on other sites

Total guess: The inference is that GROM based devices can emit interrupts and have them serviced by the console ISR (which will in turn invoke a GPL ISR in the device emitting the interrupt).

 

By omitting the CLR R8, the TI Forth programmers are assuming that no interrupt-emitting GPL devices are connected to the computer.

 

*Total* stab in the dark!

 

Tursi?

Link to comment
Share on other sites

I do not know of any GROM-based device that raised interrupts; GROMs may, however, slow down the CPU using the READY line.

 

No, honestly, as I said in the other thread, I believe CLR R8 in GPLWS in the console is meaningless. It does not hurt, so it is not a bug in a proper sense, just a useless line. Maybe Heiner Martin was deceived by this code in the same way and believed that it clears the GROM search pointer, so he added this comment.

 

Or let's turn it this way - they may have had something in mind with clearing the GROM search pointer, but it was obviously unneeded.

Edited by mizapf
Link to comment
Share on other sites

Thanks, Guys! I would now agree that Heiner Martin was deceived because the "meaning" of R8 in the interrupt workspace as stated in the E/A manual was confirmed by a second source, viz., TI's Graphics Programming Language: Programmer"s Guide. Perhaps the programmers of the console ISR were hedging their bets because the ISR uses GPLWS R8 as a work register for VDP processing etc., which, I agree, seems pretty useless to clear because the GPL interpreter uses it as a work register as well, so far as I can tell.

If, on the other hand, their intent was, indeed, to clear the INTWS GROM search pointer, they have the two instructions reversed, i.e., lines 226 and 227, in the spoiler in my last post, should be reversed.
The only other possibility that comes to mind (and I don't think I care to research it) is that the console cassette routines rely on GPLWS R8 in places they can be interrupted and the console ISR clears it to be safe. The fact that the TI Forth programmers ignored it as a useless instruction (and, of course, allowing that the console ROM programmers actually knew what they were doing by clearing GPLWS R8) makes a little more sense in this context because TI Forth did not provide for cassette use. I suppose I should consider this horse well and truly dead! :P
...lee
Link to comment
Share on other sites

Total guess: The inference is that GROM based devices can emit interrupts and have them serviced by the console ISR (which will in turn invoke a GPL ISR in the device emitting the interrupt).

 

By omitting the CLR R8, the TI Forth programmers are assuming that no interrupt-emitting GPL devices are connected to the computer.

 

*Total* stab in the dark!

 

Tursi?

 

Mizapf already covered it, GROMs are only memory devices. The only hardware they can control is the HOLD line on the CPU, and that is gated only during a GROM access (GROMs actually hold that line active in their idle state, so the console ensures that the CPU doesn't see it when GROMs are not being accessed).

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