!!$ 6( 5! 0$ 8! 5( <! .#+ .# 3 7+ 8# 4) ;( 0$! :$ 1!$ >& 1)($ 8! 3$ 6! >$& :(& 4$ 5 0$ 8 8$& 8& 0$# <$ 4&( 4) ;& 0) 0( 7&()& 7- 8& 8!( 1& 0$ 4! 3#$ 2& 3#!(!$ 6( 5! 0$ 8! 5( <! .&(#+# .# 3! 7+ 8&# 4$) ;( 0$! :#$ 1!!$ >& 1)+(($ 8! 3&$ 6! >(+$& :-!/!$ 6( 5! 0$ 8! 5( <! .-+#+* .# 3& 7+ 8# 4() ;( 0$! :$ 1!*$ >& 1)+($ 8! 3-$ 6! >+*$& :+(& 4$ 5 0$ 8 8&+$& 8-*& 0$+(# <$ 4&&( 4) ;&& 0) 0(( 7&()& 7- 8& 8!( 1/& 0$ 4! 3-#$ 2& 3+#$+(& 4$ 5 0&$ 8 8+$& 8&& 0$# <$ 4-&&( 4) ;/& 0) 0(&( 7&()& 7-- 8& 8+ 1/&-& 0# 4+! 3-#$ 2+& 3*&#$!(&$#+(+-!-!($(   +($ (&+$&$(#&!$ #!#+&  (#  &#-&+&($!+&+-(+--&#(+$&&($&(-++(+&(+(   ##&+&#!)$)-+(($  (+  !)&)-&!&))#+&+)()+#!$!$&(&$!+&#  +&+&$  +&&+&!-(&$#$##! ! /! ! !#!$+$&(&$##$&&+!(-          #    !      !        !        !    !  ! ! !! !!  !! ! !!  ! !  !  !! ! !$! ! !!  !#  !  !   !  !  !  !  $   !  !& & !&   !&  !  !& & !&  !&  !  !  !!    / ! ?  #+  !  !&  ! 8 !! !(  !! ! !!  ! !  !  !  !! ! ! !-! +! #(! + !+  !  !   !&  !  !  !#  $ 0  !$  !& & !&   !&  !  !!& & !&  #&  !$  !  !   !!  ##  !  !  ! 8 !! !$ # !!! ! !!  ! ! ! !  !! ! !! ! !!  !# ! !# & !  # !! # !  !  !  $ # & !( & !$& !& !&   !&  !( & !$& !& !&  !#  !$ ! !  !   ! $ #& # !  !  ! 8 !! !- + !(! $! !!  !! !$ & !+  ! & !  !#!  !$  !! $ !  !! $ !& ( !  ( !, ( $ #   !& $ !!  !  !! ! !( & !( + ! ( !& $ !( & !( + ! ( !&! $ ## $ !&  ! # ! 8! !! !$ # !$! &! ! !  ! !$  !#  !!! ! !! ! !! ! !$ # !$ & !  $ !& ( ! + !   !+ ( $ & $ !+ ( !+& -& !&   !(& & !$ ! !$& && !(& & !$ ! !$   !!  !$  ! !$ & #+ - !( & !$ !& ! 8# !! !- + !-! $! !!  (! !) ( !&  !  !  !-! $ !+$ $ !$ ($ !$  !) ( !&  !$  !#  $ # & !( & !$ & !  !#! $ !) ( !  ) !(  !$ ) !#   ! 0 # ( !   ( !# 0   # 0 # ( !   ( !# 0    ! 8& 0 !( (! - $ + %# ( 6!! & "! ( !  & $! $ 6 ! "! $  ! ' $ 4! & 5! ( '! & ! $ #! ! 5 + $# & ! + "# - 8& / 3  - $# / ! + &# + 2 & # & ' #  # " # 3 ! #  $& $ 6( ! %& $ $& & #!& ( 8&  & $ ( & ) ' ) 8( $ "& ) $& + $!& - 7& + % ) # $ # ) 4$ $ %! ) 7 + " -   + % ) 4 - "$ + 5& & # + $ - 5 /  - " + 8 & FP! - %$ + # ( #$! + #&! +  !  ( &! & 4$ ( & ( 7# $ " ! !! $ 3! $ $! & ! ( !! - 2! + $$ & 6# # #$ $ 5& & $  $ $ # "& + 7( - # + + ( 5  & ! ( 2+ & ( $ #& & 1$ ) 4+ ( ( $ #+& ! 7-& $ $&  !  $ &(& & & ) $$ ( &! ) 7$& + && - !(& + 5& ) $$ $ 3! ! $  1  ! ! $ # ) $  $ !! ! 7$ $ & # #+  5- # $( & 1& + $ - $&!( #!& 1! !- ( !+! -! !-!  (! ! - !   !-   !- - ! ! - !& / $  !! / $ !& / $  !! / $ !#  !  !- + $ ( & ! & !&  !&  !+! & !& $ !! $ !!  !  !( : # = !  # , !( ? #  ! - # . !& ? !+  !( * !&  !# = ! / !  !# :! 0(  !!  ! ! !! ! ! # & ( 0 *   &    #! !  0 +  &#& & (& & && # & ( +     0*  (  & ! !- ( !+! -! !-!  (! ! - !   !-   !- - ! ! - !- / +  !( / & !( / &  !$ / ! !( - ! , !  $ , , !  & !&  !&  !+! & !& $ !! $ !!  !  !( : * = !,  ( , !,  /  !/ - , . !& ! 8 ! 0 ! ( ! - !  !  ! ! 0/  !!  ! ! +!! * (  0 *   -    +! !  0 (  &&  & & && *   +     0* + ( & # &! 0(  !!  ! !  /!!             ! !    &&  & & &&    $ $$ $ $ $ 0 $ $ $$ $ $! 0$   !!  ! !  /!!             ! !    &&  & & &&    $ $$ $ $ $ 0!!$!$!!$!$!$!$! 0!$ $!!!$!! /!! $  0!$&!!!&!!!!! 0!$&!!$&!&!$ 0!$$$$!$!$!$!$!$!$!$$!$! 0!$ $!!!$!!!!$ 0!$&!& ! 0!& !(&!&!$!!& !$!& 0!&$&!!$&!&!$ $& 0!# $&!#!!!#!!!!(! !! 0!$ $!!!-$ !!+!!(+$  0!*$&!& ! 0!& !-&!*!(*!& !$!! 0!$$&!!$&!&!($&$ 0!# $&!#!!!#!!!!(!+ (!& ?! ?! ?$ > > > = =$ = < < <! ; ;! ; : : : 9 9! 9 8 8 8 7 7$ 7! 6 6 6 5 5! 5 /! 4 4 4 3 3! 3  2 2 2 1 1$ 1  0 0 0 / /! / . . . - -$ - , , , + +! + * * * ) )! ) ( (! ( ' ' ' & &! & % %! % $ $ $ # #! # " " "! ! !! ! !   !      $    &  !      !      $ &     ! &     !      $      !     $  !  !  !      !   !      !   !     ! KG4UvWSNߛhEEeff4tNΊheUfvwxW4tNΚhUUVvwfDNifVUefhFCN͊gVUefxFCNjwVUeexFCNハwUUeeF4N*geUefF3N͚hfUVfV3tNhVUUfyF3tN͚xVUeexF#NzxVUUexF#NxVUUeG#NzxVUfUG#NzxfUVUV2N{fUUUF#NzyfUUTF"NzxWeFTF"N̛WUVTF"tN͚VeETF!NォgfESG2NͫgfESF#Nͫgf5SF"Nͫgf5SF#tNͻff4SF"uNͻgg4Ty5BNиͼgg#Sv62NͼwW3Sv52NݼwG3Svy52N͊F#Svy52N͛G#Cuy52NݛW#Bey52uNΫF"Bux5"NݬF"2ux5"uNݬV"2dx52NݼG"2uh53NW#!dw63uNW#2dwECN޽W#2dwgECNW#2dwgDCNW#2dvgECN͚W32TvfECNΫh$3TvgETN͛h43TffEUNάy43CefVDvN޻yE3CUfUEN޼F33TfVUvN̊FD3TeUUN޼zF43DUUUN͊VD3DUUUwvN͊VD3CUUewN͉WE3BDEegN͊fE#2SEgNȼ͚gF32TTvgN͛xV$2CEvguNp͛xV#"CTwWNpݛf#3SvWNp͚g#2SvWNpͪh#!"TwFNpͪh$!"TFuNpݼh!ewUNpݻi#1dhVNpͻy4"1uwVNݽi42vwVNݼi4BvwUNͫh4SvgUNͫh4SvwVNݼy5SvxfNݼz5CvfNݼz6CvxfN޼6BvxfNެ6RvxfN%SvgfNpެxʼ5!CvgVNpޛx̋%!SvgUNpߛg̊%"SwgUM`߬w̋6"SvgUuN`w݋$"SvVDN`w5"SfF3uNpw53Sf42Np{53TV$2xNzEDeV#CެxNߊVDeV3CݫxNߋWDeF#CͫxNߛgUeF3C̛xN߫hUfV3CuxMޫhegE4CuhNެxvwE3CugN޼xwwE33ufN޽yxE#2ufNxE32uyVNixF#2uxVN͚xE#!dxVvNp͚V#!dxUvMpzF#2dhUM`W!chUMPͻX!dWDM`̛W1uxVDNpݼ̛X1uxVDNpݫW1vhEDNpͼݫHBgEDNpͻެXBxECNpެGCg5CNp̼ޭ7!Sg4CN`̼'Sg#BN`̻$!tF"BNp53uF#CNpETE#CNpFTF#CNpVUF#CNpgeF#CN`ߋfvy5"S} 񺬺xuBa ˻kH%bTdkxBU"4 ۻkTe5AvɭgUhFBz܉ɘgEd˛7yX11wÿwTxwiRwٻgj2fiܫuJcwigWvuxʼU{E˫sAwpYEWFDdX`7RV65Ww*ITv65EqɩjۋXRF5Tydf(YGAp&=7IFvGɃ`w4[ڭEB(pGd&ۍuȻ$q|yB4ڪZ4D`8u$ewpHfGu6gEvqHTdh˩hTvApudvy}nytfVvlwhUfgUEvgȭwzEgv{UwwyUf˛Yye`٭y65뫇iDdA`zFCwctQ͍yɚdw&4#`JDTX5a˘˪ftxXD3eQ뛛̝iUf!DyP݋TxCwɽ{cXSvQx5TDVeQ˚Qx%FgD`˭YVTiXVe`޺VguUީTXTQޫHTfe7DdwpXfVu޻VgEupϪWfFTߛVfxEp޻XVfEުXf6tagUcgxG4#`HuVrϫ7#A`雓ꋔ7kTKsaΫy#tW#`޺9uGaκ8%rp:uWRG6caݪ[5ew$1s`ʾFw&aμ4w#`7v&bͬEQ͜FT62wPݬ7FSܝG#A̙XcWyU22w@ޫW5cߪdz$AͻWevF$DAϫxevxwE3vPzvyyTwܪzvyidAޛwgwxEfwg0ًwU7wwEg1ygWyvfvT싙gfu&vNyCdTTN*UνwUufUeN:UͼfgevfeN:UͼyfgUvfeNIDμyVWUvfeNШYDܼyfWUvfeNYDܼyfVUvfeNYCyfVUvfeNiCܽyfVUfffMiDܼyfVUvffNjDܽyfVUffvNz4͉fUUfffN{4͊fEUffvNzD˼gEUffvNp{5͉fEUffvNpE̊gEUffvNpE͊gEUffvN`E͊gEUffvN`E͊gEUffvN@VwffUVvN he̛xwfwwwǡYʻ+1yc5wDUwTSTSmqܬ{v8DESzvᨚͪxfVD$"SVv͚gE31Svߛg4BvΊW3Bݚf5""!CѩͫgE3!2T޽hE#"!Sv߼W4!TgD#"!SvgE""SvQze31dfTQͻhU5"2SUQ̛xG4#!Su1ɺ{E%!2dFu1̭y{V1dUQފF3D3#1vQF#"#CaxgE$1Cew1ݼhfF41DIQ߽hD"SS޻yVD"!TvqݛfVU41eQݼwxg5!Twxaܭh$!B4"SqͫX#!2TUfvq͚xg$2Tww޼G3"2ETeqgE#"3U޼hU#!2TvǑެV4#"TvDZͫg53"CeͻgE$"BU͚wF3!3SΫV4#BeݫVE#"3eͬg4#!Cd͋gV#!Cuqͻf4"!BDujiBxETj"%1X"d%1jj`0YhgELPxwzwyilxwgK 1Ȕjijli o[yKnsfQ yjxYuS ΨxwڪkvHwFkvWyijjxhi\YjyƘؗwjJګ8XkjyxẙyYewzzy{kyyhȨəyyy{HHte$CvwwwwwwtuxhEDeV$=Y!TtttUt%t5t5t5333333#"""!"t%!""!"333"3t%tt!!t5""""t"!""""""""t5GXD"Ieސހ@0J`XʪGgTgxItPzXev|eUtzPʗʫfuqizwp@ϊ8ZyxVjGj K0vxKֶiiizzˉId%BHhޠޠF*۴ZwZd_fȼTfvXސސppW挄u~7V]zj=O]śfwTʆސZoJY̕tv;e{vg\E7grfZkpPPPPWphHη8zxr7WFbϩZu{xTUh&tpixX5Ɨ\DVx@P@yQ션fIwd=800tpo[uzguk@0@o|Hw˶>BP[˅ȋdȺYyFtPnLjIyֈihk D0xux[iDy0XX4\ƦlF#Xuڜ%Aɬ%cɌ%s#Z2H2XTiu#gX3GCy#gvi4dWD˛#﫹XTy4SX#t#qyEWDuhE#QyyVG2dhu#AXTG2x#1ΉyEdxU#1zfEdgv#ݛjDewEuh#ޫz$Bf#ݜiBgv#VUg#ޫxvgw#߼hEʫx###S*ʊ5fgVuB5w(uyhd؛eBȚVuYSB຋UxyfEBjvzJgHvByw[kgjHBnrx{46n88i͇:rtXnШSsZwvZen̫f6ew{'UoɮTr\yhGeo˜#d{wuvۯ̫iSw5Adhvt尙ͨyFexxj&੻SvAVyxЙy2vWvtgwyʙI3AyoˆGwewfwnЩw%S̉Ebvo͛2uhb5toڽW#ziwBxnwgXXXo̪7UiyIrnкܙxeZSnhxw5fxnfH{XwuXzyn𙫩YUjfzo{jgQ۫heyWnغxUvz4Tqn݉'UȚ{s퐚]xYec̺The˨ʊwU7UvxZS5uqۙ]i6u;vTE&S(fڌXɯxsx|zeISfuXٜE{xXv8RhW[sjVrEɜ#zuXuyc[UyuW8VZt&ȻVjbٜhU6wY7dͼxzeF3Teݼ8BttTwa˭zETܛ&B3r񈛚6#TVuzޠʫhh4U8vހuKCe{uSxސHˋGvEXਛUFvJyX苘Z57ܼXghX\{Y$鼜jruxXؽVyVWeX}iehʙ1ytߊHjhc3'fXиiTfvg˝zT*BXڮe8J3evX𹊉txueyhXyguhɪauxXٹQwzxJfxXؽidȌ2uyX𙭛wuee۫BgXw1HȜXWHvWɬy7{wjvgXvzgYUʻ%bWy%EEvixfuWϪxCdzeYDX฼z6cw%uW͊I5s˜xf5vWz{$uzwWW੼j2izxw3vXɻtiv6s!9DDDDDDDDDD@DDDDDDDDDDDD@DDDDDDD@DDDDDDDDDDDDDDDDDDDDD@DD@DDDDDD@DDDD@D@DDDDDDDD@DDDDDDD@DDDDDDDD@DDDDDD@DDDDDDDDDDD@DDDDUUPPUUUUUPUPPPUUUUUUUUUUPUUUUUPUUUPPUUUUPUPUUUPUUUUPUPUUUUUUUUPUPUUUUUUUUUPPPUUUUUPUUPPUUUUPUPUUUPUUUUUPUPUUUUUUPUPUUUPPfffffff`ffffff`fff`fffffff`ffffffffffffff`fff`fffffffffffffffffffffff`fffffff`fffff`fffffff`fff`fffffffffffffffff`ffffffwwwpwwpwwwwwwwwpwwwwwpwwpwwwwwwwwpwwwwwwwpwwpwwpwwwwwwwwwwpwwpwwwwwwwwpwwpwwwwwpwwpwwpwwwwwwwwwwwwwwwwpwwwwwpwwwwwwwwwwwffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'-- title: ticmodplayer -- author: Gasman / Wavesitter -- desc: MOD playback on TIC-80 -- script: lua -- 1=start address of sample (0-based) -- 2=sample length in frames -- 3=address to repeat from (0-based) -- 4=repeat length in frames -- 5=sample's native note frequency samples_meta = { {0,116,1368,40,27}, {2088,67,2088,0,36}, {3294,21,3294,0,27}, {3672,41,4122,16,40}, {4410,4,4410,0,30}, {4482,45,4482,0,35}, {5292,17,5292,0,33}, {5598,19,5598,0,33}, {5940,38,5940,0,33}, {6624,20,6624,0,38}, {6984,35,7002,34,33}, {7614,36,7758,27,29}, {8262,36,8262,0,29}, {8910,36,8910,0,29}, {9558,36,9558,0,29}, {10206,36,10206,0,29}, {10854,36,10854,0,29}, {11502,36,11502,0,29}, {12150,36,12150,0,29}, {12798,36,12798,0,29}, {13446,36,13446,0,29}, {14094,36,14094,0,29}, {14742,36,14742,0,29}, {15390,36,15390,0,29}, {16038,36,16038,0,29}, {16686,36,16686,0,29}, {17334,36,17334,0,29}, {17982,36,17982,0,29}, {18630,36,18630,0,29}, {19278,36,19278,0,29}, {19926,36,19926,0,29} } positions = {0,1,2,3,4,5,6,6,7,8,9,10,11,13,12,14,15,16,12,14,17,19,18,20,21,22,23,23,24,25,23,26,27} -- 1=sample address pointer -- 2=sample number -- 3=frames left until sample ends or repeats -- 4=semitone shift from sample's native frequency -- 5=vol multiplier channel_states = { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, } t=0 row_duration = 1.2*6 next_row_time = 0 row_num = -1 position_num = 1 pattern_num = positions[1] pattern_data_start_addr = 16384 sample_data_start_addr = 45056 function play_frame() if next_row_time <= t then -- read new row row_num = row_num + 1 if row_num == 64 then -- read new pattern row_num = 0 position_num = (position_num % #positions) + 1 pattern_num = positions[position_num] end pattern_addr = pattern_data_start_addr + pattern_num * 64 * 4 * 4 row_addr = pattern_addr + row_num * 4 * 4 for chan=1,4 do cell_addr = row_addr + (chan - 1) * 4 note_num = peek(cell_addr) if note_num ~= 255 then sample_num = peek(cell_addr + 1) sample_meta=samples_meta[sample_num] channel_states[chan][1] = sample_meta[1]+sample_data_start_addr channel_states[chan][2] = sample_num channel_states[chan][3] = sample_meta[2] channel_states[chan][4] = note_num - sample_meta[5] effect = peek(cell_addr + 2) if effect == 0x0c then param = peek(cell_addr + 3) channel_states[chan][5] = param / 64 else channel_states[chan][5] = 1 end if effect == 0x0f then param = peek(cell_addr + 3) if param <= 32 then row_duration = 1.2*param else row_duration = 900/param end end end end -- advance next_row_time next_row_time = next_row_time + row_duration end for chan=1,4 do chan_addr = 0xff9c+(chan-1)*18 state=channel_states[chan] if state[2] > 0 then sample_meta=samples_meta[state[2]] if state[3]==0 then -- sample end reached if sample_meta[4] > 0 then -- repeating state[1]=sample_meta[3]+sample_data_start_addr state[3]=sample_meta[4] else -- non-repeating - stop sample state[2]=0 end end end if state[2]>0 then addr=state[1] b1=peek(addr) b2=peek(addr+1) addr = addr + 2 wave_freq=b1|((b2 & 0x0f) << 8) freq=((wave_freq*(2^(state[4]/12)))+0.5)//1 wave_vol=b2>>4 vol=wave_vol*state[5]//1 poke(chan_addr,freq&0xff) poke(chan_addr+1,(freq>>8)|(vol<<4)) chan_addr = chan_addr + 2 for i=0,15 do poke(chan_addr+i,peek(addr)) addr = addr + 1 end state[1]=addr state[3]=state[3] - 1 else poke(chan_addr+1,0) end end t=t+1 end scrolltext = "Hello! I'm Gasman, and you're listening to Guitarous by The Warlock / Grace... quite possibly the first MOD file I ever listened to, from some music collection that ran on a PC speaker (erk). So it seemed quite appropriate to borrow it for the world's first MOD player for TIC-80. The TIC-80 still doesn't do real sample playback, so this is a continuation of the technique from my Mercedes Benz demo: first, chop each sample into 1/60s slices, and do some statistical analysis on it to work out where the waveform repeats. (Which seems like it should be FFT, but... isn't, really.) Then take one of those waves, and squish it down into the 32 nibbles that the TIC-80 provides for a waveform on a single frame. Do that 60 times a second, and as long as that sample is something close to a musical note, we can play it back in a more or less recognisable form. Even better, we can pitch-shift it by altering the playback frequency... and combining that with the TIC-80's four channels, you've got everything you need for a MOD player. It's a very limited one, of course (not least since I've only been hacking on it over the Christmas holidays) - there's some hacky code to deal with drum samples, trying to work out when the pitch matching breaks down and ham-fistedly dropping some noise in instead. Hardly any effects are implemented right now, and we're also constrained by the TIC-80 memory map, so there's a 48Kb limit on the crunched sample and pattern data. Is this the future of music on the TIC-80? Probably not, assuming people don't want their soundtracks to sound like vacuum cleaners full of nails. But at the end of the day all we're really doing here is massively increasing the 16 waveforms that the native TIC-80 tracker gives us... I reckon that if we used this technique on original tracks written with the TIC-80 in mind, with some combination of sampled, synthesised and simple chip instruments - rather than expecting an automatic conversion to do a good job across the board - we could get some really nice-sounding results, and maybe even bless the TIC-80 with its very own 'signature sound'. That would be pretty awesome, wouldn't it? Maybe we can make 2023 the year that we really start pushing TIC-80 demos beyond the arena of size-coding :-) Greetings to Field-FX, Slipstream, TUHB, ScenePT All-Stars, Bitshifters, RBBS, Poo-Brain, MountainBytes, Marquee Design, pestis, ilmenit, dave84, p01 and my fellow Demozoo conspirators. Happy holidays and a prosperous 2023 to you all! Gasman signing off at the end of 2022... " scroll_x = 0 scroll_next_char_index = 1 scroll_buffer = " " function get_width(s) return print(s, 0, -100, 6, false, 2) end scroll_first_char_width = get_width(string.sub(scroll_buffer, 1, 1)) scroll_buffer_width = get_width(scroll_buffer) function oscilloscope() cls() for chan=0,3 do addr = 0xff9c+chan*18 a=0 b1 = peek(addr) b2 = peek(addr+1) freq = b1 | ((b2 & 0x0f) << 8) ampl = b2 >> 4 step = freq/480 wave_addr = (addr + 2) * 2 for x=0,239 do v = ampl * (peek4(wave_addr + a) - 7) pix(x, v/16 + chan*24 + 16, chan+4) a = (a + step) % 32 end end end function firescope() for y=0,95 do for x=0,239 do v = (pix(x,y)+pix(x-1,y+1)+pix(x,y+1)+pix(x+1,y+1))/4.1 pix(x, y, v) end end for chan=0,3 do addr = 0xff9c+chan*18 a=0 b1 = peek(addr) b2 = peek(addr+1) freq = b1 | ((b2 & 0x0f) << 8) ampl = b2 >> 4 step = freq/480 wave_addr = (addr + 2) * 2 for x=0,119 do v = ampl * (peek4(wave_addr + a) - 7) pix(x + 120*(chan%2), v/16 + (chan//2)*48 + 32, 12) a = (a + step) % 32 end end -- clear scroller area rect(0,96,240,135,0) end function zoomscope() for y=0,47 do for x=0,119 do pix(119-x,47-y,pix(119-x*1.1-1,47-y*1.1-1)) pix(120+x,47-y,pix(120+x*1.1+1,47-y*1.1-1)) pix(120+x,48+y,pix(120+x*1.1+1,48+y*1.1+1)) pix(119-x,48+y,pix(119-x*1.1-1,48+y*1.1+1)) end end for chan=0,3 do addr = 0xff9c+chan*18 a=0 b1 = peek(addr) b2 = peek(addr+1) freq = b1 | ((b2 & 0x0f) << 8) ampl = b2 >> 4 step = freq/480 wave_addr = (addr + 2) * 2 rot = time() / 2000 + math.pi * chan / 2 for i=0,239 do x = i - 120 y = ampl * (peek4(wave_addr + a) - 7) / 8 + 32 pix(120 + x*math.cos(rot) + y*math.sin(rot), 48 + y*math.cos(rot) - x*math.sin(rot), ampl) a = (a + step) % 32 end end -- clear scroller area rect(0,96,240,135,0) end waves = {} for chan=0,3 do waves[chan] = {} end function circularscope() for chan=0,3 do addr = 0xff9c+chan*18 a=0 b1 = peek(addr) b2 = peek(addr+1) freq = b1 | ((b2 & 0x0f) << 8) ampl = b2 >> 4 step = freq/480 wave_addr = (addr + 2) * 2 for x=0,239 do waves[chan][x] = ampl * (peek4(wave_addr + a) - 7) / 128 a = (a + step) % 32 end end t0=time()/1000 for x=0,239 do for y=0,96 do x0 = x - 119.5 y0 = y - 47.5 z = (math.atan2(x0,y0) / math.pi * 119 + 120) // 1 band = math.sqrt(x0*x0+y0*y0) / 24 - t0 pix(x,y,(band+waves[(band+0.5)%4//1][z]/2)%4+8) end end -- clear scroller area rect(0,96,240,135,0) end function TIC() play_frame() fx = (time() // 15360) % 4 if fx == 0 then oscilloscope() elseif fx == 1 then firescope() elseif fx == 2 then circularscope() else zoomscope() end if -scroll_x >= scroll_first_char_width then scroll_buffer = string.sub(scroll_buffer, 2) scroll_x = scroll_x + scroll_first_char_width scroll_buffer_width = scroll_buffer_width - scroll_first_char_width scroll_first_char_width = get_width(string.sub(scroll_buffer, 1, 1)) end if scroll_buffer_width < 260 then next_char = string.sub(scrolltext, scroll_next_char_index, scroll_next_char_index) scroll_buffer_width = scroll_buffer_width + get_width(next_char) scroll_buffer = scroll_buffer .. next_char scroll_next_char_index = (scroll_next_char_index % #scrolltext) + 1 end print(scroll_buffer, scroll_x, 110, 6, false, 2) scroll_x = scroll_x - 2 end