├── README.md ├── io_putchar.c ├── wormboy ├── video.h ├── platform.h ├── types.h ├── sound.h ├── cpu.h ├── ntsc.h ├── platform.cpp ├── mem.h ├── vincent_data.h ├── ntsc.cpp ├── sound.cpp ├── video.cpp ├── mem.cpp └── cpu.cpp ├── interrupt_in.cpp ├── usb_serial.cpp ├── alternate_functions.cpp ├── stm32h7xx_hal_msp.c ├── setup.cpp ├── main.cpp ├── stm32h7xx_it.c ├── digital_inout.cpp ├── analog_in.cpp ├── gp_timer.cpp └── system_stm32h7xx.c /README.md: -------------------------------------------------------------------------------- 1 | # stm32-gameboy 2 | A gameboy emulator for the STM32H743ZI. Output over composite video (NTSC). 3 | This is an AC6 workbench project 4 | 5 | See: 6 | https://www.youtube.com/watch?v=k2v0xZqib_4 7 | for a demo 8 | -------------------------------------------------------------------------------- /io_putchar.c: -------------------------------------------------------------------------------- 1 | #include "stm32h7xx.h" 2 | 3 | extern UART_HandleTypeDef UartHandle; 4 | 5 | int __io_putchar(int ch) 6 | { 7 | /* Place your implementation of fputc here */ 8 | /* e.g. write a character to the USART1 and Loop until the end of transmission */ 9 | HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, 0xFFFF); 10 | 11 | return ch; 12 | } 13 | -------------------------------------------------------------------------------- /wormboy/video.h: -------------------------------------------------------------------------------- 1 | #ifndef GBC_VIDEO_H 2 | #define GBC_VIDEO_H 3 | 4 | #include "types.h" 5 | 6 | struct VideoState { 7 | u32 mode; 8 | u32 modeClock; 9 | u32 line; 10 | u8* frameBuffer; 11 | }; 12 | 13 | extern VideoState globalVideoState; 14 | 15 | void initVideo(u8* frameBuffer); 16 | void stepVideo(u32 cycles); 17 | void renderLine(); 18 | 19 | 20 | #endif //GBC_VIDEO_H 21 | -------------------------------------------------------------------------------- /wormboy/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef GBC_PLATFORM_H 2 | #define GBC_PLATFORM_H 3 | 4 | #include "types.h" 5 | 6 | struct FileLoadData { 7 | u8* data; 8 | u32 size; 9 | }; 10 | 11 | void* badalloc_check(u32 size, const char* alloc_name); 12 | FileLoadData loadFile(const char* name); 13 | void saveFile(const char* name, FileLoadData info); 14 | void updateKeyboard(KeyState* keys); 15 | 16 | #endif //GBC_PLATFORM_H 17 | -------------------------------------------------------------------------------- /interrupt_in.cpp: -------------------------------------------------------------------------------- 1 | #include "interrupt_in.h" 2 | #include "digital_inout.h" 3 | #include "stm32h7xx.h" 4 | #include "stm32h7xx_hal.h" 5 | 6 | void __gpio_set_exti(uint8_t port, uint8_t pin) { 7 | //this also eanbles the GPIO clock 8 | __gpio_set_input(port, pin); 9 | 10 | //enable syscfg clock 11 | RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; 12 | 13 | //configure EXTI mapping 14 | uint8_t exticrx = pin / 4; 15 | uint8_t exticrp = pin % 4; 16 | SYSCFG->EXTICR[exticrx] &= ~(0xf << (4 * exticrp)); 17 | SYSCFG->EXTICR[exticrx] |= port << (4 * exticrp); 18 | } 19 | -------------------------------------------------------------------------------- /usb_serial.cpp: -------------------------------------------------------------------------------- 1 | #include "stm32h7xx.h" 2 | #include "usb_serial.h" 3 | 4 | UART_HandleTypeDef UartHandle; 5 | 6 | void Init_USB_Serial(uint32_t baud) { 7 | UartHandle.Instance = USARTx; 8 | UartHandle.Init.BaudRate = baud; 9 | UartHandle.Init.WordLength = UART_WORDLENGTH_8B; 10 | UartHandle.Init.StopBits = UART_STOPBITS_1; 11 | UartHandle.Init.Parity = UART_PARITY_NONE; 12 | UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; 13 | UartHandle.Init.Mode = UART_MODE_TX_RX; 14 | UartHandle.Init.OverSampling = UART_OVERSAMPLING_16; 15 | 16 | HAL_UART_Init(&UartHandle); 17 | } 18 | -------------------------------------------------------------------------------- /wormboy/types.h: -------------------------------------------------------------------------------- 1 | #ifndef GBC_TYPES_H 2 | #define GBC_TYPES_H 3 | 4 | #include 5 | #include 6 | 7 | #define u8 uint8_t 8 | #define u16 uint16_t 9 | #define u32 uint32_t 10 | #define s8 int8_t 11 | #define s16 int16_t 12 | #define s32 int32_t 13 | #define nullptr NULL 14 | 15 | //using u8 = uint8_t; 16 | //using u16 = uint16_t; 17 | //using u32 = uint32_t; 18 | // 19 | //using s8 = int8_t; 20 | //using s16 = int16_t; 21 | //using s32 = int32_t; 22 | 23 | struct KeyState { 24 | bool a,b,u,d,l,r,start,select,turbo, save, load; 25 | }; 26 | 27 | extern KeyState keyboard; 28 | 29 | #endif //GBC_TYPES_H 30 | -------------------------------------------------------------------------------- /alternate_functions.cpp: -------------------------------------------------------------------------------- 1 | #include "alternate_functions.h" 2 | #include "digital_inout.h" 3 | #include "stm32h7xx.h" 4 | 5 | void __map_af(uint8_t port, uint8_t pin, uint8_t af) { 6 | //gpio clock enable 7 | RCC->AHB4ENR |= __gpiox_rcc_en[port]; 8 | 9 | //gpio alternate function mode 10 | *__gpiox_moder[port] &= ~(1 << (2 * pin)); 11 | *__gpiox_moder[port] |= (1 << (2 * pin + 1)); 12 | 13 | //which AFR register? 14 | volatile uint32_t * afr; 15 | if (pin > 7) afr = __gpiox_afrh[port]; 16 | else afr = __gpiox_afrl[port]; 17 | if (pin > 7) pin -= 8; 18 | 19 | //set alternate function 20 | *afr &= ~(0x0f << (4 * pin)); 21 | *afr |= (af << (4 * pin)); 22 | } 23 | -------------------------------------------------------------------------------- /wormboy/sound.h: -------------------------------------------------------------------------------- 1 | /* 2 | * sound.h 3 | * 4 | * Created on: Jan 13, 2019 5 | * Author: jared 6 | */ 7 | 8 | #ifndef WORMBOY_SOUND_H_ 9 | #define WORMBOY_SOUND_H_ 10 | 11 | #include "types.h" 12 | 13 | struct SqOsc { 14 | u32 per; 15 | //u32 duty; 16 | u32 amp; 17 | u32 cnt; 18 | }; 19 | 20 | 21 | void snd(); 22 | void run_sound(u32 cpuCycles); 23 | 24 | void ch1_trigger_load(); 25 | void ch1_length_load(); 26 | void ch1_update_freq(); 27 | 28 | void ch2_trigger_load(); 29 | void ch2_length_load(); 30 | void ch2_update_freq(); 31 | 32 | void ch3_trigger_load(); 33 | void ch3_length_load(); 34 | void ch3_update_freq(); 35 | 36 | void ch4_trigger_load(); 37 | void ch4_length_load(); 38 | void ch4_update_freq(); 39 | 40 | #endif /* WORMBOY_SOUND_H_ */ 41 | -------------------------------------------------------------------------------- /wormboy/cpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cpu.h 3 | * 4 | * Created on: Jan 13, 2019 5 | * Author: jared 6 | */ 7 | 8 | #ifndef WORMBOY_CPU_H_ 9 | #define WORMBOY_CPU_H_ 10 | 11 | #include "types.h" 12 | 13 | extern u32 frameNum; 14 | 15 | // 16 bit register 16 | union reg { 17 | u16 v; 18 | struct { 19 | u8 lo; 20 | u8 hi; 21 | }; 22 | }; 23 | 24 | struct CpuState { 25 | // registers 26 | reg bc, de, hl; 27 | u8 f; 28 | u8 a; 29 | u16 sp, pc; 30 | 31 | bool halt; 32 | uint64_t cycleCount; 33 | uint64_t divOffset; 34 | u8 ime; 35 | u32 timSubcount; 36 | }; 37 | 38 | // external interface: 39 | extern CpuState globalState; 40 | void resetCpu(); // reinitialize the cpu 41 | u32 cpuStep(); // step 1 instruction, returns number of clock cycles elapsed 42 | 43 | 44 | bool getZeroFlag(); 45 | bool getSubtractFlag(); 46 | bool getHalfCarryFlag(); 47 | bool getCarryFlag(); 48 | void setZeroFlag(); 49 | void clearZeroFlag(); 50 | void setSubtractFlag(); 51 | void clearSubtractFlag(); 52 | void setHalfCarryFlag(); 53 | void clearHalfCarryFlag(); 54 | void setCarryFlag(); 55 | void clearCarryFlag(); 56 | void clearAllFlags(); 57 | 58 | #endif /* WORMBOY_CPU_H_ */ 59 | -------------------------------------------------------------------------------- /wormboy/ntsc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ntsc.h 3 | * 4 | * Created on: Jan 13, 2019 5 | * Author: jared 6 | */ 7 | 8 | #ifndef WORMBOY_NTSC_H_ 9 | #define WORMBOY_NTSC_H_ 10 | 11 | #include "types.h" 12 | #include 13 | 14 | 15 | void draw_vincent_string(char* str); 16 | void new_line(); 17 | void clear_all_text(); 18 | void draw_gfx_line(float x0, float y0, float x1, float y1); 19 | void init_ntsc(); 20 | void vid(); 21 | extern volatile uint8_t drawing; 22 | // Buffer sizes 23 | #define V_RES 144 24 | #define H_RES (279 - 80 + 20 - 4) 25 | 26 | // Porches 27 | #define V_PORCH_SIZE 30 28 | #define H_PORCH_SIZE 25 29 | 30 | // good new stuff 31 | #define X0s 50 // start of image in X 32 | #define Y0s 0 // start of image in Y 33 | #define XL (224 - 80 + 20 - 4) // 25 chars 34 | #define YL 144 // 20 chars 35 | 36 | //video 37 | #define VIDEO_FRAME_SIZE (XL * YL / 2) 38 | 39 | //*SD card*/ 40 | #define DI PC_3 41 | #define DO PC_2 42 | #define SCK PB_10 43 | #define CS PB_12 44 | 45 | extern uint8_t *im_line_vas[]; 46 | extern volatile uint8_t bufferSelect; 47 | extern uint32_t tics; 48 | 49 | #define set_pixel(x, y, color) im_line_va[H_RES*((y)+Y0) + (x) + X0] = (color) 50 | 51 | #endif /* WORMBOY_NTSC_H_ */ 52 | -------------------------------------------------------------------------------- /stm32h7xx_hal_msp.c: -------------------------------------------------------------------------------- 1 | #include "setup.h" 2 | #include "usb_serial.h" 3 | #include "stm32h7xx_hal.h" 4 | 5 | void HAL_UART_MspInit(UART_HandleTypeDef *huart) 6 | { 7 | GPIO_InitTypeDef GPIO_InitStruct; 8 | 9 | RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit; 10 | 11 | USARTx_TX_GPIO_CLK_ENABLE(); 12 | USARTx_RX_GPIO_CLK_ENABLE(); 13 | 14 | RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART3; 15 | RCC_PeriphClkInit.Usart16ClockSelection = RCC_USART3CLKSOURCE_D2PCLK1; 16 | HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit); 17 | 18 | USARTx_CLK_ENABLE(); 19 | 20 | GPIO_InitStruct.Pin = USARTx_TX_PIN; 21 | GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 22 | GPIO_InitStruct.Pull = GPIO_PULLUP; 23 | GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; 24 | GPIO_InitStruct.Alternate = USARTx_TX_AF; 25 | 26 | HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct); 27 | 28 | GPIO_InitStruct.Pin = USARTx_RX_PIN; 29 | GPIO_InitStruct.Alternate = USARTx_RX_AF; 30 | 31 | HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct); 32 | } 33 | 34 | void HAL_UART_MspDeInit(UART_HandleTypeDef *huart) 35 | { 36 | USARTx_FORCE_RESET(); 37 | USARTx_RELEASE_RESET(); 38 | 39 | HAL_GPIO_DeInit(USARTx_TX_GPIO_PORT, USARTx_TX_PIN); 40 | 41 | HAL_GPIO_DeInit(USARTx_RX_GPIO_PORT, USARTx_RX_PIN); 42 | } 43 | -------------------------------------------------------------------------------- /setup.cpp: -------------------------------------------------------------------------------- 1 | #include "setup.h" 2 | #include "stm32h7xx.h" 3 | #include "stm32h7xx_hal.h" 4 | 5 | void SystemClock_Config() 6 | { 7 | RCC_ClkInitTypeDef RCC_ClkInitStruct; 8 | RCC_OscInitTypeDef RCC_OscInitStruct; 9 | 10 | /*!< Supply configuration update enable */ 11 | MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0); 12 | 13 | /* The voltage scaling allows optimizing the power consumption when the device is 14 | clocked below the maximum system frequency, to update the voltage scaling value 15 | regarding system frequency refer to product datasheet. */ 16 | __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); 17 | 18 | while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {} 19 | 20 | /* Enable HSE Oscillator and activate PLL with HSE as source */ 21 | RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; 22 | RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; 23 | RCC_OscInitStruct.HSIState = RCC_HSI_OFF; 24 | RCC_OscInitStruct.CSIState = RCC_CSI_OFF; 25 | RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; 26 | RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; 27 | 28 | RCC_OscInitStruct.PLL.PLLM = 4; 29 | RCC_OscInitStruct.PLL.PLLN = 400; 30 | RCC_OscInitStruct.PLL.PLLP = 2; 31 | RCC_OscInitStruct.PLL.PLLR = 2; 32 | RCC_OscInitStruct.PLL.PLLQ = 4; 33 | 34 | RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; 35 | RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2; 36 | HAL_RCC_OscConfig(&RCC_OscInitStruct); 37 | 38 | /* Select PLL as system clock source and configure bus clocks dividers */ 39 | RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \ 40 | RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1); 41 | 42 | RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; 43 | RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; 44 | RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2; 45 | RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; 46 | RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; 47 | RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 48 | RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; 49 | HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); 50 | } 51 | 52 | void CPU_CACHE_Enable() 53 | { 54 | /* Enable I-Cache */ 55 | SCB_EnableICache(); 56 | 57 | /* Enable D-Cache */ 58 | SCB_EnableDCache(); 59 | } 60 | 61 | void System_Setup() { 62 | CPU_CACHE_Enable(); 63 | HAL_Init(); 64 | SystemClock_Config(); 65 | } 66 | -------------------------------------------------------------------------------- /wormboy/platform.cpp: -------------------------------------------------------------------------------- 1 | // Linux specific code (excluding graphics, those are in graphics_display.cpp) 2 | #include "platform.h" 3 | #include 4 | #include 5 | 6 | // global keyboard 7 | KeyState keyboard; 8 | 9 | u32 totalAlloc = 0; 10 | void* badalloc_check(u32 size, const char* alloc_name) { 11 | void* ptr = malloc(size); 12 | if(ptr) { 13 | totalAlloc += size; 14 | printf("[badalloc] success %s (%d bytes, total %d)\n", alloc_name, size, totalAlloc); 15 | } else { 16 | printf("~!~!~!1!!!!!-----> [badalloc] fail %s (tried to do %d when %d is already done...)\n", alloc_name, size, totalAlloc); 17 | } 18 | return ptr; 19 | } 20 | 21 | // open a file 22 | FILE* fp; 23 | FileLoadData loadFile(const char* name) { 24 | fp = fopen(name, "rb"); 25 | FileLoadData loadData; 26 | 27 | if(!fp) { 28 | printf("loadFile(%s) failed!\r\n", name); 29 | loadData.data = nullptr; 30 | loadData.size = 0; 31 | return loadData; 32 | } 33 | printf("loadfile fp is good\r\n"); 34 | 35 | fseek(fp, 0, SEEK_END); 36 | u32 fileSize = (u32)ftell(fp); 37 | fileSize = 0x400; 38 | printf("loadFile 0x%x bytes\r\n", fileSize); 39 | fseek(fp, 0, SEEK_SET); 40 | u8* fileData = (u8*)badalloc_check(fileSize, name); 41 | if(fileData) { 42 | printf("allocation for loadFile succes\r\n"); 43 | } else { 44 | printf("allocation for loadfile fail\r\n"); 45 | } 46 | fread(fileData, 1, fileSize, fp); 47 | //fclose(fp); 48 | 49 | printf("loadfile(%s) has loaded %d bytes (%.3f MB)\r\n", name, fileSize, (float)fileSize / (1 << 20)); 50 | 51 | loadData.size = fileSize; 52 | loadData.data = fileData; 53 | return loadData; 54 | } 55 | 56 | void saveFile(const char* name, FileLoadData info) { 57 | // FILE* fp = fopen(name, "wb"); 58 | // printf("save 0x%x bytes 0x%llx\n", info.size, info.data); 59 | // fwrite(info.data, info.size, 1, fp); 60 | // fclose(fp); 61 | } 62 | 63 | // update keyboard. Also checks to see if it's time to quit. 64 | void updateKeyboard(KeyState* keys) { 65 | // SDL_Event e; 66 | // SDL_PollEvent(&e); 67 | // if(e.type == SDL_QUIT) { 68 | // exit(0); 69 | // } 70 | // const u8* keyStats = SDL_GetKeyboardState(nullptr); 71 | // keys->a = keyStats[SDL_SCANCODE_A]; 72 | // keys->b = keyStats[SDL_SCANCODE_B]; 73 | // keys->u = keyStats[SDL_SCANCODE_UP]; 74 | // keys->d = keyStats[SDL_SCANCODE_DOWN]; 75 | // keys->l = keyStats[SDL_SCANCODE_LEFT]; 76 | // keys->r = keyStats[SDL_SCANCODE_RIGHT]; 77 | // keys->save = keyStats[SDL_SCANCODE_S]; 78 | // keys->load = keyStats[SDL_SCANCODE_D]; 79 | // keys->start = keyStats[SDL_SCANCODE_P]; 80 | // keys->select = keyStats[SDL_SCANCODE_L]; 81 | // keys->turbo = keyStats[SDL_SCANCODE_SPACE]; 82 | } 83 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "mbad.h" 2 | #include "wormboy/ntsc.h" 3 | #include "wormboy/cpu.h" 4 | #include "wormboy/mem.h" 5 | #include "wormboy/video.h" 6 | #include "stm32h7xx_nucleo_144.h" 7 | //#include "wormboy/tetris.h" 8 | #include "wormboy/pkmn_red.h" 9 | #include "wormboy/sound.h" 10 | 11 | int led_on = 1; 12 | 13 | void toggle_led() { 14 | if (led_on) BSP_LED_On(LED2); 15 | else BSP_LED_Off(LED2); 16 | led_on = !led_on; 17 | } 18 | 19 | void update_inputs() { 20 | keyboard.a = !digitalRead(PC, 6); 21 | keyboard.b = !digitalRead(PB, 15); 22 | keyboard.start = !digitalRead(PB, 13); 23 | keyboard.select = !digitalRead(PB, 12); 24 | keyboard.r = !digitalRead(PA, 15); 25 | keyboard.d = !digitalRead(PC, 7); 26 | keyboard.l = !digitalRead(PB, 5); 27 | keyboard.u = !digitalRead(PB, 3); 28 | } 29 | 30 | int main(void) 31 | { 32 | // initialize MBAD 33 | System_Setup(); 34 | Init_USB_Serial(115200); 35 | 36 | // debuggin prints 37 | printf("derp!\r\n"); 38 | printf("System core clock : %f MHz\r\n", (double)SystemCoreClock / 1e6); 39 | 40 | // initialize pins with MBAD 41 | BSP_LED_Init(LED2); // flashy light on the board 42 | digitalOut(PD, 7); // video output 43 | digitalOut(PD, 6); 44 | digitalOut(PD, 5); 45 | digitalOut(PD, 4); 46 | digitalOut(PD, 3); 47 | digitalOut(PD, 2); 48 | digitalOut(PD, 1); 49 | digitalOut(PD, 0); 50 | digitalOut(PA, 3); 51 | digitalOut(PC, 0); 52 | 53 | digitalIn(PC, 6, 1); // controller 54 | digitalIn(PB, 15, 1); 55 | digitalIn(PB, 13, 1); 56 | digitalIn(PB, 12, 1); 57 | digitalIn(PA, 15, 1); 58 | digitalIn(PC, 7, 1); 59 | digitalIn(PB, 5, 1); 60 | digitalIn(PB, 3, 1); 61 | 62 | digitalOut(PC, 8); // sound 63 | 64 | // initialize video out 65 | init_ntsc(); 66 | 67 | printf("[main] start vid\r\n"); 68 | // set up video interrupt 69 | tickerStart_usecs(TIMER5, 64); 70 | tickerAttach(TIMER5, vid); 71 | 72 | // tickerStart_usecs(TIMER2, 20); 73 | // tickerAttach(TIMER2, snd); 74 | 75 | printf("[main] screen %d x %d (buffer %d x %d, %d bytes)\n", 76 | XL,YL,H_RES,V_RES,H_RES*V_RES); 77 | 78 | printf("[main] load rom\r\n"); 79 | FileLoadData rom; 80 | rom.size = 0x10000; 81 | rom.data = (u8*)(void*)pkmn_red; 82 | 83 | printf("[main] START WORM BOY\r\n"); 84 | initMem(rom); 85 | initVideo(nullptr); 86 | 87 | __gpio_set_analog(PA, 4); 88 | 89 | __HAL_RCC_DAC12_CLK_ENABLE(); // enable DAC clock 90 | DAC1->CR = 1; 91 | DAC1->DHR12R1 = (1 << 11); 92 | 93 | for(;;) { 94 | 95 | update_inputs(); 96 | u32 soundCycleCounter = 0; 97 | 98 | for(int i = 0; i < 100; i++) { 99 | u32 cpuCycles = cpuStep(); 100 | stepVideo(cpuCycles); 101 | soundCycleCounter += cpuCycles; 102 | } 103 | run_sound(soundCycleCounter); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /stm32h7xx_it.c: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file stm32h7xx_it.c 4 | * @author Ac6 5 | * @version V1.0 6 | * @date 02-Feb-2015 7 | * @brief Default Interrupt Service Routines. 8 | ****************************************************************************** 9 | */ 10 | 11 | /* Includes ------------------------------------------------------------------*/ 12 | #include "stm32h7xx_hal.h" 13 | #include "stm32h7xx.h" 14 | #ifdef USE_RTOS_SYSTICK 15 | #include 16 | #endif 17 | #include "stm32h7xx_it.h" 18 | #include "gp_timer.h" 19 | 20 | /* Private typedef -----------------------------------------------------------*/ 21 | /* Private define ------------------------------------------------------------*/ 22 | /* Private macro -------------------------------------------------------------*/ 23 | /* Private variables ---------------------------------------------------------*/ 24 | /* Private function prototypes -----------------------------------------------*/ 25 | /* Private functions ---------------------------------------------------------*/ 26 | 27 | /******************************************************************************/ 28 | /* Processor Exceptions Handlers */ 29 | /******************************************************************************/ 30 | 31 | /** 32 | * @brief This function handles SysTick Handler, but only if no RTOS defines it. 33 | * @param None 34 | * @retval None 35 | */ 36 | void SysTick_Handler(void) 37 | { 38 | HAL_IncTick(); 39 | HAL_SYSTICK_IRQHandler(); 40 | #ifdef USE_RTOS_SYSTICK 41 | osSystickHandler(); 42 | #endif 43 | } 44 | 45 | __weak void TIM2_IRQHandler() { 46 | if (TIM2->SR & TIM_SR_UIF) { 47 | if (__gp_timx_irq[TIMER2] != NULL) (*__gp_timx_irq[TIMER2])(); 48 | } 49 | TIM2->SR = 0x00; 50 | } 51 | 52 | __weak void TIM3_IRQHandler() { 53 | if (TIM3->SR & TIM_SR_UIF) { 54 | if (__gp_timx_irq[TIMER3] != NULL) (*__gp_timx_irq[TIMER3])(); 55 | } 56 | TIM3->SR = 0x00; 57 | } 58 | 59 | __weak void TIM4_IRQHandler() { 60 | if (TIM4->SR & TIM_SR_UIF) { 61 | if (__gp_timx_irq[TIMER4] != NULL) (*__gp_timx_irq[TIMER4])(); 62 | } 63 | TIM4->SR = 0x00; 64 | } 65 | 66 | __weak void TIM5_IRQHandler() { 67 | if (TIM5->SR & TIM_SR_UIF) { 68 | if (__gp_timx_irq[TIMER5] != NULL) (*__gp_timx_irq[TIMER5])(); 69 | } 70 | TIM5->SR = 0x00; 71 | } 72 | 73 | __weak void TIM15_IRQHandler() { 74 | if (TIM15->SR & TIM_SR_UIF) { 75 | if (__gp_timx_irq[TIMER15] != NULL) (*__gp_timx_irq[TIMER15])(); 76 | } 77 | TIM15->SR = 0x00; 78 | } 79 | 80 | __weak void TIM16_IRQHandler() { 81 | if (TIM16->SR & TIM_SR_UIF) { 82 | if (__gp_timx_irq[TIMER16] != NULL) (*__gp_timx_irq[TIMER16])(); 83 | } 84 | TIM16->SR = 0x00; 85 | } 86 | 87 | __weak void TIM17_IRQHandler() { 88 | if (TIM17->SR & TIM_SR_UIF) { 89 | if (__gp_timx_irq[TIMER17] != NULL) (*__gp_timx_irq[TIMER17])(); 90 | } 91 | TIM17->SR = 0x00; 92 | } 93 | -------------------------------------------------------------------------------- /digital_inout.cpp: -------------------------------------------------------------------------------- 1 | #include "setup.h" 2 | #include "digital_inout.h" 3 | 4 | volatile uint32_t * __gpiox_moder[11] = { 5 | &(GPIOA->MODER), &(GPIOB->MODER), &(GPIOC->MODER), &(GPIOD->MODER), 6 | &(GPIOE->MODER), &(GPIOF->MODER), &(GPIOG->MODER), &(GPIOH->MODER), 7 | &(GPIOI->MODER), &(GPIOJ->MODER), &(GPIOK->MODER) 8 | }; 9 | 10 | volatile uint32_t * __gpiox_otyper[11] = { 11 | &(GPIOA->OTYPER), &(GPIOB->OTYPER), &(GPIOC->OTYPER), &(GPIOD->OTYPER), 12 | &(GPIOE->OTYPER), &(GPIOF->OTYPER), &(GPIOG->OTYPER), &(GPIOH->OTYPER), 13 | &(GPIOI->OTYPER), &(GPIOJ->OTYPER), &(GPIOK->OTYPER) 14 | }; 15 | 16 | volatile uint32_t * __gpiox_ospeedr[11] = { 17 | &(GPIOA->OSPEEDR), &(GPIOB->OSPEEDR), &(GPIOC->OSPEEDR), &(GPIOD->OSPEEDR), 18 | &(GPIOE->OSPEEDR), &(GPIOF->OSPEEDR), &(GPIOG->OSPEEDR), &(GPIOH->OSPEEDR), 19 | &(GPIOI->OSPEEDR), &(GPIOJ->OSPEEDR), &(GPIOK->OSPEEDR) 20 | }; 21 | 22 | volatile uint32_t * __gpiox_pupdr[11] = { 23 | &(GPIOA->PUPDR), &(GPIOB->PUPDR), &(GPIOC->PUPDR), &(GPIOD->PUPDR), 24 | &(GPIOE->PUPDR), &(GPIOF->PUPDR), &(GPIOG->PUPDR), &(GPIOH->PUPDR), 25 | &(GPIOI->PUPDR), &(GPIOJ->PUPDR), &(GPIOK->PUPDR) 26 | }; 27 | 28 | volatile uint32_t * __gpiox_idr[11] = { 29 | &(GPIOA->IDR), &(GPIOB->IDR), &(GPIOC->IDR), &(GPIOD->IDR), 30 | &(GPIOE->IDR), &(GPIOF->IDR), &(GPIOG->IDR), &(GPIOH->IDR), 31 | &(GPIOI->IDR), &(GPIOJ->IDR), &(GPIOK->IDR) 32 | }; 33 | 34 | volatile uint32_t * __gpiox_odr[11] = { 35 | &(GPIOA->ODR), &(GPIOB->ODR), &(GPIOC->ODR), &(GPIOD->ODR), 36 | &(GPIOE->ODR), &(GPIOF->ODR), &(GPIOG->ODR), &(GPIOH->ODR), 37 | &(GPIOI->ODR), &(GPIOJ->ODR), &(GPIOK->ODR) 38 | }; 39 | 40 | volatile uint32_t *__gpiox_afrl[11] = { 41 | &(GPIOA->AFR[0]), &(GPIOB->AFR[0]), &(GPIOC->AFR[0]), &(GPIOD->AFR[0]), 42 | &(GPIOE->AFR[0]), &(GPIOF->AFR[0]), &(GPIOG->AFR[0]), &(GPIOH->AFR[0]), 43 | &(GPIOI->AFR[0]), &(GPIOJ->AFR[0]), &(GPIOK->AFR[0]), 44 | }; 45 | 46 | volatile uint32_t *__gpiox_afrh[11] = { 47 | &(GPIOA->AFR[1]), &(GPIOB->AFR[1]), &(GPIOC->AFR[1]), &(GPIOD->AFR[1]), 48 | &(GPIOE->AFR[1]), &(GPIOF->AFR[1]), &(GPIOG->AFR[1]), &(GPIOH->AFR[1]), 49 | &(GPIOI->AFR[1]), &(GPIOJ->AFR[1]), &(GPIOK->AFR[1]), 50 | }; 51 | 52 | uint32_t __gpiox_rcc_en[11] = {RCC_AHB4ENR_GPIOAEN, RCC_AHB4ENR_GPIOBEN, RCC_AHB4ENR_GPIOCEN, RCC_AHB4ENR_GPIODEN, 53 | RCC_AHB4ENR_GPIOEEN, RCC_AHB4ENR_GPIOFEN, RCC_AHB4ENR_GPIOGEN, RCC_AHB4ENR_GPIOHEN, 54 | RCC_AHB4ENR_GPIOIEN, RCC_AHB4ENR_GPIOJEN, RCC_AHB4ENR_GPIOKEN,}; 55 | 56 | void __gpio_set_input(uint8_t port, uint8_t pin) { 57 | //gpio clock enable 58 | RCC->AHB4ENR |= __gpiox_rcc_en[port]; 59 | 60 | //input 61 | *__gpiox_moder[port] &= ~(1 << (2 * pin)); 62 | *__gpiox_moder[port] &= ~(1 << (2 * pin + 1)); 63 | 64 | //hi-z 65 | *__gpiox_pupdr[port] &= ~(1 << (2 * pin)); 66 | *__gpiox_pupdr[port] &= ~(1 << (2 * pin + 1)); 67 | } 68 | 69 | void __gpio_set_output(uint8_t port, uint8_t pin) { 70 | //gpio clock enable 71 | RCC->AHB4ENR |= __gpiox_rcc_en[port]; 72 | 73 | //general purpose output 74 | *__gpiox_moder[port] |= 1 << (2 * pin); 75 | *__gpiox_moder[port] &= ~(1 << (2 * pin + 1)); 76 | 77 | //push-pull 78 | *__gpiox_otyper[port] &= ~(1 << pin); 79 | 80 | //very high speed 81 | *__gpiox_ospeedr[port] |= 1 << (2 * pin); 82 | *__gpiox_ospeedr[port] |= 1 << (2 * pin + 1); 83 | } 84 | 85 | void __gpio_set_pupd(uint8_t port, uint8_t pin, uint8_t value) { 86 | //clear bits 2 * pin, 2 * pin + 1 87 | *__gpiox_pupdr[port] &= ~(1 << (2 * pin)); 88 | 89 | //set bits 2 * pin, 2 * pin + 1 to [value] 90 | *__gpiox_pupdr[port] |= (value << (2 * pin)); 91 | } 92 | 93 | void digitalIn(uint8_t port, uint8_t pin, uint8_t pullup) { 94 | __gpio_set_input(port, pin); 95 | __gpio_set_pupd(port, pin, pullup); 96 | } 97 | 98 | void digitalOut(uint8_t port, uint8_t pin) { 99 | __gpio_set_output(port, pin); 100 | } 101 | 102 | uint8_t digitalRead(uint8_t port, uint8_t pin) { 103 | return __gpio_get(port, pin); 104 | } 105 | 106 | void digitalWrite(uint8_t port, uint8_t pin, uint8_t value) { 107 | __gpio_set(port, pin, value); 108 | } 109 | -------------------------------------------------------------------------------- /wormboy/mem.h: -------------------------------------------------------------------------------- 1 | #ifndef GBC_MEM_H 2 | #define GBC_MEM_H 3 | 4 | #include "types.h" 5 | #include "stdio.h" 6 | #include "platform.h" 7 | 8 | // interrupt flags 9 | #define INTERRUPT_KEY 0x10; 10 | 11 | // interrupt vector locations 12 | #define VBLANK_INTERRUPT 0x0040 13 | #define LCDC_INTERRUPT 0x0048 14 | #define TIMER_INTERRUPT 0x0050 15 | #define SERIAL_INTERRUPT 0x0058 16 | #define HIGH_TO_LOW_P10_P13 0x0060 17 | 18 | // IO Regs (offset from 0xff00) 19 | #define IO_P1 0x0 // joystick (needs keyboard handler) 20 | #define IO_SERIAL_SB 0x01 // serial transfer data (don't support) 21 | #define IO_SERIAL_SC 0x02 // serial control (don't support) 22 | #define IO_DIV 0x04 // div 23 | #define IO_TIMA 0x05 // timer value (nyi) 24 | #define IO_TMA 0x06 // timer reload (nyi) 25 | #define IO_TAC 0x07 // TIMER CONTROL (enable, speed) (nyi) 26 | #define IO_IF 0x0f 27 | 28 | #define IO_NR10 0x10 29 | #define IO_NR11 0x11 30 | #define IO_NR12 0x12 31 | #define IO_NR13 0x13 32 | #define IO_NR14 0x14 33 | 34 | #define IO_NR21 0x16 35 | #define IO_NR22 0x17 36 | #define IO_NR23 0x18 37 | #define IO_NR24 0x19 38 | 39 | #define IO_NR30 0x1a 40 | #define IO_NR31 0x1b 41 | #define IO_NR32 0x1c 42 | #define IO_NR33 0x1d 43 | #define IO_NR34 0x1e 44 | 45 | #define IO_NR41 0x20 46 | #define IO_NR42 0x21 47 | #define IO_NR43 0x22 48 | #define IO_NR44 0x23 49 | 50 | #define IO_NR50 0x24 51 | #define IO_NR51 0x25 52 | #define IO_NR52 0x26 53 | 54 | #define IO_WAVE_PATTERN 0x30 // this is 16 bytes 55 | 56 | #define IO_LCDC 0x40 // yes, revisit 57 | #define IO_STAT 0x41 // no 58 | #define IO_SCROLLY 0x42 59 | #define IO_SCROLLX 0x43 60 | #define IO_LY 0x44 61 | #define IO_LYC 0x45 // no 62 | 63 | #define IO_DMA 0x46 // yes 64 | #define IO_BGP 0x47 // ?? 65 | #define IO_OBP0 0x48 // ?? 66 | #define IO_OBP1 0x49 // ?? 67 | #define IO_WINY 0x4a // no 68 | #define IO_WINX 0x4b // no 69 | 70 | #define IO_GBCSPEED 0x4d 71 | 72 | #define IO_EXIT_BIOS 0x50 73 | 74 | 75 | #define CART_INFO_ADDR 0x0100 76 | 77 | 78 | // cartridge types 79 | enum CartType { 80 | ROM_ONLY = 0, 81 | ROM_MBC1 = 1, 82 | ROM_MBC1_RAM = 2, 83 | ROM_MBC1_RAM_BATT = 3, 84 | ROM_MBC2 = 5, 85 | ROM_MBC2_BATT = 6, 86 | ROM_RAM = 8, 87 | ROM_RAM_BATT = 9, 88 | ROM_MM01 = 0xb, 89 | ROM_MM01_SRAM = 0xc, 90 | ROM_MMM01_SRAM_BATT = 0xd, 91 | ROM_MBC3_TIMER_BATT = 0xf, 92 | ROM_MBC3_TIMER_RAM_BATT = 0x10, // pkmn crystal 93 | ROM_MBC3 = 0x11, 94 | ROM_MBC3_RAM = 0x12, 95 | ROM_MBC3_RAM_BATT = 0x13, // pkmn red 96 | ROM_MBC5 = 0x19, 97 | ROM_MBC_RAM = 0x1a, 98 | ROM_MBC5_RAM_BATT = 0x1b, 99 | ROM_MBC5_RUMBLE = 0x1c, 100 | ROM_MBC5_RUMBLE_SRAM = 0x1d, 101 | ROM_MBC5_RUMBLE_SRAM_BATT = 0x1e, 102 | POCKET_CAMERA = 0x1f, 103 | BANDAI_TAMA5 = 0xfd, 104 | HUDSON_HUC3 = 0xfe, 105 | HUDSON_HUC1 = 0xff 106 | }; 107 | 108 | // in banks, of 16 KByte each 109 | enum RomSize { 110 | BANK_2 = 0, 111 | BANK_4 = 1, 112 | BANK_8 = 2, 113 | BANK_16 = 3, 114 | BANK_32 = 4, 115 | BANK_64 = 5, // pkmn red: 1 MB 116 | BANK_128 = 6, // pkmn crystal 117 | BANK_72 = 0x52, 118 | BANK_80 = 0x53, 119 | BANK_96 = 0x54 120 | }; 121 | 122 | enum RamSize { 123 | NONE = 0, 124 | SIZE_2KB = 1, // 1 bank 125 | SIZE_8KB = 2, // 1 bank 126 | SIZE_32KB = 3, // 4 banks, pkmn red, crystal 127 | SIZE_128KB = 4, // 16 banks 128 | }; 129 | 130 | struct CartInfo { 131 | u8 beginExec[4]; // 0x0 -> 0x3 132 | u8 nintendoGraphic[48]; // 0x3 -> 133 | char title[15]; 134 | u8 isColor; // compare against CARTRIDGE_IS_COLOR 135 | u8 license1; 136 | u8 license2; 137 | u8 SGB; 138 | CartType cartType; 139 | RomSize romSize; 140 | RamSize ramSize; 141 | u8 notJapan; 142 | u8 license3; 143 | u8 maskRomVersion; 144 | 145 | void print(); 146 | }; 147 | 148 | struct MemState { 149 | bool inBios; 150 | u8 mbcType; 151 | u8 romBank; 152 | u8 nRomBanks; 153 | u8 nRamBanks; 154 | u8* rom0; 155 | u8* mappedRom; 156 | u8* vram; 157 | u8* mappedRam; 158 | u8* disabledMappedRam; 159 | u8* internalRam; 160 | u8* mappedRamAllocation; 161 | u8* ioRegs; 162 | u8* upperRam; 163 | u8* spriteAttribute; 164 | }; 165 | 166 | // external interface 167 | extern MemState globalMemState; 168 | void initMem(FileLoadData file); // intialize memory for a cartridge 169 | u8 readByte(u16 addr); // read a byte from memory 170 | u16 readU16(u16 addr); // read 16-bits from memory 171 | void writeByte(u8 byte, u16 addr); // write a byte to memory 172 | void writeU16(u16 mem, u16 addr); // read 16-bits from memory 173 | void saveGame(); 174 | void loadGame(); 175 | 176 | 177 | #endif //GBC_MEM_H 178 | -------------------------------------------------------------------------------- /analog_in.cpp: -------------------------------------------------------------------------------- 1 | #include "analog_in.h" 2 | #include "digital_inout.h" 3 | #include "stm32h7xx.h" 4 | #include "stm32h7xx_hal.h" 5 | #include "stdio.h" 6 | 7 | volatile uint32_t * __adcx_cr[3] = {&(ADC1->CR), &(ADC2->CR), &(ADC3->CR)}; 8 | volatile uint32_t * __adcx_rcc[3] = {&(RCC->AHB1ENR), &(RCC->AHB1ENR), &(RCC->AHB4ENR)}; 9 | volatile uint32_t * __adcx_cfgr[3] = {&(ADC1->CFGR), &(ADC2->CFGR), &(ADC3->CFGR)}; 10 | volatile uint32_t * __adcx_ccr[3] = {&(ADC12_COMMON->CCR), &(ADC12_COMMON->CCR), &(ADC3_COMMON->CCR)}; 11 | volatile uint32_t * __adcx_isr[3] = {&(ADC1->ISR), &(ADC2->ISR), &(ADC3->ISR)}; 12 | volatile uint32_t * __adcx_sqr1[3] = {&(ADC1->SQR1), &(ADC2->SQR1), &(ADC3->SQR1)}; 13 | volatile uint32_t * __adcx_dr[3] = {&(ADC1->DR), &(ADC2->DR), &(ADC3->DR)}; 14 | volatile uint32_t * __adcx_pcsel[3] = {&(ADC1->PCSEL), &(ADC2->PCSEL), &(ADC3->PCSEL)}; 15 | 16 | volatile uint32_t __adc_rcc_adcxen[3] = {RCC_AHB1ENR_ADC12EN, RCC_AHB1ENR_ADC12EN, RCC_AHB4ENR_ADC3EN}; 17 | 18 | void __gpio_set_analog(uint8_t port, uint8_t pin) { 19 | //gpio clock enable 20 | RCC->AHB4ENR |= __gpiox_rcc_en[port]; 21 | 22 | //analog input 23 | *__gpiox_moder[port] |= 1 << (2 * pin); 24 | *__gpiox_moder[port] |= 1 << (2 * pin + 1); 25 | } 26 | 27 | void __init_adc(uint8_t adc, uint8_t bits) { 28 | //set ADC clock source to per_ck 29 | RCC->D3CCIPR &= ~RCC_D3CCIPR_ADCSEL; 30 | RCC->D3CCIPR |= (0x2U << 16U); 31 | 32 | //enable ADCx clock 33 | *__adcx_rcc[adc] |= __adc_rcc_adcxen[adc]; 34 | 35 | //ADCx clock prescaler = 4 36 | *__adcx_ccr[adc] &= ~ADC_CCR_PRESC; 37 | *__adcx_ccr[adc] |= 0x2U << 18U; 38 | 39 | //set data resolution 40 | *__adcx_cfgr[adc] &= ~ADC_CFGR_RES; 41 | uint8_t res_code = (16 - bits) >> 1; 42 | *__adcx_cfgr[adc] |= res_code << 2U; 43 | 44 | //exit deep power-down 45 | *__adcx_cr[adc] &= ~ADC_CR_DEEPPWD; 46 | 47 | //enable ADCx voltage regulator 48 | *__adcx_cr[adc] |= ADC_CR_ADVREGEN; 49 | 50 | //wait a while 51 | volatile uint32_t delay; 52 | for (delay = 0; delay < 4000; delay++) {} 53 | 54 | //enable BOOST MODE 55 | *__adcx_cr[adc] |= ADC_CR_BOOST; 56 | 57 | //launch ADC calibration 58 | *__adcx_cr[adc] &= ~ADC_CR_ADEN; 59 | *__adcx_cr[adc] |= ADC_CR_ADCALLIN; 60 | *__adcx_cr[adc] |= ADC_CR_ADCAL; 61 | 62 | while ((*__adcx_cr[adc] & ADC_CR_ADCAL) != 0) {} 63 | 64 | //enable adc 65 | *__adcx_isr[adc] |= ADC_ISR_ADRD; 66 | *__adcx_cr[adc] |= ADC_CR_ADEN; 67 | 68 | while ((*__adcx_isr[adc] & ADC_ISR_ADRD) == 0) {} 69 | } 70 | 71 | uint32_t __adc_single_conversion(uint8_t adc, uint8_t ch) { 72 | //sequence length = 1 73 | *__adcx_sqr1[adc] &= ~ADC_SQR1_L; 74 | *__adcx_sqr1[adc] |= 0x0; 75 | 76 | //1st conversion in regular sequence = ch 77 | *__adcx_pcsel[adc] |= (1 << ch); 78 | *__adcx_sqr1[adc] &= ~ADC_SQR1_SQ1; 79 | *__adcx_sqr1[adc] |= (ch << 6U); 80 | 81 | //start conversion 82 | *__adcx_cr[adc] |= ADC_CR_ADSTART; 83 | 84 | //wait for completion 85 | while ((*__adcx_isr[adc] & ADC_ISR_EOC) == 0) {} 86 | 87 | //un-preselect channel 88 | *__adcx_pcsel[adc] &= ~(1 << ch); 89 | 90 | //return result 91 | return *__adcx_dr[adc]; 92 | } 93 | 94 | void __adc_double_conversion(uint8_t adc1, uint8_t ch1, uint8_t adc2, uint8_t ch2, uint32_t * r1, uint32_t * r2) { 95 | //sequence length = 1 96 | *__adcx_sqr1[adc1] &= ~ADC_SQR1_L; 97 | *__adcx_sqr1[adc1] |= 0x0; 98 | *__adcx_sqr1[adc2] &= ~ADC_SQR1_L; 99 | *__adcx_sqr1[adc2] |= 0x0; 100 | 101 | //1st conversion in regular sequence = ch 102 | *__adcx_pcsel[adc1] |= (1 << ch1); 103 | *__adcx_sqr1[adc1] &= ~ADC_SQR1_SQ1; 104 | *__adcx_sqr1[adc1] |= (ch1 << 6U); 105 | *__adcx_pcsel[adc2] |= (1 << ch2); 106 | *__adcx_sqr1[adc2] &= ~ADC_SQR1_SQ1; 107 | *__adcx_sqr1[adc2] |= (ch2 << 6U); 108 | 109 | //start conversions 110 | *__adcx_cr[adc1] |= ADC_CR_ADSTART; 111 | *__adcx_cr[adc2] |= ADC_CR_ADSTART; 112 | 113 | //wait for completion 114 | while ((*__adcx_isr[adc1] & ADC_ISR_EOC) == 0) {} 115 | 116 | //un-preselect channels 117 | *__adcx_pcsel[adc1] &= ~(1 << ch1); 118 | *__adcx_pcsel[adc2] &= ~(1 << ch2); 119 | 120 | //return result 121 | *r1 = *__adcx_dr[adc1]; 122 | *r2 = *__adcx_dr[adc2]; 123 | } 124 | 125 | void __adc_triple_conversion(uint8_t ch1, uint8_t ch2, uint8_t ch3, uint32_t * r1, uint32_t * r2, uint32_t * r3) { 126 | //sequence length = 1 127 | *__adcx_sqr1[AD1] &= ~ADC_SQR1_L; 128 | *__adcx_sqr1[AD1] |= 0x0; 129 | *__adcx_sqr1[AD2] &= ~ADC_SQR1_L; 130 | *__adcx_sqr1[AD2] |= 0x0; 131 | *__adcx_sqr1[AD3] &= ~ADC_SQR1_L; 132 | *__adcx_sqr1[AD3] |= 0x0; 133 | 134 | //1st conversion in regular sequence = ch 135 | *__adcx_sqr1[AD1] &= ~ADC_SQR1_SQ1; 136 | *__adcx_sqr1[AD1] |= (ch1 << 6U); 137 | *__adcx_pcsel[AD1] |= (1 << ch1); 138 | *__adcx_sqr1[AD2] &= ~ADC_SQR1_SQ1; 139 | *__adcx_sqr1[AD2] |= (ch2 << 6U); 140 | *__adcx_pcsel[AD2] |= (1 << ch2); 141 | *__adcx_sqr1[AD3] &= ~ADC_SQR1_SQ1; 142 | *__adcx_sqr1[AD3] |= (ch2 << 6U); 143 | *__adcx_pcsel[AD3] |= (1 << ch3); 144 | 145 | //start conversions 146 | *__adcx_cr[AD1] |= ADC_CR_ADSTART; 147 | *__adcx_cr[AD2] |= ADC_CR_ADSTART; 148 | *__adcx_cr[AD3] |= ADC_CR_ADSTART; 149 | 150 | //wait for completion 151 | while ((*__adcx_isr[AD1] & ADC_ISR_EOC) == 0) {} 152 | 153 | //un-preselect channels 154 | *__adcx_pcsel[AD1] &= ~(1 << ch1); 155 | *__adcx_pcsel[AD2] &= ~(1 << ch2); 156 | *__adcx_pcsel[AD3] &= ~(1 << ch3); 157 | 158 | //return result 159 | *r1 = *__adcx_dr[AD1]; 160 | *r2 = *__adcx_dr[AD2]; 161 | *r3 = *__adcx_dr[AD3]; 162 | } 163 | 164 | void analogIn(uint8_t port, uint8_t pin, uint8_t adc) { 165 | __gpio_set_analog(port, pin); 166 | __init_adc(adc, 12); 167 | } 168 | 169 | uint32_t analogRead(uint8_t adc, uint8_t ch) { 170 | return __adc_single_conversion(adc, ch); 171 | } 172 | -------------------------------------------------------------------------------- /gp_timer.cpp: -------------------------------------------------------------------------------- 1 | #include "gp_timer.h" 2 | #include "digital_inout.h" 3 | #include "alternate_functions.h" 4 | #include "stm32h7xx.h" 5 | 6 | volatile uint16_t * __gp_timx_cr1[7] = {&(TIM2->CR1), &(TIM3->CR1), &(TIM4->CR1), &(TIM5->CR1), 7 | &(TIM15->CR1), &(TIM16->CR1), &(TIM17->CR1)}; 8 | 9 | volatile uint32_t * __gp_timx_dier[7] = {&(TIM2->DIER), &(TIM3->DIER), &(TIM4->DIER), &(TIM5->DIER), 10 | &(TIM15->DIER), &(TIM16->DIER), &(TIM17->DIER)}; 11 | 12 | volatile uint32_t * __gp_timx_egr[7] = {&(TIM2->EGR), &(TIM3->EGR), &(TIM4->EGR), &(TIM5->EGR), 13 | &(TIM15->EGR), &(TIM16->EGR), &(TIM17->EGR)}; 14 | 15 | volatile uint16_t * __gp_timx_psc[7] = {&(TIM2->PSC), &(TIM3->PSC), &(TIM4->PSC), &(TIM5->PSC), 16 | &(TIM15->PSC), &(TIM16->PSC), &(TIM17->PSC)}; 17 | 18 | volatile uint32_t * __gp_timx_arr[7] = {&(TIM2->ARR), &(TIM3->ARR), &(TIM4->ARR), &(TIM5->ARR), 19 | &(TIM15->ARR), &(TIM16->ARR), &(TIM17->ARR)}; 20 | 21 | volatile uint32_t * __gp_timx_ccmr1[7] = {&(TIM2->CCMR1), &(TIM3->CCMR1), &(TIM4->CCMR1), &(TIM5->CCMR1), 22 | &(TIM15->CCMR1), &(TIM16->CCMR1), &(TIM17->CCMR1)}; 23 | 24 | volatile uint32_t * __gp_timx_ccmr2[7] = {&(TIM2->CCMR2), &(TIM3->CCMR2), &(TIM4->CCMR2), &(TIM5->CCMR2), 25 | NULL, NULL, NULL}; 26 | 27 | volatile uint32_t * __gp_timx_ccer[7] = {&(TIM2->CCER), &(TIM3->CCER), &(TIM4->CCER), &(TIM5->CCER), 28 | &(TIM15->CCER), &(TIM16->CCER), &(TIM17->CCER)}; 29 | 30 | volatile uint32_t * __gp_timx_ccr1[7] = {&(TIM2->CCR1), &(TIM3->CCR1), &(TIM4->CCR1), &(TIM5->CCR1), 31 | &(TIM15->CCR1), &(TIM16->CCR1), &(TIM17->CCR1)}; 32 | 33 | volatile uint32_t * __gp_timx_ccr2[7] = {&(TIM2->CCR2), &(TIM3->CCR2), &(TIM4->CCR2), &(TIM5->CCR2), 34 | &(TIM15->CCR2), NULL, NULL}; 35 | 36 | volatile uint32_t * __gp_timx_ccr3[7] = {&(TIM2->CCR3), &(TIM3->CCR3), &(TIM4->CCR3), &(TIM5->CCR3), 37 | NULL, NULL, NULL}; 38 | 39 | volatile uint32_t * __gp_timx_ccr4[7] = {&(TIM2->CCR4), &(TIM3->CCR4), &(TIM4->CCR4), &(TIM5->CCR4), 40 | NULL, NULL, NULL}; 41 | 42 | volatile uint32_t ** __gp_timx_ccr[4] = {__gp_timx_ccr1, __gp_timx_ccr2, __gp_timx_ccr3, __gp_timx_ccr4}; 43 | 44 | IRQn_Type __gp_timx_irqn[7] = {TIM2_IRQn, TIM3_IRQn, TIM4_IRQn, TIM5_IRQn, 45 | TIM15_IRQn, TIM16_IRQn, TIM17_IRQn}; 46 | 47 | uint32_t __gp_timx_rcc_en[7] = {RCC_APB1LENR_TIM2EN, RCC_APB1LENR_TIM3EN, RCC_APB1LENR_TIM4EN, RCC_APB1LENR_TIM5EN, 48 | RCC_APB2ENR_TIM15EN, RCC_APB2ENR_TIM16EN, RCC_APB2ENR_TIM17EN}; 49 | 50 | uint32_t __gp_timx_ocxm[4] = {TIM_CCMR1_OC1M, TIM_CCMR1_OC2M, TIM_CCMR2_OC3M, TIM_CCMR2_OC4M}; 51 | uint32_t __gp_timx_ocxce[4] = {TIM_CCMR1_OC1CE, TIM_CCMR1_OC2CE, TIM_CCMR2_OC3CE, TIM_CCMR2_OC4CE}; 52 | uint32_t __gp_timx_ocxpe[4] = {TIM_CCMR1_OC1PE, TIM_CCMR1_OC2PE, TIM_CCMR2_OC3PE, TIM_CCMR2_OC4PE}; 53 | uint32_t __gp_timx_ccxp[4] = {TIM_CCER_CC1P, TIM_CCER_CC2P, TIM_CCER_CC3P, TIM_CCER_CC4P}; 54 | uint32_t __gp_timx_ccxe[4] = {TIM_CCER_CC1E, TIM_CCER_CC2E, TIM_CCER_CC3E, TIM_CCER_CC4E}; 55 | 56 | uint32_t __gp_timx_ocxm_mode1[4] = {0x06U << 4U, 0x06U << 12U, 0x06U << 4U, 0x06U << 12U}; 57 | 58 | void (*__gp_timx_irq[7])(void); 59 | 60 | void __init_gp_timer(uint8_t tim, uint32_t tics) { 61 | *__gp_timx_cr1[tim] &= ~TIM_CR1_CEN; 62 | __gp_timx_irq[tim] = NULL; 63 | 64 | if (tim < TIMER15) RCC->APB1LENR |= __gp_timx_rcc_en[tim]; 65 | else RCC->APB2ENR |= __gp_timx_rcc_en[tim]; 66 | 67 | //buffered auto-reload 68 | *__gp_timx_cr1[tim] |= TIM_CR1_ARPE; 69 | 70 | //prescaler = 0 71 | *__gp_timx_psc[tim] = 0x00; 72 | 73 | //set auto-reload register 74 | *__gp_timx_arr[tim] = tics; 75 | 76 | //update registers once 77 | *__gp_timx_egr[tim] |= TIM_EGR_UG; 78 | 79 | //enable timer 80 | *__gp_timx_cr1[tim] |= TIM_CR1_CEN; 81 | } 82 | 83 | void __attach_fn(uint8_t tim, void (*fn)(void)) { 84 | //disable timer 85 | *__gp_timx_cr1[tim] &= ~TIM_CR1_CEN; 86 | 87 | //attach function 88 | __gp_timx_irq[tim] = fn; 89 | 90 | //update interrupt enable 91 | NVIC_EnableIRQ(__gp_timx_irqn[tim]); 92 | *__gp_timx_dier[tim] |= TIM_DIER_UIE; 93 | 94 | //enable timer 95 | *__gp_timx_cr1[tim] |= TIM_CR1_CEN; 96 | } 97 | 98 | void __enable_pwm(uint8_t tim, uint8_t ch) { 99 | if (ch < 1) return; //1-indexed! 100 | if (tim > TIMER5 && ch > 2) return; //15, 16, 17 1 or 2 channels only 101 | if (tim > TIMER15 && ch > 1) return; //16, 17 1 chanel only 102 | 103 | //disable timer 104 | *__gp_timx_cr1[tim] &= ~TIM_CR1_CEN; 105 | 106 | //figure out which CCMR register controls this channel 107 | volatile uint32_t * ccmr; 108 | 109 | if (ch > 2) ccmr = __gp_timx_ccmr2[tim]; 110 | else ccmr = __gp_timx_ccmr1[tim]; 111 | 112 | //OCxM = 0110 (PWM mode 1) 113 | *ccmr &= ~__gp_timx_ocxm[ch - 1]; 114 | *ccmr |= __gp_timx_ocxm_mode1[ch - 1]; 115 | 116 | //capture compare preload enable 117 | *ccmr |= __gp_timx_ocxpe[ch - 1]; 118 | 119 | //duty cycle = 0; 120 | *__gp_timx_ccr[ch - 1][tim] = 0; 121 | 122 | //output active high 123 | *__gp_timx_ccer[tim] &= ~__gp_timx_ccxp[ch - 1]; 124 | 125 | //output enable 126 | *__gp_timx_ccer[tim] |= __gp_timx_ccxe[ch - 1]; 127 | 128 | //update registers once 129 | *__gp_timx_egr[tim] |= TIM_EGR_UG; 130 | 131 | //enable timer 132 | *__gp_timx_cr1[tim] |= TIM_CR1_CEN; 133 | } 134 | 135 | void __set_on_time(uint8_t tim, uint8_t ch, uint32_t tics) { 136 | *__gp_timx_ccr[ch - 1][tim] = tics; 137 | } 138 | 139 | void tickerStart(uint8_t tim, uint32_t tics) { 140 | __init_gp_timer(tim, tics); 141 | } 142 | 143 | void tickerStart_usecs(uint8_t tim, uint32_t usecs) { 144 | uint64_t tics; 145 | tics = SystemCoreClock / 2 / 1000000 * usecs; 146 | __init_gp_timer(tim, (uint32_t)tics); 147 | } 148 | 149 | void tickerStart_secs(uint8_t tim, double secs) { 150 | double tics; 151 | tics = SystemCoreClock / 2 * secs; 152 | __init_gp_timer(tim, (uint32_t)tics); 153 | } 154 | 155 | void tickerAttach(uint8_t tim, void (*fn)(void)) { 156 | __attach_fn(tim, fn); 157 | } 158 | 159 | void pwmOut(uint8_t tim, uint8_t ch, uint8_t port, uint8_t pin, uint8_t af) { 160 | __init_gp_timer(tim, SystemCoreClock / 2 / 10000); 161 | __map_af(port, pin, af); 162 | __enable_pwm(tim, ch); 163 | } 164 | 165 | void pwmOutFreq(uint8_t tim, uint8_t ch, uint32_t freq) { 166 | uint32_t tics = SystemCoreClock / 2 / freq; 167 | *__gp_timx_arr[tim] = tics; 168 | } 169 | 170 | void pwmOutDtc(uint8_t tim, uint8_t ch, float dtc) { 171 | float fperiod = (float)(*__gp_timx_arr[tim]); 172 | uint32_t tics = (uint32_t)(dtc * fperiod); 173 | __set_on_time(tim, ch, tics); 174 | } 175 | -------------------------------------------------------------------------------- /wormboy/vincent_data.h: -------------------------------------------------------------------------------- 1 | // font 2 | char vincent_data[128][8] = { 3 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 4 | { 0x00, 0x3E, 0x41, 0x55, 0x41, 0x55, 0x49, 0x3E }, 5 | { 0x00, 0x3E, 0x7F, 0x6B, 0x7F, 0x6B, 0x77, 0x3E }, 6 | { 0x00, 0x22, 0x77, 0x7F, 0x7F, 0x3E, 0x1C, 0x08 }, 7 | { 0x00, 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08 }, 8 | { 0x00, 0x08, 0x1C, 0x2A, 0x7F, 0x2A, 0x08, 0x1C }, 9 | { 0x00, 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x08, 0x1C }, 10 | { 0x00, 0x00, 0x1C, 0x3E, 0x3E, 0x3E, 0x1C, 0x00 }, 11 | { 0xFF, 0xFF, 0xE3, 0xC1, 0xC1, 0xC1, 0xE3, 0xFF }, 12 | { 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00 }, 13 | { 0xFF, 0xFF, 0xE3, 0xDD, 0xDD, 0xDD, 0xE3, 0xFF }, 14 | { 0x00, 0x0F, 0x03, 0x05, 0x39, 0x48, 0x48, 0x30 }, 15 | { 0x00, 0x08, 0x3E, 0x08, 0x1C, 0x22, 0x22, 0x1C }, 16 | { 0x00, 0x18, 0x14, 0x10, 0x10, 0x30, 0x70, 0x60 }, 17 | { 0x00, 0x0F, 0x19, 0x11, 0x13, 0x37, 0x76, 0x60 }, 18 | { 0x00, 0x08, 0x2A, 0x1C, 0x77, 0x1C, 0x2A, 0x08 }, 19 | { 0x00, 0x60, 0x78, 0x7E, 0x7F, 0x7E, 0x78, 0x60 }, 20 | { 0x00, 0x03, 0x0F, 0x3F, 0x7F, 0x3F, 0x0F, 0x03 }, 21 | { 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x2A, 0x1C, 0x08 }, 22 | { 0x00, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66 }, 23 | { 0x00, 0x3F, 0x65, 0x65, 0x3D, 0x05, 0x05, 0x05 }, 24 | { 0x00, 0x0C, 0x32, 0x48, 0x24, 0x12, 0x4C, 0x30 }, 25 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F }, 26 | { 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x2A, 0x1C, 0x3E }, 27 | { 0x00, 0x08, 0x1C, 0x3E, 0x7F, 0x1C, 0x1C, 0x1C }, 28 | { 0x00, 0x1C, 0x1C, 0x1C, 0x7F, 0x3E, 0x1C, 0x08 }, 29 | { 0x00, 0x08, 0x0C, 0x7E, 0x7F, 0x7E, 0x0C, 0x08 }, 30 | { 0x00, 0x08, 0x18, 0x3F, 0x7F, 0x3F, 0x18, 0x08 }, 31 | { 0x00, 0x00, 0x00, 0x70, 0x70, 0x70, 0x7F, 0x7F }, 32 | { 0x00, 0x00, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x00 }, 33 | { 0x00, 0x08, 0x1C, 0x1C, 0x3E, 0x3E, 0x7F, 0x7F }, 34 | { 0x00, 0x7F, 0x7F, 0x3E, 0x3E, 0x1C, 0x1C, 0x08 }, 35 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 36 | { 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18 }, 37 | { 0x00, 0x36, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00 }, 38 | { 0x00, 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36 }, 39 | { 0x00, 0x08, 0x1E, 0x20, 0x1C, 0x02, 0x3C, 0x08 }, 40 | { 0x00, 0x60, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x06 }, 41 | { 0x00, 0x3C, 0x66, 0x3C, 0x28, 0x65, 0x66, 0x3F }, 42 | { 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00 }, 43 | { 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60 }, 44 | { 0x00, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06 }, 45 | { 0x00, 0x00, 0x36, 0x1C, 0x7F, 0x1C, 0x36, 0x00 }, 46 | { 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00 }, 47 | { 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60 }, 48 | { 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00 }, 49 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60 }, 50 | { 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00 }, 51 | { 0x00, 0x3C, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x3C }, 52 | { 0x00, 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E }, 53 | { 0x00, 0x3C, 0x66, 0x06, 0x0C, 0x30, 0x60, 0x7E }, 54 | { 0x00, 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C }, 55 | { 0x00, 0x0C, 0x1C, 0x2C, 0x4C, 0x7E, 0x0C, 0x0C }, 56 | { 0x00, 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C }, 57 | { 0x00, 0x3C, 0x66, 0x60, 0x7C, 0x66, 0x66, 0x3C }, 58 | { 0x00, 0x7E, 0x66, 0x0C, 0x0C, 0x18, 0x18, 0x18 }, 59 | { 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C }, 60 | { 0x00, 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C }, 61 | { 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00 }, 62 | { 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30 }, 63 | { 0x00, 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06 }, 64 | { 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x00 }, 65 | { 0x00, 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60 }, 66 | { 0x00, 0x3C, 0x66, 0x06, 0x1C, 0x18, 0x00, 0x18 }, 67 | { 0x00, 0x38, 0x44, 0x5C, 0x58, 0x42, 0x3C, 0x00 }, 68 | { 0x00, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66 }, 69 | { 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C }, 70 | { 0x00, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C }, 71 | { 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C }, 72 | { 0x00, 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E }, 73 | { 0x00, 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60 }, 74 | { 0x00, 0x3C, 0x66, 0x60, 0x60, 0x6E, 0x66, 0x3C }, 75 | { 0x00, 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66 }, 76 | { 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C }, 77 | { 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x6C, 0x6C, 0x38 }, 78 | { 0x00, 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66 }, 79 | { 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E }, 80 | { 0x00, 0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63 }, 81 | { 0x00, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x63, 0x63 }, 82 | { 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C }, 83 | { 0x00, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60 }, 84 | { 0x00, 0x3C, 0x66, 0x66, 0x66, 0x6E, 0x3C, 0x06 }, 85 | { 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x78, 0x6C, 0x66 }, 86 | { 0x00, 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C }, 87 | { 0x00, 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x18 }, 88 | { 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3E }, 89 | { 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18 }, 90 | { 0x00, 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63 }, 91 | { 0x00, 0x63, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x63 }, 92 | { 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18 }, 93 | { 0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E }, 94 | { 0x00, 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E }, 95 | { 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00 }, 96 | { 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78 }, 97 | { 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00, 0x00 }, 98 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F }, 99 | { 0x00, 0x0C, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00 }, 100 | { 0x00, 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E }, 101 | { 0x00, 0x60, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C }, 102 | { 0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C }, 103 | { 0x00, 0x06, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E }, 104 | { 0x00, 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C }, 105 | { 0x00, 0x1C, 0x36, 0x30, 0x30, 0x7C, 0x30, 0x30 }, 106 | { 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C }, 107 | { 0x00, 0x60, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66 }, 108 | { 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3C }, 109 | { 0x00, 0x0C, 0x00, 0x0C, 0x0C, 0x6C, 0x6C, 0x38 }, 110 | { 0x00, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66 }, 111 | { 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }, 112 | { 0x00, 0x00, 0x00, 0x63, 0x77, 0x7F, 0x6B, 0x6B }, 113 | { 0x00, 0x00, 0x00, 0x7C, 0x7E, 0x66, 0x66, 0x66 }, 114 | { 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C }, 115 | { 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60 }, 116 | { 0x00, 0x00, 0x3C, 0x6C, 0x6C, 0x3C, 0x0D, 0x0F }, 117 | { 0x00, 0x00, 0x00, 0x7C, 0x66, 0x66, 0x60, 0x60 }, 118 | { 0x00, 0x00, 0x00, 0x3E, 0x40, 0x3C, 0x02, 0x7C }, 119 | { 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x18 }, 120 | { 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E }, 121 | { 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3C, 0x18 }, 122 | { 0x00, 0x00, 0x00, 0x63, 0x6B, 0x6B, 0x6B, 0x3E }, 123 | { 0x00, 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66 }, 124 | { 0x00, 0x00, 0x00, 0x66, 0x66, 0x3E, 0x06, 0x3C }, 125 | { 0x00, 0x00, 0x00, 0x3C, 0x0C, 0x18, 0x30, 0x3C }, 126 | { 0x00, 0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E }, 127 | { 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18 }, 128 | { 0x00, 0x70, 0x18, 0x18, 0x0C, 0x18, 0x18, 0x70 }, 129 | { 0x00, 0x00, 0x00, 0x3A, 0x6C, 0x00, 0x00, 0x00 }, 130 | { 0x00, 0x08, 0x1C, 0x36, 0x63, 0x41, 0x41, 0x7F } 131 | }; 132 | -------------------------------------------------------------------------------- /wormboy/ntsc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ntsc.cpp 3 | * 4 | * Created on: Jan 13, 2019 5 | * Author: jared 6 | */ 7 | 8 | #include "ntsc.h" 9 | #include "types.h" 10 | #include "vincent_data.h" 11 | #include "mbad.h" 12 | #include "sound.h" 13 | #include "stm32h7xx_nucleo_144.h" 14 | #include 15 | #include 16 | 17 | #define TEXT_LEVEL 5 18 | 19 | #define TX 24 // number of characters in X 20 | #define TY 10 // number of characters in Y 21 | 22 | #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) 23 | #define MIN(a,b) (((a)<(b))?(a):(b)) 24 | #define MAX(a,b) (((a)>(b))?(a):(b)) 25 | 26 | #define DAC_SYNC 2 27 | 28 | uint8_t char_col = 0; // current column counter 29 | uint8_t char_row = 0; // current row counter 30 | 31 | uint8_t* im_line_va_1; 32 | uint8_t bl_line_s[H_RES]; //blank line sync buffer 33 | uint8_t bl_line_v[H_RES]; //blank line video buffer 34 | uint8_t vb_line_s[H_RES]; //vertical sync, sync buffer 35 | uint8_t vb_line_v[H_RES]; //vertical sync, video buffer 36 | uint8_t im_line_s[H_RES]; //image sync buffer 37 | 38 | //buffers 39 | uint8_t current_frame[YL][XL / 2]; 40 | 41 | volatile u8 bufferSelect = 0; 42 | uint8_t im_line_va_2[H_RES*V_RES]; 43 | 44 | //double buffered drawing 45 | u8* im_line_vas[2]; 46 | 47 | uint16_t l=0; //current line of scan 48 | uint32_t tics = 0; //timer 49 | 50 | // positive or negative? 51 | int16_t sign(int16_t a) 52 | { 53 | if(a > 0) return 1; 54 | if(a < 0) return -1; 55 | return 0; 56 | } 57 | 58 | // clear the screen and the text buffer 59 | void clr() 60 | { 61 | for(int i = 0; i < H_RES; i++) 62 | for(int j = 0; j < V_RES; j++) 63 | im_line_vas[bufferSelect][i+j*H_RES] = 0; 64 | } 65 | 66 | // initialize video buffers 67 | void init_buffers() 68 | { 69 | clr(); // zero buffers 70 | for(int i = 0; i < H_RES; i++) 71 | { 72 | im_line_s[i] = DAC_SYNC; 73 | bl_line_s[i] = DAC_SYNC; 74 | bl_line_v[i] = 0; 75 | vb_line_s[i] = 0; 76 | vb_line_v[i] = 0; 77 | } 78 | im_line_s[0] = 0; 79 | im_line_s[1] = 0; 80 | im_line_s[2] = 0; 81 | im_line_s[3] = 0; 82 | 83 | for(int i = 0; i < 15; i++) im_line_s[i] = 0; 84 | 85 | bl_line_s[0] = 0; 86 | vb_line_s[0] = DAC_SYNC; 87 | bl_line_s[1] = 0; 88 | vb_line_s[1] = DAC_SYNC; 89 | 90 | bl_line_s[3] = 0; 91 | vb_line_s[3] = DAC_SYNC; 92 | bl_line_s[2] = 0; 93 | vb_line_s[2] = DAC_SYNC; 94 | } 95 | 96 | volatile uint8_t drawing = 0; 97 | // video interrupt 98 | 99 | //extern u32 frameCount; 100 | u32 frameCountNTSC = 0; 101 | void vid() { 102 | uint8_t nop = 0, img = 0; //use nops or use wait_us 103 | uint8_t* sptr; //pointer to sync buffer for line 104 | uint8_t* vptr; //pointer to video buffer for line 105 | uint8_t* pptr; //porch 106 | 107 | // drawing = (l > 30 && l < (30 + YL)); 108 | 109 | //if(l > 180) { 110 | // drawing = 1; 111 | // } 112 | if (l < V_PORCH_SIZE) { 113 | 114 | vptr = bl_line_v; 115 | sptr = im_line_s; 116 | nop = 1; 117 | } 118 | else if ((l < YL + V_PORCH_SIZE)) { 119 | //drawing = l - V_PORCH_SIZE + 1; 120 | //drawing = 1; 121 | vptr = im_line_vas[bufferSelect] + (l - 30) * H_RES; 122 | sptr = im_line_s; 123 | nop = 1; 124 | img = 1; 125 | } 126 | else if (l < 255) { 127 | //drawing = 0; 128 | vptr = bl_line_v; 129 | sptr = bl_line_s; 130 | nop = 0; 131 | } 132 | else { 133 | frameCountNTSC++; 134 | vptr = vb_line_v; 135 | sptr = vb_line_s; 136 | nop = 1; 137 | } 138 | 139 | 140 | pptr = img ? bl_line_v : vptr; 141 | 142 | if (nop) { 143 | for (uint16_t i = 0; i < H_PORCH_SIZE; i++) { 144 | __gpio_set_port(PD,(pptr[i] + sptr[i]) << 4); 145 | 146 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 147 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 148 | 149 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 150 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 151 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 152 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 153 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 154 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 155 | asm("nop");asm("nop");asm("nop");asm("nop"); 156 | } 157 | 158 | for (uint16_t i = 0; i < H_RES; i++) //loop over each column 159 | { 160 | __gpio_set_port(PD,(vptr[i] + sptr[i]) << 4); 161 | 162 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 163 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 164 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 165 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 166 | asm("nop");asm("nop");asm("nop");asm("nop"); 167 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 168 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 169 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 170 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 171 | } 172 | } else { 173 | for(uint16_t i = 0; i < 12; i++) //loop over each column 174 | { 175 | __gpio_set_port(PD,sptr[i] << 4); 176 | 177 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 178 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 179 | asm("nop");asm("nop");asm("nop");asm("nop"); 180 | 181 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 182 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 183 | asm("nop");asm("nop");asm("nop");asm("nop"); 184 | 185 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 186 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 187 | asm("nop");asm("nop");asm("nop");asm("nop"); 188 | 189 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 190 | asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); 191 | asm("nop");asm("nop");asm("nop");asm("nop"); 192 | } 193 | } 194 | 195 | //move to next line 196 | l++; 197 | tics++; 198 | if(l > 255) {l = 0;} 199 | 200 | snd(); 201 | } 202 | 203 | // draw letter 204 | void draw_vincent(uint16_t x0, uint16_t y0, uint8_t c) 205 | { 206 | if(c == 40) c = 41; 207 | else if(c == 41) c = 40; 208 | char* letter = vincent_data[c]; 209 | for(uint16_t xp = 0; xp < 8; xp++) 210 | { 211 | for(uint16_t yp = 0; yp < 8; yp++) 212 | { 213 | im_line_vas[bufferSelect][H_RES*(yp+y0) + xp + x0] = CHECK_BIT(letter[yp],8-xp)?TEXT_LEVEL:0; 214 | } 215 | } 216 | } 217 | 218 | // draw string 219 | void draw_vincent_string(char* str) 220 | { 221 | for(int i = 0; i < strlen(str); i++) 222 | { 223 | if(str[i] == 0) return; 224 | char_col++; 225 | if(char_col >= TX) 226 | { 227 | char_col = 0; 228 | char_row++; 229 | } 230 | if(char_row >= TY) 231 | { 232 | char_row = 0; 233 | } 234 | 235 | draw_vincent(X0s +2 + 8*char_col, Y0s + 14 + 8*char_row, 236 | str[i]); 237 | } 238 | } 239 | 240 | void set_status_string(char* str) 241 | { 242 | uint8_t char_col_backup = char_col; 243 | uint8_t char_row_backup = char_row; 244 | char_col = 0; 245 | char_row = TY - 1; 246 | 247 | draw_vincent_string(str); 248 | 249 | char_col = char_col_backup; 250 | char_row = char_row_backup; 251 | } 252 | 253 | void init_ntsc() { 254 | im_line_vas[0] = im_line_va_2; 255 | im_line_vas[1] = im_line_va_1; 256 | printf("[NTSC] Init...\r\n"); 257 | init_buffers(); 258 | 259 | for(int x = 0; x < XL; x++) { 260 | for(int y = 0; y < YL; y++) { 261 | im_line_vas[bufferSelect][H_RES*(y+Y0s) + x + X0s] = 6 * (((x+y)>>1)&1); 262 | } 263 | } 264 | 265 | printf("[NTSC] Init done!\r\n"); 266 | } 267 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /system_stm32h7xx.c: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file system_stm32h7xx.c 4 | * @author MCD Application Team 5 | * @version V1.2.0 6 | * @date 29-December-2017 7 | * @brief CMSIS Cortex-M Device Peripheral Access Layer System Source File. 8 | * 9 | * This file provides two functions and one global variable to be called from 10 | * user application: 11 | * - SystemInit(): This function is called at startup just after reset and 12 | * before branch to main program. This call is made inside 13 | * the "startup_stm32h7xx.s" file. 14 | * 15 | * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used 16 | * by the user application to setup the SysTick 17 | * timer or configure other parameters. 18 | * 19 | * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must 20 | * be called whenever the core clock is changed 21 | * during program execution. 22 | * 23 | * 24 | ****************************************************************************** 25 | * @attention 26 | * 27 | *

© COPYRIGHT(c) 2017 STMicroelectronics

28 | * 29 | * Redistribution and use in source and binary forms, with or without modification, 30 | * are permitted provided that the following conditions are met: 31 | * 1. Redistributions of source code must retain the above copyright notice, 32 | * this list of conditions and the following disclaimer. 33 | * 2. Redistributions in binary form must reproduce the above copyright notice, 34 | * this list of conditions and the following disclaimer in the documentation 35 | * and/or other materials provided with the distribution. 36 | * 3. Neither the name of STMicroelectronics nor the names of its contributors 37 | * may be used to endorse or promote products derived from this software 38 | * without specific prior written permission. 39 | * 40 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 41 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 42 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 43 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 44 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 45 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 46 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 47 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 48 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 49 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50 | * 51 | ****************************************************************************** 52 | */ 53 | 54 | /** @addtogroup CMSIS 55 | * @{ 56 | */ 57 | 58 | /** @addtogroup stm32h7xx_system 59 | * @{ 60 | */ 61 | 62 | /** @addtogroup STM32H7xx_System_Private_Includes 63 | * @{ 64 | */ 65 | 66 | #include "stm32h7xx_hal.h" 67 | 68 | /** 69 | * @} 70 | */ 71 | 72 | /** @addtogroup STM32H7xx_System_Private_TypesDefinitions 73 | * @{ 74 | */ 75 | 76 | /** 77 | * @} 78 | */ 79 | 80 | /** @addtogroup STM32H7xx_System_Private_Defines 81 | * @{ 82 | */ 83 | 84 | /************************* Miscellaneous Configuration ************************/ 85 | /*!< Uncomment the following line if you need to relocate your vector Table in 86 | Internal SRAM. */ 87 | /* #define VECT_TAB_SRAM */ 88 | #define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field. 89 | This value must be a multiple of 0x200. */ 90 | /******************************************************************************/ 91 | 92 | /** 93 | * @} 94 | */ 95 | 96 | /** @addtogroup STM32H7xx_System_Private_Macros 97 | * @{ 98 | */ 99 | 100 | /** 101 | * @} 102 | */ 103 | 104 | /** @addtogroup STM32H7xx_System_Private_Variables 105 | * @{ 106 | */ 107 | /* This variable is updated in three ways: 108 | 1) by calling CMSIS function SystemCoreClockUpdate() 109 | 2) by calling HAL API function HAL_RCC_GetHCLKFreq() 110 | 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency 111 | Note: If you use this function to configure the system clock; then there 112 | is no need to call the 2 first functions listed above, since SystemCoreClock 113 | variable is updated automatically. 114 | */ 115 | uint32_t SystemCoreClock = 64000000; 116 | uint32_t SystemD2Clock = 64000000; 117 | const uint8_t D1CorePrescTable[16] = {0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9}; 118 | 119 | /** 120 | * @} 121 | */ 122 | 123 | /** @addtogroup STM32H7xx_System_Private_Functions 124 | * @{ 125 | */ 126 | 127 | /** 128 | * @brief Setup the microcontroller system 129 | * Initialize the FPU setting, vector table location. 130 | * @param None 131 | * @retval None 132 | */ 133 | void SystemInit (void) 134 | { 135 | /* FPU settings ------------------------------------------------------------*/ 136 | #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) 137 | SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ 138 | #endif 139 | /* Reset the RCC clock configuration to the default reset state ------------*/ 140 | /* Set HSION bit */ 141 | RCC->CR |= RCC_CR_HSION; 142 | 143 | /* Reset CFGR register */ 144 | RCC->CFGR = 0x00000000; 145 | 146 | /* Reset HSEON, CSSON , CSION,RC48ON, CSIKERON PLL1ON, PLL2ON and PLL3ON bits */ 147 | RCC->CR &= (uint32_t)0xEAF6ED7F; 148 | 149 | /* Reset D1CFGR register */ 150 | RCC->D1CFGR = 0x00000000; 151 | 152 | /* Reset D2CFGR register */ 153 | RCC->D2CFGR = 0x00000000; 154 | 155 | /* Reset D3CFGR register */ 156 | RCC->D3CFGR = 0x00000000; 157 | 158 | /* Reset PLLCKSELR register */ 159 | RCC->PLLCKSELR = 0x00000000; 160 | 161 | /* Reset PLLCFGR register */ 162 | RCC->PLLCFGR = 0x00000000; 163 | /* Reset PLL1DIVR register */ 164 | RCC->PLL1DIVR = 0x00000000; 165 | /* Reset PLL1FRACR register */ 166 | RCC->PLL1FRACR = 0x00000000; 167 | 168 | /* Reset PLL2DIVR register */ 169 | RCC->PLL2DIVR = 0x00000000; 170 | 171 | /* Reset PLL2FRACR register */ 172 | 173 | RCC->PLL2FRACR = 0x00000000; 174 | /* Reset PLL3DIVR register */ 175 | RCC->PLL3DIVR = 0x00000000; 176 | 177 | /* Reset PLL3FRACR register */ 178 | RCC->PLL3FRACR = 0x00000000; 179 | 180 | /* Reset HSEBYP bit */ 181 | RCC->CR &= (uint32_t)0xFFFBFFFF; 182 | 183 | /* Disable all interrupts */ 184 | RCC->CIER = 0x00000000; 185 | 186 | /* Change the switch matrix read issuing capability to 1 for the AXI SRAM target (Target 7) */ 187 | *((__IO uint32_t*)0x51008108) = 0x00000001; 188 | 189 | /* Configure the Vector Table location add offset address ------------------*/ 190 | #ifdef VECT_TAB_SRAM 191 | SCB->VTOR = D1_AXISRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ 192 | #else 193 | SCB->VTOR = FLASH_BANK1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ 194 | #endif 195 | 196 | 197 | } 198 | 199 | /** 200 | * @brief Update SystemCoreClock variable according to Clock Register Values. 201 | * The SystemCoreClock variable contains the core clock , it can 202 | * be used by the user application to setup the SysTick timer or configure 203 | * other parameters. 204 | * 205 | * @note Each time the core clock changes, this function must be called 206 | * to update SystemCoreClock variable value. Otherwise, any configuration 207 | * based on this variable will be incorrect. 208 | * 209 | * @note - The system frequency computed by this function is not the real 210 | * frequency in the chip. It is calculated based on the predefined 211 | * constant and the selected clock source: 212 | * 213 | * - If SYSCLK source is CSI, SystemCoreClock will contain the CSI_VALUE(*) 214 | * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(**) 215 | * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(***) 216 | * - If SYSCLK source is PLL, SystemCoreClock will contain the CSI_VALUE(*), 217 | * HSI_VALUE(**) or HSE_VALUE(***) multiplied/divided by the PLL factors. 218 | * 219 | * (*) CSI_VALUE is a constant defined in stm32h7xx_hal.h file (default value 220 | * 4 MHz) but the real value may vary depending on the variations 221 | * in voltage and temperature. 222 | * (**) HSI_VALUE is a constant defined in stm32h7xx_hal.h file (default value 223 | * 64 MHz) but the real value may vary depending on the variations 224 | * in voltage and temperature. 225 | * 226 | * (***)HSE_VALUE is a constant defined in stm32h7xx_hal.h file (default value 227 | * 25 MHz), user has to ensure that HSE_VALUE is same as the real 228 | * frequency of the crystal used. Otherwise, this function may 229 | * have wrong result. 230 | * 231 | * - The result of this function could be not correct when using fractional 232 | * value for HSE crystal. 233 | * @param None 234 | * @retval None 235 | */ 236 | void SystemCoreClockUpdate (void) 237 | { 238 | uint32_t pllp = 2, pllsource = 0, pllm = 2 ,tmp, pllfracen =0 , hsivalue = 0; 239 | float fracn1, pllvco = 0 ; 240 | 241 | /* Get SYSCLK source -------------------------------------------------------*/ 242 | 243 | switch (RCC->CFGR & RCC_CFGR_SWS) 244 | { 245 | case 0x00: /* HSI used as system clock source */ 246 | 247 | SystemCoreClock = (uint32_t) (HSI_VALUE >> ((RCC->CR & RCC_CR_HSIDIV)>> 3)); 248 | 249 | break; 250 | 251 | case 0x08: /* CSI used as system clock source */ 252 | SystemCoreClock = CSI_VALUE; 253 | break; 254 | 255 | case 0x10: /* HSE used as system clock source */ 256 | SystemCoreClock = HSE_VALUE; 257 | break; 258 | 259 | case 0x18: /* PLL1 used as system clock source */ 260 | 261 | /* PLL_VCO = (HSE_VALUE or HSI_VALUE or CSI_VALUE/ PLLM) * PLLN 262 | SYSCLK = PLL_VCO / PLLR 263 | */ 264 | pllsource = (RCC->PLLCKSELR & RCC_PLLCKSELR_PLLSRC); 265 | pllm = ((RCC->PLLCKSELR & RCC_PLLCKSELR_DIVM1)>> 4) ; 266 | pllfracen = RCC->PLLCFGR & RCC_PLLCFGR_PLL1FRACEN; 267 | fracn1 = (pllfracen* ((RCC->PLL1FRACR & RCC_PLL1FRACR_FRACN1)>> 3)); 268 | switch (pllsource) 269 | { 270 | 271 | case 0x00: /* HSI used as PLL clock source */ 272 | hsivalue = (HSI_VALUE >> ((RCC->CR & RCC_CR_HSIDIV)>> 3)) ; 273 | pllvco = (hsivalue/ pllm) * ((RCC->PLL1DIVR & RCC_PLL1DIVR_N1) + (fracn1/0x2000) +1 ); 274 | break; 275 | 276 | case 0x01: /* CSI used as PLL clock source */ 277 | pllvco = (CSI_VALUE / pllm) * ((RCC->PLL1DIVR & RCC_PLL1DIVR_N1) + (fracn1/0x2000) +1 ); 278 | break; 279 | 280 | case 0x02: /* HSE used as PLL clock source */ 281 | pllvco = (HSE_VALUE / pllm) * ((RCC->PLL1DIVR & RCC_PLL1DIVR_N1) + (fracn1/0x2000) +1 ); 282 | break; 283 | 284 | default: 285 | pllvco = (CSI_VALUE / pllm) * ((RCC->PLL1DIVR & RCC_PLL1DIVR_N1) + (fracn1/0x2000) +1 ); 286 | break; 287 | } 288 | pllp = (((RCC->PLL1DIVR & RCC_PLL1DIVR_P1) >>9) + 1 ) ; 289 | SystemCoreClock = (uint32_t) (pllvco/pllp); 290 | break; 291 | 292 | default: 293 | SystemCoreClock = CSI_VALUE; 294 | break; 295 | } 296 | 297 | /* Compute HCLK frequency --------------------------------------------------*/ 298 | /* Get HCLK prescaler */ 299 | tmp = D1CorePrescTable[(RCC->D1CFGR & RCC_D1CFGR_D1CPRE)>> POSITION_VAL(RCC_D1CFGR_D1CPRE_0)]; 300 | /* HCLK frequency */ 301 | SystemCoreClock >>= tmp; 302 | } 303 | 304 | /** 305 | * @} 306 | */ 307 | 308 | /** 309 | * @} 310 | */ 311 | 312 | /** 313 | * @} 314 | */ 315 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 316 | -------------------------------------------------------------------------------- /wormboy/sound.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * sound.cpp 3 | * 4 | * Created on: Jan 13, 2019 5 | * Author: jared 6 | */ 7 | 8 | #include "sound.h" 9 | #include "types.h" 10 | #include "mem.h" 11 | #include "mbad.h" 12 | #include "stm32h7xx_nucleo_144.h" 13 | 14 | #include 15 | 16 | 17 | #define SND_PERIOD 64 // microseconds 18 | 19 | struct Ch1SoundState { 20 | u8 vol; 21 | 22 | u8 sweepShadow; 23 | u8 sweepEnable; 24 | 25 | u8 len; 26 | u8 lenCntEnable; 27 | 28 | u8 enable; 29 | u32 gbFreq; 30 | u32 per; 31 | }; 32 | 33 | struct Ch2SoundState { 34 | u8 vol; 35 | 36 | u8 len; 37 | u8 lenCntEnable; 38 | 39 | u8 enable; 40 | u32 gbFreq; 41 | u32 per; 42 | }; 43 | 44 | struct Ch3SoundState { 45 | u8 vol; 46 | 47 | u32 len; 48 | u8 lenCntEnable; 49 | 50 | u8 enable; 51 | u32 gbFreq; 52 | u32 per; 53 | }; 54 | 55 | struct Ch4SoundState { 56 | u8 vol; 57 | u32 len; 58 | u8 lenCntEnable; 59 | u8 enable; 60 | }; 61 | 62 | struct SoundState { 63 | u8 masterEnable; 64 | Ch1SoundState ch1; 65 | Ch2SoundState ch2; 66 | Ch3SoundState ch3; 67 | Ch4SoundState ch4; 68 | }; 69 | 70 | SoundState sndState; 71 | 72 | u32 sndCount = 0; 73 | u32 c1c = 0, c2c = 0, c3c = 0; 74 | void snd() { 75 | u8 sndOut = 0; 76 | sndCount++; 77 | if(!sndState.masterEnable) return; 78 | 79 | if(sndState.ch1.enable && sndState.ch1.vol) { 80 | c1c++; 81 | u32 progress = sndCount % sndState.ch1.per; 82 | if(progress > sndState.ch1.per / 2) { 83 | sndOut += sndState.ch1.vol; 84 | } 85 | if(c1c > sndState.ch1.per) c1c = 0; 86 | } 87 | 88 | if(sndState.ch2.enable && sndState.ch2.vol) { 89 | c2c++; 90 | u32 progress = sndCount % sndState.ch2.per; 91 | if(progress > sndState.ch2.per / 2) { 92 | sndOut += sndState.ch2.vol; 93 | } 94 | if(c2c > sndState.ch2.per) c2c = 0; 95 | } 96 | 97 | if(sndState.ch3.enable && sndState.ch3.vol) { 98 | c3c++; 99 | u32 progress = sndCount % sndState.ch3.per; 100 | if(progress > sndState.ch3.per / 2) { 101 | sndOut += sndState.ch3.vol; 102 | } 103 | if(c3c > sndState.ch3.per) c3c = 0; 104 | } 105 | 106 | if(sndState.ch4.enable && sndState.ch4.vol) { 107 | sndOut += sndState.ch4.vol * (rand() & 1); 108 | } 109 | 110 | DAC1->DHR12R1 = (sndOut << 6); 111 | //digitalWrite(PC, 8, sndOut); 112 | } 113 | 114 | 115 | void init_sound() { 116 | printf("[sound] init\r\n"); 117 | sndState.masterEnable = 0; 118 | 119 | sndState.ch1.vol = 0; 120 | sndState.ch1.sweepShadow = 0; 121 | sndState.ch1.sweepEnable = 0; 122 | sndState.ch1.len = 0; 123 | sndState.ch1.lenCntEnable = 0; 124 | sndState.ch1.enable = 0; 125 | sndState.ch1.per = 0; 126 | sndState.ch1.gbFreq = 0; 127 | 128 | sndState.ch2.vol = 0; 129 | sndState.ch2.len = 0; 130 | sndState.ch2.lenCntEnable = 0; 131 | sndState.ch2.enable = 0; 132 | sndState.ch2.per = 0; 133 | sndState.ch2.gbFreq = 0; 134 | 135 | sndState.ch3.vol = 0; 136 | sndState.ch3.len = 0; 137 | sndState.ch3.lenCntEnable = 0; 138 | sndState.ch3.enable = 0; 139 | sndState.ch3.per = 0; 140 | sndState.ch3.gbFreq = 0; 141 | 142 | sndState.ch4.vol = 0; 143 | sndState.ch4.len = 0; 144 | sndState.ch4.lenCntEnable = 0; 145 | sndState.ch4.enable = 0; 146 | } 147 | 148 | 149 | 150 | void ch4_length_load() { 151 | // todo: currently ignoring duty cycle 152 | // first check the mode: 153 | u8 nr44 = globalMemState.ioRegs[IO_NR44]; 154 | u8 lenEnable = (nr44 & 0x40) != 0; 155 | sndState.ch4.lenCntEnable = lenEnable; 156 | 157 | if(lenEnable) { 158 | //printf("length load\r\n"); 159 | u8 nr41 = globalMemState.ioRegs[IO_NR41]; 160 | sndState.ch4.len = nr41 & 0x3f; 161 | sndState.ch4.enable = 1; 162 | } else { 163 | //sndState.ch2.enable = 1; // not 100% sure about this... 164 | } 165 | } 166 | 167 | void ch4_trigger_load() { 168 | u8 nr44 = globalMemState.ioRegs[IO_NR44]; 169 | u8 nr42 = globalMemState.ioRegs[IO_NR42]; 170 | 171 | u8 lenEnable = (nr44 & 0x40) != 0; 172 | sndState.ch4.lenCntEnable = lenEnable; 173 | //printf("trigger: 0x%x\r\n", nr14); 174 | if(nr44 & 0x80) { 175 | // trigger! 176 | u8 startingVol = nr42 >> 4; 177 | sndState.ch4.enable = 1; 178 | sndState.ch4.vol = startingVol; 179 | u8 nr41 = globalMemState.ioRegs[IO_NR41]; 180 | sndState.ch4.len = (64 - (nr41 & 0x3f)); 181 | } 182 | } 183 | 184 | void ch4_len_update() { 185 | if(sndState.ch4.lenCntEnable && sndState.ch4.len) { 186 | //printf("len: %d\r\n", sndState.ch1.len); 187 | sndState.ch4.len--; 188 | } 189 | 190 | if(sndState.ch4.len == 0) { 191 | sndState.ch4.lenCntEnable = 0; 192 | sndState.ch4.enable = 0; 193 | } 194 | 195 | } 196 | 197 | u8 ch4_env_subcount = 0; 198 | void ch4_env_update() { 199 | u8 nr42 = globalMemState.ioRegs[IO_NR42]; 200 | 201 | ch4_env_subcount++; 202 | u8 dir = (nr42 & 8) != 0; 203 | u8 sbc = nr42 & 7; 204 | if(ch4_env_subcount > sbc) { 205 | ch4_env_subcount = 0; 206 | if(dir && sndState.ch4.vol < 15) { 207 | sndState.ch4.vol++; 208 | } else if(sndState.ch4.vol > 0){ 209 | sndState.ch4.vol--; 210 | } 211 | } 212 | } 213 | 214 | ////////// 215 | // ch3 216 | ////////// 217 | 218 | // update the sndState with frequency from the NR13/NR14 register 219 | void ch3_update_freq() { 220 | u8 nr33 = globalMemState.ioRegs[IO_NR33]; 221 | u8 nr34 = globalMemState.ioRegs[IO_NR34]; 222 | 223 | // compute the "gb freq" 224 | u16 gbFreq = (((u16)nr34 & 0x7) << 8) + (u16)nr33; 225 | sndState.ch3.gbFreq = gbFreq; 226 | 227 | // compute frequency in Hz (todo don't use floats) 228 | float gbf = gbFreq; 229 | float freqHz = 131072.f / (2048.f - gbf); 230 | //printf("freq %f\r\n", freqHz); 231 | // compute period in timer ticks 232 | float period = 2.f*15625.f / freqHz; 233 | sndState.ch3.per = period; 234 | } 235 | 236 | void ch3_length_load() { 237 | ch3_update_freq(); 238 | // todo: currently ignoring duty cycle 239 | // first check the mode: 240 | u8 nr34 = globalMemState.ioRegs[IO_NR34]; 241 | u8 lenEnable = (nr34 & 0x40) != 0; 242 | sndState.ch3.lenCntEnable = lenEnable; 243 | 244 | if(lenEnable) { 245 | //printf("length load\r\n"); 246 | u8 nr31 = globalMemState.ioRegs[IO_NR31]; 247 | sndState.ch3.len = nr31 & 0x3f; 248 | sndState.ch3.enable = 1; 249 | } else { 250 | //sndState.ch2.enable = 1; // not 100% sure about this... 251 | } 252 | } 253 | 254 | void ch3_trigger_load() { 255 | u8 nr34 = globalMemState.ioRegs[IO_NR34]; 256 | u8 nr32 = globalMemState.ioRegs[IO_NR32]; 257 | ch3_update_freq(); 258 | u8 lenEnable = (nr34 & 0x40) != 0; 259 | sndState.ch3.lenCntEnable = lenEnable; 260 | //printf("trigger: 0x%x\r\n", nr14); 261 | if(nr34 & 0x80) { 262 | // trigger! 263 | u8 startingVol = nr32 >> 5; 264 | sndState.ch3.enable = 1; 265 | sndState.ch3.vol = (startingVol & 0x3) << 3; 266 | u8 nr31 = globalMemState.ioRegs[IO_NR31]; 267 | sndState.ch3.len = ((u16)256 - (nr31)); 268 | } 269 | } 270 | 271 | void ch3_len_update() { 272 | if(sndState.ch3.lenCntEnable && sndState.ch3.len) { 273 | //printf("len: %d\r\n", sndState.ch1.len); 274 | sndState.ch3.len--; 275 | } 276 | 277 | if(sndState.ch3.len == 0) { 278 | sndState.ch3.lenCntEnable = 0; 279 | sndState.ch3.enable = 0; 280 | } 281 | 282 | u8 nr32 = globalMemState.ioRegs[IO_NR32]; 283 | ch3_update_freq(); 284 | u8 startingVol = nr32 >> 5; 285 | sndState.ch3.vol = (startingVol & 0x3) << 3; 286 | 287 | } 288 | 289 | ////////// 290 | // snd2 291 | ///////// 292 | 293 | // update the sndState with frequency from the NR13/NR14 register 294 | void ch2_update_freq() { 295 | u8 nr23 = globalMemState.ioRegs[IO_NR23]; 296 | u8 nr24 = globalMemState.ioRegs[IO_NR24]; 297 | 298 | // compute the "gb freq" 299 | u16 gbFreq = (((u16)nr24 & 0x7) << 8) + (u16)nr23; 300 | sndState.ch2.gbFreq = gbFreq; 301 | 302 | // compute frequency in Hz (todo don't use floats) 303 | float gbf = gbFreq; 304 | float freqHz = 131072.f / (2048.f - gbf); 305 | //printf("freq %f\r\n", freqHz); 306 | // compute period in timer ticks 307 | float period = 2.f*15625.f / freqHz; 308 | sndState.ch2.per = period; 309 | } 310 | 311 | void ch2_length_load() { 312 | ch2_update_freq(); 313 | // todo: currently ignoring duty cycle 314 | // first check the mode: 315 | u8 nr24 = globalMemState.ioRegs[IO_NR24]; 316 | u8 lenEnable = (nr24 & 0x40) != 0; 317 | sndState.ch2.lenCntEnable = lenEnable; 318 | 319 | if(lenEnable) { 320 | //printf("length load\r\n"); 321 | u8 nr21 = globalMemState.ioRegs[IO_NR21]; 322 | sndState.ch2.len = nr21 & 0x3f; 323 | sndState.ch2.enable = 1; 324 | } else { 325 | //sndState.ch2.enable = 1; // not 100% sure about this... 326 | } 327 | } 328 | 329 | void ch2_trigger_load() { 330 | u8 nr24 = globalMemState.ioRegs[IO_NR24]; 331 | u8 nr22 = globalMemState.ioRegs[IO_NR22]; 332 | ch2_update_freq(); 333 | u8 lenEnable = (nr24 & 0x40) != 0; 334 | sndState.ch2.lenCntEnable = lenEnable; 335 | //printf("trigger: 0x%x\r\n", nr14); 336 | if(nr24 & 0x80) { 337 | // trigger! 338 | u8 startingVol = nr22 >> 4; 339 | sndState.ch2.enable = 1; 340 | sndState.ch2.vol = startingVol; 341 | u8 nr21 = globalMemState.ioRegs[IO_NR21]; 342 | sndState.ch2.len = (64 - (nr21 & 0x3f)); 343 | } 344 | } 345 | 346 | void ch2_len_update() { 347 | if(sndState.ch2.lenCntEnable && sndState.ch2.len) { 348 | //printf("len: %d\r\n", sndState.ch1.len); 349 | sndState.ch2.len--; 350 | } 351 | 352 | if(sndState.ch2.len == 0) { 353 | sndState.ch2.lenCntEnable = 0; 354 | sndState.ch2.enable = 0; 355 | } 356 | 357 | } 358 | 359 | u8 ch2_env_subcount = 0; 360 | void ch2_env_update() { 361 | u8 nr22 = globalMemState.ioRegs[IO_NR22]; 362 | 363 | ch2_env_subcount++; 364 | u8 dir = (nr22 & 8) != 0; 365 | u8 sbc = nr22 & 7; 366 | if(ch2_env_subcount > sbc) { 367 | ch2_env_subcount = 0; 368 | if(dir && sndState.ch2.vol < 15) { 369 | sndState.ch2.vol++; 370 | } else if(sndState.ch2.vol > 0){ 371 | sndState.ch2.vol--; 372 | } 373 | } 374 | } 375 | 376 | /////////////// 377 | // channel 1 378 | /////////////// 379 | 380 | // update the sndState with frequency from the NR13/NR14 register 381 | void ch1_update_freq() { 382 | u8 nr13 = globalMemState.ioRegs[IO_NR13]; 383 | u8 nr14 = globalMemState.ioRegs[IO_NR14]; 384 | 385 | // compute the "gb freq" 386 | u16 gbFreq = (((u16)nr14 & 0x7) << 8) + (u16)nr13; 387 | sndState.ch1.gbFreq = gbFreq; 388 | 389 | // compute frequency in Hz (todo don't use floats) 390 | float gbf = gbFreq; 391 | float freqHz = 131072.f / (2048.f - gbf); 392 | //printf("freq %f\r\n", freqHz); 393 | // compute period in timer ticks 394 | float period = 2.f*15625.f / freqHz; 395 | sndState.ch1.per = period; 396 | } 397 | 398 | void ch1_length_load() { 399 | ch1_update_freq(); 400 | // todo: currently ignoring duty cycle 401 | // first check the mode: 402 | u8 nr14 = globalMemState.ioRegs[IO_NR14]; 403 | u8 lenEnable = (nr14 & 0x40) != 0; 404 | sndState.ch1.lenCntEnable = lenEnable; 405 | 406 | if(lenEnable) { 407 | //printf("length load\r\n"); 408 | u8 nr11 = globalMemState.ioRegs[IO_NR11]; 409 | sndState.ch1.len = nr11 & 0x3f; 410 | sndState.ch1.enable = 1; 411 | } else { 412 | //sndState.ch1.enable = 1; // not 100% sure about this... 413 | } 414 | } 415 | 416 | void ch1_trigger_load() { 417 | u8 nr14 = globalMemState.ioRegs[IO_NR14]; 418 | u8 nr12 = globalMemState.ioRegs[IO_NR12]; 419 | ch1_update_freq(); 420 | u8 lenEnable = (nr14 & 0x40) != 0; 421 | sndState.ch1.lenCntEnable = lenEnable; 422 | //printf("trigger: 0x%x\r\n", nr14); 423 | if(nr14 & 0x80) { 424 | // trigger! 425 | u8 startingVol = nr12 >> 4; 426 | sndState.ch1.enable = 1; 427 | sndState.ch1.vol = startingVol; 428 | u8 nr11 = globalMemState.ioRegs[IO_NR11]; 429 | sndState.ch1.len = (64 - (nr11 & 0x3f)); 430 | } 431 | } 432 | 433 | void ch1_len_update() { 434 | if(sndState.ch1.lenCntEnable && sndState.ch1.len) { 435 | //printf("len: %d\r\n", sndState.ch1.len); 436 | sndState.ch1.len--; 437 | } 438 | 439 | if(sndState.ch1.len == 0) { 440 | sndState.ch1.lenCntEnable = 0; 441 | sndState.ch1.enable = 0; 442 | } 443 | 444 | } 445 | 446 | u8 ch1_env_subcount = 0; 447 | void ch1_env_update() { 448 | u8 nr12 = globalMemState.ioRegs[IO_NR12]; 449 | 450 | ch1_env_subcount++; 451 | u8 dir = (nr12 & 8) != 0; 452 | u8 sbc = nr12 & 7; 453 | if(ch1_env_subcount > sbc) { 454 | ch1_env_subcount = 0; 455 | if(dir && sndState.ch1.vol < 15) { 456 | sndState.ch1.vol++; 457 | } else if(sndState.ch1.vol > 0){ 458 | sndState.ch1.vol--; 459 | } 460 | } 461 | } 462 | 463 | void ch1_sweep_update() { 464 | 465 | } 466 | 467 | 468 | u32 sndCpuCycleCount = 0; 469 | u32 sndIterCnt = 0; 470 | void run_sound(u32 cpuCycles) { 471 | // read main sound registers 472 | u8 nr50 = globalMemState.ioRegs[IO_NR50]; 473 | u8 nr51 = globalMemState.ioRegs[IO_NR51]; 474 | u8 nr52 = globalMemState.ioRegs[IO_NR52]; 475 | 476 | u8 nr30 = globalMemState.ioRegs[IO_NR30]; 477 | 478 | // unpack 479 | u8 so2Vol = (nr50 & 0x70) >> 4; 480 | u8 so1Vol = (nr50 & 0x7); 481 | 482 | u8 ch1So1 = (nr51 & 0x01) != 0; 483 | u8 ch2So1 = (nr51 & 0x02) != 0; 484 | u8 ch3So1 = (nr51 & 0x04) != 0; 485 | u8 ch4So1 = (nr51 & 0x08) != 0; 486 | u8 ch1So2 = (nr51 & 0x10) != 0; 487 | u8 ch2So2 = (nr51 & 0x20) != 0; 488 | u8 ch3So2 = (nr51 & 0x40) != 0; 489 | u8 ch4So2 = (nr51 & 0x80) != 0; 490 | 491 | u8 ch1Master = (nr52 & 0x01) != 0; 492 | u8 ch2Master = (nr52 & 0x02) != 0; 493 | u8 ch3Master = (nr52 & 0x04) != 0; 494 | u8 ch4Master = (nr52 & 0x08) != 0; 495 | u8 sndMaster = (nr52 & 0x80) != 0; 496 | //ch1_update_freq(); 497 | if(sndMaster) { 498 | // do the thing 499 | sndState.masterEnable = 1; 500 | sndCpuCycleCount += cpuCycles; 501 | if(sndCpuCycleCount >= (1 << 14)) { 502 | sndCpuCycleCount = 0; 503 | //printf("sdupdate\r\n"); 504 | sndIterCnt++; 505 | ch1_len_update(); 506 | ch2_len_update(); 507 | ch3_len_update(); 508 | ch4_len_update(); 509 | if((sndIterCnt & 0x1) == 0) { 510 | ch1_sweep_update(); 511 | //ch2_sweep_update(); 512 | } 513 | if((sndIterCnt & 0x3) == 0) { 514 | ch1_env_update(); 515 | ch2_env_update(); 516 | ch4_env_update(); 517 | } 518 | } 519 | if(!(ch1So1 || ch1So2)) { 520 | sndState.ch1.enable = 0; 521 | } 522 | 523 | if(!((ch2So1 || ch2So2))) { 524 | sndState.ch2.enable = 0; 525 | } 526 | 527 | if(!((ch3So1 || ch3So2))) { 528 | sndState.ch3.enable = 0; 529 | } 530 | 531 | if(!((ch4So1 || ch4So2))) { 532 | sndState.ch4.enable = 0; 533 | } 534 | 535 | if(!(nr30 & 0x80)) { 536 | sndState.ch3.enable = 0; 537 | } 538 | } else { 539 | sndState.masterEnable = 0; 540 | } 541 | } 542 | 543 | -------------------------------------------------------------------------------- /wormboy/video.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #define NDEBUG 3 | #include 4 | #include "video.h" 5 | #include "mem.h" 6 | #include "ntsc.h" 7 | 8 | u32 frameCount = 0; 9 | 10 | // the implementation of sprites is poor. 11 | // pick a value for this that is between 0 and 256 and not equal to one of the colors 12 | #define TRANSPARENT_SPRITE 37 13 | 14 | #define BRIGHTEST_COLOR 6 15 | 16 | VideoState globalVideoState; 17 | 18 | // colors on screen (white, light gray, dark gray, black) 19 | // the first one must be "BRIGHTEST_COLOR" - other code depends on this! 20 | static const u8 colors[4] = {BRIGHTEST_COLOR, 4, 2, 0}; 21 | 22 | 23 | 24 | 25 | void initVideo(u8* frameBuffer) { 26 | globalVideoState.mode = 0; 27 | globalVideoState.modeClock = 0; 28 | globalVideoState.line = 0; 29 | globalVideoState.frameBuffer = frameBuffer; // <- todo change for STM32 30 | 31 | // for(u32 i = 0; i < 160*144; i++) { 32 | // globalVideoState.frameBuffer[i] = 255; 33 | // } 34 | 35 | } 36 | 37 | inline u32 xy2px(u8 x, u8 y) { 38 | return H_RES*(y+Y0s) + x + X0s; 39 | } 40 | 41 | void dumpVideo() { 42 | for(u16 i = 0x8000; i < 0x87ff; i++) { 43 | if((i%8) == 1) { 44 | printf("@ 0x%04x: ", i); 45 | } 46 | printf("0x%02x ", readByte(i)); 47 | if(!(i%8)) printf("\n"); 48 | } 49 | printf("\n"); 50 | assert(false); 51 | } 52 | 53 | // read a pixel out of a tile and apply the given palette 54 | u8 readTile(u16 tileAddr, u8 x, u8 y, u8 palette) { 55 | assert(x <= 8); 56 | assert(y <= 8); 57 | x = (7 - x); 58 | u16 loAddr = tileAddr + (y*(u16)2); 59 | u16 hiAddr = loAddr + (u16)1; 60 | u8 lo = readByte(loAddr); 61 | u8 hi = readByte(hiAddr); 62 | u8 loV = (lo >> x) & (u8)1; 63 | u8 hiV = (hi >> x) & (u8)1; 64 | //u8 result = loV * 120 + hiV * 60; 65 | u8 colorIdx = loV + 2 * hiV; 66 | u8 colorID = (palette >> (2 * colorIdx)) & 3; 67 | return colors[colorID]; 68 | } 69 | 70 | u8 readTilePtr(u8* tileAddr, u8 x, u8 y, u8 palette) { 71 | assert(x <= 8); 72 | assert(y <= 8); 73 | x = (7 - x); 74 | u8* loAddr = tileAddr + (y*(u16)2); 75 | u8* hiAddr = loAddr + (u16)1; 76 | u8 lo = *(loAddr); 77 | u8 hi = *(hiAddr); 78 | u8 loV = (lo >> x) & (u8)1; 79 | u8 hiV = (hi >> x) & (u8)1; 80 | //u8 result = loV * 120 + hiV * 60; 81 | u8 colorIdx = loV + 2 * hiV; 82 | u8 colorID = (palette >> (2 * colorIdx)) & 3; 83 | return colors[colorID]; 84 | } 85 | 86 | // read a pixel out of a tile and apply the given palette 87 | // returns TRANSPARENT_SPRITE if the sprite should be transparent 88 | u8 readSpriteTile(u16 tileAddr, u8 x, u8 y, u8 palette) { 89 | //tileAddr = 0x8180; 90 | assert(x <= 8); 91 | assert(y <= 8); 92 | x = (7 - x); 93 | u16 loAddr = tileAddr + (y*(u16)2); 94 | u16 hiAddr = loAddr + (u16)1; 95 | u8 lo = readByte(loAddr); 96 | u8 hi = readByte(hiAddr); 97 | u8 loV = (lo >> x) & (u8)1; 98 | u8 hiV = (hi >> x) & (u8)1; 99 | u8 colorIdx = loV + 2 * hiV; 100 | if(colorIdx == 0) { 101 | return TRANSPARENT_SPRITE; 102 | } 103 | u8 colorID = (palette >> (2 * colorIdx)) & 3; 104 | return colors[colorID]; 105 | } 106 | 107 | u8 readSpriteTileAddr(u8* tileAddr, u8 x, u8 y, u8 palette) { 108 | //tileAddr = 0x8180; 109 | assert(x <= 8); 110 | assert(y <= 8); 111 | x = (7 - x); 112 | u8* loAddr = tileAddr + (y*(u16)2); 113 | u8* hiAddr = loAddr + (u16)1; 114 | u8 lo = *(loAddr); 115 | u8 hi = *(hiAddr); 116 | u8 loV = (lo >> x) & (u8)1; 117 | u8 hiV = (hi >> x) & (u8)1; 118 | u8 colorIdx = loV + 2 * hiV; 119 | if(colorIdx == 0) { 120 | return TRANSPARENT_SPRITE; 121 | } 122 | u8 colorID = (palette >> (2 * colorIdx)) & 3; 123 | return colors[colorID]; 124 | } 125 | 126 | // compute the address of the tile from the tile's index 127 | // this is confusing because depending on the tileData selected, 128 | // the tileIdx is either signed or unsigned 129 | u16 computeTileAddr(u8 tileIdx, bool tileData) { 130 | if(tileData) { 131 | return 0x8000 + 16 * tileIdx; 132 | } else { 133 | if(tileIdx <= 127) { 134 | return 0x9000 + 16 * tileIdx; 135 | } else { 136 | return 0x8000 + 16 * (tileIdx); 137 | } 138 | } 139 | } 140 | 141 | u8* computeTileAddrPtr(u8 tileIdx, bool tileData) { 142 | if(tileData) { 143 | return globalMemState.vram + 16 * tileIdx; 144 | } else { 145 | if(tileIdx <= 127) { 146 | return globalMemState.vram + 0x1000 + 16 * tileIdx; 147 | } else { 148 | return globalMemState.vram + 16 * (tileIdx); 149 | } 150 | } 151 | } 152 | 153 | // main function to render a line of the display. 154 | // this implementation is missing a number of things, including (but not limited to) 155 | // -- proper position of the WINDOW 156 | // -- 16x8 sprites 157 | // -- sprite sorting 158 | // -- 10 sprite limit 159 | u32 gameboyFrameCount = 0; 160 | extern u32 frameCountNTSC; 161 | u8 skipFrame = 0; 162 | void renderLine() { 163 | if(frameCount % 8) return; 164 | // if(skipFrame || (globalVideoState.line == 0 && (frameCountNTSC - gameboyFrameCount) > 5)) { 165 | // skipFrame = 1; 166 | // return; 167 | // } 168 | //return; 169 | //if(drawing) return; 170 | //printf("%x %x\n", im_line_va, im_back); 171 | globalVideoState.frameBuffer = im_line_vas[bufferSelect]; 172 | u8 lcdc = globalMemState.ioRegs[IO_LCDC]; // lcd control register 173 | bool lcdOn = (lcdc >> 7) & (u8)1; // lcd display on? 174 | bool windowTileMap = (lcdc >> 6) & (u8)1; // select tilemap source for window 175 | bool windowEnable = (lcdc >> 5) & (u8)1; // draw window? 176 | bool tileData = (lcdc >> 4) & (u8)1; // select tile data source 177 | bool bgTileMap = (lcdc >> 3) & (u8)1; // select tilemap source for background 178 | bool objSize = (lcdc >> 2) & (u8)1; // pick sprite size (nyi) 179 | bool objEnable = (lcdc >> 1) & (u8)1; // enable sprite renderer 180 | bool bgWinEnable = (lcdc >> 0) & (u8)1; // enable background and window renderer? 181 | 182 | u16 windowMapAddr = (u16)(windowTileMap ? 0x9c00 : 0x9800); 183 | u16 bgTileMapAddr = (u16)(bgTileMap ? 0x9c00 : 0x9800); 184 | 185 | // background renderer 186 | if(lcdOn && bgWinEnable) { 187 | // render background onto framebuffer 188 | u8 pal = globalMemState.ioRegs[IO_BGP]; // color palette 189 | u16 tileMapRowAddr = (u16)(bgTileMapAddr + 32*((((u16)globalVideoState.line + 190 | globalMemState.ioRegs[IO_SCROLLY]) & (u16)255) >> 3)); // address of the row of the tilemap 191 | u8 tileMapColIdx = globalMemState.ioRegs[IO_SCROLLX] >> 3; // column index of the tilemap 192 | u8 yPixOffset = ((u8)globalVideoState.line + globalMemState.ioRegs[IO_SCROLLY]) & (u8)7; // y-pixel of tile 193 | u8 xPixOffset = globalMemState.ioRegs[IO_SCROLLX] & (u8)7; // x-pixel of tile 194 | //u8* linePtr = globalVideoState.frameBuffer + 160 * globalVideoState.line; // frame buffer pointer 195 | u8 tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // tile index 196 | 197 | // loop over pixels in the line 198 | for(u8 px = 0; px < 160; px++) { 199 | globalVideoState.frameBuffer = im_line_vas[bufferSelect]; 200 | globalVideoState.frameBuffer[xy2px(px,globalVideoState.line)] = 201 | readTilePtr(computeTileAddrPtr(tileIdx, tileData), xPixOffset, yPixOffset, pal); 202 | //readTile(computeTileAddr(tileIdx, tileData), xPixOffset, yPixOffset, pal); // set the frame buffer 203 | 204 | xPixOffset++; // increment tile pixel 205 | //linePtr++; // increment frame buffer pixel 206 | if(xPixOffset == 8) { // if we have overflowed the tile 207 | xPixOffset = 0; // go to the beginning 208 | tileMapColIdx = (tileMapColIdx + 1) & 31; // of the next tile (allow wraparound) 209 | tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // and look up the tile index in the tile map 210 | } 211 | } 212 | } 213 | 214 | // window renderer 215 | if(windowEnable) { 216 | u8 pal = globalMemState.ioRegs[IO_BGP]; // palette 217 | u8 wx = globalMemState.ioRegs[IO_WINX]; // location of the window (nyi) 218 | u8 wy = globalMemState.ioRegs[IO_WINY]; // location of the window (nyi) 219 | if(wx > 166 || wy > 143) { 220 | // if the window is out of this range, it is disabled too. 221 | } else { 222 | u16 tileMapRowAddr = windowMapAddr + 32*((((u16)globalVideoState.line)) >> 3); // address of the row of the tilemap 223 | u8 tileMapColIdx = 0; // column index of the tilemap 224 | u8 yPixOffset = ((u8)globalVideoState.line) & (u8)7; // y-pixel of tile 225 | u8 xPixOffset = 0; // x-pixel of tile 226 | //u8* linePtr = globalVideoState.frameBuffer + 160 * globalVideoState.line; // frame buffer pointer 227 | u8 tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // tile index 228 | 229 | // loop over pixels in the line 230 | for(u8 px = 0; px < 160; px++) { 231 | globalVideoState.frameBuffer[xy2px(px,globalVideoState.line)] = 232 | readTilePtr(computeTileAddrPtr(tileIdx, tileData), xPixOffset, yPixOffset, pal); 233 | //*linePtr = readTile(computeTileAddr(tileIdx, tileData), xPixOffset, yPixOffset, pal); // set the frame buffer 234 | 235 | xPixOffset++; // increment tile pixel 236 | //linePtr++; // increment frame buffer pixel 237 | if(xPixOffset == 8) { // if we have overflowed the tile 238 | xPixOffset = 0; // go to the beginning 239 | tileMapColIdx = (tileMapColIdx + 1) & 31; // of the next tile (allow wraparound, but it shouldn't happen?) 240 | tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // and look up the tile index in the tile map 241 | } 242 | } 243 | } 244 | } 245 | 246 | // sprite renderer 247 | if(objEnable) { 248 | for(u16 spriteID = 0; spriteID < 40; spriteID++) { 249 | u16 oamPtr = 0xfe00 + 4 * spriteID; // sprite information table 250 | u8 spriteY = readByte(oamPtr); // y-coordinate of sprite 251 | u8 spriteX = readByte(oamPtr + 1); // x-coordinate of sprite 252 | u8 patternIdx = readByte(oamPtr + 2); // sprite pattern 253 | u8 flags = readByte(oamPtr + 3); // flag bits 254 | 255 | bool pri = (flags >> 7) & (u8)1; // priority (transparency stuff) 256 | bool yFlip = (flags >> 6) & (u8)1; // flip around y? 257 | bool xFlip = (flags >> 5) & (u8)1; // flip around x? 258 | bool palID = (flags >> 4) & (u8)1; // palette ID (OBP0/OBP2) 259 | 260 | u8 pal = palID ? globalMemState.ioRegs[IO_OBP1] : globalMemState.ioRegs[IO_OBP0]; 261 | 262 | 263 | if(spriteX | spriteY) { 264 | // the sprite coordinates have an offset 265 | u8 spriteStartY = spriteY - 16; 266 | u8 spriteLastY = spriteStartY + 8; // todo 16 row sprites 267 | 268 | // reject based on y if the sprite won't be visible in the current line 269 | if(globalVideoState.line < spriteStartY || globalVideoState.line >= spriteLastY) { 270 | continue; 271 | } 272 | 273 | // get y px relative to the sprite pattern 274 | u8 tileY = globalVideoState.line - spriteStartY; 275 | if(yFlip) { 276 | tileY = 7 - tileY; 277 | } 278 | 279 | assert(tileY < 8); 280 | 281 | // loop over the 8 pixels that the sprite is on: 282 | for(u8 tileX = 0; tileX < 8; tileX++) { 283 | 284 | u8 xPos = spriteX - 8 + tileX; // position on the screen 285 | if(xPos >= 160) continue; // reject if we go off the end, don't wrap around 286 | 287 | u32 fbIdx = xy2px(xPos, globalVideoState.line); 288 | 289 | globalVideoState.frameBuffer = im_line_vas[bufferSelect]; 290 | 291 | 292 | // current color at the screen 293 | u8 old = globalVideoState.frameBuffer[fbIdx]; 294 | 295 | // get the pixel from the sprite pattern data 296 | u8 tileLookupX = tileX; 297 | if(xFlip) { 298 | tileLookupX = 7 - tileX; 299 | } 300 | //u8 tileValue = readSpriteTile(0x8000 + patternIdx * 16, tileLookupX, tileY, pal); 301 | u8 tileValue = readSpriteTileAddr(globalMemState.vram + patternIdx * 16, tileLookupX, tileY, pal); 302 | //u8 tileValue = readSpriteTileAddr(globalMemState.vram + patternIdx*16, tileLookupX, tileY, pal); 303 | //u8 tileValue = 4; 304 | // don't draw transparent 305 | if(tileValue == TRANSPARENT_SPRITE) continue; // (transparent sprites) 306 | 307 | // not sure this is 100% right... 308 | if(!pri) { 309 | globalVideoState.frameBuffer[fbIdx] = tileValue; 310 | } else { 311 | if(old == BRIGHTEST_COLOR) { 312 | globalVideoState.frameBuffer[fbIdx] = tileValue; 313 | } 314 | } 315 | } 316 | } 317 | } 318 | } 319 | } 320 | 321 | static u32 oldTics = 0; 322 | 323 | float filteredFrameRate = 0.f; 324 | 325 | // step the video by a number of clock cycles 326 | void stepVideo(u32 cycles) { 327 | globalVideoState.modeClock += cycles; 328 | switch(globalVideoState.mode) { 329 | case 2: // OAM read, scanning 330 | 331 | if(globalVideoState.modeClock >= 80) { 332 | globalVideoState.modeClock = 0; 333 | globalVideoState.mode = 3; // VRAM read, scanning 334 | } 335 | break; 336 | case 3: // VRAM read, scanning 337 | if(globalVideoState.modeClock >= 172) { 338 | globalVideoState.modeClock = 0; 339 | globalVideoState.mode = 0; // hblank 340 | renderLine(); // draw line into framebuffer 341 | } 342 | break; 343 | case 0: // hblank 344 | if(globalVideoState.modeClock >= 204) { 345 | globalVideoState.modeClock = 0; 346 | globalVideoState.line++; 347 | 348 | if(globalVideoState.line == 143) { 349 | 350 | globalVideoState.mode = 1; // vblank 351 | globalMemState.ioRegs[IO_IF] |= 0x1; // set interrupt for vblank 352 | printf("lag: %d\r\n", frameCountNTSC - gameboyFrameCount); 353 | 354 | gameboyFrameCount++; 355 | while(gameboyFrameCount > frameCountNTSC) { 356 | 357 | } 358 | //if(!keyboard.turbo) // if we are in "turbo" mode, don't update graphics 359 | //updateGraphics(); // display framebuffer on screen 360 | //bufferSelect = !bufferSelect; 361 | 362 | skipFrame = 0; 363 | int aaaa = tics - oldTics; 364 | float frameTime = 0.0000064 * aaaa; 365 | filteredFrameRate = (0.94 * filteredFrameRate) + (0.06 * frameTime); 366 | //printf("%d %f %f\n", aaaa, 1.f/frameTime, 1.f/filteredFrameRate); 367 | frameCount++; 368 | oldTics = tics; 369 | 370 | } else { 371 | globalVideoState.mode = 2; // oam 372 | } 373 | } 374 | break; 375 | case 1: // vblank 376 | if(globalVideoState.modeClock >= 456) { 377 | globalVideoState.modeClock = 0; 378 | globalVideoState.line++; 379 | 380 | if(globalVideoState.line > 153) { 381 | globalVideoState.mode = 2; 382 | globalVideoState.line = 0; 383 | } 384 | } 385 | break; 386 | default: 387 | assert(false); 388 | } 389 | 390 | globalMemState.ioRegs[IO_LY] = (u8)globalVideoState.line; // update current line 391 | 392 | 393 | // this is likely somewhat wrong. 394 | u8 stat = globalMemState.ioRegs[IO_STAT]; // update STAT register (this is likely the source of issue on bubble bobble) 395 | stat &= ~7; // clear mode, coincidence 396 | stat += globalVideoState.mode; // set current mode 397 | if(globalMemState.ioRegs[IO_LYC] == globalMemState.ioRegs[IO_LY]) { // check coincidence 398 | stat += 4; 399 | if((stat >> 6) & 1) { 400 | globalMemState.ioRegs[IO_IF] |= 2; // stat interrupt 401 | } 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /wormboy/mem.cpp: -------------------------------------------------------------------------------- 1 | #define NDEBUG 2 | #include 3 | #include 4 | #include 5 | 6 | #include "mem.h" 7 | #include "platform.h" 8 | #include "rom.h" 9 | #include "sound.h" 10 | 11 | #define DANGER_MODE 12 | 13 | MemState globalMemState; 14 | 15 | void CartInfo::print() { 16 | printf("Gameboy Cartridge\n" 17 | "\ttitle: %s\n" 18 | "\tisColor: 0x%x\n" 19 | "\tSGB: 0x%x\n" 20 | "\tcartType: 0x%x\n" 21 | "\tromSize: 0x%x\n" 22 | "\tramSize: 0x%x\n" 23 | "\tnotJapan: 0x%x\n", 24 | title, isColor, SGB, (u8)cartType, (u8)romSize, (u8)ramSize, notJapan); 25 | } 26 | 27 | // number of banks for given cartridge types 28 | static const u8 romSizeIDToNBanks[7] = {2,4,8,16,32,64,128}; 29 | static const u8 ramSizeIDToNBanks[5] = {0,1,1,4,4}; 30 | static u8* fileData = nullptr; 31 | 32 | void saveGame() { 33 | FileLoadData fld; 34 | fld.size = 0x2000 * globalMemState.nRamBanks; 35 | fld.data = globalMemState.mappedRamAllocation; 36 | saveFile("savegame.gam", fld); 37 | } 38 | 39 | void loadGame() { 40 | FileLoadData fld = loadFile("savegame.gam"); 41 | memcpy(globalMemState.mappedRamAllocation, fld.data, 0x2000 * globalMemState.nRamBanks); 42 | } 43 | 44 | 45 | 46 | 47 | void initMem(FileLoadData file) { 48 | CartInfo* cartInfo = (CartInfo*)(file.data + CART_INFO_ADDR); 49 | cartInfo->print(); 50 | fileData = file.data; 51 | printf("ROM size: 0x%x bytes\n", file.size); 52 | 53 | 54 | globalMemState.inBios = true; // start in BIOS mode 55 | globalMemState.rom0 = file.data; // ROM-bank 0 is the bottom of the cartridge 56 | 57 | // initialize everything to zero 58 | //globalMemState.mappedRom = nullptr; 59 | globalMemState.nRamBanks = 0; 60 | globalMemState.nRomBanks = 0; 61 | globalMemState.vram = nullptr; 62 | globalMemState.mappedRam = nullptr; 63 | globalMemState.disabledMappedRam = nullptr; 64 | globalMemState.mappedRamAllocation = nullptr; 65 | globalMemState.internalRam = nullptr; 66 | globalMemState.upperRam = nullptr; 67 | 68 | switch(cartInfo->cartType) { 69 | case ROM_ONLY: 70 | globalMemState.romBank = 1; 71 | //globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default 72 | break; 73 | 74 | case ROM_MBC1: 75 | globalMemState.romBank = 1; 76 | //globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default 77 | globalMemState.mbcType = 1; 78 | if((u8)cartInfo->romSize < 7) { 79 | globalMemState.nRomBanks = romSizeIDToNBanks[(u8)cartInfo->romSize]; // has ROM banks 80 | } else { 81 | printf("unknown number of rom banks\n"); 82 | assert(false); 83 | } 84 | break; 85 | 86 | case ROM_MBC1_RAM: 87 | globalMemState.romBank = 1; 88 | //globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default 89 | globalMemState.mbcType = 1; 90 | if((u8)cartInfo->romSize < 7) { 91 | globalMemState.nRomBanks = romSizeIDToNBanks[(u8)cartInfo->romSize]; // has ROM banks 92 | } else { 93 | printf("unknown number of rom banks\n"); 94 | assert(false); 95 | } 96 | 97 | if((u8)cartInfo->ramSize < 5) { 98 | globalMemState.nRamBanks = ramSizeIDToNBanks[(u8)cartInfo->ramSize]; // has RAM banks 99 | } else { 100 | printf("unknown number of ram banks\n"); 101 | assert(false); 102 | } 103 | break; 104 | 105 | case ROM_MBC3_RAM_BATT: 106 | case ROM_MBC3_TIMER_RAM_BATT: 107 | globalMemState.romBank = 1; 108 | //globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default 109 | globalMemState.mbcType = 3; 110 | if((u8)cartInfo->romSize < 7) { 111 | globalMemState.nRomBanks = romSizeIDToNBanks[(u8)cartInfo->romSize]; // has ROM banks 112 | } else { 113 | printf("unknown number of rom banks\n"); 114 | assert(false); 115 | } 116 | 117 | if((u8)cartInfo->ramSize < 5) { 118 | globalMemState.nRamBanks = ramSizeIDToNBanks[(u8)cartInfo->ramSize]; // has RAM banks 119 | } else { 120 | printf("unknown number of ram banks\n"); 121 | } 122 | break; 123 | default: 124 | printf("unknown cart type 0x%x\n", (u8)cartInfo->cartType); 125 | assert(false); 126 | break; 127 | } 128 | 129 | printf("mbc%d\n", globalMemState.mbcType); 130 | printf("rom-banks: %d\n", globalMemState.nRomBanks); 131 | printf("ram-banks: %d\n", globalMemState.nRamBanks); 132 | 133 | // allocate cartridge RAM (8 KB * # of banks) 134 | if(globalMemState.nRamBanks) { 135 | globalMemState.mappedRamAllocation = (u8*)badalloc_check(0x2000 * globalMemState.nRamBanks, "mapped-ram"); 136 | memset((void*)globalMemState.mappedRamAllocation, 0, 0x2000 * globalMemState.nRamBanks); 137 | globalMemState.disabledMappedRam = globalMemState.mappedRamAllocation; 138 | } else { 139 | 140 | } 141 | 142 | // allocate memories: 143 | // internal RAM 144 | globalMemState.internalRam = (u8*)badalloc_check(0x2000, "internal-ram"); 145 | globalMemState.vram = (u8*)badalloc_check(0x2000, "vram"); 146 | globalMemState.upperRam = (u8*)badalloc_check(0x80, "upperRam"); 147 | globalMemState.ioRegs = (u8*)badalloc_check(0x80, "ioRegs"); 148 | 149 | // clear RAMs 150 | memset(globalMemState.internalRam, 0, 0x2000); 151 | memset(globalMemState.vram, 0, 0x2000); 152 | memset(globalMemState.upperRam, 0, 0x80); 153 | memset(globalMemState.ioRegs, 0, 0x80); 154 | 155 | // setup i/o regs and friends 156 | u8* io = globalMemState.ioRegs; 157 | io[IO_TIMA] = 0; // reset TIMER COUNT to 0 158 | io[IO_TMA] = 0; // TIMER RELOAD 159 | io[IO_TAC] = 0; // TIMER STOP 160 | io[IO_NR10] = 0x80; 161 | io[IO_NR11] = 0xbf; 162 | io[IO_NR12] = 0xf3; 163 | io[IO_NR14] = 0xbf; 164 | io[IO_NR21] = 0x3f; 165 | io[IO_NR22] = 0x00; 166 | io[IO_NR24] = 0xbf; 167 | io[IO_NR30] = 0x7f; 168 | io[IO_NR31] = 0xff; 169 | io[IO_NR32] = 0x9f; 170 | io[IO_NR34] = 0xbf; 171 | io[IO_NR41] = 0xff; 172 | io[IO_NR42] = 0x00; 173 | io[IO_NR43] = 0x00; 174 | io[IO_NR44] = 0xbf; 175 | io[IO_NR50] = 0x77; 176 | io[IO_NR51] = 0xf3; 177 | io[IO_NR52] = 0xf1; 178 | io[IO_LCDC] = 0x91; 179 | io[IO_SCROLLY] = 0x00; 180 | io[IO_SCROLLX] = 0x00; 181 | io[IO_LYC] = 0x00; 182 | io[IO_BGP] = 0xfc; 183 | io[IO_OBP0] = 0xff; 184 | io[IO_OBP1] = 0xff; 185 | io[IO_WINY] = 0x00; 186 | io[IO_WINX] = 0x00; 187 | 188 | // turn off interrupts 189 | globalMemState.upperRam[0x7f] = 0; 190 | } 191 | 192 | 193 | 194 | // boot ROM 195 | static const u8 bios[256] = { 0x31, 0xFE, 0xFF, 0xAF, 0x21, 0xFF, 0x9F, 0x32, 0xCB, 0x7C, 0x20, 0xFB, 0x21, 0x26, 0xFF, 0x0E, 196 | 0x11, 0x3E, 0x80, 0x32, 0xE2, 0x0C, 0x3E, 0xF3, 0xE2, 0x32, 0x3E, 0x77, 0x77, 0x3E, 0xFC, 0xE0, 197 | 0x47, 0x11, 0x04, 0x01, 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B, 198 | 0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 0x23, 0x05, 0x20, 0xF9, 199 | 0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20, 200 | 0xF9, 0x2E, 0x0F, 0x18, 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04, 201 | 0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 0xF7, 0x1D, 0x20, 0xF2, 202 | 0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06, 203 | 0x7B, 0xE2, 0x0C, 0x3E, 0x87, 0xE2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20, 204 | 0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 0xC1, 0xCB, 0x11, 0x17, 205 | 0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 206 | 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, 207 | 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, 208 | 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 0x3C, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x3C, 209 | 0x21, 0x04, 0x01, 0x11, 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x00, 0x00, 0x23, 0x7D, 0xFE, 0x34, 0x20, 210 | 0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x00, 0x00, 0x3E, 0x01, 0xE0, 0x50}; 211 | // handler for MBC0 switch 212 | void mbc0Handler(u16 addr, u8 value) { 213 | 214 | // it looks like tetris tries to select ROM 1 for banked ROM, so we need to allow this: 215 | if(addr >= 0x2000 && addr < 0x3fff) { 216 | if(value == 0 || value == 1) { 217 | // nothing to do! 218 | } else { 219 | assert(false); 220 | } 221 | } else { 222 | assert(false); 223 | } 224 | 225 | } 226 | 227 | // handler for MBC1 switch (doesn't handle everything yet...) 228 | void mbc1Handler(u16 addr, u8 value) { 229 | if(addr >= 0x2000 && addr < 0x3fff) { 230 | // ROM bank switch 231 | if(value >= globalMemState.nRomBanks) { 232 | printf("\trequested rom bank %d when there are only %d banks!\n", value, globalMemState.nRomBanks); 233 | assert(false); 234 | } 235 | 236 | if(value == 0) value = 1; 237 | if(value == 0x21) value = 0x20; 238 | if(value == 0x41) value = 0x40; 239 | globalMemState.romBank = value; 240 | //globalMemState.mappedRom = fileData + 0x4000 * value; 241 | } else if(addr >= 0 && addr < 0x1fff) { 242 | // enable RAM 243 | if(value == 0) { 244 | globalMemState.disabledMappedRam = globalMemState.mappedRam; 245 | globalMemState.mappedRam = nullptr; 246 | } else if(value == 0xa) { 247 | globalMemState.mappedRam = globalMemState.disabledMappedRam; 248 | } else { 249 | assert(false); 250 | } 251 | } else { 252 | assert(false); 253 | } 254 | 255 | } 256 | 257 | // handler for MBC2 switch (doesn't handle anything yet...) 258 | void mbc2Handler(u16 addr, u8 value) { 259 | assert(false); 260 | } 261 | 262 | // handler for MBC3 switch (doesn't handle anything yet...) 263 | void mbc3Handler(u16 addr, u8 value) { 264 | 265 | if(addr >= 0x2000 && addr < 0x3fff) { 266 | // ROM bank switch 267 | if(value >= globalMemState.nRomBanks) { 268 | printf("\trequested rom bank %d when there are only %d banks!\n", value, globalMemState.nRomBanks); 269 | assert(false); 270 | } 271 | 272 | if(value == 0) value = 1; 273 | globalMemState.romBank = value; 274 | //globalMemState.mappedRom = fileData + 0x4000 * value; 275 | } else if(addr >= 0 && addr < 0x1fff) { 276 | // RAM enable/disable 277 | if(value == 0) { 278 | globalMemState.disabledMappedRam = globalMemState.mappedRam; 279 | globalMemState.mappedRam = nullptr; 280 | } else if(value == 0xa) { 281 | globalMemState.mappedRam = globalMemState.disabledMappedRam; 282 | } else { 283 | //assert(false); 284 | } 285 | } else if(addr >= 0x4000 && addr < 0x5fff) { 286 | // RAM bank switch 287 | if(value < globalMemState.nRamBanks) { 288 | globalMemState.mappedRam = globalMemState.mappedRamAllocation + 0x2000 * value; 289 | } else { 290 | //assert(false); 291 | } 292 | } else if(addr == 0x6000) { 293 | // ?? RTC latch nonsense 294 | } else { 295 | assert(false); 296 | } 297 | } 298 | 299 | 300 | // handler for all MBC switches 301 | void mbcHandler(u16 addr, u8 value) { 302 | switch(globalMemState.mbcType) { 303 | case 0: 304 | mbc0Handler(addr, value); 305 | break; 306 | case 1: 307 | mbc1Handler(addr, value); 308 | break; 309 | case 2: 310 | mbc2Handler(addr, value); 311 | break; 312 | case 3: 313 | mbc3Handler(addr, value); 314 | break; 315 | default: 316 | assert(false); 317 | break; 318 | } 319 | } 320 | 321 | u32 hash(u32 key) { 322 | key = ~key + (key << 15); // key = (key << 15) - key - 1; 323 | key = key ^ (key >> 12); 324 | key = key + (key << 2); 325 | key = key ^ (key >> 4); 326 | key = key * 2057; // key = (key + (key << 3)) + (key << 11); 327 | key = key ^ (key >> 16); 328 | return key; 329 | } 330 | 331 | 332 | #define CACHE_ENTRY_SIZE_LG2 9 333 | #define N_CACHE_ENTRIES_LG2 6 334 | extern FILE* fp; 335 | u8 romBankCache[(1 << CACHE_ENTRY_SIZE_LG2) * (1 << N_CACHE_ENTRIES_LG2)]; 336 | u32 blockIDs[(1<> CACHE_ENTRY_SIZE_LG2; // block index (relative to start of cartridge) 344 | // u32 tableSlot = hash(block) & ((1 << N_CACHE_ENTRIES_LG2) - 1); 345 | // assert(tableSlot < (1 << N_CACHE_ENTRIES_LG2)); 346 | // 347 | // if(blockIDs[tableSlot] == block) { 348 | // // yeet 349 | // return romBankCache[tableSlot * (1 << CACHE_ENTRY_SIZE_LG2) + (fileAddress & ((1 << CACHE_ENTRY_SIZE_LG2) - 1))]; 350 | // } else { 351 | // fseek(fp, (block << CACHE_ENTRY_SIZE_LG2), SEEK_SET); 352 | // fread(romBankCache + (tableSlot * (1<> 8), addr + (u16)1); 370 | } 371 | 372 | // read byte from memory 373 | u8 readByte(u16 addr) { 374 | switch(addr & 0xf000) { 375 | case 0x0000: // either BIOS or ROM 0: 376 | if(globalMemState.inBios) { 377 | if(addr < 0x100) { 378 | return bios[addr]; 379 | } else if(addr == 0x100) { 380 | printf("EXIT BIOS ERROR\n"); 381 | assert(false); 382 | } else { 383 | //return pkmn_red_rom_0[addr]; 384 | return globalMemState.rom0[addr]; // todo <- change this for stm32 385 | } 386 | } else { 387 | //return pkmn_red_rom_0[addr]; 388 | return globalMemState.rom0[addr]; // todo <- change this for stm32 389 | } 390 | 391 | case 0x1000: // ROM 0 392 | case 0x2000: // ROM 0 393 | case 0x3000: // ROM 0 394 | //return pkmn_red_rom_0[addr]; 395 | return globalMemState.rom0[addr]; // todo <- change this for stm32 396 | 397 | case 0x4000: // banked ROM 398 | case 0x5000: 399 | case 0x6000: 400 | case 0x7000: 401 | return readByteRomBank(addr, globalMemState.romBank); 402 | //return globalMemState.mappedRom[addr & 0x3fff]; // todo <- change this for stm32 403 | 404 | case 0x8000: // VRAM 405 | case 0x9000: 406 | return globalMemState.vram[addr & 0x1fff]; 407 | 408 | case 0xa000: // mapped RAM 409 | case 0xb000: 410 | if(!globalMemState.mappedRam) { 411 | #ifndef DANGER_MODE 412 | assert(false); 413 | #endif 414 | return 0xff; 415 | } 416 | return globalMemState.mappedRam[addr & 0x1fff]; 417 | 418 | case 0xc000: // internal RAM 419 | case 0xd000: 420 | return globalMemState.internalRam[addr & 0x1fff]; 421 | 422 | case 0xe000: // interal RAM copy 423 | return globalMemState.internalRam[addr & 0x1fff]; 424 | 425 | case 0xf000: // either internal RAM copy or I/O or top-ram 426 | switch(addr & 0x0f00) { 427 | case 0x000: 428 | case 0x100: 429 | case 0x200: 430 | case 0x300: 431 | case 0x400: 432 | case 0x500: 433 | case 0x600: 434 | case 0x700: 435 | case 0x800: 436 | case 0x900: 437 | case 0xa00: 438 | case 0xb00: 439 | case 0xc00: 440 | case 0xd00: 441 | case 0xe00: 442 | return globalMemState.internalRam[addr & 0x1fff]; 443 | 444 | 445 | case 0xf00: 446 | if(addr >= 0xff80) { 447 | return globalMemState.upperRam[addr & 0x7f]; 448 | } else { 449 | u8 lowAddr = (u8)(addr & 0xff); 450 | switch(lowAddr) { 451 | case IO_LY: 452 | case IO_SCROLLX: 453 | case IO_SCROLLY: 454 | case IO_NR10: // nyi 455 | case IO_NR11: // nyi 456 | case IO_NR12: // nyi 457 | case IO_NR13: // nyi 458 | case IO_NR14: // nyi 459 | case IO_NR21: // nyi 460 | case IO_NR22: // nyi 461 | case IO_NR23: // nyi 462 | case IO_NR24: // nyi 463 | case IO_NR30: // nyi 464 | case IO_NR31: // nyi 465 | case IO_NR32: // nyi 466 | case IO_NR33: // nyi 467 | case IO_NR34: // nyi 468 | case IO_NR41: // nyi 469 | case IO_NR42: // nyi 470 | case IO_NR43: // nyi 471 | case IO_NR44: // nyi 472 | case IO_NR50: // nyi 473 | case IO_NR51: // nyi 474 | case IO_NR52: // nyi 475 | case IO_STAT: // nyi 476 | case IO_WAVE_PATTERN: // nyi 477 | case IO_LCDC: // nyi 478 | case IO_BGP: 479 | case IO_OBP0: 480 | case IO_OBP1: 481 | case IO_SERIAL_SB: 482 | case IO_SERIAL_SC: 483 | case IO_DIV: 484 | case IO_TIMA: 485 | case IO_TMA: 486 | case IO_TAC: 487 | case IO_WINY: 488 | case IO_WINX: 489 | return globalMemState.ioRegs[lowAddr]; 490 | break; 491 | 492 | case IO_IF: 493 | printf("read if: 0x%x\n", globalMemState.ioRegs[lowAddr]); 494 | printf("timer value: 0x%x\n", globalMemState.ioRegs[IO_TIMA]); 495 | return globalMemState.ioRegs[lowAddr]; 496 | break; 497 | 498 | 499 | case IO_P1: 500 | { 501 | u8 regP1 = globalMemState.ioRegs[IO_P1]; 502 | //printf("ireg: 0x%x\n", regP1); 503 | u8 joypad_data = 0; 504 | if(regP1 & 0x10) { 505 | if(keyboard.a) joypad_data += 0x1; 506 | if(keyboard.b) joypad_data += 0x2; 507 | if(keyboard.select) joypad_data += 0x4; 508 | if(keyboard.start) joypad_data += 0x8; 509 | } 510 | if(regP1 & 0x20) { 511 | if(keyboard.r) joypad_data += 0x1; 512 | if(keyboard.l) joypad_data += 0x2; 513 | if(keyboard.u) joypad_data += 0x4; 514 | if(keyboard.d) joypad_data += 0x8; 515 | } 516 | 517 | regP1 = (regP1 & 0xf0); 518 | joypad_data = ~joypad_data; 519 | joypad_data = regP1 + (joypad_data & 0xf); 520 | //globalMemState.ioRegs[IO_P1] = joypad_data; 521 | //printf("jpd: 0x%x\n", joypad_data); 522 | return joypad_data; 523 | } 524 | 525 | break; 526 | 527 | case IO_GBCSPEED: 528 | return 0xff; 529 | 530 | case IO_LYC: 531 | case IO_DMA: 532 | 533 | printf("unhandled I/O read @ 0x%x\n", addr); 534 | #ifndef DANGER_MODE 535 | assert(false); 536 | #endif 537 | break; 538 | default: 539 | printf("unknown I/O read @ 0x%x\n", addr); 540 | #ifndef DANGER_MODE 541 | assert(false); 542 | #endif 543 | break; 544 | } 545 | 546 | } 547 | default: 548 | #ifndef DANGER_MODE 549 | assert(false); 550 | #endif 551 | break; 552 | 553 | } 554 | break; 555 | 556 | default: 557 | #ifndef DANGER_MODE 558 | assert(false); 559 | #endif 560 | break; 561 | } 562 | assert(false); 563 | return 0xff; 564 | } 565 | 566 | 567 | void writeByte(u8 byte, u16 addr) { 568 | switch(addr & 0xf000) { 569 | case 0x0000: // ROM 0, but possibly the BIOS area 570 | if(globalMemState.inBios) { 571 | printf("ERROR: tried to write into ROM0 or BIOS (@ 0x%04x) during BIOS!\n", addr); 572 | #ifndef DANGER_MODE 573 | assert(false); 574 | #endif 575 | } else { 576 | mbcHandler(addr, byte); 577 | } 578 | break; 579 | 580 | 581 | case 0x1000: // ROM 0 582 | case 0x2000: // ROM 0 583 | case 0x3000: // ROM 0 584 | case 0x4000: // ROM 1 585 | case 0x5000: // ROM 1 586 | case 0x6000: // ROM 1 587 | case 0x7000: // ROM 1 588 | mbcHandler(addr, byte); 589 | break; 590 | 591 | case 0x8000: // VRAM 592 | case 0x9000: 593 | globalMemState.vram[addr & 0x1fff] = byte; 594 | break; 595 | 596 | case 0xa000: // mapped RAM 597 | case 0xb000: 598 | if(!globalMemState.mappedRam) { 599 | //printf("write to unmapped ram @ 0x%x value 0x%x\n", addr, byte); 600 | //#ifndef DANGER_MODE 601 | // assert(false); 602 | //#endif 603 | break; 604 | } 605 | globalMemState.mappedRam[addr & 0x1fff] = byte; 606 | break; 607 | 608 | case 0xc000: // internal RAM 609 | case 0xd000: 610 | globalMemState.internalRam[addr & 0x1fff] = byte; 611 | break; 612 | 613 | case 0xe000: // interal RAM copy 614 | globalMemState.internalRam[addr & 0x1fff] = byte; 615 | break; 616 | 617 | case 0xf000: // either internal RAM copy or I/O or top-ram 618 | switch(addr & 0x0f00) { 619 | case 0x000: 620 | case 0x100: 621 | case 0x200: 622 | case 0x300: 623 | case 0x400: 624 | case 0x500: 625 | case 0x600: 626 | case 0x700: 627 | case 0x800: 628 | case 0x900: 629 | case 0xa00: 630 | case 0xb00: 631 | case 0xc00: 632 | case 0xd00: 633 | case 0xe00: 634 | globalMemState.internalRam[addr & 0x1fff] = byte; 635 | break; 636 | 637 | 638 | case 0xf00: 639 | if(addr >= 0xff80) { 640 | globalMemState.upperRam[addr & 0x7f] = byte; 641 | break; 642 | } else { 643 | u16 maskedAddress = addr & 0x7f; 644 | globalMemState.ioRegs[maskedAddress] = byte; 645 | u8 lowAddr = (u8)(addr & 0xff); 646 | switch(lowAddr) { 647 | 648 | case IO_NR11: 649 | ch1_length_load(); 650 | break; 651 | case IO_NR14: 652 | ch1_trigger_load(); 653 | break; 654 | 655 | case IO_NR13: 656 | ch1_update_freq(); 657 | break; 658 | 659 | case IO_NR21: 660 | ch2_length_load(); 661 | break; 662 | 663 | case IO_NR23: 664 | ch2_update_freq(); 665 | break; 666 | 667 | case IO_NR24: 668 | ch2_trigger_load(); 669 | break; 670 | 671 | case IO_NR31: 672 | ch3_length_load(); 673 | break; 674 | case IO_NR33: 675 | ch3_update_freq(); 676 | break; 677 | case IO_NR34: 678 | ch3_trigger_load(); 679 | break; 680 | 681 | case IO_NR41: 682 | ch4_length_load(); 683 | break; 684 | case IO_NR44: 685 | ch4_trigger_load(); 686 | 687 | case IO_NR12: 688 | case IO_NR10: 689 | 690 | case IO_NR22: 691 | 692 | 693 | case IO_NR30: 694 | 695 | case IO_NR32: 696 | 697 | 698 | 699 | case IO_NR42: 700 | case IO_NR43: 701 | case IO_NR50: 702 | case IO_NR51: 703 | case IO_NR52: 704 | // printf("snd 0x%x 0x%x\r\n", addr, byte); 705 | // break; 706 | 707 | 708 | case IO_WAVE_PATTERN: 709 | case 0x31: 710 | case 0x32: 711 | case 0x33: 712 | case 0x34: 713 | case 0x35: 714 | case 0x36: 715 | case 0x37: 716 | case 0x38: 717 | case 0x39: 718 | case 0x3a: 719 | case 0x3b: 720 | case 0x3c: 721 | case 0x3d: 722 | case 0x3e: 723 | case 0x3f: 724 | case IO_BGP: 725 | case IO_SCROLLX: 726 | case IO_SCROLLY: 727 | case IO_LCDC: 728 | case IO_STAT: 729 | case IO_OBP0: 730 | case IO_OBP1: 731 | case IO_P1: 732 | case IO_IF: 733 | case IO_TAC: 734 | case IO_TIMA: 735 | case IO_TMA: 736 | case IO_SERIAL_SB: 737 | case IO_SERIAL_SC: 738 | case IO_WINY: 739 | case IO_WINX: 740 | case IO_LYC: 741 | break; 742 | 743 | case IO_EXIT_BIOS: 744 | if(globalMemState.inBios) { 745 | printf("EXIT BIOS by write 0x%x to 0x%x", byte, addr); 746 | globalMemState.inBios = false; 747 | break; 748 | } else { 749 | printf("tried to write to 0xff50 when not in bios?\n"); 750 | break; 751 | } 752 | break; 753 | 754 | case IO_DMA: 755 | { 756 | u16 dmaAddr = ((u16)byte) << 8; 757 | for(u16 i = 0; i < 160; i++) { 758 | writeByte(readByte(dmaAddr + i), (u16)0xfe00 + i); 759 | } 760 | break; 761 | } 762 | 763 | case 0x7f: 764 | printf("OOPS\n"); 765 | break; 766 | 767 | 768 | case IO_LY: 769 | printf("unhandled I/O write @ 0x%x\n", addr); 770 | #ifndef DANGER_MODE 771 | assert(false); 772 | #endif 773 | break; 774 | default: 775 | printf("unknown I/O write @ 0x%x\n", addr); 776 | #ifndef DANGER_MODE 777 | assert(false); 778 | #endif 779 | break; 780 | } 781 | break; 782 | } 783 | default: 784 | #ifndef DANGER_MODE 785 | assert(false); 786 | #endif 787 | break; 788 | } 789 | break; 790 | default: 791 | #ifndef DANGER_MODE 792 | assert(false); 793 | #endif 794 | break; 795 | } 796 | } 797 | -------------------------------------------------------------------------------- /wormboy/cpu.cpp: -------------------------------------------------------------------------------- 1 | // Implementation of OPCODES and cpu step function 2 | 3 | #define NDEBUG 4 | #include 5 | 6 | #include "cpu.h" 7 | #include "mem.h" 8 | 9 | 10 | // cpu registers, halted state, and timers 11 | CpuState globalState; 12 | 13 | typedef void OpcodeHandler(u8 opcode); 14 | 15 | // the GB has several invalid opcodes 16 | void invHandler(u8 opcode) { 17 | printf("got invalid opcode 0x%x\n", opcode); 18 | assert(false); 19 | } 20 | 21 | //////////////////////////////// 22 | // CB Opcode Helper Functions // 23 | //////////////////////////////// 24 | 25 | u8 rlcReg(u8 value, bool isA) { 26 | u8 result = value; 27 | clearAllFlags(); 28 | if((result & 0x80) != 0) { 29 | setCarryFlag(); 30 | result <<= 1; 31 | result |= 0x1; 32 | } else { 33 | result <<= 1; 34 | } 35 | if(!isA) { 36 | if((u8)result == 0) { 37 | setZeroFlag(); 38 | } 39 | } 40 | return result; 41 | } 42 | 43 | u8 rlReg(u8 value, bool isA) { 44 | u8 carry = getCarryFlag() ? (u8)1 : (u8)0; 45 | u8 result = value; 46 | clearAllFlags(); 47 | if((result & 0x80) != 0) { 48 | setCarryFlag(); 49 | } 50 | result <<= 1; 51 | result |= carry; 52 | if(!isA) { 53 | if((u8)result == 0) { 54 | setZeroFlag(); 55 | } 56 | } 57 | return result; 58 | } 59 | 60 | u8 rrc(u8 value, bool isA) { 61 | u8 result = value; 62 | clearAllFlags(); 63 | if((result & 1) != 0) { 64 | setCarryFlag(); 65 | result >>= 1; 66 | result |= 0x80; 67 | } else { 68 | result >>= 1; 69 | } 70 | if(!isA) { 71 | if((u8)result == 0) { 72 | setZeroFlag(); 73 | } 74 | } 75 | return result; 76 | } 77 | 78 | u8 rr(u8 value, bool isA) { 79 | u8 carry = getCarryFlag() ? (u8)0x80 : (u8)0x00; 80 | u8 result = value; 81 | clearAllFlags(); 82 | if((result & 1) != 0) { 83 | setCarryFlag(); 84 | } 85 | result >>= 1; 86 | result |= carry; 87 | if(!isA) { 88 | if((u8)result == 0) { 89 | setZeroFlag(); 90 | } 91 | } 92 | return result; 93 | } 94 | 95 | u8 srl(u8 value) { 96 | u8 result = value; 97 | clearAllFlags(); 98 | if(result & 1) { 99 | setCarryFlag(); 100 | } 101 | result >>= 1; 102 | if(result == 0) { 103 | setZeroFlag(); 104 | } 105 | return result; 106 | } 107 | 108 | u8 sla(u8 value) { 109 | clearAllFlags(); 110 | if(value & 0x80) { 111 | setCarryFlag(); 112 | } 113 | u8 result = value << 1; 114 | if(result == 0) { 115 | setZeroFlag(); 116 | } 117 | return result; 118 | } 119 | 120 | u8 sra(u8 value) { 121 | u8 result = value; 122 | clearAllFlags(); 123 | if(result & 1) { 124 | setCarryFlag(); 125 | } 126 | if((result & 0x80)) { 127 | result >>= 1; 128 | result |= 0x80; 129 | } else { 130 | result >>= 1; 131 | } 132 | if(result == 0) { 133 | setZeroFlag(); 134 | } 135 | return result; 136 | } 137 | 138 | u8 swapRegister(u8 value) { 139 | u8 low = value & 0xf; 140 | u8 hi = (value >> 4) & 0xf; 141 | u8 result = (low << 4) + hi; 142 | clearAllFlags(); 143 | if((u8)result == 0) { 144 | setZeroFlag(); 145 | } 146 | return result; 147 | } 148 | 149 | 150 | //////////////////////////////// 151 | // CB Opcodes // 152 | //////////////////////////////// 153 | 154 | void SLA_A(u8 opcode) { // 0x27 155 | globalState.pc++; 156 | globalState.cycleCount += 8; 157 | globalState.a = sla(globalState.a); 158 | } 159 | 160 | void SLA_B(u8 opcode) { // 0x20 161 | globalState.pc++; 162 | globalState.cycleCount += 8; 163 | globalState.bc.hi = sla(globalState.bc.hi); 164 | } 165 | 166 | void SLA_C(u8 opcode) { // 0x21 167 | globalState.pc++; 168 | globalState.cycleCount += 8; 169 | globalState.bc.lo = sla(globalState.bc.lo); 170 | } 171 | 172 | void SLA_D(u8 opcode) { // 0x22 173 | globalState.pc++; 174 | globalState.cycleCount += 8; 175 | globalState.de.hi = sla(globalState.de.hi); 176 | } 177 | 178 | void SLA_E(u8 opcode) { // 0x23 179 | globalState.pc++; 180 | globalState.cycleCount += 8; 181 | globalState.de.lo = sla(globalState.de.lo); 182 | } 183 | 184 | void SLA_H(u8 opcode) { // 0x24 185 | globalState.pc++; 186 | globalState.cycleCount += 8; 187 | globalState.hl.hi = sla(globalState.hl.hi); 188 | } 189 | 190 | void SLA_L(u8 opcode) { // 0x25 191 | globalState.pc++; 192 | globalState.cycleCount += 8; 193 | globalState.hl.lo = sla(globalState.hl.lo); 194 | } 195 | 196 | void SLA_DHL(u8 opcode) { // 0x26 197 | globalState.pc++; 198 | globalState.cycleCount += 16; 199 | writeByte(sla(readByte(globalState.hl.v)), globalState.hl.v); 200 | } 201 | 202 | void SRA_A(u8 opcode) { // 0x2f 203 | globalState.pc++; 204 | globalState.cycleCount += 8; 205 | globalState.a = sra(globalState.a); 206 | } 207 | 208 | void SRA_B(u8 opcode) { // 0x28 209 | globalState.pc++; 210 | globalState.cycleCount += 8; 211 | globalState.bc.hi = sra(globalState.bc.hi); 212 | } 213 | 214 | void SRA_C(u8 opcode) { // 0x29 215 | globalState.pc++; 216 | globalState.cycleCount += 8; 217 | globalState.bc.lo = sra(globalState.bc.lo); 218 | } 219 | 220 | void SRA_D(u8 opcode) { // 0x2a 221 | globalState.pc++; 222 | globalState.cycleCount += 8; 223 | globalState.de.hi = sra(globalState.de.hi); 224 | } 225 | 226 | void SRA_E(u8 opcode) { // 0x2b 227 | globalState.pc++; 228 | globalState.cycleCount += 8; 229 | globalState.de.lo = sra(globalState.de.lo); 230 | } 231 | 232 | void SRA_H(u8 opcode) { // 0x2c 233 | globalState.pc++; 234 | globalState.cycleCount += 8; 235 | globalState.hl.hi = sra(globalState.hl.hi); 236 | } 237 | 238 | void SRA_L(u8 opcode) { // 0x2d 239 | globalState.pc++; 240 | globalState.cycleCount += 8; 241 | globalState.hl.lo = sra(globalState.hl.lo); 242 | } 243 | 244 | void SRA_DHL(u8 opcode) { // 0x2e 245 | globalState.pc++; 246 | globalState.cycleCount += 16; 247 | writeByte(sra(readByte(globalState.hl.v)), globalState.hl.v); 248 | } 249 | 250 | 251 | void SRL_A(u8 opcode) { // 0x3f 252 | globalState.pc++; 253 | globalState.cycleCount += 8; 254 | globalState.a = srl(globalState.a); 255 | } 256 | 257 | void SRL_B(u8 opcode) { // 0x38 258 | globalState.pc++; 259 | globalState.cycleCount += 8; 260 | globalState.bc.hi = srl(globalState.bc.hi); 261 | } 262 | 263 | void SRL_C(u8 opcode) { // 0x39 264 | globalState.pc++; 265 | globalState.cycleCount += 8; 266 | globalState.bc.lo = srl(globalState.bc.lo); 267 | } 268 | 269 | void SRL_D(u8 opcode) { // 0x3a 270 | globalState.pc++; 271 | globalState.cycleCount += 8; 272 | globalState.de.hi = srl(globalState.de.hi); 273 | } 274 | 275 | void SRL_E(u8 opcode) { // 0x3b 276 | globalState.pc++; 277 | globalState.cycleCount += 8; 278 | globalState.de.lo = srl(globalState.de.lo); 279 | } 280 | 281 | void SRL_H(u8 opcode) { // 0x3c 282 | globalState.pc++; 283 | globalState.cycleCount += 8; 284 | globalState.hl.hi = srl(globalState.hl.hi); 285 | } 286 | 287 | void SRL_L(u8 opcode) { // 0x3d 288 | globalState.pc++; 289 | globalState.cycleCount += 8; 290 | globalState.hl.lo = srl(globalState.hl.lo); 291 | } 292 | 293 | void SRL_DHL(u8 opcode) { // 0x3e 294 | globalState.pc++; 295 | globalState.cycleCount += 16; 296 | writeByte(srl(readByte(globalState.hl.v)), globalState.hl.v); 297 | } 298 | 299 | void RR_A(u8 opcode) { // 0x1f 300 | globalState.pc++; 301 | globalState.cycleCount += 8; 302 | globalState.a = rr(globalState.a, false); 303 | } 304 | 305 | void RR_B(u8 opcode) { // 0x18 306 | globalState.pc++; 307 | globalState.cycleCount += 8; 308 | globalState.bc.hi = rr(globalState.bc.hi, false); 309 | } 310 | 311 | void RR_C(u8 opcode) { // 0x19 312 | globalState.pc++; 313 | globalState.cycleCount += 8; 314 | globalState.bc.lo = rr(globalState.bc.lo, false); 315 | } 316 | 317 | void RR_D(u8 opcode) { // 0x1a 318 | globalState.pc++; 319 | globalState.cycleCount += 8; 320 | globalState.de.hi = rr(globalState.de.hi, false); 321 | } 322 | 323 | void RR_E(u8 opcode) { // 0x1b 324 | globalState.pc++; 325 | globalState.cycleCount += 8; 326 | globalState.de.lo = rr(globalState.de.lo, false); 327 | } 328 | 329 | void RR_H(u8 opcode) { // 0x1c 330 | globalState.pc++; 331 | globalState.cycleCount += 8; 332 | globalState.hl.hi = rr(globalState.hl.hi, false); 333 | } 334 | 335 | void RR_L(u8 opcode) { // 0x1d 336 | globalState.pc++; 337 | globalState.cycleCount += 8; 338 | globalState.hl.lo = rr(globalState.hl.lo, false); 339 | } 340 | 341 | void RR_DHL(u8 opcode) { // 0x1e 342 | globalState.pc++; 343 | globalState.cycleCount += 16; 344 | writeByte(rr(readByte(globalState.hl.v), false), globalState.hl.v); 345 | } 346 | 347 | void RL_A(u8 opcode) { // 0x17 348 | globalState.pc++; 349 | globalState.cycleCount += 8; 350 | globalState.a = rlReg(globalState.a, false); 351 | } 352 | 353 | void RL_B(u8 opcode) { // 0x10 354 | globalState.pc++; 355 | globalState.cycleCount += 8; 356 | globalState.bc.hi = rlReg(globalState.bc.hi, false); 357 | } 358 | 359 | void RL_C(u8 opcode) { // 0x11 360 | globalState.pc++; 361 | globalState.cycleCount += 8; 362 | globalState.bc.lo = rlReg(globalState.bc.lo, false); 363 | } 364 | 365 | void RL_D(u8 opcode) { // 0x12 366 | globalState.pc++; 367 | globalState.cycleCount += 8; 368 | globalState.de.hi = rlReg(globalState.de.hi, false); 369 | } 370 | 371 | void RL_E(u8 opcode) { // 0x13 372 | globalState.pc++; 373 | globalState.cycleCount += 8; 374 | globalState.de.lo = rlReg(globalState.de.lo, false); 375 | } 376 | 377 | void RL_H(u8 opcode) { // 0x14 378 | globalState.pc++; 379 | globalState.cycleCount += 8; 380 | globalState.hl.hi = rlReg(globalState.hl.hi, false); 381 | } 382 | 383 | void RL_L(u8 opcode) { // 0x15 384 | globalState.pc++; 385 | globalState.cycleCount += 8; 386 | globalState.hl.lo = rlReg(globalState.hl.lo, false); 387 | } 388 | 389 | void RL_DHL(u8 opcode) { // 0x16 390 | globalState.pc++; 391 | globalState.cycleCount += 16; 392 | writeByte(rlReg(readByte(globalState.hl.v), false), globalState.hl.v); 393 | } 394 | 395 | void SWAP_A(u8 opcode) { // 37 396 | globalState.pc++; 397 | globalState.cycleCount += 8; 398 | globalState.a = swapRegister(globalState.a); 399 | } 400 | 401 | void SWAP_B(u8 opcode) { // 30 402 | globalState.pc++; 403 | globalState.cycleCount += 8; 404 | globalState.bc.hi = swapRegister(globalState.bc.hi); 405 | } 406 | 407 | void SWAP_C(u8 opcode) { // 31 408 | globalState.pc++; 409 | globalState.cycleCount += 8; 410 | globalState.bc.lo = swapRegister(globalState.bc.lo); 411 | } 412 | 413 | void SWAP_D(u8 opcode) { // 32 414 | globalState.pc++; 415 | globalState.cycleCount += 8; 416 | globalState.de.hi = swapRegister(globalState.de.hi); 417 | } 418 | 419 | void SWAP_E(u8 opcode) { // 33 420 | globalState.pc++; 421 | globalState.cycleCount += 8; 422 | globalState.de.lo = swapRegister(globalState.de.lo); 423 | } 424 | 425 | void SWAP_H(u8 opcode) { // 34 426 | globalState.pc++; 427 | globalState.cycleCount += 8; 428 | globalState.hl.hi = swapRegister(globalState.hl.hi); 429 | } 430 | 431 | void SWAP_L(u8 opcode) { // 35 432 | globalState.pc++; 433 | globalState.cycleCount += 8; 434 | globalState.hl.lo = swapRegister(globalState.hl.lo); 435 | } 436 | 437 | void SWAP_DHL(u8 opcode) { // 36 438 | globalState.pc++; 439 | globalState.cycleCount += 16; 440 | writeByte(swapRegister(readByte(globalState.hl.v)), globalState.hl.v); 441 | } 442 | 443 | void RLC_A(u8 opcode) { // 07 444 | globalState.pc++; 445 | globalState.cycleCount += 8; 446 | globalState.a = rlcReg(globalState.a, false); 447 | } 448 | 449 | void RLC_B(u8 opcode) { // 00 450 | globalState.pc++; 451 | globalState.cycleCount += 8; 452 | globalState.bc.hi = rlcReg(globalState.bc.hi, false); 453 | } 454 | 455 | void RLC_C(u8 opcode) { // 01 456 | globalState.pc++; 457 | globalState.cycleCount += 8; 458 | globalState.bc.lo = rlcReg(globalState.bc.lo, false); 459 | } 460 | 461 | void RLC_D(u8 opcode) { // 02 462 | globalState.pc++; 463 | globalState.cycleCount += 8; 464 | globalState.de.hi = rlcReg(globalState.de.hi, false); 465 | } 466 | 467 | void RLC_E(u8 opcode) { // 03 468 | globalState.pc++; 469 | globalState.cycleCount += 8; 470 | globalState.de.lo = rlcReg(globalState.de.lo, false); 471 | } 472 | 473 | void RLC_H(u8 opcode) { // 04 474 | globalState.pc++; 475 | globalState.cycleCount += 8; 476 | globalState.hl.hi = rlcReg(globalState.hl.hi, false); 477 | } 478 | 479 | void RLC_L(u8 opcode) { // 05 480 | globalState.pc++; 481 | globalState.cycleCount += 8; 482 | globalState.hl.lo = rlcReg(globalState.hl.lo, false); 483 | } 484 | 485 | void RLC_DHL(u8 opcode) { // 06 486 | globalState.pc++; 487 | globalState.cycleCount += 16; 488 | u8 result = rlcReg(readByte(globalState.hl.v), false); 489 | writeByte(result, globalState.hl.v); 490 | } 491 | 492 | 493 | void RRC_A(u8 opcode) { // 0f 494 | globalState.pc++; 495 | globalState.cycleCount += 8; 496 | globalState.a = rrc(globalState.a, false); 497 | } 498 | 499 | void RRC_B(u8 opcode) { // 08 500 | globalState.pc++; 501 | globalState.cycleCount += 8; 502 | globalState.bc.hi = rrc(globalState.bc.hi, false); 503 | } 504 | 505 | void RRC_C(u8 opcode) { // 09 506 | globalState.pc++; 507 | globalState.cycleCount += 8; 508 | globalState.bc.lo = rrc(globalState.bc.lo, false); 509 | } 510 | 511 | void RRC_D(u8 opcode) { // 0a 512 | globalState.pc++; 513 | globalState.cycleCount += 8; 514 | globalState.de.hi = rrc(globalState.de.hi, false); 515 | } 516 | 517 | void RRC_E(u8 opcode) { // 0b 518 | globalState.pc++; 519 | globalState.cycleCount += 8; 520 | globalState.de.lo = rrc(globalState.de.lo, false); 521 | } 522 | 523 | void RRC_H(u8 opcode) { // 0c 524 | globalState.pc++; 525 | globalState.cycleCount += 8; 526 | globalState.hl.hi = rrc(globalState.hl.hi, false); 527 | } 528 | 529 | void RRC_L(u8 opcode) { // 0d 530 | globalState.pc++; 531 | globalState.cycleCount += 8; 532 | globalState.hl.lo = rrc(globalState.hl.lo, false); 533 | } 534 | 535 | void RRC_DHL(u8 opcode) { // 0e 536 | globalState.pc++; 537 | globalState.cycleCount += 16; 538 | u8 result = rrc(readByte(globalState.hl.v), false); 539 | writeByte(result, globalState.hl.v); 540 | } 541 | 542 | 543 | void bit_B_set(u8 opcode) { 544 | u8 bitID = (opcode - (u8)0xC0) >> 3; 545 | assert(bitID < 8); 546 | globalState.bc.hi |= (1 << bitID); 547 | globalState.pc += 1; 548 | globalState.cycleCount += 8; 549 | } 550 | 551 | void bit_C_set(u8 opcode) { 552 | u8 bitID = (opcode - (u8)0xC1) >> 3; 553 | assert(bitID < 8); 554 | globalState.bc.lo |= (1 << bitID); 555 | globalState.pc += 1; 556 | globalState.cycleCount += 8; 557 | } 558 | 559 | void bit_D_set(u8 opcode) { 560 | u8 bitID = (opcode - (u8)0xC2) >> 3; 561 | assert(bitID < 8); 562 | globalState.de.hi |= (1 << bitID); 563 | globalState.pc += 1; 564 | globalState.cycleCount += 8; 565 | } 566 | 567 | void bit_E_set(u8 opcode) { 568 | u8 bitID = (opcode - (u8)0xC3) >> 3; 569 | assert(bitID < 8); 570 | globalState.de.lo |= (1 << bitID); 571 | globalState.pc += 1; 572 | globalState.cycleCount += 8; 573 | } 574 | 575 | void bit_H_set(u8 opcode) { 576 | u8 bitID = (opcode - (u8)0xC4) >> 3; 577 | assert(bitID < 8); 578 | globalState.hl.hi |= (1 << bitID); 579 | globalState.pc += 1; 580 | globalState.cycleCount += 8; 581 | } 582 | 583 | void bit_L_set(u8 opcode) { 584 | u8 bitID = (opcode - (u8)0xC5) >> 3; 585 | assert(bitID < 8); 586 | globalState.hl.lo |= (1 << bitID); 587 | globalState.pc += 1; 588 | globalState.cycleCount += 8; 589 | } 590 | 591 | void bit_DHL_set(u8 opcode) { 592 | u8 bitID = (opcode - (u8)0xC6) >> 3; 593 | assert(bitID < 8); 594 | u8 value = readByte(globalState.hl.v); 595 | value |= (1 << bitID); 596 | writeByte(value, globalState.hl.v); 597 | globalState.pc += 1; 598 | globalState.cycleCount += 16; 599 | } 600 | 601 | void bit_A_set(u8 opcode) { 602 | u8 bitID = (opcode - (u8)0xC7) >> 3; 603 | assert(bitID < 8); 604 | globalState.a |= (1 << bitID); 605 | globalState.pc += 1; 606 | globalState.cycleCount += 8; 607 | } 608 | 609 | void bit_B_res(u8 opcode) { 610 | u8 bitID = (opcode - (u8)0x80) >> 3; 611 | assert(bitID < 8); 612 | globalState.bc.hi &= ~(1 << bitID); 613 | globalState.pc += 1; 614 | globalState.cycleCount += 8; 615 | } 616 | 617 | void bit_C_res(u8 opcode) { 618 | u8 bitID = (opcode - (u8)0x81) >> 3; 619 | assert(bitID < 8); 620 | globalState.bc.lo &= ~(1 << bitID); 621 | globalState.pc += 1; 622 | globalState.cycleCount += 8; 623 | } 624 | 625 | void bit_D_res(u8 opcode) { 626 | u8 bitID = (opcode - (u8)0x82) >> 3; 627 | assert(bitID < 8); 628 | globalState.de.hi &= ~(1 << bitID); 629 | globalState.pc += 1; 630 | globalState.cycleCount += 8; 631 | } 632 | 633 | void bit_E_res(u8 opcode) { 634 | u8 bitID = (opcode - (u8)0x83) >> 3; 635 | assert(bitID < 8); 636 | globalState.de.lo &= ~(1 << bitID); 637 | globalState.pc += 1; 638 | globalState.cycleCount += 8; 639 | } 640 | 641 | void bit_H_res(u8 opcode) { 642 | u8 bitID = (opcode - (u8)0x84) >> 3; 643 | assert(bitID < 8); 644 | globalState.hl.hi &= ~(1 << bitID); 645 | globalState.pc += 1; 646 | globalState.cycleCount += 8; 647 | } 648 | 649 | void bit_L_res(u8 opcode) { 650 | u8 bitID = (opcode - (u8)0x85) >> 3; 651 | assert(bitID < 8); 652 | globalState.hl.lo &= ~(1 << bitID); 653 | globalState.pc += 1; 654 | globalState.cycleCount += 8; 655 | } 656 | 657 | void bit_DHL_res(u8 opcode) { 658 | u8 bitID = (opcode - (u8)0x86) >> 3; 659 | assert(bitID < 8); 660 | u8 value = readByte(globalState.hl.v); 661 | value &= ~(1 << bitID); 662 | writeByte(value, globalState.hl.v); 663 | globalState.pc += 1; 664 | globalState.cycleCount += 16; 665 | } 666 | 667 | void bit_A_res(u8 opcode) { 668 | u8 bitID = (opcode - (u8)0x87) >> 3; 669 | assert(bitID < 8); 670 | globalState.a &= ~(1 << bitID); 671 | globalState.pc += 1; 672 | globalState.cycleCount += 8; 673 | } 674 | 675 | void bit_A_test(u8 opcode) { 676 | u8 bitID = (opcode - (u8)0x47) >> 3; 677 | assert(bitID < 8); 678 | //printf("check bit %d of A\n", bitID); 679 | u8 val = globalState.a; 680 | if(((val >> bitID) & 1) == 0) { 681 | setZeroFlag(); 682 | } else { 683 | clearZeroFlag(); 684 | } 685 | setHalfCarryFlag(); 686 | clearSubtractFlag(); 687 | globalState.pc += 1; 688 | globalState.cycleCount += 8; 689 | } 690 | 691 | void bit_B_test(u8 opcode) { 692 | u8 bitID = (opcode - (u8)0x40) >> 3; 693 | //printf("B opcode 0x%x bitId %d\n", opcode, bitID); 694 | assert(bitID < 8); 695 | //printf("check bit %d of B\n", bitID); 696 | u8 val = globalState.bc.hi; 697 | if(((val >> bitID) & 1) == 0) { 698 | setZeroFlag(); 699 | } else { 700 | clearZeroFlag(); 701 | } 702 | setHalfCarryFlag(); 703 | clearSubtractFlag(); 704 | globalState.pc += 1; 705 | globalState.cycleCount += 8; 706 | } 707 | 708 | 709 | void bit_C_test(u8 opcode) { 710 | u8 bitID = (opcode - (u8)0x41) >> 3; 711 | assert(bitID < 8); 712 | //printf("check bit %d of C\n", bitID); 713 | u8 val = globalState.bc.lo; 714 | if(((val >> bitID) & 1) == 0) { 715 | setZeroFlag(); 716 | } else { 717 | clearZeroFlag(); 718 | } 719 | setHalfCarryFlag(); 720 | clearSubtractFlag(); 721 | globalState.pc += 1; 722 | globalState.cycleCount += 8; 723 | } 724 | 725 | void bit_D_test(u8 opcode) { 726 | u8 bitID = (opcode - (u8)0x42) >> 3; 727 | assert(bitID < 8); 728 | //printf("check bit %d of D\n", bitID); 729 | u8 val = globalState.de.hi; 730 | if(((val >> bitID) & 1) == 0) { 731 | setZeroFlag(); 732 | } else { 733 | clearZeroFlag(); 734 | } 735 | setHalfCarryFlag(); 736 | clearSubtractFlag(); 737 | globalState.pc += 1; 738 | globalState.cycleCount += 8; 739 | } 740 | 741 | void bit_E_test(u8 opcode) { 742 | u8 bitID = (opcode - (u8)0x43) >> 3; 743 | assert(bitID < 8); 744 | //printf("check bit %d of E\n", bitID); 745 | u8 val = globalState.de.lo; 746 | if(((val >> bitID) & 1) == 0) { 747 | setZeroFlag(); 748 | } else { 749 | clearZeroFlag(); 750 | } 751 | setHalfCarryFlag(); 752 | clearSubtractFlag(); 753 | globalState.pc += 1; 754 | globalState.cycleCount += 8; 755 | } 756 | 757 | void bit_H_test(u8 opcode) { 758 | u8 bitID = (opcode - (u8)0x44) >> 3; 759 | assert(bitID < 8); 760 | //printf("check bit %d of H\n", bitID); 761 | u8 val = globalState.hl.hi; 762 | if(((val >> bitID) & 1) == 0) { 763 | setZeroFlag(); 764 | } else { 765 | clearZeroFlag(); 766 | } 767 | setHalfCarryFlag(); 768 | clearSubtractFlag(); 769 | globalState.pc += 1; 770 | globalState.cycleCount += 8; 771 | } 772 | 773 | void bit_L_test(u8 opcode) { 774 | u8 bitID = (opcode - (u8)0x45) >> 3; 775 | assert(bitID < 8); 776 | //printf("check bit %d of L\n", bitID); 777 | u8 val = globalState.hl.lo; 778 | if(((val >> bitID) & 1) == 0) { 779 | setZeroFlag(); 780 | } else { 781 | clearZeroFlag(); 782 | } 783 | setHalfCarryFlag(); 784 | clearSubtractFlag(); 785 | globalState.pc += 1; 786 | globalState.cycleCount += 8; 787 | } 788 | 789 | void bit_DHL_test(u8 opcode) { 790 | u8 bitID = (opcode - (u8)0x45) >> 3; 791 | assert(bitID < 8); 792 | //printf("check bit %d of L\n", bitID); 793 | u8 val = readByte(globalState.hl.v); 794 | if(((val >> bitID) & 1) == 0) { 795 | setZeroFlag(); 796 | } else { 797 | clearZeroFlag(); 798 | } 799 | setHalfCarryFlag(); 800 | clearSubtractFlag(); 801 | globalState.pc += 1; 802 | globalState.cycleCount += 16; 803 | } 804 | 805 | // CB-prefixed opcode handler table 806 | static OpcodeHandler* opcode_cbs[256] = 807 | {RLC_B, RLC_C, RLC_D, RLC_E, RLC_H, RLC_L, RLC_DHL, RLC_A, // 0x0 - 0x7 808 | RRC_B, RRC_C, RRC_D, RRC_E, RRC_H, RRC_L, RRC_DHL, RRC_A, // 0x8 - 0xf 809 | RL_B, RL_C, RL_D, RL_E, RL_H, RL_L, RL_DHL, RL_A, // 0x10 - 0x17 810 | RR_B, RR_C, RR_D, RR_E, RR_H, RR_L, RR_DHL, RR_A, // 0x18 - 0x1f 811 | SLA_B, SLA_C, SLA_D, SLA_E, SLA_H, SLA_L, SLA_DHL, SLA_A, // 0x20 - 0x27 812 | SRA_B, SRA_C, SRA_D, SRA_E, SRA_H, SRA_L, SRA_DHL, SRA_A, // 0x28 - 0x2f 813 | SWAP_B, SWAP_C, SWAP_D, SWAP_E, SWAP_H, SWAP_L, SWAP_DHL, SWAP_A, // 0x30 - 0x37 814 | SRL_B, SRL_C, SRL_D, SRL_E, SRL_H, SRL_L, SRL_DHL, SRL_A, // 0x38 - 0x3f 815 | bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x40 - 0x47 816 | bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x48 - 0x4f 817 | bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x50 - 0x57 818 | bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x58 - 0x5f 819 | bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x60 - 0x67 820 | bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x68 - 0x6f 821 | bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x70 - 0x77 822 | bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x78 - 0x7f 823 | bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0x80 - 0x8 824 | bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0x88 - 0x8f 825 | bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0x90 - 0x97 826 | bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0x98 - 0x9f 827 | bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0xa0 - 0xa7 828 | bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0xa8 - 0xaf 829 | bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0xb0 - 0xb7 830 | bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0xb8 - 0xbf 831 | bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xc0 - 0xc7 832 | bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xc8 - 0xcf 833 | bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xd0 - 0xd7 834 | bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xd8 - 0xdf 835 | bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xe0 - 0xe7 836 | bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xe8 - 0xef 837 | bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xf0 - 0xf7 838 | bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set }; // 0xf8 - 0xff 839 | 840 | 841 | //////////////////////////////// 842 | // Opcodes // 843 | //////////////////////////////// 844 | void LD_B_n(u8 opocde) { // 06 845 | u8 imm = readByte(++globalState.pc); 846 | globalState.pc++; 847 | globalState.bc.hi = imm; 848 | globalState.cycleCount += 8; 849 | } 850 | 851 | void LD_C_n(u8 opocde) { // 0E 852 | u8 imm = readByte(++globalState.pc); 853 | globalState.pc++; 854 | globalState.bc.lo = imm; 855 | globalState.cycleCount += 8; 856 | } 857 | 858 | void LD_D_n(u8 opocde) { // 16 859 | u8 imm = readByte(++globalState.pc); 860 | globalState.pc++; 861 | globalState.de.hi = imm; 862 | globalState.cycleCount += 8; 863 | } 864 | 865 | void LD_E_n(u8 opocde) { // 1e 866 | u8 imm = readByte(++globalState.pc); 867 | globalState.pc++; 868 | globalState.de.lo = imm; 869 | globalState.cycleCount += 8; 870 | } 871 | 872 | void LD_H_n(u8 opocde) { // 26 873 | u8 imm = readByte(++globalState.pc); 874 | globalState.pc++; 875 | globalState.hl.hi = imm; 876 | globalState.cycleCount += 8; 877 | } 878 | 879 | void LD_L_n(u8 opocde) { // 2e 880 | u8 imm = readByte(++globalState.pc); 881 | globalState.pc++; 882 | globalState.hl.lo = imm; 883 | globalState.cycleCount += 8; 884 | } 885 | 886 | // REGISTER-REGISTER LOADS (REGISTER A) 887 | void LD_A_A(u8 opcode) { // 0x7f 888 | globalState.pc++; 889 | globalState.cycleCount += 4; 890 | globalState.a = globalState.a; 891 | } 892 | 893 | void LD_A_B(u8 opcode) { // 0x78 894 | globalState.pc++; 895 | globalState.cycleCount += 4; 896 | globalState.a = globalState.bc.hi; 897 | } 898 | 899 | void LD_A_C(u8 opcode) { // 0x79 900 | globalState.pc++; 901 | globalState.cycleCount += 4; 902 | globalState.a = globalState.bc.lo; 903 | } 904 | 905 | void LD_A_D(u8 opcode) { // 0x7a 906 | globalState.pc++; 907 | globalState.cycleCount += 4; 908 | globalState.a = globalState.de.hi; 909 | } 910 | 911 | void LD_A_E(u8 opcode) { // 0x7b 912 | globalState.pc++; 913 | globalState.cycleCount += 4; 914 | globalState.a = globalState.de.lo; 915 | } 916 | 917 | void LD_A_H(u8 opcode) { // 0x7c 918 | globalState.pc++; 919 | globalState.cycleCount += 4; 920 | globalState.a = globalState.hl.hi; 921 | } 922 | 923 | void LD_A_L(u8 opcode) { // 0x7d 924 | globalState.pc++; 925 | globalState.cycleCount += 4; 926 | globalState.a = globalState.hl.lo; 927 | } 928 | 929 | void LD_A_DHL(u8 opcode) { // 0x7e 930 | globalState.pc++; 931 | globalState.cycleCount += 8; 932 | globalState.a = readByte(globalState.hl.v); 933 | } 934 | 935 | 936 | 937 | // REGISTER-REGISTER LOADS (REGISTER B) 938 | void LD_B_B(u8 opcode) { // 0x40 939 | globalState.pc++; 940 | globalState.cycleCount += 4; 941 | globalState.bc.hi = globalState.bc.hi; 942 | } 943 | 944 | void LD_B_C(u8 opcode) { // 0x41 945 | globalState.pc++; 946 | globalState.cycleCount += 4; 947 | globalState.bc.hi = globalState.bc.lo; 948 | } 949 | 950 | void LD_B_D(u8 opcode) { // 0x42 951 | globalState.pc++; 952 | globalState.cycleCount += 4; 953 | globalState.bc.hi = globalState.de.hi; 954 | } 955 | 956 | void LD_B_E(u8 opcode) { // 0x43 957 | globalState.pc++; 958 | globalState.cycleCount += 4; 959 | globalState.bc.hi = globalState.de.lo; 960 | } 961 | 962 | void LD_B_H(u8 opcode) { // 0x44 963 | globalState.pc++; 964 | globalState.cycleCount += 4; 965 | globalState.bc.hi = globalState.hl.hi; 966 | } 967 | 968 | void LD_B_L(u8 opcode) { // 0x45 969 | globalState.pc++; 970 | globalState.cycleCount += 4; 971 | globalState.bc.hi = globalState.hl.lo; 972 | } 973 | 974 | void LD_B_DHL(u8 opcode) { // 0x46 975 | globalState.pc++; 976 | globalState.cycleCount += 8; 977 | globalState.bc.hi = readByte(globalState.hl.v); 978 | } 979 | 980 | 981 | 982 | // REGISTER-REGISTER LOADS (REGISTER C) 983 | void LD_C_B(u8 opcode) { // 0x48 984 | globalState.pc++; 985 | globalState.cycleCount += 4; 986 | globalState.bc.lo = globalState.bc.hi; 987 | } 988 | 989 | void LD_C_C(u8 opcode) { // 0x49 990 | globalState.pc++; 991 | globalState.cycleCount += 4; 992 | globalState.bc.lo = globalState.bc.lo; 993 | } 994 | 995 | void LD_C_D(u8 opcode) { // 0x4a 996 | globalState.pc++; 997 | globalState.cycleCount += 4; 998 | globalState.bc.lo = globalState.de.hi; 999 | } 1000 | 1001 | void LD_C_E(u8 opcode) { // 0x4b 1002 | globalState.pc++; 1003 | globalState.cycleCount += 4; 1004 | globalState.bc.lo = globalState.de.lo; 1005 | } 1006 | 1007 | void LD_C_H(u8 opcode) { // 0x4c 1008 | globalState.pc++; 1009 | globalState.cycleCount += 4; 1010 | globalState.bc.lo = globalState.hl.hi; 1011 | } 1012 | 1013 | void LD_C_L(u8 opcode) { // 0x4d 1014 | globalState.pc++; 1015 | globalState.cycleCount += 4; 1016 | globalState.bc.lo = globalState.hl.lo; 1017 | } 1018 | 1019 | void LD_C_DHL(u8 opcode) { // 0x4e 1020 | globalState.pc++; 1021 | globalState.cycleCount += 8; 1022 | globalState.bc.lo = readByte(globalState.hl.v); 1023 | } 1024 | 1025 | 1026 | 1027 | // REGISTER-REGISTER LOADS (REGISTER D) 1028 | void LD_D_B(u8 opcode) { // 0x50 1029 | globalState.pc++; 1030 | globalState.cycleCount += 4; 1031 | globalState.de.hi = globalState.bc.hi; 1032 | } 1033 | 1034 | void LD_D_C(u8 opcode) { // 0x51 1035 | globalState.pc++; 1036 | globalState.cycleCount += 4; 1037 | globalState.de.hi = globalState.bc.lo; 1038 | } 1039 | 1040 | void LD_D_D(u8 opcode) { // 0x52 1041 | globalState.pc++; 1042 | globalState.cycleCount += 4; 1043 | globalState.de.hi = globalState.de.hi; 1044 | } 1045 | 1046 | void LD_D_E(u8 opcode) { // 0x53 1047 | globalState.pc++; 1048 | globalState.cycleCount += 4; 1049 | globalState.de.hi = globalState.de.lo; 1050 | } 1051 | 1052 | void LD_D_H(u8 opcode) { // 0x54 1053 | globalState.pc++; 1054 | globalState.cycleCount += 4; 1055 | globalState.de.hi = globalState.hl.hi; 1056 | } 1057 | 1058 | void LD_D_L(u8 opcode) { // 0x55 1059 | globalState.pc++; 1060 | globalState.cycleCount += 4; 1061 | globalState.de.hi = globalState.hl.lo; 1062 | } 1063 | 1064 | void LD_D_DHL(u8 opcode) { // 0x56 1065 | globalState.pc++; 1066 | globalState.cycleCount += 8; 1067 | globalState.de.hi = readByte(globalState.hl.v); 1068 | } 1069 | 1070 | 1071 | // REGISTER-REGISTER LOADS (REGISTER E) 1072 | void LD_E_B(u8 opcode) { // 0x58 1073 | globalState.pc++; 1074 | globalState.cycleCount += 4; 1075 | globalState.de.lo = globalState.bc.hi; 1076 | } 1077 | 1078 | void LD_E_C(u8 opcode) { // 0x59 1079 | globalState.pc++; 1080 | globalState.cycleCount += 4; 1081 | globalState.de.lo = globalState.bc.lo; 1082 | } 1083 | 1084 | void LD_E_D(u8 opcode) { // 0x5a 1085 | globalState.pc++; 1086 | globalState.cycleCount += 4; 1087 | globalState.de.lo = globalState.de.hi; 1088 | } 1089 | 1090 | void LD_E_E(u8 opcode) { // 0x5b 1091 | globalState.pc++; 1092 | globalState.cycleCount += 4; 1093 | globalState.de.lo = globalState.de.lo; 1094 | } 1095 | 1096 | void LD_E_H(u8 opcode) { // 0x5c 1097 | globalState.pc++; 1098 | globalState.cycleCount += 4; 1099 | globalState.de.lo = globalState.hl.hi; 1100 | } 1101 | 1102 | void LD_E_L(u8 opcode) { // 0x5d 1103 | globalState.pc++; 1104 | globalState.cycleCount += 4; 1105 | globalState.de.lo = globalState.hl.lo; 1106 | } 1107 | 1108 | void LD_E_DHL(u8 opcode) { // 0x5e 1109 | globalState.pc++; 1110 | globalState.cycleCount += 8; 1111 | globalState.de.lo = readByte(globalState.hl.v); 1112 | } 1113 | 1114 | // REGISTER-REGISTER LOADS (REGISTER H) 1115 | void LD_H_B(u8 opcode) { // 0x60 1116 | globalState.pc++; 1117 | globalState.cycleCount += 4; 1118 | globalState.hl.hi = globalState.bc.hi; 1119 | } 1120 | 1121 | void LD_H_C(u8 opcode) { // 0x61 1122 | globalState.pc++; 1123 | globalState.cycleCount += 4; 1124 | globalState.hl.hi = globalState.bc.lo; 1125 | } 1126 | 1127 | void LD_H_D(u8 opcode) { // 0x62 1128 | globalState.pc++; 1129 | globalState.cycleCount += 4; 1130 | globalState.hl.hi = globalState.de.hi; 1131 | } 1132 | 1133 | void LD_H_E(u8 opcode) { // 0x63 1134 | globalState.pc++; 1135 | globalState.cycleCount += 4; 1136 | globalState.hl.hi = globalState.de.lo; 1137 | } 1138 | 1139 | void LD_H_H(u8 opcode) { // 0x64 1140 | globalState.pc++; 1141 | globalState.cycleCount += 4; 1142 | globalState.hl.hi = globalState.hl.hi; 1143 | } 1144 | 1145 | void LD_H_L(u8 opcode) { // 0x65 1146 | globalState.pc++; 1147 | globalState.cycleCount += 4; 1148 | globalState.hl.hi = globalState.hl.lo; 1149 | } 1150 | 1151 | void LD_H_DHL(u8 opcode) { // 0x66 1152 | globalState.pc++; 1153 | globalState.cycleCount += 8; 1154 | globalState.hl.hi = readByte(globalState.hl.v); 1155 | } 1156 | 1157 | // REGISTER-REGISTER LOADS (REGISTER L) 1158 | void LD_L_B(u8 opcode) { // 0x60 1159 | globalState.pc++; 1160 | globalState.cycleCount += 4; 1161 | globalState.hl.lo = globalState.bc.hi; 1162 | } 1163 | 1164 | void LD_L_C(u8 opcode) { // 0x61 1165 | globalState.pc++; 1166 | globalState.cycleCount += 4; 1167 | globalState.hl.lo = globalState.bc.lo; 1168 | } 1169 | 1170 | void LD_L_D(u8 opcode) { // 0x62 1171 | globalState.pc++; 1172 | globalState.cycleCount += 4; 1173 | globalState.hl.lo = globalState.de.hi; 1174 | } 1175 | 1176 | void LD_L_E(u8 opcode) { // 0x63 1177 | globalState.pc++; 1178 | globalState.cycleCount += 4; 1179 | globalState.hl.lo = globalState.de.lo; 1180 | } 1181 | 1182 | void LD_L_H(u8 opcode) { // 0x64 1183 | globalState.pc++; 1184 | globalState.cycleCount += 4; 1185 | globalState.hl.lo = globalState.hl.hi; 1186 | } 1187 | 1188 | void LD_L_L(u8 opcode) { // 0x65 1189 | globalState.pc++; 1190 | globalState.cycleCount += 4; 1191 | globalState.hl.lo = globalState.hl.lo; 1192 | } 1193 | 1194 | void LD_L_DHL(u8 opcode) { // 0x66 1195 | globalState.pc++; 1196 | globalState.cycleCount += 8; 1197 | globalState.hl.lo = readByte(globalState.hl.v); 1198 | } 1199 | 1200 | // REGISTER-REGISTER LOADS (REGISTER (HL)) 1201 | void LD_DHL_B(u8 opcode) { // 0x70 1202 | globalState.pc++; 1203 | globalState.cycleCount += 8; 1204 | writeByte(globalState.bc.hi, globalState.hl.v); 1205 | } 1206 | 1207 | void LD_DHL_C(u8 opcode) { // 0x71 1208 | globalState.pc++; 1209 | globalState.cycleCount += 8; 1210 | writeByte(globalState.bc.lo, globalState.hl.v); 1211 | } 1212 | 1213 | void LD_DHL_D(u8 opcode) { // 0x72 1214 | globalState.pc++; 1215 | globalState.cycleCount += 8; 1216 | writeByte(globalState.de.hi, globalState.hl.v); 1217 | } 1218 | 1219 | void LD_DHL_E(u8 opcode) { // 0x73 1220 | globalState.pc++; 1221 | globalState.cycleCount += 8; 1222 | writeByte(globalState.de.lo, globalState.hl.v); 1223 | } 1224 | 1225 | void LD_DHL_H(u8 opcode) { // 0x74 1226 | globalState.pc++; 1227 | globalState.cycleCount += 8; 1228 | writeByte(globalState.hl.hi, globalState.hl.v); 1229 | } 1230 | 1231 | void LD_DHL_L(u8 opcode) { // 0x75 1232 | globalState.pc++; 1233 | globalState.cycleCount += 8; 1234 | writeByte(globalState.hl.lo, globalState.hl.v); 1235 | } 1236 | 1237 | // Random 1238 | void LD_DHL_n(u8 opcode) { // 0x36 1239 | u8 imm = readByte(++globalState.pc); 1240 | globalState.pc++; 1241 | globalState.cycleCount += 12; 1242 | writeByte(imm, globalState.hl.v); 1243 | } 1244 | 1245 | // Load Into Register A Specials 1246 | void LD_A_DBC(u8 opcode) { // 0A 1247 | globalState.pc++; 1248 | globalState.cycleCount += 8; 1249 | globalState.a = readByte(globalState.bc.v); 1250 | } 1251 | 1252 | void LD_A_DDE(u8 opcode) { // 1A 1253 | globalState.pc++; 1254 | globalState.cycleCount += 8; 1255 | globalState.a = readByte(globalState.de.v); 1256 | } 1257 | 1258 | 1259 | void LD_A_Dnn(u8 opcode) { // FA 1260 | globalState.pc++; 1261 | globalState.cycleCount += 16; 1262 | globalState.a = readByte(readU16(globalState.pc)); 1263 | globalState.pc += 2; 1264 | } 1265 | 1266 | void LD_A_n(u8 opocde) { // 0x3e 1267 | u8 imm = readByte(++globalState.pc); 1268 | globalState.pc++; 1269 | globalState.a = imm; 1270 | globalState.cycleCount += 8; 1271 | } 1272 | 1273 | // LOAD FROM REGISTER A 1274 | void LD_B_A(u8 opcode) { // 0x47 1275 | globalState.pc++; 1276 | globalState.cycleCount += 4; 1277 | globalState.bc.hi = globalState.a; 1278 | } 1279 | 1280 | void LD_C_A(u8 opcode) { // 0x4f 1281 | globalState.pc++; 1282 | globalState.cycleCount += 4; 1283 | globalState.bc.lo = globalState.a; 1284 | } 1285 | 1286 | void LD_D_A(u8 opcode) { // 0x57 1287 | globalState.pc++; 1288 | globalState.cycleCount += 4; 1289 | globalState.de.hi = globalState.a; 1290 | } 1291 | 1292 | void LD_E_A(u8 opcode) { // 0x5f 1293 | globalState.pc++; 1294 | globalState.cycleCount += 4; 1295 | globalState.de.lo = globalState.a; 1296 | } 1297 | 1298 | void LD_H_A(u8 opcode) { // 0x67 1299 | globalState.pc++; 1300 | globalState.cycleCount += 4; 1301 | globalState.hl.hi = globalState.a; 1302 | } 1303 | 1304 | void LD_L_A(u8 opcode) { // 0x6f 1305 | globalState.pc++; 1306 | globalState.cycleCount += 4; 1307 | globalState.hl.lo = globalState.a; 1308 | } 1309 | 1310 | void LD_DBC_A(u8 opcode) { // 0x02 1311 | globalState.pc++; 1312 | globalState.cycleCount += 8; 1313 | writeByte(globalState.a, globalState.bc.v); 1314 | } 1315 | 1316 | void LD_DDE_A(u8 opcode) { // 0x12 1317 | globalState.pc++; 1318 | globalState.cycleCount += 8; 1319 | writeByte(globalState.a, globalState.de.v); 1320 | } 1321 | 1322 | void LD_DHL_A(u8 opcode) { // 0x77 1323 | globalState.pc++; 1324 | globalState.cycleCount += 8; 1325 | writeByte(globalState.a, globalState.hl.v); 1326 | } 1327 | 1328 | void LD_Dnn_A(u8 opcode) { // 0xea 1329 | globalState.pc++; 1330 | globalState.cycleCount += 16; 1331 | writeByte(globalState.a, readU16(globalState.pc)); 1332 | globalState.pc += 2; 1333 | } 1334 | 1335 | // Load A with memory offset from 0xff00 1336 | void LD_A_FF00_C(u8 opcode) { //0xf2 1337 | globalState.pc++; 1338 | globalState.cycleCount += 8; 1339 | globalState.a = readByte((u16)0xff00 + globalState.bc.lo); 1340 | } 1341 | 1342 | // Store A with memory offset from 0xff00 1343 | void LD_FF00_C_A(u8 opcode) { // 0xe2 1344 | globalState.pc++; 1345 | writeByte(globalState.a, (u16)0xff00 + globalState.bc.lo); 1346 | globalState.cycleCount += 8; 1347 | } 1348 | 1349 | // Load (HL) into A, decrement HL 1350 | void LDD_A_DHL(u8 opcode) { // 0x3a 1351 | globalState.pc++; 1352 | globalState.cycleCount += 8; 1353 | globalState.a = readByte(globalState.hl.v); 1354 | globalState.hl.v--; 1355 | } 1356 | 1357 | // Load A into (HL), decrement HL 1358 | void LDD_DHL_A(u8 opcode) { // 0x32 1359 | writeByte(globalState.a, globalState.hl.v); 1360 | globalState.hl.v--; 1361 | globalState.pc += 1; 1362 | globalState.cycleCount += 8; 1363 | } 1364 | 1365 | // Load (HL) into A, increment HL 1366 | void LDI_A_DHL(u8 opcode){ // 0x2a 1367 | globalState.pc += 1; 1368 | globalState.cycleCount += 8; 1369 | globalState.a = readByte(globalState.hl.v); 1370 | globalState.hl.v++; 1371 | } 1372 | 1373 | // Load A into (HL), increment HL 1374 | void LDI_DHL_A(u8 opcode) { // 0x22 1375 | writeByte(globalState.a, globalState.hl.v); 1376 | globalState.hl.v++; 1377 | globalState.pc += 1; 1378 | globalState.cycleCount += 8; 1379 | } 1380 | // page 75 1381 | 1382 | // Store A into FF00 + n 1383 | void LD_FF00_n_A(u8 opcode) { //0xe0 1384 | u8 imm = readByte(++globalState.pc); 1385 | globalState.pc++; 1386 | writeByte(globalState.a, (u16)0xff00 + imm); 1387 | globalState.cycleCount += 12; 1388 | } 1389 | 1390 | // Load A with FF00 + n 1391 | void LD_A_FF00_n(u8 opcode) { // 0xf0 1392 | u8 imm = readByte(++globalState.pc); 1393 | globalState.pc++; 1394 | globalState.a = readByte((u16)0xff00 + imm); 1395 | globalState.cycleCount += 12; 1396 | } 1397 | 1398 | // 16-bit loads 1399 | void LD_BC_nn(u8 opcode) { // 0x01 1400 | u16 imm = readU16(globalState.pc + (u16)1); 1401 | globalState.bc.v = imm; 1402 | globalState.pc += 3; 1403 | globalState.cycleCount += 12; 1404 | } 1405 | 1406 | void LD_DE_nn(u8 opcode) { // 0x11 1407 | u16 imm = readU16(globalState.pc + (u16)1); 1408 | globalState.de.v = imm; 1409 | globalState.pc += 3; 1410 | globalState.cycleCount += 12; 1411 | } 1412 | 1413 | void LD_HL_nn(u8 opcode) { // 0x21 1414 | u16 imm = readU16(globalState.pc + (u16)1); 1415 | globalState.hl.v = imm; 1416 | globalState.pc += 3; 1417 | globalState.cycleCount += 12; 1418 | } 1419 | 1420 | void LD_SP_nn(u8 opcode) { // 0x31 1421 | u16 imm = readU16(globalState.pc + (u16)1); 1422 | globalState.sp = imm; 1423 | globalState.pc += 3; 1424 | globalState.cycleCount += 12; 1425 | } 1426 | 1427 | void LD_SP_HL(u8 opcode) { // 0xf9 1428 | globalState.sp = globalState.hl.v; 1429 | globalState.pc++; 1430 | globalState.cycleCount += 8; 1431 | } 1432 | 1433 | // load effective address relative to stack pointer 1434 | void LD_HL_SP_n(u8 opcode) { //0xf8 1435 | globalState.pc++; 1436 | globalState.cycleCount += 12; 1437 | s8 imm = readByte(globalState.pc); 1438 | globalState.pc++; 1439 | clearAllFlags(); 1440 | u16 result = globalState.sp + imm; 1441 | if(((globalState.sp ^ imm ^ result) & 0x100) == 0x100) { 1442 | setCarryFlag(); 1443 | } 1444 | if(((globalState.sp ^ imm ^ result) & 0x10) == 0x10) { 1445 | setHalfCarryFlag(); 1446 | } 1447 | globalState.hl.v = result; 1448 | } 1449 | 1450 | // store stack pointer 1451 | void LD_Dnn_SP(u8 opcode) { // 08 1452 | globalState.pc++; 1453 | u16 addr = readU16(globalState.pc); 1454 | globalState.pc+=2; 1455 | globalState.cycleCount += 20; 1456 | writeU16(globalState.sp, addr); 1457 | } 1458 | 1459 | // push U16 register onto stack 1460 | void PUSH_AF(u8 opcode) { // 0xf5 1461 | globalState.pc++; 1462 | globalState.cycleCount += 16; 1463 | writeU16(globalState.f + (((u16)globalState.a) << 8), globalState.sp - (u16)2); 1464 | globalState.sp -= 2; 1465 | } 1466 | 1467 | void PUSH_BC(u8 opcode) { // 0xc5 1468 | globalState.pc++; 1469 | globalState.cycleCount += 16; 1470 | writeU16(globalState.bc.v, globalState.sp - (u16)2); 1471 | globalState.sp -= 2; 1472 | } 1473 | 1474 | void PUSH_DE(u8 opcode) { // 0xd5 1475 | globalState.pc++; 1476 | globalState.cycleCount += 16; 1477 | writeU16(globalState.de.v, globalState.sp - (u16)2); 1478 | globalState.sp -= 2; 1479 | } 1480 | 1481 | void PUSH_HL(u8 opcode) { // 0xe5 1482 | globalState.pc++; 1483 | globalState.cycleCount += 16; 1484 | writeU16(globalState.hl.v, globalState.sp - (u16)2); 1485 | globalState.sp -= 2; 1486 | } 1487 | 1488 | // POP U16 register from stack 1489 | void POP_AF(u8 opcode) { // 0xf1 1490 | globalState.pc++; 1491 | globalState.cycleCount += 12; 1492 | u16 v = readU16(globalState.sp); 1493 | globalState.sp += 2; 1494 | globalState.a = (u8)(v >> 8); 1495 | globalState.f = (u8)(v & 0xf0); 1496 | } 1497 | 1498 | void POP_BC(u8 opcode) { // 0xc1 1499 | globalState.pc++; 1500 | globalState.cycleCount += 12; 1501 | u16 v = readU16(globalState.sp); 1502 | globalState.sp += 2; 1503 | globalState.bc.v = v; 1504 | } 1505 | 1506 | void POP_DE(u8 opcode) { // 0xd1 1507 | globalState.pc++; 1508 | globalState.cycleCount += 12; 1509 | u16 v = readU16(globalState.sp); 1510 | globalState.sp += 2; 1511 | globalState.de.v = v; 1512 | } 1513 | 1514 | void POP_HL(u8 opcode) { // 0xe1 1515 | globalState.pc++; 1516 | globalState.cycleCount += 12; 1517 | u16 v = readU16(globalState.sp); 1518 | globalState.sp += 2; 1519 | globalState.hl.v = v; 1520 | } 1521 | 1522 | void addToA(u8 number) { 1523 | int result = globalState.a + number; 1524 | int carryBits = globalState.a ^ number ^ result; 1525 | globalState.a = (u8)result; 1526 | clearAllFlags(); 1527 | 1528 | if((u8)result == 0){ 1529 | setZeroFlag(); 1530 | } 1531 | 1532 | if((carryBits & 0x100) != 0) { 1533 | setCarryFlag(); 1534 | } 1535 | if((carryBits & 0x10) != 0) { 1536 | setHalfCarryFlag(); 1537 | } 1538 | } 1539 | 1540 | // ADD Opcodes 1541 | void ADD_A(u8 opcode) { // 0x87 1542 | globalState.pc++; 1543 | globalState.cycleCount += 4; 1544 | addToA(globalState.a); 1545 | } 1546 | 1547 | void ADD_B(u8 opcode) { // 0x80 1548 | globalState.pc++; 1549 | globalState.cycleCount += 4; 1550 | addToA(globalState.bc.hi); 1551 | } 1552 | 1553 | void ADD_C(u8 opcode) { // 0x81 1554 | globalState.pc++; 1555 | globalState.cycleCount += 4; 1556 | addToA(globalState.bc.lo); 1557 | } 1558 | 1559 | void ADD_D(u8 opcode) { // 0x82 1560 | globalState.pc++; 1561 | globalState.cycleCount += 4; 1562 | addToA(globalState.de.hi); 1563 | } 1564 | 1565 | void ADD_E(u8 opcode) { // 0x83 1566 | globalState.pc++; 1567 | globalState.cycleCount += 4; 1568 | addToA(globalState.de.lo); 1569 | } 1570 | 1571 | void ADD_H(u8 opcode) { // 0x84 1572 | globalState.pc++; 1573 | globalState.cycleCount += 4; 1574 | addToA(globalState.hl.hi); 1575 | } 1576 | 1577 | void ADD_L(u8 opcode) { // 0x85 1578 | globalState.pc++; 1579 | globalState.cycleCount += 4; 1580 | addToA(globalState.hl.lo); 1581 | } 1582 | 1583 | void ADD_DHL(u8 opcode) { // 0x86 1584 | globalState.pc++; 1585 | globalState.cycleCount += 8; 1586 | addToA(readByte(globalState.hl.v)); 1587 | } 1588 | 1589 | void ADD_n(u8 opcode) { // 0xC6 1590 | u8 imm = readByte(++globalState.pc); 1591 | globalState.pc++; 1592 | globalState.cycleCount += 8; 1593 | addToA(imm); 1594 | } 1595 | 1596 | void adcToA(u8 number) { 1597 | int carry = getCarryFlag() ? 1 : 0; 1598 | int result = globalState.a + number + carry; 1599 | clearAllFlags(); 1600 | if((u8)result == 0) { 1601 | setZeroFlag(); 1602 | } 1603 | if(result > 0xff) { 1604 | setCarryFlag(); 1605 | } 1606 | if(((globalState.a & 0xf) + (number & 0xf) + carry) > 0xf) { 1607 | setHalfCarryFlag(); 1608 | } 1609 | globalState.a = (u8) result; 1610 | } 1611 | 1612 | // ADC Opcodes 1613 | void ADC_A(u8 opcode) { // 0x8f 1614 | globalState.pc++; 1615 | globalState.cycleCount += 4; 1616 | adcToA(globalState.a); 1617 | } 1618 | 1619 | void ADC_B(u8 opcode) { // 0x88 1620 | globalState.pc++; 1621 | globalState.cycleCount += 4; 1622 | adcToA(globalState.bc.hi); 1623 | } 1624 | 1625 | void ADC_C(u8 opcode) { // 0x89 1626 | globalState.pc++; 1627 | globalState.cycleCount += 4; 1628 | adcToA(globalState.bc.lo); 1629 | } 1630 | 1631 | void ADC_D(u8 opcode) { // 0x8a 1632 | globalState.pc++; 1633 | globalState.cycleCount += 4; 1634 | adcToA(globalState.de.hi); 1635 | } 1636 | 1637 | void ADC_E(u8 opcode) { // 0x8b 1638 | globalState.pc++; 1639 | globalState.cycleCount += 4; 1640 | adcToA(globalState.de.lo); 1641 | } 1642 | 1643 | void ADC_H(u8 opcode) { // 0x8c 1644 | globalState.pc++; 1645 | globalState.cycleCount += 4; 1646 | adcToA(globalState.hl.hi); 1647 | } 1648 | 1649 | void ADC_L(u8 opcode) { // 0x8d 1650 | globalState.pc++; 1651 | globalState.cycleCount += 4; 1652 | adcToA(globalState.hl.lo); 1653 | } 1654 | 1655 | void ADC_DHL(u8 opcode) { // 0x8e 1656 | globalState.pc++; 1657 | globalState.cycleCount += 8; 1658 | adcToA(readByte(globalState.hl.v)); 1659 | } 1660 | 1661 | void ADC_n(u8 opcode) { // 0xCe 1662 | u8 imm = readByte(++globalState.pc); 1663 | globalState.pc++; 1664 | globalState.cycleCount += 8; 1665 | adcToA(imm); 1666 | } 1667 | 1668 | void subToA(u8 number) { 1669 | int result = globalState.a - number; 1670 | int carryBits = globalState.a ^ number ^ result; 1671 | globalState.a = (u8)result; 1672 | clearAllFlags(); 1673 | setSubtractFlag(); 1674 | if((u8)result == 0) { 1675 | setZeroFlag(); 1676 | } 1677 | if((carryBits & 0x100) != 0) { 1678 | setCarryFlag(); 1679 | } 1680 | if((carryBits & 0x10) != 0) { 1681 | setHalfCarryFlag(); 1682 | } 1683 | } 1684 | 1685 | // SUB Opcodes 1686 | void SUB_A(u8 opcode) { // 0x97 1687 | globalState.pc++; 1688 | globalState.cycleCount += 4; 1689 | subToA(globalState.a); 1690 | } 1691 | 1692 | void SUB_B(u8 opcode) { // 0x90 1693 | globalState.pc++; 1694 | globalState.cycleCount += 4; 1695 | subToA(globalState.bc.hi); 1696 | } 1697 | 1698 | void SUB_C(u8 opcode) { // 0x91 1699 | globalState.pc++; 1700 | globalState.cycleCount += 4; 1701 | subToA(globalState.bc.lo); 1702 | } 1703 | 1704 | void SUB_D(u8 opcode) { // 0x92 1705 | globalState.pc++; 1706 | globalState.cycleCount += 4; 1707 | subToA(globalState.de.hi); 1708 | } 1709 | 1710 | void SUB_E(u8 opcode) { // 0x93 1711 | globalState.pc++; 1712 | globalState.cycleCount += 4; 1713 | subToA(globalState.de.lo); 1714 | } 1715 | 1716 | void SUB_H(u8 opcode) { // 0x94 1717 | globalState.pc++; 1718 | globalState.cycleCount += 4; 1719 | subToA(globalState.hl.hi); 1720 | } 1721 | 1722 | void SUB_L(u8 opcode) { // 0x95 1723 | globalState.pc++; 1724 | globalState.cycleCount += 4; 1725 | subToA(globalState.hl.lo); 1726 | } 1727 | 1728 | void SUB_DHL(u8 opcode) { // 0x96 1729 | globalState.pc++; 1730 | globalState.cycleCount += 8; 1731 | subToA(readByte(globalState.hl.v)); 1732 | } 1733 | 1734 | void SUB_n(u8 opcode) { // 0xD6 1735 | u8 imm = readByte(++globalState.pc); 1736 | globalState.pc++; 1737 | globalState.cycleCount += 8; 1738 | subToA(imm); 1739 | } 1740 | 1741 | void sbcToA(u8 number) { 1742 | int carry = getCarryFlag() ? 1 : 0; 1743 | int result = globalState.a - number - carry; 1744 | clearAllFlags(); 1745 | setSubtractFlag(); 1746 | if((u8)result == 0) { 1747 | setZeroFlag(); 1748 | } 1749 | if(result < 0) { 1750 | setCarryFlag(); 1751 | } 1752 | if(((globalState.a & 0xf) - (number & 0xf) - carry) < 0) { 1753 | setHalfCarryFlag(); 1754 | } 1755 | globalState.a = (u8)result; 1756 | } 1757 | 1758 | // SBC Opcodes 1759 | void SBC_A(u8 opcode) { // 0x9f 1760 | globalState.pc++; 1761 | globalState.cycleCount += 4; 1762 | sbcToA(globalState.a); 1763 | } 1764 | 1765 | void SBC_B(u8 opcode) { // 0x98 1766 | globalState.pc++; 1767 | globalState.cycleCount += 4; 1768 | sbcToA(globalState.bc.hi); 1769 | } 1770 | 1771 | void SBC_C(u8 opcode) { // 0x99 1772 | globalState.pc++; 1773 | globalState.cycleCount += 4; 1774 | sbcToA(globalState.bc.lo); 1775 | } 1776 | 1777 | void SBC_D(u8 opcode) { // 0x9a 1778 | globalState.pc++; 1779 | globalState.cycleCount += 4; 1780 | sbcToA(globalState.de.hi); 1781 | } 1782 | 1783 | void SBC_E(u8 opcode) { // 0x9b 1784 | globalState.pc++; 1785 | globalState.cycleCount += 4; 1786 | sbcToA(globalState.de.lo); 1787 | } 1788 | 1789 | void SBC_H(u8 opcode) { // 0x9c 1790 | globalState.pc++; 1791 | globalState.cycleCount += 4; 1792 | sbcToA(globalState.hl.hi); 1793 | } 1794 | 1795 | void SBC_L(u8 opcode) { // 0x9d 1796 | globalState.pc++; 1797 | globalState.cycleCount += 4; 1798 | sbcToA(globalState.hl.lo); 1799 | } 1800 | 1801 | void SBC_DHL(u8 opcode) { // 0x9e 1802 | globalState.pc++; 1803 | globalState.cycleCount += 8; 1804 | sbcToA(readByte(globalState.hl.v)); 1805 | } 1806 | 1807 | void SBC_n(u8 opcode) { // 0xDe 1808 | u8 imm = readByte(++globalState.pc); 1809 | globalState.pc++; 1810 | globalState.cycleCount += 8; 1811 | sbcToA(imm); 1812 | } 1813 | 1814 | void andtoA(u8 number) { 1815 | u8 result = globalState.a & number; 1816 | globalState.a = result; 1817 | clearAllFlags(); 1818 | setHalfCarryFlag(); 1819 | if((u8)result == 0) { 1820 | setZeroFlag(); 1821 | } 1822 | } 1823 | 1824 | // AND Opcodes 1825 | void AND_A(u8 opcode) { // 0xa7 1826 | globalState.pc++; 1827 | globalState.cycleCount += 4; 1828 | andtoA(globalState.a); 1829 | } 1830 | 1831 | void AND_B(u8 opcode) { // 0xa0 1832 | globalState.pc++; 1833 | globalState.cycleCount += 4; 1834 | andtoA(globalState.bc.hi); 1835 | } 1836 | 1837 | void AND_C(u8 opcode) { // 0xa1 1838 | globalState.pc++; 1839 | globalState.cycleCount += 4; 1840 | andtoA(globalState.bc.lo); 1841 | } 1842 | 1843 | void AND_D(u8 opcode) { // 0xa2 1844 | globalState.pc++; 1845 | globalState.cycleCount += 4; 1846 | andtoA(globalState.de.hi); 1847 | } 1848 | 1849 | void AND_E(u8 opcode) { // 0xa3 1850 | globalState.pc++; 1851 | globalState.cycleCount += 4; 1852 | andtoA(globalState.de.lo); 1853 | } 1854 | 1855 | void AND_H(u8 opcode) { // 0xa4 1856 | globalState.pc++; 1857 | globalState.cycleCount += 4; 1858 | andtoA(globalState.hl.hi); 1859 | } 1860 | 1861 | void AND_L(u8 opcode) { // 0xa5 1862 | globalState.pc++; 1863 | globalState.cycleCount += 4; 1864 | andtoA(globalState.hl.lo); 1865 | } 1866 | 1867 | void AND_DHL(u8 opcode) { // 0xa6 1868 | globalState.pc++; 1869 | globalState.cycleCount += 8; 1870 | andtoA(readByte(globalState.hl.v)); 1871 | } 1872 | 1873 | void AND_n(u8 opcode) { // 0xe6 1874 | u8 imm = readByte(++globalState.pc); 1875 | globalState.pc++; 1876 | globalState.cycleCount += 8; 1877 | andtoA(imm); 1878 | } 1879 | 1880 | void xorToA(u8 number) { 1881 | u8 result = globalState.a ^ number; 1882 | globalState.a = result; 1883 | clearAllFlags(); 1884 | if((u8)result == 0) { 1885 | setZeroFlag(); 1886 | } 1887 | } 1888 | 1889 | // XOR Opcodes 1890 | void XOR_A(u8 opcode) { // 0xaf 1891 | globalState.pc++; 1892 | globalState.cycleCount += 4; 1893 | xorToA(globalState.a); 1894 | } 1895 | 1896 | void XOR_B(u8 opcode) { // 0xa8 1897 | globalState.pc++; 1898 | globalState.cycleCount += 4; 1899 | xorToA(globalState.bc.hi); 1900 | } 1901 | 1902 | void XOR_C(u8 opcode) { // 0xa9 1903 | globalState.pc++; 1904 | globalState.cycleCount += 4; 1905 | xorToA(globalState.bc.lo); 1906 | } 1907 | 1908 | void XOR_D(u8 opcode) { // 0xaa 1909 | globalState.pc++; 1910 | globalState.cycleCount += 4; 1911 | xorToA(globalState.de.hi); 1912 | } 1913 | 1914 | void XOR_E(u8 opcode) { // 0xab 1915 | globalState.pc++; 1916 | globalState.cycleCount += 4; 1917 | xorToA(globalState.de.lo); 1918 | } 1919 | 1920 | void XOR_H(u8 opcode) { // 0xac 1921 | globalState.pc++; 1922 | globalState.cycleCount += 4; 1923 | xorToA(globalState.hl.hi); 1924 | } 1925 | 1926 | void XOR_L(u8 opcode) { // 0xad 1927 | globalState.pc++; 1928 | globalState.cycleCount += 4; 1929 | xorToA(globalState.hl.lo); 1930 | } 1931 | 1932 | void XOR_DHL(u8 opcode) { // 0xae 1933 | globalState.pc++; 1934 | globalState.cycleCount += 8; 1935 | xorToA(readByte(globalState.hl.v)); 1936 | } 1937 | 1938 | void XOR_n(u8 opcode) { // 0xee 1939 | u8 imm = readByte(++globalState.pc); 1940 | globalState.pc++; 1941 | globalState.cycleCount += 8; 1942 | xorToA(imm); 1943 | } 1944 | 1945 | void orToA(u8 number) { 1946 | u8 result = globalState.a | number; 1947 | globalState.a = result; 1948 | clearAllFlags(); 1949 | if((u8)result == 0) { 1950 | setZeroFlag(); 1951 | } 1952 | } 1953 | 1954 | // OR Opcodes 1955 | void OR_A(u8 opcode) { // 0xb7 1956 | globalState.pc++; 1957 | globalState.cycleCount += 4; 1958 | orToA(globalState.a); 1959 | } 1960 | 1961 | void OR_B(u8 opcode) { // 0xb0 1962 | globalState.pc++; 1963 | globalState.cycleCount += 4; 1964 | orToA(globalState.bc.hi); 1965 | } 1966 | 1967 | void OR_C(u8 opcode) { // 0xb1 1968 | globalState.pc++; 1969 | globalState.cycleCount += 4; 1970 | orToA(globalState.bc.lo); 1971 | } 1972 | 1973 | void OR_D(u8 opcode) { // 0xb2 1974 | globalState.pc++; 1975 | globalState.cycleCount += 4; 1976 | orToA(globalState.de.hi); 1977 | } 1978 | 1979 | void OR_E(u8 opcode) { // 0xb3 1980 | globalState.pc++; 1981 | globalState.cycleCount += 4; 1982 | orToA(globalState.de.lo); 1983 | } 1984 | 1985 | void OR_H(u8 opcode) { // 0xb4 1986 | globalState.pc++; 1987 | globalState.cycleCount += 4; 1988 | orToA(globalState.hl.hi); 1989 | } 1990 | 1991 | void OR_L(u8 opcode) { // 0xb5 1992 | globalState.pc++; 1993 | globalState.cycleCount += 4; 1994 | orToA(globalState.hl.lo); 1995 | } 1996 | 1997 | void OR_DHL(u8 opcode) { // 0xb6 1998 | globalState.pc++; 1999 | globalState.cycleCount += 8; 2000 | orToA(readByte(globalState.hl.v)); 2001 | } 2002 | 2003 | void OR_n(u8 opcode) { // 0xf6 2004 | u8 imm = readByte(++globalState.pc); 2005 | globalState.pc++; 2006 | globalState.cycleCount += 8; 2007 | orToA(imm); 2008 | } 2009 | 2010 | void cpToA(u8 number) { 2011 | clearAllFlags(); 2012 | setSubtractFlag(); 2013 | if(globalState.a < number) { 2014 | setCarryFlag(); 2015 | } 2016 | if(globalState.a == number) { 2017 | setZeroFlag(); 2018 | } 2019 | if(((globalState.a - number) & 0xf) > (globalState.a & 0xf)) { 2020 | setHalfCarryFlag(); 2021 | } 2022 | } 2023 | 2024 | // CP Opcodes 2025 | void CP_A(u8 opcode) { // 0xbf 2026 | globalState.pc++; 2027 | globalState.cycleCount += 4; 2028 | cpToA(globalState.a); 2029 | } 2030 | 2031 | void CP_B(u8 opcode) { // 0xb8 2032 | globalState.pc++; 2033 | globalState.cycleCount += 4; 2034 | cpToA(globalState.bc.hi); 2035 | } 2036 | 2037 | void CP_C(u8 opcode) { // 0xb9 2038 | globalState.pc++; 2039 | globalState.cycleCount += 4; 2040 | cpToA(globalState.bc.lo); 2041 | } 2042 | 2043 | void CP_D(u8 opcode) { // 0xba 2044 | globalState.pc++; 2045 | globalState.cycleCount += 4; 2046 | cpToA(globalState.de.hi); 2047 | } 2048 | 2049 | void CP_E(u8 opcode) { // 0xbb 2050 | globalState.pc++; 2051 | globalState.cycleCount += 4; 2052 | cpToA(globalState.de.lo); 2053 | } 2054 | 2055 | void CP_H(u8 opcode) { // 0xbc 2056 | globalState.pc++; 2057 | globalState.cycleCount += 4; 2058 | cpToA(globalState.hl.hi); 2059 | } 2060 | 2061 | void CP_L(u8 opcode) { // 0xbd 2062 | globalState.pc++; 2063 | globalState.cycleCount += 4; 2064 | cpToA(globalState.hl.lo); 2065 | } 2066 | 2067 | void CP_DHL(u8 opcode) { // 0xbe 2068 | globalState.pc++; 2069 | globalState.cycleCount += 8; 2070 | cpToA(readByte(globalState.hl.v)); 2071 | } 2072 | 2073 | void CP_n(u8 opcode) { // 0xfe 2074 | u8 imm = readByte(++globalState.pc); 2075 | globalState.pc++; 2076 | globalState.cycleCount += 8; 2077 | cpToA(imm); 2078 | } 2079 | 2080 | void DecodeCB(u8 opcode) { 2081 | globalState.pc += 1; 2082 | u8 newOpcode = readByte(globalState.pc); 2083 | opcode_cbs[newOpcode](newOpcode); 2084 | } 2085 | 2086 | u8 increment(u8 in) { 2087 | u8 result = in + (u8)1; 2088 | if(getCarryFlag()) { 2089 | clearAllFlags(); 2090 | setCarryFlag(); 2091 | } else { 2092 | clearAllFlags(); 2093 | } 2094 | if(result == 0) { 2095 | setZeroFlag(); 2096 | } 2097 | if((result & 0x0f) == 0x00) { 2098 | setHalfCarryFlag(); 2099 | } 2100 | return result; 2101 | } 2102 | 2103 | void INC_A(u8 opcode) { // 0x3c 2104 | globalState.a = increment(globalState.a); 2105 | globalState.pc++; 2106 | globalState.cycleCount += 4; 2107 | } 2108 | 2109 | void INC_B(u8 opcode) { // 0x04 2110 | globalState.bc.hi = increment(globalState.bc.hi); 2111 | globalState.pc++; 2112 | globalState.cycleCount += 4; 2113 | } 2114 | 2115 | void INC_C(u8 opcode) { // 0x0c 2116 | globalState.bc.lo = increment(globalState.bc.lo); 2117 | globalState.pc++; 2118 | globalState.cycleCount += 4; 2119 | } 2120 | 2121 | void INC_D(u8 opcode) { // 0x14 2122 | globalState.de.hi = increment(globalState.de.hi); 2123 | globalState.pc++; 2124 | globalState.cycleCount += 4; 2125 | } 2126 | 2127 | void INC_E(u8 opcode) { // 0x1c 2128 | globalState.de.lo = increment(globalState.de.lo); 2129 | globalState.pc++; 2130 | globalState.cycleCount += 4; 2131 | } 2132 | 2133 | void INC_H(u8 opcode) { // 0x24 2134 | globalState.hl.hi = increment(globalState.hl.hi); 2135 | globalState.pc++; 2136 | globalState.cycleCount += 4; 2137 | } 2138 | 2139 | void INC_L(u8 opcode) { // 0x2c 2140 | globalState.hl.lo = increment(globalState.hl.lo); 2141 | globalState.pc++; 2142 | globalState.cycleCount += 4; 2143 | } 2144 | 2145 | void INC_DHL(u8 opcode) { // 0x34 2146 | // todo this one is hard. 2147 | u8 v = readByte(globalState.hl.v); 2148 | writeByte(increment(v), globalState.hl.v); 2149 | globalState.pc++; 2150 | globalState.cycleCount += 12; 2151 | } 2152 | 2153 | u8 decrement(u8 in) { 2154 | u8 result = in - (u8)1; 2155 | if(getCarryFlag()) { 2156 | clearAllFlags(); 2157 | setCarryFlag(); 2158 | } else { 2159 | clearAllFlags(); 2160 | } 2161 | setSubtractFlag(); 2162 | if(result == 0) { 2163 | setZeroFlag(); 2164 | } 2165 | if((result & 0xf) == 0xf) { 2166 | setHalfCarryFlag(); 2167 | } 2168 | return result; 2169 | } 2170 | 2171 | void DEC_A(u8 opcode) { // 0x3d 2172 | globalState.a = decrement(globalState.a); 2173 | globalState.pc++; 2174 | globalState.cycleCount += 4; 2175 | } 2176 | 2177 | void DEC_B(u8 opcode) { // 0x05 2178 | globalState.bc.hi = decrement(globalState.bc.hi); 2179 | globalState.pc++; 2180 | globalState.cycleCount += 4; 2181 | } 2182 | 2183 | void DEC_C(u8 opcode) { // 0x0d 2184 | globalState.bc.lo = decrement(globalState.bc.lo); 2185 | globalState.pc++; 2186 | globalState.cycleCount += 4; 2187 | } 2188 | 2189 | void DEC_D(u8 opcode) { // 0x15 2190 | globalState.de.hi = decrement(globalState.de.hi); 2191 | globalState.pc++; 2192 | globalState.cycleCount += 4; 2193 | } 2194 | 2195 | void DEC_E(u8 opcode) { // 0x1d 2196 | globalState.de.lo = decrement(globalState.de.lo); 2197 | globalState.pc++; 2198 | globalState.cycleCount += 4; 2199 | } 2200 | 2201 | void DEC_H(u8 opcode) { // 0x25 2202 | globalState.hl.hi = decrement(globalState.hl.hi); 2203 | globalState.pc++; 2204 | globalState.cycleCount += 4; 2205 | } 2206 | 2207 | void DEC_L(u8 opcode) { // 0x2d 2208 | globalState.hl.lo = decrement(globalState.hl.lo); 2209 | globalState.pc++; 2210 | globalState.cycleCount += 4; 2211 | } 2212 | 2213 | void DEC_DHL(u8 opcode) { // 0x35 2214 | // todo this one is hard. 2215 | u8 v = readByte(globalState.hl.v); 2216 | writeByte(decrement(v), globalState.hl.v); 2217 | globalState.pc++; 2218 | globalState.cycleCount += 12; 2219 | } 2220 | 2221 | void addToHl(u16 number) { 2222 | int result = globalState.hl.v + number; 2223 | if(getZeroFlag()) { 2224 | clearAllFlags(); 2225 | setZeroFlag(); 2226 | } else { 2227 | clearAllFlags(); 2228 | } 2229 | if(result & 0x10000) { 2230 | setCarryFlag(); 2231 | } 2232 | if((globalState.hl.v ^ number ^ (result & 0xffff)) & 0x1000) { 2233 | setHalfCarryFlag(); 2234 | } 2235 | globalState.hl.v = (u16)result; 2236 | } 2237 | 2238 | void ADD_HL_BC(u8 opcode) { // 09 2239 | globalState.pc++; 2240 | globalState.cycleCount += 8; 2241 | addToHl(globalState.bc.v); 2242 | } 2243 | 2244 | void ADD_HL_DE(u8 opcode) { // 19 2245 | globalState.pc++; 2246 | globalState.cycleCount += 8; 2247 | addToHl(globalState.de.v); 2248 | } 2249 | 2250 | void ADD_HL_HL(u8 opcode) { // 29 2251 | globalState.pc++; 2252 | globalState.cycleCount += 8; 2253 | addToHl(globalState.hl.v); 2254 | } 2255 | 2256 | void ADD_HL_SP(u8 opcode) { // 39 2257 | globalState.pc++; 2258 | globalState.cycleCount += 8; 2259 | addToHl(globalState.sp); 2260 | } 2261 | 2262 | void ADD_SP_n(u8 opcode) { // E8 2263 | s8 number = readByte(++globalState.pc); 2264 | globalState.pc++; 2265 | globalState.cycleCount += 16; 2266 | int result = globalState.sp + number; 2267 | clearAllFlags(); 2268 | if(((globalState.sp ^ number ^ (result & 0xffff)) & 0x100) == 0x100) { 2269 | setCarryFlag(); 2270 | } 2271 | if(((globalState.sp ^ number ^ (result & 0xffff)) & 0x10) == 0x10) { 2272 | setHalfCarryFlag(); 2273 | } 2274 | globalState.sp = (u16)result; 2275 | } 2276 | 2277 | // 16 bit incs 2278 | void INC_BC(u8 opcode) { // 03 2279 | globalState.pc++; 2280 | globalState.cycleCount += 8; 2281 | globalState.bc.v++; 2282 | } 2283 | 2284 | void INC_DE(u8 opcode) { // 13 2285 | globalState.pc++; 2286 | globalState.cycleCount += 8; 2287 | globalState.de.v++; 2288 | } 2289 | 2290 | void INC_HL(u8 opcode) { // 23 2291 | globalState.pc++; 2292 | globalState.cycleCount += 8; 2293 | globalState.hl.v++; 2294 | } 2295 | 2296 | void INC_SP(u8 opcode) { // 33 2297 | globalState.pc++; 2298 | globalState.cycleCount += 8; 2299 | globalState.sp++; 2300 | } 2301 | 2302 | // 16 bit decs 2303 | void DEC_BC(u8 opcode) { // 0B 2304 | globalState.pc++; 2305 | globalState.cycleCount += 8; 2306 | globalState.bc.v--; 2307 | } 2308 | 2309 | void DEC_DE(u8 opcode) { // 1B 2310 | globalState.pc++; 2311 | globalState.cycleCount += 8; 2312 | globalState.de.v--; 2313 | } 2314 | 2315 | void DEC_HL(u8 opcode) { // 2B 2316 | globalState.pc++; 2317 | globalState.cycleCount += 8; 2318 | globalState.hl.v--; 2319 | } 2320 | 2321 | void DEC_SP(u8 opcode) { // 3B 2322 | globalState.pc++; 2323 | globalState.cycleCount += 8; 2324 | globalState.sp--; 2325 | } 2326 | 2327 | void DAA(u8 opcode) { // 0x27 2328 | globalState.pc++; 2329 | globalState.cycleCount += 4; 2330 | u8 a = globalState.a; 2331 | 2332 | if(!getSubtractFlag()) { 2333 | if(getCarryFlag() || a > 0x99) { 2334 | a += 0x60; 2335 | setCarryFlag(); 2336 | } 2337 | if(getHalfCarryFlag() || (a & 0x0f) > 0x09) { 2338 | a += 0x6; 2339 | } 2340 | } else { 2341 | if(getCarryFlag()) { 2342 | a -= 0x60; 2343 | } 2344 | if(getHalfCarryFlag()) { 2345 | a -= 0x6; 2346 | } 2347 | } 2348 | clearZeroFlag(); 2349 | clearHalfCarryFlag(); 2350 | if(a == 0) { 2351 | setZeroFlag(); 2352 | } 2353 | globalState.a = (u8)a; 2354 | } 2355 | 2356 | void CPL(u8 opcode) { // 0x2f 2357 | globalState.pc++; 2358 | globalState.cycleCount += 4; 2359 | globalState.a = ~globalState.a; 2360 | setHalfCarryFlag(); 2361 | setSubtractFlag(); 2362 | } 2363 | 2364 | void CCF(u8 opcode) { // 0x3f 2365 | globalState.pc++; 2366 | globalState.cycleCount += 4; 2367 | if(getCarryFlag()) { 2368 | clearCarryFlag(); 2369 | } else { 2370 | setCarryFlag(); 2371 | } 2372 | clearHalfCarryFlag(); 2373 | clearSubtractFlag(); 2374 | } 2375 | 2376 | void SCF(u8 opcode) { // 0x37 2377 | globalState.pc++; 2378 | globalState.cycleCount += 4; 2379 | clearHalfCarryFlag(); 2380 | clearSubtractFlag(); 2381 | setCarryFlag(); 2382 | } 2383 | 2384 | void NOP(u8 opcode) { // 00 2385 | globalState.pc++; 2386 | globalState.cycleCount += 4; 2387 | } 2388 | 2389 | 2390 | void HALT(u8 opcode) { // 76 2391 | globalState.cycleCount += 4; 2392 | globalState.pc++; 2393 | //if(globalState.ime) { 2394 | globalState.halt = true; 2395 | //} 2396 | 2397 | } 2398 | 2399 | void STOP(u8 opcode) { // 10 (00) 2400 | printf("stop not yet implemented\n"); 2401 | assert(false); 2402 | } 2403 | 2404 | void DI(u8 opcode) { // F3 2405 | // todo instruction after bs 2406 | globalState.ime = 0; 2407 | globalState.cycleCount += 4; 2408 | globalState.pc++; 2409 | // printf("di not yet implemented\n"); 2410 | // assert(false); 2411 | } 2412 | 2413 | void EI(u8 opcode) { // FB 2414 | globalState.ime = 1; 2415 | globalState.cycleCount += 4; 2416 | globalState.pc++; 2417 | } 2418 | 2419 | void RLCA(u8 opcode) { // 07 2420 | globalState.pc++; 2421 | globalState.cycleCount += 4; 2422 | globalState.a = rlcReg(globalState.a, true); 2423 | } 2424 | 2425 | void RLA(u8 opcode) { // 17 2426 | globalState.pc++; 2427 | globalState.cycleCount += 4; 2428 | globalState.a = rlReg(globalState.a, true); 2429 | } 2430 | 2431 | void RRCA(u8 opcode) { // 0x0f 2432 | globalState.pc++; 2433 | globalState.cycleCount += 4; 2434 | globalState.a = rrc(globalState.a, true); 2435 | } 2436 | 2437 | void RRA(u8 opcode) { // 0x1f 2438 | globalState.pc++; 2439 | globalState.cycleCount += 4; 2440 | globalState.a = rr(globalState.a, true); 2441 | } 2442 | 2443 | void JP_nn(u8 opcodes) { // 0xc3 2444 | globalState.pc++; 2445 | u16 addr = readU16(globalState.pc); 2446 | globalState.pc += 2; 2447 | globalState.cycleCount += 12; 2448 | globalState.pc = addr; 2449 | } 2450 | 2451 | void JP_NZ_nn(u8 opcodes) { // 0xc2 2452 | globalState.pc++; 2453 | u16 addr = readU16(globalState.pc); 2454 | globalState.pc += 2; 2455 | if(!getZeroFlag()) { 2456 | globalState.pc = addr; 2457 | } 2458 | globalState.cycleCount += 12; 2459 | } 2460 | 2461 | void JP_Z_nn(u8 opcodes) { // 0xca 2462 | globalState.pc++; 2463 | u16 addr = readU16(globalState.pc); 2464 | globalState.pc += 2; 2465 | if(getZeroFlag()) { 2466 | globalState.pc = addr; 2467 | } 2468 | globalState.cycleCount += 12; 2469 | } 2470 | 2471 | void JP_NC_nn(u8 opcodes) { // 0xd2 2472 | globalState.pc++; 2473 | u16 addr = readU16(globalState.pc); 2474 | globalState.pc += 2; 2475 | if(!getCarryFlag()) { 2476 | globalState.pc = addr; 2477 | } 2478 | globalState.cycleCount += 12; 2479 | } 2480 | 2481 | void JP_C_nn(u8 opcodes) { // 0xda 2482 | globalState.pc++; 2483 | u16 addr = readU16(globalState.pc); 2484 | globalState.pc += 2; 2485 | if(getCarryFlag()) { 2486 | globalState.pc = addr; 2487 | } 2488 | globalState.cycleCount += 12; 2489 | } 2490 | 2491 | void JP_HL(u8 opcodes) { // 0xe9 2492 | globalState.pc = globalState.hl.v; 2493 | globalState.cycleCount += 4; 2494 | } 2495 | 2496 | void JR_n(u8 opcode) { // 18 2497 | s8 imm = readByte(++globalState.pc); 2498 | globalState.pc++; 2499 | globalState.pc += imm; 2500 | globalState.cycleCount += 8; 2501 | } 2502 | 2503 | void JR_NZ(u8 opcode) { // 20 2504 | s8 imm = readByte(++globalState.pc); 2505 | globalState.pc++; 2506 | if(!getZeroFlag()){ 2507 | globalState.pc += imm; 2508 | } 2509 | globalState.cycleCount += 8; 2510 | } 2511 | 2512 | void JR_Z(u8 opcode) { // 28 2513 | s8 imm = readByte(++globalState.pc); 2514 | globalState.pc++; 2515 | if(getZeroFlag()){ 2516 | globalState.pc += imm; 2517 | } 2518 | globalState.cycleCount += 8; 2519 | } 2520 | 2521 | void JR_NC(u8 opcode) { // 30 2522 | s8 imm = readByte(++globalState.pc); 2523 | globalState.pc++; 2524 | if(!getCarryFlag()){ 2525 | globalState.pc += imm; 2526 | } 2527 | globalState.cycleCount += 8; 2528 | } 2529 | 2530 | void JR_C(u8 opcode) { // 38 2531 | s8 imm = readByte(++globalState.pc); 2532 | globalState.pc++; 2533 | if(getCarryFlag()){ 2534 | globalState.pc += imm; 2535 | } 2536 | globalState.cycleCount += 8; 2537 | } 2538 | 2539 | 2540 | void CALL_nn(u8 opcode) { // 0xCD 2541 | globalState.pc++; 2542 | u16 addr = readU16(globalState.pc); 2543 | //printf("call address 0x%x\n", addr); 2544 | globalState.pc += 2; 2545 | writeU16(globalState.pc, globalState.sp - (u16)2); 2546 | globalState.sp -= 2; 2547 | globalState.pc = addr; 2548 | globalState.cycleCount += 12; 2549 | } 2550 | 2551 | void CALL_NZ(u8 opcode) { // 0xC4 2552 | globalState.pc++; 2553 | u16 addr = readU16(globalState.pc); 2554 | globalState.pc += 2; 2555 | if(!getZeroFlag()) { 2556 | writeU16(globalState.pc, globalState.sp - (u16)2); 2557 | globalState.sp -= 2; 2558 | globalState.pc = addr; 2559 | } 2560 | globalState.cycleCount += 12; 2561 | } 2562 | 2563 | void CALL_Z(u8 opcode) { // 0xCc 2564 | globalState.pc++; 2565 | u16 addr = readU16(globalState.pc); 2566 | globalState.pc += 2; 2567 | if(getZeroFlag()) { 2568 | writeU16(globalState.pc, globalState.sp - (u16)2); 2569 | globalState.sp -= 2; 2570 | globalState.pc = addr; 2571 | } 2572 | globalState.cycleCount += 12; 2573 | } 2574 | 2575 | 2576 | void CALL_NC(u8 opcode) { // 0d4 2577 | globalState.pc++; 2578 | u16 addr = readU16(globalState.pc); 2579 | globalState.pc += 2; 2580 | if(!getCarryFlag()) { 2581 | writeU16(globalState.pc, globalState.sp - (u16)2); 2582 | globalState.sp -= 2; 2583 | globalState.pc = addr; 2584 | } 2585 | globalState.cycleCount += 12; 2586 | } 2587 | 2588 | void CALL_C(u8 opcode) { // 0xdc 2589 | globalState.pc++; 2590 | u16 addr = readU16(globalState.pc); 2591 | globalState.pc += 2; 2592 | if(getCarryFlag()) { 2593 | writeU16(globalState.pc, globalState.sp - (u16)2); 2594 | globalState.sp -= 2; 2595 | globalState.pc = addr; 2596 | } 2597 | globalState.cycleCount += 12; 2598 | } 2599 | 2600 | void REST_00(u8 opcode) { // 0xc7 2601 | globalState.pc++; 2602 | writeU16(globalState.pc, globalState.sp - (u16)2); 2603 | globalState.sp -= 2; 2604 | globalState.pc = 0x0000; 2605 | globalState.cycleCount += 32; 2606 | } 2607 | 2608 | void REST_08(u8 opcode) { // 0xcf 2609 | globalState.pc++; 2610 | writeU16(globalState.pc, globalState.sp - (u16)2); 2611 | globalState.sp -= 2; 2612 | globalState.pc = 0x0008; 2613 | globalState.cycleCount += 32; 2614 | } 2615 | 2616 | void REST_10(u8 opcode) { // 0xd7 2617 | globalState.pc++; 2618 | writeU16(globalState.pc, globalState.sp - (u16)2); 2619 | globalState.sp -= 2; 2620 | globalState.pc = 0x0010; 2621 | globalState.cycleCount += 32; 2622 | } 2623 | 2624 | void REST_18(u8 opcode) { // 0xdf 2625 | globalState.pc++; 2626 | writeU16(globalState.pc, globalState.sp - (u16)2); 2627 | globalState.sp -= 2; 2628 | globalState.pc = 0x0018; 2629 | globalState.cycleCount += 32; 2630 | } 2631 | 2632 | void REST_20(u8 opcode) { // 0xe7 2633 | globalState.pc++; 2634 | writeU16(globalState.pc, globalState.sp - (u16)2); 2635 | globalState.sp -= 2; 2636 | globalState.pc = 0x0020; 2637 | globalState.cycleCount += 32; 2638 | } 2639 | 2640 | void REST_28(u8 opcode) { // 0xef 2641 | globalState.pc++; 2642 | writeU16(globalState.pc, globalState.sp - (u16)2); 2643 | globalState.sp -= 2; 2644 | globalState.pc = 0x0028; 2645 | globalState.cycleCount += 32; 2646 | } 2647 | 2648 | void REST_30(u8 opcode) { // 0xf7 2649 | globalState.pc++; 2650 | writeU16(globalState.pc, globalState.sp - (u16)2); 2651 | globalState.sp -= 2; 2652 | globalState.pc = 0x0030; 2653 | globalState.cycleCount += 32; 2654 | } 2655 | 2656 | void REST_38(u8 opcode) { // 0xff 2657 | globalState.pc++; 2658 | writeU16(globalState.pc, globalState.sp - (u16)2); 2659 | globalState.sp -= 2; 2660 | globalState.pc = 0x0038; 2661 | globalState.cycleCount += 32; 2662 | } 2663 | 2664 | void RET(u8 opcode) { // 0xc9 2665 | globalState.pc++; 2666 | globalState.cycleCount += 8; 2667 | globalState.pc = readU16(globalState.sp); 2668 | globalState.sp += 2; 2669 | } 2670 | 2671 | void RET_NZ(u8 opcode) { // 0xc0 2672 | globalState.pc++; 2673 | globalState.cycleCount += 8; 2674 | if(!getZeroFlag()) { 2675 | globalState.pc = readU16(globalState.sp); 2676 | globalState.sp += 2; 2677 | } 2678 | } 2679 | 2680 | void RET_Z(u8 opcode) { // 0xc8 2681 | globalState.pc++; 2682 | globalState.cycleCount += 8; 2683 | if(getZeroFlag()) { 2684 | globalState.pc = readU16(globalState.sp); 2685 | globalState.sp += 2; 2686 | } 2687 | } 2688 | 2689 | void RET_NC(u8 opcode) { // 0xd0 2690 | globalState.pc++; 2691 | globalState.cycleCount += 8; 2692 | if(!getCarryFlag()) { 2693 | globalState.pc = readU16(globalState.sp); 2694 | globalState.sp += 2; 2695 | } 2696 | } 2697 | 2698 | void RET_C(u8 opcode) { // 0xd8 2699 | globalState.pc++; 2700 | globalState.cycleCount += 8; 2701 | if(getCarryFlag()) { 2702 | globalState.pc = readU16(globalState.sp); 2703 | globalState.sp += 2; 2704 | } 2705 | } 2706 | 2707 | void RETI(u8 opcode) { // 0xd9 2708 | globalState.pc++; 2709 | globalState.cycleCount += 8; 2710 | globalState.pc = readU16(globalState.sp); 2711 | globalState.sp += 2; 2712 | globalState.ime = 1; 2713 | } 2714 | 2715 | // normal opcode handler table 2716 | static OpcodeHandler* opcodes[256] = 2717 | {NOP, LD_BC_nn, LD_DBC_A, INC_BC, INC_B, DEC_B, LD_B_n, RLCA, // 0x0 - 0x7 2718 | LD_Dnn_SP, ADD_HL_BC, LD_A_DBC, DEC_BC, INC_C, DEC_C, LD_C_n, RRCA, // 0x8 - 0xf 2719 | STOP, LD_DE_nn, LD_DDE_A, INC_DE, INC_D, DEC_D, LD_D_n, RLA, // 0x10 - 0x17 2720 | JR_n, ADD_HL_DE, LD_A_DDE, DEC_DE, INC_E, DEC_E, LD_E_n, RRA, // 0x18 - 0x1f 2721 | JR_NZ, LD_HL_nn, LDI_DHL_A, INC_HL, INC_H, DEC_H, LD_H_n, DAA, // 0x20 - 0x27 2722 | JR_Z, ADD_HL_HL, LDI_A_DHL, DEC_HL, INC_L, DEC_L, LD_L_n, CPL, // 0x28 - 0x2f 2723 | JR_NC, LD_SP_nn, LDD_DHL_A, INC_SP, INC_DHL, DEC_DHL, LD_DHL_n, SCF, // 0x30 - 0x37 2724 | JR_C, ADD_HL_SP, LDD_A_DHL, DEC_SP, INC_A, DEC_A, LD_A_n, CCF, // 0x38 - 0x3f 2725 | LD_B_B, LD_B_C, LD_B_D, LD_B_E, LD_B_H, LD_B_L, LD_B_DHL, LD_B_A, // 0x40 - 0x47 2726 | LD_C_B, LD_C_C, LD_C_D, LD_C_E, LD_C_H, LD_C_L, LD_C_DHL, LD_C_A, // 0x48 - 0x4f 2727 | LD_D_B, LD_D_C, LD_D_D, LD_D_E, LD_D_H, LD_D_L, LD_D_DHL, LD_D_A, // 0x50 - 0x57 2728 | LD_E_B, LD_E_C, LD_E_D, LD_E_E, LD_E_H, LD_E_L, LD_E_DHL, LD_E_A, // 0x58 - 0x5f 2729 | LD_H_B, LD_H_C, LD_H_D, LD_H_E, LD_H_H, LD_H_L, LD_H_DHL, LD_H_A, // 0x60 - 0x67 2730 | LD_L_B, LD_L_C, LD_L_D, LD_L_E, LD_L_H, LD_L_L, LD_L_DHL, LD_L_A, // 0x68 - 0x6f 2731 | LD_DHL_B, LD_DHL_C, LD_DHL_D, LD_DHL_E, LD_DHL_H, LD_DHL_L, HALT, LD_DHL_A, // 0x70 - 0x77 2732 | LD_A_B, LD_A_C, LD_A_D, LD_A_E, LD_A_H, LD_A_L, LD_A_DHL, LD_A_A , // 0x78 - 0x7f 2733 | ADD_B, ADD_C, ADD_D, ADD_E, ADD_H, ADD_L, ADD_DHL, ADD_A, // 0x80 - 0x87 2734 | ADC_B, ADC_C, ADC_D, ADC_E, ADC_H, ADC_L, ADC_DHL, ADC_A, // 0x88 - 0x8f 2735 | SUB_B, SUB_C, SUB_D, SUB_E, SUB_H, SUB_L, SUB_DHL, SUB_A, // 0x90 - 0x97 2736 | SBC_B, SBC_C, SBC_D, SBC_E, SBC_H, SBC_L, SBC_DHL, SBC_A, // 0x98 - 0x9f 2737 | AND_B, AND_C, AND_D, AND_E, AND_H, AND_L, AND_DHL, AND_A, // 0xa0 - 0xa7 2738 | XOR_B, XOR_C, XOR_D, XOR_E, XOR_H, XOR_L, XOR_DHL, XOR_A, // 0xa8 - 0xaf 2739 | OR_B, OR_C, OR_D, OR_E, OR_H, OR_L, OR_DHL, OR_A, // 0xb0 - 0xb7 2740 | CP_B, CP_C, CP_D, CP_E, CP_H, CP_L, CP_DHL, CP_A, // 0xb8 - 0xbf 2741 | RET_NZ, POP_BC, JP_NZ_nn, JP_nn, CALL_NZ, PUSH_BC, ADD_n, REST_00, // 0xc0 - 0xc7 2742 | RET_Z, RET, JP_Z_nn, DecodeCB, CALL_Z, CALL_nn, ADC_n, REST_08, // 0xc8 - 0xcf 2743 | RET_NC, POP_DE, JP_NC_nn, invHandler, CALL_NC, PUSH_DE, SUB_n, REST_10, // 0xd0 - 0xd7 2744 | RET_C, RETI, JP_C_nn, invHandler, CALL_C, invHandler, SBC_n, REST_18, // 0xd8 - 0xdf 2745 | LD_FF00_n_A, POP_HL, LD_FF00_C_A, invHandler, invHandler, PUSH_HL, AND_n, REST_20, // 0xe0 - 0xe7 2746 | ADD_SP_n, JP_HL, LD_Dnn_A, invHandler, invHandler, invHandler, XOR_n, REST_28, // 0xe8 - 0xef 2747 | LD_A_FF00_n, POP_AF, LD_A_FF00_C, DI, invHandler, PUSH_AF, OR_n, REST_30, // 0xf0 - 0xf7 2748 | LD_HL_SP_n, LD_SP_HL, LD_A_Dnn, EI, invHandler, invHandler, CP_n, REST_38}; // 0xf8 - 0xff 2749 | 2750 | 2751 | // reset the globalState structure, including registers and timers 2752 | void resetCpu() { 2753 | globalState.f = 0x1; // or 0x11? ( 2754 | globalState.a = 0xb0; // maybe f, a swap?? 2755 | globalState.bc.v = 0x0013; 2756 | globalState.de.v = 0x00d8; 2757 | globalState.hl.v = 0x014d; 2758 | globalState.sp = 0xfffe; 2759 | globalState.pc = 0x0; 2760 | globalState.halt = false; 2761 | globalState.cycleCount = 0; 2762 | globalState.divOffset = 0; 2763 | globalState.timSubcount = 0; 2764 | } 2765 | 2766 | // set the globalState so the next call to step() will run an ISR 2767 | void interrupt(u16 addr) { 2768 | globalState.ime = 0; // disable interrupts 2769 | writeU16(globalState.pc, globalState.sp - (u16)2); // push pc to stack 2770 | globalState.sp -= 2; // push pc to stack 2771 | globalState.cycleCount += 12; // timing 2772 | globalState.pc = addr; // jump to ISR 2773 | } 2774 | 2775 | // timer speeds: once this number of clock cycles has elapsed, the timer ticks. 2776 | static u32 timReset[4] = {(1 << 10), (1 << 4), (1 << 6), (1 << 8)}; 2777 | 2778 | // step the CPU 1 instruction 2779 | // returns number of clock cycles 2780 | u32 cpuStep() { 2781 | uint64_t oldCycleCount = globalState.cycleCount; 2782 | 2783 | // update div register 2784 | globalMemState.ioRegs[IO_DIV] = (u8)((globalState.cycleCount - globalState.divOffset) >> 8); 2785 | 2786 | // execute, if we aren't halted. 2787 | if(!globalState.halt) { 2788 | // fetch opcode 2789 | u8 opcode = readByte(globalState.pc); 2790 | // execute opcode 2791 | opcodes[opcode](opcode); 2792 | } 2793 | 2794 | assert(false); 2795 | // interrupts 2796 | if(globalState.ime && globalMemState.upperRam[0x7f] && globalMemState.ioRegs[IO_IF]) { 2797 | 2798 | // mask interrupts with the interrupt enable register at 0xffff. 2799 | u8 interrupts = globalMemState.upperRam[0x7f] & globalMemState.ioRegs[IO_IF]; 2800 | 2801 | if(interrupts & 0x01) { 2802 | globalMemState.ioRegs[IO_IF] &= ~1; 2803 | interrupt(VBLANK_INTERRUPT); 2804 | globalState.halt = false; 2805 | } else if(interrupts & 0x02) { 2806 | globalMemState.ioRegs[IO_IF] &= ~2; 2807 | interrupt(LCDC_INTERRUPT); 2808 | globalState.halt = false; 2809 | } else if(interrupts & 0x04) { 2810 | globalMemState.ioRegs[IO_IF] &= ~4; 2811 | interrupt(TIMER_INTERRUPT); 2812 | globalState.halt = false; 2813 | } else if(interrupts & 0x08) { 2814 | globalMemState.ioRegs[IO_IF] &= ~8; 2815 | interrupt(SERIAL_INTERRUPT); 2816 | globalState.halt = false; 2817 | } else if(interrupts & 0x10) { 2818 | globalMemState.ioRegs[IO_IF] &= ~0x10; 2819 | interrupt(HIGH_TO_LOW_P10_P13); 2820 | globalState.halt = false; 2821 | } 2822 | } 2823 | 2824 | // even if we have IME off, and we're halted, we're supposed to check IF and IE register 2825 | // this won't fire an interrupt, but will get us out of halt 2826 | // this behavior isn't well documented, but was required to pass cpu_instr.gb 2827 | // (though none of the games seem to need it...) 2828 | if(globalState.halt) { 2829 | globalState.cycleCount += 200; // just to keep ticking the timer... 2830 | u8 interrupts = globalMemState.upperRam[0x7f] & globalMemState.ioRegs[IO_IF]; 2831 | if(interrupts & 0x01) { 2832 | globalState.halt = false; 2833 | } else if(interrupts & 0x02) { 2834 | globalState.halt = false; 2835 | } else if(interrupts & 0x04) { 2836 | globalState.halt = false; 2837 | } else if(interrupts & 0x08) { 2838 | globalState.halt = false; 2839 | } else if(interrupts & 0x10) { 2840 | globalState.halt = false; 2841 | } 2842 | } 2843 | 2844 | 2845 | // cycle count 2846 | uint64_t cyclesThisIteration = globalState.cycleCount - oldCycleCount; 2847 | 2848 | // update timer 2849 | u8 tac = globalMemState.ioRegs[IO_TAC]; 2850 | bool ten = ((tac >> 2) & 1) != 0; // timer enable? 2851 | if(ten) { 2852 | u8 tclk = (tac & (u8)3); // timer speed 2853 | globalState.timSubcount += cyclesThisIteration; 2854 | if(globalState.timSubcount >= timReset[tclk]) { // timer tick 2855 | globalState.timSubcount = 0; 2856 | u8 timv = globalMemState.ioRegs[IO_TIMA]; // check for overflow 2857 | if(timv == 255) { 2858 | globalMemState.ioRegs[IO_IF] |= 4; // set interrupt 2859 | globalMemState.ioRegs[IO_TIMA] = globalMemState.ioRegs[IO_TMA]; // reset 2860 | } else { 2861 | globalMemState.ioRegs[IO_TIMA] = timv + (u8)1; // increment. 2862 | } 2863 | } 2864 | } 2865 | 2866 | return (u32)cyclesThisIteration; 2867 | } 2868 | 2869 | bool getZeroFlag() { 2870 | return (globalState.f & 0x80) != 0; 2871 | } 2872 | 2873 | bool getSubtractFlag() { 2874 | return (globalState.f & 0x40) != 0; 2875 | } 2876 | 2877 | bool getHalfCarryFlag() { 2878 | return (globalState.f & 0x20) != 0; 2879 | } 2880 | 2881 | bool getCarryFlag() { 2882 | return (globalState.f & 0x10) != 0; 2883 | } 2884 | 2885 | void setZeroFlag() { 2886 | globalState.f = globalState.f | (u8)0x80; 2887 | } 2888 | 2889 | void setSubtractFlag() { 2890 | globalState.f = globalState.f | (u8)0x40; 2891 | } 2892 | 2893 | void setHalfCarryFlag() { 2894 | globalState.f = globalState.f | (u8)0x20; 2895 | } 2896 | 2897 | void setCarryFlag() { 2898 | globalState.f = globalState.f | (u8)0x10; 2899 | } 2900 | 2901 | void clearZeroFlag(){ 2902 | globalState.f = globalState.f & ~((u8)0x80); 2903 | } 2904 | 2905 | void clearSubtractFlag(){ 2906 | globalState.f = globalState.f & ~((u8)0x40); 2907 | } 2908 | 2909 | void clearHalfCarryFlag(){ 2910 | globalState.f = globalState.f & ~((u8)0x20); 2911 | } 2912 | 2913 | void clearCarryFlag(){ 2914 | globalState.f = globalState.f & ~((u8)0x10); 2915 | } 2916 | 2917 | void clearAllFlags() { 2918 | clearZeroFlag(); 2919 | clearSubtractFlag(); 2920 | clearHalfCarryFlag(); 2921 | clearCarryFlag(); 2922 | } 2923 | --------------------------------------------------------------------------------