OS SERIES 8 GEOFF COX ************************************************************************* * * * OSBYTE &F7 (247) INTERCEPT BREAK * * * ************************************************************************* EAD9 LDA &0287 ;get BREAK vector code EADC EOR #&4C ;produces 0 if JMP not in &287 EADE BNE &EAF3 ;if not goto EAF3 EAE0 JMP &0287 ;else jump to user BREAK code ************************************************************************* * * * OSBYTE &90 (144) *TV * * * ************************************************************************* ; on entry, X=vertical display offset ; Y=interlace ; on exit, XY=previous settings ; EAE3 LDA &0290 ;VDU vertical adjustment EAE6 STX &0290 ;store new value EAE9 TAX ;put old value in X EAEA TYA ;put interlace flag in A EAEB AND #&01 ;maximum value =1 EAED LDY &0291 ;get old value into Y EAF0 STA &0291 ;put new value into A EAF3 RTS ;and Exit ************************************************************************* * * * OSBYTE &93 (147) WRITE TO FRED * * * ************************************************************************* ;X is offset within page ;Y is byte to write EAF4 TYA ; EAF5 STA &FC00,X ; EAF8 RTS ; ************************************************************************* * * * OSBYTE &95 (149) WRITE TO JIM * * * ************************************************************************* ;X is offset within page ;Y is byte to write ; EAF9 TYA ; EAFA STA &FD00,X ; EAFD RTS ; ************************************************************************* * * * OSBYTE &97 (151) WRITE TO SHEILA * * * ************************************************************************* ;X is offset within page ;Y is byte to write ; EAFE TYA ; EAFF STA &FE00,X ; EB02 RTS ; ****************** Silence a sound channel ******************************* ;X=channel number EB03 LDA #&04 ;mark end of release phase EB05 STA &0808,X ;to channel X EB08 LDA #&C0 ;load code for zero volume ****** if sound not disabled set sound generator volume ****************** EB0A STA &0804,X ;store A to give basic sound level of Zero EB0D LDY &0262 ;get sound output/enable flag EB10 BEQ &EB14 ;if sound enabled goto EB14 EB12 LDA #&C0 ;else load zero sound code EB14 SEC ;set carry EB15 SBC #&40 ;subtract &40 EB17 LSR ;divide by 8 EB18 LSR ;to get into bits 0 - 3 EB19 LSR ; EB1A EOR #&0F ;invert bits 0-3 EB1C ORA &EB3C,X ;get channel number into top nybble EB1F ORA #&10 ; EB21 PHP ; EB22 SEI ;disable interrupts EB23 LDY #&FF ;System VIA port A all outputs EB25 STY &FE43 ;set EB28 STA &FE4F ;output A on port A EB2B INY ;Y=0 EB2C STY &FE40 ;enable sound chip EB2F LDY #&02 ;set and EB31 DEY ;execute short delay EB32 BNE &EB31 ; EB34 LDY #&08 ;then disable sound chip again EB36 STY &FE40 ; EB39 LDY #&04 ;set delay EB3B DEY ;and loop delay EB3C BNE &EB3B ; EB3E PLP ;get back flags EB3F RTS ;and exit *******: Sound parameters look up table ********************************** DB &E0 ; channel 0 register address DB &C0 ; channel 1 register address DB &A0 ; channel 2 register address DB &80 ; channel 3 register address EB44 JMP &EC59 ;just to allow relative branches in early part ;of sound interrupt routine ************************************************************************* * * * PROCESS SOUND INTERRUPT * * * ************************************************************************* EB47 LDA #&00 ; EB49 STA &083B ;zero number of channels on hold for sync EB4C LDA &0838 ;get number of channels required for sync EB4F BNE &EB57 ;if this <>0 then EB57 EB51 INC &083B ;else number of chanels on hold for sync =1 EB54 DEC &0838 ;number of channels required for sync =255 EB57 LDX #&08 ;set loop counter EB59 DEX ;loop EB5A LDA &0800,X ;get value of &800 +offset (sound queue occupancy) EB5D BEQ &EB44 ;if 0 goto EC59 no sound this channel EB5F LDA &02CF,X ;else get buffer busy flag EB62 BMI &EB69 ;if negative (buffer empty) goto EB69 EB64 LDA &0818,X ;else if duration count not zer0 EB67 BNE &EB6C ;goto EB6C EB69 JSR &EC6B ;check and pick up new sound if required EB6C LDA &0818,X ;if duration count 0 EB6F BEQ &EB84 ;goto EB84 EB71 CMP #&FF ;else if it is &FF (infinite duration) EB73 BEQ &EB87 ;go onto EB87 EB75 DEC &081C,X ;decrement 10 mS count EB78 BNE &EB87 ;and if 0 EB7A LDA #&05 ;reset to 5 EB7C STA &081C,X ;to give 50 mSec delay EB7F DEC &0818,X ;and decrement main counter EB82 BNE &EB87 ;if not zero then EB87 EB84 JSR &EC6B ;else check and get nw sound EB87 LDA &0824,X ;if step progress counter is 0 no envelope involved EB8A BEQ &EB91 ;so jump to EB91 EB8C DEC &0824,X ;else decrement it EB8F BNE &EB44 ;and if not zero go on to EC59 EB91 LDY &0820,X ;get envelope data offset from (8C0) EB94 CPY #&FF ;if 255 no envelope set so EB96 BEQ &EB44 ;goto EC59 EB98 LDA &08C0,Y ;else get get step length EB9B AND #&7F ;zero repeat bit EB9D STA &0824,X ;and store it EBA0 LDA &0808,X ;get phase counter EBA3 CMP #&04 ;if release phase completed EBA5 BEQ &EC07 ;goto EC07 EBA7 LDA &0808,X ;else start new step by getting phase EBAA CLC ; EBAB ADC &0820,X ;add it to interval multiplier EBAE TAY ;transfer to Y EBAF LDA &08CB,Y ;and get target value base for envelope EBB2 SEC ; EBB3 SBC #&3F ; EBB5 STA &083A ;store modified number as current target amplitude EBB8 LDA &08C7,Y ;get byte from envelope store EBBB STA &0839 ;store as current amplitude step EBBE LDA &0804,X ;get base volumelevel EBC1 PHA ;save it EBC2 CLC ;clear carry EBC3 ADC &0839 ;add to current amplitude step EBC6 BVC &EBCF ;if no overflow EBC8 ROL ;double it Carry = bit 7 EBC9 LDA #&3F ;if bit =1 A=&3F EBCB BCS &EBCF ;into &EBCF EBCD EOR #&FF ;else toggle bits (A=&C0) ;at this point the BASIC volume commands are converted ; &C0 (0) to &38 (-15) 3 times, In fact last 3 bits ;are ignored so &3F represents -15 EBCF STA &0804,X ;store in current volume EBD2 ROL ;multiply by 2 EBD3 EOR &0804,X ;if bits 6 and 7 are equal EBD6 BPL &EBE1 ;goto &EBE1 EBD8 LDA #&3F ;if carry clear A=&3F (maximum) EBDA BCC &EBDE ;or EBDC EOR #&FF ;&C0 minimum EBDE STA &0804,X ;and this is stored in current volume EBE1 DEC &0839 ;decrement amplitude change per step EBE4 LDA &0804,X ;get volume again EBE7 SEC ;set carry EBE8 SBC &083A ;subtract target value EBEB EOR &0839 ;negative value undicates correct trend EBEE BMI &EBF9 ;so jump to next part EBF0 LDA &083A ;else enter new phase EBF3 STA &0804,X ; EBF6 INC &0808,X ; EBF9 PLA ;get the old volume level EBFA EOR &0804,X ;and compare with the old EBFD AND #&F8 ; EBFF BEQ &EC07 ;if they are the same goto EC07 EC01 LDA &0804,X ;else set new level EC04 JSR &EB0A ;via EB0A EC07 LDA &0810,X ;get absolute pitch value EC0A CMP #&03 ;if it =3 EC0C BEQ &EC59 ;skip rest of loop as all sections are finished EC0E LDA &0814,X ;else if 814,X is not 0 current section is not ;complete EC11 BNE &EC3D ;so EC3D EC13 INC &0810,X ;else implement a section change EC16 LDA &0810,X ;check if its complete EC19 CMP #&03 ;if not EC1B BNE &EC2D ;goto EC2D EC1D LDY &0820,X ;else set A from EC20 LDA &08C0,Y ;&820 and &8C0 (first envelope byte) EC23 BMI &EC59 ;if negative there is no repeat EC25 LDA #&00 ;else restart section sequence EC27 STA &0830,X ; EC2A STA &0810,X ; EC2D LDA &0810,X ;get number of steps in new section EC30 CLC ; EC31 ADC &0820,X ; EC34 TAY ; EC35 LDA &08C4,Y ; EC38 STA &0814,X ;set in 814+X EC3B BEQ &EC59 ;and if 0 then EC59 EC3D DEC &0814,X ;decrement EC40 LDA &0820,X ;and pick up rate of pitch change EC43 CLC ; EC44 ADC &0810,X ; EC47 TAY ; EC48 LDA &08C1,Y ; EC4B CLC ; EC4C ADC &0830,X ;add to rate of differential pitch change EC4F STA &0830,X ;and save it EC52 CLC ; EC53 ADC &080C,X ;ad to base pitch EC56 JSR &ED01 ;and set new pitch EC59 CPX #&04 ;if X=4 (last channel) EC5B BEQ &EC6A ;goto EC6A (RTS) EC5D JMP &EB59 ;else do loop again EC60 LDX #&08 ;X=7 again EC62 DEX ;loop EC63 JSR &ECA2 ;clear channel EC66 CPX #&04 ;if not 4 EC68 BNE &EC62 ;do it again EC6A RTS ;and return ; EC6B LDA &0808,X ;check for last channel EC6E CMP #&04 ;is it 4 (release complete) EC70 BEQ &EC77 ;if so EC77 EC72 LDA #&03 ;else mark release in progress EC74 STA &0808,X ;and store it EC77 LDA &02CF,X ;is buffer not empty EC7A BEQ &EC90 ;if so EC90 EC7C LDA #&00 ;else mark buffer not empty EC7E STA &02CF,X ;an store it EC81 LDY #&04 ;loop counter EC83 STA &082B,Y ;zero sync bytes EC86 DEY ; EC87 BNE &EC83 ;until Y=0 EC89 STA &0818,X ;zero duration count EC8C DEY ;and set sync count to EC8D STY &0838 ;&FF EC90 LDA &0828,X ;get synchronising flag EC93 BEQ &ECDB ;if its 0 then ECDB EC95 LDA &083B ;else get number of channels on hold EC98 BEQ &ECD0 ;if 0 then ECD0 EC9A LDA #&00 ;else EC9C STA &0828,X ;zero note length interval EC9F JMP &ED98 ;and goto ED98 ECA2 JSR &EB03 ;silence the channel ECA5 TYA ;Y=0 A=Y ECA6 STA &0818,X ;zero main count ECA9 STA &02CF,X ;mark buffer not empty ECAC STA &0800,X ;mark channel dormant ECAF LDY #&03 ;loop counter ECB1 STA &082C,Y ;zero sync flags ECB4 DEY ; ECB5 BPL &ECB1 ; ECB7 STY &0838 ;number of channels to &FF ECBA BMI &ED06 ;jump to ED06 ALWAYS ECBC PHP ;save flags ECBD SEI ;and disable interrupts ECBE LDA &0808,X ;check for end of release ECC1 CMP #&04 ; ECC3 BNE &ECCF ;and if not found ECCF ECC5 JSR &E45B ;elseexamine buffer ECC8 BCC &ECCF ;if not empty ECCF ECCA LDA #&00 ;else mark channel dormant ECCC STA &0800,X ; ECCF PLP ;get back flags ECD0 LDY &0820,X ;if no envelope 820=&FF ECD3 CPY #&FF ; ECD5 BNE &ECDA ;then terminate sound ECD7 JSR &EB03 ;via EB03 ECDA RTS ;else return ************ Synchronise sound routines ********************************** ECDB JSR &E45B ;examine buffer if empty carry set ECDE BCS &ECBC ; ECE0 AND #&03 ;else examine next word if>3 or 0 ECE2 BEQ &EC9F ;goto ED98 (via EC9F) ECE4 LDA &0838 ;else get synchronising count ECE7 BEQ &ECFE ;in 0 (complete) goto ECFE ECE9 INC &0828,X ;else set sync flag ECEC BIT &0838 ;if 0838 is +ve S has already been set so ECEF BPL &ECFB ;jump to ECFB ECF1 JSR &E45B ;else get first byte ECF4 AND #&03 ;mask bits 0,1 ECF6 STA &0838 ;and store result ECF9 BPL &ECFE ;Jump to ECFE (ALWAYS!!) ECFB DEC &0838 ;decrement 0838 ECFE JMP &ECD0 ;and silence the channel if envelope not in use ************ Pitch setting *********************************************** ED01 CMP &082C,X ;If A=&82C,X then pitch is unchanged ED04 BEQ &ECDA ;then exit via ECDA ED06 STA &082C,X ;store new pitch ED09 CPX #&04 ;if X<>4 then not noise so ED0B BNE &ED16 ;jump to ED16 *********** Noise setting ************************************************ ED0D AND #&0F ;convert to chip format ED0F ORA &EB3C,X ; ED12 PHP ;save flags ED13 JMP &ED95 ;and pass to chip control routine at EB22 via ED95 ED16 PHA ; ED17 AND #&03 ; ED19 STA &083C ;lose eigth tone surplus ED1C LDA #&00 ; ED1E STA &083D ; ED21 PLA ;get back A ED22 LSR ;divide by 12 ED23 LSR ; ED24 CMP #&0C ; ED26 BCC &ED2F ; ED28 INC &083D ;store result ED2B SBC #&0C ;with remainder in A ED2D BNE &ED24 ; ;at this point 83D defines the Octave ;A the semitone within the octave ED2F TAY ;Y=A ED30 LDA &083D ;get octave number into A ED33 PHA ;push it ED34 LDA &EDFB,Y ;get byte from look up table ED37 STA &083D ;store it ED3A LDA &EE07,Y ;get byte from second table ED3D PHA ;push it ED3E AND #&03 ;keep two LS bits only ED40 STA &083E ;save them ED43 PLA ;pull second table byte ED44 LSR ;push hi nybble into lo nybble ED45 LSR ; ED46 LSR ; ED47 LSR ; ED48 STA &083F ;store it ED4B LDA &083D ;get back octave number ED4E LDY &083C ;adjust for surplus eighth tones ED51 BEQ &ED5F ; ED53 SEC ; ED54 SBC &083F ; ED57 BCS &ED5C ; ED59 DEC &083E ; ED5C DEY ; ED5D BNE &ED53 ; ED5F STA &083D ; ED62 PLA ; ED63 TAY ; ED64 BEQ &ED6F ; ED66 LSR &083E ; ED69 ROR &083D ; ED6C DEY ; ED6D BNE &ED66 ; ED6F LDA &083D ; ED72 CLC ; ED73 ADC &C43D,X ; ED76 STA &083D ; ED79 BCC &ED7E ; ED7B INC &083E ; ED7E AND #&0F ; ED80 ORA &EB3C,X ; ED83 PHP ;push P ED84 SEI ;bar interrupts ED85 JSR &EB21 ;set up chip access 1 ED88 LDA &083D ; ED8B LSR &083E ; ED8E ROR ; ED8F LSR &083E ; ED92 ROR ; ED93 LSR ; ED94 LSR ; ED95 JMP &EB22 ;set up chip access 2 and return **************** Pick up and interpret sound buffer data ***************** ED98 PHP ;push flags ED99 SEI ;disable interrupts ED9A JSR &E460 ;read a byte from buffer ED9D PHA ;push A ED9E AND #&04 ;isolate H bit EDA0 BEQ &EDB7 ;if 0 then EDB7 EDA2 PLA ;get back A EDA3 LDY &0820,X ;get envelope setting for this channel EDA6 CPY #&FF ;check for &FF=no envelope EDA8 BNE &EDAD ;envelope is in use, keep playing it EDAA JSR &EB03 ;no envelope, so call EB03 to silence channel EDAD JSR &E460 ;clear buffer of redundant data, get ignored pitch EDB0 JSR &E460 ;get ignored duration EDB3 PLP ;get flags back EDB4 JMP &EDF7 ;set main duration count using last byte from buffer EDB7 PLA ;get back A EDB8 AND #&F8 ;zero bits 0-2, Hold and Sync bits EDBA ASL ;put bit 7 volume/envelope flag into carry EDBB BCC &EDC8 ;if zero (envelope) jump to EDC8 EDBD EOR #&FF ;invert A EDBF LSR ;shift right EDC0 SEC ; EDC1 SBC #&40 ;subtract &40 EDC3 JSR &EB0A ;and set volume EDC6 LDA #&FF ;A=&FF EDC8 STA &0820,X ;get envelope no.-1 *16 into A EDCB LDA #&05 ;set duration sub-counter EDCD STA &081C,X ; EDD0 LDA #&01 ;set phase counter EDD2 STA &0824,X ; EDD5 LDA #&00 ;set step counter EDD7 STA &0814,X ; EDDA STA &0808,X ;and envelope phase EDDD STA &0830,X ;and pitch differential EDE0 LDA #&FF ; EDE2 STA &0810,X ;set step count EDE5 JSR &E460 ;get pitch from buffer EDE8 STA &080C,X ;set it EDEB JSR &E460 ;get duration from buffer EDEE PLP ; EDEF PHA ;save duration EDF0 LDA &080C,X ;get back pitch value EDF3 JSR &ED01 ;and set it EDF6 PLA ;get back duration EDF7 STA &0818,X ;set it EDFA RTS ;and return ********************* Pitch lookup table 1***************************** EDFB DB &F0 EDFC DB &B7 EDFD DB &82 EDFE DB &4F EDFF DB &20 EE00 DB &F3 EE01 DB &C8 EE02 DB &A0 EE03 DB &7B EE04 DB &57 EE05 DB &35 EE06 DB &16 ********************* Pitch lookup table 2 ***************************** EE07 DB &E7 EE08 DB &D7 EE09 DB &CB EE0A DB &C3 EE0B DB &B7 EE0C DB &AA EE0D DB &A2 EE0E DB &9A EE0F DB &92 EE10 DB &8A EE11 DB &82 EE12 DB &7A *********: set current filing system ROM/PHROM ************************** EE13 LDA #&EF ;start with ROM &F0-1 EE15 STA &F5 ;store it EE17 RTS ;return ********** Initialise ROMFS or PHROM ROM *************************************** EE18 LDX #&0D ;X=13 EE1A INC &F5 ;increment current ROM number EE1C LDY &F5 ;get ROM EE1E BPL &EE59 ;if +ve it's a sideways ROM else it's a PHROM EE20 LDX #&00 ;PHROM EE22 STX &F7 ;set address pointer in PHROM EE24 INX ; EE25 STX &F6 ;to 0001 EE27 JSR &EEBB ;pass info to speech processor EE2A LDX #&03 ;X=3 EE2C JSR &EE62 ;check for speech processor and output until ;it reports, read byte from ROM EE2F CMP &DF0C,X ;if A <> DF0C+X then EE18 (DF0C = (C)) EE32 BNE &EE18 ; EE34 DEX ;else decrement X EE35 BPL &EE2C ;and do it again EE37 LDA #&3E ; EE39 STA &F6 ;set lo byte address EE3B JSR &EEBB ;select address and PROM from &F5/6/7 EE3E LDX #&FF ;read to bytes, to &F6 and &F7 EE40 JSR &EE62 ;read byte from this address from PHROM EE43 LDY #&08 ;8 bits to rotate EE45 ASL ;reverse the byte into &F6 on first pass EE46 ROR &F7,X ;and then into &F7 on the second pass EE48 DEY ; EE49 BNE &EE45 ;loop for 8 bits EE4B INX ; EE4C BEQ &EE40 ;loop for two bytes EE4E CLC ; EE4F BCC &EEBB ;jump to set PHROM address to this address we've just read ************ ROM SERVICE ************************************************ EE51 LDX #&0E ; EE53 LDY &F5 ;if Y is negative (PHROM) EE55 BMI &EE62 ;GOTO EE62 EE57 LDY #&FF ;else Y=255 ; BUG! If ROMFS 15 (ROM 0) has been scanned, &F5 incremented to &10 all the ; way to &EF and back through PHROMs and back to ROMFS 0 (ROM 15) and loops ; through ROMFS ROMs again. ; Should do CPY #16, BCS return EE59 PHP ;push flags EE5A JSR &F168 ;offer paged rom service EE5D PLP ;pull processor flags EE5E CMP #&01 ;if A>0 set carry EE60 TYA ;A=Y EE61 RTS ;return ********* PHROM SERVICE ************************************************* ; EE62 PHP ;push processor flags EE63 SEI ;disable interrupts EE64 LDY #&10 ;Y=&10 - Read Byte EE66 JSR &EE7F ;Send command to Speech Processor EE69 LDY #&00 ;Y=0 EE6B BEQ &EE84 ;Jump to EE84 to read resultant byte from Speech ************************************************************************* * * * OSBYTE 158 read from speech processor * * * ************************************************************************* EE6D LDY #&00 ;Y=0 to set speech proc to read EE6F BEQ &EE82 ;jump to EE82 always ;write A to speech processor as two nybbles EE71 PHA ;push A EE72 JSR &EE7A ;to write to speech processor EE75 PLA ;get back A EE76 ROR ;bring upper nybble to lower nybble EE77 ROR ;by rotate right EE78 ROR ;4 times EE79 ROR ; EE7A AND #&0F ;Y=lo nybble A +&40 EE7C ORA #&40 ; EE7E TAY ;forming command for speech processor ************************************************************************* * * * OSBYTE 159 Write to speech processor * * * ************************************************************************* ; on entry data or command in Y EE7F TYA ;transfer command to A EE80 LDY #&01 ;to set speech proc to write ;if Y=0 read speech processor ;if Y=1 write speech processor EE82 PHP ;push flags EE83 SEI ;disable interrupts EE84 BIT &027B ;test for presence of speech processor EE87 BPL &EEAA ;if not there goto EEAA EE89 PHA ;else push A EE8A LDA &F075,Y ; EE8D STA &FE43 ;set DDRA of system VIA to give input (Y=0) or output (Y=1) EE90 PLA ;get command back in A EE91 STA &FE4F ;and send to speech chip, write to PortA EE94 LDA &F077,Y ; Get A=&01 for read, A=&02 for write EE97 STA &FE40 ; Write to PortB, A=&01 set VP_READ, A=&02 set VP_WRITE EE9A BIT &FE40 ; Test System VIA PortA, bit 7 is VP_RDY EE9D BMI &EE9A ; Loop until Speech Processor reports ready EE9F LDA &FE4F ; Read Speech Processor data from Port A EEA2 PHA ; Save it - will be returned data for read, data written for write EEA3 LDA &F079,Y ; Get A=&09 for read, A=&0A for write EEA6 STA &FE40 ; Write to PortB, A=&09 clear VP_READ, A=&0A clear VP_WRITE EEA9 PLA ; Get data byte back EEAA PLP ; Restore IRQs EEAB TAY ; Transfer A to Y to set flags EEAC RTS ; And return ; EEAD LDA &03CB ;set rom displacement pointer EEB0 STA &F6 ;in &F6 EEB2 LDA &03CC ; EEB5 STA &F7 ;And &F7 EEB7 LDA &F5 ;if F5 is +ve ROM is selected so EEB9 BPL &EED9 ;goto EED9 EEBB PHP ;else push processor EEBC SEI ;disable interrupts EEBD LDA &F6 ;get lo displacement EEBF JSR &EE71 ;pass two nybbles to speech proc. EEC2 LDA &F5 ;&FA=&F5 = ROM number or SOUND command &80-&FF EEC4 STA &FA ; EEC6 LDA &F7 ;get hi displacement value EEC8 ROL ;replace two most significant bits of A EEC9 ROL ;by 2 LSBs of &FA EECA LSR &FA ; EECC ROR ; EECD LSR &FA ; EECF ROR ;A= now %rraaaaaa EED0 JSR &EE71 ;pass two nybbles to speech processor EED3 LDA &FA ;FA has now been divided by 4 so pass EED5 JSR &EE7A ;lower nybble to speech proc., passing %xxrr EED8 PLP ;get back flags EED9 RTS ;and Return