saf.h 120 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874
  1. #ifndef SAF_H
  2. #define SAF_H
  3. /**
  4. @file saf.h
  5. Small Abstract Fish (SAF)
  6. [] [][][][][]
  7. [][][] [][]
  8. [][] []
  9. [] XX XX[]
  10. [] XXXX []
  11. [][] []
  12. [][][] [][]
  13. [] [][][][][]
  14. Simple interface for programming small portable games, especially for open
  15. consoles but also the PC and other platforms.
  16. Some attributes of the SAF console are:
  17. - 64 x 64 pixels display
  18. - framebuffer
  19. - 256 colors, RGB332 palette
  20. - 25 FPS
  21. - 7 buttons
  22. - simple speaker
  23. - Von Neumann architecture (single memory space for program and data)
  24. - no limit on resource usage (RAM, cycles, cores, ...)
  25. - behavior such as rasterization should be the same on all platforms (i.e.
  26. rasterization or circle is implemented internally rather than relying on
  27. the platform's circle rasterization)
  28. - without extensions, SAF programs should be deterministic
  29. by drummyfish, 2021
  30. thanks for suggestions/testing/etc go also to: jsangradorp
  31. Released under CC0 1.0 (https://creativecommons.org/publicdomain/zero/1.0/)
  32. plus a waiver of all other intellectual property. The goal of this work is
  33. be and remain completely in the public domain forever, available for any use
  34. whatsoever. */
  35. /* user settings, these can be redefined before including the library (platform
  36. specific settings are listed under each platform implementation later): */
  37. #ifndef SAF_SETTING_FORCE_1BIT
  38. /** Forces monochrome (1 bit) graphics even on platforms that can display more
  39. than 2 colors. This can be good for testing how a color game would look like
  40. on 1 bit displays. */
  41. #define SAF_SETTING_FORCE_1BIT 0
  42. #endif
  43. #ifndef SAF_SETTING_1BIT_DITHER
  44. /** Says if dithering should be used for monochrome (1 bit) platforms. Whether
  45. to use dithering or not depends on each program, some look better with it,
  46. some don't. Dithering consumes significantly more CPU power.*/
  47. #define SAF_SETTING_1BIT_DITHER 0
  48. #endif
  49. #ifndef SAF_SETTING_FASTER_1BIT
  50. /** If non-zero, the conversion of color to 1bit (monochromatic) will be done
  51. with an approximation that is faster but gives a slightly different
  52. (incorrect) result. 1 will set a mild approximation, 2 will set a faster one,
  53. 3 a fastest one. This may be good for slow platforms. */
  54. #define SAF_SETTING_FASTER_1BIT 1
  55. #endif
  56. #ifndef SAF_SETTING_ENABLE_SOUND
  57. /** Can be used to disable sound at compile time. This is good to do if your
  58. game doesn't use any sounds so that the frontend doesn't have to
  59. unnecessarily manage sound libraries. Disabling sound may increase
  60. performance. */
  61. #define SAF_SETTING_ENABLE_SOUND 1
  62. #endif
  63. #ifndef SAF_SETTING_ENABLE_SAVES
  64. /** If 0, persistent memory for saves will be disabled so that saved data will
  65. only last during the program run. Disabling saves for games that don't use
  66. them may help the compiler optimize the program and not include libraries it
  67. won't need. */
  68. #define SAF_SETTING_ENABLE_SAVES 1
  69. #endif
  70. #ifndef SAF_SETTING_BACKGROUND_COLOR
  71. /** Specifies the color that should be used as a background, e.g. on platforms
  72. that have regions on screen where the game isn't drawn due to non-square
  73. resolution. */
  74. #define SAF_SETTING_BACKGROUND_COLOR 0
  75. #endif
  76. // removes usleep warning
  77. #if (defined SAF_PLATFORM_X11 || defined SAF_PLATFORM_SDL2)
  78. #define _DEFAULT_SOURCE
  79. #endif
  80. #include <stdint.h>
  81. /* ============================= FOR PROGRAMS ==================================
  82. These are resources (functions, macros, ...) that are to be used by SAF client
  83. programs. If you are creating a program (a game etc.), only use these. A
  84. program is REQUIRED to implement:
  85. - SAF_PROGRAM_NAME macro: this must be set to a string with the program's name
  86. (e.g. #define SAF_PROGRAM_NAME "My game"). All version of the program should
  87. keep the same name as this name may be used e.g. to compute a hash that will
  88. determine its save address in EEPROM.
  89. - SAF_init function: in this function program should be initialized
  90. - SAF_loop function: this function handles the main loop
  91. Before including saf.h a platform also needs to be specified by defining one
  92. of the possible SAF_PLATFORM_* macros.
  93. The program must NOT implement the main() function. */
  94. // do NOT redefine these macros, they're read-only:
  95. #define SAF_SCREEN_WIDTH 64
  96. #define SAF_SCREEN_HEIGHT 64
  97. #define SAF_FPS 25 ///< A divisor of 1000 prevents desync with RT.
  98. #define SAF_SAVE_SIZE 32
  99. #define SAF_MS_PER_FRAME (1000 / SAF_FPS)
  100. #define SAF_VERSION_STRING "1.013"
  101. #define SAF_BUTTON_UP 0x00
  102. #define SAF_BUTTON_DOWN 0x01
  103. #define SAF_BUTTON_LEFT 0x02
  104. #define SAF_BUTTON_RIGHT 0x03
  105. #define SAF_BUTTON_A 0x04
  106. #define SAF_BUTTON_B 0x05
  107. #define SAF_BUTTON_C 0x06
  108. #define SAF_BUTTONS 7 ///< number of buttons
  109. #define SAF_COLOR_BLACK 0x00
  110. #define SAF_COLOR_WHITE 0xff
  111. #define SAF_COLOR_GRAY 0x92
  112. #define SAF_COLOR_GRAY_DARK 0x49
  113. #define SAF_COLOR_RED 0xe0
  114. #define SAF_COLOR_RED_DARK 0x80
  115. #define SAF_COLOR_GREEN 0x1c
  116. #define SAF_COLOR_GREEN_DARK 0x10
  117. #define SAF_COLOR_BLUE 0x03
  118. #define SAF_COLOR_BLUE_DARK 0x01
  119. #define SAF_COLOR_YELLOW 0xf8
  120. #define SAF_COLOR_ORANGE 0xf0
  121. #define SAF_COLOR_BROWN 0x8d
  122. #define SAF_COLOR_RGB(r,g,b) (((r / 32) << 5) | ((g / 32) << 2) | (b / 64))
  123. #define SAF_SOUND_BEEP 0x00 ///< beep sound for special events
  124. #define SAF_SOUND_CLICK 0x01 ///< click sound, e.g. for menu
  125. #define SAF_SOUND_BOOM 0x02 ///< boom sound, e.g. for shooting
  126. #define SAF_SOUND_BUMP 0x03 ///< bump sound, e.g. for hitting walls
  127. #define SAF_SOUNDS 4 ///< number of sounds
  128. #define SAF_TRANSFORM_NONE 0x00
  129. #define SAF_TRANSFORM_ROTATE_90 0x01
  130. #define SAF_TRANSFORM_ROTATE_180 0x02
  131. #define SAF_TRANSFORM_ROTATE_270 0x03
  132. #define SAF_TRANSFORM_FLIP 0x04 ///< horizontal flip before rotation
  133. #define SAF_TRANSFORM_SCALE_2 0x08
  134. #define SAF_TRANSFORM_SCALE_3 0x10
  135. #define SAF_TRANSFORM_SCALE_4 0x18
  136. #define SAF_TRANSFORM_INVERT 0x20 ///< invert colors
  137. #define SAF_INFO_STRING \
  138. "made with SAF (SmallAbstractFish) library v. " SAF_VERSION_STRING
  139. // these will potentially be redefined by each platform
  140. #define SAF_PLATFORM_NAME "platform"
  141. #define SAF_PLATFORM_COLOR_COUNT 256
  142. #define SAF_PLATFORM_BUTTON_COUNT 7
  143. #define SAF_PLATFORM_RAM 0
  144. #define SAF_PLATFORM_FREQUENCY 0
  145. #define SAF_PLATFORM_HAS_SAVES 1
  146. #define SAF_PLATFORM_HAS_SOUND 1
  147. #define SAF_PLATFORM_HARWARD 0 ///< Harward architecture
  148. #define SAF_LOGO_IMAGE 0xbee3c1938dc1e3be ///< 8x8 1b logo as 64 bit int
  149. #ifndef SAF_PROGRAM_NAME
  150. #error SAF_PROGRAM_NAME has to be defined before including the library.
  151. #endif
  152. /** Implement this function in your program and put initialization code in it.
  153. Frontend will call this when the program starts to initialize it. */
  154. void SAF_init(void);
  155. /** Implement this function in your program and put main loop code inside it.
  156. This function will be called periodically SAF_FPS times per second. When the
  157. function finishes, the framebuffer is presented to screen. The frame buffer
  158. is NOT cleared before this function is called. The function should return
  159. non-zero if the program continues or 0 if the program has ended. */
  160. uint8_t SAF_loop(void);
  161. /** Returns the number of frames for which a button has been continuously held,
  162. up to 255. If button >= SAF_BUTTONS, 0 will be returned. */
  163. uint8_t SAF_buttonPressed(uint8_t button);
  164. /** Checks if the button has been pressed exactly in the current frame. */
  165. static inline uint8_t SAF_buttonJustPressed(uint8_t button);
  166. /** Plays given sound. */
  167. void SAF_playSound(uint8_t sound);
  168. /** Saves a byte of data to persistent storage (e.g. a file, cookie etc.). If
  169. index >= SAF_SAVE_SIZE, nothing happens. WARNING: it may potentially be bad to
  170. call this function extremely often as on some platforms the save memory may be
  171. slow (disk) or prone to wearing off (EEPROM). The function tries to eliminate
  172. the writes, but you should also try to reduce the calls if possible. */
  173. void SAF_save(uint8_t index, uint8_t data);
  174. /** Loads a byte from persistent storage (saved with SAF_save). If no data were
  175. ever saved with SAF_save at the index, 0 is returned. 0 is always returned for
  176. index >= SAF_SAVE_SIZE. WARNING: The function keeps a cache of loaded values
  177. so that loading from the actual save memory only happens at most once per
  178. program run. */
  179. uint8_t SAF_load(uint8_t index);
  180. /** Gets the number of frames from start of the program. */
  181. static inline uint32_t SAF_frame(void);
  182. /** Gets the time from start of the program in milliseconds. */
  183. static inline uint32_t SAF_time(void);
  184. /** Returns a simple pseudorandom number. The number sequence will be the same
  185. in each program run and will repeat after 256 calls. SAF_randomSeed() can be
  186. called to seed this pseudorandom generator. */
  187. uint8_t SAF_random(void);
  188. /** Seeds the pseudorandom generator with an initial number. Numbers returned
  189. by SAF_random depend on this value. The generator is automatically seeded with
  190. 0 at the start of a program. */
  191. static inline void SAF_randomSeed(uint8_t seed);
  192. /** Computes sin function of the argument (255 corresponds to 2*pi, i.e. the
  193. full angle). Returns a value between -127 to 127 (including). */
  194. int8_t SAF_sin(uint8_t phase);
  195. int8_t SAF_cos(uint8_t phase);
  196. /** Computes integer square root of a number. */
  197. uint16_t SAF_sqrt(uint32_t number);
  198. /** Returns a 332 color closest to given RGB values. */
  199. uint8_t SAF_colorFromRGB(uint8_t red, uint8_t green, uint8_t blue);
  200. /** Converts given 332 color to amount of red, green and blue. This conversion
  201. aligns the blue levels with red/green levels so that it is possible to get
  202. true gray. */
  203. void SAF_colorToRGB(uint8_t colorIndex, uint8_t *red, uint8_t *green, uint8_t *blue);
  204. /** Converts given 332 color to an approximate 8bit grayscale value. Note that
  205. doing this per-pixel can negatively affect performance, in which case you may
  206. consider creating a lookup table using this function. */
  207. static inline uint8_t SAF_colorToGrayscale(uint8_t colorIndex);
  208. /** Converts given 332 color to a 1 bit value (black&white). The result returned
  209. will either be 0 (black) or non-zero (white). The conversion performed by this
  210. function is affected by SAF_SETTING_FASTER_1BIT. */
  211. static inline uint8_t SAF_colorTo1Bit(uint8_t colorIndex);
  212. /** Returns an "opposite" color of given 332 color. */
  213. static inline uint8_t SAF_colorInvert(uint8_t color);
  214. /** Sets a single pixel of the frame buffer to given color. */
  215. void SAF_drawPixel(int8_t x, int8_t y, uint8_t color);
  216. /** Draws a rectangle. */
  217. void SAF_drawRect(int8_t x, int8_t y, int8_t width, int8_t height, uint8_t color, uint8_t filled);
  218. /** Draws a line using the DDA algorithm. */
  219. void SAF_drawLine(int8_t x1, int8_t y1, int8_t x2, int8_t y2, uint8_t color);
  220. /** Draws a circle. */
  221. void SAF_drawCircle(int8_t x, int8_t y, uint8_t radius, uint8_t color, uint8_t filled);
  222. /** Clears the screen with given color, typically called before rendering a new
  223. frame. */
  224. static inline void SAF_clearScreen(uint8_t color);
  225. /** Draws given text with the built-in 4x4 font. */
  226. int8_t SAF_drawText(const char *text, int8_t x, int8_t y, uint8_t color, uint8_t size);
  227. /** Gets the built-in character mask, in case you want to draw the font
  228. character yourself. The 4x4 character is returned as a 2 byte binary image. */
  229. void SAF_getFontCharacter(uint8_t asciiIndex, uint8_t result[2]);
  230. /** Draws an uncompressed 332 color image. Transformation can be applies by
  231. passing a bitwise or value or SAF_TRANSFORM_* values.
  232. The image format is following: 1st byte unsigned width, 2nd byte is unsigned
  233. height, following bytes each hold the 332 color of pixels starting from top
  234. left of the image and going right and down. */
  235. void SAF_drawImage(const uint8_t *image, int8_t x, int8_t y, uint8_t transform, uint8_t transparentColor);
  236. /** Same as SAF_drawImage but takes a compressed image as an input. This will
  237. most likely be slower than SAF_drawImage but will save ~2/3 memory on images.
  238. The compressed format is both lossy (palette reduction) and lossless (RLE). It
  239. is following: 1st byte is unsigned width, 2nd byte is unsigned height, the
  240. following 16 bytes are the image palette, then RLE bytes follow: each RLE byte
  241. specifies the palette color index in its lower 4 bits and the number of
  242. repetitions of that color in upper 4 bits (so e.g. value 0 means 1 pixel will
  243. be drawn). Images can be compressed to this format by tools that come with
  244. SAF. */
  245. void SAF_drawImageCompressed(const uint8_t *image, int8_t x, int8_t y, uint8_t transform, uint8_t transparentColor);
  246. /** Same as SAF_drawImage but for 1bit (monochrome) images. Transparency mask
  247. (in the same format as the drawn image) can be used.
  248. The 1bit image format is following: 1st byte is unsigned width, 2nd byte is
  249. unsigned height, following bytes encode bits if the image, each byte holds
  250. 8 bits, MSB coming as 1st bit of the byte, going from top left of the image
  251. to the right and down. */
  252. void SAF_drawImage1Bit(const uint8_t *image, int8_t x, int8_t y, const uint8_t *mask, uint8_t color1, uint8_t color2, uint8_t transform);
  253. /** Converts integer to string. The string must have enough space allocated
  254. (safe size is 12). The string will be zero terminated by the function. Pointer
  255. identical to "string" will be returned. */
  256. char *SAF_intToStr(int32_t number, char *string);
  257. /** Same as SAF_intToStr but for floats. The safe allocated size for the string
  258. is 23. Maximum decimals in the result will be 10. */
  259. char *SAF_floatToStr(float number, char *string, uint8_t decimals);
  260. /** RESERVED for possible future implementation of extension via a text
  261. protocol, at the moment this function does nothing.
  262. The extension should work like this: the client program will send a string
  263. (call this function) and receive a string from the frontend (the return
  264. value). This may be exploited e.g. for network communication or file system
  265. operations. Empty string means an empty answer and will returned if the
  266. the extension is unsupported or similar cases.
  267. The string passed to the function can be dealocated or changed after the call,
  268. the frontend should make a copy if it needs it. */
  269. const char *SAF_extension(const char *string);
  270. /* ============================ FOR FRONTENDS ==================================
  271. These functions are NOT to be used by SAF client programs, they are for
  272. frontend implementations, i.e. if you're adding a new platform support. A
  273. frontend is normally required to do the following (but see below):
  274. - Redefine some or all of SAF_PLATFORM_* macros (e.g. the platform name,
  275. button count etc.). First undefine the macro and define again (to prevent
  276. warnings).
  277. - Implement the specific SAF_FE_* functions that are required to be
  278. implemented (see below); The functions mostly correspond to the client
  279. functions (e.g. SAF_drawPixel vs SAF_FE_drawPixel), but don't have to
  280. preform parameter checks. Call SAF_FE_init and SAF_FE_loop in the right
  281. places. The remaining SAF_FE_* functions are for the convenience of
  282. frontends that you may or may not use as you wish.
  283. - The frame buffer should be initialized to all zeros at the beginning of your
  284. frontend program.
  285. - Implement the main program body (e.g. the main() function or setup/loop
  286. Arduino functions) and call SAF_FE_init and SAF_FE_loop. Frame buffer should
  287. be presented to the screen after SAF_FE_loop finishes. Do NOT clear the
  288. frame buffer in main loop, the program is supposed to do this.
  289. - Init the console's state, i.e. clear screen to all black, buttons states to
  290. 0 etc.
  291. - Try to respect and take into account SAF_SETTING_* macros.
  292. - Try to make your global identifiers unlikely to collide with the user
  293. program, i.e. you may e.g. prefix them with '_'.
  294. When implementing a new frontend take a look at already implemented ones to
  295. see how it's done.
  296. Another way to implement a frontend is to use a partly preimplemented PC
  297. stdlib generic frontend with advanced. For details look at
  298. SAF_FE_GENERIC_FRONTEND. */
  299. //~~~ IMPLEMENT THE FOLLOWING ~~~
  300. /** DO NOT USE IN PROGRAMS, this is for frontends only! Programs should use
  301. SAF_drawPixel instead.
  302. Implement this function in your platform.
  303. Draws pixel to the screen back buffer, i.e. this shouldn't be visible on the
  304. display right after calling this function, but only when the screen is updated
  305. by the frontend at the end of the frame. The coordinates passed are
  306. guaranteed to be from 0 to 63, therefore no bound check is needed to be
  307. performed by this function. */
  308. static inline void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color);
  309. /** DO NOT USE IN PROGRAMS, this is for frontends only! Programs should use
  310. SAF_playSound instead.
  311. Implement this function in your platform. */
  312. static inline void SAF_FE_playSound(uint8_t sound);
  313. /** DO NOT USE IN PROGRAMS, this is for frontends only! Programs should use
  314. SAF_save instead.
  315. Implement this function in your platform.
  316. This function should save given data byte to a specified address (index) in
  317. the persistent storage to last between HW resets. You can use e.g. files,
  318. cookies or EEPROM to implement this memory. Index passed to this function will
  319. always be < SAF_SAVE_SIZE. You don't have to implement any optimizations
  320. (e.g. buffers, ignoring overwrites of same values etc.) as SAF already does
  321. them internally. */
  322. static inline void SAF_FE_save(uint8_t index, uint8_t data);
  323. /** DO NOT USE IN PROGRAMS, this is for frontends only! Programs should use
  324. SAF_load instead.
  325. Implement this function in your platform.
  326. This function should load and return data byte from specified address (index)
  327. in the persistent storage. This data was saved with SAF_FE_save. If no
  328. data have ever been written to that memory address, 0 should be returned.
  329. Index passed to this function will always be < SAF_SAVE_SIZE. You don't have
  330. to implement any optimizations (e.g. buffers) as SAF already does this
  331. internally. */
  332. static inline uint8_t SAF_FE_load(uint8_t index);
  333. /** DO NOT USE IN PROGRAMS, this is for frontends only! Programs should use
  334. SAF_buttonPressed instead.
  335. Implement this function in your platform.
  336. This function should return a non-zero value if given button is pressed, or 0
  337. if the button is not pressed. The function will be called for each button
  338. exactly one per frame (so there is no need to worry about returning a
  339. consistent value during a frame). Button number passed to this function will
  340. always be < SAF_BUTTONS. */
  341. static inline uint8_t SAF_FE_buttonPressed(uint8_t button);
  342. /* RESERVED, at this moment this function should always return an empty string
  343. (a pointer to value 0). */
  344. static inline const char *SAF_FE_extension(const char *string);
  345. // ~~~ CALL THE FOLLOWING IN RIGHT PLACES ~~~
  346. /** DO NOT USE IN PROGRAMS, this is for frontends only!
  347. In your platform implementation call this function once in SAF_MS_PER_FRAME.
  348. This function calls the client program's SAF_loop (don't call this directly).
  349. If this function returns 0, halt the program, otherwise continue. */
  350. static inline uint8_t SAF_FE_loop(void);
  351. /** DO NOT USE IN PROGRAMS, this is for frontends only!
  352. In your platform implementation call this function at the start of the
  353. program. */
  354. static inline void SAF_FE_init(void);
  355. // ~~~ FOR FRONTEND CONVENIENCE ~~~
  356. /* The following macros can optinally be defined by your frontend:
  357. SAF_FE_GENERIC_FRONTEND enables a partly preimplemented generic frontend
  358. that uses stdio functions and has advanced
  359. features (screenshot taking, volume control, ...).
  360. If this is defined, you don't have to implement
  361. SAF_FE_save, SAF_FE_load, SAF_FE_drawPixel,
  362. SAF_FE_buttonPressed, SAF_FE_extension and the
  363. main function, but you need to implement some
  364. other functions: see SAF_FE_GF_* functions. This
  365. frontend also handles emscripten integration.
  366. SAF_FE_STDIO_SAVE_LOAD includes the stdio.h library and implements
  367. SAF_FE_save and SAF_FE_load using stdio files (so
  368. you don't have to implement these). With
  369. emscripten cookies are used instead of stdio
  370. files. */
  371. char SAF_FE_emptyString[1] = {0};
  372. #define _SAF_UNUSED(identifier) (void)(identifier) ///< for suppressing warnings
  373. /** Returns a simple 16bit hash of given string, useful for e.g. determining the
  374. save location in EEPROM based on the program's name. If you need an 8bit hash,
  375. just take the lower 8 bits of this hash. */
  376. uint16_t SAF_FE_hashStr(const char *str);
  377. /** Parses CLI arguments of form '-x' or '-xN' (x and N being chars). After
  378. calling, paramValues will hold the value of corresponding flags, e.g. if -s2
  379. was passed, paramValues['s'] will hold value '2'. If parameter wasn't present,
  380. the value will be 0. If the parameter was present without a value (-x), the
  381. value will be 1. */
  382. void SAF_FE_paramParse(int argc, char **argv, uint8_t paramValues[128]);
  383. /** Converts a 332 color to monochrome (1bit) color, taking into account
  384. potential dithering. This function should be used by monochromatic platforms.
  385. */
  386. static inline uint8_t SAF_FE_colorTo1Bit(uint8_t color, uint8_t x, uint8_t y);
  387. /** Uses the pixel art scaling algorithm "scale2x" to expand a single pixel into
  388. four pixels depending on its neighbouring pixels. To scale whole screen use
  389. SAF_FE_scale2xScreen. */
  390. void SAF_FE_scale2xPixel(uint8_t middle, uint8_t top, uint8_t right,
  391. uint8_t bottom, uint8_t left, uint8_t result[4]);
  392. /** Quickly Scales the whole SAF screen 4 times (twice in each dimention) using
  393. the smart "scale2x" pixel art scaling algorithm. */
  394. void SAF_FE_scale2xScreen(
  395. const uint8_t screen[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT],
  396. uint8_t result[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT * 4]);
  397. #define SAF_FE_SOUND_SAMPLE_COUNT 1024
  398. /** The function returns an 8 bit 8 KHz sample of a default built-in sound of
  399. this library, for implementing SAF_FE_playSound. If you're using custom
  400. external sounds or the platform's built-in beeps, don't use this. Each sound
  401. has SAF_FE_SOUND_SAMPLE_COUNT samples. */
  402. int8_t SAF_FE_getSoundSample(uint8_t sound, uint16_t sampleNumber);
  403. /** If SAF_FE_GENERIC_FRONTEND is defined, you need to implement this function
  404. in which you initialize your frontend. CLIParameters holds values of parsed
  405. command line arguments of format as returned by SAF_FE_paramParse. Certain
  406. flags (see generic frontend's help) are used by the generic frontend and these
  407. are guaranteed to hold only valid values when accessed (e.g. 's' will always
  408. have values '1' to '8'). Your frontend can use the rest of the flags as it
  409. wishes (to include these into help define SAF_FE_GF_EXTRA_HELP). */
  410. void SAF_FE_GF_init(uint8_t CLIParameters[128]);
  411. /** If SAF_FE_GENERIC_FRONTEND is defined, you need to implement this function
  412. in which you free your allocated resources. This function will be called
  413. before the program exit. */
  414. void SAF_FE_GF_end();
  415. /** If SAF_FE_GENERIC_FRONTEND is defined, you need to implement this function
  416. in which you handle the main loop (only things that your frontend needs, the
  417. rest us handled by the generic frontend). CLIParameters is the same array as
  418. CLIParameters in SAF_FE_GF_init. The function should return a non-zero value
  419. if the program keeps running and 0 if the program has been exited (e.g. by
  420. closing the window). */
  421. uint8_t SAF_FE_GF_loop(uint8_t params[128]);
  422. const uint8_t *SAF_FE_GF_getScreenPointer();
  423. /** If SAF_FE_GENERIC_FRONTEND is defined, you need to implement this function
  424. in which you copy the passed screen data into your frontend's screen. */
  425. void SAF_FE_GF_present(
  426. const uint8_t screen[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT]);
  427. /** If SAF_FE_GENERIC_FRONTEND is defined, you need to implement this function
  428. which should return a bool value indicating whether specific keyboard key is
  429. pressed or not. Lowercase letters ('a', 'b', 'c', ...) represent letter keys,
  430. 'U', 'D', 'L', 'R' represent arrow keys, 'E' is escape, 'X', 'Y', 'Z' are
  431. controller buttons. */
  432. uint8_t SAF_FE_GF_keyPressed(char keyChar);
  433. /** If SAF_FE_GENERIC_FRONTEND is defined, you need to implement this function
  434. which should do two things: sleep (yield CPU) for sleepMs milliseconds, then
  435. return the current number of milliseconds (after the sleep) from the start of
  436. the program. */
  437. uint32_t SAF_FE_GF_sleep(uint16_t sleepMs);
  438. /** If SAF_FE_GENERIC_FRONTEND is defined, you need to implement this function
  439. which should behave the same as SAF_FE_extension (at this moment should only
  440. return an empty string pointer).*/
  441. const char *SAF_FE_GF_extension(const char *string);
  442. /// SAF palette as 565 values.
  443. #define SAF_FE_PALETTE_565 \
  444. 0,9,18,31,288,297,306,319,576,585,594,607,864,873,882,895,1152,1161,1170,1183,\
  445. 1440,1449,1458,1471,1728,1737,1746,1759,2016,2025,2034,2047,8192,8201,8210,\
  446. 8223,8480,8489,8498,8511,8768,8777,8786,8799,9056,9065,9074,9087,9344,9353,\
  447. 9362,9375,9632,9641,9650,9663,9920,9929,9938,9951,10208,10217,10226,10239,\
  448. 18432,18441,18450,18463,18720,18729,18738,18751,19008,19017,19026,19039,19296,\
  449. 19305,19314,19327,19584,19593,19602,19615,19872,19881,19890,19903,20160,20169,\
  450. 20178,20191,20448,20457,20466,20479,26624,26633,26642,26655,26912,26921,26930,\
  451. 26943,27200,27209,27218,27231,27488,27497,27506,27519,27776,27785,27794,27807,\
  452. 28064,28073,28082,28095,28352,28361,28370,28383,28640,28649,28658,28671,36864,\
  453. 36873,36882,36895,37152,37161,37170,37183,37440,37449,37458,37471,37728,37737,\
  454. 37746,37759,38016,38025,38034,38047,38304,38313,38322,38335,38592,38601,38610,\
  455. 38623,38880,38889,38898,38911,45056,45065,45074,45087,45344,45353,45362,45375,\
  456. 45632,45641,45650,45663,45920,45929,45938,45951,46208,46217,46226,46239,46496,\
  457. 46505,46514,46527,46784,46793,46802,46815,47072,47081,47090,47103,55296,55305,\
  458. 55314,55327,55584,55593,55602,55615,55872,55881,55890,55903,56160,56169,56178,\
  459. 56191,56448,56457,56466,56479,56736,56745,56754,56767,57024,57033,57042,57055,\
  460. 57312,57321,57330,57343,63488,63497,63506,63519,63776,63785,63794,63807,64064,\
  461. 64073,64082,64095,64352,64361,64370,64383,64640,64649,64658,64671,64928,64937,\
  462. 64946,64959,65216,65225,65234,65247,65504,65513,65522,65535
  463. //======================= PLATFORM FRONTENDS ===================================
  464. #if defined(SAF_PLATFORM_SDL2) || defined(SAF_PLATFORM_SDL2_TINY)
  465. #include <SDL2/SDL.h>
  466. // code common to all SDL frontends
  467. uint8_t _SDL_volume = 0;
  468. int8_t _SDL_currentSound = -1;
  469. uint16_t _SDL_soundPosition = 0;
  470. void SAF_SDL_playSound(uint8_t sound)
  471. {
  472. #if SAF_SETTING_ENABLE_SOUND
  473. _SDL_currentSound = sound;
  474. _SDL_soundPosition = 0;
  475. #else
  476. _SAF_UNUSED(sound);
  477. #endif
  478. }
  479. #if SAF_SETTING_ENABLE_SOUND
  480. void SAF_SDL_audioFillCallback(void *userdata, uint8_t *s, int l)
  481. {
  482. _SAF_UNUSED(userdata);
  483. int16_t *s16 = (int16_t *) s;
  484. l /= 2;
  485. for (int i = 0; i < l; ++i)
  486. {
  487. if (_SDL_currentSound < 0)
  488. *s16 = 0;
  489. else
  490. {
  491. *s16 = SAF_FE_getSoundSample(_SDL_currentSound,_SDL_soundPosition);
  492. _SDL_soundPosition++;
  493. if (_SDL_soundPosition >= SAF_FE_SOUND_SAMPLE_COUNT)
  494. {
  495. _SDL_currentSound = -1;
  496. _SDL_soundPosition = 0;
  497. }
  498. }
  499. s16++;
  500. }
  501. }
  502. uint8_t SAF_SDL_initAudio(void)
  503. {
  504. SDL_AudioSpec audioSpec;
  505. SDL_memset(&audioSpec, 0, sizeof(audioSpec));
  506. audioSpec.callback = SAF_SDL_audioFillCallback;
  507. audioSpec.freq = 8000;
  508. audioSpec.format = AUDIO_S16;
  509. audioSpec.channels = 1;
  510. #ifdef __EMSCRIPTEN__
  511. audioSpec.samples = 1024;
  512. #else
  513. audioSpec.samples = 256;
  514. #endif
  515. if (SDL_OpenAudio(&audioSpec,NULL) < 0)
  516. return 0;
  517. SDL_PauseAudio(0);
  518. return 1;
  519. }
  520. #endif // SAF_SETTING_ENABLE_SOUND
  521. #endif // SAF_PLATFORM_SDL2 || SAF_PLATFORM_SDL2_TINY
  522. #ifdef SAF_PLATFORM_NULL
  523. /* Null frontend, has no I/O implemented and runs at highest reachable FPS
  524. instead of SAF_FPS. This can be useful for testing, e.g. if you want to see
  525. the compiled size or performance of just the game without any frontend.
  526. ------------------------------------------------------------------------------*/
  527. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  528. { _SAF_UNUSED(x); _SAF_UNUSED(y); _SAF_UNUSED(color); }
  529. void SAF_FE_playSound(uint8_t sound)
  530. { _SAF_UNUSED(sound); }
  531. void SAF_FE_save(uint8_t index, uint8_t data)
  532. { _SAF_UNUSED(index); _SAF_UNUSED(data); }
  533. uint8_t SAF_FE_load(uint8_t index)
  534. { _SAF_UNUSED(index); return 0; }
  535. uint8_t SAF_FE_buttonPressed(uint8_t button)
  536. { _SAF_UNUSED(button); return 0; }
  537. const char *SAF_FE_extension(const char *string)
  538. { _SAF_UNUSED(string); return SAF_FE_emptyString; }
  539. int main(void)
  540. {
  541. SAF_FE_init();
  542. while (SAF_FE_loop());
  543. return 0;
  544. }
  545. #elif defined(SAF_PLATFORM_SDL2)
  546. /* SDL2 platform using the SAF_FE_GENERIC_FRONTEND, also usable with emscripten
  547. (browser JavaScript).
  548. requirements: libsdl2-dev, stdio.h, unistd.h, stdlib.h
  549. compiling: link SDL2, e.g. -lSDL2 (emscripten: -s USE_SDL=2)
  550. ------------------------------------------------------------------------------*/
  551. #undef SAF_PLATFORM_NAME
  552. #define SAF_PLATFORM_NAME "SDL2"
  553. #define SAF_FE_GENERIC_FRONTEND
  554. #include <SDL2/SDL.h>
  555. #include <stdlib.h> // for malloc/free
  556. #include <unistd.h> // for usleep
  557. const uint8_t *_SDL_keyboardState;
  558. SDL_Window *_SDL_window;
  559. SDL_Renderer *_SDL_renderer;
  560. SDL_Texture *_SDL_texture;
  561. uint8_t _SDL_pixelArtUpscale = 0;
  562. uint8_t *_SDL_upscaleScreen = 0;
  563. SDL_GameController *_SDL_controller = 0;
  564. void SAF_FE_playSound(uint8_t sound)
  565. {
  566. SAF_SDL_playSound(sound);
  567. }
  568. void SAF_FE_GF_init(uint8_t CLIParameters[128])
  569. {
  570. SDL_Init(
  571. SDL_INIT_EVENTS |
  572. #if SAF_SETTING_ENABLE_SOUND
  573. SDL_INIT_AUDIO |
  574. #endif
  575. SDL_INIT_JOYSTICK);
  576. _SDL_volume = CLIParameters['v'] - '0';
  577. if (CLIParameters['u'] != 0)
  578. {
  579. _SDL_pixelArtUpscale = 1;
  580. _SDL_upscaleScreen = malloc(SAF_SCREEN_WIDTH * SAF_SCREEN_WIDTH * 4);
  581. }
  582. uint16_t screenScale = CLIParameters['s'] - '0';
  583. uint8_t fullscreen = screenScale == 0;
  584. if (screenScale == 0)
  585. screenScale = 1;
  586. _SDL_window = SDL_CreateWindow(SAF_PROGRAM_NAME, SDL_WINDOWPOS_UNDEFINED,
  587. SDL_WINDOWPOS_UNDEFINED, SAF_SCREEN_WIDTH * screenScale,
  588. SAF_SCREEN_HEIGHT * screenScale,SDL_WINDOW_SHOWN);
  589. if (fullscreen)
  590. SDL_SetWindowFullscreen(_SDL_window,SDL_WINDOW_FULLSCREEN_DESKTOP);
  591. _SDL_renderer = SDL_CreateRenderer(_SDL_window,-1,0);
  592. _SDL_texture = SDL_CreateTexture(_SDL_renderer,
  593. SDL_PIXELFORMAT_RGB332,SDL_TEXTUREACCESS_STATIC,
  594. SAF_SCREEN_WIDTH * (_SDL_pixelArtUpscale ? 2 : 1),
  595. SAF_SCREEN_HEIGHT * (_SDL_pixelArtUpscale ? 2 : 1));
  596. _SDL_keyboardState = SDL_GetKeyboardState(NULL);
  597. _SDL_controller = SDL_GameControllerOpen(0);
  598. SDL_PumpEvents();
  599. SDL_GameControllerUpdate();
  600. #if SAF_SETTING_ENABLE_SOUND
  601. if (!SAF_SDL_initAudio())
  602. puts("SDL: could not initialize audio");
  603. #endif
  604. }
  605. void SAF_FE_GF_end()
  606. {
  607. #if SAF_SETTING_ENABLE_SOUND
  608. SDL_PauseAudio(1);
  609. SDL_CloseAudio();
  610. #endif
  611. if (_SDL_controller != 0)
  612. SDL_GameControllerClose(_SDL_controller);
  613. SDL_DestroyTexture(_SDL_texture);
  614. SDL_DestroyRenderer(_SDL_renderer);
  615. SDL_DestroyWindow(_SDL_window);
  616. if (_SDL_pixelArtUpscale)
  617. free(_SDL_upscaleScreen);
  618. }
  619. uint8_t SAF_FE_GF_loop(uint8_t params[128])
  620. {
  621. _SDL_volume = params['v'] - '0';
  622. SDL_Event event;
  623. while (SDL_PollEvent(&event))
  624. if (event.type == SDL_QUIT)
  625. return 0;
  626. return 1;
  627. }
  628. void
  629. SAF_FE_GF_present(const uint8_t screen[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT])
  630. {
  631. if (_SDL_pixelArtUpscale)
  632. {
  633. SAF_FE_scale2xScreen(screen,_SDL_upscaleScreen);
  634. SDL_UpdateTexture(_SDL_texture,NULL,_SDL_upscaleScreen,SAF_SCREEN_WIDTH * 2);
  635. }
  636. else
  637. SDL_UpdateTexture(_SDL_texture,NULL,screen,SAF_SCREEN_WIDTH);
  638. SDL_RenderClear(_SDL_renderer);
  639. SDL_RenderCopy(_SDL_renderer,_SDL_texture,NULL,NULL);
  640. SDL_RenderPresent(_SDL_renderer);
  641. }
  642. uint8_t SAF_FE_GF_keyPressed(char keyChar)
  643. {
  644. if (keyChar <= 'z' && keyChar >= 'a')
  645. return _SDL_keyboardState[SDL_SCANCODE_A + (keyChar - 'a')];
  646. #define b(x) ((_SDL_controller != NULL) && \
  647. SDL_GameControllerGetButton(_SDL_controller,SDL_CONTROLLER_BUTTON_ ## x))
  648. switch (keyChar)
  649. {
  650. case 'U': return
  651. _SDL_keyboardState[SDL_SCANCODE_UP] ||
  652. b(DPAD_UP);
  653. break;
  654. case 'R': return
  655. _SDL_keyboardState[SDL_SCANCODE_RIGHT] ||
  656. b(DPAD_RIGHT);
  657. break;
  658. case 'D': return
  659. _SDL_keyboardState[SDL_SCANCODE_DOWN] ||
  660. b(DPAD_DOWN);
  661. break;
  662. case 'L': return
  663. _SDL_keyboardState[SDL_SCANCODE_LEFT] ||
  664. b(DPAD_LEFT);
  665. break;
  666. case 'E': return _SDL_keyboardState[SDL_SCANCODE_ESCAPE]; break;
  667. case 'X': return b(X); break;
  668. case 'Y': return b(Y) || b(B); break;
  669. case 'Z': return b(A) || b(START); break;
  670. default: return 0; break;
  671. }
  672. #undef b
  673. }
  674. uint32_t SAF_FE_GF_sleep(uint16_t sleepMs)
  675. {
  676. if (sleepMs != 0)
  677. usleep(sleepMs * 1000);
  678. return SDL_GetTicks();
  679. }
  680. const char *SAF_FE_GF_extension(const char *string)
  681. {
  682. _SAF_UNUSED(string);
  683. return SAF_FE_emptyString;
  684. }
  685. #elif defined(SAF_PLATFORM_SDL2_TINY)
  686. /* Minimal SDL2 frontend, this frontend does NOT work with Emscripten (use
  687. normal SDL2 platform for that).
  688. requirements: libsdl2-dev, stdio.h, unistd.h, stdlib.h
  689. compiling: link SDL2, e.g. -lSDL2
  690. ------------------------------------------------------------------------------*/
  691. #undef SAF_PLATFORM_NAME
  692. #define SAF_PLATFORM_NAME "SDL2 tiny"
  693. #define SAF_FE_STDIO_SAVE_LOAD
  694. #include <SDL2/SDL.h>
  695. #include <unistd.h> // for usleep
  696. #ifndef SAF_SETTING_SDL2_TINY_SCALE
  697. #define SAF_SETTING_SDL2_TINY_SCALE 4
  698. #endif
  699. #define SDL_UPSCALE
  700. #define SDL_SCREEN_WIDTH \
  701. (SAF_SCREEN_WIDTH * SAF_SETTING_SDL2_TINY_SCALE)
  702. #define SDL_SCREEN_HEIGHT \
  703. (SAF_SCREEN_HEIGHT * SAF_SETTING_SDL2_TINY_SCALE)
  704. const uint8_t *_SDL_keyboardState;
  705. uint8_t _SDL_screen[SDL_SCREEN_WIDTH * SDL_SCREEN_HEIGHT];
  706. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  707. {
  708. #if SAF_SETTING_SDL2_TINY_SCALE == 1
  709. _SDL_screen[y * SDL_SCREEN_WIDTH + x] = color;
  710. #else
  711. uint8_t *pixel = _SDL_screen +
  712. y * (SDL_SCREEN_WIDTH * SAF_SETTING_SDL2_TINY_SCALE) +
  713. x * SAF_SETTING_SDL2_TINY_SCALE;
  714. for (int y = 0; y < SAF_SETTING_SDL2_TINY_SCALE; ++y)
  715. {
  716. for (int x = 0; x < SAF_SETTING_SDL2_TINY_SCALE; ++x)
  717. {
  718. *pixel = color;
  719. pixel++;
  720. }
  721. pixel += SDL_SCREEN_WIDTH - SAF_SETTING_SDL2_TINY_SCALE;
  722. }
  723. #endif
  724. }
  725. void SAF_FE_playSound(uint8_t sound)
  726. {
  727. SAF_SDL_playSound(sound);
  728. }
  729. uint8_t SAF_FE_buttonPressed(uint8_t button)
  730. {
  731. switch (button)
  732. {
  733. case SAF_BUTTON_UP: return
  734. _SDL_keyboardState[SDL_SCANCODE_W] ||
  735. _SDL_keyboardState[SDL_SCANCODE_UP];
  736. break;
  737. case SAF_BUTTON_DOWN: return
  738. _SDL_keyboardState[SDL_SCANCODE_S] ||
  739. _SDL_keyboardState[SDL_SCANCODE_DOWN];
  740. break;
  741. case SAF_BUTTON_LEFT: return
  742. _SDL_keyboardState[SDL_SCANCODE_A] ||
  743. _SDL_keyboardState[SDL_SCANCODE_LEFT];
  744. break;
  745. case SAF_BUTTON_RIGHT: return
  746. _SDL_keyboardState[SDL_SCANCODE_D] ||
  747. _SDL_keyboardState[SDL_SCANCODE_RIGHT];
  748. break;
  749. case SAF_BUTTON_A: return
  750. _SDL_keyboardState[SDL_SCANCODE_Y] ||
  751. _SDL_keyboardState[SDL_SCANCODE_Z] ||
  752. _SDL_keyboardState[SDL_SCANCODE_J] ||
  753. _SDL_keyboardState[SDL_SCANCODE_SPACE];
  754. break;
  755. case SAF_BUTTON_B: return
  756. _SDL_keyboardState[SDL_SCANCODE_X] ||
  757. _SDL_keyboardState[SDL_SCANCODE_K] ||
  758. _SDL_keyboardState[SDL_SCANCODE_RETURN];
  759. break;
  760. case SAF_BUTTON_C: return
  761. _SDL_keyboardState[SDL_SCANCODE_C] ||
  762. _SDL_keyboardState[SDL_SCANCODE_L];
  763. break;
  764. default: return 0; break;
  765. }
  766. }
  767. static inline const char *SAF_FE_extension(const char *string)
  768. {
  769. _SAF_UNUSED(string);
  770. return SAF_FE_emptyString;
  771. }
  772. int main(int argc, char **argv)
  773. {
  774. uint8_t fullscreen = 0;
  775. _SDL_volume = 8;
  776. if (argc > 1 && argv[1][0] == '-' && argv[1][2] == 0)
  777. {
  778. if (argv[1][1] == 'f')
  779. fullscreen = 1;
  780. else if (argv[1][1] == 'm')
  781. _SDL_volume = 0;
  782. else if (argv[1][1] == 'h')
  783. {
  784. puts(SAF_PROGRAM_NAME ", " SAF_INFO_STRING " (" SAF_PLATFORM_NAME ")");
  785. puts("-f = fullscreen, -m = mute");
  786. puts("controls: WSAD (arrows), JKL (XYZC), ESC = exit");
  787. return 0;
  788. }
  789. }
  790. SDL_Init(
  791. SDL_INIT_EVENTS |
  792. #if SAF_SETTING_ENABLE_SOUND
  793. SDL_INIT_AUDIO |
  794. #endif
  795. SDL_INIT_JOYSTICK);
  796. SDL_Window *window = SDL_CreateWindow(SAF_PROGRAM_NAME,
  797. SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
  798. SDL_SCREEN_WIDTH,SDL_SCREEN_HEIGHT,SDL_WINDOW_SHOWN);
  799. if (fullscreen)
  800. SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN_DESKTOP);
  801. SDL_Renderer *renderer = SDL_CreateRenderer(window,-1,0);
  802. SDL_Texture *texture =
  803. SDL_CreateTexture(renderer,
  804. SDL_PIXELFORMAT_RGB332,SDL_TEXTUREACCESS_STATIC,
  805. SDL_SCREEN_WIDTH,
  806. SDL_SCREEN_HEIGHT);
  807. _SDL_keyboardState = SDL_GetKeyboardState(NULL);
  808. for (int i = 0; i < SDL_SCREEN_WIDTH * SDL_SCREEN_HEIGHT; ++i)
  809. _SDL_screen[i] = 0;
  810. SDL_PumpEvents();
  811. SDL_GameControllerUpdate();
  812. #if SAF_SETTING_ENABLE_SOUND
  813. if (!SAF_SDL_initAudio())
  814. puts("SDL: could not initialize audio");
  815. #endif
  816. SAF_FE_init();
  817. uint32_t SAF_FE_GF_nextFrameTime = 0;
  818. while (1)
  819. {
  820. // SDL_PumpEvents();
  821. SDL_Event event;
  822. while (SDL_PollEvent(&event))
  823. if (event.type == SDL_QUIT)
  824. return 0;
  825. if (_SDL_keyboardState[SDL_SCANCODE_ESCAPE])
  826. break;
  827. uint32_t time = SDL_GetTicks();
  828. while (time >= SAF_FE_GF_nextFrameTime)
  829. {
  830. if (!SAF_FE_loop())
  831. break;
  832. SAF_FE_GF_nextFrameTime += SAF_MS_PER_FRAME;
  833. }
  834. SDL_UpdateTexture(texture,NULL,_SDL_screen,SDL_SCREEN_WIDTH);
  835. SDL_RenderClear(renderer);
  836. SDL_RenderCopy(renderer,texture,NULL,NULL);
  837. SDL_RenderPresent(renderer);
  838. usleep(((SAF_FE_GF_nextFrameTime - time) * 3 / 4) * 1000); // relieve CPU
  839. }
  840. #if SAF_SETTING_ENABLE_SOUND
  841. SDL_PauseAudio(1);
  842. SDL_CloseAudio();
  843. #endif
  844. SDL_DestroyTexture(texture);
  845. SDL_DestroyRenderer(renderer);
  846. SDL_DestroyWindow(window);
  847. return 0;
  848. }
  849. #elif defined(SAF_PLATFORM_CSFML)
  850. /* CSFML (C binding for SFML) platform with SAF_FE_GENERIC_FRONTEND.
  851. requirements: libscfml-dev
  852. compiling: link CSFML, usually -lcsfml-graphics -lcsfml-window -lcsfml-system
  853. -lcsfml-audio
  854. ------------------------------------------------------------------------------*/
  855. #undef SAF_PLATFORM_NAME
  856. #define SAF_PLATFORM_NAME "CSFML"
  857. #define SAF_FE_GENERIC_FRONTEND
  858. #include <SFML/Audio.h>
  859. #include <SFML/Graphics.h>
  860. #include <SFML/System.h>
  861. #include <SFML/Audio/Types.h>
  862. #include <stdio.h>
  863. #include <stdlib.h> // for malloc/free
  864. sfClock *_CSFML_clock;
  865. sfRenderWindow* _CSFML_window;
  866. sfTexture* _CSFML_windowTexture;
  867. sfSprite* _CSFML_windowSprite;
  868. uint8_t _CSFML_screenSize = SAF_SCREEN_WIDTH;
  869. uint8_t _CSFML_SDL_pixelArtUpscale = 0;
  870. uint8_t *_CSFML_upscaledScreen = 0;
  871. #if SAF_SETTING_ENABLE_SOUND
  872. sfSound *_CSFML_sound;
  873. sfSoundBuffer *_CSFML_sounds[SAF_SOUNDS];
  874. uint8_t _CSFML_previousVolume = 0;
  875. #endif
  876. uint32_t _CSFML_windowPixels[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT * 4];
  877. uint32_t _CSFML_paletteRGB32[256]; // SFML can't do 332, so precompute RGB here
  878. void SAF_FE_playSound(uint8_t sound)
  879. {
  880. #if SAF_SETTING_ENABLE_SOUND
  881. sfSound_setBuffer(_CSFML_sound,_CSFML_sounds[sound]);
  882. sfSound_play(_CSFML_sound);
  883. #else
  884. _SAF_UNUSED(sound);
  885. #endif
  886. }
  887. void SAF_FE_GF_init(uint8_t CLIParameters[128])
  888. {
  889. for (int i = 0; i < 256; ++i) // precompute RGB palette
  890. {
  891. uint8_t r,g,b;
  892. SAF_colorToRGB(i,&r,&g,&b);
  893. _CSFML_paletteRGB32[i] = 0xff000000 | (((uint32_t) b) << 16) |
  894. (((uint32_t) g) << 8) | r;
  895. }
  896. if (CLIParameters['u'] != 0)
  897. {
  898. _CSFML_SDL_pixelArtUpscale = 1;
  899. _CSFML_upscaledScreen = malloc(SAF_SCREEN_WIDTH * SAF_SCREEN_WIDTH * 4);
  900. _CSFML_screenSize *= 2;
  901. }
  902. for (int i = 0; i < _CSFML_screenSize * _CSFML_screenSize; ++i)
  903. _CSFML_windowPixels[i] = 0;
  904. _CSFML_clock = sfClock_create();
  905. sfClock_restart(_CSFML_clock);
  906. uint16_t screenScale = CLIParameters['s'] - '0';
  907. uint8_t fullscreen = screenScale == 0;
  908. _CSFML_windowTexture = sfTexture_create(_CSFML_screenSize,
  909. _CSFML_screenSize);
  910. sfTexture_setSmooth(_CSFML_windowTexture,sfFalse);
  911. _CSFML_windowSprite = sfSprite_create();
  912. sfVideoMode mode = {_CSFML_screenSize, _CSFML_screenSize, 32};
  913. _CSFML_window = sfRenderWindow_create(mode, SAF_PROGRAM_NAME,
  914. fullscreen ? sfFullscreen : (sfResize | sfClose ), NULL);
  915. sfVector2i winPos;
  916. winPos.x = 1;
  917. winPos.y = 1;
  918. sfWindow_setPosition(_CSFML_window,winPos);
  919. sfSprite_setTexture(_CSFML_windowSprite,_CSFML_windowTexture,sfTrue);
  920. sfWindow_setVerticalSyncEnabled((sfWindow *) _CSFML_window,sfFalse);
  921. if (screenScale != 0)
  922. {
  923. sfVector2u winSize;
  924. winSize.x = _CSFML_screenSize * screenScale;
  925. winSize.y = winSize.x;
  926. sfWindow_setSize(_CSFML_window,winSize);
  927. }
  928. #if SAF_SETTING_ENABLE_SOUND
  929. _CSFML_sound = sfSound_create();
  930. {
  931. int16_t samples[SAF_FE_SOUND_SAMPLE_COUNT];
  932. for (int j = 0; j < SAF_SOUNDS; ++j)
  933. {
  934. for (int i = 0; i < SAF_FE_SOUND_SAMPLE_COUNT; ++i)
  935. samples[i] = ((int16_t) SAF_FE_getSoundSample(j,i)) << 7;
  936. _CSFML_sounds[j] = sfSoundBuffer_createFromSamples(
  937. samples,SAF_FE_SOUND_SAMPLE_COUNT,1,8000);
  938. }
  939. }
  940. #endif
  941. }
  942. void SAF_FE_GF_end()
  943. {
  944. #if SAF_SETTING_ENABLE_SOUND
  945. sfSound_destroy(_CSFML_sound);
  946. for (int i = 0; i < SAF_SOUNDS; ++i)
  947. sfSoundBuffer_destroy(_CSFML_sounds[i]);
  948. #endif
  949. sfClock_destroy(_CSFML_clock);
  950. sfRenderWindow_destroy(_CSFML_window);
  951. sfSprite_destroy(_CSFML_windowSprite);
  952. sfTexture_destroy(_CSFML_windowTexture);
  953. if (_CSFML_SDL_pixelArtUpscale)
  954. free(_CSFML_upscaledScreen);
  955. }
  956. uint8_t SAF_FE_GF_loop(uint8_t params[128])
  957. {
  958. sfEvent event;
  959. while (sfRenderWindow_pollEvent(_CSFML_window,&event));
  960. if (!sfRenderWindow_isOpen(_CSFML_window))
  961. return 0;
  962. #if SAF_SETTING_ENABLE_SOUND
  963. int v = params['v'] - '0';
  964. if (_CSFML_previousVolume != v)
  965. {
  966. sfSound_setVolume(_CSFML_sound,(v * 100) / 8);
  967. _CSFML_previousVolume = v;
  968. }
  969. #endif
  970. return 1;
  971. }
  972. void SAF_FE_GF_present(
  973. const uint8_t screen[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT])
  974. {
  975. const uint8_t *pixel = screen;
  976. uint32_t *pixel2 = _CSFML_windowPixels;
  977. if (_CSFML_SDL_pixelArtUpscale)
  978. {
  979. SAF_FE_scale2xScreen(screen,_CSFML_upscaledScreen);
  980. pixel = _CSFML_upscaledScreen;
  981. }
  982. for (int i = 0; i < _CSFML_screenSize * _CSFML_screenSize; ++i)
  983. {
  984. *pixel2 = _CSFML_paletteRGB32[*pixel];
  985. pixel++;
  986. pixel2++;
  987. }
  988. sfTexture_updateFromPixels(_CSFML_windowTexture,
  989. (const sfUint8 *) _CSFML_windowPixels,
  990. _CSFML_screenSize,_CSFML_screenSize,0,0);
  991. sfRenderWindow_drawSprite(_CSFML_window,_CSFML_windowSprite,NULL);
  992. sfRenderWindow_display(_CSFML_window);
  993. }
  994. uint8_t SAF_FE_GF_keyPressed(char keyChar)
  995. {
  996. if (keyChar <= 'z' && keyChar >= 'a')
  997. return sfKeyboard_isKeyPressed(sfKeyA + (keyChar - 'a'));
  998. #define k(x) sfKeyboard_isKeyPressed(sfKey ## x)
  999. switch (keyChar)
  1000. {
  1001. case 'U':
  1002. return k(Up) || (sfJoystick_getAxisPosition(0,sfJoystickY) <= -50);
  1003. break;
  1004. case 'D':
  1005. return k(Down) || (sfJoystick_getAxisPosition(0,sfJoystickY) >= 50);
  1006. break;
  1007. case 'L':
  1008. return k(Left) || (sfJoystick_getAxisPosition(0,sfJoystickX) <= -50);
  1009. break;
  1010. case 'R':
  1011. return k(Right) || (sfJoystick_getAxisPosition(0,sfJoystickX) >= 50);
  1012. break;
  1013. case 'E': return k(Escape); break;
  1014. case 'X': return sfJoystick_isButtonPressed(0,0) ||
  1015. sfJoystick_isButtonPressed(0,3); break;
  1016. case 'Y': return sfJoystick_isButtonPressed(0,1); break;
  1017. case 'Z': return sfJoystick_isButtonPressed(0,2); break;
  1018. default: break;
  1019. }
  1020. #undef k
  1021. return 0;
  1022. }
  1023. uint32_t SAF_FE_GF_sleep(uint16_t sleepMs)
  1024. {
  1025. sfTime t;
  1026. t.microseconds = sleepMs * 1000;
  1027. sfSleep(t);
  1028. return sfClock_getElapsedTime(_CSFML_clock).microseconds / 1000;
  1029. }
  1030. const char *SAF_FE_GF_extension(const char *string)
  1031. {
  1032. _SAF_UNUSED(string);
  1033. return SAF_FE_emptyString;
  1034. }
  1035. #elif defined(SAF_PLATFORM_POKITTO)
  1036. /* Pokitto platform using the official PokittoLib.
  1037. requirements: PokittoLib
  1038. compiling: as any other pokitto program, leave My_settings.h empty
  1039. ------------------------------------------------------------------------------*/
  1040. #ifndef SAF_SETTING_POKITTO_SCALE
  1041. #define SAF_SETTING_POKITTO_SCALE 0 /**< type of screen scale for Pokitto,
  1042. possible values: 0 (176x176), 1
  1043. (128x128), 2 (220x176) */
  1044. #endif
  1045. #ifndef SAF_SETTING_POKITTO_VOLUME
  1046. #define SAF_SETTING_POKITTO_VOLUME 4 //< 1 to 8 volume (if sound is on)
  1047. #endif
  1048. #ifndef SAF_SETTING_POKITTO_JOYHAT
  1049. #define SAF_SETTING_POKITTO_JOYHAT 0
  1050. #endif
  1051. #undef SAF_PLATFORM_NAME
  1052. #define SAF_PLATFORM_NAME "Pokitto"
  1053. #undef SAF_PLATFORM_RAM
  1054. #define SAF_PLATFORM_RAM 36000
  1055. #undef SAF_PLATFORM_FREQUENCY
  1056. #define SAF_PLATFORM_FREQUENCY ((_OSCT != 2) ? 48000000 : 72000000)
  1057. #include "Pokitto.h"
  1058. #include "POKITTO_HW/HWLCD.h"
  1059. #include "POKITTO_CORE/PokittoCookie.h"
  1060. #if SAF_SETTING_ENABLE_SOUND
  1061. #include "POKITTO_HW/HWSound.h"
  1062. #include "POKITTO_HW/clock_11u6x.h"
  1063. #include "POKITTO_HW/timer_11u6x.h"
  1064. #endif
  1065. #if SAF_SETTING_POKITTO_JOYHAT
  1066. #include "JoyHat/JoyHat.h"
  1067. JoyHat pokittoJoy;
  1068. uint16_t pokittoAxisThreshold1, pokittoAxisThreshold2;
  1069. uint16_t pokittoRumbleCooldown = 0;
  1070. #endif
  1071. uint8_t *pokittoScreen;
  1072. static const uint16_t pokittoPalette[256] = // 332 palette in 565 format
  1073. {
  1074. SAF_FE_PALETTE_565
  1075. };
  1076. #define CUSTOM_SCREEN_BUFFER \
  1077. (PROJ_SCREENBUFFERSIZE < SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT)
  1078. #if CUSTOM_SCREEN_BUFFER
  1079. // if PokittoLib doesn't have large enough screen buffer, we create our own:
  1080. uint8_t pokittoScreenBuffer[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT];
  1081. #endif
  1082. #if SAF_SETTING_ENABLE_SAVES
  1083. class SaveCookie: public Pokitto::Cookie
  1084. {
  1085. public:
  1086. uint8_t data[SAF_SAVE_SIZE];
  1087. };
  1088. SaveCookie pokittoSave;
  1089. #endif
  1090. #if SAF_SETTING_ENABLE_SOUND
  1091. int8_t pokittoCurrentSound = -1;
  1092. uint16_t pokittoSoundPos = 0;
  1093. void pokittoOnTimer() // for sound
  1094. {
  1095. if (Chip_TIMER_MatchPending(LPC_TIMER32_0,1))
  1096. {
  1097. Chip_TIMER_ClearMatch(LPC_TIMER32_0, 1);
  1098. if (pokittoCurrentSound >= 0)
  1099. {
  1100. Pokitto::dac_write((SAF_FE_getSoundSample(pokittoCurrentSound,
  1101. pokittoSoundPos) >> (8 - SAF_SETTING_POKITTO_VOLUME)
  1102. & (0xff >> (8 - SAF_SETTING_POKITTO_VOLUME))));
  1103. pokittoSoundPos++;
  1104. if (pokittoSoundPos >= SAF_FE_SOUND_SAMPLE_COUNT)
  1105. {
  1106. pokittoCurrentSound = -1;
  1107. pokittoSoundPos = 0;
  1108. }
  1109. }
  1110. }
  1111. }
  1112. void pokittoTimerInit(uint32_t samplingRate)
  1113. {
  1114. Chip_TIMER_Init(LPC_TIMER32_0);
  1115. Chip_TIMER_Reset(LPC_TIMER32_0);
  1116. Chip_TIMER_MatchEnableInt(LPC_TIMER32_0, 1);
  1117. Chip_TIMER_SetMatch(LPC_TIMER32_0, 1,
  1118. (Chip_Clock_GetSystemClockRate() / samplingRate));
  1119. Chip_TIMER_ResetOnMatchEnable(LPC_TIMER32_0, 1);
  1120. Chip_TIMER_Enable(LPC_TIMER32_0);
  1121. #define weirdNumber ((IRQn_Type) 18)
  1122. NVIC_ClearPendingIRQ(weirdNumber);
  1123. NVIC_SetVector(weirdNumber,(uint32_t) &pokittoOnTimer);
  1124. NVIC_EnableIRQ(weirdNumber);
  1125. #undef weirdNumber
  1126. }
  1127. #endif // if SAF_SETTING_ENABLE_SOUND
  1128. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  1129. {
  1130. #if SAF_SETTING_POKITTO_JOYHAT
  1131. pokittoScreen[x * SAF_SCREEN_WIDTH + (SAF_SCREEN_HEIGHT - 1 - y)] = color;
  1132. #else
  1133. pokittoScreen[y * SAF_SCREEN_WIDTH + x] = color;
  1134. #endif
  1135. }
  1136. void SAF_FE_playSound(uint8_t sound)
  1137. {
  1138. #if SAF_SETTING_ENABLE_SOUND
  1139. pokittoCurrentSound = sound;
  1140. #if SAF_SETTING_POKITTO_JOYHAT
  1141. if (sound == SAF_SOUND_BOOM && pokittoRumbleCooldown == 0)
  1142. {
  1143. pokittoJoy.Rumble(0.025);
  1144. pokittoRumbleCooldown = 32;
  1145. }
  1146. #endif
  1147. #else
  1148. _SAF_UNUSED(sound);
  1149. #endif
  1150. }
  1151. void SAF_FE_save(uint8_t index, uint8_t data)
  1152. {
  1153. #if SAF_SETTING_ENABLE_SAVES
  1154. pokittoSave.data[index] = data;
  1155. pokittoSave.saveCookie();
  1156. #if SAF_SETTING_ENABLE_SOUND
  1157. // PokittoLib bug: saving disables timer, so re-enable it:
  1158. pokittoTimerInit(8000);
  1159. #endif
  1160. #else
  1161. _SAF_UNUSED(index);
  1162. _SAF_UNUSED(data);
  1163. #endif
  1164. }
  1165. uint8_t SAF_FE_load(uint8_t index)
  1166. {
  1167. #if SAF_SETTING_ENABLE_SAVES
  1168. return pokittoSave.data[index];
  1169. #else
  1170. _SAF_UNUSED(index);
  1171. return 0;
  1172. #endif
  1173. }
  1174. uint8_t SAF_FE_buttonPressed(uint8_t button)
  1175. {
  1176. switch (button)
  1177. {
  1178. #if !SAF_SETTING_POKITTO_JOYHAT
  1179. case SAF_BUTTON_UP: return Pokitto::Core::upBtn(); break;
  1180. case SAF_BUTTON_RIGHT: return Pokitto::Core::rightBtn(); break;
  1181. case SAF_BUTTON_DOWN: return Pokitto::Core::downBtn(); break;
  1182. case SAF_BUTTON_LEFT: return Pokitto::Core::leftBtn(); break;
  1183. case SAF_BUTTON_A: return Pokitto::Core::aBtn(); break;
  1184. case SAF_BUTTON_B: return Pokitto::Core::bBtn(); break;
  1185. case SAF_BUTTON_C: return Pokitto::Core::cBtn(); break;
  1186. #else
  1187. case SAF_BUTTON_UP: return Pokitto::Core::rightBtn() ||
  1188. (pokittoJoy.JoyX() < pokittoAxisThreshold1);
  1189. break;
  1190. case SAF_BUTTON_RIGHT: return Pokitto::Core::downBtn() ||
  1191. (pokittoJoy.JoyY() > pokittoAxisThreshold2);
  1192. break;
  1193. case SAF_BUTTON_DOWN: return Pokitto::Core::leftBtn() ||
  1194. (pokittoJoy.JoyX() > pokittoAxisThreshold2);
  1195. break;
  1196. case SAF_BUTTON_LEFT: return Pokitto::Core::upBtn() ||
  1197. (pokittoJoy.JoyY() < pokittoAxisThreshold1);
  1198. break;
  1199. case SAF_BUTTON_A:
  1200. return Pokitto::Core::aBtn() || pokittoJoy.Button1(); break;
  1201. case SAF_BUTTON_B:
  1202. return Pokitto::Core::bBtn() || pokittoJoy.Button2(); break;
  1203. case SAF_BUTTON_C: return Pokitto::Core::cBtn(); break;
  1204. #endif
  1205. default: return 0; break;
  1206. }
  1207. #undef AXIS_THRES
  1208. }
  1209. static inline const char *SAF_FE_extension(const char *string)
  1210. {
  1211. return SAF_FE_emptyString;
  1212. }
  1213. #if SAF_SETTING_POKITTO_SCALE == 0 || SAF_SETTING_POKITTO_SCALE == 2
  1214. static const uint8_t upscaleMap[176] =
  1215. {
  1216. 0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5,5,6,6,6,7,7,7,8,8,9,9,9,10,10,10,11,11,11,12,
  1217. 12,13,13,13,14,14,14,15,15,15,16,16,17,17,17,18,18,18,19,19,19,20,20,21,21,21,
  1218. 22,22,22,23,23,23,24,24,25,25,25,26,26,26,27,27,27,28,28,29,29,29,30,30,30,31,
  1219. 31,31,32,32,33,33,33,34,34,34,35,35,35,36,36,37,37,37,38,38,38,39,39,39,40,40,
  1220. 41,41,41,42,42,42,43,43,43,44,44,45,45,45,46,46,46,47,47,47,48,48,49,49,49,50,
  1221. 50,50,51,51,51,52,52,53,53,53,54,54,54,55,55,55,56,56,57,57,57,58,58,58,59,59,
  1222. 59,60,60,61,61,61,62,62,62,63,63
  1223. };
  1224. #endif
  1225. #if SAF_SETTING_POKITTO_SCALE == 2
  1226. static const uint8_t upscaleMap2[220] =
  1227. {
  1228. 0,0,0,0,1,1,1,2,2,2,2,3,3,3,4,4,4,4,5,5,5,6,6,6,6,7,7,7,8,8,8,9,9,9,9,10,10,
  1229. 10,11,11,11,11,12,12,12,13,13,13,13,14,14,14,15,15,15,16,16,16,16,17,17,17,18,
  1230. 18,18,18,19,19,19,20,20,20,20,21,21,21,22,22,22,22,23,23,23,24,24,24,25,25,25,
  1231. 25,26,26,26,27,27,27,27,28,28,28,29,29,29,29,30,30,30,31,31,31,32,32,32,32,33,
  1232. 33,33,34,34,34,34,35,35,35,36,36,36,36,37,37,37,38,38,38,38,39,39,39,40,40,40,
  1233. 41,41,41,41,42,42,42,43,43,43,43,44,44,44,45,45,45,45,46,46,46,47,47,47,48,48,
  1234. 48,48,49,49,49,50,50,50,50,51,51,51,52,52,52,52,53,53,53,54,54,54,54,55,55,55,
  1235. 56,56,56,57,57,57,57,58,58,58,59,59,59,59,60,60,60,61,61,61,61,62,62,62,63,63,
  1236. 63
  1237. };
  1238. #endif
  1239. int main()
  1240. {
  1241. #if SAF_SETTING_ENABLE_SAVES
  1242. pokittoSave.begin(
  1243. "SAF" SAF_PROGRAM_NAME,sizeof(pokittoSave),(char*) &pokittoSave);
  1244. #endif
  1245. Pokitto::Core::begin();
  1246. #if SAF_SETTING_ENABLE_SOUND
  1247. pokittoTimerInit(8000);
  1248. #endif
  1249. #if CUSTOM_SCREEN_BUFFER
  1250. pokittoScreen = pokittoScreenBuffer;
  1251. #else
  1252. pokittoScreen = Pokitto::Display::screenbuffer;
  1253. #endif
  1254. Pokitto::Core::setFrameRate(SAF_FPS);
  1255. Pokitto::Display::persistence = 1;
  1256. Pokitto::Display::setInvisibleColor(-1);
  1257. for (uint16_t y = 0; y < 176; ++y)
  1258. for (uint16_t x = 0; x < 220; ++x)
  1259. Pokitto::Display::directPixel(x,y,
  1260. pokittoPalette[SAF_SETTING_BACKGROUND_COLOR]);
  1261. #if SAF_SETTING_POKITTO_JOYHAT
  1262. pokittoAxisThreshold1 = pokittoJoy.joyScale / 4;
  1263. pokittoAxisThreshold2 = pokittoJoy.joyScale - pokittoAxisThreshold1;
  1264. #endif
  1265. SAF_FE_init();
  1266. uint32_t nextFrame = 0;
  1267. while (Pokitto::Core::isRunning())
  1268. {
  1269. Pokitto::Core::update(true);
  1270. #if SAF_SETTING_POKITTO_JOYHAT
  1271. if (pokittoRumbleCooldown > 0)
  1272. pokittoRumbleCooldown--;
  1273. #endif
  1274. uint32_t time = Pokitto::Core::getTime();
  1275. // we handle FPS ourselves as Pokittolib has a bug
  1276. if (time >= nextFrame)
  1277. {
  1278. while (time >= nextFrame)
  1279. {
  1280. SAF_FE_loop();
  1281. nextFrame += SAF_MS_PER_FRAME;
  1282. }
  1283. const uint8_t *p = pokittoScreen;
  1284. #if SAF_SETTING_POKITTO_SCALE == 0 || SAF_SETTING_POKITTO_SCALE == 2
  1285. // 176x176, 220x176
  1286. #if SAF_SETTING_POKITTO_SCALE == 0
  1287. #define SCR_W 176
  1288. #define SCR_X 22
  1289. #else
  1290. #define SCR_W 220
  1291. #define SCR_X 0
  1292. #endif
  1293. uint16_t line[SCR_W];
  1294. uint8_t previousLine = 255;
  1295. for (int16_t j = 0; j < 176; ++j)
  1296. {
  1297. int16_t upscaleMapRow = upscaleMap[j];
  1298. if (previousLine != upscaleMapRow)
  1299. {
  1300. const uint8_t *l = pokittoScreen + SAF_SCREEN_WIDTH * upscaleMapRow;
  1301. uint16_t *ll = line;
  1302. const uint8_t *m =
  1303. #if SAF_SETTING_POKITTO_SCALE == 0
  1304. upscaleMap;
  1305. #else
  1306. upscaleMap2;
  1307. #endif
  1308. for (int16_t i = 0; i < SCR_W; ++i)
  1309. {
  1310. uint16_t c = pokittoPalette[*(l + *m)];
  1311. *ll = c;
  1312. ll++;
  1313. m++;
  1314. p++;
  1315. }
  1316. }
  1317. previousLine = upscaleMapRow;
  1318. Pokitto::setDRAMpoint(SCR_X,j);
  1319. Pokitto::pumpDRAMdata(line,SCR_W);
  1320. }
  1321. #elif SAF_SETTING_POKITTO_SCALE == 1 // 128x128
  1322. uint16_t line[SAF_SCREEN_WIDTH * 2];
  1323. int16_t yPos = 24;
  1324. for (int16_t j = 0; j < SAF_SCREEN_WIDTH * 2; j += 2)
  1325. {
  1326. uint16_t *l = line;
  1327. for (int16_t i = 0; i < SAF_SCREEN_WIDTH; ++i)
  1328. {
  1329. uint16_t c = pokittoPalette[*p];
  1330. *l = c;
  1331. l++;
  1332. *l = c;
  1333. l++;
  1334. p++;
  1335. }
  1336. Pokitto::setDRAMpoint(46,yPos);
  1337. Pokitto::pumpDRAMdata(line,SAF_SCREEN_WIDTH * 2);
  1338. yPos++;
  1339. Pokitto::setDRAMpoint(46,yPos);
  1340. Pokitto::pumpDRAMdata(line,SAF_SCREEN_WIDTH * 2);
  1341. yPos++;
  1342. }
  1343. #endif
  1344. }
  1345. }
  1346. return 0;
  1347. }
  1348. #elif defined(SAF_PLATFORM_NCURSES)
  1349. /* ncuses (terminal, text-based) platform, this does not offer a "full"
  1350. experience as terminal real time I/O handling and image displaying are
  1351. limited, but some games are playable
  1352. requirements: libncurses-dev, sys/time.h, stdio
  1353. compiling: link ncurses, e.g. -lncurses
  1354. ------------------------------------------------------------------------------*/
  1355. #define SAF_FE_STDIO_SAVE_LOAD
  1356. #undef SAF_PLATFORM_NAME
  1357. #define SAF_PLATFORM_NAME "ncurses"
  1358. #undef SAF_PLATFORM_COLOR_COUNT
  1359. #define SAF_PLATFORM_COLOR_COUNT 2
  1360. #include <ncurses.h>
  1361. #include <sys/time.h>
  1362. #include <stdio.h> // for files
  1363. #define OFFSET_X 1
  1364. #define OFFSET_Y 1
  1365. uint8_t ncScreen[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT];
  1366. uint8_t ncButtonStates[SAF_BUTTONS];
  1367. uint8_t ncCurrentSound = 0;
  1368. uint32_t ncSoundEnd = 0;
  1369. uint32_t ncGetTime()
  1370. {
  1371. struct timeval now;
  1372. gettimeofday(&now, NULL);
  1373. return now.tv_sec * 1000 + now.tv_usec / 1000;
  1374. }
  1375. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  1376. {
  1377. ncScreen[y * SAF_SCREEN_WIDTH + x] = color;
  1378. }
  1379. void SAF_FE_playSound(uint8_t sound)
  1380. {
  1381. ncCurrentSound = sound;
  1382. ncSoundEnd = ncGetTime() + 1000;
  1383. }
  1384. uint8_t SAF_FE_buttonPressed(uint8_t button)
  1385. {
  1386. return ncButtonStates[button];
  1387. }
  1388. const char *SAF_FE_extension(const char *string)
  1389. {
  1390. _SAF_UNUSED(string);
  1391. return SAF_FE_emptyString;
  1392. }
  1393. void printHelp(void)
  1394. {
  1395. puts(SAF_PROGRAM_NAME
  1396. "\n " SAF_INFO_STRING " (" SAF_PLATFORM_NAME ")"
  1397. "\n controls: WSAD arrows, JKL YZXC space, Q = quit"
  1398. "\n possible arguments: -h (help), -i (invert colors)"
  1399. );
  1400. }
  1401. int main(int argc, char **argv)
  1402. {
  1403. int invert = 0;
  1404. for (int i = 0; i < argc; ++i)
  1405. if (argv[i][0] == '-' &&
  1406. argv[i][1] != 0 &&
  1407. argv[i][2] == 0)
  1408. {
  1409. switch (argv[i][1])
  1410. {
  1411. case 'h': printHelp(); return 0; break;
  1412. case 'i': invert = 1;
  1413. default: break;
  1414. }
  1415. }
  1416. for (int i = 0; i < SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT; ++i)
  1417. ncScreen[i] = 0;
  1418. initscr();
  1419. halfdelay(1);
  1420. keypad(stdscr,TRUE);
  1421. noecho();
  1422. curs_set(0);
  1423. SAF_FE_init();
  1424. uint32_t nextFrame = ncGetTime();
  1425. char c00 = ' ', c01 = ',', c10 = '\'', c11 = ';';
  1426. if (invert)
  1427. {
  1428. c00 = ';'; c01 = '\''; c10 = ','; c11 = ' ';
  1429. }
  1430. while (1)
  1431. {
  1432. for (int i = 0; i < SAF_BUTTONS; ++i)
  1433. ncButtonStates[i] = 0;
  1434. int c = getch();
  1435. int goOn = 1;
  1436. switch (c)
  1437. {
  1438. case KEY_UP: case 'w':
  1439. ncButtonStates[SAF_BUTTON_UP] = 1; break;
  1440. case KEY_LEFT: case 'a':
  1441. ncButtonStates[SAF_BUTTON_LEFT] = 1; break;
  1442. case KEY_RIGHT: case 'd':
  1443. ncButtonStates[SAF_BUTTON_RIGHT] = 1; break;
  1444. case KEY_DOWN: case 's':
  1445. ncButtonStates[SAF_BUTTON_DOWN] = 1; break;
  1446. case ' ': case 'y': case 'z': case 'j':
  1447. ncButtonStates[SAF_BUTTON_A] = 1; break;
  1448. case 'x': case 'k':
  1449. ncButtonStates[SAF_BUTTON_B] = 1; break;
  1450. case 'c': case 'l':
  1451. ncButtonStates[SAF_BUTTON_C] = 1; break;
  1452. case 'q':
  1453. goOn = 0; break;
  1454. default: break;
  1455. }
  1456. uint32_t time = ncGetTime();
  1457. while (time >= nextFrame)
  1458. {
  1459. if (!SAF_FE_loop())
  1460. {
  1461. goOn = 0;
  1462. break;
  1463. }
  1464. nextFrame += SAF_MS_PER_FRAME;
  1465. }
  1466. if (!goOn)
  1467. break;
  1468. /* One terminal character will correspong to two pixels vertically nexto
  1469. to each other, so we'll be scanning two display lines at once. */
  1470. const uint8_t* scr = ncScreen;
  1471. const uint8_t* scr2 = ncScreen + SAF_SCREEN_WIDTH;
  1472. erase();
  1473. move(OFFSET_Y,OFFSET_X + 1);
  1474. for (int i = 0; i < SAF_SCREEN_WIDTH; ++i)
  1475. addch('_');
  1476. for (int y = 0; y < SAF_SCREEN_HEIGHT / 2; ++y)
  1477. {
  1478. move(y + OFFSET_Y + 1,1);
  1479. addch('|');
  1480. for (int x = 0; x < SAF_SCREEN_WIDTH; ++x)
  1481. {
  1482. uint8_t pixels = ((SAF_colorTo1Bit(*scr) != 0) << 1) |
  1483. (SAF_colorTo1Bit(*scr2) != 0);
  1484. char p;
  1485. switch (pixels)
  1486. {
  1487. case 0: p = c00; break;
  1488. case 1: p = c01; break;
  1489. case 2: p = c10; break;
  1490. case 3: p = c11; break;
  1491. default: p = ' '; break;
  1492. }
  1493. addch(p);
  1494. scr++;
  1495. scr2++;
  1496. }
  1497. addch('|');
  1498. scr += SAF_SCREEN_WIDTH;
  1499. scr2 += SAF_SCREEN_WIDTH;
  1500. }
  1501. move(OFFSET_Y + 1 + SAF_SCREEN_HEIGHT / 2,2);
  1502. for (int i = 0; i < SAF_SCREEN_WIDTH; ++i)
  1503. addch('-');
  1504. move(0,1);
  1505. printw(SAF_PROGRAM_NAME);
  1506. if (time < ncSoundEnd)
  1507. {
  1508. switch (ncCurrentSound)
  1509. {
  1510. case SAF_SOUND_BEEP: printw(" (BEEP)"); break;
  1511. case SAF_SOUND_CLICK: printw(" (click)"); break;
  1512. case SAF_SOUND_BOOM: printw(" (BOOM!)"); break;
  1513. case SAF_SOUND_BUMP: printw(" (bump!)"); break;
  1514. default: break;
  1515. }
  1516. }
  1517. refresh();
  1518. }
  1519. endwin();
  1520. return 0;
  1521. }
  1522. #elif defined(SAF_PLATFORM_X11)
  1523. /* X11 (xwindow, XLib) frontend
  1524. requirements: XLib, stdio.h, unistd.h, sys/time.h
  1525. compiling: link XLib, e.g. -lX11
  1526. ------------------------------------------------------------------------------*/
  1527. #include <X11/Xlib.h>
  1528. #include <X11/keysym.h>
  1529. #include <stdio.h>
  1530. #include <sys/time.h>
  1531. #include <unistd.h> // for usleep
  1532. #undef SAF_PLATFORM_NAME
  1533. #define SAF_PLATFORM_NAME "X11"
  1534. #define SAF_FE_STDIO_SAVE_LOAD
  1535. #define SOUND_BOOM "BOOM!"
  1536. #define SOUND_CLICK "click"
  1537. #define SOUND_BEEP "Beep"
  1538. #define SOUND_BUMP "bump!"
  1539. uint8_t _x11Scr[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT];
  1540. uint8_t _x11Buttons[SAF_BUTTONS];
  1541. uint8_t _x11CurrentSound = 0;
  1542. uint32_t _x11SoundEnd = 0;
  1543. uint32_t _x11GetTime()
  1544. {
  1545. struct timeval now;
  1546. gettimeofday(&now,NULL);
  1547. return now.tv_sec * 1000 + now.tv_usec / 1000;
  1548. }
  1549. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  1550. {
  1551. _x11Scr[y * SAF_SCREEN_WIDTH + x] = color;
  1552. }
  1553. void SAF_FE_playSound(uint8_t sound)
  1554. {
  1555. _x11CurrentSound = sound;
  1556. _x11SoundEnd = _x11GetTime() + 1000;
  1557. switch (sound)
  1558. {
  1559. case SAF_SOUND_CLICK: puts(SOUND_CLICK); break;
  1560. case SAF_SOUND_BEEP: puts(SOUND_BEEP); break;
  1561. case SAF_SOUND_BOOM: puts(SOUND_BOOM); break;
  1562. case SAF_SOUND_BUMP: puts(SOUND_BUMP); break;
  1563. default: break;
  1564. }
  1565. }
  1566. uint8_t SAF_FE_buttonPressed(uint8_t button)
  1567. {
  1568. return _x11Buttons[button];
  1569. }
  1570. const char *SAF_FE_extension(const char *string)
  1571. {
  1572. _SAF_UNUSED(string);
  1573. return SAF_FE_emptyString;
  1574. }
  1575. void printHelp(void)
  1576. {
  1577. puts(SAF_PROGRAM_NAME
  1578. "\n " SAF_INFO_STRING " (" SAF_PLATFORM_NAME ")"
  1579. "\n controls: WASD arrows, JKL YZXC space return, Esc = quit"
  1580. "\n possible arguments: -h (print help), -N (N = 1..8, scale)"
  1581. );
  1582. }
  1583. unsigned long _palette[256];
  1584. int main(int argc, char **argv)
  1585. {
  1586. int scale = 4;
  1587. for (int i = 0; i < argc; ++i)
  1588. {
  1589. char *arg = argv[i];
  1590. if (arg[0] != 0 && arg[1] != 0 && arg[2] == 0)
  1591. {
  1592. if (arg[1] == 'h')
  1593. {
  1594. printHelp();
  1595. return 0;
  1596. }
  1597. if (arg[1] >= '1' && arg[1] <= '8')
  1598. scale = arg[1] - '0';
  1599. }
  1600. }
  1601. for (int i = 0; i < SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT; ++i)
  1602. _x11Scr[i] = 0;
  1603. for (int i = 0; i < SAF_BUTTONS; ++i)
  1604. _x11Buttons[i] = 0;
  1605. SAF_FE_init();
  1606. Display *display = XOpenDisplay(0);
  1607. if (display == 0)
  1608. {
  1609. puts("could not open a display");
  1610. return 0;
  1611. }
  1612. int screen = DefaultScreen(display);
  1613. Window window = XCreateSimpleWindow(display,RootWindow(display,screen),10,10,
  1614. SAF_SCREEN_WIDTH * scale,SAF_SCREEN_HEIGHT * scale,1,
  1615. BlackPixel(display,screen),WhitePixel(display,screen));
  1616. XMapWindow(display,window);
  1617. XSelectInput(display,window,KeyPressMask | KeyReleaseMask);
  1618. // create the palette:
  1619. for (int i = 0; i < 256; ++i)
  1620. {
  1621. XColor color;
  1622. uint8_t r, g, b;
  1623. SAF_colorToRGB(i,&r,&g,&b);
  1624. color.red = ((uint16_t) r) * 256;
  1625. color.green = ((uint16_t) g) * 256;
  1626. color.blue = ((uint16_t) b) * 256;
  1627. color.flags = DoRed | DoGreen | DoBlue;
  1628. XAllocColor(display,DefaultColormap(display,0),&color);
  1629. _palette[i] = color.pixel;
  1630. }
  1631. GContext context = DefaultGC(display,screen);
  1632. uint32_t nextFrame = _x11GetTime();
  1633. int goOn = 1;
  1634. uint8_t previousSound = 255;
  1635. while (1) // main loop
  1636. {
  1637. uint32_t time = _x11GetTime();
  1638. while (time >= nextFrame)
  1639. {
  1640. if (!SAF_FE_loop())
  1641. {
  1642. goOn = 0;
  1643. break;
  1644. }
  1645. nextFrame += SAF_MS_PER_FRAME;
  1646. }
  1647. usleep(((nextFrame - time) * 3 / 4) * 1000); // relieve CPU
  1648. if (!goOn)
  1649. break;
  1650. const uint8_t *pixel = _x11Scr;
  1651. int drawX = 0, drawY = 0;
  1652. for (int y = 0; y < SAF_SCREEN_HEIGHT; ++y)
  1653. {
  1654. drawX = 0;
  1655. for (int x = 0; x < SAF_SCREEN_WIDTH; ++x)
  1656. {
  1657. XSetForeground(display,context,_palette[*pixel]);
  1658. XFillRectangle(display,window,context,drawX,drawY,
  1659. scale,scale);
  1660. pixel++;
  1661. drawX += scale;
  1662. }
  1663. drawY += scale;
  1664. }
  1665. if (time >= _x11SoundEnd)
  1666. _x11CurrentSound = 255;
  1667. if (_x11CurrentSound != previousSound)
  1668. {
  1669. switch (_x11CurrentSound)
  1670. {
  1671. case SAF_SOUND_BEEP:
  1672. XStoreName(display, window, SAF_PROGRAM_NAME " " SOUND_BEEP); break;
  1673. case SAF_SOUND_CLICK:
  1674. XStoreName(display, window, SAF_PROGRAM_NAME " " SOUND_CLICK); break;
  1675. case SAF_SOUND_BOOM:
  1676. XStoreName(display, window, SAF_PROGRAM_NAME " " SOUND_BOOM); break;
  1677. case SAF_SOUND_BUMP:
  1678. XStoreName(display, window, SAF_PROGRAM_NAME " " SOUND_BUMP); break;
  1679. default: XStoreName(display, window, SAF_PROGRAM_NAME); break;
  1680. }
  1681. }
  1682. previousSound = _x11CurrentSound;
  1683. XEvent event;
  1684. while (XCheckWindowEvent(display,window,KeyPressMask | KeyReleaseMask,&event) != False)
  1685. {
  1686. uint8_t state = event.xkey.type == KeyPress;
  1687. switch (XKeycodeToKeysym(display,event.xkey.keycode,0))
  1688. {
  1689. case XK_Escape: goOn = 0; break;
  1690. case XK_Up: case XK_w:
  1691. _x11Buttons[SAF_BUTTON_UP] = state; break;
  1692. case XK_Left: case XK_a:
  1693. _x11Buttons[SAF_BUTTON_LEFT] = state; break;
  1694. case XK_Right: case XK_d:
  1695. _x11Buttons[SAF_BUTTON_RIGHT] = state; break;
  1696. case XK_Down: case XK_s:
  1697. _x11Buttons[SAF_BUTTON_DOWN] = state; break;
  1698. case XK_y: case XK_z: case XK_j: case XK_space:
  1699. _x11Buttons[SAF_BUTTON_A] = state; break;
  1700. case XK_x: case XK_k: case XK_Return:
  1701. _x11Buttons[SAF_BUTTON_B] = state; break;
  1702. case XK_c: case XK_l:
  1703. _x11Buttons[SAF_BUTTON_C] = state; break;
  1704. default: break;
  1705. }
  1706. }
  1707. if (!goOn)
  1708. break;
  1709. }
  1710. XCloseDisplay(display);
  1711. return 0;
  1712. }
  1713. #elif defined(SAF_PLATFORM_ARDUBOY)
  1714. /* Arduboy platform using the official Arduboy2 library.
  1715. requirements: Arduino environment, Arduboy2 library
  1716. compiling: compile with Arduino IDE
  1717. ------------------------------------------------------------------------------*/
  1718. #undef SAF_PLATFORM_NAME
  1719. #define SAF_PLATFORM_NAME "Arduboy"
  1720. #undef SAF_PLATFORM_COLOR_COUNT
  1721. #define SAF_PLATFORM_COLOR_COUNT 2
  1722. #undef SAF_PLATFORM_BUTTON_COUNT
  1723. #define SAF_PLATFORM_BUTTON_COUNT 6
  1724. #undef SAF_PLATFORM_HARWARD
  1725. #define SAF_PLATFORM_HARWARD 1
  1726. #undef SAF_PLATFORM_RAM
  1727. #define SAF_PLATFORM_RAM 2500
  1728. #undef SAF_PLATFORM_FREQUENCY
  1729. #define SAF_PLATFORM_FREQUENCY 16000000
  1730. #include <Arduboy2.h>
  1731. Arduboy2 arduboy;
  1732. #if SAF_SETTING_ENABLE_SOUND
  1733. BeepPin1 arduboyBeep;
  1734. #endif
  1735. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  1736. {
  1737. arduboy.drawPixel(32 + x,y,color ? WHITE : BLACK);
  1738. }
  1739. void SAF_FE_playSound(uint8_t sound)
  1740. {
  1741. #if SAF_SETTING_ENABLE_SOUND
  1742. uint16_t f = 0;
  1743. uint8_t t = 0;
  1744. switch (sound)
  1745. {
  1746. case SAF_SOUND_BEEP: f = 300; t = 10; break;
  1747. case SAF_SOUND_CLICK: f = 800; t = 1; break;
  1748. case SAF_SOUND_BOOM: f = 50; t = 8; break;
  1749. case SAF_SOUND_BUMP: f = 100; t = 2; break;
  1750. default: break;
  1751. }
  1752. arduboyBeep.tone(arduboyBeep.freq(f),t);
  1753. #endif
  1754. }
  1755. #define SAVE_VALID_VALUE 133
  1756. uint16_t saveAddress = 0;
  1757. uint8_t saveLoaded = 0;
  1758. void arduboyLoadSave(void)
  1759. {
  1760. if (!saveLoaded)
  1761. {
  1762. if (EEPROM.read(saveAddress) != SAVE_VALID_VALUE)
  1763. {
  1764. EEPROM.update(saveAddress,SAVE_VALID_VALUE);
  1765. for (uint8_t i = 0; i < SAF_SAVE_SIZE; ++i)
  1766. EEPROM.update(saveAddress + 1 + i,0);
  1767. }
  1768. saveLoaded = 1;
  1769. }
  1770. }
  1771. void SAF_FE_save(uint8_t index, uint8_t data)
  1772. {
  1773. arduboyLoadSave();
  1774. EEPROM.update(saveAddress + 1 + index,data);
  1775. }
  1776. uint8_t SAF_FE_load(uint8_t index)
  1777. {
  1778. arduboyLoadSave();
  1779. return EEPROM.read(saveAddress + 1 + index);
  1780. }
  1781. uint8_t SAF_FE_buttonPressed(uint8_t button)
  1782. {
  1783. switch (button)
  1784. {
  1785. case SAF_BUTTON_UP: button = UP_BUTTON; break;
  1786. case SAF_BUTTON_RIGHT: button = RIGHT_BUTTON; break;
  1787. case SAF_BUTTON_DOWN: button = DOWN_BUTTON; break;
  1788. case SAF_BUTTON_LEFT: button = LEFT_BUTTON; break;
  1789. case SAF_BUTTON_A: button = A_BUTTON; break;
  1790. case SAF_BUTTON_B: button = B_BUTTON; break;
  1791. default: return 0; break;
  1792. }
  1793. return arduboy.pressed(button);
  1794. }
  1795. const char *SAF_FE_extension(const char *string)
  1796. {
  1797. _SAF_UNUSED(string);
  1798. return SAF_FE_emptyString;
  1799. }
  1800. void setup()
  1801. {
  1802. arduboy.begin();
  1803. arduboy.clear();
  1804. #if SAF_SETTING_ENABLE_SOUND
  1805. arduboyBeep.begin();
  1806. arduboy.audio.on();
  1807. #endif
  1808. uint8_t c = SAF_colorTo1Bit(SAF_SETTING_BACKGROUND_COLOR) ? WHITE : BLACK;
  1809. for (uint8_t y = 0; y < 64; ++y)
  1810. for (uint8_t x = 0; x < 128; ++x)
  1811. arduboy.drawPixel(x,y,c);
  1812. arduboy.setFrameRate(SAF_FPS);
  1813. SAF_FE_init();
  1814. saveAddress = SAF_FE_hashStr(SAF_PROGRAM_NAME) & 0x03ff;
  1815. if (saveAddress > (1024 - SAF_SAVE_SIZE - 1)) // -1 for valid value
  1816. saveAddress = 1024 - SAF_SAVE_SIZE - 1;
  1817. }
  1818. void loop()
  1819. {
  1820. if (!(arduboy.nextFrame()))
  1821. return;
  1822. #if SAF_SETTING_ENABLE_SOUND
  1823. arduboyBeep.timer();
  1824. #endif
  1825. arduboy.pollButtons();
  1826. SAF_FE_loop();
  1827. arduboy.display();
  1828. }
  1829. #elif defined(SAF_PLATFORM_ESPBOY)
  1830. /* ESPBoy platform.
  1831. requirements: Arduino environment and required libraries
  1832. compiling: compile with Arduino IDE
  1833. ------------------------------------------------------------------------------*/
  1834. #undef SAF_PLATFORM_NAME
  1835. #define SAF_PLATFORM_NAME "ESPBoy"
  1836. #undef SAF_PLATFORM_RAM
  1837. #define SAF_PLATFORM_RAM 4000000
  1838. #undef SAF_PLATFORM_FREQUENCY
  1839. #define SAF_PLATFORM_FREQUENCY 80000000
  1840. #undef SAF_PLATFORM_HARWARD
  1841. #define SAF_PLATFORM_HARWARD 1
  1842. #include <Arduino.h>
  1843. #include <Adafruit_MCP23017.h>
  1844. #include <Adafruit_MCP4725.h>
  1845. #include <TFT_eSPI.h>
  1846. #if SAF_SETTING_ENABLE_SAVES
  1847. #include <ESP_EEPROM.h>
  1848. uint8_t espboySaveValidValue = 0;
  1849. EEPROMClass espboyEeprom;
  1850. #endif
  1851. #define MCP23017address 0
  1852. #define MCP4725address 0x60
  1853. Adafruit_MCP23017 espboyMcp;
  1854. Adafruit_MCP4725 espboyDac;
  1855. TFT_eSPI espboyTft;
  1856. uint8_t espboyScreen[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT];
  1857. uint8_t espboyKeys = 0;
  1858. PROGMEM uint16_t espboyPalette[256] =
  1859. {
  1860. SAF_FE_PALETTE_565
  1861. };
  1862. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  1863. {
  1864. espboyScreen[y * SAF_SCREEN_WIDTH + x] = color;
  1865. }
  1866. void SAF_FE_playSound(uint8_t sound)
  1867. {
  1868. #if SAF_SETTING_ENABLE_SOUND
  1869. int freq = 400;
  1870. int dur = 75;
  1871. switch (sound)
  1872. {
  1873. case SAF_SOUND_CLICK: freq = 400; dur = 20; break;
  1874. case SAF_SOUND_BEEP: freq = 300; dur = 150; break;
  1875. case SAF_SOUND_BOOM: freq = 70; dur = 200; break;
  1876. case SAF_SOUND_BUMP: freq = 130; dur = 50; break;
  1877. default: break;
  1878. }
  1879. tone(D3,freq,dur);
  1880. #else
  1881. _SAF_UNUSED(sound);
  1882. #endif
  1883. }
  1884. #if SAF_SETTING_ENABLE_SAVES
  1885. void espboyCheckEeprom(void)
  1886. {
  1887. if (espboyEeprom.read(0) == espboySaveValidValue)
  1888. return;
  1889. espboyEeprom.write(0,espboySaveValidValue);
  1890. for (uint8_t i = 0; i < SAF_SAVE_SIZE; ++i)
  1891. espboyEeprom.write(i + 1,0);
  1892. espboyEeprom.commit();
  1893. }
  1894. #endif
  1895. void SAF_FE_save(uint8_t index, uint8_t data)
  1896. {
  1897. #if SAF_SETTING_ENABLE_SAVES
  1898. espboyCheckEeprom();
  1899. espboyEeprom.write(index + 1,data);
  1900. espboyEeprom.commit();
  1901. #else
  1902. _SAF_UNUSED(index);
  1903. _SAF_UNUSED(data);
  1904. #endif
  1905. }
  1906. uint8_t SAF_FE_load(uint8_t index)
  1907. {
  1908. #if SAF_SETTING_ENABLE_SAVES
  1909. espboyCheckEeprom();
  1910. return espboyEeprom.read(index + 1);
  1911. #else
  1912. _SAF_UNUSED(index);
  1913. #endif
  1914. }
  1915. uint8_t SAF_FE_buttonPressed(uint8_t button)
  1916. {
  1917. switch (button)
  1918. {
  1919. case SAF_BUTTON_UP: return espboyKeys & 0x02; break;
  1920. case SAF_BUTTON_DOWN: return espboyKeys & 0x04; break;
  1921. case SAF_BUTTON_RIGHT: return espboyKeys & 0x08; break;
  1922. case SAF_BUTTON_LEFT: return espboyKeys & 0x01; break;
  1923. case SAF_BUTTON_A: return espboyKeys & 0x10; break;
  1924. case SAF_BUTTON_B: return espboyKeys & 0x20; break;
  1925. case SAF_BUTTON_C: return espboyKeys & 0x80; break;
  1926. default: return 0; break;
  1927. }
  1928. }
  1929. const char *SAF_FE_extension(const char *string)
  1930. {
  1931. _SAF_UNUSED(string);
  1932. return SAF_FE_emptyString;
  1933. }
  1934. void setup()
  1935. {
  1936. espboyDac.begin(MCP4725address);
  1937. delay(100);
  1938. espboyDac.setVoltage(0,false);
  1939. espboyMcp.begin(MCP23017address);
  1940. delay(100);
  1941. #if SAF_SETTING_ENABLE_SAVES
  1942. espboySaveValidValue = SAF_FE_hashStr(SAF_PROGRAM_NAME);
  1943. #endif
  1944. // buttons
  1945. for (uint8_t i = 0; i < 8; i++)
  1946. {
  1947. espboyMcp.pinMode(i,INPUT);
  1948. espboyMcp.pullUp(i,HIGH);
  1949. }
  1950. espboyMcp.pinMode(8,OUTPUT);
  1951. espboyMcp.digitalWrite(8,LOW);
  1952. espboyTft.begin();
  1953. delay(100);
  1954. espboyTft.setRotation(0);
  1955. espboyTft.setAddrWindow(0,128,0,128);
  1956. espboyTft.fillScreen(TFT_BLACK);
  1957. espboyDac.setVoltage(4095,true); // backlight
  1958. #if SAF_SETTING_ENABLE_SOUND
  1959. pinMode(D3,OUTPUT);
  1960. #endif
  1961. #if SAF_SETTING_ENABLE_SAVES
  1962. espboyEeprom.begin(SAF_SAVE_SIZE + 1);
  1963. #endif
  1964. for (uint16_t i = 0; i < SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT; ++i)
  1965. espboyScreen[i] = 0;
  1966. SAF_FE_init();
  1967. }
  1968. uint32_t espboyNextFrame = 0;
  1969. void loop()
  1970. {
  1971. uint32_t time = millis();
  1972. if (time < espboyNextFrame)
  1973. return;
  1974. while (time >= espboyNextFrame)
  1975. {
  1976. SAF_FE_loop();
  1977. espboyNextFrame += SAF_MS_PER_FRAME;
  1978. }
  1979. espboyKeys = ~espboyMcp.readGPIOAB() & 255;
  1980. const uint8_t *pixel = espboyScreen;
  1981. uint16_t line[SAF_SCREEN_WIDTH * 2];
  1982. for (int y = 0; y < SAF_SCREEN_HEIGHT; ++y)
  1983. {
  1984. uint16_t *p = line;
  1985. for (int x = 0; x < SAF_SCREEN_WIDTH; ++x)
  1986. {
  1987. uint16_t c = pgm_read_word(espboyPalette + *pixel);
  1988. *p = c;
  1989. p++;
  1990. *p = c;
  1991. p++;
  1992. pixel++;
  1993. }
  1994. espboyTft.pushColors(line,SAF_SCREEN_WIDTH * 2);
  1995. espboyTft.pushColors(line,SAF_SCREEN_WIDTH * 2);
  1996. }
  1997. }
  1998. #elif defined(SAF_PLATFORM_NIBBLE)
  1999. /* Circuitmess Nibble platform. Persistent save/load isn't supported (EEPROM
  2000. somehow doesn't work).
  2001. requirements: Arduino environment and required libraries
  2002. compiling: compile with Arduino IDE
  2003. ------------------------------------------------------------------------------*/
  2004. #include <Arduino.h>
  2005. #include <CircuitOS.h>
  2006. #include <Nibble.h>
  2007. #undef SAF_PLATFORM_HARWARD
  2008. #define SAF_PLATFORM_HARWARD 1
  2009. #undef SAF_PLATFORM_NAME
  2010. #define SAF_PLATFORM_NAME "Nibble"
  2011. #undef SAF_PLATFORM_FREQUENCY
  2012. #define SAF_PLATFORM_FREQUENCY 160000000
  2013. #undef SAF_PLATFORM_RAM
  2014. #define SAF_PLATFORM_RAM 80000
  2015. #undef SAF_PLATFORM_HAS_SAVES
  2016. #define SAF_PLATFORM_HAS_SAVES 0
  2017. Display *nibbleDisplay;
  2018. uint16_t *nibbleFrameBuffer;
  2019. uint8_t nibbleButtons[SAF_BUTTONS];
  2020. PROGMEM uint16_t nibblePalette[256] =
  2021. {
  2022. SAF_FE_PALETTE_565
  2023. };
  2024. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  2025. {
  2026. uint16_t c = pgm_read_word(nibblePalette + color);
  2027. c = ((c << 8) | (c >> 8)); // endien, TODO: could rather switch in the palette
  2028. uint16_t *p = nibbleFrameBuffer + y * SAF_SCREEN_WIDTH * 4 + x * 2;
  2029. *p = c;
  2030. p++;
  2031. *p = c;
  2032. p += SAF_SCREEN_WIDTH * 2 - 1;
  2033. *p = c;
  2034. p++;
  2035. *p = c;
  2036. }
  2037. void SAF_FE_playSound(uint8_t sound)
  2038. {
  2039. #if SAF_SETTING_ENABLE_SOUND
  2040. switch (sound)
  2041. {
  2042. case SAF_SOUND_CLICK: Piezo.tone(400,20); break;
  2043. case SAF_SOUND_BEEP: Piezo.tone(300,150); break;
  2044. case SAF_SOUND_BOOM: Piezo.tone(70,200); break;
  2045. case SAF_SOUND_BUMP: Piezo.tone(130,50); break;
  2046. default: break;
  2047. }
  2048. #else
  2049. _SAF_UNUSED(sound);
  2050. #endif
  2051. }
  2052. void SAF_FE_save(uint8_t index, uint8_t data)
  2053. {
  2054. _SAF_UNUSED(index);
  2055. _SAF_UNUSED(data);
  2056. }
  2057. uint8_t SAF_FE_load(uint8_t index)
  2058. {
  2059. _SAF_UNUSED(index);
  2060. return 0;
  2061. }
  2062. uint8_t SAF_FE_buttonPressed(uint8_t button)
  2063. {
  2064. return nibbleButtons[button];
  2065. }
  2066. const char *SAF_FE_extension(const char *string)
  2067. {
  2068. _SAF_UNUSED(string);
  2069. return SAF_FE_emptyString;
  2070. }
  2071. // create button callbacks:
  2072. #define cbf(b,n)\
  2073. void b ## _down() { nibbleButtons[n] = 255; }\
  2074. void b ## _up() { nibbleButtons[n] = 0; }
  2075. cbf(BTN_UP,SAF_BUTTON_UP)
  2076. cbf(BTN_RIGHT,SAF_BUTTON_RIGHT)
  2077. cbf(BTN_DOWN,SAF_BUTTON_DOWN)
  2078. cbf(BTN_LEFT,SAF_BUTTON_LEFT)
  2079. cbf(BTN_A,SAF_BUTTON_A)
  2080. cbf(BTN_B,SAF_BUTTON_B)
  2081. cbf(BTN_C,SAF_BUTTON_C)
  2082. #undef cbf
  2083. void setup()
  2084. {
  2085. Nibble.begin();
  2086. nibbleDisplay = Nibble.getDisplay();
  2087. nibbleFrameBuffer =
  2088. (uint16_t *) nibbleDisplay->getBaseSprite()->frameBuffer(0);
  2089. for (uint8_t i = 0; i < SAF_BUTTONS; ++i)
  2090. nibbleButtons[i] = 0;
  2091. // register button callbacks:
  2092. #define cb(b) \
  2093. Input::getInstance()->setBtnPressCallback(b,b ## _down); \
  2094. Input::getInstance()->setBtnReleaseCallback(b,b ## _up);
  2095. cb(BTN_UP)
  2096. cb(BTN_DOWN)
  2097. cb(BTN_LEFT)
  2098. cb(BTN_RIGHT)
  2099. cb(BTN_A)
  2100. cb(BTN_B)
  2101. cb(BTN_C)
  2102. #undef cb
  2103. SAF_FE_init();
  2104. }
  2105. uint32_t nibbleNextFrame = 0;
  2106. void loop()
  2107. {
  2108. uint32_t time = millis();
  2109. Input::getInstance()->loop(0);
  2110. if (time < nibbleNextFrame)
  2111. return;
  2112. while (time >= nibbleNextFrame)
  2113. {
  2114. SAF_FE_loop();
  2115. nibbleNextFrame += SAF_MS_PER_FRAME;
  2116. }
  2117. nibbleDisplay->commit();
  2118. }
  2119. #elif defined(SAF_PLATFORM_GAMEBUINO_META)
  2120. /* Gamebuino META frontend.
  2121. requirements: Arduino environment, Gamebuino-Meta.h
  2122. compiling: compile with Arduino IDE
  2123. ------------------------------------------------------------------------------*/
  2124. #undef SAF_PLATFORM_NAME
  2125. #define SAF_PLATFORM_NAME "GB META"
  2126. #undef SAF_PLATFORM_RAM
  2127. #define SAF_PLATFORM_RAM 32000
  2128. #undef SAF_PLATFORM_FREQUENCY
  2129. #define SAF_PLATFORM_FREQUENCY 48000000
  2130. #undef SAF_PLATFORM_HARWARD
  2131. #define SAF_PLATFORM_HARWARD 1
  2132. #include <Gamebuino-Meta.h>
  2133. #if SAF_SETTING_ENABLE_SAVES
  2134. const Gamebuino_Meta::SaveDefault metaSaveDefault[] =
  2135. { { 0, SAVETYPE_BLOB, SAF_SAVE_SIZE, 0 } };
  2136. #endif
  2137. uint8_t metaLEDCountdown = 0;
  2138. PROGMEM uint16_t gbmPalette[256] =
  2139. {
  2140. SAF_FE_PALETTE_565
  2141. };
  2142. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  2143. {
  2144. gb.display.drawPixel(8 + x,y,(Color) pgm_read_word(gbmPalette + color));
  2145. }
  2146. void SAF_FE_playSound(uint8_t sound)
  2147. {
  2148. #if SAF_SETTING_ENABLE_SOUND
  2149. switch (sound)
  2150. {
  2151. case SAF_SOUND_CLICK: gb.sound.tone(800,60); break;
  2152. case SAF_SOUND_BEEP: gb.sound.tone(300,150); break;
  2153. case SAF_SOUND_BUMP: gb.sound.tone(130,50); break;
  2154. case SAF_SOUND_BOOM:
  2155. gb.sound.playCancel();
  2156. gb.lights.fill(RED);
  2157. metaLEDCountdown = 5;
  2158. break;
  2159. default: break;
  2160. }
  2161. #else
  2162. _SAF_UNUSED(sound);
  2163. #endif
  2164. }
  2165. uint8_t SAF_FE_buttonPressed(uint8_t button)
  2166. {
  2167. Gamebuino_Meta::Button b;
  2168. switch (button)
  2169. {
  2170. case SAF_BUTTON_UP: b = BUTTON_UP; break;
  2171. case SAF_BUTTON_RIGHT: b = BUTTON_RIGHT; break;
  2172. case SAF_BUTTON_DOWN: b = BUTTON_DOWN; break;
  2173. case SAF_BUTTON_LEFT: b = BUTTON_LEFT; break;
  2174. case SAF_BUTTON_A: b = BUTTON_A; break;
  2175. case SAF_BUTTON_B: b = BUTTON_B; break;
  2176. case SAF_BUTTON_C: b = BUTTON_MENU; break;
  2177. default: return 0; break;
  2178. }
  2179. return gb.buttons.timeHeld(b) > 0;
  2180. }
  2181. const char *SAF_FE_extension(const char *string)
  2182. {
  2183. _SAF_UNUSED(string);
  2184. return SAF_FE_emptyString;
  2185. }
  2186. uint8_t SAF_FE_load(uint8_t index)
  2187. {
  2188. #if SAF_SETTING_ENABLE_SAVES
  2189. uint8_t data[SAF_SAVE_SIZE];
  2190. gb.save.get(0,data,SAF_SAVE_SIZE);
  2191. return data[index];
  2192. #else
  2193. _SAF_UNUSED(index);
  2194. #endif
  2195. }
  2196. void SAF_FE_save(uint8_t index, uint8_t data)
  2197. {
  2198. #if SAF_SETTING_ENABLE_SAVES
  2199. uint8_t d[SAF_SAVE_SIZE];
  2200. gb.save.get(0,d,SAF_SAVE_SIZE);
  2201. d[index] = data;
  2202. gb.save.set(0,d,SAF_SAVE_SIZE);
  2203. #else
  2204. _SAF_UNUSED(index);
  2205. _SAF_UNUSED(data);
  2206. #endif
  2207. }
  2208. void setup()
  2209. {
  2210. gb.begin();
  2211. gb.setFrameRate(SAF_FPS);
  2212. for (uint8_t y = 0; y < 64; ++y)
  2213. for (uint8_t x = 0; x < 80; ++x)
  2214. gb.display.drawPixel(x,y,(Color) pgm_read_word(gbmPalette +
  2215. SAF_SETTING_BACKGROUND_COLOR));
  2216. #if SAF_SETTING_ENABLE_SAVES
  2217. gb.save.config(metaSaveDefault);
  2218. #endif
  2219. SAF_FE_init();
  2220. }
  2221. void loop()
  2222. {
  2223. if (!gb.update())
  2224. return;
  2225. if (metaLEDCountdown > 0)
  2226. {
  2227. metaLEDCountdown--;
  2228. if (metaLEDCountdown == 0)
  2229. gb.lights.clear();
  2230. }
  2231. SAF_FE_loop();
  2232. }
  2233. #elif defined(SAF_PLATFORM_RINGO)
  2234. /* Circuitmess Ringo (MAKERphone) frontend.
  2235. requirements: Arduino environment, required libraries
  2236. compiling: compile with Arduino IDE
  2237. ------------------------------------------------------------------------------*/
  2238. #include <Arduino.h>
  2239. #include <MAKERphone.h>
  2240. //#include <utility/soundLib/MPWavLib.h>
  2241. #undef SAF_PLATFORM_NAME
  2242. #define SAF_PLATFORM_NAME "GB META"
  2243. #undef SAF_PLATFORM_RAM
  2244. #define SAF_PLATFORM_RAM 520000
  2245. #undef SAF_PLATFORM_FREQUENCY
  2246. #define SAF_PLATFORM_FREQUENCY 160000000
  2247. #define RINGO_SAVE_FILE_NAME ("/" SAF_PROGRAM_NAME ".sav")
  2248. uint16_t ringoPalette[256] =
  2249. {
  2250. SAF_FE_PALETTE_565
  2251. };
  2252. MAKERphone mp;
  2253. #if SAF_SETTING_ENABLE_SOUND
  2254. Oscillator *ringoOsc;
  2255. #endif
  2256. uint8_t ringoArrows;
  2257. //uint8_t ringoScreen[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT];
  2258. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  2259. {
  2260. mp.display.fillRect(16 + x * 2,y * 2,2,2,ringoPalette[color]);
  2261. }
  2262. void SAF_FE_playSound(uint8_t sound)
  2263. {
  2264. #if SAF_SETTING_ENABLE_SOUND
  2265. uint8_t wave = SINE;
  2266. uint16_t freq = 1000;
  2267. float dur = 0.2;
  2268. switch (sound)
  2269. {
  2270. case SAF_SOUND_BEEP: freq = 500; dur = 0.08; wave = SINE; break; // plasma
  2271. case SAF_SOUND_CLICK: freq = 900; dur = 0.003; wave = SAW; break; // click
  2272. case SAF_SOUND_BOOM: freq = 100; dur = 0.06; wave = SAW; break; // explosion
  2273. case SAF_SOUND_BUMP: freq = 200; dur = 0.02; wave = SQUARE; break; // plasma
  2274. default: break;
  2275. }
  2276. ringoOsc->setWaveform(wave);
  2277. ringoOsc->beep(freq,dur);
  2278. #else
  2279. _SAF_UNUSED(sound);
  2280. #endif
  2281. }
  2282. uint8_t SAF_FE_buttonPressed(uint8_t button)
  2283. {
  2284. switch (button)
  2285. {
  2286. #define b(but) (mp.buttons.timeHeld(but) > 0)
  2287. #define r(but) return b(but); break;
  2288. case SAF_BUTTON_UP: return (ringoArrows & 0x04) || b(BTN_2); break;
  2289. case SAF_BUTTON_DOWN: return (ringoArrows & 0x08) || b(BTN_5); break;
  2290. case SAF_BUTTON_RIGHT: return (ringoArrows & 0x01) || b(BTN_6); break;
  2291. case SAF_BUTTON_LEFT: return (ringoArrows & 0x02) || b(BTN_4); break;
  2292. case SAF_BUTTON_A: return b(BTN_A) || b(BTN_8); break;
  2293. case SAF_BUTTON_B: return b(BTN_B) || b(BTN_9); break;
  2294. case SAF_BUTTON_C: r(BTN_7);
  2295. default: return 0; break;
  2296. #undef b
  2297. #undef r
  2298. }
  2299. return 0;
  2300. return 0;
  2301. }
  2302. const char *SAF_FE_extension(const char *string)
  2303. {
  2304. _SAF_UNUSED(string);
  2305. return SAF_FE_emptyString;
  2306. }
  2307. #if 1//SAF_SETTING_ENABLE_SAVES
  2308. uint8_t ringoSaveLoaded = 0;
  2309. uint8_t ringoSaveData[SAF_SAVE_SIZE];
  2310. void ringoCheckSave()
  2311. {
  2312. if (!ringoSaveLoaded)
  2313. {
  2314. if (SD.exists(RINGO_SAVE_FILE_NAME))
  2315. {
  2316. File f = SD.open(RINGO_SAVE_FILE_NAME,FILE_READ);
  2317. f.read(ringoSaveData,SAF_SAVE_SIZE);
  2318. f.close();
  2319. }
  2320. else
  2321. for (uint8_t i = 0; i < SAF_SAVE_SIZE; ++i)
  2322. ringoSaveData[i] = 0;
  2323. ringoSaveLoaded = 1;
  2324. }
  2325. }
  2326. #endif
  2327. uint8_t SAF_FE_load(uint8_t index)
  2328. {
  2329. #if SAF_SETTING_ENABLE_SAVES
  2330. ringoCheckSave();
  2331. return ringoSaveData[index];
  2332. #else
  2333. _SAF_UNUSED(index);
  2334. return 0;
  2335. #endif
  2336. }
  2337. void SAF_FE_save(uint8_t index, uint8_t data)
  2338. {
  2339. #if SAF_SETTING_ENABLE_SAVES
  2340. ringoSaveData[index] = data;
  2341. ringoCheckSave();
  2342. SD.remove(RINGO_SAVE_FILE_NAME);
  2343. File f = SD.open(RINGO_SAVE_FILE_NAME,FILE_WRITE);
  2344. f.write(ringoSaveData,SAF_SAVE_SIZE);
  2345. f.close();
  2346. #else
  2347. _SAF_UNUSED(index);
  2348. _SAF_UNUSED(data);
  2349. #endif
  2350. }
  2351. void setup()
  2352. {
  2353. mp.begin();
  2354. #if SAF_SETTING_ENABLE_SOUND
  2355. ringoOsc = new Oscillator(SINE);
  2356. addOscillator(ringoOsc);
  2357. ringoOsc->setVolume(64);
  2358. #endif
  2359. SAF_FE_init();
  2360. }
  2361. uint32_t ringoNextFrame = 0;
  2362. void loop()
  2363. {
  2364. uint32_t time = millis();
  2365. if (time < ringoNextFrame)
  2366. return;
  2367. mp.display.fillRect(
  2368. 0,0,LCDWIDTH,LCDHEIGHT,ringoPalette[SAF_SETTING_BACKGROUND_COLOR]);
  2369. while (time >= ringoNextFrame)
  2370. {
  2371. SAF_FE_loop();
  2372. ringoNextFrame += SAF_MS_PER_FRAME;
  2373. }
  2374. ringoArrows = 0x00 |
  2375. ((mp.buttons.getJoystickX() < 200)) << 0 |
  2376. ((mp.buttons.getJoystickX() > 900)) << 1 |
  2377. ((mp.buttons.getJoystickY() < 200)) << 2 |
  2378. ((mp.buttons.getJoystickY() > 900)) << 3;
  2379. mp.update();
  2380. }
  2381. #else
  2382. #error No known SAF frontend specified.
  2383. #endif // platform frontends
  2384. //===================== FUNCTION IMPLEMENTATIONS ===============================
  2385. #ifdef SAF_FE_GENERIC_FRONTEND
  2386. #ifndef SAF_FE_STDIO_SAVE_LOAD
  2387. #define SAF_FE_STDIO_SAVE_LOAD
  2388. #endif
  2389. #include <stdio.h>
  2390. #define SAF_FE_GF_SAVE_FILE_NAME (SAF_PROGRAM_NAME ".rec")
  2391. uint8_t SAF_FE_GF_running = 1;
  2392. uint8_t SAF_FE_GF_volume = 4;
  2393. uint8_t SAF_FE_GF_screen[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT];
  2394. uint8_t SAF_FE_GF_parameters[128]; // CLI parameters
  2395. uint32_t SAF_FE_GF_keyStates = 0;
  2396. uint8_t SAF_FE_GF_buttonStates = 0;
  2397. uint8_t SAF_FE_GF_paused = 0;
  2398. uint32_t SAF_FE_GF_demoNextFrame = 0; // when to load the next record item
  2399. uint8_t SAF_FE_GF_demoNextButtons = 0;
  2400. int32_t SAF_FE_GF_frameTimes = 0; // for debug, measures time spent in a frame
  2401. uint32_t SAF_FE_GF_nextFrameTime = 0;
  2402. FILE *SAF_FE_GF_recordFile;
  2403. /** Records a specific key state and returns 0 if the key is not pressed, 1 if
  2404. it was just pressed or 2 if it's been pressed for multiple frames. */
  2405. uint8_t SAF_FE_GF_handleKey(char key, uint8_t position)
  2406. {
  2407. position = 0x01 << position;
  2408. uint8_t previous = (SAF_FE_GF_keyStates & position) != 0;
  2409. if (SAF_FE_GF_keyPressed(key))
  2410. {
  2411. SAF_FE_GF_keyStates |= position;
  2412. return previous ? 2 : 1;
  2413. }
  2414. else
  2415. {
  2416. SAF_FE_GF_keyStates &= ~position;
  2417. return 0;
  2418. }
  2419. }
  2420. uint8_t SAF_FE_buttonPressed(uint8_t button)
  2421. {
  2422. char key = 0, key2 = 0, key3 = 0, key4 = 0;
  2423. uint8_t bit = 255;
  2424. switch (button)
  2425. {
  2426. case SAF_BUTTON_UP: key = 'w'; key2 = 'U'; bit = 0; break;
  2427. case SAF_BUTTON_RIGHT: key = 'd'; key2 = 'R'; bit = 1; break;
  2428. case SAF_BUTTON_DOWN: key = 's'; key2 = 'D'; bit = 2; break;
  2429. case SAF_BUTTON_LEFT: key = 'a'; key2 = 'L'; bit = 3; break;
  2430. case SAF_BUTTON_A: key = 'j'; key2 = 'y'; key3 = 'z'; key4 = 'X'; bit = 4; break;
  2431. case SAF_BUTTON_B: key = 'k'; key2 = 'x'; key3 = 'Y'; bit = 5; break;
  2432. case SAF_BUTTON_C: key = 'l'; key2 = 'c'; key3 = 'Z'; bit = 6; break;
  2433. default: break;
  2434. }
  2435. return SAF_FE_GF_keyPressed(key) ||
  2436. (key2 && SAF_FE_GF_keyPressed(key2)) ||
  2437. (key3 && SAF_FE_GF_keyPressed(key3)) ||
  2438. (key4 && SAF_FE_GF_keyPressed(key4)) ||
  2439. (SAF_FE_GF_parameters['p'] && bit != 255 &&
  2440. (SAF_FE_GF_buttonStates & (0x01 << bit)));
  2441. }
  2442. void SAF_FE_drawPixel(uint8_t x, uint8_t y, uint8_t color)
  2443. {
  2444. SAF_FE_GF_screen[y * SAF_SCREEN_WIDTH + x] = color;
  2445. }
  2446. void SAF_FE_GF_saveScreenshot(const char *file)
  2447. {
  2448. FILE *f = fopen(file,"wb");
  2449. if (!f)
  2450. return;
  2451. fwrite("P6 64 64 255\n",13,1,f);
  2452. for (uint16_t i = 0; i < SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT; ++i)
  2453. {
  2454. uint8_t t[3];
  2455. SAF_colorToRGB(SAF_FE_GF_screen[i],t,t + 1,t + 2);
  2456. fwrite(t,3,1,f);
  2457. }
  2458. fclose(f);
  2459. puts("screenshot taken");
  2460. }
  2461. void SAF_FE_GF_printHelp()
  2462. {
  2463. puts(
  2464. SAF_PROGRAM_NAME
  2465. "\n " SAF_INFO_STRING
  2466. "\n " SAF_PLATFORM_NAME " (generic frontend)\n"
  2467. "controls:\n"
  2468. " WSAD, arrows direction buttons\n"
  2469. " JKL A, B and C buttons\n"
  2470. " P pause/resume\n"
  2471. " escape quit\n"
  2472. " T take screenshot\n"
  2473. " U/I volume -/+\n"
  2474. " N/M speed -/+\n"
  2475. "possible arguments (if supported):\n"
  2476. " -h print help and quit\n"
  2477. " -sX scale X times (0 = fullscreen)\n"
  2478. " -vX set volume to X (0 to 8)\n"
  2479. " -bX boost speed (1 to 5, 3 = normal)\n"
  2480. " -d show debug info\n"
  2481. " -u use pixel art scaling (scale2x)\n"
  2482. " -r record inputs (demo, can be used for saves)\n"
  2483. " -p play recorded inputs (demo)\n"
  2484. " -P like -p but rewind to end (\"load state\")\n"
  2485. " -l turns on -P and -r (save/load via demos)\n"
  2486. " -S start paused");
  2487. #ifdef SAF_FE_GF_EXTRA_HELP
  2488. puts(SAF_FE_GF_EXTRA_HELP);
  2489. #endif
  2490. }
  2491. char *SAF_FE_GF_byteToHex(uint8_t b, char s[3])
  2492. {
  2493. for (uint8_t i = 0; i < 2; ++i)
  2494. {
  2495. uint8_t r = b % 16;
  2496. s[1 - i] = r + ((r < 10) ? '0' : ('a' - 10));
  2497. b /= 16;
  2498. }
  2499. s[2] = 0;
  2500. return s;
  2501. }
  2502. uint8_t SAF_FE_GF_byteFromHex(const char *hex)
  2503. {
  2504. uint8_t result = 0;
  2505. for (int8_t i = 0; i < 2; ++i)
  2506. result = result * 16 + hex[i] -
  2507. ((hex[i] >= '0' && hex [i] <= '9') ? '0' : ('a' - 10));
  2508. return result;
  2509. }
  2510. void readNextDemoRecord(void)
  2511. {
  2512. char line[64];
  2513. SAF_FE_GF_buttonStates = SAF_FE_GF_demoNextButtons;
  2514. if (fgets(line,64,SAF_FE_GF_recordFile) != 0)
  2515. {
  2516. SAF_FE_GF_demoNextFrame =
  2517. (((uint32_t) SAF_FE_GF_byteFromHex(line)) << 24) +
  2518. (((uint32_t) SAF_FE_GF_byteFromHex(line + 2)) << 16) +
  2519. (((uint32_t) SAF_FE_GF_byteFromHex(line + 4)) << 8) +
  2520. ((uint32_t) SAF_FE_GF_byteFromHex(line + 6));
  2521. SAF_FE_GF_demoNextButtons = SAF_FE_GF_byteFromHex(line + 9);
  2522. }
  2523. else
  2524. {
  2525. puts("replaying inputs finished");
  2526. SAF_FE_GF_demoNextFrame = 0xffffffff;
  2527. fclose(SAF_FE_GF_recordFile);
  2528. if (SAF_FE_GF_parameters['r'])
  2529. {
  2530. // after "loading" the state open again for appending
  2531. SAF_FE_GF_parameters['p'] = 0;
  2532. SAF_FE_GF_parameters['P'] = 0;
  2533. SAF_FE_GF_recordFile = fopen(SAF_FE_GF_SAVE_FILE_NAME,"a");
  2534. }
  2535. if (SAF_FE_GF_parameters['S'])
  2536. SAF_FE_GF_paused = 1;
  2537. }
  2538. }
  2539. void _SAF_FE_GF_mainLoopIteration(void)
  2540. {
  2541. if (!SAF_FE_GF_loop(SAF_FE_GF_parameters))
  2542. {
  2543. SAF_FE_GF_running = 0;
  2544. return;
  2545. }
  2546. uint32_t time =
  2547. #ifdef __EMSCRIPTEN__
  2548. 0;
  2549. #else
  2550. SAF_FE_GF_sleep(0);
  2551. #endif
  2552. #ifndef __EMSCRIPTEN__
  2553. if (time >= SAF_FE_GF_nextFrameTime)
  2554. #endif
  2555. {
  2556. if (SAF_FE_GF_handleKey('t',0) == 1)
  2557. {
  2558. char fileName[64] = SAF_PROGRAM_NAME "_";
  2559. char *c = fileName;
  2560. while (*c != 0)
  2561. c++;
  2562. SAF_FE_GF_byteToHex((SAF_frame() / 256) % 256,c);
  2563. c += 2;
  2564. SAF_FE_GF_byteToHex(SAF_frame() % 256,c);
  2565. c += 2;
  2566. *c = '.'; c++; *c = 'p'; c++; *c = 'p'; c++; *c = 'm'; c++; *c = 0;
  2567. SAF_FE_GF_saveScreenshot(fileName);
  2568. }
  2569. if (SAF_FE_GF_handleKey('i',1) == 1 && SAF_FE_GF_parameters['v'] < '8')
  2570. {
  2571. SAF_FE_GF_parameters['v']++;
  2572. puts("volume +");
  2573. }
  2574. else if (SAF_FE_GF_handleKey('u',2) == 1 && SAF_FE_GF_parameters['v'] > '0')
  2575. {
  2576. SAF_FE_GF_parameters['v']--;
  2577. puts("volume -");
  2578. }
  2579. if (SAF_FE_GF_handleKey('m',3) == 1 && SAF_FE_GF_parameters['b'] < '5')
  2580. {
  2581. SAF_FE_GF_parameters['b']++;
  2582. puts("speed +");
  2583. }
  2584. else if (SAF_FE_GF_handleKey('n',4) == 1 && SAF_FE_GF_parameters['b'] > '1')
  2585. {
  2586. SAF_FE_GF_parameters['b']--;
  2587. puts("speed -");
  2588. }
  2589. if (SAF_FE_GF_handleKey('p',5) == 1)
  2590. {
  2591. SAF_FE_GF_paused = !SAF_FE_GF_paused;
  2592. puts(SAF_FE_GF_paused ? "paused" : "resumed");
  2593. }
  2594. uint16_t mult = 2;
  2595. for (uint8_t i = 0; i < '5' - SAF_FE_GF_parameters['b']; ++i)
  2596. mult *= 2;
  2597. uint8_t frames = 0;
  2598. uint8_t rewind = 0;
  2599. #ifndef __EMSCRIPTEN__
  2600. while (time >= SAF_FE_GF_nextFrameTime || rewind)
  2601. #endif
  2602. {
  2603. rewind = SAF_FE_GF_parameters['P'] &&
  2604. SAF_FE_GF_demoNextFrame != 0xffffffff;
  2605. if (!SAF_FE_GF_paused)
  2606. {
  2607. #ifndef __EMSCRIPTEN__
  2608. SAF_FE_GF_frameTimes -= SAF_FE_GF_sleep(0);
  2609. #endif
  2610. // demo recording/playing:
  2611. if (SAF_FE_GF_parameters['r'] && !SAF_FE_GF_parameters['p'])
  2612. {
  2613. uint8_t previousButtonStates = SAF_FE_GF_buttonStates;
  2614. SAF_FE_GF_buttonStates =
  2615. ((SAF_buttonPressed(SAF_BUTTON_UP) != 0)) |
  2616. ((SAF_buttonPressed(SAF_BUTTON_RIGHT) != 0) << 1) |
  2617. ((SAF_buttonPressed(SAF_BUTTON_DOWN) != 0) << 2) |
  2618. ((SAF_buttonPressed(SAF_BUTTON_LEFT) != 0) << 3) |
  2619. ((SAF_buttonPressed(SAF_BUTTON_A) != 0) << 4) |
  2620. ((SAF_buttonPressed(SAF_BUTTON_B) != 0) << 5) |
  2621. ((SAF_buttonPressed(SAF_BUTTON_C) != 0) << 6);
  2622. if (SAF_FE_GF_buttonStates != previousButtonStates)
  2623. {
  2624. char s[3];
  2625. uint32_t f = SAF_frame() - 1;
  2626. fprintf(SAF_FE_GF_recordFile,"%s",SAF_FE_GF_byteToHex((f >> 24) & 0xff,s));
  2627. fprintf(SAF_FE_GF_recordFile,"%s",SAF_FE_GF_byteToHex((f >> 16) & 0xff,s));
  2628. fprintf(SAF_FE_GF_recordFile,"%s",SAF_FE_GF_byteToHex((f >> 8) & 0xff,s));
  2629. fprintf(SAF_FE_GF_recordFile,"%s ",SAF_FE_GF_byteToHex(f & 0xff,s));
  2630. fprintf(SAF_FE_GF_recordFile,"%s\n",SAF_FE_GF_byteToHex(SAF_FE_GF_buttonStates,s));
  2631. }
  2632. }
  2633. else if (SAF_FE_GF_parameters['p'] && SAF_frame() >= SAF_FE_GF_demoNextFrame)
  2634. {
  2635. readNextDemoRecord();
  2636. } // demo handling
  2637. SAF_FE_GF_running = SAF_FE_loop();
  2638. #ifndef __EMSCRIPTEN__
  2639. SAF_FE_GF_frameTimes += SAF_FE_GF_sleep(0);
  2640. #endif
  2641. if (SAF_FE_GF_parameters['d'] && SAF_frame() % 64 == 0)
  2642. {
  2643. char debugStr[] = "frame us/frame ";
  2644. SAF_intToStr(SAF_frame(),debugStr + 6)[0] = ' ';
  2645. SAF_intToStr((SAF_FE_GF_frameTimes * 1000) / 64,debugStr + 23);
  2646. puts(debugStr);
  2647. SAF_FE_GF_frameTimes = 0;
  2648. }
  2649. } // if (!paused)
  2650. if (!rewind)
  2651. SAF_FE_GF_nextFrameTime += (SAF_MS_PER_FRAME * mult) / 8;
  2652. frames++;
  2653. }
  2654. if (SAF_FE_GF_parameters['d'] && frames > 1)
  2655. {
  2656. char debugStr[] = "skipped x frames";
  2657. debugStr[8] = '0' + frames - 1;
  2658. puts(debugStr);
  2659. }
  2660. SAF_FE_GF_present(SAF_FE_GF_screen);
  2661. } // if (time > nextFrameTime)
  2662. #ifndef __EMSCRIPTEN__
  2663. SAF_FE_GF_sleep((SAF_FE_GF_nextFrameTime - time) * 3 / 4); // relieve CPU
  2664. #endif
  2665. if (SAF_FE_GF_keyPressed('E'))
  2666. SAF_FE_GF_running = 0;
  2667. }
  2668. #ifdef __EMSCRIPTEN__
  2669. typedef void (*em_callback_func)(void);
  2670. void emscripten_set_main_loop(
  2671. em_callback_func func, int fps, int simulate_infinite_loop);
  2672. #endif
  2673. int main(int argc, char **argv)
  2674. {
  2675. SAF_FE_paramParse(argc,argv,SAF_FE_GF_parameters);
  2676. if (SAF_FE_GF_parameters['h'])
  2677. {
  2678. SAF_FE_GF_printHelp();
  2679. return 0;
  2680. }
  2681. if (SAF_FE_GF_parameters['s'] < '0' || SAF_FE_GF_parameters['s'] > '8')
  2682. SAF_FE_GF_parameters['s'] = '4';
  2683. if (SAF_FE_GF_parameters['v'] < '0' || SAF_FE_GF_parameters['v'] > '8')
  2684. SAF_FE_GF_parameters['v'] = '4';
  2685. if (SAF_FE_GF_parameters['b'] < '1' || SAF_FE_GF_parameters['b'] > '5')
  2686. SAF_FE_GF_parameters['b'] = '3';
  2687. if (SAF_FE_GF_parameters['l'])
  2688. {
  2689. SAF_FE_GF_parameters['P'] = 1;
  2690. SAF_FE_GF_parameters['r'] = 1;
  2691. }
  2692. if (SAF_FE_GF_parameters['P'])
  2693. SAF_FE_GF_parameters['p'] = 1; // automatically turn this on as well
  2694. if (SAF_FE_GF_parameters['S'] && !SAF_FE_GF_parameters['p'])
  2695. SAF_FE_GF_paused = 1;
  2696. for (uint16_t i = 0; i < SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT; ++i)
  2697. SAF_FE_GF_screen[i] = 0;
  2698. puts("starting " SAF_PROGRAM_NAME);
  2699. puts("run with -h for help");
  2700. if (SAF_FE_GF_parameters['d'])
  2701. puts("initializing frontend");
  2702. SAF_FE_GF_init(SAF_FE_GF_parameters);
  2703. if (SAF_FE_GF_parameters['d'])
  2704. puts("initializing client program");
  2705. SAF_FE_init();
  2706. if (SAF_FE_GF_parameters['p'])
  2707. {
  2708. SAF_FE_GF_recordFile = fopen(SAF_FE_GF_SAVE_FILE_NAME,"r");
  2709. if (!SAF_FE_GF_recordFile)
  2710. {
  2711. puts("couldn't open demo file for reading");
  2712. SAF_FE_GF_parameters['p'] = 0;
  2713. SAF_FE_GF_parameters['P'] = 0;
  2714. }
  2715. else
  2716. readNextDemoRecord();
  2717. }
  2718. if (!SAF_FE_GF_parameters['p'] && SAF_FE_GF_parameters['r'])
  2719. {
  2720. SAF_FE_GF_recordFile = fopen(SAF_FE_GF_SAVE_FILE_NAME,"w");
  2721. if (!SAF_FE_GF_recordFile)
  2722. {
  2723. puts("couldn't open demo file for writing");
  2724. SAF_FE_GF_parameters['r'] = 0;
  2725. }
  2726. }
  2727. #ifdef __EMSCRIPTEN__
  2728. emscripten_set_main_loop(_SAF_FE_GF_mainLoopIteration,SAF_FPS,1);
  2729. #else
  2730. while (SAF_FE_GF_running) // main loop
  2731. _SAF_FE_GF_mainLoopIteration();
  2732. #endif
  2733. if (SAF_FE_GF_recordFile)
  2734. fclose(SAF_FE_GF_recordFile);
  2735. printf("ending %s\n",SAF_PROGRAM_NAME);
  2736. SAF_FE_GF_end();
  2737. return 0;
  2738. }
  2739. const uint8_t *SAF_FE_GF_getScreenPointer()
  2740. {
  2741. return SAF_FE_GF_screen;
  2742. }
  2743. const char *SAF_FE_extension(const char *string)
  2744. {
  2745. return SAF_FE_GF_extension(string);
  2746. }
  2747. #endif // SAF_FE_GENERIC_FRONTEND
  2748. #ifdef SAF_FE_STDIO_SAVE_LOAD
  2749. #if SAF_SETTING_ENABLE_SAVES
  2750. #include <stdio.h>
  2751. uint8_t _SAF_FE_saveMemory[SAF_SAVE_SIZE];
  2752. uint8_t _SAF_FE_saveMemoryLoaded = 0;
  2753. #ifdef __EMSCRIPTEN__
  2754. char *emscripten_run_script_string(const char *script);
  2755. int emscripten_run_script_int(const char *script);
  2756. void emscripten_run_script(const char *script);
  2757. char emscriptenCookie[17];
  2758. #endif
  2759. void _SAF_FE_loadSaveMemory()
  2760. {
  2761. #ifdef __EMSCRIPTEN__
  2762. // generate the cookie name
  2763. char *progName = SAF_PROGRAM_NAME;
  2764. emscriptenCookie[0] = 'S';
  2765. emscriptenCookie[1] = 'A';
  2766. emscriptenCookie[2] = 'F';
  2767. for (uint8_t i = 3; i < 16; ++i)
  2768. emscriptenCookie[i] = 'x';
  2769. uint8_t p = 0;
  2770. while (progName[p] != 0 && p < 16 - 3)
  2771. {
  2772. emscriptenCookie[p + 3] = progName[p];
  2773. p++;
  2774. }
  2775. emscriptenCookie[16] = 0;
  2776. char script[] = "document.cookie.search('................=')";
  2777. for (uint8_t i = 0; i < 16; ++i)
  2778. script[24 + i] = emscriptenCookie[i];
  2779. int cookieIndex = emscripten_run_script_int(script);
  2780. if (cookieIndex >= 0)
  2781. {
  2782. char *cookie = emscripten_run_script_string("document.cookie")
  2783. + cookieIndex + 17;
  2784. for (uint8_t i = 0; i < SAF_SAVE_SIZE; ++i)
  2785. _SAF_FE_saveMemory[i] = (cookie[2 * i] - 'a') * 16 + (cookie[2 * i + 1] - 'a');
  2786. }
  2787. else
  2788. for (uint16_t i = 0; i < SAF_SAVE_SIZE; ++i)
  2789. _SAF_FE_saveMemory[i] = 0;
  2790. #else
  2791. FILE *f = fopen(SAF_PROGRAM_NAME ".sav","rb");
  2792. if (f)
  2793. {
  2794. fread(_SAF_FE_saveMemory,SAF_SAVE_SIZE,1,f);
  2795. fclose(f);
  2796. }
  2797. else
  2798. for (uint16_t i = 0; i < SAF_SAVE_SIZE; ++i)
  2799. _SAF_FE_saveMemory[i] = 0;
  2800. #endif // emscripten
  2801. _SAF_FE_saveMemoryLoaded = 1;
  2802. }
  2803. #endif // if SAF_SETTING_ENABLE_SAVES
  2804. void SAF_FE_save(uint8_t index, uint8_t data)
  2805. {
  2806. #if SAF_SETTING_ENABLE_SAVES
  2807. if (!_SAF_FE_saveMemoryLoaded)
  2808. _SAF_FE_loadSaveMemory();
  2809. _SAF_FE_saveMemory[index] = data;
  2810. #ifdef __EMSCRIPTEN__
  2811. char str[] = "document.cookie = '................="
  2812. " '";
  2813. for (uint8_t i = 0; i < 16; ++i)
  2814. str[19 + i] = emscriptenCookie[i];
  2815. uint8_t p = 36;
  2816. for (uint8_t i = 0; i < SAF_SAVE_SIZE; ++i)
  2817. {
  2818. char c1 = 'a' + _SAF_FE_saveMemory[i] / 16;
  2819. char c2 = 'a' + _SAF_FE_saveMemory[i] % 16;
  2820. str[p] = c1;
  2821. str[p + 1] = c2;
  2822. p += 2;
  2823. }
  2824. str[p] = '\'';
  2825. str[p + 1] = ';';
  2826. str[p + 2] = 0;
  2827. emscripten_run_script(str);
  2828. #else
  2829. FILE *f = fopen(SAF_PROGRAM_NAME ".sav","wb");
  2830. fwrite(_SAF_FE_saveMemory,SAF_SAVE_SIZE,1,f);
  2831. fclose(f);
  2832. #endif // emscripten
  2833. #else
  2834. _SAF_UNUSED(index);
  2835. _SAF_UNUSED(data);
  2836. #endif
  2837. }
  2838. uint8_t SAF_FE_load(uint8_t index)
  2839. {
  2840. #if SAF_SETTING_ENABLE_SAVES
  2841. if (!_SAF_FE_saveMemoryLoaded)
  2842. _SAF_FE_loadSaveMemory();
  2843. return _SAF_FE_saveMemory[index];
  2844. #else
  2845. return 0;
  2846. #endif
  2847. }
  2848. #endif // ifded SAF_FE_STDIO_SAVE_LOAD
  2849. void SAF_FE_paramParse(int argc, char **argv, uint8_t paramValues[128])
  2850. {
  2851. for (uint8_t i = 0; i < 128; ++i)
  2852. paramValues[i] = 0;
  2853. for (uint16_t i = 0; i < argc; ++i)
  2854. {
  2855. const char *a = *argv;
  2856. if (*a == '-')
  2857. {
  2858. a++;
  2859. if (a != 0)
  2860. {
  2861. if (a[1] == 0) // -p
  2862. paramValues[(uint8_t) a[0]] = 1;
  2863. else if (a[2] == 0) //-pX
  2864. paramValues[(uint8_t) a[0]] = a[1];
  2865. }
  2866. }
  2867. argv++;
  2868. }
  2869. }
  2870. #if SAF_SETTING_FORCE_1BIT
  2871. #undef SAF_PLATFORM_COLOR_COUNT
  2872. #define SAF_PLATFORM_COLOR_COUNT 2
  2873. #endif
  2874. #if SAF_PLATFORM_HARWARD
  2875. #include <avr/pgmspace.h>
  2876. #define _SAF_CONST PROGMEM const
  2877. #define _SAF_READ_CONST(addr) ((uint8_t) pgm_read_byte(addr))
  2878. #else
  2879. #define _SAF_CONST static const
  2880. #define _SAF_READ_CONST(addr) *(addr)
  2881. #endif
  2882. uint32_t _SAF_frame = 0;
  2883. uint8_t _SAF_currentRandom = 0;
  2884. uint8_t _SAF_buttonStates[SAF_BUTTONS] = {0,0,0,0,0,0,0};
  2885. uint8_t _SAF_saveMemory[SAF_SAVE_SIZE] =
  2886. {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  2887. uint8_t _SAF_saveMemoryLoaded = 0;
  2888. _SAF_CONST uint8_t _SAF_font[] =
  2889. {
  2890. 0x00,0x00, // 32 ' '
  2891. 0x22,0x20, // 33 '!'
  2892. 0x55,0x00, // 34 '"'
  2893. 0xea,0x57, // 35 '#'
  2894. 0x36,0x36, // 36 '$'
  2895. 0x49,0x92, // 37 '%'
  2896. 0x52,0xb6, // 38 '&'
  2897. 0x22,0x00, // 39 '''
  2898. 0x24,0x42, // 40 '('
  2899. 0x42,0x24, // 41 ')'
  2900. 0x25,0x05, // 42 '*'
  2901. 0x20,0x27, // 43 '+'
  2902. 0x00,0x22, // 44 ','
  2903. 0x00,0x07, // 45 '-'
  2904. 0x00,0x20, // 46 '.'
  2905. 0x24,0x12, // 47 '/'
  2906. 0x57,0x75, // 48 '0'
  2907. 0x46,0xe4, // 49 '1'
  2908. 0x67,0x71, // 50 '2'
  2909. 0x27,0x34, // 51 '3'
  2910. 0x55,0x47, // 52 '4'
  2911. 0x17,0x76, // 53 '5'
  2912. 0x17,0x77, // 54 '6'
  2913. 0x47,0x22, // 55 '7'
  2914. 0x77,0x75, // 56 '8'
  2915. 0x77,0x74, // 57 '9'
  2916. 0x20,0x20, // 58 ':'
  2917. 0x02,0x22, // 59 ';'
  2918. 0x40,0x42, // 60 '<'
  2919. 0x70,0x70, // 61 '='
  2920. 0x20,0x24, // 62 '>'
  2921. 0x96,0x44, // 63 '?'
  2922. 0xde,0x61, // 64 '@'
  2923. 0x57,0x57, // 65 'A'
  2924. 0x73,0x75, // 66 'B'
  2925. 0x16,0x61, // 67 'C'
  2926. 0x53,0x35, // 68 'D'
  2927. 0x37,0x71, // 69 'E'
  2928. 0x17,0x13, // 70 'F'
  2929. 0x16,0x65, // 71 'G'
  2930. 0x75,0x55, // 72 'H'
  2931. 0x27,0x72, // 73 'I'
  2932. 0x44,0x75, // 74 'J'
  2933. 0x35,0x55, // 75 'K'
  2934. 0x11,0x71, // 76 'L'
  2935. 0xfb,0x99, // 77 'M'
  2936. 0xb9,0x9d, // 78 'N'
  2937. 0x96,0x69, // 79 'O'
  2938. 0x57,0x17, // 80 'P'
  2939. 0x96,0xed, // 81 'Q'
  2940. 0x57,0x53, // 82 'R'
  2941. 0x36,0x34, // 83 'S'
  2942. 0x27,0x22, // 84 'T'
  2943. 0x55,0x75, // 85 'U'
  2944. 0x55,0x25, // 86 'V'
  2945. 0x99,0xbf, // 87 'W'
  2946. 0x25,0x52, // 88 'X'
  2947. 0x75,0x22, // 89 'Y'
  2948. 0x27,0x71, // 90 'Z'
  2949. 0x26,0x62, // 91 '['
  2950. 0x21,0x42, // 92 '\'
  2951. 0x46,0x64, // 93 ']'
  2952. 0x52,0x00, // 94 '^'
  2953. 0x00,0x70, // 95 '_'
  2954. 0x42,0x00, // 96 '`'
  2955. 0x60,0x75, // 97 'a'
  2956. 0x31,0x35, // 98 'b'
  2957. 0x60,0x61, // 99 'c'
  2958. 0x64,0x65, // 100 'd'
  2959. 0xd6,0x63, // 101 'e'
  2960. 0x26,0x27, // 102 'f'
  2961. 0x76,0x34, // 103 'g'
  2962. 0x31,0x55, // 104 'h'
  2963. 0x02,0x22, // 105 'i'
  2964. 0x04,0x64, // 106 'j'
  2965. 0x51,0x53, // 107 'k'
  2966. 0x22,0x42, // 108 'l'
  2967. 0xf0,0x9d, // 109 'm'
  2968. 0x30,0x55, // 110 'n'
  2969. 0x70,0x75, // 111 'o'
  2970. 0x70,0x17, // 112 'p'
  2971. 0x70,0x47, // 113 'q'
  2972. 0x60,0x22, // 114 'r'
  2973. 0x60,0x32, // 115 's'
  2974. 0x72,0x62, // 116 't'
  2975. 0x50,0x75, // 117 'u'
  2976. 0x50,0x25, // 118 'v'
  2977. 0x90,0xfb, // 119 'w'
  2978. 0x90,0x96, // 120 'x'
  2979. 0xa0,0x36, // 121 'y'
  2980. 0x30,0x62, // 122 'z'
  2981. 0x36,0x62, // 123 '{'
  2982. 0x22,0x22, // 124 '|'
  2983. 0x63,0x32, // 125 '}'
  2984. 0xa0,0x05, // 126 '~'
  2985. 0x00,0x00 // 127 ' '
  2986. };
  2987. _SAF_CONST int8_t _SAF_cosTable[64] =
  2988. {
  2989. 127,126,126,126,126,126,125,125,124,123,123,122,121,120,119,118,117,115,114,
  2990. 113,111,109,108,106,104,103,101,99,97,95,93,90,88,86,84,81,79,76,74,71,68,66,
  2991. 63,60,57,55,52,49,46,43,40,37,34,31,28,25,22,18,15,12,9,6,3,0
  2992. };
  2993. int8_t SAF_FE_getSoundSample(uint8_t sound, uint16_t sampleNumber)
  2994. {
  2995. switch (sound)
  2996. {
  2997. case SAF_SOUND_BEEP:
  2998. return SAF_sin(sampleNumber * 16);
  2999. break;
  3000. case SAF_SOUND_CLICK:
  3001. return (sampleNumber / 2) >> + (sampleNumber & 0x02);
  3002. break;
  3003. case SAF_SOUND_BOOM:
  3004. return sampleNumber * 2;
  3005. break;
  3006. case SAF_SOUND_BUMP:
  3007. return ((sampleNumber >> 3) + sampleNumber) ^ 0x24;
  3008. break;
  3009. default:
  3010. return 0;
  3011. break;
  3012. }
  3013. }
  3014. void SAF_FE_scale2xScreen(
  3015. const uint8_t screen[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT],
  3016. uint8_t result[SAF_SCREEN_WIDTH * SAF_SCREEN_HEIGHT * 4])
  3017. {
  3018. /* Here we try to optimize by handling the border cases separately which
  3019. should considerably reduce amount of branching per pixel (but code size will
  3020. be greater). */
  3021. uint8_t p[4] = {0,0,0,0};
  3022. const uint8_t *sCurr = screen;
  3023. const uint8_t *sBott = sCurr + SAF_SCREEN_WIDTH;
  3024. const uint8_t *sTop = sCurr - SAF_SCREEN_WIDTH;
  3025. uint8_t *rCurr = result;
  3026. uint8_t *rBott = rCurr + SAF_SCREEN_WIDTH * 2;
  3027. #define step(t,r,b,l)\
  3028. {SAF_FE_scale2xPixel(*sCurr,t,r,b,l,p);\
  3029. *rCurr = p[0]; rCurr++; *rCurr = p[1]; rCurr++;\
  3030. *rBott = p[2]; rBott++; *rBott = p[3]; rBott++;\
  3031. sCurr++; sBott++; sTop++;}
  3032. #define nextLine\
  3033. {rCurr += SAF_SCREEN_WIDTH * 2;\
  3034. rBott += SAF_SCREEN_WIDTH * 2;}
  3035. #define T *sTop
  3036. #define R *(sCurr + 1)
  3037. #define B *sBott
  3038. #define L *(sCurr - 1)
  3039. step(0,R,B,0) // first pixel (top-left)
  3040. // first row from second to second to last (top edge)
  3041. for (uint8_t i = 1; i < SAF_SCREEN_WIDTH - 1; ++i)
  3042. step(0,R,B,L)
  3043. step(0,0,B,L) // lest pixel of first row (top-right)
  3044. nextLine
  3045. // rows from second to second to last
  3046. for (uint8_t j = 1; j < SAF_SCREEN_HEIGHT - 1; ++j)
  3047. {
  3048. step(T,R,B,0) // first pixel of the row (left edge)
  3049. // middle pixels (not touching any edge)
  3050. for (uint8_t i = 1; i < SAF_SCREEN_WIDTH - 1; ++i)
  3051. step(T,R,B,L)
  3052. step(T,0,B,L) // last pixel of the row (right edge)
  3053. nextLine
  3054. }
  3055. step(T,R,0,0) // first pixel of the last row (top-bottom)
  3056. // last row from second to second to last (bottom edge)
  3057. for (uint8_t i = 1; i < SAF_SCREEN_WIDTH - 1; ++i)
  3058. step(T,R,0,L)
  3059. step(T,0,0,L) // last pixel (bottom-right)
  3060. nextLine
  3061. #undef step
  3062. #undef nextLine
  3063. #undef T
  3064. #undef R
  3065. #undef B
  3066. #undef L
  3067. }
  3068. void SAF_FE_scale2xPixel(uint8_t middle, uint8_t top, uint8_t right,
  3069. uint8_t bottom, uint8_t left, uint8_t result[4])
  3070. {
  3071. uint8_t rightBottom = right == bottom;
  3072. if (top == left)
  3073. {
  3074. result[1] = middle;
  3075. result[2] = middle;
  3076. result[0] = (bottom == left || top == right) ? middle : top;
  3077. }
  3078. else
  3079. {
  3080. result[0] = middle;
  3081. if (rightBottom)
  3082. {
  3083. result[1] = middle;
  3084. result[2] = middle;
  3085. }
  3086. else
  3087. {
  3088. result[1] = (top != right) ? middle : right;
  3089. result[2] = (bottom != left) ? middle : left;
  3090. }
  3091. }
  3092. result[3] = (!rightBottom || top == right || bottom == left) ?
  3093. middle : bottom;
  3094. }
  3095. int8_t SAF_cos(uint8_t phase)
  3096. {
  3097. uint8_t index = phase % 64;
  3098. uint8_t part = phase / 64;
  3099. if (part % 2)
  3100. index = 63 - index;
  3101. int8_t result = _SAF_READ_CONST(_SAF_cosTable + index);
  3102. if (part == 1 || part == 2)
  3103. result *= -1;
  3104. return result;
  3105. }
  3106. int8_t SAF_sin(uint8_t phase)
  3107. {
  3108. return SAF_cos(phase - 64);
  3109. }
  3110. void SAF_getFontCharacter(uint8_t asciiIndex, uint8_t result[2])
  3111. {
  3112. asciiIndex = (asciiIndex >= ' ' && asciiIndex < '~') ? asciiIndex : ' ';
  3113. asciiIndex = (asciiIndex - ' ') * 2;
  3114. result[0] = _SAF_READ_CONST(_SAF_font + asciiIndex);
  3115. result[1] = _SAF_READ_CONST(_SAF_font + asciiIndex + 1);
  3116. }
  3117. void _SAF_preprocessPosSize(int8_t *x, int8_t *y, int8_t *w, int8_t *h)
  3118. {
  3119. #define p(s,c)\
  3120. if (*s >= 0)\
  3121. {\
  3122. if (*c + *s < *c) /* overflow? */\
  3123. *s = 127 - *s;\
  3124. }\
  3125. else\
  3126. {\
  3127. if (*c + *s > *c)\
  3128. *c = -1 * (*c + 128);\
  3129. *c += *s;\
  3130. *s *= -1;\
  3131. }
  3132. p(w,x)
  3133. p(h,y)
  3134. #undef p
  3135. }
  3136. void SAF_drawRect(int8_t x, int8_t y, int8_t width, int8_t height, uint8_t color, uint8_t filled)
  3137. {
  3138. if (width == 0 || height == 0)
  3139. return;
  3140. _SAF_preprocessPosSize(&x,&y,&width,&height);
  3141. int8_t x2 = x + width;
  3142. if (filled)
  3143. {
  3144. while (height > 0)
  3145. {
  3146. int8_t x2 = x;
  3147. for (uint8_t i = 0; i < width; ++i)
  3148. {
  3149. SAF_drawPixel(x2,y,color);
  3150. x2++;
  3151. }
  3152. height--;
  3153. y++;
  3154. }
  3155. }
  3156. else
  3157. {
  3158. int8_t y2 = y;
  3159. while (height > 0)
  3160. {
  3161. SAF_drawPixel(x,y,color);
  3162. SAF_drawPixel(x2 - 1,y,color);
  3163. height--;
  3164. y++;
  3165. }
  3166. y--;
  3167. while (width > 0)
  3168. {
  3169. SAF_drawPixel(x,y,color);
  3170. SAF_drawPixel(x,y2,color);
  3171. width--;
  3172. x++;
  3173. }
  3174. }
  3175. }
  3176. int8_t SAF_drawText(const char *text, int8_t x, int8_t y, uint8_t color, uint8_t size)
  3177. {
  3178. if (size == 0)
  3179. return x;
  3180. int8_t originalX = x;
  3181. while (1) // for each string character
  3182. {
  3183. char c = *text;
  3184. if (c == 0)
  3185. break;
  3186. uint8_t character[2];
  3187. uint8_t width = 4 * size;
  3188. if (c == '\n')
  3189. {
  3190. x = originalX;
  3191. y += 5;
  3192. text++;
  3193. continue;
  3194. }
  3195. SAF_getFontCharacter(c,character);
  3196. for (int8_t i = 0; i < 2; ++i) // for both bytes
  3197. {
  3198. uint8_t byte = character[i];
  3199. for (uint8_t j = 0; j < 8; ++j)
  3200. {
  3201. if (byte & 0x01)
  3202. {
  3203. int8_t x2, y2 = y; /* we have to iterate this way sa to prevent
  3204. infinite for loops */
  3205. for (int8_t k = 0; k < size; ++k)
  3206. {
  3207. x2 = x;
  3208. for (int8_t l = 0; l < size; ++l)
  3209. {
  3210. SAF_drawPixel(x2,y2,color);
  3211. x2++;
  3212. }
  3213. y2++;
  3214. }
  3215. }
  3216. byte >>= 1;
  3217. x += size;
  3218. if (j == 3 || j == 7)
  3219. {
  3220. x -= width;
  3221. y += size;
  3222. }
  3223. }
  3224. }
  3225. x += 6 * size;
  3226. y -= width;
  3227. text++;
  3228. }
  3229. return x;
  3230. }
  3231. void SAF_drawLine(int8_t x1, int8_t y1, int8_t x2, int8_t y2, uint8_t color)
  3232. {
  3233. if (x1 > x2)
  3234. {
  3235. uint8_t tmp = x1;
  3236. x1 = x2;
  3237. x2 = tmp;
  3238. tmp = y1;
  3239. y1 = y2;
  3240. y2 = tmp;
  3241. }
  3242. uint8_t dx = x2 - x1;
  3243. uint8_t dy;
  3244. int8_t *drawX = &x1;
  3245. int8_t *drawY = &y1;
  3246. int8_t stepX = 1;
  3247. int8_t stepY = 1;
  3248. uint8_t length = dx;
  3249. uint8_t add;
  3250. uint8_t compare = dx;
  3251. if (y2 > y1)
  3252. {
  3253. dy = y2 - y1;
  3254. add = dy;
  3255. if (dy > dx)
  3256. {
  3257. drawX = &y1;
  3258. drawY = &x1;
  3259. length = dy;
  3260. add = dx;
  3261. compare = dy;
  3262. }
  3263. }
  3264. else
  3265. {
  3266. dy = y1 - y2;
  3267. add = dy;
  3268. if (dy < dx)
  3269. {
  3270. stepY = -1;
  3271. }
  3272. else
  3273. {
  3274. drawX = &y1;
  3275. drawY = &x1;
  3276. length = dy;
  3277. stepX = -1;
  3278. add = dx;
  3279. compare = dy;
  3280. }
  3281. }
  3282. uint8_t accumulator = compare / 2;
  3283. length++;
  3284. while (length > 0)
  3285. {
  3286. SAF_drawPixel(x1,y1,color);
  3287. *drawX += stepX;
  3288. length--;
  3289. accumulator += add;
  3290. if (accumulator >= compare)
  3291. {
  3292. accumulator -= compare;
  3293. *drawY += stepY;
  3294. }
  3295. }
  3296. }
  3297. uint32_t SAF_frame(void)
  3298. {
  3299. return _SAF_frame;
  3300. }
  3301. uint32_t SAF_time(void)
  3302. {
  3303. return _SAF_frame * SAF_MS_PER_FRAME;
  3304. }
  3305. void SAF_FE_init(void)
  3306. {
  3307. SAF_init();
  3308. }
  3309. uint8_t SAF_FE_loop(void)
  3310. {
  3311. for (uint8_t i = 0; i < SAF_BUTTONS; ++i)
  3312. {
  3313. uint8_t *b = _SAF_buttonStates + i;
  3314. uint8_t state = *b;
  3315. *b = SAF_FE_buttonPressed(i) ? (state < 255 ? state + 1 : state) : 0;
  3316. }
  3317. uint8_t result = SAF_loop();
  3318. _SAF_frame++;
  3319. return result;
  3320. }
  3321. static inline void SAF_clearScreen(uint8_t color)
  3322. {
  3323. SAF_drawRect(0,0,SAF_SCREEN_WIDTH,SAF_SCREEN_HEIGHT,color,1);
  3324. }
  3325. uint8_t SAF_random()
  3326. {
  3327. /* We reorder each sequence of 8 values to give some variety to specific bit
  3328. patterns. */
  3329. uint8_t remap[8] = {5,7,2,0,3,6,4,1};
  3330. _SAF_currentRandom *= 13;
  3331. _SAF_currentRandom += 7;
  3332. uint8_t remainder = _SAF_currentRandom % 8;
  3333. return _SAF_currentRandom - remainder + remap[remainder];
  3334. }
  3335. uint8_t SAF_buttonJustPressed(uint8_t button)
  3336. {
  3337. return SAF_buttonPressed(button) == 1;
  3338. }
  3339. uint8_t SAF_buttonPressed(uint8_t button)
  3340. {
  3341. return (button < SAF_BUTTONS) ? _SAF_buttonStates[button] : 0;
  3342. }
  3343. uint8_t SAF_colorFromRGB(uint8_t red, uint8_t green, uint8_t blue)
  3344. {
  3345. return SAF_COLOR_RGB(red,green,blue);
  3346. }
  3347. void SAF_colorToRGB(uint8_t colorIndex, uint8_t *red, uint8_t *green, uint8_t *blue)
  3348. {
  3349. uint8_t value = (colorIndex >> 5) & 0x07;
  3350. *red = value != 7 ? value * 36 : 255;
  3351. value = (colorIndex >> 2) & 0x07;
  3352. *green = value != 7 ? value * 36 : 255;
  3353. value = colorIndex & 0x03;
  3354. *blue = (value != 3) ? value * 72 : 255;
  3355. }
  3356. uint8_t SAF_colorInvert(uint8_t color)
  3357. {
  3358. return ~color;
  3359. }
  3360. uint16_t SAF_sqrt(uint32_t number)
  3361. {
  3362. uint32_t result = 0;
  3363. uint32_t a = number;
  3364. uint32_t b = 1u << 30;
  3365. while (b > a)
  3366. b >>= 2;
  3367. while (b != 0)
  3368. {
  3369. if (a >= result + b)
  3370. {
  3371. a -= result + b;
  3372. result = result + 2 * b;
  3373. }
  3374. b >>= 2;
  3375. result >>= 1;
  3376. }
  3377. return result;
  3378. }
  3379. #define _SAF_IMAGE_MODE_NORMAL 0
  3380. #define _SAF_IMAGE_MODE_COMPRESSED 1
  3381. #define _SAF_IMAGE_MODE_1BIT 2
  3382. struct
  3383. {
  3384. uint8_t mode;
  3385. const uint8_t *image;
  3386. uint8_t transparentColor;
  3387. const uint8_t *binaryMask;
  3388. uint8_t binaryLine;
  3389. uint8_t binaryMaskLine;
  3390. uint8_t binaryPosition;
  3391. uint8_t binaryColor1;
  3392. uint8_t binaryColor2;
  3393. uint8_t rleCount;
  3394. uint8_t rleLastColor;
  3395. const uint8_t *palette;
  3396. } _SAF_drawnImage;
  3397. uint8_t _SAF_getNextImagePixel()
  3398. {
  3399. uint8_t result = 0;
  3400. switch (_SAF_drawnImage.mode)
  3401. {
  3402. case _SAF_IMAGE_MODE_NORMAL:
  3403. result = *_SAF_drawnImage.image;
  3404. _SAF_drawnImage.image++;
  3405. break;
  3406. case _SAF_IMAGE_MODE_1BIT:
  3407. if (_SAF_drawnImage.binaryPosition == 0)
  3408. {
  3409. _SAF_drawnImage.binaryLine = *_SAF_drawnImage.image;
  3410. _SAF_drawnImage.image++;
  3411. _SAF_drawnImage.binaryPosition = 8;
  3412. if (_SAF_drawnImage.binaryMask != 0)
  3413. {
  3414. _SAF_drawnImage.binaryMaskLine = ~(*_SAF_drawnImage.binaryMask);
  3415. /* We negate the mask because we want 1 to mean transparency; this
  3416. allows to avoid an if-check before mask line shift later on. */
  3417. _SAF_drawnImage.binaryMask++;
  3418. }
  3419. }
  3420. result = ((_SAF_drawnImage.binaryMaskLine & 0x80) == 0) ?
  3421. (
  3422. (_SAF_drawnImage.binaryLine & 0x80) ?
  3423. _SAF_drawnImage.binaryColor1 : _SAF_drawnImage.binaryColor2
  3424. ) : _SAF_drawnImage.transparentColor;
  3425. _SAF_drawnImage.binaryPosition--;
  3426. _SAF_drawnImage.binaryLine <<= 1;
  3427. _SAF_drawnImage.binaryMaskLine <<= 1;
  3428. break;
  3429. case _SAF_IMAGE_MODE_COMPRESSED:
  3430. if (_SAF_drawnImage.rleCount == 0)
  3431. {
  3432. uint8_t b = *_SAF_drawnImage.image;
  3433. _SAF_drawnImage.rleLastColor = _SAF_drawnImage.palette[b & 0x0f];
  3434. _SAF_drawnImage.rleCount = (b >> 4) + 1;
  3435. _SAF_drawnImage.image++;
  3436. }
  3437. result = _SAF_drawnImage.rleLastColor;
  3438. _SAF_drawnImage.rleCount--;
  3439. break;
  3440. default: break;
  3441. }
  3442. return result;
  3443. }
  3444. void _SAF_drawImageGeneral(int8_t x, int8_t y, uint8_t transform)
  3445. {
  3446. int8_t stepX = 1;
  3447. int8_t stepY = 1;
  3448. uint8_t invert = transform & SAF_TRANSFORM_INVERT;
  3449. uint8_t scale = 1;
  3450. switch (transform & 0x18)
  3451. {
  3452. case SAF_TRANSFORM_SCALE_2: scale = 2; break;
  3453. case SAF_TRANSFORM_SCALE_3: scale = 3; break;
  3454. case SAF_TRANSFORM_SCALE_4: scale = 4; break;
  3455. default: break;
  3456. }
  3457. uint8_t width = *(_SAF_drawnImage.image);
  3458. _SAF_drawnImage.image++;
  3459. uint8_t h = *(_SAF_drawnImage.image);
  3460. _SAF_drawnImage.image++;
  3461. if (_SAF_drawnImage.mode == _SAF_IMAGE_MODE_COMPRESSED)
  3462. _SAF_drawnImage.image += 16; // skip the palette
  3463. uint8_t scaledWidth = (width - 1) * scale;
  3464. uint8_t scaledHeight = (h - 1) * scale;
  3465. int8_t *drawX = &x;
  3466. int8_t *drawY = &y;
  3467. switch (transform & 0x07)
  3468. {
  3469. case SAF_TRANSFORM_ROTATE_90:
  3470. drawY = &x; drawX = &y; stepY = -1; x += scaledHeight; break;
  3471. case SAF_TRANSFORM_ROTATE_180:
  3472. stepX = -1; stepY = -1; x += scaledWidth; y += scaledHeight; break;
  3473. case SAF_TRANSFORM_ROTATE_270:
  3474. drawY = &x; drawX = &y; stepX = -1; y += scaledWidth; break;
  3475. case SAF_TRANSFORM_FLIP:
  3476. stepX = -1; x += scaledWidth; break;
  3477. case (SAF_TRANSFORM_ROTATE_90 | SAF_TRANSFORM_FLIP):
  3478. drawY = &x; drawX = &y; stepX = -1; stepY = -1; x += scaledHeight; y += scaledWidth; break;
  3479. case (SAF_TRANSFORM_ROTATE_180 | SAF_TRANSFORM_FLIP):
  3480. stepY = -1; y += scaledHeight; break;
  3481. case (SAF_TRANSFORM_ROTATE_270 | SAF_TRANSFORM_FLIP):
  3482. drawY = &x; drawX = &y; break;
  3483. default: break;
  3484. }
  3485. stepX *= scale;
  3486. stepY *= scale;
  3487. int8_t lineBack = -1 * stepX * width;
  3488. while (h > 0)
  3489. {
  3490. uint8_t w = width;
  3491. while (w > 0)
  3492. {
  3493. uint8_t pixel = _SAF_getNextImagePixel();
  3494. if (pixel != _SAF_drawnImage.transparentColor)
  3495. for (int8_t x2 = x; x2 < x + scale; ++x2)
  3496. for (int8_t y2 = y; y2 < y + scale; ++y2)
  3497. SAF_drawPixel(x2,y2,invert ? ~(pixel) : pixel);
  3498. *drawX += stepX;
  3499. w--;
  3500. }
  3501. *drawX += lineBack;
  3502. h--;
  3503. *drawY += stepY;
  3504. }
  3505. }
  3506. /** Performs the most common cases of image drawing faster than the general
  3507. function. */
  3508. void _SAF_drawImageFast(const uint8_t *image, int8_t x, int8_t y, uint8_t flip,
  3509. uint8_t transparentColor, uint8_t is1Bit, const uint8_t *mask, uint8_t color1,
  3510. uint8_t color2)
  3511. {
  3512. uint8_t width = *image;
  3513. image++;
  3514. uint8_t height = *image;
  3515. image ++;
  3516. int8_t x0 = x;
  3517. int8_t xPlus = 1;
  3518. if (flip)
  3519. {
  3520. x0 = x + width - 1;
  3521. xPlus = -1;
  3522. }
  3523. #define loopStart\
  3524. while (height != 0) {\
  3525. x = x0;\
  3526. for (uint8_t w = width; w != 0; --w, x += xPlus) {
  3527. #define loopEnd\
  3528. }\
  3529. height--;\
  3530. y++; }
  3531. if (is1Bit)
  3532. {
  3533. uint8_t bitCount = 0, imgLine = 0, maskLine = 0;
  3534. mask += mask != 0 ? 2 : 0; // skip width and height
  3535. loopStart
  3536. if (bitCount == 0)
  3537. {
  3538. imgLine = *image;
  3539. bitCount = 8;
  3540. image++;
  3541. if (mask != 0)
  3542. {
  3543. maskLine = ~(*mask); // negation helps avoid branching later
  3544. mask++;
  3545. }
  3546. }
  3547. if ((maskLine & 0x80) == 0)
  3548. SAF_drawPixel(x,y,(imgLine & 0x80) ? color1 : color2);
  3549. bitCount--;
  3550. imgLine <<= 1;
  3551. maskLine <<= 1;
  3552. loopEnd
  3553. }
  3554. else
  3555. {
  3556. loopStart
  3557. uint8_t color = *image;
  3558. if (color != transparentColor)
  3559. SAF_drawPixel(x,y,*image);
  3560. image++;
  3561. loopEnd
  3562. }
  3563. #undef loopStart
  3564. #undef loopEnd
  3565. }
  3566. void SAF_drawImage(const uint8_t *image, int8_t x, int8_t y, uint8_t transform,
  3567. uint8_t transparentColor)
  3568. {
  3569. if ((transform & ~SAF_TRANSFORM_FLIP) == 0)
  3570. {
  3571. _SAF_drawImageFast(image,x,y,transform & SAF_TRANSFORM_FLIP,
  3572. transparentColor,0,0,0,0);
  3573. return;
  3574. }
  3575. _SAF_drawnImage.image = image;
  3576. _SAF_drawnImage.mode = _SAF_IMAGE_MODE_NORMAL;
  3577. _SAF_drawnImage.transparentColor = transparentColor;
  3578. _SAF_drawImageGeneral(x,y,transform);
  3579. }
  3580. void SAF_drawImageCompressed(const uint8_t *image, int8_t x, int8_t y,
  3581. uint8_t transform, uint8_t transparentColor)
  3582. {
  3583. _SAF_drawnImage.image = image;
  3584. _SAF_drawnImage.mode = _SAF_IMAGE_MODE_COMPRESSED;
  3585. _SAF_drawnImage.transparentColor = transparentColor;
  3586. _SAF_drawnImage.rleCount = 0;
  3587. _SAF_drawnImage.rleLastColor = 0;
  3588. _SAF_drawnImage.palette = image + 2;
  3589. _SAF_drawImageGeneral(x,y,transform);
  3590. }
  3591. void SAF_drawImage1Bit(const uint8_t *image, int8_t x, int8_t y,
  3592. const uint8_t *mask, uint8_t color1, uint8_t color2, uint8_t transform)
  3593. {
  3594. if ((transform & ~SAF_TRANSFORM_FLIP) == 0)
  3595. {
  3596. _SAF_drawImageFast(image,x,y,transform & SAF_TRANSFORM_FLIP,0,1,mask,color1,
  3597. color2);
  3598. return;
  3599. }
  3600. _SAF_drawnImage.image = image;
  3601. _SAF_drawnImage.binaryMask = (mask != 0) ? mask + 2 : 0; // skip width/height
  3602. _SAF_drawnImage.binaryLine = 0;
  3603. _SAF_drawnImage.binaryMaskLine = 0; // 0 will be negated
  3604. _SAF_drawnImage.binaryPosition = 0;
  3605. for (int i = 0; i < 3; ++i)
  3606. if (i != color1 && i != color2)
  3607. {
  3608. _SAF_drawnImage.transparentColor = i;
  3609. break;
  3610. }
  3611. _SAF_drawnImage.mode = _SAF_IMAGE_MODE_1BIT;
  3612. _SAF_drawnImage.binaryColor1 = color1;
  3613. _SAF_drawnImage.binaryColor2 = color2;
  3614. _SAF_drawImageGeneral(x,y,transform);
  3615. }
  3616. void SAF_drawPixel(int8_t x, int8_t y, uint8_t color)
  3617. {
  3618. if ((x & 0xc0) == 0 && (y & 0xc0) == 0)
  3619. #if SAF_PLATFORM_COLOR_COUNT <= 2
  3620. SAF_FE_drawPixel(x,y,SAF_FE_colorTo1Bit(color,x,y) ?
  3621. SAF_COLOR_WHITE : SAF_COLOR_BLACK);
  3622. #else
  3623. SAF_FE_drawPixel(x,y,color);
  3624. #endif
  3625. }
  3626. void SAF_playSound(uint8_t sound)
  3627. {
  3628. #if SAF_SETTING_ENABLE_SOUND
  3629. if (sound < SAF_SOUNDS)
  3630. SAF_FE_playSound(sound);
  3631. #else
  3632. _SAF_UNUSED(sound);
  3633. #endif
  3634. }
  3635. void _SAF_reloadSaveMemory()
  3636. {
  3637. if (!_SAF_saveMemoryLoaded)
  3638. {
  3639. for (uint8_t i = 0; i < SAF_SAVE_SIZE; ++i)
  3640. _SAF_saveMemory[i] =
  3641. #if SAF_SETTING_ENABLE_SAVES
  3642. SAF_FE_load(i);
  3643. #else
  3644. 0;
  3645. #endif
  3646. _SAF_saveMemoryLoaded = 1;
  3647. }
  3648. }
  3649. void SAF_save(uint8_t index, uint8_t data)
  3650. {
  3651. if (index >= SAF_SAVE_SIZE)
  3652. return;
  3653. _SAF_reloadSaveMemory();
  3654. if (_SAF_saveMemory[index] != data)
  3655. {
  3656. _SAF_saveMemory[index] = data;
  3657. #if SAF_SETTING_ENABLE_SAVES
  3658. SAF_FE_save(index,data);
  3659. #endif
  3660. }
  3661. }
  3662. uint8_t SAF_load(uint8_t index)
  3663. {
  3664. if (index >= SAF_SAVE_SIZE)
  3665. return 0;
  3666. _SAF_reloadSaveMemory();
  3667. return _SAF_saveMemory[index];
  3668. }
  3669. void SAF_randomSeed(uint8_t seed)
  3670. {
  3671. _SAF_currentRandom = seed;
  3672. }
  3673. char *SAF_intToStr(int32_t number, char *string)
  3674. {
  3675. if (number == 0)
  3676. {
  3677. *string = '0';
  3678. *(string + 1) = 0;
  3679. return string;
  3680. }
  3681. char *start = string;
  3682. if (number < 0)
  3683. {
  3684. *start = '-';
  3685. number *= -1;
  3686. start++;
  3687. }
  3688. while (number > 0)
  3689. {
  3690. *start = '0' + number % 10;
  3691. number /= 10;
  3692. start++;
  3693. }
  3694. *start = 0; // terminate
  3695. char *end = start - 1;
  3696. start = string + (*string == '-');
  3697. while (end > start)
  3698. {
  3699. char tmp = *start;
  3700. *start = *end;
  3701. *end = tmp;
  3702. start++;
  3703. end--;
  3704. }
  3705. return string;
  3706. }
  3707. char *SAF_floatToStr(float number, char *string, uint8_t decimals)
  3708. {
  3709. int32_t whole = number;
  3710. SAF_intToStr(whole,string);
  3711. if (decimals == 0)
  3712. {
  3713. *string = 0;
  3714. return string;
  3715. }
  3716. char *c = string;
  3717. while (*c != 0)
  3718. c++;
  3719. *c = '.';
  3720. c++;
  3721. if (number < 0)
  3722. {
  3723. number *= -1;
  3724. whole *= -1;
  3725. }
  3726. if (decimals > 10)
  3727. decimals = 10;
  3728. int32_t factor = 1;
  3729. while (decimals > 0)
  3730. {
  3731. factor *= 10;
  3732. decimals--;
  3733. }
  3734. SAF_intToStr((number - whole) * factor,c);
  3735. return string;
  3736. }
  3737. void SAF_drawCircle(int8_t x, int8_t y, uint8_t radius, uint8_t color, uint8_t filled)
  3738. {
  3739. int8_t drawX = 0;
  3740. int8_t drawY = radius;
  3741. int16_t d = 3 - 2 * radius;
  3742. if (!filled)
  3743. {
  3744. SAF_drawPixel(x,y + radius,color);
  3745. SAF_drawPixel(x,y - radius,color);
  3746. SAF_drawPixel(x + radius,y,color);
  3747. SAF_drawPixel(x - radius,y,color);
  3748. }
  3749. else
  3750. for (int8_t i = x - radius; i <= x + radius; ++i)
  3751. SAF_drawPixel(i,y,color);
  3752. while (drawY >= drawX)
  3753. {
  3754. if (d < 0)
  3755. {
  3756. d += 4 * drawX + 6;
  3757. }
  3758. else
  3759. {
  3760. d += 4 * (drawX - drawY) + 10;
  3761. drawY--;
  3762. }
  3763. drawX++;
  3764. int8_t xPlus = x + drawX;
  3765. int8_t xMinus = x - drawX;
  3766. int8_t yPlus = y + drawY;
  3767. int8_t yMinus = y - drawY;
  3768. int8_t x2Plus = x + drawY;
  3769. int8_t x2Minus = x - drawY;
  3770. int8_t y2Plus = y + drawX;
  3771. int8_t y2Minus = y - drawX;
  3772. if (!filled)
  3773. {
  3774. SAF_drawPixel(xPlus,yPlus,color);
  3775. SAF_drawPixel(xPlus,yMinus,color);
  3776. SAF_drawPixel(xMinus,yPlus,color);
  3777. SAF_drawPixel(xMinus,yMinus,color);
  3778. SAF_drawPixel(x2Plus,y2Plus,color);
  3779. SAF_drawPixel(x2Plus,y2Minus,color);
  3780. SAF_drawPixel(x2Minus,y2Plus,color);
  3781. SAF_drawPixel(x2Minus,y2Minus,color);
  3782. }
  3783. else
  3784. {
  3785. for (int8_t i = xMinus; i <= xPlus; ++i)
  3786. {
  3787. SAF_drawPixel(i,yPlus,color);
  3788. SAF_drawPixel(i,yMinus,color);
  3789. }
  3790. for (int8_t i = x2Minus; i <= x2Plus; ++i)
  3791. {
  3792. SAF_drawPixel(i,y2Plus,color);
  3793. SAF_drawPixel(i,y2Minus,color);
  3794. }
  3795. }
  3796. }
  3797. }
  3798. const char *SAF_extension(const char *string)
  3799. {
  3800. return SAF_FE_extension(string);
  3801. }
  3802. uint8_t SAF_colorTo1Bit(uint8_t colorIndex)
  3803. {
  3804. return
  3805. #if SAF_SETTING_FASTER_1BIT == 0
  3806. // more accurate: 7 operations
  3807. ((colorIndex & 0x03) +
  3808. ((colorIndex >> 2) & 0x07) +
  3809. (colorIndex >> 5)) > 8;
  3810. #elif SAF_SETTING_FASTER_1BIT == 1
  3811. // faster: 4 operations
  3812. ((colorIndex >> 3) +
  3813. (colorIndex & 0x1f)) >= ((0x1f + 0x1c) / 2);
  3814. #elif SAF_SETTING_FASTER_1BIT == 2
  3815. // fastest: 1 operation
  3816. colorIndex & 0x90;
  3817. #else
  3818. // fastester: 0 operations
  3819. colorIndex;
  3820. #endif
  3821. }
  3822. uint8_t SAF_colorToGrayscale(uint8_t colorIndex)
  3823. {
  3824. uint8_t tmp = colorIndex >> 2;
  3825. return
  3826. (((colorIndex << 2) & 0x7f) + tmp +
  3827. ((colorIndex << 4) & 0x3f)) | tmp;
  3828. }
  3829. uint16_t SAF_FE_hashStr(const char *str)
  3830. {
  3831. uint16_t r = 7621;
  3832. while (*str != 0)
  3833. {
  3834. r = (r << 4) ^ (r + ((uint16_t) *str));
  3835. str++;
  3836. }
  3837. return r;
  3838. }
  3839. uint8_t SAF_FE_colorTo1Bit(uint8_t color, uint8_t x, uint8_t y)
  3840. {
  3841. #if SAF_SETTING_1BIT_DITHER
  3842. color = SAF_colorToGrayscale(color);
  3843. if (color < 85)
  3844. return 0;
  3845. else if (color > 170)
  3846. return 1;
  3847. else
  3848. return (x % 2) == (y % 2);
  3849. #else
  3850. _SAF_UNUSED(x);
  3851. _SAF_UNUSED(y);
  3852. return SAF_colorTo1Bit(color);
  3853. #endif
  3854. }
  3855. #endif // guard