2020-06-17 14:36:18 +02:00
|
|
|
#include <gint/display.h>
|
|
|
|
#include <gint/keyboard.h>
|
|
|
|
#include <gint/timer.h>
|
|
|
|
#include <gint/mmu.h>
|
|
|
|
|
|
|
|
#include <gintctl/gint.h>
|
|
|
|
#include <gintctl/util.h>
|
|
|
|
|
|
|
|
#define PAGE_USED 0x01
|
|
|
|
#define PAGE_MAPPED 0x02
|
|
|
|
#define TLB_VIEW_MAX (64 - TLB_VIEW)
|
|
|
|
|
|
|
|
#ifdef FXCG50
|
|
|
|
#define PAGE_COUNT 0x200
|
|
|
|
#define SQUARE_WIDTH 8
|
|
|
|
#define SQUARE_HEIGHT 8
|
|
|
|
#define LINE_SIZE 32
|
|
|
|
#define TLB_VIEW 12
|
|
|
|
|
|
|
|
static void draw_rom_cell(int x, int y, int status)
|
|
|
|
{
|
|
|
|
uint16_t colors[4] = {
|
|
|
|
C_RGB(24,24,24), /* Unused and unmapped */
|
|
|
|
C_WHITE, /* Used, but currently unmapped */
|
2021-01-25 17:28:19 +01:00
|
|
|
C_RGB(31,24,0), /* Unused (data) but still mapped */
|
2020-06-17 14:36:18 +02:00
|
|
|
C_RGB(0,31,0), /* Used and currently mapped */
|
|
|
|
};
|
|
|
|
uint16_t border =
|
|
|
|
status == -1 ? C_RGB(0,0,31) :
|
|
|
|
status == -2 ? C_RGB(31,0,0) :
|
|
|
|
C_BLACK;
|
|
|
|
|
2020-06-18 19:56:32 +02:00
|
|
|
uint16_t fill = (status >= 0 && status < 4) ? colors[status] : C_NONE;
|
|
|
|
drect_border(x, y, x+SQUARE_WIDTH, y+SQUARE_HEIGHT, fill, 1, border);
|
2020-06-17 14:36:18 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FX9860G
|
|
|
|
#define PAGE_COUNT 0x80
|
2021-02-06 16:42:43 +01:00
|
|
|
#define SQUARE_WIDTH 5
|
|
|
|
#define SQUARE_HEIGHT 5
|
2020-06-17 14:36:18 +02:00
|
|
|
#define LINE_SIZE 16
|
|
|
|
#define TLB_VIEW 8
|
|
|
|
|
|
|
|
static void draw_rom_cell(int x, int y, int status)
|
|
|
|
{
|
|
|
|
extern bopti_image_t img_tlb_cells;
|
|
|
|
dsubimage(x, y, &img_tlb_cells, (status+2)*(SQUARE_WIDTH+2), 0,
|
|
|
|
SQUARE_WIDTH+1, SQUARE_HEIGHT+1, DIMAGE_NONE);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int rom_cell_x(int page_number)
|
|
|
|
{
|
|
|
|
int x = (page_number % LINE_SIZE) * SQUARE_WIDTH;
|
|
|
|
x += 3 * ((page_number % LINE_SIZE) >> 3);
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
static int rom_cell_y(int page_number)
|
|
|
|
{
|
|
|
|
return (page_number / LINE_SIZE) * SQUARE_HEIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void explore_pages(uint8_t *pages, uint32_t *next_miss)
|
|
|
|
{
|
|
|
|
extern uint32_t srom;
|
|
|
|
|
|
|
|
for(uint p = 0; p < PAGE_COUNT; p++)
|
|
|
|
{
|
|
|
|
pages[p] = ((p << 12) < (uint32_t)&srom) ? PAGE_USED : 0;
|
|
|
|
}
|
|
|
|
|
2020-07-08 19:41:10 +02:00
|
|
|
if(isSH3())
|
|
|
|
{
|
|
|
|
for(int way = 0; way < 4; way++)
|
|
|
|
for(int E = 0; E < 32; E++)
|
|
|
|
{
|
|
|
|
tlb_addr_t addr = *tlb_addr(way, E);
|
|
|
|
tlb_data_t data = *tlb_data(way, E);
|
|
|
|
if(!addr.V || !data.V || !data.SZ) continue;
|
|
|
|
|
|
|
|
uint32_t src = (((addr.VPN >> 2) | E) << 12);
|
|
|
|
int p = (src - 0x00300000) >> 12;
|
|
|
|
pages[p] |= PAGE_MAPPED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else for(uint E = 0; E < 64; E++)
|
2020-06-17 14:36:18 +02:00
|
|
|
{
|
|
|
|
utlb_addr_t addr = *utlb_addr(E);
|
|
|
|
utlb_data_t data = *utlb_data(E);
|
|
|
|
|
|
|
|
uint32_t src = addr.VPN << 10;
|
|
|
|
int valid = (addr.V != 0) && (data.V != 0);
|
|
|
|
int size = (data.SZ1 << 1) | data.SZ2;
|
|
|
|
if(!valid || size != 1) continue;
|
|
|
|
|
|
|
|
int p = (src - 0x00300000) >> 12;
|
|
|
|
pages[p] |= PAGE_MAPPED;
|
|
|
|
}
|
|
|
|
|
|
|
|
*next_miss = 0xffffffff;
|
|
|
|
for(uint p = 0; p < PAGE_COUNT; p++)
|
|
|
|
{
|
|
|
|
if((pages[p] & PAGE_USED) && !(pages[p] & PAGE_MAPPED))
|
|
|
|
{
|
|
|
|
*next_miss = 0x00300000 + (p << 12);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FXCG50
|
|
|
|
void show_utlb(int row, int E)
|
|
|
|
{
|
|
|
|
if(E == -1)
|
|
|
|
{
|
|
|
|
row_print(row, 2, "ID");
|
|
|
|
row_print(row, 5, "Virtual");
|
|
|
|
row_print(row, 14, "Physical");
|
|
|
|
row_print(row, 23, "Size");
|
|
|
|
row_print(row, 28, "Mode");
|
|
|
|
row_print(row, 34, "SH");
|
|
|
|
row_print(row, 37, "WT");
|
|
|
|
row_print(row, 40, "C");
|
|
|
|
row_print(row, 42, "D");
|
|
|
|
row_print(row, 44, "V");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
utlb_addr_t addr = *utlb_addr(E);
|
|
|
|
utlb_data_t data = *utlb_data(E);
|
|
|
|
|
|
|
|
uint32_t src = addr.VPN << 10;
|
|
|
|
uint32_t dst = data.PPN << 10;
|
|
|
|
|
|
|
|
int valid = (addr.V != 0) && (data.V != 0);
|
|
|
|
int size = (data.SZ1 << 1) | data.SZ2;
|
|
|
|
char const *size_str[] = { "1k", "4k", "64k", "1M" };
|
|
|
|
char const *access_str[] = { "K:r", "K:rw", "U:r", "U:rw" };
|
|
|
|
|
|
|
|
uint16_t fg = valid ? C_BLACK : C_RGB(24,24,24);
|
2021-02-06 16:42:43 +01:00
|
|
|
#define fg_ fg, C_NONE, DTEXT_LEFT, DTEXT_TOP
|
|
|
|
|
|
|
|
int y = row_y(row) + 2 * (row > 1);
|
|
|
|
|
|
|
|
dprint_opt( 14, y, fg_, "%d", E);
|
|
|
|
dprint_opt( 38, y, fg_, "%08X", src);
|
|
|
|
dprint_opt(110, y, fg_, "%08X", dst);
|
|
|
|
dprint_opt(182, y, fg_, "%s", size_str[size]);
|
|
|
|
dprint_opt(222, y, fg_, "%s", access_str[data.PR]);
|
|
|
|
dprint_opt(270, y, fg_, "%s", (data.SH ? "SH" : "-"));
|
|
|
|
dprint_opt(294, y, fg_, "%s", (data.WT ? "WT" : "CB"));
|
|
|
|
dprint_opt(318, y, fg_, "%s", (data.C ? "C" : "-"));
|
|
|
|
dprint_opt(334, y, fg_, "%s", (addr.D || data.D ? "D" : "-"));
|
|
|
|
dprint_opt(350, y, fg_, "%s", (addr.V && data.V ? "V" : "-"));
|
2020-06-17 14:36:18 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FX9860G
|
|
|
|
void show_utlb(int row, int E)
|
|
|
|
{
|
2021-02-06 16:42:43 +01:00
|
|
|
extern font_t font_mini;
|
|
|
|
font_t const *old_font = dfont(&font_mini);
|
|
|
|
int y = (row - 1) * 6 + 2 * (row > 1);
|
2020-06-17 14:36:18 +02:00
|
|
|
|
|
|
|
if(E == -1)
|
|
|
|
{
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint( 1, y, C_BLACK, "ID");
|
|
|
|
dprint( 12, y, C_BLACK, "Virtual");
|
|
|
|
dprint( 47, y, C_BLACK, "Physical");
|
|
|
|
dprint( 82, y, C_BLACK, "Len");
|
|
|
|
dprint( 97, y, C_BLACK, "Pr");
|
|
|
|
dprint(115, y, C_BLACK, "CD");
|
2020-06-17 14:36:18 +02:00
|
|
|
|
|
|
|
dfont(old_font);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char const *size_str[] = { "1k", "4k", "64k", "1M" };
|
2021-02-06 16:42:43 +01:00
|
|
|
char const *access_str[] = { "Kr", "Krw", "Ur", "Urw" };
|
|
|
|
char const *cd_str[] = { "--", "-D", "C-", "CD" };
|
2020-07-08 19:41:10 +02:00
|
|
|
uint32_t src, dst;
|
2021-02-06 16:42:43 +01:00
|
|
|
int valid, size, pr, cd;
|
2020-07-08 19:41:10 +02:00
|
|
|
|
|
|
|
if(isSH3())
|
|
|
|
{
|
|
|
|
int way = (E >> 5);
|
|
|
|
E &= 31;
|
|
|
|
|
|
|
|
tlb_addr_t addr = *tlb_addr(way, E);
|
|
|
|
tlb_data_t data = *tlb_data(way, E);
|
|
|
|
|
|
|
|
valid = (addr.V != 0 && data.V != 0);
|
|
|
|
size = data.SZ;
|
|
|
|
|
|
|
|
if(data.SZ) src = (((addr.VPN >> 2) | E) << 12);
|
|
|
|
else src = (addr.VPN | (E << 2)) << 10;
|
|
|
|
|
|
|
|
dst = data.PPN << 10;
|
|
|
|
pr = data.PR;
|
2021-02-06 16:42:43 +01:00
|
|
|
cd = (data.C << 1) | data.D;
|
2020-07-08 19:41:10 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
utlb_addr_t addr = *utlb_addr(E);
|
|
|
|
utlb_data_t data = *utlb_data(E);
|
|
|
|
|
|
|
|
src = addr.VPN << 10;
|
|
|
|
dst = data.PPN << 10;
|
|
|
|
|
|
|
|
valid = (addr.V != 0) && (data.V != 0);
|
|
|
|
size = (data.SZ1 << 1) | data.SZ2;
|
|
|
|
pr = data.PR;
|
2021-02-06 16:42:43 +01:00
|
|
|
cd = (data.C << 1) | data.D;
|
2020-07-08 19:41:10 +02:00
|
|
|
}
|
2020-06-17 14:36:18 +02:00
|
|
|
|
2020-06-18 19:56:32 +02:00
|
|
|
dprint( 1, y, C_BLACK, "%d", E);
|
2020-06-17 14:36:18 +02:00
|
|
|
|
|
|
|
if(valid)
|
|
|
|
{
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint( 12, y, C_BLACK, "%08X", src);
|
|
|
|
dprint( 47, y, C_BLACK, "%08X", dst);
|
|
|
|
dprint( 82, y, C_BLACK, "%s", size_str[size]);
|
|
|
|
dprint( 97, y, C_BLACK, "%s", access_str[pr]);
|
|
|
|
dprint(115, y, C_BLACK, "%s", cd_str[cd]);
|
2020-06-17 14:36:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dfont(old_font);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void draw(int tab, uint8_t *pages, uint32_t next_miss, int tlb_scroll)
|
|
|
|
{
|
|
|
|
extern uint32_t srom;
|
|
|
|
uint32_t rom_size = (uint32_t)&srom;
|
|
|
|
int rom_pages = (rom_size + (1 << 12)-1) >> 12;
|
|
|
|
|
|
|
|
#ifdef FX9860G
|
|
|
|
if(tab != 2)
|
|
|
|
#endif
|
|
|
|
row_title(_("TLB management", "TLB miss handler and TLB management"));
|
|
|
|
|
2021-02-06 16:42:43 +01:00
|
|
|
#ifdef FX9860G
|
|
|
|
extern font_t font_mini;
|
|
|
|
font_t const *old_font = dfont(&font_mini);
|
|
|
|
#endif
|
|
|
|
|
2020-06-17 14:36:18 +02:00
|
|
|
if(tab == 0)
|
|
|
|
{
|
2021-02-06 16:42:43 +01:00
|
|
|
#ifdef FXCG50
|
|
|
|
row_print(1, 1, "Size of ROM sections: %08X (%d pages of 4k)",
|
2020-06-17 14:36:18 +02:00
|
|
|
rom_size, rom_pages);
|
2021-02-06 16:42:43 +01:00
|
|
|
#endif
|
2020-06-17 14:36:18 +02:00
|
|
|
|
2021-02-06 16:42:43 +01:00
|
|
|
for(uint p=0, y=_(12,36); p < PAGE_COUNT; p += 2*LINE_SIZE)
|
2020-06-17 14:36:18 +02:00
|
|
|
{
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint(_(5,18), y, C_BLACK, "%08X",
|
2020-06-17 14:36:18 +02:00
|
|
|
0x00300000 + (p << 12));
|
|
|
|
y += 2*SQUARE_HEIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(uint p = 0; p < PAGE_COUNT; p++)
|
|
|
|
{
|
2021-02-06 16:42:43 +01:00
|
|
|
draw_rom_cell(_(39,88) + rom_cell_x(p),
|
|
|
|
_(11,36) + rom_cell_y(p), pages[p]);
|
2020-06-17 14:36:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FXCG50
|
|
|
|
if(next_miss != 0xffffffff)
|
|
|
|
{
|
|
|
|
uint p = (next_miss - 0x00300000) >> 12;
|
|
|
|
draw_rom_cell(88+rom_cell_x(p), 36+rom_cell_y(p), -1);
|
|
|
|
row_print(12, 1, "Next page to load: %08X", next_miss);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
row_print(12, 1, "No page left to load!");
|
|
|
|
}
|
|
|
|
row_print(13, 1, "See INFO tab for details.");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(tab == 1)
|
|
|
|
{
|
|
|
|
row_print(_(2,1), 1, "Legend:");
|
2021-02-06 16:42:43 +01:00
|
|
|
draw_rom_cell(_(8,18), _(15,36), 0);
|
|
|
|
draw_rom_cell(_(64,200), _(15,36), 1);
|
|
|
|
draw_rom_cell(_(8,18), _(22,50), 2);
|
|
|
|
draw_rom_cell(_(64,200), _(22,50), 3);
|
2020-06-17 14:36:18 +02:00
|
|
|
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint(_(16,30), _(15,36), C_BLACK,
|
2020-06-17 14:36:18 +02:00
|
|
|
_("Unused", "Unused in add-in"));
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint(_(72,212), _(15,36), C_BLACK,
|
2020-06-17 14:36:18 +02:00
|
|
|
_("Unmapped", "Currently unmapped"));
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint(_(16,30), _(22,50), C_BLACK,
|
2021-01-25 17:28:19 +01:00
|
|
|
_("Data", "Data still mapped"));
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint(_(72,212), _(22,50), C_BLACK,
|
2020-06-17 14:36:18 +02:00
|
|
|
_("Mapped", "Currently mapped"));
|
|
|
|
|
|
|
|
#ifdef FXCG50
|
|
|
|
draw_rom_cell(18, 64, -1);
|
2020-06-18 19:56:32 +02:00
|
|
|
dprint(30, 64, C_BLACK, "Next page to load");
|
2020-06-17 14:36:18 +02:00
|
|
|
|
|
|
|
row_print(6, 1,
|
2021-02-06 16:42:43 +01:00
|
|
|
"The LOAD key will load an unmapped page to TLB by");
|
2020-06-17 14:36:18 +02:00
|
|
|
row_print(7, 1,
|
|
|
|
"performing a TLB miss, which calls %%003 on fx9860g");
|
|
|
|
row_print(8, 1,
|
|
|
|
"and %%00c on fxcg50. The TIMER key performs a TLB");
|
|
|
|
row_print(9, 1,
|
|
|
|
"miss from a timer callback.");
|
|
|
|
row_print(11, 1,
|
|
|
|
"Sometimes the page might be loaded by code or");
|
|
|
|
row_print(12, 1,
|
|
|
|
"data before the intended TLB miss occurs.");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FX9860G
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint(1, 30, C_BLACK, "Size of ROM text: %X (%d pages)",
|
|
|
|
rom_size, rom_pages);
|
|
|
|
|
2020-06-17 14:36:18 +02:00
|
|
|
if(next_miss != 0xffffffff)
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint(1, 36, C_BLACK, "Next load: %08x", next_miss);
|
2020-06-17 14:36:18 +02:00
|
|
|
else
|
2021-02-06 16:42:43 +01:00
|
|
|
dprint(1, 36, C_BLACK, "No page left to load!");
|
|
|
|
|
|
|
|
dprint(1, 43, C_BLACK, "F5: Load a page by TLB miss");
|
|
|
|
dprint(1, 49, C_BLACK, "F6: Do it from a timer callback");
|
2020-06-17 14:36:18 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(tab == 2)
|
|
|
|
{
|
|
|
|
show_utlb(1, -1);
|
|
|
|
for(int i = 0; i < TLB_VIEW; i++)
|
|
|
|
show_utlb(i+2, tlb_scroll+i);
|
|
|
|
|
2021-02-06 16:42:43 +01:00
|
|
|
#ifdef FX9860G
|
|
|
|
dhline(6, C_BLACK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FXCG50
|
|
|
|
dline(12, 34, 363, 34, C_BLACK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
scrollbar_px(
|
|
|
|
/* view */ _(8,37), _(54,201),
|
|
|
|
/* range */ 0, TLB_VIEW_MAX + TLB_VIEW,
|
|
|
|
/* visible */ tlb_scroll, TLB_VIEW);
|
2020-06-17 14:36:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FX9860G
|
2021-02-06 16:42:43 +01:00
|
|
|
dfont(old_font);
|
|
|
|
|
2020-06-17 14:36:18 +02:00
|
|
|
extern bopti_image_t img_opt_gint_tlb;
|
|
|
|
dimage(0, 56, &img_opt_gint_tlb);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FXCG50
|
|
|
|
fkey_menu(1, "ROM");
|
|
|
|
fkey_menu(2, "INFO");
|
|
|
|
fkey_menu(3, "TLB");
|
2021-02-06 16:42:43 +01:00
|
|
|
fkey_action(5, "LOAD");
|
2020-06-17 14:36:18 +02:00
|
|
|
fkey_action(6, "TIMER");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_tlb_miss(volatile void *arg)
|
|
|
|
{
|
|
|
|
uint8_t volatile *next_miss = arg;
|
|
|
|
GUNUSED uint8_t volatile x = *next_miss;
|
2020-06-20 22:48:35 +02:00
|
|
|
return TIMER_STOP;
|
2020-06-17 14:36:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* gintctl_gint_tlb(): TLB miss handler and TLB management */
|
|
|
|
void gintctl_gint_tlb(void)
|
|
|
|
{
|
|
|
|
key_event_t ev;
|
|
|
|
int key=0, tab=0;
|
|
|
|
|
|
|
|
uint8_t pages[PAGE_COUNT];
|
|
|
|
uint32_t next_miss = 0xffffffff;
|
|
|
|
int tlb_scroll = 0;
|
|
|
|
|
|
|
|
while(key != KEY_EXIT)
|
|
|
|
{
|
|
|
|
dclear(C_WHITE);
|
|
|
|
|
|
|
|
if(tab == 0 || tab == 1)
|
|
|
|
explore_pages(pages, &next_miss);
|
|
|
|
draw(tab, pages, next_miss, tlb_scroll);
|
|
|
|
|
|
|
|
dupdate();
|
|
|
|
key = (ev = getkey()).key;
|
|
|
|
|
|
|
|
if(key == KEY_F1) tab = 0;
|
|
|
|
if(key == KEY_F2) tab = 1;
|
|
|
|
if(key == KEY_F3) tab = 2;
|
|
|
|
|
|
|
|
if(tab == 2 && key == KEY_UP)
|
|
|
|
{
|
2021-02-06 16:42:43 +01:00
|
|
|
if(ev.shift || keydown(KEY_SHIFT))
|
|
|
|
tlb_scroll = 0;
|
|
|
|
else if(tlb_scroll > 0)
|
|
|
|
tlb_scroll--;
|
2020-06-17 14:36:18 +02:00
|
|
|
}
|
|
|
|
if(tab == 2 && key == KEY_DOWN)
|
|
|
|
{
|
2021-02-06 16:42:43 +01:00
|
|
|
if(ev.shift || keydown(KEY_SHIFT))
|
|
|
|
tlb_scroll = TLB_VIEW_MAX;
|
|
|
|
else if (tlb_scroll < TLB_VIEW_MAX)
|
|
|
|
tlb_scroll++;
|
2020-06-17 14:36:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if(key == KEY_F5 && next_miss != 0xffffffff)
|
|
|
|
{
|
|
|
|
GUNUSED uint8_t volatile x;
|
|
|
|
x = *(uint8_t volatile *)next_miss;
|
|
|
|
}
|
|
|
|
if(key == KEY_F6 && next_miss != 0xffffffff)
|
|
|
|
{
|
2021-04-27 14:57:27 +02:00
|
|
|
int timer = timer_configure(TIMER_ANY, 10000,
|
|
|
|
GINT_CALL(generate_tlb_miss, next_miss));
|
2020-06-20 22:48:35 +02:00
|
|
|
if(timer >= 0)
|
|
|
|
{
|
|
|
|
timer_start(timer);
|
|
|
|
timer_wait(timer);
|
|
|
|
}
|
2020-06-17 14:36:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|