| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | #include <stdint.h>
 | 
					
						
							|  |  |  | #include <stdbool.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "pwrnoise.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef __cplusplus
 | 
					
						
							|  |  |  | extern "C" { | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void pwrnoise_noise_write(noise_channel_t *chan, uint8_t reg, uint8_t val) { | 
					
						
							|  |  |  | 	switch (reg & 0x1f) { | 
					
						
							|  |  |  | 		case 1: | 
					
						
							|  |  |  | 			chan->enable = (val & 0x80) != 0; | 
					
						
							|  |  |  | 			chan->am = (val & 0x02) != 0; | 
					
						
							|  |  |  | 			chan->tapb_enable = (val & 0x01) != 0; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 2: | 
					
						
							|  |  |  | 			chan->period = (chan->period & 0xf00) | val; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 3: | 
					
						
							| 
									
										
										
										
											2024-01-24 03:50:26 -05:00
										 |  |  | 			chan->period = (chan->period & 0xff) | (((uint16_t)val << 8) & 0xf00); | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 			chan->octave = val >> 4; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 4: | 
					
						
							|  |  |  | 			chan->lfsr = (chan->lfsr & 0xff00) | val; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 5: | 
					
						
							|  |  |  | 			chan->lfsr = (chan->lfsr & 0x00ff) | ((uint16_t)val << 8); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 6: | 
					
						
							|  |  |  | 			chan->tapa = val >> 4; | 
					
						
							|  |  |  | 			chan->tapb = val & 0x0f; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 7: | 
					
						
							|  |  |  | 			chan->vol = val; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | void pwrnoise_noise_step(noise_channel_t *chan, uint16_t cycles) { | 
					
						
							| 
									
										
										
										
											2024-01-23 09:33:24 -05:00
										 |  |  | 	if (!chan->enable) { | 
					
						
							|  |  |  | 		chan->out_latch = 0; | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	chan->octave_counter += cycles; | 
					
						
							| 
									
										
										
										
											2024-01-25 01:30:39 -05:00
										 |  |  | 	if (((cycles >= 2) && ((cycles >> (chan->octave + 1)) != 0)) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 		chan->period_counter += (cycles >> (chan->octave + 1)); | 
					
						
							| 
									
										
										
										
											2024-01-25 01:30:39 -05:00
										 |  |  | 		if ((cycles >> (chan->octave + 1)) == 0) ++chan->period_counter; | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2024-01-25 13:22:55 -05:00
										 |  |  | 		while (chan->period_counter >= 4096) { | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 			chan->prev = (uint8_t)(chan->lfsr >> 15); | 
					
						
							|  |  |  | 			uint16_t in = ((chan->lfsr >> chan->tapa) ^ (chan->tapb_enable ? (chan->lfsr >> chan->tapb) : 0)) & 0x0001; | 
					
						
							|  |  |  | 			chan->lfsr = (chan->lfsr << 1) | in; | 
					
						
							| 
									
										
										
										
											2024-01-25 13:22:55 -05:00
										 |  |  | 			chan->period_counter -= 4096 - chan->period; | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2024-01-23 01:56:46 -05:00
										 |  |  | 	chan->out_latch = chan->prev ? chan->vol : 0; | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void pwrnoise_slope_write(slope_channel_t *chan, uint8_t reg, uint8_t val) { | 
					
						
							|  |  |  | 	switch (reg & 0x1f) { | 
					
						
							|  |  |  | 		case 0: | 
					
						
							|  |  |  | 			chan->accum = val & 0x7f; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 1: | 
					
						
							|  |  |  | 			chan->enable = (val & 0x80) != 0; | 
					
						
							|  |  |  | 			if ((val & 0x40) != 0) { | 
					
						
							|  |  |  | 				chan->a = 0; | 
					
						
							|  |  |  | 				chan->b = 0; | 
					
						
							|  |  |  | 				chan->portion = false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			chan->flags = val & 0x3f; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 2: | 
					
						
							|  |  |  | 			chan->period = (chan->period & 0xf00) | val; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 3: | 
					
						
							| 
									
										
										
										
											2024-01-24 03:50:26 -05:00
										 |  |  | 			chan->period = (chan->period & 0xff) | (((uint16_t)val << 8) & 0xf00); | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 			chan->octave = val >> 4; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 4: | 
					
						
							|  |  |  | 			chan->alength = val; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 5: | 
					
						
							|  |  |  | 			chan->blength = val; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 6: | 
					
						
							|  |  |  | 			chan->aoffset = val >> 4; | 
					
						
							|  |  |  | 			chan->boffset = val & 0x0f; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 7: | 
					
						
							|  |  |  | 			chan->vol = val; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero) { | 
					
						
							|  |  |  | 	if (!chan->enable) { | 
					
						
							|  |  |  | 		chan->out_latch = 0; | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	chan->octave_counter += cycles; | 
					
						
							| 
									
										
										
										
											2024-01-25 01:30:39 -05:00
										 |  |  | 	if (((cycles >= 2) && ((cycles >> (chan->octave + 1)) != 0)) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 		chan->period_counter += (cycles >> (chan->octave + 1)); | 
					
						
							| 
									
										
										
										
											2024-01-25 01:30:39 -05:00
										 |  |  | 		if ((cycles >> (chan->octave + 1)) == 0) ++chan->period_counter; | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2024-01-25 13:22:55 -05:00
										 |  |  | 		while (chan->period_counter >= 4096) { | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 			if (!chan->portion) { | 
					
						
							|  |  |  | 				if ((chan->flags & 0x02) != 0) chan->accum -= chan->aoffset; | 
					
						
							|  |  |  | 				else chan->accum += chan->aoffset; | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 				if ((chan->flags & 0x20) != 0 && chan->accum > 0x7f) chan->accum = (chan->flags & 0x02) ? 0x00 : 0x7f; | 
					
						
							|  |  |  | 				chan->accum &= 0x7f; | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 				if (++chan->a > chan->alength) { | 
					
						
							| 
									
										
										
										
											2024-01-26 13:22:23 -05:00
										 |  |  | 					if ((chan->flags & 0x04) != 0) chan->accum = (chan->flags & 0x01) ? 0x7f : 0x00; | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 					chan->b = 0x00; | 
					
						
							|  |  |  | 					chan->portion = true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			else { | 
					
						
							|  |  |  | 				if ((chan->flags & 0x01) != 0) chan->accum -= chan->boffset; | 
					
						
							|  |  |  | 				else chan->accum += chan->boffset; | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 				if ((chan->flags & 0x10) != 0 && chan->accum > 0x7f) chan->accum = (chan->flags & 0x01) ? 0x00 : 0x7f; | 
					
						
							|  |  |  | 				chan->accum &= 0x7f; | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 				if (++chan->b > chan->blength) { | 
					
						
							| 
									
										
										
										
											2024-01-26 13:22:23 -05:00
										 |  |  | 					if ((chan->flags & 0x08) != 0) chan->accum = (chan->flags & 0x02) ? 0x7f : 0x00; | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 					chan->a = 0x00; | 
					
						
							|  |  |  | 					chan->portion = false; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			 | 
					
						
							| 
									
										
										
										
											2024-01-25 13:22:55 -05:00
										 |  |  | 			chan->period_counter -= 4096 - chan->period; | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 			 | 
					
						
							|  |  |  | 			uint8_t left = chan->accum >> 3; | 
					
						
							|  |  |  | 			uint8_t right = chan->accum >> 3; | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 			switch (chan->vol >> 4) { | 
					
						
							|  |  |  | 				case 0: | 
					
						
							|  |  |  | 				case 1: | 
					
						
							|  |  |  | 					left >>= 1; | 
					
						
							|  |  |  | 				case 2: | 
					
						
							|  |  |  | 				case 3: | 
					
						
							|  |  |  | 					left >>= 1; | 
					
						
							|  |  |  | 				case 4: | 
					
						
							|  |  |  | 				case 5: | 
					
						
							|  |  |  | 				case 6: | 
					
						
							|  |  |  | 				case 7: | 
					
						
							|  |  |  | 					left >>= 1; | 
					
						
							|  |  |  | 				default: break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			switch (chan->vol & 0xf) { | 
					
						
							|  |  |  | 				case 0: | 
					
						
							|  |  |  | 				case 1: | 
					
						
							|  |  |  | 					right >>= 1; | 
					
						
							|  |  |  | 				case 2: | 
					
						
							|  |  |  | 				case 3: | 
					
						
							|  |  |  | 					right >>= 1; | 
					
						
							|  |  |  | 				case 4: | 
					
						
							|  |  |  | 				case 5: | 
					
						
							|  |  |  | 				case 6: | 
					
						
							|  |  |  | 				case 7: | 
					
						
							|  |  |  | 					right >>= 1; | 
					
						
							|  |  |  | 				default: break; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 			left &= (chan->vol >> 4); | 
					
						
							|  |  |  | 			right &= (chan->vol & 0xf); | 
					
						
							|  |  |  | 			chan->prev = (left << 4) | right; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 	chan->out_latch = force_zero ? 0 : chan->prev; | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void pwrnoise_reset(power_noise_t *pn) { | 
					
						
							|  |  |  | 	memset(pn, 0, sizeof(power_noise_t)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void pwrnoise_write(power_noise_t *pn, uint8_t reg, uint8_t val) { | 
					
						
							|  |  |  | 	reg &= 0x1f; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	if (reg == 0x00) { | 
					
						
							|  |  |  | 		pn->flags = val; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else if (reg == 0x08 && !(pn->flags & 0x20)) { | 
					
						
							|  |  |  | 		pn->gpioa = val; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else if (reg == 0x10 && !(pn->flags & 0x40)) { | 
					
						
							|  |  |  | 		pn->gpiob = val; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else if (reg < 0x08) { | 
					
						
							|  |  |  | 		pwrnoise_noise_write(&pn->n1, reg % 8, val); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else if (reg < 0x10) { | 
					
						
							|  |  |  | 		pwrnoise_noise_write(&pn->n2, reg % 8, val); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else if (reg < 0x18) { | 
					
						
							|  |  |  | 		pwrnoise_noise_write(&pn->n3, reg % 8, val); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		pwrnoise_slope_write(&pn->s, reg % 8, val); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | void pwrnoise_step(power_noise_t *pn, uint16_t cycles, int16_t *left, int16_t *right) { | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 	int32_t final_left, final_right; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	if ((pn->flags & 0x80) != 0) { | 
					
						
							| 
									
										
										
										
											2024-01-23 01:41:20 -05:00
										 |  |  | 		pwrnoise_noise_step(&pn->n1, cycles); | 
					
						
							|  |  |  | 		pwrnoise_noise_step(&pn->n2, cycles); | 
					
						
							|  |  |  | 		pwrnoise_noise_step(&pn->n3, cycles); | 
					
						
							|  |  |  | 		pwrnoise_slope_step(&pn->s, cycles, (pn->n1.am && !(pn->n1.prev)) || (pn->n2.am && !(pn->n2.prev)) || (pn->n3.am && !(pn->n3.prev))); | 
					
						
							| 
									
										
										
										
											2024-01-21 06:47:38 -05:00
										 |  |  | 		 | 
					
						
							|  |  |  | 		final_left = (pn->n1.out_latch >> 4) + (pn->n2.out_latch >> 4) + (pn->n3.out_latch >> 4) + (pn->s.out_latch >> 4); | 
					
						
							|  |  |  | 		final_right = (pn->n1.out_latch & 0xf) + (pn->n2.out_latch & 0xf) + (pn->n3.out_latch & 0xf) + (pn->s.out_latch & 0xf); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		final_left = 0; | 
					
						
							|  |  |  | 		final_right = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	*left = (int16_t)((final_left * 65535 / 63 - 32768) * (pn->flags & 0x7) / 7); | 
					
						
							|  |  |  | 	*right = (int16_t)((final_right * 65535 / 63 - 32768) * (pn->flags & 0x7) / 7); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef __cplusplus
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 |