diff --git a/.gitignore b/.gitignore index 05c7c0314..47db9fa8f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.diff *.dump *.elf +*.dll *.exe *.fwjpnfont *.gba diff --git a/Makefile b/Makefile index 372b2de4f..73622556c 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,9 @@ OBJDUMP := $(PREFIX)objdump AS := $(PREFIX)as LD := $(PREFIX)ld MODERN := 1 -SDL_DIR := /mnt/c/Users/josma/Desktop/pokexe/pokefirered/sdl +SDL_DIR := /mnt/c/Users/josma/Desktop/pokexe2/SDL2-2.32.10/i686-w64-mingw32 + + EXE := ifeq ($(OS),Windows_NT) @@ -371,5 +373,5 @@ endif # Elf from object files LDFLAGS = -Map ../../$(MAP) $(ROM): $(LD_SCRIPT) $(LD_SCRIPT_DEPS) $(OBJS) - cd $(OBJ_DIR) && i686-w64-mingw32-gcc -Wno-trigraphs -Wimplicit -Wparentheses -Wunused -m32 -std=gnu99 -fleading-underscore -fno-dce -fno-builtin -Wno-unused-function -DFIRERED=1 -DREVISION=0 -DENGLISH=1 -DPORTABLE -DNONMATCHING -D UBFIX -DMODERN=1 -O3 -Wl,--demangle $(OBJS_REL) -o $(ROM) -L$(SDL_DIR)/lib -lxinput -lkernel32 -lSDL3 + cd $(OBJ_DIR) && i686-w64-mingw32-gcc -Wno-trigraphs -Wimplicit -Wparentheses -Wunused -m32 -std=gnu99 -fleading-underscore -fno-dce -fno-builtin -Wno-unused-function -DFIRERED=1 -DREVISION=0 -DENGLISH=1 -DPORTABLE -DNONMATCHING -D UBFIX -DMODERN=1 -O3 -Wl,--demangle $(OBJS_REL) -o $(ROM) -L$(SDL_DIR)/lib -lxinput -lkernel32 -lmingw32 -lSDL2main -lSDL2 mv $(OBJ_DIR)/$(ROM) ./ diff --git a/include/cgb_audio.h b/include/cgb_audio.h new file mode 100644 index 000000000..ad5140bb3 --- /dev/null +++ b/include/cgb_audio.h @@ -0,0 +1,36 @@ +#ifndef CGB_AUDIO_H +#define CGB_AUDIO_H + +#define MIXED_AUDIO_BUFFER_SIZE 4907 + +struct AudioCGB{ + u16 ch1Freq; + u8 ch1SweepCounter; + u8 ch1SweepCounterI; + bool8 ch1SweepDir; + u8 ch1SweepShift; + u8 Vol[4]; + u8 VolI[4]; + u8 Len[4]; + u8 LenI[4]; + bool8 LenOn[4]; + u8 EnvCounter[4]; + u8 EnvCounterI[4]; + bool8 EnvDir[4]; + bool8 DAC[4]; + float WAVRAM[32]; + u16 ch4LFSR [2]; + __attribute__((aligned(4))) float outBuffer[MIXED_AUDIO_BUFFER_SIZE * 2]; +}; + +void cgb_audio_init(u32 rate); +void cgb_set_sweep(u8 sweep); +void cgb_set_wavram(); +void cgb_toggle_length(u8 channel, bool8 state); +void cgb_set_length(u8 channel, u8 length); +void cgb_set_envelope(u8 channel, u8 envelope); +void cgb_trigger_note(u8 channel); +void cgb_audio_generate(u16 samplesPerFrame); +float *cgb_get_buffer(); + +#endif diff --git a/include/cgb_tables.h b/include/cgb_tables.h new file mode 100644 index 000000000..f63aacbf2 --- /dev/null +++ b/include/cgb_tables.h @@ -0,0 +1,2349 @@ +#ifndef CGB_TABLES_H +#define CGB_TABLES_H + +const int16_t PU0 [32] = { + 1, 1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1, + 1, 1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +const int16_t PU1 [32] = { + 1, 1, 1, 1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1, + 1, 1, 1, 1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +const int16_t PU2 [32] = { + 1, 1, 1, 1, 1, 1, 1, 1, + -1,-1,-1,-1,-1,-1,-1,-1, + 1, 1, 1, 1, 1, 1, 1, 1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +const int16_t PU3 [32] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1,-1,-1,-1,-1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1,-1,-1,-1,-1 +}; + +int16_t WAV [32] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1,-1,-1,-1,-1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1,-1,-1,-1,-1 +}; + +const float freqTable[2048] = { + 32, + 32.0156326331216, + 32.0312805474096, + 32.0469437652812, + 32.0626223091976, + 32.0783162016642, + 32.0940254652302, + 32.109750122489, + 32.1254901960784, + 32.1412457086807, + 32.1570166830226, + 32.1728031418753, + 32.188605108055, + 32.2044226044226, + 32.220255653884, + 32.2361042793901, + 32.251968503937, + 32.2678483505662, + 32.2837438423645, + 32.2996550024643, + 32.3155818540434, + 32.3315244203256, + 32.3474827245805, + 32.3634567901235, + 32.3794466403162, + 32.3954522985665, + 32.4114737883284, + 32.4275111331024, + 32.4435643564356, + 32.4596334819217, + 32.4757185332012, + 32.4918195339613, + 32.5079365079365, + 32.5240694789082, + 32.5402184707051, + 32.5563835072032, + 32.572564612326, + 32.5887618100448, + 32.6049751243781, + 32.6212045793927, + 32.6374501992032, + 32.6537120079721, + 32.6699900299103, + 32.6862842892768, + 32.7025948103792, + 32.7189216175736, + 32.7352647352647, + 32.751624187906, + 32.768, + 32.784392196098, + 32.8008008008008, + 32.8172258387581, + 32.8336673346693, + 32.8501253132832, + 32.8665997993982, + 32.8830908178625, + 32.8995983935743, + 32.9161225514817, + 32.9326633165829, + 32.9492207139266, + 32.9657947686117, + 32.9823855057876, + 32.9989929506546, + 33.0156171284635, + 33.0322580645161, + 33.0489157841654, + 33.0655903128153, + 33.0822816759213, + 33.0989898989899, + 33.1157150075796, + 33.1324570273003, + 33.1492159838139, + 33.165991902834, + 33.1827848101266, + 33.1995947315096, + 33.2164216928535, + 33.2332657200811, + 33.2501268391679, + 33.2670050761421, + 33.2839004570848, + 33.3008130081301, + 33.3177427554652, + 33.3346897253306, + 33.3516539440204, + 33.3686354378819, + 33.3856342333164, + 33.4026503567788, + 33.4196838347782, + 33.4367346938776, + 33.4538029606942, + 33.4708886618999, + 33.4879918242207, + 33.5051124744376, + 33.5222506393862, + 33.539406345957, + 33.5565796210957, + 33.5737704918033, + 33.5909789851358, + 33.6082051282051, + 33.6254489481786, + 33.6427104722793, + 33.6599897277863, + 33.6772867420349, + 33.6946015424164, + 33.7119341563786, + 33.7292846114256, + 33.7466529351184, + 33.7640391550747, + 33.7814432989691, + 33.7988653945333, + 33.8163054695562, + 33.8337635518844, + 33.8512396694215, + 33.8687338501292, + 33.8862461220269, + 33.9037765131919, + 33.9213250517598, + 33.9388917659244, + 33.9564766839378, + 33.9740798341109, + 33.9917012448133, + 34.0093409444733, + 34.0269989615784, + 34.0446753246753, + 34.0623700623701, + 34.0800832033281, + 34.0978147762747, + 34.1155648099948, + 34.1333333333333, + 34.1511203751954, + 34.1689259645464, + 34.1867501304121, + 34.2045929018789, + 34.222454308094, + 34.2403343782654, + 34.2582331416623, + 34.2761506276151, + 34.2940868655154, + 34.3120418848168, + 34.330015715034, + 34.3480083857442, + 34.3660199265863, + 34.3840503672613, + 34.4020997375328, + 34.4201680672269, + 34.4382553862323, + 34.4563617245005, + 34.4744871120463, + 34.4926315789474, + 34.5107951553449, + 34.5289778714436, + 34.5471797575119, + 34.5654008438819, + 34.5836411609499, + 34.6019007391763, + 34.6201796090861, + 34.6384778012685, + 34.6567953463776, + 34.6751322751323, + 34.6934886183166, + 34.7118644067797, + 34.7302596714361, + 34.7486744432662, + 34.7671087533156, + 34.7855626326964, + 34.8040361125863, + 34.8225292242295, + 34.8410419989367, + 34.8595744680851, + 34.8781266631187, + 34.8966986155485, + 34.9152903569526, + 34.9339019189765, + 34.9525333333333, + 34.9711846318036, + 34.989855846236, + 35.008547008547, + 35.0272581507215, + 35.0459893048128, + 35.0647405029427, + 35.0835117773019, + 35.10230316015, + 35.1211146838156, + 35.1399463806971, + 35.1587982832618, + 35.1776704240472, + 35.1965628356606, + 35.2154755507792, + 35.2344086021505, + 35.2533620225928, + 35.2723358449946, + 35.2913301023156, + 35.3103448275862, + 35.3293800539084, + 35.3484358144552, + 35.3675121424717, + 35.3866090712743, + 35.4057266342518, + 35.4248648648649, + 35.4440237966468, + 35.4632034632035, + 35.4824038982133, + 35.501625135428, + 35.5208672086721, + 35.5401301518438, + 35.5594139989148, + 35.5787187839305, + 35.5980445410103, + 35.6173913043478, + 35.636759108211, + 35.6561479869423, + 35.6755579749592, + 35.6949891067538, + 35.7144414168937, + 35.7339149400218, + 35.7534097108565, + 35.7729257641921, + 35.792463134899, + 35.8120218579235, + 35.8316019682887, + 35.8512035010941, + 35.8708264915161, + 35.8904709748083, + 35.9101369863014, + 35.9298245614035, + 35.9495337356007, + 35.9692645444566, + 35.9890170236134, + 36.0087912087912, + 36.0285871357889, + 36.048404840484, + 36.0682443588332, + 36.0881057268722, + 36.1079889807162, + 36.1278941565601, + 36.1478212906784, + 36.167770419426, + 36.187741579238, + 36.2077348066298, + 36.2277501381979, + 36.2477876106195, + 36.267847260653, + 36.2879291251384, + 36.3080332409972, + 36.3281596452328, + 36.3483083749307, + 36.3684794672586, + 36.388672959467, + 36.4088888888889, + 36.4291272929405, + 36.4493882091212, + 36.4696716750139, + 36.4899777282851, + 36.5103064066852, + 36.5306577480491, + 36.5510317902956, + 36.5714285714286, + 36.5918481295366, + 36.6122905027933, + 36.6327557294578, + 36.6532438478747, + 36.6737548964745, + 36.6942889137738, + 36.7148459383753, + 36.7354260089686, + 36.7560291643298, + 36.7766554433221, + 36.7973048848961, + 36.8179775280899, + 36.8386734120292, + 36.859392575928, + 36.8801350590884, + 36.9009009009009, + 36.9216901408451, + 36.9425028184893, + 36.9633389734913, + 36.9841986455982, + 37.0050818746471, + 37.025988700565, + 37.0469191633691, + 37.0678733031674, + 37.0888511601585, + 37.1098527746319, + 37.1308781869688, + 37.1519274376417, + 37.173000567215, + 37.1940976163451, + 37.2152186257808, + 37.2363636363636, + 37.2575326890279, + 37.2787258248009, + 37.2999430848036, + 37.3211845102506, + 37.3424501424501, + 37.363740022805, + 37.3850541928123, + 37.4063926940639, + 37.4277555682467, + 37.4491428571429, + 37.4705546026301, + 37.4919908466819, + 37.5134516313681, + 37.5349369988545, + 37.556446991404, + 37.5779816513761, + 37.5995410212278, + 37.6211251435132, + 37.6427340608845, + 37.664367816092, + 37.6860264519839, + 37.7077100115075, + 37.7294185377087, + 37.7511520737327, + 37.7729106628242, + 37.7946943483276, + 37.8165031736872, + 37.838337182448, + 37.8601964182553, + 37.8820809248555, + 37.903990746096, + 37.9259259259259, + 37.9478865083961, + 37.9698725376593, + 37.991884057971, + 38.0139211136891, + 38.0359837492745, + 38.0580720092915, + 38.0801859384079, + 38.1023255813953, + 38.1244909831297, + 38.1466821885914, + 38.1688992428655, + 38.1911421911422, + 38.2134110787172, + 38.2357059509918, + 38.2580268534734, + 38.2803738317757, + 38.3027469316189, + 38.3251461988304, + 38.3475716793446, + 38.3700234192037, + 38.3925014645577, + 38.4150058616647, + 38.4375366568915, + 38.4600938967136, + 38.4826776277158, + 38.5052878965922, + 38.527924750147, + 38.5505882352941, + 38.5732783990583, + 38.5959952885748, + 38.6187389510902, + 38.6415094339623, + 38.6643067846608, + 38.6871310507674, + 38.7099822799764, + 38.7328605200946, + 38.755765819042, + 38.7786982248521, + 38.801657785672, + 38.824644549763, + 38.8476585655009, + 38.870699881376, + 38.8937685459941, + 38.916864608076, + 38.9399881164587, + 38.9631391200951, + 38.9863176680547, + 39.0095238095238, + 39.0327575938058, + 39.0560190703218, + 39.0793082886106, + 39.1026252983294, + 39.1259701492537, + 39.1493428912784, + 39.1727435744172, + 39.1961722488038, + 39.2196289646918, + 39.2431137724551, + 39.2666267225884, + 39.2901678657074, + 39.3137372525495, + 39.3373349339736, + 39.360960960961, + 39.3846153846154, + 39.4082982561636, + 39.4320096269555, + 39.4557495484648, + 39.4795180722892, + 39.5033152501507, + 39.5271411338963, + 39.5509957754979, + 39.5748792270531, + 39.5987915407855, + 39.6227327690447, + 39.6467029643073, + 39.6707021791768, + 39.694730466384, + 39.7187878787879, + 39.7428744693754, + 39.7669902912621, + 39.7911353976928, + 39.8153098420413, + 39.8395136778115, + 39.8637469586375, + 39.8880097382836, + 39.9123020706455, + 39.9366240097502, + 39.9609756097561, + 39.9853569249542, + 40.009768009768, + 40.0342089187538, + 40.0586797066015, + 40.0831804281346, + 40.1077111383109, + 40.1322718922229, + 40.156862745098, + 40.1814837522992, + 40.2061349693252, + 40.2308164518109, + 40.2555282555283, + 40.280270436386, + 40.3050430504305, + 40.3298461538462, + 40.3546798029557, + 40.3795440542206, + 40.4044389642417, + 40.4293645897594, + 40.4543209876543, + 40.4793082149475, + 40.504326328801, + 40.5293753865182, + 40.5544554455446, + 40.5795665634675, + 40.6047087980174, + 40.6298822070676, + 40.6550868486352, + 40.6803227808814, + 40.7055900621118, + 40.7308887507769, + 40.7562189054726, + 40.7815805849409, + 40.8069738480697, + 40.8323987538941, + 40.857855361596, + 40.8833437305053, + 40.9088639200999, + 40.9344159900062, + 40.96, + 40.9856160100063, + 41.0112640801001, + 41.0369442705072, + 41.062656641604, + 41.0884012539185, + 41.1141781681305, + 41.1399874450722, + 41.1658291457287, + 41.1917033312382, + 41.2176100628931, + 41.2435494021397, + 41.2695214105793, + 41.2955261499685, + 41.3215636822194, + 41.3476340694006, + 41.3737373737374, + 41.3998736576121, + 41.4260429835651, + 41.4522454142948, + 41.4784810126582, + 41.504749841672, + 41.531051964512, + 41.5573874445149, + 41.5837563451777, + 41.6101587301587, + 41.6365946632783, + 41.6630642085188, + 41.6895674300254, + 41.7161043921069, + 41.7426751592357, + 41.7692797960484, + 41.7959183673469, + 41.8225909380983, + 41.8492975734355, + 41.8760383386582, + 41.9028132992327, + 41.9296225207934, + 41.9564660691421, + 41.9833440102498, + 42.0102564102564, + 42.0372033354715, + 42.0641848523748, + 42.0912010276172, + 42.1182519280206, + 42.1453376205788, + 42.1724581724582, + 42.1996136509981, + 42.2268041237113, + 42.254029658285, + 42.2812903225807, + 42.3085861846352, + 42.3359173126615, + 42.3632837750485, + 42.3906856403622, + 42.4181229773463, + 42.4455958549223, + 42.4731043421905, + 42.5006485084306, + 42.5282284231019, + 42.5558441558442, + 42.5834957764782, + 42.6111833550065, + 42.6389069616135, + 42.6666666666667, + 42.6944625407166, + 42.722294654498, + 42.7501630789302, + 42.7780678851175, + 42.8060091443501, + 42.8339869281046, + 42.8620013080445, + 42.890052356021, + 42.9181401440733, + 42.9462647444299, + 42.9744262295082, + 43.002624671916, + 43.0308601444517, + 43.0591327201051, + 43.0874424720579, + 43.1157894736842, + 43.1441737985517, + 43.1725955204216, + 43.2010547132498, + 43.2295514511873, + 43.2580858085809, + 43.2866578599736, + 43.3152676801057, + 43.3439153439153, + 43.3726009265387, + 43.4013245033113, + 43.4300861497681, + 43.4588859416446, + 43.4877239548772, + 43.5166002656043, + 43.5455149501661, + 43.5744680851064, + 43.6034597471723, + 43.6324900133156, + 43.6615589606929, + 43.6906666666667, + 43.7198132088059, + 43.7489986648865, + 43.7782231128925, + 43.807486631016, + 43.8367892976589, + 43.8661311914324, + 43.8955123911587, + 43.9249329758713, + 43.9543930248156, + 43.9838926174497, + 44.0134318334453, + 44.0430107526882, + 44.0726294552791, + 44.1022880215343, + 44.1319865319865, + 44.1617250673855, + 44.1915037086986, + 44.221322537112, + 44.2511816340311, + 44.2810810810811, + 44.3110209601082, + 44.34100135318, + 44.3710223425863, + 44.4010840108401, + 44.431186440678, + 44.4613297150611, + 44.4915139171758, + 44.5217391304348, + 44.5520054384772, + 44.5823129251701, + 44.6126616746086, + 44.6430517711172, + 44.6734832992502, + 44.7039563437926, + 44.7344709897611, + 44.7650273224044, + 44.7956254272044, + 44.8262653898769, + 44.8569472963724, + 44.8876712328767, + 44.9184372858122, + 44.9492455418381, + 44.9800960878518, + 45.010989010989, + 45.0419243986254, + 45.0729023383769, + 45.1039229181005, + 45.1349862258953, + 45.1660923501034, + 45.1972413793103, + 45.2284334023465, + 45.2596685082873, + 45.2909467864547, + 45.3222683264177, + 45.3536332179931, + 45.3850415512465, + 45.4164934164934, + 45.4479889042996, + 45.4795281054823, + 45.5111111111111, + 45.5427380125087, + 45.5744089012517, + 45.6061238691719, + 45.6378830083566, + 45.6696864111498, + 45.7015341701534, + 45.7334263782275, + 45.7653631284916, + 45.7973445143256, + 45.8293706293706, + 45.8614415675297, + 45.8935574229692, + 45.9257182901191, + 45.9579242636746, + 45.9901754385965, + 46.0224719101124, + 46.0548137737175, + 46.0872011251758, + 46.1196340605208, + 46.1521126760563, + 46.184637068358, + 46.2172073342736, + 46.2498235709245, + 46.2824858757062, + 46.3151943462898, + 46.3479490806223, + 46.3807501769285, + 46.4135977337111, + 46.446491849752, + 46.4794326241135, + 46.5124201561391, + 46.5454545454545, + 46.5785358919687, + 46.6116642958748, + 46.6448398576513, + 46.6780626780627, + 46.7113328581611, + 46.7446504992867, + 46.7780157030692, + 46.8114285714286, + 46.8448892065761, + 46.8783977110157, + 46.9119541875447, + 46.945558739255, + 46.9792114695341, + 47.012912482066, + 47.0466618808327, + 47.0804597701149, + 47.1143062544932, + 47.1482014388489, + 47.1821454283657, + 47.2161383285303, + 47.2501802451334, + 47.2842712842713, + 47.3184115523466, + 47.3526011560694, + 47.3868402024584, + 47.4211287988423, + 47.4554670528602, + 47.4898550724638, + 47.5242929659173, + 47.5587808417997, + 47.5933188090051, + 47.6279069767442, + 47.6625454545455, + 47.6972343522562, + 47.7319737800437, + 47.7667638483965, + 47.8016046681255, + 47.836496350365, + 47.8714390065741, + 47.906432748538, + 47.9414776883687, + 47.9765739385066, + 48.0117216117216, + 48.0469208211144, + 48.0821716801174, + 48.1174743024963, + 48.1528288023512, + 48.1882352941176, + 48.2236938925681, + 48.259204712813, + 48.2947678703021, + 48.330383480826, + 48.3660516605166, + 48.4017725258493, + 48.4375461936438, + 48.4733727810651, + 48.5092524056255, + 48.5451851851852, + 48.581171237954, + 48.6172106824926, + 48.6533036377134, + 48.6894502228826, + 48.7256505576208, + 48.7619047619048, + 48.7982129560685, + 48.8345752608048, + 48.8709917971663, + 48.9074626865672, + 48.9439880507842, + 48.9805680119582, + 49.0172026925954, + 49.0538922155689, + 49.0906367041199, + 49.1274362818591, + 49.1642910727682, + 49.2012012012012, + 49.2381667918858, + 49.2751879699248, + 49.3122648607976, + 49.3493975903615, + 49.3865862848531, + 49.4238310708899, + 49.4611320754717, + 49.4984894259819, + 49.535903250189, + 49.5733736762481, + 49.6109008327025, + 49.6484848484849, + 49.6861258529189, + 49.7238239757208, + 49.7615793470008, + 49.7993920972644, + 49.8372623574145, + 49.8751902587519, + 49.9131759329779, + 49.9512195121951, + 49.9893211289092, + 50.0274809160305, + 50.0656990068755, + 50.1039755351682, + 50.1423106350421, + 50.1807044410413, + 50.2191570881226, + 50.2576687116564, + 50.296239447429, + 50.3348694316436, + 50.3735588009224, + 50.4123076923077, + 50.451116243264, + 50.4899845916795, + 50.5289128758674, + 50.5679012345679, + 50.6069498069498, + 50.6460587326121, + 50.6852281515855, + 50.7244582043344, + 50.7637490317583, + 50.8031007751938, + 50.8425135764158, + 50.8819875776397, + 50.9215229215229, + 50.9611197511664, + 51.0007782101167, + 51.0404984423676, + 51.0802805923617, + 51.1201248049922, + 51.160031225605, + 51.2, + 51.2400312744331, + 51.2801251956182, + 51.3202819107283, + 51.3605015673981, + 51.4007843137255, + 51.4411302982732, + 51.4815396700707, + 51.5220125786164, + 51.5625491738788, + 51.6031496062992, + 51.6438140267928, + 51.6845425867508, + 51.7253354380426, + 51.7661927330174, + 51.8071146245059, + 51.8481012658228, + 51.889152810768, + 51.9302694136292, + 51.9714512291832, + 52.0126984126984, + 52.0540111199365, + 52.0953895071542, + 52.1368337311058, + 52.1783439490446, + 52.2199203187251, + 52.2615629984051, + 52.3032721468476, + 52.3450479233227, + 52.3868904876099, + 52.4288, + 52.470776621297, + 52.5128205128205, + 52.5549318364074, + 52.5971107544141, + 52.6393574297189, + 52.6816720257235, + 52.7240547063556, + 52.7665056360709, + 52.809024979855, + 52.8516129032258, + 52.8942695722357, + 52.9369951534734, + 52.9797898140663, + 53.0226537216829, + 53.0655870445344, + 53.1085899513776, + 53.1516626115166, + 53.1948051948052, + 53.2380178716491, + 53.2813008130081, + 53.3246541903987, + 53.3680781758958, + 53.4115729421353, + 53.4551386623165, + 53.4987755102041, + 53.5424836601307, + 53.5862632869992, + 53.6301145662848, + 53.6740376740377, + 53.7180327868852, + 53.7621000820345, + 53.8062397372742, + 53.8504519309778, + 53.8947368421053, + 53.9390946502058, + 53.9835255354201, + 54.0280296784831, + 54.0726072607261, + 54.1172584640793, + 54.1619834710744, + 54.206782464847, + 54.2516556291391, + 54.2966031483016, + 54.3416252072969, + 54.3867219917012, + 54.4318936877076, + 54.477140482128, + 54.522462562396, + 54.5678601165695, + 54.6133333333333, + 54.6588824020017, + 54.7045075125209, + 54.750208855472, + 54.7959866220736, + 54.8418410041841, + 54.8877721943049, + 54.9337803855826, + 54.9798657718121, + 55.0260285474391, + 55.072268907563, + 55.1185870479394, + 55.1649831649832, + 55.2114574557709, + 55.2580101180438, + 55.304641350211, + 55.3513513513514, + 55.3981403212172, + 55.4450084602369, + 55.4919559695174, + 55.5389830508475, + 55.5860899067006, + 55.6332767402377, + 55.6805437553101, + 55.7278911564626, + 55.7753191489362, + 55.8228279386712, + 55.8704177323103, + 55.9180887372014, + 55.9658411614005, + 56.0136752136752, + 56.0615911035073, + 56.1095890410959, + 56.1576692373608, + 56.2058319039451, + 56.2540772532189, + 56.3024054982818, + 56.3508168529665, + 56.3993115318417, + 56.4478897502153, + 56.4965517241379, + 56.5452976704055, + 56.594127806563, + 56.6430423509075, + 56.6920415224914, + 56.7411255411255, + 56.790294627383, + 56.8395490026019, + 56.8888888888889, + 56.9383145091225, + 56.9878260869565, + 57.0374238468233, + 57.0871080139373, + 57.1368788142982, + 57.1867364746946, + 57.2366812227074, + 57.2867132867133, + 57.336832895888, + 57.3870402802102, + 57.4373356704645, + 57.4877192982456, + 57.5381913959614, + 57.5887521968366, + 57.6394019349165, + 57.6901408450704, + 57.7409691629956, + 57.7918871252205, + 57.8428949691086, + 57.8939929328622, + 57.9451812555261, + 57.9964601769912, + 58.0478299379982, + 58.0992907801418, + 58.150842945874, + 58.202486678508, + 58.2542222222222, + 58.3060498220641, + 58.3579697239537, + 58.4099821746881, + 58.4620874219447, + 58.5142857142857, + 58.5665773011618, + 58.6189624329159, + 58.6714413607878, + 58.7240143369176, + 58.7766816143498, + 58.8294434470377, + 58.8823000898473, + 58.9352517985612, + 58.988298829883, + 59.0414414414414, + 59.0946798917944, + 59.1480144404332, + 59.2014453477868, + 59.254972875226, + 59.3085972850679, + 59.3623188405797, + 59.4161378059837, + 59.470054446461, + 59.5240690281562, + 59.5781818181818, + 59.6323930846224, + 59.6867030965392, + 59.7411121239745, + 59.7956204379562, + 59.8502283105023, + 59.9049360146252, + 59.9597438243367, + 60.014652014652, + 60.0696608615949, + 60.1247706422018, + 60.1799816345271, + 60.2352941176471, + 60.2907083716651, + 60.3462246777164, + 60.4018433179724, + 60.4575645756458, + 60.5133887349954, + 60.5693160813309, + 60.6253469010176, + 60.6814814814815, + 60.7377201112141, + 60.7940630797774, + 60.8505106778087, + 60.907063197026, + 60.9637209302326, + 61.0204841713222, + 61.0773532152843, + 61.134328358209, + 61.1914098972923, + 61.2485981308411, + 61.3058933582788, + 61.3632958801498, + 61.4208059981256, + 61.4784240150094, + 61.5361502347418, + 61.593984962406, + 61.6519285042333, + 61.7099811676083, + 61.7681432610745, + 61.8264150943396, + 61.8847969782814, + 61.9432892249527, + 62.0018921475875, + 62.0606060606061, + 62.1194312796209, + 62.1783681214421, + 62.2374169040836, + 62.2965779467681, + 62.3558515699334, + 62.4152380952381, + 62.4747378455672, + 62.5343511450382, + 62.5940783190067, + 62.6539196940727, + 62.7138755980861, + 62.7739463601533, + 62.8341323106424, + 62.89443378119, + 62.954851104707, + 63.0153846153846, + 63.0760346487007, + 63.1368015414258, + 63.1976856316297, + 63.2586872586873, + 63.319806763285, + 63.3810444874275, + 63.4424007744434, + 63.5038759689923, + 63.5654704170708, + 63.6271844660194, + 63.6890184645287, + 63.7509727626459, + 63.8130477117819, + 63.8752436647174, + 63.9375609756098, + 64, + 64.0625610948192, + 64.1252446183953, + 64.1880509304603, + 64.2509803921569, + 64.3140333660451, + 64.37721021611, + 64.440511307768, + 64.503937007874, + 64.5674876847291, + 64.6311637080868, + 64.6949654491609, + 64.7588932806324, + 64.8229475766568, + 64.8871287128713, + 64.9514370664024, + 65.015873015873, + 65.0804369414101, + 65.1451292246521, + 65.2099502487562, + 65.2749003984064, + 65.3399800598205, + 65.4051896207585, + 65.4705294705295, + 65.536, + 65.6016016016016, + 65.6673346693387, + 65.7331995987964, + 65.7991967871486, + 65.8653266331658, + 65.9315895372234, + 65.9979859013092, + 66.0645161290323, + 66.1311806256307, + 66.1979797979798, + 66.2649140546006, + 66.331983805668, + 66.3991894630193, + 66.4665314401623, + 66.5340101522843, + 66.6016260162602, + 66.6693794506612, + 66.7372708757637, + 66.8053007135576, + 66.8734693877551, + 66.9417773237998, + 67.0102249488753, + 67.078812691914, + 67.1475409836066, + 67.2164102564103, + 67.2854209445585, + 67.3545734840699, + 67.4238683127572, + 67.4933058702369, + 67.5628865979382, + 67.6326109391125, + 67.702479338843, + 67.7724922440538, + 67.8426501035197, + 67.9129533678757, + 67.9834024896266, + 68.0539979231568, + 68.1247401247401, + 68.1956295525494, + 68.2666666666667, + 68.3378519290928, + 68.4091858037578, + 68.4806687565308, + 68.5523012552301, + 68.6240837696335, + 68.6960167714885, + 68.7681007345226, + 68.8403361344538, + 68.9127234490011, + 68.9852631578947, + 69.0579557428872, + 69.1308016877637, + 69.2038014783527, + 69.276955602537, + 69.3502645502646, + 69.4237288135593, + 69.4973488865324, + 69.5711252653928, + 69.6450584484591, + 69.7191489361702, + 69.7933972310969, + 69.8678038379531, + 69.9423692636073, + 70.017094017094, + 70.0919786096257, + 70.1670235546039, + 70.2422293676313, + 70.3175965665236, + 70.3931256713212, + 70.4688172043011, + 70.5446716899892, + 70.6206896551724, + 70.6968716289105, + 70.7732181425486, + 70.8497297297297, + 70.9264069264069, + 71.0032502708559, + 71.0802603036876, + 71.157437567861, + 71.2347826086957, + 71.3122959738847, + 71.3899782135076, + 71.4678298800436, + 71.5458515283843, + 71.624043715847, + 71.7024070021882, + 71.7809419496166, + 71.859649122807, + 71.9385290889133, + 72.0175824175824, + 72.0968096809681, + 72.1762114537445, + 72.2557883131202, + 72.3355408388521, + 72.4154696132597, + 72.4955752212389, + 72.5758582502769, + 72.6563192904656, + 72.7369589345172, + 72.8177777777778, + 72.8987764182425, + 72.9799554565702, + 73.0613154960981, + 73.1428571428571, + 73.2245810055866, + 73.3064876957494, + 73.3885778275476, + 73.4708520179372, + 73.5533108866442, + 73.6359550561798, + 73.718785151856, + 73.8018018018018, + 73.8850056369786, + 73.9683972911964, + 74.0519774011299, + 74.1357466063348, + 74.2197055492639, + 74.3038548752835, + 74.3881952326901, + 74.4727272727273, + 74.5574516496018, + 74.6423690205011, + 74.72748004561, + 74.8127853881279, + 74.8982857142857, + 74.9839816933639, + 75.0698739977091, + 75.1559633027523, + 75.2422502870264, + 75.3287356321839, + 75.415420023015, + 75.5023041474654, + 75.5893886966551, + 75.6766743648961, + 75.764161849711, + 75.8518518518518, + 75.9397450753187, + 76.0278422273782, + 76.116144018583, + 76.2046511627907, + 76.2933643771828, + 76.3822843822844, + 76.4714119019837, + 76.5607476635514, + 76.6502923976608, + 76.7400468384075, + 76.8300117233294, + 76.9201877934272, + 77.0105757931845, + 77.1011764705882, + 77.1919905771496, + 77.2830188679245, + 77.3742621015348, + 77.4657210401891, + 77.5573964497041, + 77.6492890995261, + 77.7413997627521, + 77.833729216152, + 77.9262782401902, + 78.0190476190476, + 78.1120381406436, + 78.2052505966587, + 78.2986857825568, + 78.3923444976077, + 78.4862275449102, + 78.5803357314149, + 78.6746698679472, + 78.7692307692308, + 78.864019253911, + 78.9590361445783, + 79.0542822677925, + 79.1497584541063, + 79.2454655380895, + 79.3414043583535, + 79.4375757575758, + 79.5339805825243, + 79.6306196840826, + 79.7274939172749, + 79.8246041412911, + 79.9219512195122, + 80.019536019536, + 80.1173594132029, + 80.2154222766218, + 80.3137254901961, + 80.4122699386503, + 80.5110565110565, + 80.610086100861, + 80.7093596059113, + 80.8088779284834, + 80.9086419753086, + 81.008652657602, + 81.1089108910891, + 81.2094175960347, + 81.3101736972705, + 81.4111801242236, + 81.5124378109453, + 81.6139476961395, + 81.715710723192, + 81.8177278401998, + 81.92, + 82.0225281602002, + 82.125313283208, + 82.228356336261, + 82.3316582914573, + 82.4352201257862, + 82.5390428211587, + 82.6431273644388, + 82.7474747474748, + 82.8520859671302, + 82.9569620253165, + 83.0621039290241, + 83.1675126903553, + 83.2731893265565, + 83.3791348600509, + 83.4853503184713, + 83.5918367346939, + 83.698595146871, + 83.8056265984655, + 83.9129321382842, + 84.0205128205128, + 84.1283697047497, + 84.2365038560411, + 84.3449163449163, + 84.4536082474227, + 84.5625806451613, + 84.671834625323, + 84.7813712807245, + 84.8911917098446, + 85.0012970168612, + 85.1116883116883, + 85.222366710013, + 85.3333333333333, + 85.4445893089961, + 85.556135770235, + 85.6679738562091, + 85.7801047120419, + 85.8925294888598, + 86.005249343832, + 86.1182654402102, + 86.2315789473684, + 86.3451910408432, + 86.4591029023747, + 86.5733157199472, + 86.6878306878307, + 86.8026490066225, + 86.9177718832891, + 87.0332005312085, + 87.1489361702128, + 87.2649800266312, + 87.3813333333333, + 87.497997329773, + 87.6149732620321, + 87.7322623828648, + 87.8498659517426, + 87.9677852348993, + 88.0860215053763, + 88.2045760430686, + 88.3234501347709, + 88.442645074224, + 88.5621621621622, + 88.68200270636, + 88.8021680216802, + 88.9226594301221, + 89.0434782608696, + 89.1646258503401, + 89.2861035422343, + 89.4079126875853, + 89.5300546448087, + 89.6525307797538, + 89.7753424657534, + 89.8984910836763, + 90.021978021978, + 90.1458046767538, + 90.2699724517906, + 90.3944827586207, + 90.5193370165746, + 90.6445366528354, + 90.7700831024931, + 90.8959778085992, + 91.0222222222222, + 91.1488178025035, + 91.2757660167131, + 91.4030683403068, + 91.5307262569833, + 91.6587412587413, + 91.7871148459384, + 91.9158485273492, + 92.0449438202247, + 92.1744022503516, + 92.3042253521127, + 92.4344146685472, + 92.5649717514124, + 92.6958981612447, + 92.8271954674221, + 92.958865248227, + 93.0909090909091, + 93.2233285917497, + 93.3561253561254, + 93.4893009985735, + 93.6228571428572, + 93.7567954220315, + 93.89111747851, + 94.025824964132, + 94.1609195402299, + 94.2964028776978, + 94.4322766570605, + 94.5685425685426, + 94.7052023121387, + 94.8422575976845, + 94.9797101449275, + 95.1175616835994, + 95.2558139534884, + 95.3944687045124, + 95.533527696793, + 95.6729927007299, + 95.812865497076, + 95.9531478770132, + 96.0938416422287, + 96.2349486049927, + 96.3764705882353, + 96.5184094256259, + 96.6607669616519, + 96.8035450516987, + 96.9467455621302, + 97.0903703703704, + 97.2344213649852, + 97.3789004457652, + 97.5238095238095, + 97.6691505216095, + 97.8149253731343, + 97.9611360239163, + 98.1077844311377, + 98.2548725637181, + 98.4024024024024, + 98.5503759398496, + 98.6987951807229, + 98.8476621417798, + 98.9969788519637, + 99.1467473524962, + 99.2969696969697, + 99.4476479514416, + 99.5987841945289, + 99.7503805175038, + 99.9024390243902, + 100.054961832061, + 100.207951070336, + 100.361408882083, + 100.515337423313, + 100.669738863287, + 100.824615384615, + 100.979969183359, + 101.135802469136, + 101.292117465224, + 101.448916408669, + 101.606201550388, + 101.76397515528, + 101.922239502333, + 102.080996884735, + 102.240249609984, + 102.4, + 102.560250391236, + 102.721003134796, + 102.882260596546, + 103.044025157233, + 103.206299212598, + 103.369085173502, + 103.532385466035, + 103.696202531646, + 103.860538827258, + 104.025396825397, + 104.190779014308, + 104.356687898089, + 104.52312599681, + 104.690095846645, + 104.8576, + 105.025641025641, + 105.194221508828, + 105.363344051447, + 105.533011272142, + 105.703225806452, + 105.873990306947, + 106.045307443366, + 106.217179902755, + 106.38961038961, + 106.562601626016, + 106.736156351792, + 106.910277324633, + 107.084967320261, + 107.26022913257, + 107.436065573771, + 107.612479474548, + 107.789473684211, + 107.96705107084, + 108.145214521452, + 108.323966942149, + 108.503311258278, + 108.683250414594, + 108.863787375415, + 109.044925124792, + 109.226666666667, + 109.409015025042, + 109.591973244147, + 109.77554438861, + 109.959731543624, + 110.144537815126, + 110.329966329966, + 110.516020236088, + 110.702702702703, + 110.890016920474, + 111.077966101695, + 111.266553480475, + 111.455782312925, + 111.645655877342, + 111.836177474403, + 112.02735042735, + 112.219178082192, + 112.41166380789, + 112.604810996564, + 112.798623063683, + 112.993103448276, + 113.188255613126, + 113.384083044983, + 113.580589254766, + 113.777777777778, + 113.975652173913, + 114.174216027875, + 114.373472949389, + 114.573426573427, + 114.77408056042, + 114.975438596491, + 115.177504393673, + 115.380281690141, + 115.583774250441, + 115.787985865724, + 115.992920353982, + 116.198581560284, + 116.404973357016, + 116.612099644128, + 116.819964349376, + 117.028571428571, + 117.237924865832, + 117.448028673835, + 117.658886894075, + 117.870503597122, + 118.082882882883, + 118.296028880866, + 118.509945750452, + 118.724637681159, + 118.940108892922, + 119.156363636364, + 119.373406193078, + 119.591240875912, + 119.80987202925, + 120.029304029304, + 120.249541284404, + 120.470588235294, + 120.692449355433, + 120.915129151292, + 121.138632162662, + 121.362962962963, + 121.588126159555, + 121.814126394052, + 122.040968342644, + 122.268656716418, + 122.497196261682, + 122.7265917603, + 122.956848030019, + 123.187969924812, + 123.419962335217, + 123.652830188679, + 123.886578449906, + 124.121212121212, + 124.356736242884, + 124.593155893536, + 124.830476190476, + 125.068702290076, + 125.307839388145, + 125.547892720307, + 125.78886756238, + 126.030769230769, + 126.273603082852, + 126.517374517375, + 126.762088974855, + 127.007751937985, + 127.254368932039, + 127.501945525292, + 127.750487329435, + 128, + 128.250489236791, + 128.501960784314, + 128.75442043222, + 129.007874015748, + 129.262327416174, + 129.517786561265, + 129.774257425743, + 130.031746031746, + 130.290258449304, + 130.549800796813, + 130.810379241517, + 131.072, + 131.334669338677, + 131.598393574297, + 131.863179074447, + 132.129032258065, + 132.39595959596, + 132.663967611336, + 132.933062880325, + 133.20325203252, + 133.474541751527, + 133.74693877551, + 134.020449897751, + 134.295081967213, + 134.570841889117, + 134.847736625514, + 135.125773195876, + 135.404958677686, + 135.685300207039, + 135.966804979253, + 136.24948024948, + 136.533333333333, + 136.818371607516, + 137.10460251046, + 137.392033542977, + 137.680672268908, + 137.970526315789, + 138.261603375527, + 138.553911205074, + 138.847457627119, + 139.142250530786, + 139.43829787234, + 139.735607675906, + 140.034188034188, + 140.334047109208, + 140.635193133047, + 140.937634408602, + 141.241379310345, + 141.546436285097, + 141.852813852814, + 142.160520607375, + 142.469565217391, + 142.779956427015, + 143.091703056769, + 143.404814004376, + 143.719298245614, + 144.035164835165, + 144.352422907489, + 144.671081677704, + 144.991150442478, + 145.312638580931, + 145.635555555556, + 145.95991091314, + 146.285714285714, + 146.612975391499, + 146.941704035874, + 147.27191011236, + 147.603603603604, + 147.936794582393, + 148.27149321267, + 148.607709750567, + 148.945454545455, + 149.284738041002, + 149.625570776256, + 149.967963386728, + 150.311926605505, + 150.657471264368, + 151.004608294931, + 151.353348729792, + 151.703703703704, + 152.055684454756, + 152.409302325581, + 152.764568764569, + 153.121495327103, + 153.480093676815, + 153.840375586854, + 154.202352941176, + 154.566037735849, + 154.931442080378, + 155.298578199052, + 155.667458432304, + 156.038095238095, + 156.410501193317, + 156.784688995215, + 157.16067146283, + 157.538461538462, + 157.918072289157, + 158.299516908213, + 158.682808716707, + 159.067961165049, + 159.45498783455, + 159.843902439024, + 160.234718826406, + 160.627450980392, + 161.022113022113, + 161.418719211823, + 161.817283950617, + 162.217821782178, + 162.620347394541, + 163.024875621891, + 163.431421446384, + 163.84, + 164.250626566416, + 164.663316582915, + 165.078085642317, + 165.49494949495, + 165.913924050633, + 166.335025380711, + 166.758269720102, + 167.183673469388, + 167.611253196931, + 168.041025641026, + 168.473007712082, + 168.907216494845, + 169.343669250646, + 169.782383419689, + 170.223376623377, + 170.666666666667, + 171.11227154047, + 171.560209424084, + 172.010498687664, + 172.463157894737, + 172.918205804749, + 173.375661375661, + 173.835543766578, + 174.297872340426, + 174.762666666667, + 175.229946524064, + 175.699731903485, + 176.172043010753, + 176.646900269542, + 177.124324324324, + 177.60433604336, + 178.086956521739, + 178.572207084469, + 179.060109289618, + 179.550684931507, + 180.043956043956, + 180.539944903581, + 181.038674033149, + 181.540166204986, + 182.044444444444, + 182.551532033426, + 183.061452513966, + 183.574229691877, + 184.089887640449, + 184.608450704225, + 185.129943502825, + 185.654390934844, + 186.181818181818, + 186.712250712251, + 187.245714285714, + 187.78223495702, + 188.32183908046, + 188.864553314121, + 189.410404624277, + 189.959420289855, + 190.511627906977, + 191.067055393586, + 191.625730994152, + 192.187683284457, + 192.752941176471, + 193.321533923304, + 193.89349112426, + 194.46884272997, + 195.047619047619, + 195.629850746269, + 196.215568862275, + 196.804804804805, + 197.397590361446, + 197.993957703928, + 198.593939393939, + 199.197568389058, + 199.804878048781, + 200.415902140673, + 201.030674846626, + 201.649230769231, + 202.271604938272, + 202.897832817337, + 203.527950310559, + 204.16199376947, + 204.8, + 205.442006269592, + 206.088050314465, + 206.738170347003, + 207.392405063291, + 208.050793650794, + 208.713375796178, + 209.380191693291, + 210.051282051282, + 210.726688102894, + 211.406451612903, + 212.090614886731, + 212.779220779221, + 213.472312703583, + 214.169934640523, + 214.872131147541, + 215.578947368421, + 216.290429042904, + 217.006622516556, + 217.727574750831, + 218.453333333333, + 219.183946488294, + 219.919463087248, + 220.659932659933, + 221.405405405405, + 222.15593220339, + 222.91156462585, + 223.672354948805, + 224.438356164384, + 225.209621993127, + 225.986206896552, + 226.768166089965, + 227.555555555556, + 228.348432055749, + 229.146853146853, + 229.950877192982, + 230.760563380282, + 231.575971731449, + 232.397163120567, + 233.224199288256, + 234.057142857143, + 234.89605734767, + 235.741007194245, + 236.592057761733, + 237.449275362319, + 238.312727272727, + 239.182481751825, + 240.058608058608, + 240.941176470588, + 241.830258302583, + 242.725925925926, + 243.628252788104, + 244.537313432836, + 245.453183520599, + 246.375939849624, + 247.305660377358, + 248.242424242424, + 249.186311787072, + 250.137404580153, + 251.095785440613, + 252.061538461538, + 253.034749034749, + 254.015503875969, + 255.003891050584, + 256, + 257.003921568627, + 258.015748031496, + 259.03557312253, + 260.063492063492, + 261.099601593625, + 262.144, + 263.196787148594, + 264.258064516129, + 265.327935222672, + 266.406504065041, + 267.49387755102, + 268.590163934426, + 269.695473251029, + 270.809917355372, + 271.933609958506, + 273.066666666667, + 274.20920502092, + 275.361344537815, + 276.523206751055, + 277.694915254237, + 278.876595744681, + 280.068376068376, + 281.270386266094, + 282.48275862069, + 283.705627705628, + 284.939130434783, + 286.183406113537, + 287.438596491228, + 288.704845814978, + 289.982300884956, + 291.271111111111, + 292.571428571429, + 293.883408071749, + 295.207207207207, + 296.542986425339, + 297.890909090909, + 299.251141552511, + 300.623853211009, + 302.009216589862, + 303.407407407407, + 304.818604651163, + 306.242990654206, + 307.680751173709, + 309.132075471698, + 310.597156398104, + 312.07619047619, + 313.569377990431, + 315.076923076923, + 316.599033816425, + 318.135922330097, + 319.687804878049, + 321.254901960784, + 322.837438423645, + 324.435643564356, + 326.049751243781, + 327.68, + 329.326633165829, + 330.989898989899, + 332.670050761421, + 334.367346938775, + 336.082051282051, + 337.814432989691, + 339.564766839378, + 341.333333333333, + 343.120418848168, + 344.926315789474, + 346.751322751323, + 348.595744680851, + 350.459893048128, + 352.344086021505, + 354.248648648649, + 356.173913043478, + 358.120218579235, + 360.087912087912, + 362.077348066298, + 364.088888888889, + 366.122905027933, + 368.179775280899, + 370.25988700565, + 372.363636363636, + 374.491428571429, + 376.64367816092, + 378.820809248555, + 381.023255813953, + 383.251461988304, + 385.505882352941, + 387.786982248521, + 390.095238095238, + 392.431137724551, + 394.795180722892, + 397.187878787879, + 399.609756097561, + 402.061349693252, + 404.543209876543, + 407.055900621118, + 409.6, + 412.176100628931, + 414.784810126582, + 417.426751592357, + 420.102564102564, + 422.812903225807, + 425.558441558442, + 428.339869281046, + 431.157894736842, + 434.013245033113, + 436.906666666667, + 439.838926174497, + 442.810810810811, + 445.823129251701, + 448.876712328767, + 451.972413793103, + 455.111111111111, + 458.293706293706, + 461.521126760563, + 464.794326241135, + 468.114285714286, + 471.482014388489, + 474.898550724638, + 478.36496350365, + 481.882352941176, + 485.451851851852, + 489.074626865672, + 492.751879699248, + 496.484848484849, + 500.274809160305, + 504.123076923077, + 508.031007751938, + 512, + 516.031496062992, + 520.126984126984, + 524.288, + 528.516129032258, + 532.813008130081, + 537.180327868852, + 541.619834710744, + 546.133333333333, + 550.72268907563, + 555.389830508475, + 560.136752136752, + 564.965517241379, + 569.878260869565, + 574.877192982456, + 579.964601769912, + 585.142857142857, + 590.414414414414, + 595.781818181818, + 601.247706422018, + 606.814814814815, + 612.485981308411, + 618.264150943396, + 624.152380952381, + 630.153846153846, + 636.271844660194, + 642.509803921569, + 648.871287128713, + 655.36, + 661.979797979798, + 668.734693877551, + 675.628865979381, + 682.666666666667, + 689.852631578947, + 697.191489361702, + 704.688172043011, + 712.347826086956, + 720.175824175824, + 728.177777777778, + 736.359550561798, + 744.727272727273, + 753.287356321839, + 762.046511627907, + 771.011764705882, + 780.190476190476, + 789.590361445783, + 799.219512195122, + 809.086419753086, + 819.2, + 829.569620253165, + 840.205128205128, + 851.116883116883, + 862.315789473684, + 873.813333333333, + 885.621621621622, + 897.753424657534, + 910.222222222222, + 923.042253521127, + 936.228571428571, + 949.797101449275, + 963.764705882353, + 978.149253731343, + 992.969696969697, + 1008.24615384615, + 1024, + 1040.25396825397, + 1057.03225806452, + 1074.36065573771, + 1092.26666666667, + 1110.77966101695, + 1129.93103448276, + 1149.75438596491, + 1170.28571428571, + 1191.56363636364, + 1213.62962962963, + 1236.52830188679, + 1260.30769230769, + 1285.01960784314, + 1310.72, + 1337.4693877551, + 1365.33333333333, + 1394.3829787234, + 1424.69565217391, + 1456.35555555556, + 1489.45454545455, + 1524.09302325581, + 1560.38095238095, + 1598.43902439024, + 1638.4, + 1680.41025641026, + 1724.63157894737, + 1771.24324324324, + 1820.44444444444, + 1872.45714285714, + 1927.52941176471, + 1985.93939393939, + 2048, + 2114.06451612903, + 2184.53333333333, + 2259.86206896552, + 2340.57142857143, + 2427.25925925926, + 2520.61538461538, + 2621.44, + 2730.66666666667, + 2849.39130434783, + 2978.90909090909, + 3120.7619047619, + 3276.8, + 3449.26315789474, + 3640.88888888889, + 3855.05882352941, + 4096, + 4369.06666666667, + 4681.14285714286, + 5041.23076923077, + 5461.33333333333, + 5957.81818181818, + 6553.6, + 7281.77777777778, + 8192, + 9362.28571428571, + 10922.6666666667, + 13107.2, + 16384, + 21845.3333333333, + 32768, + 65536 +}; + +const float freqTableNSE[256] = { + 524288, + 262144, + 131072, + 87381.3333333333, + 65536, + 52428.8, + 43690.6666666667, + 37449.1428571429, + 524288, + 262144, + 131072, + 87381.3333333333, + 65536, + 52428.8, + 43690.6666666667, + 37449.1428571429, + 262144, + 131072, + 65536, + 43690.6666666667, + 32768, + 26214.4, + 21845.3333333333, + 18724.5714285714, + 262144, + 131072, + 65536, + 43690.6666666667, + 32768, + 26214.4, + 21845.3333333333, + 18724.5714285714, + 131072, + 65536, + 32768, + 21845.3333333333, + 16384, + 13107.2, + 10922.6666666667, + 9362.28571428571, + 131072, + 65536, + 32768, + 21845.3333333333, + 16384, + 13107.2, + 10922.6666666667, + 9362.28571428571, + 65536, + 32768, + 16384, + 10922.6666666667, + 8192, + 6553.6, + 5461.33333333333, + 4681.14285714286, + 65536, + 32768, + 16384, + 10922.6666666667, + 8192, + 6553.6, + 5461.33333333333, + 4681.14285714286, + 32768, + 16384, + 8192, + 5461.33333333333, + 4096, + 3276.8, + 2730.66666666667, + 2340.57142857143, + 32768, + 16384, + 8192, + 5461.33333333333, + 4096, + 3276.8, + 2730.66666666667, + 2340.57142857143, + 16384, + 8192, + 4096, + 2730.66666666667, + 2048, + 1638.4, + 1365.33333333333, + 1170.28571428571, + 16384, + 8192, + 4096, + 2730.66666666667, + 2048, + 1638.4, + 1365.33333333333, + 1170.28571428571, + 8192, + 4096, + 2048, + 1365.33333333333, + 1024, + 819.2, + 682.666666666667, + 585.142857142857, + 8192, + 4096, + 2048, + 1365.33333333333, + 1024, + 819.2, + 682.666666666667, + 585.142857142857, + 4096, + 2048, + 1024, + 682.666666666667, + 512, + 409.6, + 341.333333333333, + 292.571428571429, + 4096, + 2048, + 1024, + 682.666666666667, + 512, + 409.6, + 341.333333333333, + 292.571428571429, + 2048, + 1024, + 512, + 341.333333333333, + 256, + 204.8, + 170.666666666667, + 146.285714285714, + 2048, + 1024, + 512, + 341.333333333333, + 256, + 204.8, + 170.666666666667, + 146.285714285714, + 1024, + 512, + 256, + 170.666666666667, + 128, + 102.4, + 85.3333333333333, + 73.1428571428571, + 1024, + 512, + 256, + 170.666666666667, + 128, + 102.4, + 85.3333333333333, + 73.1428571428571, + 512, + 256, + 128, + 85.3333333333333, + 64, + 51.2, + 42.6666666666667, + 36.5714285714286, + 512, + 256, + 128, + 85.3333333333333, + 64, + 51.2, + 42.6666666666667, + 36.5714285714286, + 256, + 128, + 64, + 42.6666666666667, + 32, + 25.6, + 21.3333333333333, + 18.2857142857143, + 256, + 128, + 64, + 42.6666666666667, + 32, + 25.6, + 21.3333333333333, + 18.2857142857143, + 128, + 64, + 32, + 21.3333333333333, + 16, + 12.8, + 10.6666666666667, + 9.14285714285714, + 128, + 64, + 32, + 21.3333333333333, + 16, + 12.8, + 10.6666666666667, + 9.14285714285714, + 64, + 32, + 16, + 10.6666666666667, + 8, + 6.4, + 5.33333333333333, + 4.57142857142857, + 64, + 32, + 16, + 10.6666666666667, + 8, + 6.4, + 5.33333333333333, + 4.57142857142857, + 32, + 16, + 8, + 5.33333333333333, + 4, + 3.2, + 2.66666666666667, + 2.28571428571429, + 32, + 16, + 8, + 5.33333333333333, + 4, + 3.2, + 2.66666666666667, + 2.28571428571429, + 16, + 8, + 4, + 2.66666666666667, + 2, + 1.6, + 1.33333333333333, + 1.14285714285714, + 16, + 8, + 4, + 2.66666666666667, + 2, + 1.6, + 1.33333333333333, + 1.14285714285714, +}; + +#endif diff --git a/include/gba/defines.h b/include/gba/defines.h index f5cbfd6b9..984da6740 100644 --- a/include/gba/defines.h +++ b/include/gba/defines.h @@ -24,14 +24,15 @@ extern void * INTR_VECTOR; #define OBJ_PLTT_SIZE 0x200 #define PLTT_SIZE (BG_PLTT_SIZE + OBJ_PLTT_SIZE) -extern u8 PLTT[PLTT_SIZE]; +extern u8 PLTT[PLTT_SIZE] __attribute__ ((aligned (4))); #define BG_PLTT PLTT #define OBJ_PLTT (PLTT + BG_PLTT_SIZE) #define VRAM_SIZE 0x18000 -extern u32 VRAM[VRAM_SIZE / sizeof(u32)]; +extern u8 VRAM_[VRAM_SIZE] __attribute__ ((aligned (4))); +#define VRAM (u32)VRAM_ #define BG_VRAM VRAM #define BG_VRAM_SIZE 0x10000 @@ -54,7 +55,7 @@ extern u32 VRAM[VRAM_SIZE / sizeof(u32)]; #define OBJ_VRAM1_SIZE 0x4000 #define OAM_SIZE 0x400 -extern u8 OAM[OAM_SIZE]; +extern u8 OAM[OAM_SIZE] __attribute__ ((aligned (4))); #define ROM_HEADER_SIZE 0xC0 diff --git a/include/gba/flash_internal.h b/include/gba/flash_internal.h index 09ceb5c53..dbdc7b452 100644 --- a/include/gba/flash_internal.h +++ b/include/gba/flash_internal.h @@ -2,7 +2,7 @@ #define GUARD_GBA_FLASH_INTERNAL_H #define FLASH_ROM_SIZE_1M 131072 // 1 megabit ROM -extern u8 FLASH_BASE[FLASH_ROM_SIZE_1M]; +extern u8 FLASH_BASE[FLASH_ROM_SIZE_1M] __attribute__ ((aligned (4))); #define FLASH_WRITE(addr, data) ((*(vu8 *)(FLASH_BASE + (addr))) = (data)) @@ -44,11 +44,11 @@ struct FlashSetupInfo extern u16 gFlashNumRemainingBytes; -extern u16 (*ProgramFlashByte)(u16, u32, u8); -extern u16 (*ProgramFlashSector)(u16, void *); -extern u16 (*EraseFlashChip)(void); -extern u16 (*EraseFlashSector)(u16); -extern u16 (*WaitForFlashWrite)(u8, u8 *, u8); +extern u16 ProgramFlashByte(u16, u32, u8); +extern u16 ProgramFlashSector(u16, void *); +extern u16 EraseFlashChip(void); +extern u16 EraseFlashSector(u16); +extern u16 WaitForFlashWrite(u8, u8 *, u8); extern const u16 *gFlashMaxTime; extern const struct FlashType *gFlash; @@ -60,7 +60,6 @@ extern const struct FlashSetupInfo LE26FV10N1TS; extern const struct FlashSetupInfo DefaultFlash; void SwitchFlashBank(u8 bankNum); -u16 ReadFlashId(void); void StartFlashTimer(u8 phase); void SetReadFlash1(u16 *dest); void StopFlashTimer(void); diff --git a/include/gba/io_reg.h b/include/gba/io_reg.h index 0ab9684c4..b4b925bfb 100644 --- a/include/gba/io_reg.h +++ b/include/gba/io_reg.h @@ -1,7 +1,7 @@ #ifndef GUARD_GBA_IO_REG_H #define GUARD_GBA_IO_REG_H -extern u8 REG_BASE[]; +extern u8 REG_BASE[0x400] __attribute__ ((aligned (4))); // I/O register offsets @@ -643,9 +643,11 @@ extern u8 REG_BASE[]; #define DMA_DEST_DEC 0x0020 #define DMA_DEST_FIXED 0x0040 #define DMA_DEST_RELOAD 0x0060 +#define DMA_DEST_MASK 0x0060 #define DMA_SRC_INC 0x0000 #define DMA_SRC_DEC 0x0080 #define DMA_SRC_FIXED 0x0100 +#define DMA_SRC_MASK 0x0180 #define DMA_REPEAT 0x0200 #define DMA_16BIT 0x0000 #define DMA_32BIT 0x0400 @@ -773,4 +775,6 @@ extern u8 REG_BASE[]; #define WAITCNT_AGB (0 << 15) #define WAITCNT_CGB (1 << 15) +void printRegs(); + #endif // GUARD_GBA_IO_REG_H diff --git a/include/gba/m4a_internal.h b/include/gba/m4a_internal.h index c27a2ec7d..3255a015a 100644 --- a/include/gba/m4a_internal.h +++ b/include/gba/m4a_internal.h @@ -2,6 +2,7 @@ #define GUARD_GBA_M4A_INTERNAL_H #include "gba/gba.h" +#include "music_player.h" // ASCII encoding of 'Smsh' in reverse // This is presumably short for SMASH, the developer of MKS4AGB. @@ -152,7 +153,7 @@ struct SoundChannel u8 rhythmPan; u8 dummy3[3]; u32 count; - u32 fw; + float fw; u32 frequency; struct WaveData *wav; s8 *currentPointer; @@ -165,8 +166,7 @@ struct SoundChannel }; #define MAX_DIRECTSOUND_CHANNELS 12 - -#define PCM_DMA_BUF_SIZE 1584 // size of Direct Sound buffer +#define PCM_DMA_BUF_SIZE 4907 // size of Direct Sound buffer struct MusicPlayerInfo; @@ -205,7 +205,7 @@ struct SoundInfo u8 gap[3]; s32 pcmSamplesPerVBlank; s32 pcmFreq; - s32 divFreq; + float divFreq; struct CgbChannel *cgbChans; MPlayMainFunc MPlayMainHead; struct MusicPlayerInfo *musicPlayerHead; @@ -217,7 +217,7 @@ struct SoundInfo ExtVolPitFunc ExtVolPit; u8 gap2[16]; struct SoundChannel chans[MAX_DIRECTSOUND_CHANNELS]; - s8 pcmBuffer[PCM_DMA_BUF_SIZE * 2]; + float pcmBuffer[PCM_DMA_BUF_SIZE * 2]; }; struct SongHeader @@ -408,8 +408,8 @@ extern const struct ToneData voicegroup000; u32 umul3232H32(u32 multiplier, u32 multiplicand); void SoundMain(void); -void SoundMainBTM(void); -void TrackStop(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track); +void SoundMainBTM(void *ptr); +void TrackStop(struct MP2KPlayerState *player, struct MP2KTrack *track); void MPlayMain(struct MusicPlayerInfo *); void RealClearChain(void *x); @@ -430,7 +430,7 @@ void CgbOscOff(u8); void CgbModVol(struct CgbChannel *chan); u32 MidiKeyToCgbFreq(u8, u8, u8); void DummyFunc(void); -void MPlayJumpTableCopy(MPlayFunc *mplayJumpTable); +void MPlayJumpTableCopy(void **mplayJumpTable); void SampleFreqSet(u32 freq); void m4aSoundVSyncOn(void); void m4aSoundVSyncOff(void); @@ -456,29 +456,29 @@ void SetPokemonCryStereo(u32 val); void SetPokemonCryPriority(u8 val); // sound command handler functions -void ply_fine(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_goto(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_patt(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_pend(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_rept(struct MusicPlayerInfo *, struct MusicPlayerTrack *); +void MP2K_event_fine(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_goto(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_patt(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_pend(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_rept(struct MP2KPlayerState *, struct MP2KTrack *); void ply_memacc(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_prio(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_tempo(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_keysh(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_voice(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_vol(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_pan(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_bend(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_bendr(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_lfos(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_lfodl(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_mod(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_modt(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_tune(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_port(struct MusicPlayerInfo *, struct MusicPlayerTrack *); +void MP2K_event_prio(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_tempo(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_keysh(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_voice(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_vol(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_pan(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_bend(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_bendr(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_lfos(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_lfodl(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_mod(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_modt(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_tune(struct MP2KPlayerState *, struct MP2KTrack *); +void MP2K_event_port(struct MP2KPlayerState *, struct MP2KTrack *); void ply_xcmd(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_endtie(struct MusicPlayerInfo *, struct MusicPlayerTrack *); -void ply_note(u32 note_cmd, struct MusicPlayerInfo *, struct MusicPlayerTrack *); +void MP2K_event_endtie(struct MP2KPlayerState *, struct MP2KTrack *); +void ply_note(struct MusicPlayerInfo *, struct MusicPlayerTrack *); // extended sound command handler functions void ply_xxx(struct MusicPlayerInfo *, struct MusicPlayerTrack *); diff --git a/include/gba/macro.h b/include/gba/macro.h index cc8535305..df967a149 100644 --- a/include/gba/macro.h +++ b/include/gba/macro.h @@ -31,14 +31,7 @@ #define CpuFastCopy(src, dest, size) CpuFastSet(src, dest, ((size)/(32/8) & 0x1FFFFF)) -#define DmaSet(dmaNum, src, dest, control) \ -{ \ - vu32 *dmaRegs = (vu32 *)REG_ADDR_DMA##dmaNum; \ - dmaRegs[0] = (vu32)(src); \ - dmaRegs[1] = (vu32)(dest); \ - dmaRegs[2] = (vu32)(control); \ - dmaRegs[2]; \ -} +extern void DmaSet(int dmaNum, const void * src, void * dest, u32 control); #define DMA_FILL(dmaNum, value, dest, size, bit) \ { \ @@ -50,8 +43,8 @@ | ((size)/(bit/8))); \ } -#define DmaFill16(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 16) -#define DmaFill32(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 32) +#define DmaFill16(dmaNum, value, dest, size) CpuFill16(value, dest, size) +#define DmaFill32(dmaNum, value, dest, size) CpuFill32(value, dest, size) // Note that the DMA clear macros cause the DMA control value to be calculated // at runtime rather than compile time. The size is divided by the DMA transfer @@ -75,8 +68,8 @@ (DMA_ENABLE | DMA_START_NOW | DMA_##bit##BIT | DMA_SRC_INC | DMA_DEST_INC) << 16 \ | ((size)/(bit/8))) -#define DmaCopy16(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 16) -#define DmaCopy32(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 32) +#define DmaCopy16(dmaNum, src, dest, size) CpuCopy16(src, dest, size) +#define DmaCopy32(dmaNum, src, dest, size) CpuCopy32(src, dest, size) #define DmaStop(dmaNum) \ { \ diff --git a/include/gba/types.h b/include/gba/types.h index 35d02e263..ba9c62f07 100644 --- a/include/gba/types.h +++ b/include/gba/types.h @@ -31,6 +31,13 @@ typedef vu8 vbool8; typedef vu16 vbool16; typedef vu32 vbool32; +typedef int_fast8_t sf8; +typedef uint_fast8_t uf8; +typedef int_fast16_t sf16; +typedef uint_fast16_t uf16; +typedef int_fast32_t sf32; +typedef uint_fast32_t uf32; + struct BgCnt { u16 priority:2; diff --git a/include/malloc.h b/include/malloc.h index ac23de018..88f3a7eba 100644 --- a/include/malloc.h +++ b/include/malloc.h @@ -16,7 +16,7 @@ #define TRY_FREE_AND_SET_NULL(ptr) if (ptr != NULL) FREE_AND_SET_NULL(ptr) -extern u8 gHeap[HEAP_SIZE]; +extern ALIGNED(4) u8 gHeap[HEAP_SIZE]; void *Alloc(u32 size); void *AllocZeroed(u32 size); void Free(void *pointer); diff --git a/include/mp2k_common.h b/include/mp2k_common.h new file mode 100644 index 000000000..73dddaa44 --- /dev/null +++ b/include/mp2k_common.h @@ -0,0 +1,19 @@ +#ifndef MP2020_COMMON_H +#define MP2020_COMMON_H + +#ifndef __has_builtin +#define __has_builtin(x) defined(__GNUC__) +#endif + +#if ((-1 >> 1) == -1) && __has_builtin(__builtin_ctz) +#define FLOOR_DIV_POW2(a, b) ((a) >> __builtin_ctz(b)) +#else +#define FLOOR_DIV_POW2(a, b) ((a) > 0 ? (a) / (b) : (((a) + 1 - (b)) / (b))) +#endif + +#define NOT_GBA_BIOS +#define NOT_GBA +//#define ORIGINAL_COARSE_POSITION_CLEARING +#define POKEMON_EXTENSIONS + +#endif diff --git a/include/music_player.h b/include/music_player.h new file mode 100644 index 000000000..f3c3bb602 --- /dev/null +++ b/include/music_player.h @@ -0,0 +1,127 @@ +#ifndef MUSIC_PLAYER_H +#define MUSIC_PLAYER_H + +#include "gba/types.h" +//#include "m4a.h" +#include "sound_mixer.h" + +#define PLAYER_UNLOCKED 0x68736D53 +#define PLAYER_LOCKED PLAYER_UNLOCKED+1 + +struct WaveData2 +{ + u8 compressionFlags1; + u8 compressionFlags2; + u8 compressionFlags3; + u8 loopFlags; + u32 freq; // 22.10 fixed width decimal, freq of C4. Or just the frequency of C14. + u32 loopStart; + u32 size; // number of samples + s8 data[]; // samples +}; + + +struct MP2KInstrument { + u8 type; + u8 drumKey; + u8 cgbLength; + u8 panSweep; + union { + struct WaveData *wav; + struct MP2KInstrument *group; + u32 *cgb3Sample; + u32 squareNoiseConfig; + }; + union { + struct { + u8 attack; + u8 decay; + u8 sustain; + u8 release; + }; + u8 *keySplitTable; + }; +}; + +struct MP2KTrack { + u8 status; + u8 wait; + u8 patternLevel; + u8 repeatCount; + u8 gateTime; // 0 if TIE + u8 key; + u8 velocity; + u8 runningStatus; + s8 keyShiftCalculated; // Calculated by TrkVolPitSet using fields below. Units: semitones + u8 pitchCalculated; // Calculated by TrkVolPitSet using fields below. Units: 256ths of a semitone + s8 keyShift; // Units: semitones + s8 keyShiftPublic; // Units: semitones + s8 tune; // Units: 64ths of a semitone + u8 pitchPublic; // Units: 256ths of a semitone + s8 bend; // Units: (bendRange / 64)ths of a semitone + u8 bendRange; + u8 volRightCalculated; + u8 volLeftCalculated; + u8 vol; + u8 volPublic; // Used both for fades and MPlayVolumeControl + s8 pan; + s8 panPublic; + s8 modCalculated; // Pitch units: 16ths of a semitone + u8 modDepth; + u8 modType; + u8 lfoSpeed; + u8 lfoSpeedCounter; + u8 lfoDelay; + u8 lfoDelayCounter; + u8 priority; + u8 echoVolume; + u8 echoLength; + struct MixerSource *chan; + struct MP2KInstrument instrument; + u8 gap[10]; + u16 unk_3A; + u32 ct; + u8 *cmdPtr; + u8 *patternStack[3]; +}; + +struct MP2KPlayerState { + struct MP2KSongHeader *songHeader; + vu32 status; + u8 trackCount; + u8 priority; + u8 cmd; + bool8 checkSongPriority; + u32 clock; + u8 padding[8]; + u8 *memaccArea; + u16 tempoRawBPM; // 150 initially... this doesn't seem right but whatever + u16 tempoScale; // 0x100 initially + u16 tempoInterval; // 150 initially + u16 tempoCounter; + u16 fadeInterval; + u16 fadeCounter; + u16 isFadeTemporary:1; + u16 isFadeIn:1; + u16 fadeVolume:7; + u16 :7; // padding + struct MP2KTrack *tracks; + struct MP2KInstrument *voicegroup; + vu32 lockStatus; + void (*nextPlayerFunc)(void *); + void *nextPlayer; +}; + +struct MP2KPlayerCtor { + struct MP2KPlayerState *player; + struct MP2KTrack *tracks; + u8 trackCount; + u8 padding; + bool16 checkSongPriority; +}; + +void clear_modM(struct MP2KPlayerState *unused, struct MP2KTrack *track); +void MP2K_event_endtie(struct MP2KPlayerState *unused, struct MP2KTrack *track); +void MP2K_event_lfos(struct MP2KPlayerState *unused, struct MP2KTrack *track); +void MP2K_event_mod(struct MP2KPlayerState *unused, struct MP2KTrack *track); +#endif diff --git a/include/platform.h b/include/platform.h new file mode 100644 index 000000000..cc9d15d5d --- /dev/null +++ b/include/platform.h @@ -0,0 +1,13 @@ +#ifndef GUARD_PLATFORM_H +#define GUARD_PLATFORM_H + +#include "global.h" + + +void Platform_StoreSaveFile(void); +void Platform_ReadFlash(u16 sectorNum, u32 offset, u8 *dest, u32 size); +void Platform_QueueAudio(float *audioBuffer, s32 samplesPerFrame); +u16 Platform_GetKeyInput(void); + + +#endif \ No newline at end of file diff --git a/include/platform/dma.h b/include/platform/dma.h new file mode 100644 index 000000000..1751518f0 --- /dev/null +++ b/include/platform/dma.h @@ -0,0 +1,16 @@ +#ifndef GUARD_DMA_H +#define GUARD_DMA_H + +#include "global.h" + +#define DMA_COUNT 4 + +enum { + DMA_NOW, + DMA_VBLANK, + DMA_HBLANK, + DMA_SPECIAL +}; + +void RunDMAs(u32 type); +#endif \ No newline at end of file diff --git a/include/platform/framedraw.h b/include/platform/framedraw.h new file mode 100644 index 000000000..79a9c8769 --- /dev/null +++ b/include/platform/framedraw.h @@ -0,0 +1,7 @@ +#ifndef GUARD_FRAMEDRAW_H +#define GUARD_FRAMEDRAW_H + +#include "global.h" + +void DrawFrame(uint16_t *pixels); +#endif \ No newline at end of file diff --git a/include/sound_mixer.h b/include/sound_mixer.h new file mode 100644 index 000000000..9b172c22a --- /dev/null +++ b/include/sound_mixer.h @@ -0,0 +1,121 @@ +#ifndef SOUND_MIXER_H +#define SOUND_MIXER_H + +#include "gba/types.h" +#include "music_player.h" + +#define MIXER_UNLOCKED 0x68736D53 +#define MIXER_LOCKED PLAYER_UNLOCKED+1 + +struct MP2KPlayerState; + +struct MixerSource { + u8 status; + u8 type; + u8 rightVol; + u8 leftVol; + u8 attack; + u8 decay; + u8 sustain; + u8 release; + u8 key; + u8 envelopeVol; + union { + u8 envelopeVolR; + u8 envelopeGoal; + }__attribute__((packed)); + union { + u8 envelopeVolL; + u8 envelopeCtr; + }__attribute__((packed)); + u8 echoVol; + u8 echoLen; + u8 padding1; + u8 padding2; + u8 gateTime; + u8 untransposedKey; + u8 velocity; + u8 priority; + u8 rhythmPan; + u8 padding3; + u8 padding4; + u8 padding5; + union { + u32 ct; + struct { + u8 padding6; + u8 sustainGoal; + u8 nrx4; + u8 pan; + }; + }; + union { + float fw; + struct { + u8 panMask; + u8 cgbStatus; + u8 length; + u8 sweep; + }; + }; + u32 freq; + union { + u32 *newCgb3Sample; + struct WaveData *wav; + }; + union { + u32 *oldCgb3Sample; + s8 *current; + }; + struct MP2KTrack *track; + struct MixerSource *prev; + struct MixerSource *next; + u32 padding7; //d4 + u32 blockCount; // bdpcm block count +}; + +enum { MAX_SAMPLE_CHANNELS = 12 }; +enum { MIXED_AUDIO_BUFFER_SIZE = 4907 }; + +struct SoundMixerState { + vu32 lockStatus; + vu8 dmaCounter; + u8 reverb; + u8 numChans; + u8 masterVol; + u8 freqOption; + u8 extensionFlags; + u8 cgbCounter15; + u8 framesPerDmaCycle; + u8 maxScanlines; + u8 padding1; + u8 padding2; + u8 padding3; + s32 samplesPerFrame; + s32 sampleRate; + float sampleRateReciprocal; + struct MixerSource *cgbChans; + void (*firstPlayerFunc)(void *player); + void *firstPlayer; + void (*cgbMixerFunc)(void); + void (*cgbNoteOffFunc)(u8 chan); + u32 (*cgbCalcFreqFunc)(u8 chan, u8 key, u8 pitch); + void (**mp2kEventFuncTable)(); + void (*mp2kEventNxxFunc)(u8 clock, struct MP2KPlayerState *player, struct MP2KTrack *track); + void *reserved1; // In poke* this is called "ExtVolPit" + void *reserved2; + void *reserved3; + void *reversed4; + void *reserved5; + struct MixerSource chans[MAX_SAMPLE_CHANNELS]; + __attribute__((aligned(4))) float outBuffer[MIXED_AUDIO_BUFFER_SIZE * 2]; + //s8 outBuffer[MIXED_AUDIO_BUFFER_SIZE * 2]; +}; + +typedef void (*MixerRamFunc)(struct SoundMixerState *, u32, u16, s8 *, u16); + +#ifndef NOT_GBA +#undef REG_VCOUNT +#define REG_VCOUNT (*(vu8*)REG_ADDR_VCOUNT) +#endif +#endif//SOUND_MIXER_H diff --git a/src/agb_flash.c b/src/agb_flash.c index 35d478255..e880fbdc4 100644 --- a/src/agb_flash.c +++ b/src/agb_flash.c @@ -8,14 +8,70 @@ static u16 sSavedIme; u8 gFlashTimeoutFlag = 0; u8 (*PollFlashStatus)(u8 *) = NULL; -u16 (*WaitForFlashWrite)(u8 phase, u8 *addr, u8 lastData) = NULL; -u16 (*ProgramFlashSector)(u16 sectorNum, void *src) = NULL; -const struct FlashType *gFlash = NULL; -u16 (*ProgramFlashByte)(u16 sectorNum, u32 offset, u8 data) = NULL; + +const u16 gFlashMaxTimeData[] = +{ + 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, + 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, + 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, + 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +}; +const struct FlashType gFlashData = { + 131072, // ROM size + { + 4096, // sector size + 12, // bit shift to multiply by sector size (4096 == 1 << 12) + 32, // number of sectors + 0 // appears to be unused + }, + { 3, 1 }, // wait state setup data + { { 0xCC, 0xCC } } // ID +}; + +u16 WaitForFlashWrite(u8 phase, u8 *addr, u8 lastData); +u16 ProgramFlashSector(u16 sectorNum, void *src); +u16 ProgramFlashByte(u16 sectorNum, u32 offset, u8 data); u16 gFlashNumRemainingBytes = 0; -u16 (*EraseFlashChip)() = NULL; -u16 (*EraseFlashSector)(u16 sectorNum) = NULL; -const u16 *gFlashMaxTime = NULL; +u16 EraseFlashChip(void); +u16 EraseFlashSector(u16 sectorNum); +const u16 *gFlashMaxTime = gFlashMaxTimeData; +const struct FlashType *gFlash = &gFlashData; + +u16 WaitForFlashWrite(u8 phase, u8 *addr, u8 lastData) +{ + // stub + return 0; +} + +u16 EraseFlashChip(void) +{ + memset(FLASH_BASE, 0xFF, sizeof(FLASH_BASE)); + return 0; +} + +u16 EraseFlashSector(u16 sectorNum) +{ + u8 clearBuffer[0x1000] = { 0xFF }; + return ProgramFlashSector(sectorNum, &clearBuffer[0]); +} + +u16 ProgramFlashByte(u16 sectorNum, u32 offset, u8 data) +{ + FLASH_BASE[(sectorNum << gFlash->sector.shift) + offset] = data; + return 0; +} + +u16 ProgramFlashSector(u16 sectorNum, void *src) +{ + memcpy(&FLASH_BASE[sectorNum << gFlash->sector.shift], src, 0x1000); + return 0; +} + +u16 IdentifyFlash(void) +{ + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + return 0; +} void SetReadFlash1(u16 *dest); @@ -34,33 +90,7 @@ do { \ ; \ } while (0) -u16 ReadFlashId(void) -{ - u16 flashId; - u16 readFlash1Buffer[0x20]; - u8 (*readFlash1)(u8 *); - SetReadFlash1(readFlash1Buffer); - readFlash1 = (u8 (*)(u8 *))((s32)readFlash1Buffer + 1); - - // Enter ID mode. - FLASH_WRITE(0x5555, 0xAA); - FLASH_WRITE(0x2AAA, 0x55); - FLASH_WRITE(0x5555, 0x90); - DELAY(); - - flashId = readFlash1(FLASH_BASE + 1) << 8; - flashId |= readFlash1(FLASH_BASE); - - // Leave ID mode. - FLASH_WRITE(0x5555, 0xAA); - FLASH_WRITE(0x2AAA, 0x55); - FLASH_WRITE(0x5555, 0xF0); - FLASH_WRITE(0x5555, 0xF0); - DELAY(); - - return flashId; -} void FlashTimerIntr(void) { @@ -139,123 +169,23 @@ void ReadFlash_Core(vu8 *src, u8 *dest, u32 size) void ReadFlash(u16 sectorNum, u32 offset, void *dest, u32 size) { - u8 *src; - u16 i; - vu16 readFlash_Core_Buffer[0x40]; - vu16 *funcSrc; - vu16 *funcDest; - void (*readFlash_Core)(vu8 *, u8 *, u32); - - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; - - if (gFlash->romSize == FLASH_ROM_SIZE_1M) - { - SwitchFlashBank(sectorNum / SECTORS_PER_BANK); - sectorNum %= SECTORS_PER_BANK; - } - - funcSrc = (vu16 *)ReadFlash_Core; - funcSrc = (vu16 *)((s32)funcSrc ^ 1); - funcDest = readFlash_Core_Buffer; - - i = ((s32)ReadFlash - (s32)ReadFlash_Core) >> 1; - - while (i != 0) - { - *funcDest++ = *funcSrc++; - i--; - } - - readFlash_Core = (void (*)(vu8 *, u8 *, u32))((s32)readFlash_Core_Buffer + 1); - - src = FLASH_BASE + (sectorNum << gFlash->sector.shift) + offset; - - readFlash_Core(src, dest, size); + Platform_ReadFlash(sectorNum, offset, dest, size); + return; } u32 VerifyFlashSector_Core(u8 *src, u8 *tgt, u32 size) { - while (size-- != 0) - { - if (*tgt++ != *src++) - return (u32)(tgt - 1); - } - return 0; } u32 VerifyFlashSector(u16 sectorNum, u8 *src) { - u16 i; - vu16 verifyFlashSector_Core_Buffer[0x80]; - vu16 *funcSrc; - vu16 *funcDest; - u8 *tgt; - u16 size; - u32 (*verifyFlashSector_Core)(u8 *, u8 *, u32); - - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; - - if (gFlash->romSize == FLASH_ROM_SIZE_1M) - { - SwitchFlashBank(sectorNum / SECTORS_PER_BANK); - sectorNum %= SECTORS_PER_BANK; - } - - funcSrc = (vu16 *)VerifyFlashSector_Core; - funcSrc = (vu16 *)((s32)funcSrc ^ 1); - funcDest = verifyFlashSector_Core_Buffer; - - i = ((s32)VerifyFlashSector - (s32)VerifyFlashSector_Core) >> 1; - - while (i != 0) - { - *funcDest++ = *funcSrc++; - i--; - } - - verifyFlashSector_Core = (u32 (*)(u8 *, u8 *, u32))((s32)verifyFlashSector_Core_Buffer + 1); - - tgt = FLASH_BASE + (sectorNum << gFlash->sector.shift); - size = gFlash->sector.size; - - return verifyFlashSector_Core(src, tgt, size); // return 0 if verified. + return 0; // return 0 if verified. } u32 VerifyFlashSectorNBytes(u16 sectorNum, u8 *src, u32 n) { - u16 i; - vu16 verifyFlashSector_Core_Buffer[0x80]; - vu16 *funcSrc; - vu16 *funcDest; - u8 *tgt; - u32 (*verifyFlashSector_Core)(u8 *, u8 *, u32); - - if (gFlash->romSize == FLASH_ROM_SIZE_1M) - { - SwitchFlashBank(sectorNum / SECTORS_PER_BANK); - sectorNum %= SECTORS_PER_BANK; - } - - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; - - funcSrc = (vu16 *)VerifyFlashSector_Core; - funcSrc = (vu16 *)((s32)funcSrc ^ 1); - funcDest = verifyFlashSector_Core_Buffer; - - i = ((s32)VerifyFlashSector - (s32)VerifyFlashSector_Core) >> 1; - - while (i != 0) - { - *funcDest++ = *funcSrc++; - i--; - } - - verifyFlashSector_Core = (u32 (*)(u8 *, u8 *, u32))((s32)verifyFlashSector_Core_Buffer + 1); - - tgt = FLASH_BASE + (sectorNum << gFlash->sector.shift); - - return verifyFlashSector_Core(src, tgt, n); + return 0; // return 0 if verified. } u32 ProgramFlashSectorAndVerify(u16 sectorNum, u8 *src) diff --git a/src/agb_flash_1m.c b/src/agb_flash_1m.c deleted file mode 100644 index e249fab9a..000000000 --- a/src/agb_flash_1m.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "gba/gba.h" -#include "gba/flash_internal.h" - -static const char AgbLibFlashVersion[] = "FLASH1M_V103"; - -const struct FlashSetupInfo * const sSetupInfos[] = -{ - &MX29L010, - &LE26FV10N1TS, - &DefaultFlash -}; - -u16 IdentifyFlash(void) -{ - u16 result; - u16 flashId; - const struct FlashSetupInfo * const *setupInfo; - - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; - - flashId = ReadFlashId(); - - setupInfo = sSetupInfos; - result = 1; - - for (;;) - { - if ((*setupInfo)->type.ids.separate.makerId == 0) - break; - - if (flashId == (*setupInfo)->type.ids.joined) - { - result = 0; - break; - } - - setupInfo++; - } - - ProgramFlashByte = (*setupInfo)->programFlashByte; - ProgramFlashSector = (*setupInfo)->programFlashSector; - EraseFlashChip = (*setupInfo)->eraseFlashChip; - EraseFlashSector = (*setupInfo)->eraseFlashSector; - WaitForFlashWrite = (*setupInfo)->WaitForFlashWrite; - gFlashMaxTime = (*setupInfo)->maxTime; - gFlash = &(*setupInfo)->type; - - return result; -} - -u16 WaitForFlashWrite_Common(u8 phase, u8 *addr, u8 lastData) -{ - u16 result = 0; - u8 status; - - StartFlashTimer(phase); - - while ((status = PollFlashStatus(addr)) != lastData) - { - if (status & 0x20) - { - // The write operation exceeded the flash chip's time limit. - - if (PollFlashStatus(addr) == lastData) - break; - - FLASH_WRITE(0x5555, 0xF0); - result = phase | 0xA000u; - break; - } - - if (gFlashTimeoutFlag) - { - if (PollFlashStatus(addr) == lastData) - break; - - FLASH_WRITE(0x5555, 0xF0); - result = phase | 0xC000u; - break; - } - } - - StopFlashTimer(); - - return result; -} diff --git a/src/agb_flash_le.c b/src/agb_flash_le.c deleted file mode 100644 index 39d956e27..000000000 --- a/src/agb_flash_le.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "gba/gba.h" -#include "gba/flash_internal.h" - -const u16 leMaxTime[] = -{ - 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, - 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, - 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, - 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, -}; - -const struct FlashSetupInfo LE26FV10N1TS = -{ - ProgramFlashByte_MX, - ProgramFlashSector_MX, - EraseFlashChip_MX, - EraseFlashSector_MX, - WaitForFlashWrite_Common, - leMaxTime, - { - 131072, // ROM size - { - 4096, // sector size - 12, // bit shift to multiply by sector size (4096 == 1 << 12) - 32, // number of sectors - 0 // appears to be unused - }, - { 3, 1 }, // wait state setup data - { { 0x62, 0x13 } } // ID - } -}; diff --git a/src/agb_flash_mx.c b/src/agb_flash_mx.c deleted file mode 100644 index b4f710f36..000000000 --- a/src/agb_flash_mx.c +++ /dev/null @@ -1,197 +0,0 @@ -#include "gba/gba.h" -#include "gba/flash_internal.h" - -const u16 mxMaxTime[] = -{ - 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, - 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, - 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, - 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, -}; - -const struct FlashSetupInfo MX29L010 = -{ - ProgramFlashByte_MX, - ProgramFlashSector_MX, - EraseFlashChip_MX, - EraseFlashSector_MX, - WaitForFlashWrite_Common, - mxMaxTime, - { - 131072, // ROM size - { - 4096, // sector size - 12, // bit shift to multiply by sector size (4096 == 1 << 12) - 32, // number of sectors - 0 // appears to be unused - }, - { 3, 1 }, // wait state setup data -#if defined(GERMAN) && defined(SAPPHIRE) - { { 0xBF, 0xD4 } } // ID -#else - { { 0xC2, 0x09 } } // ID -#endif - } -}; - -const struct FlashSetupInfo DefaultFlash = -{ - ProgramFlashByte_MX, - ProgramFlashSector_MX, - EraseFlashChip_MX, - EraseFlashSector_MX, - WaitForFlashWrite_Common, - mxMaxTime, - { - 131072, // ROM size - { - 4096, // sector size - 12, // bit shift to multiply by sector size (4096 == 1 << 12) - 32, // number of sectors - 0 // appears to be unused - }, - { 3, 1 }, // wait state setup data - { { 0x00, 0x00 } } // ID of 0 - } -}; - -u16 EraseFlashChip_MX(void) -{ - u16 result; - u16 readFlash1Buffer[0x20]; - - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; - - FLASH_WRITE(0x5555, 0xAA); - FLASH_WRITE(0x2AAA, 0x55); - FLASH_WRITE(0x5555, 0x80); - FLASH_WRITE(0x5555, 0xAA); - FLASH_WRITE(0x2AAA, 0x55); - FLASH_WRITE(0x5555, 0x10); - - SetReadFlash1(readFlash1Buffer); - - result = WaitForFlashWrite(3, FLASH_BASE, 0xFF); - - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; - - return result; -} - -u16 EraseFlashSector_MX(u16 sectorNum) -{ - u16 numTries; - u16 result; - u8 *addr; - u16 readFlash1Buffer[0x20]; - - if (sectorNum >= gFlash->sector.count) - return 0x80FF; - - SwitchFlashBank(sectorNum / SECTORS_PER_BANK); - sectorNum %= SECTORS_PER_BANK; - - numTries = 0; - -try_erase: - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; - - addr = FLASH_BASE + (sectorNum << gFlash->sector.shift); - - FLASH_WRITE(0x5555, 0xAA); - FLASH_WRITE(0x2AAA, 0x55); - FLASH_WRITE(0x5555, 0x80); - FLASH_WRITE(0x5555, 0xAA); - FLASH_WRITE(0x2AAA, 0x55); - *addr = 0x30; - - SetReadFlash1(readFlash1Buffer); - - result = WaitForFlashWrite(2, addr, 0xFF); - - if (!(result & 0xA000) || numTries > 3) - goto done; - - numTries++; - - goto try_erase; - -done: - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; - - return result; -} - -u16 ProgramFlashByte_MX(u16 sectorNum, u32 offset, u8 data) -{ - u8 *addr; - u16 readFlash1Buffer[0x20]; - - if (offset >= gFlash->sector.size) - return 0x8000; - - SwitchFlashBank(sectorNum / SECTORS_PER_BANK); - sectorNum %= SECTORS_PER_BANK; - - addr = FLASH_BASE + (sectorNum << gFlash->sector.shift) + offset; - - SetReadFlash1(readFlash1Buffer); - - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; - - FLASH_WRITE(0x5555, 0xAA); - FLASH_WRITE(0x2AAA, 0x55); - FLASH_WRITE(0x5555, 0xA0); - *addr = data; - - return WaitForFlashWrite(1, addr, data); -} - -static u16 ProgramByte(u8 *src, u8 *dest) -{ - FLASH_WRITE(0x5555, 0xAA); - FLASH_WRITE(0x2AAA, 0x55); - FLASH_WRITE(0x5555, 0xA0); - *dest = *src; - - return WaitForFlashWrite(1, dest, *src); -} - -u16 ProgramFlashSector_MX(u16 sectorNum, void *src) -{ - u16 result; - u8 *dest; - u16 readFlash1Buffer[0x20]; - - if (sectorNum >= gFlash->sector.count) - return 0x80FF; - - result = EraseFlashSector_MX(sectorNum); - - if (result != 0) - return result; - - SwitchFlashBank(sectorNum / SECTORS_PER_BANK); - sectorNum %= SECTORS_PER_BANK; - - SetReadFlash1(readFlash1Buffer); - - REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; - - gFlashNumRemainingBytes = gFlash->sector.size; - dest = FLASH_BASE + (sectorNum << gFlash->sector.shift); - - while (gFlashNumRemainingBytes > 0) - { - result = ProgramByte(src, dest); - - if (result != 0) - break; - - gFlashNumRemainingBytes--; - src++; - dest++; - } - - return result; -} diff --git a/src/battle_anim_mons.c b/src/battle_anim_mons.c index 4c2ec23f9..a45a9da8d 100644 --- a/src/battle_anim_mons.c +++ b/src/battle_anim_mons.c @@ -382,7 +382,7 @@ void StoreSpriteCallbackInData6(struct Sprite *sprite, SpriteCallback callback) static void SetCallbackToStoredInData6(struct Sprite *sprite) { - u32 callback = (u16)sprite->data[6] | (sprite->data[7] << 16); + u32 callback = (u16)sprite->data[6] | ((u16)sprite->data[7] << 16); sprite->callback = (SpriteCallback)callback; } diff --git a/src/battle_anim_normal.c b/src/battle_anim_normal.c index a76ad1fa2..27c6f4397 100644 --- a/src/battle_anim_normal.c +++ b/src/battle_anim_normal.c @@ -793,7 +793,7 @@ static void AnimShakeMonOrBattleTerrain(struct Sprite *sprite) StoreSpriteCallbackInData6(sprite, (void *)&gSpriteCoordOffsetY); break; } - sprite->data[4] = *(u16 *)(sprite->data[6] | (sprite->data[7] << 16)); + sprite->data[4] = *(u16 *)LoadPointerFromVars(sprite->data[6], sprite->data[7]); sprite->data[5] = gBattleAnimArgs[3]; var0 = sprite->data[5] - 2; if (var0 < 2) @@ -816,13 +816,13 @@ static void AnimShakeMonOrBattleTerrain_Step(struct Sprite *sprite) else { sprite->data[1] = sprite->data[2]; - *(u16 *)(sprite->data[6] | (sprite->data[7] << 16)) += sprite->data[0]; + *(u16 *)LoadPointerFromVars(sprite->data[6], sprite->data[7]) += sprite->data[0]; sprite->data[0] = -sprite->data[0]; } } else { - *(u16 *)(sprite->data[6] | (sprite->data[7] << 16)) = sprite->data[4]; + *(u16 *)LoadPointerFromVars(sprite->data[6], sprite->data[7]) = sprite->data[4]; var0 = sprite->data[5] - 2; if (var0 < 2) for (i = 0; i < gBattlersCount; ++i) diff --git a/src/battle_main.c b/src/battle_main.c index 722b12eb4..5c866da1c 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3862,18 +3862,19 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void) { if (!gPaletteFade.active) { + FreeAllWindowBuffers(); // This needs to be moved up here to avoid a use-after-free + if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) + { + FreeMonSpritesGfx(); + FreeBattleResources(); + FreeBattleSpritesData(); + } ResetSpriteData(); if (gLeveledUpInBattle == 0 || gBattleOutcome != B_OUTCOME_WON) gBattleMainFunc = ReturnFromBattleToOverworld; else gBattleMainFunc = TryEvolvePokemon; FreeAllWindowBuffers(); - if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) - { - FreeMonSpritesGfx(); - FreeBattleSpritesData(); - FreeBattleResources(); - } } } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 67f90a370..d537510b7 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3858,7 +3858,7 @@ static void Cmd_playanimation(void) || gBattlescriptCurrInstr[2] == B_ANIM_SUBSTITUTE_FADE || gBattlescriptCurrInstr[2] == B_ANIM_SILPH_SCOPED) { - BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr); + BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], argumentPtr != NULL ? *argumentPtr : 0); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 7; } @@ -3872,7 +3872,7 @@ static void Cmd_playanimation(void) || gBattlescriptCurrInstr[2] == B_ANIM_SANDSTORM_CONTINUES || gBattlescriptCurrInstr[2] == B_ANIM_HAIL_CONTINUES) { - BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr); + BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], argumentPtr != NULL ? *argumentPtr : 0); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 7; } @@ -3882,7 +3882,7 @@ static void Cmd_playanimation(void) } else { - BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr); + BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], argumentPtr != NULL ? *argumentPtr : 0); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 7; } @@ -3902,7 +3902,7 @@ static void Cmd_playanimation_var(void) || *animationIdPtr == B_ANIM_SNATCH_MOVE || *animationIdPtr == B_ANIM_SUBSTITUTE_FADE) { - BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr); + BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, argumentPtr != NULL ? *argumentPtr : 0); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 10; } @@ -3915,7 +3915,7 @@ static void Cmd_playanimation_var(void) || *animationIdPtr == B_ANIM_SANDSTORM_CONTINUES || *animationIdPtr == B_ANIM_HAIL_CONTINUES) { - BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr); + BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, argumentPtr != NULL ? *argumentPtr : 0); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 10; } @@ -3925,7 +3925,7 @@ static void Cmd_playanimation_var(void) } else { - BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr); + BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, argumentPtr != NULL ? *argumentPtr : 0); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 10; } diff --git a/src/battle_transition.c b/src/battle_transition.c index d7323b680..f9a133969 100644 --- a/src/battle_transition.c +++ b/src/battle_transition.c @@ -633,6 +633,7 @@ bool8 IsBattleTransitionDone(void) InitTransitionData(); FREE_AND_SET_NULL(sTransitionData); DestroyTask(taskId); + SetHBlankCallback(NULL); //prevents use after free crash in HBlankCB_Phase2_Mugshots return TRUE; } else @@ -692,8 +693,10 @@ static bool8 Transition_StartMain(struct Task *task) static bool8 Transition_WaitForMain(struct Task *task) { task->tTransitionDone = FALSE; - if (FindTaskIdByFunc(sTasks_Main[task->tTransitionId]) == TASK_NONE) + if (FindTaskIdByFunc(sTasks_Main[task->tTransitionId]) == TASK_NONE){ task->tTransitionDone = TRUE; + SetVBlankCallback(NULL); // Fixes use-after-free of sTransitionData in callbacks + } return FALSE; } diff --git a/src/bg.c b/src/bg.c index e9f93d978..5a66a0fdc 100644 --- a/src/bg.c +++ b/src/bg.c @@ -493,7 +493,7 @@ u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset) bool8 IsDma3ManagerBusyWithBgCopy(void) { int i; - + VBlankIntrWait(); for (i = 0; i < 0x80; i++) { u8 div = i / 0x20; diff --git a/src/clear_save_data_screen.c b/src/clear_save_data_screen.c index 6c8a8934c..e410fbc32 100644 --- a/src/clear_save_data_screen.c +++ b/src/clear_save_data_screen.c @@ -141,6 +141,7 @@ static void Task_HandleYesNoMenu(u8 taskId) AddTextPrinterParameterized4(1, FONT_NORMAL, 0, 3, 1, 1, sTextColor, 0, gText_ClearingData); CopyWindowToVram(1, COPYWIN_FULL); ClearSaveData(); + Platform_StoreSaveFile(); break; case MENU_NOTHING_CHOSEN: default: diff --git a/src/libagbsyscall.c b/src/libagbsyscall.c index b183a5c7a..4c424f531 100644 --- a/src/libagbsyscall.c +++ b/src/libagbsyscall.c @@ -1,14 +1,16 @@ #include "global.h" #include "gba/flash_internal.h" -u8 REG_BASE[0x400]; struct SoundInfo *SOUND_INFO_PTR; u16 INTR_CHECK; void *INTR_VECTOR; -u8 PLTT[PLTT_SIZE]; -u32 VRAM[VRAM_SIZE/sizeof(u32)]; -u8 OAM[OAM_SIZE]; -u8 FLASH_BASE[FLASH_ROM_SIZE_1M]; + +u8 REG_BASE[0x400] __attribute__ ((aligned (4))) = {0}; +u8 PLTT[PLTT_SIZE] __attribute__ ((aligned (4))) = {0}; +u8 VRAM_[VRAM_SIZE] __attribute__ ((aligned (4))) = {0}; +u8 OAM[OAM_SIZE] __attribute__ ((aligned (4))) = {0}; +u8 FLASH_BASE[FLASH_ROM_SIZE_1M] __attribute__ ((aligned (4))) = {0}; + const s16 sineTable[256] = { (s16)0x0000, (s16)0x0192, (s16)0x0323, (s16)0x04B5, (s16)0x0645, (s16)0x07D5, (s16)0x0964, (s16)0x0AF1, @@ -451,6 +453,3 @@ u16 Sqrt(u32 num) return bound; } -void VBlankIntrWait(void) -{ -} diff --git a/src/librfu_rfu.c b/src/librfu_rfu.c index 2e161fba1..df3c54606 100644 --- a/src/librfu_rfu.c +++ b/src/librfu_rfu.c @@ -124,73 +124,7 @@ static const char str_checkMbootLL[] = "RFU-MBOOT"; *_dst++ = *_src++; \ } while (0) -u16 rfu_initializeAPI(u32 *APIBuffer, u16 buffByteSize, IntrFunc *sioIntrTable_p, bool8 copyInterruptToRam) -{ - u16 i; - u16 *dst; - const u16 *src; - u16 buffByteSizeMax; - // is in EWRAM? - if (((uintptr_t)APIBuffer & 0xF000000) == 0x2000000 && copyInterruptToRam) - return ERR_RFU_API_BUFF_ADR; - // is not 4-byte aligned? - if ((u32)APIBuffer & 3) - return ERR_RFU_API_BUFF_ADR; - if (copyInterruptToRam) - { - // An assert/debug print may have existed before, ie - // printf("%s %u < %u", "somefile.c:12345", buffByteSize, num) - // to push this into buffByteSizeMax? - buffByteSizeMax = RFU_API_BUFF_SIZE_RAM; - if (buffByteSize < buffByteSizeMax) - return ERR_RFU_API_BUFF_SIZE; - } - if (!copyInterruptToRam) - { - buffByteSizeMax = RFU_API_BUFF_SIZE_ROM; // same issue as above - if (buffByteSize < buffByteSizeMax) - return ERR_RFU_API_BUFF_SIZE; - } - gRfuLinkStatus = (void *)APIBuffer + 0; - gRfuStatic = (void *)APIBuffer + 0xb4; // + sizeof(*gRfuLinkStatus) - gRfuFixed = (void *)APIBuffer + 0xdc; // + sizeof(*gRfuStatic) - gRfuSlotStatusNI[0] = (void *)APIBuffer + 0x1bc; // + sizeof(*gRfuFixed) - gRfuSlotStatusUNI[0] = (void *)APIBuffer + 0x37c; // + sizeof(*gRfuSlotStatusNI[0]) * RFU_CHILD_MAX - for (i = 1; i < RFU_CHILD_MAX; ++i) - { - gRfuSlotStatusNI[i] = &gRfuSlotStatusNI[i - 1][1]; - gRfuSlotStatusUNI[i] = &gRfuSlotStatusUNI[i - 1][1]; - } - // remaining space in API buffer is used for `struct RfuIntrStruct`. - gRfuFixed->STWIBuffer = (struct RfuIntrStruct *)&gRfuSlotStatusUNI[3][1]; - STWI_init_all((struct RfuIntrStruct *)&gRfuSlotStatusUNI[3][1], sioIntrTable_p, copyInterruptToRam); - rfu_STC_clearAPIVariables(); - for (i = 0; i < RFU_CHILD_MAX; ++i) - { - gRfuSlotStatusNI[i]->recvBuffer = NULL; - gRfuSlotStatusNI[i]->recvBufferSize = 0; - gRfuSlotStatusUNI[i]->recvBuffer = NULL; - gRfuSlotStatusUNI[i]->recvBufferSize = 0; - } - // rfu_REQ_changeMasterSlave is the function next to rfu_STC_fastCopy -#if LIBRFU_VERSION < 1026 - src = (const u16 *)((uintptr_t)&rfu_STC_fastCopy & ~1); - dst = gRfuFixed->fastCopyBuffer; - buffByteSizeMax = ((void *)rfu_REQ_changeMasterSlave - (void *)rfu_STC_fastCopy) / sizeof(u16); - while (buffByteSizeMax-- != 0) - *dst++ = *src++; -#else - COPY( - (uintptr_t)&rfu_STC_fastCopy & ~1, - gRfuFixed->fastCopyBuffer, - buffByteSizeMax, - 0x60 / sizeof(u16) - ); -#endif - gRfuFixed->fastCopyPtr = (void *)gRfuFixed->fastCopyBuffer + 1; - return 0; -} static void rfu_STC_clearAPIVariables(void) { @@ -317,41 +251,6 @@ u16 rfu_getRFUStatus(u8 *rfuState) return 0; } -/* - * RFU Multiboot images are loaded into IWRAM - * struct RfuMbootLL - * { - * struct RfuLinkStatus status; - * u8 filler_B4[0x3C]; - * char name[10]; - * u16 checksum; - * } - * Returns 1 if the packet to inherit is malformed. - */ -u16 rfu_MBOOT_CHILD_inheritanceLinkStatus(void) -{ - const char *s1 = str_checkMbootLL; - char *s2 = (char *)0x30000F0; - u16 checksum; - u16 *mb_buff_iwram_p; - u8 i; - - // if (strcmp(s1, s2) != 0) return 1; - while (*s1 != '\0') - if (*s1++ != *s2++) - return 1; - mb_buff_iwram_p = (u16 *)0x3000000; - - // The size of struct RfuLinkStatus is 180 - checksum = 0; - for (i = 0; i < 180/2; ++i) - checksum += *mb_buff_iwram_p++; - if (checksum != *(u16 *)0x30000FA) - return 1; - CpuCopy16((u16 *)0x3000000, gRfuLinkStatus, sizeof(struct RfuLinkStatus)); - gRfuStatic->flags |= 0x80; // mboot - return 0; -} void rfu_REQ_stopMode(void) { @@ -434,7 +333,7 @@ void rfu_REQ_configSystem(u16 availSlotFlag, u8 maxMFrame, u8 mcTimer) u16 IMEBackup = REG_IME; REG_IME = 0; - gRfuStatic->linkEmergencyLimit = Div(600, mcTimer); + gRfuStatic->linkEmergencyLimit = 600 / mcTimer; REG_IME = IMEBackup; } } diff --git a/src/link.c b/src/link.c index 36ad5c779..a55edc544 100644 --- a/src/link.c +++ b/src/link.c @@ -242,21 +242,6 @@ static const u8 sLinkErrorTextColor[] = { 0x00, 0x01, 0x02 }; bool8 IsWirelessAdapterConnected(void) { - if (QL_IS_PLAYBACK_STATE) - return FALSE; - - SetWirelessCommType1(); - InitRFUAPI(); - RfuSetIgnoreError(TRUE); - if (rfu_LMAN_REQBN_softReset_and_checkID() == RFU_ID) - { - rfu_REQ_stopMode(); - rfu_waitREQComplete(); - return TRUE; - } - SetWirelessCommType0_Internal(); - CloseLink(); - RestoreSerialTimer3IntrHandlers(); return FALSE; } @@ -1637,53 +1622,6 @@ void LinkPlayerFromBlock(u32 who) // When this function returns TRUE the callbacks are skipped bool8 HandleLinkConnection(void) { - bool32 main1Failed; - bool32 main2Failed; - - if (gWirelessCommType == 0) - { - gLinkStatus = LinkMain1(&gShouldAdvanceLinkState, gSendCmd, gRecvCmds); - LinkMain2(&gMain.heldKeys); - if ((gLinkStatus & LINK_STAT_RECEIVED_NOTHING) && IsSendingKeysOverCable() == TRUE) - return TRUE; - } - else - { -#if REVISION >= 0xA - bool32 reloadOrReset = FALSE; - if (svc_51()) - { -// Documentation issue: -// Rfu_IsMaster supposedly returns bool8, but just returns gRfu.ParentChild -// That value has three states, see librfu.h. One of those states is MODE_NEUTRAL (0xFF) which means init. -// So the last condition basically means "rfu link status is connected, not initialising". -// As in, it's true if value is MODE_CHILD or MODE_PARENT but not MODE_NEUTRAL (because unsigned cmp). - if (!FuncIsActiveTask(Task_WirelessCommunicationScreen) && (InUnionRoom() || gReceivedRemoteLinkPlayers != 0 || Rfu_IsMaster() <= MODE_PARENT)) - { - reloadOrReset = TRUE; - } - CloseLink(); - } -#endif - main1Failed = RfuMain1(); // Always returns FALSE - main2Failed = RfuMain2(); -#if REVISION >= 0xA - if (reloadOrReset) - { - // If active task is mystery gift then soft reset, otherwise reload the save. - if (FuncIsActiveTask(Task_MysteryGift)) RfuSoftReset(); - else RfuReloadSave(); - } - else -#endif - if (IsSendingKeysOverCable() == TRUE) - { - // This will never be reached. - // IsSendingKeysOverCable is always FALSE for wireless communication - if (main1Failed == TRUE || IsRfuRecvQueueEmpty() || main2Failed) - return TRUE; - } - } return FALSE; } diff --git a/src/link_rfu_2.c b/src/link_rfu_2.c index 0de6ccf2a..23a18bc92 100644 --- a/src/link_rfu_2.c +++ b/src/link_rfu_2.c @@ -315,14 +315,7 @@ void InitRFU(void) void InitRFUAPI(void) { - if (!rfu_initializeAPI(sRfuAPIBuffer, RFU_API_BUFF_SIZE_RAM, &gIntrTable[1], TRUE)) - { - gLinkType = 0; - // ClearSavedLinkPlayers(); // Em fix - RfuSetIgnoreError(FALSE); - ResetLinkRfuGFLayer(); - rfu_setTimerInterrupt(3, &gIntrTable[2]); - } + } static void Task_ParentSearchForChildren(u8 taskId) diff --git a/src/load_save.c b/src/load_save.c index eac773501..f15ae44a8 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -102,18 +102,18 @@ void MoveSaveBlocks_ResetHeap(void) pokemonStorageCopy = (struct PokemonStorage *)(gHeap + sizeof(struct SaveBlock2) + sizeof(struct SaveBlock1)); // backup the saves. - *saveBlock2Copy = *gSaveBlock2Ptr; - *saveBlock1Copy = *gSaveBlock1Ptr; - *pokemonStorageCopy = *gPokemonStoragePtr; + CpuCopy32(gSaveBlock2Ptr, saveBlock2Copy, sizeof(*gSaveBlock2Ptr)); + CpuCopy32(gSaveBlock1Ptr, saveBlock1Copy, sizeof(*gSaveBlock1Ptr)); + CpuCopy32(gPokemonStoragePtr, pokemonStorageCopy, sizeof(*gPokemonStoragePtr)); // change saveblocks' pointers SetSaveBlocksPointers(); // unlike Emerald, this does not use // the trainer ID sum for an offset. // restore saveblock data since the pointers changed - *gSaveBlock2Ptr = *saveBlock2Copy; - *gSaveBlock1Ptr = *saveBlock1Copy; - *gPokemonStoragePtr = *pokemonStorageCopy; + CpuCopy32(saveBlock2Copy, gSaveBlock2Ptr, sizeof(*saveBlock2Copy)); + CpuCopy32(saveBlock1Copy, gSaveBlock1Ptr, sizeof(*saveBlock1Copy)); + CpuCopy32(pokemonStorageCopy, gPokemonStoragePtr, sizeof(*pokemonStorageCopy)); // heap was destroyed in the copying process, so reset it InitHeap(gHeap, HEAP_SIZE); diff --git a/src/m4a.c b/src/m4a.c index 88c978c94..0d1c33d35 100644 --- a/src/m4a.c +++ b/src/m4a.c @@ -1,29 +1,30 @@ #include "global.h" #include "gba/m4a_internal.h" +#include "cgb_audio.h" + extern const u8 gCgb3Vol[]; -#define BSS_CODE __attribute__((section(".bss.code"))) - -BSS_CODE ALIGNED(4) char SoundMainRAM_Buffer[0x800] = {0}; - -struct SoundInfo gSoundInfo = {0}; -struct PokemonCrySong gPokemonCrySongs[MAX_POKEMON_CRIES] = {0}; -struct MusicPlayerInfo gPokemonCryMusicPlayers[MAX_POKEMON_CRIES] = {0}; -MPlayFunc gMPlayJumpTable[36] = {0}; -struct CgbChannel gCgbChans[4] = {0}; -struct MusicPlayerTrack gPokemonCryTracks[MAX_POKEMON_CRIES * 2] = {0}; -struct PokemonCrySong gPokemonCrySong = {0}; -struct MusicPlayerInfo gMPlayInfo_BGM = {0}; -struct MusicPlayerInfo gMPlayInfo_SE1 = {0}; -struct MusicPlayerInfo gMPlayInfo_SE2 = {0}; -u8 gMPlayMemAccArea[0x10] = {0}; -struct MusicPlayerInfo gMPlayInfo_SE3 = {0}; +struct SoundInfo gSoundInfo; +struct PokemonCrySong gPokemonCrySongs[MAX_POKEMON_CRIES]; +struct MusicPlayerInfo gPokemonCryMusicPlayers[MAX_POKEMON_CRIES]; +MPlayFunc gMPlayJumpTable[36]; +struct CgbChannel gCgbChans[4]; +struct MusicPlayerTrack gPokemonCryTracks[MAX_POKEMON_CRIES * 2]; +struct PokemonCrySong gPokemonCrySong; +struct MusicPlayerInfo gMPlayInfo_BGM; +struct MusicPlayerInfo gMPlayInfo_SE1; +struct MusicPlayerInfo gMPlayInfo_SE2; +struct MusicPlayerInfo gMPlayInfo_SE3; struct MusicPlayerTrack gMPlayTrack_BGM[10]; struct MusicPlayerTrack gMPlayTrack_SE1[3]; struct MusicPlayerTrack gMPlayTrack_SE2[9]; struct MusicPlayerTrack gMPlayTrack_SE3[1]; +u8 gMPlayMemAccArea[0x10]; + +void MP2K_event_nxx(); +void MP2KPlayerMain(); u32 MidiKeyToFreq(struct WaveData *wav, u8 key, u8 fineAdjust) { @@ -76,12 +77,10 @@ void m4aSoundInit(void) { s32 i; - CpuCopy32((void *)((s32)SoundMainRAM & ~1), SoundMainRAM_Buffer, sizeof(SoundMainRAM_Buffer)); - SoundInit(&gSoundInfo); MPlayExtender(gCgbChans); m4aSoundMode(SOUND_MODE_DA_BIT_8 - | SOUND_MODE_FREQ_13379 + | SOUND_MODE_FREQ_42048 | (12 << SOUND_MODE_MASVOL_SHIFT) | (5 << SOUND_MODE_MAXCHN_SHIFT)); @@ -106,7 +105,7 @@ void m4aSoundInit(void) void m4aSoundMain(void) { - SoundMain(); + //RunMixerFrame(); } void m4aSongNumStart(u16 n) @@ -279,6 +278,11 @@ void MPlayExtender(struct CgbChannel *cgbChans) REG_NR30 = 0; REG_NR50 = 0x77; + for(u8 i = 0; i < 4; i++){ + cgb_set_envelope(i, 8); + cgb_trigger_note(i); + } + soundInfo = SOUND_INFO_PTR; ident = soundInfo->ident; @@ -290,10 +294,10 @@ void MPlayExtender(struct CgbChannel *cgbChans) #if __STDC_VERSION__ < 202311L gMPlayJumpTable[8] = ply_memacc; - gMPlayJumpTable[17] = ply_lfos; - gMPlayJumpTable[19] = ply_mod; + gMPlayJumpTable[17] = MP2K_event_lfos; + gMPlayJumpTable[19] = MP2K_event_mod; gMPlayJumpTable[28] = ply_xcmd; - gMPlayJumpTable[29] = ply_endtie; + gMPlayJumpTable[29] = MP2K_event_endtie; gMPlayJumpTable[30] = SampleFreqSet; gMPlayJumpTable[31] = TrackStop; gMPlayJumpTable[32] = FadeOutBody; @@ -386,7 +390,7 @@ void SoundInit(struct SoundInfo *soundInfo) soundInfo->maxChans = 8; soundInfo->masterVolume = 15; - soundInfo->plynote = ply_note; + soundInfo->plynote = MP2K_event_nxx; soundInfo->CgbSound = DummyFunc; soundInfo->CgbOscOff = (CgbOscOffFunc)DummyFunc; soundInfo->MidiKeyToCgbFreq = (MidiKeyToCgbFreqFunc)DummyFunc; @@ -396,7 +400,7 @@ void SoundInit(struct SoundInfo *soundInfo) soundInfo->MPlayJumpTable = gMPlayJumpTable; - SampleFreqSet(SOUND_MODE_FREQ_13379); + SampleFreqSet(SOUND_MODE_FREQ_42048); soundInfo->ident = ID_NUMBER; } @@ -407,14 +411,12 @@ void SampleFreqSet(u32 freq) freq = (freq & 0xF0000) >> 16; soundInfo->freq = freq; - soundInfo->pcmSamplesPerVBlank = gPcmSamplesPerVBlankTable[freq - 1]; + soundInfo->pcmSamplesPerVBlank = 701; soundInfo->pcmDmaPeriod = PCM_DMA_BUF_SIZE / soundInfo->pcmSamplesPerVBlank; - // LCD refresh rate 59.7275Hz - soundInfo->pcmFreq = (597275 * soundInfo->pcmSamplesPerVBlank + 5000) / 10000; + soundInfo->pcmFreq = 60.0f * soundInfo->pcmSamplesPerVBlank; - // CPU frequency 16.78Mhz - soundInfo->divFreq = (16777216 / soundInfo->pcmFreq + 1) >> 1; + soundInfo->divFreq = 1.0f / soundInfo->pcmFreq; // Turn off timer 0. REG_TM0CNT_H = 0; @@ -424,12 +426,6 @@ void SampleFreqSet(u32 freq) m4aSoundVSyncOn(); - while (*(vu8 *)REG_ADDR_VCOUNT == 159) - ; - - while (*(vu8 *)REG_ADDR_VCOUNT != 159) - ; - REG_TM0CNT_H = TIMER_ENABLE | TIMER_1CLK; } @@ -606,8 +602,8 @@ void MPlayOpen(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track soundInfo->MPlayMainHead = NULL; } - soundInfo->musicPlayerHead = mplayInfo; - soundInfo->MPlayMainHead = MPlayMain; + soundInfo->musicPlayerHead = (u32)mplayInfo; + soundInfo->MPlayMainHead = (u32)MP2KPlayerMain; soundInfo->ident = ID_NUMBER; mplayInfo->ident = ID_NUMBER; } @@ -877,6 +873,8 @@ void CgbOscOff(u8 chanNum) REG_NR42 = 8; REG_NR44 = 0x80; } + cgb_set_envelope(chanNum - 1, 8); + cgb_trigger_note(chanNum - 1); } static inline int CgbPan(struct CgbChannel *chan) @@ -1000,6 +998,7 @@ void CgbSound(void) { case 1: *nrx0ptr = channels->sweep; + cgb_set_sweep(channels->sweep); // fallthrough case 2: *nrx1ptr = ((u32)channels->wavePointer << 6) + channels->length; @@ -1013,6 +1012,7 @@ void CgbSound(void) REG_WAVE_RAM2 = channels->wavePointer[2]; REG_WAVE_RAM3 = channels->wavePointer[3]; channels->currentPointer = channels->wavePointer; + cgb_set_wavram(); } *nrx0ptr = 0; *nrx1ptr = channels->length; @@ -1032,6 +1032,7 @@ void CgbSound(void) channels->n4 = 0x00; break; } + cgb_set_length(ch - 1, channels->length); channels->envelopeCounter = channels->attack; if ((s8)(channels->attack & mask)) { @@ -1228,6 +1229,9 @@ void CgbSound(void) if (ch == 1 && !(*nrx0ptr & 0x08)) *nrx4ptr = channels->n4 | 0x80; } + cgb_set_envelope(ch - 1, *nrx2ptr); + cgb_toggle_length(ch - 1, (*nrx4ptr & 0x40)); + cgb_trigger_note(ch - 1); } channel_complete: diff --git a/src/m4a_1.c b/src/m4a_1.c index 3435d6ae9..6c0330f93 100644 --- a/src/m4a_1.c +++ b/src/m4a_1.c @@ -1,13 +1,12 @@ #include "global.h" //stubs -void umul3232H32(){} void SoundMain(){} void SoundMainRAM(){} -void SoundMainBTM(){} + void RealClearChain(){} void ply_fine(){} -void MPlayJumpTableCopy(){} + void ply_goto(){} void ply_patt(){} void ply_pend(){} @@ -24,10 +23,9 @@ void ply_lfodl(){} void ply_modt(){} void ply_tune(){} void ply_port(){} -void m4aSoundVSync(){} + void MPlayMain(){} -void TrackStop(){} -void ChnVolSetAsm(){} + void ply_note(){} void ply_endtie(){} void clear_modM(){} diff --git a/src/m4a_tables.c b/src/m4a_tables.c index 286e938ba..f8401dc21 100644 --- a/src/m4a_tables.c +++ b/src/m4a_tables.c @@ -5,36 +5,36 @@ // for now. void *const gMPlayJumpTableTemplate[] = { - ply_fine, - ply_goto, - ply_patt, - ply_pend, - ply_rept, - ply_fine, - ply_fine, - ply_fine, - ply_fine, - ply_prio, - ply_tempo, - ply_keysh, - ply_voice, - ply_vol, - ply_pan, - ply_bend, - ply_bendr, - ply_lfos, - ply_lfodl, - ply_mod, - ply_modt, - ply_fine, - ply_fine, - ply_tune, - ply_fine, - ply_fine, - ply_fine, - ply_port, - ply_fine, - ply_endtie, + MP2K_event_fine, + MP2K_event_goto, + MP2K_event_patt, + MP2K_event_pend, + MP2K_event_rept, + MP2K_event_fine, + MP2K_event_fine, + MP2K_event_fine, + MP2K_event_fine, + MP2K_event_prio, + MP2K_event_tempo, + MP2K_event_keysh, + MP2K_event_voice, + MP2K_event_vol, + MP2K_event_pan, + MP2K_event_bend, + MP2K_event_bendr, + MP2K_event_lfos, + MP2K_event_lfodl, + MP2K_event_mod, + MP2K_event_modt, + MP2K_event_fine, + MP2K_event_fine, + MP2K_event_tune, + MP2K_event_fine, + MP2K_event_fine, + MP2K_event_fine, + MP2K_event_port, + MP2K_event_fine, + MP2K_event_endtie, SampleFreqSet, TrackStop, FadeOutBody, diff --git a/src/main.c b/src/main.c index 7e56b286e..ce0191a2b 100644 --- a/src/main.c +++ b/src/main.c @@ -99,12 +99,6 @@ void EnableVCountIntrAtLine150(void); #define B_START_SELECT (B_BUTTON | START_BUTTON | SELECT_BUTTON) -int main(void) -{ - AgbMain(); - return 0; -} - void AgbMain() { #if REVISION >= 0xA @@ -122,7 +116,7 @@ void AgbMain() InitIntrHandlers(); m4aSoundInit(); EnableVCountIntrAtLine150(); - InitRFU(); + //InitRFU(); CheckForFlashMemory(); InitMainCallbacks(); InitMapMusic(); @@ -136,23 +130,14 @@ void AgbMain() SetNotInSaveFailedScreen(); - // Revision 10 has no calls into libisagbprn except this one. -#if !defined(NDEBUG) || REVISION >= 0xA -#if (LOG_HANDLER == LOG_HANDLER_MGBA_PRINT) - (void) MgbaOpen(); -#elif (LOG_HANDLER == LOG_HANDLER_AGB_PRINT) - AGBPrintInit(); -#endif -#endif - #if REVISION >= 1 if (gFlashMemoryPresent != TRUE) SetMainCallback2(NULL); #endif gLinkTransferringData = FALSE; - - for (;;) + int i=0; + for (;;i++) { ReadKeys(); @@ -190,6 +175,7 @@ void AgbMain() PlayTimeCounter_Update(); MapMusicMain(); + WaitForVBlank(); } } @@ -272,7 +258,7 @@ void InitKeys(void) static void ReadKeys(void) { - u16 keyInput = REG_KEYINPUT ^ KEYS_MASK; + u16 keyInput = Platform_GetKeyInput(); gMain.newKeysRaw = keyInput & ~gMain.heldKeysRaw; gMain.newKeys = gMain.newKeysRaw; gMain.newAndRepeatedKeys = gMain.newKeysRaw; @@ -363,13 +349,11 @@ static void VBlankIntr(void) RfuVSync(); else if (!gLinkVSyncDisabled) LinkVSync(); - if (gMain.vblankCounter1) (*gMain.vblankCounter1)++; if (gMain.vblankCallback) gMain.vblankCallback(); - gMain.vblankCounter2++; CopyBufferedValuesToGpuRegs(); @@ -380,11 +364,13 @@ static void VBlankIntr(void) #if !defined(NDEBUG) || REVISION >= 0xA sVcountBeforeSound = REG_VCOUNT; #endif + m4aSoundMain(); + #if !defined(NDEBUG) || REVISION >= 0xA sVcountAfterSound = REG_VCOUNT; -#endif +#endif TryReceiveLinkBattleData(); Random(); UpdateWirelessStatusIndicatorSprite(); @@ -438,10 +424,7 @@ static void IntrDummy(void) static void WaitForVBlank(void) { - gMain.intrCheck &= ~INTR_FLAG_VBLANK; - - while (!(gMain.intrCheck & INTR_FLAG_VBLANK)) - ; + VBlankIntrWait(); } void SetVBlankCounter1Ptr(u32 *ptr) diff --git a/src/malloc.c b/src/malloc.c index eaf945033..4ca54c88b 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -1,7 +1,7 @@ #include "global.h" #include "malloc.h" -u8 gHeap[HEAP_SIZE] = {0}; +ALIGNED(4) u8 gHeap[HEAP_SIZE] = {0}; static void *sHeapStart; static u32 sHeapSize; diff --git a/src/multiboot.c b/src/multiboot.c index e701dc88e..dab4ce730 100644 --- a/src/multiboot.c +++ b/src/multiboot.c @@ -2,403 +2,8 @@ #include "multiboot.h" static u16 MultiBoot_required_data[MULTIBOOT_NCHILD]; - -static int MultiBootSend(struct MultiBootParam *mp, u16 data); -static int MultiBootHandShake(struct MultiBootParam *mp); -static void MultiBootWaitCycles(u32 cycles); -static void MultiBootWaitSendDone(void); - -void MultiBootInit(struct MultiBootParam *mp) -{ - mp->client_bit = 0; - mp->probe_count = 0; - mp->response_bit = 0; - mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT; - mp->sendflag = 0; - mp->handshake_timeout = 0; - REG_RCNT = 0; - REG_SIOCNT = SIO_MULTI_MODE | SIO_115200_BPS; - REG_SIODATA8 = 0; -} - -int MultiBootMain(struct MultiBootParam *mp) -{ - int i, j, k; - - if (MultiBootCheckComplete(mp)) - { - return 0; - } - if (mp->check_wait > MULTIBOOT_CONNECTION_CHECK_WAIT) - { - mp->check_wait--; - return 0; - } -output_burst: - if (mp->sendflag) - { - mp->sendflag = 0; - - i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_ERROR | SIO_ID | SIO_MULTI_SD | SIO_MULTI_SI); - if (i != SIO_MULTI_SD) - { - MultiBootInit(mp); - return i ^ SIO_MULTI_SD; - } - } - if (mp->probe_count >= 0xe0) - { - i = MultiBootHandShake(mp); - if (i) - { - return i; - } - - if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK - && mp->probe_count > 0xe1 - && MultiBootCheckComplete(mp) == 0) - { - MultiBootWaitSendDone(); - goto output_burst; - } - - if (MultiBootCheckComplete(mp) == 0) - { - if (mp->handshake_timeout == 0) - { - MultiBootInit(mp); - return MULTIBOOT_ERROR_HANDSHAKE_FAILURE; - } - mp->handshake_timeout--; - } - - return 0; - } - switch (mp->probe_count) - { - case 0: - k = 0x0e; - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - if (*(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2) != 0xffff) - { - break; - } - k >>= 1; - } - k &= 0x0e; - mp->response_bit = k; - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); - if (mp->client_bit & (1 << i)) - { - if (j != ((MULTIBOOT_CLIENT_INFO << 8) | (1 << i))) - { - k = 0; - break; - } - } - } - mp->client_bit &= k; - if (k == 0) - { - mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT; - } - - if (mp->check_wait) - { - mp->check_wait--; - } - else - { - if (mp->response_bit != mp->client_bit) - { - MultiBootStartProbe(mp); - goto case_1; - } - } - output_master_info: - return MultiBootSend(mp, (MULTIBOOT_MASTER_INFO << 8) | mp->client_bit); - case_1: - case 1: - mp->probe_target_bit = 0; - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); - if ((j >> 8) == MULTIBOOT_CLIENT_INFO) - { - MultiBoot_required_data[i - 1] = j; - j &= 0xff; - if (j == (1 << i)) - { - mp->probe_target_bit |= j; - } - } - } - if (mp->response_bit != mp->probe_target_bit) - { - goto output_master_info; - } - mp->probe_count = 2; - return MultiBootSend(mp, (MULTIBOOT_MASTER_START_PROBE << 8) | mp->probe_target_bit); - case 2: - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - if (mp->probe_target_bit & (1 << i)) - { - j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); - if (j != MultiBoot_required_data[i - 1]) - { - mp->probe_target_bit ^= 1 << i; - } - } - } - goto output_header; - case 0xd0: - k = 1; - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); - mp->client_data[i - 1] = j; - if (mp->probe_target_bit & (1 << i)) - { - if ((j >> 8) != MULTIBOOT_CLIENT_INFO - && (j >> 8) != MULTIBOOT_CLIENT_DLREADY) - { - MultiBootInit(mp); - return MULTIBOOT_ERROR_NO_DLREADY; - } - if (j == MultiBoot_required_data[i - 1]) - { - k = 0; - } - } - } - if (k == 0) - { - return MultiBootSend(mp, (MULTIBOOT_MASTER_REQUEST_DLREADY << 8) | mp->palette_data); - } - mp->probe_count = 0xd1; - k = 0x11; - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - k += mp->client_data[i - 1]; - } - mp->handshake_data = k; - return MultiBootSend(mp, (MULTIBOOT_MASTER_START_DL << 8) | (k & 0xff)); - case 0xd1: - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); - if (mp->probe_target_bit & (1 << i)) - { - if ((j >> 8) != MULTIBOOT_CLIENT_DLREADY) - { - MultiBootInit(mp); - return MULTIBOOT_ERROR_NO_DLREADY; - } - } - } - i = MultiBoot(mp); - if (i == 0) - { - mp->probe_count = 0xe0; - mp->handshake_timeout = MULTIBOOT_HANDSHAKE_TIMEOUT; - return 0; - } - MultiBootInit(mp); - mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT * 2; - return MULTIBOOT_ERROR_BOOT_FAILURE; - default: - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - if (mp->probe_target_bit & (1 << i)) - { - j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); - if ((j >> 8) != (MULTIBOOT_MASTER_START_PROBE + 1 - (mp->probe_count >> 1)) - || ((j & 0xff) != (1 << i))) - { - mp->probe_target_bit ^= 1 << i; - } - } - } - if (mp->probe_count == 0xc4) - { - mp->client_bit = mp->probe_target_bit & 0x0e; - mp->probe_count = 0; - goto output_master_info; - } - output_header: - if (mp->probe_target_bit == 0) - { - MultiBootInit(mp); - return MULTIBOOT_ERROR_NO_PROBE_TARGET; - } - mp->probe_count += 2; - if (mp->probe_count == 0xc4) - { - goto output_master_info; - } - i = MultiBootSend(mp, - (mp->masterp[mp->probe_count - 4 + 1] << 8) - | mp->masterp[mp->probe_count - 4]); - if (i) - { - return i; - } - if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK) - { - MultiBootWaitSendDone(); - goto output_burst; - } - return 0; - } -} - -static int MultiBootSend(struct MultiBootParam *mp, u16 data) -{ - int i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_MULTI_SD | SIO_MULTI_SI); - if (i != SIO_MULTI_SD) - { - MultiBootInit(mp); - return i ^ SIO_MULTI_SD; - } - REG_SIODATA8 = data; - REG_SIOCNT = SIO_MULTI_MODE | SIO_START | SIO_115200_BPS; - mp->sendflag = 1; - return 0; -} - -void MultiBootStartProbe(struct MultiBootParam *mp) -{ - if (mp->probe_count != 0) - { - MultiBootInit(mp); - return; - } - mp->check_wait = 0; - mp->client_bit = 0; - mp->probe_count = 1; -} - -void MultiBootStartMaster(struct MultiBootParam *mp, const u8 *srcp, int length, u8 palette_color, s8 palette_speed) -{ - int i = 0; - - if (mp->probe_count != 0 - || mp->client_bit == 0 - || mp->check_wait != 0) - { - MultiBootInit(mp); - return; - } - mp->boot_srcp = srcp; - length = (length + 15) & ~15; - if (length < MULTIBOOT_SEND_SIZE_MIN || length > MULTIBOOT_SEND_SIZE_MAX) - { - MultiBootInit(mp); - return; - } - mp->boot_endp = srcp + length; - switch (palette_speed) - { - case -4: - case -3: - case -2: - case -1: - i = (palette_color << 3) | (3 - palette_speed); - break; - case 0: - i = 0x38 | palette_color; - break; - case 1: - case 2: - case 3: - case 4: - i = (palette_color << 3) | (palette_speed - 1); - break; - } - mp->palette_data = ((i & 0x3f) << 1) | 0x81; - mp->probe_count = 0xd0; -} - -bool32 MultiBootCheckComplete(struct MultiBootParam *mp) -{ - if (mp->probe_count == 0xe9) - return 1; - else - return 0; -} - -static int MultiBootHandShake(struct MultiBootParam *mp) -{ - int i, j; - -#define send_data (mp->system_work[0]) -#define must_data (mp->system_work[1]) - switch (mp->probe_count) - { - case_0xe0: - case 0xe0: - mp->probe_count = 0xe1; - must_data = 0x0000; - send_data = 0x100000; - return MultiBootSend(mp, 0x0000); - default: - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); - if ((mp->client_bit & (1 << i)) - && j != must_data) - { - goto case_0xe0; - } - } - mp->probe_count++; - must_data = send_data & 0xffff; - if (send_data == 0x0000) - { - must_data = mp->masterp[0xac] | (mp->masterp[0xad] << 8); - send_data = must_data << 5; - } - send_data >>= 5; - output_common: - return MultiBootSend(mp, send_data); - case 0xe7: - case 0xe8: - for (i = MULTIBOOT_NCHILD; i != 0; i--) - { - j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); - if ((mp->client_bit & (1 << i)) && j != must_data) - { - MultiBootInit(mp); - return MULTIBOOT_ERROR_HANDSHAKE_FAILURE; - } - } - - mp->probe_count++; - if (mp->probe_count == 0xe9) - { - return 0; - } - send_data = mp->masterp[0xae] | (mp->masterp[0xaf] << 8); - must_data = send_data; - goto output_common; - } -#undef send_data -#undef must_data -} - -static NOINLINE void MultiBootWaitCycles(u32 cycles) -{ - -} - -static void MultiBootWaitSendDone(void) -{ - int i; - - for (i = 0; (i < 31069) && (REG_SIOCNT & SIO_START); i++); - MultiBootWaitCycles(600); -} +void MultiBootInit(struct MultiBootParam *mp){} +int MultiBootMain(struct MultiBootParam *mp){} +void MultiBootStartProbe(struct MultiBootParam *mp){} +void MultiBootStartMaster(struct MultiBootParam *mp, const u8 *srcp, int length, u8 palette_color, s8 palette_speed){} +bool32 MultiBootCheckComplete(struct MultiBootParam *mp){} \ No newline at end of file diff --git a/src/music_player.c b/src/music_player.c new file mode 100644 index 000000000..5152b7e02 --- /dev/null +++ b/src/music_player.c @@ -0,0 +1,806 @@ +#include "mp2k_common.h" +#include "music_player.h" +#include "gba/types.h" +#include "gba/m4a_internal.h" +#include "platform.h" + +// Don't uncomment this. vvvvv +// #define POKEMON_EXTENSIONS +#define MIXED_AUDIO_BUFFER_SIZE 4907 + +static u32 MidiKeyToFreq(struct WaveData2 *wav, u8 key, u8 pitch); +extern void * const gMPlayJumpTableTemplate[]; +extern const u8 gScaleTable[]; +extern const u32 gFreqTable[]; +extern const u8 gClockTable[]; +float audioBuffer [MIXED_AUDIO_BUFFER_SIZE]; + +u32 umul3232H32(u32 a, u32 b) { + u64 result = a; + result *= b; + return result >> 32; +} + +void SoundMainBTM(void *ptr) +{ + CpuFill32(0, ptr, 0x40); +} + +// Removes chan from the doubly-linked list of channels associated with chan->track. +// Gonna rename this to like "FreeChannel" or something, similar to VGMS +void MP2KClearChain(struct MixerSource *chan) { + struct MP2KTrack *track = chan->track; + if (chan->track == NULL) { + return; + } + struct MixerSource *next = chan->next; + struct MixerSource *prev = chan->prev; + + if (prev != NULL) { + prev->next = next; + } else { + track->chan = next; + } + + if (next != NULL) { + next->prev = prev; + } + + chan->track = NULL; +} + +// In case newer compilers are too dumb to remove this logic at compile time. +#define SKIP_GBA_BIOS_CHECKS +// #define NOT_GBA + +#if defined(SKIP_GBA_BIOS_CHECKS) || defined(NOT_GBA) +#define VERIFY_PTR(x) do; while (0) +#else +#define VERIFY_PTR(x) do {\ + uintptr_t y = (uintptr_t)(x);\ + if (y < EWRAM_START && (y < (uintptr_t)&gMPlayJumpTableTemplate || y >= 0x40000)) {\ + ret = 0;\ + }\ +} while (0) +#endif + +static u8 SafeDereferenceU8(u8 *addr) { + u8 ret = *addr; + VERIFY_PTR(addr); + return ret; +} + +static u32 SafeDereferenceU32(u32 *addr) { + u32 ret = *addr; + VERIFY_PTR(addr); + return ret; +} + +static u8 *SafeDereferenceU8Ptr(u8 **addr) { + u8 *ret = *addr; + VERIFY_PTR(addr); + return ret; +} + +static u32 *SafeDereferenceU32Ptr(u32 **addr) { + u32 *ret = *addr; + VERIFY_PTR(addr); + return ret; +} + +static struct WaveData2 *SafeDereferenceWavDataPtr(struct WaveData2 **addr) { + struct WaveData2 *ret = *addr; + VERIFY_PTR(addr); + return ret; +} + +static struct MP2KInstrument *SafeDereferenceMP2KInstrumentPtr(struct MP2KInstrument **addr) { + struct MP2KInstrument *ret = *addr; + VERIFY_PTR(addr); + return ret; +} + +static void *SafeDereferenceVoidPtr(void **addr) { + void *ret = *addr; + VERIFY_PTR(addr); + return ret; +} + +#ifndef NOT_GBA +// I read an article about undefined behavior today so I'm feeling especially cautious +// I'm definitely bringing this onto compiler explorer for some laughs... heh... +// ...Sorry +struct MP2KInstrument SafeDereferenceMP2KInstrument(struct MP2KInstrument *addr) { + struct MP2KInstrument instrument; + if (addr->type == SafeDereferenceU8(&addr->type) + && addr->drumKey == SafeDereferenceU8(&addr->drumKey) + && addr->cgbLength == SafeDereferenceU8(&addr->cgbLength) + && addr->panSweep == SafeDereferenceU8(&addr->panSweep)) { + instrument.type = addr->type; + instrument.drumKey = addr->drumKey; + instrument.cgbLength = addr->cgbLength; + instrument.panSweep = addr->panSweep; + } else { + instrument.type = 0; + instrument.drumKey = 0; + instrument.cgbLength = 0; + instrument.panSweep = 0; + } + + // I don't know how much the optimizer can eff with weird union stuff so I might as well go through + // all the steps to check which union member to access...? + if (instrument.type & 0xC0) { + if (addr->group == SafeDereferenceMP2KInstrumentPtr(&addr->group)) { + instrument.group = addr->group; + } else { + instrument.group = NULL; + } + + if (addr->keySplitTable == SafeDereferenceU8Ptr(&addr->keySplitTable)) { + instrument.keySplitTable = addr->keySplitTable; + } else { + instrument.keySplitTable = NULL; + } + return instrument; + } else if (instrument.type & 0x7) { + if ((instrument.type & 0x7) == 3) { + if (addr->cgb3Sample == SafeDereferenceU32Ptr(&addr->cgb3Sample)) { + instrument.cgb3Sample = addr->cgb3Sample; + } else { + instrument.cgb3Sample = NULL; + } + } else { + if (addr->squareNoiseConfig == SafeDereferenceU32(&addr->squareNoiseConfig)) { + instrument.squareNoiseConfig = addr->squareNoiseConfig; + } else { + instrument.squareNoiseConfig = 0; + } + } + } else { + if (addr->wav == SafeDereferenceWavDataPtr(&addr->wav)) { + instrument.wav = addr->wav; + } else { + instrument.wav = NULL; + } + } + + if (addr->attack == SafeDereferenceU8(&addr->attack) + && addr->decay == SafeDereferenceU8(&addr->decay) + && addr->sustain == SafeDereferenceU8(&addr->sustain) + && addr->release == SafeDereferenceU8(&addr->release)) { + instrument.attack = addr->attack; + instrument.decay = addr->decay; + instrument.sustain = addr->sustain; + instrument.release = addr->release; + } else { + instrument.attack = 0; + instrument.decay = 0; + instrument.sustain = 0; + instrument.release = 0; + } + return instrument; +} +#endif + +#undef VERIFY_PTR + +u8 ConsumeTrackByte(struct MP2KTrack *track) { + u8 *ptr = track->cmdPtr++; + return SafeDereferenceU8(ptr); +} + +void MPlayJumpTableCopy(void **mplayJumpTable) { + for (uf8 i = 0; i < 36; i++) { + mplayJumpTable[i] = SafeDereferenceVoidPtr(&gMPlayJumpTableTemplate[i]); + } +} + +// Ends the current track. (Fine as in the Italian musical word, not English) +void MP2K_event_fine(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + struct MP2KTrack *r5 = track; + for (struct MixerSource *chan = track->chan; chan != NULL; chan = chan->next) { + if (chan->status & 0xC7) { + chan->status |= 0x40; + } + MP2KClearChain(chan); + } + track->status = 0; +} + +// Sets the track's cmdPtr to the specified address. +void MP2K_event_goto(struct MP2KPlayerState *unused, struct MP2KTrack *track) { +#ifdef NOT_GBA + u8 *addr; + memcpy(&addr, track->cmdPtr, sizeof(u8 *)); + track->cmdPtr = addr; +#else + u8 *cmdPtr = track->cmdPtr; + uintptr_t addr = 0; + for (size_t i = sizeof(uintptr_t) - 1; i > 0; i--) { + addr |= cmdPtr[i]; + addr <<= 8; + } + addr |= SafeDereferenceU8(cmdPtr); + track->cmdPtr = (u8*)addr; +#endif +} + +// Sets the track's cmdPtr to the specified address after backing up its current position. +void MP2K_event_patt(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + u8 level = track->patternLevel; + if (level < 3) { + track->patternStack[level] = track->cmdPtr + sizeof(u8 *); + track->patternLevel++; + MP2K_event_goto(unused, track); + } else { + // Stop playing this track, as an indication to the music programmer that they need to quit + // nesting patterns so darn much. + MP2K_event_fine(unused, track); + } +} + +// Marks the end of the current pattern, if there is one, by resetting the pattern to the +// most recently saved value. +void MP2K_event_pend(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + if (track->patternLevel != 0) { + u8 index = --track->patternLevel; + track->cmdPtr = track->patternStack[index]; + } +} + +// Loops back until a REPT event has been reached the specified number of times +void MP2K_event_rept(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + if (*track->cmdPtr == 0) { + // "Repeat 0 times" == loop forever + track->cmdPtr++; + MP2K_event_goto(unused, track); + } else { + u8 repeatCount = ++track->repeatCount; + if (repeatCount < ConsumeTrackByte(track)) { + MP2K_event_goto(unused, track); + } else { + track->repeatCount = 0; + track->cmdPtr += sizeof(u8) + sizeof(u8 *); + } + } +} + +// Sets the note priority for new notes in this track. +void MP2K_event_prio(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->priority = ConsumeTrackByte(track); +} + +// Sets the BPM of all tracks to the specified tempo (in beats per half-minute, because 255 as a max tempo +// kinda sucks but 510 is plenty). +void MP2K_event_tempo(struct MP2KPlayerState *player, struct MP2KTrack *track) { + u16 bpm = ConsumeTrackByte(track); + bpm *= 2; + player->tempoRawBPM = bpm; + player->tempoInterval = (bpm * player->tempoScale) / 256; +} + +void MP2K_event_keysh(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->keyShift = ConsumeTrackByte(track); + track->status |= 0xC; +} + +void MP2K_event_voice(struct MP2KPlayerState *player, struct MP2KTrack *track) { + u8 voice = *(track->cmdPtr++); + struct MP2KInstrument *instrument = &player->voicegroup[voice]; +#ifdef NOT_GBA + track->instrument = *instrument; +#else + track->instrument = SafeDereferenceMP2KInstrument(instrument); +#endif +} + +void MP2K_event_vol(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->vol = ConsumeTrackByte(track); + track->status |= 0x3; +} + +void MP2K_event_pan(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->pan = ConsumeTrackByte(track) - 0x40; + track->status |= 0x3; +} + +void MP2K_event_bend(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->bend = ConsumeTrackByte(track) - 0x40; + track->status |= 0xC; +} + +void MP2K_event_bendr(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->bendRange = ConsumeTrackByte(track); + track->status |= 0xC; +} + +void MP2K_event_lfodl(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->lfoDelay = ConsumeTrackByte(track); +} + +void MP2K_event_modt(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + u8 type = ConsumeTrackByte(track); + if (type != track->modType) { + track->modType = type; + track->status |= 0xF; + } +} + +void MP2K_event_tune(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->tune = ConsumeTrackByte(track) - 0x40; + track->status |= 0xC; +} + +void MP2K_event_port(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + // I'm really curious whether any games actually use this event... +#ifdef NOT_GBA + // I assume anything done by this command will get immediately overwritten by CgbSound? + track->cmdPtr += 2; +#else + vu8* offset = (vu8 *)(REG_ADDR_NR10 + *(track->cmdPtr++)); + *offset = ConsumeTrackByte(track); +#endif +} + +void MP2KPlayerMain(void *voidPtrPlayer) { + struct MP2KPlayerState *player = (struct MP2KPlayerState *)voidPtrPlayer; + struct SoundMixerState *mixer = SOUND_INFO_PTR; + + if (player->lockStatus != PLAYER_UNLOCKED) { + return; + } + player->lockStatus = PLAYER_LOCKED; + + if (player->nextPlayerFunc != NULL) { + player->nextPlayerFunc(player->nextPlayer); + } + + if (player->status & MUSICPLAYER_STATUS_PAUSE) { + goto returnEarly; + } + FadeOutBody(voidPtrPlayer); + if (player->status & MUSICPLAYER_STATUS_PAUSE) { + goto returnEarly; + } + + player->tempoCounter += player->tempoInterval; + while (player->tempoCounter >= 150) { + u16 trackBits = 0; + for (u32 i = 0; i < player->trackCount; i++) { + struct MP2KTrack *currentTrack = player->tracks + i; + struct MixerSource *chan; + if ((currentTrack->status & MPT_FLG_EXIST) == 0) { + continue; + } + trackBits |= (1 << i); + + chan = currentTrack->chan; + while (chan != NULL) { + if(chan->gateTime == 0) { + //chan = chan->next; + chan->status |= SOUND_CHANNEL_SF_STOP; + break; + } + if ((chan->status & SOUND_CHANNEL_SF_ON) == 0) { + ClearChain(chan); + } else if (chan->gateTime != 0 && --chan->gateTime == 0) { + chan->status |= SOUND_CHANNEL_SF_STOP; + } + chan = chan->next; + } + if (currentTrack->status & MPT_FLG_START) { + CpuFill32(0, currentTrack, 0x40); + currentTrack->status = MPT_FLG_EXIST; + currentTrack->bendRange = 2; + currentTrack->volPublic = 64; + currentTrack->lfoSpeed = 22; + currentTrack->instrument.type = 1; + } + + while (currentTrack->wait == 0) { + u8 event = *currentTrack->cmdPtr; + if (event < 0x80) { + event = currentTrack->runningStatus; + } else { + currentTrack->cmdPtr++; + if (event >= 0xBD) { + currentTrack->runningStatus = event; + } + } + + if (event >= 0xCF) { + mixer->mp2kEventNxxFunc(event - 0xCF, player, currentTrack); + } else if (event >= 0xB1) { + void (*eventFunc)(struct MP2KPlayerState *, struct MP2KTrack *); + player->cmd = event - 0xB1; + eventFunc = mixer->mp2kEventFuncTable[player->cmd]; + eventFunc(player, currentTrack); + + if (currentTrack->status == 0) { + goto nextTrack; + } + } else { + currentTrack->wait = gClockTable[event - 0x80]; + } + } + currentTrack->wait--; + + if (currentTrack->lfoSpeed != 0 && currentTrack->modDepth != 0) { + if (currentTrack->lfoDelayCounter != 0U) { + currentTrack->lfoDelayCounter--; + goto nextTrack; + } + + currentTrack->lfoSpeedCounter += currentTrack->lfoSpeed; + + s8 r; + if (currentTrack->lfoSpeedCounter >= 0x40U && currentTrack->lfoSpeedCounter < 0xC0U) { + r = 128 - currentTrack->lfoSpeedCounter; + } else if (currentTrack->lfoSpeedCounter >= 0xC0U) { + // Unsigned -> signed casts where the value is out of range are implementation defined. + // Why not add a few extra lines to make behavior the same for literally everyone? + r = currentTrack->lfoSpeedCounter - 256; + } else { + r = currentTrack->lfoSpeedCounter; + } + r = FLOOR_DIV_POW2(currentTrack->modDepth * r, 64); + + if (r != currentTrack->modCalculated) { + currentTrack->modCalculated = r; + if (currentTrack->modType == 0) { + currentTrack->status |= MPT_FLG_PITCHG; + } else { + currentTrack->status |= MPT_FLG_VOLCHG; + } + } + } + + nextTrack:; + } + player->clock++; + if (trackBits == 0) { + player->status = MUSICPLAYER_STATUS_PAUSE; + goto returnEarly; + } + player->status = trackBits; + player->tempoCounter -= 150; + } + + u32 i = 0; + + do { + printf( "probleman2\n"); + struct MP2KTrack *track = player->tracks + i; + + if ((track->status & MPT_FLG_EXIST) == 0 || (track->status & 0xF) == 0) { + continue; + } + TrkVolPitSet(player, track); + + for (struct MixerSource *chan = track->chan; chan != NULL; chan = chan->next) { + printf( "probleman3 %d\n",chan->status); + if(chan->status & SOUND_CHANNEL_SF_STOP){ break;} + if ((chan->status & 0xC7) == 0) { + ClearChain(chan); + continue; + } + u8 cgbType = chan->type & 0x7; + if (track->status & MPT_FLG_VOLCHG) { + ChnVolSetAsm(chan, track); + if (cgbType != 0) { + chan->cgbStatus |= 1; + } + } + if (track->status & MPT_FLG_PITCHG) { + s32 key = chan->key + track->keyShiftCalculated; + if (key < 0) { + key = 0; + } + if (cgbType != 0) { + chan->freq = mixer->cgbCalcFreqFunc(cgbType, key, track->pitchCalculated); + chan->cgbStatus |= 0x2; + } else { + chan->freq = MidiKeyToFreq(chan->wav, key, track->pitchCalculated); + } + } + } + track->status &= ~0xF; + } + while(++i < player->trackCount); + printf("problema3\n"); +returnEarly: ; + player->lockStatus = PLAYER_UNLOCKED; +} + +void TrackStop(struct MP2KPlayerState *player, struct MP2KTrack *track) { + if (track->status & 0x80) { + for (struct MixerSource *chan = track->chan; chan != NULL; chan = chan->next) { + if (chan->status != 0) { + u8 cgbType = chan->type & 0x7; + if (cgbType != 0) { + struct SoundMixerState *mixer = SOUND_INFO_PTR; + mixer->cgbNoteOffFunc(cgbType); + } + chan->status = 0; + } + chan->track = NULL; + } + track->chan = NULL; + } +} + +void ChnVolSetAsm(struct MixerSource *chan, struct MP2KTrack *track) { + s8 forcedPan = chan->rhythmPan; + u32 rightVolume = (u8)(forcedPan + 128) * chan->velocity * track->volRightCalculated / 128 / 128; + if (rightVolume > 0xFF) { + rightVolume = 0xFF; + } + chan->rightVol = rightVolume; + + u32 leftVolume = (u8)(127 - forcedPan) * chan->velocity * track->volLeftCalculated / 128 / 128; + if (leftVolume > 0xFF) { + leftVolume = 0xFF; + } + chan->leftVol = leftVolume; +} + +void MP2K_event_nxx(u8 clock, struct MP2KPlayerState *player, struct MP2KTrack *track) { // ply_note + struct SoundMixerState *mixer = SOUND_INFO_PTR; + + // A note can be anywhere from 1 to 4 bytes long. First is always the note length... + track->gateTime = gClockTable[clock]; + if (*track->cmdPtr < 0x80) { + // Then the note name... + track->key = *(track->cmdPtr++); + if (*track->cmdPtr < 0x80) { + // Then the velocity... + track->velocity = *(track->cmdPtr++); + if (*track->cmdPtr < 0x80) { + // Then a number to add ticks to get exotic or more precise note lengths without TIE. + track->gateTime += *(track->cmdPtr++); + } + } + } + + // sp14 + s8 forcedPan = 0; + // First r4, then r9 + struct MP2KInstrument *instrument = &track->instrument; + // sp8 + u8 key = track->key; + u8 type = instrument->type; + + if (type & (TONEDATA_TYPE_RHY | TONEDATA_TYPE_SPL)) { + u8 instrumentIndex; + if (instrument->type & TONEDATA_TYPE_SPL) { + instrumentIndex = instrument->keySplitTable[track->key]; + } else { + instrumentIndex = track->key; + } + + instrument = instrument->group + instrumentIndex; + if (instrument->type & (TONEDATA_TYPE_RHY | TONEDATA_TYPE_SPL)) { + return; + } + if (type & TONEDATA_TYPE_RHY) { + if (instrument->panSweep & 0x80) { + forcedPan = ((s8)(instrument->panSweep & 0x7F) - 0x40) * 2; + } + key = instrument->drumKey; + } + } + + // sp10 + uf16 priority = player->priority + track->priority; + if (priority > 0xFF) { + priority = 0xFF; + } + + u8 cgbType = instrument->type & TONEDATA_TYPE_CGB; + struct MixerSource *chan; + + if (cgbType != 0) { + if (mixer->cgbChans == NULL) { + return; + } + // There's only one CgbChannel of a given type, so we don't need to loop to find it. + chan = mixer->cgbChans + cgbType - 1; + + // If this channel is running and not stopped, + if ((chan->status & SOUND_CHANNEL_SF_ON) + && (chan->status & SOUND_CHANNEL_SF_STOP) == 0) { + // then make sure this note is higher priority (or same priority but from a later track). + if (chan->priority > priority || (chan->priority == priority && chan->track < track)) { + return; + } + } + } else { + uf16 p = priority; + struct MP2KTrack *t = track; + bool32 foundStoppingChannel = FALSE; + chan = NULL; + u8 maxChans = mixer->numChans; + struct MixerSource *currChan = mixer->chans; + + for (uf8 i = 0; i < maxChans; i++, currChan++) { + if ((currChan->status & SOUND_CHANNEL_SF_ON) == 0) { + // Hey, we found a completely inactive channel! Let's use that. + chan = currChan; + break; + } + + if (currChan->status & SOUND_CHANNEL_SF_STOP && !foundStoppingChannel) { + // In the absence of a completely finalized channel, we can take over one that's about to + // finalize. That's a tier above any channel that's currently playing a note. + foundStoppingChannel = TRUE; + p = currChan->priority; + t = currChan->track; + chan = currChan; + } else if ((currChan->status & SOUND_CHANNEL_SF_STOP && foundStoppingChannel) + || ((currChan->status & SOUND_CHANNEL_SF_STOP) == 0 && !foundStoppingChannel)) { + // The channel we're checking is on the same tier, so check the priority and track order + if (currChan->priority < p) { + p = currChan->priority; + t = currChan->track; + chan = currChan; + } else if (currChan->priority == p && currChan->track > t) { + t = currChan->track; + chan = currChan; + } else if (currChan->priority == p && currChan->track == t) { + chan = currChan; + } + } + } + + } + + if (chan == NULL) { + return; + } + ClearChain(chan); + + chan->prev = NULL; + chan->next = track->chan; + if (track->chan != NULL) { + track->chan->prev = chan; + } + track->chan = chan; + chan->track = track; + + track->lfoDelayCounter = track->lfoDelay; + if (track->lfoDelay != 0) { + ClearModM(track); + } + TrkVolPitSet(player, track); + + chan->gateTime = track->gateTime; + chan->untransposedKey = track->key; + chan->velocity = track->velocity; + chan->priority = priority; + chan->key = key; + chan->rhythmPan = forcedPan; + chan->type = instrument->type; + chan->wav = instrument->wav; + chan->attack = instrument->attack; + chan->decay = instrument->decay; + chan->sustain = instrument->sustain; + chan->release = instrument->release; + chan->echoVol = track->echoVolume; + chan->echoLen = track->echoLength; + ChnVolSetAsm(chan, track); + + // Avoid promoting keyShiftCalculated to u8 by splitting the addition into a separate statement + sf16 transposedKey = chan->key; + transposedKey += track->keyShiftCalculated; + if (transposedKey < 0) { + transposedKey = 0; + } + + if (cgbType != 0) { + //struct CgbChannel *cgbChan = (struct CgbChannel *)chan; + chan->length = instrument->cgbLength; + if (instrument->panSweep & 0x80 || (instrument->panSweep & 0x70) == 0) { + chan->sweep = 8; + } else { + chan->sweep = instrument->panSweep; + } + + chan->freq = mixer->cgbCalcFreqFunc(cgbType, transposedKey, track->pitchCalculated); + } else { +#ifdef POKEMON_EXTENSIONS + chan->ct = track->ct; +#endif + chan->freq = MidiKeyToFreq(chan->wav, transposedKey, track->pitchCalculated); + } + + chan->status = SOUND_CHANNEL_SF_START; + track->status &= ~0xF; +} + +void MP2K_event_endtie(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + u8 key = *track->cmdPtr; + if (key < 0x80) { + track->key = key; + track->cmdPtr++; + } else { + key = track->key; + } + + struct MixerSource *chan = track->chan; + while (chan != NULL) { + if (chan->status & 0x83 && (chan->status & 0x40) == 0 && chan->untransposedKey == key) { + chan->status |= 0x40; + return; + } + chan = chan->next; + } +} + +void MP2K_event_lfos(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->lfoSpeed = *(track->cmdPtr++); + if (track->lfoSpeed == 0) { + ClearModM(track); + } +} + +void MP2K_event_mod(struct MP2KPlayerState *unused, struct MP2KTrack *track) { + track->modDepth = *(track->cmdPtr++); + if (track->modDepth == 0) { + ClearModM(track); + } +} + +void m4aSoundVSync(void) +{ + struct SoundMixerState *mixer = SOUND_INFO_PTR; + if(mixer->lockStatus-PLAYER_UNLOCKED <= 1) + { + s32 samplesPerFrame = mixer->samplesPerFrame * 2; + float *m4aBuffer = mixer->outBuffer; + float *cgbBuffer = cgb_get_buffer(); + s32 dmaCounter = mixer->dmaCounter; + + if (dmaCounter > 1) { + m4aBuffer += samplesPerFrame * (mixer->framesPerDmaCycle - (dmaCounter - 1)); + } + + for(u32 i = 0; i < samplesPerFrame; i++) + audioBuffer[i] = m4aBuffer[i] + cgbBuffer[i]; + + Platform_QueueAudio(audioBuffer, samplesPerFrame * 4); + if((s8)(--mixer->dmaCounter) <= 0) + mixer->dmaCounter = mixer->framesPerDmaCycle; + } + +} + +#if 0 +// In: +// - wav: pointer to sample +// - key: the note after being transposed. If pitch bend puts it between notes, then the note below. +// - pitch: how many 256ths of a semitone above `key` the current note is. +// Out: +// - The frequency in Hz at which the sample should be played back. + +u32 MidiKeyToFreq(struct WaveData2 *wav, u8 key, u8 pitch) { + if (key > 178) { + key = 178; + pitch = 255; + } + + // Alternatively, note = key % 12 and octave = 14 - (key / 12) + u8 note = gScaleTable[key] & 0xF; + u8 octave = gScaleTable[key] >> 4; + u8 nextNote = gScaleTable[key + 1] & 0xF; + u8 nextOctave = gScaleTable[key + 1] >> 4; + + u32 baseFreq1 = gFreqTable[note] >> octave; + u32 baseFreq2 = gFreqTable[nextNote] >> nextOctave; + + u32 freqDifference = umul3232H32(baseFreq2 - baseFreq1, pitch << 24); + // This is added by me. The real GBA and GBA BIOS don't verify this address, and as a result the + // BIOS's memory can be dumped. + u32 freq = SafeDereferenceU32(&wav->freq); + return umul3232H32(freq, baseFreq1 + freqDifference); +} +#endif diff --git a/src/mystery_gift.c b/src/mystery_gift.c index 70f3cedb2..54f675b36 100644 --- a/src/mystery_gift.c +++ b/src/mystery_gift.c @@ -336,38 +336,7 @@ bool32 MysteryGift_TrySaveStamp(const u16 * stamp) void MysteryGift_LoadLinkGameData(struct MysteryGiftLinkGameData * data) { - s32 i; - CpuFill32(0, data, sizeof(*data)); - // Magic - data->unk_00 = GAME_DATA_VALID_VAR; - data->unk_04 = 1; - data->unk_08 = 1; - data->unk_0C = 1; - data->unk_10 = VERSION_CODE; - // Check whether a card already exists - if (ValidateSavedWonderCard()) - { - // Populate fields - data->flagId = GetSavedWonderCard()->flagId; - data->cardMetadata = *GetSavedWonderCardMetadata(); - data->maxStamps = GetSavedWonderCard()->maxStamps; - } - else - { - data->flagId = 0; - } - - for (i = 0; i < NUM_QUESTIONNAIRE_WORDS; i++) - data->questionnaireWords[i] = gSaveBlock1Ptr->mysteryGift.questionnaireWords[i]; - - CopyTrainerId(data->playerTrainerId, gSaveBlock2Ptr->playerTrainerId); - StringCopy(data->playerName, gSaveBlock2Ptr->playerName); - for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++) - data->easyChatProfile[i] = gSaveBlock1Ptr->easyChatProfile[i]; - - memcpy(data->gameCode, RomHeaderGameCode, GAME_CODE_LENGTH); - data->version = RomHeaderSoftwareVersion; } bool32 MysteryGift_ValidateLinkGameData(const struct MysteryGiftLinkGameData * data) diff --git a/src/naming_screen.c b/src/naming_screen.c index d3abdd9f8..c95e86486 100644 --- a/src/naming_screen.c +++ b/src/naming_screen.c @@ -718,10 +718,11 @@ static bool8 MainState_Exit(void) { if (!gPaletteFade.active) { - if (sNamingScreen->templateNum == NAMING_SCREEN_PLAYER) - SeedRngAndSetTrainerId(); SetMainCallback2(sNamingScreen->returnCallback); DestroyTask(FindTaskIdByFunc(Task_NamingScreen)); + ResetVHBlank(); + // Fix use-after-free issues with gNamingScreenData caused by sprites calling their callbacks which attempt to read from gNamingScreenData. + ResetSpriteData(); FreeAllWindowBuffers(); FREE_AND_SET_NULL(sNamingScreen); RestoreHelpContext(); diff --git a/src/new_game.c b/src/new_game.c index a3c4d80eb..b319c7c5a 100644 --- a/src/new_game.c +++ b/src/new_game.c @@ -47,13 +47,16 @@ void SetTrainerId(u32 trainerId, u8 *dst) void CopyTrainerId(u8 *dst, u8 *src) { s32 i; - for (i = 0; i < 4; i++) + for (i = 0; i < 4; i++){ + if (src == NULL) + dst[i] = 0; dst[i] = src[i]; + } } static void InitPlayerTrainerId(void) { - u32 trainerId = (Random() << 0x10) | GetGeneratedTrainerIdLower(); + u32 trainerId = (Random() << 16 | Random()); SetTrainerId(trainerId, gSaveBlock2Ptr->playerTrainerId); } diff --git a/src/oak_speech.c b/src/oak_speech.c index 2356b2367..c99a56764 100644 --- a/src/oak_speech.c +++ b/src/oak_speech.c @@ -1966,7 +1966,6 @@ static void DestroyPikachuOrPlatformSprites(u8 taskId, u8 spriteType) static void LoadTrainerPic(u16 whichPic, u16 tileOffset) { u32 i; - switch (whichPic) { case MALE_PLAYER_PIC: @@ -1988,7 +1987,6 @@ static void LoadTrainerPic(u16 whichPic, u16 tileOffset) default: return; } - sOakSpeechResources->trainerPicTilemap = AllocZeroed(0x60); for (i = 0; i < 0x60; i++) ((u8 *)sOakSpeechResources->trainerPicTilemap)[i] = i; diff --git a/src/platform/cgb_audio.c b/src/platform/cgb_audio.c new file mode 100644 index 000000000..b4cc975ca --- /dev/null +++ b/src/platform/cgb_audio.c @@ -0,0 +1,257 @@ +#include "global.h" +#include "cgb_audio.h" +#include "cgb_tables.h" + +struct AudioCGB gb; +float soundChannelPos[4]; +const s16 *PU1Table; +const s16 *PU2Table; +u32 apuFrame; +u8 apuCycle; +u32 sampleRate; +u16 lfsrMax[2]; +float ch4Samples; + +void cgb_audio_init(u32 rate){ + gb.ch1Freq = 0; + gb.ch1SweepCounter = 0; + gb.ch1SweepCounterI = 0; + gb.ch1SweepDir = 0; + gb.ch1SweepShift = 0; + for (u8 ch = 0; ch < 4; ch++){ + gb.Vol[ch] = 0; + gb.VolI[ch] = 0; + gb.Len[ch] = 0; + gb.LenI[ch] = 0; + gb.LenOn[ch] = 0; + gb.EnvCounter[ch] = 0; + gb.EnvCounterI[ch] = 0; + gb.EnvDir[ch] = 0; + gb.DAC[ch] = 0; + soundChannelPos[ch] = 0; + } + soundChannelPos[1] = 1; + PU1Table = PU0; + PU2Table = PU0; + sampleRate = rate; + gb.ch4LFSR[0] = 0x8000; + gb.ch4LFSR[1] = 0x80; + lfsrMax[0] = 0x8000; + lfsrMax[1] = 0x80; + ch4Samples = 0.0f; +} + + +void cgb_set_sweep(u8 sweep){ + gb.ch1SweepDir = (sweep & 0x08) >> 3; + gb.ch1SweepCounter = gb.ch1SweepCounterI = (sweep & 0x70) >> 4; + gb.ch1SweepShift = (sweep & 0x07); +} + + +void cgb_set_wavram(){ + for(u8 wavi = 0; wavi < 0x10; wavi++){ + gb.WAVRAM[(wavi << 1)] = (((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0xF0) >> 4) / 7.5f - 1.0f; + gb.WAVRAM[(wavi << 1) + 1] = (((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0x0F)) / 7.5f - 1.0f; + } +} + + +void cgb_toggle_length(u8 channel, bool8 state){ + gb.LenOn[channel] = state; +} + + +void cgb_set_length(u8 channel, u8 length){ + gb.Len[channel] = gb.LenI[channel] = length; +} + + +void cgb_set_envelope(u8 channel, u8 envelope){ + if(channel == 2){ + switch((envelope & 0xE0)){ + case 0x00: // mute + gb.Vol[2] = gb.VolI[2] = 0; + break; + case 0x20: // full + gb.Vol[2] = gb.VolI[2] = 4; + break; + case 0x40: // half + gb.Vol[2] = gb.VolI[2] = 2; + break; + case 0x60: // quarter + gb.Vol[2] = gb.VolI[2] = 1; + break; + case 0x80: // 3 quarters + gb.Vol[2] = gb.VolI[2] = 3; + break; + } + }else{ + gb.DAC[channel] = (envelope & 0xF8) > 0; + gb.Vol[channel] = gb.VolI[channel] = (envelope & 0xF0) >> 4; + gb.EnvDir[channel] = (envelope & 0x08) >> 3; + gb.EnvCounter[channel] = gb.EnvCounterI[channel] = (envelope & 0x07); + } +} + + +void cgb_trigger_note(u8 channel){ + gb.Vol[channel] = gb.VolI[channel]; + gb.Len[channel] = gb.LenI[channel]; + if(channel != 2) gb.EnvCounter[channel] = gb.EnvCounterI[channel]; + if(channel == 3) { + gb.ch4LFSR[0] = 0x8000; + gb.ch4LFSR[1] = 0x80; + } +} + + +void cgb_audio_generate(u16 samplesPerFrame){ + float *outBuffer = gb.outBuffer; + switch(REG_NR11 & 0xC0){ + case 0x00: + PU1Table = PU0; + break; + case 0x40: + PU1Table = PU1; + break; + case 0x80: + PU1Table = PU2; + break; + case 0xC0: + PU1Table = PU3; + break; + } + + switch(REG_NR21 & 0xC0){ + case 0x00: + PU2Table = PU0; + break; + case 0x40: + PU2Table = PU1; + break; + case 0x80: + PU2Table = PU2; + break; + case 0xC0: + PU2Table = PU3; + break; + } + + for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) { + apuFrame += 512; + if(apuFrame >= sampleRate){ + apuFrame -= sampleRate; + apuCycle++; + + if((apuCycle & 1) == 0){ // Length + for(u8 ch = 0; ch < 4; ch++){ + if(gb.Len[ch]){ + if(--gb.Len[ch] == 0 && gb.LenOn[ch]){ + REG_NR52 &= (0xFF ^ (1 << ch)); + } + } + } + } + + if((apuCycle & 7) == 7){ // Envelope + for(u8 ch = 0; ch < 4; ch++){ + if(ch == 2) continue; // Skip wave channel + if(gb.EnvCounter[ch]){ + if(--gb.EnvCounter[ch] == 0){ + if(gb.Vol[ch] && !gb.EnvDir[ch]){ + gb.Vol[ch]--; + gb.EnvCounter[ch] = gb.EnvCounterI[ch]; + }else if(gb.Vol[ch] < 0x0F && gb.EnvDir[ch]){ + gb.Vol[ch]++; + gb.EnvCounter[ch] = gb.EnvCounterI[ch]; + } + } + } + } + } + + if((apuCycle & 3) == 2){ // Sweep + if(gb.ch1SweepCounterI && gb.ch1SweepShift){ + if(--gb.ch1SweepCounter == 0){ + gb.ch1Freq = REG_SOUND1CNT_X & 0x7FF; + if(gb.ch1SweepDir){ + gb.ch1Freq -= gb.ch1Freq >> gb.ch1SweepShift; + if(gb.ch1Freq & 0xF800) gb.ch1Freq = 0; + }else{ + gb.ch1Freq += gb.ch1Freq >> gb.ch1SweepShift; + if(gb.ch1Freq & 0xF800){ + gb.ch1Freq = 0; + gb.EnvCounter[0] = 0; + gb.Vol[0] = 0; + } + } + REG_NR13 = gb.ch1Freq & 0xFF; + REG_NR14 &= 0xF8; + REG_NR14 += (gb.ch1Freq >> 8) & 0x07; + gb.ch1SweepCounter = gb.ch1SweepCounterI; + } + } + } + } + //Sound generation loop + soundChannelPos[0] += freqTable[REG_SOUND1CNT_X & 0x7FF] / (sampleRate / 32); + soundChannelPos[1] += freqTable[REG_SOUND2CNT_H & 0x7FF] / (sampleRate / 32); + soundChannelPos[2] += freqTable[REG_SOUND3CNT_X & 0x7FF] / (sampleRate / 32); + while(soundChannelPos[0] >= 32) soundChannelPos[0] -= 32; + while(soundChannelPos[1] >= 32) soundChannelPos[1] -= 32; + while(soundChannelPos[2] >= 32) soundChannelPos[2] -= 32; + float outputL = 0; + float outputR = 0; + if(REG_NR52 & 0x80){ + if((gb.DAC[0]) && (REG_NR52 & 0x01)){ + if(REG_NR51 & 0x10) outputL += gb.Vol[0] * PU1Table[(int)(soundChannelPos[0])] / 15.0f; + if(REG_NR51 & 0x01) outputR += gb.Vol[0] * PU1Table[(int)(soundChannelPos[0])] / 15.0f; + } + if((gb.DAC[1]) && (REG_NR52 & 0x02)){ + if(REG_NR51 & 0x20) outputL += gb.Vol[1] * PU2Table[(int)(soundChannelPos[1])] / 15.0f; + if(REG_NR51 & 0x02) outputR += gb.Vol[1] * PU2Table[(int)(soundChannelPos[1])] / 15.0f; + } + if((REG_NR30 & 0x80) && (REG_NR52 & 0x04)){ + if(REG_NR51 & 0x40) outputL += gb.Vol[2] * gb.WAVRAM[(int)(soundChannelPos[2])] / 4.0f; + if(REG_NR51 & 0x04) outputR += gb.Vol[2] * gb.WAVRAM[(int)(soundChannelPos[2])] / 4.0f; + } + if((gb.DAC[3]) && (REG_NR52 & 0x08)){ + bool32 lfsrMode = ((REG_NR43 & 0x08) == 8); + ch4Samples += freqTableNSE[REG_SOUND4CNT_H & 0xFF] / sampleRate; + int ch4Out = 0; + if(gb.ch4LFSR[lfsrMode] & 1){ + ch4Out++; + }else{ + ch4Out--; + } + int avgDiv = 1; + while(ch4Samples >= 1){ + avgDiv++; + bool8 lfsrCarry = 0; + if(gb.ch4LFSR[lfsrMode] & 2) lfsrCarry ^= 1; + gb.ch4LFSR[lfsrMode] >>= 1; + if(gb.ch4LFSR[lfsrMode] & 2) lfsrCarry ^= 1; + if(lfsrCarry) gb.ch4LFSR[lfsrMode] |= lfsrMax[lfsrMode]; + if(gb.ch4LFSR[lfsrMode] & 1){ + ch4Out++; + }else{ + ch4Out--; + } + ch4Samples--; + } + float sample = ch4Out; + if(avgDiv > 1) sample /= avgDiv; + if(REG_NR51 & 0x80) outputL += gb.Vol[3] * sample / 15.0f; + if(REG_NR51 & 0x08) outputR += gb.Vol[3] * sample / 15.0f; + } + } + outBuffer[0] = outputL / 4.0f; + outBuffer[1] = outputR / 4.0f; + } +} + + +float *cgb_get_buffer(){ + return gb.outBuffer; +} diff --git a/src/platform/dma.c b/src/platform/dma.c new file mode 100644 index 000000000..8164264d1 --- /dev/null +++ b/src/platform/dma.c @@ -0,0 +1,110 @@ +#include "global.h" +#include "platform/dma.h" + +struct DMATransfer { + union { + const void *src; + const u16 *src16; + const u32 *src32; + }; + union { + void *dst; + vu16 *dst16; + vu32 *dst32; + }; + u32 size; + u16 control; +} DMAList[DMA_COUNT]; + +void RunDMAs(u32 type) +{ + for (int dmaNum = 0; dmaNum < DMA_COUNT; dmaNum++) + { + struct DMATransfer *dma = &DMAList[dmaNum]; + u32 dmaCntReg = (®_DMA0CNT)[dmaNum * 3]; + if (!((dmaCntReg >> 16) & DMA_ENABLE)) + { + dma->control &= ~DMA_ENABLE; + } + + if ( (dma->control & DMA_ENABLE) && + (((dma->control & DMA_START_MASK) >> 12) == type)) + { + //printf("DMA%d src=%p, dest=%p, control=%d\n", dmaNum, dma->src, dma->dest, dma->control); + for (int i = 0; i < (dma->size); i++) + { + if ((dma->control) & DMA_32BIT) + *dma->dst32 = *dma->src32; + else *dma->dst16 = *dma->src16; + + // process destination pointer changes + if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_INC) + { + if ((dma->control) & DMA_32BIT) + dma->dst32++; + else dma->dst16++; + } + else if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_DEC) + { + if ((dma->control) & DMA_32BIT) + dma->dst32--; + else dma->dst16--; + } + else if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_RELOAD) // TODO + { + if ((dma->control) & DMA_32BIT) + dma->dst32++; + else dma->dst16++; + } + + // process source pointer changes + if (((dma->control) & DMA_SRC_MASK) == DMA_SRC_INC) + { + if ((dma->control) & DMA_32BIT) + dma->src32++; + else dma->src16++; + } + else if (((dma->control) & DMA_SRC_MASK) == DMA_SRC_DEC) + { + if ((dma->control) & DMA_32BIT) + dma->src32--; + else dma->src16--; + } + } + + if (dma->control & DMA_REPEAT) + { + dma->size = ((®_DMA0CNT)[dmaNum * 3] & 0x1FFFF); + if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_RELOAD) + { + dma->dst = ((®_DMA0DAD)[dmaNum * 3]); + } + } + else + { + dma->control &= ~DMA_ENABLE; + } + } + } +} + +void DmaSet(int dmaNum, const void *src, void *dest, u32 control) +{ + if (dmaNum >= DMA_COUNT) + { + printf("DmaSet with invalid DMA number: dmaNum=%d, src=%p, dest=%p, control=%d\n", dmaNum, src, dest, control); + return; + } + + (®_DMA0SAD)[dmaNum * 3] = src; + (®_DMA0DAD)[dmaNum * 3] = dest; + (®_DMA0CNT)[dmaNum * 3] = control; + + struct DMATransfer *dma = &DMAList[dmaNum]; + dma->src = src; + dma->dst = dest; + dma->size = control & 0x1ffff; + dma->control = control >> 16; + + RunDMAs(DMA_NOW); +} \ No newline at end of file diff --git a/src/platform/gba_easy_draw.c b/src/platform/gba_easy_draw.c new file mode 100644 index 000000000..5f2f27f57 --- /dev/null +++ b/src/platform/gba_easy_draw.c @@ -0,0 +1,946 @@ + +#include "global.h" +#include +#include "platform/dma.h" + +#define mosaicBGEffectX (REG_MOSAIC & 0xF) +#define mosaicBGEffectY ((REG_MOSAIC >> 4) & 0xF) +#define mosaicSpriteEffectX ((REG_MOSAIC >> 8) & 0xF) +#define mosaicSpriteEffectY ((REG_MOSAIC >> 12) & 0xF) +#define applyBGHorizontalMosaicEffect(x) (x - (x % (mosaicBGEffectX+1))) +#define applyBGVerticalMosaicEffect(y) (y - (y % (mosaicBGEffectY+1))) +#define applySpriteHorizontalMosaicEffect(x) (x - (x % (mosaicSpriteEffectX+1))) +#define applySpriteVerticalMosaicEffect(y) (y - (y % (mosaicSpriteEffectY+1))) + +#define getAlphaBit(x) ((x >> 15) & 1) +#define getRedChannel(x) ((x >> 0) & 0x1F) +#define getGreenChannel(x) ((x >> 5) & 0x1F) +#define getBlueChannel(x) ((x >> 10) & 0x1F) +#define isbgEnabled(x) ((REG_DISPCNT >> 8) & 0xF) & (1 << x) + +#define WINMASK_BG0 (1 << 0) +#define WINMASK_BG1 (1 << 1) +#define WINMASK_BG2 (1 << 2) +#define WINMASK_BG3 (1 << 3) +#define WINMASK_OBJ (1 << 4) +#define WINMASK_CLR (1 << 5) +#define WINMASK_WINOUT (1 << 6) + +extern IntrFunc gIntrTable[]; + +struct scanlineData { + uint16_t layers[4][DISPLAY_WIDTH]; + uint16_t spriteLayers[4][DISPLAY_WIDTH]; + uint16_t bgcnts[4]; + uint16_t winMask[DISPLAY_WIDTH]; + //priority bookkeeping + char bgtoprio[4]; //background to priority + char prioritySortedBgs[4][4]; + char prioritySortedBgsCount[4]; +}; + +struct bgPriority { + char priority; + char subPriority; +}; + +static const uint16_t bgMapSizes[][2] = +{ + {32, 32}, + {64, 32}, + {32, 64}, + {64, 64}, +}; + +static void RenderBGScanline(int bgNum, uint16_t control, uint16_t hoffs, uint16_t voffs, int lineNum, uint16_t *line) +{ + unsigned int charBaseBlock = (control >> 2) & 3; + unsigned int screenBaseBlock = (control >> 8) & 0x1F; + unsigned int bitsPerPixel = ((control >> 7) & 1) ? 8 : 4; + unsigned int mapWidth = bgMapSizes[control >> 14][0]; + unsigned int mapHeight = bgMapSizes[control >> 14][1]; + unsigned int mapWidthInPixels = mapWidth * 8; + unsigned int mapHeightInPixels = mapHeight * 8; + + uint8_t *bgtiles = (uint8_t *)BG_CHAR_ADDR(charBaseBlock); + uint16_t *pal = (uint16_t *)PLTT; + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + hoffs &= 0x1FF; + voffs &= 0x1FF; + + for (unsigned int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t *bgmap = (uint16_t *)BG_SCREEN_ADDR(screenBaseBlock); + // adjust for scroll + unsigned int xx; + if (control & BGCNT_MOSAIC) + xx = (applyBGHorizontalMosaicEffect(x) + hoffs) & 0x1FF; + else + xx = (x + hoffs) & 0x1FF; + + unsigned int yy = (lineNum + voffs) & 0x1FF; + + //if x or y go above 255 pixels it goes to the next screen base which are 0x400 WORDs long + if (xx > 255 && mapWidthInPixels > 256) { + bgmap += 0x400; + } + + if (yy > 255 && mapHeightInPixels > 256) { + //the width check is for 512x512 mode support, it jumps by two screen bases instead + bgmap += (mapWidthInPixels > 256) ? 0x800 : 0x400; + } + + //maximum width for bgtile block is 256 + xx &= 0xFF; + yy &= 0xFF; + + unsigned int mapX = xx / 8; + unsigned int mapY = yy / 8; + uint16_t entry = bgmap[mapY * 32 + mapX]; + + unsigned int tileNum = entry & 0x3FF; + unsigned int paletteNum = (entry >> 12) & 0xF; + + unsigned int tileX = xx % 8; + unsigned int tileY = yy % 8; + + // Flip if necessary + if (entry & (1 << 10)) + tileX = 7 - tileX; + if (entry & (1 << 11)) + tileY = 7 - tileY; + + uint16_t tileLoc = tileNum * (bitsPerPixel * 8); + uint16_t tileLocY = tileY * bitsPerPixel; + uint16_t tileLocX = tileX; + if (bitsPerPixel == 4) + tileLocX /= 2; + + uint8_t pixel = bgtiles[tileLoc + tileLocY + tileLocX]; + + if (bitsPerPixel == 4) { + if (tileX & 1) + pixel >>= 4; + else + pixel &= 0xF; + + if (pixel != 0) + line[x] = pal[16 * paletteNum + pixel] | 0x8000; + else + line[x] = 0; + } + else { + if (pixel != 0) + line[x] = pal[pixel] | 0x8000; + else + line[x] = 0; + } + } +} + +static inline uint32_t getBgX(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2X; + } + else if (bgNumber == 3) + { + return REG_BG3X; + } +} + +static inline uint32_t getBgY(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2Y; + } + else if (bgNumber == 3) + { + return REG_BG3Y; + } +} + +static inline uint16_t getBgPA(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2PA; + } + else if (bgNumber == 3) + { + return REG_BG3PA; + } +} + +static inline uint16_t getBgPB(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2PB; + } + else if (bgNumber == 3) + { + return REG_BG3PB; + } +} + +static inline uint16_t getBgPC(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2PC; + } + else if (bgNumber == 3) + { + return REG_BG3PC; + } +} + +static inline uint16_t getBgPD(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2PD; + } + else if (bgNumber == 3) + { + return REG_BG3PD; + } +} + +static void RenderRotScaleBGScanline(int bgNum, uint16_t control, uint16_t x, uint16_t y, int lineNum, uint16_t *line) +{ + vBgCnt *bgcnt = (vBgCnt *)&control; + unsigned int charBaseBlock = bgcnt->charBaseBlock; + unsigned int screenBaseBlock = bgcnt->screenBaseBlock; + unsigned int mapWidth = 1 << (4 + (bgcnt->screenSize)); // number of tiles + + uint8_t *bgtiles = (uint8_t *)(VRAM_ + charBaseBlock * 0x4000); + uint8_t *bgmap = (uint8_t *)(VRAM_ + screenBaseBlock * 0x800); + uint16_t *pal = (uint16_t *)PLTT; + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + + s16 pa = getBgPA(bgNum); + s16 pb = getBgPB(bgNum); + s16 pc = getBgPC(bgNum); + s16 pd = getBgPD(bgNum); + + if (pa == 0 && pb == 0 && pc == 0 && pd == 0) { + pa = 0x0100; + pd = 0x0100; + } + + int sizeX = 128; + int sizeY = 128; + + switch (bgcnt->screenSize) + { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + int maskX = sizeX - 1; + int maskY = sizeY - 1; + + int yshift = ((control >> 14) & 3) + 4; + + /*int dx = pa & 0x7FFF; + if (pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if (pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if (pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if (pd & 0x8000) + dmy |= 0xFFFF8000;*/ + + s32 currentX = getBgX(bgNum); + s32 currentY = getBgY(bgNum); + //sign extend 28 bit number + currentX = ((currentX & (1 << 27)) ? currentX | 0xF0000000 : currentX); + currentY = ((currentY & (1 << 27)) ? currentY | 0xF0000000 : currentY); + + currentX += lineNum * pb; + currentY += lineNum * pd; + + int realX = currentX; + int realY = currentY; + + if (bgcnt->areaOverflowMode) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8) & maskX; + int yyy = (realY >> 8) & maskY; + + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + if (pixel != 0) { + line[x] = pal[pixel] | 0x8000; + } + + realX += pa; + realY += pc; + } + } + else + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + if (xxx < 0 || yyy < 0 || xxx >= sizeX || yyy >= sizeY) + { + //line[x] = 0x80000000; + } + else + { + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + if (pixel != 0) { + line[x] = pal[pixel] | 0x8000; + } + } + realX += pa; + realY += pc; + } + } + //the only way i could figure out how to get accurate mosaic on affine bgs + //luckily i dont think pokemon emerald uses mosaic on affine bgs + if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t color = line[applyBGHorizontalMosaicEffect(x)]; + line[x] = color; + + } + } +} + +const u8 spriteSizes[][2] = +{ + {8, 16}, + {8, 32}, + {16, 32}, + {32, 64}, +}; + +static uint16_t alphaBlendColor(uint16_t targetA, uint16_t targetB) +{ + unsigned int eva = REG_BLDALPHA & 0x1F; + unsigned int evb = (REG_BLDALPHA >> 8) & 0x1F; + // shift right by 4 = division by 16 + unsigned int r = ((getRedChannel(targetA) * eva) + (getRedChannel(targetB) * evb)) >> 4; + unsigned int g = ((getGreenChannel(targetA) * eva) + (getGreenChannel(targetB) * evb)) >> 4; + unsigned int b = ((getBlueChannel(targetA) * eva) + (getBlueChannel(targetB) * evb)) >> 4; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return r | (g << 5) | (b << 10) | (1 << 15); +} + +static uint16_t alphaBrightnessIncrease(uint16_t targetA) +{ + unsigned int evy = (REG_BLDY & 0x1F); + unsigned int r = getRedChannel(targetA) + (31 - getRedChannel(targetA)) * evy / 16; + unsigned int g = getGreenChannel(targetA) + (31 - getGreenChannel(targetA)) * evy / 16; + unsigned int b = getBlueChannel(targetA) + (31 - getBlueChannel(targetA)) * evy / 16; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return r | (g << 5) | (b << 10) | (1 << 15); +} + +static uint16_t alphaBrightnessDecrease(uint16_t targetA) +{ + unsigned int evy = (REG_BLDY & 0x1F); + unsigned int r = getRedChannel(targetA) - getRedChannel(targetA) * evy / 16; + unsigned int g = getGreenChannel(targetA) - getGreenChannel(targetA) * evy / 16; + unsigned int b = getBlueChannel(targetA) - getBlueChannel(targetA) * evy / 16; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return r | (g << 5) | (b << 10) | (1 << 15); +} + +//outputs the blended pixel in colorOutput, the prxxx are the bg priority and subpriority, pixelpos is pixel offset in scanline +static bool alphaBlendSelectTargetB(struct scanlineData* scanline, uint16_t* colorOutput, char prnum, char prsub, int pixelpos, bool spriteBlendEnabled) +{ + //iterate trough every possible bg to blend with, starting from specified priorities from arguments + for (unsigned int blndprnum = prnum; blndprnum <= 3; blndprnum++) + { + //check if sprite is available to blend with, if sprite blending is enabled + if (spriteBlendEnabled == true && getAlphaBit(scanline->spriteLayers[blndprnum][pixelpos]) == 1) + { + *colorOutput = scanline->spriteLayers[blndprnum][pixelpos]; + return true; + } + + for (unsigned int blndprsub = prsub; blndprsub < scanline->prioritySortedBgsCount[blndprnum]; blndprsub++) + { + char currLayer = scanline->prioritySortedBgs[blndprnum][blndprsub]; + if (getAlphaBit( scanline->layers[currLayer][pixelpos] ) == 1 && REG_BLDCNT & ( 1 << (8 + currLayer)) && isbgEnabled(currLayer)) + { + *colorOutput = scanline->layers[currLayer][pixelpos]; + return true; + } + //if we hit a non target layer we should bail + if ( getAlphaBit( scanline->layers[currLayer][pixelpos] ) == 1 && isbgEnabled(currLayer) && prnum != blndprnum ) + { + return false; + } + } + prsub = 0; //start from zero in the next iteration + } + //no background got hit, check if backdrop is enabled and return it if enabled otherwise fail + if (REG_BLDCNT & BLDCNT_TGT2_BD) + { + *colorOutput = *(uint16_t*)PLTT; + return true; + } + else + { + return false; + } +} + +//checks if window horizontal is in bounds and takes account WIN wraparound +static bool winCheckHorizontalBounds(u16 left, u16 right, u16 xpos) +{ + if (left > right) + return (xpos >= left || xpos < right); + else + return (xpos >= left && xpos < right); +} + +// Parts of this code heavily borrowed from NanoboyAdvance. +static void DrawSprites(struct scanlineData* scanline, uint16_t vcount, bool windowsEnabled) +{ + int i; + unsigned int x; + unsigned int y; + void *objtiles = VRAM_ + 0x10000; + unsigned int blendMode = (REG_BLDCNT >> 6) & 3; + bool winShouldBlendPixel = true; + + int16_t matrix[2][2] = {}; + + if (!(REG_DISPCNT & (1 << 6))) + { + puts("2-D OBJ Character mapping not supported."); + } + + for (i = 127; i >= 0; i--) + { + struct OamData *oam = &((struct OamData *)OAM)[i]; + unsigned int width; + unsigned int height; + uint16_t *pixels; + + bool isAffine = oam->affineMode & 1; + bool doubleSizeOrDisabled = (oam->affineMode >> 1) & 1; + bool isSemiTransparent = (oam->objMode == 1); + bool isObjWin = (oam->objMode == 2); + + if (!(isAffine) && doubleSizeOrDisabled) // disable for non-affine + { + continue; + } + + if (oam->shape == 0) + { + width = (1 << oam->size) * 8; + height = (1 << oam->size) * 8; + } + else if (oam->shape == 1) // wide + { + width = spriteSizes[oam->size][1]; + height = spriteSizes[oam->size][0]; + } + else if (oam->shape == 2) // tall + { + width = spriteSizes[oam->size][0]; + height = spriteSizes[oam->size][1]; + } + else + { + continue; // prohibited, do not draw + } + + int rect_width = width; + int rect_height = height; + + int half_width = width / 2; + int half_height = height / 2; + + pixels = scanline->spriteLayers[oam->priority]; + + int32_t x = oam->x; + int32_t y = oam->y; + + if (x >= DISPLAY_WIDTH) + x -= 512; + if (y >= DISPLAY_HEIGHT) + y -= 256; + + if (isAffine) + { + //TODO: there is probably a better way to do this + u8 matrixNum = oam->matrixNum * 4; + + struct OamData *oam1 = &((struct OamData *)OAM)[matrixNum]; + struct OamData *oam2 = &((struct OamData *)OAM)[matrixNum + 1]; + struct OamData *oam3 = &((struct OamData *)OAM)[matrixNum + 2]; + struct OamData *oam4 = &((struct OamData *)OAM)[matrixNum + 3]; + + matrix[0][0] = oam1->affineParam; + matrix[0][1] = oam2->affineParam; + matrix[1][0] = oam3->affineParam; + matrix[1][1] = oam4->affineParam; + + if (doubleSizeOrDisabled) // double size for affine + { + rect_width *= 2; + rect_height *= 2; + half_width *= 2; + half_height *= 2; + } + } + else + { + // Identity + matrix[0][0] = 0x100; + matrix[0][1] = 0; + matrix[1][0] = 0; + matrix[1][1] = 0x100; + } + + x += half_width; + y += half_height; + + // Does this sprite actually draw on this scanline? + if (vcount >= (y - half_height) && vcount < (y + half_height)) + { + int local_y = (oam->mosaic == 1) ? applySpriteVerticalMosaicEffect(vcount) - y : vcount - y; + int number = oam->tileNum; + int palette = oam->paletteNum; + bool flipX = !isAffine && ((oam->matrixNum >> 3) & 1); + bool flipY = !isAffine && ((oam->matrixNum >> 4) & 1); + bool is8BPP = oam->bpp & 1; + + for (int local_x = -half_width; local_x <= half_width; local_x++) + { + uint8_t *tiledata = (uint8_t *)objtiles; + uint16_t *palette = (uint16_t *)(PLTT + 0x200); + int local_mosaicX; + int tex_x; + int tex_y; + + unsigned int global_x = local_x + x; + + if (global_x < 0 || global_x >= DISPLAY_WIDTH) + continue; + + if (oam->mosaic == 1) + { + //mosaic effect has to be applied to global coordinates otherwise the mosaic will scroll + local_mosaicX = applySpriteHorizontalMosaicEffect(global_x) - x; + tex_x = ((matrix[0][0] * local_mosaicX + matrix[0][1] * local_y) >> 8) + (width / 2); + tex_y = ((matrix[1][0] * local_mosaicX + matrix[1][1] * local_y) >> 8) + (height / 2); + }else{ + tex_x = ((matrix[0][0] * local_x + matrix[0][1] * local_y) >> 8) + (width / 2); + tex_y = ((matrix[1][0] * local_x + matrix[1][1] * local_y) >> 8) + (height / 2); + } + + /* Check if transformed coordinates are inside bounds. */ + + if (tex_x >= width || tex_y >= height || tex_x < 0 || tex_y < 0) + continue; + + if (flipX) + tex_x = width - tex_x - 1; + if (flipY) + tex_y = height - tex_y - 1; + + int tile_x = tex_x % 8; + int tile_y = tex_y % 8; + int block_x = tex_x / 8; + int block_y = tex_y / 8; + int block_offset = ((block_y * (REG_DISPCNT & 0x40 ? (width / 8) : 16)) + block_x); + uint16_t pixel = 0; + + if (!is8BPP) + { + pixel = tiledata[(block_offset + oam->tileNum) * 32 + (tile_y * 4) + (tile_x / 2)]; + if (tile_x & 1) + pixel >>= 4; + else + pixel &= 0xF; + palette += oam->paletteNum * 16; + } + else + { + pixel = tiledata[(block_offset * 2 + oam->tileNum) * 32 + (tile_y * 8) + tile_x]; + } + + if (pixel != 0) + { + uint16_t color = palette[pixel];; + + //if sprite mode is 2 then write to the window mask instead + if (isObjWin) + { + if (scanline->winMask[global_x] & WINMASK_WINOUT) + scanline->winMask[global_x] = (REG_WINOUT >> 8) & 0x3F; + continue; + } + //this code runs if pixel is to be drawn + if (global_x < DISPLAY_WIDTH && global_x >= 0) + { + //check if its enabled in the window (if window is enabled) + winShouldBlendPixel = (windowsEnabled == false || scanline->winMask[global_x] & WINMASK_CLR); + + //has to be separated from the blend mode switch statement because of OBJ semi transparancy feature + if ((blendMode == 1 && REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) || isSemiTransparent) + { + uint16_t targetA = color; + uint16_t targetB = 0; + if (alphaBlendSelectTargetB(scanline, &targetB, oam->priority, 0, global_x, false)) + { + color = alphaBlendColor(targetA, targetB); + } + } + else if (REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) + { + switch (blendMode) + { + case 2: + color = alphaBrightnessIncrease(color); + break; + case 3: + color = alphaBrightnessDecrease(color); + break; + } + } + + //write pixel to pixel framebuffer + pixels[global_x] = color | (1 << 15); + } + } + } + } + } +} + +static void DrawScanline(uint16_t *pixels, uint16_t vcount) +{ + unsigned int mode = REG_DISPCNT & 3; + unsigned char numOfBgs = (mode == 0 ? 4 : 3); + int bgnum, prnum; + struct scanlineData scanline; + unsigned int blendMode = (REG_BLDCNT >> 6) & 3; + unsigned int xpos; + + + //initialize all priority bookkeeping data + memset(scanline.layers, 0, sizeof(scanline.layers)); + memset(scanline.winMask, 0, sizeof(scanline.winMask)); + memset(scanline.spriteLayers, 0, sizeof(scanline.spriteLayers)); + memset(scanline.prioritySortedBgsCount, 0, sizeof(scanline.prioritySortedBgsCount)); + + for (bgnum = 0; bgnum < numOfBgs; bgnum++) + { + uint16_t bgcnt = *(uint16_t*)(REG_ADDR_BG0CNT + bgnum * 2); + uint16_t priority; + scanline.bgcnts[bgnum] = bgcnt; + scanline.bgtoprio[bgnum] = priority = (bgcnt & 3); + + char priorityCount = scanline.prioritySortedBgsCount[priority]; + scanline.prioritySortedBgs[priority][priorityCount] = bgnum; + scanline.prioritySortedBgsCount[priority]++; + } + + switch (mode) + { + case 0: + // All backgrounds are text mode + for (bgnum = 3; bgnum >= 0; bgnum--) + { + if (isbgEnabled(bgnum)) + { + uint16_t bghoffs = *(uint16_t *)(REG_ADDR_BG0HOFS + bgnum * 4); + uint16_t bgvoffs = *(uint16_t *)(REG_ADDR_BG0VOFS + bgnum * 4); + + RenderBGScanline(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, scanline.layers[bgnum]); + } + } + + break; + case 1: + // BG2 is affine + bgnum = 2; + if (isbgEnabled(bgnum)) + { + RenderRotScaleBGScanline(bgnum, scanline.bgcnts[bgnum], REG_BG2X, REG_BG2Y, vcount, scanline.layers[bgnum]); + } + // BG0 and BG1 are text mode + for (bgnum = 1; bgnum >= 0; bgnum--) + { + if (isbgEnabled(bgnum)) + { + uint16_t bghoffs = *(uint16_t *)(REG_ADDR_BG0HOFS + bgnum * 4); + uint16_t bgvoffs = *(uint16_t *)(REG_ADDR_BG0VOFS + bgnum * 4); + + RenderBGScanline(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, scanline.layers[bgnum]); + } + } + break; + default: + printf("Video mode %u is unsupported.\n", mode); + break; + } + + bool windowsEnabled = false; + uint16_t WIN0bottom, WIN0top, WIN0right, WIN0left; + uint16_t WIN1bottom, WIN1top, WIN1right, WIN1left; + bool WIN0enable, WIN1enable; + WIN0enable = false; + WIN1enable = false; + + //figure out if WIN0 masks on this scanline + if (REG_DISPCNT & DISPCNT_WIN0_ON) + { + //acquire the window coordinates + WIN0bottom = (REG_WIN0V & 0xFF); //y2; + WIN0top = (REG_WIN0V & 0xFF00) >> 8; //y1; + WIN0right = (REG_WIN0H & 0xFF); //x2 + WIN0left = (REG_WIN0H & 0xFF00) >> 8; //x1 + + //figure out WIN Y wraparound and check bounds accordingly + if (WIN0top > WIN0bottom) { + if (vcount >= WIN0top || vcount < WIN0bottom) + WIN0enable = true; + } else { + if (vcount >= WIN0top && vcount < WIN0bottom) + WIN0enable = true; + } + + windowsEnabled = true; + } + //figure out if WIN1 masks on this scanline + if (REG_DISPCNT & DISPCNT_WIN1_ON) + { + WIN1bottom = (REG_WIN1V & 0xFF); + WIN1top = (REG_WIN1V & 0xFF00) >> 8; + WIN1right = (REG_WIN1H & 0xFF); + WIN1left = (REG_WIN1H & 0xFF00) >> 8; + + if (WIN1top > WIN1bottom) { + if (vcount >= WIN1top || vcount < WIN1bottom) + WIN1enable = true; + } else { + if (vcount >= WIN1top && vcount < WIN1bottom) + WIN1enable = true; + } + + windowsEnabled = true; + } + //enable windows if OBJwin is enabled + if (REG_DISPCNT & DISPCNT_OBJWIN_ON && REG_DISPCNT & DISPCNT_OBJ_ON) + { + windowsEnabled = true; + } + + //draw to pixel mask + if (windowsEnabled) + { + for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++) + { + //win0 checks + if (WIN0enable && winCheckHorizontalBounds(WIN0left, WIN0right, xpos)) + scanline.winMask[xpos] = REG_WININ & 0x3F; + //win1 checks + else if (WIN1enable && winCheckHorizontalBounds(WIN1left, WIN1right, xpos)) + scanline.winMask[xpos] = (REG_WININ >> 8) & 0x3F; + else + scanline.winMask[xpos] = (REG_WINOUT & 0x3F) | WINMASK_WINOUT; + } + } + + if (REG_DISPCNT & DISPCNT_OBJ_ON) + DrawSprites(&scanline, vcount, windowsEnabled); + + //iterate trough every priority in order + for (prnum = 3; prnum >= 0; prnum--) + { + for (char prsub = scanline.prioritySortedBgsCount[prnum] - 1; prsub >= 0; prsub--) + { + char bgnum = scanline.prioritySortedBgs[prnum][prsub]; + //if background is enabled then draw it + if (isbgEnabled(bgnum)) + { + uint16_t *src = scanline.layers[bgnum]; + //copy all pixels to framebuffer + for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++) + { + uint16_t color = src[xpos]; + bool winEffectEnable = true; + + if (!getAlphaBit(color)) + continue; //do nothing if alpha bit is not set + + if (windowsEnabled) + { + winEffectEnable = ((scanline.winMask[xpos] & WINMASK_CLR) >> 5); + //if bg is disabled inside the window then do not draw the pixel + if ( !(scanline.winMask[xpos] & 1 << bgnum) ) + continue; + } + + //blending code + if (blendMode != 0 && REG_BLDCNT & (1 << bgnum) && winEffectEnable) + { + uint16_t targetA = color; + uint16_t targetB = 0; + char isSpriteBlendingEnabled; + + switch (blendMode) + { + case 1: + isSpriteBlendingEnabled = REG_BLDCNT & BLDCNT_TGT2_OBJ ? 1 : 0; + //find targetB and blend it + if (alphaBlendSelectTargetB(&scanline, &targetB, prnum, prsub+1, xpos, isSpriteBlendingEnabled)) + { + color = alphaBlendColor(targetA, targetB); + } + break; + case 2: + color = alphaBrightnessIncrease(targetA); + break; + case 3: + color = alphaBrightnessDecrease(targetA); + break; + } + } + //write the pixel to scanline buffer output + pixels[xpos] = color; + } + } + } + //draw sprites on current priority + uint16_t *src = scanline.spriteLayers[prnum]; + for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++) + { + if (getAlphaBit(src[xpos])) + { + //check if sprite pixel draws inside window + if (windowsEnabled && !(scanline.winMask[xpos] & WINMASK_OBJ)) + continue; + //draw the pixel + pixels[xpos] = src[xpos]; + } + } + } +} + +uint16_t *memsetu16(uint16_t *dst, uint16_t fill, size_t count) +{ + for (int i = 0; i < count; i++) + { + *dst++ = fill; + } +} + +void DrawFrame(uint16_t *pixels) +{ + int i; + int j; + + for (i = 0; i < DISPLAY_HEIGHT; i++) + { + REG_VCOUNT = i; + if(((REG_DISPSTAT >> 8) & 0xFF) == REG_VCOUNT) + { + REG_DISPSTAT |= INTR_FLAG_VCOUNT; + if(REG_DISPSTAT & DISPSTAT_VCOUNT_INTR) + gIntrTable[0](); + } + + // Render the backdrop color before the each individual scanline. + // backdrop color brightness effects + unsigned int blendMode = (REG_BLDCNT >> 6) & 3; + uint16_t backdropColor = *(uint16_t *)PLTT; + if (REG_BLDCNT & BLDCNT_TGT1_BD) + { + switch (blendMode) + { + case 2: + backdropColor = alphaBrightnessIncrease(backdropColor); + break; + case 3: + backdropColor = alphaBrightnessDecrease(backdropColor); + break; + } + } + + memsetu16(&pixels[i * DISPLAY_WIDTH], backdropColor, DISPLAY_WIDTH); + DrawScanline(&pixels[i * DISPLAY_WIDTH], i); + + REG_DISPSTAT |= INTR_FLAG_HBLANK; + + RunDMAs(DMA_HBLANK); + + if (REG_DISPSTAT & DISPSTAT_HBLANK_INTR) + gIntrTable[3](); + + REG_DISPSTAT &= ~INTR_FLAG_HBLANK; + REG_DISPSTAT &= ~INTR_FLAG_VCOUNT; + } +} diff --git a/src/platform/gba_fast_draw.c b/src/platform/gba_fast_draw.c new file mode 100644 index 000000000..88dc8eea6 --- /dev/null +++ b/src/platform/gba_fast_draw.c @@ -0,0 +1,2728 @@ +#ifdef FAST_DRAW +#include "global.h" +#include +#include "platform/dma.h" + +#define mosaicBGEffectX (REG_MOSAIC & 0xF) +#define mosaicBGEffectY ((REG_MOSAIC >> 4) & 0xF) +#define mosaicSpriteEffectX ((REG_MOSAIC >> 8) & 0xF) +#define mosaicSpriteEffectY ((REG_MOSAIC >> 12) & 0xF) +#define applyBGHorizontalMosaicEffect(x) (x - (x % (mosaicBGEffectX+1))) +#define applyBGVerticalMosaicEffect(y) (y - (y % (mosaicBGEffectY+1))) +#define applySpriteHorizontalMosaicEffect(x) (x - (x % (mosaicSpriteEffectX+1))) +#define applySpriteVerticalMosaicEffect(y) (y - (y % (mosaicSpriteEffectY+1))) + +#define getColorFromPixel(x) (pal[(paletteNum << 4) + (x)]) +#define setAlphaBitIfEnabled(x) (((x) != 0) << 15) +#define getbgtilePixel(x) (((x) != 0) ? (getColorFromPixel(x) | setAlphaBitIfEnabled(x)) : 0) +#define get32BitDoublePixel(x, y) getbgtilePixel(x) | (getbgtilePixel(y) << 16) + +#define getAlphaBit(x) ((x >> 15) & 1) +#define getRedChannel(x) ((x >> 0) & 0x1F) +#define getGreenChannel(x) ((x >> 5) & 0x1F) +#define getBlueChannel(x) ((x >> 10) & 0x1F) +#define isbgEnabled(x) ((REG_DISPCNT >> 8) & 0xF) & (1 << x) + +#ifdef PLATFORM_WIN32 +//gcc doesn't optimally inline some functions which causes a significant framerate drop in frame rate +#define inline_hack __attribute__ ((always_inline)) +#else +#define inline_hack +#endif + +extern IntrFunc gIntrTable[]; + +#define TILE_WIDTH 8 +#define TILE_HEIGHT 8 + +struct scanlineData { + uint16_t bgcnts[4]; + uint16_t winMask[DISPLAY_WIDTH]; + uint16_t bgMask[DISPLAY_WIDTH]; + //priority bookkeeping + char bgtoprio[4]; //background to priority + char prioritySortedBgs[4][4]; + char prioritySortedBgsCount[4]; +}; + +struct bgPriority { + char priority; + char subPriority; +}; + +static const uint16_t bgMapSizes[][2] = +{ + {32, 32}, + {64, 32}, + {32, 64}, + {64, 64}, +}; + +static uint16_t alphaBlendColor(uint16_t targetA, uint16_t targetB) +{ + unsigned int eva = REG_BLDALPHA & 0x1F; + unsigned int evb = (REG_BLDALPHA >> 8) & 0x1F; + // shift right by 4 = division by 16 + unsigned int r = ((getRedChannel(targetA) * eva) + (getRedChannel(targetB) * evb)) >> 4; + unsigned int g = ((getGreenChannel(targetA) * eva) + (getGreenChannel(targetB) * evb)) >> 4; + unsigned int b = ((getBlueChannel(targetA) * eva) + (getBlueChannel(targetB) * evb)) >> 4; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return r | (g << 5) | (b << 10) | (1 << 15); +} + +static uint16_t alphaBrightnessIncrease(uint16_t targetA) +{ + unsigned int evy = (REG_BLDY & 0x1F); + unsigned int r = getRedChannel(targetA) + (31 - getRedChannel(targetA)) * evy / 16; + unsigned int g = getGreenChannel(targetA) + (31 - getGreenChannel(targetA)) * evy / 16; + unsigned int b = getBlueChannel(targetA) + (31 - getBlueChannel(targetA)) * evy / 16; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return r | (g << 5) | (b << 10) | (1 << 15); +} + +static uint16_t alphaBrightnessDecrease(uint16_t targetA) +{ + unsigned int evy = (REG_BLDY & 0x1F); + unsigned int r = getRedChannel(targetA) - getRedChannel(targetA) * evy / 16; + unsigned int g = getGreenChannel(targetA) - getGreenChannel(targetA) * evy / 16; + unsigned int b = getBlueChannel(targetA) - getBlueChannel(targetA) * evy / 16; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return r | (g << 5) | (b << 10) | (1 << 15); +} + +#define WINMASK_BG0 (1 << 0) +#define WINMASK_BG1 (1 << 1) +#define WINMASK_BG2 (1 << 2) +#define WINMASK_BG3 (1 << 3) +#define WINMASK_OBJ (1 << 4) +#define WINMASK_CLR (1 << 5) +#define WINMASK_WINOUT (1 << 6) + +//checks if window horizontal is in bounds and takes account WIN wraparound +static bool winCheckHorizontalBounds(u16 left, u16 right, u16 xpos) +{ + if (left > right) + return (xpos >= left || xpos < right); + else + return (xpos >= left && xpos < right); +} + +static void RenderBGScanlineWinBlend(int bgNum, uint16_t control, uint16_t hoffs, uint16_t voffs, int lineNum, uint16_t *line, struct scanlineData* scanline, bool windowsEnabled) +{ + unsigned int charBaseBlock = (control >> 2) & 3; + unsigned int screenBaseBlock = (control >> 8) & 0x1F; + unsigned int bitsPerPixel = 4;//((control >> 7) & 1) ? 8 : 4; + unsigned int mapWidth = bgMapSizes[control >> 14][0]; + unsigned int mapHeight = bgMapSizes[control >> 14][1]; + unsigned int mapWidthInPixels = mapWidth * 8; + unsigned int mapHeightInPixels = mapHeight * 8; + uint16_t *bgmap = (uint16_t *)BG_SCREEN_ADDR(screenBaseBlock); + uint16_t* mask = scanline->bgMask; + uint8_t blendMode = (REG_BLDCNT >> 6) & 3; + + //for (int i = 0; i < DISPLAY_WIDTH; i++) + //{ + // line[i] = 0x8F00; + //} + //return; + + uint8_t *bgtiles = (uint8_t *)BG_CHAR_ADDR(charBaseBlock); + uint16_t *pal = (uint16_t *)PLTT; + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + unsigned int xx = hoffs & 0x1FF; + unsigned int yy = (lineNum + voffs) & 0x1FF; + + + if (yy > 255 && mapHeightInPixels > 256) { + //the width check is for 512x512 mode support, it jumps by two screen bases instead + bgmap += (mapWidthInPixels > 256) ? 0x800 : 0x400; + } + + xx &= 0x1FF; + yy &= 0xFF; + + uint32_t lineTile = ((lineNum + voffs) & 0xFF) / 8; + uint32_t firstTile = bgmap[lineTile*32] & 0x3FF; + uint32_t firstTileData = *(uint32_t*)&bgtiles[(firstTile * 32) + ((lineTile & 7) * bitsPerPixel)]; + if (firstTileData == 0 && mapWidthInPixels > 256) + { + for (uint32_t i = 1; i < 32; i++) + { + uint32_t tile = bgmap[lineTile*32+i] & 0x3FF; + if (firstTile != tile) + goto exitBlankScanlineLoop; + } + if (mapWidthInPixels > 256) + { + for (uint32_t i = 0; i < 32; i++) + { + uint32_t tile = bgmap[lineTile*32+i+0x400] & 0x3FF; + if (firstTile != tile) + goto exitBlankScanlineLoop; + } + } + return; + } + exitBlankScanlineLoop:; + + unsigned int startTileX = xx / 8; + unsigned int startTileY = yy / 8; + unsigned int startTileYLoc = startTileY * 32; + unsigned int visibleTilePixels = xx & 7; + unsigned int tileY = yy & 7; + uint32_t entry; + + if (startTileX > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + (startTileX & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + (startTileX & 31)]; + unsigned int tileNum = entry & 0x3FF; + unsigned int paletteNum = (entry >> 12) & 0xF; + uint32_t tileLoc; + uint32_t pixel; + unsigned int x = 0; + //bool shouldBlend = true; + + //memset(line, 0x8F00, DISPLAY_WIDTH*2); + //for (int i = 0; i < DISPLAY_WIDTH; i++) + //{ + // line[i] = 0x8F00; + //} + + #define writeBgPixel(pixel, x) \ + if (scanline->winMask[x] & WINMASK_CLR) { \ + switch (blendMode) { \ + case 1: \ + if ((mask[x] & (REG_BLDCNT >> 8))) \ + line[x] = alphaBlendColor(pal[(paletteNum << 4) + (pixel)], line[x]) | 0x8000; \ + else \ + line[x] = pal[(paletteNum << 4) + (pixel)] | 0x8000;\ + break; \ + case 2: \ + line[x] = alphaBrightnessIncrease(pal[(paletteNum << 4) + (pixel)]) | 0x8000; \ + break; \ + case 3: \ + line[x] = alphaBrightnessDecrease(pal[(paletteNum << 4) + (pixel)]) | 0x8000; \ + break; } \ + }else{ \ + line[x] = pal[(paletteNum << 4) + (pixel)] | 0x8000; \ + } \ + mask[x] = 1 << bgNum; + + #define writeBgPixelWin(pixel, x) \ + if ( ((pixel)) != 0 ) { \ + if (scanline->winMask[x] & (1 << bgNum)) { \ + writeBgPixel(pixel, x) \ + } \ + } + + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + + //draw left most tile + if (entry & (1 << 10)) + { + for (int i = visibleTilePixels; i < 8; i++) + { + pixel = bgtiles[tileLoc + (3-(i/2))]; + + if (i & 1) + pixel &= 0xF; + else + pixel >>= 4; + + //if (pixel != 0) + writeBgPixelWin(pixel, x) + x += 1; + } + } + else + { + for (int i = visibleTilePixels; i < 8; i++) + { + pixel = bgtiles[tileLoc + (i/2)]; + + if (i & 1) + pixel >>= 4; + else + pixel &= 0xF; + + writeBgPixelWin(pixel, x) + x += 1; + } + } + + //draw all middle pixels + for (int currTile = 1; currTile < ( (DISPLAY_WIDTH / 8)); currTile++) + { + if (( (startTileX+currTile) & 63) > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + ((startTileX+currTile) & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + ((startTileX+currTile) & 31)]; + tileNum = entry & 0x3FF; + paletteNum = (entry >> 12) & 0xF; + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + uint32_t pixel32 = *(uint32_t*)&bgtiles[tileLoc]; + if (pixel32 != 0) { + if (entry & (1 << 10)) + { + writeBgPixelWin(pixel32 >> 28, x) + writeBgPixelWin((pixel32 >> 24) & 0xF, x+1) + writeBgPixelWin((pixel32 >> 20) & 0xF, x+2) + writeBgPixelWin((pixel32 >> 16) & 0xF, x+3) + writeBgPixelWin((pixel32 >> 12) & 0xF, x+4) + writeBgPixelWin((pixel32 >> 8) & 0xF, x+5) + writeBgPixelWin((pixel32 >> 4) & 0xF, x+6) + writeBgPixelWin(pixel32 & 0xF, x+7) + } + else + { + writeBgPixelWin(pixel32 & 0xF, x) + writeBgPixelWin((pixel32 >> 4) & 0xF, x+1) + writeBgPixelWin((pixel32 >> 8) & 0xF, x+2) + writeBgPixelWin((pixel32 >> 12) & 0xF, x+3) + writeBgPixelWin((pixel32 >> 16) & 0xF, x+4) + writeBgPixelWin((pixel32 >> 20) & 0xF, x+5) + writeBgPixelWin((pixel32 >> 24) & 0xF, x+6) + writeBgPixelWin(pixel32 >> 28, x+7) + } + } + x += 8; + } + //draw right most tile + if (((startTileX+(DISPLAY_WIDTH/8)) & 63) > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + ((startTileX+(DISPLAY_WIDTH/8)) & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + ((startTileX+(DISPLAY_WIDTH/8)) & 31)]; + tileNum = entry & 0x3FF; + paletteNum = (entry >> 12) & 0xF; + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + if((*(uint32_t*)&bgtiles[tileLoc]) != 0) + { + if (entry & (1 << 10)) //horizontally flipped + { + for (int i = 0; i < visibleTilePixels; i++) + { + pixel = bgtiles[tileLoc + (3-(i/2))]; + + if (i & 1) + pixel &= 0xF; + else + pixel >>= 4; + + //if (pixel != 0) + writeBgPixelWin(pixel, x) + //line[x] = pal[16 * paletteNum + pixel] | 0x8000; + x += 1; + } + } + else //not horizontally flipped + { + for (int i = 0; i < visibleTilePixels; i++) + { + pixel = bgtiles[tileLoc + (i/2)]; + + if (i & 1) + pixel >>= 4; + else + pixel &= 0xF; + + writeBgPixelWin(pixel, x) + x += 1; + } + } + } + //apply horiziontal mosaic if needed + if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t color = line[applyBGHorizontalMosaicEffect(x)]; + line[x] = color; + } + } +} + +#undef writeBgPixel +#undef writeBgPixelWin + +static void RenderBGScanlineWin(int bgNum, uint16_t control, uint16_t hoffs, uint16_t voffs, int lineNum, uint16_t *line, struct scanlineData* scanline, bool windowsEnabled) +{ + unsigned int charBaseBlock = (control >> 2) & 3; + unsigned int screenBaseBlock = (control >> 8) & 0x1F; + unsigned int bitsPerPixel = 4;//((control >> 7) & 1) ? 8 : 4; + unsigned int mapWidth = bgMapSizes[control >> 14][0]; + unsigned int mapHeight = bgMapSizes[control >> 14][1]; + unsigned int mapWidthInPixels = mapWidth * 8; + unsigned int mapHeightInPixels = mapHeight * 8; + uint16_t *bgmap = (uint16_t *)BG_SCREEN_ADDR(screenBaseBlock); + uint16_t* mask = scanline->bgMask; + uint8_t blendMode = (REG_BLDCNT >> 6) & 3; + + //for (int i = 0; i < DISPLAY_WIDTH; i++) + //{ + // line[i] = 0x8F00; + //} + //return; + + uint8_t *bgtiles = (uint8_t *)BG_CHAR_ADDR(charBaseBlock); + uint16_t *pal = (uint16_t *)PLTT; + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + unsigned int xx = hoffs & 0x1FF; + unsigned int yy = (lineNum + voffs) & 0x1FF; + + + if (yy > 255 && mapHeightInPixels > 256) { + //the width check is for 512x512 mode support, it jumps by two screen bases instead + bgmap += (mapWidthInPixels > 256) ? 0x800 : 0x400; + } + + xx &= 0x1FF; + yy &= 0xFF; + + uint32_t lineTile = ((lineNum + voffs) & 0xFF) / 8; + uint32_t firstTile = bgmap[lineTile*32] & 0x3FF; + uint32_t firstTileData = *(uint32_t*)&bgtiles[(firstTile * 32) + ((lineTile & 7) * bitsPerPixel)]; + if (firstTileData == 0 && mapWidthInPixels > 256) + { + for (uint32_t i = 1; i < 32; i++) + { + uint32_t tile = bgmap[lineTile*32+i] & 0x3FF; + if (firstTile != tile) + goto exitBlankScanlineLoop; + } + if (mapWidthInPixels > 256) + { + for (uint32_t i = 0; i < 32; i++) + { + uint32_t tile = bgmap[lineTile*32+i+0x400] & 0x3FF; + if (firstTile != tile) + goto exitBlankScanlineLoop; + } + } + return; + } + exitBlankScanlineLoop:; + + unsigned int startTileX = xx / 8; + unsigned int startTileY = yy / 8; + unsigned int startTileYLoc = startTileY * 32; + unsigned int visibleTilePixels = xx & 7; + unsigned int tileY = yy & 7; + uint32_t entry; + + if (startTileX > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + (startTileX & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + (startTileX & 31)]; + unsigned int tileNum = entry & 0x3FF; + unsigned int paletteNum = (entry >> 12) & 0xF; + uint32_t tileLoc; + uint32_t pixel; + unsigned int x = 0; + + //memset(line, 0x8F00, DISPLAY_WIDTH*2); + //for (int i = 0; i < DISPLAY_WIDTH; i++) + //{ + // line[i] = 0x8F00; + //} + + #define writeBgPixel(pixel, x) \ + line[x] = pal[(paletteNum << 4) + (pixel)] | 0x8000; \ + mask[x] = 1 << bgNum; + + #define writeBgPixelWin(pixel, x) \ + if ( ((pixel)) != 0 ) { \ + if (scanline->winMask[x] & (1 << bgNum)) { \ + writeBgPixel(pixel, x) \ + } \ + } + + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + //if((*(uint32_t*)&bgtiles[tileLoc]) != 0) + //{ + //draw left most tile + if (entry & (1 << 10)) + { + for (int i = visibleTilePixels; i < 8; i++) + { + pixel = bgtiles[tileLoc + (3-(i/2))]; + + if (i & 1) + pixel &= 0xF; + else + pixel >>= 4; + + //if (pixel != 0) + writeBgPixelWin(pixel, x) + x += 1; + } + } + else + { + for (int i = visibleTilePixels; i < 8; i++) + { + pixel = bgtiles[tileLoc + (i/2)]; + + if (i & 1) + pixel >>= 4; + else + pixel &= 0xF; + + writeBgPixelWin(pixel, x) + x += 1; + } + } + //} + + //draw all middle pixels + for (int currTile = 1; currTile < ( (DISPLAY_WIDTH / 8)); currTile++) + { + if (( (startTileX+currTile) & 63) > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + ((startTileX+currTile) & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + ((startTileX+currTile) & 31)]; + tileNum = entry & 0x3FF; + paletteNum = (entry >> 12) & 0xF; + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + uint32_t pixel32 = *(uint32_t*)&bgtiles[tileLoc]; + if (pixel32 != 0) { + if (entry & (1 << 10)) + { + writeBgPixelWin(pixel32 >> 28, x) + writeBgPixelWin((pixel32 >> 24) & 0xF, x+1) + writeBgPixelWin((pixel32 >> 20) & 0xF, x+2) + writeBgPixelWin((pixel32 >> 16) & 0xF, x+3) + writeBgPixelWin((pixel32 >> 12) & 0xF, x+4) + writeBgPixelWin((pixel32 >> 8) & 0xF, x+5) + writeBgPixelWin((pixel32 >> 4) & 0xF, x+6) + writeBgPixelWin(pixel32 & 0xF, x+7) + } + else + { + writeBgPixelWin(pixel32 & 0xF, x) + writeBgPixelWin((pixel32 >> 4) & 0xF, x+1) + writeBgPixelWin((pixel32 >> 8) & 0xF, x+2) + writeBgPixelWin((pixel32 >> 12) & 0xF, x+3) + writeBgPixelWin((pixel32 >> 16) & 0xF, x+4) + writeBgPixelWin((pixel32 >> 20) & 0xF, x+5) + writeBgPixelWin((pixel32 >> 24) & 0xF, x+6) + writeBgPixelWin(pixel32 >> 28, x+7) + } + } + x += 8; + } + //draw right most tile + if (((startTileX+(DISPLAY_WIDTH/8)) & 63) > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + ((startTileX+(DISPLAY_WIDTH/8)) & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + ((startTileX+(DISPLAY_WIDTH/8)) & 31)]; + tileNum = entry & 0x3FF; + paletteNum = (entry >> 12) & 0xF; + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + if((*(uint32_t*)&bgtiles[tileLoc]) != 0) + { + if (entry & (1 << 10)) //horizontally flipped + { + for (int i = 0; i < visibleTilePixels; i++) + { + pixel = bgtiles[tileLoc + (3-(i/2))]; + + if (i & 1) + pixel &= 0xF; + else + pixel >>= 4; + + //if (pixel != 0) + writeBgPixelWin(pixel, x) + //line[x] = pal[16 * paletteNum + pixel] | 0x8000; + x += 1; + } + } + else //not horizontally flipped + { + for (int i = 0; i < visibleTilePixels; i++) + { + pixel = bgtiles[tileLoc + (i/2)]; + + if (i & 1) + pixel >>= 4; + else + pixel &= 0xF; + + writeBgPixelWin(pixel, x) + x += 1; + } + } + } + //apply horiziontal mosaic if needed + if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t color = line[applyBGHorizontalMosaicEffect(x)]; + line[x] = color; + } + } +} + +#undef writeBgPixel +#undef writeBgPixelWin + +static void RenderBGScanlineBlend(int bgNum, uint16_t control, uint16_t hoffs, uint16_t voffs, int lineNum, uint16_t *line, struct scanlineData* scanline, bool windowsEnabled) +{ + unsigned int charBaseBlock = (control >> 2) & 3; + unsigned int screenBaseBlock = (control >> 8) & 0x1F; + unsigned int bitsPerPixel = 4;//((control >> 7) & 1) ? 8 : 4; + unsigned int mapWidth = bgMapSizes[control >> 14][0]; + unsigned int mapHeight = bgMapSizes[control >> 14][1]; + unsigned int mapWidthInPixels = mapWidth * 8; + unsigned int mapHeightInPixels = mapHeight * 8; + uint16_t *bgmap = (uint16_t *)BG_SCREEN_ADDR(screenBaseBlock); + uint16_t* mask = scanline->bgMask; + uint8_t blendMode = (REG_BLDCNT >> 6) & 3; + + if (windowsEnabled) + { + if (!(REG_WINOUT & (1 << bgNum))) + return; + } + + uint8_t *bgtiles = (uint8_t *)BG_CHAR_ADDR(charBaseBlock); + uint16_t *pal = (uint16_t *)PLTT; + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + unsigned int xx = hoffs & 0x1FF; + unsigned int yy = (lineNum + voffs) & 0x1FF; + + + if (yy > 255 && mapHeightInPixels > 256) { + //the width check is for 512x512 mode support, it jumps by two screen bases instead + bgmap += (mapWidthInPixels > 256) ? 0x800 : 0x400; + } + + xx &= 0x1FF; + yy &= 0xFF; + + uint32_t lineTile = ((lineNum + voffs) & 0xFF) / 8; + uint32_t firstTile = bgmap[lineTile*32] & 0x3FF; + uint32_t firstTileData = *(uint32_t*)&bgtiles[(firstTile * 32) + ((lineTile & 7) * bitsPerPixel)]; + if (firstTileData == 0 && mapWidthInPixels > 256) + { + for (uint32_t i = 1; i < 32; i++) + { + uint32_t tile = bgmap[lineTile*32+i] & 0x3FF; + if (firstTile != tile) + goto exitBlankScanlineLoop; + } + if (mapWidthInPixels > 256) + { + for (uint32_t i = 0; i < 32; i++) + { + uint32_t tile = bgmap[lineTile*32+i+0x400] & 0x3FF; + if (firstTile != tile) + goto exitBlankScanlineLoop; + } + } + return; + } + exitBlankScanlineLoop:; + + unsigned int startTileX = xx / 8; + unsigned int startTileY = yy / 8; + unsigned int startTileYLoc = startTileY * 32; + unsigned int visibleTilePixels = xx & 7; + unsigned int tileY = yy & 7; + uint32_t entry; + + if (startTileX > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + (startTileX & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + (startTileX & 31)]; + unsigned int tileNum = entry & 0x3FF; + unsigned int paletteNum = (entry >> 12) & 0xF; + uint32_t tileLoc; + uint32_t pixel; + unsigned int x = 0; + + //memset(line, 0x8F00, DISPLAY_WIDTH*2); + //for (int i = 0; i < DISPLAY_WIDTH; i++) + //{ + // line[i] = 0x8F00; + //} + + #define writeBgPixel(pixel, x) \ + if ( ((pixel)) != 0 ) { \ + switch (blendMode) {\ + case 1: \ + if ((mask[x] & (REG_BLDCNT >> 8))) \ + line[x] = alphaBlendColor(pal[(paletteNum << 4) + (pixel)], line[x]) | 0x8000; \ + else \ + line[x] = pal[(paletteNum << 4) + (pixel)] | 0x8000;\ + break; \ + case 2: \ + line[x] = alphaBrightnessIncrease(pal[(paletteNum << 4) + (pixel)]) | 0x8000; \ + break; \ + case 3: \ + line[x] = alphaBrightnessDecrease(pal[(paletteNum << 4) + (pixel)]) | 0x8000; \ + break; } \ + mask[x] = 1 << bgNum; \ + } else \ + { \ + line[x] = 0; \ + } + + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + //if((*(uint32_t*)&bgtiles[tileLoc]) != 0) + { + //draw left most tile + if (entry & (1 << 10)) + { + for (int i = visibleTilePixels; i < 8; i++) + { + pixel = bgtiles[tileLoc + (3-(i/2))]; + + if (i & 1) + pixel &= 0xF; + else + pixel >>= 4; + + //if (pixel != 0) + writeBgPixel(pixel, x) + x += 1; + } + } + else + { + for (int i = visibleTilePixels; i < 8; i++) + { + pixel = bgtiles[tileLoc + (i/2)]; + + if (i & 1) + pixel >>= 4; + else + pixel &= 0xF; + + writeBgPixel(pixel, x) + x += 1; + } + } + } + + //draw all middle pixels + for (int currTile = 1; currTile < ( (DISPLAY_WIDTH / 8)); currTile++) + { + if (( (startTileX+currTile) & 63) > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + ((startTileX+currTile) & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + ((startTileX+currTile) & 31)]; + tileNum = entry & 0x3FF; + paletteNum = (entry >> 12) & 0xF; + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + uint32_t pixel32 = *(uint32_t*)&bgtiles[tileLoc]; + if (pixel32 != 0) { + if (entry & (1 << 10)) + { + writeBgPixel(pixel32 >> 28, x) + writeBgPixel((pixel32 >> 24) & 0xF, x+1) + writeBgPixel((pixel32 >> 20) & 0xF, x+2) + writeBgPixel((pixel32 >> 16) & 0xF, x+3) + writeBgPixel((pixel32 >> 12) & 0xF, x+4) + writeBgPixel((pixel32 >> 8) & 0xF, x+5) + writeBgPixel((pixel32 >> 4) & 0xF, x+6) + writeBgPixel(pixel32 & 0xF, x+7) + } + else + { + writeBgPixel(pixel32 & 0xF, x) + writeBgPixel((pixel32 >> 4) & 0xF, x+1) + writeBgPixel((pixel32 >> 8) & 0xF, x+2) + writeBgPixel((pixel32 >> 12) & 0xF, x+3) + writeBgPixel((pixel32 >> 16) & 0xF, x+4) + writeBgPixel((pixel32 >> 20) & 0xF, x+5) + writeBgPixel((pixel32 >> 24) & 0xF, x+6) + writeBgPixel(pixel32 >> 28, x+7) + } + } + x += 8; + } + //draw right most tile + if (((startTileX+(DISPLAY_WIDTH/8)) & 63) > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + ((startTileX+(DISPLAY_WIDTH/8)) & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + ((startTileX+(DISPLAY_WIDTH/8)) & 31)]; + tileNum = entry & 0x3FF; + paletteNum = (entry >> 12) & 0xF; + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + if((*(uint32_t*)&bgtiles[tileLoc]) != 0) + { + if (entry & (1 << 10)) //horizontally flipped + { + for (int i = 0; i < visibleTilePixels; i++) + { + pixel = bgtiles[tileLoc + (3-(i/2))]; + + if (i & 1) + pixel &= 0xF; + else + pixel >>= 4; + + //if (pixel != 0) + writeBgPixel(pixel, x) + //line[x] = pal[16 * paletteNum + pixel] | 0x8000; + x += 1; + } + } + else //not horizontally flipped + { + for (int i = 0; i < visibleTilePixels; i++) + { + pixel = bgtiles[tileLoc + (i/2)]; + + if (i & 1) + pixel >>= 4; + else + pixel &= 0xF; + + writeBgPixel(pixel, x) + x += 1; + } + } + } + //apply horiziontal mosaic if needed + if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t color = line[applyBGHorizontalMosaicEffect(x)]; + line[x] = color; + } + } +} +#undef writeBgPixel + +static void RenderBGScanlineNoEffect(int bgNum, uint16_t control, uint16_t hoffs, uint16_t voffs, int lineNum, uint16_t *line, struct scanlineData* scanline, bool windowsEnabled) +{ + unsigned int charBaseBlock = (control >> 2) & 3; + unsigned int screenBaseBlock = (control >> 8) & 0x1F; + unsigned int bitsPerPixel = 4;//((control >> 7) & 1) ? 8 : 4; + unsigned int mapWidth = bgMapSizes[control >> 14][0]; + unsigned int mapHeight = bgMapSizes[control >> 14][1]; + unsigned int mapWidthInPixels = mapWidth * 8; + unsigned int mapHeightInPixels = mapHeight * 8; + uint16_t *bgmap = (uint16_t *)BG_SCREEN_ADDR(screenBaseBlock); + uint16_t* mask = scanline->bgMask; + uint8_t blendMode = (REG_BLDCNT >> 6) & 3; + + if (windowsEnabled) + { + if (!(REG_WINOUT & (1 << bgNum))) + return; + } + + uint8_t *bgtiles = (uint8_t *)BG_CHAR_ADDR(charBaseBlock); + uint16_t *pal = (uint16_t *)PLTT; + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + unsigned int xx = hoffs & 0x1FF; + unsigned int yy = (lineNum + voffs) & 0x1FF; + + + if (yy > 255 && mapHeightInPixels > 256) { + //the width check is for 512x512 mode support, it jumps by two screen bases instead + bgmap += (mapWidthInPixels > 256) ? 0x800 : 0x400; + } + + xx &= 0x1FF; + yy &= 0xFF; + + uint32_t lineTile = ((lineNum + voffs) & 0xFF) / 8; + uint32_t firstTile = bgmap[lineTile*32] & 0x3FF; + uint32_t firstTileData = *(uint32_t*)&bgtiles[(firstTile * 32) + ((lineTile & 7) * bitsPerPixel)]; + if (firstTileData == 0 && mapWidthInPixels > 256) + { + for (uint32_t i = 1; i < 32; i++) + { + uint32_t tile = bgmap[lineTile*32+i] & 0x3FF; + if (firstTile != tile) + goto exitBlankScanlineLoop; + } + if (mapWidthInPixels > 256) + { + for (uint32_t i = 0; i < 32; i++) + { + uint32_t tile = bgmap[lineTile*32+i+0x400] & 0x3FF; + if (firstTile != tile) + goto exitBlankScanlineLoop; + } + } + return; + } + exitBlankScanlineLoop:; + + unsigned int startTileX = xx / 8; + unsigned int startTileY = yy / 8; + unsigned int startTileYLoc = startTileY * 32; + unsigned int visibleTilePixels = xx & 7; + unsigned int tileY = yy & 7; + uint32_t entry; + + if (startTileX > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + (startTileX & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + (startTileX & 31)]; + unsigned int tileNum = entry & 0x3FF; + unsigned int paletteNum = (entry >> 12) & 0xF; + uint32_t tileLoc; + uint32_t pixel; + unsigned int x = 0; + bool shouldBlend = true; + + //memset(line, 0x8F00, DISPLAY_WIDTH*2); + //for (int i = 0; i < DISPLAY_WIDTH; i++) + //{ + // line[i] = 0x8F00; + //} + + #define writeBgPixel(pixel, x) \ + if ( ((pixel)) != 0 ) { \ + line[x] = pal[(paletteNum << 4) + (pixel)] | 0x8000; \ + mask[x] = 1 << bgNum; \ + } \ + else \ + { \ + line[x] = 0; \ + } \ + + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + //if((*(uint32_t*)&bgtiles[tileLoc]) != 0) + { + //draw left most tile + if (entry & (1 << 10)) + { + for (int i = visibleTilePixels; i < 8; i++) + { + pixel = bgtiles[tileLoc + (3-(i/2))]; + + if (i & 1) + pixel &= 0xF; + else + pixel >>= 4; + + //if (pixel != 0) + writeBgPixel(pixel, x) + x += 1; + } + } + else + { + for (int i = visibleTilePixels; i < 8; i++) + { + pixel = bgtiles[tileLoc + (i/2)]; + + if (i & 1) + pixel >>= 4; + else + pixel &= 0xF; + + writeBgPixel(pixel, x) + x += 1; + } + } + } + + //draw all middle pixels + for (int currTile = 1; currTile < ( (DISPLAY_WIDTH / 8)); currTile++) + { + if (( (startTileX+currTile) & 63) > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + ((startTileX+currTile) & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + ((startTileX+currTile) & 31)]; + tileNum = entry & 0x3FF; + paletteNum = (entry >> 12) & 0xF; + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + uint32_t pixel32 = *(uint32_t*)&bgtiles[tileLoc]; + if (pixel32 != 0) { + if (entry & (1 << 10)) + { + writeBgPixel(pixel32 >> 28, x) + writeBgPixel((pixel32 >> 24) & 0xF, x+1) + writeBgPixel((pixel32 >> 20) & 0xF, x+2) + writeBgPixel((pixel32 >> 16) & 0xF, x+3) + writeBgPixel((pixel32 >> 12) & 0xF, x+4) + writeBgPixel((pixel32 >> 8) & 0xF, x+5) + writeBgPixel((pixel32 >> 4) & 0xF, x+6) + writeBgPixel(pixel32 & 0xF, x+7) + } + else + { + writeBgPixel(pixel32 & 0xF, x) + writeBgPixel((pixel32 >> 4) & 0xF, x+1) + writeBgPixel((pixel32 >> 8) & 0xF, x+2) + writeBgPixel((pixel32 >> 12) & 0xF, x+3) + writeBgPixel((pixel32 >> 16) & 0xF, x+4) + writeBgPixel((pixel32 >> 20) & 0xF, x+5) + writeBgPixel((pixel32 >> 24) & 0xF, x+6) + writeBgPixel(pixel32 >> 28, x+7) + } + } + x += 8; + } + //draw right most tile + if (((startTileX+(DISPLAY_WIDTH/8)) & 63) > 31 && mapWidthInPixels > 256) + entry = bgmap[startTileYLoc + ((startTileX+(DISPLAY_WIDTH/8)) & 31) + 0x400]; + else + entry = bgmap[startTileYLoc + ((startTileX+(DISPLAY_WIDTH/8)) & 31)]; + tileNum = entry & 0x3FF; + paletteNum = (entry >> 12) & 0xF; + if (entry & (1 << 11)) + tileLoc = (tileNum * (bitsPerPixel * 8)) + (7 - tileY)*bitsPerPixel; + else + tileLoc = (tileNum * (bitsPerPixel * 8)) + tileY*bitsPerPixel; + if((*(uint32_t*)&bgtiles[tileLoc]) != 0) + { + if (entry & (1 << 10)) //horizontally flipped + { + for (int i = 0; i < visibleTilePixels; i++) + { + pixel = bgtiles[tileLoc + (3-(i/2))]; + + if (i & 1) + pixel &= 0xF; + else + pixel >>= 4; + + //if (pixel != 0) + writeBgPixel(pixel, x) + //line[x] = pal[16 * paletteNum + pixel] | 0x8000; + x += 1; + } + } + else //not horizontally flipped + { + for (int i = 0; i < visibleTilePixels; i++) + { + pixel = bgtiles[tileLoc + (i/2)]; + + if (i & 1) + pixel >>= 4; + else + pixel &= 0xF; + + writeBgPixel(pixel, x) + x += 1; + } + } + } + //apply horiziontal mosaic if needed + if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t color = line[applyBGHorizontalMosaicEffect(x)]; + line[x] = color; + } + } +} +#undef writeBgPixel + +static inline uint32_t getBgX(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2X; + } + else if (bgNumber == 3) + { + return REG_BG3X; + } +} + +static inline uint32_t getBgY(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2Y; + } + else if (bgNumber == 3) + { + return REG_BG3Y; + } +} + +static inline uint16_t getBgPA(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2PA; + } + else if (bgNumber == 3) + { + return REG_BG3PA; + } +} + +static inline uint16_t getBgPB(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2PB; + } + else if (bgNumber == 3) + { + return REG_BG3PB; + } +} + +static inline uint16_t getBgPC(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2PC; + } + else if (bgNumber == 3) + { + return REG_BG3PC; + } +} + +static inline uint16_t getBgPD(int bgNumber) +{ + if (bgNumber == 2) + { + return REG_BG2PD; + } + else if (bgNumber == 3) + { + return REG_BG3PD; + } +} + +static void RenderRotScaleBGScanlineWinBlend(int bgNum, uint16_t control, uint16_t x, uint16_t y, int lineNum, uint16_t *line, struct scanlineData* scanline, bool windowsEnabled) +{ + vBgCnt *bgcnt = (vBgCnt *)&control; + unsigned int charBaseBlock = bgcnt->charBaseBlock; + unsigned int screenBaseBlock = bgcnt->screenBaseBlock; + unsigned int mapWidth = 1 << (4 + (bgcnt->screenSize)); // number of tiles + + uint8_t *bgtiles = (uint8_t *)(VRAM_ + charBaseBlock * 0x4000); + uint8_t *bgmap = (uint8_t *)(VRAM_ + screenBaseBlock * 0x800); + uint16_t *pal = (uint16_t *)PLTT; + uint8_t blendMode = (REG_BLDCNT >> 6) & 3; + uint16_t* mask = scanline->bgMask; + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + + s16 pa = getBgPA(bgNum); + s16 pb = getBgPB(bgNum); + s16 pc = getBgPC(bgNum); + s16 pd = getBgPD(bgNum); + + int sizeX = 128; + int sizeY = 128; + + switch (bgcnt->screenSize) + { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + int maskX = sizeX - 1; + int maskY = sizeY - 1; + int yshift = ((control >> 14) & 3) + 4; + int currentX = getBgX(bgNum); + int currentY = getBgY(bgNum); + //sign extend 28 bit number + currentX = ((currentX & (1 << 27)) ? currentX | 0xF0000000 : currentX); + currentY = ((currentY & (1 << 27)) ? currentY | 0xF0000000 : currentY); + + currentX += lineNum * pb; + currentY += lineNum * pd; + + int realX = currentX; + int realY = currentY; + + #define writeBgPixel(pixel, x) \ + if (scanline->winMask[x] & WINMASK_CLR) { \ + switch (blendMode) { \ + case 1: \ + if ((mask[x] & (REG_BLDCNT >> 8))) \ + line[x] = alphaBlendColor(pal[pixel], line[x]) | 0x8000; \ + else \ + line[x] = pal[pixel] | 0x8000;\ + break; \ + case 2: \ + line[x] = alphaBrightnessIncrease(pal[pixel]) | 0x8000; \ + break; \ + case 3: \ + line[x] = alphaBrightnessDecrease(pal[pixel]) | 0x8000; \ + break; } \ + }else{ \ + line[x] = pal[pixel] | 0x8000; \ + } \ + mask[x] = 1 << bgNum; + + #define writeBgPixelWin(pixel, x) \ + if ( ((pixel)) != 0 ) { \ + if (scanline->winMask[x] & (1 << bgNum)) { \ + writeBgPixel(pixel, x) \ + } \ + } + + if (bgcnt->areaOverflowMode) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8) & maskX; + int yyy = (realY >> 8) & maskY; + + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + writeBgPixelWin(pixel, x) + + realX += pa; + realY += pc; + } + } + else + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + if (xxx < 0 || yyy < 0 || xxx >= sizeX || yyy >= sizeY) + { + //line[x] = 0x80000000; + } + else + { + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + writeBgPixelWin(pixel, x) + } + realX += pa; + realY += pc; + } + } + //apply mosaic effect if enabled + if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t color = line[applyBGHorizontalMosaicEffect(x)]; + line[x] = color; + + } + } +} + +#undef writeBgPixel +#undef writeBgPixelWin + +static void RenderRotScaleBGScanlineWin(int bgNum, uint16_t control, uint16_t x, uint16_t y, int lineNum, uint16_t *line, struct scanlineData* scanline, bool windowsEnabled) +{ + vBgCnt *bgcnt = (vBgCnt *)&control; + unsigned int charBaseBlock = bgcnt->charBaseBlock; + unsigned int screenBaseBlock = bgcnt->screenBaseBlock; + unsigned int mapWidth = 1 << (4 + (bgcnt->screenSize)); // number of tiles + + uint8_t *bgtiles = (uint8_t *)(VRAM_ + charBaseBlock * 0x4000); + uint8_t *bgmap = (uint8_t *)(VRAM_ + screenBaseBlock * 0x800); + uint16_t *pal = (uint16_t *)PLTT; + uint16_t* mask = scanline->bgMask; + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + + s16 pa = getBgPA(bgNum); + s16 pb = getBgPB(bgNum); + s16 pc = getBgPC(bgNum); + s16 pd = getBgPD(bgNum); + + int sizeX = 128; + int sizeY = 128; + + switch (bgcnt->screenSize) + { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + int maskX = sizeX - 1; + int maskY = sizeY - 1; + int yshift = ((control >> 14) & 3) + 4; + int currentX = getBgX(bgNum); + int currentY = getBgY(bgNum); + //sign extend 28 bit number + currentX = ((currentX & (1 << 27)) ? currentX | 0xF0000000 : currentX); + currentY = ((currentY & (1 << 27)) ? currentY | 0xF0000000 : currentY); + + currentX += lineNum * pb; + currentY += lineNum * pd; + + int realX = currentX; + int realY = currentY; + + #define writeBgPixel(pixel, x) \ + line[x] = pal[pixel] | 0x8000; \ + mask[x] = 1 << bgNum; + + #define writeBgPixelWin(pixel, x) \ + if ( ((pixel)) != 0 ) { \ + if (scanline->winMask[x] & (1 << bgNum)) { \ + writeBgPixel(pixel, x) \ + } \ + } + + if (bgcnt->areaOverflowMode) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8) & maskX; + int yyy = (realY >> 8) & maskY; + + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + writeBgPixelWin(pixel, x) + + realX += pa; + realY += pc; + } + } + else + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + if (xxx < 0 || yyy < 0 || xxx >= sizeX || yyy >= sizeY) + { + //line[x] = 0x80000000; + } + else + { + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + writeBgPixelWin(pixel, x) + } + realX += pa; + realY += pc; + } + } + //apply mosaic effect if enabled + if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t color = line[applyBGHorizontalMosaicEffect(x)]; + line[x] = color; + + } + } +} + +#undef writeBgPixel +#undef writeBgPixelWin + +static void RenderRotScaleBGScanlineBlend(int bgNum, uint16_t control, uint16_t x, uint16_t y, int lineNum, uint16_t *line, struct scanlineData* scanline, bool windowsEnabled) +{ + vBgCnt *bgcnt = (vBgCnt *)&control; + unsigned int charBaseBlock = bgcnt->charBaseBlock; + unsigned int screenBaseBlock = bgcnt->screenBaseBlock; + unsigned int mapWidth = 1 << (4 + (bgcnt->screenSize)); // number of tiles + + uint8_t *bgtiles = (uint8_t *)(VRAM_ + charBaseBlock * 0x4000); + uint8_t *bgmap = (uint8_t *)(VRAM_ + screenBaseBlock * 0x800); + uint16_t *pal = (uint16_t *)PLTT; + uint8_t blendMode = (REG_BLDCNT >> 6) & 3; + uint16_t* mask = scanline->bgMask; + + if (windowsEnabled) + { + if (!(REG_WINOUT & (1 << bgNum))) + return; + } + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + + s16 pa = getBgPA(bgNum); + s16 pb = getBgPB(bgNum); + s16 pc = getBgPC(bgNum); + s16 pd = getBgPD(bgNum); + + int sizeX = 128; + int sizeY = 128; + + + switch (bgcnt->screenSize) + { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + int maskX = sizeX - 1; + int maskY = sizeY - 1; + int yshift = ((control >> 14) & 3) + 4; + int currentX = getBgX(bgNum); + int currentY = getBgY(bgNum); + //sign extend 28 bit number + currentX = ((currentX & (1 << 27)) ? currentX | 0xF0000000 : currentX); + currentY = ((currentY & (1 << 27)) ? currentY | 0xF0000000 : currentY); + + currentX += lineNum * pb; + currentY += lineNum * pd; + + int realX = currentX; + int realY = currentY; + + #define writeBgPixel(pixel, x) \ + if ( ((pixel)) != 0 ) { \ + switch (blendMode) {\ + case 1: \ + if ((mask[x] & (REG_BLDCNT >> 8))) \ + line[x] = alphaBlendColor(pal[pixel], line[x]) | 0x8000; \ + else \ + line[x] = pal[pixel] | 0x8000;\ + break; \ + case 2: \ + line[x] = alphaBrightnessIncrease(pal[pixel]) | 0x8000; \ + break; \ + case 3: \ + line[x] = alphaBrightnessDecrease(pal[pixel]) | 0x8000; \ + break; } \ + mask[x] = 1 << bgNum; \ + } + + if (bgcnt->areaOverflowMode) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8) & maskX; + int yyy = (realY >> 8) & maskY; + + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + //if (pixel != 0) { + // line[x] = pal[pixel] | 0x8000; + //} + writeBgPixel(pixel, x) + + realX += pa; + realY += pc; + } + } + else + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + if (xxx < 0 || yyy < 0 || xxx >= sizeX || yyy >= sizeY) + { + //line[x] = 0x80000000; + } + else + { + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + writeBgPixel(pixel, x) + } + realX += pa; + realY += pc; + } + } + //apply mosaic effect if enabled + if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t color = line[applyBGHorizontalMosaicEffect(x)]; + line[x] = color; + + } + } +} + +#undef writeBgPixel + +static void RenderRotScaleBGScanlineNoEffect(int bgNum, uint16_t control, uint16_t x, uint16_t y, int lineNum, uint16_t *line, struct scanlineData* scanline, bool windowsEnabled) +{ + vBgCnt *bgcnt = (vBgCnt *)&control; + unsigned int charBaseBlock = bgcnt->charBaseBlock; + unsigned int screenBaseBlock = bgcnt->screenBaseBlock; + unsigned int mapWidth = 1 << (4 + (bgcnt->screenSize)); // number of tiles + + uint8_t *bgtiles = (uint8_t *)(VRAM_ + charBaseBlock * 0x4000); + uint8_t *bgmap = (uint8_t *)(VRAM_ + screenBaseBlock * 0x800); + uint16_t *pal = (uint16_t *)PLTT; + + if (windowsEnabled) + { + if (!(REG_WINOUT & (1 << bgNum))) + return; + } + + if (control & BGCNT_MOSAIC) + lineNum = applyBGVerticalMosaicEffect(lineNum); + + + s16 pa = getBgPA(bgNum); + s16 pb = getBgPB(bgNum); + s16 pc = getBgPC(bgNum); + s16 pd = getBgPD(bgNum); + + int sizeX = 128; + int sizeY = 128; + + switch (bgcnt->screenSize) + { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + int maskX = sizeX - 1; + int maskY = sizeY - 1; + int yshift = ((control >> 14) & 3) + 4; + int currentX = getBgX(bgNum); + int currentY = getBgY(bgNum); + //sign extend 28 bit number + currentX = ((currentX & (1 << 27)) ? currentX | 0xF0000000 : currentX); + currentY = ((currentY & (1 << 27)) ? currentY | 0xF0000000 : currentY); + + currentX += lineNum * pb; + currentY += lineNum * pd; + + int realX = currentX; + int realY = currentY; + + if (bgcnt->areaOverflowMode) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8) & maskX; + int yyy = (realY >> 8) & maskY; + + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + if (pixel != 0) { + line[x] = pal[pixel] | 0x8000; + } + + realX += pa; + realY += pc; + } + } + else + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + if (xxx < 0 || yyy < 0 || xxx >= sizeX || yyy >= sizeY) + { + //line[x] = 0x80000000; + } + else + { + int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)]; + + int tileX = xxx & 7; + int tileY = yyy & 7; + + uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX]; + + if (pixel != 0) { + line[x] = pal[pixel] | 0x8000; + } + } + realX += pa; + realY += pc; + } + } + //apply mosaic effect if enabled + if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0) + { + for (int x = 0; x < DISPLAY_WIDTH; x++) + { + uint16_t color = line[applyBGHorizontalMosaicEffect(x)]; + line[x] = color; + + } + } +} + +const u8 spriteSizes[][2] = +{ + {8, 16}, + {8, 32}, + {16, 32}, + {32, 64}, +}; + +static void DrawSpritesWinMask(struct scanlineData* scanline, uint16_t vcount) +{ + int i; + void *objtiles = VRAM_ + 0x10000; + + int16_t matrix[2][2] = {}; + + if (!(REG_DISPCNT & (1 << 6))) + { + puts("2-D OBJ Character mapping not supported."); + } + + for (i = 127; i >= 0; i--) + { + struct OamData *oam = &((struct OamData *)OAM)[i]; + if (oam->objMode != 2) + continue; + + unsigned int width; + unsigned int height; + + bool isAffine = oam->affineMode & 1; + bool doubleSizeOrDisabled = (oam->affineMode >> 1) & 1; + + if (!(isAffine) && doubleSizeOrDisabled) // disable for non-affine + { + continue; + } + + if (oam->shape == 0) + { + width = (1 << oam->size) * 8; + height = (1 << oam->size) * 8; + } + else if (oam->shape == 1) // wide + { + width = spriteSizes[oam->size][1]; + height = spriteSizes[oam->size][0]; + } + else if (oam->shape == 2) // tall + { + width = spriteSizes[oam->size][0]; + height = spriteSizes[oam->size][1]; + } + else + { + continue; // prohibited, do not draw + } + + int rect_width = width; + int rect_height = height; + + int half_width = width / 2; + int half_height = height / 2; + + int32_t x = oam->x; + int32_t y = oam->y; + + if (x >= DISPLAY_WIDTH) + x -= 512; + if (y >= DISPLAY_HEIGHT) + y -= 256; + + if (isAffine) + { + //TODO: there is probably a better way to do this + u8 matrixNum = oam->matrixNum * 4; + + struct OamData *oam1 = &((struct OamData *)OAM)[matrixNum]; + struct OamData *oam2 = &((struct OamData *)OAM)[matrixNum + 1]; + struct OamData *oam3 = &((struct OamData *)OAM)[matrixNum + 2]; + struct OamData *oam4 = &((struct OamData *)OAM)[matrixNum + 3]; + + matrix[0][0] = oam1->affineParam; + matrix[0][1] = oam2->affineParam; + matrix[1][0] = oam3->affineParam; + matrix[1][1] = oam4->affineParam; + + if (doubleSizeOrDisabled) // double size for affine + { + rect_width *= 2; + rect_height *= 2; + half_width *= 2; + half_height *= 2; + } + } + else + { + // Identity + matrix[0][0] = 0x100; + matrix[0][1] = 0; + matrix[1][0] = 0; + matrix[1][1] = 0x100; + } + + x += half_width; + y += half_height; + + // Does this sprite actually draw on this scanline? + if (vcount >= (y - half_height) && vcount < (y + half_height)) + { + int local_y = (oam->mosaic == 1) ? applySpriteVerticalMosaicEffect(vcount) - y : vcount - y; + bool flipX = !isAffine && ((oam->matrixNum >> 3) & 1); + bool flipY = !isAffine && ((oam->matrixNum >> 4) & 1); + bool is8BPP = oam->bpp & 1; + + for (int local_x = -half_width; local_x <= half_width; local_x++) + { + uint8_t *tiledata = (uint8_t *)objtiles; + uint16_t *palette = (uint16_t *)(PLTT + 0x200); + int local_mosaicX; + int tex_x; + int tex_y; + + unsigned int global_x = local_x + x; + + if (global_x < 0 || global_x >= DISPLAY_WIDTH) + continue; + + if (oam->mosaic == 1) + { + //mosaic effect has to be applied to global coordinates otherwise the mosaic will scroll + local_mosaicX = applySpriteHorizontalMosaicEffect(global_x) - x; + tex_x = ((matrix[0][0] * local_mosaicX + matrix[0][1] * local_y) >> 8) + (width / 2); + tex_y = ((matrix[1][0] * local_mosaicX + matrix[1][1] * local_y) >> 8) + (height / 2); + }else{ + tex_x = ((matrix[0][0] * local_x + matrix[0][1] * local_y) >> 8) + (width / 2); + tex_y = ((matrix[1][0] * local_x + matrix[1][1] * local_y) >> 8) + (height / 2); + } + + + /* Check if transformed coordinates are inside bounds. */ + + if (tex_x >= width || tex_y >= height || tex_x < 0 || tex_y < 0) + continue; + + if (flipX) + tex_x = width - tex_x - 1; + if (flipY) + tex_y = height - tex_y - 1; + + int tile_x = tex_x % 8; + int tile_y = tex_y % 8; + int block_x = tex_x / 8; + int block_y = tex_y / 8; + int block_offset = ((block_y * (REG_DISPCNT & 0x40 ? (width / 8) : 16)) + block_x); + uint16_t pixel = 0; + + if (!is8BPP) + { + pixel = tiledata[(block_offset + oam->tileNum) * 32 + (tile_y * 4) + (tile_x / 2)]; + if (tile_x & 1) + pixel >>= 4; + else + pixel &= 0xF; + palette += oam->paletteNum * 16; + } + else + { + pixel = tiledata[(block_offset * 2 + oam->tileNum) * 32 + (tile_y * 8) + tile_x]; + } + + if (pixel != 0) + { + //this code runs if pixel is to be drawn + if (global_x < DISPLAY_WIDTH && global_x >= 0) + { + if (scanline->winMask[global_x] & WINMASK_WINOUT) + scanline->winMask[global_x] = (REG_WINOUT >> 8) & 0x3F; + } + } + } + } + } +} + +static void inline_hack DrawAffineSprite(int SpriteIndex, struct scanlineData* scanline, uint16_t vcount, bool windowsEnabled, uint16_t* pixels, bool IsInsideWinIn) +{ + struct OamData *oam = &((struct OamData *)OAM)[SpriteIndex]; + void *objtiles = VRAM_ + 0x10000; + unsigned int blendMode = (REG_BLDCNT >> 6) & 3; + bool winShouldBlendPixel = true; + unsigned int width; + unsigned int height; + + bool isAffine = oam->affineMode & 1; + bool doubleSizeOrDisabled = (oam->affineMode >> 1) & 1; + bool isSemiTransparent = (oam->objMode == 1); + + if (!(isAffine) && doubleSizeOrDisabled) // disable for non-affine + { + return; + } + + if (oam->shape == 0) + { + width = (1 << oam->size) * 8; + height = (1 << oam->size) * 8; + } + else if (oam->shape == 1) // wide + { + width = spriteSizes[oam->size][1]; + height = spriteSizes[oam->size][0]; + } + else if (oam->shape == 2) // tall + { + width = spriteSizes[oam->size][0]; + height = spriteSizes[oam->size][1]; + } + else + { + return; // prohibited, do not draw + } + + int rect_width = width; + int rect_height = height; + + int half_width = width / 2; + int half_height = height / 2; + + int32_t x = oam->x; + int32_t y = oam->y; + + if (x >= DISPLAY_WIDTH) + x -= 512; + if (y >= DISPLAY_HEIGHT) + y -= 256; + + int16_t matrix[2][2]; + + if (isAffine) + { + //TODO: there is probably a better way to do this + u8 matrixNum = oam->matrixNum * 4; + + struct OamData *oam1 = &((struct OamData *)OAM)[matrixNum]; + struct OamData *oam2 = &((struct OamData *)OAM)[matrixNum + 1]; + struct OamData *oam3 = &((struct OamData *)OAM)[matrixNum + 2]; + struct OamData *oam4 = &((struct OamData *)OAM)[matrixNum + 3]; + + matrix[0][0] = oam1->affineParam; + matrix[0][1] = oam2->affineParam; + matrix[1][0] = oam3->affineParam; + matrix[1][1] = oam4->affineParam; + + if (doubleSizeOrDisabled) // double size for affine + { + rect_width *= 2; + rect_height *= 2; + half_width *= 2; + half_height *= 2; + } + } + else + { + // Identity + matrix[0][0] = 0x100; + matrix[0][1] = 0; + matrix[1][0] = 0; + matrix[1][1] = 0x100; + } + + x += half_width; + y += half_height; + + // Does this sprite actually draw on this scanline? + if (vcount >= (y - half_height) && vcount < (y + half_height)) + { + int local_y = (oam->mosaic == 1) ? applySpriteVerticalMosaicEffect(vcount) - y : vcount - y; + bool flipX = !isAffine && ((oam->matrixNum >> 3) & 1); + bool flipY = !isAffine && ((oam->matrixNum >> 4) & 1); + bool is8BPP = oam->bpp & 1; + + for (int local_x = -half_width; local_x <= half_width; local_x++) + { + uint8_t *tiledata = (uint8_t *)objtiles; + uint16_t *palette = (uint16_t *)(PLTT + 0x200); + int local_mosaicX; + int tex_x; + int tex_y; + + unsigned int global_x = local_x + x; + + if (global_x < 0 || global_x >= DISPLAY_WIDTH) + continue; + + if (isAffine) + { + if (oam->mosaic == 1) + { + //mosaic effect has to be applied to global coordinates otherwise the mosaic will scroll + local_mosaicX = applySpriteHorizontalMosaicEffect(global_x) - x; + tex_x = ((matrix[0][0] * local_mosaicX + matrix[0][1] * local_y) >> 8) + (width / 2); + tex_y = ((matrix[1][0] * local_mosaicX + matrix[1][1] * local_y) >> 8) + (height / 2); + } + else + { + tex_x = ((matrix[0][0] * local_x + matrix[0][1] * local_y) >> 8) + (width / 2); + tex_y = ((matrix[1][0] * local_x + matrix[1][1] * local_y) >> 8) + (height / 2); + } + } + else + { + tex_x = (oam->mosaic == 1 ? applySpriteHorizontalMosaicEffect(global_x) - x : local_x) + (width / 2); + tex_y = local_y + (height / 2); + } + + + /* Check if transformed coordinates are inside bounds. */ + + if (tex_x >= width || tex_y >= height || tex_x < 0 || tex_y < 0) + continue; + + if (flipX) + tex_x = width - tex_x - 1; + if (flipY) + tex_y = height - tex_y - 1; + + int tile_x = tex_x % 8; + int tile_y = tex_y % 8; + int block_x = tex_x / 8; + int block_y = tex_y / 8; + int block_offset = ((block_y * (REG_DISPCNT & 0x40 ? (width / 8) : 16)) + block_x); + uint16_t pixel = 0; + + if (!is8BPP) + { + pixel = tiledata[(block_offset + oam->tileNum) * 32 + (tile_y * 4) + (tile_x / 2)]; + if (tile_x & 1) + pixel >>= 4; + else + pixel &= 0xF; + palette += oam->paletteNum * 16; + } + else + { + pixel = tiledata[(block_offset * 2 + oam->tileNum) * 32 + (tile_y * 8) + tile_x]; + } + + if (pixel != 0) + { + uint16_t color = palette[pixel];; + + //if sprite mode is 2 then write to the window mask instead + /*if (isObjWin) + { + if (scanline->winMask[global_x] & WINMASK_WINOUT) + scanline->winMask[global_x] = (REG_WINOUT >> 8) & 0x3F; + continue; + }*/ + if (windowsEnabled && !(scanline->winMask[global_x] & WINMASK_OBJ) && IsInsideWinIn == true) + { + continue; + } + + //this code runs if pixel is to be drawn + if (global_x < DISPLAY_WIDTH && global_x >= 0) + { + //check if its enabled in the window (if window is enabled) + if (IsInsideWinIn == true) + winShouldBlendPixel = (windowsEnabled == false || scanline->winMask[global_x] & WINMASK_CLR); + else + winShouldBlendPixel = (windowsEnabled == false || REG_WINOUT & WINOUT_WIN01_CLR); + //has to be separated from the blend mode switch statement because of OBJ semi transparancy feature + if ((blendMode == 1 && REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) || isSemiTransparent) + { + uint16_t targetA = color; + + if (scanline->bgMask[global_x] & (REG_BLDCNT >> 8)) + color = alphaBlendColor(targetA, pixels[global_x]); + } + else if (REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) + { + switch (blendMode) + { + case 2: + color = alphaBrightnessIncrease(color); + break; + case 3: + color = alphaBrightnessDecrease(color); + break; + } + } + + //write pixel to pixel framebuffer + pixels[global_x] = color | (1 << 15); + scanline->bgMask[global_x] = (1 << 4); // top most obj pixel bit + } + } + } + } +} + +static void inline_hack DrawNonAffineSprite(int SpriteIndex, struct scanlineData* scanline, uint16_t vcount, bool windowsEnabled, uint16_t* pixels, bool IsInsideWinIn) +{ + struct OamData *oam = &((struct OamData *)OAM)[SpriteIndex]; + void *objtiles = VRAM_ + 0x10000; + unsigned int blendMode = (REG_BLDCNT >> 6) & 3; + bool winShouldBlendPixel = true; + unsigned int width; + unsigned int height; + + bool isAffine = oam->affineMode & 1; + bool doubleSizeOrDisabled = (oam->affineMode >> 1) & 1; + bool isSemiTransparent = (oam->objMode == 1); + + if (!(isAffine) && doubleSizeOrDisabled) // disable for non-affine + { + return; + } + + if (oam->shape == 0) + { + width = (1 << oam->size) * 8; + height = (1 << oam->size) * 8; + } + else if (oam->shape == 1) // wide + { + width = spriteSizes[oam->size][1]; + height = spriteSizes[oam->size][0]; + } + else if (oam->shape == 2) // tall + { + width = spriteSizes[oam->size][0]; + height = spriteSizes[oam->size][1]; + } + else + { + return; // prohibited, do not draw + } + + int half_height = height / 2; + int32_t x = oam->x; + int32_t y = oam->y; + + if (x >= DISPLAY_WIDTH) + x -= 512; + if (y >= DISPLAY_HEIGHT) + y -= 256; + + y += half_height; + + // Does this sprite actually draw on this scanline? + if (vcount >= (y - half_height) && vcount < (y + half_height)) + { + int local_y = (oam->mosaic == 1) ? applySpriteVerticalMosaicEffect(vcount) - y : vcount - y; + //int palette = oam->paletteNum; + bool flipX = !isAffine && ((oam->matrixNum >> 3) & 1); + bool flipY = !isAffine && ((oam->matrixNum >> 4) & 1); + uint8_t *tiledata = (uint8_t *)objtiles; + uint16_t *palette = (uint16_t *)OBJ_PLTT; + palette += oam->paletteNum * 16; //choose the palette + int tex_y = local_y + (height / 2); + if (flipY) + tex_y = height - tex_y - 1; + //run the block drawing loop in reverse if sprite is flipped otherwise run as normal + int blockStart, blockEnd, blockIncrement; + if (flipX) + { + blockStart = width / TILE_WIDTH-1; + blockEnd = -1; + blockIncrement = -1; + } + else + { + blockStart = 0; + blockEnd = width / TILE_WIDTH; + blockIncrement = 1; + } + + for (int block_x = blockStart; block_x != blockEnd; block_x += blockIncrement) + { + int tile_y = tex_y & (TILE_HEIGHT-1); + int block_y = tex_y / TILE_HEIGHT; + int block_offset = ((block_y * (REG_DISPCNT & DISPCNT_OBJ_1D_MAP ? (width / 8) : 16)) + block_x); + uint8_t* pixelData = &tiledata[(oam->tileNum + block_offset) * TILE_SIZE_4BPP + (tile_y * 4)]; + uint32_t pixel32 = *(uint32_t*)pixelData; //load whole tile worth of palette pixels + + if (x >= 0 && x + TILE_WIDTH <= DISPLAY_WIDTH) + { + if (windowsEnabled) + { + if (blendMode != 0 || isSemiTransparent) //Windowing and blending + { + #define writeSpritePixelWinBlend(pixel, x) \ + if (pixel && ((IsInsideWinIn && scanline->winMask[x] & WINMASK_OBJ) || (!IsInsideWinIn && REG_WINOUT & WINOUT_WIN01_OBJ))) { \ + uint16_t color = palette[pixel]; \ + winShouldBlendPixel = (IsInsideWinIn && scanline->winMask[x] & WINMASK_CLR) || (!IsInsideWinIn && REG_WINOUT & WINOUT_WIN01_CLR); \ + \ + if (((blendMode == 1 && REG_BLDCNT & BLDCNT_TGT1_OBJ) && winShouldBlendPixel) || isSemiTransparent) \ + { \ + if (scanline->bgMask[x] & (REG_BLDCNT >> 8)) \ + color = alphaBlendColor(color, pixels[x]); \ + } \ + else if(REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) \ + { \ + switch (blendMode) \ + { \ + case 2: \ + color = alphaBrightnessIncrease(color); \ + break; \ + case 3: \ + color = alphaBrightnessDecrease(color); \ + break; \ + } \ + } \ + pixels[x] = color | (1 << 15); \ + scanline->bgMask[x] = (1 << 4); \ + } + + if (flipX) + { + writeSpritePixelWinBlend(pixel32 >> 28, x) + writeSpritePixelWinBlend((pixel32 >> 24) & 0xF, x+1) + writeSpritePixelWinBlend((pixel32 >> 20) & 0xF, x+2) + writeSpritePixelWinBlend((pixel32 >> 16) & 0xF, x+3) + writeSpritePixelWinBlend((pixel32 >> 12) & 0xF, x+4) + writeSpritePixelWinBlend((pixel32 >> 8) & 0xF, x+5) + writeSpritePixelWinBlend((pixel32 >> 4) & 0xF, x+6) + writeSpritePixelWinBlend(pixel32 & 0xF, x+7) + } + else + { + writeSpritePixelWinBlend(pixel32 & 0xF, x) + writeSpritePixelWinBlend((pixel32 >> 4) & 0xF, x+1) + writeSpritePixelWinBlend((pixel32 >> 8) & 0xF, x+2) + writeSpritePixelWinBlend((pixel32 >> 12) & 0xF, x+3) + writeSpritePixelWinBlend((pixel32 >> 16) & 0xF, x+4) + writeSpritePixelWinBlend((pixel32 >> 20) & 0xF, x+5) + writeSpritePixelWinBlend((pixel32 >> 24) & 0xF, x+6) + writeSpritePixelWinBlend(pixel32 >> 28, x+7) + } + #undef writeSpritePixelWinBlend + } + else //Windowing + { + #define writeSpritePixelWin(pixel, x) \ + if (pixel && ((IsInsideWinIn && scanline->winMask[x] & WINMASK_OBJ) || (!IsInsideWinIn && REG_WINOUT & WINOUT_WIN01_OBJ))) { \ + pixels[x] = palette[pixel] | (1 << 15); \ + scanline->bgMask[x] = (1 << 4); \ + } + + if (flipX) + { + writeSpritePixelWin(pixel32 >> 28, x) + writeSpritePixelWin((pixel32 >> 24) & 0xF, x+1) + writeSpritePixelWin((pixel32 >> 20) & 0xF, x+2) + writeSpritePixelWin((pixel32 >> 16) & 0xF, x+3) + writeSpritePixelWin((pixel32 >> 12) & 0xF, x+4) + writeSpritePixelWin((pixel32 >> 8) & 0xF, x+5) + writeSpritePixelWin((pixel32 >> 4) & 0xF, x+6) + writeSpritePixelWin(pixel32 & 0xF, x+7) + } + else + { + writeSpritePixelWin(pixel32 & 0xF, x) + writeSpritePixelWin((pixel32 >> 4) & 0xF, x+1) + writeSpritePixelWin((pixel32 >> 8) & 0xF, x+2) + writeSpritePixelWin((pixel32 >> 12) & 0xF, x+3) + writeSpritePixelWin((pixel32 >> 16) & 0xF, x+4) + writeSpritePixelWin((pixel32 >> 20) & 0xF, x+5) + writeSpritePixelWin((pixel32 >> 24) & 0xF, x+6) + writeSpritePixelWin(pixel32 >> 28, x+7) + } + #undef writeSpritePixelWin + } + } + else //Choose between Blend and none + { + if (blendMode != 0 || isSemiTransparent) //Blend + { + #define writeSpritePixelBlend(pixel, x) \ + if (pixel) { \ + uint16_t color = palette[pixel]; \ + \ + if ( (blendMode == 1 && REG_BLDCNT & BLDCNT_TGT1_OBJ) || isSemiTransparent) \ + { \ + if (scanline->bgMask[x] & (REG_BLDCNT >> 8)) \ + color = alphaBlendColor(color, pixels[x]); \ + } \ + else if(REG_BLDCNT & BLDCNT_TGT1_OBJ) \ + { \ + switch (blendMode) \ + { \ + case 2: \ + color = alphaBrightnessIncrease(color); \ + break; \ + case 3: \ + color = alphaBrightnessDecrease(color); \ + break; \ + } \ + } \ + \ + pixels[x] = color | (1 << 15); \ + scanline->bgMask[x] = (1 << 4); \ + } + + if (flipX) + { + writeSpritePixelBlend(pixel32 >> 28, x) + writeSpritePixelBlend((pixel32 >> 24) & 0xF, x+1) + writeSpritePixelBlend((pixel32 >> 20) & 0xF, x+2) + writeSpritePixelBlend((pixel32 >> 16) & 0xF, x+3) + writeSpritePixelBlend((pixel32 >> 12) & 0xF, x+4) + writeSpritePixelBlend((pixel32 >> 8) & 0xF, x+5) + writeSpritePixelBlend((pixel32 >> 4) & 0xF, x+6) + writeSpritePixelBlend(pixel32 & 0xF, x+7) + } + else + { + writeSpritePixelBlend(pixel32 & 0xF, x) + writeSpritePixelBlend((pixel32 >> 4) & 0xF, x+1) + writeSpritePixelBlend((pixel32 >> 8) & 0xF, x+2) + writeSpritePixelBlend((pixel32 >> 12) & 0xF, x+3) + writeSpritePixelBlend((pixel32 >> 16) & 0xF, x+4) + writeSpritePixelBlend((pixel32 >> 20) & 0xF, x+5) + writeSpritePixelBlend((pixel32 >> 24) & 0xF, x+6) + writeSpritePixelBlend(pixel32 >> 28, x+7) + } + #undef writeSpritePixelBlend + } + else //None + { + #define writeSpritePixel(pixel, x) \ + if (pixel) { \ + pixels[x] = palette[pixel] | (1 << 15); \ + scanline->bgMask[x] = (1 << 4); \ + } + + if (flipX) + { + writeSpritePixel(pixel32 >> 28, x) + writeSpritePixel((pixel32 >> 24) & 0xF, x+1) + writeSpritePixel((pixel32 >> 20) & 0xF, x+2) + writeSpritePixel((pixel32 >> 16) & 0xF, x+3) + writeSpritePixel((pixel32 >> 12) & 0xF, x+4) + writeSpritePixel((pixel32 >> 8) & 0xF, x+5) + writeSpritePixel((pixel32 >> 4) & 0xF, x+6) + writeSpritePixel(pixel32 & 0xF, x+7) + } + else + { + writeSpritePixel(pixel32 & 0xF, x) + writeSpritePixel((pixel32 >> 4) & 0xF, x+1) + writeSpritePixel((pixel32 >> 8) & 0xF, x+2) + writeSpritePixel((pixel32 >> 12) & 0xF, x+3) + writeSpritePixel((pixel32 >> 16) & 0xF, x+4) + writeSpritePixel((pixel32 >> 20) & 0xF, x+5) + writeSpritePixel((pixel32 >> 24) & 0xF, x+6) + writeSpritePixel(pixel32 >> 28, x+7) + } + #undef writeSpritePixel + } + } + } + else //handle tiles that are partially cut off screen, for the sake of readibility the writeSpritePixel handles all special effect spaces, should affect performance very little + { + bool winShouldDraw = true; + #define writeSpritePixel(pixel, x) \ + winShouldDraw = windowsEnabled == false || ((IsInsideWinIn && scanline->winMask[x] & WINMASK_OBJ) || (!IsInsideWinIn && REG_WINOUT & WINOUT_WIN01_OBJ));\ + if (pixel && winShouldDraw) { \ + uint16_t color = palette[pixel]; \ + \ + winShouldBlendPixel = windowsEnabled == false || ( (IsInsideWinIn && scanline->winMask[x] & WINMASK_CLR) || (!IsInsideWinIn && REG_WINOUT & WINOUT_WIN01_CLR) ); \ + if ((blendMode == 1 && REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) || isSemiTransparent) \ + { \ + if (scanline->bgMask[x] & (REG_BLDCNT >> 8)) \ + color = alphaBlendColor(color, pixels[x]); \ + } \ + else if(REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) \ + { \ + switch (blendMode) \ + { \ + case 2: \ + color = alphaBrightnessIncrease(color); \ + break; \ + case 3: \ + color = alphaBrightnessDecrease(color); \ + break; \ + } \ + } \ + \ + pixels[x] = color | (1 << 15); \ + scanline->bgMask[x] = (1 << 4); \ + } + + if (x > -TILE_WIDTH && x < 0) //left side + { + int amountOfPixelsToBeDrawn = TILE_WIDTH - abs(x); + if (flipX) + { + for (int i = 0; i < amountOfPixelsToBeDrawn; i++) + { + int pixelOffset = abs(x); + writeSpritePixel((pixel32 >> (28-((pixelOffset+i)*4))) & 0xF, i); + } + } + else + { + for (int i = 0; i < amountOfPixelsToBeDrawn; i++) + { + int pixelOffset = abs(x); + writeSpritePixel((pixel32 >> ((pixelOffset+i)*4)) & 0xF, i); + } + } + } + else if(x < DISPLAY_WIDTH && x + TILE_WIDTH > DISPLAY_WIDTH) //right side + { + int amountOfPixelsToBeDrawn = DISPLAY_WIDTH-x; + if (flipX) + { + for (int i = 0; i < amountOfPixelsToBeDrawn; i++) + { + writeSpritePixel((pixel32 >> (28-(i*4))) & 0xF, x+i); + } + } + else + { + for (int i = 0; i < amountOfPixelsToBeDrawn; i++) + { + writeSpritePixel((pixel32 >> i*4) & 0xF, x+i); + } + } + } + #undef writeSpritePixel + } + x += TILE_WIDTH; //Move on onto the next block + } + } +} + +// Parts of this code heavily borrowed from NanoboyAdvance. +static void DrawSprites(struct scanlineData* scanline, uint16_t vcount, bool windowsEnabled, uint8_t priority, uint16_t* pixels, bool IsInsideWinIn) +{ + int SpriteIndex; + + if (windowsEnabled == true && IsInsideWinIn == false) + { + if (!(REG_WINOUT & WINOUT_WIN01_OBJ)) + return; + } + + if (!(REG_DISPCNT & (1 << 6))) + { + puts("2-D OBJ Character mapping not supported."); + } + + for (SpriteIndex = 127; SpriteIndex >= 0; SpriteIndex--) + { + struct OamData *oam = &((struct OamData *)OAM)[SpriteIndex]; + + if (oam->priority != priority) + continue; + if (oam->objMode == 2) + continue; + + if (oam->affineMode & 1 || oam->bpp & 1 || oam->mosaic == 1) + DrawAffineSprite(SpriteIndex, scanline, vcount, windowsEnabled, pixels, IsInsideWinIn); + else + DrawNonAffineSprite(SpriteIndex, scanline, vcount, windowsEnabled, pixels, IsInsideWinIn); + } +} + +static void DrawScanline(uint16_t *pixels, uint16_t vcount) +{ + unsigned int mode = REG_DISPCNT & 3; + unsigned char numOfBgs = (mode == 0 ? 4 : 3); + int bgnum, prnum; + struct scanlineData scanline; + unsigned int blendMode = (REG_BLDCNT >> 6) & 3; + unsigned int xpos; + + + //initialize all priority bookkeeping data + memset(scanline.prioritySortedBgsCount, 0, sizeof(scanline.prioritySortedBgsCount)); + + //windows code!!! + bool windowsEnabled = false; + bool IsInsideWinIn; + uint16_t WIN0bottom, WIN0top, WIN0right, WIN0left; + uint16_t WIN1bottom, WIN1top, WIN1right, WIN1left; + bool WIN0enable, WIN1enable; + WIN0enable = false; + WIN1enable = false; + + //figure out if WIN0 masks on this scanline + if (REG_DISPCNT & DISPCNT_WIN0_ON) + { + //acquire the window coordinates + WIN0bottom = (REG_WIN0V & 0xFF); //y2; + WIN0top = (REG_WIN0V & 0xFF00) >> 8; //y1; + WIN0right = (REG_WIN0H & 0xFF); //x2 + WIN0left = (REG_WIN0H & 0xFF00) >> 8; //x1 + + //figure out WIN Y wraparound and check bounds accordingly + if (WIN0top > WIN0bottom) { + if (vcount >= WIN0top || vcount < WIN0bottom) + WIN0enable = true; + } else { + if (vcount >= WIN0top && vcount < WIN0bottom) + WIN0enable = true; + } + + windowsEnabled = true; + } + //figure out if WIN1 masks on this scanline + if (REG_DISPCNT & DISPCNT_WIN1_ON) + { + WIN1bottom = (REG_WIN0V & 0xFF); //y2; + WIN1top = (REG_WIN0V & 0xFF00) >> 8; //y1; + WIN1right = (REG_WIN0H & 0xFF); //x2 + WIN1left = (REG_WIN0H & 0xFF00) >> 8; //x1 + + if (WIN1top > WIN1bottom) { + if (vcount >= WIN1top || vcount < WIN1bottom) + WIN1enable = true; + } else { + if (vcount >= WIN1top && vcount < WIN1bottom) + WIN1enable = true; + } + + windowsEnabled = true; + } + //enable windows if OBJwin is enabled + if (REG_DISPCNT & DISPCNT_OBJWIN_ON && REG_DISPCNT & DISPCNT_OBJ_ON) + windowsEnabled = true; + + IsInsideWinIn = (WIN0enable || WIN1enable || (REG_DISPCNT & DISPCNT_OBJWIN_ON && REG_DISPCNT & DISPCNT_OBJ_ON)); + + + //draw to pixel mask + if (IsInsideWinIn) + { + for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++) + { + //win0 checks + if (WIN0enable && winCheckHorizontalBounds(WIN0left, WIN0right, xpos)) + scanline.winMask[xpos] = REG_WININ & 0x3F; + //win1 checks + else if (WIN1enable && winCheckHorizontalBounds(WIN1left, WIN1right, xpos)) + scanline.winMask[xpos] = (REG_WININ >> 8) & 0x3F; + else + scanline.winMask[xpos] = (REG_WINOUT & 0x3F) | WINMASK_WINOUT; + } + } + + //draw to window mask if OBJwin is enabled + if (REG_DISPCNT & DISPCNT_OBJWIN_ON && REG_DISPCNT & DISPCNT_OBJ_ON) + DrawSpritesWinMask(&scanline, vcount); + + //init bgmask for alpha blending + for (int i = 0; i < DISPLAY_WIDTH; i++) + { + scanline.bgMask[i] = 0x20; //backdrop bit + } + + for (bgnum = 0; bgnum < numOfBgs; bgnum++) + { + uint16_t bgcnt = *(uint16_t*)(REG_ADDR_BG0CNT + bgnum * 2); + uint16_t priority; + scanline.bgcnts[bgnum] = bgcnt; + scanline.bgtoprio[bgnum] = priority = (bgcnt & 3); + + char priorityCount = scanline.prioritySortedBgsCount[priority]; + scanline.prioritySortedBgs[priority][priorityCount] = bgnum; + scanline.prioritySortedBgsCount[priority]++; + } + + switch (mode) + { + case 0: + for (prnum = 3; prnum >= 0; prnum--) + { + for (char prsub = scanline.prioritySortedBgsCount[prnum] - 1; prsub >= 0; prsub--) + { + bgnum = scanline.prioritySortedBgs[prnum][prsub]; + if (isbgEnabled(bgnum)) + { + uint16_t bghoffs = *(uint16_t *)(REG_ADDR_BG0HOFS + bgnum * 4); + uint16_t bgvoffs = *(uint16_t *)(REG_ADDR_BG0VOFS + bgnum * 4); + bool doesBGblend = (blendMode != 0 && REG_BLDCNT & (1 << bgnum)); + + if (IsInsideWinIn) + { + //blending check + if (doesBGblend) + RenderBGScanlineWinBlend(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + else + RenderBGScanlineWin(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + + } else { + + if (doesBGblend && (windowsEnabled == false || (REG_WINOUT & WINOUT_WIN01_CLR)) ) + RenderBGScanlineBlend(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + else + RenderBGScanlineNoEffect(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + } + + } + } + if (REG_DISPCNT & DISPCNT_OBJ_ON) + DrawSprites(&scanline, vcount, windowsEnabled, prnum, pixels, IsInsideWinIn); + } + + break; + case 1: + for (prnum = 3; prnum >= 0; prnum--) + { + for (char prsub = scanline.prioritySortedBgsCount[prnum] - 1; prsub >= 0; prsub--) + { + bgnum = scanline.prioritySortedBgs[prnum][prsub]; + if (isbgEnabled(bgnum)) + { + uint16_t bghoffs = *(uint16_t *)(REG_ADDR_BG0HOFS + bgnum * 4); + uint16_t bgvoffs = *(uint16_t *)(REG_ADDR_BG0VOFS + bgnum * 4); + bool doesBGblend = (blendMode != 0 && REG_BLDCNT & (1 << bgnum)); + + + if (bgnum != 2) + { + if (IsInsideWinIn) + { + //blending check + if (doesBGblend) + RenderBGScanlineWinBlend(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + else + RenderBGScanlineWin(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + + } else { + + if (doesBGblend) + RenderBGScanlineBlend(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + else + RenderBGScanlineNoEffect(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + } + } + else + { + if (IsInsideWinIn) + { + //blending check + if (doesBGblend) + RenderRotScaleBGScanlineWinBlend(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + else + RenderRotScaleBGScanlineWin(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + + } else { + + if (doesBGblend && (windowsEnabled == false || (REG_WINOUT & WINOUT_WIN01_CLR))) + RenderRotScaleBGScanlineBlend(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + else + RenderRotScaleBGScanlineNoEffect(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, pixels, &scanline, windowsEnabled); + } + } + } + } + if (REG_DISPCNT & DISPCNT_OBJ_ON) + DrawSprites(&scanline, vcount, windowsEnabled, prnum, pixels, IsInsideWinIn); + } + break; + default: + printf("Video mode %u is unsupported.\n", mode); + break; + } +} + +uint16_t *memsetu16(uint16_t *dst, uint16_t fill, size_t count) +{ + for (int i = 0; i < count; i++) + { + *dst++ = fill; + } +} + +unsigned int frameskipcounter = 0; + +void DrawFrame(uint16_t *pixels) +{ + int i; + int j; + + //memsetu16(pixels, *(uint16_t *)PLTT, DISPLAY_WIDTH * DISPLAY_HEIGHT); + + for (i = 0; i < DISPLAY_HEIGHT; i++) + { + REG_VCOUNT = i; + if(((REG_DISPSTAT >> 8) & 0xFF) == REG_VCOUNT) + { + REG_DISPSTAT |= INTR_FLAG_VCOUNT; + if(REG_DISPSTAT & DISPSTAT_VCOUNT_INTR) + gIntrTable[0](); + } + + // backdrop color brightness effects + unsigned int blendMode = (REG_BLDCNT >> 6) & 3; + uint16_t backdropColor = *(uint16_t *)PLTT; + if (REG_BLDCNT & BLDCNT_TGT1_BD) + { + switch (blendMode) + { + case 2: + backdropColor = alphaBrightnessIncrease(backdropColor); + break; + case 3: + backdropColor = alphaBrightnessDecrease(backdropColor); + break; + } + } + + memsetu16(&pixels[i*DISPLAY_WIDTH], backdropColor, DISPLAY_WIDTH); + DrawScanline(&pixels[i*DISPLAY_WIDTH], i); + + REG_DISPSTAT |= INTR_FLAG_HBLANK; + + RunDMAs(DMA_HBLANK); + + if (REG_DISPSTAT & DISPSTAT_HBLANK_INTR) + gIntrTable[3](); + + REG_DISPSTAT &= ~INTR_FLAG_HBLANK; + REG_DISPSTAT &= ~INTR_FLAG_VCOUNT; + } +} +#endif diff --git a/src/platform/sdl2.c b/src/platform/sdl2.c new file mode 100644 index 000000000..aed28190a --- /dev/null +++ b/src/platform/sdl2.c @@ -0,0 +1,513 @@ +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include + +#include "global.h" +#include "platform.h" +#include "gba/defines.h" +#include "gba/m4a_internal.h" +#include "cgb_audio.h" +#include "gba/flash_internal.h" +#include "platform/dma.h" +#include "platform/framedraw.h" + +extern IntrFunc gIntrTable[]; + +SDL_Thread *mainLoopThread; +SDL_Window *sdlWindow; +SDL_Renderer *sdlRenderer; +SDL_Texture *sdlTexture; +SDL_sem *vBlankSemaphore; +SDL_atomic_t isFrameAvailable; +bool speedUp = false; +unsigned int videoScale = 1; +bool videoScaleChanged = false; +bool isRunning = true; +bool paused = false; +double simTime = 0; +double lastGameTime = 0; +double curGameTime = 0; +double fixedTimestep = 1.0 / 60.0; // 16.666667ms +double timeScale = 1.0; + + +static FILE *sSaveFile = NULL; + +extern void AgbMain(void); +extern void DoSoftReset(void); + +int DoMain(void *param); +void ProcessEvents(void); +void VDraw(SDL_Texture *texture); + +static void ReadSaveFile(char *path); +static void StoreSaveFile(void); +static void CloseSaveFile(void); + + + +int main(int argc, char **argv) +{ + // Open an output console on Windows +#ifdef _WIN32 + AllocConsole() ; + AttachConsole( GetCurrentProcessId() ) ; + freopen( "CON", "w", stdout ) ; +#endif + + ReadSaveFile("pokefirered.sav"); + + if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) + { + printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); + return 1; + } + + sdlWindow = SDL_CreateWindow("pokefirered", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DISPLAY_WIDTH * videoScale, DISPLAY_HEIGHT * videoScale, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); + if (sdlWindow == NULL) + { + printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); + return 1; + } + + sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_PRESENTVSYNC); + if (sdlRenderer == NULL) + { + printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); + return 1; + } + + SDL_SetRenderDrawColor(sdlRenderer, 255, 255, 255, 255); + SDL_RenderClear(sdlRenderer); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + SDL_RenderSetLogicalSize(sdlRenderer, DISPLAY_WIDTH, DISPLAY_HEIGHT); + + sdlTexture = SDL_CreateTexture(sdlRenderer, + SDL_PIXELFORMAT_ABGR1555, + SDL_TEXTUREACCESS_STREAMING, + DISPLAY_WIDTH, DISPLAY_HEIGHT); + if (sdlTexture == NULL) + { + printf("Texture could not be created! SDL_Error: %s\n", SDL_GetError()); + return 1; + } + + simTime = curGameTime = lastGameTime = SDL_GetPerformanceCounter(); + + isFrameAvailable.value = 0; + vBlankSemaphore = SDL_CreateSemaphore(0); + + SDL_AudioSpec want; + + SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */ + want.freq = 42048; + want.format = AUDIO_F32; + want.channels = 2; + want.samples = 1024; + cgb_audio_init(want.freq); + + + if (SDL_OpenAudio(&want, 0) < 0) + SDL_Log("Failed to open audio: %s", SDL_GetError()); + else + { + if (want.format != AUDIO_F32) /* we let this one thing change. */ + SDL_Log("We didn't get Float32 audio format."); + SDL_PauseAudio(0); + } + + VDraw(sdlTexture); + mainLoopThread = SDL_CreateThread(DoMain, "AgbMain", NULL); + + double accumulator = 0.0; + + while (isRunning) + { + ProcessEvents(); + + if (!paused) + { + double dt = fixedTimestep / timeScale; // TODO: Fix speedup + + curGameTime = SDL_GetPerformanceCounter(); + double deltaTime = (double)((curGameTime - lastGameTime) / (double)SDL_GetPerformanceFrequency()); + if (deltaTime > (dt * 5)) + deltaTime = dt; + lastGameTime = curGameTime; + + accumulator += deltaTime; + + while (accumulator >= dt) + { + if (SDL_AtomicGet(&isFrameAvailable)) + { + VDraw(sdlTexture); + + SDL_RenderClear(sdlRenderer); + SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); + SDL_AtomicSet(&isFrameAvailable, 0); + + REG_DISPSTAT |= INTR_FLAG_VBLANK; + + RunDMAs(DMA_HBLANK); + if (REG_DISPSTAT & DISPSTAT_VBLANK_INTR){ + gIntrTable[4](); + } + REG_DISPSTAT &= ~INTR_FLAG_VBLANK; + + SDL_SemPost(vBlankSemaphore); + + accumulator -= dt; + } + } + } + + if (videoScaleChanged) + { + SDL_SetWindowSize(sdlWindow, DISPLAY_WIDTH * videoScale, DISPLAY_HEIGHT * videoScale); + videoScaleChanged = false; + } + + SDL_RenderPresent(sdlRenderer); + } + + //StoreSaveFile(); + CloseSaveFile(); + + SDL_DestroyWindow(sdlWindow); + SDL_Quit(); + return 0; +} + +static void ReadSaveFile(char *path) +{ + // Check whether the saveFile exists, and create it if not + sSaveFile = fopen(path, "r+b"); + if (sSaveFile == NULL) + { + sSaveFile = fopen(path, "w+b"); + } + + fseek(sSaveFile, 0, SEEK_END); + int fileSize = ftell(sSaveFile); + fseek(sSaveFile, 0, SEEK_SET); + + // Only read as many bytes as fit inside the buffer + // or as many bytes as are in the file + int bytesToRead = (fileSize < sizeof(FLASH_BASE)) ? fileSize : sizeof(FLASH_BASE); + + int bytesRead = fread(FLASH_BASE, 1, bytesToRead, sSaveFile); + + // Fill the buffer if the savefile was just created or smaller than the buffer itself + for (int i = bytesRead; i < sizeof(FLASH_BASE); i++) + { + FLASH_BASE[i] = 0xFF; + } +} + +static void StoreSaveFile() +{ + if (sSaveFile != NULL) + { + fseek(sSaveFile, 0, SEEK_SET); + fwrite(FLASH_BASE, 1, sizeof(FLASH_BASE), sSaveFile); + } +} + +void Platform_StoreSaveFile(void) +{ + StoreSaveFile(); +} + +void Platform_ReadFlash(u16 sectorNum, u32 offset, u8 *dest, u32 size) +{ + printf("ReadFlash(sectorNum=0x%04X,offset=0x%08X,size=0x%02X)\n",sectorNum,offset,size); + FILE * savefile = fopen("pokefirered.sav", "r+b"); + if (savefile == NULL) + { + puts("Error opening save file."); + return; + } + if (fseek(savefile, (sectorNum << gFlash->sector.shift) + offset, SEEK_SET)) + { + fclose(savefile); + return; + } + if (fread(dest, 1, size, savefile) != size) + { + fclose(savefile); + return; + } + fclose(savefile); +} + +void Platform_QueueAudio(float *audioBuffer, s32 samplesPerFrame) +{ + SDL_QueueAudio(1, audioBuffer, samplesPerFrame); +} + + +static void CloseSaveFile() +{ + if (sSaveFile != NULL) + { + fclose(sSaveFile); + } +} + +// Key mappings +#define KEY_A_BUTTON SDLK_z +#define KEY_B_BUTTON SDLK_x +#define KEY_START_BUTTON SDLK_RETURN +#define KEY_SELECT_BUTTON SDLK_BACKSLASH +#define KEY_L_BUTTON SDLK_a +#define KEY_R_BUTTON SDLK_s +#define KEY_DPAD_UP SDLK_UP +#define KEY_DPAD_DOWN SDLK_DOWN +#define KEY_DPAD_LEFT SDLK_LEFT +#define KEY_DPAD_RIGHT SDLK_RIGHT + +#define HANDLE_KEYUP(key) \ +case KEY_##key: keys &= ~key; break; + +#define HANDLE_KEYDOWN(key) \ +case KEY_##key: keys |= key; break; + +static u16 keys; + +void ProcessEvents(void) +{ + SDL_Event event; + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + isRunning = false; + break; + case SDL_KEYUP: + switch (event.key.keysym.sym) + { + HANDLE_KEYUP(A_BUTTON) + HANDLE_KEYUP(B_BUTTON) + HANDLE_KEYUP(START_BUTTON) + HANDLE_KEYUP(SELECT_BUTTON) + HANDLE_KEYUP(L_BUTTON) + HANDLE_KEYUP(R_BUTTON) + HANDLE_KEYUP(DPAD_UP) + HANDLE_KEYUP(DPAD_DOWN) + HANDLE_KEYUP(DPAD_LEFT) + HANDLE_KEYUP(DPAD_RIGHT) + case SDLK_SPACE: + if (speedUp) + { + speedUp = false; + timeScale = 1.0; + SDL_ClearQueuedAudio(1); + SDL_PauseAudio(0); + } + break; + } + break; + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + HANDLE_KEYDOWN(A_BUTTON) + HANDLE_KEYDOWN(B_BUTTON) + HANDLE_KEYDOWN(START_BUTTON) + HANDLE_KEYDOWN(SELECT_BUTTON) + HANDLE_KEYDOWN(L_BUTTON) + HANDLE_KEYDOWN(R_BUTTON) + HANDLE_KEYDOWN(DPAD_UP) + HANDLE_KEYDOWN(DPAD_DOWN) + HANDLE_KEYDOWN(DPAD_LEFT) + HANDLE_KEYDOWN(DPAD_RIGHT) + case SDLK_r: + if (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) + { + DoSoftReset(); + } + break; + case SDLK_p: + if (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) + { + paused = !paused; + } + break; + case SDLK_SPACE: + if (!speedUp) + { + speedUp = true; + timeScale = 5.0; + SDL_PauseAudio(1); + } + break; + } + break; + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + unsigned int w = event.window.data1; + unsigned int h = event.window.data2; + + videoScale = 0; + if (w / DISPLAY_WIDTH > videoScale) + videoScale = w / DISPLAY_WIDTH; + if (h / DISPLAY_HEIGHT > videoScale) + videoScale = h / DISPLAY_HEIGHT; + if (videoScale < 1) + videoScale = 1; + + videoScaleChanged = true; + } + break; + } + } +} + +#ifdef _WIN32 +#define STICK_THRESHOLD 0.5f +u16 GetXInputKeys() +{ + XINPUT_STATE state; + ZeroMemory(&state, sizeof(XINPUT_STATE)); + + DWORD dwResult = XInputGetState(0, &state); + u16 xinputKeys = 0; + + if (dwResult == ERROR_SUCCESS) + { + /* A */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) >> 12; + /* B */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) >> 13; + /* Start */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) >> 1; + /* Select */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) >> 3; + /* L */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) << 1; + /* R */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) >> 1; + /* Up */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) << 6; + /* Down */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) << 6; + /* Left */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) << 3; + /* Right */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) << 1; + + + /* Control Stick */ + float xAxis = (float)state.Gamepad.sThumbLX / (float)SHRT_MAX; + float yAxis = (float)state.Gamepad.sThumbLY / (float)SHRT_MAX; + + if (xAxis < -STICK_THRESHOLD) xinputKeys |= DPAD_LEFT; + if (xAxis > STICK_THRESHOLD) xinputKeys |= DPAD_RIGHT; + if (yAxis < -STICK_THRESHOLD) xinputKeys |= DPAD_DOWN; + if (yAxis > STICK_THRESHOLD) xinputKeys |= DPAD_UP; + + + /* Speedup */ + // Note: 'speedup' variable is only (un)set on keyboard input + double oldTimeScale = timeScale; + timeScale = (state.Gamepad.bRightTrigger > 0x80 || speedUp) ? 5.0 : 1.0; + + if (oldTimeScale != timeScale) + { + if (timeScale > 1.0) + { + SDL_PauseAudio(1); + } + else + { + SDL_ClearQueuedAudio(1); + SDL_PauseAudio(0); + } + } + } + + return xinputKeys; +} +#endif // _WIN32 + +u16 Platform_GetKeyInput(void) +{ +#ifdef _WIN32 + u16 gamepadKeys = GetXInputKeys(); + return (gamepadKeys != 0) ? gamepadKeys : keys; +#endif + + return keys; +} + +void VDraw(SDL_Texture *texture) +{ + static uint16_t image[DISPLAY_WIDTH * DISPLAY_HEIGHT]; + + memset(image, 0, sizeof(image)); + DrawFrame(image); + SDL_UpdateTexture(texture, NULL, image, DISPLAY_WIDTH * sizeof (Uint16)); + REG_VCOUNT = 161; // prep for being in VBlank period +} + +int DoMain(void *data) +{ + AgbMain(); +} + +void VBlankIntrWait(void) +{ + SDL_AtomicSet(&isFrameAvailable, 1); + SDL_SemWait(vBlankSemaphore); +} + +void printRegs(){ + printf("REG_DISPCNT %04X\n", REG_DISPCNT); + printf("REG_DISPSTAT %04X\n", REG_DISPSTAT); + printf("REG_VCOUNT %04X\n", REG_VCOUNT); + printf("REG_BG0CNT %04X\n", REG_BG0CNT); + printf("REG_BG1CNT %04X\n", REG_BG1CNT); + printf("REG_BG2CNT %04X\n", REG_BG2CNT); + printf("REG_BG3CNT %04X\n", REG_BG3CNT); + printf("REG_BG0HOFS %04X\n", REG_BG0HOFS); + printf("REG_BG0VOFS %04X\n", REG_BG0VOFS); + printf("REG_BG1HOFS %04X\n", REG_BG1HOFS); + printf("REG_BG1VOFS %04X\n", REG_BG1VOFS); + printf("REG_BG2HOFS %04X\n", REG_BG2HOFS); + printf("REG_BG2VOFS %04X\n", REG_BG2VOFS); + printf("REG_BG3HOFS %04X\n", REG_BG3HOFS); + printf("REG_BG3VOFS %04X\n", REG_BG3VOFS); + printf("REG_BG2PA %04X\n", REG_BG2PA); + printf("REG_BG2PB %04X\n", REG_BG2PB); + printf("REG_BG2PC %04X\n", REG_BG2PC); + printf("REG_BG2PD %04X\n", REG_BG2PD); + printf("REG_BG2X %04X\n", REG_BG2X); + printf("REG_BG2X_L %04X\n", REG_BG2X_L); + printf("REG_BG2X_H %04X\n", REG_BG2X_H); + printf("REG_BG2Y %04X\n", REG_BG2Y); + printf("REG_BG2Y_L %04X\n", REG_BG2Y_L); + printf("REG_BG2Y_H %04X\n", REG_BG2Y_H); + printf("REG_BG3PA %04X\n", REG_BG3PA); + printf("REG_BG3PB %04X\n", REG_BG3PB); + printf("REG_BG3PC %04X\n", REG_BG3PC); + printf("REG_BG3PD %04X\n", REG_BG3PD); + printf("REG_BG3X %04X\n", REG_BG3X); + printf("REG_BG3X_L %04X\n", REG_BG3X_L); + printf("REG_BG3X_H %04X\n", REG_BG3X_H); + printf("REG_BG3Y %04X\n", REG_BG3Y); + printf("REG_BG3Y_L %04X\n", REG_BG3Y_L); + printf("REG_BG3Y_H %04X\n", REG_BG3Y_H); + printf("REG_WIN0H %04X\n", REG_WIN0H); + printf("REG_WIN1H %04X\n", REG_WIN1H); + printf("REG_WIN0V %04X\n", REG_WIN0V); + printf("REG_WIN1V %04X\n", REG_WIN1V); + printf("REG_WININ %04X\n", REG_WININ); + printf("REG_WINOUT %04X\n", REG_WINOUT); + printf("REG_MOSAIC %04X\n", REG_MOSAIC); + printf("REG_BLDCNT %04X\n", REG_BLDCNT); + printf("REG_BLDALPHA %04X\n", REG_BLDALPHA); + printf("REG_BLDY %04X\n", REG_BLDY); + printf("____________________________________\n"); + +} diff --git a/src/pokeball.c b/src/pokeball.c index a77401d44..7a95d718d 100644 --- a/src/pokeball.c +++ b/src/pokeball.c @@ -682,7 +682,7 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId) u8 wantedCry = gTasks[taskId].tCryTaskWantedCry; s8 pan = gTasks[taskId].tCryTaskPan; u16 species = gTasks[taskId].tCryTaskSpecies; - struct Pokemon *mon = (void *)(u32)((gTasks[taskId].tCryTaskMonPtr1 << 16) | (u16)(gTasks[taskId].tCryTaskMonPtr2)); + struct Pokemon *mon = (void *)(u32)(((u16)gTasks[taskId].tCryTaskMonPtr1 << 16) | (u16)(gTasks[taskId].tCryTaskMonPtr2)); switch (gTasks[taskId].tCryTaskState) { diff --git a/src/save.c b/src/save.c index 03c6aef16..0d6f763cb 100644 --- a/src/save.c +++ b/src/save.c @@ -691,6 +691,7 @@ u8 HandleSavingData(u8 saveType) WriteSaveSectorOrSlot(FULL_SAVE_SLOT, gRamSaveSectorLocations); break; } + Platform_StoreSaveFile(); gMain.vblankCounter1 = backupPtr; #if REVISION >= 0xA svc_FinishSave(); @@ -709,6 +710,7 @@ u8 TrySavingData(u8 saveType) HandleSavingData(saveType); if (!gDamagedSaveSectors) { + Platform_StoreSaveFile(); gSaveAttemptStatus = SAVE_STATUS_OK; return SAVE_STATUS_OK; } diff --git a/src/sound_mixer.c b/src/sound_mixer.c new file mode 100644 index 000000000..6d05fd211 --- /dev/null +++ b/src/sound_mixer.c @@ -0,0 +1,487 @@ +#include "global.h" +#include "music_player.h" +#include "sound_mixer.h" +#include "mp2k_common.h" + +#include "cgb_audio.h" + + +#define VCOUNT_VBLANK 160 +#define TOTAL_SCANLINES 228 + + +static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData2 *wav, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal); +void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, float *outBuffer, u8 dmaCounter, u16 maxBufSize); +static inline bool32 TickEnvelope(struct MixerSource *chan, struct WaveData2 *wav); +void GeneratePokemonSampleAudio(struct SoundMixerState *mixer, struct MixerSource *chan, s8 *current, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal, s32 samplesLeftInWav, signed envR, signed envL, s32 loopLen); +static s8 sub_82DF758(struct MixerSource *chan, u32 current); + +void RunMixerFrame(void) { + struct SoundMixerState *mixer = SOUND_INFO_PTR; + + if (mixer->lockStatus != MIXER_UNLOCKED) { + return; + } + mixer->lockStatus = MIXER_LOCKED; + + u32 maxScanlines = mixer->maxScanlines; + if (mixer->maxScanlines != 0) { + u32 vcount = REG_VCOUNT; + maxScanlines += vcount; + if (vcount < VCOUNT_VBLANK) { + maxScanlines += TOTAL_SCANLINES; + } + } + + if (mixer->firstPlayerFunc != NULL) { + mixer->firstPlayerFunc(mixer->firstPlayer); + } + + mixer->cgbMixerFunc(); + + s32 samplesPerFrame = mixer->samplesPerFrame; + float *outBuffer = mixer->outBuffer; + s32 dmaCounter = mixer->dmaCounter; + + if (dmaCounter > 1) { + outBuffer += samplesPerFrame * (mixer->framesPerDmaCycle - (dmaCounter - 1)) * 2; + } + + //MixerRamFunc mixerRamFunc = ((MixerRamFunc)MixerCodeBuffer); + SampleMixer(mixer, maxScanlines, samplesPerFrame, outBuffer, dmaCounter, MIXED_AUDIO_BUFFER_SIZE); + + cgb_audio_generate(samplesPerFrame); + +} + + + +//__attribute__((target("thumb"))) +void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, float *outBuffer, u8 dmaCounter, u16 maxBufSize) { + u32 reverb = mixer->reverb; + if (reverb) { + // The vanilla reverb effect outputs a mono sound from four sources: + // - L/R channels as they were mixer->framesPerDmaCycle frames ago + // - L/R channels as they were (mixer->framesPerDmaCycle - 1) frames ago + float *tmp1 = outBuffer; + float *tmp2; + if (dmaCounter == 2) { + tmp2 = mixer->outBuffer; + } else { + tmp2 = outBuffer + samplesPerFrame * 2; + } + uf16 i = 0; + do { + float s = tmp1[0] + tmp1[1] + tmp2[0] + tmp2[1]; + s *= ((float)reverb / 512.0f); + tmp1[0] = tmp1[1] = s; + tmp1+=2; + tmp2+=2; + } + while(++i < samplesPerFrame); + } else { + // memset(outBuffer, 0, samplesPerFrame); + // memset(outBuffer + maxBufSize, 0, samplesPerFrame); + for (int i = 0; i < samplesPerFrame; i++) { + float *dst = &outBuffer[i*2]; + dst[1] = dst[0] = 0.0f; + } + } + + float sampleRateReciprocal = mixer->sampleRateReciprocal; + sf8 numChans = mixer->numChans; + struct MixerSource *chan = mixer->chans; + + for (int i = 0; i < numChans; i++, chan++) { + struct WaveData2 *wav = chan->wav; + + if (scanlineLimit != 0) { + uf16 vcount = REG_VCOUNT; + if (vcount < VCOUNT_VBLANK) { + vcount += TOTAL_SCANLINES; + } + if (vcount >= scanlineLimit) { + goto returnEarly; + } + } + + if (TickEnvelope(chan, wav)) + { + + GenerateAudio(mixer, chan, wav, outBuffer, samplesPerFrame, sampleRateReciprocal); + } + } +returnEarly: + mixer->lockStatus = MIXER_UNLOCKED; +} + +// Returns TRUE if channel is still active after moving envelope forward a frame +//__attribute__((target("thumb"))) +static inline bool32 TickEnvelope(struct MixerSource *chan, struct WaveData2 *wav) { + // MP2K envelope shape + // | + // (linear)^ | + // Attack / \Decay (exponential) | + // / \_ | + // / '., Sustain | + // / '.______________ | + // / '-. Echo (linear) | + // / Release (exp) ''--..|\ | + // / \ | + + u8 status = chan->status; + if ((status & 0xC7) == 0) { + return FALSE; + } + + u8 env = 0; + if ((status & 0x80) == 0) { + env = chan->envelopeVol; + + if (status & 4) { + // Note-wise echo + --chan->echoVol; + if (chan->echoVol <= 0) { + chan->status = 0; + return FALSE; + } else { + return TRUE; + } + } else if (status & 0x40) { + // Release + chan->envelopeVol = env * chan->release / 256U; + u8 echoVol = chan->echoVol; + if (chan->envelopeVol > echoVol) { + return TRUE; + } else if (echoVol == 0) { + chan->status = 0; + return FALSE; + } else { + chan->status |= 4; + return TRUE; + } + } + + switch (status & 3) { + uf16 newEnv; + case 2: + // Decay + chan->envelopeVol = env * chan->decay / 256U; + + u8 sustain = chan->sustain; + if (chan->envelopeVol <= sustain && sustain == 0) { + // Duplicated echo check from Release section above + if (chan->echoVol == 0) { + chan->status = 0; + return FALSE; + } else { + chan->status |= 4; + return TRUE; + } + } else if (chan->envelopeVol <= sustain) { + chan->envelopeVol = sustain; + --chan->status; + } + break; + case 3: + attack: + newEnv = env + chan->attack; + if (newEnv > 0xFF) { + chan->envelopeVol = 0xFF; + --chan->status; + } else { + chan->envelopeVol = newEnv; + } + break; + case 1: // Sustain + default: + break; + } + + return TRUE; + } else if (status & 0x40) { + // Init and stop cancel each other out + chan->status = 0; + return FALSE; + } else { + // Init channel + chan->status = 3; +#ifdef POKEMON_EXTENSIONS + chan->current = wav->data + chan->ct; + chan->ct = wav->size - chan->ct; +#else + chan->current = wav->data; + chan->ct = wav->size; +#endif + chan->fw = 0; + chan->envelopeVol = 0; + if (wav->loopFlags & 0xC0) { + chan->status |= 0x10; + } + goto attack; + } +} + +//__attribute__((target("thumb"))) +static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData2 *wav, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal) {/*, [[[]]]) {*/ + uf8 v = chan->envelopeVol * (mixer->masterVol + 1) / 16U; + chan->envelopeVolR = chan->rightVol * v / 256U; + chan->envelopeVolL = chan->leftVol * v / 256U; + + s32 loopLen = 0; + s8 *loopStart; + if (chan->status & 0x10) { + loopStart = wav->data + wav->loopStart; + loopLen = wav->size - wav->loopStart; + } + s32 samplesLeftInWav = chan->ct; + s8 *current = chan->current; + signed envR = chan->envelopeVolR; + signed envL = chan->envelopeVolL; +#ifdef POKEMON_EXTENSIONS + if (chan->type & 0x30) { + GeneratePokemonSampleAudio(mixer, chan, current, outBuffer, samplesPerFrame, sampleRateReciprocal, samplesLeftInWav, envR, envL, loopLen); + } + else +#endif + if (chan->type & 8) { + for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) { + sf8 c = *(current++); + + outBuffer[1] += (c * envR) / 32768.0f; + outBuffer[0] += (c * envL) / 32768.0f; + if (--samplesLeftInWav == 0) { + samplesLeftInWav = loopLen; + if (loopLen != 0) { + current = loopStart; + } else { + chan->status = 0; + return; + } + } + } + + chan->ct = samplesLeftInWav; + chan->current = current; + } else { + float finePos = chan->fw; + float romSamplesPerOutputSample = chan->freq * sampleRateReciprocal; + + sf16 b = current[0]; + sf16 m = current[1] - b; + current += 1; + + for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) { + // Use linear interpolation to calculate a value between the current sample in the wav + // and the next sample. Also cancel out the 9.23 stuff + float sample = (finePos * m) + b; + + outBuffer[1] += (sample * envR) / 32768.0f; + outBuffer[0] += (sample * envL) / 32768.0f; + + finePos += romSamplesPerOutputSample; + u32 newCoarsePos = finePos; + if (newCoarsePos != 0) { + finePos -= (int)finePos; + samplesLeftInWav -= newCoarsePos; + if (samplesLeftInWav <= 0) { + if (loopLen != 0) { + current = loopStart; + newCoarsePos = -samplesLeftInWav; + samplesLeftInWav += loopLen; + while (samplesLeftInWav <= 0) { + newCoarsePos -= loopLen; + samplesLeftInWav += loopLen; + } + b = current[newCoarsePos]; + m = current[newCoarsePos + 1] - b; + current += newCoarsePos + 1; + } else { + chan->status = 0; + return; + } + } else { + b = current[newCoarsePos - 1]; + m = current[newCoarsePos] - b; + current += newCoarsePos; + } + } + } + + chan->fw = finePos; + chan->ct = samplesLeftInWav; + chan->current = current - 1; + } +} + +struct WaveData +{ + u16 type; + u16 status; + u32 freq; + u32 loopStart; + u32 size; // number of samples + s8 data[1]; // samples +}; + +void GeneratePokemonSampleAudio(struct SoundMixerState *mixer, struct MixerSource *chan, s8 *current, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal, s32 samplesLeftInWav, signed envR, signed envL, s32 loopLen) { + struct WaveData *wav = chan->wav; // r6 + float finePos = chan->fw; + if((chan->status & 0x20) == 0) { + chan->status |= 0x20; + if(chan->type & 0x10) { + s8 *waveEnd = wav->data + wav->size; + current = wav->data + (waveEnd - current); + chan->current = current; + } + if(wav->type != 0) { + current -= (uintptr_t)&wav->data; + chan->current = current; + } + } + float romSamplesPerOutputSample = chan->type & 8 ? 1.0f : chan->freq * sampleRateReciprocal; + if(wav->type != 0) { // is compressed + chan->blockCount = 0xFF000000; + if(chan->type & 0x10) { // is reverse + current -= 1; + sf16 b = sub_82DF758(chan, (uintptr_t)current); + sf16 m = sub_82DF758(chan, (uintptr_t)current - 1) - b; + + for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) { + float sample = (finePos * m) + b; + + outBuffer[1] += (sample * envR) / 32768.0f; + outBuffer[0] += (sample * envL) / 32768.0f; + + finePos += romSamplesPerOutputSample; + int newCoarsePos = finePos; + if (newCoarsePos != 0) { + finePos -= (int)finePos; + samplesLeftInWav -= newCoarsePos; + if (samplesLeftInWav <= 0) { + chan->status = 0; + break; + } + else { + current -= newCoarsePos; + b = sub_82DF758(chan, (uintptr_t)current); + m = sub_82DF758(chan, (uintptr_t)current - 1) - b; + } + } + } + + chan->fw = finePos; + chan->ct = samplesLeftInWav; + chan->current = current + 1; + } + else { + sf16 b = sub_82DF758(chan, (uintptr_t)current); + sf16 m = sub_82DF758(chan, (uintptr_t)current + 1) - b; + current += 1; + + for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) { + float sample = (finePos * m) + b; + + outBuffer[1] += (sample * envR) / 32768.0f; + outBuffer[0] += (sample * envL) / 32768.0f; + + finePos += romSamplesPerOutputSample; + u32 newCoarsePos = finePos; // lr + if (newCoarsePos != 0) { + finePos -= (int)finePos; + samplesLeftInWav -= newCoarsePos; + if (samplesLeftInWav <= 0) { + if (loopLen != 0) { + current = chan->wav->loopStart; + newCoarsePos = -samplesLeftInWav; + samplesLeftInWav += loopLen; + while (samplesLeftInWav <= 0) { + newCoarsePos -= loopLen; + samplesLeftInWav += loopLen; + } + current += newCoarsePos; + b = sub_82DF758(chan, (uintptr_t)current); + m = sub_82DF758(chan, (uintptr_t)current + 1) - b; + current += 1; + } else { + chan->status = 0; + return; + } + } else { + current += newCoarsePos - 1; + b = sub_82DF758(chan, (uintptr_t)current); + m = sub_82DF758(chan, (uintptr_t)current + 1) - b; + current += 1; + } + } + } + + chan->fw = finePos; + chan->ct = samplesLeftInWav; + chan->current = current - 1; + } + } + else { + if(chan->type & 0x10) { // is reverse + current -= 1; + sf16 b = current[0]; + sf16 m = current[-1] - b; + + for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) { + float sample = (finePos * m) + b; + + outBuffer[1] += (sample * envR) / 32768.0f; + outBuffer[0] += (sample * envL) / 32768.0f; + + finePos += romSamplesPerOutputSample; + int newCoarsePos = finePos; + if (newCoarsePos != 0) { + finePos -= (int)finePos; + samplesLeftInWav -= newCoarsePos; + if (samplesLeftInWav <= 0) { + chan->status = 0; + break; + } + else { + current -= newCoarsePos; + b = current[0]; + m = current[-1] - b; + } + } + } + + chan->fw = finePos; + chan->ct = samplesLeftInWav; + chan->current = current + 1; + } + } +} + +s8 gBDPCMBlockBuffer[64]; +extern const s8 gDeltaEncodingTable[]; + +static s8 sub_82DF758(struct MixerSource *chan, u32 current) { + u32 blockOffset = current >> 6; // current / 64 + u8 * blockPtr; + int i; + //In route 102 lotad wild battle when it growls crashes the game because it decompresses out of bounds data + //I gave it its own printf error so it wouldn't get forgotten as this needs a more proper fix + if (chan->wav->size < blockOffset * 0x21) { + printf("Out of bounds decompress in %s wav->size = %u blockPtr = %u\n", __func__, chan->wav->size, blockOffset * 0x21); + return gBDPCMBlockBuffer[current & 63]; + } + + if(chan->blockCount != blockOffset) { // decode block if not decoded + s32 s; + chan->blockCount = blockOffset; + blockPtr = chan->wav->data + chan->blockCount * 0x21; + gBDPCMBlockBuffer[0] = s = (s8)*blockPtr++; + gBDPCMBlockBuffer[1] = s += gDeltaEncodingTable[*blockPtr++ & 0xF]; + for(i = 2; i < 64; i+=2) { + u32 temp = *blockPtr++; + gBDPCMBlockBuffer[i] = s += gDeltaEncodingTable[temp >> 4]; + gBDPCMBlockBuffer[i+1] = s += gDeltaEncodingTable[temp & 0xF]; + } + } + return gBDPCMBlockBuffer[current & 63]; // index same as current % 64 +} diff --git a/src/union_room.c b/src/union_room.c index 82e8fb9c4..f719921fb 100644 --- a/src/union_room.c +++ b/src/union_room.c @@ -3536,71 +3536,7 @@ void InitUnionRoom(void) static void Task_InitUnionRoom(u8 taskId) { - s32 i; - u8 text[32]; - struct WirelessLink_URoom * data = sWirelessLinkMain.uRoom; - switch (data->state) - { - case 0: - data->state = 1; - break; - case 1: - SetHostRfuGameData(ACTIVITY_SEARCH, 0, FALSE); - SetWirelessCommType1(); - OpenLink(); - InitializeRfuLinkManager_EnterUnionRoom(); - RfuSetIgnoreError(TRUE); - data->state = 2; - break; - case 2: - data->incomingChildList = AllocZeroed(RFU_CHILD_MAX * sizeof(struct RfuIncomingPlayer)); - ClearIncomingPlayerList(data->incomingChildList->players, RFU_CHILD_MAX); - data->incomingParentList = AllocZeroed(RFU_CHILD_MAX * sizeof(struct RfuIncomingPlayer)); - ClearIncomingPlayerList(data->incomingParentList->players, RFU_CHILD_MAX); - data->playerList = AllocZeroed(MAX_UNION_ROOM_LEADERS * sizeof(struct RfuPlayer)); - ClearRfuPlayerList(data->playerList->players, MAX_UNION_ROOM_LEADERS); - data->spawnPlayer = AllocZeroed(sizeof(struct RfuPlayer)); - ClearRfuPlayerList(&data->spawnPlayer->players[0], 1); - data->searchTaskId = CreateTask_SearchForChildOrParent(data->incomingParentList, data->incomingChildList, LINK_GROUP_UNION_ROOM_INIT); - data->state = 3; - break; - case 3: - switch (HandlePlayerListUpdate()) - { - case PLIST_NEW_PLAYER: - case PLIST_RECENT_UPDATE: - if (sUnionRoomPlayerName[0] == EOS) - { - for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++) - { - if (data->playerList->players[i].groupScheduledAnim == UNION_ROOM_SPAWN_IN) - { - CopyAndTranslatePlayerName2(text, data->playerList->players[i]); - if (PlayerHasMetTrainerBefore(ReadAsU16(data->playerList->players[i].rfu.data.compatibility.playerTrainerId), text)) - { - StringCopy(sUnionRoomPlayerName, text); - break; - } - } - } - } - break; - case PLIST_UNUSED: - break; - } - break; - case 4: - Free(data->spawnPlayer); - Free(data->playerList); - Free(data->incomingParentList); - Free(data->incomingChildList); - DestroyTask(data->searchTaskId); - Free(sWirelessLinkMain.uRoom); - LinkRfu_Shutdown(); - DestroyTask(taskId); - break; - } } bool16 BufferUnionRoomPlayerName(void)