MD/32X Programming - Extra Lessons

Lesson 1 - Make your own makefiles

Since a makefile is an important part of any gcc project, we should probably take a look at an example and see how everything works together so you can make your own. One thing I should stress right off the bat - KEEP IT SIMPLE, STUPID!! The KISS method is especially important for makefiles. Makefiles can be truly, horrifically, needlessly complicated. Look at this example.

Code:
# Makefile.in generated by automake 1.10.2 from Makefile.am.

# Makefile.  Generated from Makefile.in by configure.

# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,

# 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.

# This Makefile.in is free software; the Free Software Foundation

# gives unlimited permission to copy and/or distribute it,

# with or without modifications, as long as this notice is preserved.

# This program is distributed in the hope that it will be useful,

# but WITHOUT ANY WARRANTY, to the extent permitted by law; without

# even the implied warranty of MERCHANTABILITY or FITNESS FOR A

# PARTICULAR PURPOSE.

# Copyright (c) 2007 Hans Ulrich Niedermann <hun@n-dimensional.de>

#

# This Makefile fragment is free software; the author(s) give(s)

# unlimited permission to copy, distribute and modify it.

pkgdatadir = $(datadir)/gens

pkglibdir = $(libdir)/gens

pkgincludedir = $(includedir)/gens

am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd

install_sh_DATA = $(install_sh) -c -m 644

install_sh_PROGRAM = $(install_sh) -c

install_sh_SCRIPT = $(install_sh) -c

INSTALL_HEADER = $(INSTALL_DATA)

transform = $(program_transform_name)

NORMAL_INSTALL = :

PRE_INSTALL = :

POST_INSTALL = :

NORMAL_UNINSTALL = :

PRE_UNINSTALL = :

POST_UNINSTALL = :

build_triplet = i686-pc-linux-gnu

host_triplet = i686-pc-linux-gnu

target_triplet = i686-pc-linux-gnu

DIST_COMMON = $(am__configure_deps) $(srcdir)/Makefile.am \

	$(srcdir)/Makefile.in $(srcdir)/config.h.in \

	$(srcdir)/git_version.am $(top_srcdir)/configure compile \

	config.guess config.sub depcomp install-sh ltmain.sh missing \

	mkinstalldirs

subdir = .

ACLOCAL_M4 = $(top_srcdir)/aclocal.m4

am__aclocal_m4_deps = $(top_srcdir)/m4/gtk-2.0.m4 \

	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \

	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \

	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \

	$(top_srcdir)/m4/sdl.m4 $(top_srcdir)/configure.ac

am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \

	$(ACLOCAL_M4)

am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \

 configure.lineno config.status.lineno

mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs

CONFIG_HEADER = config.h

CONFIG_CLEAN_FILES =

SOURCES =

DIST_SOURCES =

RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \

	html-recursive info-recursive install-data-recursive \

	install-dvi-recursive install-exec-recursive \

	install-html-recursive install-info-recursive \

	install-pdf-recursive install-ps-recursive install-recursive \

	installcheck-recursive installdirs-recursive pdf-recursive \

	ps-recursive uninstall-recursive

RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\

  distclean-recursive maintainer-clean-recursive

ETAGS = etags

CTAGS = ctags

DIST_SUBDIRS = $(SUBDIRS)

DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)

distdir = $(PACKAGE)-$(VERSION)

top_distdir = $(distdir)

am__remove_distdir = \

  { test ! -d $(distdir) \

    || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \

         && rm -fr $(distdir); }; }

DIST_ARCHIVES = $(distdir).tar.gz

GZIP_ENV = --best

distuninstallcheck_listfiles = find . -type f -print

distcleancheck_listfiles = find . -type f -print

ACLOCAL = ${SHELL} /home/jlfenton/Projects/linux/gens-gs-r7/missing --run aclocal-1.10

ALLOCA = 

AMTAR = ${SHELL} /home/jlfenton/Projects/linux/gens-gs-r7/missing --run tar

AM_CFLAGS =  -Wall -Wextra -O0 -ggdb 

AM_CPPFLAGS = 

AM_CXXFLAGS =  -Wall -Wextra -O0 -ggdb  -fvisibility-inlines-hidden

AM_LDFLAGS =  -Wl,--as-needed -Wl,--hash-style=both  

AR = ar

AUTOCONF = ${SHELL} /home/jlfenton/Projects/linux/gens-gs-r7/missing --run autoconf

AUTOHEADER = ${SHELL} /home/jlfenton/Projects/linux/gens-gs-r7/missing --run autoheader

AUTOMAKE = ${SHELL} /home/jlfenton/Projects/linux/gens-gs-r7/missing --run automake-1.10

AWK = gawk

CC = gcc -std=gnu99

CCAS = gcc

CCASDEPMODE = depmode=gcc3

CCASFLAGS = -g -O2

CCDEPMODE = depmode=gcc3

CFLAGS = -g -O2

CPP = gcc -E

CPPFLAGS = 

CXX = g++

CXXCPP = g++ -E

CXXDEPMODE = depmode=gcc3

CXXFLAGS = -g -O2

CYGPATH_W = echo

DEFS = -DHAVE_CONFIG_H

DEPDIR = .deps

DSYMUTIL = 

DUMPBIN = 

ECHO_C = 

ECHO_N = -n

ECHO_T = 

EGREP = /bin/grep -E

EXEEXT = 

FGREP = /bin/grep -F

GETTIMEFLAG = 

GL_LIBS = -lGL

GREP = /bin/grep

GTK_CFLAGS = -pthread -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/libpng12   -DGTK_DISABLE_DEPRECATED -DDISABLE_DEPRECATED -DGSEAL_ENABLE

GTK_LIBS = -pthread -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lm -lcairo -lpng12 -lpango-1.0 -lfreetype -lfontconfig -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lrt -lglib-2.0  

ICONV_LIBS = 

INSTALL = /usr/bin/install -c

INSTALL_DATA = ${INSTALL} -m 644

INSTALL_PROGRAM = ${INSTALL}

INSTALL_SCRIPT = ${INSTALL}

INSTALL_STRIP_PROGRAM = $(install_sh) -c -s

LD = /usr/bin/ld

LDFLAGS = 

LIBOBJS = 

LIBS = 

LIBTOOL = $(SHELL) $(top_builddir)/libtool

LIPO = 

LN_S = ln -s

LTLIBOBJS = 

MAKEINFO = ${SHELL} /home/jlfenton/Projects/linux/gens-gs-r7/missing --run makeinfo

MKDIR_P = /bin/mkdir -p

NASM = /usr/bin/nasm -O99 -f elf -D __GCC2 -g -F dwarf -I$(top_srcdir)/src/ -w-orphan-labels

NM = /usr/bin/nm -B

NMEDIT = 

OBJDUMP = objdump

OBJEXT = o

OTOOL = 

OTOOL64 = 

PACKAGE = gens

PACKAGE_BUGREPORT = [email]gerbilsoft@verizon.net[/email]

PACKAGE_NAME = gens

PACKAGE_STRING = gens 2.16.7

PACKAGE_TARNAME = gens

PACKAGE_VERSION = 2.16.7

PATH_SEPARATOR = :

PKG_CONFIG = /usr/bin/pkg-config

PNG_LIBS = -lpng

RANLIB = ranlib

RC = 

RT_LIBS = -lrt

SDL_CFLAGS = -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT

SDL_CONFIG = /usr/bin/sdl-config

SDL_LIBS = -L/usr/lib -lSDL

SED = /bin/sed

SET_MAKE = 

SHELL = /bin/bash

STRIP = strip

VERSION = 2.16.7

X11_CFLAGS =  

X11_LIBS = -lX11  

XMKMF = 

YASM = 

abs_builddir = /home/jlfenton/Projects/linux/gens-gs-r7

abs_srcdir = /home/jlfenton/Projects/linux/gens-gs-r7

abs_top_builddir = /home/jlfenton/Projects/linux/gens-gs-r7

abs_top_srcdir = /home/jlfenton/Projects/linux/gens-gs-r7

ac_ct_CC = gcc

ac_ct_CXX = g++

ac_ct_DUMPBIN = 

am__include = include

am__leading_dot = .

am__quote = 

am__tar = ${AMTAR} chof - "$$tardir"

am__untar = ${AMTAR} xf -

bindir = ${exec_prefix}/bin

build = i686-pc-linux-gnu

build_alias = 

build_cpu = i686

build_os = linux-gnu

build_vendor = pc

builddir = .

datadir = ${datarootdir}

datarootdir = ${prefix}/share

docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}

dvidir = ${docdir}

exec_prefix = ${prefix}

host = i686-pc-linux-gnu

host_alias = 

host_cpu = i686

host_os = linux-gnu

host_vendor = pc

htmldir = ${docdir}

includedir = ${prefix}/include

infodir = ${datarootdir}/info

install_sh = $(SHELL) /home/jlfenton/Projects/linux/gens-gs-r7/install-sh

libdir = ${exec_prefix}/lib

libexecdir = ${exec_prefix}/libexec

localedir = ${datarootdir}/locale

localstatedir = ${prefix}/var

lt_ECHO = echo

mandir = ${datarootdir}/man

mkdir_p = /bin/mkdir -p

oldincludedir = /usr/include

pdfdir = ${docdir}

prefix = /usr/local

program_transform_name = s,x,x,

psdir = ${docdir}

sbindir = ${exec_prefix}/sbin

sharedstatedir = ${prefix}/com

srcdir = .

sysconfdir = ${prefix}/etc

target = i686-pc-linux-gnu

target_alias = 

target_cpu = i686

target_os = linux-gnu

target_vendor = pc

top_build_prefix = 

top_builddir = .

top_srcdir = .

AUTOMAKE_OPTIONS = foreign

SUBDIRS = doc images xdg src

ACLOCAL_AMFLAGS = -I m4 --install

# The stamp file which is never created ensures that git_version.h is updated

# before every build. Having git_version.h in foo_SOURCES ensures a recompile

# of foo-bar.c if it is newer than the foo-bar.o file. Using noinst_foo_SOURCES

# instead of foo_SOURCES prevents shipping git_version.h in dist tarballs,

# which may cause false GIT_FOO readings.

BUILT_SOURCES = git_version.stamp

CLEANFILES = git_version.h

GIT_VERSION_CMD = $(SHELL) $(top_srcdir)/git_version.sh

all: $(BUILT_SOURCES) config.h

	$(MAKE) $(AM_MAKEFLAGS) all-recursive

.SUFFIXES:

am--refresh:

	@:

$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am $(srcdir)/git_version.am $(am__configure_deps)

	@for dep in $?; do \

	  case '$(am__configure_deps)' in \

	    *$$dep*) \

	      echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \

	      cd $(srcdir) && $(AUTOMAKE) --foreign  \

		&& exit 0; \

	      exit 1;; \

	  esac; \

	done; \

	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  Makefile'; \

	cd $(top_srcdir) && \

	  $(AUTOMAKE) --foreign  Makefile

.PRECIOUS: Makefile

Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status

	@case '$?' in \

	  *config.status*) \

	    echo ' $(SHELL) ./config.status'; \

	    $(SHELL) ./config.status;; \

	  *) \

	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \

	    cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \

	esac;

$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)

	$(SHELL) ./config.status --recheck

$(top_srcdir)/configure:  $(am__configure_deps)

	cd $(srcdir) && $(AUTOCONF)

$(ACLOCAL_M4):  $(am__aclocal_m4_deps)

	cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)

config.h: stamp-h1

	@if test ! -f $@; then \

	  rm -f stamp-h1; \

	  $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \

	else :; fi

stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status

	@rm -f stamp-h1

	cd $(top_builddir) && $(SHELL) ./config.status config.h

$(srcdir)/config.h.in:  $(am__configure_deps) 

	cd $(top_srcdir) && $(AUTOHEADER)

	rm -f stamp-h1

	touch $@

distclean-hdr:

	-rm -f config.h stamp-h1

mostlyclean-libtool:

	-rm -f *.lo

clean-libtool:

	-rm -rf .libs _libs

distclean-libtool:

	-rm -f libtool config.lt

# This directory's subdirectories are mostly independent; you can cd

# into them and run `make' without going through this Makefile.

# To change the values of `make' variables: instead of editing Makefiles,

# (1) if the variable is set in `config.status', edit `config.status'

#     (which will cause the Makefiles to be regenerated when you run `make');

# (2) otherwise, pass the desired values on the `make' command line.

$(RECURSIVE_TARGETS):

	@failcom='exit 1'; \

	for f in x $$MAKEFLAGS; do \

	  case $$f in \

	    *=* | --[!k]*);; \

	    *k*) failcom='fail=yes';; \

	  esac; \

	done; \

	dot_seen=no; \

	target=`echo $@ | sed s/-recursive//`; \

	list='$(SUBDIRS)'; for subdir in $$list; do \

	  echo "Making $$target in $$subdir"; \

	  if test "$$subdir" = "."; then \

	    dot_seen=yes; \

	    local_target="$$target-am"; \

	  else \

	    local_target="$$target"; \

	  fi; \

	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \

	  || eval $$failcom; \

	done; \

	if test "$$dot_seen" = "no"; then \

	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \

	fi; test -z "$$fail"

$(RECURSIVE_CLEAN_TARGETS):

	@failcom='exit 1'; \

	for f in x $$MAKEFLAGS; do \

	  case $$f in \

	    *=* | --[!k]*);; \

	    *k*) failcom='fail=yes';; \

	  esac; \

	done; \

	dot_seen=no; \

	case "$@" in \

	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \

	  *) list='$(SUBDIRS)' ;; \

	esac; \

	rev=''; for subdir in $$list; do \

	  if test "$$subdir" = "."; then :; else \

	    rev="$$subdir $$rev"; \

	  fi; \

	done; \

	rev="$$rev ."; \

	target=`echo $@ | sed s/-recursive//`; \

	for subdir in $$rev; do \

	  echo "Making $$target in $$subdir"; \

	  if test "$$subdir" = "."; then \

	    local_target="$$target-am"; \

	  else \

	    local_target="$$target"; \

	  fi; \

	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \

	  || eval $$failcom; \

	done && test -z "$$fail"

tags-recursive:

	list='$(SUBDIRS)'; for subdir in $$list; do \

	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \

	done

ctags-recursive:

	list='$(SUBDIRS)'; for subdir in $$list; do \

	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \

	done

ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)

	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \

	unique=`for i in $$list; do \

	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \

	  done | \

	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \

	      END { if (nonempty) { for (i in files) print i; }; }'`; \

	mkid -fID $$unique

tags: TAGS

TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \

		$(TAGS_FILES) $(LISP)

	tags=; \

	here=`pwd`; \

	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \

	  include_option=--etags-include; \

	  empty_fix=.; \

	else \

	  include_option=--include; \

	  empty_fix=; \

	fi; \

	list='$(SUBDIRS)'; for subdir in $$list; do \

	  if test "$$subdir" = .; then :; else \

	    test ! -f $$subdir/TAGS || \

	      tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \

	  fi; \

	done; \

	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \

	unique=`for i in $$list; do \

	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \

	  done | \

	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \

	      END { if (nonempty) { for (i in files) print i; }; }'`; \

	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \

	  test -n "$$unique" || unique=$$empty_fix; \

	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \

	    $$tags $$unique; \

	fi

ctags: CTAGS

CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \

		$(TAGS_FILES) $(LISP)

	tags=; \

	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \

	unique=`for i in $$list; do \

	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \

	  done | \

	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \

	      END { if (nonempty) { for (i in files) print i; }; }'`; \

	test -z "$(CTAGS_ARGS)$$tags$$unique" \

	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \

	     $$tags $$unique

GTAGS:

	here=`$(am__cd) $(top_builddir) && pwd` \

	  && cd $(top_srcdir) \

	  && gtags -i $(GTAGS_ARGS) $$here

distclean-tags:

	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags

distdir: $(DISTFILES)

	$(am__remove_distdir)

	test -d $(distdir) || mkdir $(distdir)

	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \

	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \

	list='$(DISTFILES)'; \

	  dist_files=`for file in $$list; do echo $$file; done | \

	  sed -e "s|^$$srcdirstrip/||;t" \

	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \

	case $$dist_files in \

	  */*) $(MKDIR_P) `echo "$$dist_files" | \

			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \

			   sort -u` ;; \

	esac; \

	for file in $$dist_files; do \

	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \

	  if test -d $$d/$$file; then \

	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \

	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \

	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \

	    fi; \

	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \

	  else \

	    test -f $(distdir)/$$file \

	    || cp -p $$d/$$file $(distdir)/$$file \

	    || exit 1; \

	  fi; \

	done

	list='$(DIST_SUBDIRS)'; for subdir in $$list; do \

	  if test "$$subdir" = .; then :; else \

	    test -d "$(distdir)/$$subdir" \

	    || $(MKDIR_P) "$(distdir)/$$subdir" \

	    || exit 1; \

	    distdir=`$(am__cd) $(distdir) && pwd`; \

	    top_distdir=`$(am__cd) $(top_distdir) && pwd`; \

	    (cd $$subdir && \

	      $(MAKE) $(AM_MAKEFLAGS) \

	        top_distdir="$$top_distdir" \

	        distdir="$$distdir/$$subdir" \

		am__remove_distdir=: \

		am__skip_length_check=: \

	        distdir) \

	      || exit 1; \

	  fi; \

	done

	$(MAKE) $(AM_MAKEFLAGS) \

	  top_distdir="$(top_distdir)" distdir="$(distdir)" \

	  dist-hook

	-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \

	  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \

	  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \

	  ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \

	|| chmod -R a+r $(distdir)

dist-gzip: distdir

	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz

	$(am__remove_distdir)

dist-bzip2: distdir

	tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2

	$(am__remove_distdir)

dist-lzma: distdir

	tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma

	$(am__remove_distdir)

dist-tarZ: distdir

	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z

	$(am__remove_distdir)

dist-shar: distdir

	shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz

	$(am__remove_distdir)

dist-zip: distdir

	-rm -f $(distdir).zip

	zip -rq $(distdir).zip $(distdir)

	$(am__remove_distdir)

dist dist-all: distdir

	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz

	$(am__remove_distdir)

# This target untars the dist file and tries a VPATH configuration.  Then

# it guarantees that the distribution is self-contained by making another

# tarfile.

distcheck: dist

	case '$(DIST_ARCHIVES)' in \

	*.tar.gz*) \

	  GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\

	*.tar.bz2*) \

	  bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\

	*.tar.lzma*) \

	  unlzma -c $(distdir).tar.lzma | $(am__untar) ;;\

	*.tar.Z*) \

	  uncompress -c $(distdir).tar.Z | $(am__untar) ;;\

	*.shar.gz*) \

	  GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\

	*.zip*) \

	  unzip $(distdir).zip ;;\

	esac

	chmod -R a-w $(distdir); chmod a+w $(distdir)

	mkdir $(distdir)/_build

	mkdir $(distdir)/_inst

	chmod a-w $(distdir)

	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \

	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \

	  && cd $(distdir)/_build \

	  && ../configure --srcdir=.. --prefix="$$dc_install_base" \

	    $(DISTCHECK_CONFIGURE_FLAGS) \

	  && $(MAKE) $(AM_MAKEFLAGS) \

	  && $(MAKE) $(AM_MAKEFLAGS) dvi \

	  && $(MAKE) $(AM_MAKEFLAGS) check \

	  && $(MAKE) $(AM_MAKEFLAGS) install \

	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \

	  && $(MAKE) $(AM_MAKEFLAGS) uninstall \

	  && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \

	        distuninstallcheck \

	  && chmod -R a-w "$$dc_install_base" \

	  && ({ \

	       (cd ../.. && umask 077 && mkdir "$$dc_destdir") \

	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \

	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \

	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \

	            distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \

	      } || { rm -rf "$$dc_destdir"; exit 1; }) \

	  && rm -rf "$$dc_destdir" \

	  && $(MAKE) $(AM_MAKEFLAGS) dist \

	  && rm -rf $(DIST_ARCHIVES) \

	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck

	$(am__remove_distdir)

	@(echo "$(distdir) archives ready for distribution: "; \

	  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \

	  sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'

distuninstallcheck:

	@cd $(distuninstallcheck_dir) \

	&& test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \

	   || { echo "ERROR: files left after uninstall:" ; \

	        if test -n "$(DESTDIR)"; then \

	          echo "  (check DESTDIR support)"; \

	        fi ; \

	        $(distuninstallcheck_listfiles) ; \

	        exit 1; } >&2

distcleancheck: distclean

	@if test '$(srcdir)' = . ; then \

	  echo "ERROR: distcleancheck can only run from a VPATH build" ; \

	  exit 1 ; \

	fi

	@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \

	  || { echo "ERROR: files left in build directory after distclean:" ; \

	       $(distcleancheck_listfiles) ; \

	       exit 1; } >&2

check-am: all-am

check: $(BUILT_SOURCES)

	$(MAKE) $(AM_MAKEFLAGS) check-recursive

all-am: Makefile config.h

installdirs: installdirs-recursive

installdirs-am:

install: $(BUILT_SOURCES)

	$(MAKE) $(AM_MAKEFLAGS) install-recursive

install-exec: install-exec-recursive

install-data: install-data-recursive

uninstall: uninstall-recursive

install-am: all-am

	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am

installcheck: installcheck-recursive

install-strip:

	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \

	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \

	  `test -z '$(STRIP)' || \

	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install

mostlyclean-generic:

clean-generic:

	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)

distclean-generic:

	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)

maintainer-clean-generic:

	@echo "This command is intended for maintainers to use"

	@echo "it deletes files that may require special tools to rebuild."

	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)

clean: clean-recursive

clean-am: clean-generic clean-libtool mostlyclean-am

distclean: distclean-recursive

	-rm -f $(am__CONFIG_DISTCLEAN_FILES)

	-rm -f Makefile

distclean-am: clean-am distclean-generic distclean-hdr \

	distclean-libtool distclean-tags

dvi: dvi-recursive

dvi-am:

html: html-recursive

info: info-recursive

info-am:

install-data-am:

install-dvi: install-dvi-recursive

install-exec-am:

install-html: install-html-recursive

install-info: install-info-recursive

install-man:

install-pdf: install-pdf-recursive

install-ps: install-ps-recursive

installcheck-am:

maintainer-clean: maintainer-clean-recursive

	-rm -f $(am__CONFIG_DISTCLEAN_FILES)

	-rm -rf $(top_srcdir)/autom4te.cache

	-rm -f Makefile

maintainer-clean-am: distclean-am maintainer-clean-generic

mostlyclean: mostlyclean-recursive

mostlyclean-am: mostlyclean-generic mostlyclean-libtool

pdf: pdf-recursive

pdf-am:

ps: ps-recursive

ps-am:

uninstall-am:

.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \

	install-strip

.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \

	all all-am am--refresh check check-am clean clean-generic \

	clean-libtool ctags ctags-recursive dist dist-all dist-bzip2 \

	dist-gzip dist-hook dist-lzma dist-shar dist-tarZ dist-zip \

	distcheck distclean distclean-generic distclean-hdr \

	distclean-libtool distclean-tags distcleancheck distdir \

	distuninstallcheck dvi dvi-am html html-am info info-am \

	install install-am install-data install-data-am install-dvi \

	install-dvi-am install-exec install-exec-am install-html \

	install-html-am install-info install-info-am install-man \

	install-pdf install-pdf-am install-ps install-ps-am \

	install-strip installcheck installcheck-am installdirs \

	installdirs-am maintainer-clean maintainer-clean-generic \

	mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \

	ps ps-am tags tags-recursive uninstall uninstall-am

git_version.stamp:

	@if test -f "$(srcdir)/git_version.h"; then \

		if test -f "git_version.h"; then :; \

		else \

			cp "$(srcdir)/git_version.h" "git_version.h"; \

		fi; \

	fi

	$(GIT_VERSION_CMD) -k -s $(top_srcdir) -o git_version.h

	@if test -s "$(srcdir)/git_version.h"; then \

		if cmp "$(srcdir)/git_version.h" "git_version.h"; then :; \

		else \

			echo "Error: $(srcdir)/git_version.h and git_version.h differ."; \

			echo "       You probably want to remove the former."; \

			exit 1; \

		fi; \

	fi

dist-hook: git_version.stamp

	if test -f "git_version.h"; then \

		$(SED) -e 's|^#undef GIT_IS_DIST.*|#define GIT_IS_DIST 1|' \

			"git_version.h" > "$(distdir)/git_version.h"; \

	fi

love:

	@echo "What is love?"

	@echo "Baby don't hurt me."

	@echo "Don't hurt me."

	@echo "No more."

me:

	@if [ "`id -u`" != "0" ]; then echo "What? Make it yourself!" ; else echo "Okay." ; fi

a:

	@echo >/dev/null

sandwich:

	@echo >/dev/null

# Tell versions [3.59,3.63) of GNU make to not export all variables.

# Otherwise a system limit (for SysV at least) may be exceeded.

.NOEXPORT:

Do you have the vaguest idea of what it's doing? Neither does ANYBODY else.
tongue.gif
biggrin.gif


Now let's look at the Wolf32X makefile.

Code:
MDLD = $(GENDEV)/m68k/bin/ld

MDAS = $(GENDEV)/m68k/bin/as

SHLD = $(GENDEV)/sh2/bin/ld

SHCC = $(GENDEV)/sh2/bin/gcc

SHAS = $(GENDEV)/sh2/bin/as

RM = rm -f

FLAGS = -m2 -mb -O3 -Wall -g -fomit-frame-pointer -DHAVE_FFBLK -DDOSISM -DWMODE=0 -I./src -I$(GENDEV)/sh2/include

OBJS = \

	src/sh2_crt0.o \

	src/id_ca.o \

	src/id_us.o \

	src/id_vh.o \

	src/misc.o \

	src/objs.o \

	src/vi_comm.o \

	src/vi_32x.o \

	src/wl_act1.o \

	src/wl_act2.o \

	src/wl_act3.o \

	src/wl_agent.o \

	src/wl_debug.o \

	src/sd_comm.o \

	src/sd_32x.o \

	src/adlibtables_wsw.o \

	src/wl_draw.o \

	src/wl_game.o \

	src/wl_inter.o \

	src/wl_main.o \

	src/wl_menu.o \

	src/wl_play.o \

	src/wl_state.o \

	src/wl_text.o \

	src/w3dsw_data.o \

	src/automap.o \

	src/debug_32x.o \

	src/debug_font.o

all: m68k_crt0.bin m68k_crt1.bin wolf32x.bin

wolf32x.bin: $(OBJS)

	$(SHLD) -T $(GENDEV)/sh2/lib/32x.ld -relax -small -e _start --oformat binary -o wolf32x.bin $(OBJS) $(GENDEV)/sh2/lib/libm.a $(GENDEV)/sh2/lib/libc.a $(GENDEV)/sh2/lib/libgcc.a $(GENDEV)/sh2/lib/libgcc-Os-4-200.a

m68k_crt0.bin: src/m68k_crt0_wsw.s

	$(MDAS) -m68000 --register-prefix-optional -o src/m68k_crt0_wsw.o src/m68k_crt0_wsw.s

	$(MDLD) -T $(GENDEV)/m68k/lib/md.ld --oformat binary -o src/m68k_crt0.bin src/m68k_crt0_wsw.o

m68k_crt1.bin: src/m68k_crt1.s

	$(MDAS) -m68000 --register-prefix-optional -o src/m68k_crt1.o src/m68k_crt1.s

	$(MDLD) -T $(GENDEV)/m68k/lib/md.ld --oformat binary -o src/m68k_crt1.bin src/m68k_crt1.o

%.o: %.c

	$(SHCC) $(FLAGS) -c $< -o $@

%.o: %.s

	$(SHAS) --small -o $@ $<

clean:

	$(RM) -f src/*.o src/*.out src/*.bin *.bin

Let's look at it bit by bit.

Code:
MDLD = $(GENDEV)/m68k/bin/ld

MDAS = $(GENDEV)/m68k/bin/as

SHLD = $(GENDEV)/sh2/bin/ld

SHCC = $(GENDEV)/sh2/bin/gcc

SHAS = $(GENDEV)/sh2/bin/as

RM = rm -f

We start by setting some variables for the programs we will use to build our game. Any time you see $( ), whatever is inside is a variable, be it one defined in the makefile, or a system environment variable passed in from the shell. To call the SH2 compiler, we just use $(SHCC) and make will replace that with $(GENDEV)/sh2/bin/gcc.

After that, most folks set variables for commonly used options for the programs.

Code:
FLAGS = -m2 -mb -O3 -Wall -g -fomit-frame-pointer -DHAVE_FFBLK -DDOSISM -DWMODE=0 -I./src -I$(GENDEV)/sh2/include

OBJS = \

	src/sh2_crt0.o \

	src/id_ca.o \

	src/id_us.o \

	src/id_vh.o \

	src/misc.o \

	src/objs.o \

	src/vi_comm.o \

	src/vi_32x.o \

	src/wl_act1.o \

	src/wl_act2.o \

	src/wl_act3.o \

	src/wl_agent.o \

	src/wl_debug.o \

	src/sd_comm.o \

	src/sd_32x.o \

	src/adlibtables_wsw.o \

	src/wl_draw.o \

	src/wl_game.o \

	src/wl_inter.o \

	src/wl_main.o \

	src/wl_menu.o \

	src/wl_play.o \

	src/wl_state.o \

	src/wl_text.o \

	src/w3dsw_data.o \

	src/automap.o \

	src/debug_32x.o \

	src/debug_font.o

In the above, FLAGS is used by gcc when compiling. OBJS is a list of object files we need to build the game. Using $(OBJS) is much easier than using the list over and over. The list can have multiple items per line. If you need more line, the "\" is used to tell make the next line continues the current line. VERY IMPORTANT! The indent used at the start of a line in a makefile MUST be a TAB!! Any other form of indentation (spaces) will cause the makefile to fail. That's why you should post makefiles in code tags for people unless you are trying to do a tutorial like this one; nearly all boards replace the tabs with spaces, which causes the makefile to fail if you try to use it directly.

Now let's get to the heart of the makefile - the rules.

Code:
all: m68k_crt0.bin m68k_crt1.bin wolf32x.bin

wolf32x.bin: $(OBJS)

	$(SHLD) -T $(GENDEV)/sh2/lib/32x.ld -relax -small -e _start --oformat binary -o wolf32x.bin $(OBJS) $(GENDEV)/sh2/lib/libm.a $(GENDEV)/sh2/lib/libc.a $(GENDEV)/sh2/lib/libgcc.a $(GENDEV)/sh2/lib/libgcc-Os-4-200.a

m68k_crt0.bin: src/m68k_crt0_wsw.s

	$(MDAS) -m68000 --register-prefix-optional -o src/m68k_crt0_wsw.o src/m68k_crt0_wsw.s

	$(MDLD) -T $(GENDEV)/m68k/lib/md.ld --oformat binary -o src/m68k_crt0.bin src/m68k_crt0_wsw.o

m68k_crt1.bin: src/m68k_crt1.s

	$(MDAS) -m68000 --register-prefix-optional -o src/m68k_crt1.o src/m68k_crt1.s

	$(MDLD) -T $(GENDEV)/m68k/lib/md.ld --oformat binary -o src/m68k_crt1.bin src/m68k_crt1.o

%.o: %.c

	$(SHCC) $(FLAGS) -c $< -o $@

%.o: %.s

	$(SHAS) --small -o $@ $<

clean:

	$(RM) -f src/*.o src/*.out src/*.bin *.bin

Each rule has this format:

Code:
target : dependencies

<tab>command1

<tab>command2

etc

The target is what make is currently trying to build; the dependencies tell make the list of things that need to be built before the target can be built. If the dependencies don't exist, make goes through the list looking for a rule to make the dependencies. Once the dependencies exist, make then uses the commands to build the target.

Code:
all: m68k_crt0.bin m68k_crt1.bin wolf32x.bin

The first target is all - when you just type "make", this will be the first target. Make sees that in order to make "all", it first needs to make m68k_Crt0.bin, m68k_crt1.bin, and wolf32x.bin. So it now looks for rules to make each of those.

Code:
m68k_crt0.bin: src/m68k_crt0_wsw.s

	$(MDAS) -m68000 --register-prefix-optional -o src/m68k_crt0_wsw.o src/m68k_crt0_wsw.s

	$(MDLD) -T $(GENDEV)/m68k/lib/md.ld --oformat binary -o src/m68k_crt0.bin src/m68k_crt0_wsw.o

There is the first. It says to make m68k_crt0.bin, it first needs src/m68k_crt0.s, which is the source file which always exists. So it now uses the commands to make the file. The first command calls the MD assembler to make the object file, and the second command calls the MD linker to convert the object file to binary. We now have the m68k_crt0.bin file.

The m68k_crt1.bin is done in a similar manner, so let's look at the third.

Code:
wolf32x.bin: $(OBJS)

	$(SHLD) -T $(GENDEV)/sh2/lib/32x.ld -relax -small -e _start --oformat binary -o wolf32x.bin $(OBJS) $(GENDEV)/sh2/lib/libm.a $(GENDEV)/sh2/lib/libc.a $(GENDEV)/sh2/lib/libgcc.a $(GENDEV)/sh2/lib/libgcc-Os-4-200.a

This tells make that in order to build wolf32x.bin, we first need $(OBJS). So the entire list of object files needs to exist before we can build wolf32x.bin. So make now looks for a rule for each object in the list of objects. However, we don't have any rules for them! That's where generic rules come in; while we don't have a specific rule for src/wl_play.o, we DO have a rule for %.o.

Code:
%.o: %.c

	$(SHCC) $(FLAGS) -c $< -o $@

%.o: %.s

	$(SHAS) --small -o $@ $<

These rules use pattern matching to allow use to make one rule for a bunch of files. Any time you can do multiple files with a generic rule, do so. We COULD make a rule for every single object in OBJS, but that would be silly! KISS!!!

The first generic rule above says that for any file ending in .o that is not handled by a specific rule, we need a dependency of the same file with an ending of .c. Is there a src/wl_play.c file? You bet your sweet bippy! So, we have met the rule, so we do the commands to build the object file. In this case, we call the SH2 compiler with this line.

Code:
	$(SHCC) $(FLAGS) -c $< -o $@

We saw the FLAGS earlier. What we are concerned with here are $< and $@. These are special symbols that stand for the dependency and the target file names, respectively. So $< is src/wl_play.c, and $@ is src/wl_play.o in our example. You can see how the pattern matching in generic rules work.

The second generic rule above similarly calls the SH2 assembler for assembly files as opposed to the compiler for c files. Between these two, we build all the object files in the list.

So now we have all the object files needed to build wolf32x.bin. Look at the command.

Code:
	$(SHLD) -T $(GENDEV)/sh2/lib/32x.ld -relax -small -e _start --oformat binary -o wolf32x.bin $(OBJS) $(GENDEV)/sh2/lib/libm.a $(GENDEV)/sh2/lib/libc.a $(GENDEV)/sh2/lib/libgcc.a $(GENDEV)/sh2/lib/libgcc-Os-4-200.a

That calls the SH2 linker, which makes the binary file from the list of object files as well as a few SH2 libraries that come with the compiler.

So there you have it! You have made the game using a few variables and rules. But what about that last line?

Code:
clean:

	$(RM) -f src/*.o src/*.out src/*.bin *.bin

That tells make that when you run "make clean" as the command in the shell, that there are no dependencies for the target "clean", so just run the command, which removes (deletes) the list of files. The wildcards mean all files in src ending in .o or .out or .bin will be deleted, as well as all files in the current directory that end in .bin. This allows us to delete all the various intermediate files, leaving just the source files.

Lesson 2 - Using assembly in your project

Let me start by saying inline assembly is a joke - don't bother. If you're going to use some assembly in your project, do it right and make a separate assembly file. It's actually easier than trying to remember all those ridiculous formats inline assembly require. It's also much more powerful.

Let's start with 68000 assembly; you may use it for Genesis or 32X projects. The standard compiler in the GNU Compiler Collection is AS; it is a part of binutils. You use "standard" Motorola syntax; the main differences from more common assemblers is the directives. Let's look at a few.

Code:
        .text

This sets the current section to the code section. With the linker script I supply with my Genesis toolchain, this puts any code or data following this directive into the rom.

Code:
        .data

This sets the current section to the data section. This puts any code or data following the directive into ram... with the proper startup code. In actuality, the code and data after this directive is still in the rom, but it has all references set so that when the startup code copies this code and data into ram, it will be referenced properly. If you look at the crt0.s file in my example, you'll see the loop that copies the code and data in the data section to ram.

Let's briefly look at a few handy directives you'll commonly use.

Code:
        .byte   0x00,0x01,0x02,0x03

        .word   0x0001,0x0203

        .long   0x00010203

These directives make tables of bytes, words, and longs, respectively. You can have one value (as in the line with .long), or multiple values (as in the other two). Values are stored in big-endian format since that is the format the 68000 uses. It does NOT handle alignment of the data, so be careful with words and longs, which must be on at least a word boundary to avoid an exception. Which brings us to our next directive.

Code:
        .align  2

This directive increments the current address to the next boundary specified. For the 68000 version of AS, the value of the align directive is in bytes. 2 means align to two bytes, 4 means align to 4 bytes, etc. When in doubt, use an align directive. Be liberal - it doesn't hurt and is needed before words, longs, and instructions to avoid an exception. Only byte data can be on a byte boundary. Most hacks or homebrew that work in emulators, but fail on real hardware, usually have data or code on a byte boundary somewhere because they forgot to use the align directive.

Here are two more handy directives.

Code:
        .ascii  "SEGA MD Example "

        .asciz  "SEGA MD Example "

These turn the string into a byte table of the ASCII values of the string. The difference is the asciz directive always adds a byte of 0x00 to the end of the string. That would be necessary for standard C strings, which MUST be null terminated.

Now let's look at labels. AS for the 68000 allows you to use labels exactly the same as in C. There are no special characters needed. The labels are case sensitive, so watch the capitalization! Any label you use in assembly that isn't defined in the file is assumed to be defined in another file, so there is no extern directive like you see in C. To define a label as being seen externally requires the global directive.

Code:
        .global gTicks

gTicks:

        .long   0

The global directive tells AS that the specified label may be seen by other files. Not using the global directive makes any other defined labels local, so there is no static directive like you see in C. Labels may refer to code or data. It's up to the programmer to use what the labels refer to properly. There is no type safety - you can easily use a C array of longs as bytes in an assembly file. You could use C code as data, or vice versa.

So now how do we use code in assembly that properly interacts with C? This is what is referred to as the Application Binary Interface, or ABI. The 68000 ABI says that when you call an assembly language function, you must save registers D2 to D7, and A2 to A6. D0, D1, A0, and A1 may be modified freely without saving or restoring them. If you can, only use those registers in your assembly for better speed. When you have to use more registers, you must push them on the stack before you do so, and pop them off the stack before returning. When the code is entered, the stack points to a long that is the return address. The long at the stack pointer + 4 is the first argument passed to the function, if any. The long at the stack pointer + 8 is the second argument passed to the function, if any. Additional arguments, if any, are accessed on the stack in a similar manner, being at + 12, + 16, + 20, etc. First, in this case, means the left-most argument, with other arguments going right accessed at higher addresses. All arguments are pushed as long values regardless of size! If you pass a byte from C to assembly, it's pushed on the stack as a long. The return value, if any, should be left in D0.

Let's look at a simple example.

Code:
        .align  2

| short set_sr(short new_sr);

| set SR, return previous SR

| entry: arg = SR value

| exit:  d0 = previous SR value

        .global set_sr

set_sr:

        moveq   #0,d0

        move.w  sr,d0

        move.l  4(sp),d1

        move.w  d1,sr

        rts

The "|" character indicates the start of a comment. You can also use /* */ for comments. Notice how we put an align directive before the function. Do this if your code ever follows byte data to avoid exceptions. Notice that since we only use D0 and D1, we don't need to save or restore any registers. Notice that the argument (short new_sr) is accessed at the stack pointer + 4. Notice how we put the old version of the status register in D0 as the return value.

Now let's look at a more complex example.

Code:
| short get_pad(short pad);

| return buttons for selected pad

| entry: arg = pad index (0 or 1)

| exit:  d0 = pad value (0 0 0 1 M X Y Z S A C B R L D U) or (0 0 0 0 0 0 0 0 S A C B R L D U)

        .global get_pad

get_pad:

        move.l  d2,-(sp)

        move.l  8(sp),d0        /* first arg is pad number */

        cmpi.w  #1,d0

        bhi     no_pad

        add.w   d0,d0

        addi.l  #0xA10003,d0    /* pad control register */

        movea.l d0,a0

        bsr.b   get_input       /* - 0 s a 0 0 d u - 1 c b r l d u */

        move.w  d0,d1

        andi.w  #0x0C00,d0

        bne.b   no_pad

        bsr.b   get_input       /* - 0 s a 0 0 d u - 1 c b r l d u */

        bsr.b   get_input       /* - 0 s a 0 0 0 0 - 1 c b m x y z */

        move.w  d0,d2

        bsr.b   get_input       /* - 0 s a 1 1 1 1 - 1 c b r l d u */

        andi.w  #0x0F00,d0      /* 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 */

        cmpi.w  #0x0F00,d0

        beq.b   common          /* six button pad */

        move.w  #0x010F,d2      /* three button pad */

common:

        lsl.b   #4,d2           /* - 0 s a 0 0 0 0 m x y z 0 0 0 0 */

        lsl.w   #4,d2           /* 0 0 0 0 m x y z 0 0 0 0 0 0 0 0 */

        andi.w  #0x303F,d1      /* 0 0 s a 0 0 0 0 0 0 c b r l d u */

        move.b  d1,d2           /* 0 0 0 0 m x y z 0 0 c b r l d u */

        lsr.w   #6,d1           /* 0 0 0 0 0 0 0 0 s a 0 0 0 0 0 0 */

        or.w    d1,d2           /* 0 0 0 0 m x y z s a c b r l d u */

        eori.w  #0x1FFF,d2      /* 0 0 0 1 M X Y Z S A C B R L D U */

        move.w  d2,d0

        move.l  (sp)+,d2

        rts

| 3-button/6-button pad not found

no_pad:

        move.w  #0xF000,d0      /* SEGA_CTRL_NONE */

        move.l  (sp)+,d2

        rts

| read single phase from controller

get_input:

        move.b  #0x00,(a0)

        nop

        nop

        move.b  (a0),d0

        move.b  #0x40,(a0)

        lsl.w   #8,d0

        move.b  (a0),d0

        rts

Notice how we use D2, so we must save it at the start, and restore it before we return. Because we pushed D2 onto the stack, any arguments are now one long further back on the stack; that is why the argument is now accessed at the stack pointer + 8 instead of + 4. Notice that we use both kinds of comments this function. Be sure to comment your code well enough that you can figure out what you are doing when you look at your code years later. Again, the return value is left in D0.

We are not going to cover more in this post as this should be enough to get you going. If you wish to know more of the directives, especially macros, consult the binutils documentation for AS.

Now let's look at AS for the SH2. It's very nearly the same as AS for the 68000 as all platforms use most of the same directives. So we mainly need to concern ourselves with the differences. The data directives are the same, but there is a difference as far as alignment goes - words need to be on word boundaries, like the 68000, but longs need to be on long boundaries; the 68000 can take longs on word or long boundaries - the SH2 cannot. Which brings the first difference in the directives - the value used with the align directive is the power of two used to set the current address. For example, the following are the same.

68000

Code:
        .align  2

        .align  4

        .align  16

SH2

Code:
        .align  1

        .align  2

        .align  4

SH2 code must be word aligned, but is slightly faster if aligned to a 16 byte boundary (.align 4).

Our next difference is in the labels - the SH2 assembler needs a "_" (underscore) character in front of labels if they are defined in C code, or if you wish to access the label from C. For example.

Code:
! Cache clear line function

! On entry: r4 = ptr - should be 16 byte aligned

        .align  4

        .global _CacheClearLine

_CacheClearLine:

        mov.l   _cache_flush,r0

        or      r0,r4

        mov     #0,r0

        mov.l   r0,@r4

        rts

        nop

This function would be called from C like this.

Code:
    CacheClearLine(&data_variable);

Some more differences immediately are apparent. You use "!" for a comment instead of "|"; you can still use /* */ for comments. The next difference is in how arguments are passed in; the first argument is passed in R4, the second in R5, the third in R6, and the fourth in R7. Any other arguments are pushed on the stack. Use no more than four arguments for best speed. On the SH2, you must save R8 to R14, and the PR register. For example.

Code:
! Entry: r4 = sound buffer to fill

        .global _fill_buffer

_fill_buffer:

        sts.l   pr,@-r15                /* save return address */

        mov.l   r8,@-r15

        mov.l   r9,@-r15

        mov.l   r10,@-r15

        mov.l   r11,@-r15

        mov.l   r12,@-r15

        mov.l   r13,@-r15

        mov.l   r14,@-r15

        mov     r4,r14

! lots of code left out here as it isn't needed for the example

        mov.l   @r15+,r14

        mov.l   @r15+,r13

        mov.l   @r15+,r12

        mov.l   @r15+,r11

        mov.l   @r15+,r10

        mov.l   @r15+,r9

        mov.l   @r15+,r8

        lds.l   @r15+,pr                /* restore return address */

        rts

        nop

A practical example of an assembly routine is my code to stretch the frame buffer contents - it takes a half a line of data and doubles it to fill an entire line.

Code:
! void ScreenStretch(int src, int width, int height, int interp);

! On entry: r4 = src pointer, r5 = width, r6 = height, r7 = interpolate

        .align  4

        .global _ScreenStretch

_ScreenStretch:

        cmp/pl  r7

        bt      ss_interp

! stretch screen without interpolation

0:

        mov     r5,r3

        shll    r3

        mov     r3,r2

        shll    r2

        add     r4,r3

        add     r4,r2

1:

        add     #-2,r3

        mov.w   @r3,r0

        extu.w  r0,r1

        shll16  r0

        or      r1,r0

        mov.l   r0,@-r2

        cmp/eq  r3,r4

        bf      1b

        /* next line */

        mov.w   ss_pitch,r0

        dt      r6

        bf/s    0b

        add     r0,r4

        rts

        nop

ss_interp:

! stretch screen with interpolation

0:

        mov     r5,r3

        shll    r3

        mov     r3,r2

        shll    r2

        add     r4,r3

        add     r4,r2

        mov     #0,r7

1:

        add     #-2,r3

        mov.w   @r3,r0

        mov.w   ss_mask,r1

        and     r0,r1               /* masked curr pixel */

        shll16  r0

        add     r1,r7               /* add to masked prev pixel */

        shlr    r7                  /* blended pixel */

        or      r7,r0               /* curr pixel << 16 | blended pixel */

        mov     r1,r7               /* masked prev pixel = masked curr pixel */

        mov.l   r0,@-r2

        cmp/eq  r3,r4

        bf      1b

        /* next line */

        mov.w   ss_pitch,r0

        dt      r6

        bf/s    0b

        add     r0,r4

        rts

        nop

ss_mask:

        .word   0x7BDE

ss_pitch:

        .word   640

As far as the sections go, with my 32X toolchain, the text section still goes to the rom, while data goes to the SDRAM.

Okay, that's enough for now. I encourage people to look at the assembly files in the Tic Tac Toe examples, as well as the Yeti3D example.
 
Top