RZ_API ut64 rz_core_analysis_address(RzCore *core, ut64 addr) {
	ut64 types = 0;
	RzRegSet *rs = NULL;
	if (!core) {
		return 0;
	}
	if (core->dbg && core->dbg->reg) {
		rs = rz_reg_regset_get(core->dbg->reg, RZ_REG_TYPE_GPR);
	}
	if (rs) {
		RzRegItem *r;
		RzListIter *iter;
		rz_list_foreach (rs->regs, iter, r) {
			if (r->type == RZ_REG_TYPE_GPR) {
				ut64 val = rz_reg_getv(core->dbg->reg, r->name);
				if (addr == val) {
					types |= RZ_ANALYSIS_ADDR_TYPE_REG;
					break;
				}
			}
		}
	}
	if (rz_flag_get_i(core->flags, addr)) {
		types |= RZ_ANALYSIS_ADDR_TYPE_FLAG;
	}
	if (rz_analysis_get_fcn_in(core->analysis, addr, 0)) {
		types |= RZ_ANALYSIS_ADDR_TYPE_FUNC;
	}
	// check registers
	if (core->bin && core->bin->is_debugger && core->dbg) { // TODO: if cfg.debug here
		RzDebugMap *map;
		RzListIter *iter;
		// use 'dm'
		// XXX: this line makes r2 debugging MUCH slower
		// rz_debug_map_sync (core->dbg);
		rz_list_foreach (core->dbg->maps, iter, map) {
			if (addr >= map->addr && addr < map->addr_end) {
				if (map->name && map->name[0] == '/') {
					if (core->io && core->io->desc &&
						core->io->desc->name &&
						!strcmp(map->name,
							core->io->desc->name)) {
						types |= RZ_ANALYSIS_ADDR_TYPE_PROGRAM;
					} else {
						types |= RZ_ANALYSIS_ADDR_TYPE_LIBRARY;
					}
				}
				if (map->perm & RZ_PERM_X) {
					types |= RZ_ANALYSIS_ADDR_TYPE_EXEC;
				}
				if (map->perm & RZ_PERM_R) {
					types |= RZ_ANALYSIS_ADDR_TYPE_READ;
				}
				if (map->perm & RZ_PERM_W) {
					types |= RZ_ANALYSIS_ADDR_TYPE_WRITE;
				}
				// find function
				if (map->name && strstr(map->name, "heap")) {
					types |= RZ_ANALYSIS_ADDR_TYPE_HEAP;
				}
				if (map->name && strstr(map->name, "stack")) {
					types |= RZ_ANALYSIS_ADDR_TYPE_STACK;
				}
				break;
			}
		}
	} else {
		int _perm = -1;
		if (core->io) {
			// sections
			void **it;
			RzPVector *maps = rz_io_maps(core->io);
			rz_pvector_foreach (maps, it) {
				RzIOMap *s = *it;
				if (addr >= s->itv.addr && addr < (s->itv.addr + s->itv.size)) {
					// sections overlap, so we want to get the one with lower perms
					_perm = (_perm != -1) ? RZ_MIN(_perm, s->perm) : s->perm;
					// TODO: we should identify which maps come from the program or other
					//types |= RZ_ANALYSIS_ADDR_TYPE_PROGRAM;
					// find function those sections should be created by hand or esil init
					if (s->name && strstr(s->name, "heap")) {
						types |= RZ_ANALYSIS_ADDR_TYPE_HEAP;
					}
					if (s->name && strstr(s->name, "stack")) {
						types |= RZ_ANALYSIS_ADDR_TYPE_STACK;
					}
				}
			}
		}
		if (_perm != -1) {
			if (_perm & RZ_PERM_X) {
				types |= RZ_ANALYSIS_ADDR_TYPE_EXEC;
			}
			if (_perm & RZ_PERM_R) {
				types |= RZ_ANALYSIS_ADDR_TYPE_READ;
			}
			if (_perm & RZ_PERM_W) {
				types |= RZ_ANALYSIS_ADDR_TYPE_WRITE;
			}
		}
	}

	// check if it's ascii
	if (addr != 0) {
		int not_ascii = 0;
		int i, failed_sequence, dir, on;
		for (i = 0; i < 8; i++) {
			ut8 n = (addr >> (i * 8)) & 0xff;
			if (n && !IS_PRINTABLE(n)) {
				not_ascii = 1;
			}
		}
		if (!not_ascii) {
			types |= RZ_ANALYSIS_ADDR_TYPE_ASCII;
		}
		failed_sequence = 0;
		dir = on = -1;
		for (i = 0; i < 8; i++) {
			ut8 n = (addr >> (i * 8)) & 0xff;
			if (on != -1) {
				if (dir == -1) {
					dir = (n > on) ? 1 : -1;
				}
				if (n == on + dir) {
					// ok
				} else {
					failed_sequence = 1;
					break;
				}
			}
			on = n;
		}
		if (!failed_sequence) {
			types |= RZ_ANALYSIS_ADDR_TYPE_SEQUENCE;
		}
	}
	return types;
}