JCH DOX!JCH DOX!
Pulse Tables:
-------------

The quickest way to  play around with these is to load up a fresh v2.53 editor 
with pulse/filter display from Antitrack/Legend. Now press / to pop up the 
arpeggio-waveform and pulse and filter tables. Now press F2 so that you can 
hear the instrument you play. Press z so that the cursor moves to instrument 0 
in the instrument table and make the first 2 bytes $22 $F2 (thats the ADSR)

Now press l (thats an L and not an i!) so that you are in the waveform and 
arpeggio tables. Type a table like this:

$00 $41      (select pulse waveform)
$7f $00      (loop to position 0)

Okay! This has now selected the pulse waveform and you're ready to go!


Press ; to edit the pulse table. The bytes are in groups of 4, which I will 
call 'lines'.  The bytes in each line now have the following meaning

first byte: $xy means
            $x00 is the minimum pulse value
            $y00 is the maximum pulse value

            when the max value is reached, then the 'step' (see next byte)
            starts being subtracted and goes the other way until the
            minimum value is reached. This way the pulse width oscillates
            between the max and min values at the speed in this following byte.

second byte: $xx means that $xx0 is the pulse 'step' that is added each time

third byte: %xyzzzzzz means that
            if x = 1 (ie bit 7 set) then the fourth byte will be used
                     to initialise the pulse value
            if x = 0 then the existing pulse value is retained

            if y = 1 (ie bit #6 set) then this is the last line in the table
            if y = 0 then the next line will be fetched when the delay value
                      counts down to 0

            zzzzzz is the delay value from $00 to $3f. This value is DOUBLED
            and is then the delay before the next line of the pulsetable is
            fetched.

fourth byte: $xy means that if bit #7 of the third byte is set then the 
initial             value of the pulsewidth will be $0yx0


Okay! Now for some examples.
First to make a square waveform is easy. The line will be $00 $00 $c1 $08

This means that there is no max or min, (because) no step is added. The pulse 
width will be initially set to $800 and this is the last line of the table (
delay is 1 as well).

If you forget to set bit #6, then the next line of the table will be fetched 
and all sorts of strange things will happen.


Okay! Now for a more complex example.
Lets make a waveform that pulsates and also changes speed sometime. The bytes 
will be:
$1e $10 $8f $02
$1e $02 $41 $00

This means that for the first $f*2 vbls the first line will be done, and then
the second line will be done after that - which is the last line. The pulse 
width will go up and down between $100 and $e00 at a speed of $10 per vbl, and
this will last for $f*2 vbls. it will initially be set to $200. After this, 
thesecond line of the table will kick in, and the pulse width will still go 
between $100 and $e00, but at a MUCH slower rate! The pulsewidth will NOT be 
initialised as it goes on to the second line.

You can press the top keys on the keyboard to play your instrument in F2 mode!

I hope you can understand that! Now for the filters.


Filter tables:
--------------

To select a filter for this instrument, you need to use the FX byte in the 
instrument specification (remember press z to get into the instrument table) (
the instrument fx byte is the 5th one along from the left!) The bottom nibble 
of the FX byte controls the filters. All you need to know for now is that you 
can select the 3 different filters for this instrument with the bottom 3 bits 
of this byte (ie values $00 to $07) don't try any other values in this byte 
otherwise you
get some funny results .. (they wont be funny when I tell you what the other 
bits do)


Okay. Now I can tell you that the filter tables works EXACTLY the same as the
pulse tables, except the filter value is 8 bits instead of 12, so some of the 
numbers are a magnitute of $10 less!

Press the key to the right of ; to edit the filter table. The bytes are in 
groups of 4, which I will call 'lines'.  The bytes in each line now have the 
following meaning

first byte: $xy means
            $x0 is the minimum filter cutoff value
            $y0 is the maximum filter cutoff value

            when the max value is reached, then the 'step' (see next byte)
            starts being subtracted and goes the other way until the
            minimum value is reached. This way the fliter cutoff oscillates
            between the max and min values at the speed in this following byte.


second byte: $xx means that $xx is the filter 'step' that is added each time
             to the filter cutoff value

third byte: %xyzzzzzz means that
            if x = 1 (ie bit 7 set) then the fourth byte will be used
                     to initialise the filter cutoff value value
            if x = 0 then the existing filter cutoff value is retained

            if y = 1 (ie bit #6 set) then this is the last line in the table
            if y = 0 then the next line will be fetched when the delay value
                      counts down to 0

            zzzzzz is the delay value from $00 to $3f. This value is DOUBLED
            and is then the delay before the next line of the filtertable is
            fetched.

fourth byte: $xy means that if bit #7 of the third byte is set then the 
initial             value of the filter cutoff will be $xy


I'm sure you can make up examples of a filter instrument now.



Drums and Arpeggios and waveforms:
----------------------------------

I was chatting to Antitrack the other night about DRUMS and Arpeggios... do 
you all know how to use the drums and arpeggios on the JCH editor??? The top 
nibble of the fx byte determines whether this is a drum or an arpeggio:

fxbyte = $1x means that this is a drum
fxbyte = ANY (and I repeat ANY) other value, then this is regular arpeggio.

If a drum is selected, then the 'arpeggio byte' for each place in the table is 
jammed into the high byte of frequency, whilst the waveform is the same as 
usual. Therefore the drum is INDEPENDENT of the note. A cool feature to note 
is that you can acually SET the time for release in your drum by making bit #0 
of the waveform equal zero at some time. Here is an example of a snare drum

$30 $81       (noise wave, frequency will be $3000) $28 $41       (pulse wave, 
frequency will be $2800, nb.make sure pulsevalue ok) $34 $81       etc
$24 $40       (pulse wave, start release now!) $38 $81       (noise getting 
higher)
... etc

the table can be looped to a position as per normal (eg is current step is $c, 
then loop to $b to finish the instrument)


If a normal arpeggio is selected, then both RELATIVE (to the note) and 
ABSOLUTE(ie independant from the note) arpeggios can be done.

eg: heres an instrument

$18 $41     note + 2 octaves, pulse wave selected (check pulse okay eg $800) $
0c $41     note + 1 octave
$00 $41
$0c $41
$00 $41    <----
$7f $04    ----! loop to position 4

This is a cool instrument, but I can make it cooler!! Lets put a HIHAT on this
channel also!!! It can be made part of this instrument - by playing a noise 
waveform for only 1 50th of a second, it can make it sound like a HIHAT is 
also playing!!! This is how we do it:

$18 $81     noise waveform
$18 $41     note + 2 octaves, pulse wave selected (check pulse okay eg $800) $
0c $41     note = 1 octave
$00 $41
$0c $41
$00 $41    <----
$7f $04    ----! loop to position 5


This is cool, BUT as we play the note at different octaves, the sound of the 
noise waveform gets lower or higher too - and it doesn't sound like a hihat at 
all - in fact it sounds SHIT!

This is where absolute arpeggios come in to the picture. We can make the noise 
waveform part of the instrument play at EXACTLY the same frequency, while the 
rest of the instrument plays at the note frequency!!! An ABSOLUTE arpeggio is 
done by adding $80 to the arpeggio byte - but remember that this will actually 
be the note played - so add a few actaves onto it!!

!!
\/
$c8 $81     noise waveform always plays at the same frequency!!! $18 $41 note +
2 octaves, pulse wave selected (check pulse okay eg $800) $0c $41     note = 1 
octave
$00 $41
$0c $41
$00 $41    <----
$7f $04    ----! loop to position 5


well.. thats all I can be bothered typing in for now, so until next time 
regards from Bad.

P.S. sorry for any errors... the sourcecodes to all this will be posted in a 
few hours/days??


-----------

Hope this helps Ivan + also some more code from jch player and editor that 
someone tried to disassemble (though this code only looks liek the menu system 
to me).  Its very well commented !

-----------

To get the thing running, you would also need the little routine at $0a00-$
0e00 to print the main screen, and the newplayer datas at $e000->$e2be.

;======================================;
;======================================;
;                                      ;
; JCH EDITOR MAIN MENU RIP BY BAD 1993 ;
;                                      ;
; SYS $C800 TO ENTER MAIN MENU         ;
; SYS $C803 TO ENTER EDITOR            ;
;                                      ;
;======================================;
;======================================;

        *= $C800

        JMP JCHMENU   ;ENTER MAIN MENU

        LDA #$36      ;ENTER EDITOR
        STA $01
        JMP $A000

;=======================================
;=======================================
;
;JCH MAIN MENU
;
;THE MAIN MENU ENABLES THE SYSTEM THEN
;PRINTS THE MAIN MENU SCREEN ($0A00)
;AND THE NEWPLAYER VERSION. NOTE THAT
;THE NEWPLAYER CAN EITHER BE IDENTIFIED
;FROM THE DATA AT $0FA0->$0FEE AND
;$E000->, OR IN ALL LATER VERSIONS THE.
;NUMBER APPEARS AS 5 SCREENCODE BYTES
;AT $0FEE.
;
;THEN:
;
; F1 LOADS MUSIC OR TABLES
; F2 SAVES TABLES ONLY
; F3 SAVES ENTIRE MUSIC
; F5 SENDS A DISK COMMAND
; F7 DOES A DISK DIRECTORY
; F8 ENTERS THE MUSIC EDITOR
;
;THE PLAYER IS LOCATED AT $0F00->$A000
;THE EDITOR IS LOCATED AT $A000->$C800
;
;=======================================
;=======================================

JCHMENU SEI

;       LDA #$B0      ;?? THIS MAY BE
;       STA LCFFD     ;?? USED BY EDITOR

        LDA #$00      ;KERNAL MESSAGES
        STA $9D       ;OFF

        STA $D020     ;SCREEN BLACK
        STA $D021

        STA $D015     ;ALL SPRITES OFF

        LDA #<$EA31   ;SET SYSTEM IRQ
        STA $0314
        LDA #>$EA31
        STA $0315

        LDA #$F0      ;ENABLE ALL IRQS
        STA $D01A
        LDA #$01
        STA $DC0E

        LDA #$37      ;KERNAL/BASIC IN
        STA $01
        CLI

        LDY #$00      ;KILL ALL SOUND
        TYA
KSLOOP  STA $D400,Y
        INY
        CPY #$19
        BNE KSLOOP


;=======================================
;MAIN LOOPS, DEPENDING ON WHETHER THE
;SCREEN IS CLEARED FOR A DIRECTORY, OR
;A NEW NEWPLAYER IS LOADED.

SCNLOOP JSR $0A00     ;PRINT SCREEN

NWPLOOP JSR PRNEWPL   ;PRINT PLAYER VERS

KEYLOOP JSR $FFE4     ;GET A KEY
        BEQ KEYLOOP

        JSR CLRLINE   ;CLR INPUT LINE


;=======================================
;F1 LOAD MUSIC OR TABLES

CHKF1   CMP #$85      ;CHECK FOR F1
        BNE CHKF2

        JSR LOADMUS   ;INP FNAME,LOAD

        JMP NWPLOOP   ;DRAWNEWLAYER LOOP


;=======================================
;F2 SAVE TABLES ONLY

;THE LOCATION OF THE TABLES IS PROBABLY
;DIFFERENT FOR DIFFERENT NEWPLAYERS,
;BUT THE STARTING ADDRESS IS ALWAYS
;FOUND IN ($0FBC).W AND THE END ADDRESS
;FOUND IN ($0FCC).W

;THUS TO SAVE THE TABLES WE ONLY NEED
;TO SAVE BETWEEN THESE ADDRESSES.

CHKF2   CMP #$89      ;CHECK FOR F2
        BNE CHKF3

        LDA $0FBC     ;SAVE THE START
        STA SVBEGLO   ;ADDRESS OF THE
        LDA $0FBD     ;TABLES
        STA SVBEGHI

        LDA $0FCC     ;SAVE THE END
        STA SVENDLO   ;ADDRESS OF THE
        LDA $0FCD     ;TABLES
        STA SVENDHI

        JSR SAVEMUS   ;INP FNAME, SAVE

        JSR PRTDSTS   ;PRINT DISK STATUS

        JMP KEYLOOP


;=======================================
;F3 SAVE ENTIRE MUSIC

;ONLY THE PATTERNS THAT ARE ACTUALLY
;USED ARE SAVED, SO THIS ROUTINE FIRSTLY
;WORKS OUT WHICH PATTERNS HAVE BEEN
;USED. THIS IS LIKE THE MASTER OF NOISE
;IN THAT IT COUNTS BACKWARDS LOOKING
;FOR EMPTY PATTERNS, AND THEN USES THE
;FIRST NON-EMPTY PATTERN ENCOUNTERED
;(BACKWARDS REMEMBER) AS THE ENDING
;ADDRESS TO SAVE TO.

CHKF3   CMP #$86      ;CHECK FOR F3
        BNE CHKF5

        LDA $0FD2     ;I THINK SET PTR
        STA $FB       ;TO BLANK PATTERN
        STA $FE       ;IN ($FE).W AND
        LDA #$9E      ;POINTER TO LAST
        STA $FC       ;(USED OR UNUSED)
        LDA $0FD3     ;PTN IN ($FB).W
        STA $FF

;SEE IF THE CURRENT PATTERN IS UNUSED

ANORPTN LDY #$00

ANORBYT LDA ($FB),Y   ;CHECK IF PATTERN
        CMP ($FE),Y   ;IS NOT EMPTY
        BNE NONZERO

        CMP #$7F      ;CHECK FOR END OF
        BEQ NEXTPTN   ;EMPTY PATTERN

        INY           ;EMPTY SO FAR, BUT
        JMP ANORBYT   ;MORE BYTES TO CHK

;THAT PATTERN WAS EMPTY, SO CHECK THE
;NEXT ONE (BEFORE THIS ONE IN MEMORY)
;

NEXTPTN DEC $FC       ;PTNS #$0100 LONG

        LDA $FC       ;CHECK FOR ALL
        CMP $0FD3     ;PATTERNS CHECKED
        BNE ANORPTN

;RIGHT NOW WE'VE FOUND THE LAST NONZERO
;(UN-EMPTY?) PATTERN AND THE STARTING
;ADDRESS OF THIS PATTERN IS IN ($FB).W
;THUS WE HAVE TO ADD #$0100 TO IT TO
;FIND THE ENDING ADDRESS OF THE ENTIRE
;MUSIX!!

NONZERO LDA #<$0F00   ;SET START ADDRE
        STA SVBEGLO   ;FOR SAVING $0F00
        LDA #>$0F00
        STA SVBEGHI

        LDA $FB       ;SET END ADDRESS
        STA SVENDLO   ;FOR SAVING, AND
        LDA $FC       ;ADD #$0100 TO
        STA SVENDHI   ;ACCOUNT FOR THE
        INC SVENDHI   ;FINAL PATTERN

        JSR SAVEMUS   ;SAVE THIS RAM

        JSR PRTDSTS   ;PRINT DISK STATUS

        JMP KEYLOOP


;=======================================
;F5 SEND DISK COMMAND

CHKF5   CMP #$87      ;CHECK FOR F5
        BNE CHKF7

        JSR DISKCMD   ;INPUT CMD,DO CMD

        JMP KEYLOOP


;=======================================
;F7 DISK DIRECTORY

CHKF7   CMP #$88      ;CHECK FOR F7
        BNE CHKF8

        JSR DISKDIR   ;CLS,DIR,WAITKEY

        JMP SCNLOOP   ;REDRAW SCREENLOOP


;=======================================
;F8 ENTER THE MUSIC EDITOR

CHKF8   CMP #$8C      ;CHECK FOR F8
        BNE JUMPKLP

        LDA #$36      ;BASIC ROM OUT
        STA $01

        JMP $A000     ;JUMP TO EDITOR


;=======================================
;THATS ALL

JUMPKLP JMP KEYLOOP


;=======================================
;=======================================
;ROUTINE TO PRINT THE NEWPLAYER
;CURRENTLY IN MEMORY
;
;THE NEWPLAYER CAN BE IDENTIFIED FROM
;THE #$4E BYTES STARTING AT $0FA0. THESE
;BYTES ARE A WHOLE HEAP OF WORD POINTERS
;TO THE MAJOR PARTS OF THE MUSIC, SUCH
;AS THE ON/OFF BYTES ETC,ETC.
;
;THIS ROUTINE CAN CHECK FOR 9 EARLY
;NEWPLAYERS BY COMPARING THE DATA AT
;$0FA0 WITH THE (9*$4E) BYTES AT $E000.
;WHEN THE NEWPLAYER IS LOCATED, ITS
;CORRESPONDING NUMBER IS PRINTED TO THE
;SCREEN AT $07B9.
;
;IF NO NEWPLAYER IS MATCHED, THEN IT IS
;AN UNKNOWN PLAYER, AND THE BYTES AT
;$0FEE ARE PRINTED AS THE VERSION
;NUMBER.
;
;THIS IS EXACTLY WHAT HAPPENS FOR THE
;14.<0 NEWPLAYER WHICH IS 'UNKNOWN',
;AND THE VERSION NUMBER IS AT $0FEE.
;
;THE FIRST THING I WILL DO IS GET RID
;OF THE NEWPLAYER CHECK, AND MAKE IT
;PRINT 5 BYTES AT $0FEE UNLESS $0FF0
;IS NOT A "." WHEREBY IT WILL PRINT
;"??.??".
;
;=======================================
;=======================================

;FIRST CHECK FOR AN EARLY NEWPLAYER
;VERSION WHERE THE XX.XX DOESNT APPEAR
;AT $0FEE

PRNEWPL LDX #<$E000   ;CHECK AGAINST
        LDY #>$E000   ;NEWPLAYER 05.02
        JSR CMPNEWP
        BCS CHK0600

        LDX #NP0502
        JMP GOTNEWP

CHK0600 LDX #<$E04E   ;CHECK AGAINST
        LDY #>$E04E   ;NEWPLAYER 06.00
        JSR CMPNEWP
        BCS CHK0601

        LDX #NP0600
        JMP GOTNEWP

CHK0601 LDX #<$E09C   ;CHECK AGAINST
        LDY #>$E09C   ;NEWPLAYER 06.01
        JSR CMPNEWP
        BCS CHK0700

        LDX #NP0601
        JMP GOTNEWP

CHK0700 LDX #<$E0EA   ;CHECK AGAINST
        LDY #>$E0EA   ;NEWPLAYER 07.00
        JSR CMPNEWP
        BCS CHK0800

        LDX #NP0700
        JMP GOTNEWP

CHK0800 LDX #<$E138   ;CHECK AGAINST
        LDY #>$E138   ;NEWPLAYER 08.00
        JSR CMPNEWP
        BCS CHK0801

        LDX #NP0800
        JMP GOTNEWP

CHK0801 LDX #<$E186   ;CHECK AGAINST
        LDY #>$E186   ;NEWPLAYER 08.01
        JSR CMPNEWP
        BCS CHK0802

        LDX #NP0801
        JMP GOTNEWP

CHK0802 LDX #<$E1D4   ;CHECK AGAINST
        LDY #>$E1D4   ;NEWPLAYER 08.02
        JSR CMPNEWP
        BCS CHK0803

        LDX #NP0802
        JMP GOTNEWP

CHK0803 LDX #<$E222   ;CHECK AGAINST
        LDY #>$E222   ;NEWPLAYER 08.03
        JSR CMPNEWP
        BCS CHK0901

        LDX #NP0803
        JMP GOTNEWP

CHK0901 LDX #<$E270   ;CHECK AGAINST
        LDY #>$E270   ;NEWPLAYER 09.01
        JSR CMPNEWP
        BCS UNKNOWN

        LDX #NP0901
        JMP GOTNEWP

;IT SEEMS THAT AFTER NEWPLAYER 09.01
;THE VERSION NUMBER WILL BE FOUND IN
;MEMORY AT $0FEE

UNKNOWN LDX #<$0FEE
        LDY #>$0FEE
        JMP GOTNEWP


;---------------
;THIS IS THE LITTLE ROUTINE THAT CHECKS
;THE #$4E BYTES FROM #Y WITH
;THOSE AT $0FA0. IF THEY ARE THE SAME
;THEN CARRY IS CLEARED, OTHERWISE CARRY
;IS SET IF THEY ARE DIFFERENT.

CMPNEWP STX $FB       ;SAVE ADDRESS TO
        STY $FC       ;($FB).W

        SEI           ;KERNAL OUT
        LDA #$35
        STA $01

        LDY #$00      ;COMPARE ALL THE
MORELP  LDA ($FB),Y   ;BYTES TO SEE IF
        CMP $0FA0,Y   ;THEY ARE THE SAME
        BNE DIFERNT
        INY
        CPY #$4E
        BNE MORELP

        CLC           ;THEY ARE THE SAME

JUMPKIN LDA #$37      ;KERNAL IN
        STA $01
        CLI

        RTS


DIFERNT SEC           ;THEY ARE DIFFERNT
        BCS JUMPKIN


;---------------
;NOW WE'VE GOT THE NEWPLAYER VERSION
;AS 5 SCREEN CODE BYTES POINTED TO BY
;X AND Y, SO PRINT THEM TO THE SCREEN
;AND QUIT.

GOTNEWP STX $FB       ;SAVE ADDRESS OF
        STY $FC       ;TEXT TO ($FB).W

        LDA #$B9      ;PRINT TO SCREEN
        STA $FE       ;AT $07B9 WHICH
        LDA #$07      ;IS ($FE).W
        STA $FF

        LDY #$00      ;COPY THE 5 BYTES
PRNLOOP LDA ($FB),Y   ;TO THE SCREEN
        STA ($FE),Y
        INY
        CPY #$05
        BNE PRNLOOP

        RTS


;---------------
;THE NEWPLAYER TEXTS

NP0502  .TEXT "05.02"
NP0600  .TEXT "06.00"
NP0601  .TEXT "06.01"
NP0700  .TEXT "07.00"
NP0800  .TEXT "08.00"
NP0801  .TEXT "08.01"
NP0802  .TEXT "08.02"
NP0803  .TEXT "08.03"
NP0901  .TEXT "09.01"


;=======================================
;=======================================
;LOAD MUSIC OR TABLES ROUTINE
;
;THE "ENTER READ FILENAME" PROMPT AND
;ANY LAST FILENAME ARE FADED ONTO THE
;INPUT LINE AT THETOP OF THE SCREEN.
;THE FILENAME CAN THEN BE ENTERED BY THE
;USER. WHEN THE RETURN KEY IS PRESSED,
;THE FILE IS LOADED IF IT EXISTS.
;
;THE TYPE OF FILE (MUSIC OR TABLES) IS
;DETERMINED BY ITS LOADING ADDRESS.
;A LOADING ADDRESS OF $0F00 IS A MUSIC,
;WHILE ANY OTHER IS TABLES.
;
;THERE ARE NO ROUTINES TO CHECK WHAT
;IS BEING LOADED, WHICH IS A BAD POINT.
;
;AFTER A *MUSIC* HAS BEEN LOADED, THE
;UNUSED PATTERNS AT THE END ARE CLEARED
;(I THINK).
;
;=======================================
;=======================================

;FIRST FADE IN THE TEXT TO THE INPUT
;LINE AT THE TOP OF THE SCREEN. NOTE
;THAT ANY PREVIOUS FILENAME WILL APPEAR
;IN THE INPUT LINE TOO.

LOADMUS LDA #LOADTXT
        STA $FC
        JSR FDTXTIN

;INPUT THE FILENAME ITSELF INTO THE
;INPUT LINE AT THE TOP OF THE SCREEN

        LDA #$3D      ;SCREEN ADDRESS
        STA $FB
        LDA #$04
        STA $FC
        LDA #FILNAME
        STA $FF
        LDA #$10      ;BUFFER SIZE
        JSR INPUTXT

;WORK OUT THE NUMBER OF CHARACTERS IN
;THE FILE NAME BY COUNTING PAST ANY
;SPACES AT THE END OF THE STRING.

        LDY #$10      ;INITIAL LENGTH
        STY CMDLEN    ;OF THE STRING
        DEY

NMCLOOP LDA ($FE),Y   ;CHECK FOR A SPACE
        CMP #$20      ;END IF ANY OTHER
        BNE GOTNUCS

        DEC CMDLEN    ;COUNT PAST THE
        DEY           ;SPACE
        BPL NMCLOOP

;FADE OUT THE TEXT FROM THE INPUT LINE.

GOTNUCS LDA #LOADTXT
        STA $FC
        JSR FDTXTOU


;---------------
;A FILENAME HAS BEEN TYPED IN, SO WE
;MUST CHECK WHETHER THIS FILE EXISTS
;AT ALL! FIRST, IF THERE IS NO NAME,
;THEN QUIT. OTHERWISE, TO CHECK THE
;FILE WE TRY TO GET THE FIRST TWO BYTES
;FROM IT AND CHECK THE DISK STATUS
;WHICH SHOULD BE 00,OK ETC.

        LDA CMDLEN    ;IF NO NAME, THEN
        BEQ JUMPRTS   ;QUIT

        LDX #FILNAME ;PARAMETERS
        JSR $FFBD     ;(NAMELENGTH,NAME)

        LDA #$08      ;SET LOGICAL FILE
        TAX           ;PARAMETERS
        LDY #$00      ;(FILENUM,DEVICE,
        JSR $FFBA     ;SECONDARY DEVICE)

        JSR LOADDRS   ;GET LOAD ADDRESS

        JSR PRTDSTS   ;PRINT DISK STATUS

        LDA $042A     ;CHECK WHETHER THE
        CMP #"0"      ;FILE EXISTS BY
        BNE JUMPRTS   ;COMPARING THE
        LDA $042B     ;PRINTED DISK
        CMP #"0"      ;STATUS WITH "00"
        BEQ LOADING

JUMPRTS RTS           ;QUIT IF NO FILE


;---------------
;PRINT "LOADING...", AND THEN FIGURE
;OUT EXACTLY WHAT IS BEING LOADED. THIS
;CAN BE DETERMINED FROM THE LOAD ADDRESS
;OF THIS FILE. IF THE LOAD ADDRESS IS
;$0F00 THEN A WHOLE MUSIC IS BEING
;LOADED, OTHERWISE ITS TABLES ONLY.

LOADING JSR CLRLINE   ;CLEAR INPUT LINE

        LDY #$00      ;PRINT THE LOADING
LINLOOP LDA LDINTXT,Y ;TEXT
        STA $042A,Y
        INY
        CPY #$0A
        BNE LINLOOP

        LDA LADDRLO   ;CHECK IF THE LOAD
        CMP #<$0F00   ;ADDRESS IS $0F00
        BNE LDTABLE   ;FOR WHOLE MUSIC
        LDA LADDRHI
        CMP #>$0F00
        BNE LDTABLE


;---------------
;LOAD THE ENTIRE MUSIC TO $0F00

        LDX #FILNAME ;PARAMETERS
        LDA CMDLEN    ;(NAMELENGTH,NAME)
        JSR $FFBD

        LDA #$00      ;SUPRESS KERNAL
        STA $9D       ;MESSAGES

        LDX #<$0F00   ;LOAD THE FILE
        LDY #>$0F00   ;TO $0F00
        JSR $FFD5

        JSR CLRLINE   ;CLEAR INPUT LINE

        JSR PRTDSTS   ;PRINT DISK STATUS

        LDA $042A     ;CHECK FOR ERRORS
        CMP #$30      ;BY CHECKING THE
        BNE LOADERR   ;PRINTED DISK
        LDA $042B     ;STATUS WITH "00"
        CMP #$30
        BNE LOADERR


;---------------
;ONLY PATTERNS WHICH ARE ACTUALLY USED
;ARE SAVED, SO ANY UNUSED PATTERNS FOR
;THIS SONG NEED TO BE CLEARED OR THE
;EXISTING DATA WILL STILL BE THERE.

        LDA $0FD2     ;POINTER TO THE
        STA $FE       ;UNUSED PATTERN
        LDA $0FD3
        STA $FF

        LDA $0FD2     ;POINTER TO THE
        STA $FB       ;LAST PATTERN USED
        LDA $AF
        STA $FC

ANOPTN  LDY #$00      ;COPY THE UNUSED
ANOBYT  LDA ($FE),Y   ;PATTERN
        STA ($FB),Y
        INY
        BNE ANOBYT

        INC $FC       ;CHECK IF WE'VE
        LDA $FC       ;FILLED UP ALL
        CMP #$9F      ;REMAINING PTNS
        BNE ANOPTN

LOADERR RTS


;---------------
;LOAD ONLY THE TABLES TO THE ADDRESS
;IN ($0FBC).W WHICH IS A POINTER IN
;THE NEWPLAYER TO SHOW WHERE THE TABLES
;START IN MEMORY.

LDTABLE LDX #FILNAME ;PARAMETERS
        LDA CMDLEN    ;(NAME,NAMELENGTH)
        JSR $FFBD

        LDA #$08      ;SET LOGICAL FILE
        TAX           ;PARAMETERS
        LDY #$00      ;(FILENUM,DEVICE,
        JSR $FFBA     ;SECONDARY DEVICE)

        LDA #$00      ;SUPRESS KERNAL
        STA $9D       ;MESSAGES

        LDX $0FBC     ;LOAD FILE TO
        LDY $0FBD     ;TABLES PART OF
        JSR $FFD5     ;MEMORY ($0FBC).W

        JSR CLRLINE   ;CLEAR INPUT LINE

        JMP PRTDSTS   ;PRINT DISK STATUS

        RTS


;----------------
;THIS LITTLE ROUTINE GETS THE LOAD ADDR
;OF THE FILE WHOSE FILENAME HAS ALREADY
;BEEN SET UP.

LOADDRS JSR $FFC0     ;OPEN FILE

        LDX #$08      ;DEFINE AN INPUT
        JSR $FFC6     ;CHANNEL

        LDA #$08      ;COMMAND SERIAL
        JSR $FFB4     ;BUS TO TALK

        LDA #$08      ;SEND SECONDARY
        JSR $FF96     ;ADDR AFTER TALK

        JSR $FFA5     ;GET 1ST AND 2ND
        STA LADDRLO   ;BYTES FROM THE
        JSR $FFA5     ;FILE
        STA LADDRHI

        JSR $FFCC     ;RESTORE DFLT DEVC

        LDA #$08      ;CLOSE FILE
        JSR $FFC3

        LDA #$00      ;CLEAR KEYBOARD
        STA $C6       ;BUFFER

        JMP $FFC6     ;DEFINE OUTPUT CHN


;---------------

LOADTXT .TEXT " READ - ENTER NAME: "
FILNAME .TEXT "                  "

CMDLEN  .BYTE $00

LADDRLO .BYTE $00
LADDRHI .BYTE $00

LDINTXT .TEXT "LOADING..."


;=======================================
;=======================================
;SAVE RAM TO A FILE
;
;THE "ENTER WRITE FILENAME" PROMPT AND
;ANY LAST FILENAME ARE FADED ONTO THE
;INPUT LINE AT THE TOP OF THE SCREEN.
;THE FILENAME CAN THEN BE ENTERED BY THE
;USER. WHEN THE RETURN KEY IS PRESSED,
;THE RAM BETWEEN THE SUPPLIED BEGINNING
;AND END ADDRESSES IS SAVED AS THE FILE.
;
;THE INPUT PARAMETERS ARE:
; - SVBEGLO AND SVBEGHI => START ADDRESS
; - SVENDLO AND SVENDHI => END ADDRESS
;
;=======================================
;=======================================

;FIRST FADE IN THE TEXT TO THE INPUT
;LINE AT THE TOP OF THE SCREEN. NOTE
;THAT ANY PREVIOUS FILENAME WILL APPEAR
;IN THE INPUT LINE TOO.

SAVEMUS LDA #SAVETXT
        STA $FC
        JSR FDTXTIN

;INPUT THE FILENAME ITSELF INTO THE
;INPUT LINE AT THE TOP OF THE SCREEN

        LDA #$3D      ;SCREEN ADDRESS
        STA $FB
        LDA #$04
        STA $FC
        LDA #SAVNAME
        STA $FF
        LDA #$10      ;BUFFER SIZE
        JSR INPUTXT

;WORK OUT THE NUMBER OF CHARACTERS IN
;THE FILE NAME BY COUNTING PAST ANY
;SPACES AT THE END OF THE STRING.

        LDY #$10      ;INITIAL LENGTH
        STY CMDLEN    ;OF THE STRING
        DEY

ANCLOOP LDA ($FE),Y   ;CHECK FOR A SPACE
        CMP #$20      ;END IF ANY OTHER
        BNE GOTANCS

        DEC CMDLEN    ;COUNT PAST THE
        DEY           ;SPACE
        BPL ANCLOOP

;FADE OUT THE TEXT FROM THE INPUT LINE.

GOTANCS LDA #SAVETXT
        STA $FC
        JSR FDTXTOU

;---------------
;IF NO FILENAME HAS BEEN TYPED IN THEN
;QUIT, OTHERWISE PRINT "SAVING...", AND
;THEN SAVE THE FILE WHICH IS THE MEMORY
;BETWEEN THE ALREADY SAVED BEGINNING
;AND END ADDRESSES.

        LDA CMDLEN    ;IN NO NAME THEN
        BNE SAVING    ;QUIT

        RTS

SAVING  JSR CLRLINE   ;CLEAR INPUT LINE

        LDY #$00      ;PRINT THE SAVING
SVNLOOP LDA SVINTXT,Y ;TEXT
        STA $042A,Y
        INY
        CPY #$09
        BNE SVNLOOP

        LDA CMDLEN    ;SET FILENAME
        LDX #SAVNAME ;(NAMELENGTH,NAME)
        JSR $FFBD

        LDA #$08      ;SET LOGICAL FILE
        TAX           ;PARAMETERS
        TAY           ;(FILENUM,DEVICE,
        JSR $FFBA     ;SECONDARY DEVICE)

        LDA #$00      ;SUPRESS KERNAL
        STA $9D       ;MESSAGES

        LDA SVBEGLO   ;SAVE THE FILE
        STA $FE       ;(START ADDRESS)
        LDA SVBEGHI
        STA $FF
        LDA #$FE      ;(PTR TO THESE)
        LDX SVENDLO   ;(END ADDRESS)
        LDY SVENDHI
        JMP $FFD8


;---------------

SAVETXT .TEXT " SAVE - ENTER NAME: "
SAVNAME .TEXT "                  "

SVBEGLO .BYTE $00
SVBEGHI .BYTE $00
SVENDLO .BYTE $00
SVENDHI .BYTE $00

SVINTXT .TEXT "SAVING..."


;=======================================
;=======================================
;SEND DISK COMMAND ROUTINE
;
;THE CURRENT TEXT IS FADED INTO THE
;INPUT LINE AT THE TOP OF THE SCREEN.
;THE DISK COMMAND THEN CAN BE INPUT BY
;THE USER. WHEN THE RETURN KEY IS
;PRESSED, THE DISK COMMAND IS DONE IF
;THERE WAS SOME TEXT TYPED IN.
;
;=======================================
;=======================================

;FIRST FADE IN THE TEXT TO THE INPUT
;LINE AT THE TOP OF THE SCREEN. NOTE
;THAT THERE MAY WELL BE TEXT IN THE LINE
;FROM A PREVIOUS COMMAND.

DISKCMD LDA #CMDTEXT
        STA $FC
        JSR FDTXTIN

;INPUT THE COMMAND TEXT ITSELF INTO THE
;INPUT LINE.

        LDA #$33      ;SCREEN ADDRESS
        STA $FB
        LDA #$04
        STA $FC
        LDA #COMMAND
        STA $FF
        LDA #$1B      ;BUFFER SIZE
        JSR INPUTXT

;WORK OUT THE NUMBER OF CHARACTERS IN
;THE INPUT STRING BY COUNTING PAST ANY
;SPACES AT THE END OF THE STRING.

        LDY #$1B      ;INITIAL LENGTH OF
        STY CMDLEN    ;THE STRING
        DEY

NCHLOOP LDA ($FE),Y   ;CHECK FOR A SPACE
        CMP #$20      ;END IF ANY OTHER
        BNE GOTNCHS

        DEC CMDLEN    ;COUNT PAST THE
        DEY           ;SPACE
        BPL NCHLOOP

;FADE OUT THE TEXT FROM THE INPUT LINE.

GOTNCHS LDA #CMDTEXT
        STA $FC
        JSR FDTXTOU

;THE DISK COMMAND IS DONE HERE IF THERE
;WAS ONE TYPED IN.

        LDA CMDLEN    ;GET NUM OF CHARS
        BNE DOCMD     ;IN THE COMMAND

        JMP PRTDSTS   ;STATUS IF NO CMD

DOCMD   LDX #COMMAND ;PARAMETERS (ACC
        JSR $FFBD     ;CONTAINS LENGTH)

        LDA #$0F      ;SET LOGICAL FILE
        TAY           ;PARAMETERS
        LDX #$08      ;(DEVNUM, FILENUM,
        JSR $FFBA     ;SECONDARY ADDR)

        JSR $FFC0     ;OPEN FILE

        JMP PRTCSTS   ;END & GET STATUS


CMDTEXT .TEXT " COMMAND: "
COMMAND .TEXT "              "
        .TEXT "              "


;=======================================
;=======================================
;DISK DIRECTORY ROUTINE
;
;THE SCREEN IS CLEARED AND THE DIRECTORY
;IS DISPLAYED UNLESS THE STOP KEY IS
;PRESSED.
;
;WHEN THE END OF THE DIRECTORY IS FOUND,
;ANY KEY WILL RETURN TO THE MAIN MENU.
;
;=======================================
;=======================================

DISKDIR LDA #$93      ;CLEAR THE SCREEN
        JSR $FFD2     ;(OUTPUT CLR/HOME)

        LDA #$01      ;SET FILENAME
        LDX #DOLLARS ;(NAMELENGTH,NAME)
        JSR $FFBD

        LDA #$01      ;SET LOGICAL FILE
        LDX #$08      ;PARAMETERS
        LDY #$60      ;(FILENUM,DEVICE,
        JSR $FFBA     ;SECONDARY DEVICE)

        JSR $FFC0     ;OPEN THE FILE
        BCC OPENOK

        LDA #$00      ;CLEAR KEYBOARD
        STA $C6       ;BUFFER

        RTS


OPENOK  LDX #$01      ;DEFINE AN INPUT
        JSR $FFC6     ;CHANNEL

        LDA #$08      ;COMMAND SERIAL
        JSR $FFB4     ;BUS TO TALK

        LDA #$60      ;SEND SECONDARY
        JSR $FF96     ;ADDR AFTER TALK

        JSR $FFA5     ;INPUT BYTES FROM
        JSR $FFA5     ;SERIAL BUS
NMLOOP  JSR $FFA5
        JSR $FFA5

        LDX $90       ;FINISH IF EOF OR
        BNE DIRDONE   ;DEV NOT PRESENT

        JSR $FFA5     ;GET 2 BYTES AND
        TAX           ;WRITE THIS NUMBER
        JSR $FFA5     ;TO SCRN IN ASCII
        JSR $BDCD

        LDA #$20      ;WRITE A SPACE
        JSR $FFD2

CHLOOP1 JSR $FFA5     ;OUTPUT THE CHARS
        LDX $90       ;IN THE FILENAME
        BNE DIRDONE
        JSR $FFD2
        BNE CHLOOP1

        LDA #$0D      ;OUTPUT A RETURN
        JSR $FFD2

        JSR $FFEA     ;UPDATE SW CLOCK

        JSR $FFE1     ;CHECK STOP KEY
        BNE NMLOOP


DIRDONE JSR $FFCC     ;DEFAULT DEVICES

        LDA #$01      ;CLOSE FILE
        JSR $FFC3

        LDA #$00      ;CLEAR KEYBOARD
        STA $C6       ;BUFFER

        JSR $FFC6     ;DEFINE INPUT CHAN

WAITKEY JSR $FFE4     ;WAIT FOR ANY KEY
        BEQ WAITKEY

        RTS


DOLLARS .TEXT "$"


;=======================================
;=======================================
;ROUTINE TO INPUT TEXT TO THE INPUT LINE
;AT THE TOP OF THE SCREEN.
;
;THE CURRENT INSERTION POINT IS SHOWN BY
;AN INVERSED CHARACTER. THE ROUTINE WILL
;REACT TO EACH NEW KEYPRESS TO THAT:
;
; - RETURN WILL END THE ROUTINE
; - CRSR LEFT/RIGHT WILL MOVE THE CURSOR
; - INS/DEL WILL INSERT/DELETE CHARS
; - CLR WILL CLEAR THE WHOLE LINE
; - ANY CHAR KEY WILL PRINT THAT CHAR
;
;THE INPUTS ARE:
; ($FB).W = ADDR OF SCREEN TO PRINT TEXT
; ($FE).W = ADDR OF STRING BUFFER
;     ACC = MAX SIZE OF THE STRING
;
;THE OUTPUTS ARE: STRING IS PUT IN THE
; - THE STRING BUFFER IS FILLED
; - THE STRING IS PRINTED ON THE SCREEN
;
;=======================================
;=======================================

INPUTXT STA MAXCPOS   ;SAVE MAX CRSRPOS
        LDA #$00
        STA CRSRPOS   ;INIT CUR CRSRPOS
        STA DELAYLO   ;INIT FLASH DLA LO
        STA DELAYHI   ;INIT FLASH DLA HI


INPUTLP JSR $FFE4     ;GET A CHARACTER

        BNE GOTCHAR   ;IF NONE THEN DO
        JMP FLASHIT   ;FLASH CRSR ROUT


GOTCHAR JSR INVSON    ;INVERSE CURR CHAR


;---------------
;CURSOR RIGHT

;IF CRSR RIGHT IS PRESSED THEN THE CHAR
;UNDER THE CURRENT INSERTION POINT IS
;UN-INVERSED AND THE CURRENT INSERTION
;POINT IN MOVED RIGHT IF IT CAN BE.
;(IE THE CURSOR WILL FLASH IN THIS NEW
;POSITION)

        CMP #$1D      ;CHECK FOR CRSRGHT
        BNE CHKLEFT

        JSR INVSOFF   ;INV OFF CURR CHAR

        LDA CRSRPOS   ;CHECK IF CURSOR
        CLC           ;CAN GO ANY
        ADC #$01      ;FURTHER RIGHT
        CMP MAXCPOS
        BEQ JUMPFLN

        INC CRSRPOS   ;CRSR RIGHT IF OK

JUMPFLN JMP FLSHNOW   ;FLASH NOW POS NOW


;---------------
;CURSOR LEFT

;IF CURSOR LEFT IS PRESSED THEN THE CHAR
;UNDER THE CURRENT INSERTION POINT IS
;UN-INVERSED AND THE CURRENT INSERTION
;POINT IN MOVED LEFT IF IT CAN BE.
;(IE THE CURSOR WILL FLASH IN THIS NEW
;POSITION)

CHKLEFT CMP #$9D      ;CHECK FOR CRSRLFT
        BNE CHKCLR

        JSR INVSOFF   ;INV OFF CURR CHAR

        LDA CRSRPOS   ;CHECK IF CRSR CAN
        BEQ FARLEFT   ;GO FURTHER LEFT

        DEC CRSRPOS   ;CRSR LEFT IF OK

FARLEFT JMP FLSHNOW


;---------------
;CLEAR LINE

;IF CLR IS PRESSED THEN THE ENTIRE INPUT
;LINE IS CLEARED (BOTH ON THE SCREEN AND
;IN THE STRING BUFFER) AND THE CURSOR
;POSITION IS SET TO 0.

CHKCLR  CMP #$93      ;CHECK FOR CLR
        BNE CHKINS

        LDY #$00      ;SET CRSR POSITION
        STY CRSRPOS   ;TO 0

        LDA #$20      ;DELETE THE ENTIRE
CTLOOP  STA ($FB),Y   ;LINE FROM SCREEN
        STA ($FE),Y   ;AND STRING BUFFER
        INY
        CPY MAXCPOS
        BNE CTLOOP

        JMP FLSHNOW


;---------------
;INSERT CHARACTER

;IF INSERT IS PRESSED THEN ALL THE CHARS
;RIGHT OF AND INCLUDING THE CRSRPOS ARE
;MOVED UP ONE PLACE IF THERE IS ROOM.
;THIS MEANS THAT THE CHAR FURTHEST RIGHT
;MUST BE A SPACE WHICH WILL BE NUKED.

;IF THESE IS NO ROOM THEN NOTHING WILL
;HAPPEN.

;IF THERE IS ROOM THEN THE NEW INSERTED
;CHARACTER WILL BE SET TO A SPACE.

CHKINS  CMP #$94      ;CHECK FOR INSERT
        BNE CHKDEL

        JSR INVSOFF   ;INV OFF CURR CHAR

        LDY MAXCPOS   ;CHECK IF THERE IS
        DEY           ;ROOM TO INSERT
        LDA ($FB),Y   ;(IE THE LAST CHAR
        CMP #$20      ;(IS NOT A SPACE)
        BNE JUMPFLN

        DEC CRSRPOS   ;CHARS ABOVE&INCL
        DEY           ;CRSRPOS ARE MOVED

MVLOOP  LDA ($FB),Y   ;COPY SCREEN CHAR
        INY           ;UP ONE POSITION
        STA ($FB),Y
        DEY

        LDA ($FE),Y   ;COPY STRING BUFF
        INY           ;CHAR UP ONE POS
        STA ($FE),Y
        DEY

        DEY           ;REPEAT FOR ALL
        CPY CRSRPOS   ;CHARS ABOVE THE
        BNE MVLOOP    ;ORIGINAL CRSRPOS

        INY           ;INSERT A SPACE IN
        LDA #$20      ;THE NEW VACANT
        STA ($FB),Y   ;POSITION
        STA ($FE),Y

        INC CRSRPOS   ;CORRECT CRSRPOS

        JMP FLSHNOW


;---------------
;DELETE CHARACTER

;IF DELETE IS PRESSED THEN THE CHARACTER
;BELOW THE CRSRPOS IS NUKED (THATS IF
;THE CRSRPOS ISNT ALREADY FAR LEFT) AND
;ALL THE CHARACTERS ABOVE AND INCLUDING
;THE CHAR UNDER THE CRSRPOS ARE MOVED
;DOWN ONE PLACE.

CHKDEL  CMP #$14      ;CHECK FOR DELETE
        BNE CHKRET

        LDY CRSRPOS   ;CANT DELETE IF
        BEQ FLSHNOW   ;AT CRSRPOS 0

LCDA8   LDA ($FB),Y   ;COPY SCREEN CHAR
        DEY           ;DOWN ONE POSITION
        STA ($FB),Y
        INY

        LDA ($FE),Y   ;COPY STRING BUFF
        DEY           ;CHAR DOWN ONE POS
        STA ($FE),Y
        INY

        INY           ;REPEAT FOR ALL
        CPY MAXCPOS   ;CHARS ABOVE THE
        BNE LCDA8     ;CURSORPOS

        DEY           ;PUT A SPACE IN
        LDA #$20      ;NEW VACANT BYTE
        STA ($FB),Y
        STA ($FE),Y

        DEC CRSRPOS   ;SET NEW CRSRPOS

        LDY #$00      ;WE DONT FLASH THE
        STY DELAYHI   ;CURSOR RIGHT NOW
        JMP INPUTLP   ;BUT JUST CONTINUE


;---------------
;RETURN KEY ENDS INPUT

;IF RETURN IS PRESSED THEN THE CHARACTER
;UNDER THE INSERTION POINT IS UNINVERSED
;AND THIS ROUTINE ENDS.

CHKRET  CMP #$0D      ;CHECK FOR RETURN
        BNE CHKOTHR

        JMP INVSOFF   ;INV OFF AND END


;---------------
;OTHER CHARACTERS

;ANY OTHER PRINTABLE CHARACTER (AND SOME
;OTHERS!!) IS PUT UNDER THE CURRENT
;INSERTION POINT AND THIS CRSRPOS IS
;MOVED TO THE RIGHT ONE PLACE IF THAT
;IS POSSIBLE.

CHKOTHR BMI FLASHIT   ;FILTER OUT SHIT

        LDY CRSRPOS   ;SAVE THIS CHAR TO
        STA ($FB),Y   ;THE SCREEN AND
        STA ($FE),Y   ;THE STRING BUFFER

        LDA CRSRPOS   ;CHECK IF CRSR POS
        CLC           ;IS AT THE END OF
        ADC #$01      ;LINE ALREADY
        CMP MAXCPOS
        BEQ FLSHNOW

        INC CRSRPOS   ;CRSR RIGHT IF NOT

        JMP FLSHNOW


;---------------
;JUMPS HERE FROM ABOVE IF NO KEY HAS
;BEEN PRESSED. THE CHARACTER UNDER THE
;CRSRPOS WILL FLASH AFTER A DELAY HAS
;COUNTED DOWN.

FLASHIT INC DELAYLO   ;CHECK IF ITS TIME
        BNE FLSHNOT   ;TO FLASH YET
        INC DELAYHI
        LDA DELAYHI
        CMP #$26
        BNE FLSHNOT

;FLASH THE CHARACTER UNDER THE CRSRPOS
;NOW BY EORING IT WITH $80.

FLSHNOW LDY #$00      ;RESET DELAY AND
        STY DELAYHI   ;FLASH THE CHAR
        LDY CRSRPOS   ;UNDER THE CRSRPOS
        LDA ($FB),Y
        EOR #$80
        STA ($FB),Y

;JUMP BACK TO THE INPUT LOOP TO CHECK
;FOR A KEY AGAIN.

FLSHNOT JMP INPUTLP
