( mp3.tal ) ( commands are ended by newlines: ) ( ) ( help show all these commands ) ( load FILE load FILE and start playing ) ( loadpaused FILE load FILE without playing ) ( pause pause, or unpause, playback ) ( stop stops playback, unloads file ) ( seek # seeks to sample # ) ( seek +N | seek -N seeks forward, or back, N samples ) ( seek +Ns | seek -Ns seeks forward, or back, N seconds ) ( jump # jumps to MPEG frame # ) ( jump +N | jump -N jumps forward, or back, N frames ) ( jump +Ns | jump -Ns jumps forward, or back, N seconds ) ( volume P set volume to P percent, 0-100 ) ( mute mute playback ) ( unmute unmute playback ) ( tag print all ID3 data; seeking back may remove tag data ) @Console [ |10 &vector $2 |12 &read $1 ( 13 - 14 padding ) |15 &live $1 |15 &exit $1 |17 &type $1 |18 &write $1 |19 &error $1 ( 1a - 1b padding ) |1c &addr $2 |1e &mode $1 |1f &exec $1 ] |0100 ( initialize buffer ) ;buffer ;buffer/pos STA2 ( run mpg123 ) ;on-console .Console/vector DEO2 ;program .Console/addr DEO2 ( cmd addr ) #03 .Console/mode DEO ( cmd mode ) #01 .Console/exec DEO ( exec ) ;cmd1 print ;cmd2 print BRK @printerr ( s* -> ) LDAk ?{ #0a .Console/error DEO POP2 JMP2r } LDAk .Console/error DEO INC2 !printerr @print ( s* -> ) LDAk ?{ POP2 JMP2r } LDAk .Console/write DEO INC2 !print @on-console ( -> brk ) .Console/type DEI #01 EQU ?{ BRK } ( ) .Console/read DEI #0a EQU ?&newline ( ) ;buffer/pos LDA2k STH2k ( pos* buf* [buf*] ) .Console/read DEI STH2r STA ( pos* buf ; buf<-c ) INC2 SWP2 STA2 BRK ( ; pos<-buf+1 ) &newline ( ) #00 ;buffer/pos LDA2 STA on-line ( ; buf<-0, run on-line ) ;buffer ;buffer/pos STA2 BRK ( ) ( called when a newline is reached ) ( buffer is guaranteed to be null-terminated ) ( newline is implied but not included ) @on-line ( -> ) ;buffer LDAk LIT "@ EQU ?{ POP2 JMP2r } INC2k LDA LIT "F EQU ?on-frame ( INC2k LDA LIT "H EQU ?on-help ) ( INC2k LDA LIT "I EQU ?on-id3 ) ( INC2k LDA LIT "P EQU ?on-paused ) ( INC2k LDA LIT "R EQU ?on-revision ) ( INC2k LDA LIT "S EQU ?on-status ) ( INC2k LDA LIT "T EQU ?on-tag ) !printerr ( === START NEW CODE === ) ( e.g. "@F 184 8713 4.81 227.60" ) ( seen when playing ) ( the fields here are: ) ( @F ) ( ) ( debugging output on stderr is: ) @on-frame ( buf0* -> ) #0003 ADD2 ( buf1* ; skip "@F " ) #20 find-next INC2 ( buf2* ; skip curr frame ) #20 find-next INC2 ( buf3* ; skip next frame ) parse-dec STH2 INC2 ( buf4* [curr*] ; parse curr time ) LDAk LIT "5 LTH ?{ INC2r } ( buf4* [curr*] ; maybe round curr ) #20 find-next INC2 ( buf5* [curr*] ; skip fractional part ) parse-dec STH2 INC2 ( buf4* [curr* rem*] ; parse total time ) LDAk LIT "5 LTH ?{ INC2r } ( buf4* [curr* rem*] ; maybe round rem ) POP2 STH2r STH2r SWP2 ( curr* rem* ) OVR2 emit #2019 DEO ( curr* rem* ; print curr ) DUP2 emit #2019 DEO ( curr* rem* ; print rem ) OVR2 ADD2 SWP2 ( total* curr* ) calc-width emit #0a19 DEO ( width* ) JMP2r ( width* ) ( emit a short in hexadecimal notation. ) @emit ( n* -> ) SWP /byte &byte DUP #04 SFT /hex #0f AND ( >> ) &hex #30 ADD DUP #39 GTH #27 MUL ADD .Console/error DEO JMP2r ( returns value between 0000 and 00d0 ) ( ) ( the basic idea is that we shift both values left until ) ( the total is small enough that it can be multiplied by ) ( 0xd0, i.e. 208. we take some care to round correctly, ) ( which helps give a smoother transition between values. ) @calc-width ( total* curr* -> width* ) STH2 ( total* [curr*] ) DUP2 #013c LTH2 ?&ready &scale DUP2 #0277 LTH2 ?&almost #01 SFT2 LITr 01 SFT2r !&scale &almost INC2 #01 SFT2 INC2r LITr 01 SFT2r &ready LIT2r 00d0 MUL2r STH2r SWP2 DIV2 JMP2r ( find the next occurrence of a byte in the given string. ) ( ) ( returns the position of the character found. ) @find-next ( buf* c^ -> buf+i* ) STH &loop LDAk STHkr EQU ?{ INC2 !&loop } POPr JMP2r ( parse a decimal number as a short ) ( ) ( returns an updated buffer position and the parsed short ) @parse-dec ( buf* -> buf+i* x* ) LIT2r 000a LIT2r 0000 ( buf* [10* acc*] ) &loop LDAk LIT "0 SUB ( buf+i* n^ [10* acc*] ) DUP #09 GTH ?&done ( buf+i* n^ [10* acc*] ) OVR2r MUL2r #00 SWP ( buf+i* n* [10* 10acc*] ) STH2 ADD2r INC2 !&loop ( buf+i+1* [10* 10acc+n*] ) &done POP STH2r POP2r JMP2r ( buf+i* acc* ) ( === END NEW CODE === ) ( e.g. "@H HELP/H: command listing (LONG/SHORT forms), command case insensitve" ) ( seen in response to the "help" command ) @on-help ( buf* -> ) POP2r JMP2r ( e.g. "@I ID3v2.artist:Chipzel" ) ( seen when loading a track ) @on-id3 ( buf* -> ) POP2 JMP2r ( e.g. "@P 0" or "@P 1" ) ( seen when pausing or unpausing ) @on-paused ( buf* -> ) POP2 JMP2r ( e.g. "@R MPG123 (ThOr) v10" ) ( seen on start up ) @on-revision ( buf* -> ) POP2 JMP2r ( e.g. "@S 1.0 3 44100 Joint-Stereo 0 1044 2 0 0 0 320 0 1" ) ( seen when playback starts ) @on-status ( buf* -> ) POP2 JMP2r ( e.g. "@T ID3v2.TPE1:" ) ( seen in response to the "tag" command for extended tag info ) @on-tag ( buf* -> ) POP2 JMP2r @program "mpg123 20 "-R 00 @cmd1 "loadpaused 20 "tokyo-skies.mp3 0a 00 @cmd2 "pause 0a 00 @buffer $200 &pos $2 ( input buffer )