saf.h 120 KB

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