diff --git a/Code/pico_multi_booter/picomite b/Code/pico_multi_booter/picomite deleted file mode 160000 index 6c312cc..0000000 --- a/Code/pico_multi_booter/picomite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6c312cc754c4347db0a2aba91bd63203cab7a21b diff --git a/Code/pico_multi_booter/picomite/.commit_hash b/Code/pico_multi_booter/picomite/.commit_hash new file mode 100644 index 0000000..4c7d27f --- /dev/null +++ b/Code/pico_multi_booter/picomite/.commit_hash @@ -0,0 +1,3 @@ +https://github.com/madcock/PicoMiteAllVersions + +6c312cc754c4347db0a2aba91bd63203cab7a21b diff --git a/Code/pico_multi_booter/picomite/AllCommands.h b/Code/pico_multi_booter/picomite/AllCommands.h new file mode 100644 index 0000000..5c55319 --- /dev/null +++ b/Code/pico_multi_booter/picomite/AllCommands.h @@ -0,0 +1,713 @@ +/* + * @cond + * The following section will be excluded from the documentation. + */ +/* ******************************************************************************** + the C language function associated with commands, functions or operators should be + declared here +**********************************************************************************/ +#if !defined(INCLUDE_COMMAND_TABLE) && !defined(INCLUDE_TOKEN_TABLE) +// format: +// void cmd_???(void) +// void fun_???(void) +// void op_???(void) + +void cmd_clear(void); +void cmd_continue(void); +void cmd_delete(void); +void cmd_dim(void); +void cmd_do(void); +void cmd_else(void); +void cmd_end(void); +void cmd_endfun(void); +void cmd_endsub(void); +void cmd_erase(void); +void cmd_error(void); +void cmd_exit(void); +void cmd_exitfor(void); +void cmd_for(void); +void cmd_subfun(void); +void cmd_gosub(void); +void cmd_goto(void); +void cmd_if(void); +void cmd_inc(void); +void cmd_input(void); +void cmd_let(void); +void cmd_lineinput(void); +void cmd_list(void); +void cmd_load(void); +void cmd_loop(void); +void cmd_merge(void); +void cmd_chain(void); +void cmd_new(void); +void cmd_next(void); +void cmd_null(void); +void cmd_on(void); +void cmd_print(void); +void cmd_randomize(void); +void cmd_read(void); +void cmd_restore(void); +void cmd_return(void); +void cmd_run(void); +void cmd_save(void); +void cmd_troff(void); +void cmd_tron(void); +void cmd_trace(void); +void cmd_const(void); +void cmd_select(void); +void cmd_case(void); +void cmd_option(void); +void cmd_dump(void); +void cmd_call(void); +void cmd_execute(void); +void cmd_mid(void); +void cmd_null(void); +void cmd_open(void); +void cmd_close(void); +void cmd_files(void); +void cmd_mkdir(void); +void cmd_rmdir(void); +void cmd_chdir(void); +void cmd_kill(void); +void cmd_copy(void); +void cmd_name(void); +void cmd_exitmmb(void); +void cmd_pause(void); +void cmd_timer(void); +void cmd_copyright(void); +void cmd_seek(void); +void cmd_library(void); +void cmd_pio(void); +void cmd_date(void); +void cmd_time(void); +void cmd_flash(void); +void cmd_var(void); +void cmd_flush(void); +void cmd_disk(void); +void cmd_play(void); +void cmd_text(void); +void cmd_pixel(void); +void cmd_circle(void); +void cmd_line(void); +void cmd_box(void); +void cmd_rbox(void); +void cmd_arc(void) ; +void cmd_triangle(void); +void cmd_blit(void); +void cmd_cls(void); +void cmd_font(void); +void cmd_colour(void); +void cmd_refresh(void); +void cmd_polygon(void); +void cmd_gui(void); +void cmd_tile(void); +void cmd_mode(void); +void cmd_3D(void); +void cmd_framebuffer(void); +void cmd_edit(void); +void cmd_editfile(void); +void cmd_port(void); +void cmd_adc(void); +void cmd_ir(void); +void cmd_lcd(void); +void cmd_keypad(); +void cmd_backlight(void); +void cmd_device(void); +void cmd_sync(void); +void cmd_setpin(void); +void cmd_pulse(void); +void cmd_pwm(void); +void cmd_pin(void); +void cmd_i2c(void); +void cmd_i2c2(void); +void cmd_rtc(void); +void cmd_math(void); +void cmd_memory(void); +void cmd_autosave(void); +void cmd_option(void); +void cmd_pause(void); +void cmd_timer(void); +void cmd_date(void); +void cmd_time(void); +void cmd_ireturn(void); +void cmd_poke(void); +void cmd_settick(void); +void cmd_watchdog(void); +void cmd_cpu(void); +void cmd_cfunction(void); +void cmd_longString(void); +void cmd_sort(void); +void cmd_csubinterrupt(void); +void cmd_library(void); +void cmd_onewire(void); +void cmd_ds18b20(void); +void cmd_spi(void); +void cmd_spi2(void); +void cmd_xmodem(void); +void cmd_ctrlval(void); +void cmd_GUIpage(unsigned char *p); +void cmd_gamepad(void); +void cmd_sprite(void); +void cmd_comment(void); +void cmd_endcomment(void); +void cmd_blitmemory(void); +void cmd_configure(void); +void cmd_colourmap(void); +void cmd_map(void); +void cmd_WS2812(void); +void cmd_DHT22(void); +void cmd_Classic(void); +void cmd_Nunchuck(void); +void cmd_mouse(void); +void cmd_camera(void); +void cmd_Servo(void); +void cmd_chain(void); +void cmd_psram(void); +void cmd_wrap(void); +void cmd_wraptarget(void); +void cmd_sideset(void); +void cmd_PIOline(void); +void cmd_program(void); +void cmd_endprogram(void); +void cmd_label(void); +void cmd_jmp(void); +void cmd_wait(void); +void cmd_in(void); +void cmd_out(void); +void cmd_push(void); +void cmd_pull(void); +void cmd_mov(void); +void cmd_nop(void); +void cmd_irqset(void); +void cmd_irqwait(void); +void cmd_irqclear(void); +void cmd_irqnowait(void); +void cmd_irq(void); +void cmd_set(void); +void cmd_byte(void); +void cmd_bit(void); +void cmd_flag(void); +void cmd_flags(void); +void cmd_frame(void); +void cmd_help(void); +void cmd_slice(void); +void cmd_insert(void); +void cmd_add(void); +void cmd_arrayset(void); +#ifdef PICOMITEWEB + void cmd_web(void); +#endif +#ifdef rp2350 + void cmd_loadCMM2(void); + void cmd_RunCMM2(void); +#endif +void op_invalid(void); +void op_exp(void); +void op_mul(void); +void op_div(void); +void op_divint(void); +void op_add(void); +void op_subtract(void); +void op_mod(void); +void op_ne(void); +void op_gte(void); +void op_lte(void); +void op_lt(void); +void op_gt(void); +void op_equal(void); +void op_and(void); +void op_or(void); +void op_xor(void); +void op_not(void); +void op_shiftleft(void); +void op_shiftright(void); +void op_inv(void); +void fun_pin(void); +void fun_port(void); +void fun_distance(void); +void fun_pulsin(void); +void fun_rgb(void); +void fun_mmhres(void); +void fun_mmvres(void); +void fun_mmcharwidth(void); +void fun_mmcharheight(void); +void fun_at(void); +void fun_pixel(void); +void fun_getscanline(void); +void fun_3D(void); +void fun_sprite(void); +void fun_eof(void); +void fun_loc(void); +void fun_lof(void); +void fun_cwd(void); +void fun_inputstr(void); +void fun_mmfname(void); +void fun_dir(void); +void fun_date(void); +void fun_time(void); +void fun_timer(void); +void fun_device(void); +void fun_pio(void); +void fun_abs(void); +void fun_asc(void); +void fun_atn(void); +void fun_atan2(void); +void fun_bin(void); +void fun_chr(void); +void fun_cint(void); +void fun_cos(void); +void fun_deg(void); +void fun_exp(void); +void fun_fix(void); +void fun_hex(void); +void fun_inkey(void); +void fun_instr(void); +void fun_int(void); +void fun_lcase(void); +void fun_left(void); +void fun_len(void); +void fun_log(void); +void fun_errno(void); +void fun_errmsg(void); +void fun_mid(void); +void fun_oct(void); +void fun_peek(void); +void fun_pi(void); +void fun_pos(void); +void fun_rad(void); +void fun_right(void); +void fun_rnd(void); +void fun_sgn(void); +void fun_sin(void); +void fun_space(void); +void fun_sqr(void); +void fun_str(void); +void fun_string(void); +void fun_tab(void); +void fun_tan(void); +void fun_ucase(void); +void fun_val(void); +void fun_eval(void); +void fun_version(void); +void fun_asin(void); +void fun_acos(void); +void fun_field(void); +void fun_max(void); +void fun_min(void); +void fun_bin2str(void); +void fun_str2bin(void); +void fun_test(void); +void fun_bound(void); +void fun_ternary(void); +void fun_call(void); +void fun_GPS(void); +void fun_mmi2c(void); +void fun_math(void); +void fun_timer(void); +void fun_date(void); +void fun_time(void); +void fun_keydown(void); +void fun_peek(void); +void fun_restart(void); +void fun_day(void); +void fun_info(void); +void fun_LLen(void); +void fun_LGetByte(void); +void fun_LGetStr(void); +void fun_LCompare(void); +void fun_LInstr(void); +void fun_epoch(void); +void fun_datetime(void); +void fun_json(void); +void cmd_update(void); +void fun_format(void); +void fun_mmOW(void); +void fun_ds18b20(void); +void fun_pixel(void); +void fun_getscanline(void); +void fun_spi(void); +void fun_spi2(void); +void fun_msgbox(void); +void fun_ctrlval(void); +void fun_mmhpos(void); +void fun_mmvpos(void); +void fun_tilde(void); +void fun_byte(void); +void fun_bit(void); +void fun_flag(void); +#ifdef PICOMITEWEB + void fun_json(void); +#endif +void fun_dev(void); +void fun_map(void); +#endif + +/* ******************************************************************************** + All command tokens tokens (eg, PRINT, FOR, etc) should be inserted in this table +**********************************************************************************/ +#ifdef INCLUDE_COMMAND_TABLE + + { (unsigned char *)"Call", T_CMD, 0, cmd_call }, + { (unsigned char *)"Clear", T_CMD, 0, cmd_clear }, + { (unsigned char *)"Continue", T_CMD, 0, cmd_continue }, + { (unsigned char *)"Data", T_CMD, 0, cmd_null }, + { (unsigned char *)"Dim", T_CMD, 0, cmd_dim }, + { (unsigned char *)"Do", T_CMD, 0, cmd_do }, + { (unsigned char *)"Else If", T_CMD, 0, cmd_else }, + { (unsigned char *)"End If", T_CMD, 0, cmd_null }, + { (unsigned char *)"Exit Do", T_CMD, 0, cmd_exit }, + { (unsigned char *)"ElseIf", T_CMD, 0, cmd_else }, + { (unsigned char *)"Case Else", T_CMD, 0, cmd_case }, + { (unsigned char *)"Else", T_CMD, 0, cmd_else }, + { (unsigned char *)"Select Case", T_CMD, 0, cmd_select }, + { (unsigned char *)"End Select", T_CMD, 0, cmd_null }, + { (unsigned char *)"Case", T_CMD, 0, cmd_case }, + { (unsigned char *)"EndIf", T_CMD, 0, cmd_null }, + { (unsigned char *)"End Function", T_CMD, 0, cmd_endfun }, // this entry must come before END and FUNCTION + { (unsigned char *)"End Sub", T_CMD, 0, cmd_return }, // this entry must come before END and SUB + { (unsigned char *)"End", T_CMD, 0, cmd_end }, + { (unsigned char *)"Erase", T_CMD, 0, cmd_erase }, + { (unsigned char *)"Error", T_CMD, 0, cmd_error }, + { (unsigned char *)"Exit For", T_CMD, 0, cmd_exitfor }, // this entry must come before EXIT and FOR + { (unsigned char *)"Exit Sub", T_CMD, 0, cmd_return }, // this entry must come before EXIT and SUB + { (unsigned char *)"Exit Function", T_CMD, 0, cmd_endfun }, // this entry must come before EXIT and FUNCTION + { (unsigned char *)"Exit", T_CMD, 0, cmd_exit }, + { (unsigned char *)"For", T_CMD, 0, cmd_for }, + { (unsigned char *)"Function", T_CMD, 0, cmd_subfun }, + { (unsigned char *)"GoSub", T_CMD, 0, cmd_gosub }, + { (unsigned char *)"GoTo", T_CMD, 0, cmd_goto }, + { (unsigned char *)"Inc", T_CMD, 0, cmd_inc }, + { (unsigned char *)"If", T_CMD, 0, cmd_if }, + { (unsigned char *)"Line Input", T_CMD, 0, cmd_lineinput}, // this entry must come before INPUT + { (unsigned char *)"Input", T_CMD, 0, cmd_input }, + { (unsigned char *)"Let", T_CMD, 0, cmd_let }, + { (unsigned char *)"List", T_CMD, 0, cmd_list }, + { (unsigned char *)"Load", T_CMD, 0, cmd_load }, + { (unsigned char *)"Local", T_CMD, 0, cmd_dim }, + { (unsigned char *)"Loop", T_CMD, 0, cmd_loop }, + { (unsigned char *)"Next", T_CMD, 0, cmd_next }, + { (unsigned char *)"On", T_CMD, 0, cmd_on }, + { (unsigned char *)"Print", T_CMD, 0, cmd_print }, + { (unsigned char *)"Read", T_CMD, 0, cmd_read }, + { (unsigned char *)"Rem", T_CMD, 0, cmd_null, }, + { (unsigned char *)"Restore", T_CMD, 0, cmd_restore }, + { (unsigned char *)"Return", T_CMD, 0, cmd_return, }, + { (unsigned char *)"Run", T_CMD, 0, cmd_run }, + { (unsigned char *)"Save", T_CMD, 0, cmd_save }, + { (unsigned char *)"Static", T_CMD, 0, cmd_dim }, + { (unsigned char *)"Sub", T_CMD, 0, cmd_subfun }, + { (unsigned char *)"Trace", T_CMD, 0, cmd_trace }, + { (unsigned char *)"While", T_CMD, 0, cmd_do }, + { (unsigned char *)"Const", T_CMD, 0, cmd_const }, + { (unsigned char *)"Execute", T_CMD, 0, cmd_execute }, + { (unsigned char *)"MID$(", T_CMD | T_FUN, 0, cmd_mid }, + { (unsigned char *)"/*", T_CMD, 0, cmd_comment }, + { (unsigned char *)"*/", T_CMD, 0, cmd_endcomment }, + { (unsigned char *)"Open", T_CMD, 0, cmd_open }, + { (unsigned char *)"Close", T_CMD, 0, cmd_close }, + { (unsigned char *)"Kill", T_CMD, 0, cmd_kill }, + { (unsigned char *)"Rmdir", T_CMD, 0, cmd_rmdir }, + { (unsigned char *)"Chdir", T_CMD, 0, cmd_chdir }, + { (unsigned char *)"Mkdir", T_CMD, 0, cmd_mkdir }, + { (unsigned char *)"Copy", T_CMD, 0, cmd_copy }, + { (unsigned char *)"Rename", T_CMD, 0, cmd_name }, + { (unsigned char *)"Seek", T_CMD, 0, cmd_seek }, + { (unsigned char *)"Flash", T_CMD, 0, cmd_flash }, + { (unsigned char *)"VAR", T_CMD, 0, cmd_var }, + { (unsigned char *)"Flush", T_CMD, 0, cmd_flush }, + { (unsigned char *)"Drive", T_CMD, 0, cmd_disk }, + { (unsigned char *)"Play", T_CMD, 0, cmd_play }, + { (unsigned char *)"PIO", T_CMD, 0, cmd_pio }, + { (unsigned char *)"Text", T_CMD, 0, cmd_text }, + { (unsigned char *)"Pixel", T_CMD, 0, cmd_pixel }, + { (unsigned char *)"Circle", T_CMD, 0, cmd_circle }, + { (unsigned char *)"Line", T_CMD, 0, cmd_line }, + { (unsigned char *)"Box", T_CMD, 0, cmd_box }, + { (unsigned char *)"RBox", T_CMD, 0, cmd_rbox }, + { (unsigned char *)"CLS", T_CMD, 0, cmd_cls }, + { (unsigned char *)"Font", T_CMD, 0, cmd_font }, + { (unsigned char *)"Triangle", T_CMD, 0, cmd_triangle }, + { (unsigned char *)"Arc", T_CMD, 0, cmd_arc }, + { (unsigned char *)"Polygon", T_CMD, 0, cmd_polygon }, + { (unsigned char *)"FRAMEBUFFER", T_CMD, 0, cmd_framebuffer }, + { (unsigned char *)"Sprite", T_CMD, 0, cmd_sprite }, + { (unsigned char *)"Blit", T_CMD, 0, cmd_blit }, + { (unsigned char *)"Edit File", T_CMD, 0, cmd_editfile }, + { (unsigned char *)"Edit", T_CMD, 0, cmd_edit }, + { (unsigned char *)"ADC", T_CMD, 0, cmd_adc }, + { (unsigned char *)"Pin(", T_CMD | T_FUN, 0, cmd_pin }, + { (unsigned char *)"SetPin", T_CMD, 0, cmd_setpin }, + { (unsigned char *)"Pulse", T_CMD, 0, cmd_pulse }, + { (unsigned char *)"Port(", T_CMD | T_FUN, 0, cmd_port }, + { (unsigned char *)"IR", T_CMD, 0, cmd_ir }, + { (unsigned char *)"Blit Memory", T_CMD, 0, cmd_blitmemory }, +#ifdef GUICONTROLS + { (unsigned char *)"GUI", T_CMD, 0, cmd_gui }, +#else + { (unsigned char *)"GUI", T_CMD, 0, cmd_guiMX170 }, +#endif + { (unsigned char *)"Device", T_CMD, 0, cmd_device }, + { (unsigned char *)"PWM", T_CMD, 0, cmd_pwm }, + { (unsigned char *)"CSub", T_CMD, 0, cmd_cfunction}, + { (unsigned char *)"End CSub", T_CMD, 0, cmd_null }, + { (unsigned char *)"SYNC", T_CMD, 0, cmd_sync }, + { (unsigned char *)"I2C", T_CMD, 0, cmd_i2c }, + { (unsigned char *)"I2C2", T_CMD, 0, cmd_i2c2 }, + { (unsigned char *)"RTC", T_CMD, 0, cmd_rtc }, + { (unsigned char *)"Math", T_CMD, 0, cmd_math }, + { (unsigned char *)"Memory", T_CMD, 0, cmd_memory }, + { (unsigned char *)"Option", T_CMD, 0, cmd_option }, + { (unsigned char *)"Pause", T_CMD, 0, cmd_pause }, + { (unsigned char *)"Timer", T_CMD | T_FUN, 0, cmd_timer }, + { (unsigned char *)"Date$", T_CMD | T_FUN, 0, cmd_date }, + { (unsigned char *)"Time$", T_CMD | T_FUN, 0, cmd_time }, + { (unsigned char *)"IReturn", T_CMD, 0, cmd_ireturn }, + { (unsigned char *)"Poke", T_CMD, 0, cmd_poke }, + { (unsigned char *)"SetTick", T_CMD, 0, cmd_settick }, + { (unsigned char *)"WatchDog", T_CMD, 0, cmd_watchdog }, + { (unsigned char *)"CPU", T_CMD, 0, cmd_cpu }, + { (unsigned char *)"Sort", T_CMD, 0, cmd_sort }, + { (unsigned char *)"DefineFont", T_CMD, 0, cmd_cfunction}, + { (unsigned char *)"End DefineFont", T_CMD, 0, cmd_null }, + { (unsigned char *)"LongString", T_CMD, 0, cmd_longString }, + { (unsigned char *)"Interrupt", T_CMD, 0, cmd_csubinterrupt}, + { (unsigned char *)"Library", T_CMD, 0, cmd_library }, + { (unsigned char *)"OneWire", T_CMD, 0, cmd_onewire }, + { (unsigned char *)"TEMPR START", T_CMD, 0, cmd_ds18b20 }, + { (unsigned char *)"SPI", T_CMD, 0, cmd_spi }, + { (unsigned char *)"SPI2", T_CMD, 0, cmd_spi2 }, + { (unsigned char *)"XModem", T_CMD, 0, cmd_xmodem }, + { (unsigned char *)"Cat", T_CMD, 0, cmd_inc }, + { (unsigned char *)"Color", T_CMD, 0, cmd_colour }, + { (unsigned char *)"Files", T_CMD, 0, cmd_files }, + { (unsigned char *)"New", T_CMD, 0, cmd_new }, + { (unsigned char *)"Autosave", T_CMD, 0, cmd_autosave }, + { (unsigned char *)"WS2812", T_CMD, 0, cmd_WS2812 }, + { (unsigned char *)"Keypad", T_CMD, 0, cmd_keypad }, + { (unsigned char *)"Humid", T_CMD, 0, cmd_DHT22 }, + { (unsigned char *)"LCD", T_CMD, 0, cmd_lcd }, + { (unsigned char *)"Wii Classic", T_CMD, 0, cmd_Classic }, + { (unsigned char *)"Wii Nunchuck", T_CMD, 0, cmd_Nunchuck }, + { (unsigned char *)"Wii", T_CMD, 0, cmd_Classic }, + { (unsigned char *)"Servo", T_CMD, 0, cmd_Servo }, + { (unsigned char *)"Mouse", T_CMD, 0, cmd_mouse }, + { (unsigned char *)"Chain", T_CMD, 0, cmd_chain }, + { (unsigned char *)"_wrap target", T_CMD, 0, cmd_wraptarget }, + { (unsigned char *)"_wrap", T_CMD, 0, cmd_wrap }, + { (unsigned char *)"_line", T_CMD, 0, cmd_PIOline }, + { (unsigned char *)"_program", T_CMD, 0, cmd_program }, + { (unsigned char *)"_end program", T_CMD, 0, cmd_endprogram }, + { (unsigned char *)"_side set", T_CMD, 0, cmd_sideset }, + { (unsigned char *)"_label", T_CMD, 0, cmd_label }, + { (unsigned char *)"Jmp", T_CMD, 0, cmd_jmp }, + { (unsigned char *)"Wait", T_CMD, 0, cmd_wait }, + { (unsigned char *)"In", T_CMD, 0, cmd_in }, + { (unsigned char *)"Out", T_CMD, 0, cmd_out }, + { (unsigned char *)"Push", T_CMD, 0, cmd_push }, + { (unsigned char *)"Pull", T_CMD, 0, cmd_pull }, + { (unsigned char *)"Mov", T_CMD, 0, cmd_mov }, + { (unsigned char *)"Nop", T_CMD, 0, cmd_nop }, + { (unsigned char *)"IRQ SET", T_CMD, 0, cmd_irqset }, + { (unsigned char *)"IRQ WAIT", T_CMD, 0, cmd_irqwait }, + { (unsigned char *)"IRQ CLEAR", T_CMD, 0, cmd_irqclear }, + { (unsigned char *)"IRQ NOWAIT",T_CMD, 0, cmd_irqnowait }, + { (unsigned char *)"IRQ", T_CMD, 0, cmd_irq }, + { (unsigned char *)"Set", T_CMD, 0, cmd_set }, + { (unsigned char *)"Byte(", T_CMD | T_FUN, 0, cmd_byte }, + { (unsigned char *)"Flag(", T_CMD | T_FUN, 0, cmd_flag }, +/*frame + { (unsigned char *)"Frame", T_CMD | T_FUN, 0, cmd_frame }, +*/ + #ifdef PICOMITEVGA + { (unsigned char *)"TILE", T_CMD, 0, cmd_tile }, + { (unsigned char *)"MODE", T_CMD, 0, cmd_mode }, + { (unsigned char *)"Map(", T_CMD | T_FUN , 0, cmd_map }, + { (unsigned char *)"Map", T_CMD, 0, cmd_map }, + { (unsigned char *)"Colour Map", T_CMD, 0, cmd_colourmap }, +#else + { (unsigned char *)"Camera", T_CMD, 0, cmd_camera }, + { (unsigned char *)"Refresh", T_CMD, 0, cmd_refresh }, +#endif +#ifdef GUICONTROLS + { (unsigned char *)"CtrlVal(", T_CMD | T_FUN, 0, cmd_ctrlval }, +#endif +#ifdef PICOMITE + { (unsigned char *)"Backlight", T_CMD, 0, cmd_backlight }, +#endif +#ifdef PICOMITEWEB + { (unsigned char *)"Backlight", T_CMD, 0, cmd_backlight }, + { (unsigned char *)"WEB", T_CMD, 0, cmd_web }, +#else + { (unsigned char *)"Draw3D", T_CMD, 0, cmd_3D }, +#endif +#ifndef USBKEYBOARD + { (unsigned char *)"Update Firmware", T_CMD, 0, cmd_update}, +#else + { (unsigned char *)"Gamepad", T_CMD, 0, cmd_gamepad }, +#endif + { (unsigned char *)"Configure", T_CMD, 0, cmd_configure }, + { (unsigned char *)"Colour", T_CMD, 0, cmd_colour }, +#ifdef rp2350 + { (unsigned char *)"CMM2 Load", T_CMD, 0, cmd_loadCMM2 }, + { (unsigned char *)"CMM2 Run", T_CMD, 0, cmd_RunCMM2 }, + { (unsigned char *)"Randomize", T_CMD, 0, cmd_null}, +#ifndef PICOMITEWEB + { (unsigned char *)"Ram", T_CMD, 0, cmd_psram }, +#endif +#else + { (unsigned char *)"Randomize", T_CMD, 0, cmd_randomize}, +#endif +{ (unsigned char *)"Bit(", T_CMD | T_FUN, 0, cmd_bit }, +{ (unsigned char *)"Flags", T_CMD | T_FUN, 0, cmd_flags }, +{ (unsigned char *)"Help", T_CMD | T_FUN, 0, cmd_help }, +{ (unsigned char *)"Array Slice", T_CMD, 0, cmd_slice }, +{ (unsigned char *)"Array Insert", T_CMD, 0, cmd_insert }, +{ (unsigned char *)"Array Add", T_CMD, 0, cmd_add }, +{ (unsigned char *)"Array Set", T_CMD, 0, cmd_arrayset }, +{ (unsigned char *)"", 0, 0, cmd_null, } // this dummy entry is always at the end +#endif +/* ******************************************************************************** + All other tokens (keywords, functions, operators) should be inserted in this table +**********************************************************************************/ +#ifdef INCLUDE_TOKEN_TABLE +// These 4 operators mustn't be moved + { (unsigned char *)"Not", T_OPER | T_NBR | T_INT, 3, op_not }, + { (unsigned char *)"INV", T_OPER | T_NBR | T_INT, 3, op_inv }, + { (unsigned char *)"+", T_OPER | T_NBR | T_INT | T_STR, 2, op_add }, + { (unsigned char *)"-", T_OPER | T_NBR | T_INT, 2, op_subtract }, +// + { (unsigned char *)"^", T_OPER | T_NBR | T_INT, 0, op_exp }, + { (unsigned char *)"*", T_OPER | T_NBR | T_INT, 1, op_mul }, + { (unsigned char *)"/", T_OPER | T_NBR, 1, op_div }, + { (unsigned char *)"\\", T_OPER | T_INT, 1, op_divint }, + { (unsigned char *)"Mod", T_OPER | T_INT, 1, op_mod }, + { (unsigned char *)"<<", T_OPER | T_INT, 4, op_shiftleft }, + { (unsigned char *)">>", T_OPER | T_INT, 4, op_shiftright }, + { (unsigned char *)"<>", T_OPER | T_NBR | T_INT | T_STR, 5, op_ne }, + { (unsigned char *)">=", T_OPER | T_NBR | T_INT | T_STR, 5, op_gte }, + { (unsigned char *)"<=", T_OPER | T_NBR | T_INT | T_STR, 5, op_lte }, + { (unsigned char *)"<", T_OPER | T_NBR | T_INT | T_STR, 5, op_lt }, + { (unsigned char *)">", T_OPER | T_NBR | T_INT | T_STR, 5, op_gt }, + { (unsigned char *)"=", T_OPER | T_NBR | T_INT | T_STR, 6, op_equal }, + { (unsigned char *)"And", T_OPER | T_INT, 7, op_and }, + { (unsigned char *)"Or", T_OPER | T_INT, 7, op_or }, + { (unsigned char *)"Xor", T_OPER | T_INT, 7, op_xor }, + { (unsigned char *)"ACos(", T_FUN | T_NBR, 0, fun_acos }, + { (unsigned char *)"Abs(", T_FUN | T_NBR | T_INT, 0, fun_abs }, + { (unsigned char *)"Asc(", T_FUN | T_INT, 0, fun_asc }, + { (unsigned char *)"ASin(", T_FUN | T_NBR, 0, fun_asin }, + { (unsigned char *)"Atn(", T_FUN | T_NBR, 0, fun_atn }, + { (unsigned char *)"Atan2(", T_FUN | T_NBR, 0, fun_atan2 }, + { (unsigned char *)"Bin$(", T_FUN | T_STR, 0, fun_bin }, + { (unsigned char *)"Bound(", T_FUN | T_INT, 0, fun_bound }, + { (unsigned char *)"Choice(", T_FUN | T_STR | T_INT | T_NBR, 0, fun_ternary }, + { (unsigned char *)"Chr$(", T_FUN | T_STR, 0, fun_chr, }, + { (unsigned char *)"Cint(", T_FUN | T_INT, 0, fun_cint }, + { (unsigned char *)"Cos(", T_FUN | T_NBR, 0, fun_cos }, + { (unsigned char *)"Deg(", T_FUN | T_NBR, 0, fun_deg }, + { (unsigned char *)"Exp(", T_FUN | T_NBR, 0, fun_exp }, + { (unsigned char *)"Field$(", T_FUN | T_STR, 0, fun_field }, + { (unsigned char *)"Fix(", T_FUN | T_INT, 0, fun_fix }, + { (unsigned char *)"Hex$(", T_FUN | T_STR, 0, fun_hex }, + { (unsigned char *)"Inkey$", T_FNA | T_STR, 0, fun_inkey }, + { (unsigned char *)"Instr(", T_FUN | T_INT, 0, fun_instr}, + { (unsigned char *)"Int(", T_FUN | T_INT, 0, fun_int }, + { (unsigned char *)"LCase$(", T_FUN | T_STR, 0, fun_lcase }, + { (unsigned char *)"Left$(", T_FUN | T_STR, 0, fun_left }, + { (unsigned char *)"Len(", T_FUN | T_INT, 0, fun_len }, + { (unsigned char *)"Log(", T_FUN | T_NBR, 0, fun_log }, + { (unsigned char *)"Mid$(", T_FUN | T_STR, 0, fun_mid }, + { (unsigned char *)"TEMPR(", T_FUN | T_NBR, 0, fun_ds18b20 }, + { (unsigned char *)"SPI(", T_FUN | T_INT, 0, fun_spi, }, + { (unsigned char *)"Oct$(", T_FUN | T_STR, 0, fun_oct }, + { (unsigned char *)"Pi", T_FNA | T_NBR, 0, fun_pi }, + { (unsigned char *)"Pos", T_FNA | T_INT, 0, fun_pos }, + { (unsigned char *)"Rad(", T_FUN | T_NBR, 0, fun_rad }, + { (unsigned char *)"Right$(", T_FUN | T_STR, 0, fun_right }, + { (unsigned char *)"Rnd(", T_FUN | T_NBR, 0, fun_rnd }, // this must come before Rnd - without bracket + { (unsigned char *)"Rnd", T_FNA | T_NBR, 0, fun_rnd }, // this must come after Rnd( + { (unsigned char *)"Sgn(", T_FUN | T_INT, 0, fun_sgn }, + { (unsigned char *)"Sin(", T_FUN | T_NBR, 0, fun_sin }, + { (unsigned char *)"Space$(", T_FUN | T_STR, 0, fun_space }, + { (unsigned char *)"Sqr(", T_FUN | T_NBR, 0, fun_sqr }, + { (unsigned char *)"Str$(", T_FUN | T_STR, 0, fun_str }, + { (unsigned char *)"String$(", T_FUN | T_STR, 0, fun_string }, + { (unsigned char *)"Tab(", T_FUN | T_STR, 0, fun_tab, }, + { (unsigned char *)"Tan(", T_FUN | T_NBR, 0, fun_tan }, + { (unsigned char *)"UCase$(", T_FUN | T_STR, 0, fun_ucase }, + { (unsigned char *)"Val(", T_FUN | T_NBR | T_INT, 0, fun_val }, + { (unsigned char *)"Eval(", T_FUN | T_NBR | T_INT | T_STR, 0, fun_eval }, + { (unsigned char *)"Max(", T_FUN | T_NBR, 0, fun_max }, + { (unsigned char *)"Min(", T_FUN | T_NBR, 0, fun_min }, + { (unsigned char *)"Bin2str$(", T_FUN | T_STR, 0, fun_bin2str }, + { (unsigned char *)"Str2bin(", T_FUN | T_NBR | T_INT, 0, fun_str2bin }, + { (unsigned char *)"Call(", T_FUN | T_STR | T_INT | T_NBR, 0, fun_call }, + { (unsigned char *)"For", T_NA, 0, op_invalid }, + { (unsigned char *)"Else", T_NA, 0, op_invalid }, + { (unsigned char *)"GoSub", T_NA, 0, op_invalid }, + { (unsigned char *)"GoTo", T_NA, 0, op_invalid }, + { (unsigned char *)"Step", T_NA, 0, op_invalid }, + { (unsigned char *)"Then", T_NA, 0, op_invalid }, + { (unsigned char *)"To", T_NA, 0, op_invalid }, + { (unsigned char *)"Until", T_NA, 0, op_invalid }, + { (unsigned char *)"While", T_NA, 0, op_invalid }, + { (unsigned char *)"Eof(", T_FUN | T_INT, 0, fun_eof }, + { (unsigned char *)"Loc(", T_FUN | T_INT, 0, fun_loc }, + { (unsigned char *)"Lof(", T_FUN | T_INT, 0, fun_lof }, + { (unsigned char *)"Cwd$", T_FNA | T_STR, 0, fun_cwd }, + { (unsigned char *)"As", T_NA, 0, op_invalid }, + { (unsigned char *)"Input$(", T_FUN | T_STR, 0, fun_inputstr }, + { (unsigned char *)"Dir$(", T_FUN | T_STR, 0, fun_dir }, + { (unsigned char *)"Pio(", T_FUN | T_INT, 0, fun_pio }, + { (unsigned char *)"RGB(", T_FUN | T_INT, 0, fun_rgb }, + { (unsigned char *)"Pixel(", T_FUN | T_INT, 0, fun_pixel }, + { (unsigned char *)"SPI2(", T_FUN | T_INT, 0, fun_spi2, }, + { (unsigned char *)"DEVICE(", T_FUN | T_INT| T_NBR | T_STR, 0, fun_dev, }, + { (unsigned char *)"@(", T_FUN | T_STR, 0, fun_at }, + { (unsigned char *)"Pin(", T_FUN | T_NBR | T_INT, 0, fun_pin }, + { (unsigned char *)"Port(", T_FUN | T_INT, 0, fun_port }, + { (unsigned char *)"Distance(", T_FUN | T_NBR, 0, fun_distance }, + { (unsigned char *)"Pulsin(", T_FUN | T_INT, 0, fun_pulsin }, + { (unsigned char *)"GPS(", T_FUN | T_NBR | T_INT| T_STR, 0, fun_GPS }, + { (unsigned char *)"Byte(", T_FUN | T_INT, 0, fun_byte, }, + { (unsigned char *)"Math(", T_FUN | T_NBR | T_INT, 0, fun_math }, + { (unsigned char *)"Timer", T_FNA | T_NBR , 0, fun_timer }, + { (unsigned char *)"LInStr(", T_FUN | T_INT, 0, fun_LInstr }, + { (unsigned char *)"LCompare(", T_FUN | T_INT, 0, fun_LCompare }, + { (unsigned char *)"LLen(", T_FUN | T_INT, 0, fun_LLen }, + { (unsigned char *)"LGetStr$(", T_FUN | T_STR, 0, fun_LGetStr }, + { (unsigned char *)"LGetByte(", T_FUN | T_INT, 0, fun_LGetByte }, + { (unsigned char *)"Date$", T_FNA | T_STR, 0, fun_date }, + { (unsigned char *)"Day$(", T_FUN | T_STR, 0, fun_day }, + { (unsigned char *)"Peek(", T_FUN | T_INT | T_STR | T_NBR, 0, fun_peek }, + { (unsigned char *)"Time$", T_FNA | T_STR, 0, fun_time }, + { (unsigned char *)"sprite(", T_FUN | T_INT | T_NBR, 0, fun_sprite }, + { (unsigned char *)"Flag(", T_FUN | T_INT, 0, fun_flag, }, + { (unsigned char *)"Epoch(", T_FUN | T_INT, 0, fun_epoch }, + { (unsigned char *)"DateTime$(", T_FUN | T_STR, 0, fun_datetime }, + { (unsigned char *)"MM.Info(", T_FUN | T_INT | T_NBR| T_STR, 0, fun_info }, + { (unsigned char *)"Format$(", T_FUN | T_STR, 0, fun_format }, + { (unsigned char*)"~(", T_FUN | T_INT | T_NBR | T_STR , 0, fun_tilde }, + #ifdef USBKEYBOARD + { (unsigned char*)"KeyDown(", T_FUN | T_INT, 0, fun_keydown }, +#endif +#ifdef PICOMITEVGA + { (unsigned char*)"DRAW3D(", T_FUN | T_INT, 0, fun_3D, }, + { (unsigned char *)"GetScanLine", T_FNA | T_INT, 0, fun_getscanline }, + { (unsigned char*)"Map(", T_FUN | T_INT, 0, fun_map, }, +#else + { (unsigned char *)"Touch(", T_FUN | T_INT, 0, fun_touch }, +#endif +#ifdef PICOMITEWEB + { (unsigned char *)"Json$(", T_FUN | T_STR, 0, fun_json }, +#endif +#ifdef GUICONTROLS + { (unsigned char *)"MsgBox(", T_FUN | T_INT, 0, fun_msgbox }, + { (unsigned char *)"CtrlVal(", T_FUN | T_NBR | T_STR, 0, fun_ctrlval }, +#endif +{ (unsigned char *)"Bit(", T_FUN | T_INT, 0, fun_bit, }, +{ (unsigned char *)"", 0, 0, cmd_null, } // this dummy entry is always at the end +#endif +/* @endcond */ + + diff --git a/Code/pico_multi_booter/picomite/ArialNumFontPlus.h b/Code/pico_multi_booter/picomite/ArialNumFontPlus.h new file mode 100644 index 0000000..cffcd83 --- /dev/null +++ b/Code/pico_multi_booter/picomite/ArialNumFontPlus.h @@ -0,0 +1,27 @@ +/* + * @cond + * The following section will be excluded from the documentation. + */ +// ArialNumFontPlus.c +// Font type : Special (SubSet) +// Font size : 32x50 pixels +// Memory usage : 2204 bytes + + #define PROGMEM + #define fontdatatype const unsigned char + +fontdatatype ArialNumFontPlus[2204] PROGMEM={ +0x20,0x32,0x30,0x0B, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0x00,0x1F,0xF8,0x00,0x00,0x7F,0xFE,0x00,0x00,0xFF,0xFF,0x00,0x01,0xFF,0xFF,0x00,0x03,0xF8,0x1F,0x80,0x03,0xF0,0x0F,0xC0,0x07,0xE0,0x07,0xC0,0x07,0xC0,0x03,0xE0,0x0F,0xC0,0x03,0xE0,0x0F,0x80,0x01,0xF0,0x0F,0x80,0x01,0xF0,0x1F,0x80,0x01,0xF0,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0xFC,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3F,0x00,0x00,0x7C,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x0F,0x80,0x01,0xF0,0x0F,0x80,0x01,0xF0,0x0F,0x80,0x01,0xF0,0x07,0xC0,0x03,0xE0,0x07,0xC0,0x03,0xE0,0x03,0xE0,0x07,0xC0,0x03,0xF0,0x0F,0xC0,0x01,0xF8,0x1F,0x80,0x00,0xFF,0xFF,0x00,0x00,0xFF,0xFE,0x00,0x00,0x7F,0xFC,0x00,0x00,0x1F,0xF8,0x00,0x00,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x03,0xF0,0x00,0x00,0x07,0xF0,0x00,0x00,0x0F,0xF0,0x00,0x00,0x1F,0xF0,0x00,0x00,0x3F,0xF0,0x00,0x00,0x7F,0xF0,0x00,0x01,0xFF,0xF0,0x00,0x03,0xFD,0xF0,0x00,0x0F,0xF1,0xF0,0x00,0x0F,0xE1,0xF0,0x00,0x0F,0x81,0xF0,0x00,0x0F,0x01,0xF0,0x00,0x0C,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //1 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0x00,0x7F,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0xC0,0x03,0xFF,0xFF,0xE0,0x07,0xE0,0x0F,0xF0,0x0F,0xC0,0x03,0xF0,0x0F,0x80,0x01,0xF0,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x3E,0x00,0x00,0xF8,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x03,0xF0,0x00,0x00,0x07,0xF0,0x00,0x00,0x0F,0xE0,0x00,0x00,0x1F,0xC0,0x00,0x00,0x3F,0x80,0x00,0x00,0x7F,0x00,0x00,0x00,0xFE,0x00,0x00,0x01,0xFC,0x00,0x00,0x03,0xF8,0x00,0x00,0x07,0xF0,0x00,0x00,0x0F,0xE0,0x00,0x00,0x1F,0xC0,0x00,0x00,0x3F,0x80,0x00,0x00,0x7F,0x00,0x00,0x00,0xFE,0x00,0x00,0x01,0xFC,0x00,0x00,0x03,0xF8,0x00,0x00,0x07,0xF0,0x00,0x00,0x07,0xE0,0x00,0x00,0x0F,0xC0,0x00,0x00,0x0F,0x80,0x00,0x00,0x1F,0x80,0x00,0x00,0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x7F,0xFF,0xFF,0xFC,0x7F,0xFF,0xFF,0xFC,0x7F,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 2 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0x00,0x3F,0xFC,0x00,0x00,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x03,0xE0,0x1F,0xC0,0x07,0xC0,0x0F,0xC0,0x07,0x80,0x07,0xE0,0x0F,0x80,0x03,0xE0,0x0F,0x00,0x03,0xE0,0x0F,0x00,0x03,0xF0,0x0F,0x00,0x01,0xF0,0x00,0x00,0x03,0xF0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x07,0xE0,0x00,0x00,0x0F,0xC0,0x00,0x00,0x1F,0xC0,0x00,0x00,0x3F,0x80,0x00,0x07,0xFF,0x00,0x00,0x07,0xFF,0x00,0x00,0x07,0xFF,0x00,0x00,0x07,0xFF,0x80,0x00,0x00,0x1F,0xC0,0x00,0x00,0x0F,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x01,0xF0,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x01,0xF8,0x0F,0x80,0x01,0xF8,0x0F,0x80,0x03,0xF0,0x0F,0xC0,0x07,0xF0,0x07,0xE0,0x0F,0xE0,0x03,0xFC,0x3F,0xC0,0x01,0xFF,0xFF,0x80,0x00,0xFF,0xFF,0x00,0x00,0x7F,0xFC,0x00,0x00,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 3 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0xFF,0x00,0x00,0x01,0xFF,0x00,0x00,0x01,0xFF,0x00,0x00,0x03,0xFF,0x00,0x00,0x07,0xDF,0x00,0x00,0x07,0xDF,0x00,0x00,0x0F,0x9F,0x00,0x00,0x0F,0x9F,0x00,0x00,0x1F,0x1F,0x00,0x00,0x3E,0x1F,0x00,0x00,0x7E,0x1F,0x00,0x00,0x7C,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x01,0xF0,0x1F,0x00,0x03,0xE0,0x1F,0x00,0x03,0xE0,0x1F,0x00,0x07,0xC0,0x1F,0x00,0x07,0xC0,0x1F,0x00,0x0F,0x80,0x1F,0x00,0x1F,0x80,0x1F,0x00,0x1F,0x00,0x1F,0x00,0x3E,0x00,0x1F,0x00,0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //4 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0xFF,0xF0,0x01,0xFF,0xFF,0xF0,0x01,0xFF,0xFF,0xF0,0x01,0xFF,0xFF,0xF0,0x01,0xFF,0xFF,0xF0,0x03,0xE0,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00,0x00,0x07,0xC0,0x00,0x00,0x07,0xC0,0x00,0x00,0x07,0xC0,0x00,0x00,0x07,0xC0,0x00,0x00,0x07,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0xF0,0x00,0x0F,0x8F,0xFE,0x00,0x0F,0xBF,0xFF,0x80,0x0F,0xFF,0xFF,0xC0,0x0F,0xFF,0xFF,0xE0,0x1F,0xFF,0x07,0xF0,0x1F,0xE0,0x03,0xF0,0x1F,0xC0,0x01,0xF8,0x1F,0x00,0x00,0xF8,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x3F,0x00,0x00,0xF8,0x3F,0x00,0x00,0xF8,0x3F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF0,0x1F,0x00,0x01,0xF0,0x1F,0x80,0x03,0xE0,0x0F,0xC0,0x07,0xE0,0x07,0xE0,0x1F,0xC0,0x03,0xFF,0xFF,0x80,0x03,0xFF,0xFF,0x00,0x01,0xFF,0xFE,0x00,0x00,0x7F,0xF8,0x00,0x00,0x0F,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 5 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0x00,0x3F,0xFE,0x00,0x00,0xFF,0xFF,0x00,0x01,0xFF,0xFF,0x80,0x03,0xFF,0xFF,0xC0,0x07,0xE0,0x07,0xE0,0x07,0xC0,0x03,0xF0,0x0F,0xC0,0x01,0xF0,0x0F,0x80,0x00,0xF8,0x1F,0x80,0x00,0xF8,0x1F,0x80,0x00,0xF8,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x03,0xF0,0x00,0x3F,0x1F,0xFC,0x00,0x3F,0x7F,0xFE,0x00,0x3F,0xFF,0xFF,0x80,0x3F,0xFF,0xFF,0xC0,0x3F,0xF8,0x0F,0xE0,0x3F,0xE0,0x03,0xF0,0x3F,0xE0,0x03,0xF0,0x3F,0xC0,0x01,0xF8,0x3F,0x80,0x00,0xF8,0x3F,0x80,0x00,0xF8,0x3F,0x00,0x00,0x7C,0x3F,0x00,0x00,0x7C,0x3F,0x00,0x00,0x7C,0x3F,0x00,0x00,0x7C,0x3F,0x00,0x00,0x7C,0x3F,0x00,0x00,0x7C,0x1F,0x00,0x00,0x7C,0x1F,0x80,0x00,0x78,0x1F,0x80,0x00,0xF8,0x1F,0x80,0x00,0xF8,0x0F,0x80,0x00,0xF8,0x0F,0xC0,0x01,0xF0,0x07,0xF0,0x03,0xF0,0x03,0xF8,0x0F,0xE0,0x01,0xFF,0xFF,0xC0,0x00,0xFF,0xFF,0x80,0x00,0x7F,0xFF,0x00,0x00,0x1F,0xFC,0x00,0x00,0x03,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 6 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,0xFC,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0xFC,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x07,0xC0,0x00,0x00,0x0F,0x80,0x00,0x00,0x1F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x01,0xF0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x07,0xC0,0x00,0x00,0x07,0xC0,0x00,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 7 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0x00,0x7F,0xFE,0x00,0x00,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x80,0x03,0xF8,0x1F,0xC0,0x07,0xE0,0x07,0xE0,0x07,0xC0,0x03,0xE0,0x0F,0xC0,0x03,0xF0,0x0F,0x80,0x01,0xF0,0x0F,0x80,0x01,0xF0,0x0F,0x80,0x01,0xF0,0x0F,0x80,0x01,0xF0,0x0F,0x80,0x01,0xF0,0x0F,0x80,0x01,0xF0,0x07,0xC0,0x03,0xE0,0x07,0xC0,0x03,0xE0,0x03,0xE0,0x07,0xC0,0x01,0xF0,0x07,0x80,0x00,0xFC,0x3F,0x00,0x00,0x7F,0xFF,0x00,0x00,0x3F,0xFC,0x00,0x00,0x7F,0xFE,0x00,0x00,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0xC0,0x07,0xF0,0x0F,0xE0,0x0F,0xC0,0x03,0xF0,0x0F,0x80,0x01,0xF0,0x1F,0x80,0x01,0xF8,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x3E,0x00,0x00,0x7C,0x1F,0x00,0x00,0xF8,0x1F,0x00,0x00,0xF8,0x0F,0x80,0x01,0xF0,0x0F,0x80,0x03,0xF0,0x07,0xC0,0x07,0xE0,0x03,0xF0,0x0F,0xC0,0x01,0xFF,0xFF,0x80,0x00,0xFF,0xFF,0x00,0x00,0x3F,0xFC,0x00,0x00,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 8 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0x00,0x3F,0xFC,0x00,0x00,0xFF,0xFF,0x00,0x01,0xFF,0xFF,0x80,0x03,0xFF,0xFF,0xC0,0x03,0xF8,0x1F,0xE0,0x07,0xE0,0x07,0xE0,0x07,0xE0,0x07,0xF0,0x0F,0xC0,0x03,0xF0,0x0F,0xC0,0x03,0xF8,0x1F,0x80,0x01,0xF8,0x1F,0x80,0x01,0xF8,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x80,0x01,0xFC,0x1F,0x80,0x01,0xFC,0x0F,0xC0,0x03,0xFC,0x0F,0xC0,0x03,0xFC,0x07,0xE0,0x07,0xFC,0x07,0xE0,0x07,0xFC,0x03,0xF8,0x1F,0xFC,0x01,0xFF,0xFF,0xFC,0x00,0xFF,0xFF,0xFC,0x00,0xFF,0xFE,0xFC,0x00,0x7F,0xF8,0xFC,0x00,0x1F,0xE0,0xFC,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0xFC,0x00,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF0,0x1F,0x00,0x03,0xF0,0x1F,0x80,0x07,0xE0,0x0F,0xC0,0x0F,0xE0,0x0F,0xE0,0x3F,0xC0,0x07,0xFF,0xFF,0x80,0x03,0xFF,0xFF,0x00,0x01,0xFF,0xFE,0x00,0x00,0xFF,0xF8,0x00,0x00,0x3F,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 9 neu +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x01,0xF0,0x00,0x00,0x03,0xF8,0x00,0x00,0x03,0xF8,0x00,0x00,0x03,0xF8,0x00,0x00,0x01,0xF0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x01,0xF0,0x00,0x00,0x03,0xF8,0x00,0x00,0x03,0xF8,0x00,0x00,0x03,0xF8,0x00,0x00,0x01,0xF0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // : +}; +/* @endcond */ diff --git a/Code/pico_multi_booter/picomite/Audio.c b/Code/pico_multi_booter/picomite/Audio.c new file mode 100644 index 0000000..f6d0da1 --- /dev/null +++ b/Code/pico_multi_booter/picomite/Audio.c @@ -0,0 +1,2095 @@ +/*********************************************************************************************************************** +PicoMite MMBasic + +audio.c + + Geoff Graham, Peter Mather +Copyright (c) 2021, All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the distribution. +3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed + on the console at startup (additional copyright messages may be added). +4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed + by the . +5. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software + without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +************************************************************************************************************************/ +/** +* @file Audio.c +* @author Geoff Graham, Peter Mather +* @brief Source for Audio MMBasic command +*/ +/** + * @cond + * The following section will be excluded from the documentation. + */ +#include +#include // Pascal +#include // Pascal +#include "ffconf.h" +#include "hardware/pwm.h" +#include "hardware/irq.h" +#include "hardware/flash.h" +#include "MMBasic_Includes.h" +#include "Hardware_Includes.h" +#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#define DR_WAV_IMPLEMENTATION +#define DR_WAV_NO_SIMD +#define DR_WAV_NO_STDIO +#include "dr_wav.h" +#define DR_FLAC_IMPLEMENTATION +#define DR_FLAC_NO_STDIO +#define DR_FLAC_NO_CRC +#define DR_FLAC_NO_SIMD +#define DR_FLAC_NO_OGG +#define FLAC_BUFFER_SIZE WAV_BUFFER_SIZE +#define DR_MP3_IMPLEMENTATION +#define DR_MP3_NO_STDIO +//#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +//#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +//#define DR_MP3_FLOAT_OUTPUT +#define DR_MP3_ONLY_MP3 +#define DR_MP3_NO_SIMD +#define DRMP3_DATA_CHUNK_SIZE 32768 +#define MP3_BUFFER_SIZE WAV_BUFFER_SIZE +#ifdef rp2350 +#include "dr_mp3.h" +#define MOD_BUFFER_SIZE WAV_BUFFER_SIZE +#else +#define MOD_BUFFER_SIZE (WAV_BUFFER_SIZE/4)*3 +#endif +#include "hardware/pio.h" +#include "hardware/pio_instructions.h" +#include "dr_flac.h" +#include "hxcmod.h" +#include "VS1053.h" +extern BYTE MDD_SDSPI_CardDetectState(void); +#define MAXALBUM 20 +extern int InitSDCard(void); +extern const int ErrorMap[21]; +extern char *GetCWD(void); +extern void ErrorCheck(int fnbr); +extern const int mapping[101]; +extern PIO pioi2s; +extern uint8_t i2ssm; + +/******************************************************************************************************************************************** +commands and functions + each function is responsible for decoding a command + all function names are in the form cmd_xxxx() (for a basic command) or fun_xxxx() (for a basic function) so, if you want to search for the + function responsible for the NAME command look for cmd_name + + There are 4 items of information that are setup before the command is run. + All these are globals. + + int cmdtoken This is the token number of the command (some commands can handle multiple + statement types and this helps them differentiate) + + char *cmdline This is the command line terminated with a zero char and trimmed of leading + spaces. It may exist anywhere in memory (or even ROM). + + char *nextstmt This is a pointer to the next statement to be executed. The only thing a + command can do with it is save it or change it to some other location. + + char *CurrentLinePtr This is read only and is set to NULL if the command is in immediate mode. + + The only actions a command can do to change the program flow is to change nextstmt or + execute longjmp(mark, 1) if it wants to abort the program. + + ********************************************************************************************************************************************/ + +// define the PWM output frequency for making a tone +const char* const PlayingStr[] = {"PAUSED TONE", "PAUSED FLAC", "PAUSED MP3", "PAUSED SOUND", "PAUSED MOD", "PAUSED WAV", "OFF", + "OFF", "TONE", "SOUND", "WAV", "FLAC", "MP3", + "MIDI", "", "MOD", "STREAM","" +} ; +volatile unsigned char PWM_count = 0; +volatile float PhaseM_left, PhaseM_right; +volatile uint64_t SoundPlay; + +volatile e_CurrentlyPlaying CurrentlyPlaying = P_NOTHING; +volatile int v_left, v_right, vol_left = 100, vol_right = 100; +char *wav_buf; // pointer to the buffer for received wav data +volatile int wav_filesize; // head and tail of the ring buffer for com1 +volatile int tickspersample; +char *WAVInterrupt = NULL; +bool WAVcomplete; +int WAV_fnbr=0; +int PWM_FREQ; +volatile int swingbuf = 0,nextbuf = 0, playreadcomplete = 1; +char *sbuff1=NULL, *sbuff2=NULL; +uint16_t *ubuff1, *ubuff2; +int16_t *g_buff1, *g_buff2; +char *modbuff=NULL; +modcontext *mcontext=NULL; +int modfilesamplerate=22050; +char *pbuffp; +void audio_checks(void); +uint16_t *playbuff; +int16_t *uplaybuff; +volatile int ppos = 0; // playing position for PLAY WAV +uint8_t nchannels; +volatile uint32_t bcount[3] = {0, 0, 0}; +volatile int sound_v_left[MAXSOUNDS]={[0 ... MAXSOUNDS-1 ]=25}; +volatile int sound_v_right[MAXSOUNDS]={[0 ... MAXSOUNDS-1 ]=25}; +volatile float sound_PhaseAC_left[MAXSOUNDS], sound_PhaseAC_right[MAXSOUNDS]; +volatile float sound_PhaseM_left[MAXSOUNDS], sound_PhaseM_right[MAXSOUNDS]; +volatile unsigned short * sound_mode_left[MAXSOUNDS]; +volatile unsigned short * sound_mode_right[MAXSOUNDS]; +volatile uint8_t audiorepeat=1; +volatile uint8_t mono; +//int debug = 0; +drwav *mywav; +drflac *myflac; +a_flist *alist=NULL; +uint8_t trackplaying=0, trackstoplay=0; +unsigned short *noisetable=NULL; +unsigned short *usertable=NULL; +const unsigned short whitenoise[2]={0}; +int noloop=0; +int8_t XDCS=-1,XCS=-1,DREQ=-1,XRST=-1; +uint8_t midienabled=0; +int streamsize=0; +volatile int *streamwritepointer=NULL; +volatile int *streamreadpointer=NULL; +char *streambuffer=NULL; +char WAVfilename[FF_MAX_LFN]={0}; +#ifdef rp2350 +drmp3 *mymp3; +#endif +void* my_malloc(size_t sz, void* pUserData) +{ + return GetMemory(sz); +} +void* my_realloc(void* p, size_t sz, void* pUserData) +{ + return ReAllocMemory((p), (sz)); +} +void my_free(void* p, void* pUserData) +{ + FreeMemory((void *)p); + p=NULL; +} +//************************************************************************************* + + + +volatile float PhaseAC_left, PhaseAC_right; +//#define PSpeedDiv (PeripheralBusSpeed)/2 +const unsigned short nulltable[1]={97}; +const unsigned short squaretable[1]={99}; +const unsigned short triangletable[4096]={ + 2000,2001,2003,2005,2007,2009,2011,2012,2014,2016,2018,2020,2022,2024,2025,2027, + 2029,2031,2033,2035,2037,2038,2040,2042,2044,2046,2048,2050,2051,2053,2055,2057, + 2059,2061,2063,2064,2066,2068,2070,2072,2074,2076,2077,2079,2081,2083,2085,2087, + 2089,2090,2092,2094,2096,2098,2100,2102,2103,2105,2107,2109,2111,2113,2115,2116, + 2118,2120,2122,2124,2126,2128,2129,2131,2133,2135,2137,2139,2141,2142,2144,2146, + 2148,2150,2152,2154,2155,2157,2159,2161,2163,2165,2166,2168,2170,2172,2174,2176, + 2178,2179,2181,2183,2185,2187,2189,2191,2192,2194,2196,2198,2200,2202,2204,2205, + 2207,2209,2211,2213,2215,2217,2218,2220,2222,2224,2226,2228,2230,2231,2233,2235, + 2237,2239,2241,2243,2244,2246,2248,2250,2252,2254,2256,2257,2259,2261,2263,2265, + 2267,2269,2270,2272,2274,2276,2278,2280,2282,2283,2285,2287,2289,2291,2293,2295, + 2296,2298,2300,2302,2304,2306,2308,2309,2311,2313,2315,2317,2319,2320,2322,2324, + 2326,2328,2330,2332,2333,2335,2337,2339,2341,2343,2345,2346,2348,2350,2352,2354, + 2356,2358,2359,2361,2363,2365,2367,2369,2371,2372,2374,2376,2378,2380,2382,2384, + 2385,2387,2389,2391,2393,2395,2397,2398,2400,2402,2404,2406,2408,2410,2411,2413, + 2415,2417,2419,2421,2423,2424,2426,2428,2430,2432,2434,2436,2437,2439,2441,2443, + 2445,2447,2449,2450,2452,2454,2456,2458,2460,2462,2463,2465,2467,2469,2471,2473, + 2475,2476,2478,2480,2482,2484,2486,2487,2489,2491,2493,2495,2497,2499,2500,2502, + 2504,2506,2508,2510,2512,2513,2515,2517,2519,2521,2523,2525,2526,2528,2530,2532, + 2534,2536,2538,2539,2541,2543,2545,2547,2549,2551,2552,2554,2556,2558,2560,2562, + 2564,2565,2567,2569,2571,2573,2575,2577,2578,2580,2582,2584,2586,2588,2590,2591, + 2593,2595,2597,2599,2601,2603,2604,2606,2608,2610,2612,2614,2616,2617,2619,2621, + 2623,2625,2627,2629,2630,2632,2634,2636,2638,2640,2641,2643,2645,2647,2649,2651, + 2653,2654,2656,2658,2660,2662,2664,2666,2667,2669,2671,2673,2675,2677,2679,2680, + 2682,2684,2686,2688,2690,2692,2693,2695,2697,2699,2701,2703,2705,2706,2708,2710, + 2712,2714,2716,2718,2719,2721,2723,2725,2727,2729,2731,2732,2734,2736,2738,2740, + 2742,2744,2745,2747,2749,2751,2753,2755,2757,2758,2760,2762,2764,2766,2768,2770, + 2771,2773,2775,2777,2779,2781,2783,2784,2786,2788,2790,2792,2794,2795,2797,2799, + 2801,2803,2805,2807,2808,2810,2812,2814,2816,2818,2820,2821,2823,2825,2827,2829, + 2831,2833,2834,2836,2838,2840,2842,2844,2846,2847,2849,2851,2853,2855,2857,2859, + 2860,2862,2864,2866,2868,2870,2872,2873,2875,2877,2879,2881,2883,2885,2886,2888, + 2890,2892,2894,2896,2898,2899,2901,2903,2905,2907,2909,2911,2912,2914,2916,2918, + 2920,2922,2924,2925,2927,2929,2931,2933,2935,2937,2938,2940,2942,2944,2946,2948, + 2950,2951,2953,2955,2957,2959,2961,2962,2964,2966,2968,2970,2972,2974,2975,2977, + 2979,2981,2983,2985,2987,2988,2990,2992,2994,2996,2998,3000,3001,3003,3005,3007, + 3009,3011,3013,3014,3016,3018,3020,3022,3024,3026,3027,3029,3031,3033,3035,3037, + 3039,3040,3042,3044,3046,3048,3050,3052,3053,3055,3057,3059,3061,3063,3065,3066, + 3068,3070,3072,3074,3076,3078,3079,3081,3083,3085,3087,3089,3091,3092,3094,3096, + 3098,3100,3102,3104,3105,3107,3109,3111,3113,3115,3116,3118,3120,3122,3124,3126, + 3128,3129,3131,3133,3135,3137,3139,3141,3142,3144,3146,3148,3150,3152,3154,3155, + 3157,3159,3161,3163,3165,3167,3168,3170,3172,3174,3176,3178,3180,3181,3183,3185, + 3187,3189,3191,3193,3194,3196,3198,3200,3202,3204,3206,3207,3209,3211,3213,3215, + 3217,3219,3220,3222,3224,3226,3228,3230,3232,3233,3235,3237,3239,3241,3243,3245, + 3246,3248,3250,3252,3254,3256,3258,3259,3261,3263,3265,3267,3269,3270,3272,3274, + 3276,3278,3280,3282,3283,3285,3287,3289,3291,3293,3295,3296,3298,3300,3302,3304, + 3306,3308,3309,3311,3313,3315,3317,3319,3321,3322,3324,3326,3328,3330,3332,3334, + 3335,3337,3339,3341,3343,3345,3347,3348,3350,3352,3354,3356,3358,3360,3361,3363, + 3365,3367,3369,3371,3373,3374,3376,3378,3380,3382,3384,3386,3387,3389,3391,3393, + 3395,3397,3399,3400,3402,3404,3406,3408,3410,3412,3413,3415,3417,3419,3421,3423, + 3425,3426,3428,3430,3432,3434,3436,3437,3439,3441,3443,3445,3447,3449,3450,3452, + 3454,3456,3458,3460,3462,3463,3465,3467,3469,3471,3473,3475,3476,3478,3480,3482, + 3484,3486,3488,3489,3491,3493,3495,3497,3499,3501,3502,3504,3506,3508,3510,3512, + 3514,3515,3517,3519,3521,3523,3525,3527,3528,3530,3532,3534,3536,3538,3540,3541, + 3543,3545,3547,3549,3551,3553,3554,3556,3558,3560,3562,3564,3566,3567,3569,3571, + 3573,3575,3577,3579,3580,3582,3584,3586,3588,3590,3591,3593,3595,3597,3599,3601, + 3603,3604,3606,3608,3610,3612,3614,3616,3617,3619,3621,3623,3625,3627,3629,3630, + 3632,3634,3636,3638,3640,3642,3643,3645,3647,3649,3651,3653,3655,3656,3658,3660, + 3662,3664,3666,3668,3669,3671,3673,3675,3677,3679,3681,3682,3684,3686,3688,3690, + 3692,3694,3695,3697,3699,3701,3703,3705,3707,3708,3710,3712,3714,3716,3718,3720, + 3721,3723,3725,3727,3729,3731,3733,3734,3736,3738,3740,3742,3744,3745,3747,3749, + 3751,3753,3755,3757,3758,3760,3762,3764,3766,3768,3770,3771,3773,3775,3777,3779, + 3781,3783,3784,3786,3788,3790,3792,3794,3796,3797,3799,3801,3803,3805,3807,3809, + 3810,3812,3814,3816,3818,3820,3822,3823,3825,3827,3829,3831,3833,3835,3836,3838, + 3840,3842,3844,3846,3848,3849,3851,3853,3855,3857,3859,3861,3862,3864,3866,3868, + 3870,3872,3874,3875,3877,3879,3881,3883,3885,3887,3888,3890,3892,3894,3896,3898, + 3896,3894,3892,3890,3888,3887,3885,3883,3881,3879,3877,3875,3874,3872,3870,3868, + 3866,3864,3862,3861,3859,3857,3855,3853,3851,3849,3848,3846,3844,3842,3840,3838, + 3836,3835,3833,3831,3829,3827,3825,3823,3822,3820,3818,3816,3814,3812,3810,3809, + 3807,3805,3803,3801,3799,3797,3796,3794,3792,3790,3788,3786,3784,3783,3781,3779, + 3777,3775,3773,3771,3770,3768,3766,3764,3762,3760,3758,3757,3755,3753,3751,3749, + 3747,3745,3744,3742,3740,3738,3736,3734,3733,3731,3729,3727,3725,3723,3721,3720, + 3718,3716,3714,3712,3710,3708,3707,3705,3703,3701,3699,3697,3695,3694,3692,3690, + 3688,3686,3684,3682,3681,3679,3677,3675,3673,3671,3669,3668,3666,3664,3662,3660, + 3658,3656,3655,3653,3651,3649,3647,3645,3643,3642,3640,3638,3636,3634,3632,3630, + 3629,3627,3625,3623,3621,3619,3617,3616,3614,3612,3610,3608,3606,3604,3603,3601, + 3599,3597,3595,3593,3591,3590,3588,3586,3584,3582,3580,3579,3577,3575,3573,3571, + 3569,3567,3566,3564,3562,3560,3558,3556,3554,3553,3551,3549,3547,3545,3543,3541, + 3540,3538,3536,3534,3532,3530,3528,3527,3525,3523,3521,3519,3517,3515,3514,3512, + 3510,3508,3506,3504,3502,3501,3499,3497,3495,3493,3491,3489,3488,3486,3484,3482, + 3480,3478,3476,3475,3473,3471,3469,3467,3465,3463,3462,3460,3458,3456,3454,3452, + 3450,3449,3447,3445,3443,3441,3439,3437,3436,3434,3432,3430,3428,3426,3425,3423, + 3421,3419,3417,3415,3413,3412,3410,3408,3406,3404,3402,3400,3399,3397,3395,3393, + 3391,3389,3387,3386,3384,3382,3380,3378,3376,3374,3373,3371,3369,3367,3365,3363, + 3361,3360,3358,3356,3354,3352,3350,3348,3347,3345,3343,3341,3339,3337,3335,3334, + 3332,3330,3328,3326,3324,3322,3321,3319,3317,3315,3313,3311,3309,3308,3306,3304, + 3302,3300,3298,3296,3295,3293,3291,3289,3287,3285,3283,3282,3280,3278,3276,3274, + 3272,3270,3269,3267,3265,3263,3261,3259,3258,3256,3254,3252,3250,3248,3246,3245, + 3243,3241,3239,3237,3235,3233,3232,3230,3228,3226,3224,3222,3220,3219,3217,3215, + 3213,3211,3209,3207,3206,3204,3202,3200,3198,3196,3194,3193,3191,3189,3187,3185, + 3183,3181,3180,3178,3176,3174,3172,3170,3168,3167,3165,3163,3161,3159,3157,3155, + 3154,3152,3150,3148,3146,3144,3142,3141,3139,3137,3135,3133,3131,3129,3128,3126, + 3124,3122,3120,3118,3116,3115,3113,3111,3109,3107,3105,3104,3102,3100,3098,3096, + 3094,3092,3091,3089,3087,3085,3083,3081,3079,3078,3076,3074,3072,3070,3068,3066, + 3065,3063,3061,3059,3057,3055,3053,3052,3050,3048,3046,3044,3042,3040,3039,3037, + 3035,3033,3031,3029,3027,3026,3024,3022,3020,3018,3016,3014,3013,3011,3009,3007, + 3005,3003,3001,3000,2998,2996,2994,2992,2990,2988,2987,2985,2983,2981,2979,2977, + 2975,2974,2972,2970,2968,2966,2964,2962,2961,2959,2957,2955,2953,2951,2950,2948, + 2946,2944,2942,2940,2938,2937,2935,2933,2931,2929,2927,2925,2924,2922,2920,2918, + 2916,2914,2912,2911,2909,2907,2905,2903,2901,2899,2898,2896,2894,2892,2890,2888, + 2886,2885,2883,2881,2879,2877,2875,2873,2872,2870,2868,2866,2864,2862,2860,2859, + 2857,2855,2853,2851,2849,2847,2846,2844,2842,2840,2838,2836,2834,2833,2831,2829, + 2827,2825,2823,2821,2820,2818,2816,2814,2812,2810,2808,2807,2805,2803,2801,2799, + 2797,2795,2794,2792,2790,2788,2786,2784,2783,2781,2779,2777,2775,2773,2771,2770, + 2768,2766,2764,2762,2760,2758,2757,2755,2753,2751,2749,2747,2745,2744,2742,2740, + 2738,2736,2734,2732,2731,2729,2727,2725,2723,2721,2719,2718,2716,2714,2712,2710, + 2708,2706,2705,2703,2701,2699,2697,2695,2693,2692,2690,2688,2686,2684,2682,2680, + 2679,2677,2675,2673,2671,2669,2667,2666,2664,2662,2660,2658,2656,2654,2653,2651, + 2649,2647,2645,2643,2641,2640,2638,2636,2634,2632,2630,2629,2627,2625,2623,2621, + 2619,2617,2616,2614,2612,2610,2608,2606,2604,2603,2601,2599,2597,2595,2593,2591, + 2590,2588,2586,2584,2582,2580,2578,2577,2575,2573,2571,2569,2567,2565,2564,2562, + 2560,2558,2556,2554,2552,2551,2549,2547,2545,2543,2541,2539,2538,2536,2534,2532, + 2530,2528,2526,2525,2523,2521,2519,2517,2515,2513,2512,2510,2508,2506,2504,2502, + 2500,2499,2497,2495,2493,2491,2489,2487,2486,2484,2482,2480,2478,2476,2475,2473, + 2471,2469,2467,2465,2463,2462,2460,2458,2456,2454,2452,2450,2449,2447,2445,2443, + 2441,2439,2437,2436,2434,2432,2430,2428,2426,2424,2423,2421,2419,2417,2415,2413, + 2411,2410,2408,2406,2404,2402,2400,2398,2397,2395,2393,2391,2389,2387,2385,2384, + 2382,2380,2378,2376,2374,2372,2371,2369,2367,2365,2363,2361,2359,2358,2356,2354, + 2352,2350,2348,2346,2345,2343,2341,2339,2337,2335,2333,2332,2330,2328,2326,2324, + 2322,2320,2319,2317,2315,2313,2311,2309,2308,2306,2304,2302,2300,2298,2296,2295, + 2293,2291,2289,2287,2285,2283,2282,2280,2278,2276,2274,2272,2270,2269,2267,2265, + 2263,2261,2259,2257,2256,2254,2252,2250,2248,2246,2244,2243,2241,2239,2237,2235, + 2233,2231,2230,2228,2226,2224,2222,2220,2218,2217,2215,2213,2211,2209,2207,2205, + 2204,2202,2200,2198,2196,2194,2192,2191,2189,2187,2185,2183,2181,2179,2178,2176, + 2174,2172,2170,2168,2166,2165,2163,2161,2159,2157,2155,2154,2152,2150,2148,2146, + 2144,2142,2141,2139,2137,2135,2133,2131,2129,2128,2126,2124,2122,2120,2118,2116, + 2115,2113,2111,2109,2107,2105,2103,2102,2100,2098,2096,2094,2092,2090,2089,2087, + 2085,2083,2081,2079,2077,2076,2074,2072,2070,2068,2066,2064,2063,2061,2059,2057, + 2055,2053,2051,2050,2048,2046,2044,2042,2040,2038,2037,2035,2033,2031,2029,2027, + 2025,2024,2022,2020,2018,2016,2014,2012,2011,2009,2007,2005,2003,2001,2000,1998, + 1996,1994,1992,1990,1988,1987,1985,1983,1981,1979,1977,1975,1974,1972,1970,1968, + 1966,1964,1962,1961,1959,1957,1955,1953,1951,1949,1948,1946,1944,1942,1940,1938, + 1936,1935,1933,1931,1929,1927,1925,1923,1922,1920,1918,1916,1914,1912,1910,1909, + 1907,1905,1903,1901,1899,1897,1896,1894,1892,1890,1888,1886,1884,1883,1881,1879, + 1877,1875,1873,1871,1870,1868,1866,1864,1862,1860,1858,1857,1855,1853,1851,1849, + 1847,1845,1844,1842,1840,1838,1836,1834,1833,1831,1829,1827,1825,1823,1821,1820, + 1818,1816,1814,1812,1810,1808,1807,1805,1803,1801,1799,1797,1795,1794,1792,1790, + 1788,1786,1784,1782,1781,1779,1777,1775,1773,1771,1769,1768,1766,1764,1762,1760, + 1758,1756,1755,1753,1751,1749,1747,1745,1743,1742,1740,1738,1736,1734,1732,1730, + 1729,1727,1725,1723,1721,1719,1717,1716,1714,1712,1710,1708,1706,1704,1703,1701, + 1699,1697,1695,1693,1691,1690,1688,1686,1684,1682,1680,1679,1677,1675,1673,1671, + 1669,1667,1666,1664,1662,1660,1658,1656,1654,1653,1651,1649,1647,1645,1643,1641, + 1640,1638,1636,1634,1632,1630,1628,1627,1625,1623,1621,1619,1617,1615,1614,1612, + 1610,1608,1606,1604,1602,1601,1599,1597,1595,1593,1591,1589,1588,1586,1584,1582, + 1580,1578,1576,1575,1573,1571,1569,1567,1565,1563,1562,1560,1558,1556,1554,1552, + 1550,1549,1547,1545,1543,1541,1539,1537,1536,1534,1532,1530,1528,1526,1525,1523, + 1521,1519,1517,1515,1513,1512,1510,1508,1506,1504,1502,1500,1499,1497,1495,1493, + 1491,1489,1487,1486,1484,1482,1480,1478,1476,1474,1473,1471,1469,1467,1465,1463, + 1461,1460,1458,1456,1454,1452,1450,1448,1447,1445,1443,1441,1439,1437,1435,1434, + 1432,1430,1428,1426,1424,1422,1421,1419,1417,1415,1413,1411,1409,1408,1406,1404, + 1402,1400,1398,1396,1395,1393,1391,1389,1387,1385,1383,1382,1380,1378,1376,1374, + 1372,1370,1369,1367,1365,1363,1361,1359,1358,1356,1354,1352,1350,1348,1346,1345, + 1343,1341,1339,1337,1335,1333,1332,1330,1328,1326,1324,1322,1320,1319,1317,1315, + 1313,1311,1309,1307,1306,1304,1302,1300,1298,1296,1294,1293,1291,1289,1287,1285, + 1283,1281,1280,1278,1276,1274,1272,1270,1268,1267,1265,1263,1261,1259,1257,1255, + 1254,1252,1250,1248,1246,1244,1242,1241,1239,1237,1235,1233,1231,1229,1228,1226, + 1224,1222,1220,1218,1216,1215,1213,1211,1209,1207,1205,1204,1202,1200,1198,1196, + 1194,1192,1191,1189,1187,1185,1183,1181,1179,1178,1176,1174,1172,1170,1168,1166, + 1165,1163,1161,1159,1157,1155,1153,1152,1150,1148,1146,1144,1142,1140,1139,1137, + 1135,1133,1131,1129,1127,1126,1124,1122,1120,1118,1116,1114,1113,1111,1109,1107, + 1105,1103,1101,1100,1098,1096,1094,1092,1090,1088,1087,1085,1083,1081,1079,1077, + 1075,1074,1072,1070,1068,1066,1064,1062,1061,1059,1057,1055,1053,1051,1050,1048, + 1046,1044,1042,1040,1038,1037,1035,1033,1031,1029,1027,1025,1024,1022,1020,1018, + 1016,1014,1012,1011,1009,1007,1005,1003,1001,999,998,996,994,992,990,988, + 986,985,983,981,979,977,975,973,972,970,968,966,964,962,960,959, + 957,955,953,951,949,947,946,944,942,940,938,936,934,933,931,929, + 927,925,923,921,920,918,916,914,912,910,908,907,905,903,901,899, + 897,895,894,892,890,888,886,884,883,881,879,877,875,873,871,870, + 868,866,864,862,860,858,857,855,853,851,849,847,845,844,842,840, + 838,836,834,832,831,829,827,825,823,821,819,818,816,814,812,810, + 808,806,805,803,801,799,797,795,793,792,790,788,786,784,782,780, + 779,777,775,773,771,769,767,766,764,762,760,758,756,754,753,751, + 749,747,745,743,741,740,738,736,734,732,730,729,727,725,723,721, + 719,717,716,714,712,710,708,706,704,703,701,699,697,695,693,691, + 690,688,686,684,682,680,678,677,675,673,671,669,667,665,664,662, + 660,658,656,654,652,651,649,647,645,643,641,639,638,636,634,632, + 630,628,626,625,623,621,619,617,615,613,612,610,608,606,604,602, + 600,599,597,595,593,591,589,587,586,584,582,580,578,576,575,573, + 571,569,567,565,563,562,560,558,556,554,552,550,549,547,545,543, + 541,539,537,536,534,532,530,528,526,524,523,521,519,517,515,513, + 511,510,508,506,504,502,500,498,497,495,493,491,489,487,485,484, + 482,480,478,476,474,472,471,469,467,465,463,461,459,458,456,454, + 452,450,448,446,445,443,441,439,437,435,433,432,430,428,426,424, + 422,420,419,417,415,413,411,409,408,406,404,402,400,398,396,395, + 393,391,389,387,385,383,382,380,378,376,374,372,370,369,367,365, + 363,361,359,357,356,354,352,350,348,346,344,343,341,339,337,335, + 333,331,330,328,326,324,322,320,318,317,315,313,311,309,307,305, + 304,302,300,298,296,294,292,291,289,287,285,283,281,279,278,276, + 274,272,270,268,266,265,263,261,259,257,255,254,252,250,248,246, + 244,242,241,239,237,235,233,231,229,228,226,224,222,220,218,216, + 215,213,211,209,207,205,203,202,200,198,196,194,192,190,189,187, + 185,183,181,179,177,176,174,172,170,168,166,164,163,161,159,157, + 155,153,151,150,148,146,144,142,140,138,137,135,133,131,129,127, + 125,124,122,120,118,116,114,112,111,109,107,105,103,101,100,98, + 100,101,103,105,107,109,111,112,114,116,118,120,122,124,125,127, + 129,131,133,135,137,138,140,142,144,146,148,150,151,153,155,157, + 159,161,163,164,166,168,170,172,174,176,177,179,181,183,185,187, + 189,190,192,194,196,198,200,202,203,205,207,209,211,213,215,216, + 218,220,222,224,226,228,229,231,233,235,237,239,241,242,244,246, + 248,250,252,254,255,257,259,261,263,265,266,268,270,272,274,276, + 278,279,281,283,285,287,289,291,292,294,296,298,300,302,304,305, + 307,309,311,313,315,317,318,320,322,324,326,328,330,331,333,335, + 337,339,341,343,344,346,348,350,352,354,356,357,359,361,363,365, + 367,369,370,372,374,376,378,380,382,383,385,387,389,391,393,395, + 396,398,400,402,404,406,408,409,411,413,415,417,419,420,422,424, + 426,428,430,432,433,435,437,439,441,443,445,446,448,450,452,454, + 456,458,459,461,463,465,467,469,471,472,474,476,478,480,482,484, + 485,487,489,491,493,495,497,498,500,502,504,506,508,510,511,513, + 515,517,519,521,523,524,526,528,530,532,534,536,537,539,541,543, + 545,547,549,550,552,554,556,558,560,562,563,565,567,569,571,573, + 575,576,578,580,582,584,586,587,589,591,593,595,597,599,600,602, + 604,606,608,610,612,613,615,617,619,621,623,625,626,628,630,632, + 634,636,638,639,641,643,645,647,649,651,652,654,656,658,660,662, + 664,665,667,669,671,673,675,677,678,680,682,684,686,688,690,691, + 693,695,697,699,701,703,704,706,708,710,712,714,716,717,719,721, + 723,725,727,729,730,732,734,736,738,740,741,743,745,747,749,751, + 753,754,756,758,760,762,764,766,767,769,771,773,775,777,779,780, + 782,784,786,788,790,792,793,795,797,799,801,803,805,806,808,810, + 812,814,816,818,819,821,823,825,827,829,831,832,834,836,838,840, + 842,844,845,847,849,851,853,855,857,858,860,862,864,866,868,870, + 871,873,875,877,879,881,883,884,886,888,890,892,894,895,897,899, + 901,903,905,907,908,910,912,914,916,918,920,921,923,925,927,929, + 931,933,934,936,938,940,942,944,946,947,949,951,953,955,957,959, + 960,962,964,966,968,970,972,973,975,977,979,981,983,985,986,988, + 990,992,994,996,998,999,1001,1003,1005,1007,1009,1011,1012,1014,1016,1018, + 1020,1022,1024,1025,1027,1029,1031,1033,1035,1037,1038,1040,1042,1044,1046,1048, + 1050,1051,1053,1055,1057,1059,1061,1062,1064,1066,1068,1070,1072,1074,1075,1077, + 1079,1081,1083,1085,1087,1088,1090,1092,1094,1096,1098,1100,1101,1103,1105,1107, + 1109,1111,1113,1114,1116,1118,1120,1122,1124,1126,1127,1129,1131,1133,1135,1137, + 1139,1140,1142,1144,1146,1148,1150,1152,1153,1155,1157,1159,1161,1163,1165,1166, + 1168,1170,1172,1174,1176,1178,1179,1181,1183,1185,1187,1189,1191,1192,1194,1196, + 1198,1200,1202,1204,1205,1207,1209,1211,1213,1215,1216,1218,1220,1222,1224,1226, + 1228,1229,1231,1233,1235,1237,1239,1241,1242,1244,1246,1248,1250,1252,1254,1255, + 1257,1259,1261,1263,1265,1267,1268,1270,1272,1274,1276,1278,1280,1281,1283,1285, + 1287,1289,1291,1293,1294,1296,1298,1300,1302,1304,1306,1307,1309,1311,1313,1315, + 1317,1319,1320,1322,1324,1326,1328,1330,1332,1333,1335,1337,1339,1341,1343,1345, + 1346,1348,1350,1352,1354,1356,1358,1359,1361,1363,1365,1367,1369,1370,1372,1374, + 1376,1378,1380,1382,1383,1385,1387,1389,1391,1393,1395,1396,1398,1400,1402,1404, + 1406,1408,1409,1411,1413,1415,1417,1419,1421,1422,1424,1426,1428,1430,1432,1434, + 1435,1437,1439,1441,1443,1445,1447,1448,1450,1452,1454,1456,1458,1460,1461,1463, + 1465,1467,1469,1471,1473,1474,1476,1478,1480,1482,1484,1486,1487,1489,1491,1493, + 1495,1497,1499,1500,1502,1504,1506,1508,1510,1512,1513,1515,1517,1519,1521,1523, + 1525,1526,1528,1530,1532,1534,1536,1537,1539,1541,1543,1545,1547,1549,1550,1552, + 1554,1556,1558,1560,1562,1563,1565,1567,1569,1571,1573,1575,1576,1578,1580,1582, + 1584,1586,1588,1589,1591,1593,1595,1597,1599,1601,1602,1604,1606,1608,1610,1612, + 1614,1615,1617,1619,1621,1623,1625,1627,1628,1630,1632,1634,1636,1638,1640,1641, + 1643,1645,1647,1649,1651,1653,1654,1656,1658,1660,1662,1664,1666,1667,1669,1671, + 1673,1675,1677,1679,1680,1682,1684,1686,1688,1690,1691,1693,1695,1697,1699,1701, + 1703,1704,1706,1708,1710,1712,1714,1716,1717,1719,1721,1723,1725,1727,1729,1730, + 1732,1734,1736,1738,1740,1742,1743,1745,1747,1749,1751,1753,1755,1756,1758,1760, + 1762,1764,1766,1768,1769,1771,1773,1775,1777,1779,1781,1782,1784,1786,1788,1790, + 1792,1794,1795,1797,1799,1801,1803,1805,1807,1808,1810,1812,1814,1816,1818,1820, + 1821,1823,1825,1827,1829,1831,1833,1834,1836,1838,1840,1842,1844,1845,1847,1849, + 1851,1853,1855,1857,1858,1860,1862,1864,1866,1868,1870,1871,1873,1875,1877,1879, + 1881,1883,1884,1886,1888,1890,1892,1894,1896,1897,1899,1901,1903,1905,1907,1909, + 1910,1912,1914,1916,1918,1920,1922,1923,1925,1927,1929,1931,1933,1935,1936,1938, + 1940,1942,1944,1946,1948,1949,1951,1953,1955,1957,1959,1961,1962,1964,1966,1968, + 1970,1972,1974,1975,1977,1979,1981,1983,1985,1987,1988,1990,1992,1994,1996,1998 +}; +const unsigned short sawtable[1]={98}; +const unsigned short SineTable[4096] = { + 2000,2003,2006,2009,2012,2015,2017,2020,2023,2026,2029,2032,2035,2038,2041,2044,2047,2050,2052,2055,2058,2061,2064,2067,2070,2073,2076,2079,2082,2084,2087,2090, + 2093,2096,2099,2102,2105,2108,2111,2114,2117,2119,2122,2125,2128,2131,2134,2137,2140,2143,2146,2148,2151,2154,2157,2160,2163,2166,2169,2172,2175,2178,2180,2183, + 2186,2189,2192,2195,2198,2201,2204,2207,2209,2212,2215,2218,2221,2224,2227,2230,2233,2235,2238,2241,2244,2247,2250,2253,2256,2259,2261,2264,2267,2270,2273,2276, + 2279,2282,2285,2287,2290,2293,2296,2299,2302,2305,2308,2310,2313,2316,2319,2322,2325,2328,2331,2333,2336,2339,2342,2345,2348,2351,2354,2356,2359,2362,2365,2368, + 2371,2374,2376,2379,2382,2385,2388,2391,2394,2396,2399,2402,2405,2408,2411,2413,2416,2419,2422,2425,2428,2430,2433,2436,2439,2442,2445,2448,2450,2453,2456,2459, + 2462,2464,2467,2470,2473,2476,2479,2481,2484,2487,2490,2493,2496,2498,2501,2504,2507,2510,2512,2515,2518,2521,2524,2526,2529,2532,2535,2538,2540,2543,2546,2549, + 2552,2554,2557,2560,2563,2565,2568,2571,2574,2577,2579,2582,2585,2588,2590,2593,2596,2599,2602,2604,2607,2610,2613,2615,2618,2621,2624,2626,2629,2632,2635,2637, + 2640,2643,2646,2648,2651,2654,2657,2659,2662,2665,2667,2670,2673,2676,2678,2681,2684,2687,2689,2692,2695,2697,2700,2703,2706,2708,2711,2714,2716,2719,2722,2724, + 2727,2730,2732,2735,2738,2741,2743,2746,2749,2751,2754,2757,2759,2762,2765,2767,2770,2773,2775,2778,2781,2783,2786,2789,2791,2794,2797,2799,2802,2804,2807,2810, + 2812,2815,2818,2820,2823,2826,2828,2831,2833,2836,2839,2841,2844,2846,2849,2852,2854,2857,2859,2862,2865,2867,2870,2872,2875,2878,2880,2883,2885,2888,2891,2893, + 2896,2898,2901,2903,2906,2908,2911,2914,2916,2919,2921,2924,2926,2929,2931,2934,2937,2939,2942,2944,2947,2949,2952,2954,2957,2959,2962,2964,2967,2969,2972,2974, + 2977,2979,2982,2984,2987,2989,2992,2994,2997,2999,3002,3004,3007,3009,3012,3014,3016,3019,3021,3024,3026,3029,3031,3034,3036,3039,3041,3043,3046,3048,3051,3053, + 3056,3058,3060,3063,3065,3068,3070,3072,3075,3077,3080,3082,3084,3087,3089,3092,3094,3096,3099,3101,3104,3106,3108,3111,3113,3115,3118,3120,3122,3125,3127,3129, + 3132,3134,3137,3139,3141,3144,3146,3148,3150,3153,3155,3157,3160,3162,3164,3167,3169,3171,3174,3176,3178,3180,3183,3185,3187,3190,3192,3194,3196,3199,3201,3203, + 3205,3208,3210,3212,3214,3217,3219,3221,3223,3226,3228,3230,3232,3234,3237,3239,3241,3243,3245,3248,3250,3252,3254,3256,3259,3261,3263,3265,3267,3269,3272,3274, + 3276,3278,3280,3282,3285,3287,3289,3291,3293,3295,3297,3300,3302,3304,3306,3308,3310,3312,3314,3316,3319,3321,3323,3325,3327,3329,3331,3333,3335,3337,3339,3341, + 3344,3346,3348,3350,3352,3354,3356,3358,3360,3362,3364,3366,3368,3370,3372,3374,3376,3378,3380,3382,3384,3386,3388,3390,3392,3394,3396,3398,3400,3402,3404,3406, + 3408,3410,3412,3414,3416,3418,3419,3421,3423,3425,3427,3429,3431,3433,3435,3437,3439,3441,3442,3444,3446,3448,3450,3452,3454,3456,3458,3459,3461,3463,3465,3467, + 3469,3471,3472,3474,3476,3478,3480,3482,3483,3485,3487,3489,3491,3492,3494,3496,3498,3500,3501,3503,3505,3507,3509,3510,3512,3514,3516,3517,3519,3521,3523,3524, + 3526,3528,3530,3531,3533,3535,3536,3538,3540,3542,3543,3545,3547,3548,3550,3552,3553,3555,3557,3558,3560,3562,3563,3565,3567,3568,3570,3572,3573,3575,3577,3578, + 3580,3581,3583,3585,3586,3588,3589,3591,3593,3594,3596,3597,3599,3601,3602,3604,3605,3607,3608,3610,3611,3613,3615,3616,3618,3619,3621,3622,3624,3625,3627,3628, + 3630,3631,3633,3634,3636,3637,3639,3640,3642,3643,3644,3646,3647,3649,3650,3652,3653,3655,3656,3657,3659,3660,3662,3663,3665,3666,3667,3669,3670,3672,3673,3674, + 3676,3677,3678,3680,3681,3682,3684,3685,3687,3688,3689,3691,3692,3693,3694,3696,3697,3698,3700,3701,3702,3704,3705,3706,3707,3709,3710,3711,3713,3714,3715,3716, + 3718,3719,3720,3721,3723,3724,3725,3726,3727,3729,3730,3731,3732,3733,3735,3736,3737,3738,3739,3741,3742,3743,3744,3745,3746,3747,3749,3750,3751,3752,3753,3754, + 3755,3756,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3770,3771,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784,3785,3786,3787,3788, + 3789,3790,3791,3792,3793,3794,3795,3796,3797,3798,3799,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3810,3811,3812,3813,3814,3815,3816,3816,3817, + 3818,3819,3820,3821,3822,3822,3823,3824,3825,3826,3826,3827,3828,3829,3830,3830,3831,3832,3833,3833,3834,3835,3836,3837,3837,3838,3839,3839,3840,3841,3842,3842, + 3843,3844,3844,3845,3846,3847,3847,3848,3849,3849,3850,3851,3851,3852,3853,3853,3854,3854,3855,3856,3856,3857,3858,3858,3859,3859,3860,3861,3861,3862,3862,3863, + 3863,3864,3865,3865,3866,3866,3867,3867,3868,3868,3869,3869,3870,3871,3871,3872,3872,3873,3873,3874,3874,3874,3875,3875,3876,3876,3877,3877,3878,3878,3879,3879, + 3879,3880,3880,3881,3881,3882,3882,3882,3883,3883,3883,3884,3884,3885,3885,3885,3886,3886,3886,3887,3887,3887,3888,3888,3888,3889,3889,3889,3890,3890,3890,3891, + 3891,3891,3891,3892,3892,3892,3892,3893,3893,3893,3893,3894,3894,3894,3894,3895,3895,3895,3895,3895,3896,3896,3896,3896,3896,3897,3897,3897,3897,3897,3897,3898, + 3898,3898,3898,3898,3898,3898,3898,3899,3899,3899,3899,3899,3899,3899,3899,3899,3899,3899,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900, + 3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3900,3899,3899,3899,3899,3899,3899,3899,3899,3899,3899,3899,3898,3898,3898,3898,3898,3898, + 3898,3898,3897,3897,3897,3897,3897,3897,3896,3896,3896,3896,3896,3895,3895,3895,3895,3895,3894,3894,3894,3894,3893,3893,3893,3893,3892,3892,3892,3892,3891,3891, + 3891,3891,3890,3890,3890,3889,3889,3889,3888,3888,3888,3887,3887,3887,3886,3886,3886,3885,3885,3885,3884,3884,3883,3883,3883,3882,3882,3882,3881,3881,3880,3880, + 3879,3879,3879,3878,3878,3877,3877,3876,3876,3875,3875,3874,3874,3874,3873,3873,3872,3872,3871,3871,3870,3869,3869,3868,3868,3867,3867,3866,3866,3865,3865,3864, + 3863,3863,3862,3862,3861,3861,3860,3859,3859,3858,3858,3857,3856,3856,3855,3854,3854,3853,3853,3852,3851,3851,3850,3849,3849,3848,3847,3847,3846,3845,3844,3844, + 3843,3842,3842,3841,3840,3839,3839,3838,3837,3837,3836,3835,3834,3833,3833,3832,3831,3830,3830,3829,3828,3827,3826,3826,3825,3824,3823,3822,3822,3821,3820,3819, + 3818,3817,3816,3816,3815,3814,3813,3812,3811,3810,3810,3809,3808,3807,3806,3805,3804,3803,3802,3801,3800,3799,3799,3798,3797,3796,3795,3794,3793,3792,3791,3790, + 3789,3788,3787,3786,3785,3784,3783,3782,3781,3780,3779,3778,3777,3776,3775,3774,3773,3772,3771,3770,3768,3767,3766,3765,3764,3763,3762,3761,3760,3759,3758,3756, + 3755,3754,3753,3752,3751,3750,3749,3747,3746,3745,3744,3743,3742,3741,3739,3738,3737,3736,3735,3733,3732,3731,3730,3729,3727,3726,3725,3724,3723,3721,3720,3719, + 3718,3716,3715,3714,3713,3711,3710,3709,3707,3706,3705,3704,3702,3701,3700,3698,3697,3696,3694,3693,3692,3691,3689,3688,3687,3685,3684,3682,3681,3680,3678,3677, + 3676,3674,3673,3672,3670,3669,3667,3666,3665,3663,3662,3660,3659,3657,3656,3655,3653,3652,3650,3649,3647,3646,3644,3643,3642,3640,3639,3637,3636,3634,3633,3631, + 3630,3628,3627,3625,3624,3622,3621,3619,3618,3616,3615,3613,3611,3610,3608,3607,3605,3604,3602,3601,3599,3597,3596,3594,3593,3591,3589,3588,3586,3585,3583,3581, + 3580,3578,3577,3575,3573,3572,3570,3568,3567,3565,3563,3562,3560,3558,3557,3555,3553,3552,3550,3548,3547,3545,3543,3542,3540,3538,3536,3535,3533,3531,3530,3528, + 3526,3524,3523,3521,3519,3517,3516,3514,3512,3510,3509,3507,3505,3503,3501,3500,3498,3496,3494,3492,3491,3489,3487,3485,3483,3482,3480,3478,3476,3474,3472,3471, + 3469,3467,3465,3463,3461,3459,3458,3456,3454,3452,3450,3448,3446,3444,3442,3441,3439,3437,3435,3433,3431,3429,3427,3425,3423,3421,3419,3418,3416,3414,3412,3410, + 3408,3406,3404,3402,3400,3398,3396,3394,3392,3390,3388,3386,3384,3382,3380,3378,3376,3374,3372,3370,3368,3366,3364,3362,3360,3358,3356,3354,3352,3350,3348,3346, + 3344,3341,3339,3337,3335,3333,3331,3329,3327,3325,3323,3321,3319,3316,3314,3312,3310,3308,3306,3304,3302,3300,3297,3295,3293,3291,3289,3287,3285,3282,3280,3278, + 3276,3274,3272,3269,3267,3265,3263,3261,3259,3256,3254,3252,3250,3248,3245,3243,3241,3239,3237,3234,3232,3230,3228,3226,3223,3221,3219,3217,3214,3212,3210,3208, + 3205,3203,3201,3199,3196,3194,3192,3190,3187,3185,3183,3180,3178,3176,3174,3171,3169,3167,3164,3162,3160,3157,3155,3153,3150,3148,3146,3144,3141,3139,3137,3134, + 3132,3129,3127,3125,3122,3120,3118,3115,3113,3111,3108,3106,3104,3101,3099,3096,3094,3092,3089,3087,3084,3082,3080,3077,3075,3072,3070,3068,3065,3063,3060,3058, + 3056,3053,3051,3048,3046,3043,3041,3039,3036,3034,3031,3029,3026,3024,3021,3019,3016,3014,3012,3009,3007,3004,3002,2999,2997,2994,2992,2989,2987,2984,2982,2979, + 2977,2974,2972,2969,2967,2964,2962,2959,2957,2954,2952,2949,2947,2944,2942,2939,2937,2934,2931,2929,2926,2924,2921,2919,2916,2914,2911,2908,2906,2903,2901,2898, + 2896,2893,2891,2888,2885,2883,2880,2878,2875,2872,2870,2867,2865,2862,2859,2857,2854,2852,2849,2846,2844,2841,2839,2836,2833,2831,2828,2826,2823,2820,2818,2815, + 2812,2810,2807,2804,2802,2799,2797,2794,2791,2789,2786,2783,2781,2778,2775,2773,2770,2767,2765,2762,2759,2757,2754,2751,2749,2746,2743,2741,2738,2735,2732,2730, + 2727,2724,2722,2719,2716,2714,2711,2708,2706,2703,2700,2697,2695,2692,2689,2687,2684,2681,2678,2676,2673,2670,2667,2665,2662,2659,2657,2654,2651,2648,2646,2643, + 2640,2637,2635,2632,2629,2626,2624,2621,2618,2615,2613,2610,2607,2604,2602,2599,2596,2593,2590,2588,2585,2582,2579,2577,2574,2571,2568,2565,2563,2560,2557,2554, + 2552,2549,2546,2543,2540,2538,2535,2532,2529,2526,2524,2521,2518,2515,2512,2510,2507,2504,2501,2498,2496,2493,2490,2487,2484,2481,2479,2476,2473,2470,2467,2464, + 2462,2459,2456,2453,2450,2448,2445,2442,2439,2436,2433,2430,2428,2425,2422,2419,2416,2413,2411,2408,2405,2402,2399,2396,2394,2391,2388,2385,2382,2379,2376,2374, + 2371,2368,2365,2362,2359,2356,2354,2351,2348,2345,2342,2339,2336,2333,2331,2328,2325,2322,2319,2316,2313,2310,2308,2305,2302,2299,2296,2293,2290,2287,2285,2282, + 2279,2276,2273,2270,2267,2264,2261,2259,2256,2253,2250,2247,2244,2241,2238,2235,2233,2230,2227,2224,2221,2218,2215,2212,2209,2207,2204,2201,2198,2195,2192,2189, + 2186,2183,2180,2178,2175,2172,2169,2166,2163,2160,2157,2154,2151,2148,2146,2143,2140,2137,2134,2131,2128,2125,2122,2119,2117,2114,2111,2108,2105,2102,2099,2096, + 2093,2090,2087,2084,2082,2079,2076,2073,2070,2067,2064,2061,2058,2055,2052,2050,2047,2044,2041,2038,2035,2032,2029,2026,2023,2020,2017,2015,2012,2009,2006,2003, + 2000,1997,1994,1991,1988,1985,1983,1980,1977,1974,1971,1968,1965,1962,1959,1956,1953,1950,1948,1945,1942,1939,1936,1933,1930,1927,1924,1921,1918,1916,1913,1910, + 1907,1904,1901,1898,1895,1892,1889,1886,1883,1881,1878,1875,1872,1869,1866,1863,1860,1857,1854,1852,1849,1846,1843,1840,1837,1834,1831,1828,1825,1822,1820,1817, + 1814,1811,1808,1805,1802,1799,1796,1793,1791,1788,1785,1782,1779,1776,1773,1770,1767,1765,1762,1759,1756,1753,1750,1747,1744,1741,1739,1736,1733,1730,1727,1724, + 1721,1718,1715,1713,1710,1707,1704,1701,1698,1695,1692,1690,1687,1684,1681,1678,1675,1672,1669,1667,1664,1661,1658,1655,1652,1649,1646,1644,1641,1638,1635,1632, + 1629,1626,1624,1621,1618,1615,1612,1609,1606,1604,1601,1598,1595,1592,1589,1587,1584,1581,1578,1575,1572,1570,1567,1564,1561,1558,1555,1552,1550,1547,1544,1541, + 1538,1536,1533,1530,1527,1524,1521,1519,1516,1513,1510,1507,1504,1502,1499,1496,1493,1490,1488,1485,1482,1479,1476,1474,1471,1468,1465,1462,1460,1457,1454,1451, + 1448,1446,1443,1440,1437,1435,1432,1429,1426,1423,1421,1418,1415,1412,1410,1407,1404,1401,1398,1396,1393,1390,1387,1385,1382,1379,1376,1374,1371,1368,1365,1363, + 1360,1357,1354,1352,1349,1346,1343,1341,1338,1335,1333,1330,1327,1324,1322,1319,1316,1313,1311,1308,1305,1303,1300,1297,1294,1292,1289,1286,1284,1281,1278,1276, + 1273,1270,1268,1265,1262,1259,1257,1254,1251,1249,1246,1243,1241,1238,1235,1233,1230,1227,1225,1222,1219,1217,1214,1211,1209,1206,1203,1201,1198,1196,1193,1190, + 1188,1185,1182,1180,1177,1174,1172,1169,1167,1164,1161,1159,1156,1154,1151,1148,1146,1143,1141,1138,1135,1133,1130,1128,1125,1122,1120,1117,1115,1112,1109,1107, + 1104,1102,1099,1097,1094,1092,1089,1086,1084,1081,1079,1076,1074,1071,1069,1066,1063,1061,1058,1056,1053,1051,1048,1046,1043,1041,1038,1036,1033,1031,1028,1026, + 1023,1021,1018,1016,1013,1011,1008,1006,1003,1001,998,996,993,991,988,986,984,981,979,976,974,971,969,966,964,961,959,957,954,952,949,947, + 944,942,940,937,935,932,930,928,925,923,920,918,916,913,911,908,906,904,901,899,896,894,892,889,887,885,882,880,878,875,873,871, + 868,866,863,861,859,856,854,852,850,847,845,843,840,838,836,833,831,829,826,824,822,820,817,815,813,810,808,806,804,801,799,797, + 795,792,790,788,786,783,781,779,777,774,772,770,768,766,763,761,759,757,755,752,750,748,746,744,741,739,737,735,733,731,728,726, + 724,722,720,718,715,713,711,709,707,705,703,700,698,696,694,692,690,688,686,684,681,679,677,675,673,671,669,667,665,663,661,659, + 656,654,652,650,648,646,644,642,640,638,636,634,632,630,628,626,624,622,620,618,616,614,612,610,608,606,604,602,600,598,596,594, + 592,590,588,586,584,582,581,579,577,575,573,571,569,567,565,563,561,559,558,556,554,552,550,548,546,544,542,541,539,537,535,533, + 531,529,528,526,524,522,520,518,517,515,513,511,509,508,506,504,502,500,499,497,495,493,491,490,488,486,484,483,481,479,477,476, + 474,472,470,469,467,465,464,462,460,458,457,455,453,452,450,448,447,445,443,442,440,438,437,435,433,432,430,428,427,425,423,422, + 420,419,417,415,414,412,411,409,407,406,404,403,401,399,398,396,395,393,392,390,389,387,385,384,382,381,379,378,376,375,373,372, + 370,369,367,366,364,363,361,360,358,357,356,354,353,351,350,348,347,345,344,343,341,340,338,337,335,334,333,331,330,328,327,326, + 324,323,322,320,319,318,316,315,313,312,311,309,308,307,306,304,303,302,300,299,298,296,295,294,293,291,290,289,287,286,285,284, + 282,281,280,279,277,276,275,274,273,271,270,269,268,267,265,264,263,262,261,259,258,257,256,255,254,253,251,250,249,248,247,246, + 245,244,242,241,240,239,238,237,236,235,234,233,232,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212, + 211,210,209,208,207,206,205,204,203,202,201,201,200,199,198,197,196,195,194,193,192,191,190,190,189,188,187,186,185,184,184,183, + 182,181,180,179,178,178,177,176,175,174,174,173,172,171,170,170,169,168,167,167,166,165,164,163,163,162,161,161,160,159,158,158, + 157,156,156,155,154,153,153,152,151,151,150,149,149,148,147,147,146,146,145,144,144,143,142,142,141,141,140,139,139,138,138,137, + 137,136,135,135,134,134,133,133,132,132,131,131,130,129,129,128,128,127,127,126,126,126,125,125,124,124,123,123,122,122,121,121, + 121,120,120,119,119,118,118,118,117,117,117,116,116,115,115,115,114,114,114,113,113,113,112,112,112,111,111,111,110,110,110,109, + 109,109,109,108,108,108,108,107,107,107,107,106,106,106,106,105,105,105,105,105,104,104,104,104,104,103,103,103,103,103,103,102, + 102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,101,101,101,101,101,101,101,101,101,101,101,102,102,102,102,102,102, + 102,102,103,103,103,103,103,103,104,104,104,104,104,105,105,105,105,105,106,106,106,106,107,107,107,107,108,108,108,108,109,109, + 109,109,110,110,110,111,111,111,112,112,112,113,113,113,114,114,114,115,115,115,116,116,117,117,117,118,118,118,119,119,120,120, + 121,121,121,122,122,123,123,124,124,125,125,126,126,126,127,127,128,128,129,129,130,131,131,132,132,133,133,134,134,135,135,136, + 137,137,138,138,139,139,140,141,141,142,142,143,144,144,145,146,146,147,147,148,149,149,150,151,151,152,153,153,154,155,156,156, + 157,158,158,159,160,161,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,174,174,175,176,177,178,178,179,180,181, + 182,183,184,184,185,186,187,188,189,190,190,191,192,193,194,195,196,197,198,199,200,201,201,202,203,204,205,206,207,208,209,210, + 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,232,233,234,235,236,237,238,239,240,241,242,244, + 245,246,247,248,249,250,251,253,254,255,256,257,258,259,261,262,263,264,265,267,268,269,270,271,273,274,275,276,277,279,280,281, + 282,284,285,286,287,289,290,291,293,294,295,296,298,299,300,302,303,304,306,307,308,309,311,312,313,315,316,318,319,320,322,323, + 324,326,327,328,330,331,333,334,335,337,338,340,341,343,344,345,347,348,350,351,353,354,356,357,358,360,361,363,364,366,367,369, + 370,372,373,375,376,378,379,381,382,384,385,387,389,390,392,393,395,396,398,399,401,403,404,406,407,409,411,412,414,415,417,419, + 420,422,423,425,427,428,430,432,433,435,437,438,440,442,443,445,447,448,450,452,453,455,457,458,460,462,464,465,467,469,470,472, + 474,476,477,479,481,483,484,486,488,490,491,493,495,497,499,500,502,504,506,508,509,511,513,515,517,518,520,522,524,526,528,529, + 531,533,535,537,539,541,542,544,546,548,550,552,554,556,558,559,561,563,565,567,569,571,573,575,577,579,581,582,584,586,588,590, + 592,594,596,598,600,602,604,606,608,610,612,614,616,618,620,622,624,626,628,630,632,634,636,638,640,642,644,646,648,650,652,654, + 656,659,661,663,665,667,669,671,673,675,677,679,681,684,686,688,690,692,694,696,698,700,703,705,707,709,711,713,715,718,720,722, + 724,726,728,731,733,735,737,739,741,744,746,748,750,752,755,757,759,761,763,766,768,770,772,774,777,779,781,783,786,788,790,792, + 795,797,799,801,804,806,808,810,813,815,817,820,822,824,826,829,831,833,836,838,840,843,845,847,850,852,854,856,859,861,863,866, + 868,871,873,875,878,880,882,885,887,889,892,894,896,899,901,904,906,908,911,913,916,918,920,923,925,928,930,932,935,937,940,942, + 944,947,949,952,954,957,959,961,964,966,969,971,974,976,979,981,984,986,988,991,993,996,998,1001,1003,1006,1008,1011,1013,1016,1018,1021, + 1023,1026,1028,1031,1033,1036,1038,1041,1043,1046,1048,1051,1053,1056,1058,1061,1063,1066,1069,1071,1074,1076,1079,1081,1084,1086,1089,1092,1094,1097,1099,1102, + 1104,1107,1109,1112,1115,1117,1120,1122,1125,1128,1130,1133,1135,1138,1141,1143,1146,1148,1151,1154,1156,1159,1161,1164,1167,1169,1172,1174,1177,1180,1182,1185, + 1188,1190,1193,1196,1198,1201,1203,1206,1209,1211,1214,1217,1219,1222,1225,1227,1230,1233,1235,1238,1241,1243,1246,1249,1251,1254,1257,1259,1262,1265,1268,1270, + 1273,1276,1278,1281,1284,1286,1289,1292,1294,1297,1300,1303,1305,1308,1311,1313,1316,1319,1322,1324,1327,1330,1333,1335,1338,1341,1343,1346,1349,1352,1354,1357, + 1360,1363,1365,1368,1371,1374,1376,1379,1382,1385,1387,1390,1393,1396,1398,1401,1404,1407,1410,1412,1415,1418,1421,1423,1426,1429,1432,1435,1437,1440,1443,1446, + 1448,1451,1454,1457,1460,1462,1465,1468,1471,1474,1476,1479,1482,1485,1488,1490,1493,1496,1499,1502,1504,1507,1510,1513,1516,1519,1521,1524,1527,1530,1533,1536, + 1538,1541,1544,1547,1550,1552,1555,1558,1561,1564,1567,1570,1572,1575,1578,1581,1584,1587,1589,1592,1595,1598,1601,1604,1606,1609,1612,1615,1618,1621,1624,1626, + 1629,1632,1635,1638,1641,1644,1646,1649,1652,1655,1658,1661,1664,1667,1669,1672,1675,1678,1681,1684,1687,1690,1692,1695,1698,1701,1704,1707,1710,1713,1715,1718, + 1721,1724,1727,1730,1733,1736,1739,1741,1744,1747,1750,1753,1756,1759,1762,1765,1767,1770,1773,1776,1779,1782,1785,1788,1791,1793,1796,1799,1802,1805,1808,1811, + 1814,1817,1820,1822,1825,1828,1831,1834,1837,1840,1843,1846,1849,1852,1854,1857,1860,1863,1866,1869,1872,1875,1878,1881,1883,1886,1889,1892,1895,1898,1901,1904, + 1907,1910,1913,1916,1918,1921,1924,1927,1930,1933,1936,1939,1942,1945,1948,1950,1953,1956,1959,1962,1965,1968,1971,1974,1977,1980,1983,1985,1988,1991,1994,1997 +}; +const char wavheader[44]={0x52,0x49,0x46,0x46, + 0xFF,0xFF,0xFF,0xFF, + 0x57,0x41,0x56,0x45, + 0x66,0x6d,0x74,0x20, + 0x10,0x00,0x00,0x00, + 0x01,0x00, + 0x02,0x00, + 0x80,0x3E,0x00,0x00, + 0x0,0xFA,0x00,0x00, + 0x04,0x00, + 0x10,0x00, + 0x64,0x61,0x74,0x61, + 0xFF,0xFF,0xFF,0xFF}; +const char toneheader[44]={0x52,0x49,0x46,0x46, + 0xFF,0xFF,0xFF,0xFF, + 0x57,0x41,0x56,0x45, + 0x66,0x6d,0x74,0x20, + 0x10,0x00,0x00,0x00, + 0x01,0x00, + 0x02,0x00, + 0x44,0xAC,0x00,0x00, + 0x10,0xB1,0x02,0x00, + 0x04,0x00, + 0x10,0x00, + 0x64,0x61,0x74,0x61, + 0xFF,0xFF,0xFF,0xFF}; + +void __not_in_flash_func(iconvert)(uint16_t *ibuff, int16_t *sbuff, int count){ + int i; + for(i=0;i<(count);i+=2){ + ibuff[i]=(uint16_t)((((int)sbuff[i]*mapping[vol_left]/2000+32768))>>4); + ibuff[i+1]=(uint16_t)((((int)sbuff[i+1]*mapping[vol_right]/2000+32768))>>4); + } +} +void MIPS64 __not_in_flash_func(i2sconvert)(int16_t *fbuff, int16_t *sbuff, int count){ + int i; + for(i=0;i<(count);i+=2){ + sbuff[i]=(int16_t)((int)(fbuff[i])*mapping[vol_left]/2000); + sbuff[i+1]=(int32_t)((int)(fbuff[i+1])*mapping[vol_right]/2000); + } +} + +size_t onRead(void *userdata, char *pBufferOut, size_t bytesToRead){ + unsigned int nbr; + if(filesource[WAV_fnbr]==FATFSFILE){ + FSerror=f_read(FileTable[WAV_fnbr].fptr,pBufferOut, bytesToRead, &nbr); + if(FSerror)nbr=0; + if(!MDD_SDSPI_CardDetectState())ErrorCheck(WAV_fnbr); + } else { + nbr=lfs_file_read(&lfs, FileTable[WAV_fnbr].lfsptr, pBufferOut, bytesToRead); + if(nbr<0)FSerror=nbr; + else FSerror=0; + ErrorCheck(WAV_fnbr); + } + return nbr; +} +drwav_bool32 onSeek(void *userdata, int offset, drwav_seek_origin origin){ + if(filesource[WAV_fnbr]==FATFSFILE){ + if(origin==drwav_seek_origin_start) FSerror=f_lseek(FileTable[WAV_fnbr].fptr,offset); + else FSerror=f_lseek(FileTable[WAV_fnbr].fptr,FileTable[WAV_fnbr].fptr->fptr+offset); + } else { + if(origin==drwav_seek_origin_start) FSerror=lfs_file_seek(&lfs, FileTable[WAV_fnbr].lfsptr, offset, LFS_SEEK_SET); + else FSerror=lfs_file_seek(&lfs, FileTable[WAV_fnbr].lfsptr, offset, LFS_SEEK_CUR); + } + return 1; +} +/*drwav_bool32 onTell(void *userdata, drwav_int64* pCursor){ + if(filesource[WAV_fnbr]==FATFSFILE){ + *pCursor=(*(FileTable[WAV_fnbr].fptr)).fptr; + } else { + *pCursor=lfs_file_tell(&lfs,FileTable[WAV_fnbr].lfsptr); + } + return 1; +}*/ + +void CloseAudio(int all){ +#ifdef rp2350 + if(!PSRAMsize) +#endif + modbuff = (Option.modbuff ? (char *)(XIP_BASE + RoundUpK4(TOP_OF_SYSTEM_FLASH)) : NULL); + int was_playing=CurrentlyPlaying; + if(!Option.audio_i2s_bclk){ + bcount[1] = bcount[2] = wav_filesize = 0; + swingbuf = nextbuf = playreadcomplete = 0; + } + StopAudio(); + if(CurrentlyPlaying==P_WAVOPEN)CurrentlyPlaying=P_NOTHING; + ForceFileClose(WAV_fnbr); + FreeMemorySafe((void **)&sbuff1); + FreeMemorySafe((void **)&sbuff2); + FreeMemorySafe((void **)&noisetable); + //FreeMemorySafe((void **)&usertable); + usertable=NULL; + FreeMemorySafe((void **)&mcontext); + if(all){ + FreeMemorySafe((void **)&alist); + trackstoplay=0; + trackplaying=0; + } + memset(WAVfilename,0,sizeof(WAVfilename)); + WAVcomplete = true; + FSerror = 0; + if(was_playing == P_FLAC || was_playing == P_PAUSE_FLAC )FreeMemorySafe((void **)&myflac); + if(was_playing == P_WAV || was_playing == P_PAUSE_WAV )FreeMemorySafe((void **)&mywav); +#ifdef rp2350 + if((was_playing == P_MP3 || was_playing == P_PAUSE_MP3 ) && (Option.AUDIO_L || Option.audio_i2s_bclk || Option.AUDIO_MOSI_PIN)){ + drmp3_uninit(mymp3); + FreeMemorySafe((void **)&mymp3); + } + if(PSRAMsize && was_playing == P_MOD)FreeMemorySafe((void **)&modbuff); +#endif + int i; + for(i=0;i>1)*4000)/4096),(int)(((AUDIO_WRAP>>1)*4000)/4096)); + } + pwm_clear_irq(AUDIO_SLICE); + if(Option.audio_i2s_bclk){ + float clockdiv=(Option.CPU_Speed*1000.0f)/(float)(rate*128); + pio_sm_set_clkdiv(pioi2s,i2ssm,clockdiv); + } +} +void playvs1053(int mode){ + XCS=PinDef[Option.AUDIO_CS_PIN].GPno; + XDCS=PinDef[Option.AUDIO_DCS_PIN].GPno; + DREQ=PinDef[Option.AUDIO_DREQ_PIN].GPno; + XRST=PinDef[Option.AUDIO_RESET_PIN].GPno; + VS1053(XCS,XDCS,DREQ,XRST); + switchToMp3Mode(); + loadDefaultVs1053Patches(); + setVolumes(vol_left,vol_right); + //playing a file + setrate(6000); //32KHz should be fast enough + if(mode==P_MOD){ + memcpy((char *)sbuff1,wavheader,sizeof(wavheader)); + bcount[1]=44; + wav_filesize=bcount[1]; + } else { + sbuff1 = GetMemory(WAV_BUFFER_SIZE); + sbuff2 = GetMemory(WAV_BUFFER_SIZE); + bcount[1]=onRead(NULL,sbuff1,WAV_BUFFER_SIZE); + wav_filesize=bcount[1]; + } + CurrentlyPlaying = mode; + swingbuf=1; + nextbuf=2; + ppos=0; + playreadcomplete=0; + pwm_set_irq0_enabled(AUDIO_SLICE, true); + pwm_set_enabled(AUDIO_SLICE, true); + uint64_t t=time_us_64(); + uSec(25); + while(1){ //read all the headers without stalling + checkWAVinput(); + uSec(25); + if(time_us_64()-t>500000)break; + } +} +void playimmediatevs1053(int play){ + if(CurrentlyPlaying==P_WAVOPEN)return; + XCS=PinDef[Option.AUDIO_CS_PIN].GPno; + XDCS=PinDef[Option.AUDIO_DCS_PIN].GPno; + DREQ=PinDef[Option.AUDIO_DREQ_PIN].GPno; + XRST=PinDef[Option.AUDIO_RESET_PIN].GPno; + VS1053(XCS,XDCS,DREQ,XRST); + switchToMp3Mode(); + loadDefaultVs1053Patches(); + setVolumes(vol_left,vol_right); + //playing a file + setrate(PWM_FREQ); //16KHz should be fast enough + CurrentlyPlaying = play; + playChunk((uint8_t *)toneheader,sizeof(toneheader)); +} +void wavcallback(char *p){ + int actualrate; + if(strchr((char *)p, '.') == NULL) strcat((char *)p, ".wav"); + if(CurrentlyPlaying == P_WAV){ + CloseAudio(0); + } + WAV_fnbr = FindFreeFileNbr(); + if(!BasicFileOpen(p, WAV_fnbr, FA_READ)) return; + strcpy(WAVfilename,p); + if(Option.AUDIO_MISO_PIN){ + playvs1053(P_WAV); + return; + } + drwav_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = my_malloc; + allocationCallbacks.onRealloc = NULL; + allocationCallbacks.onFree = my_free; + mywav=GetMemory(sizeof(drwav)); + drwav_init(mywav,(drwav_read_proc)onRead, (drwav_seek_proc)onSeek, NULL, &allocationCallbacks); + if(mywav->sampleRate>44100*((int)(Option.CPU_Speed/126000)))error("Max %KHz sample rate",44100*((int)(Option.CPU_Speed/126000))); +// PInt(mywav.channels);MMPrintString(" Channels\r\n"); +// PInt(mywav.bitsPerSample);MMPrintString(" Bits per sample\r\n"); +// PInt(mywav.sampleRate);MMPrintString(" Sample rate\r\n"); + if(Option.AUDIO_L){ + audiorepeat=1; + actualrate=mywav->sampleRate; + while(actualrate<32000){ + actualrate +=mywav->sampleRate; + audiorepeat++; + } + setrate(actualrate); + } else { + setrate(mywav->sampleRate); + } + FreeMemorySafe((void **)&sbuff1); + FreeMemorySafe((void **)&sbuff2); + sbuff1 = GetMemory(WAV_BUFFER_SIZE); + sbuff2 = GetMemory(WAV_BUFFER_SIZE); + ubuff1 = (uint16_t *)sbuff1; + ubuff2 = (uint16_t *)sbuff2; + mono=(mywav->channels == 1 ? 1 : 0); + g_buff1 = (int16_t *)sbuff1; + g_buff2 = (int16_t *)sbuff2; + bcount[1]=(volatile unsigned int)drwav_read_pcm_frames_s16(mywav, WAV_BUFFER_SIZE/4, (drwav_int16*)sbuff1) * mywav->channels; + if(Option.audio_i2s_bclk) i2sconvert((drwav_int16*)sbuff1,(drwav_int16*)sbuff1,bcount[1]); + else iconvert(ubuff1, (int16_t *)sbuff1, bcount[1]); + wav_filesize=bcount[1]; + CurrentlyPlaying = P_WAV; + swingbuf=1; + nextbuf=2; + ppos=0; + playreadcomplete=0; + pwm_set_irq0_enabled(AUDIO_SLICE, true); + pwm_set_enabled(AUDIO_SLICE, true); +} +void mp3callback(char *p, int position){ + if(strchr((char *)p, '.') == NULL) strcat((char *)p, ".mp3"); + if(CurrentlyPlaying == P_MP3){ + CloseAudio(0); + } + WAV_fnbr = FindFreeFileNbr(); + strcpy(WAVfilename,p); + if(!BasicFileOpen(p, WAV_fnbr, FA_READ)) return; + if(Option.AUDIO_MISO_PIN){ + positionfile(WAV_fnbr, position); + playvs1053(P_MP3); + return; + } +#ifdef rp2350 + int actualrate; + drmp3_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = my_malloc; + allocationCallbacks.onRealloc = my_realloc; + allocationCallbacks.onFree = my_free; + if(CurrentlyPlaying == P_MP3){ + CloseAudio(0); + } + mymp3=GetMemory(sizeof(drmp3)); + if(drmp3_init(mymp3, (drmp3_read_proc)onRead, (drmp3_seek_proc)onSeek, NULL, &allocationCallbacks)==DRMP3_FALSE)error("Mp3 init"); + FreeMemorySafe((void *)&sbuff1); + FreeMemorySafe((void *)&sbuff2); +// PInt(mymp3->channels);MMPrintString(" Channels\r\n"); +// PInt(mymp3->sampleRate);MMPrintString(" Sample rate\r\n"); + sbuff1 = GetMemory(MP3_BUFFER_SIZE); + sbuff2 = GetMemory(MP3_BUFFER_SIZE); + ubuff1 = (uint16_t *)sbuff1; + ubuff2 = (uint16_t *)sbuff2; + g_buff1 = (int16_t *)sbuff1; + g_buff2 = (int16_t *)sbuff2; + mono=(mymp3->channels == 1 ? 1 : 0); + if(Option.AUDIO_L){ + audiorepeat=1; + actualrate=mymp3->sampleRate; + while(actualratesampleRate; + audiorepeat++; + } + setrate(actualrate); + } else { + setrate(mymp3->sampleRate); + } + bcount[1]=drmp3_read_pcm_frames_s16(mymp3, MP3_BUFFER_SIZE/4, (drmp3_int16*)sbuff1) * mymp3->channels; + if(!Option.audio_i2s_bclk)iconvert(ubuff1, (int16_t *)sbuff1, bcount[1]); + else i2sconvert(g_buff1, (int16_t *)sbuff1, bcount[1]); + wav_filesize=bcount[1]; + CurrentlyPlaying = P_MP3; + swingbuf=1; + nextbuf=2; + ppos=0; + playreadcomplete=0; + pwm_set_irq0_enabled(AUDIO_SLICE, true); + pwm_set_enabled(AUDIO_SLICE, true); +// MMPrintString("Playing ");MMPrintString(p);PRet(); +#endif +} +void midicallback(char *p){ + if(strchr((char *)p, '.') == NULL) strcat((char *)p, ".mid"); + if(CurrentlyPlaying == P_MIDI){ + CloseAudio(0); + } + WAV_fnbr = FindFreeFileNbr(); + if(!BasicFileOpen(p, WAV_fnbr, FA_READ)) return; + strcpy(WAVfilename,p); + playvs1053(P_MIDI); +} +void flaccallback(char *p){ + int actualrate; + if(strchr((char *)p, '.') == NULL) strcat((char *)p, ".flac"); + if(CurrentlyPlaying == P_FLAC){ + CloseAudio(0); + } + WAV_fnbr = FindFreeFileNbr(); + if(!BasicFileOpen(p, WAV_fnbr, FA_READ)) return; + strcpy(WAVfilename,p); + if(Option.AUDIO_MISO_PIN){ + playvs1053(P_FLAC); + return; + } + drflac_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = my_malloc; + allocationCallbacks.onRealloc = my_realloc; + allocationCallbacks.onFree = my_free; + myflac=drflac_open((drflac_read_proc)onRead, (drflac_seek_proc)onSeek, NULL, &allocationCallbacks); +#ifdef rp2350 + if(myflac->sampleRate>48000*((int)((float)Option.CPU_Speed/126000.0f)))error("Max %KHz sample rate",48000*((int)((float)Option.CPU_Speed/126000.0f))); +#else + if(myflac->sampleRate>44100*((int)((float)Option.CPU_Speed/126000.0f)))error("Max %KHz sample rate",44100*((int)((float)Option.CPU_Speed/126000.0f))); +#endif +// PInt(myflac->channels);MMPrintString(" Channels\r\n"); +// PInt(myflac->bitsPerSample);MMPrintString(" Bits per sample\r\n"); +// PInt(myflac->sampleRate);MMPrintString(" Sample rate\r\n"); + mono=(myflac->channels == 1 ? 1 : 0); + if(Option.AUDIO_L){ + audiorepeat=1; + actualrate=myflac->sampleRate; + while(actualratesampleRate; + audiorepeat++; + } + setrate(actualrate); + } else { + setrate(myflac->sampleRate); + } + FreeMemorySafe((void **)&sbuff1); + FreeMemorySafe((void **)&sbuff2); + sbuff1 = GetMemory(FLAC_BUFFER_SIZE); + sbuff2 = GetMemory(FLAC_BUFFER_SIZE); + ubuff1 = (uint16_t *)sbuff1; + ubuff2 = (uint16_t *)sbuff2; + g_buff1 = (int16_t *)sbuff1; + g_buff2 = (int16_t *)sbuff2; + if(Option.audio_i2s_bclk){ + bcount[1]=(volatile unsigned int)drflac_read_pcm_frames_s16(myflac, FLAC_BUFFER_SIZE/4, (drwav_int16*)sbuff1) * myflac->channels; + i2sconvert((drwav_int16*)sbuff1,(drwav_int16*)sbuff1,bcount[1]); + } + else + { + bcount[1]=(volatile unsigned int)drflac_read_pcm_frames_s16(myflac, FLAC_BUFFER_SIZE/4, (drwav_int16*)sbuff1) * myflac->channels; + iconvert(ubuff1, (int16_t *)sbuff1, bcount[1]); + } + wav_filesize=bcount[1]; + CurrentlyPlaying = P_FLAC; + swingbuf=1; + nextbuf=2; + ppos=0; + playreadcomplete=0; + pwm_set_irq0_enabled(AUDIO_SLICE, true); + pwm_set_enabled(AUDIO_SLICE, true); + +} +void rampvolume(int l, int r, int channel, int target){ + if(optionfastaudio){ + if(l)sound_v_left[channel]=target; + if(r)sound_v_right[channel]=target; + } else { + int ramptime=1000000/PWM_FREQ+2; + if(l && r){ + if(sound_v_left[channel]>target){ + for(int i=sound_v_left[channel]-1;i>=target;i--){ + sound_v_left[channel]=i; + sound_v_right[channel]=i; + uSec(ramptime); + } + } else { + for(int i=sound_v_left[channel]+1;i<=target;i++){ + sound_v_left[channel]=i; + sound_v_right[channel]=i; + uSec(ramptime); + } + } + } else if(l){ + if(sound_v_left[channel]>target){ + for(int i=sound_v_left[channel]-1;i>=target;i--){ + sound_v_left[channel]=i; + uSec(ramptime); + } + } else { + for(int i=sound_v_left[channel]+1;i<=target;i++){ + sound_v_left[channel]=i; + uSec(ramptime); + } + } + } else if(r){ + if(sound_v_right[channel]>target){ + for(int i=sound_v_right[channel]-1;i>=target;i--){ + sound_v_right[channel]=i; + uSec(ramptime); + } + } else { + for(int i=sound_v_right[channel]+1;i<=target;i++){ + sound_v_right[channel]=i; + uSec(ramptime); + } + } + } + } +} + +void setnoise(void){ + uint32_t noise; + int i; + if(noisetable)return; + noisetable=GetMemory(4096*sizeof(uint16_t)); + for(i=0;i<4096;i++){ + noise=rand() % 3800+100; + noisetable[i]=(int)noise; + } + return; + +} +/* @endcond */ +// The MMBasic command: PLAY +void MIPS16 cmd_play(void) { + unsigned char *tp; + if(checkstring(cmdline, (unsigned char *)"STOP")) { + if(CurrentlyPlaying == P_NOTHING)return; + CloseAudio(1); + return; + } + if(!(Option.AUDIO_L || Option.AUDIO_CLK_PIN || Option.audio_i2s_bclk))error((char *)"Audio not enabled"); + if((tp=checkstring(cmdline, (unsigned char *)"LOAD SOUND"))) { + if(Option.AUDIO_MISO_PIN)error("Not available with VS1053 audio"); + if(usertable!=NULL)error("Already loaded"); +// unsigned int nbr; + uint16_t *dd; + int64_t *aint; + skipspace(tp); + int size=parseintegerarray(tp,&aint,1,1,NULL,false); + dd = (uint16_t *)aint; + if(size!=1024) error("Array size"); + usertable=dd; + return; + } + if(checkstring(cmdline, (unsigned char *)"NEXT")) { + if(CurrentlyPlaying == P_FLAC){ + if(trackplaying==trackstoplay){ + if(!CurrentLinePtr)MMPrintString("Last track is playing\r\n"); + return; + } + trackplaying++; + flaccallback(alist[trackplaying].fn); + } else if(CurrentlyPlaying == P_WAV){ + if(trackplaying==trackstoplay){ + if(!CurrentLinePtr)MMPrintString("Last track is playing\r\n"); + return; + } + trackplaying++; + wavcallback(alist[trackplaying].fn); + } else if(CurrentlyPlaying == P_MP3){ + if(trackplaying==trackstoplay){ + if(!CurrentLinePtr)MMPrintString("Last track is playing\r\n"); + return; + } + trackplaying++; + mp3callback(alist[trackplaying].fn,0); + } else if(CurrentlyPlaying == P_MIDI){ + if(trackplaying==trackstoplay){ + if(!CurrentLinePtr)MMPrintString("Last track is playing\r\n"); + return; + } + trackplaying++; + midicallback(alist[trackplaying].fn); + } else error("Nothing to play"); + + return; + } + if(checkstring(cmdline, (unsigned char *)"PREVIOUS")) { + if(CurrentlyPlaying == P_FLAC){ + if(trackplaying==0){ + if(!CurrentLinePtr)MMPrintString("First track is playing\r\n"); + return; + } + trackplaying--; + flaccallback(alist[trackplaying].fn); + } else if(CurrentlyPlaying == P_WAV){ + if(trackplaying==0){ + if(!CurrentLinePtr)MMPrintString("First track is playing\r\n"); + return; + } + trackplaying--; + wavcallback(alist[trackplaying].fn); + } else if(CurrentlyPlaying == P_MP3){ + if(trackplaying==0){ + if(!CurrentLinePtr)MMPrintString("First track is playing\r\n"); + return; + } + trackplaying--; + mp3callback(alist[trackplaying].fn,0); + } else if(CurrentlyPlaying == P_MIDI){ + if(trackplaying==0){ + if(!CurrentLinePtr)MMPrintString("First track is playing\r\n"); + return; + } + trackplaying--; + midicallback(alist[trackplaying].fn); + } else error("Nothing to play"); + + return; + } + if(checkstring(cmdline, (unsigned char *)"PAUSE")) { + if(CurrentlyPlaying22050.0)error("Valid is 0Hz to 20KHz"); + if(f_right<0.0 || f_right>22050.0)error("Valid is 0Hz to 20KHz"); + if(argc > 4) { + duration = ((float)getint(argv[4], 0, INT_MAX)/1000.0); //tone duration in seconds + PlayDuration=(uint64_t)duration; + } else duration=1; + if(argc == 7) { + if(!CurrentLinePtr)error("No program running"); + WAVInterrupt = (char *)GetIntAddress(argv[6]); // get the interrupt location + WAVcomplete=false; + InterruptUsed = true; + } + if(duration == 0) return; + if(PlayDuration != 0xffffffffffffffff && f_left >=10.0){ + hw=((float)PWM_FREQ/(float)f_left); //number of interrupts per cycle + duration = duration * (float)PWM_FREQ; // number of interrupts for the requested waveform + // This should now be an exact multiple of the number per waveform + PlayDuration=(((uint64_t)(duration/hw))*hw); + } + pwm_set_irq0_enabled(AUDIO_SLICE, false); + PhaseM_left = f_left / (float)PWM_FREQ * 4096.0; + PhaseM_right = f_right / (float)PWM_FREQ * 4096.0; + WAV_fnbr=0; + + SoundPlay = PlayDuration; + if (!(CurrentlyPlaying == P_PAUSE_TONE || CurrentlyPlaying == P_TONE )){ + setrate(PWM_FREQ); + PhaseAC_right=0.0; + PhaseAC_left=0.0; + if(Option.AUDIO_MISO_PIN)playimmediatevs1053(P_TONE); + } + CurrentlyPlaying = P_TONE; + pwm_set_irq0_enabled(AUDIO_SLICE, true); + pwm_set_enabled(AUDIO_SLICE, true); + return; + } + } + if((tp = checkstring(cmdline, (unsigned char *)"SOUND"))) {//PLAY SOUND channel, type, position, frequency, volume + float f_in, PhaseM; + int channel, left=0, right=0, lset=0, rset=0, local_sound_v_left=0,local_sound_v_right=0; + char *p; + uint16_t *lastleft=NULL, *lastright=NULL, *local_sound_mode_left=(uint16_t *)nulltable, *local_sound_mode_right=(uint16_t *)nulltable; + // get the command line arguments + getargs(&tp, 9,(unsigned char *)","); // this MUST be the first executable line in the function + if(!(argc == 9 || argc == 7 || argc == 5)) error("Argument count"); + if(checkstring(argv[4],(unsigned char *)"O")==NULL && argc == 5) error("Argument count"); + WAV_fnbr=0; + channel=getint(argv[0],1,MAXSOUNDS)-1; + lastleft=local_sound_mode_left=(uint16_t *)sound_mode_left[channel]; + lastright=local_sound_mode_right=(uint16_t *)sound_mode_right[channel]; + if(checkstring(argv[2],(unsigned char *)"L")!=NULL){ + left=1; + } else if(checkstring(argv[2],(unsigned char *)"R")!=NULL){ + right=1; + } else if(checkstring(argv[2],(unsigned char *)"B")!=NULL){ + right=1; + left=1; + } else { + p=(char *)getCstring(argv[2]); + if(strcasecmp(p,"B")==0){ + right=1; + left=1; + } else if(strcasecmp(p,"M")==0){ + right=1; + left=1; + } else if (strcasecmp(p,"L")==0){ + left=1; + } else if (strcasecmp(p,"R")==0){ + right=1; + } else error("Position must be L, R, or B"); + } + if(!(CurrentlyPlaying == P_NOTHING || CurrentlyPlaying == P_SOUND || CurrentlyPlaying == P_PAUSE_SOUND || CurrentlyPlaying == P_STOP || CurrentlyPlaying == P_WAVOPEN)) error("Sound output in use for $",PlayingStr[CurrentlyPlaying]); + if(checkstring(argv[4],(unsigned char *)"O")!=NULL && left){lset=1;local_sound_mode_left=(uint16_t *)nulltable;} + if(checkstring(argv[4],(unsigned char *)"O")!=NULL && right){rset=1;local_sound_mode_right=(uint16_t *)nulltable;} + if(checkstring(argv[4],(unsigned char *)"Q")!=NULL && left){lset=1;local_sound_mode_left=(uint16_t *)squaretable;} + if(checkstring(argv[4],(unsigned char *)"Q")!=NULL && right){rset=1;local_sound_mode_right=(uint16_t *)squaretable;} + if(checkstring(argv[4],(unsigned char *)"T")!=NULL && left){lset=1;local_sound_mode_left=(uint16_t *)triangletable;} + if(checkstring(argv[4],(unsigned char *)"T")!=NULL && right){rset=1;local_sound_mode_right=(uint16_t *)triangletable;} + if(checkstring(argv[4],(unsigned char *)"W")!=NULL && left){lset=1;local_sound_mode_left=(uint16_t *)sawtable;} + if(checkstring(argv[4],(unsigned char *)"W")!=NULL && right){rset=1;local_sound_mode_right=(uint16_t *)sawtable;} + if(checkstring(argv[4],(unsigned char *)"S")!=NULL && left){lset=1;local_sound_mode_left=(uint16_t *)SineTable;} + if(checkstring(argv[4],(unsigned char *)"S")!=NULL && right){rset=1;local_sound_mode_right=(uint16_t *)SineTable;} + if(checkstring(argv[4],(unsigned char *)"P")!=NULL && left){lset=1;setnoise();local_sound_mode_left=(uint16_t *)noisetable;} + if(checkstring(argv[4],(unsigned char *)"P")!=NULL && right){rset=1;setnoise();local_sound_mode_right=(uint16_t *)noisetable;} + if(checkstring(argv[4],(unsigned char *)"N")!=NULL && left){lset=1;local_sound_mode_left=(uint16_t *)whitenoise;} + if(checkstring(argv[4],(unsigned char *)"N")!=NULL && right){rset=1;local_sound_mode_right=(uint16_t *)whitenoise;} + if(checkstring(argv[4],(unsigned char *)"U")!=NULL && left){lset=1;local_sound_mode_left=(uint16_t *)usertable;} + if(checkstring(argv[4],(unsigned char *)"U")!=NULL && right){rset=1;local_sound_mode_right=(uint16_t *)usertable;} + if(left && lset==0){ + p=(char *)getCstring(argv[4]); + if(strcasecmp(p,"O")==0)local_sound_mode_left=(uint16_t *)nulltable; + if(strcasecmp(p,"Q")==0)local_sound_mode_left=(uint16_t *)squaretable; + if(strcasecmp(p,"T")==0)local_sound_mode_left=(uint16_t *)triangletable; + if(strcasecmp(p,"W")==0)local_sound_mode_left=(uint16_t *)sawtable; + if(strcasecmp(p,"S")==0)local_sound_mode_left=(uint16_t *)SineTable; + if(strcasecmp(p,"P")==0){setnoise();local_sound_mode_left=(uint16_t *)noisetable;} + if(strcasecmp(p,"N")==0)local_sound_mode_left=(uint16_t *)whitenoise; + if(strcasecmp(p,"U")==0)local_sound_mode_left=(uint16_t *)usertable; + if(local_sound_mode_left==NULL)error("Invalid type"); + else lset=1; + } + if(right && rset==0){ + p=(char *)getCstring(argv[4]); + if(strcasecmp(p,"O")==0)local_sound_mode_right=(uint16_t *)nulltable; + if(strcasecmp(p,"Q")==0)local_sound_mode_right=(uint16_t *)squaretable; + if(strcasecmp(p,"T")==0)local_sound_mode_right=(uint16_t *)triangletable; + if(strcasecmp(p,"W")==0)local_sound_mode_right=(uint16_t *)sawtable; + if(strcasecmp(p,"S")==0)local_sound_mode_right=(uint16_t *)SineTable; + if(strcasecmp(p,"P")==0){setnoise();local_sound_mode_right=(uint16_t *)noisetable;;} + if(strcasecmp(p,"N")==0)local_sound_mode_right=(uint16_t *)whitenoise; + if(strcasecmp(p,"U")==0)local_sound_mode_right=(uint16_t *)usertable; + if(local_sound_mode_right==NULL)error("Invalid type"); + else rset=1; + } + if((local_sound_mode_left==usertable || local_sound_mode_right==usertable) && usertable==NULL) error("Not loaded"); + f_in=10.0; + if(argc>=7)f_in = getnumber(argv[6]); + // get the arguments + if(f_in<1.0 || f_in>20000.0)error("Valid is 1Hz to 20KHz"); + if(left){ + if(!(sound_mode_left[channel]==whitenoise )){ + PhaseM = f_in / (float)PWM_FREQ * 4096.0; + } else { + PhaseM = f_in; + } + if(lastleft!=local_sound_mode_left){ + if(!Option.AUDIO_MISO_PIN)rampvolume(1,0,channel,0); + sound_PhaseAC_left[channel] = 0.0; + } + sound_PhaseM_left[channel] = PhaseM; + if(argc==9)local_sound_v_left=getint(argv[8],0,100/MAXSOUNDS); + else local_sound_v_left=25; + local_sound_v_left=local_sound_v_left*41/(100/MAXSOUNDS); + } + if(right){ + if(!(sound_mode_right[channel]==whitenoise )){ + PhaseM = f_in / (float)PWM_FREQ * 4096.0; + } else { + PhaseM = f_in; + } + if(lastright!=local_sound_mode_right){ + if(!Option.AUDIO_MISO_PIN)rampvolume(0,1,channel,0); + sound_PhaseAC_right[channel] = 0.0; + } + sound_PhaseM_right[channel] = PhaseM; + if(argc==9)local_sound_v_right=getint(argv[8],0,100/MAXSOUNDS); + else local_sound_v_right=25; + local_sound_v_right=local_sound_v_right*41/(100/MAXSOUNDS); + } + if(left && right && local_sound_v_left==local_sound_v_right && sound_v_left[channel]==sound_v_right[channel] && !Option.AUDIO_MISO_PIN)rampvolume(1,1,channel,local_sound_v_left); + else { + if(left && !Option.AUDIO_MISO_PIN)rampvolume(1,0,channel,local_sound_v_left); + if(right && !Option.AUDIO_MISO_PIN)rampvolume(0,1,channel,local_sound_v_right); + } + if(left)sound_mode_left[channel]=local_sound_mode_left; + if(right)sound_mode_right[channel]=local_sound_mode_right; + if(!(CurrentlyPlaying == P_SOUND || CurrentlyPlaying==P_PAUSE_SOUND)){ + setrate(PWM_FREQ); + if(Option.AUDIO_MISO_PIN)playimmediatevs1053(P_SOUND); + pwm_set_irq0_enabled(AUDIO_SLICE, true); + pwm_set_enabled(AUDIO_SLICE, true); + } + CurrentlyPlaying = P_SOUND; + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"WAV"))) { + char *p; + int i __attribute((unused))=0; + getargs(&tp, 3,(unsigned char *)","); // this MUST be the first executable line in the function + if(!(argc == 1 || argc == 3)) error("Argument count"); + if(CurrentlyPlaying==P_WAVOPEN)CloseAudio(1); + if(CurrentlyPlaying != P_NOTHING) error("Sound output in use for $",PlayingStr[CurrentlyPlaying]); + + if(!InitSDCard()) return; + p = (char *)getFstring(argv[0]); // get the file name + char q[FF_MAX_LFN]={0}; + getfullfilename(p,q); + WAVInterrupt = NULL; + WAVcomplete = 0; + if(argc == 3) { + if(!CurrentLinePtr)error("No program running"); + WAVInterrupt = (char *)GetIntAddress(argv[2]); // get the interrupt location + InterruptUsed = true; + } + if(FatFSFileSystem){ + FRESULT fr; + FILINFO fno; + int i; + if(ExistsDir(q,q,&i)){ + alist=GetMemory(sizeof(a_flist)*MAXALBUM); + trackstoplay=0; + trackplaying=0; + DIR djd; + djd.pat="*.wav"; + if(!CurrentLinePtr)MMPrintString("Directory found - commencing player\r\n"); + FSerror = f_opendir(&djd, q); + for(;;){ + fr=f_readdir(&djd, &fno); + if (fr != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ + // Get a directory item + if (pattern_matching(djd.pat, fno.fname, 0, 0)){ + // Get a directory item + strcpy(alist[trackstoplay].fn,"B:"); + strcat(alist[trackstoplay].fn,q); + strcat(alist[trackstoplay].fn,"/"); + strcat(alist[trackstoplay].fn,fno.fname); + str_replace(alist[trackstoplay].fn, "//", "/",3); + str_replace(alist[trackstoplay].fn, "/./", "/",3); + if(!CurrentLinePtr){ + MMPrintString(fno.fname); + PRet(); + } + trackstoplay++; + if(trackstoplay==MAXALBUM)break; + } + } + trackstoplay--; + f_closedir(&djd); + wavcallback(alist[trackplaying].fn); + return; + } + } + // open the file + trackstoplay=0; + trackplaying=0; + memset(q,0,sizeof(q)); + getfullfilename(p,q); + memmove(&q[2],q,strlen(q)); + q[1]=':'; + q[0]=FatFSFileSystem ? 'B' : 'A'; + wavcallback(q); + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"FLAC"))) { + char *p; + int i __attribute((unused))=0; + getargs(&tp, 3,(unsigned char *)","); // this MUST be the first executable line in the function + if(!(argc == 1 || argc == 3)) error("Argument count"); + if(CurrentlyPlaying==P_WAVOPEN)CloseAudio(1); + if(CurrentlyPlaying != P_NOTHING) error("Sound output in use for $",PlayingStr[CurrentlyPlaying]); + + if(!InitSDCard()) return; + p = (char *)getFstring(argv[0]); // get the file name + char q[FF_MAX_LFN]={0}; + getfullfilename(p,q); + WAVInterrupt = NULL; + WAVcomplete = 0; + if(argc == 3) { + if(!CurrentLinePtr)error("No program running"); + WAVInterrupt = (char *)GetIntAddress(argv[2]); // get the interrupt location + InterruptUsed = true; + } + if(FatFSFileSystem){ + FRESULT fr; + FILINFO fno; + int i; + if(ExistsDir(q,q,&i)){ + alist=GetMemory(sizeof(a_flist)*MAXALBUM); + trackstoplay=0; + trackplaying=0; + DIR djd; + djd.pat="*.flac"; + if(!CurrentLinePtr)MMPrintString("Directory found - commencing player\r\n"); + FSerror = f_opendir(&djd, q); + for(;;){ + fr=f_readdir(&djd, &fno); + if (fr != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ + // Get a directory item + if (pattern_matching(djd.pat, fno.fname, 0, 0)){ + // Get a directory item + strcpy(alist[trackstoplay].fn,"B:"); + strcat(alist[trackstoplay].fn,q); + strcat(alist[trackstoplay].fn,"/"); + strcat(alist[trackstoplay].fn,fno.fname); + str_replace(alist[trackstoplay].fn, "//", "/",3); + str_replace(alist[trackstoplay].fn, "/./", "/",3); + if(!CurrentLinePtr){ + MMPrintString(fno.fname); + PRet(); + } + trackstoplay++; + if(trackstoplay==MAXALBUM)break; + } + } + trackstoplay--; + f_closedir(&djd); + flaccallback(alist[trackplaying].fn); + return; + } + } + // open the file + trackstoplay=0; + trackplaying=0; + memset(q,0,sizeof(q)); + getfullfilename(p,q); + memmove(&q[2],q,strlen(q)); + q[1]=':'; + q[0]=FatFSFileSystem ? 'B' : 'A'; + flaccallback(q); + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"NOTE"))) { + if(!Option.AUDIO_MISO_PIN)error("Only available with VS1053 audio"); + if(!midienabled)error("Midi output not enabled"); + unsigned char *xp; + if((xp = checkstring(tp, (unsigned char *)"ON"))) { + getargs(&xp,5,(unsigned char *)","); + if(!(argc==5))error("Syntax"); + uint8_t channel=getint(argv[0],0,15); + uint8_t note=getint(argv[2],0,127); + uint8_t velocity=getint(argv[4],0,127); + noteOn(channel,note,velocity); + } else if((xp = checkstring(tp, (unsigned char *)"OFF"))) { + getargs(&xp,5,(unsigned char *)","); + if(!(argc==5 || argc==3))error("Syntax"); + uint8_t channel=getint(argv[0],0,15); + uint8_t note=getint(argv[2],0,127); + uint8_t velocity=0; + if(argc==5)velocity=getint(argv[4],0,127); + noteOff(channel,note,velocity); + } else error("Syntax"); + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"STREAM"))) { + getargs(&tp,5,(unsigned char *)","); + if(!(argc == 5 )) error("Syntax"); + if(!Option.AUDIO_MISO_PIN)error("Only available with VS1053 audio"); + if(CurrentlyPlaying==P_WAVOPEN)CloseAudio(1); + if(CurrentlyPlaying != P_NOTHING) error("Sound output in use for $",PlayingStr[CurrentlyPlaying]); + WAVInterrupt = NULL; + WAVcomplete = 0; + if(XDCS!=-1)error("VS1053 already open"); + void *ptr1 = NULL; + int64_t *aint; + streamsize=parseintegerarray(argv[0], &aint, 1, 1, NULL, true) * 8; + streambuffer=(char *)aint; + ptr1 = findvar(argv[2], V_FIND | V_EMPTY_OK | V_NOFIND_ERR); + if(g_vartbl[g_VarIndex].type & T_INT) { + if(g_vartbl[g_VarIndex].dims[0] != 0) error("Argument 2 must be an integer"); + streamreadpointer = (int *)ptr1; + } else error("Argument 2 must be an integer"); + ptr1 = findvar(argv[4], V_FIND | V_EMPTY_OK | V_NOFIND_ERR); + if(g_vartbl[g_VarIndex].type & T_INT) { + if(g_vartbl[g_VarIndex].dims[0] != 0) error("Argument 3 must be an integer"); + streamwritepointer = (int *)ptr1; + } else error("Argument 3 must be an integer"); + XCS=PinDef[Option.AUDIO_CS_PIN].GPno; + XDCS=PinDef[Option.AUDIO_DCS_PIN].GPno; + DREQ=PinDef[Option.AUDIO_DREQ_PIN].GPno; + XRST=PinDef[Option.AUDIO_RESET_PIN].GPno; + VS1053(XCS,XDCS,DREQ,XRST); + switchToMp3Mode(); + loadDefaultVs1053Patches(); + setVolumes(vol_left,vol_right); + MMPrintString("Stream output enabled\r\n"); + playreadcomplete=0; + CurrentlyPlaying=P_STREAM; + setrate(16000); //16KHz should be fast enough + pwm_set_irq0_enabled(AUDIO_SLICE, true); + pwm_set_enabled(AUDIO_SLICE, true); + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"MIDI"))) { + unsigned char *xp; + if((xp = checkstring(tp, (unsigned char *)"CMD"))) { + getargs(&xp,5,(unsigned char *)","); + if(!midienabled)error("Midi output not enabled"); + if(!(argc==5 || argc==3))error("Syntax"); + uint8_t cmd=getint(argv[0],128,255); + uint8_t data1=getint(argv[2],0,127); + uint8_t data2=0; + if(argc==5)data2=getint(argv[4],0,127); + talkMIDI(cmd, data1, data2); + return; + } + if((xp = checkstring(tp, (unsigned char *)"TEST"))) { + getargs(&xp,1,(unsigned char *)","); + if(!midienabled)error("Midi output not enabled"); + miditest(getint(argv[0],1,3)); + return; + } + if(!Option.AUDIO_MISO_PIN)error("Only available with VS1053 audio"); + if(CurrentlyPlaying==P_WAVOPEN)CloseAudio(1); + if(CurrentlyPlaying != P_NOTHING) error("Sound output in use for $",PlayingStr[CurrentlyPlaying]); + WAVInterrupt = NULL; + WAVcomplete = 0; + XCS=PinDef[Option.AUDIO_CS_PIN].GPno; + XDCS=PinDef[Option.AUDIO_DCS_PIN].GPno; + DREQ=PinDef[Option.AUDIO_DREQ_PIN].GPno; + XRST=PinDef[Option.AUDIO_RESET_PIN].GPno; + VS1053(XCS,XDCS,DREQ,XRST); + setVolumes(vol_left,vol_right); + midienabled=1; + miditest(0); + if(!CurrentLinePtr)MMPrintString("Real Time MIDI mode enabled\r\n"); + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"MIDIFILE"))) { + char *p; + int i __attribute((unused))=0; + getargs(&tp, 3,(unsigned char *)","); // this MUST be the first executable line in the function + if(!(argc == 1 || argc == 3)) error("Argument count"); + if(!Option.AUDIO_MISO_PIN)error("Only available with VS1053 audio"); + if(CurrentlyPlaying==P_WAVOPEN)CloseAudio(1); + if(CurrentlyPlaying != P_NOTHING) error("Sound output in use for $",PlayingStr[CurrentlyPlaying]); + + if(!InitSDCard()) return; + p = (char *)getFstring(argv[0]); // get the file name + char q[FF_MAX_LFN]={0}; + getfullfilename(p,q); + WAVInterrupt = NULL; + + WAVcomplete = 0; + if(argc == 3) { + if(!CurrentLinePtr)error("No program running"); + WAVInterrupt = (char *)GetIntAddress(argv[2]); // get the interrupt location + InterruptUsed = true; + } + if(FatFSFileSystem){ + FRESULT fr; + FILINFO fno; + int i; + if(ExistsDir(q,q,&i)){ + alist=GetMemory(sizeof(a_flist)*MAXALBUM); + trackstoplay=0; + trackplaying=0; + DIR djd; + djd.pat="*.mid"; + if(!CurrentLinePtr)MMPrintString("Directory found - commencing player\r\n"); + FSerror = f_opendir(&djd, q); + for(;;){ + fr=f_readdir(&djd, &fno); + if (fr != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ + // Get a directory item + if (pattern_matching(djd.pat, fno.fname, 0, 0)){ + // Get a directory item + strcpy(alist[trackstoplay].fn,"B:"); + strcat(alist[trackstoplay].fn,q); + strcat(alist[trackstoplay].fn,"/"); + strcat(alist[trackstoplay].fn,fno.fname); + str_replace(alist[trackstoplay].fn, "//", "/",3); + str_replace(alist[trackstoplay].fn, "/./", "/",3); + if(!CurrentLinePtr){ + MMPrintString(fno.fname); + PRet(); + } + trackstoplay++; + if(trackstoplay==MAXALBUM)break; + } + } + trackstoplay--; + f_closedir(&djd); + midicallback(alist[trackplaying].fn); + return; + } + } + // open the file + trackstoplay=0; + trackplaying=0; + memset(q,0,sizeof(q)); + getfullfilename(p,q); + memmove(&q[2],q,strlen(q)); + q[1]=':'; + q[0]=FatFSFileSystem ? 'B' : 'A'; + midicallback(q); + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"HALT"))) { + if(CurrentlyPlaying != P_MP3) error("Not playing an MP3"); + int fnbr = FindFreeFileNbr(); + char *buff=GetTempMemory(STRINGSIZE); + char *p=&WAVfilename[strlen(WAVfilename)]; + while(*p-- != '/'){} + p+=2; + strcpy(buff,"A:/"); + strcat(buff,p); + str_replace(buff,".mp3",".mem",1); + str_replace(buff,".MP3",".mem",1); + str_replace(buff,".Mp3",".mem",1); + str_replace(buff,".mP3",".mem",1); + if(!BasicFileOpen(buff, fnbr, FA_WRITE | FA_CREATE_ALWAYS)) return; + int i; + if(filesource[WAV_fnbr]==FLASHFILE)i = lfs_file_tell(&lfs,FileTable[fnbr].lfsptr) + 1; + else i = (*(FileTable[WAV_fnbr].fptr)).fptr + 1; + i-=418; + if(i<0)i=0; + IntToStr(buff,i,10); + FilePutStr(strlen(buff),buff,fnbr); + FilePutChar(',',fnbr); + FilePutStr(strlen(WAVfilename),WAVfilename,fnbr); + FileClose(fnbr); + CloseAudio(1); + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"CONTINUE"))) { + if(!Option.AUDIO_MISO_PIN)error("Only available with VS1053 audio"); + int fnbr = FindFreeFileNbr(); + char *p=(char *)getFstring(tp); + char *buff=GetTempMemory(STRINGSIZE); + if(strchr(p,'/') || strchr(p,':') || strchr(p,'\\') || strchr(p,'.'))error("Track name"); + strcpy(buff, "A:/"); + strcat(buff,p); + strcat(buff,".mem"); + if(!ExistsFile(buff))error("Track name"); + if(!BasicFileOpen(buff, fnbr, FA_READ)) return; + memset(buff,0,STRINGSIZE); + lfs_file_read(&lfs, FileTable[fnbr].lfsptr, buff, 255); + FileClose(fnbr); + p=strchr(buff,','); + p++; + int num=atoi(buff); + WAVInterrupt = NULL; + WAVcomplete = 0; + trackstoplay=0; + trackplaying=0; + mp3callback(p,num); + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"MP3"))) { + char *p; + int i __attribute((unused))=0; + getargs(&tp, 3,(unsigned char *)","); // this MUST be the first executable line in the function + if(!(argc == 1 || argc == 3)) error("Argument count"); +#ifndef rp2350 + if(!Option.AUDIO_MISO_PIN)error("Only available with VS1053 audio"); +#else + if(Option.CPU_Speed<200000 && !Option.AUDIO_MISO_PIN)error("CPUSPEED >=200000 for MP3 playback"); +#endif + if(CurrentlyPlaying==P_WAVOPEN)CloseAudio(1); + if(CurrentlyPlaying != P_NOTHING) error("Sound output in use for $",PlayingStr[CurrentlyPlaying]); + + if(!InitSDCard()) return; + p = (char *)getFstring(argv[0]); // get the file name + char q[FF_MAX_LFN]={0}; + getfullfilename(p,q); + WAVInterrupt = NULL; + WAVcomplete = 0; + if(argc == 3) { + if(!CurrentLinePtr)error("No program running"); + WAVInterrupt = (char *)GetIntAddress(argv[2]); // get the interrupt location + InterruptUsed = true; + } + if(FatFSFileSystem){ + FRESULT fr; + FILINFO fno; + int i; + if(ExistsDir(q,q,&i)){ + alist=GetMemory(sizeof(a_flist)*MAXALBUM); + trackstoplay=0; + trackplaying=0; + DIR djd; + djd.pat="*.mp3"; + if(!CurrentLinePtr)MMPrintString("Directory found - commencing player\r\n"); + FSerror = f_opendir(&djd, q); + for(;;){ + fr=f_readdir(&djd, &fno); + if (fr != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ + // Get a directory item + if (pattern_matching(djd.pat, fno.fname, 0, 0)){ + // Get a directory item + strcpy(alist[trackstoplay].fn,"B:"); + strcat(alist[trackstoplay].fn,q); + strcat(alist[trackstoplay].fn,"/"); + strcat(alist[trackstoplay].fn,fno.fname); + str_replace(alist[trackstoplay].fn, "//", "/",3); + str_replace(alist[trackstoplay].fn, "/./", "/",3); + if(!CurrentLinePtr){ + MMPrintString(fno.fname); + PRet(); + } + trackstoplay++; + if(trackstoplay==MAXALBUM)break; + } + } + trackstoplay--; + f_closedir(&djd); + mp3callback(alist[trackplaying].fn,0); + return; + } + } + // open the file + trackstoplay=0; + trackplaying=0; + memset(q,0,sizeof(q)); + getfullfilename(p,q); + memmove(&q[2],q,strlen(q)); + q[1]=':'; + q[0]=FatFSFileSystem ? 'B' : 'A'; + mp3callback(q,0); + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"MODFILE"))) { + getargs(&tp, 3,(unsigned char *)","); // this MUST be the first executable line in the function + char *p; + int i __attribute((unused))=0,fsize; + modfilesamplerate=22050; + if(CurrentlyPlaying==P_WAVOPEN)CloseAudio(1); + if(CurrentlyPlaying != P_NOTHING) error("Sound output in use"); +#ifdef rp2350 + if(!(modbuff || PSRAMsize))error("Mod playback not enabled"); +#else + if(!(modbuff))error("Mod playback not enabled"); +#endif + if(!InitSDCard()) return; + sbuff1 = GetMemory(MOD_BUFFER_SIZE); + sbuff2 = GetMemory(MOD_BUFFER_SIZE); + ubuff1 = (uint16_t *)sbuff1; + ubuff2 = (uint16_t *)sbuff2; + g_buff1 = (int16_t *)sbuff1; + g_buff2 = (int16_t *)sbuff2; + p = (char *)getFstring(argv[0]); // get the file name + WAVInterrupt = NULL; + WAVcomplete = 0; + // open the file + if(strchr((char *)p, '.') == NULL) strcat((char *)p, ".MOD"); + char q[FF_MAX_LFN]={0}; + getfullfilename(p,q); + memmove(&q[2],q,strlen(q)); + q[1]=':'; + q[0]=FatFSFileSystem ? 'B' : 'A'; + strcpy(WAVfilename,q); + WAV_fnbr = FindFreeFileNbr(); + if(!BasicFileOpen(p, WAV_fnbr, FA_READ)) return; + if(argc==3){ + if(!CurrentLinePtr)error("No program running"); + WAVInterrupt = (char *)GetIntAddress(argv[2]); // get the interrupt location + InterruptUsed = true; + noloop=1; + } else noloop=0; + i=0; + if(filesource[WAV_fnbr]!=FLASHFILE) fsize = f_size(FileTable[WAV_fnbr].fptr); + else fsize = lfs_file_size(&lfs,FileTable[WAV_fnbr].lfsptr); + int alreadythere=1; +#ifdef rp2350 + if(!PSRAMsize){ +#endif + if(RoundUpK4(fsize)>1024*Option.modbuffsize)error("File too large for modbuffer"); + char *check=modbuff; + while(!FileEOF(WAV_fnbr)) { + if(*check++ != FileGetChar(WAV_fnbr)){ + alreadythere=0; + break; + } + } +#ifdef rp2350 + } else { + modbuff=GetMemory(RoundUpK4(fsize)); + positionfile(WAV_fnbr,0); + char *r=modbuff; + while(!FileEOF(WAV_fnbr)) { + *r++=FileGetChar(WAV_fnbr); + } + } +#endif + if(!alreadythere){ + unsigned char *r = GetTempMemory(256); + positionfile(WAV_fnbr,0); + uint32_t j = RoundUpK4(TOP_OF_SYSTEM_FLASH); + disable_interrupts_pico(); + flash_range_erase(j, RoundUpK4(fsize)); + enable_interrupts_pico(); + while(!FileEOF(WAV_fnbr)) { + memset(r,0,256) ; + for(i=0;i<256;i++) { + if(FileEOF(WAV_fnbr))break; + r[i] = FileGetChar(WAV_fnbr); + } + disable_interrupts_pico(); + flash_range_program(j, (uint8_t *)r, 256); + enable_interrupts_pico(); + routinechecks(); + j+=256; + } + FileClose(WAV_fnbr); + } + FileClose(WAV_fnbr); + mcontext=GetMemory(sizeof(modcontext)); + hxcmod_init( mcontext ); + hxcmod_setcfg(mcontext, modfilesamplerate,1,1 ); + hxcmod_load( mcontext, (void*)modbuff, fsize ); + if(!mcontext->mod_loaded)error("Load failed"); + if(!CurrentLinePtr){ + MMPrintString("Playing ");MMPrintString((char *)mcontext->song.title);PRet(); + } + if(Option.AUDIO_MISO_PIN){ + playvs1053(P_MOD); + return; + } else { + hxcmod_fillbuffer( mcontext, (msample*)sbuff1, MOD_BUFFER_SIZE/8, NULL, noloop ); + } + wav_filesize=MOD_BUFFER_SIZE/4; + bcount[1]=MOD_BUFFER_SIZE/4; + bcount[2]=0; + if(Option.audio_i2s_bclk)i2sconvert(g_buff1, (int16_t *)sbuff1, bcount[1]); + else iconvert((uint16_t *)ubuff1, (int16_t *)sbuff1, bcount[1]); + nchannels=2; + CurrentlyPlaying = P_MOD; + swingbuf=1; + nextbuf=2; + ppos=0; + playreadcomplete=0; + setrate(44100); + audiorepeat=2; + pwm_set_irq0_enabled(AUDIO_SLICE, true); + pwm_set_enabled(AUDIO_SLICE, true); + Timer1=500; + while(Timer1){ + checkWAVinput(); + #ifdef PICOMITEWEB + ProcessWeb(1); + #endif + } + return; + } + if((tp = checkstring(cmdline, (unsigned char *)"MODSAMPLE"))) { + unsigned short sampnum, seffectnum; + unsigned char volume; + unsigned int samprate, period; + getargs(&tp, 5,(unsigned char *)","); // this MUST be the first executable line in the function + if(!(argc == 5 || argc == 3)) error("Argument count"); + + if(!(CurrentlyPlaying == P_MOD)) error("Samples play over MOD file"); + + sampnum = getint(argv[0],1,32) - 1; + + seffectnum = getint(argv[2],1,NUMMAXSEFFECTS) - 1; + + volume=63; + if(argc >= 5 && *argv[4]) { + volume = getint(argv[4],0,64) - 1; + if (volume <0 ) volume = 0; + } + samprate = 16000; + + period = 3579545 / samprate; + + hxcmod_playsoundeffect( mcontext, sampnum, seffectnum, volume, period ); + return; + } + error("Unknown command"); +} +/* + * @cond + * The following section will be excluded from the documentation. + */ + +/****************************************************************************************** +Timer interrupt. +Used to send data to the DAC +*******************************************************************************************/ + +/****************************************************************************************** +Stop playing the music or toneb: + +*******************************************************************************************/ +void StopAudio(void) { + + if(CurrentlyPlaying != P_NOTHING ) { + int ramptime=1000000/PWM_FREQ-1; + pwm_set_irq0_enabled(AUDIO_SLICE, false); + uSec(100); // + if(!(Option.audio_i2s_bclk)) + { + uint32_t rr,r=right; + uint32_t ll,l=left; + uint32_t m=2000; + l=m-l; + r=m-r; + for(int i=100;i>=0;i--){ + ll=(uint32_t)((int)m-(int)l*i/100); + rr=(uint32_t)((int)m-(int)r*i/100); + AudioOutput(ll,rr); + uSec(ramptime); + } + CurrentlyPlaying = P_STOP; + uSec(ramptime*2); + setrate(PWM_FREQ); + } + ppos=0; + if(Option.AUDIO_MISO_PIN && (CurrentlyPlaying == P_TONE || CurrentlyPlaying==P_SOUND))CurrentlyPlaying = P_WAVOPEN; + else CurrentlyPlaying = P_NOTHING; + } + SoundPlay = 0; +} +/****************************************************************************************** + * Maintain the WAV sample buffer +*******************************************************************************************/ +void checkWAVinput(void){ + audio_checks(); + if(playreadcomplete==1)return; + if(swingbuf != nextbuf){ //IR has moved to next buffer + if(Option.AUDIO_MISO_PIN){ + if(CurrentlyPlaying == P_FLAC || CurrentlyPlaying == P_WAV || (CurrentlyPlaying == P_MP3 && Option.AUDIO_MISO_PIN) ||CurrentlyPlaying == P_MIDI){ + if(swingbuf==2){ + bcount[1]=(volatile unsigned int)onRead(NULL,sbuff1,WAV_BUFFER_SIZE); + wav_filesize = bcount[1]; + } else { + bcount[2]=(volatile unsigned int)onRead(NULL,sbuff2,WAV_BUFFER_SIZE); + wav_filesize = bcount[2]; + } + nextbuf=swingbuf; + diskchecktimer=DISKCHECKRATE; + } else if(CurrentlyPlaying == P_MOD){ + if(swingbuf==2){ + if(hxcmod_fillbuffer( mcontext, (msample*)sbuff1, WAV_BUFFER_SIZE/4,NULL, noloop ))playreadcomplete = 1; + wav_filesize=WAV_BUFFER_SIZE; + bcount[1]=WAV_BUFFER_SIZE; + } else { + if(hxcmod_fillbuffer( mcontext, (msample*)sbuff2, WAV_BUFFER_SIZE/4,NULL, noloop ))playreadcomplete = 1; + wav_filesize=WAV_BUFFER_SIZE; + bcount[2]=WAV_BUFFER_SIZE; + } + nextbuf=swingbuf; + } + } else { + if(CurrentlyPlaying == P_FLAC){ + if(swingbuf==2){ + bcount[1]=(volatile unsigned int)drflac_read_pcm_frames_s16(myflac, FLAC_BUFFER_SIZE/4, (drwav_int16*)sbuff1) * myflac->channels; + if(Option.audio_i2s_bclk) i2sconvert((drwav_int16*)sbuff1,(drwav_int16*)sbuff1,bcount[1]); + else iconvert(ubuff1, (int16_t *)sbuff1, bcount[1]); + wav_filesize = bcount[1]; + } else { + bcount[2]=(volatile unsigned int)drflac_read_pcm_frames_s16(myflac, FLAC_BUFFER_SIZE/4, (drwav_int16*)sbuff2) * myflac->channels; + if(Option.audio_i2s_bclk) i2sconvert((drwav_int16*)sbuff2,(drwav_int16*)sbuff2,bcount[2]); + else iconvert(ubuff2, (int16_t *)sbuff2, bcount[2]); + wav_filesize = bcount[2]; + } + nextbuf=swingbuf; +#ifdef rp2350 + } else if(CurrentlyPlaying == P_MP3){ + if(swingbuf==2){ + bcount[1]=drmp3_read_pcm_frames_s16(mymp3, MP3_BUFFER_SIZE/4, (int16_t *)sbuff1) * mymp3->channels; + if(!Option.audio_i2s_bclk)iconvert(ubuff1, (int16_t *)sbuff1, bcount[1]); + else i2sconvert(g_buff1, (int16_t *)sbuff1, bcount[1]); + wav_filesize = bcount[1]; + } else { + bcount[2]=drmp3_read_pcm_frames_s16(mymp3, MP3_BUFFER_SIZE/4, (int16_t *)sbuff2) * mymp3->channels; + if(!Option.audio_i2s_bclk)iconvert(ubuff2, (int16_t *)sbuff2, bcount[2]); + else i2sconvert(g_buff2, (int16_t *)sbuff2, bcount[2]); + wav_filesize = bcount[2]; + } + nextbuf=swingbuf; +#endif + } else if(CurrentlyPlaying == P_MOD){ + if(swingbuf==2){ + if(hxcmod_fillbuffer( mcontext, (msample*)sbuff1, MOD_BUFFER_SIZE/4,NULL, noloop ))playreadcomplete = 1; + wav_filesize=MOD_BUFFER_SIZE/2; + bcount[1]=MOD_BUFFER_SIZE/2; + if(Option.audio_i2s_bclk)i2sconvert(g_buff1, (int16_t *)sbuff1, bcount[1]); + else iconvert((uint16_t *)ubuff1, (int16_t *)sbuff1, bcount[1]); + } else { + if(hxcmod_fillbuffer( mcontext, (msample*)sbuff2, MOD_BUFFER_SIZE/4,NULL, noloop ))playreadcomplete = 1; + wav_filesize=MOD_BUFFER_SIZE/2; + bcount[2]=MOD_BUFFER_SIZE/2; + if(Option.audio_i2s_bclk)i2sconvert(g_buff2, (int16_t *)sbuff2, bcount[2]); + else iconvert((uint16_t *)ubuff2, (int16_t *)sbuff2, bcount[2]); + } + nextbuf=swingbuf; + } else if(CurrentlyPlaying == P_WAV){ + if(swingbuf==2){ + bcount[1]=(volatile unsigned int)drwav_read_pcm_frames_s16(mywav, WAV_BUFFER_SIZE/4, (drwav_int16*)sbuff1) * mywav->channels; + if(Option.audio_i2s_bclk) i2sconvert(g_buff1,(drwav_int16*)sbuff1,bcount[1]); + else iconvert(ubuff1, (int16_t *)sbuff1, bcount[1]); + wav_filesize = bcount[1]; + } else { + bcount[2]=(volatile unsigned int)drwav_read_pcm_frames_s16(mywav, WAV_BUFFER_SIZE/4, (drwav_int16*)sbuff2) * mywav->channels; + if(Option.audio_i2s_bclk) i2sconvert(g_buff2,(drwav_int16*)sbuff2,bcount[2]); + else iconvert(ubuff2, (int16_t *)sbuff2, bcount[2]); + wav_filesize = bcount[2]; + } + nextbuf=swingbuf; + diskchecktimer=DISKCHECKRATE; + } + } + } + if(wav_filesize<=0 && (CurrentlyPlaying == P_WAV || (CurrentlyPlaying == P_FLAC)|| (CurrentlyPlaying == P_MP3) || (CurrentlyPlaying == P_MIDI))){ + if(trackplaying==trackstoplay) { + playreadcomplete=1; + } else { + if(CurrentlyPlaying == P_WAV){ + trackplaying++; + wavcallback(alist[trackplaying].fn); + } else if(CurrentlyPlaying == P_FLAC){ + trackplaying++; + flaccallback(alist[trackplaying].fn); + } else if(CurrentlyPlaying == P_MP3){ + trackplaying++; + mp3callback(alist[trackplaying].fn,0); + } else if(CurrentlyPlaying == P_MIDI){ + if(Option.AUDIO_MISO_PIN && VSbuffer>32)return; + trackplaying++; + midicallback(alist[trackplaying].fn); + } + } + } +} +void audio_checks(void){ + if(playreadcomplete == 1) { + if(!(bcount[1] || bcount[2]) ){ + if(Option.AUDIO_MISO_PIN && VSbuffer>32)return; + if(CurrentlyPlaying == P_FLAC)drflac_close(myflac); + if(CurrentlyPlaying == P_MOD)FreeMemory((void *)mcontext); + if(CurrentlyPlaying == P_WAV)FreeMemorySafe((void **)&mywav); +#ifdef rp2350 + if(CurrentlyPlaying == P_MP3 && Option.AUDIO_MISO_PIN==0){ + drmp3_uninit(mymp3); + FreeMemorySafe((void **)&mymp3); + } +#endif + FreeMemorySafe((void **)&sbuff1); + FreeMemorySafe((void **)&sbuff2); + FreeMemorySafe((void **)&alist); + if(Option.AUDIO_MISO_PIN){ + pwm_set_irq0_enabled(AUDIO_SLICE, false); + CurrentlyPlaying = P_NOTHING; + } else StopAudio(); + FileClose(WAV_fnbr); + WAVcomplete = true; +// playreadcomplete = 0; + } + } +} +/* @endcond */ diff --git a/Code/pico_multi_booter/picomite/Audio.h b/Code/pico_multi_booter/picomite/Audio.h new file mode 100644 index 0000000..d8d304f --- /dev/null +++ b/Code/pico_multi_booter/picomite/Audio.h @@ -0,0 +1,102 @@ +/* + * @cond + * The following section will be excluded from the documentation. + */ +/* ********************************************************************************************************************* +PicoMite MMBasic + +audio.h + + Geoff Graham, Peter Mather +Copyright (c) 2021, All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the distribution. +3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed + on the console at startup (additional copyright messages may be added). +4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed + by the . +5. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software + without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +************************************************************************************************************************/ + + + +/* ******************************************************************************** + the C language function associated with commands, functions or operators should be + declared here +**********************************************************************************/ +#if !defined(INCLUDE_COMMAND_TABLE) && !defined(INCLUDE_TOKEN_TABLE) +// void cmd_tts(void); + void CloseAudio(int all); + void StopAudio(void); + void audioInterrupt(void); + void CheckAudio(void); + extern volatile int vol_left, vol_right; +#endif + + +#if !defined(INCLUDE_COMMAND_TABLE) && !defined(INCLUDE_TOKEN_TABLE) +// General definitions used by other modules + +#ifndef AUDIO_HEADER +#define AUDIO_HEADER +typedef enum {P_PAUSE_TONE, P_PAUSE_FLAC, P_PAUSE_MP3, P_PAUSE_SOUND, P_PAUSE_MOD, P_PAUSE_WAV, P_STOP, + P_NOTHING, P_TONE, P_SOUND, P_WAV, P_FLAC, P_MP3, + P_MIDI, P_SYNC, P_MOD, P_STREAM, P_WAVOPEN} e_CurrentlyPlaying; +extern const char* const PlayingStr[]; +extern volatile e_CurrentlyPlaying CurrentlyPlaying; +extern char *WAVInterrupt; +extern bool WAVcomplete; +extern int WAV_fnbr; +extern int PWM_FREQ; +extern char *sbuff1, *sbuff2, *modbuff; +extern int16_t *g_buff1, *g_buff2; +extern volatile uint32_t bcount[3]; +extern volatile int wav_filesize; // head and tail of the ring buffer for com1 +extern uint8_t trackplaying, trackstoplay; +extern void checkWAVinput(void); +extern volatile uint64_t SoundPlay; +extern void (*AudioOutput)(uint16_t left, uint16_t right); +#define WAV_BUFFER_SIZE 8192 +extern const unsigned short SineTable[4096]; +extern const unsigned short nulltable[]; +extern const unsigned short squaretable[]; +extern volatile float PhaseM_left, PhaseM_right; +extern volatile unsigned char PWM_count; +extern uint16_t *playbuff; +extern int16_t *uplaybuff; +extern uint16_t left,right; +extern volatile int sound_v_left[MAXSOUNDS]; +extern volatile int sound_v_right[MAXSOUNDS]; +extern volatile float sound_PhaseAC_left[MAXSOUNDS], sound_PhaseAC_right[MAXSOUNDS]; +extern volatile float sound_PhaseM_left[MAXSOUNDS], sound_PhaseM_right[MAXSOUNDS]; +extern volatile unsigned short * sound_mode_left[MAXSOUNDS]; +extern volatile unsigned short * sound_mode_right[MAXSOUNDS]; +extern volatile int ppos; // playing position for PLAY WAV +extern volatile float PhaseAC_left, PhaseAC_right; +extern volatile int swingbuf,nextbuf, playreadcomplete; +extern volatile uint8_t mono; +extern volatile uint8_t audiorepeat; +extern int PWM_FREQ; +extern void (*AudioOutput)(uint16_t left, uint16_t right); +extern uint16_t AUDIO_SPI, AUDIO_CLK_PIN,AUDIO_MOSI_PIN,AUDIO_MISO_PIN, AUDIO_CS_PIN, AUDIO_RESET_PIN, AUDIO_DREQ_PIN, AUDIO_DCS_PIN, AUDIO_LDAC_PIN; +extern int streamsize; +extern volatile int *streamwritepointer; +extern volatile int *streamreadpointer; +extern char *streambuffer; +extern char WAVfilename[FF_MAX_LFN]; +typedef struct sa_flist { + char fn[FF_MAX_LFN]; +} a_flist; +extern a_flist *alist; +#endif +#endif +/* @endcond */ diff --git a/Code/pico_multi_booter/picomite/BmpDecoder.c b/Code/pico_multi_booter/picomite/BmpDecoder.c new file mode 100644 index 0000000..308d97c --- /dev/null +++ b/Code/pico_multi_booter/picomite/BmpDecoder.c @@ -0,0 +1,526 @@ +/* + * @cond + * The following section will be excluded from the documentation. + */ +#define __BMPDECODER_C__ +/****************************************************************************** + +* FileName: BmpDecoder.c +* Dependencies: Image decoding library; project requires File System library +* Processor: PIC24/dsPIC30/dsPIC33/PIC32MX +* Compiler: C30 v2.01/C32 v0.00.18 +* Company: Microchip Technology, Inc. + + * Software License Agreement + * + * Copyright � 2008 Microchip Technology Inc. All rights reserved. + * Microchip licenses to you the right to use, modify, copy and distribute + * Software only when embedded on a Microchip microcontroller or digital + * signal controller, which is integrated into your product or third party + * product (pursuant to the sublicense terms in the accompanying license + * agreement). + * + * You should refer to the license agreement accompanying this Software + * for additional information regarding your rights and obligations. + * + * SOFTWARE AND DOCUMENTATION ARE PROVIDED �AS IS� WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY + * OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR + * PURPOSE. IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR + * OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, + * BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT + * DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, + * INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, + * COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY + * CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), + * OR OTHER SIMILAR COSTS. + +Author Date Comments +-------------------------------------------------------------------------------- +Pradeep Budagutta 03-Mar-2008 First release +*******************************************************************************/ + +//#include "GenericTypeDefs.h" +#include "MMBasic_Includes.h" +#include "Hardware_Includes.h" +//** SD CARD INCLUDES *********************************************************** +#include "ff.h" + + +#define IMG_FILE fnbr +int IMG_FREAD(int fnbr, void *buff, int count, unsigned int *read){ + if(filesource[IMG_FILE]==FATFSFILE){ + return f_read(FileTable[IMG_FILE].fptr, buff, count, (UINT *)read); + } else { + int n=lfs_file_read(&lfs, FileTable[IMG_FILE].lfsptr, buff, count); + if(n>=0)return 0; + else return n; + } +} +#define IMG_vSetboundaries() +#define IMG_vLoopCallback() +#define IMG_vCheckAndAbort() CheckAbort() +union colourmap +{ + char rgbbytes[4]; + unsigned int rgb; +} colour, colour2; +#define IMG_vSetColor(red, green, blue) {colour.rgbbytes[2] = red; colour.rgbbytes[1] = green; colour.rgbbytes[0] = blue;} +#define IMG_vSetColor2(red, green, blue) {colour2.rgbbytes[2] = red; colour2.rgbbytes[1] = green; colour2.rgbbytes[0] = blue;} +#define IMG_vPutPixel(xx, yy) DrawPixel(xx + x, yy + y, colour.rgb) +#define IMG_vPutPixel2(xx, yy) DrawPixel(xx + x, yy + y, colour2.rgb) +#define LONG long +int IMG_wImageWidth, IMG_wImageHeight; +int bufpos; + +/*************************/ +/**** DATA STRUCTURES ****/ +/*************************/ +typedef struct _BMPDECODER +{ + LONG lWidth; + LONG lHeight; + LONG lImageOffset; + WORD wPaletteEntries; + BYTE bBitsPerPixel; + BYTE bHeaderType; + BYTE blBmMarkerFlag : 1; + BYTE blCompressionType : 3; + BYTE bNumOfPlanes : 3; + BYTE b16bit565flag : 1; + BYTE aPalette[256][3]; /* Each palette entry has RGB */ +} BMPDECODER; + +/**************************/ +/******* FUNCTIONS *******/ +/**************************/ + +/******************************************************************************* +Function: void BDEC_vResetData(BMPDECODER *pBmpDec) + +Precondition: None + +Overview: This function resets the variables so that new Bitmap image + can be decoded + +Input: Bitmap decoder's data structure + +Output: None +*******************************************************************************/ +void BDEC_vResetData(BMPDECODER *pBmpDec) +{ + pBmpDec->lWidth = 0; + pBmpDec->lHeight = 0; + pBmpDec->lImageOffset = 0; + pBmpDec->wPaletteEntries = 0; + pBmpDec->bBitsPerPixel = 0; + pBmpDec->bHeaderType = 0; + pBmpDec->blBmMarkerFlag = 0; + pBmpDec->blCompressionType = 0; + pBmpDec->bNumOfPlanes = 0; + pBmpDec->b16bit565flag = 0; +} + +/******************************************************************************* +Function: BYTE BDEC_bReadHeader(BMPDECODER *pBmpDec) + +Precondition: None + +Overview: This function reads the Bitmap file header and + fills the data structure + +Input: Bitmap decoder's data structure + +Output: Error code - '0' means no error +*******************************************************************************/ +BYTE BDEC_bReadHeader(BMPDECODER *pBmpDec, int fnbr) +{ + BYTE bByte1, bByte2; + WORD wWord; + LONG lLong; + unsigned int nbr; + FSerror = IMG_FREAD(IMG_FILE, &bByte1, 1, &nbr); /* Marker */ + FSerror = IMG_FREAD(IMG_FILE, &bByte2, 1, &nbr); /* Marker */ + + if(bByte1 == 'B' && bByte2 == 'M') + { + pBmpDec->blBmMarkerFlag = 1; + } + else + { + return(100); + } + + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* File length */ + FSerror = IMG_FREAD(IMG_FILE, &wWord, 2, &nbr); /* Reserved */ + FSerror = IMG_FREAD(IMG_FILE, &wWord, 2, &nbr); /* Reserved */ + + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* Image offset */ + pBmpDec->lImageOffset = lLong; + + FSerror = IMG_FREAD(IMG_FILE,&lLong, 4, &nbr); /* Header length */ + pBmpDec->bHeaderType = (BYTE)lLong; + + if(pBmpDec->bHeaderType >= 40) + { + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* Image Width */ + pBmpDec->lWidth = lLong; + + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* Image Height */ + pBmpDec->lHeight = lLong; + + FSerror = IMG_FREAD(IMG_FILE, &wWord, 2, &nbr); /* Number of Planes */ + pBmpDec->bNumOfPlanes = (BYTE)wWord; + + FSerror = IMG_FREAD(IMG_FILE, &wWord, 2, &nbr); /* Bits per Pixel */ + pBmpDec->bBitsPerPixel = (BYTE)wWord; + + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* Compression info */ + pBmpDec->blCompressionType = (BYTE)lLong; + + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* Image length */ + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* xPixels per metre */ + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* yPixels per metre */ + + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* Palette entries */ + pBmpDec->wPaletteEntries = (WORD)lLong; + + if(pBmpDec->wPaletteEntries == 0) + { + WORD wTemp = (WORD)(pBmpDec->lImageOffset - 14 - 40)/4; + if(wTemp > 0) + { + pBmpDec->wPaletteEntries = wTemp; /* This is because of a bug in MSPAINT */ + } + } + + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* Important colors */ + if(pBmpDec->bBitsPerPixel == 16 && pBmpDec->bHeaderType > 40) + { + FSerror = IMG_FREAD(IMG_FILE, &lLong, 4, &nbr); /* Red mask */ + if((WORD)lLong == 0xF800) + { + pBmpDec->b16bit565flag = 1; + } + } + +// IMG_FSEEK(&nbr, pBmpDec->bHeaderType + 14, 0); + + if(pBmpDec->wPaletteEntries <= 256) + { + WORD wCounter; + for(wCounter = 0; wCounter < pBmpDec->wPaletteEntries; wCounter++) + { + FSerror = IMG_FREAD(IMG_FILE, &pBmpDec->aPalette[wCounter][2], 1, &nbr); /* R */ + FSerror = IMG_FREAD(IMG_FILE, &pBmpDec->aPalette[wCounter][1], 1, &nbr); /* G */ + FSerror = IMG_FREAD(IMG_FILE, &pBmpDec->aPalette[wCounter][0], 1, &nbr); /* B */ + FSerror = IMG_FREAD(IMG_FILE, &wWord, 1, &nbr); /* Dummy */ + } + } + } + return(0); +} + +/******************************************************************************* +Function: BYTE BMP_bDecode(IMG_FILE *pFile) + +Precondition: None + +Overview: This function decodes and displays a Bitmap image + +Input: Image file + +Output: Error code - '0' means no error +*******************************************************************************/ +BYTE BMP_bDecode(int x, int y, int fnbr) +{ + BMPDECODER BmpDec; + WORD wX, wY; + BYTE bPadding; + unsigned int nbr; + BDEC_vResetData(&BmpDec); + BDEC_bReadHeader(&BmpDec, fnbr); + if(BmpDec.blBmMarkerFlag == 0 || BmpDec.bHeaderType < 40 || (BmpDec.blCompressionType != 0 && BmpDec.blCompressionType != 2 && BmpDec.blCompressionType != 3)) + { + return 100; + } + IMG_wImageWidth = (WORD)BmpDec.lWidth; + IMG_wImageHeight = (WORD)BmpDec.lHeight; + IMG_vSetboundaries(); + char *linebuff=GetMemory(IMG_wImageWidth*3); //get a line buffer + +// IMG_FSEEK(pFile, BmpDec.lImageOffset, 0); + if(BmpDec.wPaletteEntries == 0 && BmpDec.bBitsPerPixel == 8) /* Grayscale Image */ + { + bPadding = (4 - (BmpDec.lWidth % 4))%4; + for(wY = 0; wY < BmpDec.lHeight; wY++) + { + IMG_vLoopCallback(); + IMG_vCheckAndAbort(); + for(wX = 0; wX < BmpDec.lWidth; wX++) + { + BYTE bY; + FSerror = IMG_FREAD(IMG_FILE, &bY, 1, &nbr); /* Y */ + IMG_vSetColor(bY, bY, bY); + IMG_vPutPixel(wX, BmpDec.lHeight - wY - 1); + } + for(wX = 0; wX < bPadding; wX++) + { + BYTE bValue; + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + } + } + } + else if(BmpDec.bBitsPerPixel == 16) /* 16-bit Color Image */ + { + bPadding = (4 - ((BmpDec.lWidth*2) % 4))%4; + for(wY = 0; wY < BmpDec.lHeight; wY++) + { + IMG_vLoopCallback(); + IMG_vCheckAndAbort(); + for(wX = 0; wX < BmpDec.lWidth; wX++) + { + WORD wColor; + BYTE bR, bG, bB; + FSerror = IMG_FREAD(IMG_FILE, &wColor, 2, &nbr); /* RGB */ + if(BmpDec.b16bit565flag == 1) + { + bR = (wColor >> 11) << 3; + bG = ((wColor & 0x07E0) >> 5) << 2; + bB = (wColor & 0x001F) << 3; + } + else + { + bR = ((wColor & 0x7FFF) >> 10) << 3; + bG = ((wColor & 0x03E0) >> 5) << 3; + bB = (wColor & 0x001F) << 3; + } + IMG_vSetColor(bR, bG, bB); + IMG_vPutPixel(wX, BmpDec.lHeight - wY - 1); + } + for(wX = 0; wX < bPadding; wX++) + { + BYTE bValue; + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + } + } + } + else if(BmpDec.bBitsPerPixel == 24) /* True color Image */ + { + int pp; + bPadding = (4 - ((BmpDec.lWidth*3) % 4))%4; + for(wY = 0; wY < BmpDec.lHeight; wY++) + { + IMG_vLoopCallback(); + IMG_vCheckAndAbort(); + FSerror = IMG_FREAD(IMG_FILE, linebuff, BmpDec.lWidth*3, &nbr); /* B */ + pp=0; + for(wX = 0; wX < BmpDec.lWidth; wX++) + { + colour.rgbbytes[0]=linebuff[pp++]; + colour.rgbbytes[1]=linebuff[pp++]; + colour.rgbbytes[2]=linebuff[pp++]; + IMG_vPutPixel(wX, BmpDec.lHeight - wY - 1); + } + for(wX = 0; wX < bPadding; wX++) + { + BYTE bValue; + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + } + } + } + else if(BmpDec.wPaletteEntries != 0 && BmpDec.bBitsPerPixel == 1) /* B/W Image */ + { + WORD wBytesPerRow = BmpDec.lWidth/8; + BYTE bAdditionalBitsPerRow = BmpDec.lWidth % 8; + bPadding = (4 - ((wBytesPerRow + (bAdditionalBitsPerRow?1:0)) % 4))%4; + for(wY = 0; wY < BmpDec.lHeight; wY++) + { + BYTE bBits, bValue; + IMG_vLoopCallback(); + IMG_vCheckAndAbort(); + for(wX = 0; wX < wBytesPerRow; wX++) + { + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + + for(bBits = 0; bBits < 8; bBits++) + { + BYTE bIndex = (bValue & (0x80 >> bBits))?1:0; + IMG_vSetColor(BmpDec.aPalette[bIndex][0], BmpDec.aPalette[bIndex][1], BmpDec.aPalette[bIndex][2]); + IMG_vPutPixel(wX*8+bBits, BmpDec.lHeight - wY - 1); + } + } + if(bAdditionalBitsPerRow > 0) + { + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + + for(bBits = 0; bBits < bAdditionalBitsPerRow; bBits++) + { + BYTE bIndex = (bValue & (0x80 >> bBits))?1:0; + IMG_vSetColor(BmpDec.aPalette[bIndex][0], BmpDec.aPalette[bIndex][1], BmpDec.aPalette[bIndex][2]); + IMG_vPutPixel(wX*8+bBits, BmpDec.lHeight - wY - 1); + } + } + for(wX = 0; wX < bPadding; wX++) + { + BYTE bValue; + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + } + } + } + else if(BmpDec.wPaletteEntries != 0 && BmpDec.bBitsPerPixel == 4 && BmpDec.blCompressionType != 2) /* 16 colors Image */ + { + WORD wBytesPerRow = BmpDec.lWidth/2; + BYTE bAdditionalNibblePerRow = BmpDec.lWidth % 2; + bPadding = (4 - ((wBytesPerRow + bAdditionalNibblePerRow) % 4))%4; + for(wY = 0; wY < BmpDec.lHeight; wY++) + { + IMG_vLoopCallback(); + IMG_vCheckAndAbort(); + for(wX = 0; wX < wBytesPerRow; wX++) + { + BYTE bIndex, bValue; + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + bIndex = bValue >> 4; + IMG_vSetColor(BmpDec.aPalette[bIndex][0], BmpDec.aPalette[bIndex][1], BmpDec.aPalette[bIndex][2]); + IMG_vPutPixel(wX*2, BmpDec.lHeight - wY - 1); + bIndex = bValue & 0x0F; + IMG_vSetColor(BmpDec.aPalette[bIndex][0], BmpDec.aPalette[bIndex][1], BmpDec.aPalette[bIndex][2]); + IMG_vPutPixel(wX*2+1, BmpDec.lHeight - wY - 1); + } + if(bAdditionalNibblePerRow) + { + BYTE bIndex, bValue; + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); /* Bits8 */ + bIndex = bValue >> 4; + IMG_vSetColor(BmpDec.aPalette[bIndex][0], BmpDec.aPalette[bIndex][1], BmpDec.aPalette[bIndex][2]); + IMG_vPutPixel(wX*2, BmpDec.lHeight - wY - 1); + } + for(wX = 0; wX < bPadding; wX++) + { + BYTE bValue; + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + } + } + } + else if(BmpDec.wPaletteEntries != 0 && BmpDec.bBitsPerPixel == 4 && BmpDec.blCompressionType==2) /* 16 colors Image */ + { + BYTE bIndex; + int c; + unsigned char b[2]; + for(wY = 0; wY < BmpDec.lHeight; wY++) + { + IMG_vCheckAndAbort(); + wX=0; + do { + FSerror = IMG_FREAD(IMG_FILE, b, 2, &nbr); + c=b[0]; + bIndex = b[1] >> 4; + IMG_vSetColor(BmpDec.aPalette[bIndex][0], BmpDec.aPalette[bIndex][1], BmpDec.aPalette[bIndex][2]); + bIndex = b[1] & 0xf; + IMG_vSetColor2(BmpDec.aPalette[bIndex][0], BmpDec.aPalette[bIndex][1], BmpDec.aPalette[bIndex][2]); + while(c){ + IMG_vPutPixel(wX++, BmpDec.lHeight - wY - 1); + IMG_vPutPixel2(wX++, BmpDec.lHeight - wY - 1); + c-=2; + } + } while(!(b[0]==0 && b[1]==0)); + + } + } + + else if(BmpDec.wPaletteEntries != 0 && BmpDec.bBitsPerPixel == 8) /* 256 colors Image */ + { + bPadding = (4 - (BmpDec.lWidth % 4))%4; + for(wY = 0; wY < BmpDec.lHeight; wY++) + { + IMG_vLoopCallback(); IMG_vCheckAndAbort(); + for(wX = 0; wX < BmpDec.lWidth; wX++) + { + BYTE bIndex; + FSerror = IMG_FREAD(IMG_FILE, &bIndex, 1, &nbr); + IMG_vSetColor(BmpDec.aPalette[bIndex][0], BmpDec.aPalette[bIndex][1], BmpDec.aPalette[bIndex][2]); + IMG_vPutPixel(wX, BmpDec.lHeight - wY - 1); + } + for(wX = 0; wX < bPadding; wX++) + { + BYTE bValue; + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + } + } + } + FreeMemory((void *)linebuff); + return 0; +} +/******************************************************************************* +Function: BYTE BMP_bDecode(IMG_FILE *pFile) + +Precondition: None + +Overview: This function decodes and puts a section of an image into a memory array + +Input: Image file + +Output: Error code - '0' means no error +*******************************************************************************/ +BYTE BMP_bDecode_memory(int x, int y, int xlen, int ylen, int fnbr, char *p) +{ + BMPDECODER BmpDec; + WORD wX, wY; + BYTE bPadding; + unsigned int nbr; + BDEC_vResetData(&BmpDec); + BDEC_bReadHeader(&BmpDec, fnbr); + if(BmpDec.blBmMarkerFlag == 0 || BmpDec.bHeaderType < 40 || (BmpDec.blCompressionType != 0 && BmpDec.blCompressionType != 3)) + { + return 100; + } + IMG_wImageWidth = (WORD)BmpDec.lWidth; + IMG_wImageHeight = (WORD)BmpDec.lHeight; + IMG_vSetboundaries(); + char *linebuff=GetMemory(IMG_wImageWidth*3); //get a line buffer + +// IMG_FSEEK(pFile, BmpDec.lImageOffset, 0); + if(BmpDec.bBitsPerPixel == 24) /* True color Image */ + { + int pp; + bPadding = (4 - ((BmpDec.lWidth*3) % 4))%4; + for(wY = 0; wY < BmpDec.lHeight; wY++) + { + routinechecks(); + IMG_vLoopCallback(); + IMG_vCheckAndAbort(); + FSerror = IMG_FREAD(IMG_FILE, linebuff, BmpDec.lWidth*3, &nbr); /* B */ + /* if((void *)DrawBuffer != (void *)DisplayNotSet) { + PInt(linebuff[0]);PIntComma(linebuff[1]);PIntComma(linebuff[2]);PRet(); + DrawBuffer(x, BmpDec.lHeight - wY - 1 + y, x+BmpDec.lWidth-1, BmpDec.lHeight - wY - 1 + y, linebuff); + } else */{ //must be a loadable driver so no helper function available + pp=0; + for(wX = 0; wX < BmpDec.lWidth; wX++) + { + colour.rgbbytes[0]=linebuff[pp++]; + colour.rgbbytes[1]=linebuff[pp++]; + colour.rgbbytes[2]=linebuff[pp++]; + int px=wX-x; + int py=BmpDec.lHeight - wY - 1 - y; + if(px=0 && py=0){ + char *q= p+(py*xlen+px)*3; + *q++=colour.rgbbytes[0]; + *q++=colour.rgbbytes[1]; + *q++=colour.rgbbytes[2]; + } + } + } + for(wX = 0; wX < bPadding; wX++) + { + BYTE bValue; + FSerror = IMG_FREAD(IMG_FILE, &bValue, 1, &nbr); + } + } + } + else error("Only 24-bit colour images supported"); + FreeMemory((void *)linebuff); + return 0; +} +/* @endcond */ + diff --git a/Code/pico_multi_booter/picomite/CFunction.c b/Code/pico_multi_booter/picomite/CFunction.c new file mode 100644 index 0000000..ce361e0 --- /dev/null +++ b/Code/pico_multi_booter/picomite/CFunction.c @@ -0,0 +1,170 @@ +/* + * CFunctions.c + * + * Created on: 3 Jul 2020 + * Author: peter + */ +#include "MMBasic_Includes.h" +#include "Hardware_Includes.h" +//Vector to CFunction static RAM + +//Vector to CFunction routine called every mSec +unsigned int CFuncmSec = (unsigned int)NULL; +extern volatile uint64_t uSecTimer; +extern volatile uint64_t FastTimer; +//extern TIM_HandleTypeDef htim2; +extern uint32_t ticks_per_microsecond; +void CallExecuteProgram(char *p); +void CallCFuncmSec(void); +extern const void * const CallTable[]; +extern void routinechecksExternal(void); +//Vector to CFunction routine called every command (ie, from the BASIC interrupt checker) +unsigned int CFuncInt1 = (unsigned int)NULL; +//Vector to CFunction routine called by the interrupt 2 handler +unsigned int CFuncInt2 = (unsigned int)NULL; +unsigned int CFuncInt3 = (unsigned int)NULL; +unsigned int CFuncInt4 = (unsigned int)NULL; +unsigned int CFuncAudio = (unsigned int)NULL; +//static uint64_t timer(void){ return time_us_64();} +//static int64_t PinReadFunc(int a){return gpio_get(PinDef[a].GPno);} + + +// used by CallCFunction() below to find a CFunction or CSub in program flash or the library +unsigned int *FindCFunction(unsigned int *p, unsigned char *CmdPtr, unsigned char *offset) { + while(*p != 0xffffffff) { + //if(*p++ == (unsigned int)(CmdPtr-ProgMemory)) return p; + if(*p++ == (unsigned int)(CmdPtr-offset)) return p; + p += (*p + 4) / sizeof(unsigned int); + } + return p; +} + +long long int MIPS16 CallCFunction(unsigned char *CmdPtr, unsigned char *ArgList, unsigned char *DefP, unsigned char *CallersLinePtr) { + void *arg[10] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + int typ[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + long long int ret, i64[10]; + MMFLOAT ff[10]; + unsigned char *pp; + int i,type; + uint32_t ii; + unsigned int *p=(void * const )CallTable; + MMFLOAT ftmp; +// if((uint32_t)p > 0x10000000)error("Internal error"); + // find the C code in flash + if(*ArgList == '(') ArgList++; // and step over it + p = FindCFunction((unsigned int *)CFunctionFlash, CmdPtr,ProgMemory); // search through the program flash looking for a match to the function being called + if(*p == 0xffffffff && CFunctionLibrary != NULL) + p = FindCFunction((unsigned int *)CFunctionLibrary, CmdPtr,LibMemory);// if unsuccessful search the library area + if(*p == 0xffffffff) error("Internal fault 5(sorry)"); + + // next, get the argument types (if specified) + { // first copy the type list to a buffer and trim the following closing bracket (if there) + char buf[MAXSTRLEN]; + unsigned char *p = (unsigned char *)buf; + if(*DefP == '(') DefP++; + while(*DefP && *DefP != ')' && *DefP != '\'') *p++ = *DefP++; + *p = 0; + p = (unsigned char *)buf; + skipspace(p); + CheckIfTypeSpecified(p, &i, true); + if(i != DefaultType) { + // if there is a type list get each entry + getargs(&p, 19, (unsigned char *)","); + for(i = 0; i < argc; i+=2) { // get each definition + CheckIfTypeSpecified(argv[i], &typ[i/2], false); + typ[i/2] &= ~T_IMPLIED; + } + } + } + + // we have found the CFunction or CSub and the types on its command line + CurrentLinePtr = CallersLinePtr; // report errors at the caller + if(*ArgList != ')') { + getargs(&ArgList, 19, (unsigned char *)","); // expand the command line of the caller + for(i = 0; i < argc; i += 2) { + // if this is a straight variable we want to pass a pointer to its value in RAM + if(isnamestart((uint8_t)*argv[i]) && (*skipvar(argv[i], false) == 0 || *skipvar(argv[i], false) == ')') && !(FindSubFun(argv[i], 1) >= 0 && strchr((const char *)argv[i], '(') != NULL)) { + arg[i/2] = findvar(argv[i], V_FIND | V_EMPTY_OK /* | V_NOFIND_ERR */ ); // if the argument + if(typ[i/2] != 0 && !(TypeMask(g_vartbl[g_VarIndex].type) & typ[i/2])) error("Incompatible type"); + } else { + // else it must be an expression of some sort + // get the value based on the type specified in the definition + switch(typ[i/2]) { + case T_INT: i64[i/2] = getinteger(argv[i]); + arg[i/2] = &i64[i/2]; + break; + case T_NBR: ftmp = getnumber(argv[i]); + ff[i/2] = ftmp; + arg[i/2] = &ff[i/2]; + break; + case T_STR: arg[i/2] = GetTempMemory(STRINGSIZE); + Mstrcpy(arg[i/2], getstring(argv[i])); + break; + default: // the type has not been specified (old style CFunction) + type = T_NOTYPE; + evaluate(argv[i], &ftmp, &i64[i/2], &pp, &type, false); + ff[i/2] = ftmp; + if(type & T_NBR) { + arg[i/2] = &ff[i/2]; + } else if(type & T_INT) + arg[i/2] = &i64[i/2]; + else { + arg[i/2] = GetTempMemory(STRINGSIZE); + Mstrcpy(arg[i/2], pp); + } + break; + } + } + } + } + p++; // step over the size word + + // run the function in flash + ii = *p++; + p = (unsigned int *)((unsigned int) p | 0x1); + ret = ((long long int (*)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *)) (p + ii)) (arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], arg[8], arg[9]); // run the CFunction + + return ret; +} +// If the CFuncmSec vector is set then call the CFunction +void CallCFuncmSec(void){ + typedef void func(void); + func* f=(func*)(void *)CFuncmSec; + f(); +} + +// save the interpreter state if re entering it +void CallExecuteProgram(char *p) { + unsigned char *nextstmtSaved = nextstmt; + g_LocalIndex++; + ExecuteProgram((unsigned char *)p); + nextstmt = nextstmtSaved; + g_LocalIndex--; + g_TempMemoryIsChanged = true; // signal that temporary memory should be checked +} + +// If the CFuncmInt1 vector is set then call the CFunction +void CallCFuncInt1(void){ + typedef void func(void); + func* f=(func*)(void *)CFuncInt1; + f(); +} + +// If the CFuncmInt2 vector is set then call the CFunction +void CallCFuncInt2(void){ + typedef void func(void); + func* f=(func*)(void *)CFuncInt2; + f(); +} +void CallCFuncInt3(void){ + typedef void func(void); + func* f=(func*)(void *)CFuncInt3; + f(); +} +void CallCFuncInt4(void){ + typedef void func(void); + func* f=(func*)(void *)CFuncInt4; + f(); +} + + diff --git a/Code/pico_multi_booter/picomite/CMakeLists.txt b/Code/pico_multi_booter/picomite/CMakeLists.txt new file mode 100644 index 0000000..59c5e41 --- /dev/null +++ b/Code/pico_multi_booter/picomite/CMakeLists.txt @@ -0,0 +1,174 @@ + + +set(APP_NAME picomite) +set(PICOCALC true) +#set(SDBOOT true) + +# Compile for PICO 1 Board +set(COMPILE PICO) + +set(PICO_PLATFORM rp2040) +set(PICO_BOARD pico) + + +add_executable(${APP_NAME} + PicoMite.c + Memory.c + regex.c + MMBasic.c + Operators.c + Custom.c + Functions.c + Commands.c + FileIO.c + ff.c + ffsystem.c + ffunicode.c + mmc_stm32.c + Draw.c + Editor.c + XModem.c + MM_Misc.c + External.c + MATHS.c + Onewire.c + I2C.c + SPI.c + Serial.c + SPI-LCD.c + BmpDecoder.c + GPS.c + Audio.c + CFunction.c + picojpeg.c + lfs.c + lfs_util.c + hxcmod.c + VS1053.c + aes.c +) + +target_sources(${APP_NAME} PRIVATE + SSD1963.c + Touch.c + GUI.c) + +target_sources(${APP_NAME} PRIVATE + Keyboard.c + mouse.c + ) + + + +if(PICOCALC STREQUAL "true") + target_sources(${APP_NAME} PRIVATE + picocalc/i2ckbd.c + ) +endif() + +set_source_files_properties(mmc_stm32.c PROPERTIES COMPILE_FLAGS -O2) +set_source_files_properties(ff.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(GUI.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(BmpDecoder.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(GPS.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(I2C.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(lfs.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(picojpeg.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(regex.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(hxcmod.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(MATHS.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(Editor.c PROPERTIES COMPILE_FLAGS -Os) +set_source_files_properties(aes.c PROPERTIES COMPILE_FLAGS -Os) +pico_generate_pio_header(${APP_NAME} ${CMAKE_CURRENT_LIST_DIR}/PicoMiteI2S.pio) + +pico_define_boot_stage2(slower_boot2 ${PICO_DEFAULT_BOOT_STAGE2_FILE}) +target_compile_definitions(slower_boot2 PRIVATE PICO_FLASH_SPI_CLKDIV=4) +pico_set_boot_stage2(${APP_NAME} slower_boot2) + +pico_enable_stdio_usb(${APP_NAME} 1) +pico_enable_stdio_uart(${APP_NAME} 0) + +pico_add_extra_outputs(${APP_NAME}) +pico_add_uf2_output(${APP_NAME}) + +target_link_options(${APP_NAME} PRIVATE -Wl,--print-memory-usage) +target_link_options(${APP_NAME} PRIVATE -Wl,-z,max-page-size=4096) + +pico_set_linker_script(${APP_NAME} ${CMAKE_BINARY_DIR}/${APP_NAME}.ld) + + +pico_set_printf_implementation(${APP_NAME} compiler) + +target_compile_options(${APP_NAME} PRIVATE -DNDEBUG + -DPICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE=0 + -DPICO_ADC_CLKDIV_ROUND_NEAREST + -DPICO_XOSC_STARTUP_DELAY_MULTIPLIER=64 + -DPICO_CLOCK_AJDUST_PERI_CLOCK_WITH_SYS_CLOCK + -DPICO_XOSC_STARTUP_DELAY_MULTIPLIER=64 + -DPICO_FLASH_SIZE_BYTES=16777216 + -DPICO_CORE1_STACK_SIZE=0x00 + -DPICO_MALLOC_PANIC + -O2 + -Wall) + + +target_compile_options(${APP_NAME} PRIVATE -DPICOMITE + -DPICO_HEAP_SIZE=0x1000 + -DGUICONTROLS + -DPICO_CORE0_STACK_SIZE=0x1000 + ) + +#set the PICOCALC flag +if(PICOCALC STREQUAL "true") + target_compile_options(${APP_NAME} PRIVATE -DPICOCALC + ) +endif() + + +target_link_libraries(${APP_NAME} + pico_stdlib + hardware_flash + hardware_irq + hardware_adc + hardware_pwm + hardware_i2c + hardware_spi + hardware_dma + hardware_exception + hardware_pio + pico_multicore + pico_unique_id +) + + +set(UF2_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/${APP_NAME}.uf2) +set(UF2_DEST ${CMAKE_BINARY_DIR}/${APP_NAME}.uf2) + +add_custom_command( + OUTPUT ${UF2_DEST} + DEPENDS ${UF2_SOURCE} + COMMAND ${CMAKE_COMMAND} -E copy ${UF2_SOURCE} ${UF2_DEST} + COMMENT "Copying ${APP_NAME}.uf2 to top-level build dir" +) + + +add_custom_target(PREPARE_${APP_NAME} + COMMENT "Create Linker Script for '${APP_NAME}'" + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_SOURCE_DIR}/applink.py PREPARE + ${CMAKE_BINARY_DIR} + ${APP_NAME}) + +add_custom_target(BUILT_${APP_NAME} + COMMENT "Record Build Details for '${APP_NAME}'" + DEPENDS ${UF2_DEST} + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_SOURCE_DIR}/applink.py BUILT + ${CMAKE_BINARY_DIR} + ${APP_NAME}) + + + +if(SDBOOT STREQUAL "true") + pico_set_linker_script(${APP_NAME} ${CMAKE_SOURCE_DIR}/memmap_sdcard_app.ld) +endif() diff --git a/Code/pico_multi_booter/picomite/Commands.c b/Code/pico_multi_booter/picomite/Commands.c new file mode 100644 index 0000000..0a8365d --- /dev/null +++ b/Code/pico_multi_booter/picomite/Commands.c @@ -0,0 +1,3667 @@ +/*********************************************************************************************************************** +PicoMite MMBasic + +* @file commands.c + + @author Geoff Graham, Peter Mather +Copyright (c) 2021, All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the distribution. +3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed + on the console at startup (additional copyright messages may be added). +4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed + by the . +5. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software + without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +************************************************************************************************************************/ +/** +* @file Commands.c +* @author Geoff Graham, Peter Mather +* @brief Source for standard MMBasic commands +*/ +/** + * @cond + * The following section will be excluded from the documentation. + */ + + +#include "MMBasic_Includes.h" +#include "Hardware_Includes.h" +#include "hardware/flash.h" +#include "hardware/dma.h" +#include "hardware/structs/watchdog.h" +#ifdef PICOMITE +#include "pico/multicore.h" +#endif +#define overlap (VRes % (FontTable[gui_font >> 4][1] * (gui_font & 0b1111)) ? 0 : 1) +#include +void flist(int, int, int); +//void clearprog(void); +char *KeyInterrupt=NULL; +unsigned char* SaveNextDataLine = NULL; +void execute_one_command(unsigned char *p); +void ListNewLine(int *ListCnt, int all); +int printWrappedText(const char *text, int screenWidth, int listcnt, int all) ; +char MMErrMsg[MAXERRMSG]; // the error message +volatile bool Keycomplete=false; +int keyselect=0; +extern volatile unsigned int ScrewUpTimer; +int SaveNextData = 0; +struct sa_data datastore[MAXRESTORE]; +int restorepointer = 0; +uint64_t g_flag=0; +const uint8_t pinlist[]={ //this is a Basic program to print out the status of all the pins + 1,132,128,95,113,37,0, + 1,153,128,95,113,37,144,48,32,204,32,241,109,97,120,32,103,112,41,0, + 1,168,128,34,71,80,34,130,186,95,113,37,41,44,32,241,112,105,110,110,111,32,34,71,80,34,130,186, + 95,113,37,41,41,44,241,112,105,110,32,241,112,105,110,110,111,32,34,71,80,34,130,186,95,113,37,41,41,41,0, + 1,166,128,0, + 1,147,128,95,113,37,0,0 +}; +const uint8_t i2clist[]={ //this is a Basic program to print out the I2C devices connected to the SYSTEM I2C pins + 1, 132, 128, 105, 110, 116, 101, 103, 101, 114, 32, 95, 97, 100, 0, + 1, 132, 128, 105, 110, 116, 101, 103, 101, 114, 32, 120, 95, 44, 121, 95, 0, + 1, 168, 128, 34, 32, 72, 69, 88, 32, 32, 48, 32, 32, 49, 32, 32, 50, 32, 32, 51, 32, 32, 52, 32, 32, 53, 32, 32, 54, 32, 32, 55, 32, 32, + 56, 32, 32, 57, 32, 32, 65, 32, 32, 66, 32, 32, 67, 32, 32, 68, 32, 32, 69, 32, 32, 70, 34, 0, + 1, 153, 128, 121, 95, 32, 144, 32, 48, 32, 204, 32, 55, 0, + 1, 168, 128, 34, 32, 34, 59, 32, 164, 121, 95, 44, 32, 49, 41, 59, 32, 34, 48, 58, 32, 34, 59, 0, + 1, 153, 128, 120, 95, 32, 144, 32, 48, 32, 204, 32, 49, 53, 0, + 1, 161, 128, 95, 97, 100, 32, 144, 32, 121, 95, 32, 133, 32, 49, 54, 32, 130, 32, 120, 95, 0, + 1, 158, 128, 241, 83, 89, 83, 84, 69, 77, 32, 73, 50, 67, 41, 144, 34, 73, 50, 67, 34, 32, 203, 32, 228, 128, 99, 104, + 101, 99, 107, 32, 95, 97, 100, 32, 199, 32, 229, 128, 32, 99, 104, 101, 99, 107, 32, 95, 97, 100, 0, + 1, 158, 128, 243, 68, 41, 32, 144, 32, 48, 32, 203, 0, + 1, 158, 128, 95, 97, 100, 32, 144, 32, 48, 32, 203, 32, 168, 128, 34, 45, 45, 32, 34, 59, 0, + 1, 158, 128, 95, 97, 100, 32, 143, 32, 48, 32, 203, 32, 168, 128, 164, 95, 97, 100, 44, 32, 50, 41, 59, 34, 32, 34, 59, 0, + 1, 139, 128, 0, 1, 168, 128, 34, 45, 45, 32, 34, 59, 0, 1, 143, 128, 0, 1, 166, 128, 120, 95, 0, + 1, 168, 128, 0, 1, 166, 128, 121, 95, 0, 1, 147, 128, 120, 95, 44, 121, 95, 0, + 1, 147, 128, 95, 97, 100, 0,0 + }; +// stack to keep track of nested FOR/NEXT loops +struct s_forstack g_forstack[MAXFORLOOPS + 1]; +int g_forindex; + + + +// stack to keep track of nested DO/LOOP loops +struct s_dostack g_dostack[MAXDOLOOPS]; +int g_doindex; // counts the number of nested DO/LOOP loops + + +// stack to keep track of GOSUBs, SUBs and FUNCTIONs +unsigned char *gosubstack[MAXGOSUB]; +unsigned char *errorstack[MAXGOSUB]; +int gosubindex; + +unsigned char g_DimUsed = false; // used to catch OPTION BASE after DIM has been used + +int TraceOn; // used to track the state of TRON/TROFF +unsigned char *TraceBuff[TRACE_BUFF_SIZE]; +int TraceBuffIndex; // used for listing the contents of the trace buffer +int OptionErrorSkip; // how to handle an error +int MMerrno; // the error number +unsigned char cmdlinebuff[STRINGSIZE]; +const unsigned int CaseOption = 0xffffffff; // used to store the case of the listed output + +static inline CommandToken commandtbl_decode(const unsigned char *p){ + return ((CommandToken)(p[0] & 0x7f)) | ((CommandToken)(p[1] & 0x7f)<<7); +} + +void __not_in_flash_func(cmd_null)(void) { + // do nothing (this is just a placeholder for commands that have no action) +} +/** @endcond */ +/** + * This command increments an integer or a float or concatenates two strings + * @param a the integer, float or string to be changed + * @param b OPTIONAL for integers and floats - defaults to 1. Otherwise the amount to increment the number or the string to concatenate + */ +#ifdef rp2350 +void MIPS16 __not_in_flash_func(cmd_inc)(void){ +#else +#ifdef PICOMITEVGA +void MIPS16 cmd_inc(void){ +#else +void MIPS16 __not_in_flash_func(cmd_inc)(void){ +#endif +#endif + unsigned char *p, *q; + int vtype; + getargs(&cmdline,3,(unsigned char *)","); + if(argc==1){ + p = findvar(argv[0], V_FIND); + if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); + vtype = TypeMask(g_vartbl[g_VarIndex].type); + if(vtype & T_STR) error("Invalid variable"); // sanity check + if(vtype & T_NBR) + (*(MMFLOAT *)p) = (*(MMFLOAT *)p) + 1.0; + else if(vtype & T_INT)*(long long int *)p = *(long long int *)p + 1; + else error("Syntax"); + } else { + p = findvar(argv[0], V_FIND); + if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); + vtype = TypeMask(g_vartbl[g_VarIndex].type); + if(vtype & T_STR){ + int size=g_vartbl[g_VarIndex].size; + q=getstring(argv[2]); + if(*p + *q > size) error("String too long"); + Mstrcat(p, q); + } else if(vtype & T_NBR){ + (*(MMFLOAT *)p) = (*(MMFLOAT *)p)+getnumber(argv[2]); + } else if(vtype & T_INT){ + *(long long int *)p = *(long long int *)p+getinteger(argv[2]); + } else error("syntax"); + } +} +// the PRINT command +void cmd_print(void) { + unsigned char *s, *p; + unsigned char *ss; + MMFLOAT f; + long long int i64; + int i, t, fnbr; + int docrlf; // this is used to suppress the cr/lf if needed + + getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)";,"); // this is a macro and must be the first executable stmt + +// s = 0; *s = 56; // for testing the exception handler + + docrlf = true; + + if(argc > 0 && *argv[0] == '#') { // check if the first arg is a file number + argv[0]++; + if((*argv[0] == 'G') || (*argv[0] == 'g')){ + argv[0]++; + if(!((*argv[0] == 'P') || (*argv[0] == 'p')))error("Syntax"); + argv[0]++; + if(!((*argv[0] == 'S') || (*argv[0] == 's')))error("Syntax"); + if(!GPSchannel) error("GPS not activated"); + if(argc!=3) error("Only a single string parameter allowed"); + p = argv[2]; + t = T_NOTYPE; + p = evaluate(p, &f, &i64, &s, &t, true); // get the value and type of the argument + ss=(unsigned char *)s; + if(!(t & T_STR)) error("Only a single string parameter allowed"); + int i,xsum=0; + if(ss[1]!='$' || ss[ss[0]]!='*')error("GPS command must start with dollar and end with star"); + for(i=1;i<=ss[0];i++){ + SerialPutchar(GPSchannel, s[i]); + if(s[i]=='$')xsum=0; + if(s[i]!='*')xsum ^=s[i]; + } + i=xsum/16; + i=i+'0'; + if(i>'9')i=i-'0'+'A'; + SerialPutchar(GPSchannel, i); + i=xsum % 16; + i=i+'0'; + if(i>'9')i=i-'0'+'A'; + SerialPutchar(GPSchannel, i); + SerialPutchar(GPSchannel, 13); + SerialPutchar(GPSchannel, 10); + return; + } else { + fnbr = getinteger(argv[0]); // get the number + i = 1; + if(argc >= 2 && *argv[1] == ',') i = 2; // and set the next argument to be looked at + } + } else { + fnbr = 0; // no file number so default to the standard output + i = 0; + } + + for(; i < argc; i++) { // step through the arguments + if(*argv[i] == ',') { + MMfputc('\t', fnbr); // print a tab for a comma + docrlf = false; // a trailing comma should suppress CR/LF + } + else if(*argv[i] == ';') { + docrlf = false; // other than suppress cr/lf do nothing for a semicolon + } + else { // we have a normal expression + p = argv[i]; + while(*p) { + t = T_NOTYPE; + p = evaluate(p, &f, &i64, &s, &t, true); // get the value and type of the argument + if(t & T_NBR) { + *inpbuf = ' '; // preload a space + FloatToStr((char *)inpbuf + ((f >= 0) ? 1:0), f, 0, STR_AUTO_PRECISION, (unsigned char)' ');// if positive output a space instead of the sign + MMfputs((unsigned char *)CtoM(inpbuf), fnbr); // convert to a MMBasic string and output + } else if(t & T_INT) { + *inpbuf = ' '; // preload a space + IntToStr((char *)inpbuf + ((i64 >= 0) ? 1:0), i64, 10); // if positive output a space instead of the sign + MMfputs((unsigned char *)CtoM(inpbuf), fnbr); // convert to a MMBasic string and output + } else if(t & T_STR) { + MMfputs((unsigned char *)s, fnbr); // print if a string (s is a MMBasic string) + } else error("Attempt to print reserved word"); + } + docrlf = true; + } + } + if(docrlf) MMfputs((unsigned char *)"\2\r\n", fnbr); // print the terminating cr/lf unless it has been suppressed + if(PrintPixelMode!=0)SSPrintString("\033[m"); + PrintPixelMode=0; +} +void cmd_arrayset(void){ + array_set(cmdline); +} +void array_set(unsigned char *tp){ + MMFLOAT f; + long long int i64; + unsigned char *s; + #ifdef rp2350 + int dims[MAXDIM]={0}; + #else + short dims[MAXDIM]={0}; + #endif + int i,t,copy,card1=1; + unsigned char size=0; + MMFLOAT *a1float=NULL; + int64_t *a1int=NULL; + unsigned char *a1str=NULL; + getargs(&tp, 3,(unsigned char *)","); + if(!(argc == 3)) error("Argument count"); + findvar(argv[2], V_FIND | V_EMPTY_OK | V_NOFIND_ERR); + t=g_vartbl[g_VarIndex].type; + evaluate(argv[0], &f, &i64, &s, &t, false); + if(t & T_STR){ + card1=parsestringarray(argv[2],&a1str,2,0, dims, true, &size); + copy=(int)size+1; + memset(a1str,0,copy*card1); + if(*s){ + for(i=0; i< card1;i++){ + Mstrcpy(&a1str[i*copy],s); + } + } + } else { + card1=parsenumberarray(argv[2],&a1float,&a1int,2,0, dims, true); + if(t & T_STR) error("Syntax"); + + if(a1float!=NULL){ + for(i=0; i< card1;i++)*a1float++ = ((t & T_INT) ? (MMFLOAT)i64 : f); + } else { + for(i=0; i< card1;i++)*a1int++ = ((t & T_INT) ? i64 : FloatToInt64(f)); + } + } +} +void cmd_add(void){ + array_add(cmdline); +} + +void array_add(unsigned char *tp){ + MMFLOAT f; + long long int i64; + unsigned char *s; + #ifdef rp2350 + int dims[MAXDIM]={0}; + #else + short dims[MAXDIM]={0}; + #endif + int i,t,card1=1, card2=1; + MMFLOAT *a1float=NULL,*a2float=NULL, scale; + int64_t *a1int=NULL, *a2int=NULL; + unsigned char *a1str=NULL, *a2str=NULL; + getargs(&tp, 5,(unsigned char *)","); + if(!(argc == 5)) error("Argument count"); + findvar(argv[0], V_FIND | V_EMPTY_OK | V_NOFIND_ERR); + t=g_vartbl[g_VarIndex].type; + if(t & T_STR){ + unsigned char size=0,size2=0; + unsigned char *toadd; + card1=parsestringarray(argv[0], &a1str, 1, 0,dims, false, &size); + evaluate(argv[2], &f, &i64, &s, &t, false); + if(!(t & T_STR)) error("Syntax"); + toadd=getstring(argv[2]); + card2=parsestringarray(argv[4], &a2str, 3, 0,dims, true, &size2); + if(card1 != card2)error("Array size mismatch"); + unsigned char *buff = GetTempMemory(STRINGSIZE); // this will last for the life of the command + int copy=size+1; + int copy2=size2+1; + for(i=0; i< card1;i++){ + unsigned char *sarg1=a1str+i*copy; + unsigned char *sarg2=a2str+i*copy2; + if(*sarg1 + *toadd > size2) error("String too long"); + Mstrcpy(buff, sarg1); + Mstrcat(buff, toadd); + Mstrcpy(sarg2,buff); + } + } else { + card1=parsenumberarray(argv[0], &a1float, &a1int, 1, 0,dims, false); + evaluate(argv[2], &f, &i64, &s, &t, false); + if(t & T_STR) error("Syntax"); + scale=getnumber(argv[2]); + card2=parsenumberarray(argv[4], &a2float, &a2int, 3, 0,dims, true); + if(card1 != card2)error("Array size mismatch"); + if(scale!=0.0){ + if(a2float!=NULL && a1float!=NULL){ + for(i=0; i< card1;i++)*a2float++ = ((t & T_INT) ? (MMFLOAT)i64 : f) + (*a1float++); + } else if(a2float!=NULL && a1float==NULL){ + for(i=0; i< card1;i++)(*a2float++) = ((t & T_INT) ? (MMFLOAT)i64 : f) + ((MMFLOAT)*a1int++); + } else if(a2float==NULL && a1float!=NULL){ + for(i=0; i< card1;i++)(*a2int++) = FloatToInt64(((t & T_INT) ? i64 : FloatToInt64(f)) + (*a1float++)); + } else { + for(i=0; i< card1;i++)(*a2int++) = ((t & T_INT) ? i64 : FloatToInt64(f)) + (*a1int++); + } + } else { + if(a2float!=NULL && a1float!=NULL){ + for(i=0; i< card1;i++)*a2float++ = *a1float++; + } else if(a2float!=NULL && a1float==NULL){ + for(i=0; i< card1;i++)(*a2float++) = ((MMFLOAT)*a1int++); + } else if(a2float==NULL && a1float!=NULL){ + for(i=0; i< card1;i++)(*a2int++) = FloatToInt64(*a1float++); + } else { + for(i=0; i< card1;i++)*a2int++ = *a1int++; + } + } + } +} +void cmd_insert(void){ + array_insert(cmdline); +} +void array_insert(unsigned char *tp){ + int i, j, t, start, increment, dim[MAXDIM], pos[MAXDIM],off[MAXDIM], dimcount=0, target=-1; + int64_t *a1int=NULL,*a2int=NULL; + MMFLOAT *afloat=NULL; + unsigned char *a1str=NULL, *a2str=NULL; + unsigned char size=0,size2=0; +#ifdef rp2350 + int dims[MAXDIM]={0}; +#else + short dims[MAXDIM]={0}; +#endif + getargs(&tp, 15,(unsigned char *)","); + if(argc<7)error("Argument count"); + findvar(argv[0], V_FIND | V_EMPTY_OK | V_NOFIND_ERR); + t=g_vartbl[g_VarIndex].type; + if(t & T_STR){ + parsestringarray(argv[0],&a1str,1,0,dims, false,&size); + } else { + parsenumberarray(argv[0],&afloat,&a1int,1,0,dims, false); + if(!a1int)a1int=(int64_t *)afloat; + } + if(dims[1]<=0)error("Argument 1 must be a 2D or more array"); + for(i=0;i0){ + dimcount++; + dim[i]=dims[i]-g_OptionBase; + } else dim[i]=0; + } + if(((argc-1)/2-1)!=dimcount)error("Argument count"); + for(i=0; i=0){ + off[i]=1; + for(j=0; j0){ + dimcount++; + dim[i]=dims[i]-g_OptionBase; + } else dim[i]=0; + } + if(((argc-1)/2-1)!=dimcount)error("Argument count"); + for(i=0; i=0){ + off[i]=1; + for(j=0; j size) error("String too long"); + Mstrcpy(p2, s); + } + else if(g_vartbl[g_VarIndex].type & T_NBR) { + t = T_NBR; + p1 = evaluate(p1, &f, &i64, &s, &t, false); + if(t & T_NBR) + (*(MMFLOAT *)p2) = f; + else + (*(MMFLOAT *)p2) = (MMFLOAT)i64; + } else { + t = T_INT; + p1 = evaluate(p1, &f, &i64, &s, &t, false); + if(t & T_INT) + (*(long long int *)p2) = i64; + else + (*(long long int *)p2) = FloatToInt64(f); + } + checkend(p1); +} +/** + * @cond + * The following section will be excluded from the documentation. + */ +int MIPS16 as_strcmpi (const char *s1, const char *s2) +{ + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + + if (p1 == p2) + return 0; + + do + { + c1 = tolower (*p1++); + c2 = tolower (*p2++); + if (c1 == '\0') + break; + } + while (c1 == c2); + + return c1 - c2; +} +void MIPS16 sortStrings(char **arr, int n) +{ + char temp[STRINGSIZE]; + int i,j; + // Sorting strings using bubble sort + for (j=0; j 0) + { + strcpy(temp, arr[j]); + strcpy(arr[j], arr[i]); + strcpy(arr[i], temp); + } + } + } +} +void MIPS16 ListFile(char *pp, int all) { + char buff[STRINGSIZE]; + int fnbr; + int i,ListCnt = CurrentY/(FontTable[gui_font >> 4][1] * (gui_font & 0b1111)) + 2; + fnbr = FindFreeFileNbr(); + if(!BasicFileOpen(pp, fnbr, FA_READ)) return; + while(!FileEOF(fnbr)) { // while waiting for the end of file + memset(buff,0,256); + MMgetline(fnbr, (char *)buff); // get the input line + for(i=0;i= Option.Height-overlap) { + #ifdef USBKEYBOARD + clearrepeat(); + #endif + MMPrintString("PRESS ANY KEY ..."); + MMgetchar(); + MMPrintString("\r \r"); + if(Option.DISPLAY_CONSOLE){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;} + *ListCnt = 2; + } + Option.NoScroll=noscroll; +} + + +void MIPS16 ListProgram(unsigned char *p, int all) { + char b[STRINGSIZE]; + char *pp; + int ListCnt = CurrentY/(FontTable[gui_font >> 4][1] * (gui_font & 0b1111)) + 2; + while(!(*p == 0 || *p == 0xff)) { // normally a LIST ends at the break so this is a safety precaution + if(*p == T_NEWLINE) { + p = llist((unsigned char *)b, p); // otherwise expand the line + if(!(b[0]=='\'' && b[1]=='#')){ + pp = b; + if(Option.continuation){ + format_string(pp,Option.Width); + while(*pp) { + if(*pp=='\n'){ + ListNewLine(&ListCnt, all); + pp++; + continue; + } + MMputchar(*pp++,0); + } + } else { + while(*pp) { + if(MMCharPos > Option.Width) ListNewLine(&ListCnt, all); + MMputchar(*pp++,0); + } + } + fflush(stdout); + ListNewLine(&ListCnt, all); + if(p[0] == 0 && p[1] == 0) break; // end of the listing ? + } + } + } +} + + +void MIPS16 do_run(unsigned char *cmdline, bool CMM2mode) { + // RUN [ filename$ ] [, cmd_args$ ] + unsigned char *filename = (unsigned char *)"", *cmd_args = (unsigned char *)""; + unsigned char *cmdbuf=GetMemory(256); + memcpy(cmdbuf,cmdline,STRINGSIZE); + getargs(&cmdbuf, 3, (unsigned char *)","); + switch (argc) { + case 0: + break; + case 1: + filename = getCstring(argv[0]); + break; + case 2: + cmd_args = getCstring(argv[1]); + break; + default: + filename = getCstring(argv[0]); + if(*argv[2])cmd_args = getCstring(argv[2]); + break; + } + + // The memory allocated by getCstring() is not preserved across + // a call to FileLoadProgram() so we need to cache 'filename' and + // 'cmd_args' on the stack. + unsigned char buf[MAXSTRLEN + 1]; + if (snprintf((char *)buf, MAXSTRLEN + 1, "\"%s\",%s", filename, cmd_args) > MAXSTRLEN) { + error("RUN command line too long"); + } + unsigned char *pcmd_args = buf + strlen((char *)filename) + 3; // *** THW 16/4/23 + +#ifdef rp2350 + if(CMM2mode){ + if (*filename && !FileLoadCMM2Program((char *)buf,false)) return; + } else { +#endif + if (*filename && !FileLoadProgram(buf, false)) return; +#ifdef rp2350 + } +#endif + ClearRuntime(true); + PrepareProgram(true); + if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;} + // Create a global constant MM.CMDLINE$ containing 'cmd_args'. +// void *ptr = findvar((unsigned char *)"MM.CMDLINE$", V_FIND | V_DIM_VAR | T_CONST); + CtoM(pcmd_args); +// memcpy(cmdlinebuff, pcmd_args, *pcmd_args + 1); // *** THW 16/4/23 + Mstrcpy(cmdlinebuff, pcmd_args); + IgnorePIN = false; + if(Option.LIBRARY_FLASH_SIZE == MAX_PROG_SIZE) ExecuteProgram(LibMemory ); // run anything that might be in the library + if(*ProgMemory != T_NEWLINE) return; // no program to run +#ifdef PICOMITEWEB + cleanserver(); +#endif +#ifndef USBKEYBOARD + if(mouse0==false && Option.MOUSE_CLOCK)initMouse0(0); //see if there is a mouse to initialise +#endif + nextstmt = ProgMemory; +} +/** @endcond */ +void MIPS16 cmd_list(void) { + unsigned char *p; + int i,j,k,m,step; + if((p = checkstring(cmdline, (unsigned char *)"ALL"))) { + if(!(*p == 0 || *p == '\'')) { + if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;} + getargs(&p,1,(unsigned char *)","); + char *buff=GetTempMemory(STRINGSIZE); + strcpy(buff,(char *)getCstring(argv[0])); + if(strchr(buff, '.') == NULL) strcat(buff, ".bas"); + ListFile(buff, true); + } else { + if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;} + ListProgram(ProgMemory, true); + checkend(p); + } + } else if((p = checkstring(cmdline, (unsigned char *)"VARIABLES"))) { + int count=0; + int64_t *dest=NULL; + char *buff=NULL; + int j=0; + getargs(&p,1,(unsigned char *)","); + if(argc){ + j=(parseintegerarray(argv[0],&dest,1,1,NULL,true)-1)*8; + dest[0] = 0; + } + for(int i=0;i0){ + strcat(out,"("); + for(int k=0;k0){ + char s[20]; + IntToStr(s, (int64_t)g_vartbl[i].dims[k], 10); + strcat(out,s) ; + } + if(k0)strcat(out,","); + } + strcat(out,")"); + } + c[j]= (char *)((int)c + sizeof(char *) * count + j*(MAXVARLEN+30)); + strcpy(c[j],out); + j++; + } + } + sortStrings(c,count); + int ListCnt = 2; + if(dest==NULL){ + for(int i=0;i j)error("Destination array too small"); + else ol+=strlen(c[i])+2; + strcat(buff,c[i]); + strcat(buff,"\r\n"); + } + dest[0]=ol; + } + + } else if((p = checkstring(cmdline, (unsigned char *)"PINS"))) { +#if defined(PICOMITEWEB) && defined(rp2350) + if(!rp2350a)error("Incompatible board, RP2350A only" ); +#endif + CallExecuteProgram((char *)pinlist); + return; + } else if((p = checkstring(cmdline, (unsigned char *)"SYSTEM I2C"))) { + if(I2C0locked || I2C1locked)CallExecuteProgram((char *)i2clist); + else error("System I2c not defined"); + return; + } else if((p = checkstring(cmdline, (unsigned char *)"COMMANDS"))) { + int ListCnt = 2; + step=Option.DISPLAY_CONSOLE ? HRes/gui_font_width/20 : 5; + if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;} + m=0; + int x=0; + char** c=GetTempMemory((CommandTableSize+x)*sizeof(*c)+(CommandTableSize+x)*18); + for(i=0;i=TokenTableSize)strcpy(c[m],overlaid_functions[i-TokenTableSize]); + else if(m==TokenTableSize+MMEND)strcpy(c[m],"=<"); + else if(m==TokenTableSize+MMEND+1)strcpy(c[m],"=>"); + else strcpy(c[m],"MM.Info$("); + m++; + } + sortStrings(c,m); + for(i=1;i +#include + +int printWrappedText(const char *text, int screenWidth, int listcnt, int all) { + int length = strlen(text); + int start = 0; // Start index of the current line + char buff[STRINGSIZE]; + while (start < length) { + int end = start + screenWidth; // Calculate the end index for the current line + if (end >= length) { + // If end is beyond the text length, just print the remaining text + memset(buff,0,STRINGSIZE); + sprintf(buff,"%s", text + start); + MMPrintString(buff); + ListNewLine(&listcnt, all); + break; + } + + // Find the last space within the current screen width + int lastSpace = -1; + for (int i = start; i < end; i++) { + if (text[i] == ' ') { + lastSpace = i; + } + } + + if (lastSpace != -1) { + // If a space is found, break at the space + memset(buff,0,STRINGSIZE); + sprintf(buff, "%.*s", lastSpace - start, text + start); + MMPrintString(buff); + ListNewLine(&listcnt, all); + start = lastSpace + 1; // Skip the space + } else { + // If no space is found, truncate at screen width + memset(buff,0,STRINGSIZE); + sprintf(buff,"%.*s", screenWidth, text + start); + MMPrintString(buff); + ListNewLine(&listcnt, all); + start += screenWidth; + } + } + return listcnt; +} + +void cmd_help(void){ + getargs(&cmdline,1,(unsigned char *)","); + if(!ExistsFile("A:/help.txt"))error("A:/help.txt not found"); + if(!argc){ + MMPrintString("Enter help and the name of the command or function\r\nUse * for multicharacter wildcard or ? for single character wildcard\r\n"); + } else { + int fnbr = FindFreeFileNbr(); + char *buff=GetTempMemory(STRINGSIZE); + BasicFileOpen("A:/help.txt",fnbr, FA_READ); + int ListCnt = CurrentY/(FontTable[gui_font >> 4][1] * (gui_font & 0b1111)) + 2; + char *p=(char *)getCstring(argv[0]); + bool end=false; + while (!FileEOF(fnbr)) { // while waiting for the end of file + memset(buff,0,STRINGSIZE); + char *in=buff; + while(1){ + if(FileEOF(fnbr)){end=true;break;} + char c = FileGetChar(fnbr); + if(c=='\n')break; + if(c=='\r')continue; + *in++=c; + } + if(end)break; + skipspace(p); + if(buff[0]=='~'){ + if(pattern_matching(p,&buff[1],0,0)){ + while(1){ //loop through all lines for the command + memset(buff,0,STRINGSIZE); + char *in=buff; + while(1){ //get this line + if(FileEOF(fnbr)){end=true;break;} + char c = FileGetChar(fnbr); + if(c=='\n')break; + if(c=='\r')continue; + *in++=c; + } + if(end)break; + if(buff[0]=='~'){ //now we need to rewind the file to check this line + ListNewLine(&ListCnt, false); + lfs_file_seek(&lfs, FileTable[fnbr].lfsptr, -(strlen(buff)+2), LFS_SEEK_CUR); + break; + } else { + ListCnt=printWrappedText(buff,Option.Width-1,ListCnt,false); + } + } + } + } + } + FileClose(fnbr); + } +} +void MIPS16 cmd_run(void){ + do_run(cmdline,false); +} + +void MIPS16 cmd_RunCMM2(void){ + do_run(cmdline,true); +} + +void MIPS16 cmd_continue(void) { + if(*cmdline == tokenFOR) { + if(g_forindex == 0) error("No FOR loop is in effect"); + nextstmt = g_forstack[g_forindex - 1].nextptr; + return; + } + if(checkstring(cmdline, (unsigned char *)"DO")) { + if(g_doindex == 0) error("No DO loop is in effect"); + nextstmt = g_dostack[g_doindex - 1].loopptr; + return; + } + // must be a normal CONTINUE + checkend(cmdline); + if(CurrentLinePtr) error("Invalid in a program"); + if(ContinuePoint == NULL) error("Cannot continue"); +// IgnorePIN = false; + nextstmt = ContinuePoint; +} + +void MIPS16 cmd_new(void) { + closeframebuffer('A'); + checkend(cmdline); + ClearProgram(true); + FlashLoad=0; + uSec(250000); + FlashWriteInit(PROGRAM_FLASH); + flash_range_erase(realflashpointer, MAX_PROG_SIZE); + FlashWriteByte(0); FlashWriteByte(0); FlashWriteByte(0); // terminate the program in flash + FlashWriteClose(); +#ifdef PICOMITEVGA + int mode = DISPLAY_TYPE-SCREENMODE1+1; + setmode(mode, true); +#endif + memset(inpbuf,0,STRINGSIZE); + longjmp(mark, 1); // jump back to the input prompt +} + + +void MIPS16 cmd_erase(void) { + int i,j,k, len; + char p[MAXVARLEN + 1], *s, *x; + + getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block + if((argc & 0x01) == 0) error("Argument count"); + for(i = 0; i < argc; i += 2) { + strcpy((char *)p, (char *)argv[i]); + if(*argv[i] & 0x80)error("You can't erase an in-built function"); + while(!isnamechar(p[strlen(p) - 1])) p[strlen(p) - 1] = 0; + makeupper((unsigned char *)p); + for(j = MAXVARS/2; j < MAXVARS; j++) { + s = p; x = (char *)g_vartbl[j].name; len = strlen(p); + while(len > 0 && *s == *x) { // compare the variable to the name that we have + len--; s++; x++; + } + if(!(len == 0 && (*x == 0 || strlen(p) == MAXVARLEN))) continue; + // found the variable + if(((g_vartbl[j].type & T_STR) || g_vartbl[j].dims[0] != 0) && !(g_vartbl[j].type & T_PTR)) { + FreeMemory(g_vartbl[j].val.s); // free any memory (if allocated) + g_vartbl[j].val.s=NULL; + } + k=j+1; + if(k==MAXVARS)k=MAXVARS/2; + if(g_vartbl[k].type){ + g_vartbl[j].name[0]='~'; + g_vartbl[j].type=T_BLOCKED; + } else { + g_vartbl[j].name[0]=0; + g_vartbl[j].type=T_NOTYPE; + } + g_vartbl[j].dims[0] = 0; // and again + g_vartbl[j].level = 0; + g_Globalvarcnt--; + break; + } + if(j == MAXVARS) error("Cannot find $", p); + } +} +void MIPS16 cmd_clear(void) { + checkend(cmdline); + if(g_LocalIndex)error("Invalid in a subroutine"); + ClearVars(0,true); +} + + +void cmd_goto(void) { + if(isnamestart(*cmdline)) + nextstmt = findlabel(cmdline); // must be a label + else + nextstmt = findline(getinteger(cmdline), true); // try for a line number + CurrentLinePtr = nextstmt; +} + + + +#ifdef PICOMITEWEB +#ifdef rp2350 +void MIPS16 __not_in_flash_func(cmd_if)(void) { +#else +void cmd_if(void) { +#endif +#else +#ifndef rp2350 +#ifdef PICOMITEVGA +void cmd_if(void) { +#else +void MIPS16 __not_in_flash_func(cmd_if)(void) { +#endif +#else +void MIPS16 __not_in_flash_func(cmd_if)(void) { +#endif +#endif + int r, i, testgoto, testelseif; + unsigned char ss[3]; // this will be used to split up the argument line + unsigned char *p, *tp; + unsigned char *rp = NULL; + + ss[0] = tokenTHEN; + ss[1] = tokenELSE; + ss[2] = 0; + + testgoto = false; + testelseif = false; + +retest_an_if: + { // start a new block + getargs(&cmdline, 20, ss); // getargs macro must be the first executable stmt in a block + + if(testelseif && argc > 2) error("Unexpected text"); + + // if there is no THEN token retry the test with a GOTO. If that fails flag an error + if(argc < 2 || *argv[1] != ss[0]) { + if(testgoto) error("IF without THEN"); + ss[0] = tokenGOTO; + testgoto = true; + goto retest_an_if; + } + + + // allow for IF statements embedded inside this IF + if (argc >= 3 && commandtbl_decode(argv[2]) == cmdIF) argc = 3; // this is IF xx=yy THEN IF ... so we want to evaluate only the first 3 + if (argc >= 5 && commandtbl_decode(argv[4]) == cmdIF) argc = 5; // this is IF xx=yy THEN cmd ELSE IF ... so we want to evaluate only the first 5 + + if(argc == 4 || (argc == 5 && *argv[3] != ss[1])) error("Syntax"); + + r = (getnumber(argv[0]) != 0); // evaluate the expression controlling the if statement + + if(r) { + // the test returned TRUE + // first check if it is a multiline IF (ie, only 2 args) + if(argc == 2) { + // if multiline do nothing, control will fall through to the next line (which is what we want to execute next) + ; + } + else { + // This is a standard single line IF statement + // Because the test was TRUE we are just interested in the THEN cmd stage. + if(*argv[1] == tokenGOTO) { + cmdline = argv[2]; + cmd_goto(); + return; + } else if(isdigit(*argv[2])) { + nextstmt = findline(getinteger(argv[2]), true); + } else { + if(argc == 5) { + // this is a full IF THEN ELSE and the statement we want to execute is between the THEN & ELSE + // this is handled by a special routine + execute_one_command(argv[2]); + } else { + // easy - there is no ELSE clause so just point the next statement pointer to the byte after the THEN token + for(p = cmdline; *p && *p != ss[0]; p++); // search for the token + nextstmt = p + 1; // and point to the byte after + } + } + } + } else { + // the test returned FALSE so we are just interested in the ELSE stage (if present) + // first check if it is a multiline IF (ie, only 2 args) + if(argc == 2) { + // search for the next ELSE, or ENDIF and pass control to the following line + // if an ELSEIF is found re execute this function to evaluate the condition following the ELSEIF + i = 1; p = nextstmt; + while(1) { + p = GetNextCommand(p, &rp, (unsigned char *)"No matching ENDIF"); + CommandToken tkn=commandtbl_decode(p); + if(tkn == cmdtoken) { + // found a nested IF command, we now need to determine if it is a single or multiline IF + // search for a THEN, then check if only white space follows. If so, it is multiline. + tp = p + sizeof(CommandToken); + while(*tp && *tp != ss[0]) tp++; + if(*tp) tp++; // step over the THEN + skipspace(tp); + if(*tp == 0 || *tp == '\'') // yes, only whitespace follows + i++; // count it as a nested IF + else // no, it is a single line IF + skipelement(p); // skip to the end so that we avoid an ELSE + continue; + } + + if(tkn == cmdELSE && i == 1) { + // found an ELSE at the same level as this IF. Step over it and continue with the statement after it + skipelement(p); + nextstmt = p; + break; + } + + if((tkn == cmdELSEIF || tkn==cmdELSE_IF) && i == 1) { + // we have found an ELSEIF statement at the same level as our IF statement + // setup the environment to make this function evaluate the test following ELSEIF and jump back + // to the start of the function. This is not very clean (it uses the dreaded goto for a start) but it works + p+=sizeof(CommandToken); // step over the token + skipspace(p); + CurrentLinePtr = rp; + if(*p == 0) error("Syntax"); // there must be a test after the elseif + cmdline = p; + skipelement(p); + nextstmt = p; + testgoto = false; + testelseif = true; + goto retest_an_if; + } + + if(tkn == cmdENDIF || tkn==cmdEND_IF) i--; // found an ENDIF so decrement our nested counter + if(i == 0) { + // found our matching ENDIF stmt. Step over it and continue with the statement after it + skipelement(p); + nextstmt = p; + break; + } + } + } + else { + // this must be a single line IF statement + // check if there is an ELSE on the same line + if(argc == 5) { + // there is an ELSE command + if(isdigit(*argv[4])) + // and it is just a number, so get it and find the line + nextstmt = findline(getinteger(argv[4]), true); + else { + // there is a statement after the ELSE clause so just point to it (the byte after the ELSE token) + for(p = cmdline; *p && *p != ss[1]; p++); // search for the token + nextstmt = p + 1; // and point to the byte after + } + } else { + // no ELSE on a single line IF statement, so just continue with the next statement + skipline(cmdline); + nextstmt = cmdline; + } + } + } + } +} + + + +#ifdef PICOMITEWEB +#ifdef rp2350 +void MIPS16 __not_in_flash_func(cmd_else)(void) { +#else +void cmd_else(void) { +#endif +#else +#ifndef rp2350 +#ifdef PICOMITEVGA +void cmd_else(void) { +#else +void MIPS16 __not_in_flash_func(cmd_else)(void) { +#endif +#else +void MIPS16 __not_in_flash_func(cmd_else)(void) { +#endif +#endif + int i; + unsigned char *p, *tp; + + // search for the next ENDIF and pass control to the following line + i = 1; p = nextstmt; + + if(cmdtoken == cmdELSE) checkend(cmdline); + + while(1) { + p = GetNextCommand(p, NULL, (unsigned char *)"No matching ENDIF"); + CommandToken tkn=commandtbl_decode(p); + if(tkn == cmdIF) { + // found a nested IF command, we now need to determine if it is a single or multiline IF + // search for a THEN, then check if only white space follows. If so, it is multiline. + tp = p + sizeof(CommandToken); + while(*tp && *tp != tokenTHEN) tp++; + if(*tp) tp++; // step over the THEN + skipspace(tp); + if(*tp == 0 || *tp == '\'') // yes, only whitespace follows + i++; // count it as a nested IF + } + if(tkn == cmdENDIF || tkn==cmdEND_IF) i--; // found an ENDIF so decrement our nested counter + if(i == 0) break; // found our matching ENDIF stmt + } + // found a matching ENDIF. Step over it and continue with the statement after it + skipelement(p); + nextstmt = p; +} + + + +void do_end(bool ecmd) { +#ifdef PICOMITE + if(mergerunning){ + multicore_fifo_push_blocking(0xFF); + mergerunning=false; + busy_wait_ms(100); + } +#endif +if(Option.SerialConsole)while(ConsoleTxBufHead!=ConsoleTxBufTail)routinechecks(); + fflush(stdout); + if(ecmd){ + getargs(&cmdline,1,(unsigned char *)","); + if(argc==1){ + if(FindSubFun((unsigned char *)"MM.END", 0) >= 0 && checkstring(argv[0],(unsigned char *)"NOEND")==NULL) { + ExecuteProgram((unsigned char *)"MM.END\0"); + if(Option.SerialConsole)while(ConsoleTxBufHead!=ConsoleTxBufTail)routinechecks(); + fflush(stdout); + memset(inpbuf,0,STRINGSIZE); + } else { + unsigned char *cmd_args = (unsigned char *)""; + cmd_args = getCstring(argv[0]); + void *ptr = findvar((unsigned char *)"MM.ENDLINE$", T_STR| V_NOFIND_NULL); + if(ptr==NULL)ptr = findvar((unsigned char *)"MM.ENDLINE$", V_FIND |V_DIM_VAR); + strcpy(ptr, (char *)cmd_args ); // *** THW 16/4/23 + CtoM(ptr); + } + } else if(FindSubFun((unsigned char *)"MM.END", 0) >= 0) { + ExecuteProgram((unsigned char *)"MM.END\0"); + if(Option.SerialConsole)while(ConsoleTxBufHead!=ConsoleTxBufTail)routinechecks(); + fflush(stdout); + memset(inpbuf,0,STRINGSIZE); + } + } + if(!(MMerrno == 16))hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS); + irq_set_enabled(DMA_IRQ_1, false); + dma_hw->abort = ((1u << dma_rx_chan2) | (1u << dma_rx_chan)); + if(dma_channel_is_busy(dma_rx_chan))dma_channel_abort(dma_rx_chan); + if(dma_channel_is_busy(dma_rx_chan2))dma_channel_abort(dma_rx_chan2); +// dma_channel_cleanup(dma_rx_chan); +// dma_channel_cleanup(dma_rx_chan2); + dma_hw->abort = ((1u << dma_tx_chan2) | (1u << dma_tx_chan)); + if(dma_channel_is_busy(dma_tx_chan))dma_channel_abort(dma_tx_chan); + if(dma_channel_is_busy(dma_tx_chan2))dma_channel_abort(dma_tx_chan2); +// dma_channel_cleanup(dma_tx_chan); +// dma_channel_cleanup(dma_tx_chan2); + dma_hw->abort = ((1u << ADC_dma_chan2) | (1u << ADC_dma_chan)); + if(dma_channel_is_busy(ADC_dma_chan))dma_channel_abort(ADC_dma_chan); + if(dma_channel_is_busy(ADC_dma_chan2))dma_channel_abort(ADC_dma_chan2); +// dma_channel_cleanup(ADC_dma_chan); +// dma_channel_cleanup(ADC_dma_chan2); + for(int i=0; i< NBRSETTICKS;i++){ + TickPeriod[i]=0; + TickTimer[i]=0; + TickInt[i]=NULL; + TickActive[i]=0; + } + InterruptUsed=0; + InterruptReturn = NULL ; + memset(inpbuf,0,STRINGSIZE); + CloseAudio(1); + CloseAllFiles(); + ADCDualBuffering=0; + WatchdogSet = false; + WDTimer = 0; + hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS); + _excep_code=0; + dmarunning = false; + multi=false; + WAVInterrupt = NULL; + WAVcomplete = 0; + if(g_myrand)FreeMemory((void *)g_myrand); + g_myrand=NULL; + OptionConsole=3; +#ifdef PICOMITEVGA + int mode = DISPLAY_TYPE-SCREENMODE1+1; + setmode(mode,false); +#endif + SSPrintString("\033[?25h"); //in case application has turned the cursor off + SSPrintString("\033[97;40m"); +#ifdef PICOMITEWEB + close_tcpclient(); +#endif +#ifndef USBKEYBOARD + if(mouse0==false && Option.MOUSE_CLOCK)initMouse0(0); //see if there is a mouse to initialise +#endif +} +void cmd_end(void) { + do_end(true); + longjmp(mark, 1); // jump back to the input prompt +} +extern unsigned int mmap[HEAP_MEMORY_SIZE/ PAGESIZE / PAGESPERWORD]; +extern unsigned int psmap[7*1024*1024/ PAGESIZE / PAGESPERWORD]; +extern struct s_hash g_hashlist[MAXVARS/2]; +extern int g_hashlistpointer; +extern short g_StrTmpIndex; +extern bool g_TempMemoryIsChanged; +extern volatile char *g_StrTmp[MAXTEMPSTRINGS]; // used to track temporary string space on the heap +extern volatile char g_StrTmpLocalIndex[MAXTEMPSTRINGS]; // used to track the g_LocalIndex for each temporary string space on the heap +void SaveContext(void){ + CloseAudio(1); + #if defined(rp2350) && !defined(PICOMITEWEB) + if(PSRAMsize){ + ClearTempMemory(); + uint8_t *p=(uint8_t *)PSRAMbase+PSRAMsize; + memcpy(p, &g_StrTmpIndex, sizeof(g_StrTmpIndex)); + p+=sizeof(g_StrTmpIndex); + memcpy(p, &g_TempMemoryIsChanged, sizeof(g_TempMemoryIsChanged)); + p+=sizeof(g_TempMemoryIsChanged); + memcpy(p, (void *)g_StrTmp, sizeof(g_StrTmp)); + p+=sizeof(g_StrTmp); + memcpy(p, (void *)g_StrTmpLocalIndex, sizeof(g_StrTmpLocalIndex)); + p+=sizeof(g_StrTmpLocalIndex); + memcpy(p, &g_LocalIndex, sizeof(g_LocalIndex)); + p+=sizeof(g_LocalIndex); + memcpy(p, &g_OptionBase, sizeof(g_OptionBase)); + p+=sizeof(g_OptionBase); + memcpy(p, &g_DimUsed, sizeof(g_DimUsed)); + p+=sizeof(g_DimUsed); + memcpy(p, &g_varcnt, sizeof(g_varcnt)); + p+=sizeof(g_varcnt); + memcpy(p, &g_Globalvarcnt, sizeof(g_Globalvarcnt)); + p+=sizeof(g_Globalvarcnt); + memcpy(p, &g_Localvarcnt, sizeof(g_Localvarcnt)); + p+=sizeof(g_Localvarcnt); + memcpy(p, &g_hashlistpointer, sizeof(g_hashlistpointer)); + p+=sizeof(g_hashlistpointer); + memcpy(p, &g_forindex, sizeof(g_forindex)); + p+=sizeof(g_forindex); + memcpy(p, &g_doindex, sizeof(g_doindex)); + p+=sizeof(g_doindex); + memcpy(p, g_forstack, sizeof(struct s_forstack)*MAXFORLOOPS); + p+=sizeof(struct s_forstack)*MAXFORLOOPS; + memcpy(p, g_dostack, sizeof(struct s_dostack)*MAXDOLOOPS); + p+=sizeof(struct s_dostack)*MAXDOLOOPS; + memcpy(p, g_vartbl, sizeof(struct s_vartbl)*MAXVARS); + p+=sizeof(struct s_vartbl)*MAXVARS; + memcpy(p, g_hashlist, sizeof(struct s_hash)*MAXVARS/2); + p+=sizeof(struct s_hash)*MAXVARS/2; + memcpy(p, MMHeap, heap_memory_size+256); + p+=heap_memory_size+256; + memcpy(p, mmap, sizeof(mmap)); + p+=sizeof(mmap); + memcpy(p, psmap, sizeof(psmap)); + p+=sizeof(psmap); + } else { +#endif + lfs_file_t lfs_file; + if(ExistsFile("A:/.vars")){ + lfs_remove(&lfs, "/.vars"); + } + int sizeneeded= sizeof(g_StrTmpIndex)+ sizeof(g_TempMemoryIsChanged)+sizeof(g_StrTmp)+sizeof(g_StrTmpLocalIndex)+sizeof(g_Localvarcnt)+ + sizeof(g_LocalIndex)+sizeof(g_OptionBase)+sizeof(g_DimUsed)+sizeof(g_varcnt)+sizeof(g_Globalvarcnt)+ + sizeof(g_hashlistpointer)+sizeof(g_forindex)+sizeof(g_doindex)+sizeof(struct s_forstack)*MAXFORLOOPS+sizeof(struct s_dostack)*MAXDOLOOPS+ + sizeof(struct s_vartbl)*MAXVARS+sizeof(struct s_hash)*MAXVARS/2+heap_memory_size+256+sizeof(mmap); + if(sizeneeded>=Option.FlashSize-(Option.modbuff ? 1024*Option.modbuffsize : 0)-RoundUpK4(TOP_OF_SYSTEM_FLASH)-lfs_fs_size(&lfs)*4096)error("Not enough free space on A: drive: % needed",sizeneeded); + lfs_file_open(&lfs, &lfs_file, ".vars", LFS_O_RDWR | LFS_O_CREAT);; + int dt=get_fattime(); + ClearTempMemory(); + lfs_setattr(&lfs, ".vars", 'A', &dt, 4); + lfs_file_write(&lfs, &lfs_file, &g_StrTmpIndex, sizeof(g_StrTmpIndex)); + lfs_file_write(&lfs, &lfs_file, &g_TempMemoryIsChanged, sizeof(g_TempMemoryIsChanged)); + lfs_file_write(&lfs, &lfs_file, (void *)g_StrTmp, sizeof(g_StrTmp)); + lfs_file_write(&lfs, &lfs_file, (void *)g_StrTmpLocalIndex, sizeof(g_StrTmpLocalIndex)); + lfs_file_write(&lfs, &lfs_file, &g_LocalIndex, sizeof(g_LocalIndex)); + lfs_file_write(&lfs, &lfs_file, &g_OptionBase, sizeof(g_OptionBase)); + lfs_file_write(&lfs, &lfs_file, &g_DimUsed, sizeof(g_DimUsed)); + lfs_file_write(&lfs, &lfs_file, &g_varcnt, sizeof(g_varcnt)); + lfs_file_write(&lfs, &lfs_file, &g_Globalvarcnt, sizeof(g_Globalvarcnt)); + lfs_file_write(&lfs, &lfs_file, &g_Localvarcnt, sizeof(g_Globalvarcnt)); + lfs_file_write(&lfs, &lfs_file, &g_hashlistpointer, sizeof(g_hashlistpointer)); + lfs_file_write(&lfs, &lfs_file, &g_forindex, sizeof(g_forindex)); + lfs_file_write(&lfs, &lfs_file, &g_doindex, sizeof(g_doindex)); + lfs_file_write(&lfs, &lfs_file, g_forstack, sizeof(struct s_forstack)*MAXFORLOOPS); + lfs_file_write(&lfs, &lfs_file, g_dostack, sizeof(struct s_dostack)*MAXDOLOOPS); + lfs_file_write(&lfs, &lfs_file, g_vartbl, sizeof(struct s_vartbl)*MAXVARS); + lfs_file_write(&lfs, &lfs_file, g_hashlist, sizeof(struct s_hash)*MAXVARS/2); + lfs_file_write(&lfs, &lfs_file, MMHeap, heap_memory_size+256); + lfs_file_write(&lfs, &lfs_file, mmap, sizeof(mmap)); + lfs_file_close(&lfs, &lfs_file); +#if defined(rp2350) && !defined(PICOMITEWEB) + } +#endif +} +void RestoreContext(bool keep){ + CloseAudio(1); + #if defined(rp2350) && !defined(PICOMITEWEB) + if(PSRAMsize){ + uint8_t *p=(uint8_t *)PSRAMbase+PSRAMsize; + memcpy(&g_StrTmpIndex, p, sizeof(g_StrTmpIndex)); + p+=sizeof(g_StrTmpIndex); + memcpy(&g_TempMemoryIsChanged, p, sizeof(g_TempMemoryIsChanged)); + p+=sizeof(g_TempMemoryIsChanged); + memcpy((void *)g_StrTmp, p, sizeof(g_StrTmp)); + p+=sizeof(g_StrTmp); + memcpy((void *)g_StrTmpLocalIndex, p, sizeof(g_StrTmpLocalIndex)); + p+=sizeof(g_StrTmpLocalIndex); + memcpy(&g_LocalIndex, p, sizeof(g_LocalIndex)); + p+=sizeof(g_LocalIndex); + memcpy(&g_OptionBase, p, sizeof(g_OptionBase)); + p+=sizeof(g_OptionBase); + memcpy(&g_DimUsed, p, sizeof(g_DimUsed)); + p+=sizeof(g_DimUsed); + memcpy(&g_varcnt, p, sizeof(g_varcnt)); + p+=sizeof(g_varcnt); + memcpy(&g_Globalvarcnt, p, sizeof(g_Globalvarcnt)); + p+=sizeof(g_Globalvarcnt); + memcpy(&g_Localvarcnt, p, sizeof(g_Localvarcnt)); + p+=sizeof(g_Localvarcnt); + memcpy(&g_hashlistpointer, p, sizeof(g_hashlistpointer)); + p+=sizeof(g_hashlistpointer); + memcpy(&g_forindex, p, sizeof(g_forindex)); + p+=sizeof(g_forindex); + memcpy(&g_doindex, p, sizeof(g_doindex)); + p+=sizeof(g_doindex); + memcpy(g_forstack, p, sizeof(struct s_forstack)*MAXFORLOOPS); + p+=sizeof(struct s_forstack)*MAXFORLOOPS; + memcpy(g_dostack, p, sizeof(struct s_dostack)*MAXDOLOOPS); + p+=sizeof(struct s_dostack)*MAXDOLOOPS; + memcpy(g_vartbl, p, sizeof(struct s_vartbl)*MAXVARS); + p+=sizeof(struct s_vartbl)*MAXVARS; + memcpy(g_hashlist, p, sizeof(struct s_hash)*MAXVARS/2); + p+=sizeof(struct s_hash)*MAXVARS/2; + memcpy(MMHeap, p, heap_memory_size+256); + p+=heap_memory_size+256; + memcpy(mmap, p, sizeof(mmap)); + p+=sizeof(mmap); + memcpy(psmap, p, sizeof(psmap)); + p+=sizeof(psmap); + } else { +#endif + lfs_file_t lfs_file; + if(!ExistsFile("A:/.vars"))error("Internal error"); + lfs_file_open(&lfs, &lfs_file, "/.vars", LFS_O_RDONLY); + lfs_file_read(&lfs, &lfs_file, &g_StrTmpIndex, sizeof(g_StrTmpIndex)); + lfs_file_read(&lfs, &lfs_file, &g_TempMemoryIsChanged, sizeof(g_TempMemoryIsChanged)); + lfs_file_read(&lfs, &lfs_file, (void *)g_StrTmp, sizeof(g_StrTmp)); + lfs_file_read(&lfs, &lfs_file, (void *)g_StrTmpLocalIndex, sizeof(g_StrTmpLocalIndex)); + lfs_file_read(&lfs, &lfs_file, &g_LocalIndex, sizeof(g_LocalIndex)); + lfs_file_read(&lfs, &lfs_file, &g_OptionBase, sizeof(g_OptionBase)); + lfs_file_read(&lfs, &lfs_file, &g_DimUsed, sizeof(g_DimUsed)); + lfs_file_read(&lfs, &lfs_file, &g_varcnt, sizeof(g_varcnt)); + lfs_file_read(&lfs, &lfs_file, &g_Globalvarcnt, sizeof(g_Globalvarcnt)); + lfs_file_read(&lfs, &lfs_file, &g_Localvarcnt, sizeof(g_Globalvarcnt)); + lfs_file_read(&lfs, &lfs_file, &g_hashlistpointer, sizeof(g_hashlistpointer)); + lfs_file_read(&lfs, &lfs_file, &g_forindex, sizeof(g_forindex)); + lfs_file_read(&lfs, &lfs_file, &g_doindex, sizeof(g_doindex)); + lfs_file_read(&lfs, &lfs_file, g_forstack, sizeof(struct s_forstack)*MAXFORLOOPS); + lfs_file_read(&lfs, &lfs_file, g_dostack, sizeof(struct s_dostack)*MAXDOLOOPS); + lfs_file_read(&lfs, &lfs_file, g_vartbl, sizeof(struct s_vartbl)*MAXVARS); + lfs_file_read(&lfs, &lfs_file, g_hashlist, sizeof(struct s_hash)*MAXVARS/2); + lfs_file_read(&lfs, &lfs_file, MMHeap, heap_memory_size+256); + lfs_file_read(&lfs, &lfs_file, mmap, sizeof(mmap)); + lfs_file_close(&lfs, &lfs_file); + if(!keep)lfs_remove(&lfs, "/.vars"); +#if defined(rp2350) && !defined(PICOMITEWEB) + } +#endif +} +extern void chdir(char *p); +void MIPS16 do_chain(unsigned char *cmdline){ + unsigned char *filename = (unsigned char *)"", *cmd_args = (unsigned char *)""; + unsigned char *cmdbuf=GetMemory(256); + memcpy(cmdbuf,cmdline,STRINGSIZE); + getargs(&cmdbuf, 3, (unsigned char *)","); + switch (argc) { + case 0: + break; + case 1: + filename = getCstring(argv[0]); + break; + case 2: + cmd_args = getCstring(argv[1]); + break; + default: + filename = getCstring(argv[0]); + if(*argv[2])cmd_args = getCstring(argv[2]); + break; + } + + // The memory allocated by getCstring() is not preserved across + // a call to FileLoadProgram() so we need to cache 'filename' and + // 'cmd_args' on the stack. + unsigned char buf[MAXSTRLEN + 1]; + if (snprintf((char *)buf, MAXSTRLEN + 1, "\"%s\",%s", filename, cmd_args) > MAXSTRLEN) { + error("RUN command line too long"); + } + FreeMemory(cmdbuf); + unsigned char *pcmd_args = buf + strlen((char *)filename) + 3; // *** THW 16/4/23 + *cmdline=0; + do_end(false); + SaveContext(); + ClearVars(0,false); + InitHeap(false); + if (*filename && !FileLoadProgram(buf, true)) return; + ClearRuntime(false); + PrepareProgram(true); + RestoreContext(false); + if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;} + // Create a global constant MM.CMDLINE$ containing 'cmd_args'. +// void *ptr = findvar((unsigned char *)"MM.CMDLINE$", V_NOFIND_ERR); + CtoM(pcmd_args); +// memcpy(cmdlinebuff, pcmd_args, *pcmd_args + 1); // *** THW 16/4/23 + Mstrcpy(cmdlinebuff, pcmd_args); + IgnorePIN = false; + if(Option.LIBRARY_FLASH_SIZE == MAX_PROG_SIZE) ExecuteProgram(LibMemory ); // run anything that might be in the library + if(*ProgMemory != T_NEWLINE) return; // no program to run +#ifdef PICOMITEWEB + cleanserver(); +#endif +#ifndef USBKEYBOARD + if(mouse0==false && Option.MOUSE_CLOCK)initMouse0(0); //see if there is a mouse to initialise +#endif + nextstmt = ProgMemory; +} +void cmd_chain(void){ + do_chain(cmdline); +} + +void cmd_select(void) { + int i, type; + unsigned char *p, *rp = NULL, *SaveCurrentLinePtr; + void *v; + MMFLOAT f = 0; + long long int i64 = 0; + unsigned char s[STRINGSIZE]; + + // these are the tokens that we will be searching for + // they are cached the first time this command is called + + type = T_NOTYPE; + v = DoExpression(cmdline, &type); // evaluate the select case value + type = TypeMask(type); + if(type & T_NBR) f = *(MMFLOAT *)v; + if(type & T_INT) i64 = *(long long int *)v; + if(type & T_STR) Mstrcpy((unsigned char *)s, (unsigned char *)v); + + // now search through the program looking for a matching CASE statement + // i tracks the nesting level of any nested SELECT CASE commands + SaveCurrentLinePtr = CurrentLinePtr; // save where we are because we will have to fake CurrentLinePtr to get errors reported correctly + i = 1; p = nextstmt; + while(1) { + p = GetNextCommand(p, &rp, (unsigned char *)"No matching END SELECT"); + CommandToken tkn=commandtbl_decode(p); + + if(tkn == cmdSELECT_CASE) i++; // found a nested SELECT CASE command, increase the nested count and carry on searching + // is this a CASE stmt at the same level as this SELECT CASE. + if(tkn == cmdCASE && i == 1) { + int t; + MMFLOAT ft, ftt; + long long int i64t, i64tt; + unsigned char *st, *stt; + + CurrentLinePtr = rp; // and report errors at the line we are on + p++; //step past rest of command token + // loop through the comparison elements on the CASE line. Each element is separated by a comma + do { + p++; + skipspace(p); + t = type; + // check for CASE IS, eg CASE IS > 5 -or- CASE > 5 and process it if it is + // an operator can be >, <>, etc but it can also be a prefix + or - so we must not catch them + if((SaveCurrentLinePtr = checkstring(p, (unsigned char *)"IS")) || ((tokentype(*p) & T_OPER) && !(*p == GetTokenValue((unsigned char *)"+") || *p == GetTokenValue((unsigned char *)"-")))) { + int o; + if(SaveCurrentLinePtr) p += 2; + skipspace(p); + if(tokentype(*p) & T_OPER) + o = *p++ - C_BASETOKEN; // get the operator + else + error("Syntax"); + if(type & T_NBR) ft = f; + if(type & T_INT) i64t = i64; + if(type & T_STR) st = s; + while(o != E_END) p = doexpr(p, &ft, &i64t, &st, &o, &t); // get the right hand side of the expression and evaluate the operator in o + if(!(t & T_INT)) error("Syntax"); // comparisons must always return an integer + if(i64t) { // evaluates to true + skipelement(p); + nextstmt = p; + CurrentLinePtr = SaveCurrentLinePtr; + return; // if we have a match just return to the interpreter and let it execute the code + } else { // evaluates to false + skipspace(p); + continue; + } + } + + // it must be either a single value (eg, "foo") or a range (eg, "foo" TO "zoo") + // evaluate the first value + p = evaluate(p, &ft, &i64t, &st, &t, true); + skipspace(p); + if(*p == tokenTO) { // is there is a TO keyword? + p++; + t = type; + p = evaluate(p, &ftt, &i64tt, &stt, &t, false); // evaluate the right hand side of the TO expression + if(((type & T_NBR) && f >= ft && f <= ftt) || ((type & T_INT) && i64 >= i64t && i64 <= i64tt) || (((type & T_STR) && Mstrcmp(s, st) >= 0) && (Mstrcmp(s, stt) <= 0))) { + skipelement(p); + nextstmt = p; + CurrentLinePtr = SaveCurrentLinePtr; + return; // if we have a match just return to the interpreter and let it execute the code + } else { + skipspace(p); + continue; // otherwise continue searching + } + } + + // if we got to here the element must be just a single match. So make the test + if(((type & T_NBR) && f == ft) || ((type & T_INT) && i64 == i64t) || ((type & T_STR) && Mstrcmp(s, st) == 0)) { + skipelement(p); + nextstmt = p; + CurrentLinePtr = SaveCurrentLinePtr; + return; // if we have a match just return to the interpreter and let it execute the code + } + skipspace(p); + } while(*p == ','); // keep looping through the elements on the CASE line + checkend(p); + CurrentLinePtr = SaveCurrentLinePtr; + } + + // test if we have found a CASE ELSE statement at the same level as this SELECT CASE + // if true it means that we did not find a matching CASE - so execute this code + if(tkn == cmdCASE_ELSE && i == 1) { + p+=sizeof(CommandToken); // step over the token + checkend(p); + skipelement(p); + nextstmt = p; + CurrentLinePtr = SaveCurrentLinePtr; + return; + } + + if(tkn == cmdEND_SELECT) {i--; p++;} // found an END SELECT so decrement our nested counter + + if(i == 0) { + // found our matching END SELECT stmt. Step over it and continue with the statement after it + skipelement(p); + nextstmt = p; + CurrentLinePtr = SaveCurrentLinePtr; + return; + } + } +} + + +// if we have hit a CASE or CASE ELSE we must search for a END SELECT at this level and resume at that point +void cmd_case(void) { + int i; + unsigned char *p; + + // search through the program looking for a END SELECT statement + // i tracks the nesting level of any nested SELECT CASE commands + i = 1; p = nextstmt; + while(1) { + p = GetNextCommand(p, NULL, (unsigned char *)"No matching END SELECT"); + CommandToken tkn=commandtbl_decode(p); + if(tkn == cmdSELECT_CASE) i++; // found a nested SELECT CASE command, we now need to search for its END CASE + if(tkn == cmdEND_SELECT) i--; // found an END SELECT so decrement our nested counter + if(i == 0) { + // found our matching END SELECT stmt. Step over it and continue with the statement after it + skipelement(p); + nextstmt = p; + break; + } + } +} + + +void cmd_input(void) { + unsigned char s[STRINGSIZE]; + unsigned char *p, *sp, *tp; + int i, fnbr; + getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)",;"); // this is a macro and must be the first executable stmt + + // is the first argument a file number specifier? If so, get it + if(argc >= 3 && *argv[0] == '#') { + argv[0]++; + fnbr = getinteger(argv[0]); + i = 2; + } + else { + fnbr = 0; + // is the first argument a prompt? + // if so, print it followed by an optional question mark + if(argc >= 3 && *argv[0] == '"' && (*argv[1] == ',' || *argv[1] == ';')) { + *(argv[0] + strlen((char *)argv[0]) - 1) = 0; + argv[0]++; + MMPrintString((char *)argv[0]); + if(*argv[1] == ';') MMPrintString((char *)"? "); + i = 2; + } else { + MMPrintString((char *)"? "); // no prompt? then just print the question mark + i = 0; + } + } + + if(argc - i < 1) error("Syntax"); // no variable to input to + *inpbuf = 0; // start with an empty buffer + MMgetline(fnbr, (char *)inpbuf); // get the line + p = inpbuf; + + // step through the variables listed for the input statement + // and find the next item on the line and assign it to the variable + for(; i < argc; i++) { + sp = s; // sp is a temp pointer into s[] + if(*argv[i] == ',' || *argv[i] == ';') continue; + skipspace(p); + if(*p != 0) { + if(*p == '"') { // if it is a quoted string + p++; // step over the quote + while(*p && *p != '"') *sp++ = *p++; // and copy everything upto the next quote + while(*p && *p != ',') p++; // then find the next comma + } else { // otherwise it is a normal string of characters + while(*p && *p != ',') *sp++ = *p++; // copy up to the comma + while(sp > s && sp[-1] == ' ') sp--; // and trim trailing whitespace + } + } + *sp = 0; // terminate the string + tp = findvar(argv[i], V_FIND); // get the variable and save its new value + if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); + if(g_vartbl[g_VarIndex].type & T_STR) { + if(strlen((char *)s) > g_vartbl[g_VarIndex].size) error("String too long"); + strcpy((char *)tp, (char *)s); + CtoM(tp); // convert to a MMBasic string + } else + if(g_vartbl[g_VarIndex].type & T_INT) { + *((long long int *)tp) = strtoll((char *)s, (char **)&sp, 10); // convert to an integer + } + else + *((MMFLOAT *)tp) = (MMFLOAT)atof((char *)s); + if(*p == ',') p++; + } +} + + +void MIPS16 cmd_trace(void) { + if(checkstring(cmdline, (unsigned char *)"ON")) + TraceOn = true; + else if(checkstring(cmdline, (unsigned char *)"OFF")) + TraceOn = false; + else if(checkstring(cmdline, (unsigned char *)"LIST")) { + int i; + cmdline += 4; + skipspace(cmdline); + if(*cmdline == 0 || *cmdline ==(unsigned char)'\'') //' + i = TRACE_BUFF_SIZE - 1; + else + i = getint(cmdline, 0, TRACE_BUFF_SIZE - 1); + i = TraceBuffIndex - i; + if(i < 0) i += TRACE_BUFF_SIZE; + while(i != TraceBuffIndex) { + if(TraceBuff[i] >= ProgMemory && TraceBuff[i] <= ProgMemory+MAX_PROG_SIZE){ + inpbuf[0] = '['; + IntToStr((char *)inpbuf + 1, CountLines(TraceBuff[i]), 10); + strcat((char *)inpbuf, "]"); + }else if(TraceBuff[i]){ + strcpy((char *)inpbuf, "[Lib]"); + }else{ + inpbuf[0] = 0; + } + MMPrintString((char *)inpbuf); + if(++i >= TRACE_BUFF_SIZE) i = 0; + } + } + else error("Unknown command"); +} + + + +// FOR command +#ifndef PICOMITE +#ifdef rp2350 +void MIPS16 __not_in_flash_func(cmd_for)(void) { +#else +void cmd_for(void) { +#endif +#else +void MIPS16 __not_in_flash_func(cmd_for)(void) { +#endif + int i, t, vlen, test; + unsigned char ss[4]; // this will be used to split up the argument line + unsigned char *p, *tp, *xp; + void *vptr; + unsigned char *vname, vtype; +// static unsigned char fortoken, nexttoken; + + // cache these tokens for speed +// if(!fortoken) fortoken = GetCommandValue((unsigned char *)"For"); +// if(!nexttoken) nexttoken = GetCommandValue((unsigned char *)"Next"); + + ss[0] = tokenEQUAL; + ss[1] = tokenTO; + ss[2] = tokenSTEP; + ss[3] = 0; + { // start a new block + getargs(&cmdline, 7, ss); // getargs macro must be the first executable stmt in a block + if(argc < 5 || argc == 6 || *argv[1] != ss[0] || *argv[3] != ss[1]) error("FOR with misplaced = or TO"); + if(argc == 6 || (argc == 7 && *argv[5] != ss[2])) error("Syntax"); + + // get the variable name and trim any spaces + vname = argv[0]; + if(*vname && *vname == ' ') vname++; + while(*vname && vname[strlen((char *)vname) - 1] == ' ') vname[strlen((char *)vname) - 1] = 0; + vlen = strlen((char *)vname); + vptr = findvar(argv[0], V_FIND); // create the variable + if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); + vtype = TypeMask(g_vartbl[g_VarIndex].type); + if(vtype & T_STR) error("Invalid variable"); // sanity check + + // check if the FOR variable is already in the stack and remove it if it is + // this is necessary as the program can jump out of the loop without hitting + // the NEXT statement and this will eventually result in a stack overflow + for(i = 0; i < g_forindex ;i++) { + if(g_forstack[i].var == vptr && g_forstack[i].level == g_LocalIndex) { + while(i < g_forindex - 1) { + g_forstack[i].forptr = g_forstack[i+1].forptr; + g_forstack[i].nextptr = g_forstack[i+1].nextptr; + g_forstack[i].var = g_forstack[i+1].var; + g_forstack[i].vartype = g_forstack[i+1].vartype; + g_forstack[i].level = g_forstack[i+1].level; + g_forstack[i].tovalue.i = g_forstack[i+1].tovalue.i; + g_forstack[i].stepvalue.i = g_forstack[i+1].stepvalue.i; + i++; + } + g_forindex--; + break; + } + } + + if(g_forindex == MAXFORLOOPS) error("Too many nested FOR loops"); + + g_forstack[g_forindex].var = vptr; // save the variable index + g_forstack[g_forindex].vartype = vtype; // save the type of the variable + g_forstack[g_forindex].level = g_LocalIndex; // save the level of the variable in terms of sub/funs + g_forindex++; // incase functions use for loops + if(vtype & T_NBR) { + *(MMFLOAT *)vptr = getnumber(argv[2]); // get the starting value for a float and save + g_forstack[g_forindex - 1].tovalue.f = getnumber(argv[4]); // get the to value and save + if(argc == 7) + g_forstack[g_forindex - 1].stepvalue.f = getnumber(argv[6]);// get the step value for a float and save + else + g_forstack[g_forindex - 1].stepvalue.f = 1.0; // default is +1 + } else { + *(long long int *)vptr = getinteger(argv[2]); // get the starting value for an integer and save + g_forstack[g_forindex - 1].tovalue.i = getinteger(argv[4]); // get the to value and save + if(argc == 7) + g_forstack[g_forindex - 1].stepvalue.i = getinteger(argv[6]);// get the step value for an integer and save + else + g_forstack[g_forindex - 1].stepvalue.i = 1; // default is +1 + } + g_forindex--; + + g_forstack[g_forindex].forptr = nextstmt + 1; // return to here when looping + + // now find the matching NEXT command + t = 1; p = nextstmt; + while(1) { + p = GetNextCommand(p, &tp, (unsigned char *)"No matching NEXT"); +// if(*p == fortoken) t++; // count the FOR +// if(*p == nexttoken) { // is it NEXT + CommandToken tkn=commandtbl_decode(p); + + if(tkn == cmdFOR) t++; // count the FOR + + if(tkn == cmdNEXT) { // is it NEXT + xp = p + sizeof(CommandToken); // point to after the NEXT token + while(*xp && mystrncasecmp(xp, vname, vlen)) xp++; // step through looking for our variable + if(*xp && !isnamechar(xp[vlen])) // is it terminated correctly? + t = 0; // yes, found the matching NEXT + else + t--; // no luck, just decrement our stack counter + } + if(t == 0) { // found the matching NEXT + g_forstack[g_forindex].nextptr = p; // pointer to the start of the NEXT command + break; + } + } + + // test the loop value at the start + if(g_forstack[g_forindex].vartype & T_INT) + test = (g_forstack[g_forindex].stepvalue.i >= 0 && *(long long int *)vptr > g_forstack[g_forindex].tovalue.i) || (g_forstack[g_forindex].stepvalue.i < 0 && *(long long int *)vptr < g_forstack[g_forindex].tovalue.i) ; + else + test = (g_forstack[g_forindex].stepvalue.f >= 0 && *(MMFLOAT *)vptr > g_forstack[g_forindex].tovalue.f) || (g_forstack[g_forindex].stepvalue.f < 0 && *(MMFLOAT *)vptr < g_forstack[g_forindex].tovalue.f) ; + + if(test) { + // loop is invalid at the start, so go to the end of the NEXT command + skipelement(p); // find the command after the NEXT command + nextstmt = p; // this is where we will continue + } else { + g_forindex++; // save the loop data and continue on with the command after the FOR statement + } + } +} + + + +#ifndef PICOMITE +#ifdef rp2350 +void MIPS16 __not_in_flash_func(cmd_next)(void) { +#else +void cmd_next(void) { +#endif +#else +void MIPS16 __not_in_flash_func(cmd_next)(void) { +#endif + int i, vindex, test; + void *vtbl[MAXFORLOOPS]; + int vcnt; + unsigned char *p; + getargs(&cmdline, MAXFORLOOPS * 2, (unsigned char *)","); // getargs macro must be the first executable stmt in a block + + vindex = 0; // keep lint happy + + for(vcnt = i = 0; i < argc; i++) { + if(i & 0x01) { + if(*argv[i] != ',') error("Syntax"); + } else + vtbl[vcnt++] = findvar(argv[i], V_FIND | V_NOFIND_ERR); // find the variable and error if not found + } + + loopback: + // first search the for stack for a loop with the same variable specified on the NEXT's line + if(vcnt) { + for(i = g_forindex - 1; i >= 0; i--) + for(vindex = vcnt - 1; vindex >= 0 ; vindex--) + if(g_forstack[i].var == vtbl[vindex]) + goto breakout; + } else { + // if no variables specified search the for stack looking for an entry with the same program position as + // this NEXT statement. This cheats by using the cmdline as an identifier and may not work inside an IF THEN ELSE + for(i = 0; i < g_forindex; i++) { + p = g_forstack[i].nextptr + sizeof(CommandToken); + skipspace(p); + if(p == cmdline) goto breakout; + } + } + + error("Cannot find a matching FOR"); + + breakout: + + // found a match + // apply the STEP value to the variable and test against the TO value + if(g_forstack[i].vartype & T_INT) { + *(long long int *)g_forstack[i].var += g_forstack[i].stepvalue.i; + test = (g_forstack[i].stepvalue.i >= 0 && *(long long int *)g_forstack[i].var > g_forstack[i].tovalue.i) || (g_forstack[i].stepvalue.i < 0 && *(long long int *)g_forstack[i].var < g_forstack[i].tovalue.i) ; + } else { + *(MMFLOAT *)g_forstack[i].var += g_forstack[i].stepvalue.f; + test = (g_forstack[i].stepvalue.f >= 0 && *(MMFLOAT *)g_forstack[i].var > g_forstack[i].tovalue.f) || (g_forstack[i].stepvalue.f < 0 && *(MMFLOAT *)g_forstack[i].var < g_forstack[i].tovalue.f) ; + } + + if(test) { + // the loop has terminated + // remove the entry in the table, then skip forward to the next element and continue on from there + while(i < g_forindex - 1) { + g_forstack[i].forptr = g_forstack[i+1].forptr; + g_forstack[i].nextptr = g_forstack[i+1].nextptr; + g_forstack[i].var = g_forstack[i+1].var; + g_forstack[i].vartype = g_forstack[i+1].vartype; + g_forstack[i].level = g_forstack[i+1].level; + g_forstack[i].tovalue.i = g_forstack[i+1].tovalue.i; + g_forstack[i].stepvalue.i = g_forstack[i+1].stepvalue.i; + i++; + } + g_forindex--; + if(vcnt > 0) { + // remove that entry from our FOR stack + for(; vindex < vcnt - 1; vindex++) vtbl[vindex] = vtbl[vindex + 1]; + vcnt--; + if(vcnt > 0) + goto loopback; + else + return; + } + + } else { + // we have not reached the terminal value yet, so go back and loop again + nextstmt = g_forstack[i].forptr; + } +} + + + + +#ifndef PICOMITE +#ifdef rp2350 +void MIPS16 __not_in_flash_func(cmd_do)(void) { +#else +void cmd_do(void) { +#endif +#else +void MIPS16 __not_in_flash_func(cmd_do)(void) { +#endif + int i; + unsigned char *p, *tp, *evalp; + if(cmdtoken==cmdWHILE)error("Unknown command"); + // if it is a DO loop find the WHILE token and (if found) get a pointer to its expression + while(*cmdline && *cmdline != tokenWHILE) cmdline++; + if(*cmdline == tokenWHILE) { + evalp = ++cmdline; + } + else + evalp = NULL; + // check if this loop is already in the stack and remove it if it is + // this is necessary as the program can jump out of the loop without hitting + // the LOOP or WEND stmt and this will eventually result in a stack overflow + for(i = 0; i < g_doindex ;i++) { + if(g_dostack[i].doptr == nextstmt) { + while(i < g_doindex - 1) { + g_dostack[i].evalptr = g_dostack[i+1].evalptr; + g_dostack[i].loopptr = g_dostack[i+1].loopptr; + g_dostack[i].doptr = g_dostack[i+1].doptr; + g_dostack[i].level = g_dostack[i+1].level; + i++; + } + g_doindex--; + break; + } + } + + // add our pointers to the top of the stack + if(g_doindex == MAXDOLOOPS) error("Too many nested DO or WHILE loops"); + g_dostack[g_doindex].evalptr = evalp; + g_dostack[g_doindex].doptr = nextstmt; + g_dostack[g_doindex].level = g_LocalIndex; + + // now find the matching LOOP command + i = 1; p = nextstmt; + while(1) { + p = GetNextCommand(p, &tp, (unsigned char *)"No matching LOOP"); + CommandToken tkn=commandtbl_decode(p); + if(tkn == cmdtoken) i++; // entered a nested DO or WHILE loop + if(tkn == cmdLOOP) i--; // exited a nested loop + + if(i == 0) { // found our matching LOOP or WEND stmt + g_dostack[g_doindex].loopptr = p; + break; + } + } + + if(g_dostack[g_doindex].evalptr != NULL) { + // if this is a DO WHILE ... LOOP statement + // search the LOOP statement for a WHILE or UNTIL token (p is pointing to the matching LOOP statement) + p+=sizeof(CommandToken); + while(*p && *p < 0x80) p++; + if(*p == tokenWHILE) error("LOOP has a WHILE test"); + if(*p == tokenUNTIL) error("LOOP has an UNTIL test"); + } + + g_doindex++; + + // do the evaluation (if there is something to evaluate) and if false go straight to the command after the LOOP or WEND statement + if(g_dostack[g_doindex - 1].evalptr != NULL && getnumber(g_dostack[g_doindex - 1].evalptr) == 0) { + g_doindex--; // remove the entry in the table + nextstmt = g_dostack[g_doindex].loopptr; // point to the LOOP or WEND statement + skipelement(nextstmt); // skip to the next command + } + +} + + + + +#ifdef PICOMITEWEB +#ifdef rp2350 +void MIPS16 __not_in_flash_func(cmd_loop)(void) { +#else +void cmd_loop(void) { +#endif +#else +void MIPS16 __not_in_flash_func(cmd_loop)(void) { +#endif + unsigned char *p; + int tst = 0; // initialise tst to stop the compiler from complaining + int i; + + // search the do table looking for an entry with the same program position as this LOOP statement + for(i = 0; i < g_doindex ;i++) { + p = g_dostack[i].loopptr + sizeof(CommandToken); + skipspace(p); + if(p == cmdline) { + // found a match + // first check if the DO statement had a WHILE component + // if not find the WHILE statement here and evaluate it + if(g_dostack[i].evalptr == NULL) { // if it was a DO without a WHILE + if(*cmdline >= 0x80) { // if there is something + if(*cmdline == tokenWHILE) + tst = (getnumber(++cmdline) != 0); // evaluate the expression + else if(*cmdline == tokenUNTIL) + tst = (getnumber(++cmdline) == 0); // evaluate the expression + else + error("Syntax"); + } + else { + tst = 1; // and loop forever + checkend(cmdline); // make sure that there is nothing else + } + } + else { // if was DO WHILE + tst = (getnumber(g_dostack[i].evalptr) != 0); // evaluate its expression + checkend(cmdline); // make sure that there is nothing else + } + + // test the expression value and reset the program pointer if we are still looping + // otherwise remove this entry from the do stack + if(tst) + nextstmt = g_dostack[i].doptr; // loop again + else { + // the loop has terminated + // remove the entry in the table, then just let the default nextstmt run and continue on from there + g_doindex = i; + // just let the default nextstmt run + } + return; + } + } + error("LOOP without a matching DO"); +} + + + +void cmd_exitfor(void) { + if(g_forindex == 0) error("No FOR loop is in effect"); + nextstmt = g_forstack[--g_forindex].nextptr; + checkend(cmdline); + skipelement(nextstmt); +} + + + +void cmd_exit(void) { + if(g_doindex == 0) error("No DO loop is in effect"); + nextstmt = g_dostack[--g_doindex].loopptr; + checkend(cmdline); + skipelement(nextstmt); +} + + + +/*void cmd_error(void) { + unsigned char *s; + if(*cmdline && *cmdline != '\'') { + s = getCstring(cmdline); + char *p=GetTempMemory(STRINGSIZE); + strcpy(p,"["); + int ln=CountLines(CurrentLinePtr); + IntToStr(&p[1],ln,10); + SaveCurrentLinePtr=CurrentLinePtr; + CurrentLinePtr = NULL; // suppress printing the line that caused the issue + strcat((char *)p,"] "); + strcat((char *)p,(char *)s); + error(p); + } + else + error(""); +}*/ +void cmd_error(void) { + unsigned char *s; + if(*cmdline && *cmdline != '\'') { + s = getCstring(cmdline); + // CurrentLinePtr = NULL; // suppress printing the line that caused the issue + error((char *) s); + } + else + error(""); +} + + +#ifndef rp2350 + void cmd_randomize(void) { + int i; + getargs(&cmdline,1,(unsigned char *)","); + if(argc==1)i = getinteger(argv[0]); + else i=time_us_32(); + if(i < 0) error("Number out of bounds"); + srand(i); +} +#endif + +// this is the Sub or Fun command +// it simply skips over text until it finds the end of it +void cmd_subfun(void) { + unsigned char *p; + unsigned short returntoken, errtoken; + + if(gosubindex != 0) error("No matching END declaration"); // we have hit a SUB/FUN while in another SUB or FUN + if(cmdtoken == cmdSUB) { + returntoken = cmdENDSUB; + errtoken = cmdENDFUNCTION; + } else { + returntoken = cmdENDFUNCTION; + errtoken = cmdENDSUB; + } + p = nextstmt; + while(1) { + p = GetNextCommand(p, NULL, (unsigned char *)"No matching END declaration"); + CommandToken tkn=commandtbl_decode(p); + if(tkn == cmdSUB || tkn == cmdFUN || tkn == errtoken) error("No matching END declaration"); + if(tkn == returntoken) { // found the next return + skipelement(p); + nextstmt = p; // point to the next command + break; + } + } +} +// this is the Sub or Fun command +// it simply skips over text until it finds the end of it +void cmd_comment(void) { + unsigned char *p; + unsigned short returntoken; + + returntoken = GetCommandValue((unsigned char *)"*/"); +// errtoken = cmdENDSUB; + p = nextstmt; + while(1) { + p = GetNextCommand(p, NULL, (unsigned char *)"No matching END declaration"); + CommandToken tkn=commandtbl_decode(p); + if(tkn == cmdComment) error("No matching END declaration"); + if(tkn == returntoken) { // found the next return + skipelement(p); + nextstmt = p; // point to the next command + break; + } + } +} +void cmd_endcomment(void){ + +} + + +void cmd_gosub(void) { + if(gosubindex >= MAXGOSUB) error("Too many nested GOSUB"); + char *return_to = (char *)nextstmt; + if(isnamestart(*cmdline)) + nextstmt = findlabel(cmdline); + else + nextstmt = findline(getinteger(cmdline), true); + IgnorePIN = false; + + errorstack[gosubindex] = CurrentLinePtr; + gosubstack[gosubindex++] = (unsigned char *)return_to; + g_LocalIndex++; + CurrentLinePtr = nextstmt; +} + +void cmd_mid(void){ + unsigned char *p; + getargs(&cmdline,5,(unsigned char *)","); + findvar(argv[0], V_NOFIND_ERR); + if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); + if(!(g_vartbl[g_VarIndex].type & T_STR)) error("Not a string"); + int size=g_vartbl[g_VarIndex].size; + char *sourcestring=(char *)getstring(argv[0]); + int start=getint(argv[2],1,sourcestring[0]); + int num=0; + if(argc==5)num=getint(argv[4],1,sourcestring[0]); + if(start+num-1>sourcestring[0])error("Selection exceeds length of string"); + while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++; + if(!*cmdline) error("Syntax"); + ++cmdline; + if(!*cmdline) error("Syntax"); + char *value = (char *)getstring(cmdline); + if(num==0)num=value[0]; + p=(unsigned char *)&value[1]; + if(num==value[0]) memcpy(&sourcestring[start],p,num); + else { + int change=value[0]-num; + if(sourcestring[0]+change>size)error("String too long"); + memmove(&sourcestring[start+value[0]],&sourcestring[start+num],sourcestring[0]-(start+num-1)); + sourcestring[0]+=change; + memcpy(&sourcestring[start],p,value[0]); + } +} +void cmd_byte(void){ + getargs(&cmdline,3,(unsigned char *)","); + findvar(argv[0], V_NOFIND_ERR); + if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); + if(!(g_vartbl[g_VarIndex].type & T_STR)) error("Not a string"); + unsigned char *sourcestring=(unsigned char *)getstring(argv[0]); + int start=getint(argv[2],1,sourcestring[0]); + while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++; + if(!*cmdline) error("Syntax"); + ++cmdline; + if(!*cmdline) error("Syntax"); + int value = getint(cmdline,0,255); + sourcestring[start]=value; +} +void cmd_bit(void){ + getargs(&cmdline,3,(unsigned char *)","); + uint64_t *source=(uint64_t *)findvar(argv[0], V_NOFIND_ERR); + if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); + if(!(g_vartbl[g_VarIndex].type & T_INT)) error("Not an integer"); + uint64_t bit=(uint64_t)1<<(uint64_t)getint(argv[2],0,63); + while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++; + if(!*cmdline) error("Syntax"); + ++cmdline; + if(!*cmdline) error("Syntax"); + int value = getint(cmdline,0,1); + if(value)*source|=bit; + else *source&=(~bit); +} +void cmd_flags(void) { + while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++; + if(!*cmdline) error("Syntax"); + g_flag=getinteger(++cmdline); +} + +void cmd_flag(void){ + getargs(&cmdline,1,(unsigned char *)","); + uint64_t bit=(uint64_t)1<<(uint64_t)getint(argv[0],0,63); + while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++; + if(!*cmdline) error("Syntax"); + ++cmdline; + if(!*cmdline) error("Syntax"); + int value = getint(cmdline,0,1); + if(value)g_flag |=bit; + else g_flag &=~bit; +} + +void MIPS16 __not_in_flash_func(cmd_return)(void) { + checkend(cmdline); + if(gosubindex == 0 || gosubstack[gosubindex - 1] == NULL) error("Nothing to return to"); + ClearVars(g_LocalIndex--, true); // delete any local variables + g_TempMemoryIsChanged = true; // signal that temporary memory should be checked + nextstmt = gosubstack[--gosubindex]; // return to the caller + CurrentLinePtr = errorstack[gosubindex]; +} +/*frame +#define c_topleft 218 +#define c_topright 191 +#define c_bottomleft 192 +#define c_bottomright 217 +#define c_horizontal 196 +#define c_vertical 179 +#define c_cross 197 +#define c_tup 193 +#define c_tdown 194 +#define c_tleft 195 +#define c_tright 180 +#define c_d_topleft 201 +#define c_d_topright 187 +#define c_d_bottomleft 200 +#define c_d_bottomright 188 +#define c_d_horizontal 205 +#define c_d_vertical 186 +#define c_d_cross 206 +#define c_d_tup 202 +#define c_d_tdown 203 +#define c_d_tleft 204 +#define c_d_tright 185 + + +extern const int colours[16]; +extern void setterminal(void); +unsigned short *frame=NULL, *outframe=NULL; +bool framecursor=true; +static int framex=0,framey=0; +static inline void framewritechar(int x, int y, uint8_t ascii, uint8_t fcolour, uint8_t attributes){ + if(x>=framex || y>=framey)return; + frame[(y*framex)+x]=ascii | (fcolour<<8) | (attributes<<12); +} +static inline uint16_t framegetchar(int x, int y ){ + return frame[(y*framex)+x]; +} +static void SCursor(int x, int y) { + char s[30]; + ShowCursor(0); + CurrentX=x*gui_font_width; + CurrentY=y*gui_font_height; + sprintf(s,"\033[%d;%dH",y+1,x+1); + SSPrintString(s); + ShowCursor(framecursor); +} +static void SColour(int colour, int fore){ + char s[24]={0}; + int r=colour>>16; + int g=(colour>>8) & 0xFF; + int b=colour & 0xff; + if(fore){ + strcpy(s,"\033[38;2;"); + gui_fcolour=colour; + } else { + strcpy(s,"\033[48;2;"); + gui_bcolour=colour; + } + sprintf(&s[7],"%d;%d;%dm",r,g,b); + SSPrintString(s); +} + +void cmd_frame(void){ + unsigned char *p=NULL; + if((p=checkstring(cmdline,(unsigned char *)"CREATE"))){ + if(frame)error("Frame already exists"); + framex=HRes/gui_font_width; + framey=VRes/gui_font_height; + frame=(uint16_t *)GetMemory(framex*framey*sizeof(uint16_t)); + outframe=(uint16_t *)GetMemory(framex*framey*sizeof(uint16_t)); + for(int i=0;i=9 && *argv[8])fc=getint(argv[8],0,WHITE); + if(argc==11){ + if(checkstring(argv[10],(unsigned char *)"DOUBLE"))dual=true; + } + fc=RGB121(fc); + framewritechar(x,y,dual ? c_d_topleft : c_topleft,fc,0); + framewritechar(x+w,y,dual ? c_d_topright : c_topright,fc,0); + framewritechar(x+w,y+h,dual ? c_d_bottomright : c_bottomright,fc,0); + framewritechar(x,y+h,dual ? c_d_bottomleft : c_bottomleft,fc,0); + for(int i=x+1;i=13 && *argv[12])fc=getint(argv[12],0,WHITE); + if(argc==15){ + if(checkstring(argv[14],(unsigned char *)"DOUBLE"))dual=true; + } + fc=RGB121(fc); + framewritechar(x,y,dual ? c_d_topleft : c_topleft,fc,0); + framewritechar(x+w,y,dual ? c_d_tdown : c_tdown,fc,0); + framewritechar(x+w+w2,y,dual ? c_d_topright : c_topright,fc,0); + framewritechar(x,y+h,dual ? c_d_tleft : c_tleft,fc,0); + framewritechar(x+w,y+h,dual ? c_d_cross : c_cross,fc,0); + framewritechar(x+w+w2,y+h,dual ? c_d_tright : c_tright,fc,0); + framewritechar(x,y+h+h2,dual ? c_d_bottomleft : c_bottomleft,fc,0); + framewritechar(x+w,y+h+h2,dual ? c_d_tup : c_tup,fc,0); + framewritechar(x+w+w2,y+h+h2,dual ? c_d_bottomright : c_bottomright,fc,0); + for(int i=x+1;i=11 && *argv[10])fc=getint(argv[10],0,WHITE); + if(argc==13){ + if(checkstring(argv[12],(unsigned char *)"DOUBLE"))dual=true; + } + fc=RGB121(fc); + framewritechar(x,y,dual ? c_d_topleft : c_d_topleft,fc,0); + framewritechar(x+w,y,dual ? c_d_topright : c_d_topright,fc,0); + framewritechar(x+w,y+h,dual ? c_d_tright : c_d_tright,fc,0); + framewritechar(x+w,y+h+h2,dual ? c_d_bottomright : c_d_bottomright,fc,0); + framewritechar(x,y+h+h2,dual ? c_d_bottomleft : c_d_bottomleft,fc,0); + framewritechar(x,y+h,dual ? c_d_tleft : c_d_tleft,fc,0); + for(int i=x+1;i=11 && *argv[10])fc=getint(argv[10],0,WHITE); + if(argc==13){ + if(checkstring(argv[12],(unsigned char *)"DOUBLE"))dual=true; + } + fc=RGB121(fc); + framewritechar(x,y,dual ? c_d_topleft : c_topleft,fc,0); + framewritechar(x+w,y,dual ? c_d_tdown : c_tdown,fc,0); + framewritechar(x+w,y+h,dual ? c_d_tup : c_tup,fc,0); + framewritechar(x+w+w2,y,dual ? c_d_topright : c_topright,fc,0); + framewritechar(x+w+w2,y+h,dual ? c_d_bottomright : c_bottomright,fc,0); + framewritechar(x,y+h,dual ? c_d_bottomleft : c_bottomleft,fc,0); + for(int i=x+1;i=5 && *argv[4])xstart=getint(argv[4],0,xmax); + if(argc>=7 && *argv[6])ystart=getint(argv[6],0,ymax); + if(argc>=9 && *argv[8])xend=getint(argv[8],1,xmax); + if(argc==11)yend=getint(argv[10],1,ymax); + SCursor(xstart,ystart); + if(DISPLAY_TYPE==SCREENMODE1)tilefcols[ystart*X_TILE+xstart]=RGB; + if(abs(xs)>=0){ + for(int y=ystart;y<=yend;y++){ + uint16_t *line=&frame[y*framex]; + if(xs>0){ + memmove((uint8_t*)&line[xstart+xs],(uint8_t*)&line[xstart],(xend-xstart-xs+1)*sizeof(uint16_t)); + memset((uint8_t*)&line[xstart],0,xs*sizeof(uint16_t)); + } else { + memmove((uint8_t*)&line[xstart],(uint8_t*)&line[xstart-xs],(xend-xstart+xs+1)*sizeof(uint16_t)); + memset((uint8_t*)&line[xend+xs+1],0,abs(xs)*sizeof(uint16_t)); + } + } + } + if(ys>0){ + uint16_t *line1=&frame[(yend)*framex+xstart]; + uint16_t *line2=&frame[(yend-ys)*framex+xstart]; + for(int y=yend;y>ystart-ys+1;y--){ + memcpy((uint8_t*)line1,(uint8_t*)line2,(xend-xstart+1)*sizeof(uint16_t)); + line1-=framex; + line2-=framex; + } + line1=&frame[ystart*framex+xstart]; + for(int y=ystart;y>8) & 0xF]; + if(outc!=lastc){ + lastc=outc; + SColour(outc,1); + } + if(c==0)c=' '; + DisplayPutC(c&0xFF); + SerialConsolePutC(c&0xFF,0); + } + } + } + fflush(stdout); + gui_fcolour=savefcol; + SCursor(sx,sy); + framecursor=ccursor; + ShowCursor(framecursor); + if(DISPLAY_TYPE==SCREENMODE1)tilefcols[sy*X_TILE+sx]=Option.VGAFC; + SColour(gui_fcolour,1); + } else { + int attributes=0, fc=gui_fcolour; + getargs(&cmdline,9,(unsigned char *)","); + if(argc<5)error("Syntax"); + int x=getint(argv[0],0,framex-1); + int y=getint(argv[2],0,framey-1); + p=getCstring(argv[4]); + if(argc>=7 && *argv[6])fc=getint(argv[6],0,WHITE); + if(argc==9)attributes=getint(argv[8],0,15); + int l=strlen((char *)p); + fc=RGB121(fc); + while(l--){ + if(x==framex){y++;x=0;} + if(y==framey)return; + framewritechar(x++,y,*p++,fc,attributes); + } + } +}*/ + + +void cmd_endfun(void) { + checkend(cmdline); + if(gosubindex == 0 || gosubstack[gosubindex - 1] != NULL) error("Nothing to return to"); + nextstmt = (unsigned char *)"\0\0\0"; // now terminate this run of ExecuteProgram() +} + + + +void MIPS16 cmd_read(void) { + int i, j, k, len, card; + unsigned char *p, *lineptr = NULL, *ptr; + unsigned short datatoken; + int vcnt, vidx, num_to_read=0; + if (checkstring(cmdline, (unsigned char*)"SAVE")) { + if(restorepointer== MAXRESTORE - 1)error((char*)"Too many saves"); + datastore[restorepointer].SaveNextDataLine = NextDataLine; + datastore[restorepointer].SaveNextData = NextData; + restorepointer++; + return; + } + if (checkstring(cmdline, (unsigned char*)"RESTORE")) { + if (!restorepointer)error((char*)"Nothing to restore"); + restorepointer--; + NextDataLine = datastore[restorepointer].SaveNextDataLine; + NextData = datastore[restorepointer].SaveNextData; + return; + } + getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block + if(argc == 0) error("Syntax"); + // first count the elements and do the syntax checking + for(vcnt = i = 0; i < argc; i++) { + if(i & 0x01) { + if(*argv[i] != ',') error("Syntax"); + } else { + findvar(argv[i], V_FIND | V_EMPTY_OK); + if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); + card=1; + if(emptyarray){ //empty array + for(k=0;k argc) { + skipline(p); + NextData = 0; + goto search_again; + } + CurrentLinePtr = lineptr; + if(vtype[vidx] & T_STR) { + char *p1, *p2; + if(*argv[NextData] == '"') { // if quoted string + int toggle=0; + for(len = 0, p1 = vtbl[vidx], p2 = (char *)argv[NextData] + 1; *p2 && *p2 != '"'; len++) { + if(*p2=='\\' && p2[1]!='"' && OptionEscape)toggle^=1; + if(toggle){ + if(*p2=='\\' && isdigit((unsigned char)p2[1]) && isdigit((unsigned char)p2[2]) && isdigit((unsigned char)p2[3])){ + p2++; + i=(*p2++)-48; + i*=10; + i+=(*p2++)-48; + i*=10; + i+=(*p2++)-48; + if(i==0)error("Null character \\000 in escape sequence - use CHR$(0)","$"); + *p1++=i; + } else { + p2++; + switch(*p2){ + case '\\': + *p1++='\\'; + p2++; + break; + case 'a': + *p1++='\a'; + p2++; + break; + case 'b': + *p1++='\b'; + p2++; + break; + case 'e': + *p1++='\e'; + p2++; + break; + case 'f': + *p1++='\f'; + p2++; + break; + case 'n': + *p1++='\n'; + p2++; + break; + case 'q': + *p1++='\"'; + p2++; + break; + case 'r': + *p1++='\r'; + p2++; + break; + case 't': + *p1++='\t'; + p2++; + break; + case 'v': + *p1++='\v'; + p2++; + break; + case '&': + p2++; + if(isxdigit((unsigned char)*p2) && isxdigit((unsigned char)p2[1])){ + i=0; + i = (i << 4) | ((mytoupper(*p2) >= 'A') ? mytoupper(*p2) - 'A' + 10 : *p2 - '0'); + p++; + i = (i << 4) | ((mytoupper(*p2) >= 'A') ? mytoupper(*p2) - 'A' + 10 : *p2 - '0'); + if(i==0)error("Null character \\&00 in escape sequence - use CHR$(0)","$"); + p2++; + *p1++=i; + } else *p1++='x'; + break; + default: + *p1++=*p2++; + } + } + toggle=0; + } else *p1++ = *p2++; + } + } else { // else if not quoted + for(len = 0, p1 = vtbl[vidx], p2 = (char *)argv[NextData]; *p2 && *p2 != '\'' ; len++, p1++, p2++) { + if(*p2 < 0x20 || *p2 >= 0x7f) error("Invalid character"); + *p1 = *p2; // copy up to the comma + } + } + if(len > vsize[vidx]) error("String too long"); + *p1 = 0; // terminate the string + CtoM((unsigned char *)vtbl[vidx]); // convert to a MMBasic string + } + else if(vtype[vidx] & T_INT) + *((long long int *)vtbl[vidx]) = getinteger(argv[NextData]); // much easier if integer variable + else + *((MMFLOAT *)vtbl[vidx]) = getnumber(argv[NextData]); // same for numeric variable + + vidx++; + NextData += 2; + } + } +} + +void cmd_call(void){ + int i; + unsigned char *p=getCstring(cmdline); //get the command we want to call + unsigned char *q = skipexpression(cmdline); + if(*q==',')q++; + i = FindSubFun(p, false); // it could be a defined command + strcat((char *)p," "); + strcat((char *)p,(char *)q); + if(i >= 0) { // >= 0 means it is a user defined command + DefinedSubFun(false, p, i, NULL, NULL, NULL, NULL); + } + else + error("Unknown user subroutine"); +} + + + +void MIPS16 cmd_restore(void) { + if(*cmdline == 0 || *cmdline == '\'') { + if(CurrentLinePtr >= ProgMemory && CurrentLinePtr < ProgMemory + MAX_PROG_SIZE ) + NextDataLine = ProgMemory; + else + NextDataLine = LibMemory; + NextData = 0; + } else { + skipspace(cmdline); + if(*cmdline=='"') { + NextDataLine = findlabel(getCstring(cmdline)); + NextData = 0; + } + else if(isdigit(*cmdline) || *cmdline==GetTokenValue((unsigned char *)"+") || *cmdline==GetTokenValue((unsigned char *)"-") || *cmdline=='.'){ + NextDataLine = findline(getinteger(cmdline), true); // try for a line number + NextData = 0; + } else { + void *ptr=findvar(cmdline,V_NOFIND_NULL); + if(ptr){ + if(g_vartbl[g_VarIndex].type & T_NBR) { + if(g_vartbl[g_VarIndex].dims[0] > 0) { // Not an array + error("Syntax"); + } + NextDataLine = findline(getinteger(cmdline), true); + } else if(g_vartbl[g_VarIndex].type & T_INT) { + if(g_vartbl[g_VarIndex].dims[0] > 0) { // Not an array + error("Syntax"); + } + NextDataLine = findline(getinteger(cmdline), true); + } else { + NextDataLine = findlabel(getCstring(cmdline)); // must be a label + } + } else if(isnamestart(*cmdline)) { + NextDataLine = findlabel(cmdline); // must be a label + } + NextData = 0; + } + } +} + + + +void cmd_lineinput(void) { + unsigned char *vp; + int i, fnbr; + getargs(&cmdline, 3, (unsigned char *)",;"); // this is a macro and must be the first executable stmt + if(argc == 0 || argc == 2) error("Syntax"); + + i = 0; + fnbr = 0; + if(argc == 3) { + // is the first argument a file number specifier? If so, get it + if(*argv[0] == '#' && *argv[1] == ',') { + argv[0]++; + fnbr = getinteger(argv[0]); + } + else { + // is the first argument a prompt? if so, print it otherwise there are too many arguments + if(*argv[1] != ',' && *argv[1] != ';') error("Syntax"); + MMfputs((unsigned char *)getstring(argv[0]), 0); + } + i = 2; + } + + if(argc - i != 1) error("Syntax"); + vp = findvar(argv[i], V_FIND); + if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant"); + if(!(g_vartbl[g_VarIndex].type & T_STR)) error("Invalid variable"); + MMgetline(fnbr, (char *)inpbuf); // get the input line + if(strlen((char *)inpbuf) > g_vartbl[g_VarIndex].size) error("String too long"); + strcpy((char *)vp, (char *)inpbuf); + CtoM(vp); // convert to a MMBasic string +} + + +void cmd_on(void) { + int r; + unsigned char ss[4]; // this will be used to split up the argument line + unsigned char *p; + // first check if this is: ON KEY location + p = checkstring(cmdline, (unsigned char *)"PS2"); + if(p){ + getargs(&p,1,(unsigned char *)","); + if(*argv[0] == '0' && !isdigit(*(argv[0]+1))){ + OnPS2GOSUB = NULL; // the program wants to turn the interrupt off + } else { + OnPS2GOSUB = GetIntAddress(argv[0]); // get a pointer to the interrupt routine + InterruptUsed = true; + } + return; + } + p = checkstring(cmdline, (unsigned char *)"KEY"); + if(p) { + getargs(&p,3,(unsigned char *)","); + if(argc==1){ + if(*argv[0] == '0' && !isdigit(*(argv[0]+1))){ + OnKeyGOSUB = NULL; // the program wants to turn the interrupt off + } else { + OnKeyGOSUB = GetIntAddress(argv[0]); // get a pointer to the interrupt routine + InterruptUsed = true; + } + return; + } else { + keyselect=getint(argv[0],0,255); + if(keyselect==0){ + KeyInterrupt = NULL; // the program wants to turn the interrupt off + } else { + if(*argv[2] == '0' && !isdigit(*(argv[2]+1))){ + KeyInterrupt = NULL; // the program wants to turn the interrupt off + } else { + KeyInterrupt = (char *)GetIntAddress(argv[2]); // get a pointer to the interrupt routine + InterruptUsed = true; + } + } + return; + } + } + p = checkstring(cmdline, (unsigned char *)"ERROR"); + if(p) { + if(checkstring(p, (unsigned char *)"ABORT")) { + OptionErrorSkip = 0; + return; + } + MMerrno = 0; // clear the error flags + *MMErrMsg = 0; + if(checkstring(p, (unsigned char *)"CLEAR")) return; + if(checkstring(p, (unsigned char *)"IGNORE")) { + OptionErrorSkip = -1; + return; + } + if((p = checkstring(p, (unsigned char *)"SKIP"))) { + if(*p == 0 || *p == (unsigned char)'\'') + OptionErrorSkip = 2; + else + OptionErrorSkip = getint(p, 1, 10000) + 1; + return; + } + error("Syntax"); + } + + // if we got here the command must be the traditional: ON nbr GOTO|GOSUB line1, line2,... etc + + ss[0] = tokenGOTO; + ss[1] = tokenGOSUB; + ss[2] = ','; + ss[3] = 0; + { // start a new block + getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, ss); // getargs macro must be the first executable stmt in a block + if(argc < 3 || !(*argv[1] == ss[0] || *argv[1] == ss[1])) error("Syntax"); + if(argc%2 == 0) error("Syntax"); + + r = getint(argv[0], 0, 255); // evaluate the expression controlling the statement + if(r == 0 || r > argc/2) return; // microsoft say that we just go on to the next line + + if(*argv[1] == ss[1]) { + // this is a GOSUB, same as a GOTO but we need to first push the return pointer + if(gosubindex >= MAXGOSUB) error("Too many nested GOSUB"); + errorstack[gosubindex] = CurrentLinePtr; + gosubstack[gosubindex++] = nextstmt; + g_LocalIndex++; + } + + if(isnamestart(*argv[r*2])) + nextstmt = findlabel(argv[r*2]); // must be a label + else + nextstmt = findline(getinteger(argv[r*2]), true); // try for a line number + } +// IgnorePIN = false; +} + +/** + * @cond + * The following section will be excluded from the documentation. + */ +// utility routine used by DoDim() below and other places in the interpreter +// checks if the type has been explicitly specified as in DIM FLOAT A, B, ... etc +unsigned char *CheckIfTypeSpecified(unsigned char *p, int *type, int AllowDefaultType) { + unsigned char *tp; + + if((tp = checkstring(p, (unsigned char *)"INTEGER")) != NULL) + *type = T_INT | T_IMPLIED; + else if((tp = checkstring(p, (unsigned char *)"STRING")) != NULL) + *type = T_STR | T_IMPLIED; + else if((tp = checkstring(p, (unsigned char *)"FLOAT")) != NULL) + *type = T_NBR | T_IMPLIED; + else { + if(!AllowDefaultType) error("Variable type"); + tp = p; + *type = DefaultType; // if the type is not specified use the default + } + return tp; +} + + + +unsigned char *SetValue(unsigned char *p, int t, void *v) { + MMFLOAT f; + long long int i64; + unsigned char *s; + char TempCurrentSubFunName[MAXVARLEN + 1]; + strcpy(TempCurrentSubFunName, (char *)CurrentSubFunName); // save the current sub/fun name + if(t & T_STR) { + p = evaluate(p, &f, &i64, &s, &t, true); + Mstrcpy(v, s); + } + else if(t & T_NBR) { + p = evaluate(p, &f, &i64, &s, &t, false); + if(t & T_NBR) + (*(MMFLOAT *)v) = f; + else + (*(MMFLOAT *)v) = (MMFLOAT)i64; + } else { + p = evaluate(p, &f, &i64, &s, &t, false); + if(t & T_INT) + (*(long long int *)v) = i64; + else + (*(long long int *)v) = FloatToInt64(f); + } + strcpy((char *)CurrentSubFunName, TempCurrentSubFunName); // restore the current sub/fun name + return p; +} + +/** @endcond */ + + +// define a variable +// DIM [AS INTEGER|FLOAT|STRING] var[(d1 [,d2,...]] [AS INTEGER|FLOAT|STRING] [, ..., ...] +// LOCAL also uses this function the routines only differ in that LOCAL can only be used in a sub/fun +void MIPS16 cmd_dim(void) { + int i, j, k, type, typeSave, ImpliedType = 0, VIndexSave, StaticVar = false; + unsigned char *p, chSave, *chPosit; + unsigned char VarName[(MAXVARLEN * 2) + 1]; + void *v, *tv; + + if(*cmdline == tokenAS) cmdline++; // this means that we can use DIM AS INTEGER a, b, etc + p = CheckIfTypeSpecified(cmdline, &type, true); // check for DIM FLOAT A, B, ... + ImpliedType = type; + { // getargs macro must be the first executable stmt in a block + getargs(&p, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); + if((argc & 0x01) == 0) error("Syntax"); + + for(i = 0; i < argc; i += 2) { + p = skipvar(argv[i], false); // point to after the variable + while(!(*p == 0 || *p == tokenAS || *p == (unsigned char)'\'' || *p == tokenEQUAL)) + p++; // skip over a LENGTH keyword if there and see if we can find "AS" + chSave = *p; chPosit = p; *p = 0; // save the char then terminate the string so that LENGTH is evaluated correctly + if(chSave == tokenAS) { // are we using Microsoft syntax (eg, AS INTEGER)? + if(ImpliedType & T_IMPLIED) error("Type specified twice"); + p++; // step over the AS token + p = CheckIfTypeSpecified(p, &type, true); // and get the type + if(!(type & T_IMPLIED)) error("Variable type"); + } + + if(cmdtoken == cmdLOCAL) { + if(g_LocalIndex == 0) error("Invalid here"); + type |= V_LOCAL; // local if defined in a sub/fun + } + + if(cmdtoken == cmdSTATIC) { + if(g_LocalIndex == 0) error("Invalid here"); + // create a unique global name + if(*CurrentInterruptName) + strcpy((char *)VarName, CurrentInterruptName); // we must be in an interrupt sub + else + strcpy((char *)VarName, CurrentSubFunName); // normal sub/fun + for(k = 1; k <= MAXVARLEN; k++) if(!isnamechar(VarName[k])) { + VarName[k] = 0; // terminate the string on a non valid char + break; + } + strcat((char *)VarName,"."); + strcat((char *)VarName, (char *)argv[i]); // by prefixing the var name with the sub/fun name + StaticVar = true; + } else + strcpy((char *)VarName, (char *)argv[i]); + + v = findvar(VarName, type | V_NOFIND_NULL); // check if the variable exists + typeSave = type; + VIndexSave = g_VarIndex; + if(v == NULL) { // if not found + v = findvar(VarName, type | V_FIND | V_DIM_VAR); // create the variable + type = TypeMask(g_vartbl[g_VarIndex].type); + VIndexSave = g_VarIndex; + *chPosit = chSave; // restore the char previously removed + if(g_vartbl[g_VarIndex].dims[0] == -1) error("Array dimensions"); + if(g_vartbl[g_VarIndex].dims[0] > 0) { + g_DimUsed = true; // prevent OPTION BASE from being used + v = g_vartbl[g_VarIndex].val.s; + } + while(*p && *p != '\'' && tokenfunction(*p) != op_equal) p++; // search through the line looking for the equals sign + if(tokenfunction(*p) == op_equal) { + p++; // step over the equals sign + skipspace(p); + if(g_vartbl[g_VarIndex].dims[0] > 0 && *p == '(') { + // calculate the overall size of the array + for(j = 1, k = 0; k < MAXDIM && g_vartbl[VIndexSave].dims[k]; k++) { + j *= (g_vartbl[VIndexSave].dims[k] + 1 - g_OptionBase); + } + do { + p++; // step over the opening bracket or terminating comma + p = SetValue(p, type, v); + if(type & T_STR) v = (char *)v + g_vartbl[VIndexSave].size + 1; + if(type & T_NBR) v = (char *)v + sizeof(MMFLOAT); + if(type & T_INT) v = (char *)v + sizeof(long long int); + skipspace(p); j--; + } while(j > 0 && *p == ','); + if(*p != ')') error("Number of initialising values"); + if(j != 0) error("Number of initialising values"); + } else + SetValue(p, type, v); + } + type = ImpliedType; + } else { + if(!StaticVar) error("$ already declared", VarName); + } + + + // if it is a STATIC var create a local var pointing to the global var + if(StaticVar) { + tv = findvar(argv[i], typeSave | V_LOCAL | V_NOFIND_NULL); // check if the local variable exists + if(tv != NULL) error("$ already declared", argv[i]); + tv = findvar(argv[i], typeSave | V_LOCAL | V_FIND | V_DIM_VAR); // create the variable + if(g_vartbl[VIndexSave].dims[0] > 0 || (g_vartbl[VIndexSave].type & T_STR)) { + FreeMemory(tv); // we don't need the memory allocated to the local + g_vartbl[g_VarIndex].val.s = g_vartbl[VIndexSave].val.s; // point to the memory of the global variable + } else + g_vartbl[g_VarIndex].val.ia = &(g_vartbl[VIndexSave].val.i); // point to the data of the variable + g_vartbl[g_VarIndex].type = g_vartbl[VIndexSave].type | T_PTR; // set the type to a pointer + g_vartbl[g_VarIndex].size = g_vartbl[VIndexSave].size; // just in case it is a string copy the size + for(j = 0; j < MAXDIM; j++) g_vartbl[g_VarIndex].dims[j] = g_vartbl[VIndexSave].dims[j]; // just in case it is an array copy the dimensions + } + } + } +} + + + + +void cmd_const(void) { + unsigned char *p; + void *v; + int i, type; + + getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block + if((argc & 0x01) == 0) error("Syntax"); + + for(i = 0; i < argc; i += 2) { + p = skipvar(argv[i], false); // point to after the variable + skipspace(p); + if(tokenfunction(*p) != op_equal) error("Syntax"); // must be followed by an equals sign + p++; // step over the equals sign + type = T_NOTYPE; + v = DoExpression(p, &type); // evaluate the constant's value + type = TypeMask(type); + type |= V_FIND | V_DIM_VAR | T_CONST | T_IMPLIED; + if(g_LocalIndex != 0) type |= V_LOCAL; // local if defined in a sub/fun + findvar(argv[i], type); // create the variable + if(g_vartbl[g_VarIndex].dims[0] != 0) error("Invalid constant"); + if(TypeMask(g_vartbl[g_VarIndex].type) != TypeMask(type)) error("Invalid constant"); + else { + if(type & T_NBR) g_vartbl[g_VarIndex].val.f = *(MMFLOAT *)v; // and set its value + if(type & T_INT) g_vartbl[g_VarIndex].val.i = *(long long int *)v; + if(type & T_STR) { + if((unsigned char)*(unsigned char *)v<(MAXDIM-1)*sizeof(g_vartbl[g_VarIndex].dims[1])){ + FreeMemorySafe((void **)&g_vartbl[g_VarIndex].val.s); + g_vartbl[g_VarIndex].val.s=(void *)&g_vartbl[g_VarIndex].dims[1]; + } + Mstrcpy((unsigned char *)g_vartbl[g_VarIndex].val.s, (unsigned char *)v); + } + } + } +} + +/** + * @cond + * The following section will be excluded from the documentation. + */ + +// utility function used by llist() below +// it copys a command or function honouring the case selected by the user +void strCopyWithCase(char *d, char *s) { + if(Option.Listcase == CONFIG_LOWER) { + while(*s) *d++ = tolower(*s++); + } else if(Option.Listcase == CONFIG_UPPER) { + while(*s) *d++ = mytoupper(*s++); + } else { + while(*s) *d++ = *s++; + } + *d = 0; +} + +void replaceAlpha(char *str, const char *replacements[MMEND]){ + char buffer[STRINGSIZE]; // Buffer to store the modified string + int bufferIndex = 0; + int len = strlen(str); + int i = 0; + + while (i < len) { + // Check for the pattern "~(X)" where X is an uppercase letter + if (i= len) { // If remaining text fits in one line + strcpy(result + pos, c + start); + pos += strlen(c + start); + break; + } + + while (split_pos > start && !(c[split_pos] == ' ' || c[split_pos] == ',')) { + split_pos--; // Try to find a space to break at + } + + if (split_pos == start) { + split_pos = start + n - 1; // No space found, force a split + } + + strncpy(result + pos, c + start, split_pos - start + 1); + pos += (split_pos - start + 1); + + start = split_pos + 1; + + if (start < len) { // Only add underscore if not the last substring + result[pos++] = ' '; + result[pos++] = Option.continuation; + result[pos++] = '\n'; + count++; + } + } + + result[pos] = '\0'; + strcpy(c,result); + FreeMemory((void *)result); + return count; +} +// list a line into a buffer (b) given a pointer to the beginning of the line (p). +// the returned string is a C style string (terminated with a zero) +// this is used by cmd_list(), cmd_edit() and cmd_xmodem() +unsigned char *llist(unsigned char *b, unsigned char *p) { + int i, firstnonwhite = true; + unsigned char *b_start = b; + + while(1) { + if(*p == T_NEWLINE) { + p++; + firstnonwhite = true; + continue; + } + + if(*p == T_LINENBR) { + i = (((p[1]) << 8) | (p[2])); // get the line number + p += 3; // and step over the number + IntToStr((char *)b, i, 10); + b += strlen((char *)b); + if(*p != ' ') *b++ = ' '; + } + + if(*p == T_LABEL) { // got a label + for(i = p[1], p += 2; i > 0; i--) + *b++ = *p++; // copy to the buffer + *b++ = ':'; // terminate with a colon + if(*p && *p != ' ') *b++ = ' '; // and a space if necessary + firstnonwhite = true; + } // this deliberately drops through in case the label is the only thing on the line + + if(*p >= C_BASETOKEN) { + if(firstnonwhite) { + CommandToken tkn=commandtbl_decode(p); + if(tkn == GetCommandValue( (unsigned char *)"Let")) + *b = 0; // use nothing if it LET + else { + strCopyWithCase((char *)b, (char *)commandname(tkn)); // expand the command (if it is not LET) + if(*b=='_'){ + if(!strncasecmp((char *)&b[1],"SIDE SET",8) || + !strncasecmp((char *)&b[1],"END PROGRAM",11) || + !strncasecmp((char *)&b[1],"WRAP",4) || + !strncasecmp((char *)&b[1],"LINE",4) || + !strncasecmp((char *)&b[1],"PROGRAM",7) || + !strncasecmp((char *)&b[1],"LABEL",5) + ) *b='.'; + else if(b[1]=='(')*b='&'; + } + b += strlen((char *)b); // update pointer to the end of the buffer + if(isalpha(*(b - 1))) *b++ = ' '; // add a space to the end of the command name + } + firstnonwhite = false; + p+=sizeof(CommandToken); + } else { // not a command so must be a token + strCopyWithCase((char *)b, (char *)tokenname(*p)); // expand the token + b += strlen((char *)b); // update pointer to the end of the buffer + if(*p == tokenTHEN || *p == tokenELSE) + firstnonwhite = true; + else + firstnonwhite = false; + p++; + } + continue; + } + + // hey, an ordinary char, just copy it to the output + if(*p) { + *b = *p; // place the char in the buffer + if(*p != ' ') firstnonwhite = false; + p++; b++; // move the pointers + continue; + } + + // at this point the char must be a zero + // zero char can mean both a separator or end of line + if(!(p[1] == T_NEWLINE || p[1] == 0)) { + *b++ = ':'; // just a separator + firstnonwhite = true; + p++; + continue; + } + + // must be the end of a line - so return to the caller + while(*(b-1) == ' ' && b > b_start) --b; // eat any spaces on the end of the line + *b = 0; + replaceAlpha((char *)b_start, overlaid_functions) ; //replace the user version of all the MM. functions + STR_REPLACE((char *)b_start, "PEEK(INT8", "PEEK(BYTE",0); + return ++p; + } // end while +} + + + +void execute_one_command(unsigned char *p) { + int i; + + CheckAbort(); + targ = T_CMD; + skipspace(p); // skip any whitespace + if(p[0]>= C_BASETOKEN && p[1]>=C_BASETOKEN){ +// if(*(char*)p >= C_BASETOKEN && *(char*)p - C_BASETOKEN < CommandTableSize - 1 && (commandtbl[*(char*)p - C_BASETOKEN].type & T_CMD)) { + CommandToken cmd=commandtbl_decode(p); + if(cmd == cmdWHILE || cmd == cmdDO || cmd == cmdFOR) error("Invalid inside THEN ... ELSE") ; + cmdtoken=cmd; + cmdline = p + sizeof(CommandToken); + skipspace(cmdline); + commandtbl[cmd].fptr(); // execute the command + } else { + if(!isnamestart(*p)) error("Invalid character"); + i = FindSubFun(p, false); // it could be a defined command + if(i >= 0) // >= 0 means it is a user defined command + DefinedSubFun(false, p, i, NULL, NULL, NULL, NULL); + else + error("Unknown command"); + } + ClearTempMemory(); // at the end of each command we need to clear any temporary string vars +} + +void execute(char* mycmd) { + // char *temp_tknbuf; + unsigned char* ttp=NULL; + int i = 0, toggle = 0; + // temp_tknbuf = GetTempStrMemory(); + // strcpy(temp_tknbuf, tknbuf); + // first save the current token buffer in case we are in immediate mode + // we have to fool the tokeniser into thinking that it is processing a program line entered at the console + skipspace(mycmd); + strcpy((char *)inpbuf, (const char *)getCstring((unsigned char *)mycmd)); // then copy the argument + if (!(toupper(inpbuf[0]) == 'R' && toupper(inpbuf[1]) == 'U' && toupper(inpbuf[2]) == 'N')) { //convert the string to upper case + while (inpbuf[i]) { + if (inpbuf[i] == 34) { + if (toggle == 0)toggle = 1; + else toggle = 0; + } + if (!toggle) { + if (inpbuf[i] == ':')error((char *)"Only single statements allowed"); + inpbuf[i] = toupper(inpbuf[i]); + } + i++; + } + multi=false; + tokenise(true); // and tokenise it (the result is in tknbuf) + memset(inpbuf, 0, STRINGSIZE); + tknbuf[strlen((char *)tknbuf)] = 0; + tknbuf[strlen((char*)tknbuf) + 1] = 0; + if(CurrentLinePtr)ttp = nextstmt; // save the globals used by commands + ScrewUpTimer = 1000; + ExecuteProgram(tknbuf); // execute the function's code + ScrewUpTimer = 0; + // g_TempMemoryIsChanged = true; // signal that temporary memory should be checked + if(CurrentLinePtr)nextstmt = ttp; + return; + } + else { + unsigned char* p = inpbuf; + char* q; +// char fn[STRINGSIZE] = { 0 }; + unsigned short tkn=GetCommandValue((unsigned char *)"RUN"); + tknbuf[0] = (tkn & 0x7f ) + C_BASETOKEN; + tknbuf[1] = (tkn >> 7) + C_BASETOKEN; //tokens can be 14-bit + p[0] = (tkn & 0x7f ) + C_BASETOKEN; + p[1] = (tkn >> 7) + C_BASETOKEN; //tokens can be 14-bit + memmove(&p[2], &p[4], strlen((char *)p) - 4); + if ((q = strchr((char *)p, ':'))) { + q--; + *q = '0'; + } + p[strlen((char*)p) - 2] = 0; +// MMPrintString(fn); PRet(); +// CloseAudio(1); + strcpy((char *)tknbuf, (char*)inpbuf); + if (CurrentlyPlaying != P_NOTHING)CloseAudio(1); + longjmp(jmprun, 1); + } +} +/** @endcond */ + +void cmd_execute(void) { + execute((char*)cmdline); +} + + diff --git a/Code/pico_multi_booter/picomite/Commands.h b/Code/pico_multi_booter/picomite/Commands.h new file mode 100644 index 0000000..14520c6 --- /dev/null +++ b/Code/pico_multi_booter/picomite/Commands.h @@ -0,0 +1,108 @@ +/* + * @cond + * The following section will be excluded from the documentation. + */ +/* ********************************************************************************************************************* +PicoMite MMBasic + +commands.h + + Geoff Graham, Peter Mather +Copyright (c) 2021, All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the distribution. +3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed + on the console at startup (additional copyright messages may be added). +4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed + by the . +5. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software + without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +************************************************************************************************************************/ +#include +#if !defined(INCLUDE_COMMAND_TABLE) && !defined(INCLUDE_TOKEN_TABLE) + +typedef struct s_forstack { + unsigned char *forptr; // pointer to the FOR command in program memory + unsigned char *nextptr; // pointer to the NEXT command in program memory + void *var; // value of the FOR variable + unsigned char vartype; // type of the variable + unsigned char level; // the sub/function level that the loop was created + union u_totype { + MMFLOAT f; // the TO value if it is a float + long long int i; // the TO value if it is an integer + } tovalue; + union u_steptype { + MMFLOAT f; // the STEP value if it is a float + long long int i; // the STEP value if it is an integer + } stepvalue; +}forstackval; +extern unsigned char topicbuff[STRINGSIZE]; +extern unsigned char messagebuff[STRINGSIZE]; +extern unsigned char addressbuff[20]; + +extern struct s_forstack g_forstack[MAXFORLOOPS + 1] ; +extern int g_forindex; +extern unsigned char cmdlinebuff[STRINGSIZE]; +typedef struct s_dostack { + unsigned char *evalptr; // pointer to the expression to be evaluated + unsigned char *loopptr; // pointer to the loop statement + unsigned char *doptr; // pointer to the DO statement + unsigned char level; // the sub/function level that the loop was created +}dostackval; + +extern struct s_dostack g_dostack[MAXDOLOOPS]; +extern int g_doindex; + +extern unsigned char *gosubstack[MAXGOSUB]; +extern unsigned char *errorstack[MAXGOSUB]; +extern int gosubindex; +extern unsigned char g_DimUsed; + +//extern unsigned char *GetFileName(char* CmdLinePtr, unsigned char *LastFilePtr); +//extern void mergefile(unsigned char *fname, unsigned char *MemPtr); +extern void ListProgram(unsigned char *p, int all); +extern unsigned char *llist(unsigned char *b, unsigned char *p); +extern unsigned char *CheckIfTypeSpecified(unsigned char *p, int *type, int AllowDefaultType); + +extern void MIPS16 ListNewLine(int *ListCnt, int all); +// definitions related to setting video off and on +extern const unsigned int CaseOption; +extern volatile bool Keycomplete; +extern char *KeyInterrupt; +extern int keyselect; +extern uint64_t g_flag; +#define TRACE_BUFF_SIZE 128 + +extern unsigned int BusSpeed; +extern unsigned char *OnKeyGOSUB; +extern unsigned char EchoOption; +extern unsigned char *GetFileName(unsigned char* CmdLinePtr, unsigned char *LastFilePtr); +extern void mergefile(unsigned char *fname, unsigned char *MemPtr); +extern volatile unsigned int ScrewUpTimer; +struct sa_data{ + unsigned char* SaveNextDataLine; + int SaveNextData; +}; +extern void SaveContext(void); +extern void RestoreContext(bool keep); +extern void do_end(bool ecmd); +extern struct sa_data datastore[MAXRESTORE]; +extern int restorepointer; +extern unsigned short *frame, *outframe; +extern bool framecursor; +extern void array_slice(unsigned char *tp); +extern void array_insert(unsigned char *tp); +extern void array_add(unsigned char *tp); +extern void array_set(unsigned char *tp); +extern int format_string(char *c, int n); + +#endif +/* @endcond */ diff --git a/Code/pico_multi_booter/picomite/Custom.c b/Code/pico_multi_booter/picomite/Custom.c new file mode 100644 index 0000000..44b1cd1 --- /dev/null +++ b/Code/pico_multi_booter/picomite/Custom.c @@ -0,0 +1,2057 @@ +/*********************************************************************************************************************** +PicoMite MMBasic + +custom.c + + Geoff Graham, Peter Mather +Copyright (c) 2021, All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the distribution. +3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed + on the console at startup (additional copyright messages may be added). +4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed + by the . +5. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software + without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +************************************************************************************************************************/ +/** +* @file Custom.c +* @author Geoff Graham, Peter Mather +* @brief Source for PIO, JSON and WEB MMBasic commands and function +*/ +/** + * @cond + * The following section will be excluded from the documentation. + */ +#include + +#include "MMBasic_Includes.h" +#include "Hardware_Includes.h" +#include "hardware/dma.h" +#include "hardware/structs/bus_ctrl.h" +#include "hardware/structs/dma.h" +#include "hardware/irq.h" +#include "hardware/pwm.h" +#ifdef PICOMITEWEB +#define CJSON_NESTING_LIMIT 100 +#include "cJSON.h" +#endif +#define STATIC static + +/************************************************************************************************************************* +************************************************************************************************************************** +IMPORTANT: +This module is empty and should be used for your special functions and commands. In the standard distribution this file +will never be changed, so your code should be safe here. You should avoid placing commands and functions in other files as +they may be changed and you would then need to re insert your changes in a new release of the source. + +************************************************************************************************************************** +**************************************************************************************************************************/ + + +/******************************************************************************************************************************************** + custom commands and functions + each function is responsible for decoding a command + all function names are in the form cmd_xxxx() (for a basic command) or fun_xxxx() (for a basic function) so, if you want to search for the + function responsible for the NAME command look for cmd_name + + There are 4 items of information that are setup before the command is run. + All these are globals. + + int cmdtoken This is the token number of the command (some commands can handle multiple + statement types and this helps them differentiate) + + unsigned char *cmdline This is the command line terminated with a zero unsigned char and trimmed of leading + spaces. It may exist anywhere in memory (or even ROM). + + unsigned char *nextstmt This is a pointer to the next statement to be executed. The only thing a + command can do with it is save it or change it to some other location. + + unsigned char *CurrentLinePtr This is read only and is set to NULL if the command is in immediate mode. + + The only actions a command can do to change the program flow is to change nextstmt or + execute longjmp(mark, 1) if it wants to abort the program. + + ********************************************************************************************************************************************/ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "pico/stdlib.h" +#include "hardware/irq.h" +#include "hardware/claim.h" +#define PIO_NUM(pio) ((pio) == pio0 ? 0 : (pio1 ? 1 :2)) +#define CLKMIN ((Option.CPU_Speed*125)>>13) +#define CLKMAX (Option.CPU_Speed *1000) + +#include "hardware/pio.h" +#include "hardware/pio_instructions.h" +char *pioRXinterrupts[4][3]={0}; +char *pioTXinterrupts[4][3]={0}; +uint8_t pioTXlast[4][3]={0}; +char *DMAinterruptRX=NULL; +char *DMAinterruptTX=NULL; +uint32_t dma_rx_chan = PIO_RX_DMA; +uint32_t dma_tx_chan = PIO_TX_DMA; +uint32_t dma_rx_chan2 = PIO_RX_DMA2; +uint32_t dma_tx_chan2 = PIO_TX_DMA2; +int dma_tx_pio; +int dma_tx_sm; +int dma_rx_pio; +int dma_rx_sm; +#ifdef PICOMITE +#ifdef rp2350 +bool PIO0=true; +bool PIO1=true; +bool PIO2=true; +#else +bool PIO0=true; +bool PIO1=true; +bool PIO2=false; +#endif +#endif +#ifdef PICOMITEVGA +#ifdef rp2350 +#ifdef HDMI +bool PIO0=true; +#else +bool PIO0=false; +#endif +bool PIO1=true; +bool PIO2=true; +#else +bool PIO0=false; +bool PIO1=true; +bool PIO2=false; +#endif +#endif +#ifdef PICOMITEWEB +#ifdef rp2350 +bool PIO0=true; +bool PIO1=false; +bool PIO2=true; +#else +bool PIO0=true; +bool PIO1=false; +bool PIO2=false; +#endif +extern void setwifi(unsigned char *tp); +volatile bool TCPreceived=false; +char *TCPreceiveInterrupt=NULL; +#endif +#ifdef rp2350 +uint64_t piomap[2]={0}; +#endif +#define MAXLABEL 16 +static int sidepins=0,sideopt=0,sidepinsdir=0, PIOlinenumber=0, PIOstart=0, p_wrap=31, p_wrap_target=0; +static int delaypossible=5; +static int checksideanddelay=0; +int dirOK=2; +static int *instructions=NULL; +static char *labelsfound=NULL, *labelsneeded=NULL; +int piointerrupt=0; +uint8_t nextline[4]={0}; +static int pioinuse=99; +extern bool PIO2, PIO1, PIO0; +extern void on_pwm_wrap(void); +PIO pioi2s; +uint8_t i2ssm; +//#if defined( PICOMITEVGA) && !defined(HDMI) +#include "PicoMiteI2S.pio.h" +//#endif +static inline uint32_t pio_sm_calc_wrap(uint wrap_target, uint wrap) { + uint32_t calc=0; +// valid_params_if(PIO, wrap < PIO_INSTRUCTION_COUNT); +// valid_params_if(PIO, wrap_target < PIO_INSTRUCTION_COUNT); + return (calc & ~(PIO_SM0_EXECCTRL_WRAP_TOP_BITS | PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) | + (wrap_target << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) | + (wrap << PIO_SM0_EXECCTRL_WRAP_TOP_LSB); +} +int calcsideanddelay(char *p, int sidepins, int maxdelaybits){ + int data=0; + char *pp; + if((pp=fstrstr(p,"side "))){ + pp+=5; + skipspace(pp); + char *ss=pp; + char save=0; + if((*ss>='0' && *ss<='9') || *ss=='&' ){ + char *ppp=ss; + if(*ss=='&'){ + if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax"); + ppp+=2; + } + while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;} + if(*ppp){ + save=*ppp; + *ppp=','; + } + data=(int)(getint((unsigned char *)ss,0,((1<='0' && *ppp<='9' && *ppp){ppp++;} + if(*ppp){ + save=*ppp; + *ppp=','; + } + data=(int)getint((unsigned char *)pp,0,7); + if(*ppp==',')*ppp=save; + if((pp=fstrstr(p," rel")) && (pp[4]==0 || pp[4]==' ' || pp[4]==';'))rel=1; +#ifdef rp2350 + if((pp=fstrstr(p," prev")) && (pp[5]==0 || pp[5]==' ' || pp[5]==';'))prev=1; + if((pp=fstrstr(p," next")) && (pp[5]==0 || pp[5]==' ' || pp[5]==';'))next=1; + if(next)data|=0b11; + if(prev)data|=0b01; +#endif + if(rel){ + data|=0x10; + } + return data; +} +void pio_init(int pior, int sm, uint32_t pinctrl, uint32_t execctrl, uint32_t shiftctrl, int start, float clock, bool sideout, bool setout, bool outout){ + pio_sm_config mypio=pio_get_default_sm_config(); +#ifdef rp2350 + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + clock=(float)Option.CPU_Speed*1000.0/clock; + int sidebase=(pinctrl & 0b111110000000000)>>10; + int setbase=(pinctrl & 0b1111100000)>>5; + int outbase=(pinctrl & 0b11111); + int sidecount=(pinctrl & 0xE0000000)>>29; + int setcount=(pinctrl & 0x1C000000)>>26; + int outcount=(pinctrl & 0x3F00000)>>20; +// int inbase=(pinctrl & 0xF8000)>>15; + int opt=(execctrl>>30) & 1; + mypio.clkdiv = (uint32_t) (clock * (1 << 16)); + mypio.execctrl=execctrl; + mypio.shiftctrl=shiftctrl; + mypio.pinctrl=pinctrl; + #ifdef rp2350 + #ifdef PICOMITEWEB + for(int i = 1; i < (NBRPINS) ; i++) { + #else + for(int i = 1; i < (rp2350a ? 44:NBRPINS) ; i++) { + #endif + #else + for(int i = 1; i < (NBRPINS) ; i++) { + #endif + if(CheckPin(i, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED)) { // don't reset invalid or boot reserved pins + gpio_set_input_enabled(PinDef[i].GPno, true); + } + } + if(sidecount && sideout)pio_sm_set_consecutive_pindirs(pio, sm, sidebase, sidecount-opt, true); + if(outcount && outout)pio_sm_set_consecutive_pindirs(pio, sm, outbase, outcount, true); + if(setcount && setout)pio_sm_set_consecutive_pindirs(pio, sm, setbase, setcount, true); + pio_sm_set_config(pio, sm, &mypio); + pio_sm_init(pio, sm, start, &mypio); + pio_sm_clear_fifos(pio,sm); +} +int checkblock(char *p){ + int data=0; + char *pp; + if((pp=fstrstr(p,"IFFULL"))){ + if(!(pp[6]==' ' || pp[6]==0))error("Syntax"); + data=0b1000000; + } + if((pp=fstrstr(p,"NOBLOCK"))){ + if(!(pp[7]==' ' || pp[7]==0))error("Syntax"); + return data; + } + if((pp=fstrstr(p,"BLOCK"))){ + if(!(pp[5]==' ' || pp[5]==0))error("Syntax"); + data=0b100000; + } + return data; +} +extern uint I2SOff; +extern void start_vga_i2s(void); +extern void start_i2s(int pio, int sm); + +void start_i2s(int pior, int sm){ + if(!Option.audio_i2s_bclk)return; + i2ssm=sm; +#ifdef rp2350 + pioi2s = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + pioi2s = (pior==0 ? pio0: pio1); +#endif +#if !defined(PICOMITEVGA) || defined(HDMI) +#ifdef rp2350 + if(PinDef[Option.audio_i2s_bclk].GPno+1>31 || PinDef[Option.audio_i2s_data].GPno>31)pio_set_gpio_base(pioi2s,16); +#endif + I2SOff = pio_add_program(pioi2s, &i2s_program); + +#endif + gpio_set_input_enabled(PinDef[Option.audio_i2s_data].GPno, true); + gpio_set_input_enabled(PinDef[Option.audio_i2s_bclk].GPno, true); + gpio_set_input_enabled(PinDef[Option.audio_i2s_bclk].GPno+1, true); +#ifdef rp2350 + gpio_set_function(PinDef[Option.audio_i2s_bclk].GPno , pior==0 ? GPIO_FUNC_PIO0 : (pior==1? GPIO_FUNC_PIO1 : GPIO_FUNC_PIO2)); + gpio_set_function(PinDef[Option.audio_i2s_data].GPno , pior==0 ? GPIO_FUNC_PIO0 : (pior==1? GPIO_FUNC_PIO1 : GPIO_FUNC_PIO2)); + gpio_set_function(PinDef[Option.audio_i2s_bclk].GPno+1, pior==0 ? GPIO_FUNC_PIO0 : (pior==1? GPIO_FUNC_PIO1 : GPIO_FUNC_PIO2)); +#else + gpio_set_function(PinDef[Option.audio_i2s_bclk].GPno , pior==0 ? GPIO_FUNC_PIO0 : GPIO_FUNC_PIO1); + gpio_set_function(PinDef[Option.audio_i2s_data].GPno , pior==0 ? GPIO_FUNC_PIO0 : GPIO_FUNC_PIO1); + gpio_set_function(PinDef[Option.audio_i2s_bclk].GPno+1, pior==0 ? GPIO_FUNC_PIO0 : GPIO_FUNC_PIO1); +#endif + ExtCfg(Option.audio_i2s_bclk, EXT_BOOT_RESERVED, 0); + ExtCfg(Option.audio_i2s_data, EXT_BOOT_RESERVED, 0); + ExtCfg(PINMAP[PinDef[Option.audio_i2s_bclk].GPno+1], EXT_BOOT_RESERVED, 0); + + // prepare default PIO program config + pio_sm_config cfg = i2s_program_get_default_config(I2SOff); + + // map state machine's OUT and MOV pins + sm_config_set_out_pins(&cfg, PinDef[Option.audio_i2s_data].GPno, 1); + + // set sideset pins (BCLK and LCLK) + sm_config_set_sideset_pins(&cfg, PinDef[Option.audio_i2s_bclk].GPno); + sm_config_set_sideset(&cfg,2, false, false); + + // join FIFO to send only + sm_config_set_fifo_join(&cfg, PIO_FIFO_JOIN_TX); + + // PIO clock divider + float clockdiv=(Option.CPU_Speed*1000.0f)/(float)(44100*128); +// pio_sm_set_clkdiv(pioi2s,i2ssm,clockdiv); + sm_config_set_clkdiv(&cfg, clockdiv); + sm_config_set_out_shift(&cfg,false,false,false); + sm_config_set_in_shift(&cfg,false,false,false); + +// initialize state machine + pio_sm_init(pioi2s, sm, I2SOff, &cfg); + pio_sm_set_consecutive_pindirs(pioi2s, sm, PinDef[Option.audio_i2s_data].GPno, 1, true); + pio_sm_set_consecutive_pindirs(pioi2s, sm, PinDef[Option.audio_i2s_bclk].GPno, 2, true); + pio_sm_set_enabled(pioi2s, sm, true); + + AUDIO_SLICE=Option.AUDIO_SLICE; + AUDIO_WRAP=(Option.CPU_Speed*10)/441 - 1 ; + pwm_set_wrap(AUDIO_SLICE, AUDIO_WRAP); + pwm_clear_irq(AUDIO_SLICE); + irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap); + irq_set_enabled(PWM_IRQ_WRAP, true); + irq_set_priority(PWM_IRQ_WRAP,255); + pwm_set_enabled(AUDIO_SLICE, true); + if(pior==2)PIO2=false; + else if(pior==1)PIO1=false; + else PIO0=false; +} +int getGPpin(unsigned char *pinarg, int pio, int base){ + char code; + int pin; + if(!(code=codecheck(pinarg)))pinarg+=2; + pin = getinteger(pinarg); + if(!code)pin=codemap(pin); + if(IsInvalidPin(pin)) error("Invalid pin"); + if((ExtCurrentConfig[pin] == EXT_PIO0_OUT && pio!=0) || + (ExtCurrentConfig[pin] == EXT_PIO1_OUT && pio!=1) +#ifdef rp2350 + || (ExtCurrentConfig[pin] == EXT_PIO2_OUT && pio!=2) +#endif + || ExtCurrentConfig[pin] == EXT_BOOT_RESERVED + ) error("Pin in use"); + if(PinDef[pin].GPnobase+31)error("Pin out of range for base %"); + return PinDef[pin].GPno; + +} + +/* @endcond */ +void MIPS16 cmd_pio(void){ + unsigned char *tp; + #ifdef rp2350 + int dims[MAXDIM]={0}; + #else + short dims[MAXDIM]={0}; + #endif + tp = checkstring(cmdline, (unsigned char *)"EXECUTE"); + if(tp){ + int i; + getargs(&tp, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); + if((argc & 0x01) == 0) error("Syntax"); + if(argc<5)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + int sm=getint(argv[2],0,3); + for(i = 4; i < argc; i += 2) { + pio_sm_exec(pio, sm, getint(argv[i],0,65535)); + } + return; + } + tp = checkstring(cmdline, (unsigned char *)"WRITE"); + if(tp){ + int i=6; + getargs(&tp, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); + if((argc & 0x01) == 0) error("Syntax"); + if(argc<5)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + int sm=getint(argv[2],0,3); + int count=getint(argv[4],0,MAX_ARG_COUNT-3); + while(count--) { + pio_sm_put_blocking(pio, sm, getint(argv[i],0,(int64_t)0xFFFFFFFF)); + i+=2; + } + return; + } + tp = checkstring(cmdline, (unsigned char *)"DMA RX"); + if(tp){ + getargs(&tp, 13, (unsigned char *)","); + if(checkstring(argv[0],(unsigned char *)"OFF")){ + dma_hw->abort = ((1u << dma_rx_chan2) | (1u << dma_rx_chan)); + if(dma_channel_is_busy(dma_rx_chan))dma_channel_abort(dma_rx_chan); + if(dma_channel_is_busy(dma_rx_chan2))dma_channel_abort(dma_rx_chan2); + return; + } + if(DMAinterruptRX || dma_channel_is_busy(dma_rx_chan)|| dma_channel_is_busy(dma_rx_chan2)) { + dma_hw->abort = ((1u << dma_rx_chan2) | (1u << dma_rx_chan)); + if(dma_channel_is_busy(dma_rx_chan))dma_channel_abort(dma_rx_chan); + if(dma_channel_is_busy(dma_rx_chan2))dma_channel_abort(dma_rx_chan2); + } + if(argc<7)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + int sm=getint(argv[2],0,3); + dma_rx_pio=pior; + dma_rx_sm=sm; + int nbr=getint(argv[4],0,0xFFFFFFFF); + uint32_t s_nbr=nbr; + static uint32_t *a1int=NULL; + int64_t *aint=NULL; + int toarraysize=parseintegerarray(argv[6],&aint,4,1,dims, true); + a1int=(uint32_t *)aint; + if(argc>=9 && *argv[8]){ + if(nbr==0)error("Interrupt incopmpatible with continuous running"); + DMAinterruptRX=(char *)GetIntAddress(argv[8]); + InterruptUsed=true; + } + int dmasize=DMA_SIZE_32; + if(argc>=11 && *argv[10]){ + dmasize=getinteger(argv[10]); + if(!(dmasize==8 || dmasize==16 || dmasize==32))error("Invalid transfer size"); + if(dmasize==8)dmasize=DMA_SIZE_8; + else if(dmasize==16)dmasize=DMA_SIZE_16; + else if(dmasize==32)dmasize=DMA_SIZE_32; + } + dma_channel_config c = dma_channel_get_default_config(dma_rx_chan); + channel_config_set_read_increment(&c, false); + channel_config_set_transfer_data_size(&c, dmasize); + if(dma_rx_pio==2)channel_config_set_dreq(&c, 20+sm); + else channel_config_set_dreq(&c, pio_get_dreq(pio, sm, true)); + channel_config_set_dreq(&c, pio_get_dreq(pio, sm, false)); + if(argc==13){ + int size=getinteger(argv[12]); + if(!(size==1 || size==2 || size==4 || size==8 || size==16 || size==32 || size==64 || size==128 || size==256 || size==512 || size==1024 || size== 2048 || size==4096 || size==8192 || size==16384 || size==32768))error("Not power of 2"); + if(size!=1){ + int i=0,j=size; + if(((uint32_t)a1int & (j-1)) && nbr==0)error("Data alignment error"); + while(j>>=1)i++; + i+=dmasize; + if((1<(toarraysize*8))error("Array size"); + if(nbr==0){ + nbr=size; + dma_channel_config c2 = dma_channel_get_default_config(dma_rx_chan2); //Get configurations for control channel + channel_config_set_transfer_data_size(&c2, DMA_SIZE_32); //Set control channel data transfer size to 32 bits + channel_config_set_read_increment(&c2, false); //Set control channel read increment to false + channel_config_set_write_increment(&c2, false); //Set control channel write increment to false + channel_config_set_dreq(&c2, 0x3F); + dma_channel_configure(dma_rx_chan2, + &c2, + &dma_hw->ch[dma_rx_chan].al2_write_addr_trig, + &a1int, + 1, + false); //Configure control channel + } + if(s_nbr!=0)channel_config_set_ring(&c,true,i); + channel_config_set_write_increment(&c, true); + } else channel_config_set_write_increment(&c, false); + } else { + if((nbr<(toarraysize*8))error("Array size"); + channel_config_set_write_increment(&c, true); + } + if(s_nbr==0) channel_config_set_chain_to(&c, dma_rx_chan2); //When this channel completes, it will trigger the channel indicated by chain_to + dma_channel_configure(dma_rx_chan, + &c, + a1int, // Destination pointer + &pio->rxf[sm], // Source pointer + nbr, // Number of transfers + (s_nbr==0 ? false :true) // Start immediately + ); + if(s_nbr==0) dma_start_channel_mask(1u << dma_rx_chan2); + pio_sm_restart(pio, sm); + pio_sm_set_enabled(pio, sm, true); + return; + } + tp = checkstring(cmdline, (unsigned char *)"DMA TX"); + if(tp){ + getargs(&tp, 13, (unsigned char *)","); + if(checkstring(argv[0],(unsigned char *)"OFF")){ + dma_hw->abort = ((1u << dma_tx_chan2) | (1u << dma_tx_chan)); + if(dma_channel_is_busy(dma_tx_chan))dma_channel_abort(dma_tx_chan); + if(dma_channel_is_busy(dma_tx_chan2))dma_channel_abort(dma_tx_chan2); + return; + } + if(DMAinterruptTX || dma_channel_is_busy(dma_tx_chan) || dma_channel_is_busy(dma_tx_chan2)){ + dma_hw->abort = ((1u << dma_tx_chan2) | (1u << dma_tx_chan)); + if(dma_channel_is_busy(dma_tx_chan))dma_channel_abort(dma_tx_chan); + if(dma_channel_is_busy(dma_tx_chan2))dma_channel_abort(dma_tx_chan2); + } + if(argc<7)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + int sm=getint(argv[2],0,3); + dma_tx_pio=pior; + dma_tx_sm=sm; + uint32_t nbr=getint(argv[4],0,0xFFFFFFFF); + uint32_t s_nbr=nbr; + static uint32_t *a1int=NULL; + int64_t *aint=NULL; + int toarraysize=parseintegerarray(argv[6],&aint,4,1,dims, true); + a1int=(uint32_t *)aint; + if(argc>=9 && *argv[8]){ + if(nbr==0)error("Interrupt incopmpatible with continuous running"); + DMAinterruptTX=(char *)GetIntAddress(argv[8]); + InterruptUsed=true; + } + int dmasize=DMA_SIZE_32; + if(argc>=11 && *argv[10]){ + dmasize=getinteger(argv[10]); + if(!(dmasize==8 || dmasize==16 || dmasize==32))error("Invalid transfer size"); + if(dmasize==8)dmasize=DMA_SIZE_8; + else if(dmasize==16)dmasize=DMA_SIZE_16; + else if(dmasize==32)dmasize=DMA_SIZE_32; + } + dma_channel_config c = dma_channel_get_default_config(dma_tx_chan); + channel_config_set_write_increment(&c, false); + if(dma_tx_pio==2)channel_config_set_dreq(&c, 16+sm); + else channel_config_set_dreq(&c, pio_get_dreq(pio, sm, true)); + + channel_config_set_transfer_data_size(&c, dmasize); + if(argc==13){ + int size=getinteger(argv[12]); + if(!(size==1 || size==2 || size==4 || size==8 || size==16 || size==32 || size==64 || size==128 || size==256 || size==512 || size==1024 || size== 2048 || size==4096 || size==8192 || size==16384 || size==32768))error("Not power of 2"); + if(size!=1){ + int i=0,j=size; + if(((uint32_t)a1int & (j-1)) && nbr==0)error("Data alignment error"); + while(j>>=1)i++; + i+=dmasize; + if((1<(toarraysize*8))error("Array size"); + if(nbr==0){ + nbr=size; + dma_channel_config c2 = dma_channel_get_default_config(dma_tx_chan2); //Get configurations for control channel + channel_config_set_transfer_data_size(&c2, DMA_SIZE_32); //Set control channel data transfer size to 32 bits + channel_config_set_read_increment(&c2, false); //Set control channel read increment to false + channel_config_set_write_increment(&c2, false); //Set control channel write increment to false + channel_config_set_dreq(&c2, 0x3F); +// channel_config_set_chain_to(&c2, dma_tx_chan); + dma_channel_configure(dma_tx_chan2, + &c2, + &dma_hw->ch[dma_tx_chan].al3_read_addr_trig, + &a1int, + 1, + false); //Configure control channel + } + channel_config_set_read_increment(&c, true); + if(s_nbr!=0)channel_config_set_ring(&c,false,i); + } else channel_config_set_read_increment(&c, false); + } else { + if((nbr<(toarraysize*8))error("Array size"); + channel_config_set_read_increment(&c, true); + } + if(s_nbr==0) channel_config_set_chain_to(&c, dma_tx_chan2); //When this channel completes, it will trigger the channel indicated by chain_to + + + dma_channel_configure(dma_tx_chan, + &c, + &pio->txf[sm], // Destination pointer + a1int, // Source pointer + nbr, // Number of transfers + (s_nbr==0 ? false :true) // Start immediately + ); + if(s_nbr==0) dma_start_channel_mask(1u << dma_tx_chan2); + pio_sm_restart(pio, sm); + pio_sm_set_enabled(pio, sm, true); + return; + } + tp = checkstring(cmdline, (unsigned char *)"INTERRUPT"); + if(tp){ + getargs(&tp, 7, (unsigned char *)","); + if((argc & 0x01) == 0) error("Syntax"); + if(argc<5)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); +#endif + int sm=getint(argv[2],0,3); + if(*argv[4]){ + if(checkstring(argv[4],(unsigned char *)"0"))pioRXinterrupts[sm][pior]=NULL; + else pioRXinterrupts[sm][pior]=(char *)GetIntAddress(argv[4]); + } + if(argc==7){ + if(checkstring(argv[6],(unsigned char *)"0"))pioTXinterrupts[sm][pior]=NULL; + else pioTXinterrupts[sm][pior]=(char *)GetIntAddress(argv[6]); + } + piointerrupt=0; + for(int i=0;i<4;i++){ + for(int j=0 ;j 0 && g_vartbl[g_VarIndex].dims[1] == 0)) + { // integer array + if( (((long long int *)dd - g_vartbl[g_VarIndex].val.ia) + nbr) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase) ) + error("Insufficient array size"); + } else if ((g_vartbl[g_VarIndex].type & T_INT) && g_vartbl[g_VarIndex].dims[0] == 0 && nbr==1){ + // single variable + } else error("Invalid variable"); + + while(nbr--) { + *dd = pio_sm_get(pio, sm); + if(pio->fdebug & (1<<(sm + 16)))*dd=-1; + if(nbr)dd++; + } + return; + } +#ifdef rp2350 + tp = checkstring(cmdline, (unsigned char *)"WRITEFIFO"); + if(tp){ + getargs(&tp,7,(unsigned char *)","); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); + int sm=getint(argv[2],0,3); + int fifo=getint(argv[4],0,3); + pio->rxf_putget[sm][fifo]=getint(argv[6],0,0xFFFFFFFF); // jmp pin + targ=T_INT; + return; + } +#endif + tp = checkstring(cmdline, (unsigned char *)"PROGRAM LINE"); + if(tp){ + getargs(&tp,5,(unsigned char *)","); + if(argc!=5)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + int slot=getint(argv[2],0,31); + int instruction=getint(argv[4],0,0xFFFF); + pio->instr_mem[slot]=instruction; + return; + } + tp = checkstring(cmdline, (unsigned char *)"ASSEMBLE"); + if(tp){ + static int wrap_target_set=0, wrap_set=0; + getargs(&tp,3,(unsigned char *)","); + if(!argc)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); + if(PIO2==false && pior==2)error("PIO 2 not available"); + pioinuse=pior; +#ifdef rp2350 + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + unsigned int ins=0; + char *ss; + if(argc==3){ + ss=(char *)getCstring(argv[2]); + char *comment=strchr(ss,';'); + if(comment)*comment=0; + skipspace(ss); + if(*ss==0)return; + } else return; + if(!strncasecmp(ss,".PROGRAM ",9)){ + if(dirOK!=2)error("Program already started"); + sidepins=0,sideopt=0,sidepinsdir=0; + delaypossible=5; + checksideanddelay=0; + dirOK=1; + PIOlinenumber=0; + instructions = (int *)GetMemory(sizeof(int)*32); + for(int i=0;i<32;i++)instructions[i]=-1; + labelsfound = GetMemory(32 * MAXLABEL); + labelsneeded = GetMemory(32 * MAXLABEL); + PIOstart=0; + p_wrap=31; + p_wrap_target=0; + wrap_target_set=0; + wrap_set=0; + return; + } + if(dirOK!=2){ + char *p; + if(strchr(ss,':') && !fstrstr(ss,"::")){ + p=strchr(ss,':'); + skipspace(ss); + *p=0; + if(((uint32_t)p-(uint32_t)ss) > (MAXLABEL-1))error("Label too long"); + for(int j=PIOstart;j31)error("Program too large"); + if(!strncasecmp(ss,"JMP ",4)){ + int dup=0;checksideanddelay=1; + ss+=3; + dirOK=0; + skipspace(ss); + if(strncasecmp(ss,"!X",2)==0 && (ss[2]==' ' || ss[2]==',')) { + ins|=0x20; + dup=1; + ss+=2; + } + if(strncasecmp(ss,"X--",3)==0 && (ss[3]==' ' || ss[3]==',')) { + if(dup)error("Syntax"); + ins|=0x40; + dup=1; + ss+=3; + } + if(strncasecmp(ss,"!Y",2)==0 && (ss[2]==' ' || ss[2]==',')) { + if(dup)error("Syntax"); + ins|=0x60; + dup=1; + ss+=2; + } + if(strncasecmp(ss,"Y--",3)==0 && (ss[3]==' ' || ss[3]==',')) { + if(dup)error("Syntax"); + ins|=0x80; + dup=1; + ss+=3; + } + if(strncasecmp(ss,"X!=Y",4)==0 && (ss[4]==' ' || ss[4]==',')) { + if(dup)error("Syntax"); + ins|=0xA0; + dup=1; + ss+=4; + } + if(strncasecmp(ss,"PIN",3)==0 && (ss[3]==' ' || ss[3]==',')) { + if(dup)error("Syntax"); + ins|=0xC0; + dup=1; + ss+=3; + } + if(strncasecmp(ss,"!OSRE",5)==0 && (ss[5]==' ' || ss[5]==',')) { + if(dup)error("Syntax"); + ins|=0xC0; + dup=1; + ss+=5; + } + char save=0; + skipspace(ss); + if(*ss==','){ + ss++; + skipspace(ss); + } + if((*ss>='0' && *ss<='9') || *ss=='&' ){ + char *ppp=ss; + if(*ss=='&'){ + if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax"); + ppp+=2; + } + while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;} + if(*ppp){ + save=*ppp; + *ppp=','; + } + + ins|=getint((unsigned char *)ss,0,31); + ins|=0x10000; + if(*ppp==',')*ppp=save; + } else { + char *ppp=ss; + if(!isnamestart(*ppp))error("Syntax"); + while(isnamechar(*ppp)){ppp++;} + if(*ppp){ + save=*ppp; + *ppp=0; + } + strcpy(&labelsneeded[PIOlinenumber*MAXLABEL],ss); + *ppp=save; + } + } else if(!strncasecmp(ss,"WAIT ",5)){ + dirOK=0; + ss+=4; + bool PINCTRL_JMP_PIN=false; + ins=0x2000;checksideanddelay=1; + skipspace(ss); + if(!(*ss=='1' || *ss=='0'))error("Syntax"); + if(*ss=='1')ins |=0x80; + ss++; + skipspace(ss); + if(*ss==','){ + ss++; + skipspace(ss); + } + int rel=2; + if(strncasecmp(ss,"GPIO",4)==0 && (ss[4]==' ' || ss[4]==',')){ + ss+=4; + } else if(strncasecmp(ss,"PIN",3)==0 && (ss[3]==' ' || ss[3]==',')){ + ss+=3; + ins |=0b0100000; + } else if(strncasecmp(ss,"IRQ",3)==0 && (ss[3]==' ' || ss[3]==',')){ + char *pp; + ss+=3; + rel=0; + ins |=0b1000000; + if((pp=fstrstr(ss," rel")) && (pp[4]==0 || pp[4]==' ' || pp[4]==';'))rel=1; +#ifdef rp2350 + } else if(strncasecmp(ss,"JMPPIN",6)==0 && (ss[6]==' ' || ss[6]==',')){ + ss+=6; + ins |=0b1100000; + PINCTRL_JMP_PIN=true; +#endif + } else error("syntax"); + skipspace(ss); + if(*ss==','){ + ss++; + skipspace(ss); + } + char save=0; + char *ppp=ss; + if((*ss>='0' && *ss<='9') || *ss=='&' ){ + char *ppp=ss; + if(*ss=='&'){ + if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax"); + ppp+=2; + } + while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;} + if(*ppp){ + save=*ppp; + *ppp=','; + } + } else error("Syntax"); + int bits=getint((unsigned char *)ss,0,rel==2? 31 : PINCTRL_JMP_PIN ? 3: 7); + if(*ppp==',')*ppp=save; + if(rel==1) bits |=0x10; + ins |=bits; + } else if(!strncasecmp(ss,"IN ",3)){ + dirOK=0; + ss+=3; + ins=0x4000;checksideanddelay=1; + skipspace(ss); + if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==' ' || ss[4]==',') ){ + ss+=4; + } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b00100000; + } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b01000000; + } else if(strncasecmp(ss,"NULL",4)==0 && (ss[4]==' ' || ss[4]==',') ){ + ss+=4; + ins|=0b01100000; + } else if(strncasecmp(ss,"ISR",3)==0 && (ss[3]==' ' || ss[3]==',') ){ + ss+=3; + ins|=0b11000000; + } else if(strncasecmp(ss,"OSR",3)==0 && (ss[3]==' ' || ss[3]==',') ){ + ss+=3; + ins|=0b11100000; + } else error("Syntax"); + skipspace(ss); + if(*ss!=',')error("Syntax"); + ss++; + char save=0; + skipspace(ss); + char *ppp=ss; + if((*ss>='0' && *ss<='9') || *ss=='&' ){ + char *ppp=ss; + if(*ss=='&'){ + if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax"); + ppp+=2; + } + while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;} + if(*ppp){ + save=*ppp; + *ppp=','; + } + } else error("Syntax"); + int bits=getint((unsigned char *)ss,1,32); + if(bits==32)bits=0; + ins|=bits; + if(*ppp==',')*ppp=save; + } else if(!strncasecmp(ss,"OUT ",4)){ + dirOK=0; + ss+=3; + ins=0x6000;checksideanddelay=1; + skipspace(ss); + if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==' ' || ss[4]==',') ){ + ss+=4; + } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b00100000; + } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b01000000; + } else if(strncasecmp(ss,"NULL",4)==0 && (ss[4]==' ' || ss[4]==',') ){ + ss+=4; + ins|=0b01100000; + } else if(strncasecmp(ss,"PINDIRS",7)==0 && (ss[7]==' ' || ss[7]==',') ){ + ss+=7; + ins|=0b10000000; + } else if(strncasecmp(ss,"PC",2)==0 && (ss[2]==' ' || ss[2]==',') ){ + ss+=2; + ins|=0b10100000; + } else if(strncasecmp(ss,"ISR",3)==0 && (ss[3]==' ' || ss[3]==',') ){ + ss+=3; + ins|=0b11000000; + } else if(strncasecmp(ss,"EXEC",4)==0 && (ss[4]==' ' || ss[4]==',') ){ + ss+=4; + ins|=0b11100000; + } else error("Syntax"); + skipspace(ss); + if(*ss!=',')error("Syntax"); + ss++; + char save=0; + skipspace(ss); + char *ppp=ss; + if((*ss>='0' && *ss<='9') || *ss=='&' ){ + char *ppp=ss; + if(*ss=='&'){ + if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax"); + ppp+=2; + } + while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;} + if(*ppp){ + save=*ppp; + *ppp=','; + } + } else error("Syntax"); + int bits=getint((unsigned char *)ss,1,32); + if(bits==32)bits=0; + ins|=bits; + if(*ppp==',')*ppp=save; + } else if(!strncasecmp(ss,"PUSH",4) && (ss[4]==0 || ss[4]==' ')){ + dirOK=0; + ss+=4; + ins=0x8000;checksideanddelay=1; + ins |= checkblock(ss); + } else if(!strncasecmp(ss,"PULL ",4) && (ss[4]==0 || ss[4]==' ')){ + dirOK=0; + ss+=4; + ins=0x8080;checksideanddelay=1; + ins |= checkblock(ss); + } else if(!strncasecmp(ss,"MOV ",4)){ + dirOK=0; + ss+=3; + ins=0xA000;checksideanddelay=1; + skipspace(ss); + if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==' ' || ss[4]==',') ){ + ss+=4; + } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b00100000; + } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b01000000; +#ifdef rp2350 + } else if(strncasecmp(ss,"PINDIRS",7)==0 && (ss[7]==' ' || ss[7]==',') ){ + ss+=7; + ins|=0b01100000; +#endif + } else if(strncasecmp(ss,"EXEC",4)==0 && (ss[4]==' ' || ss[4]==',') ){ + ss+=4; + ins|=0b10000000; + } else if(strncasecmp(ss,"PC",2)==0 && (ss[2]==' ' || ss[2]==',') ){ + ss+=2; + ins|=0b10100000; + } else if(strncasecmp(ss,"ISR",3)==0 && (ss[3]==' ' || ss[3]==',') ){ + ss+=3; + ins|=0b11000000; + } else if(strncasecmp(ss,"OSR",3)==0 && (ss[3]==' ' || ss[3]==',') ){ + ss+=3; + ins|=0b11100000; +#ifdef rp2350 + } else if(strncasecmp(ss,"RXFIFO[",7)==0){ //this is really a push instruction + ss+=7; + ins=0x8010; + if(strncasecmp(ss,"0]",2)==0 && (ss[2]==' ' || ss[2]==',')){ + ins|=0b1000; + } else if(strncasecmp(ss,"1]",2)==0 && (ss[2]==' ' || ss[2]==',')){ + ins|=0b1001; + } else if(strncasecmp(ss,"2]",2)==0 && (ss[2]==' ' || ss[2]==',')){ + ins|=0b1010; + } else if(strncasecmp(ss,"3]",2)==0 && (ss[2]==' ' || ss[2]==',')){ + ins|=0b1011; + } else if(!(strncasecmp(ss,"Y]",2)==0 && (ss[2]==' ' || ss[2]==',')))error("Syntax"); + ss+=2; +#endif + } else error("Syntax"); + skipspace(ss); + if(*ss!=',')error("Syntax"); + ss++; + skipspace(ss); + if(*ss=='~' || *ss=='!'){ + ins |=8; + ss++; + } + if(*ss==':' && ss[1]==':'){ + ins |=0x10; + ss+=2; + } + skipspace(ss); + if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==0 || ss[4]==' ' || ss[4]==',') ){ + ss+=4; + if(!(ins&0x2000))error("Syntax"); + } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==0 || ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b001; + if(!(ins&0x2000))error("Syntax"); + } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==0 || ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b010; + if(!(ins&0x2000))error("Syntax"); + } else if(strncasecmp(ss,"NULL",4)==0 && (ss[4]==0 || ss[4]==' ' || ss[4]==',') ){ + ss+=4; + ins|=0b011; + if(!(ins&0x2000))error("Syntax"); + } else if(strncasecmp(ss,"STATUS",6)==0 && (ss[6]==0 || ss[6]==' ' || ss[6]==',') ){ + ss+=6; + ins|=0b101; + if(!(ins&0x2000))error("Syntax"); + } else if(strncasecmp(ss,"ISR",3)==0 && (ss[3]==0 || ss[3]==' ' || ss[3]==',') ){ + ss+=3; + if(ins&0x2000)ins|=0b110; + } else if(strncasecmp(ss,"OSR",3)==0 && (ss[3]==0 || ss[3]==' ' || ss[3]==',') ){ + ss+=3; + ins|=0b111; + if(!(ins&0x2000))error("Syntax"); +#ifdef rp2350 + } else if(strncasecmp(ss,"RXFIFO",6)==0 && (ss[6]=='[') && ins==(0xA000 | 0b11100000)){ //this is really a pull instruction + ss+=7; + ins=0x8090; + if(strncasecmp(ss,"0]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')){ + ins|=0b1000; + } else if(strncasecmp(ss,"1]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')){ + ins|=0b1001; + } else if(strncasecmp(ss,"2]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')){ + ins|=0b1010; + } else if(strncasecmp(ss,"3]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')){ + ins|=0b1011; + } else if(!(strncasecmp(ss,"Y]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')))error("Syntax"); + ss+=2; +#endif + } else error("Syntax"); + + } else if(!strncasecmp(ss,"NOP",3) && (ss[3]==0 || ss[3]==' ')){ + dirOK=0; + ss+=3; + ins=0xA000; + ins |=0b01000010;checksideanddelay=1; + } else if(!strncasecmp(ss,"IRQ SET ",8)){ + dirOK=0; + ss+=7; + ins=0xC000;checksideanddelay=1; + ins |= getirqnum(ss); + } else if(!strncasecmp(ss,"IRQ WAIT ",9)){ + dirOK=0; + ss+=8; + ins=0xC020;checksideanddelay=1; + ins |= getirqnum(ss); + } else if(!strncasecmp(ss,"IRQ CLEAR ",10)){ + dirOK=0; + ss+=9; + ins=0xC040;checksideanddelay=1; + ins |= getirqnum(ss); + } else if(!strncasecmp(ss,"IRQ NOWAIT ",11)){ + dirOK=0; + ss+=10; + ins=0xC000;checksideanddelay=1; + ins |= getirqnum(ss); + } else if(!strncasecmp(ss,"IRQ ",4)){ + dirOK=0; + ss+=3; + ins=0xC000;checksideanddelay=1; + ins |= getirqnum(ss); + } else if(!strncasecmp(ss,"SET ",4)){ + dirOK=0; + ss+=3; + ins=0xE000;checksideanddelay=1; + skipspace(ss); + if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==' ' || ss[4]==',') ){ + ss+=4; + } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b100000; + } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==' ' || ss[1]==',') ){ + ss++; + ins|=0b1000000; + } else if(strncasecmp(ss,"PINDIRS",7)==0 && (ss[7]==' ' || ss[7]==',') ){ + ss+=7; + ins|=0b10000000; + } else error("Syntax"); + skipspace(ss); + if(*ss!=',')error("Syntax"); + ss++; + char save=0; + skipspace(ss); + char *ppp=ss; + if((*ss>='0' && *ss<='9') || *ss=='&' ){ + char *ppp=ss; + if(*ss=='&'){ + if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax"); + ppp+=2; + } + while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;} + if(*ppp){ + save=*ppp; + *ppp=','; + } + } else error("Syntax"); + ins|=getint((unsigned char *)ss,0,31); + if(*ppp==',')*ppp=save; + } else error("PIO instruction not found"); + if(checksideanddelay==1)ins|=calcsideanddelay(ss, sidepins, delaypossible); + instructions[PIOlinenumber]=ins; + PIOlinenumber++; + return; + } else { + if(!strncasecmp(ss,".LINE ",5)){ + if(!dirOK)error("Directive must appear before instructions"); + ss+=5; + skipspace(ss); + if(!strncasecmp(ss,"NEXT",4) && (ss[4]==0 || ss[4]==' '))PIOlinenumber=nextline[pioinuse]; + else PIOlinenumber=getint((unsigned char *)ss,0,31); + PIOstart=PIOlinenumber; + return; + } else if(!strncasecmp(ss,".WRAP TARGET",12)){ + if(wrap_target_set)error("Repeat directive"); + p_wrap_target=PIOlinenumber; + wrap_target_set=1; + return; + //sort out the jmps + } else if(!strncasecmp(ss,".WRAP",4)){ +// if(!wrap_target_set)error("Wrap target not set"); + if(wrap_set)error("Repeat directive"); + p_wrap=PIOlinenumber-1; + if(PIOlinenumber==-1)PIOlinenumber=31; + return; + //sort out the jmps + } else if(!strncasecmp(ss,".END PROGRAM",12)){ //sort out the jmps + if(dirOK==2)error("Program not started"); + int totallines=0; + dirOK=2; + for(int i=PIOstart; i<32;i++){ + if(instructions[i]!=-1){ + totallines++; + } + } + for(int i=PIOstart; iinstr_mem[i]=instructions[i]; + if(!strncasecmp(ss,"LIST",4) && (ss[4]==0 || ss[4]==' ')){ + char c[10]={0}; + PInt(i); + MMPrintString(": "); + IntToStr(c,instructions[i]+0x10000,16); + MMPrintString(&c[1]); + PRet(); + } + } + nextline[pioinuse]=PIOstart+totallines; + nextline[3]=pioinuse; + FreeMemorySafe((void **)&instructions); + FreeMemorySafe((void **)&labelsneeded); + FreeMemorySafe((void **)&labelsfound); + pioinuse=99; + return; + } else if(!strncasecmp(ss,".SIDE_SET ",10)){ + if(!dirOK)error("Directive must appear before instructions"); + ss+=10; + sidepins=*ss-'0'; + if(sidepins<1 || sidepins>5)error("Invalid side_set pin count"); + ss++; + delaypossible=5-sidepins; + ins=0xE000; + skipspace(ss); + if(!strncasecmp(ss,"opt",3) && (ss[3]==0 || ss[3]==' ')){ + sideopt=1; + delaypossible=4-sidepins; + if(sideopt && sidepins==5)error("Only 4 side set pins allowed with opt set"); + ss+=3; + skipspace(ss); + } + if(!strncasecmp(ss,"pindirs",7) && (ss[7]==0 || ss[7]==' '))sidepinsdir=1; + return; + } else error("PIO directive not found"); + } + } else error(".program must be first statement"); + } + tp = checkstring(cmdline, (unsigned char *)"CLEAR"); + if(tp){ + getargs(&tp,1,(unsigned char *)","); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + for(int sm=0;sm<4;sm++){ + hw_clear_bits(&pio->ctrl, 1 << (PIO_CTRL_SM_ENABLE_LSB + sm)); + pio->sm[sm].pinctrl=(5<<26); + pio->sm[sm].execctrl=(0x1f<<12); + pio->sm[sm].shiftctrl=(3<<18); + pio_sm_clear_fifos(pio,sm); + } + pio_clear_instruction_memory(pio); + return; + } + tp = checkstring(cmdline, (unsigned char *)"MAKE RING BUFFER"); + if(tp){ + getargs(&tp,3,(unsigned char *)","); + if(argc<3)error("Syntax"); + int size=getinteger(argv[2]); + if(!(size==256 || size==512 || size==1024 || size== 2048 || size==4096 || size==8192 || size==16384 || size==32768))error("Not power of 2"); + findvar(argv[0], V_FIND | V_NOFIND_ERR); + if ((g_vartbl[g_VarIndex].type & T_INT) && g_vartbl[g_VarIndex].dims[0] == 0 && g_vartbl[g_VarIndex].level==0){ + g_vartbl[g_VarIndex].val.s =(unsigned char *)GetAlignedMemory(size); + g_vartbl[g_VarIndex].size=255; + g_vartbl[g_VarIndex].dims[0] = size/8-1+g_OptionBase; + } else error("Invalid variable"); + return; + } + tp = checkstring(cmdline, (unsigned char *)"PROGRAM"); + if(tp){ + struct pio_program program; + getargs(&tp,3,(unsigned char *)","); + if(argc!=3)error("Syntax"); +// void *prt1; + program.length=32; + program.origin=0; + int64_t *a1int=NULL; + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + int toarraysize=parseintegerarray(argv[2],&a1int,2,1,dims, true); + if(toarraysize!=8)error("Array size"); + program.instructions = (const uint16_t *)a1int; + for(int sm=0;sm<4;sm++)hw_clear_bits(&pio->ctrl, 1 << (PIO_CTRL_SM_ENABLE_LSB + sm)); + pio_clear_instruction_memory(pio); + pio_add_program(pio, &program); + return; + } + tp = checkstring(cmdline, (unsigned char *)"START"); + if(tp){ + getargs(&tp,3,(unsigned char *)","); + if(argc!=3)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + int sm=getint(argv[2],0,3); + pio_sm_clear_fifos(pio,sm); + pio_sm_restart(pio, sm); + pio_sm_set_enabled(pio, sm, true); + return; + } + tp = checkstring(cmdline, (unsigned char *)"STOP"); + if(tp){ + getargs(&tp,3,(unsigned char *)","); + if(argc!=3)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + int sm=getint(argv[2],0,3); + pio_sm_set_enabled(pio, sm, false); + return; + } + tp = checkstring(cmdline, (unsigned char *)"INIT MACHINE"); + if(tp){ + int start=0; + bool setout=false,sideout=false,outout=false; + getargs(&tp,19,(unsigned char *)","); + if(argc<5)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); +#endif + int sm=getint(argv[2],0,3); + float clock=getnumber(argv[4]); + if(clock CLKMAX)error("Clock must be in range % to %",CLKMIN,CLKMAX); + int pinctrl=0, execctrl=0,shiftctrl=0; + if(argc>5 && *argv[6])pinctrl = getint(argv[6],0,0xFFFFFFFF); + if(argc>7 && *argv[8])execctrl= getint(argv[8],0,0xFFFFFFFF); + if(argc>9 && *argv[10])shiftctrl= getint(argv[10],0,0xFFFFFFFF); + if(argc>11 && *argv[12]) start=getint(argv[12],0,31); + if(argc>13 && *argv[14]) sideout=getint(argv[14],0,1); + if(argc>15 && *argv[16]) setout=getint(argv[16],0,1); + if(argc>171 && *argv[18]) outout=getint(argv[18],0,1); + pio_init(pior, sm, pinctrl, execctrl, shiftctrl, start, clock, sideout, setout, outout); + return; + } + #ifdef rp2350 + tp = checkstring(cmdline, (unsigned char *)"SET BASE"); + if(tp){ + getargs(&tp,3,(unsigned char *)","); + if(argc<3)error("Syntax"); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + int i=getinteger(argv[2]); + if(!(i==0 || i==16))error("Valid base values are 0 and 16"); + if(rp2350a && i==16)error("Invalid for 60-pin chip"); + pio_set_gpio_base(pio,i); + return; + } +#endif + tp = checkstring(cmdline, (unsigned char *)"CONFIGURE"); +/*PIO Configure pio, sm, clock, startaddress, +sidesetbase, sidesetno, sidesetout, setbase, setno, setout, outbase, outno, outout, inbase, +jmppin, wraptarget, wrap, sideenable,sidepindir, +pushthreshold, pullthreshold, autopush, autopull, inshiftdir, outshiftdir, joinrcfifo, jointxfifo, joinrxfifoget, joinrxfifoput +*/ + if(tp){ + int startaddress=0, base=0; + int sidesetbase=0, sidesetno=0, sidesetout=0, setbase=0, setno=0, setout=0, outbase=0, outno=0, outout=0, inbase=0; + int jmppin=-1, wraptarget=0, wrap=31, sideenable=0, sidepindir=0; + int pushthreshold=0, pullthreshold=0, autopush=0, autopull=0, inshiftdir=1, outshiftdir=1, joinrxfifo=0, jointxfifo=0; +#ifdef rp2350 + int joinrxfifoget=0, joinrxfifoput=0; + getargs(&tp,57,(unsigned char *)","); +#else + getargs(&tp,53,(unsigned char *)","); +#endif + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); + base=pio_get_gpio_base(pio); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + if(base==16){ + sidesetbase=16; + setbase=16; + outbase=16; + inbase=16; + } + int sm=getint(argv[2],0,3); + float clock=getnumber(argv[4]); + if(clock CLKMAX)error("Clock must be in range % to %",CLKMIN,CLKMAX); + if(argc>5 && *argv[6]) startaddress=getint(argv[6],0,31); + if(argc>7 && *argv[8]) sidesetbase=getGPpin(argv[8],pior, base); + if(argc>9 && *argv[10]) sidesetno=getint(argv[10],0,5); + if(argc>11 && *argv[12]) sidesetout=getint(argv[12],0,1); + if(argc>13 && *argv[14]) setbase=getGPpin(argv[14],pior, base); + if(argc>15 && *argv[16]) setno=getint(argv[16],0,5); + if(argc>17 && *argv[18]) setout=getint(argv[18],0,1); + if(argc>19 && *argv[20]) outbase=getGPpin(argv[20],pior, base); + if(argc>21 && *argv[22]) outno=getint(argv[22],0,5); + if(argc>23 && *argv[24]) outout=getint(argv[24],0,1); + if(argc>25 && *argv[26]) inbase=getGPpin(argv[26],pior, base); + if(argc>27 && *argv[28]) jmppin=getGPpin(argv[28],pior, base); + if(argc>29 && *argv[30]) wraptarget=getint(argv[30],0,31); + if(argc>31 && *argv[32]) wrap=getint(argv[32],0,31); + if(argc>33 && *argv[34]) sideenable=getint(argv[34],0,1); + if(sideenable+sidesetno>5)error("Max 4 sideset pins with option set"); + if(argc>35 && *argv[36]) sidepindir=getint(argv[36],0,1); // push threshold + if(argc>37 && *argv[38]) pushthreshold=getint(argv[38],0,31); // push threshold + if(argc>39 && *argv[40]) pullthreshold=getint(argv[40],0,31); // pull threshold + if(argc>41 && *argv[42]) autopush=getint(argv[42],0,1); // autopush + if(argc>43 && *argv[44]) autopull=getint(argv[44],0,1); // autopull + if(argc>45 && *argv[46]) inshiftdir=getint(argv[46],0,1); // IN_SHIFTDIR + if(argc>47 && *argv[48]) outshiftdir=getint(argv[48],0,1); // OUT_SHIFTDIR + if(argc>49 && *argv[50]) joinrxfifo=getint(argv[50],0,1); // FJOIN_RX + if(argc>51 && *argv[52]) jointxfifo=getint(argv[52],0,1); // FJOIN_TX + if(joinrxfifo && jointxfifo)error("Attempt to join both RX and TX FIFOs"); +#ifdef rp2350 + if(argc>53 && *argv[54]) joinrxfifoget=getint(argv[54],0,1); // FJOIN_RX_GET + if(argc>55 && *argv[56]) joinrxfifoput=getint(argv[56],0,1); // FJOIN_RX_PUT +#endif + #ifdef rp2350 + #ifdef PICOMITEWEB + for(int i = 1; i < (NBRPINS) ; i++) { + #else + for(int i = 1; i < (rp2350a ? 44:NBRPINS) ; i++) { + #endif + #else + for(int i = 1; i < (NBRPINS) ; i++) { + #endif + if(CheckPin(i, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED)) { // don't reset invalid or boot reserved pins + gpio_set_input_enabled(PinDef[i].GPno, true); + } + } + pio_set_gpio_base(pio,base); + pio_sm_config cfg = pio_get_default_sm_config(); + sm_config_set_in_pin_base(&cfg, inbase); + if(jointxfifo)sm_config_set_fifo_join(&cfg, PIO_FIFO_JOIN_TX); + if(joinrxfifo)sm_config_set_fifo_join(&cfg, PIO_FIFO_JOIN_RX); + if(outno)sm_config_set_out_pins(&cfg, outbase, outno); + if(sidesetno){ + sm_config_set_sideset_pins(&cfg,sidesetbase); + sm_config_set_sideset(&cfg,sidesetno+sideenable,sideenable,sidepindir); + } + if(setno){ + sm_config_set_set_pin_base(&cfg, setbase); + sm_config_set_set_pin_count(&cfg, setno); + } + if(jmppin>=0)sm_config_set_jmp_pin(&cfg, jmppin); + sm_config_set_wrap(&cfg, wraptarget, wrap); + sm_config_set_in_shift(&cfg,inshiftdir,autopush,pushthreshold); + sm_config_set_out_shift(&cfg,outshiftdir, autopull,pullthreshold); +#ifdef rp2350 + if(joinrxfifoget)cfg.shiftctrl|=(1<<14); + if(joinrxfifoput)cfg.shiftctrl|=(1<<15); +#endif + sm_config_set_clkdiv(&cfg, (Option.CPU_Speed*1000.0f)/clock); + pio_sm_set_config(pio, sm, &cfg); + pio_sm_init(pio, sm, startaddress, &cfg); + if(sidesetout && sidesetno)pio_sm_set_consecutive_pindirs(pio, sm, sidesetbase, sidesetno, true); + if(setout && setno)pio_sm_set_consecutive_pindirs(pio, sm, setbase, setno, true); + if(outout && outno)pio_sm_set_consecutive_pindirs(pio, sm, outbase, outno, true); + return; +} + error("Syntax"); +} +void fun_pio(void){ + unsigned char *tp; + tp = checkstring(ep, (unsigned char *)"PINCTRL"); + if(tp){ + int64_t myret=0; + getargs(&tp,13,(unsigned char *)","); + if(argc<3)error("Syntax"); + myret=(getint(argv[0],0,5)<<29); // no of side set pins + if(argc>1 && *argv[2])myret|=(getint(argv[2],0,5)<<26); // no of set pins + if(argc>3 && *argv[4])myret|=(getint(argv[4],0,31)<<20); // no of OUT pins + if(argc>5 && *argv[6]){ + if(!(toupper((char)*argv[6])=='G'))error("Syntax"); + argv[6]++; + if(!(toupper((char)*argv[6])=='P'))error("Syntax"); + argv[6]++; + myret|=(getint(argv[6],0,31)<<15); // IN base + } + if(argc>7 && *argv[8]){ + if(!(toupper((char)*argv[8])=='G'))error("Syntax"); + argv[8]++; + if(!(toupper((char)*argv[8])=='P'))error("Syntax"); + argv[8]++; + myret|=(getint(argv[8],0,31)<<10); // SIDE SET base + } + if(argc>9 && *argv[10]){ + if(!(toupper((char)*argv[10])=='G'))error("Syntax"); + argv[10]++; + if(!(toupper((char)*argv[10])=='P'))error("Syntax"); + argv[10]++; + myret|=(getint(argv[10],0,31)<<5); // SET base + } + if(argc==13){ + if(!(toupper((char)*argv[12])=='G'))error("Syntax"); + argv[12]++; + if(!(toupper((char)*argv[12])=='P'))error("Syntax"); + argv[12]++; + myret|=getint(argv[12],0,31); //OUT base + } + iret=myret; + targ=T_INT; + return; + } + tp = checkstring(ep, (unsigned char *)"EXECCTRL"); + if(tp){ + int64_t myret=0; + getargs(&tp,9,(unsigned char *)","); + if(!(argc==5 || argc==7 || argc==9))error("Syntax"); + if(!(toupper((char)*argv[0])=='G'))error("Syntax"); + argv[0]++; + if(!(toupper((char)*argv[0])=='P'))error("Syntax"); + argv[0]++; + myret=(getint(argv[0],0,31)<<24); // jmp pin + myret |= pio_sm_calc_wrap(getint(argv[2],0,31), getint(argv[4],0,31)); + if(argc>=7 && *argv[6])myret|=(getint(argv[6],0,1)<<29); //SIDE_PINDIR + if(argc==9)myret|=(getint(argv[8],0,1)<<30); // SIDE_EN + iret=myret; + targ=T_INT; + return; + } + tp = checkstring(ep, (unsigned char *)".WRAP TARGET"); + if(tp){ + iret=p_wrap_target; + targ=T_INT; + return; + } + tp = checkstring(ep, (unsigned char *)".WRAP"); + if(tp){ + iret=p_wrap; + targ=T_INT; + return; + } + tp = checkstring(ep, (unsigned char *)"SHIFTCTRL"); + if(tp){ +#ifdef rp2350 + getargs(&tp,19,(unsigned char *)","); +#else + getargs(&tp,15,(unsigned char *)","); +#endif + if(argc<1)error("Syntax"); + int64_t myret=0; + myret=(getint(argv[0],0,31)<<20); // push threshold + myret|=(getint(argv[2],0,31)<<25); // pull threshold + if(argc>3 && *argv[4])myret|=(getint(argv[4],0,1)<<16); // autopush + if(argc>5 && *argv[6])myret|=(getint(argv[6],0,1)<<17); // autopull + if(argc>7 && *argv[8])myret|=(getint(argv[8],0,1)<<18); // IN_SHIFTDIR + if(argc>9 && *argv[10])myret|=(getint(argv[10],0,1)<<19); // OUT_SHIFTDIR + if(argc>11 && *argv[12])myret|=(getint(argv[12],0,1)<<30); // FJOIN_RX + if(argc>13 && *argv[14])myret|=(getint(argv[14],0,1)<<31); // FJOIN_TX +#ifdef rp2350 + if(argc>15 && *argv[16])myret|=(getint(argv[16],0,1)<<14); // FJOIN_RX_GET + if(argc>17 && *argv[18])myret|=(getint(argv[18],0,1)<<15); // FJOIN_RX_PUT +#endif + iret=myret; + targ=T_INT; + return; + } + tp = checkstring(ep, (unsigned char *)"FSTAT"); + if(tp){ + getargs(&tp,1,(unsigned char *)","); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + iret=pio->fstat; // jmp pin + targ=T_INT; + return; + } +#ifdef rp2350 + tp = checkstring(ep, (unsigned char *)"READFIFO"); + if(tp){ + getargs(&tp,5,(unsigned char *)","); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); + int sm=getint(argv[2],0,3); + int fifo=getint(argv[4],0,3); + iret=pio->rxf_putget[sm][fifo]; // jmp pin + targ=T_INT; + return; + } +#endif + tp = checkstring(ep, (unsigned char *)"FDEBUG"); + if(tp){ + getargs(&tp,1,(unsigned char *)","); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + iret=pio->fdebug; // jmp pin + targ=T_INT; + return; + } + tp = checkstring(ep, (unsigned char *)"NEXT LINE"); + if(tp){ + getargs(&tp,1,(unsigned char *)","); + int pior=nextline[3]; + if(argc)pior=getint(argv[0],0,PIOMAX-1); + if(pior==99)error("No current PIO in use"); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); +#endif + iret=nextline[pior]; + targ=T_INT; + return; +} + tp = checkstring(ep, (unsigned char *)"FLEVEL"); + if(tp){ + getargs(&tp,5,(unsigned char *)","); + int pior=getint(argv[0],0,PIOMAX-1); + if(PIO0==false && pior==0)error("PIO 0 not available"); + if(PIO1==false && pior==1)error("PIO 1 not available"); +#ifdef rp2350 + if(PIO2==false && pior==2)error("PIO 2 not available"); + PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2)); +#else + PIO pio = (pior==0 ? pio0: pio1); +#endif + if(argc==1)iret=pio->flevel; // jmp pin + else { + if(argc!=5)error("Syntax"); + int sm=getint(argv[2],0,3)*8; + if(checkstring(argv[4],(unsigned char *)"TX"))iret=((pio->flevel)>>sm) & 0xf; + else if(checkstring(argv[4],(unsigned char *)"RX"))iret=((pio->flevel)>>(sm+4)) & 0xf; + else error("Syntax"); + } + targ=T_INT; + return; + } + tp = checkstring(ep, (unsigned char *)"DMA RX POINTER"); + if(tp){ + iret=dma_hw->ch[dma_rx_chan].write_addr; + targ=T_INT; + return; + } + tp = checkstring(ep, (unsigned char *)"DMA TX POINTER"); + if(tp){ + iret=dma_hw->ch[dma_tx_chan].read_addr; + targ=T_INT; + return; + } + error("Syntax"); + + +} +// mode determines functionality +// bit 0 sets wheter to add a space after the text +// bit 1 sets whether to omit the missing program check +// bit 2 sets whether to add a colon after the text +// bit 3 enables the diagnostic print +void call_pio(char * instruction, uint8_t mode){ + if((instructions==NULL || pioinuse==99) && !(mode & 2))error("Missing Program statement"); + skipspace(cmdline); + char *buff=GetTempMemory(STRINGSIZE); + sprintf(buff,"ASSEMBLE %d, \"",pioinuse); + strcat(buff,instruction); + if(mode & 1)strcat(buff," "); + strcat(buff,(char *)cmdline); + char *p=buff; + while(*p){ //strip comments and loose spurrious tokens + if(*p==GetTokenValue((unsigned char *)"-"))*p=(unsigned char)'-'; + if(*p==GetTokenValue((unsigned char *)"="))*p=(unsigned char)'='; + if(*p=='\'' || *p==';'){ + *p=0; + break; + } + p++; + } + // strip trailing spaces + p=buff+strlen(buff)-1; + while(*p==' ')*p--=0; + if(mode & 4)strcat(buff,":"); + strcat(buff,"\""); + cmdline=((unsigned char *)buff); + if(mode & 8){MMPrintString((char *)cmdline);PRet();} + cmd_pio(); +} +void cmd_program(void){ + call_pio(".program",3); +} +void cmd_wrap(void){ + call_pio(".wrap",0); +} +void cmd_wraptarget(void){ + call_pio(".wrap target",0); +} +void cmd_PIOline(void){ + call_pio(".line",1); +} +void cmd_sideset(void){ + call_pio(".side_set",1); +} +void cmd_endprogram(void){ + call_pio(".end program",strlen((char *)cmdline) ? 1 : 0); +} +void cmd_jmp(void){ + call_pio("jmp",1); +} +void cmd_wait(void){ + call_pio("wait",1); +} +void cmd_in(void){ + call_pio("in",1); +} +void cmd_out(void){ + call_pio("out",strlen((char *)cmdline) ? 1 : 0); +} +void cmd_push(void){ + call_pio("push",strlen((char *)cmdline) ? 1 : 0); +} +void cmd_pull(void){ + call_pio("pull",1); +} +void cmd_mov(void){ + call_pio("mov",1); +} +void cmd_nop(void){ + call_pio("nop",strlen((char *)cmdline) ? 1 : 0); +} +void cmd_irqset(void){ + call_pio("irq set",1); +} +void cmd_irqwait(void){ + call_pio("irq wait",1); +} +void cmd_irqclear(void){ + call_pio("irq clear",1); +} +void cmd_irqnowait(void){ + call_pio("irq mov",1); +} +void cmd_irq(void){ + call_pio("irq",1); +} +void cmd_set(void){ + call_pio("set",1); +} +void cmd_label(void){ + call_pio("",4); +} +/* + * @cond + * The following section will be excluded from the documentation. + */ + +#ifdef PICOMITEWEB +char *scan_dest=NULL; +volatile char *scan_dups=NULL; +volatile int scan_size; +static int scan_result(void *env, const cyw43_ev_scan_result_t *result) { + if (result) { + Timer4=5000; + char buff[STRINGSIZE]={0}; +// int found=0; + if(scan_dups==NULL)return 0; + for(int i=0;issid,(char *)&scan_dups[32*i])==0)return 0; + } + for(int i=0;i<100;i++){ + if(scan_dups[32*i]==0){ + strcpy((char *)&scan_dups[32*i],(char *)result->ssid); + scan_dups[32*100]++; + break; + } + } + sprintf(buff,"ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %u\r\n", + result->ssid, result->rssi, result->channel, + result->bssid[0], result->bssid[1], result->bssid[2], result->bssid[3], result->bssid[4], result->bssid[5], + result->auth_mode); + if(scan_dest!=NULL){ + if(strlen(((char *)&scan_dest[8]) + strlen(buff)) > scan_size){ + FreeMemorySafe((void **)&scan_dups); + scan_dest=NULL; + error("Array too small"); + } + if(scan_dest[8]==0)strcpy(&scan_dest[8],buff); + else strcat(&scan_dest[8],buff); + } else MMPrintString(buff); + } + return 0; +} +/* @endcond */ +void cmd_web(void){ + unsigned char *tp; + tp=checkstring(cmdline, (unsigned char *)"CONNECT"); + if(tp){ + if(*tp){ + setwifi(tp); + WebConnect(); + } else { + if(cyw43_wifi_link_status(&cyw43_state,CYW43_ITF_STA)<0){ + WebConnect(); + } + } + return; + } + tp=checkstring(cmdline, (unsigned char *)"SCAN"); + if(tp){ + void *ptr1 = NULL; + if(*tp){ + ptr1 = findvar(tp, V_FIND | V_EMPTY_OK); + if(g_vartbl[g_VarIndex].type & T_INT) { + if(g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable"); + if(g_vartbl[g_VarIndex].dims[0] <= 0) { // Not an array + error("Argument 1 must be integer array"); + } + scan_size=(g_vartbl[g_VarIndex].dims[0]-g_OptionBase)*8; + scan_dest = (char *)ptr1; + scan_dest[8]=0; + } else error("Argument 1 must be integer array"); + + } + scan_dups=GetMemory(32*100+1); + cyw43_wifi_scan_options_t scan_options = {0}; + int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result); + if (err == 0) { + MMPrintString("\nPerforming wifi scan\r\n"); + } else { + char buff[STRINGSIZE]={0}; + sprintf(buff,"Failed to start scan: %d\r\n", err); + MMPrintString(buff); + } + Timer4=500; + while (Timer4)if(startupcomplete)ProcessWeb(0); + if(scan_dest){ + uint64_t *p=(uint64_t *)scan_dest; + *p=strlen(&scan_dest[8]); + } + scan_dest=NULL; + FreeMemorySafe((void **)&scan_dups); + return; + } + if(!(WIFIconnected && cyw43_tcpip_link_status(&cyw43_state,CYW43_ITF_STA)==CYW43_LINK_UP))error("WIFI not connected"); + tp=checkstring(cmdline, (unsigned char *)"NTP"); + if(tp){ + cmd_ntp(tp); + return; + } + tp=checkstring(cmdline, (unsigned char *)"UDP"); + if(tp){ + cmd_udp(tp); + return; + } + if(cmd_mqtt())return; + if(cmd_tcpclient())return; + if(cmd_tcpserver())return; +// if(cmd_tls())return; + error("Syntax"); +} + +void fun_json(void){ + char *json_string=NULL; + const cJSON *root = NULL; + void *ptr1 = NULL; + char *p; + sret=GetTempMemory(STRINGSIZE); + int64_t *dest=NULL; + MMFLOAT tempd; + int i,j,k,mode,index; + char field[32],num[6]; + getargs(&ep, 3, (unsigned char *)","); + char *a=GetTempMemory(STRINGSIZE); + ptr1 = findvar(argv[0], V_FIND | V_EMPTY_OK); + if(g_vartbl[g_VarIndex].type & T_INT) { + if(g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable"); + if(g_vartbl[g_VarIndex].dims[0] <= 0) { // Not an array + error("Argument 1 must be integer array"); + } + dest = (long long int *)ptr1; + json_string=(char *)&dest[1]; + } else error("Argument 1 must be integer array"); + cJSON_InitHooks(NULL); + cJSON * parse = cJSON_Parse(json_string); + if(parse==NULL)error("Invalid JSON data"); + root=parse; + p=(char *)getCstring((unsigned char *)argv[2]); + int len = strlen(p); + memset(field,0,32); + memset(num,0,6); + i=0;j=0;k=0;mode=0; + while(ivaluedouble; + + if((MMFLOAT)((int64_t)tempd)==tempd) IntToStr(a,(int64_t)tempd,10); + else FloatToStr(a, tempd, 0, STR_AUTO_PRECISION, ' '); // set the string value to be saved + cJSON_Delete(parse); + sret=(unsigned char *)a; + sret=CtoM(sret); + targ=T_STR; + return; + } + if (cJSON_IsBool(root)){ + int64_t tempint; + tempint=root->valueint; + cJSON_Delete(parse); + if(tempint)strcpy((char *)sret,"true"); + else strcpy((char *)sret,"false"); + sret=CtoM(sret); + targ=T_STR; + return; + } + if (cJSON_IsString(root)){ + strcpy(a,root->valuestring); + cJSON_Delete(parse); + sret=(unsigned char *)a; + sret=CtoM(sret); + targ=T_STR; + return; + } + cJSON_Delete(parse); + targ=T_STR; + sret=(unsigned char *)a; +} +#endif \ No newline at end of file diff --git a/Code/pico_multi_booter/picomite/Custom.h b/Code/pico_multi_booter/picomite/Custom.h new file mode 100644 index 0000000..b265f90 --- /dev/null +++ b/Code/pico_multi_booter/picomite/Custom.h @@ -0,0 +1,144 @@ +/* + * @cond + * The following section will be excluded from the documentation. + */ +/* ********************************************************************************************************************* +PicoMite MMBasic + +Custom.h + + Geoff Graham, Peter Mather +Copyright (c) 2021, All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the distribution. +3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed + on the console at startup (additional copyright messages may be added). +4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed + by the . +5. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software + without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +************************************************************************************************************************/ +#ifdef PICOMITEWEB +#include "pico/cyw43_arch.h" +#include "lwip/dns.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/apps/mqtt.h" +#include "lwip/apps/mqtt_priv.h" +#include "lwip/timeouts.h" +#include "lwip/ip_addr.h" +#include "lwip/mem.h" +#include "lwip/err.h" +#endif + +/* ******************************************************************************** + the C language function associated with commands, functions or operators should be + declared here +**********************************************************************************/ +#if !defined(INCLUDE_COMMAND_TABLE) && !defined(INCLUDE_TOKEN_TABLE) +// format: +// void cmd_???(void) +// void fun_???(void) +// void op_???(void) +extern uint8_t pioTXlast[4][3]; +extern char *pioRXinterrupts[4][3]; +extern char *pioTXinterrupts[4][3]; +#ifdef PICOMITEWEB + extern void GetNTPTime(void); + extern void checkTCPOptions(void); + extern void open_tcp_server(void); + extern void open_udp_server(void); + extern volatile bool TCPreceived; + extern char *TCPreceiveInterrupt; + extern void TelnetPutC(int c,int flush); + extern int cmd_mqtt(void); + extern void cmd_ntp(unsigned char *tp); + extern void cmd_udp(unsigned char *tp); + extern int cmd_tcpclient(void); + extern int cmd_tcpserver(void); + extern int cmd_tftp_server_init(void); + extern int cmd_tls(); + extern void closeMQTT(void); + typedef struct TCP_SERVER_T_ { + struct tcp_pcb *server_pcb; + struct tcp_pcb *telnet_pcb; + struct tcp_pcb* client_pcb[MaxPcb]; + volatile int inttrig[MaxPcb]; + uint8_t *buffer_sent[MaxPcb]; + uint8_t* buffer_recv[MaxPcb]; + volatile int sent_len[MaxPcb]; + volatile int recv_len[MaxPcb]; + volatile int total_sent[MaxPcb]; + volatile int to_send[MaxPcb]; + volatile uint64_t pcbopentime[MaxPcb]; + volatile int keepalive[MaxPcb]; + volatile int telnet_pcb_no; + volatile int telnet_init_sent; + } TCP_SERVER_T; + typedef struct NTP_T_ { + ip_addr_t ntp_server_address; + bool dns_request_sent; + struct udp_pcb *ntp_pcb; + absolute_time_t ntp_test_time; + alarm_id_t ntp_resend_alarm; + volatile bool complete; + } NTP_T; + typedef struct TCP_CLIENT_T_ { + volatile struct tcp_pcb *tcp_pcb; + ip_addr_t remote_addr; + volatile uint8_t *buffer; + volatile int buffer_len; + volatile bool complete; + volatile bool connected; + volatile int BUF_SIZE; + volatile int TCP_PORT; + volatile int *buffer_write; + volatile int *buffer_read; + volatile char *hostname; + } TCP_CLIENT_T; + extern TCP_SERVER_T *TCPstate; + extern void cleanserver(void); + extern err_t tcp_server_close(void *arg, int pcb); + extern err_t tcp_server_send_data(void *arg, struct tcp_pcb *tpcb, int pcb); + extern void checksent(void *arg, int fn, int pcb); + extern TCP_CLIENT_T *TCP_CLIENT; + extern TCP_CLIENT_T* tcp_client_init(void); + extern void tcp_dns_found(const char *hostname, const ip_addr_t *ipaddr, void *arg); + extern void starttelnet(struct tcp_pcb *client_pcb, int pcb, void *arg); + extern char *UDPinterrupt; + extern volatile bool UDPreceive; +#endif +extern int piointerrupt; +extern char *DMAinterruptRX; +extern char *DMAinterruptTX; +extern uint32_t dma_rx_chan; +extern uint32_t dma_tx_chan; +extern uint32_t dma_rx_chan2; +extern uint32_t dma_tx_chan2; +extern int dma_tx_pio; +extern int dma_tx_sm; +extern int dma_rx_pio; +extern int dma_rx_sm; +extern int dirOK; +extern bool PIO0; +extern bool PIO1; +extern bool PIO2; +extern uint64_t piomap[]; + +extern uint8_t nextline[4]; + +#define TCP_READ_BUFFER_SIZE 2048 + +#endif + +/* @endcond */ + diff --git a/Code/pico_multi_booter/picomite/Doxyfile b/Code/pico_multi_booter/picomite/Doxyfile new file mode 100644 index 0000000..0ac5014 --- /dev/null +++ b/Code/pico_multi_booter/picomite/Doxyfile @@ -0,0 +1,2923 @@ +# Doxyfile 1.12.0 + +# This file describes the settings to be used by the documentation system +# Doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use Doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use Doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = PicoMite + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where Doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = D:/Dropbox/PicoMite/PicoMite/doxygen + +# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding Doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by Doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which Doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where Doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by Doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and Doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# Doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as Doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make Doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by Doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by Doxygen, so you can +# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 6. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 6 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled Doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let Doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# Doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then Doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# Doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run Doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use +# during processing. When set to 0 Doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and macOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + +CASE_SENSE_NAMES = SYSTEM + +# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = YES + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# Doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by Doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by Doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents Doxygen's defaults, run Doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run Doxygen from a directory containing a file called +# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH +# environment variable) so that external tools such as latex and gs can be +# found. +# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the +# path already specified by the PATH variable, and are added in the order +# specified. +# Note: This option is particularly useful for macOS version 14 (Sonoma) and +# higher, when running Doxygen from Doxywizard, because in this case any user- +# defined changes to the PATH are ignored. A typical example on macOS is to set +# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin +# together with the standard path, the full search path used by doxygen when +# launching external tools will then become +# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +EXTERNAL_TOOL_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by Doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete +# function parameter documentation. If set to NO, Doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, Doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about +# undocumented enumeration values. If set to NO, Doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the Doxygen process Doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that Doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of Doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = D:/Dropbox/PicoMite/PicoMite + +# This tag can be used to specify the character encoding of the source files +# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that Doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by Doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cxxm \ + *.cpp \ + *.cppm \ + *.ccm \ + *.c++ \ + *.c++m \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.ixx \ + *.l \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which Doxygen is +# run. + +EXCLUDE = regex.c,AllCommands.h,ArialNumFontPlus.h,arial_bold.h,Audio.h,cJSON.h,Commands.h,configuration.h,Custom.h,diskio.h,Draw.h,dr_flac.h,dr_mp3.h,dr_wav.h,Editor.h,External.h,ff.h,ffconf.h,FileIO.h,Fnt_10x16.h,font1.h,Font_8x6.h,Functions.h,GPS.h,GUI.h,Hardware_Includes.h,Hom_16x24_LE.h,hxcmod.h,I2C.h,Include.h,Inconsola.h,lfs.h,lfs_util.h,lwipopts.h,lwipopts_examples_common.h,MATHS.h,mbedtls_config.h,Memory.h,Misc_12x20_LE.h,MMBasic.h,MMBasic_Includes.h,MM_Misc.h,Onewire.h,Operators.h,PicoCFunctions.h,picojpeg.h,pio.h,pio_instructions.h,PS2Keyboard.h,Serial.h,smallfont.h,SPI-LCD.h,SPI.h,SSD1963.h,tinyfont.h,Touch.h,upng.h,Version.h,vs1053.h,vs1053b-patches.h,XModem.h,xregex.h,xregex2.h,X_8x13_LE.h +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that Doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that Doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by Doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by Doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the Doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# multi-line macros, enums or list initialized variables directly into the +# documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of Doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by Doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then Doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which Doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not Doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then Doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by Doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not Doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank Doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that Doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that Doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of Doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank Doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that Doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank Doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that Doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by Doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, Doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# Doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by Doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# Doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified +# enumeration values besides the enumeration mnemonics. +# The default value is: NO. + +SHOW_ENUM_VALUES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# Doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for +# the HTML output. The underlying search engine uses JavaScript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the JavaScript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /