Jump to content
IGNORED

May the FORTH Musical offering - FORTI Decompiled


Recommended Posts

 

May 4th Musical offering: FORTI Decompiled

 

1. Introduction

 

FORTI was a sound card for the TI-99/4A PEB, with software written in FORTH. It featured 4 of the TI sound chips, giving 12 programmable voices. Each chip's tone#3 generator was paired with a noise generator, for bass or percussion.

 

The FORTI software was best known in the form of its 3-voice demo of Bach's FWV 578 "Little" Fugue in G Minor (G-Moll), distributed with the TI FORTH Demo disk in 1983-84. The Bach demo used only 3 voices, on the sound chip in the 4A.

 

However, other compositions were made for 12 voices. I know of Chariots of Fire (Vangelis), Ricercar a 6 (Bach, Musical Offering, aka Prussian Fugue), and a newer Bach "Litle" Fugue (first 1K lost). On the plain 4A, these sound terrible but tantalizing, as 11 voices compete skilfully but hopelessly for 3 hardware sound channels.

 

What I did

 

Starting from a compiled FORTI demo of Chariots of Fire, I decompiled the FORTH words, read out data structures, and disassembled the code for the MUSIC word, which is in the ISR sound driver.

 

I benefitted from a small amount of leftover original source (from the corrupted Bach demo) and the FORTI user manual, but most of FORTI, especially the music compiling words, is lost to me and has to be reverse engineered.

 

From internal evidence, the Bach demo is older than Chariots of Fire. The FORTI user manual indicates features not supported in the Chariots of Fire MUSIC driver, which I may create again. I classify features into three versions of the FORTI software:

  • B. Bach Demo (TI FORTH graphics demo)
  • C. Chariots of Fire
  • D. Distribution with FORTI manual
Unless stated otherwise, I discuss features found in Chariots of Fire.

 

2. Capabilities of FORTI:

 

2.1 The Data Structures

  • Voiceline - a list of note numbers (byte) and duration in ticks (byte).
  • Envelope - a list of attenuation bytes, with looping index (high bit set).
  • Workspace - set of 16 TMS9900 workspace registers holding the context for one sound channel.
  • Pitchtable - arrays of frequency cmds for each note number.
2.2 The capabilities of the MUSIC ISR routine

 

For each of 12 voices, there is a separate voiceline, envelope, and pitchtable, assigned to a hardware sound channel.

 

 

The differential tempo counter:

 

Note durations in ticks (1/60th second) are multiplied by 32 and added into a "Next Note" countdown timer (per voice).

 

On each interrupt tick (1/60th second), the value DEL is subtracted from the countdown timer. DEL has an initial value of 32. When DEL is modified by the RIT, ACC, and TEMPO words, the effect is that the tempo is increased or decreased. The duration timer is never cleared so no time is lost. All voices move ahead at the same tempo.

 

When the duration timer reaches 0 or less (it is initialized to 0 to start music) a new note and duration are taken from the voiceline. If the duration is 0, the channel is silenced, and the ISR returns -1 on the stack (bug: and the ISR is removed?)

 

Looping inside the envelope: in terms of an ADSR model, the envelope will begin with an Attack-Decay section, a Sustain (looped) section, and an optional Release (tail) section which can apply during the last portion of each note. The Release feature is used in the Bach Demo but not Chariots of Fire (buggy).

 

Envelope bytes are processed every tick (1/60th second), not modified by tempo.

 

The 16 attenuation values are remapped through a dynamic offset DYN and a 48-byte dynamics table (DYT). This allows words like +VOLUME and -VOLUME to take effect

 

2.3 Pitchtables

 

A pitchtable entry is 2 data bytes to send to the sound chip for a note number (the channel number will be added later).

 

Note number 0 is used as a rest, but a rest is actually defined by having 0 for the sound chip period.

 

PT0 is the regular pitchtable. Note number 37 corresponds to low A or 110 Hz, which is the lowest musical note a tone generator can produce. Because of this limitation, PT0 fills the bottom 3 octaves (notes 1-36) with a copy of the octave 37-48. If a voice using PT0 plays notes in the range 1-36, they will still be heard, but in a higher octave.

 

PT1 notes load white noise for percussion.

 

PT4 is for channel 3 to drive the periodic noise channel and begins with note 0 corresponding to 13.75 Hz.

 

3. Examples:

 

Voiceline:

 

0 VARIABLE VOICE1 QU C D E F EI G A B C 0 ,
compiles a list of note-duration pairs into VOICE1 relying on definitions such as these:

 

49 VARIABLE XPOSE
: &* XPOSE @ + SWPB DURATION C@ + , ;

: A 0 &* ;
: A# 1 &* ;
: C 3 &* ;

: AA 12 &* ;
: CC 15 &* ;

: EI 24 DURATION C! ;   
: QU 48 DURATION C! ;  
: RE DURATION C@ , ;

: OCTAVE 12 * 49 + XPOSE ! ; ( n -- ) 

The FORTI manual says to compile a voiceline like so:

START
VOICE: VOICE1 
( 1 ) QU C D E F 
( 2 ) QU G A B C 
FINIS
My example based on C/ uses a lower level way:

( Verdi Requiem Mv2 trumpet )
0 OCTAVE
0 VARIABLE VOICE2 
 ( 131 ) 3E RE C E G E G RE C E G E G 
 ( 132 ) Q CC RE RE 3E C E$ A$ 
 ( 133 ) Q CC H RE 3E C E$ G 
 ( 134 ) Q C RE H RE
 0 ,

4. Internals

 

4.1 Workspace registers

 

FORTI takes advantage of the TMS9900 workspace pointer context. There is a block of 12 workspaces allocated in the dictionary, one for each voice. In MUSIC version C with dancing sprites, they must be contiguous so that a sprite table address can be found from (STWP-WS1)/8. In version B they are not contiguous.

 

In the Bach demo, (no sprites) Workspace WS1 might be set up like this:

 

VOICE2 VARIABLE WS1 PT0 , TRUMPET1 , 0 , 0 , 8000 , 9000 , 12 ALLOT
But with dancing sprites, all WS must be contiguous, in order to calculate the sprite#.

 

0 VARIABLE WS1 17E ALLOT
WS1 20 + WS2 CONSTANT
WS1 40 + WS3 CONSTANT
WS1 60 + WS4 CONSTANT
WS1 80 + WS5 CONSTANT
WS1 A0 + WS6 CONSTANT
WS1 C0 + WS7 CONSTANT
WS1 E0 + WS8 CONSTANT
WS1 100 + WS9 CONSTANT
WS1 120 + WSA CONSTANT
WS1 140 + WSB CONSTANT
WS1 160 + WSC CONSTANT
The word MUSIC is the core of the ISR and is written in assembler. The first instructions of MUSIC pulls the WP off the stack;

 

   ED10  0201  LI   R1,>ED18
   ED12  ED18  
   ED14  C019  MOV  *SP,R0           fetch WSP  ( wsp -- f )
   ED16  0400  BLWP R0               effect is R0->WP R1->PC. no LWP on 9900  
The full code is in Listing 1 at the end of this article.

 

The workspace registers are initialized before MUSIC first runs, and have the following meaning:

 

 

 FORTI WS usage. WS is a register workspace
 ------------------------
 R0 voice or cascade addr. List of byte pairs (note#,dur)
 R1 pitch-table: e.g. PT0 or PT1
 R2 envelope. e.g. PLUCK3 or ORGAN2
 R3 index into envelope
 R4 countdown timer to next note. init to 0 to begin.
 R5 const pitch cmd byte
 R6 const attn  cmd byte (for a noise, pitch is tone#3 and attn is tone#4/noise)
 R7 pitch cmd word for current note or 0 for rest
 R8  temp. compute vdp address (for sprite) or envelope address
 R9  vdp addr of sppat#
 R10 copy of SGCA, sound card address (copied from SGCA at each ISR entry, sic)
 R11 not used
 R12 temp. compute vdp data byte (for sprite)
 R13 ... R15 after blwp, not used

( Register numbers for use in assembling MUSIC - EO )
0 CONSTANT NTP ( Note table / voice pointer )
1 CONSTANT PTS ( pitch table start )
2 CONSTANT ATS ( attenuation table start / envelope )
3 CONSTANT ATP ( attenuation table offset )
4 CONSTANT DUR ( countdown timer duration )
5 CONSTANT PVID ( pitch voice ID )
6 CONSTANT VVID ( volume voice ID )
A CONSTANT SG   ( sound card address )
C CONSTANT PNDX ( pattern number vdp address )
( D E F are clobbered on entry to MUSIC but could be temps )

4.2 Player words

Words PLA1, PLA3, PLA12 begin playing 1, 3, or 12 voices, resp.

 

: PLA12 ' BMUSIC XMUS ; ( play 
: PLA3  ' CMUSIC XMUS ; ( play only WS1-3 on internal snd only )
: PLA1  ' DMUSIC XMUS ; ( play only WS1 on internal snd only )
where

: MUSIC0 85EE SGCA ! MUSIC ; ( 1110 1110 only chip #0 with enable low )
: MUSIC1 85F6 SGCA ! MUSIC ; ( 1111 0110 only chip #1 )
: MUSIC2 85FA SGCA ! MUSIC ; ( 1111 1010 only chip #2 )
: MUSIC3 85FC SGCA ! MUSIC ; ( 1111 1100 only chip #3 )
: MUSIC4 84FE SGCA ! MUSIC ; ( 1111 1110 internal only )
: MUSIC5 857E SGCA ! MUSIC ; ( 0111 1110 say chip 4 )
: MUSIC6 85BE SGCA ! MUSIC ; ( 1011 1110 say chip 5 )
: MUSIC7 85DE SGCA ! MUSIC ; ( 1101 1110 say chip 6 )
3424 VARIABLE PSTART
: XMUSIC WS1 MUSIC0 OR IF 83C4 @ PSTART ! 0 83C4 ! OFF ENDIF ;
: DMUSIC 0 XMUSIC ;
: YMUSIC WS2 MUSIC0 WS3 MUSIC0 OR OR XMUSIC ;
: BMUSIC WS4 MUSIC1 WS5 MUSIC1 WS6 MUSIC1 
         WS7 MUSIC2 WS8 MUSIC2
         WSA MUSIC3 WSB MUSIC3 WSC MUSIC3 OR OR OR OR OR OR OR YMUSIC ;
: CMUSIC 0 YMUSIC ; ( for PLA3 )
: XMUS CFA ISR ! INTLNK @ PSTART ! ;
When playing just one voice:

 

: MUSIC0 85EE SGCA ! MUSIC ; ( 1110 1110 only chip #1 with enable low )
: XMUSIC WS1 MUSIC0 OR IF 83C4 @ PSTART ! 0 83C4 ! OFF ENDIF ; ( f/finished? -- )
: DMUSIC 0 XMUSIC ; 
: PLA1  ' DMUSIC XMUS ; ( play only WS1 on chip#1 or internal snd  )
In PLA1, DMUSIC is the ISR which calls MUSIC on one workspace pointer. (PLA12 and BMUSIC make a player that calls each of 12 voices.)

 

 

4.3 Sprites

 

The version of MUSIC in Chariots of Fire draws dancing sprites on the screen (Bach demo does not have this.) For each of 12 voices, there is a sprite. The note number translates to a column on the screen, while the attenuation is shown by a colored bar where height indicates loudness.

 

4.4 Misc

Chariots of Fire utilizes 11 sound channels across 4 chips. Interestingly, it defines addresses for MUSIC5,6,7 as if the author imagined having more chips than 4.

 

5. Optimizations

 

It's quite likely that I'm not analyzing the final version of the MUSIC word. That said, I see the following optimizations that could be made:

 

* SGCA is placed on the stack and copied to the workspace R10 on each tick. This should be configured before music starts playing, just like other registers that don't change.

 

* The envelope release (or tail) segment looks buggy in Chariots of Fire (whose envelopes don't rely on it.)

 

* Byte swapping and SRL by 8.

movb *r8,r8
ab   @DYN+1,r8
srl   r8,8
movb @DYT(r8),r8
movb  r8,*r10
could be

clr   r11
movb *r8,r11
swpb  r11
a    @DYN,r11
movb @DYT(r11),*r10
6. Missing Words

 

Parts of the music entry system were not present in the compiled demo:

  • Defining words like VOICE: FINIS and <ENV: ENV> were not recovered. I speculate that the demos B and C were created in a lower level way, and that these defining words were invented for the final distributon.
  • Envelope word =REPEAT to encode the sustain loop
  • 3 SHARPS established a key signature, modifying the behavior of note words like F. In the demo, F pushes a constant note number 8 (plus octave). This implies
  • T for Tie or slur is not present. I don't see that MUSIC implements any means to vary frequency.
  • R: and :R to repeat a section in a voiceline. These save memory, but would require runtime implementation in MUSIC.
  • convenience word =TEMPO
  • counting words =MEAS and +MEAS
  • Envelope defining words <ENV: ENV> produce code that copies the envelope PFA into a WS. This requires a <BUILDS DOES> construct. In the demo, envelope words are just arrays.
  • The words +VOLUME and -VOLUME and dynamic markings from PPP to FFF.
  • CONDUCTOR is the top defining word and is not present
  • SILENT is not present
  • +FIFTH and words that transpose voices at runtime, not compile time, will require support in MUSIC.
  • <ALBUM and ALBUM> words
  • =GR and GR that subtracts time (for a grace note) from the previous note duration. In the demo, GR sets DURATION to 1 tick.
  • =FERMATA and FERMATA
  • =DRUM to set a WS to play noise
  • <PHRASE: PHRASE>
7. Future Innovations

 

I was disappointed to find nothing about tremolo in this demo. Envelopes are just attenuation changes, which is how an organ tremolo operates, but on other instruments, tremolo means to vary the frequency, or play two frequencies for the overtones. (T or Tie/Slur effect on frequency is unknown; perhaps it just skips the tail.) An envelope for pitch would allow many more effects.

 

The FORTI manual lacks a dictionary.

 

The pitch table is flexible enough to allow different temperament. The pitch table we take for granted is the equal tempered system in which music can be transposed to another key without changing its character.

 

8. Acknowledgments

 

David Olson retrieved the Chariots of Fire demo disk.

 

Rene LeBlanc for his FORTH disassembler, which I obtained in 1985 and never used until now.

 

Gene Hitz and Owen Brand provided the MATIUG disk library, which I converted to DSK format, finding the TI FORTH source.

 

The BERG/WERNECKE FORTH decompiler found in the MATIUG disk library.

 

Lee Stewart's fbFORTH and TI FORTH manuals which I used as a reference throughout, especially on the dictionary entry structure.

 

Tursi for Classic99 in which I did most of the work.

 

Kryoflux hardware and software was used to efficiently acquire hundreds of legacy disks.

 

CANTRELL, the TI engineer named on the FORTI "stereo card" schematic. Who is CANTRELL?

 

9. References

 

FORTI Music Card Users Manual and Schematic at WHTech

 

ADSR definition

 

 

Listing 1

 

* code field of MUSIC
* Load workspace from stack arg
   ED10  0201  LI   R1,>ED18
   ED12  ED18  
   ED14  C019  mov  *SP,R0           fetch WSP  ( wsp -- f )
   ED16  0400  blwp R0               effect is R0->WP R1->PC. no LWP on 9900  
   ED18  C2A0  mov  @SGCA,R10        ECCE is pfa of variable SGCA. typ  85EE(#1 chip)
         ECCE
   ED1C  04CC  clr  R12              
   ED1E  C104  mov  R4,R4            duration timer. clear to start         
   ED20  1531  jgt  LABEL2                 
* next note
   ED22  D330  movb *R0+,R12         get note number in R12    
   ED24  098C  srl  R12,8                 
   ED26  C1CC  mov  R12,R7           get note number in R7    
   ED28  0A9C  sla  R12,9            sprite col = note# * 2
   ED2A  0209  li   R9,>d844         WS1        
         D844
   ED2E  02A8  stwp R8                    
   ED30  6209  s    R9,R8            (WSx - WS1) is a multiple of 32     
   ED32  0938  srl  R8,3             divide by 8
   ED34  0228  ai   R8,>4301         address to write, in SAT 
         4301
   ED38  06C8  swpb R8                    
   ED3A  D808  movb R8,@>8c02        set up VDP address     
         8C02
   ED3E  06C8  swpb R8                    
   ED40  D808  movb R8,@>8c02             
         8C02  
   ED44  D80C  movb R12,@>8c00       sprite col = note# * 2
         8C00
   ED48  0588  inc  R8                     
   ED4A  C248  mov  R8,R9            vdp addr of sppat#      
   ED4C  D230  movb *R0+,R8          duration     
   ED4E  0988  srl  R8,8                  
   ED50  0A58  sla  R8,5             times 32 .  32 is the neutral value of DEL 
   ED52  A108  a    R8,R4            add, because need to pay time debt
   ED54  1509  jgt  LABEL1                 
* silence
* bug: if R4 is at worst 1-DEL, R8 is at worst 32, if R8<DEL-1 then music may stall
FINIS
   ED56  0640  dect R0               leave R0 pointing to the last (valid) note
   ED58  C206  mov  R6,R8            attn cmd byte     
   ED5A  0228  ai   R8,>0f00         silence
         0F00
   ED5E  D688  movb R8,*R10          write to sound chip
   ED60  02E0  lwpi >8300                 
         8300
   ED64  0719  seto *SP              put -1 on the stack ( wsp -- f )
   ED66  045F  b    *NEXT                  
LABEL1
   ED68  A1C7  a    R7,R7            note# * 2
   ED6A  A1C1  a    R1,R7            pitch table address     
   ED6C  C1D7  mov  *R7,R7                
   ED6E  1605  jne  NOTE             else, turn off voice:    
REST
   ED70  C206  mov  R6,R8            attn cmd byte     
   ED72  0228  ai   R8,>0f00         silence
         0F00
   ED76  D688  movb R8,*R10          write to sound chip
   ED78  1005  jmp  LABEL2           why not LABEL6      
NOTE
   ED7A  A1C5  a    R5,R7            pitch cmd byte     
   ED7C  D687  movb R7,*R10          write to sound chip
   ED7E  06C7  swpb R7                    
   ED80  D687  movb R7,*R10          write to sound chip
   ED82  04C3  clr  R3    
* note in progress
LABEL2
   ED84  C1C7  mov  R7,R7            pitch cmd or 0 if none     
   ED86  132E  jeq  LABEL6           nothing to do
   ED88  D212  movb *R2,R8           envelope first byte (N in sustain)
   ED8A  0938  srl  R8,3                  
   ED8C  8108  c    R8,R4            R4 is counting down from duration*32      
   ED8E  1501  jgt  SSTAIN                
   ED90  1003  jmp  LABEL3
SSTAIN
   ED92  0203  li   R3,>003f         start envelope at 3fh-R8*32    
         003F
   ED96  60C8  s    R8,R3                 
LABEL3
   ED98  0283  ci   R3,>003f             
         003F
   ED9C  1102  jlt  LABEL4                 
   ED9E  0203  li   R3,>003f         R3 max 3F
         003F
LABEL4
   EDA2  0583  inc  R3               at least 1     
   EDA4  C203  mov  R3,R8                 
   EDA6  A202  a    R2,R8            envelope  pointer in R8         
   EDA8  D218  movb *R8,R8           
   EDAA  0988  srl  R8,8                  
   EDAC  0288  ci   R8,>0080         0-F attentuation, 8x cmd byte     
         0080
   EDB0  1501  jgt  ENVJMP                 
   EDB2  1006  jmp  LABEL5            
* envelope cmd byte  
ENVJMP   
   EDB4  0228  ai   R8,->80              
         FF80
   EDB8  C0C8  mov  R8,R3            new envelope index in R3     
   EDBA  A202  a    R2,R8                 
   EDBC  D218  movb *R8,R8           they seem to have run out of registers     
   EDBE  1001  jmp  LABEL6   
* R8 lsb is attn    
LABEL5          
   EDC0  06C8  swpb R8
* adjust attn by dynamics table
LABEL6
   EDC2  B220  ab   @DYN+1,R8             
         ECD9
   EDC6  0988  srl  R8,8             ugh     
   EDC8  D228  movb @DYT(R8),R8         
         D1BA
   EDCC  06C9  swpb R9               vdp address of sppat#     
   EDCE  D809  movb R9,@>8c02             
         8C02
   EDD2  06C9  swpb R9                    
   EDD4  D809  movb R9,@>8c02             
         8C02
   EDD8  0A28  sla  R8,2             mpy by 4
   EDDA  D808  movb R8,@>8c00        set sppat# to attn
         8C00
   EDDE  0928  srl  R8,2                  
   EDE0  A206  a    R6,R8            add attn cmd byte             
   EDE2  D688  movb R8,*R10          write to sound chip
LABEL6
   EDE4  6120  s    @DEL,R4        Subtract DEL from timer     
         CC60
   EDE8  02E0  lwpi >8300                 
         8300
   EDEC  04D9  clr  *SP                   put 0 on the stack
   EDEE  045F  b    *NEXT                  
Edited by FarmerPotato
  • Like 9
Link to comment
Share on other sites

Download the binaries here:

 

Bach Demo

for "Little" Fugue in G Minor

6 LOAD

BPLAY

 

Chariots of Fire

for Chariots of Fire:

59 BLOAD

PLAY

 

or for Ricercar a 6

 

49 BLOAD

PLAY

 

 

This really cool. I have the beginnings of this kind of music language for Camel Forth but this is fully developed.

It inspires me to continue development in this direction.

 

Reading through the manual I get lots of ideas. I am curious about the notation's use of flats.

I saw that they use A$ B$ etc for flats if I read it correctly. To me the obvious labels are

 

Ab Bb Cb A# B# D# etc.

 

I suspect it has something to do with the search mechanism being case insensitive so their Forth could not differentiate between BB and Bb but's that's just a guess.

 

Thanks for this excellent reverse engineering work.

 

B

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Thank you for the comments. I am continuing to work on this. I will have a source code update next weekend.

In the absence of a copy of the actual FORTI distribution disk (D), here is my plan:

Road Map for FORTI

Clean source (compilable) for (B) Bach Demo and © Chariots of Fire, Ricercar a 8. Including some defining word features implemented to match (D)

FORTH assembler version of MUSIC, as well as CODE version (machine code)

Full implementation of features of (D) from user manual, with some innovations.

Collection of Bach inventions and sinfonias for 2-3 voices

MIDI import tool

TRON arcade game music (originally used AY-8910)

Well-known TI game songs remixed

Tool to export compiled music as A/L source for use outside FORTH

Reference release (E) (Erik) with innovations

4-chip FORTI card PCB (not made yet)

Extra special music card project compatible with FORTI (hardware built! waiting while I do software)

Major musical work demo

MIDI player

Speech synthesizer integration

Animatronic art installation

  • Like 4
Link to comment
Share on other sites

  • 6 years later...
2 hours ago, Asmusr said:

I think I understand now: You need to run FORTH as E/A#3, and then it's using sector I/O.

Yeah, these are the DSK images. Easy to use in Classic99 with E/A.  

 

There is no source code. (I decompiled some.)  I was hoping for source code on the original disk from @dhe but no.
 

At least I can decompile what was in the distribution to users.

 

FORTI2
 

A long while ago, I re-implemented a chunk of what the FORTi user manual describes. Long since I tested it on TI Forth. I want to compile it in fbForth.  

 

I didn't implement the CONDUCTOR or ALBUM words.

 

I did complete VOICE: and ENV: defining words. With all the notes and durations. I changed the offset to use MIDI numbers. 

 

Also PLAYER, FIFTH, BASS etc to set up before starting  MUSIC.  
 

So it does a lot, but there is no CONDUCTOR involved to change TEMPO or dynamics. 
 

Another way is I could add tempo and dynamics in the VOICE:

 

But TEMPO only makes sense  if it's one PLAYER.   (Especially parallel CRESC will not have the intended effect.)

 

On the other hand, if you have dynamics in a CONDUCTOR sequence it's better for editing (that is, conducting!) Suppose you want to assign the same VOICE to separate players with different dynamics. 
 

Oh, another feature during VOICE: compiling: I put in a MEAS; word for you to indicate end of measure.

 

In MEAS; if the durations don't add up to a multiple of the time signature, the compiler gives a warning "Short measure at 12, adding rest"


Other possibilities might be n +MEAS or n MEAS!
 


The need is for a long period of inactivity in one voice. The other way to do that is to break  the voice into pieces and write the CONDUCTOR to launch them.

 

The CONDUCTOR also might compile the +MEAS word to signal its inactivity. In FORTi you have to write a string of WH RE RE RE RE... which would be error-prone. 


If you are typing in music from a score, you know the measure numbers, and MEAS is handy.  You can always add your own compiling  words, but my changes depend on adding to the duration for every note compiled.

 

Oh, another idea I did in ANS Forth: ( n ) NOTE is the compiling word called by A etc, but it is DEFER.
 

DEFER NOTE

' PLAY-NOW IS NOTE

' COMPILE-NOTE IS NOTE

 

Another way might be to make NOTE compile or play immediately depending on STATE being compile. 

It's really a great flexible system that Lee Caldwell built so long ago. 
 

 

  • Like 3
Link to comment
Share on other sites

11 hours ago, FarmerPotato said:

Yeah, these are the DSK images. Easy to use in Classic99 with E/A.

But I don't think Classic99 emulates the FORTi, at least it didn't sound like it.

 

I also looked at the MAME implementation, and it appears that it has the enable bits for the PSGs reversed. A PSG should be on when the bit is off, AFAIK.

 

void forti_device::write(offs_t offset, uint8_t data)
{
	// Decode for 8400-87ff
	if ((offset & 0xfc01) == 0x8400)
	{
		// The generators can be accessed in parallel
		if ((offset & 0x2)!=0)
			m_generator1->write(data);

		if ((offset & 0x4)!=0)
			m_generator2->write(data);

		if ((offset & 0x8)!=0)
			m_generator3->write(data);

		if ((offset & 0x10)!=0)
			m_generator4->write(data);
	}
}

 

Link to comment
Share on other sites

Index of the FORTi Distribution disk (or 'D').  

 

Disk image provided by @dhe.  Thanks Dan!


I see that Lee Caldwell preferred to have DSK2 begin at screen 100, not 90:

 

DECIMAL 
100 DISK_SIZE !
300 DISK_HI !

( Screens 90-99 would be an unused second side. )


A screen or block is 4 sectors or 1024 bytes.  "Offset" is a hexadecimal address into the 90K disk image file.  Hexadecimal numbers start with ">".  


Screen 1 is at offset >400
Screen 4 is at offset >1000
Screen 20 (decimal)   >5000


Compiled FORTi is brought in by 20 BLOAD .  The first 24 bytes of a BSAVE/BLOAD block is for the loader, then 1000 bytes of data.


40 LOAD is the ALBUM which utilizes up to screen 82. 


I see screen numbers to BLOAD under ( TABLES ) 


There are 6 songs in ALBUM.    <ALBUM 46 51 52 56 78 58 ALBUM>

 

46 Adeste Fideles
51 Scale test
52 Sailor's Hornpipe   image offset d000.   
56 Frere Jacques       image offset e000.   
58 Boogie              image offset e800.  
78 Ermuntre Dich       image offset 13800.  
                       Load Address
Kind    Scr    Offset  First Last  Comment
------  ---    -----   ----  ----  -----------------------
source   3                          Boot screen
message  4-5                        Custom to FORTi
help     6     1800                 Editor Keys
help     7     1a00                 Instructions to load FORTi music
         8     2000                 empty? mostly formatted E5
      8 1/2    2200                 FORTH object
BSAVE   20     5000    bc8a  e288   TI Forth:  .BLK CONSTANT VARIABLE ...
BSAVE   21-28                e288   TI Forth   
BSAVE   29     7400    dfb2  e288   TI Forth:  SQNTL RLTV ...
BSAVE   30-37  7800    c4cc  e13a   FORTi music defining words
BSAVE   37     9400    e024  e13a   ... <ALBUM ALBUM>
BSAVE   38     9800    e40c  ea06   hmm
BSAVE   39     9c00    e7f4  ea06   hmm
source  40     a000                 Album page
source  41-45  a400                 Adeste Fideles    LCT 08Dec83 - 10Dec83 
BSAVE   46     b800    e13a  e4a6   Adeste Fideles
source  47-50  5c00                 unused
BSAVE   51     cc00    e13a  e288   Scale test
BSAVE   52-55  d000    e13a  ed30   Sailor's Hornpipe
BSAVE   56     e000    e13a  e322   Frere Jacques
free    57     e400
BSAVE   58-59? e800    e13a  e952   Boogie.  ZITHER
source  60-67                       Sailor's Hornpipe
BSAVE   68    11000    e13a  e5c8   ?Sailor's Hornpipe or unused
        69
source  70-71                       Frere Jacques
BSAVE   72    12000    e13a  e32e   ?
        73-74
source  75-77                       Ermuntre Dich, Mein Schwacher Geist
BSAVE   78    13800    e13a  e48e   Ermuntre Dich
free    79-80                         free  
source  81-82 14400                 Boogie
BSAVE   83    14c00    e13a  e34a   fragment of Boogie? 
free    84-89 15000                 free

 

 

Detail of Screen 20  BSAVE header
 

offs value  purpose
00  bc8a load address 
02  e288 dict pointer
04  39de CURRENT       
06  e242 CURRENT @     
08  39de CONTEXT       
0a  e242 CONTEXT @     
0c  d548 VOC-LINK      
0e  7469 'ti' as a signature            
10       unused
24       image 1000 bytes 

 

First word is .BLK
back link    >bc42
code field   >8334


These would be entry points to the inner interpreter in PAD:

 

832E  is DODOES      (Used 13 times)
8334  is DOCOL       (Used 437 times)

 

 

Link to comment
Share on other sites

10 hours ago, Asmusr said:

But I don't think Classic99 emulates the FORTi, at least it didn't sound like it.

 

I also looked at the MAME implementation, and it appears that it has the enable bits for the PSGs reversed. A PSG should be on when the bit is off, AFAIK.

No, it doesn't. It's tough to add hardware like that, I looked at it, but decided to wait till v4, if it's ever finished. ;)

 

I added FORTI support in Super Space Acer, so that required me to build hardware. The enable bits are really easy to understand once you realize that the address bits are just directly connected - so when the address bit is 0, that PSG is enabled. Assuming I got it right!

 

Thierry has a good breakdown of it: https://www.unige.ch/medecine/nouspikel/ti99/forti.htm

 

  • Like 2
Link to comment
Share on other sites

On 9/27/2024 at 11:15 PM, Asmusr said:

So it's something you plan to release in an updated version of Super Space Acer?

Yeah, it's finished. ArcadeShopper gets first dibs to sell carts for a few months before I release the ROM, but we're stalled on a shortage of UberGROM carts ;)

 

The game supports SID or ForTI for additional sound effects.

 

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