/* * (C) Copyright 2014 Leo C. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #define ODR 0x0c #define IDR 0x08 #include "debug.h" #include "serial.h" #include "z80-if.h" #include "../z180/hdrom.h" #define ESCCHAR ('^'-0x40) #define S_10MS_TO (1<<0) /* * LED Connections */ #define LED_PORT GPIOC #define LED_BLUE_PIN GPIO8 #define BLUE 8 #define LED_GREEN_PIN GPIO9 #define GREEN 9 #define LED_BLUE_ON() BBIO_PERIPH(LED_PORT+ODR, BLUE) = 1 #define LED_BLUE_OFF() BBIO_PERIPH(LED_PORT+ODR, BLUE) = 0 #define LED_BLUE_TOGGLE() BBIO_PERIPH(LED_PORT+ODR, BLUE) = !BBIO_PERIPH(LED_PORT+ODR, BLUE) #define LED_GREEN_ON() BBIO_PERIPH(LED_PORT+ODR, GREEN) = 1 #define LED_GREEN_OFF() BBIO_PERIPH(LED_PORT+ODR, GREEN) = 0 #define LED_GREEN_TOGGLE() BBIO_PERIPH(LED_PORT+ODR, GREEN) = !BBIO_PERIPH(LED_PORT+ODR, GREEN) /* * Button connections */ //BBIO_PERIPH(GPIOA+IDR, 0); #define KEY_PORT GPIOA_IDR #define KEY0 GPIO0 //#define KEY1 GPIO1 //#define KEY2 GPIO2 #define REPEAT_MASK KEY0 // repeat: key0 #define REPEAT_START 100 // after 1s #define REPEAT_NEXT 20 // every 200ms typedef enum { NOTHING, PULSE, BLINK1, BLINK2 } LED_MODE; typedef struct { uint8_t mode; uint8_t ontime, offtime; } led_stat_t; volatile uint8_t led_timer[2]; led_stat_t led_stat[2]; volatile int timeout_1s; volatile uint32_t Stat; /*--------------------------------------------------------------------------*/ static void clock_setup(void) { //rcc_clock_setup_in_hse_8mhz_out_24mhz(); rcc_clock_setup_in_hsi_out_24mhz(); /* Enable clocks for: GPIO port A (for GPIO_USART1_TX and Button) GPIO port C (LEDs) USART1 TIM16 (RST-Pin) TIM1 (IOCS1) */ rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM16EN); /* Enable clocks for: TIM3 */ rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM3EN); /* Enable clocks for: DMA1 */ rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_DMA1EN); } static void systick_setup(void) { /* SysTick interrupt every N clock pulses: set reload to N-1 */ STK_RVR = 24000000/1000 - 1; /* Set source to core clock, enable int and start counting. */ STK_CSR = STK_CSR_CLKSOURCE_AHB | STK_CSR_TICKINT | STK_CSR_ENABLE; } #if 0 static void nvic_setup(void) { // nvic_enable_irq(NVIC_RTC_IRQ); // nvic_set_priority(NVIC_RTC_IRQ, 1); } #endif static void tim3_setup(void) { TIM3_CR1 = TIM_CR1_CMS_EDGE | TIM_CR1_DIR_UP; TIM3_CCMR2 = 0 | TIM_CCMR2_OC4M_FORCE_LOW /* | TIM_CCMR2_OC4M_FORCE_HIGH */ /* | TIM_CCMR2_OC4M_PWM2 */ /* | TIM_CCMR2_OC4PE */ /* | TIM_CCMR2_OC4FE */ | TIM_CCMR2_CC4S_OUT; TIM3_CCER = TIM_CCER_CC4E | TIM_CCER_CC4P; TIM3_ARR = 48; /* default */ TIM3_CCR4 = 1; /* */ } static void gpio_setup(void) { /* Disable JTAG-DP, but leave SW-DP Enabled. (free PA15, PB3, PB4) Remap SPI1 to PB3, PB4, PB5 and PA15. Remap TIM3 (CH1/PC6, CH2/PC7, CH3/PC8, CH4/PC9) Port D0/Port D1 mapping on OSC_IN/OSC_OUT */ gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, AFIO_MAPR_SPI1_REMAP | AFIO_MAPR_TIM3_REMAP_FULL_REMAP | AFIO_MAPR_PD01_REMAP); /* LEDs and User Button. */ gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, LED_BLUE_PIN); gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, LED_GREEN_PIN); gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO0); } /*--------------------------------------------------------------------------*/ void delay_systicks(int ticks) { int start, stop, now; start = STK_CVR; stop = start - ticks; if (stop < 0) { stop += STK_RVR; do { now = STK_CVR; } while ((now > stop) || (now <= start)); } else { do { now = STK_CVR; } while ((now > stop) && (now <= start)); } } /*--------------------------------------------------------------------------*/ static void led_toggle(uint8_t lednr) { if (lednr == 0) LED_BLUE_TOGGLE(); else if (lednr == 1) LED_GREEN_TOGGLE(); } static void led_on(uint8_t lednr) { if (lednr == 0) LED_BLUE_ON(); else if (lednr == 1) LED_GREEN_ON(); } static void led_off(uint8_t lednr) { if (lednr == 0) LED_BLUE_OFF(); else if (lednr == 1) LED_GREEN_OFF(); } static uint8_t led_is_on(uint8_t lednr) { if (lednr == 0) return BBIO_PERIPH(LED_PORT+ODR, BLUE); else if (lednr == 1) return BBIO_PERIPH(LED_PORT+ODR, GREEN); else return 0; } static void ledset(uint8_t lednr, uint8_t what, uint8_t len) { led_stat[lednr].mode = what; switch (what) { case PULSE: led_stat[lednr].ontime = len; led_stat[lednr].offtime = 0; led_timer[lednr] = len; led_on(lednr); break; case BLINK1: case BLINK2: if (what == BLINK1) led_stat[lednr].offtime = 100 - len; else led_stat[lednr].offtime = 200 - len; led_stat[lednr].ontime = len; led_timer[lednr] = len; led_on(lednr); break; default: break; } } /*--------------------------------------------------------------------------*/ static volatile uint16_t key_state, key_press, // key press detect key_rpt; // key long press and repeat static uint16_t get_key_press(uint16_t key_mask) { __disable_irq(); // read and clear atomic ! key_mask &= key_press; // read key(s) key_press ^= key_mask; // clear key(s) __enable_irq(); return key_mask; } /* static uint16_t get_key_rpt(uint16_t key_mask) { __disable_irq(); // read and clear atomic ! key_mask &= key_rpt; // read key(s) key_rpt ^= key_mask; // clear key(s) __enable_irq(); return key_mask; } */ static uint16_t get_key_short(uint16_t key_mask) { __disable_irq(); // read key state and key press atomic ! return get_key_press(key_state & key_mask); } /* static uint16_t get_key_long(uint16_t key_mask) { return get_key_press(get_key_rpt(key_mask)); } */ static void key_timerproc() { static uint16_t key_in_last, rpt; uint16_t key_in, c; key_in = KEY_PORT; c = key_in_last & key_in & ~key_state; // key_state = key_state & key_in_last | (key_state | key_in_last) & key_in; // key_state = key_state & key_in | (key_state | key_in) & key_in_last; key_state = c | ((key_in_last | key_in) & key_state); // key_state = (key_state&key_in_last) | (key_state&key_in) | (key_in_last&key_in); key_press |= c; key_in_last = key_in; if ((key_state & REPEAT_MASK) == 0) // check repeat function rpt = REPEAT_START; if (--rpt == 0) { rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } } /*--------------------------------------------------------------------------*/ void sys_tick_handler(void) { static int_fast8_t tick_10ms = 0; static int_fast16_t count_ms = 0; int_fast8_t i; ++tick_10ms; if (tick_10ms == 10) { Stat |= S_10MS_TO; tick_10ms = 0; i = led_timer[0]; if (i) led_timer[0] = i - 1; i = led_timer[1]; if (i) led_timer[1] = i - 1; key_timerproc(); /* Drive timer procedure of low level disk I/O module */ //disk_timerproc(); } count_ms++; if (count_ms == 1000) { count_ms = 0; i = timeout_1s; if (i) timeout_1s = i - 1; } } void rtc_isr(void) { /* The interrupt flag isn't cleared by hardware, we have to do it. */ rtc_clear_flag(RTC_SEC); } /*--------------------------------------------------------------------------*/ void tim3_set(int mode) { uint16_t cc_mode; cc_mode = TIM_CCMR2_CC4S_OUT; TIM3_CR1 = TIM_CR1_CMS_EDGE | TIM_CR1_DIR_UP /*| TIM_CR1_OPM */ ; if (mode < 0) cc_mode |= TIM_CCMR2_OC4M_FORCE_LOW; else if (mode == 0) cc_mode |= TIM_CCMR2_OC4M_FORCE_HIGH; else { TIM3_ARR = mode; TIM3_CCR4 = mode/2; cc_mode |= TIM_CCMR2_OC4M_PWM2; } TIM3_CCMR2 = cc_mode; if (mode > 0) TIM3_CR1 |= TIM_CR1_CEN; } /*--------------------------------------------------------------------------*/ static uint32_t z80_sram_cmp(uint32_t addr, uint32_t length, uint8_t wval, int inc) { uint8_t rval; int_fast8_t errors = 0; DBG_P(1, "SRAM: Check %#.5x byte... ", length); while (length--) { if ((rval = z80_read(addr)) != wval) { if (errors == 0) { printf("\nSRAM: Address W R\n" \ " -------------\n"); // 12345 00 11 } printf(" %.5lx %.2x %.2x\n", addr, wval, rval); if (++errors > 16 ) break; } addr++; wval += inc; } DBG_P(1, "Done.\n"); return addr; } #if 0 static void z80_sram_fill(uint32_t addr, int length, uint8_t startval, int inc) { printf("SRAM: Write %#.5x byte... ", length); //fflush(stdout); while (length--) { z80_write(addr, startval); ++addr; startval += inc; } printf("Done.\n"); } void z80_sram_fill_string(uint32_t addr, int length, const char *text) { char c; const char *p = text; while (length--) { z80_write(addr++, c = *p++); if (c == 0) p = text; } } uint32_t z80_sram_cmp_string(uint32_t addr, int length, const char *text) { char c; const char *p = text; while (length--) { c = *p++; if (z80_read(addr) != c) break; ++addr; if (c == 0) p = text; } return addr; } const char * const qbfox = "Zhe quick brown fox jumps over the lazy dog!"; const char * const qbcat = "Zhe quick brown fox jumps over the lazy cat!"; #endif uint8_t z80_get_byte(uint32_t adr) { uint8_t data; z80_request_bus(); data = z80_read(adr), z80_release_bus(); return data; } /*--------------------------------------------------------------------------*/ static void do_10ms(void) { for (uint_fast8_t i = 0; i < 2; i++) { switch (led_stat[i].mode) { case PULSE: if (led_timer[i] == 0) { led_off(i); led_stat[i].mode = NOTHING; } break; case BLINK1: case BLINK2: if (led_timer[i] == 0) { if (led_is_on(i)) led_timer[i] = led_stat[i].offtime; else led_timer[i] = led_stat[i].ontime; led_toggle(i); } break; default: break; } } } struct msg_item { uint8_t fct; uint8_t sub_min, sub_max; void (*func)(uint8_t, int, uint8_t *); }; uint32_t msg_to_addr(uint8_t *msg) { uint32_t addr = msg[0] | (msg[1] << 8) | ((uint32_t)msg[2] << 16); return addr; } void do_msg_ini_msgfifo(uint8_t subf, int len, uint8_t * msg) { (void)subf; (void)len; z80_init_msg_fifo(msg_to_addr(msg)); } void do_msg_ini_memfifo(uint8_t subf, int len, uint8_t * msg) { (void)len; z80_memfifo_init(subf - 1, msg_to_addr(msg)); } void do_msg_char_out(uint8_t subf, int len, uint8_t * msg) { (void)subf; while (len--) putchar(*msg++); } const struct msg_item z80_messages[] = { { 0, /* fct nr. */ 0, 0, /* sub fct nr. from, to */ &do_msg_ini_msgfifo}, { 0, 1, 2, &do_msg_ini_memfifo}, { 1, 1, 1, &do_msg_char_out}, { 0xff, /* end mark */ 0, 0, 0}, }; void do_message(int len, uint8_t *msg) { uint8_t fct, sub_fct; int_fast8_t i = 0; if (len >= 2) { fct = *msg++; sub_fct = *msg++; len -= 2; while (fct != z80_messages[i].fct) ++i; if (z80_messages[i].fct == 0xff) { DBG_P(1, "do_message: Unknown function: %i, %i\n", fct, sub_fct); return; /* TODO: unknown message # */ } while (fct == z80_messages[i].fct) { if (sub_fct >= z80_messages[i].sub_min && sub_fct <= z80_messages[i].sub_max ) break; ++i; } if (z80_messages[i].fct != fct) { DBG_P(1, "do_message: Unknown sub function: %i, %i\n", fct, sub_fct); return; /* TODO: unknown message sub# */ } (z80_messages[i].func)(sub_fct, len, msg); } else { /* TODO: error */ DBG_P(1, "do_message: to few arguments (%i); this shouldn't happen!\n", len); } } #define CTRBUF_LEN 256 void check_msg_fifo(void) { int ch; static int_fast8_t state; static int msglen,idx; static uint8_t buffer[CTRBUF_LEN]; while (state != 3 && (ch = z80_msg_fifo_getc()) >= 0) { switch (state) { case 0: /* wait for start of message */ if (ch == 0x81) { msglen = 0; idx = 0; state = 1; } break; case 1: /* get msg len */ if (ch > 0 && ch <= CTRBUF_LEN) { msglen = ch; state = 2; } else state = 0; break; case 2: /* get message */ buffer[idx++] = ch; if (idx == msglen) state = 3; break; } } if (state == 3) { do_message(msglen, buffer); state = 0; } } void z80_load_mem(void) { unsigned sec = 0; uint32_t sec_base = hdrom_start; DBG_P(1, "Loading z80 memory... \n"); while (sec < hdrom_sections) { DBG_P(2, " From: 0x%.5lX to: 0x%.5lX (%5li bytes)\n", hdrom_address[sec], hdrom_address[sec]+hdrom_length_of_sections[sec] - 1, hdrom_length_of_sections[sec]); z80_write_block((unsigned char *) &hdrom[sec_base], /* src */ hdrom_address[sec], /* dest */ hdrom_length_of_sections[sec]); /* len */ sec_base+=hdrom_length_of_sections[sec]; sec++; } } /*--------------------------------------------------------------------------*/ int main(void) { int_fast8_t state = 0; int ch; clock_setup(); gpio_setup(); tim3_setup(); setvbuf(stdout, NULL, _IONBF, 0); serial_setup(); printf("\n(STM32F100+HD64180)_stamp Tester\n"); DBG_P(1, "z80_setup_bus... "); z80_setup_msg_fifo(); z80_setup_bus(); DBG_P(1, "done.\n"); /* * If the RTC is pre-configured just allow access, don't reconfigure. * Otherwise enable it with the LSE as clock source and 0x7fff as * prescale value. */ rtc_auto_awake(LSE, 0x7fff); systick_setup(); DBG_P(1, "Get bus... "); z80_busreq(LOW); z80_reset(HIGH); z80_request_bus(); DBG_P(1, "got it!\n"); z80_memset(0, 0x76, 0x80000); //z80_sram_fill(0, 512 * 1024, 0x76, 0); z80_sram_cmp(0, (uint32_t)512 * 1024, 0x76, 0); z80_load_mem(); z80_reset(LOW); DBG_P(1, "Bus released!\n"); z80_release_bus(); z80_reset(HIGH); DBG_P(1, "Reset released!\n"); ledset(0, BLINK1, 50); while (1) { if (Stat & S_10MS_TO) { Stat &= ~S_10MS_TO; do_10ms(); } if (get_key_short(KEY0)) { z80_reset_pulse(); } if ((ch = serial_getc()) >= 0) { switch (state) { case 0: if (ch == ESCCHAR) { state = 1; /* TODO: Timer starten */ } else z80_memfifo_putc(fifo_out, ch); break; case 1: switch (ch) { case 'h': /* test: green led on */ tim3_set(-1); break; case 'l': /* test: green led off */ tim3_set(0); break; case 'p': /* test: pulse on led pin */ tim3_set(24000000 / 1000000 * 5); /* 5 us */ break; case 'r': z80_reset_pulse(); break; case ESCCHAR: default: z80_memfifo_putc(fifo_out, ch); } state = 0; break; } } check_msg_fifo(); } return 0; }