/* * libtilemdb - Utilities for debugging Z80 assembly programs * * Copyright (C) 2010 Benjamin Moody * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include "tilemdb.h" struct _TilemListingLineCache { dword minaddr; dword maxaddr; int* order; }; static void sort_lines(TilemListing* lst, int start, int end) { int pivot, ncstart, ncend, n; dword addr, pivotaddr; int* order = lst->linecache->order; pivot = order[start]; pivotaddr = lst->lines[pivot].address; ncstart = start + 1; ncend = end; while (ncstart < ncend) { n = order[ncstart]; addr = lst->lines[n].address; if (addr < pivotaddr) { ncstart++; } else { order[ncstart] = order[ncend - 1]; order[ncend - 1] = n; ncend--; } } ncstart--; order[start] = order[ncstart]; order[ncstart] = pivot; if (ncstart > start + 1) sort_lines(lst, start, ncstart); if (end > ncend + 1) sort_lines(lst, ncend, end); } static void ensure_order(TilemListing* lst) { int i; if (lst->nlines == 0) { lst->linecache->minaddr = 0xffff; lst->linecache->maxaddr = 0; } else if (!lst->linecache->order) { lst->linecache->order = tilem_new_atomic(int, lst->nlines); for (i = 0; i < lst->nlines; i++) lst->linecache->order[i] = i; sort_lines(lst, 0, lst->nlines); i = lst->linecache->order[0]; lst->linecache->minaddr = lst->lines[i].address; i = lst->linecache->order[lst->nlines - 1]; lst->linecache->maxaddr = lst->lines[i].address; } } static void order_destroyed(TilemListing* lst) { tilem_free(lst->linecache->order); lst->linecache->order = NULL; } static int first_at_addr(TilemListing* lst, dword addr) { int start, end, i; start = 0; end = lst->nlines; while (start < end) { i = (start + end) / 2; if (lst->lines[lst->linecache->order[i]].address < addr) start = i + 1; else end = i; } return start; } TilemListing* tilem_listing_new() { TilemListing* lst = tilem_new0(TilemListing, 1); lst->lines = NULL; lst->linecache = tilem_new0(TilemListingLineCache, 1); lst->linecache->order = NULL; return lst; } void tilem_listing_free(TilemListing* lst) { int i; if (!lst) return; for (i = 0; i < lst->nlines; i++) tilem_free(lst->lines[i].text); tilem_free(lst->lines); tilem_free(lst->linecache->order); tilem_free(lst->linecache); tilem_free(lst); } void tilem_listing_file_clear(TilemListing* lst) { order_destroyed(lst); lst->nlines = 0; lst->nlines_a = 0; tilem_free(lst->lines); lst->lines = NULL; } void tilem_listing_append_line(TilemListing* lst, int srclinenum, dword address, int depth, int datasize, const byte* data, const char* text, int is_expansion) { TilemListingLine* line; int i; order_destroyed(lst); lst->nlines++; if (lst->nlines >= lst->nlines_a) { lst->nlines_a = lst->nlines * 2; lst->lines = tilem_renew(TilemListingLine, lst->lines, lst->nlines_a); } line = &lst->lines[lst->nlines - 1]; line->listing = lst; line->srclinenum = srclinenum; line->address = address & 0xffff; line->depth = depth; if (datasize > TILEM_MAX_LINE_BYTES) datasize = TILEM_MAX_LINE_BYTES; line->datasize = datasize; for (i = 0; i < datasize; i++) line->data[i] = data[i]; if (text) { line->text = tilem_new_atomic(char, strlen(text) + 1); strcpy(line->text, text); if ((text[0] >= 'A' && text[0] <= 'Z') || (text[0] >= 'a' && text[0] <= 'z') || (text[0] & 0x80)) line->is_label = 1; else line->is_label = 0; } else { line->text = NULL; line->is_label = 0; } line->is_expansion = is_expansion; } void tilem_listing_get_address_range(TilemListing* lst, dword* min, dword* max) { ensure_order(lst); if (min) *min = lst->linecache->minaddr; if (max) *max = lst->linecache->maxaddr; } TilemListingLine* tilem_listing_line_get_next(TilemListingLine* line) { if (!line) return NULL; if (line != line->listing->lines + line->listing->nlines) return line + 1; else return NULL; } TilemListingLine* tilem_listing_line_get_prev(TilemListingLine* line) { if (!line) return NULL; if (line != line->listing->lines) return line - 1; else return NULL; } TilemListingLine* tilem_listing_get_loaded_line_at_addr(TilemListing* lst, dword address, TilemCalc* calc, int match_internal) { int i; TilemListingLine *line; ensure_order(lst); i = first_at_addr(lst, address + 1); while (--i >= 0) { line = &lst->lines[lst->linecache->order[i]]; if (match_internal) { if (line->address + TILEM_MAX_LINE_BYTES <= address) return NULL; else if (line->address + line->datasize <= address) continue; } else { if (line->address != address) return NULL; } if (tilem_listing_line_is_loaded(line, calc)) return line; } return NULL; } static inline unsigned int getbyte(TilemCalc* calc, dword addr) { dword pa = (*calc->hw.mem_ltop)(calc, addr & 0xffff); return calc->mem[pa]; } static inline int line_load_count(const TilemListingLine* line, TilemCalc* calc) { int i, n; for (i = n = 0; i < line->datasize; i++) if (getbyte(calc, line->address + i) == line->data[i]) n++; return n; } static inline TilemListingLine* get_next_local(TilemListingLine* line) { TilemListingLine* nline = tilem_listing_line_get_next(line); if (nline && nline->address != line->address + line->datasize) return NULL; else return nline; } static inline TilemListingLine* get_prev_local(TilemListingLine* line) { TilemListingLine* nline = tilem_listing_line_get_prev(line); if (nline && nline->address != line->address + line->datasize) return NULL; else return nline; } int tilem_listing_line_is_loaded(TilemListingLine* line, TilemCalc* calc) { int nbytes, ngood; TilemListingLine *nline; while (line->datasize == 0 && (nline = get_next_local(line))) line = nline; if (line_load_count(line, calc) != line->datasize) return 0; else { nbytes = ngood = line->datasize; nline = line; while ((nline = get_next_local(nline)) && nline->address < line->address + 32) { nbytes += nline->datasize; ngood += line_load_count(nline, calc); } nline = line; while ((nline = get_prev_local(nline)) && line->address < nline->address + 32) { nbytes += nline->datasize; ngood += line_load_count(nline, calc); } return (ngood > nbytes / 2); } } static int bptest_listing_line(TilemCalc* calc, dword addr TILEM_ATTR_UNUSED, void* data) { return tilem_listing_line_is_loaded(data, calc); } int tilem_listing_line_add_breakpoint(TilemListingLine* line, TilemCalc* calc, int bptype, int match_internal) { dword max; if (match_internal && line->datasize > 0) max = line->address + line->datasize - 1; else max = line->address; return tilem_z80_add_breakpoint(calc, bptype, line->address, max, 0xffff, &bptest_listing_line, (void*) line); } static int bptest_listing_int(TilemCalc* calc, dword addr, void* data) { TilemListing* lst = data; if (tilem_listing_get_loaded_line_at_addr(lst, addr, calc, 1)) return 1; else return 0; } static int bptest_listing_top(TilemCalc* calc, dword addr, void* data) { TilemListing* lst = data; if (tilem_listing_get_loaded_line_at_addr(lst, addr, calc, 0)) return 1; else return 0; } int tilem_listing_add_breakpoint(TilemListing* lst, TilemCalc* calc, int bptype, int match_internal) { dword min, max; tilem_listing_get_address_range(lst, &min, &max); return tilem_z80_add_breakpoint(calc, bptype, min, max, 0xffff, (match_internal ? &bptest_listing_int : &bptest_listing_top), lst); }