From 53dd513176425872128ef26031d00952ef7a0628 Mon Sep 17 00:00:00 2001 From: uz Date: Sun, 28 May 2000 13:40:48 +0000 Subject: [PATCH] This commit was generated by cvs2svn to compensate for changes in r2, which included commits to RCS files with non-trunk default branches. git-svn-id: svn://svn.cc65.org/cc65/trunk@3 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- .cvsignore | 2 + doc/BUGS | 11 + doc/CREDITS | 62 + doc/ar65.txt | 152 + doc/ca65.txt | 1903 ++++++++ doc/cc65.txt | 600 +++ doc/cl65.txt | 183 + doc/coding.txt | 340 ++ doc/compile.txt | 159 + doc/debugging.txt | 151 + doc/internal.doc | 185 + doc/intro.txt | 206 + doc/ld65.txt | 655 +++ doc/library.txt | 235 + doc/newvers.txt | 511 ++ doc/readme.1st | 34 + doc/readme.txt | 31 + include/6502.h | 88 + include/_6525.h | 38 + include/_6526.h | 43 + include/_6545.h | 29 + include/_6551.h | 31 + include/_antic.h | 37 + include/_gtia.h | 78 + include/_pbi.h | 44 + include/_pia.h | 25 + include/_pokey.h | 55 + include/_sid.h | 43 + include/_vdc.h | 29 + include/_vic.h | 78 + include/ace.h | 86 + include/apple2.h | 58 + include/assert.h | 29 + include/atari.h | 75 + include/c128.h | 68 + include/c64.h | 68 + include/cbm.h | 74 + include/cbm610.h | 73 + include/conio.h | 158 + include/ctype.h | 70 + include/dbg.h | 96 + include/errno.h | 54 + include/fcntl.h | 39 + include/geos.h | 69 + include/geos/gconst.h | 63 + include/geos/gdisk.h | 81 + include/geos/gdlgbox.h | 102 + include/geos/gfile.h | 101 + include/geos/ggraph.h | 170 + include/geos/gmemory.h | 33 + include/geos/gmenu.h | 56 + include/geos/gprocess.h | 41 + include/geos/gsprite.h | 90 + include/geos/gstruct.h | 136 + include/geos/gsym.h | 302 ++ include/geos/gsys.h | 26 + include/iso646.h | 34 + include/joystick.h | 66 + include/limits.h | 46 + include/locale.h | 61 + include/mouse.h | 88 + include/pet.h | 24 + include/plus4.h | 81 + include/rs232.h | 125 + include/setjmp.h | 29 + include/stdarg.h | 33 + include/stddef.h | 34 + include/stdio.h | 101 + include/stdlib.h | 68 + include/string.h | 58 + include/time.h | 58 + libsrc/.cvsignore | 2 + libsrc/Makefile | 120 + libsrc/apple2/Makefile | 29 + libsrc/apple2/apple2.inc | 43 + libsrc/apple2/break.s | 109 + libsrc/apple2/cclear.s | 30 + libsrc/apple2/cgetc.s | 25 + libsrc/apple2/chline.s | 30 + libsrc/apple2/clrscr.s | 10 + libsrc/apple2/color.s | 20 + libsrc/apple2/cputc.s | 85 + libsrc/apple2/crt0.s | 112 + libsrc/apple2/ctype.s | 309 ++ libsrc/apple2/cvline.s | 30 + libsrc/apple2/kbhit.s | 18 + libsrc/apple2/read.s | 52 + libsrc/apple2/revers.s | 25 + libsrc/apple2/where.s | 18 + libsrc/apple2/write.s | 53 + libsrc/atari/Makefile | 32 + libsrc/atari/atari.inc | 1028 ++++ libsrc/atari/break.s | 106 + libsrc/atari/cclear.s | 34 + libsrc/atari/cgetc.s | 17 + libsrc/atari/chline.s | 34 + libsrc/atari/close.s | 36 + libsrc/atari/clrscr.s | 47 + libsrc/atari/color.s | 33 + libsrc/atari/conio.s | 20 + libsrc/atari/cputc.s | 190 + libsrc/atari/crt0.s | 191 + libsrc/atari/ctype.s | 309 ++ libsrc/atari/cvline.s | 35 + libsrc/atari/fdtable.s | 103 + libsrc/atari/getargs.s | 220 + libsrc/atari/gotox.s | 14 + libsrc/atari/gotoxy.s | 18 + libsrc/atari/gotoy.s | 12 + libsrc/atari/kbhit.s | 21 + libsrc/atari/open.s | 208 + libsrc/atari/oserror.s | 85 + libsrc/atari/read.s | 33 + libsrc/atari/readjoy.s | 30 + libsrc/atari/revers.s | 23 + libsrc/atari/rwcommon.s | 77 + libsrc/atari/savevec.s | 105 + libsrc/atari/where.s | 27 + libsrc/atari/write.s | 29 + libsrc/c128/Makefile | 28 + libsrc/c128/break.s | 127 + libsrc/c128/c128.inc | 185 + libsrc/c128/cgetc.s | 31 + libsrc/c128/clrscr.s | 14 + libsrc/c128/color.s | 33 + libsrc/c128/conio.s | 50 + libsrc/c128/cputc.s | 105 + libsrc/c128/crt0.s | 141 + libsrc/c128/dbgbreak.s | 24 + libsrc/c128/kbhit.s | 21 + libsrc/c128/readjoy.s | 41 + libsrc/c64/Makefile | 28 + libsrc/c64/break.s | 108 + libsrc/c64/c64.inc | 194 + libsrc/c64/cgetc.s | 61 + libsrc/c64/clrscr.s | 14 + libsrc/c64/color.s | 33 + libsrc/c64/conio.s | 23 + libsrc/c64/cputc.s | 105 + libsrc/c64/crt0.s | 132 + libsrc/c64/kbhit.s | 20 + libsrc/c64/mouse.s | 384 ++ libsrc/c64/read.s | 48 + libsrc/c64/readjoy.s | 41 + libsrc/c64/rs232.s | 699 +++ libsrc/c64/write.s | 47 + libsrc/cbm/Makefile | 32 + libsrc/cbm/c_acptr.s | 16 + libsrc/cbm/c_basin.s | 16 + libsrc/cbm/c_bsout.s | 14 + libsrc/cbm/c_ciout.s | 13 + libsrc/cbm/c_ckout.s | 24 + libsrc/cbm/c_clall.s | 13 + libsrc/cbm/c_close.s | 15 + libsrc/cbm/c_clrch.s | 12 + libsrc/cbm/c_iobase.s | 20 + libsrc/cbm/c_listen.s | 16 + libsrc/cbm/c_load.s | 26 + libsrc/cbm/c_open.s | 19 + libsrc/cbm/c_readst.s | 15 + libsrc/cbm/c_setlfs.s | 23 + libsrc/cbm/c_setnam.s | 24 + libsrc/cbm/c_talk.s | 18 + libsrc/cbm/c_unlsn.s | 16 + libsrc/cbm/c_untlk.s | 13 + libsrc/cbm/cbm.inc | 46 + libsrc/cbm/cclear.s | 30 + libsrc/cbm/chline.s | 30 + libsrc/cbm/clock.s | 22 + libsrc/cbm/ctype.s | 311 ++ libsrc/cbm/cvline.s | 30 + libsrc/cbm/getenv.s | 11 + libsrc/cbm/gotox.s | 15 + libsrc/cbm/gotoxy.s | 17 + libsrc/cbm/gotoy.s | 13 + libsrc/cbm/oserror.s | 77 + libsrc/cbm/revers.s | 26 + libsrc/cbm/where.s | 26 + libsrc/cbm610/Makefile | 29 + libsrc/cbm610/banking.s | 41 + libsrc/cbm610/break.s | 117 + libsrc/cbm610/cbm610.inc | 17 + libsrc/cbm610/cgetc.s | 49 + libsrc/cbm610/clrscr.s | 42 + libsrc/cbm610/color.s | 20 + libsrc/cbm610/conio.s | 19 + libsrc/cbm610/cputc.s | 102 + libsrc/cbm610/crt0.s | 411 ++ libsrc/cbm610/crtc.s | 58 + libsrc/cbm610/io.inc | 108 + libsrc/cbm610/kbhit.s | 23 + libsrc/cbm610/kirq.s | 100 + libsrc/cbm610/kplot.s | 76 + libsrc/cbm610/kscnkey.s | 146 + libsrc/cbm610/kudtim.s | 25 + libsrc/cbm610/page3.inc | 94 + libsrc/cbm610/pokesys.s | 38 + libsrc/cbm610/rs232.s | 631 +++ libsrc/cbm610/zeropage.inc | 100 + libsrc/common/.cvsignore | 40 + libsrc/common/Makefile | 39 + libsrc/common/_afailed.c | 21 + libsrc/common/_fdesc.s | 39 + libsrc/common/_file.h | 48 + libsrc/common/_file.s | 28 + libsrc/common/_fopen.c | 73 + libsrc/common/_hadd.c | 106 + libsrc/common/_heap.h | 43 + libsrc/common/_heap.s | 45 + libsrc/common/_hextab.c | 18 + libsrc/common/_oserror.s | 14 + libsrc/common/_printf.c | 253 + libsrc/common/_printf.h | 41 + libsrc/common/_stksize.s | 15 + libsrc/common/_swap.s | 62 + libsrc/common/_sys.s | 72 + libsrc/common/abort.c | 21 + libsrc/common/abs.s | 17 + libsrc/common/atexit.s | 69 + libsrc/common/atoi.s | 156 + libsrc/common/bsearch.c | 48 + libsrc/common/calloc.c | 25 + libsrc/common/copydata.s | 53 + libsrc/common/cprintf.c | 26 + libsrc/common/errno.inc | 24 + libsrc/common/errno.s | 33 + libsrc/common/errormsg.c | 33 + libsrc/common/fclose.c | 27 + libsrc/common/fdopen.c | 37 + libsrc/common/fgetc.c | 46 + libsrc/common/fgets.c | 59 + libsrc/common/fmisc.s | 81 + libsrc/common/fmode.inc | 15 + libsrc/common/fopen.c | 32 + libsrc/common/fprintf.c | 26 + libsrc/common/fputc.c | 34 + libsrc/common/fputs.c | 28 + libsrc/common/fread.c | 61 + libsrc/common/free.c | 65 + libsrc/common/freopen.c | 38 + libsrc/common/fwrite.c | 51 + libsrc/common/getchar.c | 22 + libsrc/common/getcpu.s | 37 + libsrc/common/gets.c | 52 + libsrc/common/isalnum.s | 15 + libsrc/common/isalpha.s | 15 + libsrc/common/isblank.s | 17 + libsrc/common/iscntrl.s | 16 + libsrc/common/isdigit.s | 15 + libsrc/common/isgraph.s | 16 + libsrc/common/islower.s | 15 + libsrc/common/isprint.s | 16 + libsrc/common/ispunct.s | 16 + libsrc/common/isspace.s | 15 + libsrc/common/isupper.s | 15 + libsrc/common/isxdigit.s | 15 + libsrc/common/itoa.s | 146 + libsrc/common/jmpvec.s | 14 + libsrc/common/labs.s | 15 + libsrc/common/locale.c | 78 + libsrc/common/longjmp.s | 48 + libsrc/common/ltoa.s | 160 + libsrc/common/malloc.c | 125 + libsrc/common/memchr.s | 57 + libsrc/common/memcmp.s | 54 + libsrc/common/memcpy.s | 113 + libsrc/common/memset.s | 52 + libsrc/common/perror.c | 24 + libsrc/common/printf.c | 26 + libsrc/common/putchar.c | 22 + libsrc/common/puts.c | 32 + libsrc/common/qsort.c | 68 + libsrc/common/rand.s | 63 + libsrc/common/realloc.c | 78 + libsrc/common/setjmp.s | 52 + libsrc/common/sprintf.c | 26 + libsrc/common/stkcheck.s | 48 + libsrc/common/strcat.s | 57 + libsrc/common/strchr.s | 41 + libsrc/common/strcmp.s | 35 + libsrc/common/strcoll.s | 13 + libsrc/common/strcpy.s | 33 + libsrc/common/strcspn.s | 54 + libsrc/common/strdup.c | 25 + libsrc/common/strerror.s | 29 + libsrc/common/stricmp.s | 59 + libsrc/common/strlen.s | 26 + libsrc/common/strlower.s | 45 + libsrc/common/strncat.s | 72 + libsrc/common/strncmp.s | 81 + libsrc/common/strncpy.s | 98 + libsrc/common/strpbrk.s | 60 + libsrc/common/strrchr.s | 47 + libsrc/common/strspn.s | 56 + libsrc/common/strstr.s | 97 + libsrc/common/strtok.c | 77 + libsrc/common/strupper.s | 45 + libsrc/common/strxfrm.c | 20 + libsrc/common/tolower.s | 22 + libsrc/common/toupper.s | 22 + libsrc/common/vcprintf.c | 44 + libsrc/common/vfprintf.c | 44 + libsrc/common/vprintf.c | 21 + libsrc/common/vsprintf.c | 45 + libsrc/common/zerobss.s | 47 + libsrc/conio/Makefile | 26 + libsrc/conio/cputhex.s | 39 + libsrc/conio/cputs.s | 36 + libsrc/conio/cursor.s | 26 + libsrc/conio/scrsize.s | 38 + libsrc/dbg/.cvsignore | 1 + libsrc/dbg/Makefile | 26 + libsrc/dbg/asmtab.s | 61 + libsrc/dbg/dbg.c | 1511 ++++++ libsrc/dbg/dbgdasm.s | 295 ++ libsrc/dbg/dbgdump.s | 81 + libsrc/dbg/dbgisram.s | 55 + libsrc/dbg/dbgsupp.s | 201 + libsrc/geos/Makefile | 35 + libsrc/geos/devel/Makefile | 17 + libsrc/geos/devel/crt0.s | 77 + libsrc/geos/devel/cvthead.s | 110 + libsrc/geos/disk/Makefile | 20 + libsrc/geos/disk/blkalloc.s | 24 + libsrc/geos/disk/calcblksfree.s | 19 + libsrc/geos/disk/changediskdevice.s | 18 + libsrc/geos/disk/chkdkgeos.s | 18 + libsrc/geos/disk/enterturbo.s | 13 + libsrc/geos/disk/exitturbo.s | 13 + libsrc/geos/disk/findbambit.s | 25 + libsrc/geos/disk/freeblock.s | 22 + libsrc/geos/disk/getblock.s | 26 + libsrc/geos/disk/getdirhead.s | 18 + libsrc/geos/disk/getptrcurdknm.s | 34 + libsrc/geos/disk/gettrse.s | 17 + libsrc/geos/disk/newdisk.s | 18 + libsrc/geos/disk/nxtblkalloc.s | 30 + libsrc/geos/disk/opendisk.s | 18 + libsrc/geos/disk/purgeturbo.s | 13 + libsrc/geos/disk/putblock.s | 26 + libsrc/geos/disk/putdirhead.s | 18 + libsrc/geos/disk/readblock.s | 26 + libsrc/geos/disk/readbuff.s | 22 + libsrc/geos/disk/setgeosdisk.s | 18 + libsrc/geos/disk/setnextfree.s | 23 + libsrc/geos/disk/verwriteblock.s | 26 + libsrc/geos/disk/writeblock.s | 26 + libsrc/geos/disk/writebuff.s | 22 + libsrc/geos/dlgbox/Makefile | 19 + libsrc/geos/dlgbox/dbget2lines.s | 17 + libsrc/geos/dlgbox/dlgboxfileselect.s | 62 + libsrc/geos/dlgbox/dlgboxgetstring.s | 40 + libsrc/geos/dlgbox/dlgboxok.s | 32 + libsrc/geos/dlgbox/dlgboxokcancel.s | 33 + libsrc/geos/dlgbox/dlgboxyesno.s | 33 + libsrc/geos/dlgbox/dodlgbox.s | 19 + libsrc/geos/dlgbox/rstrfrmdialogue.s | 13 + libsrc/geos/file/Makefile | 21 + libsrc/geos/file/appendrecord.s | 19 + libsrc/geos/file/closerecordfile.s | 18 + libsrc/geos/file/deletefile.s | 20 + libsrc/geos/file/deleterecord.s | 18 + libsrc/geos/file/findfile.s | 20 + libsrc/geos/file/findftypes.s | 28 + libsrc/geos/file/followchain.s | 26 + libsrc/geos/file/freefile.s | 20 + libsrc/geos/file/get1stdirentry.s | 19 + libsrc/geos/file/getfhdrinfo.s | 20 + libsrc/geos/file/getnxtdirentry.s | 19 + libsrc/geos/file/insertrecord.s | 18 + libsrc/geos/file/nextrecord.s | 18 + libsrc/geos/file/openrecordfile.s | 20 + libsrc/geos/file/pointrecord.s | 18 + libsrc/geos/file/previousrecord.s | 18 + libsrc/geos/file/readbyte.s | 17 + libsrc/geos/file/readfile.s | 29 + libsrc/geos/file/readrecord.s | 24 + libsrc/geos/file/renamefile.s | 24 + libsrc/geos/file/savefile.s | 20 + libsrc/geos/file/updaterecordfile.s | 18 + libsrc/geos/file/writerecord.s | 24 + libsrc/geos/graph/Makefile | 22 + libsrc/geos/graph/bitmapclip.s | 25 + libsrc/geos/graph/bitmapregs.s | 22 + libsrc/geos/graph/bitmapup.s | 17 + libsrc/geos/graph/bitotherclip.s | 37 + libsrc/geos/graph/drawline.s | 23 + libsrc/geos/graph/drawpoint.s | 17 + libsrc/geos/graph/framerectangle.s | 13 + libsrc/geos/graph/getcharwidth.s | 13 + libsrc/geos/graph/getintcharint.s | 21 + libsrc/geos/graph/graphicsstring.s | 17 + libsrc/geos/graph/hlineregs.s | 22 + libsrc/geos/graph/horizontalline.s | 19 + libsrc/geos/graph/imprintrectangle.s | 14 + libsrc/geos/graph/initdrawwindow.s | 25 + libsrc/geos/graph/invertline.s | 16 + libsrc/geos/graph/invertrectangle.s | 14 + libsrc/geos/graph/loadcharset.s | 17 + libsrc/geos/graph/pointregs.s | 25 + libsrc/geos/graph/putchar.s | 21 + libsrc/geos/graph/putdecimal.s | 18 + libsrc/geos/graph/putstring.s | 17 + libsrc/geos/graph/recoverline.s | 18 + libsrc/geos/graph/recoverrectangle.s | 14 + libsrc/geos/graph/rectangle.s | 13 + libsrc/geos/graph/setpattern.s | 13 + libsrc/geos/graph/testpoint.s | 21 + libsrc/geos/graph/usesystemfont.s | 13 + libsrc/geos/graph/verticalline.s | 24 + libsrc/geos/inc/const.inc | 418 ++ libsrc/geos/inc/diskdrv.inc | 42 + libsrc/geos/inc/geosmac.ca65.inc | 263 ++ libsrc/geos/inc/geossym.inc | 320 ++ libsrc/geos/inc/geossym2.inc | 11 + libsrc/geos/inc/inputdrv.inc | 16 + libsrc/geos/inc/jumptab.inc | 178 + libsrc/geos/inc/printdrv.inc | 14 + libsrc/geos/memory/Makefile | 19 + libsrc/geos/memory/clearram.s | 17 + libsrc/geos/memory/cmpfstring.s | 18 + libsrc/geos/memory/cmpstring.s | 16 + libsrc/geos/memory/copyfstring.s | 18 + libsrc/geos/memory/copystring.s | 16 + libsrc/geos/memory/crc.s | 21 + libsrc/geos/memory/doublepop.s | 18 + libsrc/geos/memory/doublespop.s | 21 + libsrc/geos/memory/fetchram.s | 17 + libsrc/geos/memory/fillram.s | 19 + libsrc/geos/memory/initram.s | 17 + libsrc/geos/memory/movedata.s | 24 + libsrc/geos/memory/reuregs.s | 21 + libsrc/geos/memory/stashram.s | 17 + libsrc/geos/memory/swapram.s | 18 + libsrc/geos/memory/verifyram.s | 19 + libsrc/geos/menuicon/Makefile | 18 + libsrc/geos/menuicon/doicons.s | 17 + libsrc/geos/menuicon/domenu.s | 18 + libsrc/geos/menuicon/dopreviousmenu.s | 13 + libsrc/geos/menuicon/gotofirstmenu.s | 13 + libsrc/geos/menuicon/recoverallmenus.s | 13 + libsrc/geos/menuicon/recovermenu.s | 13 + libsrc/geos/menuicon/redomenu.s | 13 + libsrc/geos/mousesprite/Makefile | 20 + libsrc/geos/mousesprite/clearmousemode.s | 13 + libsrc/geos/mousesprite/disablsprite.s | 16 + libsrc/geos/mousesprite/drawsprite.s | 23 + libsrc/geos/mousesprite/enablsprite.s | 16 + libsrc/geos/mousesprite/getnextchar.s | 14 + libsrc/geos/mousesprite/inittextprompt.s | 13 + libsrc/geos/mousesprite/ismseinregion.s | 17 + libsrc/geos/mousesprite/mouseoff.s | 13 + libsrc/geos/mousesprite/mouseup.s | 13 + libsrc/geos/mousesprite/possprite.s | 32 + libsrc/geos/mousesprite/promptoff.s | 13 + libsrc/geos/mousesprite/prompton.s | 24 + libsrc/geos/mousesprite/startmousemode.s | 15 + libsrc/geos/process/Makefile | 17 + libsrc/geos/process/processblock.s | 25 + libsrc/geos/process/processfreeze.s | 23 + .../geos/process/processinitrestartenable.s | 34 + libsrc/geos/process/sleep.s | 20 + libsrc/geos/system/Makefile | 18 + libsrc/geos/system/callroutine.s | 13 + libsrc/geos/system/enterdesktop.s | 13 + libsrc/geos/system/firstinit.s | 13 + libsrc/geos/system/getrandom.s | 13 + libsrc/geos/system/getserialnumber.s | 19 + libsrc/geos/system/initdoneio.s | 16 + libsrc/geos/system/mainloop.s | 13 + libsrc/geos/system/panic.s | 13 + libsrc/geos/system/setdevice.s | 13 + libsrc/geos/system/tobasic.s | 13 + libsrc/pet/Makefile | 27 + libsrc/pet/break.s | 109 + libsrc/pet/cgetc.s | 68 + libsrc/pet/clrscr.s | 50 + libsrc/pet/color.s | 18 + libsrc/pet/conio.s | 24 + libsrc/pet/cputc.s | 120 + libsrc/pet/crt0.s | 125 + libsrc/pet/kbhit.s | 20 + libsrc/pet/pet.inc | 32 + libsrc/plus4/Makefile | 28 + libsrc/plus4/break.s | 108 + libsrc/plus4/cgetc.s | 48 + libsrc/plus4/clrscr.s | 15 + libsrc/plus4/color.s | 33 + libsrc/plus4/conio.s | 41 + libsrc/plus4/cputc.s | 105 + libsrc/plus4/crt0.s | 129 + libsrc/plus4/kbhit.s | 21 + libsrc/plus4/plus4.inc | 66 + libsrc/plus4/readjoy.s | 29 + libsrc/runtime/Makefile | 42 + libsrc/runtime/add.s | 47 + libsrc/runtime/addeqsp.s | 24 + libsrc/runtime/and.s | 23 + libsrc/runtime/aslax1.s | 17 + libsrc/runtime/aslax2.s | 18 + libsrc/runtime/aslax3.s | 20 + libsrc/runtime/asleax1.s | 19 + libsrc/runtime/asleax2.s | 23 + libsrc/runtime/asleax3.s | 27 + libsrc/runtime/asrax1.s | 16 + libsrc/runtime/asrax2.s | 19 + libsrc/runtime/asrax3.s | 24 + libsrc/runtime/asreax1.s | 20 + libsrc/runtime/asreax2.s | 26 + libsrc/runtime/asreax3.s | 32 + libsrc/runtime/bneg.s | 21 + libsrc/runtime/bpushbsp.s | 18 + libsrc/runtime/call.s | 13 + libsrc/runtime/compl.s | 17 + libsrc/runtime/dec.s | 31 + libsrc/runtime/div.s | 22 + libsrc/runtime/enter.s | 18 + libsrc/runtime/eq.s | 17 + libsrc/runtime/ge.s | 17 + libsrc/runtime/gt.s | 18 + libsrc/runtime/icmp.s | 44 + libsrc/runtime/inc.s | 48 + libsrc/runtime/ladd.s | 32 + libsrc/runtime/laddeq.s | 53 + libsrc/runtime/laddeqsp.s | 34 + libsrc/runtime/land.s | 30 + libsrc/runtime/lbneg.s | 22 + libsrc/runtime/lcmp.s | 50 + libsrc/runtime/lcompl.s | 26 + libsrc/runtime/lconvert.s | 88 + libsrc/runtime/ldai.s | 18 + libsrc/runtime/ldasp.s | 17 + libsrc/runtime/ldau0sp.s | 21 + libsrc/runtime/ldaui.s | 18 + libsrc/runtime/ldauisp.s | 24 + libsrc/runtime/ldausp.s | 16 + libsrc/runtime/ldaxi.s | 19 + libsrc/runtime/ldaxsp.s | 28 + libsrc/runtime/ldeax.s | 38 + libsrc/runtime/ldeaxi.s | 25 + libsrc/runtime/ldec.s | 26 + libsrc/runtime/ldiv.s | 20 + libsrc/runtime/le.s | 17 + libsrc/runtime/leave.s | 37 + libsrc/runtime/leaysp.s | 33 + libsrc/runtime/leq.s | 15 + libsrc/runtime/lge.s | 13 + libsrc/runtime/lgt.s | 14 + libsrc/runtime/linc.s | 22 + libsrc/runtime/lle.s | 13 + libsrc/runtime/llt.s | 12 + libsrc/runtime/lmod.s | 26 + libsrc/runtime/lmul.s | 63 + libsrc/runtime/lne.s | 14 + libsrc/runtime/lneg.s | 31 + libsrc/runtime/lor.s | 30 + libsrc/runtime/lpop.s | 25 + libsrc/runtime/lpush.s | 34 + libsrc/runtime/lrsub.s | 33 + libsrc/runtime/lruntime.s | 100 + libsrc/runtime/lsave.s | 28 + libsrc/runtime/lshelp.s | 76 + libsrc/runtime/lshl.s | 95 + libsrc/runtime/lshr.s | 185 + libsrc/runtime/lsub.s | 36 + libsrc/runtime/lsubeq.s | 57 + libsrc/runtime/lsubeqsp.s | 37 + libsrc/runtime/lswap.s | 18 + libsrc/runtime/lswitch.s | 87 + libsrc/runtime/lt.s | 17 + libsrc/runtime/ltest.s | 22 + libsrc/runtime/ludiv.s | 93 + libsrc/runtime/luge.s | 13 + libsrc/runtime/lugt.s | 14 + libsrc/runtime/lule.s | 13 + libsrc/runtime/lult.s | 12 + libsrc/runtime/lumod.s | 21 + libsrc/runtime/lxor.s | 32 + libsrc/runtime/makebool.s | 60 + libsrc/runtime/mod.s | 23 + libsrc/runtime/mul.s | 43 + libsrc/runtime/ne.s | 18 + libsrc/runtime/neg.s | 21 + libsrc/runtime/or.s | 23 + libsrc/runtime/popsreg.s | 22 + libsrc/runtime/push.s | 83 + libsrc/runtime/pushb.s | 24 + libsrc/runtime/pushbsp.s | 17 + libsrc/runtime/pushw.s | 25 + libsrc/runtime/pushwsp.s | 20 + libsrc/runtime/rsub.s | 29 + libsrc/runtime/runtime.s | 265 ++ libsrc/runtime/shelp.s | 48 + libsrc/runtime/shl.s | 69 + libsrc/runtime/shr.s | 127 + libsrc/runtime/shrax1.s | 15 + libsrc/runtime/shrax2.s | 18 + libsrc/runtime/shrax3.s | 20 + libsrc/runtime/shreax1.s | 18 + libsrc/runtime/shreax2.s | 22 + libsrc/runtime/shreax3.s | 26 + libsrc/runtime/staxsp.s | 20 + libsrc/runtime/steaxsp.s | 27 + libsrc/runtime/sub.s | 31 + libsrc/runtime/subeqsp.s | 27 + libsrc/runtime/swap.s | 25 + libsrc/runtime/switch.s | 88 + libsrc/runtime/test.s | 17 + libsrc/runtime/udiv.s | 54 + libsrc/runtime/uge.s | 20 + libsrc/runtime/ugt.s | 18 + libsrc/runtime/ule.s | 18 + libsrc/runtime/ult.s | 18 + libsrc/runtime/umod.s | 28 + libsrc/runtime/xor.s | 23 + samples/.cvsignore | 7 + samples/Makefile | 54 + samples/c1541.rsp | 9 + samples/hello.c | 82 + samples/nachtm.c | 1185 +++++ samples/sieve.c | 67 + src/.cvsignore | 1 + src/ar65/.cvsignore | 3 + src/ar65/add.c | 83 + src/ar65/add.h | 58 + src/ar65/del.c | 84 + src/ar65/del.h | 58 + src/ar65/error.c | 106 + src/ar65/error.h | 87 + src/ar65/exports.c | 149 + src/ar65/exports.h | 62 + src/ar65/extract.c | 83 + src/ar65/extract.h | 58 + src/ar65/fileio.c | 210 + src/ar65/fileio.h | 88 + src/ar65/global.c | 51 + src/ar65/global.h | 58 + src/ar65/library.c | 470 ++ src/ar65/library.h | 88 + src/ar65/list.c | 83 + src/ar65/list.h | 58 + src/ar65/main.c | 140 + src/ar65/make/gcc.mak | 56 + src/ar65/make/watcom.mak | 123 + src/ar65/mem.c | 84 + src/ar65/mem.h | 67 + src/ar65/objdata.c | 207 + src/ar65/objdata.h | 111 + src/ar65/objfile.c | 288 ++ src/ar65/objfile.h | 72 + src/ca65/.cvsignore | 2 + src/ca65/condasm.c | 418 ++ src/ca65/condasm.h | 62 + src/ca65/ea.c | 198 + src/ca65/ea.h | 69 + src/ca65/error.c | 291 ++ src/ca65/error.h | 210 + src/ca65/expr.c | 1566 ++++++ src/ca65/expr.h | 127 + src/ca65/fname.c | 74 + src/ca65/fname.h | 61 + src/ca65/fragment.c | 51 + src/ca65/fragment.h | 78 + src/ca65/global.c | 76 + src/ca65/global.h | 82 + src/ca65/instr.c | 748 +++ src/ca65/instr.h | 156 + src/ca65/listing.c | 437 ++ src/ca65/listing.h | 116 + src/ca65/macpack.c | 154 + src/ca65/macpack.h | 69 + src/ca65/macro.c | 769 +++ src/ca65/macro.h | 90 + src/ca65/main.c | 564 +++ src/ca65/make/gcc.mak | 63 + src/ca65/make/watcom.mak | 140 + src/ca65/mem.c | 84 + src/ca65/mem.h | 67 + src/ca65/objcode.c | 842 ++++ src/ca65/objcode.h | 168 + src/ca65/objfile.c | 351 ++ src/ca65/objfile.h | 121 + src/ca65/options.c | 202 + src/ca65/options.h | 78 + src/ca65/pseudo.c | 1190 +++++ src/ca65/pseudo.h | 74 + src/ca65/scanner.c | 1202 +++++ src/ca65/scanner.h | 283 ++ src/ca65/strexpr.c | 97 + src/ca65/strexpr.h | 61 + src/ca65/symentry.h | 57 + src/ca65/symtab.c | 1127 +++++ src/ca65/symtab.h | 145 + src/ca65/toknode.c | 117 + src/ca65/toknode.h | 94 + src/ca65/ulabel.c | 242 + src/ca65/ulabel.h | 77 + src/cc65/.cvsignore | 29 + src/cc65/anonname.c | 59 + src/cc65/anonname.h | 58 + src/cc65/asmcode.c | 115 + src/cc65/asmcode.h | 86 + src/cc65/asmlabel.c | 55 + src/cc65/asmlabel.h | 56 + src/cc65/asmline.c | 184 + src/cc65/asmline.h | 90 + src/cc65/check.c | 102 + src/cc65/check.h | 96 + src/cc65/codegen.c | 3712 +++++++++++++++ src/cc65/codegen.h | 395 ++ src/cc65/copyleft.jrd | 32 + src/cc65/ctrans.c | 137 + src/cc65/ctrans.h | 57 + src/cc65/datatype.c | 568 +++ src/cc65/datatype.h | 223 + src/cc65/declare.c | 938 ++++ src/cc65/declare.h | 79 + src/cc65/error.c | 310 ++ src/cc65/error.h | 206 + src/cc65/expr.c | 2980 ++++++++++++ src/cc65/expr.h | 125 + src/cc65/funcdesc.c | 75 + src/cc65/funcdesc.h | 85 + src/cc65/function.c | 239 + src/cc65/function.h | 64 + src/cc65/global.c | 66 + src/cc65/global.h | 88 + src/cc65/goto.c | 91 + src/cc65/goto.h | 59 + src/cc65/hashstr.c | 60 + src/cc65/hashstr.h | 57 + src/cc65/ident.c | 55 + src/cc65/ident.h | 71 + src/cc65/include.c | 162 + src/cc65/include.h | 45 + src/cc65/io.c | 184 + src/cc65/io.h | 86 + src/cc65/litpool.c | 182 + src/cc65/litpool.h | 93 + src/cc65/locals.c | 469 ++ src/cc65/locals.h | 68 + src/cc65/loop.c | 81 + src/cc65/loop.h | 54 + src/cc65/macrotab.c | 329 ++ src/cc65/macrotab.h | 122 + src/cc65/main.c | 679 +++ src/cc65/make/cc65.mak | 40 + src/cc65/make/gcc.mak | 77 + src/cc65/make/watcom.mak | 157 + src/cc65/mem.c | 605 +++ src/cc65/mem.h | 76 + src/cc65/optimize.c | 4182 +++++++++++++++++ src/cc65/optimize.h | 68 + src/cc65/pragma.c | 199 + src/cc65/pragma.h | 58 + src/cc65/preproc.c | 882 ++++ src/cc65/preproc.h | 51 + src/cc65/scanner.c | 814 ++++ src/cc65/scanner.h | 217 + src/cc65/stdfunc.c | 176 + src/cc65/stdfunc.h | 67 + src/cc65/stmt.c | 733 +++ src/cc65/stmt.h | 30 + src/cc65/symentry.c | 154 + src/cc65/symentry.h | 144 + src/cc65/symtab.c | 990 ++++ src/cc65/symtab.h | 187 + src/cc65/util.c | 63 + src/cc65/util.h | 38 + src/cl65/.cvsignore | 3 + src/cl65/error.c | 107 + src/cl65/error.h | 87 + src/cl65/global.c | 49 + src/cl65/global.h | 56 + src/cl65/main.c | 853 ++++ src/cl65/make/gcc.mak | 47 + src/cl65/make/watcom.mak | 95 + src/cl65/mem.c | 85 + src/cl65/mem.h | 67 + src/cl65/spawn.c | 95 + src/cl65/spawn.h | 75 + src/common/.cvsignore | 3 + src/common/bitops.c | 128 + src/common/bitops.h | 72 + src/common/exprdefs.h | 124 + src/common/filepos.h | 65 + src/common/hashstr.c | 56 + src/common/hashstr.h | 57 + src/common/libdefs.h | 72 + src/common/make/gcc.mak | 48 + src/common/make/watcom.mak | 92 + src/common/objdefs.h | 86 + src/common/optdefs.h | 80 + src/common/segdefs.h | 83 + src/common/symdefs.h | 63 + src/common/version.h | 58 + src/geos/headergen.sh | 199 + src/ld65/.cvsignore | 5 + src/ld65/bin.c | 258 + src/ld65/bin.h | 78 + src/ld65/binfmt.c | 89 + src/ld65/binfmt.h | 73 + src/ld65/config.c | 1202 +++++ src/ld65/config.h | 147 + src/ld65/dbgsyms.c | 210 + src/ld65/dbgsyms.h | 92 + src/ld65/error.c | 106 + src/ld65/error.h | 87 + src/ld65/exports.c | 662 +++ src/ld65/exports.h | 164 + src/ld65/expr.c | 622 +++ src/ld65/expr.h | 97 + src/ld65/extsyms.c | 237 + src/ld65/extsyms.h | 99 + src/ld65/fileio.c | 269 ++ src/ld65/fileio.h | 111 + src/ld65/global.c | 59 + src/ld65/global.h | 66 + src/ld65/library.c | 285 ++ src/ld65/library.h | 59 + src/ld65/main.c | 391 ++ src/ld65/make/gcc.mak | 66 + src/ld65/make/watcom.mak | 136 + src/ld65/mapfile.c | 170 + src/ld65/mapfile.h | 64 + src/ld65/mem.c | 84 + src/ld65/mem.h | 67 + src/ld65/o65.c | 1067 +++++ src/ld65/o65.h | 119 + src/ld65/objdata.c | 112 + src/ld65/objdata.h | 106 + src/ld65/objfile.c | 225 + src/ld65/objfile.h | 80 + src/ld65/scanner.c | 535 +++ src/ld65/scanner.h | 198 + src/ld65/segments.c | 666 +++ src/ld65/segments.h | 152 + src/ld65/target.c | 351 ++ src/ld65/target.h | 64 + src/ld65/version.h | 58 + src/make/gcc.mak | 32 + src/make/watcom.mak | 42 + testcode/compiler/pptest1.c | 6 + testcode/compiler/pptest2.c | 19 + testcode/compiler/pptest3.c | 16 + testcode/compiler/pptest4.c | 3 + util/atari/ataricvt.c | 17 + 847 files changed, 91345 insertions(+) create mode 100644 .cvsignore create mode 100644 doc/BUGS create mode 100644 doc/CREDITS create mode 100644 doc/ar65.txt create mode 100644 doc/ca65.txt create mode 100644 doc/cc65.txt create mode 100644 doc/cl65.txt create mode 100644 doc/coding.txt create mode 100644 doc/compile.txt create mode 100644 doc/debugging.txt create mode 100644 doc/internal.doc create mode 100644 doc/intro.txt create mode 100644 doc/ld65.txt create mode 100644 doc/library.txt create mode 100644 doc/newvers.txt create mode 100644 doc/readme.1st create mode 100644 doc/readme.txt create mode 100644 include/6502.h create mode 100644 include/_6525.h create mode 100644 include/_6526.h create mode 100644 include/_6545.h create mode 100644 include/_6551.h create mode 100644 include/_antic.h create mode 100644 include/_gtia.h create mode 100644 include/_pbi.h create mode 100644 include/_pia.h create mode 100644 include/_pokey.h create mode 100644 include/_sid.h create mode 100644 include/_vdc.h create mode 100644 include/_vic.h create mode 100644 include/ace.h create mode 100644 include/apple2.h create mode 100644 include/assert.h create mode 100644 include/atari.h create mode 100644 include/c128.h create mode 100644 include/c64.h create mode 100644 include/cbm.h create mode 100644 include/cbm610.h create mode 100644 include/conio.h create mode 100644 include/ctype.h create mode 100644 include/dbg.h create mode 100644 include/errno.h create mode 100644 include/fcntl.h create mode 100644 include/geos.h create mode 100644 include/geos/gconst.h create mode 100644 include/geos/gdisk.h create mode 100644 include/geos/gdlgbox.h create mode 100644 include/geos/gfile.h create mode 100644 include/geos/ggraph.h create mode 100644 include/geos/gmemory.h create mode 100644 include/geos/gmenu.h create mode 100644 include/geos/gprocess.h create mode 100644 include/geos/gsprite.h create mode 100644 include/geos/gstruct.h create mode 100644 include/geos/gsym.h create mode 100644 include/geos/gsys.h create mode 100644 include/iso646.h create mode 100644 include/joystick.h create mode 100644 include/limits.h create mode 100644 include/locale.h create mode 100644 include/mouse.h create mode 100644 include/pet.h create mode 100644 include/plus4.h create mode 100644 include/rs232.h create mode 100644 include/setjmp.h create mode 100644 include/stdarg.h create mode 100644 include/stddef.h create mode 100644 include/stdio.h create mode 100644 include/stdlib.h create mode 100644 include/string.h create mode 100644 include/time.h create mode 100644 libsrc/.cvsignore create mode 100644 libsrc/Makefile create mode 100644 libsrc/apple2/Makefile create mode 100644 libsrc/apple2/apple2.inc create mode 100644 libsrc/apple2/break.s create mode 100644 libsrc/apple2/cclear.s create mode 100644 libsrc/apple2/cgetc.s create mode 100644 libsrc/apple2/chline.s create mode 100644 libsrc/apple2/clrscr.s create mode 100644 libsrc/apple2/color.s create mode 100644 libsrc/apple2/cputc.s create mode 100644 libsrc/apple2/crt0.s create mode 100644 libsrc/apple2/ctype.s create mode 100644 libsrc/apple2/cvline.s create mode 100644 libsrc/apple2/kbhit.s create mode 100644 libsrc/apple2/read.s create mode 100644 libsrc/apple2/revers.s create mode 100644 libsrc/apple2/where.s create mode 100644 libsrc/apple2/write.s create mode 100644 libsrc/atari/Makefile create mode 100644 libsrc/atari/atari.inc create mode 100644 libsrc/atari/break.s create mode 100644 libsrc/atari/cclear.s create mode 100644 libsrc/atari/cgetc.s create mode 100644 libsrc/atari/chline.s create mode 100644 libsrc/atari/close.s create mode 100644 libsrc/atari/clrscr.s create mode 100644 libsrc/atari/color.s create mode 100644 libsrc/atari/conio.s create mode 100644 libsrc/atari/cputc.s create mode 100644 libsrc/atari/crt0.s create mode 100644 libsrc/atari/ctype.s create mode 100644 libsrc/atari/cvline.s create mode 100644 libsrc/atari/fdtable.s create mode 100644 libsrc/atari/getargs.s create mode 100644 libsrc/atari/gotox.s create mode 100644 libsrc/atari/gotoxy.s create mode 100644 libsrc/atari/gotoy.s create mode 100644 libsrc/atari/kbhit.s create mode 100644 libsrc/atari/open.s create mode 100644 libsrc/atari/oserror.s create mode 100644 libsrc/atari/read.s create mode 100644 libsrc/atari/readjoy.s create mode 100644 libsrc/atari/revers.s create mode 100644 libsrc/atari/rwcommon.s create mode 100644 libsrc/atari/savevec.s create mode 100644 libsrc/atari/where.s create mode 100644 libsrc/atari/write.s create mode 100644 libsrc/c128/Makefile create mode 100644 libsrc/c128/break.s create mode 100644 libsrc/c128/c128.inc create mode 100644 libsrc/c128/cgetc.s create mode 100644 libsrc/c128/clrscr.s create mode 100644 libsrc/c128/color.s create mode 100644 libsrc/c128/conio.s create mode 100644 libsrc/c128/cputc.s create mode 100644 libsrc/c128/crt0.s create mode 100644 libsrc/c128/dbgbreak.s create mode 100644 libsrc/c128/kbhit.s create mode 100644 libsrc/c128/readjoy.s create mode 100644 libsrc/c64/Makefile create mode 100644 libsrc/c64/break.s create mode 100644 libsrc/c64/c64.inc create mode 100644 libsrc/c64/cgetc.s create mode 100644 libsrc/c64/clrscr.s create mode 100644 libsrc/c64/color.s create mode 100644 libsrc/c64/conio.s create mode 100644 libsrc/c64/cputc.s create mode 100644 libsrc/c64/crt0.s create mode 100644 libsrc/c64/kbhit.s create mode 100644 libsrc/c64/mouse.s create mode 100644 libsrc/c64/read.s create mode 100644 libsrc/c64/readjoy.s create mode 100644 libsrc/c64/rs232.s create mode 100644 libsrc/c64/write.s create mode 100644 libsrc/cbm/Makefile create mode 100644 libsrc/cbm/c_acptr.s create mode 100644 libsrc/cbm/c_basin.s create mode 100644 libsrc/cbm/c_bsout.s create mode 100644 libsrc/cbm/c_ciout.s create mode 100644 libsrc/cbm/c_ckout.s create mode 100644 libsrc/cbm/c_clall.s create mode 100644 libsrc/cbm/c_close.s create mode 100644 libsrc/cbm/c_clrch.s create mode 100644 libsrc/cbm/c_iobase.s create mode 100644 libsrc/cbm/c_listen.s create mode 100644 libsrc/cbm/c_load.s create mode 100644 libsrc/cbm/c_open.s create mode 100644 libsrc/cbm/c_readst.s create mode 100644 libsrc/cbm/c_setlfs.s create mode 100644 libsrc/cbm/c_setnam.s create mode 100644 libsrc/cbm/c_talk.s create mode 100644 libsrc/cbm/c_unlsn.s create mode 100644 libsrc/cbm/c_untlk.s create mode 100644 libsrc/cbm/cbm.inc create mode 100644 libsrc/cbm/cclear.s create mode 100644 libsrc/cbm/chline.s create mode 100644 libsrc/cbm/clock.s create mode 100644 libsrc/cbm/ctype.s create mode 100644 libsrc/cbm/cvline.s create mode 100644 libsrc/cbm/getenv.s create mode 100644 libsrc/cbm/gotox.s create mode 100644 libsrc/cbm/gotoxy.s create mode 100644 libsrc/cbm/gotoy.s create mode 100644 libsrc/cbm/oserror.s create mode 100644 libsrc/cbm/revers.s create mode 100644 libsrc/cbm/where.s create mode 100644 libsrc/cbm610/Makefile create mode 100644 libsrc/cbm610/banking.s create mode 100644 libsrc/cbm610/break.s create mode 100644 libsrc/cbm610/cbm610.inc create mode 100644 libsrc/cbm610/cgetc.s create mode 100644 libsrc/cbm610/clrscr.s create mode 100644 libsrc/cbm610/color.s create mode 100644 libsrc/cbm610/conio.s create mode 100644 libsrc/cbm610/cputc.s create mode 100644 libsrc/cbm610/crt0.s create mode 100644 libsrc/cbm610/crtc.s create mode 100644 libsrc/cbm610/io.inc create mode 100644 libsrc/cbm610/kbhit.s create mode 100644 libsrc/cbm610/kirq.s create mode 100644 libsrc/cbm610/kplot.s create mode 100644 libsrc/cbm610/kscnkey.s create mode 100644 libsrc/cbm610/kudtim.s create mode 100644 libsrc/cbm610/page3.inc create mode 100644 libsrc/cbm610/pokesys.s create mode 100644 libsrc/cbm610/rs232.s create mode 100644 libsrc/cbm610/zeropage.inc create mode 100644 libsrc/common/.cvsignore create mode 100644 libsrc/common/Makefile create mode 100644 libsrc/common/_afailed.c create mode 100644 libsrc/common/_fdesc.s create mode 100644 libsrc/common/_file.h create mode 100644 libsrc/common/_file.s create mode 100644 libsrc/common/_fopen.c create mode 100644 libsrc/common/_hadd.c create mode 100644 libsrc/common/_heap.h create mode 100644 libsrc/common/_heap.s create mode 100644 libsrc/common/_hextab.c create mode 100644 libsrc/common/_oserror.s create mode 100644 libsrc/common/_printf.c create mode 100644 libsrc/common/_printf.h create mode 100644 libsrc/common/_stksize.s create mode 100644 libsrc/common/_swap.s create mode 100644 libsrc/common/_sys.s create mode 100644 libsrc/common/abort.c create mode 100644 libsrc/common/abs.s create mode 100644 libsrc/common/atexit.s create mode 100644 libsrc/common/atoi.s create mode 100644 libsrc/common/bsearch.c create mode 100644 libsrc/common/calloc.c create mode 100644 libsrc/common/copydata.s create mode 100644 libsrc/common/cprintf.c create mode 100644 libsrc/common/errno.inc create mode 100644 libsrc/common/errno.s create mode 100644 libsrc/common/errormsg.c create mode 100644 libsrc/common/fclose.c create mode 100644 libsrc/common/fdopen.c create mode 100644 libsrc/common/fgetc.c create mode 100644 libsrc/common/fgets.c create mode 100644 libsrc/common/fmisc.s create mode 100644 libsrc/common/fmode.inc create mode 100644 libsrc/common/fopen.c create mode 100644 libsrc/common/fprintf.c create mode 100644 libsrc/common/fputc.c create mode 100644 libsrc/common/fputs.c create mode 100644 libsrc/common/fread.c create mode 100644 libsrc/common/free.c create mode 100644 libsrc/common/freopen.c create mode 100644 libsrc/common/fwrite.c create mode 100644 libsrc/common/getchar.c create mode 100644 libsrc/common/getcpu.s create mode 100644 libsrc/common/gets.c create mode 100644 libsrc/common/isalnum.s create mode 100644 libsrc/common/isalpha.s create mode 100644 libsrc/common/isblank.s create mode 100644 libsrc/common/iscntrl.s create mode 100644 libsrc/common/isdigit.s create mode 100644 libsrc/common/isgraph.s create mode 100644 libsrc/common/islower.s create mode 100644 libsrc/common/isprint.s create mode 100644 libsrc/common/ispunct.s create mode 100644 libsrc/common/isspace.s create mode 100644 libsrc/common/isupper.s create mode 100644 libsrc/common/isxdigit.s create mode 100644 libsrc/common/itoa.s create mode 100644 libsrc/common/jmpvec.s create mode 100644 libsrc/common/labs.s create mode 100644 libsrc/common/locale.c create mode 100644 libsrc/common/longjmp.s create mode 100644 libsrc/common/ltoa.s create mode 100644 libsrc/common/malloc.c create mode 100644 libsrc/common/memchr.s create mode 100644 libsrc/common/memcmp.s create mode 100644 libsrc/common/memcpy.s create mode 100644 libsrc/common/memset.s create mode 100644 libsrc/common/perror.c create mode 100644 libsrc/common/printf.c create mode 100644 libsrc/common/putchar.c create mode 100644 libsrc/common/puts.c create mode 100644 libsrc/common/qsort.c create mode 100644 libsrc/common/rand.s create mode 100644 libsrc/common/realloc.c create mode 100644 libsrc/common/setjmp.s create mode 100644 libsrc/common/sprintf.c create mode 100644 libsrc/common/stkcheck.s create mode 100644 libsrc/common/strcat.s create mode 100644 libsrc/common/strchr.s create mode 100644 libsrc/common/strcmp.s create mode 100644 libsrc/common/strcoll.s create mode 100644 libsrc/common/strcpy.s create mode 100644 libsrc/common/strcspn.s create mode 100644 libsrc/common/strdup.c create mode 100644 libsrc/common/strerror.s create mode 100644 libsrc/common/stricmp.s create mode 100644 libsrc/common/strlen.s create mode 100644 libsrc/common/strlower.s create mode 100644 libsrc/common/strncat.s create mode 100644 libsrc/common/strncmp.s create mode 100644 libsrc/common/strncpy.s create mode 100644 libsrc/common/strpbrk.s create mode 100644 libsrc/common/strrchr.s create mode 100644 libsrc/common/strspn.s create mode 100644 libsrc/common/strstr.s create mode 100644 libsrc/common/strtok.c create mode 100644 libsrc/common/strupper.s create mode 100644 libsrc/common/strxfrm.c create mode 100644 libsrc/common/tolower.s create mode 100644 libsrc/common/toupper.s create mode 100644 libsrc/common/vcprintf.c create mode 100644 libsrc/common/vfprintf.c create mode 100644 libsrc/common/vprintf.c create mode 100644 libsrc/common/vsprintf.c create mode 100644 libsrc/common/zerobss.s create mode 100644 libsrc/conio/Makefile create mode 100644 libsrc/conio/cputhex.s create mode 100644 libsrc/conio/cputs.s create mode 100644 libsrc/conio/cursor.s create mode 100644 libsrc/conio/scrsize.s create mode 100644 libsrc/dbg/.cvsignore create mode 100644 libsrc/dbg/Makefile create mode 100644 libsrc/dbg/asmtab.s create mode 100644 libsrc/dbg/dbg.c create mode 100644 libsrc/dbg/dbgdasm.s create mode 100644 libsrc/dbg/dbgdump.s create mode 100644 libsrc/dbg/dbgisram.s create mode 100644 libsrc/dbg/dbgsupp.s create mode 100644 libsrc/geos/Makefile create mode 100644 libsrc/geos/devel/Makefile create mode 100644 libsrc/geos/devel/crt0.s create mode 100644 libsrc/geos/devel/cvthead.s create mode 100644 libsrc/geos/disk/Makefile create mode 100644 libsrc/geos/disk/blkalloc.s create mode 100644 libsrc/geos/disk/calcblksfree.s create mode 100644 libsrc/geos/disk/changediskdevice.s create mode 100644 libsrc/geos/disk/chkdkgeos.s create mode 100644 libsrc/geos/disk/enterturbo.s create mode 100644 libsrc/geos/disk/exitturbo.s create mode 100644 libsrc/geos/disk/findbambit.s create mode 100644 libsrc/geos/disk/freeblock.s create mode 100644 libsrc/geos/disk/getblock.s create mode 100644 libsrc/geos/disk/getdirhead.s create mode 100644 libsrc/geos/disk/getptrcurdknm.s create mode 100644 libsrc/geos/disk/gettrse.s create mode 100644 libsrc/geos/disk/newdisk.s create mode 100644 libsrc/geos/disk/nxtblkalloc.s create mode 100644 libsrc/geos/disk/opendisk.s create mode 100644 libsrc/geos/disk/purgeturbo.s create mode 100644 libsrc/geos/disk/putblock.s create mode 100644 libsrc/geos/disk/putdirhead.s create mode 100644 libsrc/geos/disk/readblock.s create mode 100644 libsrc/geos/disk/readbuff.s create mode 100644 libsrc/geos/disk/setgeosdisk.s create mode 100644 libsrc/geos/disk/setnextfree.s create mode 100644 libsrc/geos/disk/verwriteblock.s create mode 100644 libsrc/geos/disk/writeblock.s create mode 100644 libsrc/geos/disk/writebuff.s create mode 100644 libsrc/geos/dlgbox/Makefile create mode 100644 libsrc/geos/dlgbox/dbget2lines.s create mode 100644 libsrc/geos/dlgbox/dlgboxfileselect.s create mode 100644 libsrc/geos/dlgbox/dlgboxgetstring.s create mode 100644 libsrc/geos/dlgbox/dlgboxok.s create mode 100644 libsrc/geos/dlgbox/dlgboxokcancel.s create mode 100644 libsrc/geos/dlgbox/dlgboxyesno.s create mode 100644 libsrc/geos/dlgbox/dodlgbox.s create mode 100644 libsrc/geos/dlgbox/rstrfrmdialogue.s create mode 100644 libsrc/geos/file/Makefile create mode 100644 libsrc/geos/file/appendrecord.s create mode 100644 libsrc/geos/file/closerecordfile.s create mode 100644 libsrc/geos/file/deletefile.s create mode 100644 libsrc/geos/file/deleterecord.s create mode 100644 libsrc/geos/file/findfile.s create mode 100644 libsrc/geos/file/findftypes.s create mode 100644 libsrc/geos/file/followchain.s create mode 100644 libsrc/geos/file/freefile.s create mode 100644 libsrc/geos/file/get1stdirentry.s create mode 100644 libsrc/geos/file/getfhdrinfo.s create mode 100644 libsrc/geos/file/getnxtdirentry.s create mode 100644 libsrc/geos/file/insertrecord.s create mode 100644 libsrc/geos/file/nextrecord.s create mode 100644 libsrc/geos/file/openrecordfile.s create mode 100644 libsrc/geos/file/pointrecord.s create mode 100644 libsrc/geos/file/previousrecord.s create mode 100644 libsrc/geos/file/readbyte.s create mode 100644 libsrc/geos/file/readfile.s create mode 100644 libsrc/geos/file/readrecord.s create mode 100644 libsrc/geos/file/renamefile.s create mode 100644 libsrc/geos/file/savefile.s create mode 100644 libsrc/geos/file/updaterecordfile.s create mode 100644 libsrc/geos/file/writerecord.s create mode 100644 libsrc/geos/graph/Makefile create mode 100644 libsrc/geos/graph/bitmapclip.s create mode 100644 libsrc/geos/graph/bitmapregs.s create mode 100644 libsrc/geos/graph/bitmapup.s create mode 100644 libsrc/geos/graph/bitotherclip.s create mode 100644 libsrc/geos/graph/drawline.s create mode 100644 libsrc/geos/graph/drawpoint.s create mode 100644 libsrc/geos/graph/framerectangle.s create mode 100644 libsrc/geos/graph/getcharwidth.s create mode 100644 libsrc/geos/graph/getintcharint.s create mode 100644 libsrc/geos/graph/graphicsstring.s create mode 100644 libsrc/geos/graph/hlineregs.s create mode 100644 libsrc/geos/graph/horizontalline.s create mode 100644 libsrc/geos/graph/imprintrectangle.s create mode 100644 libsrc/geos/graph/initdrawwindow.s create mode 100644 libsrc/geos/graph/invertline.s create mode 100644 libsrc/geos/graph/invertrectangle.s create mode 100644 libsrc/geos/graph/loadcharset.s create mode 100644 libsrc/geos/graph/pointregs.s create mode 100644 libsrc/geos/graph/putchar.s create mode 100644 libsrc/geos/graph/putdecimal.s create mode 100644 libsrc/geos/graph/putstring.s create mode 100644 libsrc/geos/graph/recoverline.s create mode 100644 libsrc/geos/graph/recoverrectangle.s create mode 100644 libsrc/geos/graph/rectangle.s create mode 100644 libsrc/geos/graph/setpattern.s create mode 100644 libsrc/geos/graph/testpoint.s create mode 100644 libsrc/geos/graph/usesystemfont.s create mode 100644 libsrc/geos/graph/verticalline.s create mode 100644 libsrc/geos/inc/const.inc create mode 100644 libsrc/geos/inc/diskdrv.inc create mode 100644 libsrc/geos/inc/geosmac.ca65.inc create mode 100644 libsrc/geos/inc/geossym.inc create mode 100644 libsrc/geos/inc/geossym2.inc create mode 100644 libsrc/geos/inc/inputdrv.inc create mode 100644 libsrc/geos/inc/jumptab.inc create mode 100644 libsrc/geos/inc/printdrv.inc create mode 100644 libsrc/geos/memory/Makefile create mode 100644 libsrc/geos/memory/clearram.s create mode 100644 libsrc/geos/memory/cmpfstring.s create mode 100644 libsrc/geos/memory/cmpstring.s create mode 100644 libsrc/geos/memory/copyfstring.s create mode 100644 libsrc/geos/memory/copystring.s create mode 100644 libsrc/geos/memory/crc.s create mode 100644 libsrc/geos/memory/doublepop.s create mode 100644 libsrc/geos/memory/doublespop.s create mode 100644 libsrc/geos/memory/fetchram.s create mode 100644 libsrc/geos/memory/fillram.s create mode 100644 libsrc/geos/memory/initram.s create mode 100644 libsrc/geos/memory/movedata.s create mode 100644 libsrc/geos/memory/reuregs.s create mode 100644 libsrc/geos/memory/stashram.s create mode 100644 libsrc/geos/memory/swapram.s create mode 100644 libsrc/geos/memory/verifyram.s create mode 100644 libsrc/geos/menuicon/Makefile create mode 100644 libsrc/geos/menuicon/doicons.s create mode 100644 libsrc/geos/menuicon/domenu.s create mode 100644 libsrc/geos/menuicon/dopreviousmenu.s create mode 100644 libsrc/geos/menuicon/gotofirstmenu.s create mode 100644 libsrc/geos/menuicon/recoverallmenus.s create mode 100644 libsrc/geos/menuicon/recovermenu.s create mode 100644 libsrc/geos/menuicon/redomenu.s create mode 100644 libsrc/geos/mousesprite/Makefile create mode 100644 libsrc/geos/mousesprite/clearmousemode.s create mode 100644 libsrc/geos/mousesprite/disablsprite.s create mode 100644 libsrc/geos/mousesprite/drawsprite.s create mode 100644 libsrc/geos/mousesprite/enablsprite.s create mode 100644 libsrc/geos/mousesprite/getnextchar.s create mode 100644 libsrc/geos/mousesprite/inittextprompt.s create mode 100644 libsrc/geos/mousesprite/ismseinregion.s create mode 100644 libsrc/geos/mousesprite/mouseoff.s create mode 100644 libsrc/geos/mousesprite/mouseup.s create mode 100644 libsrc/geos/mousesprite/possprite.s create mode 100644 libsrc/geos/mousesprite/promptoff.s create mode 100644 libsrc/geos/mousesprite/prompton.s create mode 100644 libsrc/geos/mousesprite/startmousemode.s create mode 100644 libsrc/geos/process/Makefile create mode 100644 libsrc/geos/process/processblock.s create mode 100644 libsrc/geos/process/processfreeze.s create mode 100644 libsrc/geos/process/processinitrestartenable.s create mode 100644 libsrc/geos/process/sleep.s create mode 100644 libsrc/geos/system/Makefile create mode 100644 libsrc/geos/system/callroutine.s create mode 100644 libsrc/geos/system/enterdesktop.s create mode 100644 libsrc/geos/system/firstinit.s create mode 100644 libsrc/geos/system/getrandom.s create mode 100644 libsrc/geos/system/getserialnumber.s create mode 100644 libsrc/geos/system/initdoneio.s create mode 100644 libsrc/geos/system/mainloop.s create mode 100644 libsrc/geos/system/panic.s create mode 100644 libsrc/geos/system/setdevice.s create mode 100644 libsrc/geos/system/tobasic.s create mode 100644 libsrc/pet/Makefile create mode 100644 libsrc/pet/break.s create mode 100644 libsrc/pet/cgetc.s create mode 100644 libsrc/pet/clrscr.s create mode 100644 libsrc/pet/color.s create mode 100644 libsrc/pet/conio.s create mode 100644 libsrc/pet/cputc.s create mode 100644 libsrc/pet/crt0.s create mode 100644 libsrc/pet/kbhit.s create mode 100644 libsrc/pet/pet.inc create mode 100644 libsrc/plus4/Makefile create mode 100644 libsrc/plus4/break.s create mode 100644 libsrc/plus4/cgetc.s create mode 100644 libsrc/plus4/clrscr.s create mode 100644 libsrc/plus4/color.s create mode 100644 libsrc/plus4/conio.s create mode 100644 libsrc/plus4/cputc.s create mode 100644 libsrc/plus4/crt0.s create mode 100644 libsrc/plus4/kbhit.s create mode 100644 libsrc/plus4/plus4.inc create mode 100644 libsrc/plus4/readjoy.s create mode 100644 libsrc/runtime/Makefile create mode 100644 libsrc/runtime/add.s create mode 100644 libsrc/runtime/addeqsp.s create mode 100644 libsrc/runtime/and.s create mode 100644 libsrc/runtime/aslax1.s create mode 100644 libsrc/runtime/aslax2.s create mode 100644 libsrc/runtime/aslax3.s create mode 100644 libsrc/runtime/asleax1.s create mode 100644 libsrc/runtime/asleax2.s create mode 100644 libsrc/runtime/asleax3.s create mode 100644 libsrc/runtime/asrax1.s create mode 100644 libsrc/runtime/asrax2.s create mode 100644 libsrc/runtime/asrax3.s create mode 100644 libsrc/runtime/asreax1.s create mode 100644 libsrc/runtime/asreax2.s create mode 100644 libsrc/runtime/asreax3.s create mode 100644 libsrc/runtime/bneg.s create mode 100644 libsrc/runtime/bpushbsp.s create mode 100644 libsrc/runtime/call.s create mode 100644 libsrc/runtime/compl.s create mode 100644 libsrc/runtime/dec.s create mode 100644 libsrc/runtime/div.s create mode 100644 libsrc/runtime/enter.s create mode 100644 libsrc/runtime/eq.s create mode 100644 libsrc/runtime/ge.s create mode 100644 libsrc/runtime/gt.s create mode 100644 libsrc/runtime/icmp.s create mode 100644 libsrc/runtime/inc.s create mode 100644 libsrc/runtime/ladd.s create mode 100644 libsrc/runtime/laddeq.s create mode 100644 libsrc/runtime/laddeqsp.s create mode 100644 libsrc/runtime/land.s create mode 100644 libsrc/runtime/lbneg.s create mode 100644 libsrc/runtime/lcmp.s create mode 100644 libsrc/runtime/lcompl.s create mode 100644 libsrc/runtime/lconvert.s create mode 100644 libsrc/runtime/ldai.s create mode 100644 libsrc/runtime/ldasp.s create mode 100644 libsrc/runtime/ldau0sp.s create mode 100644 libsrc/runtime/ldaui.s create mode 100644 libsrc/runtime/ldauisp.s create mode 100644 libsrc/runtime/ldausp.s create mode 100644 libsrc/runtime/ldaxi.s create mode 100644 libsrc/runtime/ldaxsp.s create mode 100644 libsrc/runtime/ldeax.s create mode 100644 libsrc/runtime/ldeaxi.s create mode 100644 libsrc/runtime/ldec.s create mode 100644 libsrc/runtime/ldiv.s create mode 100644 libsrc/runtime/le.s create mode 100644 libsrc/runtime/leave.s create mode 100644 libsrc/runtime/leaysp.s create mode 100644 libsrc/runtime/leq.s create mode 100644 libsrc/runtime/lge.s create mode 100644 libsrc/runtime/lgt.s create mode 100644 libsrc/runtime/linc.s create mode 100644 libsrc/runtime/lle.s create mode 100644 libsrc/runtime/llt.s create mode 100644 libsrc/runtime/lmod.s create mode 100644 libsrc/runtime/lmul.s create mode 100644 libsrc/runtime/lne.s create mode 100644 libsrc/runtime/lneg.s create mode 100644 libsrc/runtime/lor.s create mode 100644 libsrc/runtime/lpop.s create mode 100644 libsrc/runtime/lpush.s create mode 100644 libsrc/runtime/lrsub.s create mode 100644 libsrc/runtime/lruntime.s create mode 100644 libsrc/runtime/lsave.s create mode 100644 libsrc/runtime/lshelp.s create mode 100644 libsrc/runtime/lshl.s create mode 100644 libsrc/runtime/lshr.s create mode 100644 libsrc/runtime/lsub.s create mode 100644 libsrc/runtime/lsubeq.s create mode 100644 libsrc/runtime/lsubeqsp.s create mode 100644 libsrc/runtime/lswap.s create mode 100644 libsrc/runtime/lswitch.s create mode 100644 libsrc/runtime/lt.s create mode 100644 libsrc/runtime/ltest.s create mode 100644 libsrc/runtime/ludiv.s create mode 100644 libsrc/runtime/luge.s create mode 100644 libsrc/runtime/lugt.s create mode 100644 libsrc/runtime/lule.s create mode 100644 libsrc/runtime/lult.s create mode 100644 libsrc/runtime/lumod.s create mode 100644 libsrc/runtime/lxor.s create mode 100644 libsrc/runtime/makebool.s create mode 100644 libsrc/runtime/mod.s create mode 100644 libsrc/runtime/mul.s create mode 100644 libsrc/runtime/ne.s create mode 100644 libsrc/runtime/neg.s create mode 100644 libsrc/runtime/or.s create mode 100644 libsrc/runtime/popsreg.s create mode 100644 libsrc/runtime/push.s create mode 100644 libsrc/runtime/pushb.s create mode 100644 libsrc/runtime/pushbsp.s create mode 100644 libsrc/runtime/pushw.s create mode 100644 libsrc/runtime/pushwsp.s create mode 100644 libsrc/runtime/rsub.s create mode 100644 libsrc/runtime/runtime.s create mode 100644 libsrc/runtime/shelp.s create mode 100644 libsrc/runtime/shl.s create mode 100644 libsrc/runtime/shr.s create mode 100644 libsrc/runtime/shrax1.s create mode 100644 libsrc/runtime/shrax2.s create mode 100644 libsrc/runtime/shrax3.s create mode 100644 libsrc/runtime/shreax1.s create mode 100644 libsrc/runtime/shreax2.s create mode 100644 libsrc/runtime/shreax3.s create mode 100644 libsrc/runtime/staxsp.s create mode 100644 libsrc/runtime/steaxsp.s create mode 100644 libsrc/runtime/sub.s create mode 100644 libsrc/runtime/subeqsp.s create mode 100644 libsrc/runtime/swap.s create mode 100644 libsrc/runtime/switch.s create mode 100644 libsrc/runtime/test.s create mode 100644 libsrc/runtime/udiv.s create mode 100644 libsrc/runtime/uge.s create mode 100644 libsrc/runtime/ugt.s create mode 100644 libsrc/runtime/ule.s create mode 100644 libsrc/runtime/ult.s create mode 100644 libsrc/runtime/umod.s create mode 100644 libsrc/runtime/xor.s create mode 100644 samples/.cvsignore create mode 100644 samples/Makefile create mode 100644 samples/c1541.rsp create mode 100644 samples/hello.c create mode 100644 samples/nachtm.c create mode 100644 samples/sieve.c create mode 100644 src/.cvsignore create mode 100644 src/ar65/.cvsignore create mode 100644 src/ar65/add.c create mode 100644 src/ar65/add.h create mode 100644 src/ar65/del.c create mode 100644 src/ar65/del.h create mode 100644 src/ar65/error.c create mode 100644 src/ar65/error.h create mode 100644 src/ar65/exports.c create mode 100644 src/ar65/exports.h create mode 100644 src/ar65/extract.c create mode 100644 src/ar65/extract.h create mode 100644 src/ar65/fileio.c create mode 100644 src/ar65/fileio.h create mode 100644 src/ar65/global.c create mode 100644 src/ar65/global.h create mode 100644 src/ar65/library.c create mode 100644 src/ar65/library.h create mode 100644 src/ar65/list.c create mode 100644 src/ar65/list.h create mode 100644 src/ar65/main.c create mode 100644 src/ar65/make/gcc.mak create mode 100644 src/ar65/make/watcom.mak create mode 100644 src/ar65/mem.c create mode 100644 src/ar65/mem.h create mode 100644 src/ar65/objdata.c create mode 100644 src/ar65/objdata.h create mode 100644 src/ar65/objfile.c create mode 100644 src/ar65/objfile.h create mode 100644 src/ca65/.cvsignore create mode 100644 src/ca65/condasm.c create mode 100644 src/ca65/condasm.h create mode 100644 src/ca65/ea.c create mode 100644 src/ca65/ea.h create mode 100644 src/ca65/error.c create mode 100644 src/ca65/error.h create mode 100644 src/ca65/expr.c create mode 100644 src/ca65/expr.h create mode 100644 src/ca65/fname.c create mode 100644 src/ca65/fname.h create mode 100644 src/ca65/fragment.c create mode 100644 src/ca65/fragment.h create mode 100644 src/ca65/global.c create mode 100644 src/ca65/global.h create mode 100644 src/ca65/instr.c create mode 100644 src/ca65/instr.h create mode 100644 src/ca65/listing.c create mode 100644 src/ca65/listing.h create mode 100644 src/ca65/macpack.c create mode 100644 src/ca65/macpack.h create mode 100644 src/ca65/macro.c create mode 100644 src/ca65/macro.h create mode 100644 src/ca65/main.c create mode 100644 src/ca65/make/gcc.mak create mode 100644 src/ca65/make/watcom.mak create mode 100644 src/ca65/mem.c create mode 100644 src/ca65/mem.h create mode 100644 src/ca65/objcode.c create mode 100644 src/ca65/objcode.h create mode 100644 src/ca65/objfile.c create mode 100644 src/ca65/objfile.h create mode 100644 src/ca65/options.c create mode 100644 src/ca65/options.h create mode 100644 src/ca65/pseudo.c create mode 100644 src/ca65/pseudo.h create mode 100644 src/ca65/scanner.c create mode 100644 src/ca65/scanner.h create mode 100644 src/ca65/strexpr.c create mode 100644 src/ca65/strexpr.h create mode 100644 src/ca65/symentry.h create mode 100644 src/ca65/symtab.c create mode 100644 src/ca65/symtab.h create mode 100644 src/ca65/toknode.c create mode 100644 src/ca65/toknode.h create mode 100644 src/ca65/ulabel.c create mode 100644 src/ca65/ulabel.h create mode 100644 src/cc65/.cvsignore create mode 100644 src/cc65/anonname.c create mode 100644 src/cc65/anonname.h create mode 100644 src/cc65/asmcode.c create mode 100644 src/cc65/asmcode.h create mode 100644 src/cc65/asmlabel.c create mode 100644 src/cc65/asmlabel.h create mode 100644 src/cc65/asmline.c create mode 100644 src/cc65/asmline.h create mode 100644 src/cc65/check.c create mode 100644 src/cc65/check.h create mode 100644 src/cc65/codegen.c create mode 100644 src/cc65/codegen.h create mode 100644 src/cc65/copyleft.jrd create mode 100644 src/cc65/ctrans.c create mode 100644 src/cc65/ctrans.h create mode 100644 src/cc65/datatype.c create mode 100644 src/cc65/datatype.h create mode 100644 src/cc65/declare.c create mode 100644 src/cc65/declare.h create mode 100644 src/cc65/error.c create mode 100644 src/cc65/error.h create mode 100644 src/cc65/expr.c create mode 100644 src/cc65/expr.h create mode 100644 src/cc65/funcdesc.c create mode 100644 src/cc65/funcdesc.h create mode 100644 src/cc65/function.c create mode 100644 src/cc65/function.h create mode 100644 src/cc65/global.c create mode 100644 src/cc65/global.h create mode 100644 src/cc65/goto.c create mode 100644 src/cc65/goto.h create mode 100644 src/cc65/hashstr.c create mode 100644 src/cc65/hashstr.h create mode 100644 src/cc65/ident.c create mode 100644 src/cc65/ident.h create mode 100644 src/cc65/include.c create mode 100644 src/cc65/include.h create mode 100644 src/cc65/io.c create mode 100644 src/cc65/io.h create mode 100644 src/cc65/litpool.c create mode 100644 src/cc65/litpool.h create mode 100644 src/cc65/locals.c create mode 100644 src/cc65/locals.h create mode 100644 src/cc65/loop.c create mode 100644 src/cc65/loop.h create mode 100644 src/cc65/macrotab.c create mode 100644 src/cc65/macrotab.h create mode 100644 src/cc65/main.c create mode 100644 src/cc65/make/cc65.mak create mode 100644 src/cc65/make/gcc.mak create mode 100644 src/cc65/make/watcom.mak create mode 100644 src/cc65/mem.c create mode 100644 src/cc65/mem.h create mode 100644 src/cc65/optimize.c create mode 100644 src/cc65/optimize.h create mode 100644 src/cc65/pragma.c create mode 100644 src/cc65/pragma.h create mode 100644 src/cc65/preproc.c create mode 100644 src/cc65/preproc.h create mode 100644 src/cc65/scanner.c create mode 100644 src/cc65/scanner.h create mode 100644 src/cc65/stdfunc.c create mode 100644 src/cc65/stdfunc.h create mode 100644 src/cc65/stmt.c create mode 100644 src/cc65/stmt.h create mode 100644 src/cc65/symentry.c create mode 100644 src/cc65/symentry.h create mode 100644 src/cc65/symtab.c create mode 100644 src/cc65/symtab.h create mode 100644 src/cc65/util.c create mode 100644 src/cc65/util.h create mode 100644 src/cl65/.cvsignore create mode 100644 src/cl65/error.c create mode 100644 src/cl65/error.h create mode 100644 src/cl65/global.c create mode 100644 src/cl65/global.h create mode 100644 src/cl65/main.c create mode 100644 src/cl65/make/gcc.mak create mode 100644 src/cl65/make/watcom.mak create mode 100644 src/cl65/mem.c create mode 100644 src/cl65/mem.h create mode 100644 src/cl65/spawn.c create mode 100644 src/cl65/spawn.h create mode 100644 src/common/.cvsignore create mode 100644 src/common/bitops.c create mode 100644 src/common/bitops.h create mode 100644 src/common/exprdefs.h create mode 100644 src/common/filepos.h create mode 100644 src/common/hashstr.c create mode 100644 src/common/hashstr.h create mode 100644 src/common/libdefs.h create mode 100644 src/common/make/gcc.mak create mode 100644 src/common/make/watcom.mak create mode 100644 src/common/objdefs.h create mode 100644 src/common/optdefs.h create mode 100644 src/common/segdefs.h create mode 100644 src/common/symdefs.h create mode 100644 src/common/version.h create mode 100755 src/geos/headergen.sh create mode 100644 src/ld65/.cvsignore create mode 100644 src/ld65/bin.c create mode 100644 src/ld65/bin.h create mode 100644 src/ld65/binfmt.c create mode 100644 src/ld65/binfmt.h create mode 100644 src/ld65/config.c create mode 100644 src/ld65/config.h create mode 100644 src/ld65/dbgsyms.c create mode 100644 src/ld65/dbgsyms.h create mode 100644 src/ld65/error.c create mode 100644 src/ld65/error.h create mode 100644 src/ld65/exports.c create mode 100644 src/ld65/exports.h create mode 100644 src/ld65/expr.c create mode 100644 src/ld65/expr.h create mode 100644 src/ld65/extsyms.c create mode 100644 src/ld65/extsyms.h create mode 100644 src/ld65/fileio.c create mode 100644 src/ld65/fileio.h create mode 100644 src/ld65/global.c create mode 100644 src/ld65/global.h create mode 100644 src/ld65/library.c create mode 100644 src/ld65/library.h create mode 100644 src/ld65/main.c create mode 100644 src/ld65/make/gcc.mak create mode 100644 src/ld65/make/watcom.mak create mode 100644 src/ld65/mapfile.c create mode 100644 src/ld65/mapfile.h create mode 100644 src/ld65/mem.c create mode 100644 src/ld65/mem.h create mode 100644 src/ld65/o65.c create mode 100644 src/ld65/o65.h create mode 100644 src/ld65/objdata.c create mode 100644 src/ld65/objdata.h create mode 100644 src/ld65/objfile.c create mode 100644 src/ld65/objfile.h create mode 100644 src/ld65/scanner.c create mode 100644 src/ld65/scanner.h create mode 100644 src/ld65/segments.c create mode 100644 src/ld65/segments.h create mode 100644 src/ld65/target.c create mode 100644 src/ld65/target.h create mode 100644 src/ld65/version.h create mode 100644 src/make/gcc.mak create mode 100644 src/make/watcom.mak create mode 100644 testcode/compiler/pptest1.c create mode 100644 testcode/compiler/pptest2.c create mode 100644 testcode/compiler/pptest3.c create mode 100644 testcode/compiler/pptest4.c create mode 100644 util/atari/ataricvt.c diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 000000000..ecfc81b15 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,2 @@ +dist +test diff --git a/doc/BUGS b/doc/BUGS new file mode 100644 index 000000000..686374947 --- /dev/null +++ b/doc/BUGS @@ -0,0 +1,11 @@ +List of known bugs that will not get fixed in the near future: + + * The compiler does not detect if part of an expression evaluated + with && or || is constant. Since the expression evaluation is also + used for the preprocessor, this defeats the use of + + #if defined(x) && defined(y) + + * Initialization of local variables with compound data types is not + possible. + diff --git a/doc/CREDITS b/doc/CREDITS new file mode 100644 index 000000000..a43ea6371 --- /dev/null +++ b/doc/CREDITS @@ -0,0 +1,62 @@ + +Many special thanks go to the guy who started it all: + + John R.Dunning. + +Without his great work, there would not be a single freeware C crosscompiler +for the 6502 out there. He built the grounds for this cc65 and some other cc65 +implementations and a lot of his code is still in the current compiler. + + +More special thanks to: + + * Keith W. Gerdes + + Without his outstanding help, the assembler/compiler wouldn't be, what it + is now. He helped with suggestions, bug reports and even kicked me here + and then, when I started to get too lazy:-) + + * Tennessee Carmel-Veilleux + + Tennessee worked on the NES port. + + * Kevin Ruland + + Kevin did the Apple 2 port and found at least one serious error in the + C library while doing so. + + * Christian Groessler + + Christian sent me several fixes for 64 bit machines. + + * Sidney Cadot + + Sidney rewrote the random number generator. + + * Maciej Witkowiak + + Maciej is responsible for the GEOS support. + + + +Thanks to + + Mark Nasgowitz + da Blondie + Jari Tuominen + MagerValp + Ingo Korb + Robert R. Wal + Jesse Beach + Chris Ward + Eric Au + Tim Vanderhoek + Bill Craig + +for bug reports and suggestions. + + +This list is probably incomplete. So, if you think you should be mentioned +here, but cannot find yourself, please drop me a mail. + + diff --git a/doc/ar65.txt b/doc/ar65.txt new file mode 100644 index 000000000..ee618e99e --- /dev/null +++ b/doc/ar65.txt @@ -0,0 +1,152 @@ + + + ar65 + + An Archiver for Object Files Generated by ca65 + + (C) Copyright 1998-1999 Ullrich von Bassewitz + (uz@musoftware.de) + + + +Contents +-------- + + 1. Overview + + 2. Usage + + 3. Bugs/Feedback + + 4. Copyright + + + +1. Overview +----------- + +ar65 is a replacement for the libr65 archiver that was part of the cc65 C +compiler suite developed by John R. Dunning. libr65 had some problems and +the copyright does not permit some things which I wanted to be possible, +so I decided to write a completely new assembler/linker/archiver suite +for the cc65 compiler. ar65 is part of this suite. + + + +2. Usage +-------- + +The archiver is called as follows: + + Usage: ar65 lib file|module ... + Operation is one of: + a Add modules + d Delete modules + l List library contents + x Extract modules + X Print the archiver version + + +You may add modules to a library using the `a' command. If the library +does not exist, it is created (and a warning message is printed which you +may ignore if creation of the library was your intention). You may +specify any number of modules on the command line following the library. + +If a module with the same name exists in the library, it is replaced by +the new one. The archiver prints a warning, if the module in the library +has a newer timestamp than the one to add. + +Here's an example: + + ar65 a mysubs.lib sub1.o sub2.o + +This will add two modules to the library `mysubs.lib' creating the +library if necessary. If the library contains modules named sub1.o or +sub2.o, they are replaced by the new ones. + +Modules names in the library are stored without the path, so, using + + ar65 a mysubs.lib ofiles/sub1.o ofiles/sub2.o + +will add two modules named `sub1.o' and `sub2.o' to the library. + + +Deleting modules from a library is done with the `d' command. You may not +give a path when naming the modules. + +Example: + + ar65 d mysubs.lib sub1.o + +This will delete the module named `sub1.o' from the library, printing an +error if the library does not contain that module. + + +The `l' command prints a list of all modules in the library. Any module +names on the command line are ignored. + +Example: + + ar65 l mysubs.lib + + +Using the `x' command, you may extract modules from the library. The +modules named on the command line are extracted from the library and put +into the current directory. + +Note: Because of the indexing done by the archiver, the modules may have +a changed binary layout, that is, a binary compare with the old module +(before importing it into the library) may yield differences. The +extracted modules are accepted by the linker and archiver, however, so +this is not a problem. + +Example for extracting a module from the library: + + ar65 x mysubs.lib sub1.o + + +The `V' command prints the version number of the assembler. If you send +any suggestions or bugfixes, please include your version number. + +In addition to these operations, the archiver will check for, and warn +about duplicate external symbols in the library, every time when an +operation does update the library. This is only a warning, the linker +will ignore one of the duplicate symbols (which one is unspecified). + + + +3. Bugs/Feedback +---------------- + +If you have problems using the archiver, if you find any bugs, or if +you're doing something interesting with it, I would be glad to hear from +you. Feel free to contact me by email (uz@musoftware.de). + + + +4. Copyright +------------ + +ar65 (and all cc65 binutils) are (C) Copyright 1998 Ullrich von Bassewitz. +For usage of the binaries and/or sources the following conditions do +apply: + +This software is provided 'as-is', without any expressed or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. +3. This notice may not be removed or altered from any source + distribution. + + + diff --git a/doc/ca65.txt b/doc/ca65.txt new file mode 100644 index 000000000..9c5062991 --- /dev/null +++ b/doc/ca65.txt @@ -0,0 +1,1903 @@ + + + ca65 + + A Macro Crossassembler for the 6502/65C02/65816 CPUs + + (C) Copyright 1998-1999 Ullrich von Bassewitz + (uz@musoftware.de) + + + +Contents +-------- + + 1. Overview + + 2. Usage + + 3. Input format + + 4. Expressions + + 5. Symbols and labels + + 6. Control commands + + 7. Macros + + 8. Macro packages + + 9. Bugs/Feedback + + 10. Copyright + + + +1. Overview +----------- + +ca65 is a replacement for the ra65 assembler that was part of the cc65 C +compiler developed by John R. Dunning. I had some problems with ra65 and +the copyright does not permit some things which I wanted to be possible, +so I decided to write a completely new assembler/linker/archiver suite for +the cc65 compiler. ca65 is part of this suite. + +Some parts of the assembler (code generation and some routines for symbol +table handling) are taken from an older crossassembler named a816 written +by me a long time ago. + +Here's a list of the design criteria, that were important for the +development: + + * The assembler must support macros. Macros are not essential, but they + make some things easier, especially when you use the assembler in the + backend of a compiler. + + * The assembler must support the newer 65C02 and 65816 CPUs. I have been + thinking about a 65816 backend for the C compiler, and even my old + a816 assembler had support for these CPUs, so this wasn't really a + problem. + + * The assembler must produce relocatable code. This necessary for the + compiler support, and it is more convenient. + + * Conditional assembly must be supported. This is a must for bigger + projects written in assembler (like Elite128). + + * The assembler must support segments, and it must support more than + three segments (this is the count, most other assemblers support). + Having more than one code segments helps developing code for systems + with a divided ROM area (like the C64). + + * The linker must be able to resolve arbitrary expressions. Years ago I + spent half a day to convince Borlands Turbo Assembler to let me use + the size of a structure I had created. So I decided that this is a + must. The linker should be able to get things like + + .import S1, S2 + .export Special + Special = 2*S1 + S2/7 + + right. + + * True lexical nesting for symbols. This is very convenient for larger + assembly projects. + + * "Cheap" local symbols without lexical nesting for those quick, late + night hacks. + + * I liked the idea of "options" as Anre Fachats .o65 format has it, so I + introduced the concept into the object file format use by the new cc65 + binutils. + + * The assembler will be a one pass assembler. There was no real need for + this decision, but I've written several multipass assemblers, and it + started to get boring. A one pass assembler needs much more elaborated + data structures, and because of that it's much more fun:-) + There is one drawback with this point: It is nearly impossible to + generate a listing when reading the source file only once without + storing the source file in memory. Consequently, the assembler is not + able to produce a listing. This could be added by reading the source + file a second time if a listing was requested, but I didn't see an + urgent need for a listing, so this was not a disadvantage for me. + + * Non-GPLed code that may be used in any project without restrictions or + fear of "GPL infecting" other code. + + + +2. Usage +-------- + +The assembler accepts the following options: + + -g Generate debug info + -i Ignore case of symbols + -l Create a listing if assembly was ok + -o name Name the output file + -s Enable smart mode + -v Increase verbosity + -D name[=value] Define a symbol + -U Mark unresolved symbols as import + -V Print the assembler version + -W n Set warning level n + --cpu type Set cpu type + --pagelength n Set the page length for the listing + --smart Enable smart mode + +When the -g option (or the equivalent control command .DEBUGINFO) is used, +the assembler will add a section to the object file that contains all +symbols (including local ones) together with the symbol values and source +file positions. The linker will put these additional symbols into the +VICE label file, so even local symbols can be seen in the VICE monitor. + +The option -i makes the assembler case insensitive on identifiers and +labels. This option will override the default, but may itself be overriden +by the .CASE control command (see section 6). + +The default output name is the name of the input file with the extension +replaced by ".o". If you don't like that, you may give another name with +the -o option. The output file will be placed in the same directory as the +source file, or, if -o is given, the full path in this name is used. + +In smart mode (enabled by -s or the .SMART pseudo instruction) the +assembler will track usage of the REP and SEP instructions in 65816 mode +and update the operand sizes accordingly. If the operand of such an +instruction cannot be evaluated by the assembler (for example, because the +operand is an imported symbol), a warning is issued. Beware: Since the +assembler cannot trace the execution flow this may lead to false results +in some cases. If in doubt, use the .ixx and .axx instructions to tell the +assembler about the current settings. Smart mode is off by default. + +-v does increase the assembler verbosity and is usually only needed for +debugging purposes. You may use this option more than one time for even +more verbose output. + +-D allows you to predefine symbols on the command line. Without a value, +the symbol is defined with the value zero. When giving a value, you may +use the '$' prefix for hexadecimal symbols. Please note that for some +operating systems, '$' has a special meaning, so you may have to quote +the expression. + +-U marks symbols that are not defined in the sources as imported symbols. +This should be used with care since it delays error messages about typos +and such until the linker is run. The compiler uses the equivalent of +this switch (.AUTOIMPORT, see control command section below) to enable +auto imported symbols for the runtime library. However, the compiler is +supposed to generate code that runs through the assembler without +problems, something which is not always true for assembler programmers. + +-V prints the version number of the assembler. If you send any suggestions +or bugfixes, please include your version number. + +-Wn sets the warning level for the assembler. Using -W2 the assembler will +even warn about such things like unused imported symbols. The default +warning level is 1, and it would probably be silly to set it to something +lower. + +--cpu sets the default for the CPU type. The option takes a parameter, +which may be one of + + 6502, 65C02, 65816 and sunplus + +(the latter is not available in the freeware version). + +--pagelength sets the length of a listing page in lines. See the +.PAGELENGTH directive for more information. + +--smart is identical to -s - see there. + + + +3. Input format +--------------- + +The assembler accepts the standard 6502/65816 assembler syntax. One line +may contain a label (which is identified by a colon), and, in addition to +the label, an assembler mnemonic, a macro, or a control command (see +section 6 for supported control commands). Alternatively, the line may +contain a symbol definition using the '=' token. Everything after a +semicolon is handled as a comment (that is, it is ignored). + +Here are some examples for valid input lines: + + Label: ; A label and a comment + lda #$20 ; A 6502 instruction plus comment + L1: ldx #$20 ; Same with label + L2: .byte "Hello world" ; Label plus control command + mymac $20 ; Macro expansion + MySym = 3*L1 ; Symbol definition + MaSym = Label ; Another symbol + +The assembler accepts all valid 6502 mnemonics when in 6502 mode (the +default). The assembler accepts all valid 65SC02 mnemonics when in 65SC02 +mode (after a .PC02 command is found). The assembler accepts all valid +65816 mnemonics with a few exceptions after a .P816 command is found. +These exceptions are listed below. + +In 65816 mode several aliases are accepted in addition to the official +mnemonics: + + BGE is an alias for BCS + BLT is an alias for BCC + CPA is an alias for CMP + DEA is an alias for DEC A + INA is an alias for INC A + SWA is an alias for XBA + TAD is an alias for TCD + TAS is an alias for TCS + TDA is an alias for TDC + TSA is an alias for TSC + + +Evaluation of banked expressions in 65816 mode differs slightly from the +official syntax: + +Instead of accepting a 24 bit address (something that is difficult for +the assembler to determine and would have required one more special +.import command), the bank and the absolute address in that bank are +separated by a dot: + + jsl 3.$1234 ; Call subroutine at $1234 in bank 3 + +For literal values, the assembler accepts the widely used number formats: +A preceeding '$' denotes a hex value, a preceeding '%' denotes a binary +value, and a bare number is interpeted as a decimal. There are currently +no octal values and no floats. + + + +4. Expressions +-------------- + +All expressions are evaluated with (at least) 32 bit precision. An +expression may contain constant values and any combination of internal and +external symbols. Expressions that cannot be evaluated at assembly time +are stored inside the object file for evaluation by the linker. +Expressions referencing imported symbols must always be evaluated by the +linker. + +Sometimes, the assembler must know about the size of the value that is the +result of an expression. This is usually the case, if a decision has to be +made, to generate a zero page or an absolute memory references. In this +case, the assembler has to make some assumptions about the result of an +expression: + + * If the result of an expression is constant, the actual value is + checked to see if it's a byte sized expression or not. + + * If the expression is explicitly casted to a byte sized expression by + one of the '>'/'<' operators, it is a byte expression. + + * If this is not the case, and the expression contains a symbol, + explicitly declared as zero page symbol (by one of the .importzp or + .exportzp instructions), then the whole expression is assumed to be + byte sized. + + * If the expression contains symbols that are not defined, and these + symbols are local symbols, the enclosing scopes are searched for a + symbol with the same name. If one exists and this symbol is defined, + it's attributes are used to determine the result size. + + * In all other cases the expression is assumed to be word sized. + +Note: If the assembler is not able to evaluate the expression at assembly +time, the linker will evaluate it and check for range errors as soon as +the result is known. + + +Boolean expressions: + +In the context of a boolean expression, any non zero value is evaluated as +true, any other value to false. The result of a boolean expression is 1 if +it's true, and zero if it's false. There are boolean operators with extrem +low precedence with version 2.x (where x > 0). The .AND and .OR operators +are shortcut operators. That is, if the result of the expression is +already known, after evaluating the left hand side, the right hand side is +not evaluated. + + +Available operators sorted by precedence: + + Op Description Precedence + ------------------------------------------------------------------- + * Builtin pseudo variable (r/o) 1 + .BLANK Builtin function 1 + .CONST Builtin function 1 + .CPU Builtin pseudo variable (r/o) 1 + .DEFINED Builtin function 1 + .MATCH Builtin function 1 + .XMATCH Builtin function 1 + .PARAMCOUNT Builtin pseudo variable (r/o) 1 + .REFERENCED Builtin function 1 + .STRING Builtin function 1 + :: Global namespace override 1 + + Unary plus 1 + - Unary minus 1 + ~ Unary bitwise not 1 + .BITNOT Unary bitwise not 1 + < Low byte operator 1 + > High byte operator 1 + + * Multiplication 2 + / Division 2 + .MOD Modulo operation 2 + & Bitwise and 2 + .BITAND Bitwise and 2 + ^ Bitwise xor 2 + .BITXOR Bitwise xor 2 + << Shift left operator 2 + .SHL Shift left operator 2 + >> Shift right operator 2 + .SHR Shift right operator 2 + + + Binary plus 3 + - Binary minus 3 + | Binary or 3 + .BITOR Binary or 3 + + = Compare operation (equal) 4 + <> Compare operation (not equal) 4 + < Compare operation (less) 4 + > Compare operation (greater) 4 + <= Compare operation (less or equal) 4 + >= Compare operation (greater or equal) 4 + + && Boolean and 5 + .AND Boolean and 5 + .XOR Boolean xor 5 + + || Boolean or 6 + .OR Boolean or 6 + + ! Boolean not 7 + .NOT Boolean not 7 + + +To force a specific order of evaluation, braces may be used as usual. + +Some of the pseudo variables mentioned above need some more explanation: + + * This symbol is replaced by the value of the program + counter at start of the current instruction. Note, that + '*' yields a rvalue, that means, you cannot assign to it. + Use .ORG to set the program counter in sections with + absolute code. + + + +5. Symbols and labels +--------------------- + +The assembler allows you to use symbols instead of naked values to make +the source more readable. There are a lot of different ways to define and +use symbols and labels, giving a lot of flexibility. + + - Numeric constants + + Numeric constants are defined using the equal sign. After doing + + two = 2 + + may use the symbol "two" in every place where a number is expected, + and it is evaluated to the value 2 in this context. An example would be + + four = two * two + + + - Standard labels + + A label is defined by writing the name of the label at the start of + the line (before any instruction mnemonic, macro or pseudo + directive), followed by a colon. This will declare a symbol with the + given name and the value of the current program counter. + + + - Local labels and symbols + + Using the .PROC directive, it is possible to create regions of code + where the names of labels and symbols are local to this region. They + are not know outside and cannot be accessed from there. Such regions + may be nested like PROCEDUREs in Pascal. + + See the description of the .PROC directive for more information. + + - Cheap local labels + + Cheap local labels are defined like standard labels, but the name of + the label must begin with a special symbol (usually '@', but this can + be changed by the .LOCALCHAR directive). + + Cheap local labels are visible only between two no cheap labels. As + soon as a standard symbol is encountered (this may also be a local + symbol if inside a region defined with the .PROC directive), the + cheap local symbol goes out of scope. + + You may use cheap local labels as an easy way to reuse common label + names like "Loop". Here is an example: + + Clear: lda #$00 ; Global label + ldy #$20 + @Loop: sta Mem,y ; Local label + dey + bne @Loop ; Ok + rts + Sub: ... ; New global label + bne @Loop ; ERROR: Unknown identifier! + + - Unnamed labels + + If you really want to write messy code, there are also unnamed + labels. These labels do not have a name (you guessed that already, + didn't you?). A colon is used to mark the absence of the name. + + Unnamed labels may be accessed by using the colon plus several minus + or plus characters as a label designator. Using the '-' characters + will create a back reference (use the n'th label backwards), using + '+' will create a forward reference (use the n'th label in forward + direction. An example will help to understand this: + + : lda (ptr1),y ; #1 + cmp (ptr2),y + bne :+ ; -> #2 + tax + beq :+++ ; -> #4 + iny + bne :- ; -> #1 + inc ptr1+1 + inc ptr2+1 + bne :- ; -> #1 + + : bcs :+ ; #2 -> #3 + ldx #$FF + rts + + : ldx #$01 ; #3 + : rts ; #4 + + As you can see from the example, unnamed labels will make even short + sections of code hard to understand, because you have to count labels + to find branch targets (this is the reason why I for my part do + prefer the "cheap" local labels). Nevertheless, unnamed labels are + convenient in some situations, so it's your decision. + + - Using macros to define labels and constants + + While there are drawbacks with this approach, it may be handy in some + situations. Using .DEFINE, it is possible to define symbols or + constants that may be used elsewhere. Since the macro facility works + on a very low level, there is no scoping. On the other side, you may + also define string constants this way (this is not possible with the + other symbol types). + + Example: + + .DEFINE two 2 + .DEFINE version "SOS V2.3" + + four = two * two ; Ok + .byte version ; Ok + + .PROC ; Start local scope + two = 3 ; Will give "2 = 3" - invalid! + .ENDPROC + + +If .DEBUGINFO is enabled (or -g is given on the command line), global, +local and cheap local labels are written to the object file and will be +available in the symbol file via the linker. Unnamed labels are not +written to the object file, because they don't have a name which would +allow to access them. + + + +6. Control commands +------------------- + +Here's a list of all control commands and a description, what they do: + + +.A16 + + Valid only in 65816 mode. Switch the accumulator to 16 bit. + + Note: This command will not emit any code, it will tell the assembler to + create 16 bit operands for immediate accumulator adressing mode. + + See also: .SMART + + +.A8 + + Valid only in 65816 mode. Switch the accumulator to 8 bit. + + Note: This command will not emit any code, it will tell the assembler to + create 8 bit operands for immediate accu adressing mode. + + See also: .SMART + + +.ADDR + + Define word sized data. In 6502 mode, this is an alias for .WORD and + may be used for better readability if the data words are address values. + In 65816 mode, the address is forced to be 16 bit wide to fit into the + current segment. See also .FARADDR. The command must be followed by a + sequence of (not necessarily constant) expressions. + + Example: + + .addr $0D00, $AF13, _Clear + + +.ALIGN + + Align data to a given boundary. The command expects a constant integer + argument that must be a power of two, plus an optional second argument + in byte range. If there is a second argument, it is used as fill value, + otherwise the value defined in the linker configuration file is used + (the default for this value is zero). + + Since alignment depends on the base address of the module, you must + give the same (or a greater) alignment for the segment when linking. + The linker will give you a warning, if you don't do that. + + Example: + + .align 256 + + +.ASCIIZ + + Define a string with a trailing zero. + + Example: + + Msg: .asciiz "Hello world" + + This will put the string "Hello world" followed by a binary zero into + the current segment. There may be more strings separated by commas, but + the binary zero is only appended once (after the last one). + + +.AUTOIMPORT + + Is followd by a plus or a minus character. When switched on (using a + +), undefined symbols are automatically marked as import instead of + giving errors. When switched off (which is the default so this does not + make much sense), this does not happen and an error message is + displayed. The state of the autoimport flag is evaluated when the + complete source was translated, before outputing actual code, so it is + *not* possible to switch this feature on or off for separate sections of + code. The last setting is used for all symbols. + + You should probably not use this switch because it delays error + messages about undefined symbols until the link stage. The cc65 + compiler (which is supposed to produce correct assembler code in all + circumstances, something which is not true for most assembler + programmers) will insert this command to avoid importing each and every + routine from the runtime library. + + Example: + + .autoimport + ; Switch on auto import + + +.BLANK + + Builtin function. The function evaluates its argument in braces and + yields "false" if the argument is non blank (there is an argument), and + "true" if there is no argument. As an example, the .IFBLANK statement + may be replaced by + + .if .blank(arg) + + +.BSS + + Switch to the BSS segment. The name of the BSS segment is always "BSS", + so this is a shortcut for + + .segment "BSS" + + See also the .SEGMENT command. + + +.BYTE + + Define byte sized data. Must be followed by a sequence of (byte ranged) + expressions or strings. + + Example: + + .byte "Hello world", $0D, $00 + + +.CASE + + Switch on or off case sensitivity on identifiers. The default is off + (that is, identifiers are case sensitive), but may be changed by the + -i switch on the command line. + The command must be followed by a '+' or '-' character to switch the + option on or off respectively. + + Example: + + .case - ; Identifiers are not case sensitive + + +.CODE + + Switch to the CODE segment. The name of the CODE segment is always + "CODE", so this is a shortcut for + + .segment "CODE" + + See also the .SEGMENT command. + + +.CONST + + Builtin function. The function evaluates its argument in braces and + yields "true" if the argument is a constant expression (that is, an + expression that yields a constant value at assembly time) and "false" + otherwise. As an example, the .IFCONST statement may be replaced by + + .if .const(a + 3) + + +.CPU + + Reading this pseudo variable will give a constant integer value that + tells which instruction set is currently enabled. Possible values are: + + 0 --> 6502 + 1 --> 65SC02 + 2 --> 65SC816 + 3 --> SunPlus SPC + + It may be used to replace the .IFPxx pseudo instructions or to construct + even more complex expressions. + + Example: + + .if (.cpu = 0) .or (.cpu = 1) + txa + pha + tya + pha + .else + phx + phy + .endif + + +.DATA + + Switch to the DATA segment. The name of the DATA segment is always + "DATA", so this is a shortcut for + + .segment "DATA" + + See also the .SEGMENT command. + + +.DBYT + + Define word sized data with the hi and lo bytes swapped (use .WORD to + create word sized data in native 65XX format). Must be followed by a + sequence of (word ranged) expressions. + + Example: + + .dbyt $1234, $4512 + + This will emit the bytes + + $12 $34 $45 $12 + + into the current segment in that order. + + +.DEBUGINFO + + Switch on or off debug info generation. The default is off (that is, + the object file will not contain debug infos), but may be changed by the + -g switch on the command line. + The command must be followed by a '+' or '-' character to switch the + option on or off respectively. + + Example: + + .debuginfo + ; Generate debug info + + +.DEFINE + + Start a define style macro definition. The command is followed by an + identifier (the macro name) and optionally by a list of formal arguments + in braces. + See separate section about macros. + + +.DEF +.DEFINED + + Builtin function. The function expects an identifier as argument in + braces. The argument is evaluated, and the function yields "true" if the + identifier is a symbol that is already defined somewhere in the source + file up to the current position. Otherwise the function yields false. As + an example, the .IFDEF statement may be replaced by + + .if .defined(a) + + +.DWORD + + Define dword sized data (4 bytes) Must be followed by a sequence of + expressions. + + Example: + + .dword $12344512, $12FA489 + + +.ELSE + + Conditional assembly: Reverse the current condition. + + +.ELSEIF + + Conditional assembly: Reverse current condition and test a new one. + + +.END + + Forced end of assembly. Assembly stops at this point, even if the command + is read from an include file. + + +.ENDIF + + Conditional assembly: Close a .IF... or .ELSE branch. + + +.ENDMAC +.ENDMACRO + + End of macro definition (see separate section). + + +.ENDPROC + + End of local lexical level (see .PROC). + + +.ERROR + + Force an assembly error. The assembler will output an error message + preceeded by "User error" and will *not* produce an object file. + + This command may be used to check for initial conditions that must be + set before assembling a source file. + + Example: + + .if foo = 1 + ... + .elseif bar = 1 + ... + .else + .error "Must define foo or bar!" + .endif + + +.EXITMAC +.EXITMACRO + + Abort a macro expansion immidiately. This command is often useful in + recursive macros. See separate chapter about macros. + + +.EXPORT + + Make symbols accessible from other modules. Must be followed by a comma + separated list of symbols to export. + + Example: + + .export foo, bar + + +.EXPORTZP + + Make symbols accessible from other modules. Must be followed by a comma + separated list of symbols to export. The exported symbols are explicitly + marked as zero page symols. + + Example: + + .exportzp foo, bar + + +.FARADDR + + Define far (24 bit) address data. The command must be followed by a + sequence of (not necessarily constant) expressions. + + Example: + + .faraddr DrawCircle, DrawRectangle, DrawHexagon + + +.FEATURE + + This directive may be used to enable one or more compatibility features + of the assembler. While the use of .FEATURE should be avoided when + possible, it may be useful when porting sources written for other + assemblers. There is no way to switch a feature off, once you have + enabled it, so using + + .FEATURE xxx + + will enable the feature until end of assembly is reached. + + The following features are available: + + dollar_is_pc + + The dollar sign may be used as an alias for the star (`*'), which + gives the value of the current PC in expressions. + Note: Assignment to the pseudo variable is not allowed. + + labels_without_colons + + Allow labels without a trailing colon. These labels are only accepted, + if they start at the beginning of a line (no leading white space). + + loose_string_term + + Accept single quotes as well as double quotes as terminators for string + constants. + + at_in_identifiers + + Accept the at character (`@') as a valid character in identifiers. The + at character is not allowed to start an identifier, even with this + feature enabled. + + dollar_in_identifiers + + Accept the dollar sign (`$') as a valid character in identifiers. The + at character is not allowed to start an identifier, even with this + feature enabled. + + +.FILEOPT +.FOPT + + Insert an option string into the object file. There are two forms of + this command, one specifies the option by a keyword, the second + specifies it as a number. Since usage of the second one needs knowledge + of the internal encoding, its use is not recommended and I will only + describe the first form here. + + The command is followed by one of the keywords + + author + comment + compiler + + a comma and a string. The option is written into the object file + together with the string value. This is currently unidirectional and + there is no way to actually use these options once they are in the + object file. + + Examples: + + .fileopt comment, "Code stolen from my brother" + .fileopt compiler, "BASIC 2.0" + .fopt author, "J. R. User" + + +.GLOBAL + + Declare symbols as global. Must be followed by a comma separated list + of symbols to declare. Symbols from the list, that are defined somewhere + in the source, are exported, all others are imported. An additional + explicit .IMPORT or .EXPORT command for the same symbol is allowed. + + Example: + + .global foo, bar + + +.GLOBALZP + + Declare symbols as global. Must be followed by a comma separated list + of symbols to declare. Symbols from the list, that are defined + somewhere in the source, are exported, all others are imported. An + additional explicit .IMPORT or .EXPORT command for the same symbol is + explicitly allowed. The symbols in the list are explicitly marked as + zero page symols. + + Example: + + .globalzp foo, bar + + +.I16 + + Valid only in 65816 mode. Switch the index registers to 16 bit. + + Note: This command will not emit any code, it will tell the assembler to + create 16 bit operands for immediate operands. + + See also: + + .SMART + + +.I8 + + Valid only in 65816 mode. Switch the index registers to 8 bit. + + Note: This command will not emit any code, it will tell the assembler to + create 8 bit operands for immediate operands. + + See also: + + .SMART + + +.IF + + Conditional assembly: Evalute an expression and switch assembler output + on or off depending on the expression. The expression must be a constant + expression, that is, all operands must be defined. + + A expression value of zero evaluates to FALSE, any other value evaluates + to TRUE. + + +.IFBLANK + + Conditional assembly: Check if there are any remaining tokens in this + line, and evaluate to FALSE if this is the case, and to TRUE otherwise. + If the condition is not true, further lines are not assembled until + an .ELSE, .ELSEIF or .ENDIF directive. + + This command is often used to check if a macro parameter was given. + Since an empty macro parameter will evaluate to nothing, the condition + will evaluate to FALSE if an empty parameter was given. + + Example: + + .macro arg1, arg2 + .ifblank arg2 + lda #arg1 + .else + lda #arg2 + .endif + .endmacro + + See also: + + .BLANK + + +.IFCONST + + Conditional assembly: Evaluate an expression and switch assembler output + on or off depending on the constness of the expression. + + A const expression evaluates to to TRUE, a non const expression (one + containing an imported or currently undefined symbol) evaluates to + FALSE. + + See also: + + .CONST + + +.IFDEF + + Conditional assembly: Check if a symbol is defined. Must be followed by + a symbol name. The condition is true if the the given symbol is already + defined, and false otherwise. + + See also: + + .DEFINED + + +.IFNBLANK + + Conditional assembly: Check if there are any remaining tokens in this + line, and evaluate to TRUE if this is the case, and to FALSE otherwise. + If the condition is not true, further lines are not assembled until + an .ELSE, .ELSEIF or .ENDIF directive. + + This command is often used to check if a macro parameter was given. + Since an empty macro parameter will evaluate to nothing, the condition + will evaluate to FALSE if an empty parameter was given. + + Example: + + .macro arg1, arg2 + lda #arg1 + .ifnblank arg2 + lda #arg2 + .endif + .endmacro + + See also: + + .BLANK + + +.IFNDEF + + Conditional assembly: Check if a symbol is defined. Must be followed by + a symbol name. The condition is true if the the given symbol is not + defined, and false otherwise. + + See also: + + .DEFINED + + +.IFNREF + + Conditional assembly: Check if a symbol is referenced. Must be followed + by a symbol name. The condition is true if if the the given symbol was + not referenced before, and false otherwise. + + See also: + + .REFERENCED + + +.IFP02 + + Conditional assembly: Check if the assembler is currently in 6502 mode + (see .P02 command). + + +.IFP816 + + Conditional assembly: Check if the assembler is currently in 65816 mode + (see .P816 command). + + +.IFPC02 + + Conditional assembly: Check if the assembler is currently in 65C02 mode + (see .PC02 command). + + +.IFREF + + Conditional assembly: Check if a symbol is referenced. Must be followed + by a symbol name. The condition is true if if the the given symbol was + referenced before, and false otherwise. + + This command may be used to build subroutine libraries in include files + (you may use separate object modules for this purpose too). + + Example: + + .ifdef ToHex ; If someone used this subroutine + ToHex: tay ; Define subroutine + lda HexTab,y + rts + .endif + + See also: + + .REFERENCED + + +.IMPORT + + Import a symbol from another module. The command is followed by a comma + separated list of symbols to import. + + Example: + + .import foo, bar + + +.IMPORTZP + + Import a symbol from another module. The command is followed by a comma + separated list of symbols to import. The symbols are explicitly imported + as zero page symbols (that is, symbols with values in byte range). + + Example: + + .includezp foo, bar + + +.INCBIN + + Include a file as binary data. The command expects a string argument + that is the name of a file to include literally in the current segment. + + Example: + + .incbin "sprites.dat" + + +.INCLUDE + + Include another file. Include files may be nested up to a depth of 16. + + Example: + + .include "subs.inc" + + +.LINECONT + + Switch on or off line continuations using the backslash character + before a newline. The option is off by default. + Note: Line continuations do not work in a comment. A backslash at the + end of a comment is treated as part of the comment and does not trigger + line continuation. + The command must be followed by a '+' or '-' character to switch the + option on or off respectively. + + Example: + + .linecont + ; Allow line continuations + + lda \ + #$20 ; This is legal now + + +.LIST + + Enable output to the listing. The command must be followed by a boolean + switch ("on", "off", "+" or "-") and will enable or disable listing + output. + The option has no effect if the listing is not enabled by the command line + switch -l. If -l is used, an internal counter is set to 1. Lines are output + to the listing file, if the counter is greater than zero, and suppressed if + the counter is zero. Each use of .LIST will increment or decrement the + counter. + + Example: + + .list on ; Enable listing output + + +.LISTBYTES + + Set, how many bytes are shown in the listing for one source line. The + default is 12, so the listing will show only the first 12 bytes for any + source line that generates more than 12 bytes of code or data. + The directive needs an argument, which is either "unlimited", or an + integer constant in the range 4..255. + + Examples: + + .listbytes unlimited ; List all bytes + .listbytes 12 ; List the first 12 bytes + .incbin "data.bin" ; Include large binary file + + +.LOCAL + + This command may only be used inside a macro definition. It declares a + list of identifiers as local to the macro expansion. + + A problem when using macros are labels: Since they don't change their + name, you get a "duplicate symbol" error if the macro is expanded the + second time. Labels declared with .LOCAL have their name mapped to an + internal unique name (___ABCD__) with each macro invocation. + + Some other assemblers start a new lexical block inside a macro + expansion. This has some drawbacks however, since that will not allow + *any* symbol to be visible outside a macro, a feature that is sometimes + useful. The .LOCAL command is in my eyes a better way to address the + problem. + + You get an error when using .LOCAL outside a macro. + + +.LOCALCHAR + + Defines the character that start "cheap" local labels. You may use one + of '@' and '?' as start character. The default is '@'. + + Cheap local labels are labels that are visible only between two non + cheap labels. This way you can reuse identifiers like "loop" without + using explicit lexical nesting. + + Example: + + .localchar '?' + + Clear: lda #$00 ; Global label + ?Loop: sta Mem,y ; Local label + dey + bne ?Loop ; Ok + rts + Sub: ... ; New global label + bne ?Loop ; ERROR: Unknown identifier! + + +.MACPACK + + Insert a predefined macro package. The command is followed by an + identifier specifying the macro package to insert. Available macro + packages are: + + generic Defines generic macros like add and sub. + longbranch Defines conditional long jump macros. + + Including a macro package twice, or including a macro package that + redefines already existing macros will lead to an error. + + Example: + + .macpack longbranch ; Include macro package + + cmp #$20 ; Set condition codes + jne Label ; Jump long on condition + + See separate section about macros packages. + + +.MAC +.MACRO + + Start a classic macro definition. The command is followed by an identifier + (the macro name) and optionally by a comma separated list of identifiers + that are macro parameters. + See separate section about macros. + + +.MATCH + + Builtin function. Matches two token lists against each other. This is + most useful within macros, since macros are not stored as strings, but + as lists of tokens. + + The syntax is + + .MATCH(, ) + + Both token list may contain arbitrary tokens with the exception of the + terminator token (comma resp. right parenthesis) and + + * end-of-line + * end-of-file + + Often a macro parameter is used for any of the token lists. + + Please note that the function does only compare tokens, not token + attributes. So any number is equal to any other number, regardless of + the actual value. The same is true for strings. + + Example: + + Assume the macro ASR , that will shift right the accumulator by one, + while honoring the sign bit. The builtin processor instructions will + allow an optional "A" for accu addressing for instructions like ROL + and ROR. We will use the .MATCH function to check for this and print + and error for invalid calls. + + .macro asr arg + + .if (.not .blank(arg)) .and (.not .match (arg, a)) + .error "Syntax error" + .endif + + cmp #$80 ; Bit 7 into carry + lsr a ; Shit carry into bit 7 + + .endmacro + + The macro will only accept no arguments, or one argument that must + be the reserved keyword "A". + + +.ORG + + Start a section of absolute code. The command is followed by a constant + expression that gives the new PC counter location for which the code is + assembled. Use .RELOC to switch back to relocatable code. + + You may not switch segments while inside a section of absolute code. + + Example: + + .org $7FF ; Emit code starting at $7FF + + +.OUT + + Output a string to the console without producing an error. This command + is similiar to .ERROR, however, it does not force an assembler error + that prevents the creation of an object file. + + Example: + + .out "This code was written by the codebuster(tm)" + + +.P02 + + Enable the 6502 instruction set, disable 65C02 and 65816 instructions. + This is the default if not overridden by the --cpu command line option. + + +.P816 + + Enable the 65816 instruction set. This is a superset of the 65C02 and + 6502 instruction sets. + + +.PAGELEN +.PAGELENGTH + + Set the page length for the listing. Must be followed by an integer + constant. The value may be "unlimited", or in the range 32 to 127. The + statement has no effect if no listing is generated. The default value + is -1 but may be overridden by the --pagelength command line option. + Beware: Since the listing is generated after assembly is complete, you + cannot use multiple line lengths with one source. Instead, the value + set with the last .PAGELENGTH is used. + + Examples: + + .pagelength 66 ; Use 66 lines per listing page + + .pagelength unlimited ; Unlimited page length + + +.PARAMCOUNT + + This builtin pseudo variable is only available in macros. It is replaced + by the actual number of parameters that were given in the macro + invocation. + + Example: + + .macro foo arg1, arg2, arg3 + .if .paramcount <> 3 + .error "Too few parameters for macro foo" + .endif + ... + .endmacro + + +.PC02 + + Enable the 65C02 instructions set. This instruction set includes all + 6502 instructions. + + +.PROC + + Start a nested lexical level. All new symbols from now on are in the + local lexical level and are not accessible from outside. Symbols defined + outside this local level may be accessed as long as their names are not + used for new symbols inside the level. Symbols names in other lexical + levels do not clash, so you may use the same names for identifiers. The + lexical level ends when the .ENDPROC command is read. Lexical levels may + be nested up to a depth of 16. + + The command may be followed by an identifier, in this case the + identifier is declared in the outer level as a label having the value of + the program counter at the start of the lexical level. + + Note: Macro names are always in the global level and in a separate name + space. There is no special reason for this, it's just that I've never + had any need for local macro definitions. + + Example: + + .proc Clear ; Define Clear subroutine, start new level + lda #$00 + L1: sta Mem,y ; L1 is local and does not cause a + ; duplicate symbol error if used in other + ; places + dey + bne L1 ; Reference local symbol + rts + .endproc ; Leave lexical level + + +.REF +.REFERENCED + + Builtin function. The function expects an identifier as argument in + braces. The argument is evaluated, and the function yields "true" if the + identifier is a symbol that has already been referenced somewhere in the + source file up to the current position. Otherwise the function yields + false. As an example, the .IFREF statement may be replaced by + + .if .referenced(a) + + +.RELOC + + Switch back to relocatable mode. See the .ORG command. + + +.RES + + Reserve storage. The command is followed by one or two constant + expressions. The first one is mandatory and defines, how many bytes of + storage should be defined. The second, optional expression must by a + constant byte value that will be used as value of the data. If there + is no fill value given, the linker will use the value defined in the + linker configuration file (default: zero). + + Example: + + ; Reserve 12 bytes of memory with value $AA + .res 12, $AA + + +.RODATA + + Switch to the RODATA segment. The name of the RODATA segment is always + "RODATA", so this is a shortcut for + + .segment "RODATA" + + The RODATA segment is a segment that is used by the compiler for + readonly data like string constants. See also the .SEGMENT command. + + +.SEGMENT + + Switch to another segment. Code and data is always emitted into a + segment, that is, a named section of data. The default segment is + "CODE". There may be up to 254 different segments per object file + (and up to 65534 per executable). There are shortcut commands for + the most common segments ("CODE", "DATA" and "BSS"). + + The command is followed by a string containing the segment name (there + are some constraints for the name - as a rule of thumb use only those + segment names that would also be valid identifiers). There may also be + an optional attribute separated by a comma. Valid attributes are + + zeropage + and absolute + + When specifying a segment for the first time, "absolute" is the + default. For all other uses, the attribute specified the first time + is the default. + + "absolute" means that this is a segment with absolute addressing. That + is, the segment will reside somewhere in core memory outside the zero + page. "zeropage" means the opposite: The segment will be placed in the + zero page and direct (short) addressing is possible for data in this + segment. + + Beware: Only labels in a segment with the zeropage attribute are marked + as reachable by short addressing. The `*' (PC counter) operator will + work as in other segments and will create absolute variable values. + + Example: + + .segment "ROM2" ; Switch to ROM2 segment + .segment "ZP2", zeropage ; New direct segment + .segment "ZP2" ; Ok, will use last attribute + .segment "ZP2", absolute ; Error, redecl mismatch + + +.SMART + + Switch on or off smart mode. The command must be followed by a '+' or + '-' character to switch the option on or off respectively. The default + is off (that is, the assembler doesn't try to be smart), but this + default may be changed by the -s switch on the command line. + + In smart mode the assembler will track usage of the REP and SEP + instructions in 65816 mode and update the operand sizes accordingly. If + the operand of such an instruction cannot be evaluated by the assembler + (for example, because the operand is an imported symbol), a warning is + issued. Beware: Since the assembler cannot trace the execution flow this + may lead to false results in some cases. If in doubt, use the .ixx and + .axx instructions to tell the assembler about the current settings. + + Example: + + .smart ; Be smart + .smart - ; Stop being smart + + +.STRING + + Builtin function. The function accepts an argument in braces and + converts this argument into a string constant. The argument may be an + identifier, or a constant numeric value. + Since you can use a string in the first place, the use of the function + may not be obvious. However, it is useful in macros, or more complex + setups. + + Example: + + ; Emulate other assemblers: + .macro section name + .segment .string(name) + .endmacro + + +.WORD + + Define word sized data. Must be followed by a sequence of (word ranged, + but not necessarily constant) expressions. + + Example: + + .word $0D00, $AF13, _Clear + + +.ZEROPAGE + + Switch to the ZEROPAGE segment and mark it as direct (zeropage) segment. + The name of the ZEROPAGE segment is always "ZEROPAGE", so this is a + shortcut for + + .segment "ZEROPAGE", zeropage + + Because of the "zeropage" attribute, labels declared in this segment are + addressed using direct addressing mode if possible. You MUST instruct + the linker to place this segment somewhere in the address range 0..$FF + otherwise you will get errors. + + + +7. Macros +--------- + +Macros may be thought of as "parametrized super instructions". Macros are +sequences of tokens that have a name. If that name is used in the source +file, the macro is "expanded", that is, it is replaced by the tokens that +were specified when the macro was defined. + +In it's simplest form, a macro does not have parameters. Here's an +example: + + .macro asr ; Arithmetic shift right + cmp #$80 ; Put bit 7 into carry + ror ; Rotate right with carry + .endmacro + +The macro above consists of two real instructions, that are inserted into +the code, whenever the macro is expanded. Macro expansion is simply done +by using the name, like this: + + lda $2010 + asr + sta $2010 + + +When using macro parameters, macros can be even more useful: + + .macro inc16 addr + clc + lda addr + adc #$01 + sta addr + lda addr+1 + adc #$00 + sta addr+1 + .endmacro + +When calling the macro, you may give a parameter, and each occurence of +the name "addr" in the macro definition will be replaced by the given +parameter. So + + inc16 $1000 + +will be expanded to + + clc + lda $1000 + adc #$01 + sta $1000 + lda $1000+1 + adc #$00 + sta $1000+1 + +A macro may have more than one parameter, in this case, the parameters +are separated by commas. You are free to give less parameters than the +macro actually takes in the definition. You may also leave intermediate +parameters empty. Empty parameters are replaced by empty space (that is, +they are removed when the macro is exanded). If you have a look at our +macro definition above, you will see, that replacing the "addr" parameter +by nothing will lead to wrong code in most lines. To help you, writing +macros with a variable parameter list, there are some control commands: + +.IFBLANK tests the rest of the line and returns true, if there are any +tokens on the remainder of the line. Since empty parameters are replaced +by nothing, this may be used to test if a given parameter is empty. +.IFNBLANK tests the opposite. + +Look at this example: + + .macro ldaxy a, x, y + .ifnblank a + lda #a + .endif + .ifnblank x + ldx #x + .endif + .ifnblank y + ldy #y + .endif + .endmacro + +This macro may be called as follows: + + ldaxy 1, 2, 3 ; Load all three registers + + ldaxy 1, , 3 ; Load only a and y + + ldaxy , , 3 ; Load y only + +There's another helper command for determining, which macro parameters are +valid: .PARAMCOUNT. This command is replaced by the parameter count given, +*including* intermediate empty macro parameters: + + ldaxy 1 ; .PARAMCOUNT = 1 + ldaxy 1,,3 ; .PARAMCOUNT = 3 + ldaxy 1,2 ; .PARAMCOUNT = 2 + ldaxy 1, ; .PARAMCOUNT = 2 + ldaxy 1,2,3 ; .PARAMCOUNT = 3 + +Macros may be used recursively: + + .macro push r1, r2, r3 + lda r1 + pha + .if .paramcount > 1 + push r2, r3 + .endif + .endmacro + +There's also a special macro to help writing recursive macros: .EXITMACRO. +This command will stop macro expansion immidiately: + + .macro push r1, r2, r3, r4, r5, r6, r7 + .ifblank r1 + ; First parameter is empty + .exitmacro + .else + lda r1 + pha + .endif + push r2, r3, r4, r5, r6, r7 + .endmacro + +When expanding this macro, the expansion will push all given parameters +until an empty one is encountered. The macro may be called like this: + + push $20, $21, $32 ; Push 3 ZP locations + push $21 ; Push one ZP location + +Now, with recursive macros, .IFBLANK and .PARAMCOUNT, what else do you need? +Have a look at the inc16 macro above. Here is it again: + + .macro inc16 addr + clc + lda addr + adc #$01 + sta addr + lda addr+1 + adc #$00 + sta addr+1 + .endmacro + +If you have a closer look at the code, you will notice, that it could be +written more efficiently, like this: + + .macro inc16 addr + clc + lda addr + adc #$01 + sta addr + bcc Skip + inc addr+1 + Skip: + .endmacro + +But imagine what happens, if you use this macro twice? Since the label +"Skip" has the same name both times, you get a "duplicate symbol" error. +Without a way to circumvent this problem, macros are not as useful, as +they could be. One solution is, to start a new lexical block inside the +macro: + + .macro inc16 addr + .proc + clc + lda addr + adc #$01 + sta addr + bcc Skip + inc addr+1 + Skip: + .endproc + .endmacro + +Now the label is local to the block and not visible outside. However, +sometimes you want a label inside the macro to be visible outside. To make +that possible, there's a new command that's only usable inside a macro +definition: .LOCAL. .LOCAL declares one or more symbols as local to the +macro expansion. The names of local variables are replaced by a unique +name in each separate macro expansion. So we could also solve the problem +above by using .LOCAL: + + .macro inc16 addr + .local Skip ; Make Skip a local symbol + clc + lda addr + adc #$01 + sta addr + bcc Skip + inc addr+1 + Skip: ; Not visible outside + .endmacro + +Starting with version 2.5 of the assembler, there is a second macro type +available: C style macros using the .DEFINE directive. These macros are +similar to the classic macro type speified above, but behaviour is +sometimes different: + + * Macros defined with .DEFINE may not span more than a line. You may + use line continuation (.LINECONT) to spread the definition over more + than one line for increased readability, but the macro itself does + not contain an end-of-line token. + + * Macros defined with .DEFINE share the name space with classic macros, + but they are detected and replaced at the scanner level. While classic + macros may be used in every place, where a mnemonic or other directive + is allowed, .DEFINE style macros are allowed anywhere in a line. So + they are more versatile in some situations. + + * .DEFINE style macros may take parameters. While classic macros may + have empty parameters, this is not true for .DEFINE style macros. For + this macro type, the number of actual parameters must match exactly + the number of formal parameters. + To make this possible, formal parameters are enclosed in braces when + defining the macro. If there are no parameters, the empty braces may + be omitted. + + * Since .DEFINE style macros may not contain end-of-line tokens, there + are things that cannot be done. They may not contain several processor + instructions for example. So, while some things may be done with both + macro types, each type has special usages. The types complement each + other. + +Let's look at a few examples to make the advantages and disadvantages +clear. + +To emulate assemblers that use "EQU" instead of "=" you may use the +following .DEFINE: + + .define EQU = + + foo EQU $1234 ; This is accepted now + +You may use the directive to define string constants use elsewhere: + + ; Define the version number + .define VERSION "12.3a" + + ; ... and use it + .asciiz VERSION + +Macros with parameters may also be useful: + + .define DEBUG(message) .out message + + DEBUG "Assembling include file #3" + +Note that, while formal parameters have to be placed in braces, this is +not true for the actual parameters. Beware: Since the assembler cannot +detect the end of one parameter, only the first token is used. If you +don't like that, use classic macros instead: + + .macro message + .out message + .endmacro + +(This is an example where a problem can be solved with both macro types). + + + +8. Macro packages +----------------- + +Using the .macpack directive, predefined macro packages may be included +with just one command. Available macro packages are: + + - generic + + This macro package defines macros that are useful in almost any + program. Currently, two macros are defined: + + .macro add Arg + clc + adc Arg + .endmacro + + .macro sub Arg + sec + sbc Arg + .endmacro + + + - longbranch + + This macro package defines long conditional jumps. They are named like + the short counterpart but with the 'b' replaced by a 'j'. Here is a + sample definition for the "jeq" macro, the other macros are built using + the same scheme: + + .macro jeq Target + .if .def(Target) .and ((*+2)-(Target) <= 127) + beq Target + .else + bne *+5 + jmp Target + .endif + .endmacro + + All macros expand to a short branch, if the label is already defined + (back jump) and is reachable with a short jump. Otherwise the macro + expands to a conditional branch with the branch condition inverted, + followed by an absolute jump to the actual branch target. + + The package defines the following macros: + + jeq, jne, jmi, jpl, jcs, jcc, jvs, jvc + + + +9. Bugs/Feedback +---------------- + +If you have problems using the assembler, if you find any bugs, or if +you're doing something interesting with the assembler, I would be glad to +hear from you. Feel free to contact me by email (uz@musoftware.de). + + + +10. Copyright +------------- + +ca65 (and all cc65 binutils) are (C) Copyright 1998 Ullrich von Bassewitz. +For usage of the binaries and/or sources the following conditions do +apply: + +This software is provided 'as-is', without any expressed or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. +3. This notice may not be removed or altered from any source + distribution. + + + + diff --git a/doc/cc65.txt b/doc/cc65.txt new file mode 100644 index 000000000..e99f2623c --- /dev/null +++ b/doc/cc65.txt @@ -0,0 +1,600 @@ + + + cc65 + + A C Compiler for 6502 Systems + + (C) Copyright 1989 John R. Dunning + (C) Copyright 1998-2000 Ullrich von Bassewitz + (uz@musoftware.de) + + + +Contents +-------- + + 1. Overview + + 2. Usage + + 3. Input and output + + 4. Differences to the ISO standard + + 5. Extensions + + 6. Predefined macros + + 7. #pragmas + + 8. Bugs/Feedback + + 9. Copyright + + + +1. Overview +----------- + +cc65 was originally a C compiler for the Atari 8-bit machines written by +John R. Dunning. In prior releases I've described the compiler by listing +up the changes made by me. I have made many more changes in the meantime +(and rewritten major parts of the compiler), so I will no longer do that, +since the list would be too large and of no use to anyone. Instead I will +describe the compiler in respect to the ANSI/ISO C standard. In fact, I'm +planning a complete rewrite (that is, a complete new compiler) for the +next release, since there are too many limitations in the current code, +and removing these limitations would mean a rewrite of many more parts of +the compiler. + +There is a separate document named "library.txt" that covers the library +available for the compiler. If you know C and are interested in doing +actual programming, the library documentation is probably of much more use +than this document. + +If you need some hints for getting the best code out of the compiler, you +may have a look at "coding.txt" which covers some code generation issues. + + + +2. Usage +-------- + +The compiler translates C files into files containing assembler code that +may be translated by the ca65 macroassembler (for more information about +the assembler, have a look at ca65.txt). + +The compiler may be called as follows: + +Usage: cc65 [options] file + -d Debug mode + -g Add debug info to object files + -h Print this help + -j Default characters are signed + -o name Name the output file + -s Print some statistics + -tx Set target system x + -v Verbose mode + -A Strict ANSI mode + -Cl Make local variables static + -Dsym[=defn] Define a symbol + -I path Set include directory + -O Optimize code + -Oi Optimize code, inline more code + -Or Enable register variables + -Os Inline some known functions + -T Include source as comment + -V Print version number + -W Suppress warnings + +The -A option disables any compiler exensions. Have a look at section 5 +for a discussion of compiler extensions. In addition, the macro + + __STRICT_ANSI__ + +is defined, when compiling with -A. + +-d enables debug mode, something that should not be needed for mere +mortals:-) + +-g will cause the compiler to insert a .DEBUGINFO command into the +generated assembler code. This will cause the assembler to include all +symbols in a special section in the object file. + +-h and -s print some statistics, nothing spectacular. + +Using -j you can make the default characters signed. Since the 6502 has +no provisions for sign extending characters (which is needed on almost +any load operation), this will make the code larger and slower. A better +way is to declare characters explicitly as "signed" if needed. You can +also use "#pragma signedchars" for better control of this option (see +section 7). + +The -t option is used to set the target system. The target system +determines things like the character set that is used for strings and +character constants. The following target systems are supported: + + none + c64 + c128 + ace (no library support) + plus4 + cbm610 + pet (all CBM PET systems except the 2001) + nes (Nintendo Entertainment System) + apple2 + geos + +Using -v, the compiler will be somewhat more verbose if errors or warnings +are encountered. + +-Cl will use static storage for local variables instead of storage on the +stack. Since the stack is emulated in software, this gives shorter and +usually faster code, but the code is no longer reentrant. The difference +between -Cl and declaring local variables as static yourself is, that +initializer code is executed each time, the function is entered. So when +using + + void f (void) + { + unsigned a = 1; + ... + } + +the variable a will always have the value 1 when entering the function and +using -Cl, while in + + void f (void) + { + static unsigned a = 1; + .... + } + +the variable a will have the value 1 only the first time, the function is +entered, and will keep the old value from one call of the function to the +next. + +You may also use #pragma staticlocals to change this setting in your +sources (see section 7). + +-I sets the directory where the compiler searches for include files. You +may use -I multiple times to add more than one directory to the search +list. + +-O will enable an optimizer run over the produced code. Using -Oi, the +code generator will inline some code where otherwise a runtime functions +would have been called, even if the generated code is larger. This will +not only remove the overhead for a function call, but will make the code +visible for the optimizer. + +-Or will make the compiler honor the "register" keyword. Local variables +may be placed in registers (which are actually zero page locations). +There is some overhead involved with register variables, since the old +contents of the registers must be saved and restored. In addition, the +current implementation does not make good use of register variables, so +using -Or may make your program even slower and larger. Use with care! + +Using -Os will force the compiler to inline some known functions from the +C library like strlen. Note: This has two consequences: + + * You may not use names of standard C functions in your own code. If + you do that, your program is not standard compliant anyway, but + using -Os will actually break things. + + * The inlined string and memory functions will not handle strings or + memory areas larger than 255 bytes. Similar, the inlined is..() + functions will not work with values outside char range. + +It is possible to concatenate the modifiers for -O. For example, to +enable register variables and inlining of known functions, you may use +-Ors. + +-T will include the source code as comments in the generated code. This is +normally not needed. + +-V prints the version number of the compiler. When submitting a bug +report, please include the operating system you're using, and the compiler +version. + +The -W switch suppresses any warnings generated by the compiler. Since any +source file may be written in a manner that it will not produce compiler +warnings, using this option is usually not a good idea. + + + +3. Input and output +------------------- + +The compiler will accept one C file per invocation and create a file with +the same base name, but with the extension replaced by ".s". The output +file contains assembler code suitable for the use with the ca65 macro +assembler. + +In addition to the paths named in the -I option on the command line, the +directory named in the environment variable CC65_INC is added to the +search path for include files on startup. + + + +4. Differences to the ISO standard +---------------------------------- + +Here is a list of differences between the language, the compiler accepts, +and the one defined by the ISO standard: + + + * The compiler allows single line comments that start with //. This + feature is disabled in strict ANSI mode. + + * The compiler allows unnamed parameters in parameter lists. The + compiler will not issue warnings about unused parameters that don't + have a name. This feature is disabled in strict ANSI mode. + + * The compiler has some additional keywords: + + asm, __asm__, fastcall, __fastcall__, __AX__, __EAX__, __func__ + + The keywords without the underlines are disabled in strict ANSI mode. + + * The "const" modifier is available, but has no effect. + + * The datatypes "float" and "double" are not available. + + * The compiler does not support bit fields. + + * Initialization of local variables is only possible for scalar data + types (that is, not for arrays and structs). + + * Because of the "wrong" order of the parameters on the stack, there is + an additional macro needed to access parameters in a variable + parameter list in a C function. + + * The compiler has only one symbol table. Because of that, it's not + possible to use the name of a local variable in a nested block in the + same function (global and local names are distinct, however). + + + The preprocessor does not understand the "defined" keyword in + expressions evaluated in #if statements. + + * Functions may not return structs, struct assignment is not possible. + + * The size of any struct referenced via a pointer may not exceed 256 + bytes (this is because the Y register is used as index). + + * In a function, the size of the parameters plus the size of all local + variables may not exceed 256 bytes (in fact, the limit may be even less + depeding on the complexity of your expressions). + + * Part of the C library is available only with fastcall calling + conventions (see below). This means, that you may not mix pointers to + those functions with pointers to user written functions. + +There may be some more minor differences, I'm currently not aware off. The +biggest problems are the missing const and float data types. With both +these things in mind, you should be able to write fairly portable code. + + + +5. Extensions +------------- + +This cc65 version has some extensions to the ISO C standard. + + * The compiler allows // comments (like in C++ and in the proposed C9x + standard). This feature is disabled by -A. + + * The compiler allows to insert assembler statements into the output + file. The syntax is + + asm () ; + + or + + __asm__ () ; + + The first form is in the user namespace and is disabled if the -A + switch is given. + + The given string is inserted literally into the output file, and a + newline is appended. The statements in this string are not checked by + the compiler, so be careful! + + The asm statement may be used inside a function and on global file + level. + + * There is a special calling convention named "fastcall". This calling + convention is currently only usable for functions written in + assembler. The syntax for a function declaration using fastcall is + + fastcall () + + or + + __fastcall__ () + + An example would be + + void __fastcall__ f (unsigned char c) + + The first form of the fastcall keyword is in the user namespace and is + therefore disabled in strict ANSI mode. + + For functions declared as fastcall, the rightmost parameter is not + pushed on the stack but left in the primary register when the function + is called. This will reduce the cost when calling assembler functions + significantly, especially when the function itself is rather small. + + BEWARE: You must not declare C functions as fastcall! This will not + work for now and is not checked by the assembler, so you will get + wrong code. + + * There are two pseudo variables named __AX__ and __EAX__. Both refer to + the primary register that is used by the compiler to evaluate + expressions or return function results. __AX__ is of type unsigned int + and __EAX__ of type long unsigned int respectively. The pseudo + variables may be used as lvalue and rvalue as every other variable. + They are most useful together with short sequences of assembler code. + For example, the macro + + #define hi(x) (__AX__=(x),asm("\ttxa\n\tldx\t#$00",__AX__) + + will give the high byte of any unsigned value. + + * Inside a function, the identifier __func__ gives the name of the + current function as a string. Outside of functions, __func__ is + undefined. + Example: + + #define PRINT_DEBUG(s) printf ("%s: %s\n", __func__, s); + + The macro will print the name of the current function plus a given + string. + + + +6. Predefined macros +-------------------- + +The compiler defines several macros at startup: + + + __CC65__ This macro is always defined. Its value is the version + number of the compiler in hex. Version 2.0.1 of the + compiler will have this macro defined as 0x0201. + + __CBM__ This macro is defined if the target system is one of the + CBM targets. + + __C64__ This macro is defined if the target is the c64 (-t c64). + + __C128__ This macro is defined if the target is the c128 (-t c128). + + __PLUS4__ This macro is defined if the target is the plus/4 + (-t plus4). + + __CBM610__ This macro is defined if the target is one of the CBM + 600/700 family of computers (called B series in the US). + + __PET__ This macro is defined if the target is the PET family of + computers (-t pet). + + __NES__ This macro is defined if the target is the Nintendo + Entertainment System (-t nes). + + __ATARI__ This macro is defined if the target is one of the Atari + computers (400/800/130XL/800XL). Note that there is no + runtime and C library support for atari systems. + + __ACE__ This macro is defined if the target is Bruce Craigs ACE + operating system. Note that there is no longer runtime + and library support for ACE. + + __APPLE2__ This macro is defined if the target is the Apple ][ + (-t apple2). + + __GEOS__ This macro is defined if you are compiling for the GEOS + system (-t geos). + + __FILE__ This macro expands to a string containing the name of + the C source file. + + __LINE__ This macro expands to the current line number. + + __STRICT_ANSI__ This macro is defined to 1 if the -A compiler option was + given, and undefined otherwise. + + __OPT__ Is defined if the compiler was called with the -O command + line option. + + __OPT_i__ Is defined if the compiler was called with the -Oi command + line option. + + __OPT_r__ Is defined if the compiler was called with the -Or command + line option. + + __OPT_s__ Is defined if the compiler was called with the -Os command + line option. + + + +7. #pragmas +----------- + +The compiler understands some pragmas that may be used to change code +generation and other stuff. + + +#pragma bssseg () + + This pragma changes the name used for the BSS segment (the BSS segment + is used to store uninitialized data). The argument is a string enclosed + in double quotes. + + Note: The default linker configuration file does only map the standard + segments. If you use other segments, you have to create a new linker + configuration file. + + Beware: The startup code will zero only the default BSS segment. If you + use another BSS segment, you have to do that yourself, otherwise + uninitialized variables do not have the value zero. + + Example: + + #pragma bssseg ("MyBSS") + + +#pragma codeseg () + + This pragma changes the name used for the CODE segment (the CODE segment + is used to store executable code). The argument is a string enclosed in + double quotes. + + Note: The default linker configuration file does only map the standard + segments. If you use other segments, you have to create a new linker + configuration file. + + Example: + + #pragma bssseg ("MyCODE") + + +#pragma dataseg () + + This pragma changes the name used for the DATA segment (the DATA segment + is used to store initialized data). The argument is a string enclosed in + double quotes. + + Note: The default linker configuration file does only map the standard + segments. If you use other segments, you have to create a new linker + configuration file. + + Example: + + #pragma bssseg ("MyDATA") + + +#pragma rodataseg () + + This pragma changes the name used for the RODATA segment (the RODATA + segment is used to store readonly data). The argument is a string + enclosed in double quotes. + + Note: The default linker configuration file does only map the standard + segments. If you use other segments, you have to create a new linker + configuration file. + + Example: + + #pragma bssseg ("MyRODATA") + + +#pragma regvaraddr () + + The compiler does not allow to take the address of register variables. + The regvaraddr pragma changes this. Taking the address of a register + variable is allowed after using this pragma, if the argument is not + zero. Using an argument of zero changes back to the default behaviour. + + Beware: The C standard does not allow taking the address of a variable + declared as register. So your programs become non-portable if you use + this pragma. In addition, your program may not work. This is usually the + case if a subroutine is called with the address of a register variable, + and this subroutine (or a subroutine called from there) uses itself + register variables. So be careful with this #pragma. + + Example: + + #pragma regvaraddr(1) /* Allow taking the address + * of register variables + */ + + +#pragma signedchars () + + Changed the signedness of the default character type. If the argument + is not zero, default characters are signed, otherwise characters are + unsigned. The compiler default is to make characters unsigned since this + creates a lot better code. + + +#pragma staticlocals () + + Use variables in the bss segment instead of variables on the stack. This + pragma changes the default set by the compiler option -Cl. If the argument + is not zero, local variables are allocated in the BSS segment, leading to + shorter and in most cases faster, but non-reentrant code. + + +#pragma zpsym () + + Tell the compiler that the - previously as external declared - symbol with + the given name is a zero page symbol (usually from an assembler file). + The compiler will create a matching import declaration for the assembler. + + Example: + + extern int foo; + #pragma zpsym ("foo"); /* foo is in the zeropage */ + + + +8. Bugs/Feedback +---------------- + +If you have problems using the compiler, if you find any bugs, or if +you're doing something interesting with the compiler, I would be glad to +hear from you. Feel free to contact me by email (uz@musoftware.de). + + + +9. Copyright +------------ + +This is the original compiler copyright: + +-------------------------------------------------------------------------- + -*- Mode: Text -*- + + This is the copyright notice for RA65, LINK65, LIBR65, and other + Atari 8-bit programs. Said programs are Copyright 1989, by John R. + Dunning. All rights reserved, with the following exceptions: + + Anyone may copy or redistribute these programs, provided that: + + 1: You don't charge anything for the copy. It is permissable to + charge a nominal fee for media, etc. + + 2: All source code and documentation for the programs is made + available as part of the distribution. + + 3: This copyright notice is preserved verbatim, and included in + the distribution. + + You are allowed to modify these programs, and redistribute the + modified versions, provided that the modifications are clearly noted. + + There is NO WARRANTY with this software, it comes as is, and is + distributed in the hope that it may be useful. + + This copyright notice applies to any program which contains + this text, or the refers to this file. + + This copyright notice is based on the one published by the Free + Software Foundation, sometimes known as the GNU project. The idea + is the same as theirs, ie the software is free, and is intended to + stay that way. Everybody has the right to copy, modify, and re- + distribute this software. Nobody has the right to prevent anyone + else from copying, modifying or redistributing it. + +-------------------------------------------------------------------------- + +In acknowledgment of this copyright, I will place my own changes to the +compiler under the same copyright. Please note however, that the library +and all binutils are covered by another copyright, and that I'm planning +to do a complete rewrite of the compiler, after which the compiler +copyright will also change. + +For the list of changes requested by this copyright see newvers.txt. + + + diff --git a/doc/cl65.txt b/doc/cl65.txt new file mode 100644 index 000000000..0945b117a --- /dev/null +++ b/doc/cl65.txt @@ -0,0 +1,183 @@ + + + cl65 + + Compile and link utility for cc65 + + (C) Copyright 1999 Ullrich von Bassewitz + (uz@musoftware.de) + + + +Contents +-------- + + 1. Overview + + 2. Basic Usage + + 3. More usage + + 4. Examples + + 5. Bugs/Feedback + + 6. Copyright + + + +1. Overview +----------- + +cl65 is a frontend for cc65, ca65 and ld65. While you may not use the full +power of the tools when calling them through cl65, most features are +available, and the use of cl65 is much simpler. + + + +2. Usage +-------- + +The cl65 compile and link utility may be used to compile, assemble and +link files. While the separate tools do just one step, cl65 knows how to +build object files from C files (by calling the compiler, then the +assembler) and other things. + + Usage: cl65 [options] file + Options: + -A Strict ANSI mode + -C name Use linker config file + -D sym[=defn] Define a preprocessor symbol + -I path Set an include directory path + -Ln name Create a VICE label file + -O Optimize code + -Oi Optimize code, inline functions + -Or Optimize code, honour the register keyword + -Os Optimize code, inline known C funtions + -S Compile but don't assemble and link + -V Print the version number + -W Suppress warnings + -c Compiler and assemble but don't link + -d Debug mode + -g Add debug info + -h Help (this text) + -m name Create a map file + -o name Name the output file + -t system Set the target system + -v Verbose mode + -vm Verbose map file + +Most of the options have the same meaning than the corresponding compiler, +assembler or linker option. If an option is available for more than one +of the tools, it is set for all tools, where it is available. One example +for this is -v: The compiler, the assembler and the linker are all called +with the -v switch. + +There are a few remaining options that control the behaviour of cl65: + +The -S option forces cl65 to stop after the assembly step. This means that +C files are translated into assembler files, but nothing more is done. +Assembler files, object files and libraries given on the command line are +ignored. + +The -c options forces cl65 to stop after the assembly step. This means +that C and assembler files given on the command line are translated into +object files, but there is no link step, and object files and libraries +given on the command line are ignored. + +The -o option is used for the target name in the final step. This causes +problems, if the linker will not be called, and there are several input +files on the command line. In this case, the name given with -o will be +used for all of them, which makes the option pretty useless. You shouldn't +use -o when more than one output file is created. + +The default for the -t option is different from the compiler and linker in +the case that the option is missing: While the compiler and linker will +use the "none" system settings by default, cl65 will use the C64 as a +target system by default. This was choosen since most people seem to use +cc65 to develop for the C64. + + + +3. More usage +------------- + +Since cl65 was created to simplify the use of the cc65 development +package, it tries to be smart about several things. + + - If you don't give a target system on the command line, cl65 + defaults to the C64. + + - When linking, cl65 will supply the names of the startup file and + library for the target system to the linker, so you don't have to do + that. + + - If the final step is the linker, and the name of the output file was + not explicitly given, cl65 will use the name of the first input file + without the extension, provided that the name of this file has an + extension. So you don't need to name the executable name in most + cases, just give the name of your "main" file as first input file. + + + +4. Examples +----------- + +The morse trainer software, which consists of one C file (morse.c) and one +assembler file (irq.s) will need the following separate steps to compile +into an executable named morse: + + cc65 -g -Oi -t c64 morse.c + ca65 -g morse.s + ca65 -g irq.s + ld65 -t c64 -o morse c64.o morse.o irq.o c64.lib + +When using cl65, this is simplified to + + cl65 -g -Oi morse.c irq.s + + +As a general rule, you may use cl65 instead of cc65 at most times, +especially in makefiles to build object files directly from C files. Use + + .c.o: + cl65 -g -Oi $< + +to do this. + + + +5. Bugs/Feedback +---------------- + +If you have problems using the utility, if you find any bugs, or if you're +doing something interesting with it, I would be glad to hear from you. +Feel free to contact me by email (uz@musoftware.de). + + + +6. Copyright +------------ + +cl65 is (C) Copyright 1998 Ullrich von Bassewitz. For usage of the +binaries and/or sources the following conditions do apply: + +This software is provided 'as-is', without any expressed or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. +3. This notice may not be removed or altered from any source + distribution. + + + diff --git a/doc/coding.txt b/doc/coding.txt new file mode 100644 index 000000000..66e6c17a4 --- /dev/null +++ b/doc/coding.txt @@ -0,0 +1,340 @@ + +How to generate the most effective code with cc65. + + +1. Use prototypes. + + This will not only help to find errors between separate modules, it will + also generate better code, since the compiler must not assume that a + variable sized parameter list is in place and must not pass the argument + count to the called function. This will lead to shorter and faster code. + + + +2. Don't declare auto variables in nested function blocks. + + Variable declarations in nested blocks are usually a good thing. But with + cc65, there are several drawbacks: + + a. The compiler has only one symbol table (there's no true nesting). + This means that your variables must not have the same names as + variables in the enclosing block. + + b. Since the compiler generates code in one pass, it must create the + the variables on the stack each time the block is entered and destroy + them when the block is left. This causes a speed penalty and larger + code. + + + +3. Remember that the compiler does not optimize. + + The compiler needs hints from you about the code to generate. When + accessing indexed data structures, get a pointer to the element and + use this pointer instead of calculating the index again and again. + If you want to have your loops unrolled, or loop invariant code moved + outside the loop, you have to do that yourself. + + + +4. Longs are slow! + + While long support is necessary for some things, it's really, really slow + on the 6502. Remember that any long variable will use 4 bytes of memory, + and any operation works on double the data compared to an int. + + + +5. Use unsigned types wherever possible. + + The CPU has no opcodes to handle signed values greater than 8 bit. So + sign extension, test of signedness etc. has to be done by hand. The + code to handle signed operations is usually a bit slower than the same + code for unsigned types. + + + +6. Use chars instead of ints if possible. + + While in arithmetic operations, chars are immidiately promoted to ints, + they are passed as chars in parameter lists and are accessed as chars + in variables. The code generated is usually not much smaller, but it + is faster, since accessing chars is faster. For several operations, the + generated code may be better if intermediate results that are known not + to be larger than 8 bit are casted to chars. + + When doing + + unsigned char a; + ... + if ((a & 0x0F) == 0) + + the result of the & operator is an int because of the int promotion + rules of the language. So the compare is also done with 16 bits. When + using + + unsigned char a; + ... + if ((unsigned char)(a & 0x0F) == 0) + + the generated code is much shorter, since the operation is done with + 8 bits instead of 16. + + + +7. Make the size of your array elements one of 1, 2, 4, 8. + + When indexing into an array, the compiler has to calculate the byte + offset into the array, which is the index multiplied by the size of + one element. When doing the multiplication, the compiler will do a + strength reduction, that is, replace the multiplication by a shift + if possible. For the values 2, 4 and 8, there are even more specialized + subroutines available. So, array access is fastest when using one of + these sizes. + + + +8. Expressions are evaluated from left to right. + + Since cc65 is not building an explicit expression tree when parsing an + expression, constant subexpressions may not be detected and optimized + properly if you don't help. Look at this example: + + #define OFFS 4 + int i; + i = i + OFFS + 3; + + The expression is parsed from left to right, that means, the compiler sees + 'i', and puts it contents into the secondary register. Next is OFFS, which + is constant. The compiler emits code to add a constant to the secondary + register. Same thing again for the constant 3. So the code produced + contains a fetch of 'i', two additions of constants, and a store (into + 'i'). Unfortunately, the compiler does not see, that "OFFS + 3" is a + constant for itself, since it does it's evaluation from left to right. + There are some ways to help the compiler to recognize expression like + this: + + a. Write "i = OFFS + 3 + i;". Since the first and second operand are + constant, the compiler will evaluate them at compile time reducing the + code to a fetch, one addition (secondary + constant) and one store. + + b. Write "i = i + (OFFS + 3)". When seeing the opening parenthesis, the + compiler will start a new expression evaluation for the stuff in the + braces, and since all operands in the subexpression are constant, it + will detect this and reduce the code to one fetch, one addition and + one store. + + + +9. Case labels in a switch statments are checked in source order. + + Labels that appear first in a switch statement are tested first. So, + if your switch statement contains labels that are selected most of + the time, put them first in your source code. This will speed up the + code. + + + +10. Use the preincrement and predecrement operators. + + The compiler is currently not smart enough to figure out, if the rvalue of + an increment is used or not. So it has to save and restore that value when + producing code for the postincrement and postdecrement operators, even if + this value is never used. To avoid the additional overhead, use the + preincrement and predecrement operators if you don't need the resulting + value. That means, use + + ... + ++i; + ... + + instead of + + ... + i++; + ... + + + +11. Use constants to access absolute memory locations. + + The compiler produces optimized code, if the value of a pointer is a + constant. So, to access direct memory locations, use + + #define VDC_DATA 0xD601 + *(char*)VDC_STATUS = 0x01; + + That will be translated to + + lda #$01 + sta $D600 + + The constant value detection works also for struct pointers and arrays, + if the subscript is a constant. So + + #define VDC ((unsigned char*)0xD600) + #define STATUS 0x01 + VDC [STATUS] = 0x01; + + will also work. + + If you first load the constant into a variable and use that variable to + access an absolute memory location, the generated code will be much + slower, since the compiler does not know anything about the contents of + the variable. + + + +12. Use initialized local variables - but use it with care. + + Initialization of local variables when declaring them gives shorter + and faster code. So, use + + int i = 1; + + instead of + + int i; + i = 1; + + But beware: To maximize your savings, don't mix uninitialized and + initialized variables. Create one block of initialized variables and + one of uniniitalized ones. The reason for this is, that the compiler + will sum up the space needed for uninitialized variables as long as + possible, and then allocate the space once for all these variables. + If you mix uninitialized and initialized variables, you force the + compiler to allocate space for the uninitialized variables each time, + it parses an initialized one. So do this: + + int i, j; + int a = 3; + int b = 0; + + instead of + + int i; + int a = 3; + int j; + int b = 0; + + The latter will work, but will create larger and slower code. + + + +13. When using the ?: operator, cast values that are not ints. + + The result type of the ?: operator is a long, if one of the second or + third operands is a long. If the second operand has been evaluated and + it was of type int, and the compiler detects that the third operand is + a long, it has to add an additional int->long conversion for the + second operand. However, since the code for the second operand has + already been emitted, this gives much worse code. + + Look at this: + + long f (long a) + { + return (a != 0)? 1 : a; + } + + When the compiler sees the literal "1", it does not know, that the + result type of the ?: operator is a long, so it will emit code to load + a integer constant 1. After parsing "a", which is a long, a int->long + conversion has to be applied to the second operand. This creates one + additional jump, and an additional code for the conversion. + + A better way would have been to write: + + long f (long a) + { + return (a != 0)? 1L : a; + } + + By forcing the literal "1" to be of type long, the correct code is + created in the first place, and no additional conversion code is + needed. + + + +14. Use the array operator [] even for pointers. + + When addressing an array via a pointer, don't use the plus and + dereference operators, but the array operator. This will generate + better code in some common cases. + + Don't use + + char* a; + char b, c; + char b = *(a + c); + + Use + + char* a; + char b, c; + char b = a[c]; + + instead. + + + +15. Use register variables with care. + + Register variables may give faster and shorter code, but they do also + have an overhead. Register variables are actually zero page + locations, so using them saves roughly one cycle per access. Since + the old values have to be saved and restored, there is an overhead of + about 70 cycles per 2 byte variable. It is easy to see, that - apart + from the additional code that is needed to save and restore the + values - you need to make heavy use of a variable to justify the + overhead. + + An exception are pointers, especially char pointers. The optimizer + has code to detect and transform the most common pointer operations + if the pointer variable is a register variable. Declaring heavily + used character pointers as register may give significant gains in + speed and size. + + And remember: Register variables must be enabled with -Or. + + + +16. Decimal constants greater than 0x7FFF are actually long ints + + The language rules for constant numeric values specify that decimal + constants without a type suffix that are not in integer range must be + of type long int or unsigned long int. This means that a simple + constant like 40000 is of type long int, and may cause an expression + to be evaluated with 32 bits. + + An example is: + + unsigned val; + ... + if (val < 65535) { + ... + } + + Here, the compare is evaluated using 32 bit precision. This makes the + code larger and a lot slower. + + Using + + unsigned val; + ... + if (val < 0xFFFF) { + ... + } + + or + + unsigned val; + ... + if (val < 65535U) { + ... + } + + instead will give shorter and faster code. + + + diff --git a/doc/compile.txt b/doc/compile.txt new file mode 100644 index 000000000..8067858ec --- /dev/null +++ b/doc/compile.txt @@ -0,0 +1,159 @@ + + +Instructions for compiling cc65 and the ca65 binutils: + + +Linux (and probably most other Unices) +-------------------------------------- + +You need the GNU C compiler. Do a + + make -f make/gcc.mak + +twice(!) in each of the directories + + cc65 + binutils + +After that, you need to compile the libraries. Do + + cd lib + make clean c64lib + make clean c128lib + make clean plus4lib + make clean cbm610lib + make clean petlib + make clean apple2lib + +Be sure to say "clean" each time, since some of the sources have a +"#ifdef ". + + + +DOS using the DJGPP compiler +---------------------------- + +Most information in this section was provided by Keith W. Gerdes +(kwg@freebird.ghofn.org). Thanks a lot! + +The tmpfile() function in DJGPP has a bug and will not open the scratch +file in binary mode. If you have problems with the archiver (which uses +the tmpfile() function), you have two choices: + + 1. Get a fix from http://www.cartsys.com/eldredge/djgpp-patches.html + and apply it. This will solve the problem once and forever. + + 2. For a temporary solution, in the file binutils/ar65/main.c, add the + following lines: + + At top: + + #include + + At start of main: + + _fmode = O_BINARY; + + This will switch the default mode to binary and will work around the + bug. + +Keith sent me the following notes how to build the tools on a DOS system +using DJGPP (add your system type to CFLAGS if needed): + +------------------------------------------------------------------------- + +Here's my current batch file: + +cd cc65 +if exist .depend goto ahead1 +make -f make\gcc.mak +:ahead1 +make -f make\gcc.mak +move *.exe ..\binutils + +cd ..\cl65 +if exist .depend goto ahead2 +make -f make\gcc.mak +:ahead2 +make -f make\gcc.mak +move *.exe ..\binutils + +cd ..\binutils\common +if exist .depend goto ahead3 +make -f make\gcc.mak +:ahead3 +make -f make\gcc.mak + +cd ..\ca65 +if exist .depend goto ahead4 +make -f make\gcc.mak +:ahead4 +make -f make\gcc.mak +move *.exe .. + +cd ..\ld65 +if exist .depend goto ahead5 +make -f make\gcc.mak +:ahead5 +make -f make\gcc.mak +move *.exe .. + +cd ..\ar65 +if exist .depend goto ahead6 +make -f make\gcc.mak +:ahead6 +make -f make\gcc.mak +move *.exe .. + +cd ..\..\lib\common +make 'CFLAGS=-Oi -I../../include/' +ar65 a common.lib *.o +move common.lib .. + +cd ..\runtime +make 'CFLAGS=-Oi -I../../include/' +ar65 a runtime.lib *.o +move runtime.lib .. + + -- + +In djgpp.env I use: + ++LFN=Y + +for the .depend file. + + -- + +And in autoexec.bat I have: + +set CC65_INC=E:\djgpp_v2\cc65\include +set CC65_LIB=E:\djgpp_v2\cc65\lib +PATH=E:\djgpp_v2\cc65\binutils;%PATH% + +------------------------------------------------------------------------- + + +DOS, Windows, OS/2 using the Watcom Compiler +-------------------------------------------- + +This is what I'm using. You need the Borland make in addition to the +Watcom tools, or you have to change the makefile. + +1. Copy %WATCOM%\src\startup\wildargv.c from your Watcom directory into + binutils\common. + +2. Enter + + make -f make\watcom.mak + + in each of the directories + + cc65 + binutils + +3. Use Linux to build the libraries:-) If you don't have Linux, get it + now! More serious: There is no makefile to build the libraries. Use a + batch file similar to the one above, or rewrite the makefile. + + diff --git a/doc/debugging.txt b/doc/debugging.txt new file mode 100644 index 000000000..a7a0eaf91 --- /dev/null +++ b/doc/debugging.txt @@ -0,0 +1,151 @@ + + + Debugging your code using VICE + + Ullrich von Bassewitz, March 1999 + + +Contents +-------- + + 1. Overview + + 2. What is VICE? + + 3. How to prepare your sources + + 4. How to use the label file + + 5. Problems and workarounds + + + +1. Overview +----------- + +This document describes how to debug your programs using the cc65 +development tools and the VICE CBM emulator. + + + +2. What is VICE? +---------------- + +VICE is an emulator for many of the CBM machines. It runs on Unix, DOS and +Windows 95. It emulates the Commodore 64, 128, VIC20, PET and the 600/700 +machines. For more information see the VICE home page: + + http://www.cs.cmu.edu/~dsladic/vice/vice.html + +VICE has a builtin machine language monitor that may be used for debugging +your programs. Using an emulator for debugging has some advantages: + + - Since you're using a crossassembler/-compiler anyway, you don't need + to transfer the program to the real machine until it is done. + + - An emulator allows many things that are almost impossible one of the + original machines. You may set watchpoints (detect read or write + access to arbitary addresses), debug interrupt handlers and even debug + routines that run inside the 1541 floppy. + + - You may use the label file generated by the linker to make much more + use from the monitor. + +Please note that you need at least VICE version 0.16 for the label file +feature to work. This version has still some problems (see section 5 for +descriptions and some workarounds), but older versions had even more +problems and do NOT work correctly. + + + +3. How to prepare your programs +------------------------------- + +VICE support is mostly done via a label file that is generated by the +linker and that may be read by the VICE monitor, so it knows about your +program. Source level debugging is *not* available, you have to debug your +programs in the assembler view. + +The first step is to generate object files that contain information about +ALL labels in your sources, not just the exported ones. This can be done +by several means: + + - Use the -g switch on the assembler command line. + + - Use the + + .debuginfo + + + command in your source. + + - Use the -g switch when invoking the compiler. The compiler will then + put the .debuginfo command into the generated assembler source. + +So, if you have just C code, all you need is to invoke the compiler with +-g. If you're using assembler code, you have to use -g for the assembler, +or add ".debuginfo +" to your source files. Since the generated debug info +is not appended to the generated executables, it is a good idea to always +use -g. It makes the object files and libraries slightly larger (~30%), +but this is usually not a problem. + +The second step is to tell the linker that it should generate a VICE label +file. This is done by the -L switch followed by the name of the label file +(I'm usually using a .lbl extension for these files). An example for a +linker command line would be: + + ld65 -t c64 -L hello.lbl -m hello.map -o hello crt0 hello.o c64.lib + +This will generate a file named hello.lbl that contains all symbols used +in your program. + +Note: The runtime libraries and startup files were generated with debug +info, so you don't have to care about this. + + + +4. How to use the label file +---------------------------- + +Load your program, then enter the monitor and use the "pb" command to load +your label file like this: + + pb "hello.lbl" + +You will get lots of warnings and even a few errors. You may ignore safely +all these warnings and errors as long as they reference any problems VICE +thinks it has with the labels. + +After loading the labels, they are used by VICE in the disassembler +listing, and you may use them whereever you need to specify an address. +Try + + d ._main + +as an example (note that VICE needs a leading dot before all labels, and +that the compiler prepends an underline under most named labels). + + + +5. Problems and workarounds +--------------------------- + +Unfortunately, the VICE monitor has several problems with labels. However, +it is still tremendously useful, and I think that most problems are gone +in the next version. So, here is a list of the problems known to me as of +version 0.16.1: + + * The "ll" command does not work. Worse, it seems that internal memory + gets corrupted when using this command, so VICE will crash after use. + Be sure to use the "pb" command to load the label file. + + * VICE will crash if you use a label that is undefined. This is probably + the worst problem of all, since it needs just one typo to kill VICE. + So, watch your steps:-) + + * Cheap labels, that is, labels starting with '@' or '?' are not + accepted. + + * The disassembly output is somewhat suboptimal. However, most things are + just cosmetical, e.g. labels appended to the right side of the + disassembled code. + diff --git a/doc/internal.doc b/doc/internal.doc new file mode 100644 index 000000000..d7b7f7e0f --- /dev/null +++ b/doc/internal.doc @@ -0,0 +1,185 @@ + + + Internals doc for CC65 + + + +Stacks: +------- + +The program stack used by programs compiled with CC65 is located in high +memory. The stack starts there and grows down. Arguments to functions, local +data etc are allocated on this stack, and deallocated when functions exit. + +The program code and data is located in low memory. The heap is located +between the program code and the stack. The default size for the parameter +stack is 2K, you may change this by declaring an externally visible variable +named named _stksize that holds the new stack size: + + unsigned _stksize = 4*1024; /* Use 4K stack */ + +Note: The size of the stack is only needed if you use the heap, or if you +call the stack checking routine (_stkcheck) from somewhere in your program. + +When calling other functions, the return address goes on the normal 6502 +stack, *not* on the parameter stack. + + + +Registers: +---------- + +Since CC65 is a member of the Small-C family of compilers, it uses the notion +of a 'primary register'. In the CC65 implementation, I used the AX register +pair as the primary register. Just about everything interesting that the +library code does is done by somehow getting a value into AX, and then calling +some routine or other. In places where Small-C would use a secondary +register, top-of-stack is used, so for instance two argument function like +integer-multiply work by loading AX, pushing it on the stack, loading the +second value, and calling the internal function. The stack is popped, and the +result comes back in AX. + + + +Calling sequences: +------------------ + +C functions are called by pushing their args on the stack, and JSR'ing to the +entry point. (See ex 1, below) If the function returns a value, it comes back +in AX. NOTE!!! A potentially significant difference between the CC65 +environment and other C environments is that the CALLEE pops arguments, not +the CALLER. (This is done so as to generate more compact code) In normal use, +this doesn't cause any problems, as the normal function entry/exit conventions +take care of popping the right number of things off the stack, but you may +have to worry about it when doing things like writing hand-coded assembly +language routines that take variable numbers of arguments. More about that +later. + +Ex 1: Function call: Assuming 'i' declared int and 'c' declared + char, the following C code + + i = baz(i, c); + + in absence of a prototype generates this assembler code. I've added + the comments. + + lda _i ; get 'i', low byte + ldx _i+1 ; get 'i', hi byte + jsr pushax ; push it + lda _c ; get 'c' + ldx #0 ; fill hi byte with 0 + jsr pushax ; push it + ldy #4 ; arg size + jsr _baz ; call the function + sta _i ; store the result + stx _i+1 + + In presence of a prototype, the picture changes slightly, since the + compiler is able to do some optimizations: + + lda _i ; get 'i', low byte + ldx _i+1 ; get 'i', hi byte + jsr pushax ; push it + lda _c ; get 'c' + jsr pusha ; push it + jsr _baz ; call the function + sta _i ; store the result + stx _i+1 + + +Note that the two words of arguments to baz were popped before it exitted. +The way baz could tell how much to pop was by the argument count in Y at call +time. Thus, even if baz had been called with 3 args instead of the 2 it was +expecting, that would not cause stack corruption. + +There's another tricky part about all this, though. Note that the args to baz +are pushed in FORWARD order, ie the order they appear in the C statement. +That means that if you call a function with a different number of args than it +was expecting, they wont end up in the right places, ie if you call baz, as +above, with 3 args, it'll operate on the LAST two, not the first two. + + + +Symbols: +-------- + +CC65 does the usual trick of prepending an underbar ('_') to symbol names when +compiling them into assembler. Therefore if you have a C function named +'bar', CC65 will define and refer to it as '_bar'. + + + +Systems: +-------- + +Supported systems at this time are: C64, C128, Plus/4, CBM 600/700, the newer +PET machines (not 2001), and the Apple ][ (thanks to Kevin Ruland, who did the +port). + +C64: The program runs in a memory configuration, where only the kernal ROM + is enabled. The text screen is expected at the usual place ($400), so + 54K of memory are available to the program. + +C128: The startup code will reprogram the MMU, so that only the kernal ROM + is enabled. This means, there are 41K of memory available to the + program. + +Plus/4: Unfortunately, the Plus/4 is not able to disable only part of it's + ROM, it's an all or nothing approach. So, on the Plus/4, the program + has only 28K available (16K machines are detected and the amount of + free memory is reduced to 12K). + +CBM 600/700: + The C program runs in a separate segment and has almost full 64K of + memory available. + +PET: The startup code will adjust the upper memory limit to the installed + memory. However, only linear memory is used, this limits the top to + $8000, so on a 8032 or similar machine, 31K of memory are available to + the program. + +APPLE2: The program starts at $800, and of RAM is $8E00, so 33.5K of memory + (including stack) are available. + +Note: The above numbers do not mean that the remaining memory is unusable. +However, it is not linear memory and must be accessed by other, nonportable +methods. I'm thinking about a library extension that allows access to the +additional memory as a far heap, but these routines do not exist until now. + + + +Inline Assembly: +---------------- + +CC65 allows inline assembly by a special keyword named "asm". Inline assembly +looks like a function call. The string in parenthesis is output in the +assembler file. + +Example, insert a break instruction into the code: + + asm ("\t.byte\t$00") + +Note: The \t in the string is replaced by the tab character, as in all other +strings. + + + +Pseudo variables: +----------------- + +There are two special variables available named __AX__ and __EAX__. These +variables must never be declared (this gives an error), but may be used as any +other variable. However, accessing these variables will access the primary +register that is used by the compiler to evaluate expressions, return +functions results and pass parameters. + +This feature is useful with inline assembly and macros. For example, a macro +that reads a CRTC register may be written like this: + +#define wr(idx) (__AX__=(idx),asm("\tsta\t$2000\n\tlda\t$2000\n\tldx\t#$00"),__AX__) + +An obvious problem here is that macro definitions may not use more than one +line. + + + diff --git a/doc/intro.txt b/doc/intro.txt new file mode 100644 index 000000000..58b881eec --- /dev/null +++ b/doc/intro.txt @@ -0,0 +1,206 @@ + + + How to use the cc65 C compiler + + Ullrich von Bassewitz, 1998/1999 + + + +Contents +-------- + + 1. Overview + + 2. The compiler + + 3. The assembler + + 4. The linker + + 5. The easy way (using the cl65 utility) + + + +1. Overview +----------- + +This is a short intro, how to use the compiler and the binutils. It +contains a step-by-step example, how to build a complete application from +one C and one assembler module. This file does *NOT* contain a complete +reference for the tools used in the process. There are separate files +describing these tools in detail. + +Note: There is a much simpler way to compile this example using the cl65 +compiler and link utility. However, it makes sense to understand how the +separate steps work. How to do the example with the cl65 utility is +described in section 5. + +To explain the development flow, I will use the following example modules: + + +hello.c: + + #include + #include + + extern const char text[]; /* In text.s */ + + int main (void) + { + printf ("%s\n", text); + return EXIT_SUCCESS; + } + + +text.s: + + .export _text + _text: .asciiz "Hello world!" + + +We assume that the target file should be named "hello", and the target +system is the C64. + + + +---------+ + | hello.c | + +---------+ + | + cc65 + \/ + +---------+ +---------+ + | hello.s | | text.s | + +---------+ +---------+ + | | + ca65 ca65 + \/ \/ + +---------+ +---------+ +----------+ +---------+ + | hello.o | | text.o | | c64.o | | c64.lib | + +---------+ +---------+ +----------+ +---------+ + | \ / | + | \ / | + | \ / | + +----------------------->ld65<-------------------------+ + \/ + hello + + +c64.o (the startup code) and c64.lib (the c64 version of the runtime and C +library) are provided in binary form in the cc65 package. + + + +2. The compiler +--------------- + +The compiler translates one C source into one assembler source for each +invocation. It does *NOT* create object files directly, and it is *NOT* +able to translate more than one file per run. + +In the example above, we would use the following command line, to +translate hello.c into hello.s: + + cc65 -O -I ../include -t c64 hello.c + +The -O switch tells the compiler to do an additional optimizer run, which +is usually a good idea, since it makes the code smaller. If you don't care +about the size, but want to have slightly faster code, use -Oi to inline +some runtime functions. + +The -I switch gives a search path for the include files. You may also set +the environment variable CC65_INC to the search path. + +The -t switch is followed by the target system. + +If the compiler does not complain about errors in our hello world, we will +have a file named "hello.s" in our directory that contains the assembler +source for the hello module. + +For more information about the compiler see cc65.txt. + + + +3. The assembler +---------------- + +The assembler translates one assembler source into an object file for each +invocation. The assembler is *NOT* able to translate more than one source +file per run. + +Let's translate the hello.s and text.s files from our example: + + ca65 hello.s + ca65 text.s + +If the assembler does not complain, we should now have two object files +(named hello.o and text.o) in the current directory. + +For more information about the assembler see ca65.txt. + + + +4. The linker +------------- + +The linker combines several object and library file into one output file. +ld65 is very configurable, but fortunately has a builtin configuration for +the C64, so we don't need to mess with configuration files here. + +The compiler uses small functions to do things that cannot be done inline +without big impact on code size. These runtime functions, together with +the C library are in an object file archive named after the system, in +this case "c64.lib". We have to specify this file on the command line so +that the linker can resolve these functions. + +A second file (this time an object file) needed, is the startup code that +prepares the grounds for the C program to run. The startup file must be +executed first, so it must be the first file on the linker command line. + +Let's link our files to get the final executable: + + ld65 -t c64 -o hello c64.o hello.o text.o c64.lib + +The argument after -o specifies the name of the output file, the argument +after -t gives the target system. As discussed, the startup file must be the +first file on the command line (you may have to add a path here, if c64.o is +not in your current directory). Since the library resolves imports in hello.o +and text.o, it must be specified *after* these files. + +After a successful linker run, we have a file named "hello", ready for our +C64! + +For more information about the linker see ld65.txt. + + + +5. The easy way (using the cl65 utility) +---------------------------------------- + +The cl65 utility is able to do all of the steps described above in just +one call, and it has defaults for some options that are very well suited +for our example. + +To compile both files into one executable enter + + cl65 -O -I ../include hello.c test.s + +(The -I switch is not needed if you are working under Linux with the +include files in the default path, or the CC65_INC environment variable is +set correctly). + +The cl65 utility knows, how to translate C files into object files (it +will call the compiler and then the assembler). It does also know how to +create object files from assembler files (it will call the assember for +that). It knows how to build an executable (it will pass all object files +to the linker). And, finally, it has the C64 as a default target and will +supply the correct startup file and runtime library names to the linker, +so you don't have to care about that. + +The one-liner above should give you a C64 executable named "hello" in the +current directory. + +For more information about the compile & link utility see cl65.txt. + + + + diff --git a/doc/ld65.txt b/doc/ld65.txt new file mode 100644 index 000000000..e211eeb0d --- /dev/null +++ b/doc/ld65.txt @@ -0,0 +1,655 @@ + + + ld65 + + A Linker for ca65 Object modules + + (C) Copyright 1998-1999 Ullrich von Bassewitz + (uz@musoftware.de) + + + +Contents +-------- + + 1. Overview + + 2. Usage + + 3. Detailed workings + + 4. Output configuration files + 4.1 Introduction + 4.2 Reference + 4.3 Builtin configurations + + 5. Bugs/Feedback + + 6. Copyright + + + +1. Overview +----------- + +ld65 is a replacement for the link65 linker that was part of the cc65 C +compiler suite developed by John R. Dunning. link65 had some problems and +the copyright does not permit some things which I wanted to be possible, +so I decided to write a completely new assembler/linker/archiver suite +for the cc65 compiler. ld65 is part of this suite. + +The ld65 linker combines several object modules, producing an executable +file. The object modules may be read from a library created by the ar65 +archiver (this is somewhat faster and more convenient). The linker was +designed to be as flexible as possible. It complements the features that +are built into the ca65 macroassembler: + + * Accept any number of segments to form an executable module. + + * Resolve arbitrary expressions stored in the object files. + + * In case of errors, use the meta information stored in the object + files to produce helpful error messages. In case of undefined + symbols, expression range errors, or symbol type mismatches, ld65 is + able to tell you the exact location in the source, where the symbol + was referenced. + + * Flexible output. The output of ld65 is highly configurable by a + config file. More common platforms are supported by builtin + configurations that may be activated by naming the target system. + The output generation was designed with different output formats in + mind, so adding other formats shouldn't be a great problem. + + + +2. Usage +-------- + +The linker is called as follows: + + Usage: ld65 [options] module ... + Options are: + -m name Create a map file + -o name Name the default output file + -t type Type of target system + -v Verbose mode + -vm Verbose map file + -C name Use linker config file + -Ln name Create a VICE label file + -Lp Mark write protected segments as such (VICE) + -S addr Set the default start address + -V Print linker version + +The -m switch (which needs an argument that will used as a filename for +the generated map file) will cause the linker to generate a map file. The +map file does contain a detailed overview over the modules used, the +sizes for the different segments, and a table containing exported +symbols. + +The -o switch is used to give the name of the default output file. +Depending on your output configuration, this name may NOT be used as name +for the output file. However, for the builtin configurations, this name +is used for the output file name. + +The argument for the -t switch is the name of the target system. Since +this switch will activate a builtin configuration, it may not be used +together with the -C option. +The following target systems are defined (* = currently unsupported): + + none + atari + c64 + c128 + ace + plus4 + cbm610 + pet + nes + apple2 + +See section 4.3 for more information about the builtin configurations. + +Using the -v option, you may enable more output that may help you to +locate problems. If an undefined symbol is encountered, -v causes the +linker to print a detailed list of the references (that is, source file +and line) for this symbol. + +-C gives the name of an output config file to use. See section 4 for more +information about config files. -C may not be used together with -t. + +-L allows you to create a file that contains all global labels and may be +loaded into VICE emulator using the pb (playback) command. You may use +this to debug your code with VICE. Note: The label feature is very new in +VICE and has some bugs. If you have problems, please get the latest VICE +version. + +Using -S you may define the default starting address. If and how this +address is used depends on the config file in use. For the builtin +configurations, only the "none" system honors an explicit start address, +all other builtin config provide their own. + +-V prints the version number of the linker. If you send any suggestions or +bugfixes, please include this number. + + +If one of the modules is not found in the current directory, and the +module name does not have a path component, the value of the environment +variable CC65_LIB is prepended to the name, and the linker tries to open +the module with this new name. + + + +3. Detailed workings +-------------------- + +The linker does several things when combining object modules: + +First, the command line is parsed from left to right. For each object file +encountered (object files are recognized by a magic word in the header, so +the linker does not care about the name), imported and exported +identifiers are read from the file and inserted in a table. If a library +name is given (libraries are also recognized by a magic word, there are no +special naming conventions), all modules in the library are checked if an +export from this module would satisfy an import from other modules. All +modules where this is the case are marked. If duplicate identifiers are +found, the linker issues a warning. + +This procedure (parsing and reading from left to right) does mean, that a +library may only satisfy references for object modules (given directly or +from a library) named BEFORE that library. With the command line + + ld65 crt0.o clib.lib test.o + +the module test.o may not contain references to modules in the library +clib.lib. If this is the case, you have to change the order of the modules +on the command line: + + ld65 crt0.o test.o clib.lib + +Step two is, to read the configuration file, and assign start addresses +for the segments and define any linker symbols (see section 4). + +After that, the linker is ready to produce an output file. Before doing +that, it checks it's data for consistency. That is, it checks for +unresolved externals (if the output format is not relocatable) and for +symbol type mismatches (for example a zero page symbol is imported by a +module as absolute symbol). + +Step four is, to write the actual target files. In this step, the linker +will resolve any expressions contained in the segment data. Circular +references are also detected in this step (a symbol may have a circular +reference that goes unnoticed if the symbol is not used). + +Step five is to output a map file with a detailed list of all modules, +segments and symbols encountered. + +And, last step, if you give the -v switch twice, you get a dump of the +segment data. However, this may be quite unreadable if you're not a +developer:-) + + + +4. Output configuration files +----------------------------- + +Configuration files are used to describe the layout of the output file(s). +Two major topics are covered in a config file: The memory layout of the +target architecture, and the assignment of segments to memory areas. In +addition, several other attributes may be specified. + +Case is ignored for keywords, that is, section or attribute names, but it +is NOT ignored for names and strings. + + + +4.1 Introduction +---------------- + +Memory areas are specified in a "MEMORY" section. Lets have a look at an +example (this one describes the usable memory layout of the C64): + + MEMORY { + RAM1: start = $0800, size = $9800; + ROM1: start = $A000, size = $2000; + RAM2: start = $C000, size = $1000; + ROM2: start = $E000, size = $2000; + } + +As you can see, there are two ram areas and two rom areas. The names +(before the colon) are arbitrary names that must start with a letter, with +the remaining characters being letters or digits. The names of the memory +areas are used when assigning segments. As mentioned above, case is +significant for these names. + +The syntax above is used in all sections of the config file. The name +("ROM1" etc.) is said to be an identifier, the remaining tokens up to the +semicolon specify attributes for this identifier. You may use the equal +sign to assign values to attributes, and you may use a comma to separate +attributes, you may also leave both out. But you MUST use a semicolon to +mark the end of the attributes for one identifier. The section above may +also have looked like this: + + # Start of memory section + MEMORY + { + RAM1: + start $0800 + size $9800; + ROM1: + start $A000 + size $2000; + RAM2: + start $C000 + size $1000; + ROM2: + start $E000 + size $2000; + } + +There are of course more attributes for a memory section than just start +and size. Start and size are mandatory attributes, that means, each memory +area defined MUST have these attributes given (the linker will check +that). I will cover other attributes later. As you may have noticed, I've +used a comment in the example above. Comments start with a hash mark +(`#'), the remainder of the line is ignored if this character is found. + +Let's assume you have written a program for your trusty old C64, and you +would like to run it. For testing purposes, it should run in the RAM area. +So we will start to assign segments to memory sections in the SEGMENTS +section: + + SEGMENTS { + CODE: load = RAM1, type = ro; + RODATA: load = RAM1, type = ro; + DATA: load = RAM1, type = rw; + BSS: load = RAM1, type = bss, define = yes; + } + +What we are doing here is telling the linker, that all segments go into +the RAM1 memory area in the order specified in the SEGMENTS section. So +the linker will first write the CODE segment, then the RODATA segment, +then the DATA segment - but it will not write the BSS segment. Why? Enter +the segment type: For each segment specified, you may also specify a +segment attribute. There are five possible segment attributes: + + ro means readonly + wprot same as ro but will be marked as write protected in + the VICE label file if -Lp is given + rw means read/write + bss means that this is an uninitialized segment + empty will not go in any output file + +So, because we specified that the segment with the name BSS is of type +bss, the linker knows that this is uninitialized data, and will not write +it to an output file. This is an important point: For the assembler, the +BSS segment has no special meaning. You specify, which segments have the +bss attribute when linking. This approach is much more flexible than +having one fixed bss segment, and is a result of the design decision to +supporting an arbitrary segment count. + +If you specify "type = bss" for a segment, the linker will make sure that +this segment does only contain uninitialized data (that is, zeroes), and +issue a warning if this is not the case. + +For a bss type segment to be useful, it must be cleared somehow by your +program (this happens usually in the startup code - for example the +startup code for cc65 generated programs takes care about that). But how +does your code know, where the segment starts, and how big it is? The +linker is able to give that information, but you must request it. This is, +what we're doing with the "define = yes" attribute in the BSS definitions. +For each segment, where this attribute is true, the linker will export +three symbols. + + __NAME_LOAD__ This is set to the address where the segment is + loaded. + __NAME_RUN__ This is set to the run address of the segment. + We will cover run addresses later. + __NAME_SIZE__ This is set to the segment size. + +Replace "NAME" by the name of the segment, in the example above, this +would be "BSS". These symbols may be accessed by your code. + +Now, as we've configured the linker to write the first three segments and +create symbols for the last one, there's only one question left: Where +does the linker put the data? It would be very convenient to have the data +in a file, wouldn't it? + +We don't have any files specified above, and indeed, this is not needed in +a simple configuration like the one above. There is an additional +attribute "file" that may be specified for a memory area, that gives a +file name to write the area data into. If there is no file name given, the +linker will assign the default file name. This is "a.out" or the one given +with the -o option on the command line. Since the default behaviour is ok +for our purposes, I did not use the attribute in the example above. Let's +have a look at it now. + +The "file" attribute (the keyword may also be written as "FILE" if you +like that better) takes a string enclosed in double quotes (`"') that +specifies the file, where the data is written. You may specifiy the same +file several times, in that case the data for all memory areas having this +file name is written into this file, in the order of the memory areas +defined in the MEMORY section. Let's specify some file names in the MEMORY +section used above: + + MEMORY { + RAM1: start = $0800, size = $9800, file = %O; + ROM1: start = $A000, size = $2000, file = "rom1.bin"; + RAM2: start = $C000, size = $1000, file = %O; + ROM2: start = $E000, size = $2000, file = "rom2.bin"; + } + +The %O used here is a way to specify the default behaviour explicitly: %O +is replaced by a string (including the quotes) that contains the default +output name, that is, "a.out" or the name specified with the -o option on +the command line. Into this file, the linker will first write any segments +that go into RAM1, and will append then the segments for RAM2, because the +memory areas are given in this order. So, for the RAM areas, nothing has +really changed. + +We've not used the ROM areas, but we will do that below, so we give the +file names here. Segments that go into ROM1 will be written to a file +named "rom1.bin", and segments that go into ROM2 will be written to a file +named "rom2.bin". The name given on the command line is ignored in both +cases. + +Let us look now at a more complex example. Say, you've successfully tested +your new "Super Operating System" (SOS for short) for the C64, and you +will now go and replace the ROMs by your own code. When doing that, you +face a new problem: If the code runs in RAM, we need not to care about +read/write data. But now, if the code is in ROM, we must care about it. +Remember the default segments (you may of course specify your own): + + CODE read only code + RODATA read only data + DATA read/write data + BSS uninitialized data, read/write + +Since the BSS is not initialized, we must not care about it now, but what +about DATA? DATA contains initialized data, that is, data that was +explicitly assigned a value. And your program will rely on these values on +startup. Since there's no other way to remember the contents of the data +segment, than storing it into one of the ROMs, we have to put it there. +But unfortunately, ROM is not writeable, so we have to copy it into RAM +before running the actual code. + +The linker cannot help you copying the data from ROM into RAM (this must +be done by the startup code of your program), but it has some features +that will help you in this process. + +First, you may not only specify a "load" attribute for a segment, but also +a "run" attribute. The "load" attribute is mandatory, and, if you don't +specify a "run" attribute, the linker assumes that load area and run area +are the same. We will use this feature for our data area: + + SEGMENTS { + CODE: load = ROM1, type = ro; + RODATA: load = ROM2, type = ro; + DATA: load = ROM2, run = RAM2, type = rw, define = yes; + BSS: load = RAM2, type = bss, define = yes; + } + +Let's have a closer look at this SEGMENTS section. We specify that the +CODE segment goes into ROM1 (the one at $A000). The readonly data goes +into ROM2. Read/write data will be loaded into ROM2 but is run in RAM2. +That means that all references to labels in the DATA segment are relocated +to be in RAM2, but the segment is written to ROM2. All your startup code +has to do is, to copy the data from it's location in ROM2 to the final +location in RAM2. + +So, how do you know, where the data is located? This is the second point, +where you get help from the linker. Remember the "define" attribute? Since +we have set this attribute to true, the linker will define three external +symbols for the data segment that may be accessed from your code: + + __DATA_LOAD__ This is set to the address where the segment is + loaded, in this case, it is an address in ROM2. + __DATA_RUN__ This is set to the run address of the segment, in + this case, it is an address in RAM2. + __DATA_SIZE__ This is set to the segment size. + +So, what your startup code must do, is to copy __DATA_SIZE__ bytes from +__DATA_LOAD__ to __DATA_RUN__ before any other routines are called. All +references to labels in the DATA segment are relocated to RAM2 by the +linker, so things will work properly. + +There are some other attributes not covered above. Before starting the +reference section, I will discuss the remaining things here. + +You may request symbols definitions also for memory areas. This may be +useful for things like a software stack, or an i/o area. + + MEMORY { + STACK: start = $C000, size = $1000, define = yes; + } + +This will define three external symbols that may be used in your code: + + __STACK_START__ This is set to the start of the memory + area, $C000 in this example. + + __STACK_SIZE__ The size of the area, here $1000. + + + __STACK_LAST__ This is NOT the same as START+SIZE. + Instead, it it defined as the first + address that is not used by data. If we + don't define any segments for this area, + the value will be the same as START. + +A memory section may also have a type. Valid types are + + ro for readonly memory +and rw for read/write memory. + +The linker will assure, that no segment marked as read/write or bss is put +into a memory area that is marked as readonly. + +Unused memory in a memory area may be filled. Use the "fill = yes" +attribute to request this. The default value to fill unused space is zero. +If you don't like this, you may specify a byte value that is used to fill +these areas with the "fillval" attribute. This value is also used to fill +unfilled areas generated by the assemblers .ALIGN and .RES directives. + +Segments may be aligned to some memory boundary. Specify "align = num" to +request this feature. Num must be a power of two. To align all segments on +a page boundary, use + + SEGMENTS { + CODE: load = ROM1, type = ro, align = $100; + RODATA: load = ROM2, type = ro, align = $100; + DATA: load = ROM2, run = RAM2, type = rw, define = yes, + align = $100; + BSS: load = RAM2, type = bss, define = yes, align = $100; + } + +If an alignment is requested, the linker will add enough space to the +output file, so that the new segment starts at an address that is +divideable by the given number without a remainder. All addresses are +adjusted accordingly. To fill the unused space, bytes of zero are used, +or, if the memory area has a "fillval" attribute, that value. Alignment is +always needed, if you have the used the .ALIGN command in the assembler. +The alignment of a segment must be equal or greater than the alignment +used in the .ALIGN command. The linker will check that, and issue a +warning, if the alignment of a segment is lower than the alignment +requested in a .ALIGN command of one of the modules making up this +segment. + +For a given segment you may also specify a fixed offset into a memory area or +a fixed start address. Use this if you want the code to run at a specific +address (a prominent case is the interrupt vector table which must go at +address $FFFA). Only one of ALIGN or OFFSET or START may be specified. If the +directive creates empty space, it will be filled with zero, of with the value +specified with the "fillval" attribute if one is given. The linker will warn +you if it is not possible to put the code at the specified offset (this may +happen if other segments in this area are too large). Here's an example: + + SEGMENTS { + VECTORS: load = ROM2, type = ro, start = $FFFA; + } + +or (for the segment definitions from above) + + SEGMENTS { + VECTORS: load = ROM2, type = ro, offset = $1FFA; + } + +File names may be empty, data from segments assigned to a memory area with +an empty file name is discarded. This is useful, if the a memory area has +segments assigned that are empty (for example because they are of type +bss). In that case, the linker will create an empty output file. This may +be suppressed by assigning an empty file name to that memory area. + +The symbol %S may be used to access the default start address (that is, +$200 or the value given on the command line with the -S option). + + + +4.2 Reference +------------- + + + +4.3 Builtin configurations +-------------------------- + +Here is a list of the builin configurations for the different target +types: + +none: + MEMORY { + RAM: start = %S, size = $10000, file = %O; + } + SEGMENTS { + CODE: load = RAM, type = rw; + RODATA: load = RAM, type = rw; + DATA: load = RAM, type = rw; + BSS: load = RAM, type = bss, define = yes; + } + +atari: + (non-existent) + +c64: + MEMORY { + RAM: start = $7FF, size = $c801, file = %O; + } + SEGMENTS { + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw; + BSS: load = RAM, type = bss, define = yes; + } + +c128: + MEMORY { + RAM: start = $1bff, size = $a401, file = %O; + } + SEGMENTS { + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw; + BSS: load = RAM, type = bss, define = yes; + } + +ace: + (non-existent) + +plus4: + MEMORY { + RAM: start = $0fff, size = $7001, file = %O; + } + SEGMENTS { + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw; + BSS: load = RAM, type = bss, define = yes; + } + +cbm610: + MEMORY { + RAM: start = $0001, size = $FFF0, file = %O; + } + SEGMENTS { + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw; + BSS: load = RAM, type = bss, define = yes; + } + +pet: + MEMORY { + RAM: start = $03FF, size = $7BFF, file = %O; + } + SEGMENTS { + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw; + BSS: load = RAM, type = bss, define = yes; + } + +nes: + MEMORY { + RAM: start = $0200, size = $0600, file = ""; + ROM: start = $8000, size = $8000, file = %O; + } + SEGMENTS { + CODE: load = ROM, type = ro; + RODATA: load = ROM, type = ro; + DATA: load = ROM, run = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; + VECTORS: load = ROM, type = ro, start = $FFFA; + } + +apple2: + MEMORY { + RAM: start = $800, size = $8E00, file = %O; + } + SEGMENTS { + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw; + BSS: load = RAM, type = bss, define = yes; + } + +The "start" attribute for the RAM memory area of the CBM systems is two +less than the actual start of the basic RAM to account for the two bytes +load address that is needed on disk and supplied by the startup code. + + + +5. Bugs/Feedback +---------------- + +If you have problems using the linker, if you find any bugs, or if you're +doing something interesting with it, I would be glad to hear from you. +Feel free to contact me by email (uz@musoftware.de). + + + +6. Copyright +------------ + +ld65 (and all cc65 binutils) are (C) Copyright 1998 Ullrich von Bassewitz. +For usage of the binaries and/or sources the following conditions do +apply: + +This software is provided 'as-is', without any expressed or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. +3. This notice may not be removed or altered from any source + distribution. + + + + diff --git a/doc/library.txt b/doc/library.txt new file mode 100644 index 000000000..da5d9933e --- /dev/null +++ b/doc/library.txt @@ -0,0 +1,235 @@ + + + Description of the C library for the cc65 C compiler + + (C) Copyright 1998-1999 Ullrich von Bassewitz + (uz@musoftware.de) + + + +Contents +-------- + + 1. Overview + + 2. ISO C compatible library + + 3. CPU specific stuff - 6502.h + + 4. System specific stuff + + 5. Direct console I/O - conio.h + + 6. Using the joystick - joystick.h + + 7. Bugs/Feedback + + 8. Copyright + + + +1. Overview +----------- + +This file contains a description of the library routines available for the +cc65 C compiler. It is not complete in some areas, so if you miss +something, have a look into the header files. All functions, that are not +defined by the ISO C standard have a short comment in the headers, +explaining their use. + + + +2. ISO C compatible library +--------------------------- + +The C library contains a large subset of the ISO C library. Functions are +usually missing in areas, where there is no support on typical 6502 +systems. Wide character sets are an example for this. + +I will not go into detail about the ISO functions. If a function is not +mentioned here explicitly, expect it to be available and to behave as +defined in the C standard. + + +Functions that are NOT available: + + * ftell/fseek/fgetpos/fsetpos + + * tmpfile/tmpnam + + * The scanf family of functions + + * time/asctime/ctime/difftime/asctime/gmtime/localtime/mktime/strftime + + * system + + * All functions that handle floating point numbers in some manner. + + * The div and ldiv functions (because cc65 is not able to return + structs). + + * All functions handling wide character strings. + + * Signals and all related functions (having SIGSEGV would be cool:-) + + * rename/remove/rewind + + * setbuf/setvbuf/ungetc + + + +Functions that are limited in any way: + + * fopen/fread/fwrite/fclose/fputs/fgets/fscanf.... + + These functions are built on open/read/write/close. Neither of these + low level functions is currently available for the supported systems, + and so, fopen and friends do not work. However, the functions exist + and are tested to some degree under the ACE operating systems (which + is no longer supported). + + + * The va_... family of macros + + The macros do not work completely as defined by the standard. Since cc65 + has the wrong calling order, the (non-standard) va_fix macro must be used + to access fixed parameters in functions with a variable parameter size. + See newvers.txt for a discussion of the problem. + + + * The character classification functions (is...) + + These functions have unexpected results when called with arguments that + are not really chars (are outside the 0..255 range). + + + * The strerror function + + The function will return "error #n" where n is the error number. + + + * strcspn/strpbrk/strspn + + These functions have a length limitation of 256 for the second string + argument. Since this string gives a character set, and there are only 256 + distinct characters, this shouldn't be a problem. + + + * Since there is no such thing as an environment on all supported + systems, the getenv function will always return a NULL pointer. + + + * There is no other locale than the "C" locale. The native locale is + identical to the "C" locale. + + + +3. CPU specific stuff - 6502.h +------------------------------ + +The header file 6502.h contains some functions that make only sense with +the 6502 CPU. Examples are macros to insert more or less useful +instructions into your C code, or a function to call arbitrary machine +language subroutines, passing registers in and out. + + + +4. System specific stuff +------------------------ + +For each supported system there's a header file that contains calls or +defines specific for this system. So, when programming for the C64, +include c64.h, for the C128, include c128.h and so on. To make the task +for the Commodore systems easier, there is also a header file named cbm.h +that will define stuff common for all CBM systems, and include the header +file for the specific target system. + +The header files contain + + * Defines for special keys (like function keys) + + * Defines for special characters (like the graphics characters) + + * Variables with a fixed address in memory that may be used to access + special hardware. For the C64 and C128 there is a variable struct + named "sid". Writing to the fields of this struct will write to the + SID device instead. Using these variables will make your program more + readable and more portable. Don't fear ineffective code when using + these variables, the compiler will translate reads and writes to these + structs into direct memory accesses. + + * Other routines that make only sense for a specific system. One example + are routines to write memory locations in the system bank for the CBM + 600/700 family (called B128/B256 in the US). + + + +5. Direct console I/O - conio.h +------------------------------- + +The conio header file contains a large set of functions that do screen and +keyboard I/O. The functions will write directly to the screen or poll the +keyboard directly with no more help from the operating system than needed. +This has some disadvantages, but on the other side it's fast and +reasonably portable. conio implementations exist for the following +targets: + + c64 + c128 + plus/4 + cbm610 (that is, the complete 600/700 series) + pet (all PETs except the 2001) + apple 2 + +The conio.h header file does also include the system specific header files +which define constants for special characters and keys. + + + +6. Using the joystick - joystick.h +---------------------------------- + +For systems that have a joystick, joystick.h will define a subroutine to +read the current value, including constants to evaluate the result of this +function. To help in writing portable code, the header file will define +the symbol __JOYSTICK__ on systems that have a joystick. + + + +7. Bugs/Feedback +---------------- + +If you have problems using the library, if you find any bugs, or if you've +written some extensions or otherwise interesting programs, I would be glad +to hear from you. Feel free to contact me by email (uz@musoftware.de). + + + +8. Copyright +------------ + +This C runtime library implementation for the cc65 compiler is (C) +Copyright 1998-1999 Ullrich von Bassewitz. For usage of the binaries +and/or sources the following conditions do apply: + +This software is provided 'as-is', without any expressed or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. +3. This notice may not be removed or altered from any source + distribution. + + + + + diff --git a/doc/newvers.txt b/doc/newvers.txt new file mode 100644 index 000000000..d93cfe2a8 --- /dev/null +++ b/doc/newvers.txt @@ -0,0 +1,511 @@ + +This document is slightly outdated! See cc65.txt and library.txt for a more +up-to-date discussion. + + + +Discussion of some of the features/non features of the current cc65 version +--------------------------------------------------------------------------- + + 1. Copyright + + 2. Differences to the original version + + 3. Known bugs and limitations + + 4. Library + + 5. Bugs + + + + +1. Copyright +----------- + +This is the original compiler copyright: + +-------------------------------------------------------------------------- + -*- Mode: Text -*- + + This is the copyright notice for RA65, LINK65, LIBR65, and other + Atari 8-bit programs. Said programs are Copyright 1989, by John R. + Dunning. All rights reserved, with the following exceptions: + + Anyone may copy or redistribute these programs, provided that: + + 1: You don't charge anything for the copy. It is permissable to + charge a nominal fee for media, etc. + + 2: All source code and documentation for the programs is made + available as part of the distribution. + + 3: This copyright notice is preserved verbatim, and included in + the distribution. + + You are allowed to modify these programs, and redistribute the + modified versions, provided that the modifications are clearly noted. + + There is NO WARRANTY with this software, it comes as is, and is + distributed in the hope that it may be useful. + + This copyright notice applies to any program which contains + this text, or the refers to this file. + + This copyright notice is based on the one published by the Free + Software Foundation, sometimes known as the GNU project. The idea + is the same as theirs, ie the software is free, and is intended to + stay that way. Everybody has the right to copy, modify, and re- + distribute this software. Nobody has the right to prevent anyone + else from copying, modifying or redistributing it. + +-------------------------------------------------------------------------- + +In acknowledgment of this copyright, I will place my own changes to the +compiler under the same copyright. + +However, since the library and all binutils (assembler, archiver, linker) +are a complete rewrite, they are covered by another copyright: + + +-------------------------------------------------------------------------- + + CC65 C Library and Binutils + + (C) Copyright 1998 Ullrich von Bassewitz + + COPYING CONDITIONS + + + This software is provided 'as-is', without any expressed or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source + distribution + + +-------------------------------------------------------------------------- + +I will try to contact John, maybe he is also willing to place his sources +under a less restrictive copyright, after all these years:-) + + + + +2. Differences to the original version +-------------------------------------- + +This is a list of changes against the cc65 archives. I got the originals +from: + + http://www.umich.edu/~archive/atari/8bit/Languages/Cc65/ + + + + * Removed all assembler code from the compiler. It was unportable because + it made assumptions about the character set (ATASCII) and made the + sources hard to read and to debug. + + * All programs do return an error code, so they may be used by make. All + programs try to remove the target file, if there were errors. + + * The assembler now checks several error conditions (others still go + undetected - see "known bugs"). + + * Removed many bugs from the compiler. One error was invalid code + produced by the compiler that went through the assembler since the + assembler did not check for ranges itself. + + * Removed many non-portable constructs from the compiler. Code cleanups, + rewrite of the function headers and more. + + * New style function prototypes supported instead of the old K&R syntax. + The new syntax is a must, that is, the old style syntax is no longer + understood. As an extension, unnamed parameters may be used to avoid + warnings about unused parameters. + + * New void type. May also be used as a function return type. + + * Changed the memory management in the compiler. Use malloc/free instead + of the old homebrew (and unportable) stuff. + + * Default character type is unsigned. This is much more what you want in + small systems environments, since a char is often used to represent a + small numerical value, and the integer promotion does the wrong thing + in those cases. Look at the follwing piece of code: + + char c = read_char (); + switch (c) { + case 0x80: printf ("c is 0x80\n"); break; + default: printf ("c is something else\n"); break; + } + + With signed chars, the code above, will *always* run into the default + selector. c is promoted to int, and since it is signed, 0x80 will get + promoted to 0xFF80 - which will select the default label. With unsigned + chars, the code works as intended (but note: the code works for cc65 + but it is non portable anyway, since many other compilers have signed + chars by default, so be careful! Having unsigned chars is just a + convenience thing). + + * Shorter code when using the builtin operators and the lhs of an expr + is a constant (e.g. expressions like "c == 0x80" are encoded two + bytes shorter). + + * Some optimizations when pushing constants. + + * Character set translation by the compiler. A new -t option was added + to set the target system type. Use + + -t0 For no spefic target system (default) + -t1 For the atari (does not work completely, since I did not + have an ATASCII translation table). + -t2 Target system is C64. + -t3 Target system is C128. + -t4 Target system is ACE. + -t5 Target system is Plus/5. + + * Dito for the linker: Allow an option to set the target system and add + code to the linker to produce different headers and set the correct + start address. + + * Complete rewrite of the C library. See extra chapter. + + * Many changes in the runtime library. Splitted it into more than one + file to allow for smaller executables if not all of the code is needed. + + * Allow longer names. Now the first 12 characters are sigificant at the + expense of some more memory used at runtime. + + * String constants are now concatenated in all places. This allows + things like: + + fputs ("Options:\n" + " -b bomb computer\n" + " -f format hard disk\n" + " -k kill init\n", + stderr); + + saving code for more than one call to the function. + + * Several new macros are defined: + + M6502 This one is old - don't use! + __CC65__ Use this instead. Defined when compiling with cc65. + __ATARI__ Defined when the target system is atari. + __CBM__ Defined when compiling for a CBM system as target. + __C64__ Defined when the C64 is the target system. + __C128__ Defined when compiling for the 128. + __ACE__ Defined when compiling for ACE. + __PLUS4__ Defined when compiling for the Plus/4. + + The __CC65__ macro has the compiler version as its value, version + 1.0 of the compiler will define this macro as 0x100. + + * The -a option is gone. + + * The compiler will generate external references (via .globl) only if a + function is defined as extern in a module, or not defined but called + from a module. The old behaviour was to generate a reference for every + function prototype ever seen, which meant that using a header file like + stdio.h got most of the C library linked in, even if it was never used. + + * Many new warnings added (about unused parameters, unused variables, + compares of unsigneds against zero, function call without prototype + and much more). + + * Added a new compiler option (-W) to suppress all warnings. + + * New internal variable __fixargs__ that gives the size of fixed + arguments, a function takes. This allows to work (somehow) around the + problem, that cc65 has the "wrong" (that is, pascal) calling order. See + below ("Known problems") for a discussion. + + * The "empty" preprocessor directive ("#" on a line) is now ignored. + + * Added a "#error" directive to force user errors. + + * Optimization of the code generation. Constant parts of expressions are + now detected in many places where the old compiler evaluated the + constants at runtime. + + * Allow local static variables (there was code in the original compiler for + that, but it did not work). Allow also initialization in this case (no + code for that in the original). Local static variables in the top level + function block have no penalty, for static variables in nested blocks, the + compiler generates a jump around the variable space. To eliminate this, + an assembler/linker with support for segments is needed. + + * You cannot return a value from a void function, and must return a value + in a non-void function. Violations are flagged as an error. + + * Typedefs added. + + * The nonstandard evaluation of the NOARGC and FIXARGC macros has been + replaced by a smart algorithm that does the same thing automagically + and without user help (provided there are function prototypes). + + * Function pointers may now be used to call a function without + dereferencing. Given a function + + void f1 (void (*f2) ()) + + the following was valid before: + + (*f2) (); + + The ANSI standard allows a second form (because there's no ambiguity) + which is now also allowed: + + f2 (); + + * Pointer subtraction was completely messed up and did not work (that is, + subtraction of a pointer from a pointer produced wrong results). + + * Local struct definitions are allowed. + + * Check types in assignments, parameters for function calls and more. + + * A new long type (32 bit) is available. The integer promotion rules + are applied if needed. This includes much more type checking and a + better handling of chars (they are handled as chars, not as ints, in + all places where this is possible). + + * Integer constants now have an associated type, 'U' and 'L' modifers + may be used. + + * The old #asm statement is gone. Instead, there's now a asm ("xxx") + statement that has the syntax that is defined by the C++ standard + (the C standard does not define an ASM statement). The string literal + in parenthesis is inserted in the assembler output. You may also + use __asm__ instead of asm (see below). + + * Allow // comments. + + * New compiler option -A (ANSI) that disables several extensions (asm + directive, // comments, unnamed function parameters) and also defines + a macro named __STRICT_ANSI__. The header files will exclude some + non-ANSI functions if __STRICT_ANSI__ is defined (that is, -A is given + on the command line). + -A will not disable the __asm__ directive (identifiers starting with + __ are in the namespace of the implementation). + + * Create optimized code if the address of a variable is a constant. This + may be achieved by constructs like "*(char*)0x200 = 0x01" and is used + to access absolute memory locations. The compiler detects this case + also if structs or arrays are involved and generates direct stores and + fetches. + + + +3. Known problems +----------------- + + * No floats. + + * Only simple automatic variables may be initialized (no arrays). + + * "Wrong" order of arguments on the stack. The arguments are pushed in + the order, the arguments are parsed. That means that the va_xxx macros + in stdarg.h are ok (they work as expected), but the fixed parameters of + a function with a variable argument list do not match and must be + determined with the (non-standard) va_fix macro. + + Using the __fixargs__ kludge, it is possible to write standard conform + va_xxx macros to work with variable sized argument lists. However, the + fixed parameters in the function itself usually have the wrong values, + because the order of the arguments on the stack is reversed compared to + a stock C compiler. Pushing the args the other way round requires much + work and a more elaborated intermediate code than cc65 has. + + To understand the problem, have a look at this (non working!) sprintf + function: + + int sprintf (char* buf, char* format, ...) + /* Non working version */ + { + int count; + va_list ap; + va_start (ap, format); + count = vsprintf (buf, format, ap); + va_end (ap); + return count; + } + + The problem here is in the "format" and "buf" parameters. They do (in + most cases) not contain, what the caller gave us as arguments. To + access the "real" arguments, use the va_fix macro. It is only valid + before the first call to va_arg, and takes the va_list and the number + of the fixed argument as parameters. So the right way would be + + int sprintf (char* buf, char* format, ...) + /* Working version */ + { + int count; + va_list ap; + va_start (ap, format); + count = vsprintf (va_fix (ap, 1), va_fix (ap, 2), ap); + va_end (ap); + return count; + } + + The fixed parameter are obtained by using the va_fix macro with the + number of the parameter given as second argument. Beware: Since the + fixed arguments declared are usually one of the additional parameters, + the following code, which tries to be somewhat portable, does *not* + work. The assignment will overwrite the other parameters instead, + causing unexpected results: + + int sprintf (char* buf, char* format, ...) + /* Non working version */ + { + int count; + va_list ap; + va_start (ap, format); + #ifdef __CC65__ + buf = va_fix (ap, 1); + format = va_fix (ap, 2); + #endif + count = vsprintf (buf, format, ap); + va_end (ap); + return count; + } + + To write a portable version of sprintf, use code like this instead: + + int sprintf (char* buf, char* format, ...) + /* Working version */ + { + int count; + va_list ap; + va_start (ap, format); + #ifdef __CC65__ + count = vsprintf (va_fix (ap, 1), va_fix (ap, 2), ap); + #else + count = vsprintf (buf, format, ap); + #endif + va_end (ap); + return count; + } + + I know, va_fix is a kludge, but at least it *is* possible to write + functions with variable sized argument lists in a comfortable manner. + + * The assembler still accepts lots of illegal stuff without an error (and + creates wrong code). Be careful! + + * When starting a compiled program twice on the C64 (or 128), you may get + other results or the program may even crash. This is because static + variables do not have their startup values, they were changed in the + first run. + + * There's only *one* symbol table level. It is - via a flag - used for both, + locals and global symbols. However, if you have variables in nested + blocks, the names may collide with the ones in the upper block. I will + probably add real symbol tables some time to remove this problem. + + * Variables in nested blocks are handled inefficiently, especially in loops. + The frame on the stack is allocated and deallocated for each loop + iteration. There's no way around this, since the compiler has not enough + memory to hold a complete function body in memory (it would be able to + backpatch the frame generating code on function entry). + + + + +4. Library +---------- + +The C library is a complete rewrite and has nothing in common with the old +Atari stuff. When rewriting the library, I was guided by the following +rules: + + * Use standard conform functions as far as possible. In addition, if + there's a ANSI-C compatible function, it should act as defined in the + ANSI standard. If if does not act as defined, this is an error. + + * Do not use non-standard functions if the functionality of those + functions is covered by a standard function. Use exceptions only, if + there is a non-ANSI function that is very popular (example: itoa). + + * Use new style prototpyes and header files. + + * Make the library portable. For example, the complete stdio stuff is + based on only four system dependent functions: + + open, read, write, close + + So, if you rewrite these functions for a new system, all others + (printf, fprintf, fgets, fputc ...) will work, too. + + * Do not expect a common character set. Unfortunately, I was not able to + be completely consequent in this respect. C sources are no problem + since the compiler does character translation, but the assembler + sources make assumptions about the following characters: + + 0 --> code $30 + + --> code $2B + - --> code $2D + + All other functions (especially the isxxx ones) are table driven, so + only the classification table is system dependent. + + +The first port was for the ACE operating system. The current version has also +support for the C64, the C128 and the Plus/4 in native mode. The ACE port has +disk support but no conio module, all others don't have disk support but +direct console I/O. + +Currently the following limitations the are known: + + * getwd (ace) does not work. I get an error (carry flag) with an error + code of zero (aceErrStopped). Maybe my code is wrong... + + * The error codes are currently system error codes. They should be + translated to something system independent. The ace codes are a good + starting point. However, I don't like the idea, that zero is a valid + error code, and some other codes are missing ("invalid parameter" and + more). As soon as this is done, it is also possible to write a + strerror() function to give more descriptive error messages to the + user. + + * Many functions not very good tested. + + * The printf and heap functions are way too big. Rewritting _printf + and malloc/free in assembler will probably squeeze 2K out of the + code. + + * The isxxx functions do not handle EOF correctly. This is probably + a permanent restriction, even if it is non-standard. It would require + extra code in each of the isxxx functions, since EOF is defined as -1 + and cannot be handled effectively with the table approach and 8 bit + index registers. + + * The strcspn, strpbrk and strspn functions have a string length limitation + of 256 for the second argument. This is usually not a problem since the + second argument gives a character set, and a character set cannot be + larger than 256 chars for all known 6502 systems. + + + + +5. Bugs +------- + +Please note that the compiler and the libraries are beta! Send bug reports to +uz@musoftware.de. + + + + diff --git a/doc/readme.1st b/doc/readme.1st new file mode 100644 index 000000000..fa1d56b21 --- /dev/null +++ b/doc/readme.1st @@ -0,0 +1,34 @@ + +If you have got the source package, see + + doc/compile.txt + +for instructions how to compile the stuff for the different systems. + + +If you have a binary package: Have a look in the doc directory for +information on how to use the tools. If you are new to cc65, the file +intro.txt may be of interest to you. + +To avoid having to mess with paths, you may want to set the environment +variables + + CC65_LIB + and CC65_INC + +to the directory containing the libraries and the system include files +respectively. If you have installed cc65 in C:\cc65 (assuming a DOS or +Windows system), you should use + + set CC65_LIB=c:\cc65\lib + and set CC65_INC=c:\cc65\include + +Unix people probably know, how to translate these lines into the +appropriate Unix commands:-) + +Have fun! + + + Uz + + diff --git a/doc/readme.txt b/doc/readme.txt new file mode 100644 index 000000000..b19d0a3ac --- /dev/null +++ b/doc/readme.txt @@ -0,0 +1,31 @@ + +Documentation overview: + + + ar65.txt - Describes the ar65 archiver. + + debugging.txt - Debug programs using the VICE emulator. + + ca65.txt - Describes the ca65 macro assembler. + + cc65.txt - Describes the cc65 C compiler. + + cl65.txt - Describes the cl65 compile & link utility. + + coding.txt - Containes hints on creating the most effective code + with cc65. + + intro.txt - Describes the use of the tools by a short "hello world" + example. + + ld65.txt - Describes the ld65 linker. + + library.txt - Describes the cc65 runtime and C libraries. + + newvers.txt - Somewhat outdated. Lists the differences between the + current cc65 release and the original atari version + created by J.R Dunning. + + readme.txt - This file. + + diff --git a/include/6502.h b/include/6502.h new file mode 100644 index 000000000..752fe5dcb --- /dev/null +++ b/include/6502.h @@ -0,0 +1,88 @@ +/* + * 6502.h + * + * Ullrich von Bassewitz, 20.09.1998 + */ + + + +#ifndef _6502_H +#define _6502_H + + + +/* Possible returns of getcpu() */ +#define CPU_6502 0 +#define CPU_65C02 1 +#define CPU_65816 2 + +unsigned char getcpu (void); +/* Detect the CPU the program is running on */ + + + +/* Macros for CPU instructions */ +#define BRK() __asm__ ("\tbrk") +#define CLI() __asm__ ("\tcli") +#define SEI() __asm__ ("\tsei") +#define JAM() __asm__ ("\t.byte\t$02") + + + +/* Struct that holds the registers for the sys function */ +struct regs { + unsigned char a; /* A register value */ + unsigned char x; /* X register value */ + unsigned char y; /* Y register value */ + unsigned char flags; /* Flags value */ + unsigned pc; /* Program counter */ +}; + +/* Defines for the flags in the regs structure */ +#define F_NEG 0x80 /* N flag */ +#define F_OVF 0x40 /* V flag */ +#define F_BRK 0x10 /* B flag */ +#define F_DEC 0x08 /* D flag */ +#define F_IEN 0x04 /* I flag */ +#define F_ZERO 0x02 /* Z flag */ +#define F_CARRY 0x01 /* C flag */ + +/* Function to call any machine language subroutine. All registers in the + * regs structure are passed into the routine and the results are passed + * out. Some of the flags are ignored on input. The called routine must + * end with an RTS. + */ +void __fastcall__ _sys (struct regs* r); + + + +/* Set and reset the break vector. The given user function is called if + * a break occurs. The values of the registers may be read from the brk_... + * variables. The value in brk_pc will point to the address that contains + * the brk instruction. + * The set_brk function will install an exit handler that will reset the + * vector if the program ends. + */ + +extern unsigned char brk_a; /* A register value */ +extern unsigned char brk_x; /* X register value */ +extern unsigned char brk_y; /* Y register value */ +extern unsigned char brk_sr; /* Status register */ +extern unsigned brk_pc; /* PC value */ + +typedef void (*brk_handler) (void); +/* Type of the break handler */ + +void __fastcall__ set_brk (brk_handler f); +/* Set the break vector to the given address, return the old address */ + +void reset_brk (void); +/* Reset the break vector to the original value */ + + + +/* End of 6502.h */ +#endif + + + diff --git a/include/_6525.h b/include/_6525.h new file mode 100644 index 000000000..6a29252a5 --- /dev/null +++ b/include/_6525.h @@ -0,0 +1,38 @@ +/* + * _6525.h + * + * Ullrich von Bassewitz, 22.09.1998 + * + * Internal include file, do not use directly. + * + */ + + + +#ifndef __6525_H +#define __6525_H + + + +/* Define a structure with the 6525 register offsets. The shadow registers + * (if port C is unused) are currently not implemented, we would need a + * union to do that, however that would introduce an additional name. + */ +struct __6525 { + unsigned char pra; /* Port register A */ + unsigned char prb; /* Port register B */ + unsigned char prc; /* Port register C */ + unsigned char ddra; /* Data direction register A */ + unsigned char ddrb; /* Data direction register B */ + unsigned char ddrc; /* Data direction register C */ + unsigned char cr; /* Control register */ + unsigned char air; /* Active interrupt register */ +}; + + + +/* End of _6525.h */ +#endif + + + diff --git a/include/_6526.h b/include/_6526.h new file mode 100644 index 000000000..41aecf349 --- /dev/null +++ b/include/_6526.h @@ -0,0 +1,43 @@ +/* + * _6526.h + * + * Ullrich von Bassewitz, 22.09.1998 + * + * Internal include file, do not use directly. + * + */ + + + +#ifndef __6526_H +#define __6526_H + + + +/* Define a structure with the 6526 register offsets */ +struct __6526 { + unsigned char pra; /* Port register A */ + unsigned char prb; /* Port register B */ + unsigned char ddra; /* Data direction register A */ + unsigned char ddrb; /* Data direction register B */ + unsigned char ta_lo; /* Timer A, low byte */ + unsigned char ta_hi; /* Timer A, high byte */ + unsigned char tb_lo; /* Timer B, low byte */ + unsigned char tb_hi; /* Timer B, high byte */ + unsigned char tod_10; /* TOD, 1/10 sec. */ + unsigned char tod_sec; /* TOD, seconds */ + unsigned char tod_min; /* TOD, minutes */ + unsigned char tod_hour; /* TOD, hours */ + unsigned char sdr; /* Serial data register */ + unsigned char icr; /* Interrupt control register */ + unsigned char cra; /* Control register A */ + unsigned char crb; /* Control register B */ +}; + + + +/* End of _6526.h */ +#endif + + + diff --git a/include/_6545.h b/include/_6545.h new file mode 100644 index 000000000..4b46cfaf3 --- /dev/null +++ b/include/_6545.h @@ -0,0 +1,29 @@ +/* + * _6545.h + * + * Ullrich von Bassewitz, 22.09.1998 + * + * Internal include file, do not use directly. + * + */ + + + +#ifndef __6545_H +#define __6545_H + + + +/* Define a structure with the 6545 register offsets */ +struct __6545 { + unsigned char ctrl; /* Control register */ + unsigned char data; /* Data register */ +}; + + + +/* End of _6545.h */ +#endif + + + diff --git a/include/_6551.h b/include/_6551.h new file mode 100644 index 000000000..4618880d4 --- /dev/null +++ b/include/_6551.h @@ -0,0 +1,31 @@ +/* + * _6551.h + * + * Ullrich von Bassewitz, 22.09.1998 + * + * Internal include file, do not use directly. + * + */ + + + +#ifndef __6551_H +#define __6551_H + + + +/* Define a structure with the 6551 register offsets */ +struct __6551 { + unsigned char data; /* Data register */ + unsigned char status; /* Status register */ + unsigned char cmd; /* Command register */ + unsigned char ctrl; /* Control register */ +}; + + + +/* End of _6551.h */ +#endif + + + diff --git a/include/_antic.h b/include/_antic.h new file mode 100644 index 000000000..110fd2a73 --- /dev/null +++ b/include/_antic.h @@ -0,0 +1,37 @@ +/* + * _antic.h + * + * Freddy Offenga, 4/9/2000 + * + * Internal include file, do not use directly. + * + */ + + +#ifndef __ANTIC_H +#define __ANTIC_H + + +/* Define a structure with the antic register offsets */ +struct __antic { + unsigned char dmactl; /* direct memory access control */ + unsigned char chactl; /* character mode control */ + unsigned char dlistl; /* display list pointer low-byte */ + unsigned char dlisth; /* display list pointer high-byte */ + unsigned char hscrol; /* horizontal scroll enable */ + unsigned char vscrol; /* vertical scroll enable */ + unsigned char unuse0; /* unused */ + unsigned char pmbase; /* msb of p/m base address */ + unsigned char unuse1; /* unused */ + unsigned char chbase; /* character base address */ + unsigned char wsync; /* wait for horizontal synchronization */ + unsigned char vcount; /* vertical line counter */ + unsigned char penh; /* light pen horizontal position */ + unsigned char penv; /* light pen vertical position */ + unsigned char nmien; /* non-maskable interrupt enable */ + unsigned char nmires; /* nmi reset/status */ +}; + +/* End of _antic.h */ +#endif + diff --git a/include/_gtia.h b/include/_gtia.h new file mode 100644 index 000000000..b188e6285 --- /dev/null +++ b/include/_gtia.h @@ -0,0 +1,78 @@ +/* + * _gtia.h + * + * Freddy Offenga, 4/9/2000 + * + * Internal include file, do not use directly. + * + */ + + +#ifndef __GTIA_H +#define __GTIA_H + + +/* Define a structure with the gtia register offsets */ +struct __gtia_write { + unsigned char hposp0; /* horizontal position player 0 */ + unsigned char hposp1; /* horizontal position player 1 */ + unsigned char hposp2; /* horizontal position player 2 */ + unsigned char hposp3; /* horizontal position player 3 */ + unsigned char hposm0; /* horizontal position missile 0 */ + unsigned char hposm1; /* horizontal position missile 1 */ + unsigned char hposm2; /* horizontal position missile 2 */ + unsigned char hposm3; /* horizontal position missile 3 */ + unsigned char sizep0; /* size of player 0 */ + unsigned char sizep1; /* size of player 1 */ + unsigned char sizep2; /* size of player 2 */ + unsigned char sizep3; /* size of player 3 */ + unsigned char sizem; /* size of missiles */ + unsigned char grafp0; /* graphics shape player 0 */ + unsigned char grafp1; /* graphics shape player 1 */ + unsigned char grafp2; /* graphics shape player 2 */ + unsigned char grafp3; /* graphics shape player 3 */ + unsigned char grafm; /* graphics shape missiles */ + unsigned char colpm0; /* color player and missile 0 */ + unsigned char colpm1; /* color player and missile 1 */ + unsigned char colpm2; /* color player and missile 2 */ + unsigned char colpm3; /* color player and missile 3 */ + unsigned char colpf0; /* color playfield 0 */ + unsigned char colpf1; /* color playfield 1 */ + unsigned char colpf2; /* color playfield 2 */ + unsigned char colpf3; /* color playfield 3 */ + unsigned char colbk; /* color background */ + unsigned char prior; /* priority selection */ + unsigned char vdelay; /* vertical delay */ + unsigned char gractl; /* stick/paddle latch, p/m control */ + unsigned char hitclr; /* clear p/m collision */ + unsigned char consol; /* console buttons */ +}; + +/* Define a structure with the gtia register offsets */ +struct __gtia_read { + unsigned char m0pf; /* missile 0 to playfield collision */ + unsigned char m1pf; /* missile 1 to playfield collision */ + unsigned char m2pf; /* missile 2 to playfield collision */ + unsigned char m3pf; /* missile 3 to playfield collision */ + unsigned char p0pf; /* player 0 to playfield collision */ + unsigned char p1pf; /* player 1 to playfield collision */ + unsigned char p2pf; /* player 2 to playfield collision */ + unsigned char p3pf; /* player 3 to playfield collision */ + unsigned char m0pl; /* missile 0 to player collision */ + unsigned char m1pl; /* missile 1 to player collision */ + unsigned char m2pl; /* missile 2 to player collision */ + unsigned char m3pl; /* missile 3 to player collision */ + unsigned char p0pl; /* player 0 to player collision */ + unsigned char p1pl; /* player 1 to player collision */ + unsigned char p2pl; /* player 2 to player collision */ + unsigned char p3pl; /* player 3 to player collision */ + unsigned char trig0; /* joystick trigger 0 */ + unsigned char trig1; /* joystick trigger 1 */ + unsigned char trig2; /* joystick trigger 2 */ + unsigned char trig3; /* joystick trigger 3 */ + unsigned char pal; /* pal/ntsc flag */ +}; + +/* End of _gtia.h */ +#endif + diff --git a/include/_pbi.h b/include/_pbi.h new file mode 100644 index 000000000..435df7ceb --- /dev/null +++ b/include/_pbi.h @@ -0,0 +1,44 @@ +/* + * _pbi.h + * + * Freddy Offenga, 4/25/2000 + * + * Internal include file, do not use directly. + * Atari parallel bus definitions + * + */ + + +#ifndef __PBI_H +#define __PBI_H + +/* parallel bus interface area */ +#define PBI ((unsigned char*)0xD100) + +/* parallel device IRQ status */ +#define PDVI ((unsigned char*)0xD1FF) + +/* parallel device select */ +#define PDVS ((unsigned char*)0xD1FF) + +/* parallel bus interface RAM area */ +#define PBIRAM ((unsigned char*)0xD600) + +/* parallel device ID 1 */ +#define PDID1 ((unsigned char*)0xD803) + +/* parallel device I/O vector */ +#define PDIDV ((unsigned char*)0xD805) + +/* parallel device IRQ vector */ +#define PDIRQV ((unsigned char*)0xD808) + +/* parallel device ID 2 */ +#define PDID2 ((unsigned char*)0xD80B) + +/* parallel device vector table */ +#define PDVV ((unsigned char*)0xD80D) + +/* End of _pbi.h */ +#endif + diff --git a/include/_pia.h b/include/_pia.h new file mode 100644 index 000000000..d102e8ac4 --- /dev/null +++ b/include/_pia.h @@ -0,0 +1,25 @@ +/* + * _pia.h + * + * Freddy Offenga, 4/9/2000 + * + * Internal include file, do not use directly. + * + */ + + +#ifndef __PIA_H +#define __PIA_H + + +/* Define a structure with the pia register offsets */ +struct __pia { + unsigned char porta; /* port A data r/w */ + unsigned char portb; /* port B data r/w */ + unsigned char pactl; /* port A control */ + unsigned char pbctl; /* port B control */ +}; + +/* End of _pia.h */ +#endif + diff --git a/include/_pokey.h b/include/_pokey.h new file mode 100644 index 000000000..424b5c6fa --- /dev/null +++ b/include/_pokey.h @@ -0,0 +1,55 @@ +/* + * _pokey.h + * + * Freddy Offenga, 4/9/2000 + * + * Internal include file, do not use directly. + * + */ + + +#ifndef __POKEY_H +#define __POKEY_H + + +/* Define a structure with the pokey register offsets */ +struct __pokey_write { + unsigned char audf1; /* audio channel #1 frequency */ + unsigned char audc1; /* audio channel #1 control */ + unsigned char audf2; /* audio channel #2 frequency */ + unsigned char audc2; /* audio channel #2 control */ + unsigned char audf3; /* audio channel #3 frequency */ + unsigned char audc3; /* audio channel #3 control */ + unsigned char audf4; /* audio channel #4 frequency */ + unsigned char audc4; /* audio channel #4 control */ + unsigned char audctl; /* audio control */ + unsigned char stimer; /* start pokey timers */ + unsigned char skrest; /* reset serial port status reg. */ + unsigned char potgo; /* start paddle scan sequence */ + unsigned char unuse1; /* unused */ + unsigned char serout; /* serial port data output */ + unsigned char irqen; /* interrupt request enable */ + unsigned char skctl; /* serial port control */ +}; +struct __pokey_read { + unsigned char pot0; /* paddle 0 value */ + unsigned char pot1; /* paddle 1 value */ + unsigned char pot2; /* paddle 2 value */ + unsigned char pot3; /* paddle 3 value */ + unsigned char pot4; /* paddle 4 value */ + unsigned char pot5; /* paddle 5 value */ + unsigned char pot6; /* paddle 6 value */ + unsigned char pot7; /* paddle 7 value */ + unsigned char allpot; /* eight paddle port status */ + unsigned char kbcode; /* keyboard code */ + unsigned char random; /* random number generator */ + unsigned char unuse2; /* unused */ + unsigned char unuse3; /* unused */ + unsigned char serin; /* serial port input */ + unsigned char irqst; /* interrupt request status */ + unsigned char skstat; /* serial port status */ +}; + +/* End of _pokey.h */ +#endif + diff --git a/include/_sid.h b/include/_sid.h new file mode 100644 index 000000000..0524d1f32 --- /dev/null +++ b/include/_sid.h @@ -0,0 +1,43 @@ +/* + * _sid.h + * + * Ullrich von Bassewitz, 22.09.1998 + * + * Internal include file, do not use directly. + * + */ + + + +#ifndef __SID_H +#define __SID_H + + + +/* Define a structure with the sid register offsets */ +struct __sid_voice { + unsigned freq; /* Frequency */ + unsigned pw; /* Pulse width */ + unsigned char ctrl; /* Control register */ + unsigned char ad; /* Attack/decay */ + unsigned char sr; /* Sustain/release */ +}; +struct __sid { + struct __sid_voice v1; /* Voice 1 */ + struct __sid_voice v2; /* Voice 2 */ + struct __sid_voice v3; /* Voice 3 */ + unsigned flt_freq; /* Filter frequency */ + unsigned char flt_ctrl; /* Filter control register */ + unsigned char amp; /* Amplitude */ + unsigned char ad1; /* A/D converter 1 */ + unsigned char noise; /* Noise generator */ + unsigned char read3; /* Value of voice 3 */ +}; + + + +/* End of _sid.h */ +#endif + + + diff --git a/include/_vdc.h b/include/_vdc.h new file mode 100644 index 000000000..4d75386a5 --- /dev/null +++ b/include/_vdc.h @@ -0,0 +1,29 @@ +/* + * _vdc.h + * + * Ullrich von Bassewitz, 22.09.1998 + * + * Internal include file, do not use directly. + * + */ + + + +#ifndef __VDC_H +#define __VDC_H + + + +/* Define a structure with the vdc register offsets */ +struct __vdc { + unsigned char ctrl; /* Control register */ + unsigned char data; /* Data register */ +}; + + + +/* End of _vdc.h */ +#endif + + + diff --git a/include/_vic.h b/include/_vic.h new file mode 100644 index 000000000..91ea80c0b --- /dev/null +++ b/include/_vic.h @@ -0,0 +1,78 @@ +/* + * _vic.h + * + * Ullrich von Bassewitz, 12.08.1998 + * + * Internal include file, do not use directly. + * + */ + + + +#ifndef __VIC_H +#define __VIC_H + + + +/* Define a structure with the vic register offsets */ +struct __vic { + unsigned char spr0_x; /* Sprite 0, X coordinate */ + unsigned char spr0_y; /* Sprite 0, Y coordinate */ + unsigned char spr1_x; /* Sprite 1, X coordinate */ + unsigned char spr1_y; /* Sprite 1, Y coordinate */ + unsigned char spr2_x; /* Sprite 2, X coordinate */ + unsigned char spr2_y; /* Sprite 2, Y coordinate */ + unsigned char spr3_x; /* Sprite 3, X coordinate */ + unsigned char spr3_y; /* Sprite 3, Y coordinate */ + unsigned char spr4_x; /* Sprite 4, X coordinate */ + unsigned char spr4_y; /* Sprite 4, Y coordinate */ + unsigned char spr5_x; /* Sprite 5, X coordinate */ + unsigned char spr5_y; /* Sprite 5, Y coordinate */ + unsigned char spr6_x; /* Sprite 6, X coordinate */ + unsigned char spr6_y; /* Sprite 6, Y coordinate */ + unsigned char spr7_x; /* Sprite 7, X coordinate */ + unsigned char spr7_y; /* Sprite 7, Y coordinate */ + unsigned char spr_hi_x; /* High bits of X coordinate */ + unsigned char ctrl1; /* Control register 1 */ + unsigned char rasterline; /* Current raster line */ + unsigned char strobe_x; /* Light pen, X position */ + unsigned char strobe_y; /* Light pen, Y position */ + unsigned char spr_ena; /* Enable sprites */ + unsigned char ctrl2; /* Control register 2 */ + unsigned char spr_exp_x; /* Expand sprites in X dir */ + unsigned char addr; /* Address of chargen and video ram */ + unsigned char irr; /* Interrupt request register */ + unsigned char imr; /* Interrupt mask register */ + unsigned char spr_bg_prio; /* Priority to background */ + unsigned char spr_mcolor; /* Sprite multicolor bits */ + unsigned char spr_exp_y; /* Expand sprites in Y dir */ + unsigned char spr_coll; /* Sprite/sprite collision reg */ + unsigned char spr_bg_coll; /* Sprite/background collision reg */ + unsigned char bordercolor; /* Border color */ + unsigned char bgcolor0; /* Background color 0 */ + unsigned char bgcolor1; /* Background color 1 */ + unsigned char bgcolor2; /* Background color 2 */ + unsigned char bgcolor3; /* Background color 3 */ + unsigned char spr_mcolor0; /* Color 0 for multicolor sprites */ + unsigned char spr_mcolor1; /* Color 1 for multicolor sprites */ + unsigned char spr0_color; /* Color sprite 0 */ + unsigned char spr1_color; /* Color sprite 1 */ + unsigned char spr2_color; /* Color sprite 2 */ + unsigned char spr3_color; /* Color sprite 3 */ + unsigned char spr4_color; /* Color sprite 4 */ + unsigned char spr5_color; /* Color sprite 5 */ + unsigned char spr6_color; /* Color sprite 6 */ + unsigned char spr7_color; /* Color sprite 7 */ + + /* The following ones are only valid in the C128: */ + unsigned char x_kbd; /* Additional keyboard lines */ + unsigned char clock; /* Clock switch bit */ +}; + + + +/* End of _vic.h */ +#endif + + + diff --git a/include/ace.h b/include/ace.h new file mode 100644 index 000000000..4b7e211c2 --- /dev/null +++ b/include/ace.h @@ -0,0 +1,86 @@ +/* + * ace.h + * + * Ullrich von Bassewitz, 06.06.1998 + * + */ + + + +#ifndef _ACE_H +#define _ACE_H + + + +#ifndef _STDDEF_H +#include +#endif + + + +struct aceDirentBuf { + unsigned long ad_size; /* Size in bytes */ + unsigned char ad_date [8]; /* YY:YY:MM:DD:HH:MM:SS:TW */ + char ad_type [4]; /* File type as ASCIIZ string */ + unsigned char ad_flags; /* File flags */ + unsigned char ad_usage; /* More flags */ + unsigned char ad_namelen; /* Length of name */ + char ad_name [17]; /* Name itself, ASCIIZ */ +}; + +int aceDirOpen (char* dir); +int aceDirClose (int handle); +int aceDirRead (int handle, struct aceDirentBuf* buf); + +/* Type of an ACE key. Key in low byte, shift mask in high byte */ +typedef unsigned int aceKey; + +/* #defines for the shift mask returned by aceConGetKey */ +#define aceSH_KEY 0x00FF /* Mask key itself */ +#define aceSH_MASK 0xFF00 /* Mask shift mask */ +#define aceSH_EXT 0x2000 /* Extended key */ +#define aceSH_CAPS 0x1000 /* Caps lock key */ +#define aceSH_ALT 0x0800 /* Alternate key */ +#define aceSH_CTRL 0x0400 /* Ctrl key */ +#define aceSH_CBM 0x0200 /* Commodore key */ +#define aceSH_SHIFT 0x0100 /* Shift key */ + +/* #defines for the options in aceConSetOpt/aceConGetOpt */ +#define aceOP_PUTMASK 1 /* Console put mask */ +#define aceOP_CHARCOLOR 2 /* Character color */ +#define aceOP_CHARATTR 3 /* Character attribute */ +#define aceOP_FILLCOLOR 4 /* Fill color */ +#define aceOP_FILLATTR 5 /* Fill attribute */ +#define aceOP_CRSCOLOR 6 /* Cursor color */ +#define aceOP_CRSWRAP 7 /* Force cursor wrap */ +#define aceOP_SHSCROLL 8 /* Shift keys for scrolling */ +#define aceOP_MOUSCALE 9 /* Mouse scaling */ +#define aceOP_RPTDELAY 10 /* Key repeat delay */ +#define aceOP_RPTRATE 11 /* Key repeat rate */ + +/* Console functions */ +void aceConWrite (char* buf, size_t count); +void aceConPutLit (int c); +void aceConPos (unsigned x, unsigned y); +void aceConGetPos (unsigned* x, unsigned* y); +unsigned aceConGetX (void); +unsigned aceConGetY (void); +char* aceConInput (char* buf, unsigned initial); +int aceConStopKey (void); +aceKey aceConGetKey (void); +int aceConKeyAvail (aceKey* key); +void aceConKeyMat (char* matrix); +void aceConSetOpt (unsigned char opt, unsigned char val); +int aceConGetOpt (unsigned char opt); + +/* Misc stuff */ +int aceMiscIoPeek (unsigned addr); +void aceMiscIoPoke (unsigned addr, unsigned char val); + + + +/* End of ace.h */ +#endif + + + diff --git a/include/apple2.h b/include/apple2.h new file mode 100644 index 000000000..a89052d50 --- /dev/null +++ b/include/apple2.h @@ -0,0 +1,58 @@ +/* + * apple2.h + * + * Written by Kevin Ruland. + */ + + + +#ifndef _APPLE2_H +#define _APPLE2_H + + + +/* Color Defines + * Since Apple2 does not support color text these defines are only + * used to get the library to compile correctly. They should not be used + * in user code + */ +#define COLOR_BLACK 0x00 +#define COLOR_WHITE 0x01 + + + +/* Characters codes */ +#define CH_DEL 0x7F +#define CH_ESC 0x1B +#define CH_CURS_UP 0x0B +#define CH_CURS_DOWN 0x0A + +/* These are defined to be OpenApple + NumberKey */ +#define CH_F1 0xB1 +#define CH_F2 0xB2 +#define CH_F3 0xB3 +#define CH_F4 0xB4 +#define CH_F5 0xB5 +#define CH_F6 0xB6 +#define CH_F7 0xB7 +#define CH_F8 0xB8 +#define CH_F9 0xB9 +#define CH_F10 0xB0 + +#define CH_ULCORNER '+' +#define CH_URCORNER '+' +#define CH_LLCORNER '+' +#define CH_LRCORNER '+' +#define CH_TTEE '+' +#define CH_BTEE '+' +#define CH_LTEE '+' +#define CH_RTEE '+' +#define CH_CROSS '+' + + + +/* End of apple2.h */ +#endif + + + diff --git a/include/assert.h b/include/assert.h new file mode 100644 index 000000000..15f0bc61b --- /dev/null +++ b/include/assert.h @@ -0,0 +1,29 @@ +/* + * assert.h + * + * Ullrich von Bassewitz, 06.06.1998 + * + */ + + + +#ifndef _ASSERT_H +#define _ASSERT_H + + + +#undef assert +#ifdef NDEBUG +# define assert(expr) +#else +extern void _afailed (const char*, unsigned); +# define assert(expr) if ((expr) == 0) _afailed (__FILE__, __LINE__) +#endif + + + +/* End of assert.h */ +#endif + + + diff --git a/include/atari.h b/include/atari.h new file mode 100644 index 000000000..da4e012f0 --- /dev/null +++ b/include/atari.h @@ -0,0 +1,75 @@ +/* + * atari.h + * + * Contributing authors: + * Mark Keates + * Freddy Offenga + * Christian Groessler + */ + +#ifndef _ATARI_H +#define _ATARI_H + +/* Color Defines */ +#define COLOR_BLACK 0x00 +#define COLOR_WHITE 0x0E + +/* Characters codes */ +#define CH_DEL 0xFE +#define CH_ESC 0x1B +#define CH_CURS_UP 28 +#define CH_CURS_DOWN 29 +#define CH_CURS_LEFT 30 +#define CH_CURS_RIGHT 31 + +#define CH_TAB 0x7F /* tabulator */ +#define CH_EOL 0x0B /* end-of-line marker */ +#define CH_CLR 0x7D /* clear screen */ +#define CH_BEL 0xFD /* bell */ +#define CH_RUBOUT 0x7E /* back space (rubout) */ +#define CH_DELLINE 0x9C /* delete line */ +#define CH_INSLINE 0x9D /* insert line */ + +/* These are defined to be Atari + NumberKey */ +#define CH_F1 177 +#define CH_F2 178 +#define CH_F3 179 +#define CH_F4 180 +#define CH_F5 181 +#define CH_F6 182 +#define CH_F7 183 +#define CH_F8 184 +#define CH_F9 185 +#define CH_F10 176 + +#define CH_ULCORNER 0x11 +#define CH_URCORNER 0x05 +#define CH_LLCORNER 0x1A +#define CH_LRCORNER 0x03 +#define CH_TTEE 0x17 +#define CH_BTEE 0x18 +#define CH_LTEE 0x01 +#define CH_RTEE 0x04 +#define CH_CROSS 0x19 +#define CH_HLINE 0x12 +#define CH_VLINE 0x16 + +/* Define hardware */ +#include <_gtia.h> +#define GTIA (*(struct __gtia_write*)0xD000) +#define GTIA (*(struct __gtia_read*)0xD000) + +#include <_pbi.h> + +#include <_pokey.h> +#define POKEY (*(struct __pokey_write*)0xD200) +#define POKEY (*(struct __pokey_read*)0xD200) + +#include <_pia.h> +#define PIA (*(struct __pia*)0xD300) + +#include <_antic.h> +#define ANTIC (*(struct __antic*)0xD400) + +/* End of atari.h */ +#endif diff --git a/include/c128.h b/include/c128.h new file mode 100644 index 000000000..a8bd86cf2 --- /dev/null +++ b/include/c128.h @@ -0,0 +1,68 @@ +/* + * c128.h + * + * Ullrich von Bassewitz, 12.08.1998 + */ + + + +#ifndef _C128_H +#define _C128_H + + + +/* Additional key defines */ +#define CH_F1 133 +#define CH_F2 137 +#define CH_F3 134 +#define CH_F4 138 +#define CH_F5 135 +#define CH_F6 139 +#define CH_F7 136 +#define CH_F8 140 + + + +/* Color defines */ +#define COLOR_BLACK 0x00 +#define COLOR_WHITE 0x01 +#define COLOR_RED 0x02 +#define COLOR_CYAN 0x03 +#define COLOR_VIOLET 0x04 +#define COLOR_GREEN 0x05 +#define COLOR_BLUE 0x06 +#define COLOR_YELLOW 0x07 +#define COLOR_ORANGE 0x08 +#define COLOR_BROWN 0x09 +#define COLOR_LIGHTRED 0x0A +#define COLOR_GRAY1 0x0B +#define COLOR_GRAY2 0x0C +#define COLOR_LIGHTGREEN 0x0D +#define COLOR_LIGHTBLUE 0x0E +#define COLOR_GRAY3 0x0F + + + +/* Define hardware */ +#include <_vic.h> +#define VIC (*(struct __vic*)0xD000) + +#include <_sid.h> +#define SID (*(struct __sid*)0xD400) + +#include <_6526.h> +#define CIA1 (*(struct __6526*)0xDC00) +#define CIA2 (*(struct __6526*)0xDD00) + + + +/* Define special memory areas */ +#define COLOR_RAM ((unsigned char*)0xD800) + + + +/* End of c128.h */ +#endif + + + diff --git a/include/c64.h b/include/c64.h new file mode 100644 index 000000000..bb5db2878 --- /dev/null +++ b/include/c64.h @@ -0,0 +1,68 @@ +/* + * c64.h + * + * Ullrich von Bassewitz, 12.08.1998 + */ + + + +#ifndef _C64_H +#define _C64_H + + + +/* Additional key defines */ +#define CH_F1 133 +#define CH_F2 137 +#define CH_F3 134 +#define CH_F4 138 +#define CH_F5 135 +#define CH_F6 139 +#define CH_F7 136 +#define CH_F8 140 + + + +/* Color defines */ +#define COLOR_BLACK 0x00 +#define COLOR_WHITE 0x01 +#define COLOR_RED 0x02 +#define COLOR_CYAN 0x03 +#define COLOR_VIOLET 0x04 +#define COLOR_GREEN 0x05 +#define COLOR_BLUE 0x06 +#define COLOR_YELLOW 0x07 +#define COLOR_ORANGE 0x08 +#define COLOR_BROWN 0x09 +#define COLOR_LIGHTRED 0x0A +#define COLOR_GRAY1 0x0B +#define COLOR_GRAY2 0x0C +#define COLOR_LIGHTGREEN 0x0D +#define COLOR_LIGHTBLUE 0x0E +#define COLOR_GRAY3 0x0F + + + +/* Define hardware */ +#include <_vic.h> +#define VIC (*(struct __vic*)0xD000) + +#include <_sid.h> +#define SID (*(struct __sid*)0xD400) + +#include <_6526.h> +#define CIA1 (*(struct __6526*)0xDC00) +#define CIA2 (*(struct __6526*)0xDD00) + + + +/* Define special memory areas */ +#define COLOR_RAM ((unsigned char*)0xD800) + + + +/* End of c64.h */ +#endif + + + diff --git a/include/cbm.h b/include/cbm.h new file mode 100644 index 000000000..d3b62a103 --- /dev/null +++ b/include/cbm.h @@ -0,0 +1,74 @@ +/* + * cbm.h + * + * Ullrich von Bassewitz, 07.08.1998 + */ + + + +#ifndef _CBM_H +#define _CBM_H + + + +/* Load the system specific files here, if needed */ +#ifdef __C64__ +#ifndef _C64_H +#include +#endif +#endif + +#ifdef __C128__ +#ifndef _C128_H +#include +#endif +#endif + +#ifdef __PLUS4__ +#ifndef _PLUS4_H +#include +#endif +#endif + +#ifdef __CBM610__ +#ifndef _CBM610_H +#include +#endif +#endif + +#ifdef __PET__ +#ifndef _PET_H +#include +#endif +#endif + + + +/* Characters codes (CBM charset) */ +#define CH_HLINE 96 +#define CH_VLINE 125 +#define CH_ULCORNER 176 +#define CH_URCORNER 174 +#define CH_LLCORNER 173 +#define CH_LRCORNER 189 +#define CH_TTEE 178 +#define CH_RTEE 179 +#define CH_BTEE 177 +#define CH_LTEE 171 +#define CH_CROSS 123 +#define CH_CURS_UP 145 +#define CH_CURS_DOWN 17 +#define CH_CURS_LEFT 157 +#define CH_CURS_RIGHT 29 +#define CH_PI 126 +#define CH_DEL 20 +#define CH_INS 148 +#define CH_ESC 95 + + + +/* End of cbm.h */ +#endif + + + diff --git a/include/cbm610.h b/include/cbm610.h new file mode 100644 index 000000000..14c7b50f8 --- /dev/null +++ b/include/cbm610.h @@ -0,0 +1,73 @@ +/* + * cbm610.h + * + * Ullrich von Bassewitz, 12.08.1998 + */ + + + +#ifndef _CBM610_H +#define _CBM610_H + + + +/* Additional key defines */ +#define CH_F1 224 +#define CH_F2 225 +#define CH_F3 226 +#define CH_F4 227 +#define CH_F5 228 +#define CH_F6 229 +#define CH_F7 230 +#define CH_F8 231 +#define CH_F9 232 +#define CH_F10 233 +#define CH_F11 234 +#define CH_F12 235 +#define CH_F13 236 +#define CH_F14 237 +#define CH_F15 238 +#define CH_F16 239 +#define CH_F17 240 +#define CH_F18 241 +#define CH_F19 242 +#define CH_F20 243 + + + +/* Color defines */ +#define COLOR_BLACK 0x00 +#define COLOR_WHITE 0x01 + + + +/* Special routines to write bytes and words in the system bank */ +void __fastcall__ pokebsys (unsigned addr, unsigned char val); +void __fastcall__ pokewsys (unsigned addr, unsigned val); + + + +/* Define hardware */ +#include <_6545.h> +#define CRTC (*(struct __6545)0xD800) + +#include <_sid.h> +#define SID (*(struct __sid*)0xDA00) + +#include <_6526.h> +#define CIA (*(struct __cia*)0xDC00) + +#include <_6551.h> +#define ACIA (*(struct __6551*)0xDD00) + +#include <_6525.h> +#define TPI1 (*(struct __6525*)0xDE00) +#define TPI2 (*(struct __6525*)0xDF00) + + + +/* End of cbm610.h */ +#endif + + + diff --git a/include/conio.h b/include/conio.h new file mode 100644 index 000000000..552b8d5ee --- /dev/null +++ b/include/conio.h @@ -0,0 +1,158 @@ +/* + * conio.h + * + * Ullrich von Bassewitz, 06.08.1998 + * + * + * This is the direct console interface for cc65. I do not like the function + * names very much, but the first version started as a rewrite of Borlands + * conio, and, even if the interface has changed, the names did not. + * + * The interface does direct screen I/O, so it is fast enough for most + * programs. I did not implement text windows, since many applications do + * not need them and should not pay for the additional overhead. It should + * be easy to add text windows on a higher level if needed, + * + * Most routines do not check the parameters. This may be unfortunate but is + * also related to speed. The coordinates are always 0/0 based. + * + */ + + + +#ifndef _CONIO_H +#define _CONIO_H + + + +#ifndef _STDARG_H +# include +#endif + +/* Read the CBM file if we're compiling for a CBM machine */ +#ifdef __CBM__ +# ifndef _CBM_H +# include +# endif +#endif + +#ifdef __APPLE2__ +# ifndef _APPLE2_H +# include +# endif +#endif + +#ifdef __ATARI__ +# ifndef _ATARI_H +# include +# endif +#endif + + + +/*****************************************************************************/ +/* Functions */ +/*****************************************************************************/ + + + +void clrscr (void); +/* Clear the whole screen and put the cursor into the top left corner */ + +unsigned char kbhit (void); +/* Return true if there's a key waiting, return false if not */ + +void __fastcall__ gotox (unsigned char x); +/* Set the cursor to the specified X position, leave the Y position untouched */ + +void __fastcall__ gotoy (unsigned char y); +/* Set the cursor to the specified Y position, leave the X position untouched */ + +void __fastcall__ gotoxy (unsigned char x, unsigned char y); +/* Set the cursor to the specified position */ + +unsigned char wherex (void); +/* Return the X position of the cursor */ + +unsigned char wherey (void); +/* Return the Y position of the cursor */ + +void __fastcall__ cputc (char c); +/* Output one character at the current cursor position */ + +void __fastcall__ cputcxy (unsigned char x, unsigned char y, char c); +/* Same as "gotoxy (x, y); cputc (c);" */ + +void __fastcall__ cputs (const char* s); +/* Output a NUL terminated string at the current cursor position */ + +void __fastcall__ cputsxy (unsigned char x, unsigned char y, const char* s); +/* Same as "gotoxy (x, y); puts (s);" */ + +int cprintf (const char* format, ...); +/* Like printf, but uses direct screen I/O */ + +int vcprintf (const char* format, va_list ap); +/* Like vprintf, but uses direct screen I/O */ + +char cgetc (void); +/* Return a character from the keyboard. If there is no character available, + * the functions waits until the user does press a key. If cursor is set to + * 1 (see below), a blinking cursor is displayed while waiting. + */ + +unsigned char __fastcall__ cursor (unsigned char onoff); +/* If onoff is 1, a cursor is display when waiting for keyboard input. If + * onoff is 0, the cursor is hidden when waiting for keyboard input. The + * function returns the old cursor setting. + */ + +unsigned char __fastcall__ revers (unsigned char onoff); +/* Enable/disable reverse character display. This may not be supported by + * the output device. Return the old setting. + */ + +unsigned char __fastcall__ textcolor (unsigned char color); +/* Set the color for text output. The old color setting is returned. */ + +unsigned char __fastcall__ bgcolor (unsigned char color); +/* Set the color for the background. The old color setting is returned. */ + +unsigned char __fastcall__ bordercolor (unsigned char color); +/* Set the color for the border. The old color setting is returned. */ + +void __fastcall__ chline (unsigned char length); +/* Output a horizontal line with the given length starting at the current + * cursor position. + */ + +void __fastcall__ chlinexy (unsigned char x, unsigned char y, unsigned char length); +/* Same as "gotoxy (x, y); chline (length);" */ + +void __fastcall__ cvline (unsigned char length); +/* Output a vertical line with the given length at the current cursor + * position. + */ + +void __fastcall__ cvlinexy (unsigned char x, unsigned char y, unsigned char length); +/* Same as "gotoxy (x, y); cvline (length);" */ + +void __fastcall__ cclear (unsigned char length); +/* Clear part of a line (write length spaces). */ + +void __fastcall__ cclearxy (unsigned char x, unsigned char y, unsigned char length); +/* Same as "gotoxy (x, y); cclear (length);" */ + +void __fastcall__ screensize (unsigned char* x, unsigned char* y); +/* Return the current screen size. */ + +void __fastcall__ cputhex8 (unsigned char val); +void __fastcall__ cputhex16 (unsigned val); +/* These shouldn't be here... */ + + +/* End of conio.h */ +#endif + + + diff --git a/include/ctype.h b/include/ctype.h new file mode 100644 index 000000000..3a1e637a6 --- /dev/null +++ b/include/ctype.h @@ -0,0 +1,70 @@ +/* + * ctype.h + * + * Ullrich von Bassewitz, 03.06.1998 + * + */ + + + +#ifndef _CTYPE_H +#define _CTYPE_H + + + +int __fastcall__ isalnum (int c); +int __fastcall__ isalpha (int c); +int __fastcall__ iscntrl (int c); +int __fastcall__ isdigit (int c); +int __fastcall__ isgraph (int c); +int __fastcall__ islower (int c); +int __fastcall__ isprint (int c); +int __fastcall__ ispunct (int c); +int __fastcall__ isspace (int c); +int __fastcall__ isupper (int c); +int __fastcall__ isxdigit (int c); +#ifndef __STRICT_ANSI__ +int __fastcall__ isblank (int c); /* cc65 (and GNU) extension */ +#endif + +int __fastcall__ toupper (int c); /* Always external */ +int __fastcall__ tolower (int c); /* Always external */ + + + +/* When inlining of known function is enabled, overload most of the above + * functions by macros. The function prototypes are again available after + * #undef'ing the macros. +*/ +#ifdef __OPT_s__ + + +extern unsigned char _ctype[256]; + +#define isalnum(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\tand\t#$07"), __AX__) +#define isalpha(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\tand\t#$03"), __AX__) +#define iscntrl(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\tand\t#$10"), __AX__) +#define isdigit(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\tand\t#$04"), __AX__) +#define isgraph(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\teor\t#$30\n\tand\t#$30"), __AX__) +#define islower(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\tand\t#$01"), __AX__) +#define isprint(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\teor\t#$10\n\tand\t#$10"), __AX__) +#define ispunct(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\teor\t#$37\n\tand\t#$37"), __AX__) +#define isspace(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\tand\t#$60"), __AX__) +#define isupper(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\tand\t#$02"), __AX__) +#define isxdigit(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\tand\t#$08"), __AX__) + +#ifndef __STRICT_ANSI__ +/* cc65 and GNU extension */ +#define isblank(c) (__AX__ = (c), __asm__ ("\ttay\n\tlda\t__ctype,y\n\tand\t#$80"), __AX__) +#endif + + +#endif + + + +/* End of ctype.h */ +#endif + + + diff --git a/include/dbg.h b/include/dbg.h new file mode 100644 index 000000000..7aa565b01 --- /dev/null +++ b/include/dbg.h @@ -0,0 +1,96 @@ +/* + * dbg.h + * + * Ullrich von Bassewitz, 08.08.1998 + * + * + * This is the interface to the cc65 debugger. Since many of the functions + * used for the debugger are quite usable even in another context, they + * are declared here. + * + * To use the debugger, just call DbgStart in your application. This will + * clear the screen and startup the debugger with the program counter + * pointing to the next instruction after the call to DbgStart. Once DbgStart + * has been executed, the debugger will also catch any BRK opcode. Use the + * BREAK function declared below to insert additional breakpoints into your + * code. + * + * There are currently a lot of things that cannot be debugged, graphical + * applications are an example. The debugger does not save your screen + * contents, so even your text screen gets destroyed. However, you can + * debug the C and runtime library, even if the debugger is using this + * stuff itself. + * + * Note: When using the debugger, there are some other identifiers with + * external linkage, that start with Dbg. Avoid those names if you use the + * module. + */ + + + +#ifndef _DBG_H +#define _DBG_H + + + +/*****************************************************************************/ +/* Utuility functions */ +/*****************************************************************************/ + + + +unsigned __fastcall__ DbgDisAsm (unsigned Addr, char* Buf, unsigned char Len); +/* Disassemble one instruction at address addr into the given buffer. + * The resulting line has the format, "AAAA__BB_BB_BB___OPC_OPERAND", + * where AAAA is the hexadecimal representation of addr, BB are the + * bytes (in hex) that make the instruction, OPC is the mnemonic, and + * OPERAND is an operand for the instruction. + * The buffer is filled with spaces up to the given length and terminated as + * a usual C string. NOTE: Buf must be able to hold Len+1 characters. + * The function returns the length of the disassembled instruction, so, + * to disassemble the next instruction, add the return value to addr + * and call the function again. + */ + +unsigned __fastcall__ DbgDisAsmLen (unsigned Addr); +/* Disassemble one instruction, but do only return the length, do not + * create a visible representation. This function is useful when + * disassembling backwards, it is much faster than DbgDisAsm. + */ + +int __fastcall__ DbgIsRAM (unsigned Addr); +/* Return true if we can read and write the given address */ + +char* DbgMemDump (unsigned Addr, char* Buf, unsigned char Len); +/* Create a line of a memory dump in the given buffer. The buffer contains + * the starting address (4 digits hex), then Len bytes in this format: + * "AAAA__XX_YY_ZZ_...". The passed char buffer must hold Len*3+5 bytes + * plus a terminator byte. + * The function does not work correctly if the created string is longer + * than 255 bytes. + * The return value is Buf. + */ + + + +/*****************************************************************************/ +/* High level user interface */ +/*****************************************************************************/ + + + +void __fastcall__ DbgInit (unsigned unused); +/* Initialize the debugger. Use 0 as parameter. The debugger will popup on + * next brk encountered. + */ + +#define BREAK() __asm__ ("\tbrk") +/* Use this to insert breakpoints into your code */ + + + +/* End of dbg.h */ +#endif + + + diff --git a/include/errno.h b/include/errno.h new file mode 100644 index 000000000..a69baf834 --- /dev/null +++ b/include/errno.h @@ -0,0 +1,54 @@ +/* + * errno.h + * + * Ullrich von Bassewitz, 18.08.1998 + * + */ + + + +#ifndef _ERRNO_H +#define _ERRNO_H + + + +/* Operating system specific error codes */ +extern unsigned char _oserror; + +/* Mapper function, don't call directly */ +void _maperrno (void); + +/* This one is called under the hood. User callable. */ +int __fastcall__ _osmaperrno (unsigned char oserror); + +/* System error codes go here */ +extern int _errno; + +/* errno must be a macro, here the mapper is called */ +#define errno (_maperrno(), _errno) + + + +/* Possible error codes */ +#define ENOENT 1 /* No such file or directory */ +#define ENOMEM 2 /* Out of memory */ +#define EACCES 3 /* Permission denied */ +#define ENODEV 4 /* No such device */ +#define EMFILE 5 /* Too many open files */ +#define EBUSY 6 /* Device or resource busy */ +#define EINVAL 7 /* Invalid argument */ +#define ENOSPC 8 /* No space left on device */ +#define EEXIST 9 /* File exists */ +#define EAGAIN 10 /* Try again */ +#define EIO 11 /* I/O error */ +#define EINTR 12 /* Interrupted system call */ +#define ENOSYS 13 /* Function not implemented */ +#define ESPIPE 14 /* Illegal seek */ +#define EUNKNOWN 15 /* Unknown OS specific error */ + + + +#endif + + + diff --git a/include/fcntl.h b/include/fcntl.h new file mode 100644 index 000000000..416ed2fa5 --- /dev/null +++ b/include/fcntl.h @@ -0,0 +1,39 @@ +/* + * fcntl.h + * + * Ullrich von Bassewitz, 30.05.1998 + * + */ + + + +#ifndef _FCNTL_H +#define _FCNTL_H + + + +/* Flag values for the open() call */ +#define O_RDONLY 0x01 +#define O_WRONLY 0x02 +#define O_RDWR 0x03 +#define O_CREAT 0x10 +#define O_TRUNC 0x20 +#define O_APPEND 0x40 + + + +/* Functions */ +int open (const char* name, int flags, ...); /* May take a mode argument */ +int close (int fd); +int write (int fd, const void* buf, unsigned count); +int read (int fd, void* buf, unsigned count); +int mkdir (const char* name, ...); /* May take a mode argument */ +int rmdir (const char* name); + + + +/* End of fcntl.h */ +#endif + + + diff --git a/include/geos.h b/include/geos.h new file mode 100644 index 000000000..671a4aeb1 --- /dev/null +++ b/include/geos.h @@ -0,0 +1,69 @@ +/* + Supreme GEOS header file + includes all other headers + + Maciej 'YTM/Alliance' Witkowiak, 27.10.1999 +*/ + + + +#ifndef _GEOS_H +#define _GEOS_H + + + +#ifndef _GCONST_H +#include +#endif + +#ifndef _GSTRUCT_H +#include +#endif + +#ifndef _GSYM_H +#include +#endif + +#ifndef _GDISK_H +#include +#endif + +#ifndef _GFILE_H +#include +#endif + +#ifndef _GPROCESS_H +#include +#endif + +#ifndef _GGRAPH_H +#include +#endif + +#ifndef _GMENU_H +#include +#endif + +#ifndef _GSPRITE_H +#include +#endif + +#ifndef _GMEMORY_H +#include +#endif + +#ifndef _GSYS_H +#include +#endif + +#ifndef _GDLGBOX_H +#include +#endif + + + +/* End of geos.h */ +#endif + + + diff --git a/include/geos/gconst.h b/include/geos/gconst.h new file mode 100644 index 000000000..517ee37a1 --- /dev/null +++ b/include/geos/gconst.h @@ -0,0 +1,63 @@ +/* + GEOS constants, 4-2-99, 18-3-99 + + small C version: 25-27.10.99 + reassembled by Maciej 'YTM/Alliance' Witkowiak +*/ + +/* Here are constants which didn't fit into any other cathegory... */ + +#ifndef _GCONST_H +#define _GCONST_H + +#define NULL 0 +#define FALSE NULL +#define TRUE 0xff +#define MOUSE_SPRNUM 0 +#define DISK_DRV_LGH 0x0d80 + +/* drivetypes */ +#define DRV_NULL 0 +#define DRV_1541 1 +#define DRV_1571 2 +#define DRV_1581 3 +#define DRV_NETWORK 15 + +/* various disk constants */ +#define REL_FILE_NUM 9 +#define CMND_FILE_NUM 15 +#define MAX_CMND_STR 32 +#define DIR_1581_TRACK 40 +#define DIR_ACC_CHAN 13 +#define DIR_TRACK 18 +#define N_TRACKS 35 +#define DK_NM_ID_LEN 18 +#define TRACK 9 +#define SECTOR 12 +#define TOTAL_BLOCKS 664 + +/* offset to something */ +#define OFF_INDEX_PTR 1 + +/* values for MMU config - C128 */ +#define CIOIN 0x7E +#define CRAM64K 0x7F +#define CKRNLBASIOIN 0x40 +#define CKRNLIOIN 0x4E + +/* alarmSetFlag */ +#define ALARMMASK 4 + +#define CLR_SAVE 0x40 +#define CONSTRAINED 0x40 +#define UN_CONSTRAINED 0 +#define FG_SAVE 0x80 + +#define FUTURE1 7 +#define FUTURE2 8 +#define FUTURE3 9 +#define FUTURE4 10 +#define USELAST 127 +#define SHORTCUT 128 + +#endif diff --git a/include/geos/gdisk.h b/include/geos/gdisk.h new file mode 100644 index 000000000..9fac4640a --- /dev/null +++ b/include/geos/gdisk.h @@ -0,0 +1,81 @@ +/* + GEOS functions from disk driver + + ported to small C on 21.12.1999 + by Maciej 'YTM/Alliance' Witkowiak +*/ + +#ifndef _GDISK_H +#define _GDISK_H + +#ifndef _GSTRUCT_H +#include +#endif + +char __fastcall__ ReadBuff(struct tr_se *myTrSe); +char __fastcall__ WriteBuff(struct tr_se *myTrSe); + +char __fastcall__ GetBlock(struct tr_se *myTrSe, char *buffer); +char __fastcall__ PutBlock(struct tr_se *myTrSe, char *buffer); +char __fastcall__ ReadBlock(struct tr_se *myTrSe, char *buffer); +char __fastcall__ WriteBlock(struct tr_se *myTrSe, char *buffer); +char __fastcall__ VerWriteBlock(struct tr_se *myTrSe, char *buffer); + +int __fastcall__ CalcBlksFree(void); +char __fastcall__ ChkDkGEOS(void); +char __fastcall__ SetGEOSDisk(void); +char __fastcall__ NewDisk(void); +char __fastcall__ OpenDisk(void); + +char __fastcall__ FindBAMBit(struct tr_se *myTrSe); +char __fastcall__ BlkAlloc(struct tr_se output[], int length); +char __fastcall__ NxtBlkAlloc(struct tr_se *startTrSe, + struct tr_se output[], int length); +char __fastcall__ FreeBlock(struct tr_se *myTrSe); +struct tr_se __fastcall__ SetNextFree(struct tr_se *myTrSe); +// above needs (int) casts on both sides of '=' + +char __fastcall__ GetDirHead(void); +char __fastcall__ PutDirHead(void); +void __fastcall__ GetPtrCurDkNm(char *name); + +void __fastcall__ EnterTurbo(void); +void __fastcall__ ExitTurbo(void); +void __fastcall__ PurgeTurbo(void); + +char __fastcall__ ChangeDiskDevice(char newdev); + +/* disk header offsets */ +#define OFF_TO_BAM 4 +#define OFF_DISK_NAME 144 +#define OFF_GS_DTYPE 189 +#define OFF_OP_TR_SC 171 +#define OFF_GS_ID 173 +/* disk errors */ +#define ANY_FAULT 0xf0 +#define NO_BLOCKS 1 +#define INV_TRACK 2 +#define INSUFF_SPACE 3 +#define FULL_DIRECTORY 4 +#define FILE_NOT_FOUND 5 +#define BAD_BAM 6 +#define UNOPENED_VLIR 7 +#define INV_RECORD 8 +#define OUT_OF_RECORDS 9 +#define STRUCT_MISMAT 10 +#define BFR_OVERFLOW 11 +#define CANCEL_ERR 12 +#define DEV_NOT_FOUND 13 +#define INCOMPATIBLE 14 +#define HDR_NOT_THERE 0x20 +#define NO_SYNC 0x21 +#define DBLK_NOT_THERE 0x22 +#define DAT_CHKSUM_ERR 0x23 +#define WR_VER_ERR 0x25 +#define WR_PR_ON 0x26 +#define HDR_CHKSUM_ERR 0x27 +#define DSK_ID_MISMAT 0x29 +#define BYTE_DEC_ERR 0x2e +#define DOS_MISMATCH 0x73 + +#endif diff --git a/include/geos/gdlgbox.h b/include/geos/gdlgbox.h new file mode 100644 index 000000000..91744c7fc --- /dev/null +++ b/include/geos/gdlgbox.h @@ -0,0 +1,102 @@ +/* + GEOS dialog box functions + + ported to small C on 26.12.1999 + by Maciej 'YTM/Alliance' Witkowiak + 10.03.2000 - update +*/ + +#ifndef _GDLGBOX_H +#define _GDLGBOX_H + +char __fastcall__ DoDlgBox(char *dboxstring); +char __fastcall__ RstrFrmDialogue(void); + +/* These are custom, predefined dialog boxes, I'm sure you'll find them usable + Most of them show 2 lines of text */ + +char __fastcall__ DlgBoxYesNo(char *line1, char *line2); +char __fastcall__ DlgBoxOkCancel(char *line1, char *line2); +void __fastcall__ DlgBoxOk(char *line1, char *line2); +char __fastcall__ DlgBoxGetString(char *myString, char strLength, + char *line1, char *line2); +char __fastcall__ DlgBoxFileSelect(char *classtxt, char ftype, + char *fname); + +/* Now the command string type */ + +typedef void dlgBoxStr; + +/* and command string commands - macros */ + +#define DB_DEFPOS(pattern) (char)(DEF_DB_POS | (pattern)) +#define DB_SETPOS(pattern,top,bot,left,right) \ + (char)(SET_DB_POS | (pattern)), (char)(top), (char)(bot), \ + (unsigned)(left), (unsigned)(right) +#define DB_ICON(i,x,y) (char)(i), (char)(x), (char)(y) +#define DB_TXTSTR(x,y,text) (char)DBTXTSTR, (char)(x), (char)(y), (text) +#define DB_VARSTR(x,y,ptr) (char)DBVARSTR, (char)(x), (char)(y), (char)(ptr) +#define DB_GETSTR(x,y,ptr,length) (char)DBGETSTRING, (char)(x), (char)(y), (char)(ptr), (char)(length) +#define DB_SYSOPV(ptr) (char)DBSYSOPV, (unsigned)(ptr) +#define DB_GRPHSTR(ptr) (char)DBGRPHSTR, (unsigned)(ptr) +#define DB_GETFILES(x,y) (char)DBGETFILES, (char)(x), (char)(y) +#define DB_OPVEC(ptr) (char)DBOPVEC, (unsigned)(ptr) +#define DB_USRICON(x,y,ptr) (char)DBUSRICON, (char)(x), (char)(y), (unsigned)(ptr) +#define DB_USRROUT(ptr) (char)DB_USR_ROUT, (unsigned)(ptr) +#define DB_END (char)NULL + +/* + part of constants below is used internally, but some are useful for macros above +*/ + +/* icons for DB_ICON */ +#define OK 1 +#define CANCEL 2 +#define YES 3 +#define NO 4 +#define OPEN 5 +#define DISK 6 +/* commands - internally used by command macros */ +#define DBTXTSTR 11 +#define DBVARSTR 12 +#define DBGETSTRING 13 +#define DBSYSOPV 14 +#define DBGRPHSTR 15 +#define DBGETFILES 16 +#define DBOPVEC 17 +#define DBUSRICON 18 +#define DB_USR_ROUT 19 +/* icons tabulation in standard window */ +#define DBI_X_0 1 +#define DBI_X_1 9 +#define DBI_X_2 17 +#define DBI_Y_0 8 +#define DBI_Y_1 40 +#define DBI_Y_2 72 +/* standard window size defaults */ +#define SET_DB_POS 0 +#define DEF_DB_POS 0x80 +#define DEF_DB_TOP 32 +#define DEF_DB_BOT 127 +#define DEF_DB_LEFT 64 +#define DEF_DB_RIGHT 255 +/* text tabulation in standard window */ +#define TXT_LN_1_Y 16 +#define TXT_LN_2_Y 32 +#define TXT_LN_3_Y 48 +#define TXT_LN_4_Y 64 +#define TXT_LN_5_Y 80 +#define TXT_LN_X 16 +/* system icons size */ +#define SYSDBI_HEIGHT 16 +#define SYSDBI_WIDTH 6 +/* dialogbox string offsets */ +#define OFF_DB_FORM 0 +#define OFF_DB_TOP 1 +#define OFF_DB_BOT 2 +#define OFF_DB_LEFT 3 +#define OFF_DB_RIGHT 5 +#define OFF_DB_1STCMD 7 + +#endif + diff --git a/include/geos/gfile.h b/include/geos/gfile.h new file mode 100644 index 000000000..6e3174fc7 --- /dev/null +++ b/include/geos/gfile.h @@ -0,0 +1,101 @@ +/* + GEOS filesystem functions + + ported to small C on 25.12.1999 + by Maciej 'YTM/Alliance' Witkowiak +*/ + +#ifndef _GFILE_H +#define _GFILE_H + +#ifndef _GSTRUCT_H +#include +#endif + +struct filehandle *__fastcall__ Get1stDirEntry(void); +struct filehandle *__fastcall__ GetNxtDirEntry(void); + +char __fastcall__ FindFTypes(char *buffer, char ftype, char fmaxnum, char *classtxt); + +char __fastcall__ FindFile(char *fname); +char __fastcall__ ReadFile(struct tr_se *myTrSe, char *buffer, int flength); +char __fastcall__ SaveFile(struct fileheader *myHeader); +char __fastcall__ FreeFile(struct tr_se myTable[]); +char __fastcall__ DeleteFile(char *fname); +char __fastcall__ RenameFile(char *source, char *target); + +char __fastcall__ ReadByte(void); + +char __fastcall__ FollowChain(struct tr_se *startTrSe, char *buffer); +char __fastcall__ GetFHdrInfo(struct filehandle *myFile); + +char __fastcall__ OpenRecordFile(char *fname); +char __fastcall__ CloseRecordFile(void); +char __fastcall__ NextRecord(void); +char __fastcall__ PreviousRecord(void); +char __fastcall__ PointRecord(char); +char __fastcall__ DeleteRecord(void); +char __fastcall__ InsertRecord(void); +char __fastcall__ AppendRecord(void); +char __fastcall__ ReadRecord(char *buffer, int flength); +char __fastcall__ WriteRecord(char *buffer, int flength); +char __fastcall__ UpdateRecordFile(void); + +/* GEOS filetypes */ +#define NOT_GEOS 0 +#define BASIC 1 +#define ASSEMBLY 2 +#define DATA 3 +#define SYSTEM 4 +#define DESK_ACC 5 +#define APPLICATION 6 +#define APPL_DATA 7 +#define FONT 8 +#define PRINTER 9 +#define INPUT_DEVICE 10 +#define DISK_DEVICE 11 +#define SYSTEM_BOOT 12 +#define TEMPORARY 13 +#define AUTO_EXEC 14 +#define INPUT_128 15 +#define NUMFILETYPES 16 +/* supported structures */ +#define SEQUENTIAL 0 +#define VLIR 1 +/* DOS filetypes */ +#define DEL 0 +#define SEQ 1 +#define PRG 2 +#define USR 3 +#define REL 4 +#define CBM 5 +/* directory offsets */ +/* offsets in dir entry */ +#define FRST_FILE_ENTRY 2 +#define OFF_CFILE_TYPE 0 +#define OFF_DE_TR_SC 1 +#define OFF_FNAME 3 +#define OFF_GHDR_PTR 19 +#define OFF_GSTRUC_TYPE 21 +#define OFF_GFILE_TYPE 22 +#define OFF_YEAR 23 +#define OFF_SIZE 28 +#define OFF_NXT_FILE 32 +/* offsets in file header */ +#define O_GHIC_WIDTH 2 +#define O_GHIC_HEIGHT 3 +#define O_GHIC_PIC 4 +#define O_GHCMDR_TYPE 68 +#define O_GHGEOS_TYPE 69 +#define O_GHSTR_TYPE 70 +#define O_GHST_ADDR 71 +#define O_GHEND_ADDR 73 +#define O_GHST_VEC 75 +#define O_GHFNAME 77 +#define O_128_FLAGS 96 +#define O_GH_AUTHOR 97 +#define O_GHP_DISK 97 +#define O_GHP_FNAME 117 +#define O_GHINFO_TXT 0xa0 + +#endif diff --git a/include/geos/ggraph.h b/include/geos/ggraph.h new file mode 100644 index 000000000..51caad7ba --- /dev/null +++ b/include/geos/ggraph.h @@ -0,0 +1,170 @@ +/* + GEOS graphic (non icon/menu/sprite) functions + + ported to small C on 29.10.1999 + by Maciej 'YTM/Alliance' Witkowiak + 10,11.03.2000 - updates +*/ + +#ifndef _GGRAPH_H +#define _GGRAPH_H + +#ifndef _GSTRUCT_H +#include +#endif + +void __fastcall__ SetPattern(char newpattern); + +void __fastcall__ HorizontalLine(char pattern, char y, int xstart, int xend); +void __fastcall__ InvertLine(char y, int xstart, int xend); +void __fastcall__ RecoverLine(char y, int xstart, int xend); +void __fastcall__ VerticalLine(char pattern, char ystart, char yend, int x); + +void __fastcall__ InitDrawWindow(struct window *myRectangle); +void __fastcall__ Rectangle(void); +void __fastcall__ FrameRectangle(char pattern); +void __fastcall__ InvertRectangle(void); +void __fastcall__ ImprintRectangle(void); +void __fastcall__ RecoverRectangle(void); + +void __fastcall__ DrawLine(struct window *topBotCoords); + +void __fastcall__ DrawPoint(struct pixel *myPixel); +char __fastcall__ TestPoint(struct pixel *myPixel); + +void __fastcall__ PutChar(char character, char y, int x); +void __fastcall__ PutString(char *myString, char y, int x); +void __fastcall__ PutDecimal(char style, int value, char y, int x); + +char __fastcall__ GetCharWidth(char character); +void __fastcall__ LoadCharSet(struct fontdesc *myFont); +void __fastcall__ UseSystemFont(void); + +void __fastcall__ BitmapUp(struct iconpic *myIcon); +void __fastcall__ BitmapClip(char skipl, char skipr, int skiptop, + struct iconpic *myIcon); +void __fastcall__ BitOtherClip(void *proc1, void *proc2, char skipl, + char skipr, int skiptop, + struct iconpic *myIcon); + +void __fastcall__ GraphicsString(char *myGfxString); + +/* VIC colour constants */ +#define BLACK 0 +#define WHITE 1 +#define RED 2 +#define CYAN 3 +#define PURPLE 4 +#define GREEN 5 +#define BLUE 6 +#define YELLOW 7 +#define ORANGE 8 +#define BROWN 9 +#define LTRED 10 +#define DKGREY 11 +#define GREY 12 +#define MEDGREY 12 +#define LTGREEN 13 +#define LTBLUE 14 +#define LTGREY 15 +/* VIC memory banks */ +#define GRBANK0 3 +#define GRBANK1 2 +#define GRBANK2 1 +#define GRBANK3 0 +/* VIC screen sizes */ +#define VIC_X_POS_OFF 24 +#define VIC_Y_POS_OFF 50 +#define SC_BYTE_WIDTH 40 +#define SC_PIX_HEIGHT 200 +#define SC_PIX_WIDTH 320 +#define SC_SIZE 8000 +/* VDC screen constants */ +#define SCREENBYTEWIDTH 80 +#define SCREENPIXELWIDTH 640 +/* control characters for use as numbers, not chars */ +#define EOF 0 +#define BACKSPACE 8 +#define FORWARDSPACE 9 +#define TAB 9 +#define LF 10 +#define HOME 11 +#define PAGE_BREAK 12 +#define UPLINE 12 +#define CR 13 +#define ULINEON 14 +#define ULINEOFF 15 +#define ESC_GRAPHICS 16 +#define ESC_RULER 17 +#define REV_ON 18 +#define REV_OFF 19 +#define GOTOX 20 +#define GOTOY 21 +#define GOTOXY 22 +#define NEWCARDSET 23 +#define BOLDON 24 +#define ITALICON 25 +#define OUTLINEON 26 +#define PLAINTEXT 27 +/* control characters for use in + strings: eg: str[10]=BOLD "Hello"; */ +#define CCR "\015" +#define CULINEON "\016" +#define CULINEOFF "\017" +#define CREV_ON "\022" +#define CREV_OFF "\023" +#define CBOLDON "\030" +#define CITALICON "\031" +#define COUTLINEON "\032" +#define CPLAINTEXT "\033" + +/*values of currentMode */ +/* bitNumbers */ +#define UNDERLINE_BIT 7 +#define BOLD_BIT 6 +#define REVERSE_BIT 5 +#define ITALIC_BIT 4 +#define OUTLINE_BIT 3 +#define SUPERSCRIPT_BIT 2 +#define SUBSCRIPT_BIT 1 +/* bitMasks */ +#define SET_UNDERLINE 0x80 +#define SET_BOLD 0x40 +#define SET_REVERSE 0x20 +#define SET_ITALIC 0x10 +#define SET_OUTLINE 0x08 +#define SET_SUPERSCRIPT 0x04 +#define SET_SUBSCRIPT 0x02 +#define SET_PLAINTEXT 0 +/* values of dispBufferOn */ +#define ST_WRGS_FORE 0x20 +#define ST_WR_BACK 0x40 +#define ST_WR_FORE 0x80 +/* PutDecimal parameters */ +/* leading 0s? */ +#define SET_NOSURPRESS 0 +#define SET_SURPRESS 0x40 +/* justification */ +#define SET_RIGHTJUST 0 +#define SET_LEFTJUST 0x80 +/* C128 x flags */ +#define ADD1_W 0x2000 +#define DOUBLE_B 0x80 +#define DOUBLE_W 0x8000 + +typedef void graphicStr; + +#define MOVEPENTO(x,y) (char)1, (unsigned)(x), (char)(y) +#define LINETO(x,y) (char)2, (unsigned)(x), (char)(y) +#define RECTANGLETO(x,y) (char)3, (unsigned)(x), (char)(y) +#define NEWPATTERN(p) (char)5, (char)(p) +#define FRAME_RECTO(x,y) (char)7, (unsigned)(x), (char)(y) +#define PEN_X_DELTA(x) (char)8, (unsigned)(x) +#define PEN_Y_DELTA(y) (char)9, (char)(y) +#define PEN_XY_DELTA(x,y) (char)10, (unsigned)(x), (char)(y) +#define GSTR_END (char)NULL +/* ESC_PUTSTRING can't be implemented - it needs text, not pointer to it + #define ESC_PUTSTRING(x,y,text) (char)6, (unsigned)(x), (char)(y), (text), (char)NULL +*/ + +#endif diff --git a/include/geos/gmemory.h b/include/geos/gmemory.h new file mode 100644 index 000000000..fb60d6ce4 --- /dev/null +++ b/include/geos/gmemory.h @@ -0,0 +1,33 @@ +/* + GEOS memory and string functions + + ported to small C on 27.10.1999 + by Maciej 'YTM/Alliance' Witkowiak +*/ + +#ifndef _GMEMORY_H +#define _GMEMORY_H + +#ifndef _GSTRUCT_H +#include +#endif + +void __fastcall__ CopyString(char *dest, char *source); +void __fastcall__ CmpString(char *dest, char *source); +void __fastcall__ CopyFString(char len, char *dest, char *source); +void __fastcall__ CmpFString(char len, char *dest, char *source); + +int __fastcall__ CRC(char *buffer, int len); +void __fastcall__ ClearRam(char *dest, int len); +void __fastcall__ FillRam(char what, char *dest, int len); + +void __fastcall__ MoveData(char *source, char *dest, int len); + +void __fastcall__ InitRam(char *myInitTab); + +void __fastcall__ StashRAM(char REUBank, int len, char *reuaddy, char *cpuaddy); +void __fastcall__ FetchRAM(char REUBank, int len, char *reuaddy, char *cpuaddy); +void __fastcall__ SwapRAM(char REUBank, int len, char *reuaddy, char *cpuaddy); +char __fastcall__ VerifyRAM(char REUBank, int len, char *reuaddy, char *cpuaddy); + +#endif diff --git a/include/geos/gmenu.h b/include/geos/gmenu.h new file mode 100644 index 000000000..a53c13f36 --- /dev/null +++ b/include/geos/gmenu.h @@ -0,0 +1,56 @@ +/* + GEOS menu and icon functions + + ported to small C on 27.10.1999 + by Maciej 'YTM/Alliance' Witkowiak +*/ + +#ifndef _GMENU_H +#define _GMENU_H + +#ifndef _GSTRUCT_H +#include +#endif + +void __fastcall__ DoMenu(struct menu *myMenu); +void __fastcall__ ReDoMenu(void); +void __fastcall__ RecoverMenu(void); +void __fastcall__ RecoverAllMenus(void); +void __fastcall__ DoPreviousMenu(void); +void __fastcall__ GotoFirstMenu(void); + +void __fastcall__ DoIcons(struct icontab *myIconTab); + +/* DoMenu - menutypes */ +#define MENU_ACTION 0x00 +#define DYN_SUB_MENU 0x40 +#define SUB_MENU 0x80 +#define HORIZONTAL 0x00 +#define VERTICAL 0x80 +/* menu string offsets */ +#define OFF_MY_TOP 0 +#define OFF_MY_BOT 1 +#define OFF_MX_LEFT 2 +#define OFF_MX_RIGHT 4 +#define OFF_NUM_M_ITEMS 6 +#define OFF_1ST_M_ITEM 7 +/* icon string offsets */ +#define OFF_NM_ICNS 0 +#define OFF_IC_XMOUSE 1 +#define OFF_IC_YMOUSE 3 +#define OFF_PIC_ICON 0 +#define OFF_X_ICON_POS 2 +#define OFF_Y_ICON_POS 3 +#define OFF_WDTH_ICON 4 +#define OFF_HEIGHT_ICON 5 +#define OFF_SRV_RT_ICON 6 +#define OFF_NX_ICON 8 +/* icons, menus status flags */ +#define ST_FLASH 0x80 +#define ST_INVERT 0x40 +#define ST_LD_AT_ADDR 0x01 +#define ST_LD_DATA 0x80 +#define ST_PR_DATA 0x40 +#define ST_WR_PR 0x40 + +#endif diff --git a/include/geos/gprocess.h b/include/geos/gprocess.h new file mode 100644 index 000000000..c1cc5d719 --- /dev/null +++ b/include/geos/gprocess.h @@ -0,0 +1,41 @@ +/* + GEOS processes (~multitasking) functions + + ported to small C on 27.10.1999 + by Maciej 'YTM/Alliance' Witkowiak +*/ + +#ifndef _GPROCESS_H +#define _GPROCESS_H + +#ifndef _GSTRUCT_H +#include +#endif + +void __fastcall__ InitProcesses(char number, struct process *proctab); +void __fastcall__ RestartProcess(char number); +void __fastcall__ EnableProcess(char number); +void __fastcall__ BlockProcess(char number); +void __fastcall__ UnBlockProcess(char number); +void __fastcall__ FreezeProcess(char number); +void __fastcall__ UnFreezeProcess(char number); + +void __fastcall__ Sleep(int jiffies); + +/* Process control variable + these probably should be removed from here, as they are + internal GEOS information which is updated by functions above +*/ + +/* bit numbers */ +#define RUNABLE_BIT 7 +#define BLOCKED_BIT 6 +#define FROZEN_BIT 5 +#define NOTIMER_BIT 4 +/* bit masks */ +#define SET_RUNABLE 0x80 +#define SET_BLOCKED 0x40 +#define SET_FROZEN 0x20 +#define SET_NOTIMER 0x10 + +#endif diff --git a/include/geos/gsprite.h b/include/geos/gsprite.h new file mode 100644 index 000000000..6f7fc2803 --- /dev/null +++ b/include/geos/gsprite.h @@ -0,0 +1,90 @@ +/* + GEOS mouse and sprite functions + + ported to small C on 27.10.1999 + by Maciej 'YTM/Alliance' Witkowiak +*/ + +#ifndef _GSPRITE_H +#define _GSPRITE_H + +void __fastcall__ StartMouseMode(void); +void __fastcall__ ClearMouseMode(void); +void __fastcall__ MouseUp(void); +void __fastcall__ MouseOff(void); +char __fastcall__ IsMseInRegion(struct window *region); + +void __fastcall__ DrawSprite(char spritenum, char *spritepic); +void __fastcall__ PosSprite(char spritenum, struct pixel *position); +void __fastcall__ EnablSprite(char spritenum); +void __fastcall__ DisablSprite(char spritenum); + +void __fastcall__ InitTextPrompt(char height); +void __fastcall__ PromptOn(struct pixel *position); +void __fastcall__ PromptOff(void); +char __fastcall__ GetNextChar(void); + +/* keyboard constants */ +#define KEY_F1 1 +#define KEY_F2 2 +#define KEY_F3 3 +#define KEY_F4 4 +#define KEY_F5 5 +#define KEY_F6 6 +#define KEY_NOSCRL 7 +#define KEY_ENTER 11 +#define KEY_F7 14 +#define KEY_F8 15 +#define KEY_UP 16 +#define KEY_DOWN 17 +#define KEY_HOME 18 +#define KEY_CLEAR 19 +#define KEY_LARROW 20 +#define KEY_UPARROR 21 +#define KEY_STOP 22 +#define KEY_RUN 23 +#define KEY_BPS 24 +#define KEY_HELP 25 +#define KEY_ALT 26 +#define KEY_ESC 27 +#define KEY_INSERT 28 +#define KEY_DELETE 29 +#define KEY_RIGHT 30 +#define KEY_INVALID 31 +#define KEY_LEFT BACKSPACE + +/* values of faultData - pointer position vs. mouseWindow */ +/* bit numbers */ +#define OFFTOP_BIT 7 +#define OFFBOTTOM_BIT 6 +#define OFFLEFT_BIT 5 +#define OFFRIGHT_BIT 4 +#define OFFMENU_BIT 3 +/* bit masks */ +#define SET_OFFTOP 0x80 +#define SET_OFFBOTTOM 0x40 +#define SET_OFFLEFT 0x20 +#define SET_OFFRIGHT 0x10 +#define SET_OFFMENU 0x08 + +/* mouseOn */ +/* bit numbers */ +#define MOUSEON_BIT 7 +#define MENUON_BIT 6 +#define ICONSON_BIT 5 +/* bit masks */ +#define SET_MSE_ON 0x80 +#define SET_MENUON 0x40 +#define SET_ICONSON 0x20 + +/* pressFlag */ +/* bit numbers */ +#define KEYPRESS_BIT 7 +#define INPUT_BIT 6 +#define MOUSE_BIT 5 +/* bit masks */ +#define SET_KEYPRESS 0x80 +#define SET_INPUTCHG 0x40 +#define SET_MOUSE 0x20 + +#endif diff --git a/include/geos/gstruct.h b/include/geos/gstruct.h new file mode 100644 index 000000000..9c79d29d3 --- /dev/null +++ b/include/geos/gstruct.h @@ -0,0 +1,136 @@ +/* + GEOS structs + + ported to small C on 25-27.10.1999 + by Maciej 'YTM/Alliance' Witkowiak +*/ + +#ifndef _GSTRUCT_H +#define _GSTRUCT_H + +struct f_date { /* date in filedesctiptor */ + char f_year; + char f_month; + char f_day; + char f_hour; + char f_minute; +}; + +struct s_date { /* system date & time */ + char s_year; + char s_month; + char s_day; + char s_hour; + char s_minutes; + char s_seconds; +}; + +struct tr_se { /* track and sector */ + char track; + char sector; +}; + +struct fileheader { /* header block (like fileHeader) */ + struct tr_se n_block; + char icon_desc[3]; + char icon_pic[63]; + char dostype; + char type; + char structure; + int load_address; + int end_address; + int exec_address; + char class_name[19]; + char column_flag; + char author[64]; + char note[95]; +}; + +struct filehandle { /* filehandle in directory sectors */ + char dostype; /* or in dirEntryBuf */ + struct tr_se n_block; + char name[16]; + struct tr_se header; + char structure; + char type; + struct f_date date; + int size; +}; + +struct pixel { /* describes point */ + int x; + char y; +}; + +struct fontdesc { /* describes font */ + char baseline; + char width; + char height; + char *index_tbl; + char *data_ptr; +}; + +struct window { /* describes screen region */ + char top; + char bot; + int left; + int right; +}; + +struct VLIR_info { /* VLIR information */ + char curRecord; /* currently only used in VLIR */ + char usedRecords; /* as system info (curRecord is mainly of your interest */ + char fileWritten; + int fileSize; +}; + +struct process { /* process info, declare table of that type */ + int pointer; /* (like: struct process proctab[2]=... */ + int jiffies; /* last entry HAVE TO BE {0,0} */ +}; + + +struct iconpic { /* icon/encoded bitmap description */ + char *pic_ptr; /* ptr to a photo scrap (or encoded bitmap) */ + char x; /* position in cards (*8 pixels) */ + char y; + char width; /* in cards */ + char heigth; /* in lines (pixels) */ +}; + +struct icondef { /* icon definition for DoIcons */ + char *pic_ptr; /* ptr to a photo scrap (or encoded bitmap) */ + char x; /* position in cards (*8 pixels) */ + char y; + char width; /* of icon (in cards) */ + char heigth; /* of icon in lines (pixels) */ + int proc_ptr; /* pointer to function handling that icon */ +}; + +struct icontab { + char number; /* number of declared icons */ + struct pixel mousepos; /* position of mouse after DoIcons */ + struct icondef tab[]; /* table of size declared by icontab.number */ +}; + +/* everything below is obsolete and kept for unknown reasons */ + +struct menuitem { + char *name; + char type; + int rest; /* may be ptr to function, or if submenu ptr to struct menu */ +}; + +struct menu { + struct window size; + char number; + struct menuitem items[]; +}; + +struct inittab { /* use struct inittab mytab[n] for initram */ + int ptr; /* ptr to 1st byte */ + char number; /* number of following bytes */ + char values[]; /* warning - in table size of this is same for all! */ +}; + +#endif diff --git a/include/geos/gsym.h b/include/geos/gsym.h new file mode 100644 index 000000000..6dc7c5135 --- /dev/null +++ b/include/geos/gsym.h @@ -0,0 +1,302 @@ +/* + GEOS constants reassembled 4-2-99 + ported to small C 26.8.99, 25-26.10.99 + Maciej 'YTM/Alliance' Witkowiak + ytm@friko.onet.pl +*/ + +#ifndef _GSYM_H +#define _GSYM_H + +#ifndef _GSTRUCT_H +#include +#endif + +#define nameBuf char[17] +#define blockBuf char[256] + +#define zpage *(char*)0x0000 + +#define CPU_DDR *(char*)0x00 +#define CPU_DATA *(char*)0x01 + +#define r0 *(unsigned int*)0x02 +#define r0L *(char*)0x02 +#define r0H *(char*)0x03 +#define r1 *(unsigned int*)0x04 +#define r1L *(char*)0x04 +#define r1H *(char*)0x05 +#define drawWindow (*(struct window*)0x06) +#define r2 *(unsigned int*)0x06 +#define r2L *(char*)0x06 +#define r2H *(char*)0x07 +#define r3 *(unsigned int*)0x08 +#define r3L *(char*)0x08 +#define r3H *(char*)0x09 +#define r4 *(unsigned int*)0x0a +#define r4L *(char*)0x0a +#define r4H *(char*)0x0b +#define r5 *(unsigned int*)0x0c +#define r5L *(char*)0x0c +#define r5H *(char*)0x0d +#define r6 *(unsigned int*)0x0e +#define r6L *(char*)0x0e +#define r6H *(char*)0x0f +#define r7 *(unsigned int*)0x10 +#define r7L *(char*)0x10 +#define r7H *(char*)0x11 +#define r8 *(unsigned int*)0x12 +#define r8L *(char*)0x12 +#define r8H *(char*)0x13 +#define r9 *(unsigned int*)0x14 +#define r9L *(char*)0x14 +#define r9H *(char*)0x15 +#define r10 *(unsigned int*)0x16 +#define r10L *(char*)0x16 +#define r10H *(char*)0x17 +#define r11 *(unsigned int*)0x18 +#define r11L *(char*)0x18 +#define r11H *(char*)0x19 +#define r12 *(unsigned int*)0x1a +#define r12L *(char*)0x1a +#define r12H *(char*)0x1b +#define r13 *(unsigned int*)0x1c +#define r13L *(char*)0x1c +#define r13H *(char*)0x1d +#define r14 *(unsigned int*)0x1e +#define r14L *(char*)0x1e +#define r14H *(char*)0x1f +#define r15 *(unsigned int*)0x20 +#define r15L *(char*)0x20 +#define r15H *(char*)0x21 +/* WARNING - these are used by C as temporary registers! */ +#define a0 *(unsigned int*)0xfb +#define a0L *(char*)0xfb +#define a0H *(char*)0xfc +#define a1 *(unsigned int*)0xfd +#define a1L *(char*)0xfd +#define a1H *(char*)0xfe +#define a2 *(unsigned int*)0x70 +#define a2L *(char*)0x70 +#define a2H *(char*)0x71 +#define a3 *(unsigned int*)0x72 +#define a3L *(char*)0x72 +#define a3H *(char*)0x73 +#define a4 *(unsigned int*)0x74 +#define a4L *(char*)0x74 +#define a4H *(char*)0x75 +#define a5 *(unsigned int*)0x76 +#define a5L *(char*)0x76 +#define a5H *(char*)0x77 +#define a6 *(unsigned int*)0x78 +#define a6L *(char*)0x78 +#define a6H *(char*)0x79 +#define a7 *(unsigned int*)0x7a +#define a7L *(char*)0x7a +#define a7H *(char*)0x7b +#define a8 *(unsigned int*)0x7c +#define a8L *(char*)0x7c +#define a8H *(char*)0x7d +#define a9 *(unsigned int*)0x7e +#define a9L *(char*)0x7e +#define a9H *(char*)0x7f + +#define curPattern *(unsigned int*)0x22 +#define string *(unsigned int*)0x24 +#define curFontDesc (*(struct fontdesc*)0x26) +#define currentMode *(char*)0x2e +#define dispBufferOn *(char*)0x2f +#define mouseOn *(char*)0x30 +#define RAM_64K *(char*)0x30 +#define msePicPtr *(unsigned int*)0x31 +#define curWindow (*(struct window*)0x33) +/*#define IO_IN *(char*)0x35 + #define KRNL_IO_IN *(char*)0x36 + #define KRNL_BAS_IO_IN *(char*)0x37*/ +#define pressFlag *(char*)0x39 +#define mousePos (*(struct pixel*)0x3a) +#define returnAddress *(unsigned int*)0x3d +#define graphMode *(char*)0x3f +/*#define TURBO_DD00 *(char*)0x8e + #define TURBO_DD00_CPY *(char*)0x8f*/ +#define STATUS *(char*)0x90 +#define curDevice *(char*)0xba + +/* Here's my own errno location, I hope this won't confilct with anything... */ +#define errno *(char*)0x91 + +#define irqvec *(unsigned int*)0x0314 +#define bkvec *(unsigned int*)0x0316 +#define nmivec *(unsigned int*)0x0318 + +#define APP_RAM *(char*)0x0400 +#define BACK_SCR_BASE *(char*)0x6000 +#define PRINTBASE *(char*)0x7900 +#define OS_VARS *(char*)0x8000 + +#define diskBlkBuf ((blockBuf)0x8000) +#define fileHeader (*(struct fileheader*)0x8100) +#define curDirHead ((blockBuf)0x8200) +#define fileTrScTab ((struct tr_se[128])0x8300) +#define dirEntryBuf (*(struct filehandle*)0x8400) + +#define DrACurDkNm ((nameBuf)0x841e) +#define DrBCurDkNm ((nameBuf)0x8430) +#define dataFileName ((nameBuf)0x8442) +#define dataDiskName ((nameBuf)0x8453) +#define PrntFileName ((nameBuf)0x8465) +#define PrntDiskName ((nameBuf)0x8476) + +#define curDrive *(char*)0x8489 +#define diskOpenFlg *(char*)0x848a +#define isGEOS *(char*)0x848b +#define interleave *(char*)0x848c +#define NUMDRV *(char*)0x848d + +#define driveType ((char[4])0x848e) +#define turboFlags ((char[4])0x8492) + +#define VLIRInfo (*(struct VLIR_info*)0x8496) + +#define appMain *(unsigned int*)0x849b +#define intTopVector *(unsigned int*)0x849d +#define intBotVector *(unsigned int*)0x849f +#define mouseVector *(unsigned int*)0x84a1 +#define keyVector *(unsigned int*)0x84a3 +#define inputVector *(unsigned int*)0x84a5 +#define mouseFaultVec *(unsigned int*)0x84a7 +#define otherPressVec *(unsigned int*)0x84a9 +#define StringFaultVec *(unsigned int*)0x84ab +#define alarmTmtVector *(unsigned int*)0x84ad +#define BRKVector *(unsigned int*)0x84af +#define RecoverVector *(unsigned int*)0x84b1 +#define selectionFlash *(char*)0x84b3 +#define alphaFlag *(char*)0x84b4 +#define iconSelFlg *(char*)0x84b5 +#define faultData *(char*)0x84b6 +#define menuNumber *(char*)0x84b7 +#define mouseWindow (*(struct window*)0x84b8) +#define stringXY (*(struct pixel*)0x84be) +#define mousePicData *(char*)0x84c1 + +#define maxMouseSpeed *(char*)0x8501 +#define minMouseSpeed *(char*)0x8502 +#define mouseAccel *(char*)0x8503 +#define keyData *(char*)0x8504 +#define mouseData *(char*)0x8505 +#define inputData *(char*)0x8506 +#define mouseSpeed *(char*)0x8507 +#define random *(char*)0x850a +#define saveFontTab (*(struct fontdesc*)0x850c) + +#define dblClickCount *(char*)0x8515 +#define system_date (*(struct s_date*)0x8516) +#define alarmSetFlag *(char*)0x851c +#define sysDBData *(char*)0x851d +#define screencolors *(char*)0x851e +#define dlgBoxRamBuf *(char*)0x851f + +#define savedmoby2 *(char*)0x88bb +#define scr80polar *(char*)0x88bc +#define scr80colors *(char*)0x88bd +#define vdcClrMode *(char*)0x88be +#define driveData ((char[4])0x88bf) +#define ramExpSize *(char*)0x88c3 +#define sysRAMFlg *(char*)0x88c4 +#define firstBoot *(char*)0x88c5 +#define curType *(char*)0x88c6 +#define ramBase *(char*)0x88c7 +/*Original: + #define inputDevName *(char*)0x88cb + #define memBase *(char*)0x88cf*/ +#define inputDevName ((nameBuf)0x88cb) +#define DrCCurDkNm ((nameBuf)0x88dc) +#define DrDCurDkNm ((nameBuf)0x88ee) +#define dir2Head ((blockBuf)0x8900) +#define SPRITE_PICS *(char*)0x8a00 +#define sprpic ((char[8][64])0x8a00) +#define COLOR_MATRIX ((char[1000])0x8c00) +#define objPointer ((char[8])0x8ff8) + +#define DISK_BASE *(char*)0x9000 +#define SCREEN_BASE *(char*)0xa000 +#define OS_ROM *(char*)0xc000 +#define OS_JUMPTAB *(char*)0xc100 +#define RAMC_BASE *(char*)0xde00 +#define RAMC_WINDOW *(char*)0xdf00 +#define EXP_BASE *(char*)0xdf00 +#define MOUSE_BASE_128 *(char*)0xfd00 +#define MOUSE_JMP_128 *(char*)0xfd00 +#define END_MOUSE_128 *(char*)0xfe80 +#define MOUSE_BASE *(char*)0xfe80 +#define MOUSE_JMP *(char*)0xfe80 + +#define config *(char*)0xff00 +#define END_MOUSE *(char*)0xfffa +#define NMI_VECTOR *(unsigned int*)0xfffa +#define RESET_VECTOR *(unsigned int*)0xfffc +#define IRQ_VECTOR *(unsigned int*)0xfffe + +#define vicbase *(char*)0xd000 +#define sidbase *(char*)0xd400 +#define mmu *(char*)0xd500 +#define VDC *(char*)0xd600 +#define ctab *(char*)0xd800 +#define cia1base *(char*)0xdc00 +#define cia2base *(char*)0xdd00 + +#define mob0xpos *(char*)0xd000 +#define mob0ypos *(char*)0xd001 +#define mob1xpos *(char*)0xd002 +#define mob1ypos *(char*)0xd003 +#define mob2xpos *(char*)0xd004 +#define mob2ypos *(char*)0xd005 +#define mob3xpos *(char*)0xd006 +#define mob3ypos *(char*)0xd007 +#define mob4xpos *(char*)0xd008 +#define mob4ypos *(char*)0xd009 +#define mob5xpos *(char*)0xd00a +#define mob5ypos *(char*)0xd00b +#define mob6xpos *(char*)0xd00c +#define mob6ypos *(char*)0xd00d +#define mob7xpos *(char*)0xd00e +#define mob7ypos *(char*)0xd00f +#define msbxpos *(char*)0xd010 +#define grcntrl1 *(char*)0xd011 +#define rasreg *(char*)0xd012 +#define lpxpos *(char*)0xd013 +#define lpypos *(char*)0xd014 +#define mobenble *(char*)0xd015 +#define grcntrl2 *(char*)0xd016 +#define grmemptr *(char*)0xd018 +#define grirq *(char*)0xd019 +#define grirqen *(char*)0xd01a +#define moby2 *(char*)0xd017 +#define mobprior *(char*)0xd01b +#define mobmcm *(char*)0xd01c +#define mobx2 *(char*)0xd01d +#define mobmobcol *(char*)0xd01e +#define mobbakcol *(char*)0xd01f +#define extclr *(char*)0xd020 +#define bakclr0 *(char*)0xd021 +#define bakclr1 *(char*)0xd022 +#define bakclr2 *(char*)0xd023 +#define bakclr3 *(char*)0xd024 +#define mcmclr0 *(char*)0xd025 +#define mcmclr1 *(char*)0xd026 +#define mob0clr *(char*)0xd027 +#define mob1clr *(char*)0xd028 +#define mob2clr *(char*)0xd029 +#define mob3clr *(char*)0xd02a +#define mob4clr *(char*)0xd02b +#define mob5clr *(char*)0xd02c +#define mob6clr *(char*)0xd02d +#define mob7clr *(char*)0xd02e +#define keyreg *(char*)0xd02f +#define clkreg *(char*)0xd030 + +#define vdcreg *(char*)0xd600 +#define vdcdata *(char*)0xd601 + +#endif + diff --git a/include/geos/gsys.h b/include/geos/gsys.h new file mode 100644 index 000000000..028bf9c80 --- /dev/null +++ b/include/geos/gsys.h @@ -0,0 +1,26 @@ +/* + GEOS system functions + + ported to small C on 27.10.1999 + by Maciej 'YTM/Alliance' Witkowiak +*/ + +#ifndef _GSYS_H +#define _GSYS_H + +void __fastcall__ FirstInit(void); +void __fastcall__ InitForIO(void); +void __fastcall__ DoneWithIO(void); +void __fastcall__ MainLoop(void); +void __fastcall__ EnterDeskTop(void); +void __fastcall__ ToBASIC(void); +void __fastcall__ Panic(void); + +void __fastcall__ CallRoutine(void *myRoutine); + +int __fastcall__ GetSerialNumber(void); +char __fastcall__ GetRandom(void); + +void __fastcall__ SetDevice(char newdev); + +#endif diff --git a/include/iso646.h b/include/iso646.h new file mode 100644 index 000000000..a3737b9de --- /dev/null +++ b/include/iso646.h @@ -0,0 +1,34 @@ +/* + * iso646.h + * + * Ullrich von Bassewitz, 11.12.1998 + * + */ + + + +#ifndef _ISO646_H +#define _ISO646_H + + + +/* Operator tokens */ +#define and && +#define and_eq &= +#define bitand & +#define bitor | +#define compl ~ +#define not ! +#define not_eq != +#define or || +#define or_eq |= +#define xor ^ +#define xor_eq ^= + + + +/* End of iso646.h */ +#endif + + + diff --git a/include/joystick.h b/include/joystick.h new file mode 100644 index 000000000..44dab000f --- /dev/null +++ b/include/joystick.h @@ -0,0 +1,66 @@ +/* + * joystick.h + * + * Ullrich von Bassewitz, 24.09.1998 + * + * Read the joystick on systems that support it. + * + */ + + + +#ifndef _JOYSTICK_H +#define _JOYSTICK_H + + + +/* Define __JOYSTICK__ for systems that support a joystick */ +#ifdef __C64__ +# define __JOYSTICK__ +#endif +#ifdef __C128__ +# define __JOYSTICK__ +#endif +#ifdef __PLUS4__ +# define __JOYSTICK__ +#endif +#ifdef __NES__ +# define __JOYSTICK__ +#endif + +/* Argument for the function */ +#define JOY_1 0 +#define JOY_2 1 + +/* Result codes of the function. The actual code is a bitwise or + * of one or more of the following values. + */ +#ifdef __NES__ +# define JOY_A 0x01 +# define JOY_B 0x02 +# define JOY_SELECT 0x04 +# define JOY_START 0x08 +# define JOY_UP 0x10 +# define JOY_DOWN 0x20 +# define JOY_LEFT 0x40 +# define JOY_RIGHT 0x80 +#else +# define JOY_UP 0x01 +# define JOY_DOWN 0x02 +# define JOY_LEFT 0x04 +# define JOY_RIGHT 0x08 +# define JOY_FIRE 0x10 +#endif + + + +unsigned __fastcall__ readjoy (unsigned char joy); +/* Read the joystick. The argument is one of JOY_1/JOY2 */ + + + +/* End of joystick.h */ +#endif + + + diff --git a/include/limits.h b/include/limits.h new file mode 100644 index 000000000..6b28da4e5 --- /dev/null +++ b/include/limits.h @@ -0,0 +1,46 @@ +/* + * limits.h + * + * Ullrich von Bassewitz, 04.06.1998 + * + */ + + + +#ifndef _LIMITS_H +#define _LIMITS_H + + + +#define CHAR_BIT 8 + +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +#define UCHAR_MAX 255 + +#define CHAR_MIN 0 +#define CHAR_MAX 255 + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +#define USHRT_MAX 65535U + +#define INT_MIN (-32768) +#define INT_MAX 32767 + +#define UINT_MAX 65535U + +#define LONG_MAX 2147483647L +#define LONG_MIN (-2147483648L) + +#define ULONG_MAX 4294967295UL + + + +/* End of limits.h */ +#endif + + + diff --git a/include/locale.h b/include/locale.h new file mode 100644 index 000000000..c0732c15d --- /dev/null +++ b/include/locale.h @@ -0,0 +1,61 @@ +/* + * locale.h + * + * Ullrich von Bassewitz, 11.12.1998 + * + */ + + + +#ifndef _LOCALE_H +#define _LOCALE_H + + + +/* NULL pointer */ +#ifdef NULL +# undef NULL +#endif +#define NULL 0 + +/* Locale information constants */ +#define LC_ALL 0 +#define LC_COLLATE 1 +#define LC_CTYPE 2 +#define LC_MONETARY 3 +#define LC_NUMERIC 4 +#define LC_TIME 5 + +/* Struct containing locale settings */ +struct lconv { + char* currency_symbol; + char* decimal_point; + char* grouping; + char* int_curr_symbol; + char* mon_decimal_point; + char* mon_grouping; + char* mon_thousands_sep; + char* negative_sign; + char* positive_sign; + char* thousands_sep; + char frac_digits; + char int_frac_digits; + char n_cs_precedes; + char n_sep_by_space; + char n_sign_posn; + char p_cs_precedes; + char p_sep_by_space; + char p_sign_posn; +}; + +/* Function prototypes */ +struct lconv* localeconv (void); +char* setlocale (int category, const char* locale); + + + +/* End of locale.h */ +#endif + + + diff --git a/include/mouse.h b/include/mouse.h new file mode 100644 index 000000000..6af499bae --- /dev/null +++ b/include/mouse.h @@ -0,0 +1,88 @@ +/* + * mouse.h + * + * Ullrich von Bassewitz, 24.04.1999 + */ + + + +#ifndef _MOUSE_H +#define _MOUSE_H + + + +/* Define __MOUSE__ for systems that support a proportional mouse */ +#ifdef __C64__ +# define __MOUSE__ +#endif +#ifdef __C128__ +# define __MOUSE__ +#endif + + + +void __fastcall__ mouse_init (unsigned char port, unsigned char sprite); +/* Setup the mouse interrupt handler. If the sprite value is != zero, the + * mouse routines will manage the sprite with this number. That means, it + * is moved if the mouse is moved (provided that the mouse cursor is visible), + * and switch on and off in the show and hide functions. + * The port parameter gives the joystick port used for the mouse and is only + * needed to read the mouse button state. + * After calling this function, the mouse is invisble, the cursor is placed + * at 0/0 (upper left corner), and the bounding box is reset to cover the + * whole screen. Call mouse_show once to make the mouse cursor visible. + */ + +void mouse_done (void); +/* Disable the mouse, remove the interrupt handler. This function MUST be + * called before terminating the program, otherwise odd things may happen. + * If in doubt, install an exit handler (using atexit) that calls this + * function. + */ + +void mouse_hide (void); +/* Hide the mouse. This function doesn't do anything visible if no sprite is + * used. The function manages a counter and may be called more than once. + * For each call to mouse_hide there must be a call to mouse_show to make + * the mouse visible again. + */ + +void mouse_show (void); +/* Show the mouse. This function doesn't do anything visible if no sprite is + * used. See mouse_hide for more information. + */ + +void __fastcall__ mouse_box (int minx, int miny, int maxx, int maxy); +/* Set the bounding box for the mouse pointer movement. The mouse X and Y + * coordinates will never go outside the given box. + * NOTE: The function does *not* check if the mouse is currently inside the + * given margins. The proper way to use this function therefore is: + * + * - Hide the mouse + * - Set the bounding box + * - Place the mouse at the desired position + * - Show the mouse again. + * + * NOTE2: When setting the box to something that is larger than the actual + * screen, the positioning of the mouse cursor will fail. If such margins + * are really what you want, you have to use your own cursor routines. + */ + +void __fastcall__ mouse_move (int x, int y); +/* Set the mouse cursor to the given position. If a mouse cursor is defined + * and currently visible, the mouse cursor is also moved. + * NOTE: This function does not check if the given position is valid and + * inside the bounding box. + */ + +void mouse_info (void); +/* Hmmm... + */ + + + +/* End of mouse.h */ +#endif + + + diff --git a/include/pet.h b/include/pet.h new file mode 100644 index 000000000..22bc9b9be --- /dev/null +++ b/include/pet.h @@ -0,0 +1,24 @@ +/* + * pet.h + * + * Ullrich von Bassewitz, 26.11.1998 + */ + + + +#ifndef _PET_H +#define _PET_H + + + +/* Color defines */ +#define COLOR_BLACK 0x00 +#define COLOR_WHITE 0x01 + + + +/* End of pet.h */ +#endif + + + diff --git a/include/plus4.h b/include/plus4.h new file mode 100644 index 000000000..2a4dd1b6b --- /dev/null +++ b/include/plus4.h @@ -0,0 +1,81 @@ +/* + * plus4.h + * + * Ullrich von Bassewitz, 12.08.1998 + */ + + + +#ifndef _PLUS4_H +#define _PLUS4_H + + + +/* Additional key defines */ +#define CH_F1 133 +#define CH_F2 137 +#define CH_F3 134 +#define CH_F4 138 +#define CH_F5 135 +#define CH_F6 139 +#define CH_F7 136 +#define CH_F8 140 + + + +/* Color attributes */ +#define CATTR_LUMA0 0x00 +#define CATTR_LUMA1 0x10 +#define CATTR_LUMA2 0x20 +#define CATTR_LUMA3 0x30 +#define CATTR_LUMA4 0x40 +#define CATTR_LUMA5 0x50 +#define CATTR_LUMA6 0x60 +#define CATTR_LUMA7 0x70 +#define CATTR_BLINK 0x80 + +/* Base colors */ +#define BCOLOR_BLACK 0x00 +#define BCOLOR_WHITE 0x01 +#define BCOLOR_RED 0x02 +#define BCOLOR_CYAN 0x03 +#define BCOLOR_VIOLET 0x04 +#define BCOLOR_GREEN 0x05 +#define BCOLOR_BLUE 0x06 +#define BCOLOR_YELLOW 0x07 +#define BCOLOR_ORANGE 0x08 +#define BCOLOR_BROWN 0x09 +#define BCOLOR_LEMON 0x0A /* What's that color? */ +#define BCOLOR_LIGHTVIOLET 0x0B +#define BCOLOR_BLUEGREEN 0x0C +#define BCOLOR_LIGHTBLUE 0x0D +#define BCOLOR_DARKBLUE 0x0E +#define BCOLOR_LIGHTGREEN 0x0F + + + +/* Now try to mix up a C64/C128 compatible palette */ +#define COLOR_BLACK (BCOLOR_BLACK) +#define COLOR_WHITE (BCOLOR_WHITE | CATTR_LUMA7) +#define COLOR_RED (BCOLOR_RED | CATTR_LUMA4) +#define COLOR_CYAN (BCOLOR_CYAN | CATTR_LUMA7) +#define COLOR_VIOLET (BCOLOR_VIOLET | CATTR_LUMA7) +#define COLOR_GREEN (BCOLOR_GREEN | CATTR_LUMA7) +#define COLOR_BLUE (BCOLOR_BLUE | CATTR_LUMA7) +#define COLOR_YELLOW (BCOLOR_YELLOW | CATTR_LUMA7) +#define COLOR_ORANGE (BCOLOR_ORANGE | CATTR_LUMA7) +#define COLOR_BROWN (BCOLOR_BROWN | CATTR_LUMA7) +#define COLOR_LIGHTRED (BCOLOR_RED | CATTR_LUMA7) +#define COLOR_GRAY1 (BCOLOR_WHITE | CATTR_LUMA1) +#define COLOR_GRAY2 (BCOLOR_WHITE | CATTR_LUMA3) +#define COLOR_LIGHTGREEN (BCOLOR_LIGHTGREEN | CATTR_LUMA7) +#define COLOR_LIGHTBLUE (BCOLOR_LIGHTBLUE | CATTR_LUMA7) +#define COLOR_GRAY3 (BCOLOR_WHITE | CATTR_LUMA5) + + + +/* End of plus4.h */ +#endif + + + diff --git a/include/rs232.h b/include/rs232.h new file mode 100644 index 000000000..a54b4d756 --- /dev/null +++ b/include/rs232.h @@ -0,0 +1,125 @@ +/* + * rs232.h + * + * Ullrich von Bassewitz, 19.3.1999 + * + * This module is based upon the public domain swiftlink module written by + * Craig Bruce. Thanks a lot! + * + */ + + + +#ifndef _RS232_H +#define _RS232_h + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Baudrate settings */ +#define RS_BAUD_50 0x00 +#define RS_BAUD_110 0x01 +#define RS_BAUD_134_5 0x02 +#define RS_BAUD_300 0x03 +#define RS_BAUD_600 0x04 +#define RS_BAUD_1200 0x05 +#define RS_BAUD_2400 0x06 +#define RS_BAUD_4800 0x07 +#define RS_BAUD_9600 0x08 +#define RS_BAUD_19200 0x09 +#define RS_BAUD_38400 0x0A +#define RS_BAUD_57600 0x0B +#define RS_BAUD_115200 0x0C +#define RS_BAUD_230400 0x0D + +/* Stop bit settings */ +#define RS_STOP_1 0x00 +#define RS_STOP_2 0x80 + +/* Data bit settings */ +#define RS_BITS_5 0x60 +#define RS_BITS_6 0x40 +#define RS_BITS_7 0x20 +#define RS_BITS_8 0x00 + +/* Parity settings */ +#define RS_PAR_NONE 0x00 +#define RS_PAR_ODD 0x20 +#define RS_PAR_EVEN 0x60 +#define RS_PAR_MARK 0xA0 +#define RS_PAR_SPACE 0xE0 + +/* Bit masks to mask out things from the status returned by rs232_status */ +#define RS_STATUS_PE 0x01 /* Parity error */ +#define RS_STATUS_FE 0x02 /* Framing error */ +#define RS_STATUS_OVERRUN 0x04 /* Overrun error */ +#define RS_STATUS_RDRF 0x08 /* Receiver data register full */ +#define RS_STATUS_THRE 0x10 /* Transmit holding reg. empty */ +#define RS_STATUS_DCD 0x20 /* NOT data carrier detect */ +#define RS_STATUS_DSR 0x40 /* NOT data set ready */ +#define RS_STATUS_IRQ 0x80 /* IRQ condition */ + +/* Error codes returned by all functions */ +#define RS_ERR_OK 0x00 /* Not an error - relax */ +#define RS_ERR_NOT_INITIALIZED 0x01 /* Module not initialized */ +#define RS_ERR_BAUD_TOO_FAST 0x02 /* Cannot handle baud rate */ +#define RS_ERR_BAUD_NOT_AVAIL 0x03 /* Baud rate not available */ +#define RS_ERR_NO_DATA 0x04 /* Nothing to read */ +#define RS_ERR_OVERFLOW 0x05 /* No room in send buffer */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned char __fastcall__ rs232_init (char hacked); +/* Initialize the serial port, install the interrupt handler. The parameter + * must be true (non zero) for a hacked swiftlink and false (zero) otherwise. + */ + +unsigned char __fastcall__ rs232_params (unsigned char params, unsigned char parity); +/* Set the port parameters. Use a combination of the #defined values above. */ + +unsigned char __fastcall__ rs232_done (void); +/* Close the port, deinstall the interrupt hander. You MUST call this function + * before terminating the program, otherwise the machine may crash later. If + * in doubt, install an exit handler using atexit(). The function will do + * nothing, if it was already called. + */ + +unsigned char __fastcall__ rs232_get (char* b); +/* Get a character from the serial port. If no characters are available, the + * function will return RS_ERR_NO_DATA, so this is not a fatal error. + */ + +unsigned char __fastcall__ rs232_put (char b); +/* Send a character via the serial port. There is a transmit buffer, but + * transmitting is not done via interrupt. The function returns + * RS_ERR_OVERFLOW if there is no space left in the transmit buffer. + */ + +unsigned char __fastcall__ rs232_pause (void); +/* Assert flow control and disable interrupts. */ + +unsigned char __fastcall__ rs232_unpause (void); +/* Re-enable interrupts and release flow control */ + +unsigned char __fastcall__ rs232_status (unsigned char* status, + unsigned char* errors); +/* Return the serial port status. */ + + + +/* End of rs232.h */ +#endif + + + diff --git a/include/setjmp.h b/include/setjmp.h new file mode 100644 index 000000000..af7572eb5 --- /dev/null +++ b/include/setjmp.h @@ -0,0 +1,29 @@ +/* + * setjmp.h + * + * Ullrich von Bassewitz, 06.06.1998 + * + */ + + + +#ifndef _SETJMP_H +#define _SETJMP_H + + + +typedef char jmp_buf [5]; + + + +int __fastcall__ _setjmp (jmp_buf buf); +#define setjmp _setjmp /* ISO insists on a macro */ +void __fastcall__ longjmp (jmp_buf buf, int retval); + + + +/* End of stddef.h */ +#endif + + + diff --git a/include/stdarg.h b/include/stdarg.h new file mode 100644 index 000000000..591048039 --- /dev/null +++ b/include/stdarg.h @@ -0,0 +1,33 @@ +/* + * stdarg.h + * + * Ullrich von Bassewitz, 31.05.1998 + * + */ + + + +#ifndef _STDARG_H +#define _STDARG_H + + + +typedef unsigned char* va_list; + +#define va_start(ap, fix) ap = (va_list)&fix + *(((va_list)&fix)-1) - __fixargs__ +#define va_arg(ap,type) ((type)*(ap -= ((sizeof (type) + 1) & ~1))) +#define va_end(ap) + +/* This is only valid *before* the first call to va_arg. It will also work + * only for int sized parameters. + */ +#define va_fix(ap, offs) *(ap+(__fixargs__-2*offs)) + + + +/* End of stdarg.h */ +#endif + + + + diff --git a/include/stddef.h b/include/stddef.h new file mode 100644 index 000000000..6f7cbd485 --- /dev/null +++ b/include/stddef.h @@ -0,0 +1,34 @@ +/* + * stddef.h + * + * Ullrich von Bassewitz, 06.06.1998 + * + */ + + + +#ifndef _STDDEF_H +#define _STDDEF_H + + + +/* Standard data types */ +typedef int ptrdiff_t; +typedef unsigned size_t; + +/* NULL pointer */ +#ifdef NULL +# undef NULL +#endif +#define NULL 0 + +/* offsetof macro */ +#define offsetof(type, member) (size_t) (&((type*) 0)->member) + + + +/* End of stddef.h */ +#endif + + + diff --git a/include/stdio.h b/include/stdio.h new file mode 100644 index 000000000..d85aaa4ad --- /dev/null +++ b/include/stdio.h @@ -0,0 +1,101 @@ +/* + * stdio.h + * + * Ullrich von Bassewitz, 30.05.1998 + * + */ + + + +#ifndef _STDIO_H +#define _STDIO_H + + + +#ifndef _STDDEF_H +# include +#endif +#ifndef _STDARG_H +# include +#endif + + + +/* Types */ +typedef struct _FILE FILE; +typedef unsigned long fpos_t; + +/* Standard file descriptors */ +extern FILE* stdin; +extern FILE* stdout; +extern FILE* stderr; + +/* Standard defines */ +#define _IOFBF 0 +#define _IOLBF 1 +#define _IONBF 2 +#define BUFSIZ 256 +#define EOF -1 +#define FILENAME_MAX 16 +#define FOPEN_MAX 8 +#define L_tmpnam (FILENAME_MAX + 1) +#define SEEK_CUR 0 +#define SEEK_END 1 +#define SEEK_SET 2 +#define TMP_MAX 256 + + + +/* Functions */ +void __fastcall__ clearerr (FILE* f); +int fclose (FILE* f); +int __fastcall__ feof (FILE* f); +int __fastcall__ ferror (FILE* f); +int __fastcall__ fflush (FILE* f); +int fgetc (FILE* f); +char* fgets (char* buf, size_t size, FILE* f); +FILE* fopen (const char* name, const char* mode); +int fprintf (FILE* f, const char* format, ...); +int fputc (int c, FILE* f); +int fputs (const char* s, FILE* f); +size_t fread (void* buf, size_t size, size_t count, FILE* f); +FILE* freopen (const char* name, const char* mode, FILE* f); +size_t fwrite (const void* buf, size_t size, size_t count, FILE* f); +int getchar (void); +char* gets (char* s); +void perror (const char* s); +int printf (const char* format, ...); +int putchar (int c); +int puts (const char* s); +int remove (const char* name); +int rename (const char* old, const char* new); +int sprintf (char* buf, const char* format, ...); +int vfprintf (FILE* f, const char* format, va_list ap); +int vprintf (const char* format, va_list ap); +int vsprintf (char* buf, const char* format, va_list ap); + +#ifndef __STRICT_ANSI__ +FILE* fdopen (int fd, const char* mode); /* Unix */ +int __fastcall__ fileno (FILE* f); /* Unix */ +#endif + + +/* Masking macros for some functions */ +#define getchar() fgetc (stdin) /* ANSI */ +#define putchar(c) fputc (c, stdout) /* ANSI */ +#define getc(f) fgetc (f) /* ANSI */ +#define putc(c, f) fputc (c, f) /* ANSI */ + +/* Non-standard function like macros */ +#ifndef __STRICT_ANSI__ +#define flushall() /* Unix */ +#define unlink(name) remove (name) /* Unix */ +#endif + + + +/* End of stdio.h */ +#endif + + + diff --git a/include/stdlib.h b/include/stdlib.h new file mode 100644 index 000000000..7a191a3d9 --- /dev/null +++ b/include/stdlib.h @@ -0,0 +1,68 @@ +/* + * stdlib.h + * + * Ullrich von Bassewitz, 02.06.1998 + * + */ + + + +#ifndef _STDLIB_H +#define _STDLIB_H + + + +#include + + + +/* Standard exit codes */ +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 + + + +/* Memory management */ +void* malloc (size_t size); +void* calloc (size_t count, size_t size); +void* realloc (void* block, size_t size); +void free (void* block); +#ifndef __STRICT_ANSI__ +void _hadd (void* mem, size_t size); /* Non-standard */ +#endif + +/* Random numbers */ +#define RAND_MAX 0x7FFF +int rand (void); +void __fastcall__ srand (unsigned seed); + +/* Other standard stuff */ +void abort (void); +int __fastcall__ abs (int val); +long __fastcall__ labs (long val); +int __fastcall__ atoi (char* s); +long __fastcall__ atol (char* s); +int __fastcall__ atexit (void (*exitfunc) (void)); +void* bsearch (const void* key, const void* base, size_t n, + size_t size, int (*cmp) (const void*, const void*)); +void exit (int ret); +char* __fastcall__ getenv (const char* name); +void qsort (void* base, size_t count, size_t size, + int (*compare) (const void*, const void*)); + +/* Non-ANSI functions */ +#ifndef __STRICT_ANSI__ +void __fastcall__ _swap (void* p, void* q, size_t size); +char* __fastcall__ itoa (int val, char* buf, int radix); +char* __fastcall__ utoa (unsigned val, char* buf, int radix); +char* __fastcall__ ltoa (long val, char* buf, int radix); +char* __fastcall__ ultoa (unsigned long val, char* buf, int radix); +#endif + + + +/* End of stdlib.h */ +#endif + + + diff --git a/include/string.h b/include/string.h new file mode 100644 index 000000000..4acb2f8ea --- /dev/null +++ b/include/string.h @@ -0,0 +1,58 @@ +/* + * string.h + * + * Ullrich von Bassewitz, 04.06.1998 + * + */ + + + +#ifndef _STRING_H +#define _STRING_H + + + +#include + + + +char* __fastcall__ strcat (char* dest, const char* src); +char* __fastcall__ strchr (const char* s, int c); +int __fastcall__ strcmp (const char* s1, const char* s2); +int __fastcall__ strcoll (const char* s1, const char* s2); +char* __fastcall__ strcpy (char* dest, const char* src); +size_t __fastcall__ strcspn (const char* s1, const char* s2); +char* __fastcall__ strerror (int errcode); +size_t __fastcall__ strlen (const char* s); +char* __fastcall__ strncat (char* s1, const char* s2, size_t count); +int __fastcall__ strncmp (const char* s1, const char* s2, size_t count); +char* __fastcall__ strncpy (char* dest, const char* src, size_t count); +char* __fastcall__ strrchr (const char* s, int c); +size_t __fastcall__ strspn (const char* s1, const char* s2); +char* __fastcall__ strstr (const char* str, const char* substr); +char* strtok (char* s1, const char* s2); +size_t strxfrm (char* s1, const char* s2, size_t count); +void* __fastcall__ memchr (const void* mem, int c, size_t count); +int __fastcall__ memcmp (const void* p1, const void* p2, size_t count); +void* __fastcall__ memcpy (void* dest, const void* src, size_t count); +void* __fastcall__ memmove (void* dest, const void* src, size_t count); +void* __fastcall__ memset (void* s, int c, size_t count); + +/* Non standard: */ +#ifndef __STRICT_ANSI__ +char* strdup (const char* s); /* SYSV/BSD */ +int __fastcall__ stricmp (const char* s1, const char* s2); /* DOS/Windows */ +int __fastcall__ strcasecmp (const char* s1, const char* s2); /* Same for Unix */ +char* __fastcall__ strlwr (char* s); +char* __fastcall__ strlower (char* s); +char* __fastcall__ strupr (char* s); +char* __fastcall__ strupper (char* s); +#endif + + + +/* End of string.h */ +#endif + + + diff --git a/include/time.h b/include/time.h new file mode 100644 index 000000000..195ce1448 --- /dev/null +++ b/include/time.h @@ -0,0 +1,58 @@ +/* + * time.h + * + * Ullrich von Bassewitz, 17.06.1998 + * + */ + + + +#ifndef _TIME_H +#define _TIME_H + + + +#include + + + +typedef unsigned long time_t; +typedef unsigned long clock_t; + +/* Structure for broken down time */ +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +/* The 610 gets its clock from the AC current */ +#ifdef __CBM__ +# ifdef __CBM610__ +# define CLK_TCK 50 /* POSIX */ +# define CLOCKS_PER_TICK 50 /* ANSI */ +# else +# define CLK_TCK 60 /* POSIX */ +# define CLOCKS_PER_TICK 60 /* ANSI */ +# endif +#endif + + + +/* Function prototypes */ +clock_t clock (void); + + + +/* End of time.h */ + +#endif + + + diff --git a/libsrc/.cvsignore b/libsrc/.cvsignore new file mode 100644 index 000000000..bf406eff4 --- /dev/null +++ b/libsrc/.cvsignore @@ -0,0 +1,2 @@ +libr65.tmp +*.lib diff --git a/libsrc/Makefile b/libsrc/Makefile new file mode 100644 index 000000000..068bbb4f5 --- /dev/null +++ b/libsrc/Makefile @@ -0,0 +1,120 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .obj .s .c + +# Defines used by the submakes: +export CC = ../../src/cc65/cc65 +export AS = ../../src/ca65/ca65 + +# Define used within this makefile +AR = ../src/ar65/ar65 + +#----------------------------------------------------------------------------- + +all : apple2lib c64lib c128lib cbm610lib geoslib petlib plus4lib + +#----------------------------------------------------------------------------- +# Apple ][ + +apple2lib: + export CFLAGS="-Osir -g -t apple2 -I../../include";\ + for i in apple2 common runtime conio dbg; do $(MAKE) -C $$i; done + mv apple2/crt0.o apple2.o + for i in apple2 common runtime conio dbg; do \ + $(AR) a apple2.lib $$i/*.o;\ + done + +#----------------------------------------------------------------------------- +# Atari + +atarilib: + export CFLAGS="-Osir -g -t atari -I../../include";\ + for i in atari common runtime conio dbg; do $(MAKE) -C $$i; done + mv atari/crt0.o atari.o + for i in atari common runtime conio dbg; do \ + $(AR) a atari.lib $$i/*.o;\ + done + +#----------------------------------------------------------------------------- +# C64 + +c64lib: + export CFLAGS="-Osir -g -t c64 -I../../include";\ + for i in c64 cbm common runtime conio dbg; do $(MAKE) -C $$i; done + mv c64/crt0.o c64.o + for i in c64 cbm common runtime conio dbg; do \ + $(AR) a c64.lib $$i/*.o;\ + done + +#----------------------------------------------------------------------------- +# C128 + +c128lib: + export CFLAGS="-Osir -g -t c128 -I../../include";\ + for i in c128 cbm common runtime conio dbg; do $(MAKE) -C $$i; done + mv c128/crt0.o c128.o + for i in c128 cbm common runtime conio dbg; do \ + $(AR) a c128.lib $$i/*.o;\ + done + +#----------------------------------------------------------------------------- +# PET-II series + +cbm610lib: + export CFLAGS="-Osir -g -t cbm610 -I../../include";\ + for i in cbm610 cbm common runtime conio dbg; do $(MAKE) -C $$i; done + mv cbm610/crt0.o cbm610.o + for i in cbm610 cbm common runtime conio dbg; do \ + $(AR) a cbm610.lib $$i/*.o;\ + done + +#----------------------------------------------------------------------------- +# GEOS on the C64/128 + +geoslib: + export CFLAGS="-Osir -g -t geos -I../../include";\ + for i in geos common runtime; do $(MAKE) -C $$i; done + for i in common runtime; do \ + $(AR) a geos.lib $$i/*.o;\ + done + +#----------------------------------------------------------------------------- +# CBM PET machines + +petlib: + export CFLAGS="-Osir -g -t pet -I../../include";\ + for i in pet cbm common runtime conio dbg; do $(MAKE) -C $$i; done + mv pet/crt0.o pet.o + for i in pet cbm common runtime conio dbg; do \ + $(AR) a pet.lib $$i/*.o;\ + done + +#----------------------------------------------------------------------------- +# Commodore C116, C16 and Plus/4 + +plus4lib: + export CFLAGS="-Osir -g -t plus4 -I../../include";\ + for i in plus4 cbm common runtime conio dbg; do $(MAKE) -C $$i; done + mv plus4/crt0.o plus4.o + for i in plus4 cbm common runtime conio dbg; do \ + $(AR) a plus4.lib $$i/*.o;\ + done + +#----------------------------------------------------------------------------- +# Dummy targets + +.PHONY: clean +clean: + @for i in apple2 atari c128 c64 cbm cbm610 common conio dbg geos pet plus4 runtime; do \ + $(MAKE) -C $$i clean; \ + done + +.PHONY: zap +zap: clean + @rm -f *.lib + + + + diff --git a/libsrc/apple2/Makefile b/libsrc/apple2/Makefile new file mode 100644 index 000000000..57d695665 --- /dev/null +++ b/libsrc/apple2/Makefile @@ -0,0 +1,29 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $< + @$(AS) -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +C_OBJS = + +S_OBJS = break.o clrscr.o cclear.o cgetc.o chline.o color.o \ + cputc.o crt0.o ctype.o \ + cvline.o kbhit.o read.o revers.o where.o write.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f $(C_OBJS:.c=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) + @rm -f crt0.o + diff --git a/libsrc/apple2/apple2.inc b/libsrc/apple2/apple2.inc new file mode 100644 index 000000000..d24eb8d9e --- /dev/null +++ b/libsrc/apple2/apple2.inc @@ -0,0 +1,43 @@ +; Break vector +BRKVec = $03F0 + +; Goto Dos +RESTOR = $03D0 + +; Top of available memory +; This is actually for DOS 3.3 need to change it for ProDos +TOPMEM = $9600 + +; Soft switches +; +; write to USEROM to enable apple rom C000-CFFF +USEROM = $C007 +; 80 column card switches +C80ON = $C00C +C80OFF = $C00D +RD80COL = $C01F +PG2OFF = $C054 +PG2ON = $C055 +RDPAGE2 = $C01C + +; Text routines +MIN_X = $20 +MAX_X = $21 +MIN_Y = $22 +MAX_Y = $23 +CH = $24 +CV = $25 +BASL = $28 +TEXTTYP = $32 +HOME = $FC58 +VTABZ = $FC24 +COUT = $FDED + +; Keyboard entries +RDKEY = $FD0C +CLEAR_KEY_STROBE = $C010 +KEY_STROBE = $C000 + +; Game controller +OPEN_APPLE = $C061 +CLOSED_APPLE = $C062 diff --git a/libsrc/apple2/break.s b/libsrc/apple2/break.s new file mode 100644 index 000000000..e9e2e6dca --- /dev/null +++ b/libsrc/apple2/break.s @@ -0,0 +1,109 @@ +; +; Ullrich von Bassewitz, 27.09.1998 +; +; void set_brk (unsigned Addr); +; void reset_brk (void); +; + + .export _set_brk, _reset_brk + .export _brk_a, _brk_x, _brk_y, _brk_sr, _brk_pc + .import _atexit + + .include "apple2.inc" + +_brk_a = $45 +_brk_x = $46 +_brk_y = $47 +_brk_sr = $48 +_brk_sp = $49 +_brk_pc = $3A + +.bss +oldvec: .res 2 ; Old vector + + +.data +uservec: jmp $FFFF ; Patched at runtime + + +.code + +; Set the break vector +.proc _set_brk + + sta uservec+1 + stx uservec+2 ; Set the user vector + + lda oldvec + ora oldvec+1 ; Did we save the vector already? + bne L1 ; Jump if we installed the handler already + + lda BRKVec + sta oldvec + lda BRKVec+1 + sta oldvec+1 ; Save the old vector + + lda #<_reset_brk + ldx #>_reset_brk + jsr _atexit ; Install an exit handler + +L1: lda #brk_handler + sta BRKVec+1 + rts + +.endproc + + +; Reset the break vector +.proc _reset_brk + + lda oldvec + sta BRKVec + lda oldvec+1 + sta BRKVec+1 + rts + +.endproc + + + +; Break handler, called if a break occurs + +.proc brk_handler + + sec + lda _brk_pc + sbc #$02 ; Point to start of brk + sta _brk_pc + lda _brk_pc+1 + sbc #$00 + sta _brk_pc+1 + + clc + lda _brk_sp + adc #$04 ; Adjust stack pointer + sta _brk_sp + + lda _brk_sr ; Clear brk + and #$EF + sta _brk_sr + + jsr uservec ; Call the user's routine + + lda _brk_pc+1 + pha + lda _brk_pc + pha + lda _brk_sr + pha + + ldx _brk_x + ldy _brk_y + lda _brk_a + + rti ; Jump back... + +.endproc + diff --git a/libsrc/apple2/cclear.s b/libsrc/apple2/cclear.s new file mode 100644 index 000000000..586459cb0 --- /dev/null +++ b/libsrc/apple2/cclear.s @@ -0,0 +1,30 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void cclearxy (unsigned char x, unsigned char y, unsigned char length); +; void cclear (unsigned char length); +; + + .export _cclearxy, _cclear + .import popa, _gotoxy, cputdirect + .importzp tmp1 + +_cclearxy: + pha ; Save the length + jsr popa ; Get y + jsr _gotoxy ; Call this one, will pop params + pla ; Restore the length and run into _cclear + +_cclear: + cmp #0 ; Is the length zero? + beq L9 ; Jump if done + sta tmp1 +L1: lda #$20 ; Blank - screen code + jsr cputdirect ; Direct output + dec tmp1 + bne L1 +L9: rts + + + + diff --git a/libsrc/apple2/cgetc.s b/libsrc/apple2/cgetc.s new file mode 100644 index 000000000..7d540ee0f --- /dev/null +++ b/libsrc/apple2/cgetc.s @@ -0,0 +1,25 @@ + ;; + ;; Kevin Ruland + ;; + ;; char cgetc (void); + ;; + ;; If open_apple key is pressed then the high-bit of the + ;; key is set. + + .export _cgetc + + .include "apple2.inc" + +_cgetc: + lda KEY_STROBE + bpl _cgetc ; if < 128, no key pressed + ;; At this time, the high bit of the key pressed + ;; is set + sta CLEAR_KEY_STROBE; clear keyboard strobe + bit OPEN_APPLE ; check if OpenApple is down + bmi pressed + and #$7F ; If not down, then clear high bit +pressed: + ldx #0 + rts + \ No newline at end of file diff --git a/libsrc/apple2/chline.s b/libsrc/apple2/chline.s new file mode 100644 index 000000000..1347c3a11 --- /dev/null +++ b/libsrc/apple2/chline.s @@ -0,0 +1,30 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void chlinexy (unsigned char x, unsigned char y, unsigned char length); +; void chline (unsigned char length); +; + + .export _chlinexy, _chline + .import popa, _gotoxy, cputdirect + .importzp tmp1 + +_chlinexy: + pha ; Save the length + jsr popa ; Get y + jsr _gotoxy ; Call this one, will pop params + pla ; Restore the length + +_chline: + cmp #0 ; Is the length zero? + beq L9 ; Jump if done + sta tmp1 +L1: lda #$2D ; Horizontal line, screen code + jsr cputdirect ; Direct output + dec tmp1 + bne L1 +L9: rts + + + + diff --git a/libsrc/apple2/clrscr.s b/libsrc/apple2/clrscr.s new file mode 100644 index 000000000..c1a299386 --- /dev/null +++ b/libsrc/apple2/clrscr.s @@ -0,0 +1,10 @@ + ;; + ;; Kevin Ruland + ;; + ;; void clrscr (void); + + .export _clrscr + + .include "apple2.inc" + +_clrscr = HOME \ No newline at end of file diff --git a/libsrc/apple2/color.s b/libsrc/apple2/color.s new file mode 100644 index 000000000..cfc8a474c --- /dev/null +++ b/libsrc/apple2/color.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char __fastcall__ textcolor (unsigned char color); +; unsigned char __fastcall__ bgcolor (unsigned char color); +; unsigned char __fastcall__ bordercolor (unsigned char color); +; + + .export _textcolor, _bgcolor, _bordercolor + .import return0, _revers + + .include "apple2.inc" + +_textcolor = _revers + +_bgcolor = return0 + +_bordercolor = return0 + + diff --git a/libsrc/apple2/cputc.s b/libsrc/apple2/cputc.s new file mode 100644 index 000000000..f8ea39dff --- /dev/null +++ b/libsrc/apple2/cputc.s @@ -0,0 +1,85 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void cputcxy (unsigned char x, unsigned char y, char c); +; void cputc (char c); +; + + .export _cputcxy, _cputc + .export _gotoxy, cputdirect + .export newline, putchar + + .import popa + + .include "apple2.inc" + +; Plot a character - also used as internal function + +_cputcxy: + pha ; Save C + jsr popa ; Get Y + jsr _gotoxy + pla ; Restore C + +_cputc: + cmp #$0D ; Test for \r = carrage return + bne L1 + lda #$00 ; Goto left edge of screen + sta CH + rts ; That's all we do +L1: + cmp #$0A ; Test for \n = line feed + beq newline + +cputdirect: + jsr putchar + ;; Bump to next column + inc CH + lda CH + cmp MAX_X + bne return + lda #$00 + sta CH +return: + rts + +putchar: + ora #$80 ; Turn on high bit + and TEXTTYP ; Apply normal, inverse, flash + ldy CH + ldx RD80COL ; In 80 column mode? + bpl col40 ; No, in 40 cols + pha + tya + lsr ; Div by 2 + tay + pla + bcs col40 ; odd cols go in 40 col memory + sta PG2ON +col40: sta (BASL),Y + sta PG2OFF + rts + +newline: + lda CH + pha + inc CV + lda CV + cmp MAX_Y + bne L2 + lda #$00 + sta CV +L2: + jsr VTABZ + pla + sta CH + rts + +_gotoxy: + sta CV ; Store Y + jsr VTABZ + jsr popa ; Get X + sta CH ; Store X + rts + + diff --git a/libsrc/apple2/crt0.s b/libsrc/apple2/crt0.s new file mode 100644 index 000000000..08eb57fd9 --- /dev/null +++ b/libsrc/apple2/crt0.s @@ -0,0 +1,112 @@ +; +; Startup code for cc65 (Apple2 version) +; +; This must be the *first* file on the linker command line +; + + .export _exit + .import __hinit + .import zerobss, push0, doatexit + .import _main + + .include "apple2.inc" + +; ------------------------------------------------------------------------ +; Define and export the ZP variables for the C64 runtime + + .exportzp sp, sreg, regsave + .exportzp ptr1, ptr2, ptr3, ptr4 + .exportzp tmp1, tmp2, tmp3, tmp4 + .exportzp regbank, zpspace + +; These zero page entries overlap with the sweet-16 registers. +; must be changed if sweet-16 is to be supported +sp = $00 ; stack pointer +sreg = $02 ; secondary register/high 16 bit for longs +regsave = $04 ; slot to save/restore (E)AX into +ptr1 = $08 ; +ptr2 = $0A +ptr3 = $0C +ptr4 = $0E +tmp1 = $10 +tmp2 = $11 +tmp3 = $12 +tmp4 = $13 +regbank = $14 ; 6 byte register bank +zpspace = $1A ; Zero page space allocated + +; ------------------------------------------------------------------------ +; Actual code + + ldy #zpspace-1 +L1: lda sp,y + sta zpsave,y ; Save the zero page locations we need + dey + bpl L1 + +; Clear the BSS data + + jsr zerobss + +; Save system stuff and setup the stack + + tsx + stx spsave ; Save the system stack ptr + + lda #TOPMEM + sta sp+1 ; Set argument stack ptr + +; Initialize the heap + + jsr __hinit + +; Initialize conio stuff + + lda #$ff + sta TEXTTYP + +; Set up to use Apple ROM $C000-$CFFF + + ;; sta USEROM + +; Pass an empty command line + + jsr push0 ; argc + jsr push0 ; argv + + ldy #4 ; Argument size + jsr _main ; call the users code + +; fall thru to exit... + +_exit: + lda #$ff + sta TEXTTYP + + jsr doatexit ; call exit functions + + ldx spsave + txs ; Restore stack pointer + +; Copy back the zero page stuff + + ldy #zpspace-1 +L2: lda zpsave,y + sta sp,y + dey + bpl L2 + +; Reset changed vectors, back to basic + + jmp RESTOR + + +.data + +zpsave: .res zpspace + +.bss + +spsave: .res 1 diff --git a/libsrc/apple2/ctype.s b/libsrc/apple2/ctype.s new file mode 100644 index 000000000..c0233b28e --- /dev/null +++ b/libsrc/apple2/ctype.s @@ -0,0 +1,309 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; Character specification table. +; + +; The tables are readonly, put them into the code segment + +.code + +; Value that must be added to an upper case char to make it lower case +; char (example: for ASCII, this must be $E0). + + + .export __cdiff + +__cdiff: + .byte $E0 + + +; The following 256 byte wide table specifies attributes for the isxxx type +; of functions. Doing it by a table means some overhead in space, but it +; has major advantages: +; +; * It is fast. If it were'nt for the slow parameter passing of cc65, one +; could even define macros for the isxxx functions (this is usually +; done on other platforms). +; +; * It is highly portable. The only unportable part is the table itself, +; all real code goes into the common library. +; +; * We save some code in the isxxx functions. +; +; +; Bit assignments: +; +; 0 - Lower case char +; 1 - Upper case char +; 2 - Numeric digit +; 3 - Hex digit (both, lower and upper) +; 4 - Control character +; 5 - The space character itself +; 6 - Other whitespace (that is: '\f', '\n', '\r', '\t' and '\v') +; 7 - Space or tab character + + .export __ctype + +__ctype: + .byte $10 ; 0/00 ___ctrl_@___ + .byte $10 ; 1/01 ___ctrl_A___ + .byte $10 ; 2/02 ___ctrl_B___ + .byte $10 ; 3/03 ___ctrl_C___ + .byte $10 ; 4/04 ___ctrl_D___ + .byte $10 ; 5/05 ___ctrl_E___ + .byte $10 ; 6/06 ___ctrl_F___ + .byte $10 ; 7/07 ___ctrl_G___ + .byte $10 ; 8/08 ___ctrl_H___ + .byte $D0 ; 9/09 ___ctrl_I___ + .byte $50 ; 10/0a ___ctrl_J___ + .byte $50 ; 11/0b ___ctrl_K___ + .byte $50 ; 12/0c ___ctrl_L___ + .byte $50 ; 13/0d ___ctrl_M___ + .byte $10 ; 14/0e ___ctrl_N___ + .byte $10 ; 15/0f ___ctrl_O___ + .byte $10 ; 16/10 ___ctrl_P___ + .byte $10 ; 17/11 ___ctrl_Q___ + .byte $10 ; 18/12 ___ctrl_R___ + .byte $10 ; 19/13 ___ctrl_S___ + .byte $10 ; 20/14 ___ctrl_T___ + .byte $10 ; 21/15 ___ctrl_U___ + .byte $10 ; 22/16 ___ctrl_V___ + .byte $10 ; 23/17 ___ctrl_W___ + .byte $10 ; 24/18 ___ctrl_X___ + .byte $10 ; 25/19 ___ctrl_Y___ + .byte $10 ; 26/1a ___ctrl_Z___ + .byte $10 ; 27/1b ___ctrl_[___ + .byte $10 ; 28/1c ___ctrl_\___ + .byte $10 ; 29/1d ___ctrl_]___ + .byte $10 ; 30/1e ___ctrl_^___ + .byte $10 ; 31/1f ___ctrl_____ + .byte $A0 ; 32/20 ___SPACE___ + .byte $00 ; 33/21 _____!_____ + .byte $00 ; 34/22 _____"_____ + .byte $00 ; 35/23 _____#_____ + .byte $00 ; 36/24 _____$_____ + .byte $00 ; 37/25 _____%_____ + .byte $00 ; 38/26 _____&_____ + .byte $00 ; 39/27 _____'_____ + .byte $00 ; 40/28 _____(_____ + .byte $00 ; 41/29 _____)_____ + .byte $00 ; 42/2a _____*_____ + .byte $00 ; 43/2b _____+_____ + .byte $00 ; 44/2c _____,_____ + .byte $00 ; 45/2d _____-_____ + .byte $00 ; 46/2e _____._____ + .byte $00 ; 47/2f _____/_____ + .byte $0C ; 48/30 _____0_____ + .byte $0C ; 49/31 _____1_____ + .byte $0C ; 50/32 _____2_____ + .byte $0C ; 51/33 _____3_____ + .byte $0C ; 52/34 _____4_____ + .byte $0C ; 53/35 _____5_____ + .byte $0C ; 54/36 _____6_____ + .byte $0C ; 55/37 _____7_____ + .byte $0C ; 56/38 _____8_____ + .byte $0C ; 57/39 _____9_____ + .byte $00 ; 58/3a _____:_____ + .byte $00 ; 59/3b _____;_____ + .byte $00 ; 60/3c _____<_____ + .byte $00 ; 61/3d _____=_____ + .byte $00 ; 62/3e _____>_____ + .byte $00 ; 63/3f _____?_____ + + .byte $00 ; 64/40 _____@_____ + .byte $0A ; 65/41 _____A_____ + .byte $0A ; 66/42 _____B_____ + .byte $0A ; 67/43 _____C_____ + .byte $0A ; 68/44 _____D_____ + .byte $0A ; 69/45 _____E_____ + .byte $0A ; 70/46 _____F_____ + .byte $02 ; 71/47 _____G_____ + .byte $02 ; 72/48 _____H_____ + .byte $02 ; 73/49 _____I_____ + .byte $02 ; 74/4a _____J_____ + .byte $02 ; 75/4b _____K_____ + .byte $02 ; 76/4c _____L_____ + .byte $02 ; 77/4d _____M_____ + .byte $02 ; 78/4e _____N_____ + .byte $02 ; 79/4f _____O_____ + .byte $02 ; 80/50 _____P_____ + .byte $02 ; 81/51 _____Q_____ + .byte $02 ; 82/52 _____R_____ + .byte $02 ; 83/53 _____S_____ + .byte $02 ; 84/54 _____T_____ + .byte $02 ; 85/55 _____U_____ + .byte $02 ; 86/56 _____V_____ + .byte $02 ; 87/57 _____W_____ + .byte $02 ; 88/58 _____X_____ + .byte $02 ; 89/59 _____Y_____ + .byte $02 ; 90/5a _____Z_____ + .byte $00 ; 91/5b _____[_____ + .byte $00 ; 92/5c _____\_____ + .byte $00 ; 93/5d _____]_____ + .byte $00 ; 94/5e _____^_____ + .byte $00 ; 95/5f _UNDERLINE_ + .byte $00 ; 96/60 ___grave___ + .byte $09 ; 97/61 _____a_____ + .byte $09 ; 98/62 _____b_____ + .byte $09 ; 99/63 _____c_____ + .byte $09 ; 100/64 _____d_____ + .byte $09 ; 101/65 _____e_____ + .byte $09 ; 102/66 _____f_____ + .byte $01 ; 103/67 _____g_____ + .byte $01 ; 104/68 _____h_____ + .byte $01 ; 105/69 _____i_____ + .byte $01 ; 106/6a _____j_____ + .byte $01 ; 107/6b _____k_____ + .byte $01 ; 108/6c _____l_____ + .byte $01 ; 109/6d _____m_____ + .byte $01 ; 110/6e _____n_____ + .byte $01 ; 111/6f _____o_____ + .byte $01 ; 112/70 _____p_____ + .byte $01 ; 113/71 _____q_____ + .byte $01 ; 114/72 _____r_____ + .byte $01 ; 115/73 _____s_____ + .byte $01 ; 116/74 _____t_____ + .byte $01 ; 117/75 _____u_____ + .byte $01 ; 118/76 _____v_____ + .byte $01 ; 119/77 _____w_____ + .byte $01 ; 120/78 _____x_____ + .byte $01 ; 121/79 _____y_____ + .byte $01 ; 122/7a _____z_____ + .byte $00 ; 123/7b _____{_____ + .byte $00 ; 124/7c _____|_____ + .byte $00 ; 125/7d _____}_____ + .byte $00 ; 126/7e _____~_____ + .byte $40 ; 127/7f ____DEL____ + + .byte $00 ; 128/80 ___________ + .byte $00 ; 129/81 ___________ + .byte $00 ; 130/82 ___________ + .byte $00 ; 131/83 ___________ + .byte $00 ; 132/84 ___________ + .byte $00 ; 133/85 ___________ + .byte $00 ; 134/86 ___________ + .byte $00 ; 135/87 ___________ + .byte $00 ; 136/88 ___________ + .byte $00 ; 137/89 ___________ + .byte $00 ; 138/8a ___________ + .byte $00 ; 139/8b ___________ + .byte $00 ; 140/8c ___________ + .byte $00 ; 141/8d ___________ + .byte $00 ; 142/8e ___________ + .byte $00 ; 143/8f ___________ + .byte $00 ; 144/90 ___________ + .byte $00 ; 145/91 ___________ + .byte $00 ; 146/92 ___________ + .byte $10 ; 147/93 ___________ + .byte $00 ; 148/94 ___________ + .byte $00 ; 149/95 ___________ + .byte $00 ; 150/96 ___________ + .byte $00 ; 151/97 ___________ + .byte $00 ; 152/98 ___________ + .byte $00 ; 153/99 ___________ + .byte $00 ; 154/9a ___________ + .byte $00 ; 155/9b ___________ + .byte $00 ; 156/9c ___________ + .byte $00 ; 157/9d ___________ + .byte $00 ; 158/9e ___________ + .byte $00 ; 159/9f ___________ + + .byte $00 ; 160/a0 ___________ + .byte $00 ; 161/a1 ___________ + .byte $00 ; 162/a2 ___________ + .byte $00 ; 163/a3 ___________ + .byte $00 ; 164/a4 ___________ + .byte $00 ; 165/a5 ___________ + .byte $00 ; 166/a6 ___________ + .byte $00 ; 167/a7 ___________ + .byte $00 ; 168/a8 ___________ + .byte $00 ; 169/a9 ___________ + .byte $00 ; 170/aa ___________ + .byte $00 ; 171/ab ___________ + .byte $00 ; 172/ac ___________ + .byte $00 ; 173/ad ___________ + .byte $00 ; 174/ae ___________ + .byte $00 ; 175/af ___________ + .byte $00 ; 176/b0 ___________ + .byte $00 ; 177/b1 ___________ + .byte $00 ; 178/b2 ___________ + .byte $00 ; 179/b3 ___________ + .byte $00 ; 180/b4 ___________ + .byte $00 ; 181/b5 ___________ + .byte $00 ; 182/b6 ___________ + .byte $00 ; 183/b7 ___________ + .byte $00 ; 184/b8 ___________ + .byte $00 ; 185/b9 ___________ + .byte $00 ; 186/ba ___________ + .byte $00 ; 187/bb ___________ + .byte $00 ; 188/bc ___________ + .byte $00 ; 189/bd ___________ + .byte $00 ; 190/be ___________ + .byte $00 ; 191/bf ___________ + + .byte $02 ; 192/c0 ___________ + .byte $02 ; 193/c1 ___________ + .byte $02 ; 194/c2 ___________ + .byte $02 ; 195/c3 ___________ + .byte $02 ; 196/c4 ___________ + .byte $02 ; 197/c5 ___________ + .byte $02 ; 198/c6 ___________ + .byte $02 ; 199/c7 ___________ + .byte $02 ; 200/c8 ___________ + .byte $02 ; 201/c9 ___________ + .byte $02 ; 202/ca ___________ + .byte $02 ; 203/cb ___________ + .byte $02 ; 204/cc ___________ + .byte $02 ; 205/cd ___________ + .byte $02 ; 206/ce ___________ + .byte $02 ; 207/cf ___________ + .byte $02 ; 208/d0 ___________ + .byte $02 ; 209/d1 ___________ + .byte $02 ; 210/d2 ___________ + .byte $02 ; 211/d3 ___________ + .byte $02 ; 212/d4 ___________ + .byte $02 ; 213/d5 ___________ + .byte $02 ; 214/d6 ___________ + .byte $02 ; 215/d7 ___________ + .byte $02 ; 216/d8 ___________ + .byte $02 ; 217/d9 ___________ + .byte $02 ; 218/da ___________ + .byte $02 ; 219/db ___________ + .byte $02 ; 220/dc ___________ + .byte $02 ; 221/dd ___________ + .byte $02 ; 222/de ___________ + .byte $00 ; 223/df ___________ + .byte $01 ; 224/e0 ___________ + .byte $01 ; 225/e1 ___________ + .byte $01 ; 226/e2 ___________ + .byte $01 ; 227/e3 ___________ + .byte $01 ; 228/e4 ___________ + .byte $01 ; 229/e5 ___________ + .byte $01 ; 230/e6 ___________ + .byte $01 ; 231/e7 ___________ + .byte $01 ; 232/e8 ___________ + .byte $01 ; 233/e9 ___________ + .byte $01 ; 234/ea ___________ + .byte $01 ; 235/eb ___________ + .byte $01 ; 236/ec ___________ + .byte $01 ; 237/ed ___________ + .byte $01 ; 238/ee ___________ + .byte $01 ; 239/ef ___________ + .byte $01 ; 240/f0 ___________ + .byte $01 ; 241/f1 ___________ + .byte $01 ; 242/f2 ___________ + .byte $01 ; 243/f3 ___________ + .byte $01 ; 244/f4 ___________ + .byte $01 ; 245/f5 ___________ + .byte $01 ; 246/f6 ___________ + .byte $01 ; 247/f7 ___________ + .byte $01 ; 248/f8 ___________ + .byte $01 ; 249/f9 ___________ + .byte $01 ; 250/fa ___________ + .byte $01 ; 251/fb ___________ + .byte $01 ; 252/fc ___________ + .byte $01 ; 253/fd ___________ + .byte $01 ; 254/fe ___________ + .byte $00 ; 255/ff ___________ + diff --git a/libsrc/apple2/cvline.s b/libsrc/apple2/cvline.s new file mode 100644 index 000000000..d762ad1b8 --- /dev/null +++ b/libsrc/apple2/cvline.s @@ -0,0 +1,30 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void cvlinexy (unsigned char x, unsigned char y, unsigned char length); +; void cvline (unsigned char length); +; + + .export _cvlinexy, _cvline + .import popa, _gotoxy, putchar, newline + .importzp tmp1 + +_cvlinexy: + pha ; Save the length + jsr popa ; Get y + jsr _gotoxy ; Call this one, will pop params + pla ; Restore the length and run into _cvline + +_cvline: + cmp #0 ; Is the length zero? + beq L9 ; Jump if done + sta tmp1 +L1: lda #$7C ; Vertical bar + jsr putchar ; Write, no cursor advance + jsr newline ; Advance cursor to next line + dec tmp1 + bne L1 +L9: rts + + + diff --git a/libsrc/apple2/kbhit.s b/libsrc/apple2/kbhit.s new file mode 100644 index 000000000..8471330ba --- /dev/null +++ b/libsrc/apple2/kbhit.s @@ -0,0 +1,18 @@ + ;; + ;; Kevin Ruland + ;; + ;; int kbhit (void); + ;; + + .export _kbhit + + .import return0, return1 + + .include "apple2.inc" + +_kbhit: + bit KEY_STROBE ; Reading strobe checks for keypress + bmi L1 ; if KEY_STROBE > 127 key was pressed + jmp return0 +L1: + jmp return1 diff --git a/libsrc/apple2/read.s b/libsrc/apple2/read.s new file mode 100644 index 000000000..93dec57aa --- /dev/null +++ b/libsrc/apple2/read.s @@ -0,0 +1,52 @@ +; +; Ullrich von Bassewitz, 30.05.1998 +; +; int read (int fd, void* buf, int count); +; +; THIS IS A HACK! +; + + .export _read + .import popax, _cputc + .importzp ptr1, ptr2, ptr3 + + .include "apple2.inc" + +_read: jsr popax ; get count + sta ptr2 + stx ptr2+1 ; save it for later + jsr popax ; get buf + sta ptr1 + stx ptr1+1 + jsr popax ; get fd and discard it + lda #0 + sta ptr3 + sta ptr3+1 ; set count + +L1: lda ptr2 + ora ptr2+1 ; count zero? + beq L9 + jsr RDKEY + and #$7f ; clear high bit. + pha + jsr _cputc + pla + ldy #0 ; offset into string + sta (ptr1),y ; save char + inc ptr1 + bne L2 + inc ptr1+1 +L2: inc ptr3 ; increment count + bne L3 + inc ptr3+1 +L3: cmp #$0D ; CR? + bne L1 + +; Done, return the count + +L9: lda ptr3 + ldx ptr3+1 + rts + + + diff --git a/libsrc/apple2/revers.s b/libsrc/apple2/revers.s new file mode 100644 index 000000000..702082900 --- /dev/null +++ b/libsrc/apple2/revers.s @@ -0,0 +1,25 @@ + ;; + ;; Kevin Ruland + ;; + ;; unsigned char __fastcall__ revers (unsigned char onoff) + ;; + + .export _revers + + .include "apple2.inc" + +_revers: + ldy TEXTTYP ; Stash old value + and #$FF ; Test for any bit + bne reverse ; Nothing set + lda #$FF +reverse: + ora #$3F + sta TEXTTYP + tya ; What was the old value? + eor #$FF ; Normal = $FF, Reverse = $3F + beq L2 + lda #01 +L2: + rts + diff --git a/libsrc/apple2/where.s b/libsrc/apple2/where.s new file mode 100644 index 000000000..b17c1f832 --- /dev/null +++ b/libsrc/apple2/where.s @@ -0,0 +1,18 @@ + + ;; Keivn Ruland + ;; + ;; unsigned char wherex( void ); + ;; unsigned char wherey( void ); + + .export _wherex, _wherey + + .include "apple2.inc" + +_wherex: + lda CH + rts + +_wherey: + lda CV + rts + \ No newline at end of file diff --git a/libsrc/apple2/write.s b/libsrc/apple2/write.s new file mode 100644 index 000000000..cb24e0096 --- /dev/null +++ b/libsrc/apple2/write.s @@ -0,0 +1,53 @@ + ;; + ;; Kevin Ruland + ;; + ;; int write (int fd, const void* buf, int count); + ;; + ;; for now will only write to fd = stdout + ;; + + .export _write + + .import popax + + .importzp ptr1, ptr2, ptr3 + + .include "apple2.inc" + +_write: + jsr popax ; get count + sta ptr2 + stx ptr2+1 ; save for later + sta ptr3 + sta ptr3+1 ; save for result + jsr popax ; get buf + sta ptr1 + stx ptr1+1 + jsr popax ; get fd and discard +L1: lda ptr2 + ora ptr2+1 ; count zero? + beq L9 + ldy #0 + lda (ptr1),y + cmp #$0A ; Check for \n = Crtl-j + bne rawout + lda #$0D ; Issue cr +rawout: + ora #$80 + jsr COUT + inc ptr1 + bne L2 + inc ptr1+1 +L2: lda ptr2 + bne L3 + dec ptr2 + dec ptr2+1 + jmp L1 +L3: dec ptr2 + jmp L1 + +; No error, return count + +L9: lda ptr3 + ldx ptr3+1 + rts diff --git a/libsrc/atari/Makefile b/libsrc/atari/Makefile new file mode 100644 index 000000000..dd23d22fe --- /dev/null +++ b/libsrc/atari/Makefile @@ -0,0 +1,32 @@ +# +# makefile for CC65 Atari runtime library +# + +ATARIDEFS = -DDIRECT_SCREEN + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $(ATARIDEFS) $< + @$(AS) -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $(ATARIDEFS) $< + +C_OBJS = + +S_OBJS = crt0.o kbhit.o conio.o clrscr.o cputc.o ctype.o chline.o cvline.o \ + color.o gotoxy.o cclear.o revers.o readjoy.o break.o where.o write.o \ + gotox.o gotoy.o savevec.o rwcommon.o cgetc.o read.o getargs.o close.o \ + open.o oserror.o fdtable.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f $(C_OBJS:.c=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) + @rm -f crt0.o + diff --git a/libsrc/atari/atari.inc b/libsrc/atari/atari.inc new file mode 100644 index 000000000..1234cbcc7 --- /dev/null +++ b/libsrc/atari/atari.inc @@ -0,0 +1,1028 @@ +;------------------------------------------------------------------------- +; Atari System Equates -- Version 1.0.0 +; By Freddy Offenga, 4/15/2000 +; +; References: +; - Atari 400/800 OS rev.B source code, Atari 1979 +; - Atari OS manual - XL addendum +; - Atari XL/XE rev.2 source code, Atari 1984 +; - Mapping the Atari - revised edition, Ian Chadwick 1985 +; +; ##old## old OS rev.B label - moved or deleted +; ##1200xl## new label introduced in 1200XL OS (rev.10/11) +; ##rev2## new label introduced in XL/XE OS rev.2 +;------------------------------------------------------------------------- + +;------------------------------------------------------------------------- +; Configuration Equates +;------------------------------------------------------------------------- + +MAXDEV = 33 ;offset to last possible entry of HATABS +IOCBSZ = 16 ;length of IOCB + +SEIOCB = 0*IOCBSZ ;##rev2## screen editor IOCB index +MAXIOC = 8*IOCBSZ ;first invalid IOCB index + +DSCTSZ = 128 ;##rev2## disk sector size + +LEDGE = 2 ;left edge +REDGE = 39 ;right edge + +INIML = $0700 ;##rev2## initial MEMLO + +ICSORG = $CC00 ;##rev2## international character set origin +DCSORG = $E000 ;##rev2## domestic character set origin + +; IOCB Command Code Equates + +OPEN = $03 ;open +GETREC = $05 ;get record +GETCHR = $07 ;get character(s) +PUTREC = $09 ;put record +PUTCHR = $0B ;put character(s) +CLOSE = $0C ;close +STATIS = $0D ;status +SPECIL = $0E ;special + +; Special Entry Command Equates + +; Screen Commands + +DRAWLN = $11 ;draw line +FILLIN = $12 ;draw line with right fill + +; ICAX1 Auxiliary Byte 1 Equates + +APPEND = $01 ;open write append (D:) +DIRECT = $02 ;open for directory access (D:) +OPNIN = $04 ;open for input (all devices) +OPNOT = $08 ;open for output (all devices) +MXDMOD = $10 ;open for mixed mode (E:, S:) +INSCLR = $20 ;open for input without clearing screen + +; Device Code Equates + +CASSET = 'C' ;cassette +DISK = 'D' ;disk +SCREDT = 'E' ;screen editor +KBD = 'K' ;keyboard +PRINTR = 'P' ;printer +DISPLY = 'S' ;screen display + +; Character and Key Code Equates + +CLS = $7D ;##rev2## clear screen +EOL = $9B ;end of line (RETURN) + +HELP = $11 ;##1200xl## key code for HELP +CNTLF1 = $83 ;##1200xl## key code for CTRL-F1 +CNTLF2 = $84 ;##1200xl## key code for CTRL-F2 +CNTLF3 = $93 ;##1200xl## key code for CTRL-F3 +CNTLF4 = $94 ;##1200xl## key code for CTRL-F4 +CNTL1 = $9F ;##1200xl## key code for CTRL-1 + +; Status Code Equates + +SUCCES = 1 ;($01) succesful operation + +BRKABT = 128 ;($80) BREAK key abort +PRVOPN = 129 ;($81) IOCB already open error +NONDEV = 130 ;($82) nonexistent device error +WRONLY = 131 ;($83) IOCB opened for write only error +NVALID = 132 ;($84) invalid command error +NOTOPN = 133 ;($85) device/file not open error +BADIOC = 134 ;($86) invalid IOCB index error +RDONLY = 135 ;($87) IOCB opened for read only error +EOFERR = 136 ;($88) end of file error +TRNRCD = 137 ;($89) truncated record error +TIMOUT = 138 ;($8A) peripheral device timeout error +DNACK = 139 ;($8B) device does not acknowledge command +FRMERR = 140 ;($8C) serial bus framing error +CRSROR = 141 ;($8D) cursor overrange error +OVRRUN = 142 ;($8E) serial bus data overrun error +CHKERR = 143 ;($8F) serial bus checksum error +DERROR = 144 ;($90) device done (operation incomplete) +BADMOD = 145 ;($91) bad screen mode number error +FNCNOT = 146 ;($92) function not implemented in handler +SCRMEM = 147 ;($93) insufficient memory for screen mode + +; DCB Device Bus Equates + +DISKID = $31 ;##rev2## disk bus ID +PDEVN = $40 ;##rev2## printer bus ID +CASET = $60 ;##rev2## cassette bus ID + +; Bus Command Equates + +FOMAT = '!' ;##rev2## format command +PUTSEC = 'P' ;##rev2## put sector command +READ = 'R' ;##rev2## read command +STATC = 'S' ;##rev2## status command +WRITE = 'W' ;##rev2## write command + +; Command Auxiliary Byte Equates + +DOUBLE = 'D' ;##rev2## print 20 characters double width +NORMAL = 'N' ;##rev2## print 40 characters normally +PLOT = 'P' ;##rev2## plot +SIDWAY = 'S' ;##rev2## print 16 characters sideways + +; Bus Response Equates + +ACK = 'A' ;##rev2## device acknowledged +COMPLT = 'C' ;##rev2## device succesfully completed operation +ERROR = 'E' ;##rev2## device incurred error +NACK = 'N' ;##rev2## device did not understand + +; Floating Point Miscellaneous Equates + +FPREC = 6 ;precision + +FMPREC = FPREC-1 ;##rev2## length of mantissa + +; Cassette Record Type Equates + +HDR = $FB ;##rev2## header +DTA = $FC ;##rev2## data record +DT1 = $FA ;##rev2## last data record +EOT = $FE ;##rev2## end of tape (file) + +TONE1 = 2 ;##rev2## record +TONE2 = 1 ;##rev2## playback + +; Cassette Timing Equates + +WLEADN = 1152 ;##rev2## NTSC 19.2 second WRITE file leader +RLEADN = 576 ;##rev2## NTSC 9.6 second READ file leader +WIRGLN = 180 ;##rev2## NTSC 3.0 second WRITE IRG +RIRGLN = 120 ;##rev2## NTSC 2.0 second READ IRG +WSIRGN = 15 ;##rev2## NTSC 0.25 second WRITE short IRG +RSIRGN = 10 ;##rev2## NTSC 0.16 second READ short IRG +BEEPNN = 30 ;##rev2## NTSC 0.5 second beep duration +BEEPFN = 10 ;##rev2## NTSC 0.16 seconrd beep duration + +WLEADP = 960 ;##rev2## PAL 19.2 second WRITE file leader +RLEADP = 480 ;##rev2## PAL 9.6 second READ file leader +WIRGLP = 150 ;##rev2## PAL 3.0 second WRITE IRG +RIRGLP = 100 ;##rev2## PAL 2.0 second READ IRG +WSIRGP = 13 ;##rev2## PAL 0.25 second WRITE short IRG +RSIRGP = 8 ;##rev2## PAL 0.16 second READ short IRG +BEEPNP = 25 ;##rev2## PAL 0.5 second beep duration +BEEPFP = 8 ;##rev2## PAL 0.16 seconrd beep duration + +WIRGHI = 0 ;##rev2## high WRITE IRG +RIRGHI = 0 ;##rev2## high READ IRG + +; Power-up Validation Byte Value Equates + +PUPVL1 = $5C ;##rev2## power-up validation value 1 +PUPVL2 = $93 ;##rev2## power-up validation value 2 +PUPVL3 = $25 ;##rev2## power-up validation value 3 + +; Relocating Loader Miscellaneous Equates + +DATAER = 156 ;##rev2## end of record appears before END +MEMERR = 157 ;##rev2## memory insufficient for load error + +; Miscellaneous Equates + +IOCFRE = $FF ;IOCB free indication + +B19200 = $0028 ;##rev2## 19200 baud POKEY counter value +B00600 = $05CC ;##rev2## 600 baud POKEY counter value + +HITONE = $05 ;##rev2## FSK high freq. POKEY counter value +LOTONE = $07 ;##rev2## FSK low freq. POKEY counter value + +NCOMLO = $34 ;##rev2## PIA lower NOT COMMAND line command +NCOMHI = $3C ;##rev2## PIA raise NOT COMMAND line command + +MOTRGO = $34 ;##rev2## PIA cassette motor ON command +MOTRST = $3C ;##rev2## PIA cassette motor OFF command + +NODAT = $00 ;##rev2## SIO immediate operation +GETDAT = $40 ;##rev2## SIO read data frame +PUTDAT = $80 ;##rev2## SIO write data frame + +CRETRI = 13 ;##rev2## number of command frame retries +DRETRI = 1 ;##rev2## number of device retries +CTIM = 2 ;##rev2## command frame ACK timeout + +NBUFSZ = 40 ;##rev2## print normal buffer size +DBUFSZ = 20 ;##rev2## print double buffer size +SBUFSZ = 29 ;##rev2## print sideways buffer size + +;------------------------------------------------------------------------- +; Page Zero Address Equates +;------------------------------------------------------------------------- + +LINZBS = $00 ;LINBUG RAM (WILL BE REPLACED BY MONITOR RAM) +LNFLG = $00 ;##1200xl## 1-byte LNBUG flag (0 = not LNBUG) +NGFLAG = $01 ;##1200xl## 1-byte memory status (0 = failure) + +; Not Cleared + +CASINI = $02 ;CASSETTE INIT LOCATION +RAMLO = $04 ;RAM POINTER FOR MEMORY TEST +TRAMSZ = $06 ;TEMPORARY REGISTER FOR RAM SIZE +;TSTDAT = $07 ;##old## RAM TEST DATA REGISTER +CMCMD = $07 ;##rev2## 1-byte command communications + +; Cleared upon Coldstart only + +WARMST = $08 ;WARM START FLAG +BOOTQ = $09 ;SUCCESSFUL BOOT FLAG +DOSVEC = $0A ;DISK SOFTWARE START VECTOR +DOSINI = $0C ;DISK SOFTWARE INIT ADDRESS +APPMHI = $0E ;APPLICATIONS MEMORY HI LIMIT + +; Cleared upon Coldstart or Warmstart + +INTZBS = $10 ;INTERRUPT HANDLER + +POKMSK = $10 ;SYSTEM MASK FOR POKEY IRG ENABLE +BRKKEY = $11 ;BREAK KEY FLAG +RTCLOK = $12 ;REAL TIME CLOCK (IN 16 MSEC UNITS> +BUFADR = $15 ;INDIRECT BUFFER ADDRESS REGISTER +ICCOMT = $17 ;COMMAND FOR VECTOR +DSKFMS = $18 ;DISK FILE MANAGER POINTER +DSKUTL = $1A ;DISK UTILITIES POINTER +ABUFPT = $1C ;##1200xl## 4-byte ACMI buffer pointer area + +;PTIMOT = $1C ;##old## PRINTER TIME OUT REGISTER +;PBPNT = $1D ;##old## PRINT BUFFER POINTER +;PBUFSZ = $1E ;##old## PRINT BUFFER SIZE +;PTEMP = $1F ;##old## TEMPORARY REGISTER + +ZIOCB = $20 ;ZERO PAGE I/O CONTROL BLOCK +IOCBAS = $20 ;16-byte page zero IOCB +ICHIDZ = $20 ;HANDLER INDEX NUMBER (FF = IOCB FREE) +ICDNOZ = $21 ;DEVICE NUMBER (DRIVE NUMBER) +ICCOMZ = $22 ;COMMAND CODE +ICSTAZ = $23 ;STATUS OF LAST IOCB ACTION +ICBALZ = $24 ;BUFFER ADDRESS LOW BYTE +ICBAHZ = $25 ;1-byte high buffer address +ICPTLZ = $26 ;PUT BYTE ROUTINE ADDRESS -1 +ICPTHZ = $27 ;1-byte high PUT-BYTE routine address +ICBLLZ = $28 ;BUFFER LENGTH LOW BYTE +ICBLHZ = $29 ;1-byte high buffer length +ICAX1Z = $2A ;AUXILIARY INFORMATION FIRST BYTE +ICAX2Z = $2B ;1-byte second auxiliary information +ICSPRZ = $2C ;4-byte spares + +ENTVEC = $2C ;##rev2## 2-byte (not used) +ICIDNO = $2E ;IOCB NUMBER X 16 +CIOCHR = $2F ;CHARACTER BYTE FOR CURRENT OPERATION + +STATUS = $30 ;INTERNAL STATUS STORAGE +CHKSUM = $31 ;CHECKSUM (SINGLE BYTE SUM WITH CARRY) +BUFRLO = $32 ;POINTER TO DATA BUFFER (LO BYTE) +BUFRHI = $33 ;POINTER TO DATA BUFFER (HI BYTE) +BFENLO = $34 ;NEXT BYTE PAST END OF THE DATA BUFFER LO +BFENHI = $35 ;NEXT BYTE PAST END OF THE DATA BUFFER HI +;CRETRY = $36 ;##old## NUMBER OF COMMAND FRAME RETRIES +;DRETRY = $37 ;##old## NUMBER OF DEVICE RETRIES +LTEMP = $36 ;##1200xl## 2-byte loader temporary +BUFRFL = $38 ;DATA BUFFER FULL FLAG +RECVDN = $39 ;RECEIVE DONE FLAG +XMTDON = $3A ;TRANSMISSION DONE FLAG +CHKSNT = $3B ;CHECKSUM SENT FLAG +NOCKSM = $3C ;NO CHECKSUM FOLLOWS DATA FLAG +BPTR = $3D ;1-byte cassette buffer pointer +FTYPE = $3E ;1-byte cassette IRG type +FEOF = $3F ;1-byte cassette EOF flag (0 = quiet) +FREQ = $40 ;1-byte cassette beep counter +SOUNDR = $41 ;NOISY I/0 FLAG. (ZERO IS QUIET) + +CRITIC = $42 ;DEFINES CRITICAL SECTION (CRITICAL IF NON-Z) + +FMSZPG = $43 ;DISK FILE MANAGER SYSTEM ZERO PAGE + +;CKEY = $4A ;##old## FLAG SET WHEN GAME START PRESSED +ZCHAIN = $4A ;##1200xl## 2-byte handler linkage chain pointer +;CASSBT = $4B ;##old## CASSETTE BOOT FLAG +DSTAT = $4C ;DISPLAY STATUS +ATRACT = $4D ;ATRACT FLAG +DRKMSK = $4E ;DARK ATRACT MASK +COLRSH = $4F ;ATRACT COLOR SHIFTER (EOR'ED WITH PLAYFIELD + + +TMPCHR = $50 ;1-byte temporary character +HOLD1 = $51 ;1-byte temporary +LMARGN = $52 ;LEFT MARGIN (SET TO 1 AT POWER ON> +RMARGN = $53 ;RIGHT MARGIN (SET TO 38 AT POWER ON) +ROWCRS = $54 ;1-byte cursor row +COLCRS = $55 ;2-byte cursor column +DINDEX = $57 ;1-byte display mode +SAVMSC = $58 ;2-byte saved memory scan counter +OLDROW = $5A ;1-byte prior row +OLDCOL = $5B ;2-byte prior column +OLDCHR = $5D ;DATA UNDER CURSOR +OLDADR = $5E ;2-byte saved cursor memory address +FKDEF = $60 ;##1200xl## 2-byte function key definition table +;NEWROW = $60 ;##old## POINT DRAW GOES TO +;NEWCOL = $61 ;##old## +PALNTS = $62 ;##1200xl## 1-byte PAL/NTSC indicator (0 = NTSC) +LOGCOL = $63 ;POINTS AT COLUMN IN LOGICAL LINE +ADRESS = $64 ;2-byte temporary address + +MLTTMP = $66 ;1-byte temporary +OPNTMP = $66 ;FIRST BYTE IS USED IN OPEN AS TEMP +TOADR = $66 ;##rev2## 2-byte destination address + +SAVADR = $68 ;2-byte saved address +FRMADR = $68 ;##rev2## 2-byte source address + +RAMTOP = $6A ;RAM SIZE DEFINED BY POWER ON LOGIC +BUFCNT = $6B ;BUFFER COUNT +BUFSTR = $6C ;EDITOR GETCH POINTER +BITMSK = $6E ;BIT MASK +SHFAMT = $6F ;1-byte shift amount for pixel justifucation +ROWAC = $70 ;2-byte draw working row +COLAC = $72 ;2-byte draw working column +ENDPT = $74 ;2-byte end point +DELTAR = $76 ;1-byte row difference +DELTAC = $77 ;2-byte column difference +KEYDEF = $79 ;##1200xl## 2-byte key definition table address +;ROWINC = $79 ;##old## +;COLINC = $7A ;##old## +SWPFLG = $7B ;NON-0 1F TXT AND REGULAR RAM IS SWAPPED +HOLDCH = $7C ;CH IS MOVED HERE IN KGETCH BEFORE CNTL & SH +INSDAT = $7D ;1-byte temporary +COUNTR = $7E ;2-byte draw iteration count + +; Floating Point Package Page Zero Address Equates + +FR0 = $D4 ;6-byte register 0 +FR0M = $D5 ;##rev2## 5-byte register 0 mantissa +QTEMP = $D9 ;##rev2## 1-byte temporary + +FRE = $DA ;6-byte (internal) register E + +FR1 = $E0 ;FP REG1 +FR1M = $E1 ;##rev2## 5-byte register 1 mantissa + +FR2 = $E6 ;6-byte (internal) register 2 + +FRX = $EC ;1-byte temporary + +EEXP = $ED ;VALUE OF E + +FRSIGN = $EE ;##rev2## 1-byte floating point sign +NSIGN = $EE ;SIGN OF # + +PLYCNT = $EF ;##rev2## 1-byte polynomial degree +ESIGN = $EF ;SIGN OF EXPONENT + +SGNFLG = $F0 ;##rev2## 1-byte sign flag +FCHRFLG = $F0 ;1ST CHAR FLAG + +XFMFLG = $F1 ;##rev2## 1-byte transform flag +DIGRT = $F1 ;# OF DIGITS RIGHT OF DECIMAL + +CIX = $F2 ;CURRENT INPUT INDEX +INBUFF = $F3 ;POINTS TO USER'S LINE INPUT BUFFER + +ZTEMP1 = $F5 ;2-byte temporary +ZTEMP4 = $F7 ;2-byte temporary +ZTEMP3 = $F9 ;2-byte temporary + +;DEGFLG = $FB ;##old## same as RADFLG +;RADFLG = $FB ;##old## 0=RADIANS, 6=DEGREES + +FLPTR = $FC ;2-byte floating point number pointer +FPTR2 = $FE ;2-byte floating point number pointer + +;------------------------------------------------------------------------- +; Page Two Address Equates +;------------------------------------------------------------------------- + +INTABS = $0200 ;INTERRUPT RAM + +VDSLST = $0200 ;DISPLAY LIST NMI VECTOR +VPRCED = $0202 ;PROCEED LINE IRQ VECTOR +VINTER = $0204 ;INTERRUPT LINE IRQ VECTOR +VBREAK = $0206 ;SOFTWARE BREAK (00) INSTRUCTION IRQ VECTOR +VKEYBD = $0208 ;POKEY KEYBOARD IRQ VECTOR +VSERIN = $020A ;POKEY SERIAL INPUT READY IRQ +VSEROR = $020C ;POKEY SERIAL OUTPUT READY IRQ +VSEROC = $020E ;POKEY SERIAL OUTPUT COMPLETE IRQ +VTIMR1 = $0210 ;POKEY TIMER 1 IRG +VTIMR2 = $0212 ;POKEY TIMER 2 IRG +VTIMR4 = $0214 ;POKEY TIMER 4 IRG +VIMIRQ = $0216 ;IMMEDIATE IRG VECTOR +CDTMV1 = $0218 ;COUNT DOWN TIMER 1 +CDTMV2 = $021A ;COUNT DOWN TIMER 2 +CDTMV3 = $021C ;COUNT DOWN TIMER 3 +CDTMV4 = $021E ;COUNT DOWN TIMER 4 +CDTMV5 = $0220 ;COUNT DOWN TIMER 5 +VVBLKI = $0222 ;IMMEDIATE VERTICAL BLANK NMI VECTOR +VVBLKD = $0224 ;DEFERRED VERTICAL BLANK NMI VECTOR +CDTMA1 = $0226 ;COUNT DOWN TIMER 1 JSR ADDRESS +CDTMA2 = $0228 ;COUNT DOWN TIMER 2 JSR ADDRESS +CDTMF3 = $022A ;COUNT DOWN TIMER 3 FLAG +SRTIMR = $022B ;SOFTWARE REPEAT TIMER +CDTMF4 = $022C ;COUNT DOWN TIMER 4 FLAG +INTEMP = $022D ;IAN'S TEMP +CDTMF5 = $022E ;COUNT DOWN TIMER FLAG 5 +SDMCTL = $022F ;SAVE DMACTL REGISTER +SDLSTL = $0230 ;SAVE DISPLAY LIST LOW BYTE +SDLSTH = $0231 ;SAVE DISPLAY LIST HI BYTE +SSKCTL = $0232 ;SKCTL REGISTER RAM +LCOUNT = $0233 ;##1200xl## 1-byte relocating loader record +LPENH = $0234 ;LIGHT PEN HORIZONTAL VALUE +LPENV = $0235 ;LIGHT PEN VERTICAL VALUE +BRKKY = $0236 ;BREAK KEY VECTOR +;RELADR = $0238 ;##1200xl## 2-byte relocatable loader address +VPIRQ = $0238 ;##rev2## 2-byte parallel device IRQ vector +CDEVIC = $023A ;COMMAND FRAME BUFFER - DEVICE +CCOMND = $023B ;COMMAND +CAUX1 = $023C ;COMMAND AUX BYTE 1 +CAUX2 = $023D ;COMMAND AUX BYTE 2 + +TEMP = $023E ;TEMPORARY RAM CELL + +ERRFLG = $023F ;ERROR FLAG - ANY DEVICE ERROR EXCEPT TIME OUT + +DFLAGS = $0240 ;DISK FLAGS FROM SECTOR ONE +DBSECT = $0241 ;NUMBER OF DISK BOOT SECTORS +BOOTAD = $0242 ;ADDRESS WHERE DISK BOOT LOADER WILL 13E PUT +COLDST = $0244 ;COLDSTART FLAG (1=IN MIDDLE OF COLDSTART> +RECLEN = $0245 ;##1200xl## 1-byte relocating loader record length +DSKTIM = $0246 ;DISK TIME OUT REGISTER +;LINBUF = $0247 ;##old## CHAR LINE BUFFER +PDVMSK = $0247 ;##rev2## 1-byte parallel device selection mask +SHPDVS = $0248 ;##rev2## 1-byte PDVS (parallel device select) +PDIMSK = $0249 ;##rev2## 1-byte parallel device IRQ selection +RELADR = $024A ;##rev2## 2-byte relocating loader relative adr. +PPTMPA = $024C ;##rev2## 1-byte parallel device handler temporary +PPTMPX = $024D ;##rev2## 1-byte parallel device handler temporary + +CHSALT = $026B ;##1200xl## 1-byte character set alternate +VSFLAG = $026C ;##1200xl## 1-byte fine vertical scroll count +KEYDIS = $026D ;##1200xl## 1-byte keyboard disable +FINE = $026E ;##1200xl## 1-byte fine scrolling mode +GPRIOR = $026F ;GLOBAL PRIORITY CELL + +PADDL0 = $0270 ;1-byte potentiometer 0 +PADDL1 = $0271 ;1-byte potentiometer 1 +PADDL2 = $0272 ;1-byte potentiometer 2 +PADDL3 = $0273 ;1-byte potentiometer 3 +PADDL4 = $0274 ;1-byte potentiometer 4 +PADDL5 = $0275 ;1-byte potentiometer 5 +PADDL6 = $0276 ;1-byte potentiometer 6 +PADDL7 = $0277 ;1-byte potentiometer 7 + +STICK0 = $0278 ;1-byte joystick 0 +STICK1 = $0279 ;1-byte joystick 1 +STICK2 = $027A ;1-byte joystick 2 +STICK3 = $027B ;1-byte joystick 3 + +PTRIG0 = $027C ;1-byte paddle trigger 0 +PTRIG1 = $027D ;1-byte paddle trigger 1 +PTRIG2 = $027E ;1-byte paddle trigger 2 +PTRIG3 = $027F ;1-byte paddle trigger 3 +PTRIG4 = $0280 ;1-byte paddle trigger 4 +PTRIG5 = $0281 ;1-byte paddle trigger 5 +PTRIG6 = $0281 ;1-byte paddle trigger 6 +PTRIG7 = $0283 ;1-byte paddle trigger 7 + +STRIG0 = $0284 ;1-byte joystick trigger 0 +STRIG1 = $0285 ;1-byte joystick trigger 1 +STRIG2 = $0286 ;1-byte joystick trigger 2 +STRIG3 = $0287 ;1-byte joystick trigger 3 + +;CSTAT = $0288 ;##old## cassette status register +HIBYTE = $0288 ;##1200xl## 1-byte relocating loader high byte +WMODE = $0289 ;1-byte cassette WRITE mode +BLIM = $028A ;1-byte cassette buffer limit +IMASK = $028B ;##rev2## (not used) +JVECK = $028C ;2-byte jump vector or temporary +NEWADR = $028E ;##1200xl## 2-byte relocating address +TXTROW = $0290 ;TEXT ROWCRS +TXTCOL = $0291 ;TEXT COLCRS +TINDEX = $0293 ;TEXT INDEX +TXTMSC = $0294 ;FOOLS CONVRT INTO NEW MSC +TXTOLD = $0296 ;OLDROW & OLDCOL FOR TEXT (AND THEN SOME) +;TMPX1 = $029C ;##old## 1-byte temporary register +CRETRY = $029C ;##1200xl## 1-byte number of command frame retries +HOLD3 = $029D ;1-byte temporary +SUBTMP = $029E ;1-byte temporary +HOLD2 = $029F ;1-byte (not used) +DMASK = $02A0 ;1-byte display (pixel location) mask +TMPLBT = $02A1 ;1-byte (not used) +ESCFLG = $02A2 ;ESCAPE FLAG +TABMAP = $02A3 ;15-byte (120 bit) tab stop bit map +LOGMAP = $02B2 ;LOGICAL LINE START BIT MAP +INVFLG = $02B6 ;INVERSE VIDEO FLAG (TOGGLED BY ATARI KEY) +FILFLG = $02B7 ;RIGHT FILL FLAG FOR DRAW +TMPROW = $02B8 ;1-byte temporary row +TMPCOL = $02B9 ;2-byte temporary column +SCRFLG = $02BB ;SET IF SCROLL OCCURS +HOLD4 = $02BC ;TEMP CELL USED IN DRAW ONLY +;HOLD5 = $02BD ;##old## DITTO +DRETRY = $02BD ;##1200xl## 1-byte number of device retries +SHFLOK = $02BE ;1-byte shift/control lock flags +BOTSCR = $02BF ;BOTTOM OF SCREEN 24 NORM 4 SPLIT + +PCOLR0 = $02C0 ;1-byte player-missile 0 color/luminance +PCOLR1 = $02C1 ;1-byte player-missile 1 color/luminance +PCOLR2 = $02C2 ;1-byte player-missile 2 color/luminance +PCOLR3 = $02C3 ;1-byte player-missile 3 color/luminance + +COLOR0 = $02C4 ;1-byte playfield 0 color/luminance +COLOR1 = $02C5 ;1-byte playfield 1 color/luminance +COLOR2 = $02C6 ;1-byte playfield 2 color/luminance +COLOR3 = $02C7 ;1-byte playfield 3 color/luminance + +COLOR4 = $02C8 ;1-byte background color/luminance + +PARMBL = $02C9 ;##rev2## 6-byte relocating loader parameter +RUNADR = $02C9 ;##1200xl## 2-byte run address +HIUSED = $02CB ;##1200xl## 2-byte highest non-zero page address +ZHIUSE = $02CD ;##1200xl## 2-byte highest zero page address + +OLDPAR = $02CF ;##rev2## 6-byte relocating loader parameter +GBYTEA = $02CF ;##1200xl## 2-byte GET-BYTE routine address +LOADAD = $02D1 ;##1200xl## 2-byte non-zero page load address +ZLOADA = $02D3 ;##1200xl## 2-byte zero page load address + +DSCTLN = $02D5 ;##1200xl## 2-byte disk sector length +ACMISR = $02D7 ;##1200xl## 2-byte ACMI interrupt service routine +KRPDEL = $02D9 ;##1200xl## 1-byte auto-repeat delay +KEYREP = $02DA ;##1200xl## 1-byte auto-repeat rate +NOCLIK = $02DB ;##1200xl## 1-byte key click disable +HELPFG = $02DC ;##1200xl## 1-byte HELP key flag (0 = no HELP) +DMASAV = $02DD ;##1200xl## 1-byte SDMCTL save/restore +PBPNT = $02DE ;##1200xl## 1-byte printer buffer pointer +PBUFSZ = $02DF ;##1200xl## 1-byte printer buffer size + +GLBABS = $02E0 ;4-byte global variables for non-DOS users +RUNAD = $02E0 ;##map## 2-byte binary file run address +INITAD = $02E2 ;##map## 2-byte binary file initialization address + +RAMSIZ = $02E4 ;RAM SIZE (HI BYTE ONLY) +MEMTOP = $02E5 ;TOP OF AVAILABLE USER MEMORY +MEMLO = $02E7 ;BOTTOM OF AVAILABLE USER MEMORY +HNDLOD = $02E9 ;##1200xl## 1-byte user load flag +DVSTAT = $02EA ;STATUS BUFFER +CBAUDL = $02EE ;1-byte low cassette baud rate +CBAUDH = $02EF ;1-byte high cassette baud rate +CRSINH = $02F0 ;CURSOR INHIBIT (00 = CURSOR ON) +KEYDEL = $02F1 ;KEY DELAY +CH1 = $02F2 ;1-byte prior keyboard character +CHACT = $02F3 ;CHACTL REGISTER RAM +CHBAS = $02F4 ;CHBAS REGISTER RAM + +NEWROW = $02F5 ;##1200xl## 1-byte draw destination row +NEWCOL = $02F6 ;##1200xl## 2-byte draw destination column +ROWINC = $02F8 ;##1200xl## 1-byte draw row increment +COLINC = $02F9 ;##1200xl## 1-byte draw column increment + +CHAR = $02FA ;1-byte internal character +ATACHR = $02FB ;ATASCII CHARACTER +CH = $02FC ;GLOBAL VARIABLE FOR KEYBOARD +FILDAT = $02FD ;RIGHT FILL DATA +DSPFLG = $02FE ;DISPLAY FLAG DISPLAY CNTLS IF NON-ZERO +SSFLAG = $02FF ;START/STOP FLAG FOR PAGING (CNTL 1). CLEARE + +;------------------------------------------------------------------------- +; Page Three Address Equates +;------------------------------------------------------------------------- + +DCB = $0300 ;DEVICE CONTROL BLOCK +DDEVIC = $0300 ;PERIPHERAL UNIT 1 BUS I.D. NUMBER +DUNIT = $0301 ;UNIT NUMBER +DCOMND = $0302 ;BUS COMMAND +DSTATS = $0303 ;COMMAND TYPE/STATUS RETURN +DBUFLO = $0304 ;1-byte low data buffer address +DBUFHI = $0305 ;1-byte high data buffer address +DTIMLO = $0306 ;DEVICE TIME OUT IN 1 SECOND UNITS +DUNUSE = $0307 ;UNUSED BYTE +DBYTLO = $0308 ;1-byte low number of bytes to transfer +DBYTHI = $0309 ;1-byte high number of bytes to transfer +DAUX1 = $030A ;1-byte first command auxiliary +DAUX2 = $030B ;1-byte second command auxiliary + +TIMER1 = $030C ;INITIAL TIMER VALUE +;ADDCOR = $030E ;##old## ADDITION CORRECTION +JMPERS = $030E ;##1200xl## 1-byte jumper options +CASFLG = $030F ;CASSETTE MODE WHEN SET +TIMER2 = $0310 ;2-byte final baud rate timer value +TEMP1 = $0312 ;TEMPORARY STORAGE REGISTER +;TEMP2 = $0314 ;##old## TEMPORARY STORAGE REGISTER +TEMP2 = $0313 ;##1200xl## 1-byte temporary +PTIMOT = $0314 ;##1200xl## 1-byte printer timeout +TEMP3 = $0315 ;TEMPORARY STORAGE REGISTER +SAVIO = $0316 ;SAVE SERIAL IN DATA PORT +TIMFLG = $0317 ;TIME OUT FLAG FOR BAUD RATE CORRECTION +STACKP = $0318 ;SIO STACK POINTER SAVE CELL +TSTAT = $0319 ;TEMPORARY STATUS HOLDER + +HATABS = $031A ;35-byte handler address table (was 38 bytes) +PUPBT1 = $033D ;##1200xl## 1-byte power-up validation byte 1 +PUPBT2 = $033E ;##1200xl## 1-byte power-up validation byte 2 +PUPBT3 = $033F ;##1200xl## 1-byte power-up validation byte 3 + +IOCB = $0340 ;I/O CONTROL BLOCKS +ICHID = $0340 ;HANDLER INDEX NUMBER (FF=IOCB FREE) +ICDNO = $0341 ;DEVICE NUMBER (DRIVE NUMBER) +ICCOM = $0342 ;COMMAND CODE +ICSTA = $0343 ;STATUS OF LAST IOCB ACTION +ICBAL = $0344 ;1-byte low buffer address +ICBAH = $0345 ;1-byte high buffer address +ICPTL = $0346 ;1-byte low PUT-BYTE routine address - 1 +ICPTH = $0347 ;1-byte high PUT-BYTE routine address - 1 +ICBLL = $0348 ;1-byte low buffer length +ICBLH = $0349 ;1-byte high buffer length +ICAX1 = $034A ;1-byte first auxiliary information +ICAX2 = $034B ;1-byte second auxiliary information +ICSPR = $034C ;FOUR SPARE BYTES + +PRNBUF = $03C0 ;PRINTER BUFFER +SUPERF = $03E8 ;##1200xl## 1-byte editor super function flag +CKEY = $03E9 ;##1200xl## 1-byte cassette boot request flag +CASSBT = $03EA ;##1200xl## 1-byte cassette boot flag +CARTCK = $03EB ;##1200xl## 1-byte cartridge equivalence check +DERRF = $03EC ;##rev2## 1-byte screen OPEN error flag + +; Remainder of Page Three Not Cleared upon Reset + +ACMVAR = $03ED ;##1200xl## 11 bytes reserved for ACMI +BASICF = $03F8 ;##rev2## 1-byte BASIC switch flag +MINTLK = $03F9 ;##1200xl## 1-byte ACMI module interlock +GINTLK = $03FA ;##1200xl## 1-byte cartridge interlock +CHLINK = $03FB ;##1200xl## 2-byte loaded handler chain link +CASBUF = $03FD ;CASSETTE BUFFER + +;------------------------------------------------------------------------- +; Page Four/Five Address Equates +;------------------------------------------------------------------------- + +; USER AREA STARTS HERE AND GOES TO END OF PAGE FIVE +USAREA = $0480 ;128 bytes reserved for application + +LBPR1 = $057E ;LBUFF PREFIX 1 +LBPR2 = $057F ;LBUFF PREFIX 2 +LBUFF = $0580 ;128-byte line buffer + +PLYARG = $05E0 ;6-byte floating point polynomial argument +FPSCR = $05E6 ;6-byte floating point temporary +FPSCR1 = $05EC ;6-byte floating point temporary + +;LBFEND = $05FF ;##old## END OF LBUFF + +;------------------------------------------------------------------------- +; Cartridge Address Equates +;------------------------------------------------------------------------- + +CARTCS = $BFFA ;##rev2## 2-byte cartridge coldstart address +CART = $BFFC ;##rev2## 1-byte cartridge present indicator +CARTFG = $BFFD ;##rev2## 1-byte cartridge flags +CARTAD = $BFFE ;##rev2## 2-byte cartridge start vector + +;------------------------------------------------------------------------- +; CTIA/GTIA Address Equates +;------------------------------------------------------------------------- + +GTIA = $D000 ;CTIA/GTIA area + +; Read/Write Addresses + +CONSOL = $D01F ;console switches and speaker control + +; Read Addresses + +M0PF = $D000 ;missile 0 and playfield collision +M1PF = $D001 ;missile 1 and playfield collision +M2PF = $D002 ;missile 2 and playfield collision +M3PF = $D003 ;missile 3 and playfield collision + +P0PF = $D004 ;player 0 and playfield collision +P1PF = $D005 ;player 1 and playfield collision +P2PF = $D006 ;player 2 and playfield collision +P3PF = $D007 ;player 3 and playfield collision + +M0PL = $D008 ;missile 0 and player collision +M1PL = $D009 ;missile 1 and player collision +M2PL = $D00A ;missile 2 and player collision +M3PL = $D00B ;missile 3 and player collision + +P0PL = $D00C ;player 0 and player collision +P1PL = $D00D ;player 1 and player collision +P2PL = $D00E ;player 2 and player collision +P3PL = $D00F ;player 3 and player collision + +TRIG0 = $D010 ;joystick trigger 0 +TRIG1 = $D011 ;joystick trigger 1 + +TRIG2 = $D012 ;cartridge interlock +TRIG3 = $D013 ;ACMI module interlock + +PAL = $D014 ;##rev2## PAL/NTSC indicator + +; Write Addresses + +HPOSP0 = $D000 ;player 0 horizontal position +HPOSP1 = $D001 ;player 1 horizontal position +HPOSP2 = $D002 ;player 2 horizontal position +HPOSP3 = $D003 ;player 3 horizontal position + +HPOSM0 = $D004 ;missile 0 horizontal position +HPOSM1 = $D005 ;missile 1 horizontal position +HPOSM2 = $D006 ;missile 2 horizontal position +HPOSM3 = $D007 ;missile 3 horizontal position + +SIZEP0 = $D008 ;player 0 size +SIZEP1 = $D009 ;player 1 size +SIZEF2 = $D00A ;player 2 size +SIZEP3 = $D00B ;player 3 size + +SIZEM = $D00C ;missile sizes + +GRAFP0 = $D00D ;player 0 graphics +GRAFP1 = $D00E ;player 1 graphics +GRAFP2 = $D00F ;player 2 graphics +GRAFP3 = $D010 ;player 3 graphics + +GRAFM = $D011 ;missile graphics + +COLPM0 = $D012 ;player-missile 0 color/luminance +COLPM1 = $D013 ;player-missile 1 color/luminance +COLPM2 = $D014 ;player-missile 2 color/luminance +COLPM3 = $D015 ;player-missile 3 color/luminance + +COLPF0 = $D016 ;playfield 0 color/luminance +COLPF1 = $D017 ;playfield 1 color/luminance +COLPF2 = $D018 ;playfield 2 color/luminance +COLPF3 = $D019 ;playfield 3 color/luminance + +COLBK = $D01A ;background color/luminance + +PRIOR = $D01B ;priority select +VDELAY = $D01C ;vertical delay +GRACTL = $D01D ;graphic control +HITCLR = $D01E ;collision clear + +;------------------------------------------------------------------------- +; PBI Address Equates +;------------------------------------------------------------------------- + +PBI = $D100 ;##rev2## parallel bus interface area + +; Read Addresses + +PDVI = $D1FF ;##rev2## parallel device IRQ status + +; Write Addresses + +PDVS = $D1FF ;##rev2## parallel device select + +; PBI RAM Address Equates + +PBIRAM = $D600 ;##rev2## parallel bus interface RAM area + +; Parallel Device Address Equates + +PDID1 = $D803 ;##rev2## parallel device ID 1 +PDIDV = $D805 ;##rev2## parallel device I/O vector +PDIRQV = $D808 ;##rev2## parallel device IRQ vector +PDID2 = $D80B ;##rev2## parallel device ID 2 +PDVV = $D80D ;##rev2## parallel device vector table + +;------------------------------------------------------------------------- +; POKEY Address Equates +;------------------------------------------------------------------------- + +POKEY = $D200 ;POKEY area + +; Read Addresses + +POT0 = $D200 ;potentiometer 0 +POT1 = $D201 ;potentiometer 1 +POT2 = $D202 ;potentiometer 2 +POT3 = $D203 ;potentiometer 3 +POT4 = $D204 ;potentiometer 4 +POT5 = $D205 ;potentiometer 5 +POT6 = $D206 ;potentiometer 6 +POT7 = $D207 ;potentiometer 7 + +ALLPOT = $D208 ;potentiometer port status +KBCODE = $D209 ;keyboard code +RANDOM = $D20A ;random number generator +SERIN = $D20D ;serial port input +IRQST = $D20E ;IRQ interrupt status +SKSTAT = $D20F ;serial port and keyboard status + +; Write Addresses + +AUDF1 = $D200 ;channel 1 audio frequency +AUDC1 = $D201 ;channel 1 audio control + +AUDF2 = $D202 ;channel 2 audio frequency +AUDC2 = $D203 ;channel 2 audio control + +AUDF3 = $D204 ;channel 3 audio frequency +AUDC3 = $D205 ;channel 3 audio control + +AUDF4 = $D206 ;channel 4 audio frequency +AUDC4 = $D207 ;channel 4 audio control + +AUDCTL = $D208 ;audio control +STIMER = $D209 ;start timers +SKRES = $D20A ;reset SKSTAT status +POTGO = $D20B ;start potentiometer scan sequence +SEROUT = $D20D ;serial port output +IRQEN = $D20E ;IRQ interrupt enable +SKCTL = $D20F ;serial port and keyboard control + +;------------------------------------------------------------------------- +; PIA Address Equates +;------------------------------------------------------------------------- + +PIA = $D300 ;PIA area + +PORTA = $D300 ;port A direction register or jacks one/two +PORTB = $D301 ;port B direction register or memory management + +PACTL = $D302 ;port A control +PBCTL = $D303 ;port B control + +;------------------------------------------------------------------------- +; ANTIC Address Equates +;------------------------------------------------------------------------- + +ANTIC = $D400 ;ANTIC area + +; Read Addresses + +VCOUNT = $D40B ;vertical line counter +PENH = $D40C ;light pen horizontal position +PENV = $D40D ;light pen vertical position +NMIST = $D40F ;NMI interrupt status + +; Write Addresses + +DMACTL = $D400 ;DMA control +CHACTL = $D401 ;character control +DLISTL = $D402 ;low display list address +DLISTH = $D403 ;high display list address +HSCROL = $D404 ;horizontal scroll +VSCROL = $D405 ;vertical scroll +PMBASE = $D407 ;player-missile base address +CHBASE = $D409 ;character base address +WSYNC = $D40A ;wait for HBLANK synchronization +NMIEN = $D40E ;NMI enable +NMIRES = $D40F ;NMI iterrupt reset + +;------------------------------------------------------------------------- +; Floating Point Package Address Equates +;------------------------------------------------------------------------- + +AFP = $D800 ;convert ASCII to floating point +FASC = $D8E6 ;convert floating point to ASCII +IFP = $D9AA ;convert integer to floating point +FPI = $D9D2 ;convert floating point to integer +ZFR0 = $DA44 ;zero FR0 +ZF1 = $DA46 ;zero floating point number +FSUB = $DA60 ;subtract floating point numbers +FADD = $DA66 ;add floating point numbers +FMUL = $DADB ;multiply floating point numbers +FDIV = $DB28 ;divide floating point numbers +PLYEVL = $DD40 ;evaluate floating point polynomial +FLD0R = $DD89 ;load floating point number +FLD0P = $DD8D ;load floating point number +FLD1R = $DD98 ;load floating point number +PLD1P = $DD9C ;load floating point number +FST0R = $DDA7 ;store floating point number +FST0P = $DDAB ;store floating point number +FMOVE = $DDB6 ;move floating point number +LOG = $DECD ;calculate floating point logarithm +LOG10 = $DED1 ;calculate floating point base 10 logarithm +EXP = $DDC0 ;calculate floating point exponential +EXP10 = $DDCC ;calculate floating point base 10 exponential + +;------------------------------------------------------------------------- +; Device Handler Vector Table Address Equates +;------------------------------------------------------------------------- + +EDITRV = $E400 ;editor handler vector table +SCRENV = $E410 ;screen handler vector table +KEYBDV = $E420 ;keyboard handler vector table +PRINTV = $E430 ;printer handler vector table +CASETV = $E440 ;cassette handler vector table + +;------------------------------------------------------------------------- +; Jump Vector Address Equates +;------------------------------------------------------------------------- + +DISKIV = $E450 ;vector to initialize DIO +DSKINV = $E453 ;vector to DIO +CIOV = $E456 ;vector to CIO +SIOV = $E459 ;vector to SIO +SETVBV = $E45C ;vector to set VBLANK parameters +SYSVBV = $E45F ;vector to process immediate VBLANK +XITVBV = $E462 ;vector to process deferred VBLANK +SIOINV = $E465 ;vector to initialize SIO +SENDEV = $E468 ;vector to enable SEND +INTINV = $E46B ;vector to initialize interrupt handler +CIOINV = $E46E ;vector to initialize CIO +BLKBDV = $E471 ;vector to power-up display +WARMSV = $E474 ;vector to warmstart +COLDSV = $E477 ;vector to coldstart +RBLOKV = $E47A ;vector to read cassette block +CSOPIV = $E47D ;vector to open cassette for input +VCTABL = $E480 ;RAM vector initial value table +PUPDIV = $E480 ;##rev2## vector to power-up display +SLFTSV = $E483 ;##rev2## vector to self-test +PHENTV = $E486 ;##rev2## vector to enter peripheral handler +PHUNLV = $E489 ;##rev2## vector to unlink peripheral handler +PHINIV = $E48C ;##rev2## vector to initialize peripheral handler +GPDVV = $E48F ;##rev2## generic parallel device handler vector + +; NOTE: OS rom self-test labels are not included in this file + +;------------------------------------------------------------------------- +; Some misc. stuff from the 400/800 rev.B source +;------------------------------------------------------------------------- + +; THE FOLLOWING ARE IN BASIC CARTRIDGE: +SIN = $BD81 ;FR0 <- SIN (FR0) DEGFLG (0=RAD,6=DEG) CARRY +COS = $BD73 ;FR0 <- COS (FR0) CARRY +ATAN = $BE43 ;FR0 <- ATAN(FR0) CARRY +SQR = $BEB1 ;FR0 <- ROOT(FR0) CARRY + +RADON = 0 ;INDICATES RADIANS +DEGON = 6 ;INDICATES DEGREES + +ASCZER = '0' ;ASCII ZERO +COLON = $3A ;ASCII COLON +CR = $9B ;SYSTEM EOL (CARRIAGE RETURN) + +;------------------------------------------------------------------------- +; 6502 +;------------------------------------------------------------------------- + +NMIVEC = $FFFA +RESVEC = $FFFC +IRQVEC = $FFFE + +;------------------------------------------------------------------------- +; BASIC +;------------------------------------------------------------------------- + +LOMEM = $80 ;2-byte low memory pointer +VNTP = $82 ;2-byte variable name table address +VNTD = $84 ;2-byte variable name table end + 1 +VVTP = $86 ;2-byte variable value table +STMTAB = $88 ;2-byte statement table address +STMCUR = $8A ;2-byte current statement pointer +STARP = $8C ;2-byte string and array table pointer +RUNSTK = $8E ;2-byte runtime stack address +;MEMTOP = $90 ;2-byte top of memory pointer +STOPLN = $BA ;2-byte stopped line number +ERRSAVE = $C3 ;1-byte error code +PTABW = $C9 ;1-byte tab width + +;------------------------------------------------------------------------- +; DOS +;------------------------------------------------------------------------- + +DOS = $0700 + +RENAME = $20 ;RENAME DISK FILE +DELETE = $21 ;DELETE DISK FILE +FORMAT = $21 ;FORMAT +LOCKFL = $23 ;LOCK FILE TO READ ONLY +UNLOCK = $24 ;UNLOCK LOCKED FILE +POINT = $25 ;POINT SECTOR +NOTE = $26 ;NOTE SECTOR + +; Command line table, Index values for (DOSVEC),Y -- COMTAB +; Compatible with OS/A+, DOS XL and SpartaDOS + +COMTAB = 0 ;DOS entry jump vector +ZCRNAME = 3 ;file name crunch routine jump vector +BUFOFF = 10 ;next parameter buffer offset +COMFNAM = 33 ;destination buffer for crunch routine +LBUF = 63 ;command line input buffer + +;------------------------------------------------------------------------- +; ATASCII CHARACTER DEFS +;------------------------------------------------------------------------- + +ATCLR = $7D ;CLEAR SCREEN CHARACTER +ATRUB = $7E ;BACK SPACE (RUBOUT) +ATTAB = $7F ;TAB +ATEOL = $9B ;END-OF-LINE +ATDELL = $9C ;Delete line +ATBEL = $FD ;CONSOLE BELL +ATURW = $1C ;UP-ARROW +ATDRW = $1D ;DOWN-ARROW +ATLRW = $1E ;LEFT-ARROW +ATRRW = $1F ;RIGHT-ARROW + +;------------------------------------------------------------------------- +; End of atari.inc +;------------------------------------------------------------------------- + diff --git a/libsrc/atari/break.s b/libsrc/atari/break.s new file mode 100644 index 000000000..975d38774 --- /dev/null +++ b/libsrc/atari/break.s @@ -0,0 +1,106 @@ +; +; Christian Groessler, 27-Feb-2000 +; +; void set_brk (unsigned Addr); +; void reset_brk (void); +; + + .export _set_brk, _reset_brk + .export _brk_a, _brk_x, _brk_y, _brk_sr, _brk_pc + .import _atexit + + .include "atari.inc" + + +.bss +_brk_a: .res 1 +_brk_x: .res 1 +_brk_y: .res 1 +_brk_sr: .res 1 +_brk_pc: .res 2 + +oldvec: .res 2 ; Old vector + + +.data +uservec: jmp $FFFF ; Patched at runtime + + +.code + +; Set the break vector +.proc _set_brk + + sta uservec+1 + stx uservec+2 ; Set the user vector + + lda oldvec + ora oldvec+1 ; Did we save the vector already? + bne L1 ; Jump if we installed the handler already + + lda VBREAK + sta oldvec + lda VBREAK+1 + sta oldvec+1 ; Save the old vector + + lda #<_reset_brk + ldx #>_reset_brk + jsr _atexit ; Install an exit handler + +L1: lda #brk_handler + sta VBREAK+1 + rts + +.endproc + + +; Reset the break vector +.proc _reset_brk + + lda oldvec + sta VBREAK + lda oldvec+1 + sta VBREAK+1 + rts + +.endproc + + + +; Break handler, called if a break occurs + +.proc brk_handler + + sty _brk_y + stx _brk_x + pla + sta _brk_a + pla + and #$EF ; Clear break bit + sta _brk_sr + pla ; PC low + sec + sbc #2 ; Point to start of brk + sta _brk_pc + pla ; PC high + sbc #0 + sta _brk_pc+1 + + jsr uservec ; Call the user's routine + + lda _brk_pc+1 + pha + lda _brk_pc + pha + lda _brk_sr + pha + ldx _brk_x + ldy _brk_y + lda _brk_a + rti ; Jump back... + +.endproc + + diff --git a/libsrc/atari/cclear.s b/libsrc/atari/cclear.s new file mode 100644 index 000000000..f4ee4e94f --- /dev/null +++ b/libsrc/atari/cclear.s @@ -0,0 +1,34 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void cclearxy (unsigned char x, unsigned char y, unsigned char length); +; void cclear (unsigned char length); +; + + .export _cclearxy, _cclear + .import popa, _gotoxy, cputdirect + .importzp tmp1 + +_cclearxy: + pha ; Save the length + jsr popa ; Get y + jsr _gotoxy ; Call this one, will pop params + pla ; Restore the length and run into _cclear + +_cclear: + cmp #0 ; Is the length zero? + beq L9 ; Jump if done + sta tmp1 +.ifdef DIRECT_SCREEN +L1: lda #0 ; Blank - screen code +.else +L1: lda #$20 ; Blank +.endif + jsr cputdirect ; Direct output + dec tmp1 + bne L1 +L9: rts + + + + diff --git a/libsrc/atari/cgetc.s b/libsrc/atari/cgetc.s new file mode 100644 index 000000000..f64d97b37 --- /dev/null +++ b/libsrc/atari/cgetc.s @@ -0,0 +1,17 @@ +; +; get a kbd char. +; +; char cgetc(void) +; + + .include "atari.inc" + .export _cgetc + +_cgetc: + lda KEYBDV+5 + pha + lda KEYBDV+4 + pha + rts + ldx #0 + rts diff --git a/libsrc/atari/chline.s b/libsrc/atari/chline.s new file mode 100644 index 000000000..6b5e6a19b --- /dev/null +++ b/libsrc/atari/chline.s @@ -0,0 +1,34 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void chlinexy (unsigned char x, unsigned char y, unsigned char length); +; void chline (unsigned char length); +; + + .export _chlinexy, _chline + .import popa, _gotoxy, cputdirect + .importzp tmp1 + +_chlinexy: + pha ; Save the length + jsr popa ; Get y + jsr _gotoxy ; Call this one, will pop params + pla ; Restore the length + +_chline: + cmp #0 ; Is the length zero? + beq L9 ; Jump if done + sta tmp1 +.ifdef DIRECT_SCREEN +L1: lda #$12+64 ; Horizontal line, screen code +.else +L1: lda #$12 ; Horizontal line +.endif + jsr cputdirect ; Direct output + dec tmp1 + bne L1 +L9: rts + + + + diff --git a/libsrc/atari/close.s b/libsrc/atari/close.s new file mode 100644 index 000000000..c7995e953 --- /dev/null +++ b/libsrc/atari/close.s @@ -0,0 +1,36 @@ +; +; Christian Groessler, May-2000 +; +; int close(int fd); +; + + .include "atari.inc" + .export _close + .import __do_oserror,popax,__oserror + .import fdtoiocb_down,__inviocb + +.proc _close + jsr popax + jsr fdtoiocb_down ; get iocb index into X and decr. usage count + bmi inverr + bne ok ; not last one -> don't close yet +; asl a +; asl a +; asl a +; asl a +; tax + lda #CLOSE + sta ICCOM,x + jsr CIOV + bpl ok + jmp __do_oserror + +ok: ldx #0 + stx __oserror ; clear system specific error code + txa + rts + +inverr: jmp __inviocb + +.endproc + diff --git a/libsrc/atari/clrscr.s b/libsrc/atari/clrscr.s new file mode 100644 index 000000000..ac8f89619 --- /dev/null +++ b/libsrc/atari/clrscr.s @@ -0,0 +1,47 @@ +; +; Christian Groessler, Apr-2000 +; +; void clrscr (void); +; + + .export _clrscr + + .include "atari.inc" + +.ifdef DIRECT_SCREEN + + .importzp ptr1 + +_clrscr:lda SAVMSC ; screen memory + sta ptr1 + lda SAVMSC+1 + clc + adc #>(40*24) + sta ptr1+1 + lda #0 ; screen code of space char + ldy #<(40*24) ; 40x24: size of default atari screen + ldx #>(40*24) +_clr1: sta (ptr1),y + dey + bne _clr1 + sta (ptr1),y + dex + bmi done + ldy ptr1+1 + dey + sty ptr1+1 + ldy #255 + jmp _clr1 + +done: sta COLCRS + sta ROWCRS + rts + +.else + + .import putchar +_clrscr: + lda #ATCLR + jmp putchar + +.endif diff --git a/libsrc/atari/color.s b/libsrc/atari/color.s new file mode 100644 index 000000000..ea398edb2 --- /dev/null +++ b/libsrc/atari/color.s @@ -0,0 +1,33 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char __fastcall__ textcolor (unsigned char color); +; unsigned char __fastcall__ bgcolor (unsigned char color); +; unsigned char __fastcall__ bordercolor (unsigned char color); +; + + + .export _textcolor, _bgcolor, _bordercolor + + .include "atari.inc" + +_textcolor: + ldx COLOR1 ; get old value + sta COLOR1 ; set new value + txa + rts + + +_bgcolor: + ldx COLOR2 ; get old value + sta COLOR2 ; set new value + txa + rts + + +_bordercolor: + ldx COLOR4 ; get old value + sta COLOR4 ; set new value + txa + rts + diff --git a/libsrc/atari/conio.s b/libsrc/atari/conio.s new file mode 100644 index 000000000..651d0e511 --- /dev/null +++ b/libsrc/atari/conio.s @@ -0,0 +1,20 @@ +; +; Christian Groessler +; +; Low level stuff for screen output/console input +; + + .export initconio + .import xsize, ysize, plot + + .include "atari.inc" + +.code + +initconio: + ldx #40 + ldy #24 + stx xsize + sty ysize + rts + diff --git a/libsrc/atari/cputc.s b/libsrc/atari/cputc.s new file mode 100644 index 000000000..95bc7756c --- /dev/null +++ b/libsrc/atari/cputc.s @@ -0,0 +1,190 @@ +; +; Mark Keates, Christian Groessler +; +; void cputcxy (unsigned char x, unsigned char y, char c); +; void cputc (char c); +; + + .export _cputcxy, _cputc + .export plot, cputdirect, putchar + .import popa, _gotoxy + + .include "atari.inc" + +_cputcxy: + pha ; Save C + jsr popa ; Get Y + jsr _gotoxy ; Set cursor, drop x + pla ; Restore C + +.ifdef DIRECT_SCREEN + + .importzp tmp4,ptr4 + +_cputc: + cmp #$0D ; CR + bne L4 + lda #0 + sta COLCRS + beq plot ; return + +L4: cmp #$0A ; LF + beq newline + cmp #ATEOL ; Atari-EOL? + beq newline + + tay + rol a + rol a + rol a + rol a + and #3 + tax + tya + and #$9f + ora ataint,x + +cputdirect: ; accepts screen code + jsr putchar + +; advance cursor + inc COLCRS + lda COLCRS + cmp #40 + bcc plot + lda #0 + sta COLCRS + + .export newline +newline: + inc ROWCRS + lda ROWCRS + cmp #24 + bne plot + lda #0 + sta ROWCRS +plot: ldy COLCRS + ldx ROWCRS + rts + +putchar: + pha ; save char + lda #0 + sta tmp4 + lda ROWCRS + asl a + rol tmp4 + asl a + rol tmp4 ; row * 4 + adc ROWCRS + bcc L1 + inc tmp4 ; row * 5 +L1: asl a + rol tmp4 ; row * 10 + asl a + rol tmp4 + asl a + rol tmp4 ; row * 40 +L3: clc + adc SAVMSC ; add start of screen memory + sta ptr4 + lda tmp4 + adc SAVMSC+1 + sta ptr4+1 + pla ; get char again + ora INVFLG + ldy COLCRS + sta (ptr4),y + rts + + .rodata +ataint: .byte 64,0,32,96 + +;**************************************************************** +.else ;***** above DIRECT_SCREEN, below thru OS *************** +;**************************************************************** + + .import __do_oserror,cursor,__oserror + + +; Plot a character - also used as internal function + +_cputc: cmp #$0D ; CR? + bne L1 + lda #0 + sta COLCRS + beq plot ; Recalculate pointers + +; don't know whether this is needed. the compiler generates +; already ATEOL chars for \n + +L1: cmp #$0A ; LF? + bne L2 + lda #ATEOL + +; Printable char of some sort + +L2: +cputdirect: + pha + and #$7f + cmp #32 ; control char? + bcs goon + lda #$1b + jsr putchar +goon: pla + jsr putchar ; Write the character to the screen + +plot: ldy COLCRS + ldx ROWCRS + rts + +; Write one character to the screen without doing anything else, return X +; position in Y + +putchar: +.if 0 + tax + lda #>(retr-1) + pha + lda #<(retr-1) + pha + lda ICPTH + pha + lda ICPTL + pha + lda #0 + sta LOGCOL + txa + rts +retr: +.endif +.if 1 + pha + ldx #0 ; iocb #0 (screen editor) + txa + sta ICBLL,x + sta ICBLH,x + sta ICBAL,x + sta ICBAH,x + lda #PUTCHR + sta ICCOM,x + lda cursor + beq putc7 + lda #0 + beq putc8 +putc7: lda #1 +putc8: sta CRSINH + pla + jsr CIOV + bpl putc9 + jmp __do_oserror ; update system specific error code + +putc9: tya + ldx #0 + stx __oserror + ldy COLCRS +.endif + rts + +.endif ; not defined DIRECT_SCREEN diff --git a/libsrc/atari/crt0.s b/libsrc/atari/crt0.s new file mode 100644 index 000000000..39235e013 --- /dev/null +++ b/libsrc/atari/crt0.s @@ -0,0 +1,191 @@ +; +; Startup code for cc65 (ATARI version) +; +; Contributing authors: +; Mark Keates +; Freddy Offenga +; Christian Groessler +; +; This must be the *first* file on the linker command line +; + + .export _exit + .import getargs, argc, argv + .import __hinit, initconio, zerobss, pushax, doatexit + .import _main,__filetab + .import __CODE_LOAD__, __BSS_LOAD__ + + .include "atari.inc" + +; ------------------------------------------------------------------------ +; Define and export the ZP variables for the runtime + + .exportzp sp, sreg, regsave + .exportzp ptr1, ptr2, ptr3, ptr4 + .exportzp tmp1, tmp2, tmp3, tmp4 + .exportzp fntemp, regbank, zpspace + +sp = $D2 ; (2bytes) stack pointer +sreg = $D4 ; (2bytes) secondary register/high 16 bit for longs +regsave = $D6 ; (4bytes) slot to save/restore (E)AX into +ptr1 = $DA ; (2bytes) +ptr2 = $DC ; (2bytes) +ptr3 = $DE ; (2bytes) +ptr4 = $E0 ; (2bytes) +tmp1 = $E2 ; (1byte) +tmp2 = $E3 ; (1byte) +tmp3 = $E4 ; (1byte) +tmp4 = $E5 ; (1byte) +fntemp = $E6 ; (2bytes) pointer to file name +regbank = $E8 ; (6bytes) 6 byte register bank +zpspace = $EE - sp ; Zero page space allocated + +; ------------------------------------------------------------------------ +; EXE header + + .segment "EXEHDR" + .word $FFFF + .word __CODE_LOAD__ + .word __BSS_LOAD__ - 1 + .code + .reloc + +; ------------------------------------------------------------------------ +; Actual code + + rts ; fix for SpartaDOS / OS/A+ + ; they first call the entry point from AUTOSTRT and + ; then the load addess (this rts here). + ; We point AUTOSTRT directly after the rts. + +; Real entry point: + +; Save the zero page locations we need + + ldy #zpspace-1 +L1: lda sp,y + sta zpsave,y + dey + bpl L1 + +; Clear the BSS data + + jsr zerobss + +; setup the stack + + tsx + stx spsave + +; report memory usage and initialize stack pointer + + lda APPMHI + sta appmsav + lda APPMHI+1 + sta appmsav+1 + + lda #<$8000 + sta sp + sta APPMHI + lda #>$8000 + sta sp+1 ; Set argument stack ptr + sta APPMHI+1 + +; set left margin to 0 + + lda LMARGN + sta old_lmargin + lda #0 + sta LMARGN + +; set keyb to upper/lowercase mode + + ldx SHFLOK + stx old_shflok + sta SHFLOK + +; Initialize the heap + + jsr __hinit + +; Initialize conio stuff + + jsr initconio + + lda #$FF + sta CH + +; ugly hack for now: set stdio stream handles +; all to iocb #0 +; until we know where to go with fd<->iocb relation +; this won't stay here! + + lda #0 + sta __filetab + 2 + sta __filetab + 4 + +; Pass command line if present + + jsr getargs + + lda argc + ldx argc+1 + jsr pushax ; argc + lda #argv + jsr pushax ; argv + + ldy #4 ; Argument size + jsr _main ; call the users code + +; fall thru to exit... + +_exit: jsr doatexit ; call exit functions + + ldx spsave + txs ; Restore stack pointer + +; restore left margin + + lda old_lmargin + sta LMARGN + +; restore kb mode + + lda old_shflok + sta SHFLOK + +; restore APPMHI + + lda appmsav + sta APPMHI + lda appmsav+1 + sta APPMHI+1 + +; Copy back the zero page stuff + + ldy #zpspace-1 +L2: lda zpsave,y + sta sp,y + dey + bpl L2 + +; Back to DOS + + rts + +.data + +zpsave: .res zpspace + +.bss + +spsave: .res 1 +appmsav: .res 1 +old_shflok: .res 1 +old_lmargin: .res 1 + + .segment "AUTOSTRT" + .word $02E0 + .word $02E1 + .word __CODE_LOAD__ + 1 diff --git a/libsrc/atari/ctype.s b/libsrc/atari/ctype.s new file mode 100644 index 000000000..c0233b28e --- /dev/null +++ b/libsrc/atari/ctype.s @@ -0,0 +1,309 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; Character specification table. +; + +; The tables are readonly, put them into the code segment + +.code + +; Value that must be added to an upper case char to make it lower case +; char (example: for ASCII, this must be $E0). + + + .export __cdiff + +__cdiff: + .byte $E0 + + +; The following 256 byte wide table specifies attributes for the isxxx type +; of functions. Doing it by a table means some overhead in space, but it +; has major advantages: +; +; * It is fast. If it were'nt for the slow parameter passing of cc65, one +; could even define macros for the isxxx functions (this is usually +; done on other platforms). +; +; * It is highly portable. The only unportable part is the table itself, +; all real code goes into the common library. +; +; * We save some code in the isxxx functions. +; +; +; Bit assignments: +; +; 0 - Lower case char +; 1 - Upper case char +; 2 - Numeric digit +; 3 - Hex digit (both, lower and upper) +; 4 - Control character +; 5 - The space character itself +; 6 - Other whitespace (that is: '\f', '\n', '\r', '\t' and '\v') +; 7 - Space or tab character + + .export __ctype + +__ctype: + .byte $10 ; 0/00 ___ctrl_@___ + .byte $10 ; 1/01 ___ctrl_A___ + .byte $10 ; 2/02 ___ctrl_B___ + .byte $10 ; 3/03 ___ctrl_C___ + .byte $10 ; 4/04 ___ctrl_D___ + .byte $10 ; 5/05 ___ctrl_E___ + .byte $10 ; 6/06 ___ctrl_F___ + .byte $10 ; 7/07 ___ctrl_G___ + .byte $10 ; 8/08 ___ctrl_H___ + .byte $D0 ; 9/09 ___ctrl_I___ + .byte $50 ; 10/0a ___ctrl_J___ + .byte $50 ; 11/0b ___ctrl_K___ + .byte $50 ; 12/0c ___ctrl_L___ + .byte $50 ; 13/0d ___ctrl_M___ + .byte $10 ; 14/0e ___ctrl_N___ + .byte $10 ; 15/0f ___ctrl_O___ + .byte $10 ; 16/10 ___ctrl_P___ + .byte $10 ; 17/11 ___ctrl_Q___ + .byte $10 ; 18/12 ___ctrl_R___ + .byte $10 ; 19/13 ___ctrl_S___ + .byte $10 ; 20/14 ___ctrl_T___ + .byte $10 ; 21/15 ___ctrl_U___ + .byte $10 ; 22/16 ___ctrl_V___ + .byte $10 ; 23/17 ___ctrl_W___ + .byte $10 ; 24/18 ___ctrl_X___ + .byte $10 ; 25/19 ___ctrl_Y___ + .byte $10 ; 26/1a ___ctrl_Z___ + .byte $10 ; 27/1b ___ctrl_[___ + .byte $10 ; 28/1c ___ctrl_\___ + .byte $10 ; 29/1d ___ctrl_]___ + .byte $10 ; 30/1e ___ctrl_^___ + .byte $10 ; 31/1f ___ctrl_____ + .byte $A0 ; 32/20 ___SPACE___ + .byte $00 ; 33/21 _____!_____ + .byte $00 ; 34/22 _____"_____ + .byte $00 ; 35/23 _____#_____ + .byte $00 ; 36/24 _____$_____ + .byte $00 ; 37/25 _____%_____ + .byte $00 ; 38/26 _____&_____ + .byte $00 ; 39/27 _____'_____ + .byte $00 ; 40/28 _____(_____ + .byte $00 ; 41/29 _____)_____ + .byte $00 ; 42/2a _____*_____ + .byte $00 ; 43/2b _____+_____ + .byte $00 ; 44/2c _____,_____ + .byte $00 ; 45/2d _____-_____ + .byte $00 ; 46/2e _____._____ + .byte $00 ; 47/2f _____/_____ + .byte $0C ; 48/30 _____0_____ + .byte $0C ; 49/31 _____1_____ + .byte $0C ; 50/32 _____2_____ + .byte $0C ; 51/33 _____3_____ + .byte $0C ; 52/34 _____4_____ + .byte $0C ; 53/35 _____5_____ + .byte $0C ; 54/36 _____6_____ + .byte $0C ; 55/37 _____7_____ + .byte $0C ; 56/38 _____8_____ + .byte $0C ; 57/39 _____9_____ + .byte $00 ; 58/3a _____:_____ + .byte $00 ; 59/3b _____;_____ + .byte $00 ; 60/3c _____<_____ + .byte $00 ; 61/3d _____=_____ + .byte $00 ; 62/3e _____>_____ + .byte $00 ; 63/3f _____?_____ + + .byte $00 ; 64/40 _____@_____ + .byte $0A ; 65/41 _____A_____ + .byte $0A ; 66/42 _____B_____ + .byte $0A ; 67/43 _____C_____ + .byte $0A ; 68/44 _____D_____ + .byte $0A ; 69/45 _____E_____ + .byte $0A ; 70/46 _____F_____ + .byte $02 ; 71/47 _____G_____ + .byte $02 ; 72/48 _____H_____ + .byte $02 ; 73/49 _____I_____ + .byte $02 ; 74/4a _____J_____ + .byte $02 ; 75/4b _____K_____ + .byte $02 ; 76/4c _____L_____ + .byte $02 ; 77/4d _____M_____ + .byte $02 ; 78/4e _____N_____ + .byte $02 ; 79/4f _____O_____ + .byte $02 ; 80/50 _____P_____ + .byte $02 ; 81/51 _____Q_____ + .byte $02 ; 82/52 _____R_____ + .byte $02 ; 83/53 _____S_____ + .byte $02 ; 84/54 _____T_____ + .byte $02 ; 85/55 _____U_____ + .byte $02 ; 86/56 _____V_____ + .byte $02 ; 87/57 _____W_____ + .byte $02 ; 88/58 _____X_____ + .byte $02 ; 89/59 _____Y_____ + .byte $02 ; 90/5a _____Z_____ + .byte $00 ; 91/5b _____[_____ + .byte $00 ; 92/5c _____\_____ + .byte $00 ; 93/5d _____]_____ + .byte $00 ; 94/5e _____^_____ + .byte $00 ; 95/5f _UNDERLINE_ + .byte $00 ; 96/60 ___grave___ + .byte $09 ; 97/61 _____a_____ + .byte $09 ; 98/62 _____b_____ + .byte $09 ; 99/63 _____c_____ + .byte $09 ; 100/64 _____d_____ + .byte $09 ; 101/65 _____e_____ + .byte $09 ; 102/66 _____f_____ + .byte $01 ; 103/67 _____g_____ + .byte $01 ; 104/68 _____h_____ + .byte $01 ; 105/69 _____i_____ + .byte $01 ; 106/6a _____j_____ + .byte $01 ; 107/6b _____k_____ + .byte $01 ; 108/6c _____l_____ + .byte $01 ; 109/6d _____m_____ + .byte $01 ; 110/6e _____n_____ + .byte $01 ; 111/6f _____o_____ + .byte $01 ; 112/70 _____p_____ + .byte $01 ; 113/71 _____q_____ + .byte $01 ; 114/72 _____r_____ + .byte $01 ; 115/73 _____s_____ + .byte $01 ; 116/74 _____t_____ + .byte $01 ; 117/75 _____u_____ + .byte $01 ; 118/76 _____v_____ + .byte $01 ; 119/77 _____w_____ + .byte $01 ; 120/78 _____x_____ + .byte $01 ; 121/79 _____y_____ + .byte $01 ; 122/7a _____z_____ + .byte $00 ; 123/7b _____{_____ + .byte $00 ; 124/7c _____|_____ + .byte $00 ; 125/7d _____}_____ + .byte $00 ; 126/7e _____~_____ + .byte $40 ; 127/7f ____DEL____ + + .byte $00 ; 128/80 ___________ + .byte $00 ; 129/81 ___________ + .byte $00 ; 130/82 ___________ + .byte $00 ; 131/83 ___________ + .byte $00 ; 132/84 ___________ + .byte $00 ; 133/85 ___________ + .byte $00 ; 134/86 ___________ + .byte $00 ; 135/87 ___________ + .byte $00 ; 136/88 ___________ + .byte $00 ; 137/89 ___________ + .byte $00 ; 138/8a ___________ + .byte $00 ; 139/8b ___________ + .byte $00 ; 140/8c ___________ + .byte $00 ; 141/8d ___________ + .byte $00 ; 142/8e ___________ + .byte $00 ; 143/8f ___________ + .byte $00 ; 144/90 ___________ + .byte $00 ; 145/91 ___________ + .byte $00 ; 146/92 ___________ + .byte $10 ; 147/93 ___________ + .byte $00 ; 148/94 ___________ + .byte $00 ; 149/95 ___________ + .byte $00 ; 150/96 ___________ + .byte $00 ; 151/97 ___________ + .byte $00 ; 152/98 ___________ + .byte $00 ; 153/99 ___________ + .byte $00 ; 154/9a ___________ + .byte $00 ; 155/9b ___________ + .byte $00 ; 156/9c ___________ + .byte $00 ; 157/9d ___________ + .byte $00 ; 158/9e ___________ + .byte $00 ; 159/9f ___________ + + .byte $00 ; 160/a0 ___________ + .byte $00 ; 161/a1 ___________ + .byte $00 ; 162/a2 ___________ + .byte $00 ; 163/a3 ___________ + .byte $00 ; 164/a4 ___________ + .byte $00 ; 165/a5 ___________ + .byte $00 ; 166/a6 ___________ + .byte $00 ; 167/a7 ___________ + .byte $00 ; 168/a8 ___________ + .byte $00 ; 169/a9 ___________ + .byte $00 ; 170/aa ___________ + .byte $00 ; 171/ab ___________ + .byte $00 ; 172/ac ___________ + .byte $00 ; 173/ad ___________ + .byte $00 ; 174/ae ___________ + .byte $00 ; 175/af ___________ + .byte $00 ; 176/b0 ___________ + .byte $00 ; 177/b1 ___________ + .byte $00 ; 178/b2 ___________ + .byte $00 ; 179/b3 ___________ + .byte $00 ; 180/b4 ___________ + .byte $00 ; 181/b5 ___________ + .byte $00 ; 182/b6 ___________ + .byte $00 ; 183/b7 ___________ + .byte $00 ; 184/b8 ___________ + .byte $00 ; 185/b9 ___________ + .byte $00 ; 186/ba ___________ + .byte $00 ; 187/bb ___________ + .byte $00 ; 188/bc ___________ + .byte $00 ; 189/bd ___________ + .byte $00 ; 190/be ___________ + .byte $00 ; 191/bf ___________ + + .byte $02 ; 192/c0 ___________ + .byte $02 ; 193/c1 ___________ + .byte $02 ; 194/c2 ___________ + .byte $02 ; 195/c3 ___________ + .byte $02 ; 196/c4 ___________ + .byte $02 ; 197/c5 ___________ + .byte $02 ; 198/c6 ___________ + .byte $02 ; 199/c7 ___________ + .byte $02 ; 200/c8 ___________ + .byte $02 ; 201/c9 ___________ + .byte $02 ; 202/ca ___________ + .byte $02 ; 203/cb ___________ + .byte $02 ; 204/cc ___________ + .byte $02 ; 205/cd ___________ + .byte $02 ; 206/ce ___________ + .byte $02 ; 207/cf ___________ + .byte $02 ; 208/d0 ___________ + .byte $02 ; 209/d1 ___________ + .byte $02 ; 210/d2 ___________ + .byte $02 ; 211/d3 ___________ + .byte $02 ; 212/d4 ___________ + .byte $02 ; 213/d5 ___________ + .byte $02 ; 214/d6 ___________ + .byte $02 ; 215/d7 ___________ + .byte $02 ; 216/d8 ___________ + .byte $02 ; 217/d9 ___________ + .byte $02 ; 218/da ___________ + .byte $02 ; 219/db ___________ + .byte $02 ; 220/dc ___________ + .byte $02 ; 221/dd ___________ + .byte $02 ; 222/de ___________ + .byte $00 ; 223/df ___________ + .byte $01 ; 224/e0 ___________ + .byte $01 ; 225/e1 ___________ + .byte $01 ; 226/e2 ___________ + .byte $01 ; 227/e3 ___________ + .byte $01 ; 228/e4 ___________ + .byte $01 ; 229/e5 ___________ + .byte $01 ; 230/e6 ___________ + .byte $01 ; 231/e7 ___________ + .byte $01 ; 232/e8 ___________ + .byte $01 ; 233/e9 ___________ + .byte $01 ; 234/ea ___________ + .byte $01 ; 235/eb ___________ + .byte $01 ; 236/ec ___________ + .byte $01 ; 237/ed ___________ + .byte $01 ; 238/ee ___________ + .byte $01 ; 239/ef ___________ + .byte $01 ; 240/f0 ___________ + .byte $01 ; 241/f1 ___________ + .byte $01 ; 242/f2 ___________ + .byte $01 ; 243/f3 ___________ + .byte $01 ; 244/f4 ___________ + .byte $01 ; 245/f5 ___________ + .byte $01 ; 246/f6 ___________ + .byte $01 ; 247/f7 ___________ + .byte $01 ; 248/f8 ___________ + .byte $01 ; 249/f9 ___________ + .byte $01 ; 250/fa ___________ + .byte $01 ; 251/fb ___________ + .byte $01 ; 252/fc ___________ + .byte $01 ; 253/fd ___________ + .byte $01 ; 254/fe ___________ + .byte $00 ; 255/ff ___________ + diff --git a/libsrc/atari/cvline.s b/libsrc/atari/cvline.s new file mode 100644 index 000000000..4ff351e3b --- /dev/null +++ b/libsrc/atari/cvline.s @@ -0,0 +1,35 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void cvlinexy (unsigned char x, unsigned char y, unsigned char length); +; void cvline (unsigned char length); +; + .include "atari.inc" + + .export _cvlinexy, _cvline + .import popa, _gotoxy, putchar, newline + .importzp tmp1 + +_cvlinexy: + pha ; Save the length + jsr popa ; Get y + jsr _gotoxy ; Call this one, will pop params + pla ; Restore the length and run into _cvline + +_cvline: + cmp #0 ; Is the length zero? + beq L9 ; Jump if done + sta tmp1 +L1: lda COLCRS + pha + lda #$7C ; Vertical bar + jsr putchar ; Write, no cursor advance + pla + sta COLCRS + inc ROWCRS + dec tmp1 + bne L1 +L9: rts + + + diff --git a/libsrc/atari/fdtable.s b/libsrc/atari/fdtable.s new file mode 100644 index 000000000..547681e12 --- /dev/null +++ b/libsrc/atari/fdtable.s @@ -0,0 +1,103 @@ +; +; Christian Groessler, May-2000 +; +; fd indirection table & helper functions +; + + .include "atari.inc" + .export fdtoiocb + .export fdtoiocb_down + .export fd_table + + .data + +fd_table: + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + .byte 0,$ff,0,0 + +MAX_FD_VAL = (* - fd_table) / 4 + +ft_usa = 0 ; usage counter +ft_iocb = 1 ; iocb index (0,$10,$20,etc.), $ff for empty entry +ft_dev = 2 ; device of open iocb +ft_flag = 3 ; flags + + .code + +; gets fd in ax, decrements usage counter +; return iocb index in X +; return N bit set for invalid fd +; return Z bit set if last user +; all registers destroyed +.proc fdtoiocb_down + + cpx #0 + bne inval + cmp #MAX_FD_VAL + bcs inval + asl a ; create index into fd table + asl a + tax + lda #$ff + cmp fd_table+ft_iocb,x ; entry in use? + beq inval ; no, return error + lda fd_table+ft_usa,x ; get usage counter + beq ok_notlast ; 0? + sec + sbc #1 ; decr usage counter + sta fd_table+ft_usa,x +retiocb:php + txa + tay + lda fd_table+ft_iocb,x ; get iocb + tax + plp + bne cont + php + lda #$ff + sta fd_table+ft_iocb,y ; clear table entry + plp +cont: rts + +ok_notlast: + lda #1 ; clears Z + jmp retiocb + +.endproc + +inval: ldx #$ff ; sets N + rts + + +; gets fd in ax +; return iocb index in X +; return N bit set for invalid fd +; all registers destroyed +.proc fdtoiocb + + cpx #0 + bne inval + cmp #MAX_FD_VAL + bcs inval + asl a ; create index into fd table + asl a + tax + lda #$ff + cmp fd_table+ft_iocb,x ; entry in use? + beq inval ; no, return error + lda fd_table+ft_usa,x ; get usage counter + beq inval ; 0? should not happen + lda fd_table+ft_iocb,x ; get iocb + rts + +.endproc diff --git a/libsrc/atari/getargs.s b/libsrc/atari/getargs.s new file mode 100644 index 000000000..04cf25ec4 --- /dev/null +++ b/libsrc/atari/getargs.s @@ -0,0 +1,220 @@ +; get arguments from command line (when DOS supports it) +; and supply function to get default device: char *getdefdev(void); + +; Freddy Offenga, 4/21/2000 + +; SpartaDOS: +; the ZCRNAME routine is only used to get the default drive because +; ZCRNAME has two disadvantages: +; 1. It will convert D: into D1: instead of Dn: (n = default drive) +; 2. It will give a 'no arguments' status if it detects something +; like Dn: (without filename). + +; OS/A+ DOS: +; ZCRNAME is slightly different from SpartaDOS. It will convert D: +; into Dn: where n is the default drive. + +MAXARGS = 16 ; max. amount of arguments in arg. table +CL_SIZE = 64 ; command line buffer size +SPACE = 32 ; SPACE char. + + .include "atari.inc" + .export getargs, argc, argv + .export _getdefdev ; get default device (e.g. "D1:") + .importzp ptr1 + +; Get command line + +getargs: + lda #0 + sta argc + sta argc+1 + sta argv + sta argv+1 + + jsr detect + bcs argdos ; carry set = DOS supports arguments + rts + +; Move SpartaDOS command line to our own buffer + +argdos: lda DOSVEC + clc + adc #LBUF + sta ptr1+1 + + ldy #0 +cpcl: lda (ptr1),y + sta ourcl,y + iny + cmp #ATEOL + beq movdon + cpy #CL_SIZE + bne cpcl + +movdon: lda #0 + sta ourcl,y ; null terminate behind ATEOL + +; Get default device (LBUF will be destroyed!!) + + ldy #BUFOFF + lda #0 + sta (DOSVEC),y ; reset buffer offset + +; Store dummy argument + + ldy #LBUF + lda dumpar1 + sta (DOSVEC),y + iny + lda dumpar2 + sta (DOSVEC),y + +; One extra store to avoid the buggy sequence from OS/A+ DOS: +; <:> => drive number = + + iny + sta (DOSVEC),y + +; Create crunch vector + + ldy #ZCRNAME+1 + lda (DOSVEC),y + sta crvec+1 + iny + lda (DOSVEC),y + sta crvec+2 + +crvec: jsr $FFFF ; will be set to crunch vector + +; Get default device + + ldy #COMFNAM ; COMFNAM is always "Dn:" + lda (DOSVEC),y + sta defdev + iny + lda (DOSVEC),y + sta defdev+1 + +; Turn command line into argv table + + ldy #0 +eatspc: lda ourcl,y ; eat spaces + cmp #ATEOL + beq finargs + cmp #SPACE + bne rpar ; begin of argument found + iny + cpy #CL_SIZE + bne eatspc + beq finargs ; only spaces is no argument + +; Store argument vector + +rpar: lda argc ; low-byte + asl + tax ; table index + tya ; ourcl index + clc + adc #ourcl + adc #0 + sta argv+1,x + ldx argc + inx + stx argc + cpx #MAXARGS + beq finargs + +; Skip this arg. + +skiparg: + ldx ourcl,y + cpx #ATEOL ; end of line? + beq eopar + cpx #SPACE + beq eopar + iny + cpy #CL_SIZE + bne skiparg + +; End of arg. -> place 0 + +eopar: + lda #0 + sta ourcl,y + iny ; y behind arg. + cpx #ATEOL ; was it the last arg? + bne eatspc + +; Finish args + +finargs: + lda argc + asl + tax + lda #0 + sta argv,x + sta argv+1,x + rts + +; DOS type detection + +detect: + lda DOS + cmp #$53 ; "S" (SpartaDOS) + beq spdos + + ldy #COMTAB + lda #$4C + cmp (DOSVEC),y + bne nordos + + ldy #ZCRNAME + cmp (DOSVEC),y + bne nordos + + ldy #6 ; OS/A+ has a jmp here + cmp (DOSVEC),y + beq nordos + +spdos: sec ; SpartaDOS, OS/A+ or DOS XL + rts + +nordos: clc ; normal DOS (no args) detected + rts + +; Get default device (set by getargs routine) + +_getdefdev: + lda #defdev + rts + + .data + +; Dummy argument to get default device + +dumpar1: + .byte "X" +dumpar2: + .byte ATEOL + +; Buffer for command line / argv strings + +ourcl: .res CL_SIZE + .byte ATEOL + +; Default device + +defdev: + .byte "D1:", 0 + + .bss + +argc: .res 2 +argv: .res (1 + MAXARGS) * 2 diff --git a/libsrc/atari/gotox.s b/libsrc/atari/gotox.s new file mode 100644 index 000000000..3b3efe01b --- /dev/null +++ b/libsrc/atari/gotox.s @@ -0,0 +1,14 @@ +; +; Christian Groessler, 19-Feb-2000 +; +; void gotox (unsigned char x); +; + + .include "atari.inc" + .export _gotox + +_gotox: + sta COLCRS ; Set X + lda #0 + sta COLCRS+1 + rts diff --git a/libsrc/atari/gotoxy.s b/libsrc/atari/gotoxy.s new file mode 100644 index 000000000..671629833 --- /dev/null +++ b/libsrc/atari/gotoxy.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void gotoxy (unsigned char x, unsigned char y); +; + + .include "atari.inc" + + .export _gotoxy + .import popa + +_gotoxy: ; Set the cursor position + sta ROWCRS ; Set Y + jsr popa ; Get X + sta COLCRS ; Set X + lda #0 + sta COLCRS+1 ; + rts diff --git a/libsrc/atari/gotoy.s b/libsrc/atari/gotoy.s new file mode 100644 index 000000000..73593afab --- /dev/null +++ b/libsrc/atari/gotoy.s @@ -0,0 +1,12 @@ +; +; Christian Groessler, 19-Feb-2000 +; +; void gotoy (unsigned char y); +; + + .include "atari.inc" + .export _gotoy + +_gotoy: + sta ROWCRS ; Set Y + rts diff --git a/libsrc/atari/kbhit.s b/libsrc/atari/kbhit.s new file mode 100644 index 000000000..25418c15d --- /dev/null +++ b/libsrc/atari/kbhit.s @@ -0,0 +1,21 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; int kbhit (void); +; + + .export _kbhit + .import return0, return1 + + .include "atari.inc" + +_kbhit: + lda CH ; Get number of characters + cmp #$FF + bne L1 + jmp return1 +L1: jmp return0 + + + + diff --git a/libsrc/atari/open.s b/libsrc/atari/open.s new file mode 100644 index 000000000..93519a6e8 --- /dev/null +++ b/libsrc/atari/open.s @@ -0,0 +1,208 @@ +; +; Christian Groessler, May-2000 +; +; int open(const char *name,int flags,...); +; returns fd +; + +UCASE_FILENAME = 1 ; comment it out if filename shouldn't be uppercased + + .include "atari.inc" + .include "../common/fmode.inc" + .include "../common/errno.inc" + .export _open + .import __do_oserror,__seterrno,incsp4 + .import ldaxysp,addysp,subysp + .import _strupr,__oserror + .importzp tmp4,sp +.ifdef UCASE_FILENAME + .importzp tmp3,ptr4 +.endif + +.proc _open + + cpy #4 ; correct # of arguments (bytes)? + beq parmok ; parameter count ok + tya ; parm count < 4 shouldn't be needed to be checked + sec ; (it generates a c compiler warning) + sbc #4 + tay + jsr addysp ; fix stack, throw away unused parameters + +parmok: jsr findfreeiocb + beq iocbok ; we found one + + lda #EMFILE +seterr: jsr __seterrno + jsr incsp4 ; clean up stack + lda #$FF + tax + rts ; return -1 + + ; process the mode argument + ; @@@TODO: append not handled yet! + +iocbok: stx tmp4 + jsr clriocb ; init with zero + ldy #1 + jsr ldaxysp ; get mode + ldx tmp4 + cmp #O_RDONLY + bne l1 + lda #OPNIN +set: sta ICAX1,x + bne cont + +l1: cmp #O_WRONLY + bne l2 + lda #OPNOT + bne set + +l2: ; O_RDWR + lda #OPNOT|OPNIN + bne set + + ; process the filename argument + +cont: ldy #3 + jsr ldaxysp + +.ifdef UCASE_FILENAME + ; we make sure that the filename doesn't contain lowercase letters + ; we copy the filename we got onto the stack, uppercase it and use this + ; one to open the iocb + ; we're using tmp3, ptr4 + + ; save the original pointer + sta ptr4 + stx ptr4+1 + + ; now we need the length of the name + ldy #0 +loop: lda (ptr4),y + beq str_end + cmp #ATEOL ; we also accept Atari EOF char as end of string + beq str_end + iny + bne loop ; not longer than 255 chars (127 real limit) +toolong:lda #EINVAL + jmp seterr + +str_end:iny ; room for terminating zero + cpy #128 ; we only can handle lenght < 128 + bcs toolong + sty tmp3 ; save size + jsr subysp ; make room on the stack + + ; copy filename to the temp. place on the stack + lda #0 ; end-of-string + sta (sp),y ; Y still contains length + 1 + dey +loop2: lda (ptr4),y + sta (sp),y + dey + bpl loop2 ; bpl: this way we only support a max. length of 127 + + ; uppercase the temp. filename + ldx sp+1 + lda sp + jsr _strupr + + ; leave X and Y pointing to the modified filename + lda sp + ldx sp+1 + +.endif ; defined UCASE_FILENAME + + ldy tmp4 + + jsr newfd ; maybe we don't need to open and can reuse an iocb + bcc noopen + + sta ICBAL,y + txa + sta ICBAH,y + ldx tmp4 + lda #OPEN + sta ICCOM,x + jsr CIOV + + ; clean up the stack + + php + txa + pha + tya + pha + +.ifdef UCASE_FILENAME + ldy tmp3 ; get size + jsr addysp ; free used space on the stack +.endif ; defined UCASE_FILENAME + + jsr incsp4 ; clean up stack + + pla + tay + pla + tax + plp + + bpl ok + jmp __do_oserror + +ok: txa + lsr a + lsr a + lsr a + lsr a + ldx #0 + stx __oserror + rts + +.endproc + + +; find a free iocb +; no entry parameters +; return ZF = 0/1 for not found/found +; index in X if found +; all registers destroyed + +.proc findfreeiocb + + ldx #0 + ldy #$FF +loop: tya + cmp ICHID,x + beq found + txa + clc + adc #$10 + tax + cmp #$80 + bcc loop + inx ; return ZF cleared +found: rts + +.endproc + + +; clear iocb except for ICHID field +; expects X to be index to IOCB (0,$10,$20,etc.) +; all registers destroyed + +.proc clriocb + + inx ; don't clear ICHID + ldy #15 + lda #0 +loop: sta ICHID,x + dey + inx + bne loop + rts + +.endproc diff --git a/libsrc/atari/oserror.s b/libsrc/atari/oserror.s new file mode 100644 index 000000000..dcbe7dbb4 --- /dev/null +++ b/libsrc/atari/oserror.s @@ -0,0 +1,85 @@ +; +; Christian Groessler, May-2000 +; +; os specific error code mapping +; int __fastcall__ _osmaperrno (unsigned char oserror); +; + + .include "../common/errno.inc" + .export __osmaperrno + +.proc __osmaperrno + + cmp #$80 ; error or success + bcs errcode ; error, jump + + lda #0 ; no error, return 0 + tax + rts + +errcode:and #$7f ; create index from error number + tax + cpx #MAX_OSERR_VAL ; valid number? + bcs inverr ; no + + lda maptable,x + ldx #0 + rts + +inverr: lda #EUNKNOWN + rts + +.endproc + +.rodata + +maptable: + .byte EINTR ;BRKABT = 128 ;($80) BREAK key abort + .byte EBUSY ;PRVOPN = 129 ;($81) IOCB already open error + .byte ENODEV ;NONDEV = 130 ;($82) nonexistent device error + .byte EACCES ;WRONLY = 131 ;($83) IOCB opened for write only error + .byte ENOSYS ;NVALID = 132 ;($84) invalid command error + .byte EINVAL ;NOTOPN = 133 ;($85) device/file not open error + .byte EINVAL ;BADIOC = 134 ;($86) invalid IOCB index error + .byte EACCES ;RDONLY = 135 ;($87) IOCB opened for read only error + .byte EINVAL ;EOFERR = 136 ;($88) end of file error (should never come, + ; specially handled by read.s) + .byte EIO ;TRNRCD = 137 ;($89) truncated record error + .byte EIO ;TIMOUT = 138 ;($8A) peripheral device timeout error + .byte EIO ;DNACK = 139 ;($8B) device does not acknowledge command + .byte EIO ;FRMERR = 140 ;($8C) serial bus framing error + .byte EINVAL ;CRSROR = 141 ;($8D) cursor overrange error + .byte EIO ;OVRRUN = 142 ;($8E) serial bus data overrun error + .byte EIO ;CHKERR = 143 ;($8F) serial bus checksum error + .byte EIO ;DERROR = 144 ;($90) device done (operation incomplete) + .byte EINVAL ;BADMOD = 145 ;($91) bad screen mode number error + .byte ENOSYS ;FNCNOT = 146 ;($92) function not implemented in handler + .byte ENOMEM ;SCRMEM = 147 ;($93) insufficient memory for screen mode +; codes below taken from "Mein Atari Computer" (german version of "Your Atari Computer") + .byte EUNKNOWN ; 148 - haven't found documentation + .byte EUNKNOWN ; 149 - haven't found documentation + .byte EBUSY ; 150 - serial port already open + .byte EACCES ; 151 - concurrent mode I/O not enabled (serial) + .byte EINVAL ; 152 - invalid buffer address for concurrent mode + .byte EAGAIN ; 153 - concurrent mode enabled (and another access tried) + .byte EACCES ; 154 - concurrent mode I/O not active (serial) + .byte EUNKNOWN ; 155 - haven't found documentation + .byte EUNKNOWN ; 156 - haven't found documentation + .byte EUNKNOWN ; 157 - haven't found documentation + .byte EUNKNOWN ; 158 - haven't found documentation + .byte EUNKNOWN ; 159 - haven't found documentation + .byte ENOENT ; 160 - drive number error (DOS) + .byte EMFILE ; 161 - too many open files + .byte ENOSPC ; 162 - disk full + .byte EIO ; 163 - unrecoverable system data I/O error + .byte ESPIPE ; 164 - file number mismatch (inv. seek or disk data strucs damaged) + .byte ENOENT ; 165 - invalid file name (e.g. lowercase) + .byte ESPIPE ; 166 - point data length error + .byte EACCES ; 167 - file locked (read-only) + .byte ENOSYS ; 168 - command invalid + .byte ENOSPC ; 169 - directory full + .byte ENOENT ; 170 - file not found + .byte ESPIPE ; 171 - point command invalid + +MAX_OSERR_VAL = (* - maptable) diff --git a/libsrc/atari/read.s b/libsrc/atari/read.s new file mode 100644 index 000000000..de2990d1a --- /dev/null +++ b/libsrc/atari/read.s @@ -0,0 +1,33 @@ +; +; Christian Groessler, Apr-2000 +; +; int read(int fd,void *buf,int count) +; + + .include "atari.inc" + .import __rwsetup,__do_oserror,__inviocb,__oserror + .export _read + +_read: jsr __rwsetup ; do common setup for read and write + beq done ; if size 0, it's a no-op + cpx #$FF ; invalid iocb? + beq _inviocb + lda #GETCHR ; iocb command code + sta ICCOM,x + jsr CIOV ; read it + bpl done + cpy #EOFERR ; eof is treated specially + beq done + jmp __do_oserror ; update errno + +done: lda ICBLL,x ; buf len lo + pha ; save + lda ICBLH,x ; get buf len hi + tax ; to X + lda #0 + sta __oserror ; clear system dependend error code + pla ; get buf len lo + rts + +_inviocb: + jmp __inviocb diff --git a/libsrc/atari/readjoy.s b/libsrc/atari/readjoy.s new file mode 100644 index 000000000..c9e3cd554 --- /dev/null +++ b/libsrc/atari/readjoy.s @@ -0,0 +1,30 @@ +; +; Christian Groessler +; +; unsigned readjoy (unsigned char joy); +; + + .export _readjoy + + .include "atari.inc" + + +.proc _readjoy + + and #3 ; fix joystick number + tax ; Joystick number (0-3) into X + +; Read joystick + + lda STRIG0,x ; get button + asl a + asl a + asl a + asl a + ora STICK0,x ; add position information + eor #$1F + ldx #0 ; fix X + rts + +.endproc + diff --git a/libsrc/atari/revers.s b/libsrc/atari/revers.s new file mode 100644 index 000000000..ee59e7129 --- /dev/null +++ b/libsrc/atari/revers.s @@ -0,0 +1,23 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; unsigned char revers (unsigned char onoff); +; + .include "atari.inc" + + .export _revers + +_revers: + ldx #$00 ; Assume revers off + tay ; Test onoff + beq L1 ; Jump if off + ldx #$80 ; Load on value +L1: ldy #$00 ; Assume old value is zero + lda INVFLG ; Load old value + stx INVFLG ; Set new value + beq L2 ; Jump if old value zero + iny ; Make old value = 1 +L2: ldx #$00 ; Load high byte of result + tya ; Load low byte, set CC + rts + diff --git a/libsrc/atari/rwcommon.s b/libsrc/atari/rwcommon.s new file mode 100644 index 000000000..8a888142d --- /dev/null +++ b/libsrc/atari/rwcommon.s @@ -0,0 +1,77 @@ +; +; common iocb setup routine for read, write +; expects parameters (int fd,void *buf,int count) +; + .include "atari.inc" + .include "../common/errno.inc" + .importzp tmp2,tmp3 + .import incsp6,ldaxysp + .import __errno,__oserror + .import fdtoiocb + + .export __rwsetup + +__rwsetup: + + ldy #5 + jsr ldaxysp ; get fd + jsr fdtoiocb ; convert to iocb + bmi iocberr +; asl a ; iocb # --> iocb index +; asl a +; asl a +; asl a + sta tmp3 ; save it + ldy #1 + jsr ldaxysp ; get size + php ; save cond codes, for zero-ness + stx tmp2 + ldx tmp3 ; iocb + cpx #$80 ; iocb must be 0...7 + bcs iocberr + sta ICBLL,x + lda tmp2 ; size hi + sta ICBLH,x + ldy #3 ; get buf addr (was 2 in orig. version) + jsr ldaxysp + stx tmp2 + ldx tmp3 + sta ICBAL,x + lda tmp2 + sta ICBAH,x + jsr incsp6 ; pop args + plp + rts + +iocberr:jsr incsp6 ; pop args + plp ; throw away + ldx #$FF ; indicate error + clear ZF + rts + + +; +; this routine updates errno. do a JMP here right after calling +; CIOV. we expect status in Y. +; + .export __do_oserror,__seterrno,__inviocb +__do_oserror: + sty __oserror ; save os dependent error code +retminus: + lda #$FF + tax ; return -1 + rts + +__seterrno: + sta __errno + stx __errno+1 + rts + +; +; sets EINVAL error code and returns -1 +; + +__inviocb: + lda #EINVAL + jsr __seterrno + jmp retminus ; return -1 diff --git a/libsrc/atari/savevec.s b/libsrc/atari/savevec.s new file mode 100644 index 000000000..d29bde622 --- /dev/null +++ b/libsrc/atari/savevec.s @@ -0,0 +1,105 @@ +; +; save and restore system vectors +; originally by Mark Keates +; +; void save_vecs(void); +; void rest_vecs(void); +; + + .export _save_vecs,_rest_vecs +.include "atari.inc" + + .bss + +old_dli: .res 2 +old_dlist: .res 2 +old_vbi: .res 2 +old_vbd: .res 2 +old_gra: .res 1 +old_dma: .res 1 +old_prior: .res 1 +old_cols: .res 8 +old_set: .res 1 +old_rmargin: .res 1 ; lmargin saved in startup code + + .code + +.proc _save_vecs + + lda VDSLST + sta old_dli + lda VDSLST+1 + sta old_dli+1 + lda SDLSTL + sta old_dlist + lda SDLSTL+1 + sta old_dlist+1 + lda VVBLKI + sta old_vbi + lda VVBLKI+1 + sta old_vbi+1 + lda VVBLKD + sta old_vbd + lda VVBLKD+1 + sta old_vbd+1 + lda GRACTL + sta old_gra + lda SDMCTL + sta old_dma + lda GPRIOR + sta old_prior + lda CHBAS + sta old_set + lda RMARGN + sta old_rmargin + + ldy #7 +SETUP1: + lda PCOLR0,y + sta old_cols,y + dey + bpl SETUP1 + rts + +.endproc + +.proc _rest_vecs + + lda #6 + ldx old_vbi+1 + ldy old_vbi + jsr SETVBV + lda #7 + ldx old_vbd+1 + ldy old_vbd + jsr SETVBV + lda old_dli + sta VDSLST + lda old_dli+1 + sta VDSLST+1 + lda old_dlist + sta SDLSTL + lda old_dlist+1 + sta SDLSTL+1 + lda old_gra + sta GRACTL + lda old_prior + sta GPRIOR + lda old_dma + sta SDMCTL + lda old_set + sta CHBAS + lda old_rmargin + sta RMARGN + lda #$FF + sta CH + ldy #7 +SETUP2: + lda old_cols,Y + sta PCOLR0,Y + dey + bpl SETUP2 + rts + +.endproc + diff --git a/libsrc/atari/where.s b/libsrc/atari/where.s new file mode 100644 index 000000000..25c2d0fd8 --- /dev/null +++ b/libsrc/atari/where.s @@ -0,0 +1,27 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char wherex (void); +; unsigned char wherey (void); + + .export _wherex, _wherey + .import plot + + .include "atari.inc" + +_wherex: + sec + jsr plot ; Get cursor position + tya + rts + +_wherey: + sec + jsr plot ; Get cursor position + txa + rts + + + + + diff --git a/libsrc/atari/write.s b/libsrc/atari/write.s new file mode 100644 index 000000000..e6bc8cc8d --- /dev/null +++ b/libsrc/atari/write.s @@ -0,0 +1,29 @@ +; +; write(iocb, buf, nbytes)->nbytes written +; + .include "atari.inc" + .import __rwsetup,__do_oserror,__inviocb,__oserror + .export _write +_write: + jsr __rwsetup ; do common setup + beq write9 ; if size 0, it's a no-op + cpx #$FF ; invalid iocb? + beq _inviocb + lda #PUTCHR + sta ICCOM,x + jsr CIOV + bpl write9 + jmp __do_oserror ; update errno + +write9: + lda ICBLL,x ; get buf len lo + pha + lda ICBLH,x ; buf len hi + tax + lda #0 + sta __oserror ; clear system dependend error code + pla + rts + +_inviocb: + jmp __inviocb diff --git a/libsrc/c128/Makefile b/libsrc/c128/Makefile new file mode 100644 index 000000000..868e8e433 --- /dev/null +++ b/libsrc/c128/Makefile @@ -0,0 +1,28 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $< + @$(AS) -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +C_OBJS = + +S_OBJS = crt0.o conio.o kbhit.o clrscr.o cgetc.o readjoy.o\ + color.o cputc.o break.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f $(C_OBJS:.c=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) + @rm -f crt0.o + diff --git a/libsrc/c128/break.s b/libsrc/c128/break.s new file mode 100644 index 000000000..f0d69ceff --- /dev/null +++ b/libsrc/c128/break.s @@ -0,0 +1,127 @@ +; +; Ullrich von Bassewitz, 27.09.1998 +; +; void set_brk (unsigned Addr); +; void reset_brk (void); +; + + .export _set_brk, _reset_brk + .export _brk_a, _brk_x, _brk_y, _brk_sr, _brk_pc + .import _atexit + .importzp ptr1 + + .include "c128.inc" + + +.bss +_brk_a: .res 1 +_brk_x: .res 1 +_brk_y: .res 1 +_brk_sr: .res 1 +_brk_pc: .res 2 + +oldvec: .res 2 ; Old vector + + +.data +uservec: jmp $FFFF ; Patched at runtime + +.code + +; Where will we put the break stub? +stub_addr = $0E00 ; BASIC sprite area + + + +; Set the break vector +.proc _set_brk + + sta uservec+1 + stx uservec+2 ; Set the user vector + + lda oldvec + ora oldvec+1 ; Did we save the vector already? + bne L2 ; Jump if we installed the handler already + + lda BRKVec + sta oldvec + lda BRKVec+1 + sta oldvec+1 ; Save the old vector + + ldy #stub_size-1 ; Copy our stub into the low mem area +L1: lda brk_stub,y + sta stub_addr,y + dey + bpl L1 + + lda #<_reset_brk + ldx #>_reset_brk + jsr _atexit ; Install an exit handler + +L2: lda #stub_addr + sta BRKVec+1 + rts + +.endproc + + +; Reset the break vector +.proc _reset_brk + + lda oldvec + sta BRKVec + lda oldvec+1 + sta BRKVec+1 + rts + +.endproc + + + +; Break handler, called if a break occurs + +.proc brk_handler + + pla + sta _brk_y + pla + sta _brk_x + pla + sta _brk_a + pla + and #$EF ; Clear break bit + sta _brk_sr + pla ; PC low + sec + sbc #2 ; Point to start of brk + sta _brk_pc + pla ; PC high + sbc #0 + sta _brk_pc+1 + + jsr uservec ; Call the user's routine + + lda _brk_pc+1 + pha + lda _brk_pc + pha + lda _brk_sr + pha + ldx _brk_x + ldy _brk_y + lda _brk_a + rti ; Jump back... + +.endproc + + +brk_stub: + .org stub_addr + pla ; Get original MMU value + sta MMU_CR ; Re-enable our config + jmp brk_handler ; Jump to the user handler + .reloc + +stub_size = * - brk_stub diff --git a/libsrc/c128/c128.inc b/libsrc/c128/c128.inc new file mode 100644 index 000000000..7bfcfcd07 --- /dev/null +++ b/libsrc/c128/c128.inc @@ -0,0 +1,185 @@ +; +; C64 generic definitions. Stolen from Elite128 +; + + +; --------------------------------------------------------------------------- +; Zero page, Commodore stuff + +ST = $90 ; IEC status byte + +FNAM_LEN = $B7 ; Length of filename +SECADR = $B9 ; Secondary address +DEVNUM = $BA ; Device number +FNAM_BANK = $C7 ; Bank for filename +FNAM_LO = $BB ; Address of filename +FNAM_HI = $BC +KEY_COUNT = $D0 ; Number of keys in input buffer +MODE = $D7 ; 40/80 column mode flag +CURS_X = $EC ; Cursor column +CURS_Y = $EB ; Cursor row +SCREEN_PTR = $E0 ; Pointer to current char in text screen +CRAM_PTR = $E2 ; Pointer to current char in color RAM + +CHARCOLOR = $F1 +FKEY_COUNT = $D1 ; Characters for function key +FKEY_LEN = $1000 ; Function key lengths +FKEY_TEXT = $100A ; Function key texts + +; --------------------------------------------------------------------------- +; Kernal routines + +; Direct entries +CURS_ON = $CD6F +CURS_OFF = $CD9F +CLRSCR = $C142 +KBDREAD = $C006 + +; --------------------------------------------------------------------------- +; Vectors + +IRQVec = $0314 +BRKVec = $0316 +NMIVec = $0318 +KeyStoreVec = $033C + +; --------------------------------------------------------------------------- +; I/O: VIC + +VIC = $D000 +VIC_SPR0_X = $D000 +VIC_SPR0_Y = $D001 +VIC_SPR1_X = $D002 +VIC_SPR1_Y = $D003 +VIC_SPR2_X = $D004 +VIC_SPR2_Y = $D005 +VIC_SPR3_X = $D006 +VIC_SPR3_Y = $D007 +VIC_SPR4_X = $D008 +VIC_SPR4_Y = $D009 +VIC_SPR5_X = $D00A +VIC_SPR5_Y = $D00B +VIC_SPR6_X = $D00C +VIC_SPR6_Y = $D00D +VIC_SPR7_X = $D00E +VIC_SPR7_Y = $D00F +VIC_SPR_HI_X = $D010 +VIC_SPR_ENA = $D015 +VIC_SPR_EXP_X = $D017 +VIC_SPR_EXP_Y = $D01D +VIC_SPR_MCOLOR = $D01C +VIC_SPR_BG_PRIO = $D01B + +VIC_SPR_MCOLOR0 = $D025 +VIC_SPR_MCOLOR1 = $D026 + +VIC_SPR0_COLOR = $D027 +VIC_SPR1_COLOR = $D028 +VIC_SPR2_COLOR = $D029 +VIC_SPR3_COLOR = $D02A +VIC_SPR4_COLOR = $D02B +VIC_SPR5_COLOR = $D02C +VIC_SPR6_COLOR = $D02D +VIC_SPR7_COLOR = $D02E + +VIC_CTRL1 = $D011 +VIC_CTRL2 = $D016 + +VIC_HLINE = $D012 + +VIC_VIDEO_ADR = $D018 + +VIC_IRR = $D019 ; Interrupt request register +VIC_IMR = $D01A ; Interrupt mask register + +VIC_BORDERCOLOR = $D020 +VIC_BG_COLOR0 = $D021 +VIC_BG_COLOR1 = $D022 +VIC_BG_COLOR2 = $D023 +VIC_BG_COLOR3 = $D024 + +; 128 stuff: +VIC_KBD_128 = $D02F ; Extended kbd bits (visible in 64 mode) +VIC_CLK_128 = $D030 ; Clock rate register (visible in 64 mode) + + +; --------------------------------------------------------------------------- +; I/O: SID + +SID = $D400 +SID_S1Lo = $D400 +SID_S1Hi = $D401 +SID_PB1Lo = $D402 +SID_PB1Hi = $D403 +SID_Ctl1 = $D404 +SID_AD1 = $D405 +SID_SUR1 = $D406 + +SID_S2Lo = $D407 +SID_S2Hi = $D408 +SID_PB2Lo = $D409 +SID_PB2Hi = $D40A +SID_Ctl2 = $D40B +SID_AD2 = $D40C +SID_SUR2 = $D40D + +SID_S3Lo = $D40E +SID_S3Hi = $D40F +SID_PB3Lo = $D410 +SID_PB3Hi = $D411 +SID_Ctl3 = $D412 +SID_AD3 = $D413 +SID_SUR3 = $D414 + +SID_FltLo = $D415 +SID_FltHi = $D416 +SID_FltCtl = $D417 +SID_Amp = $D418 +SID_ADConv1 = $D419 +SID_ADConv2 = $D41A +SID_Noise = $D41B +SID_Read3 = $D41C + +; --------------------------------------------------------------------------- +; I/O: VDC (128 only) + +VDC_INDEX = $D600 +VDC_DATA = $D601 + +; --------------------------------------------------------------------------- +; I/O: CIAs + +CIA1 = $DC00 +CIA1_PRA = $DC00 +CIA1_PRB = $DC01 +CIA1_DDRA = $DC02 +CIA1_DDRB = $DC03 +CIA1_ICR = $DC0D +CIA1_CRA = $DC0E +CIA1_CRB = $DC0F + +CIA2 = $DD00 +CIA2_PRA = $DD00 +CIA2_PRB = $DD01 +CIA2_DDRA = $DD02 +CIA2_DDRB = $DD03 +CIA2_ICR = $DD0D +CIA2_CRA = $DD0E +CIA2_CRB = $DD0F + +; --------------------------------------------------------------------------- +; I/O: MMU + +MMU_CR = $FF00 + +; --------------------------------------------------------------------------- +; Super CPU + +SCPU_VIC_Bank1 = $D075 +SCPU_Slow = $D07A +SCPU_Fast = $D07B +SCPU_EnableRegs = $D07E +SCPU_DisableRegs= $D07F +SCPU_Detect = $D0BC + + diff --git a/libsrc/c128/cgetc.s b/libsrc/c128/cgetc.s new file mode 100644 index 000000000..391a25fd2 --- /dev/null +++ b/libsrc/c128/cgetc.s @@ -0,0 +1,31 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; char cgetc (void); +; + + .export _cgetc + .import cursor + + .include "c128.inc" + +_cgetc: lda KEY_COUNT ; Get number of characters + bne L2 ; Jump if there are already chars waiting + +; Switch on the cursor if needed + + lda cursor + beq L1 + jsr CURS_ON + jmp L2 +L1: lda #$01 + jsr CURS_OFF +L2: lda KEY_COUNT ; Check characters again + beq L2 + jsr CURS_OFF ; Switch cursor off, if characters available + + jsr KBDREAD ; Read char and return in A + ldx #0 + rts + + diff --git a/libsrc/c128/clrscr.s b/libsrc/c128/clrscr.s new file mode 100644 index 000000000..54219b007 --- /dev/null +++ b/libsrc/c128/clrscr.s @@ -0,0 +1,14 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void clrscr (void); +; + + .export _clrscr + + .include "c128.inc" + +_clrscr = CLRSCR + + + diff --git a/libsrc/c128/color.s b/libsrc/c128/color.s new file mode 100644 index 000000000..c63309032 --- /dev/null +++ b/libsrc/c128/color.s @@ -0,0 +1,33 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char __fastcall__ textcolor (unsigned char color); +; unsigned char __fastcall__ bgcolor (unsigned char color); +; unsigned char __fastcall__ bordercolor (unsigned char color); +; + + .export _textcolor, _bgcolor, _bordercolor + + .include "c128.inc" + + +_textcolor: + ldx CHARCOLOR ; get old value + sta CHARCOLOR ; set new value + txa + rts + + +_bgcolor: + ldx VIC_BG_COLOR0 ; get old value + sta VIC_BG_COLOR0 ; set new value + txa + rts + + +_bordercolor: + ldx VIC_BG_COLOR0 ; get old value + sta VIC_BORDERCOLOR ; set new value + txa + rts + diff --git a/libsrc/c128/conio.s b/libsrc/c128/conio.s new file mode 100644 index 000000000..481b4e7b4 --- /dev/null +++ b/libsrc/c128/conio.s @@ -0,0 +1,50 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; Low level stuff for screen output/console input +; + + .export initconio, doneconio + .exportzp CURS_X, CURS_Y + .import xsize, ysize + + .include "c128.inc" + .include "../cbm/cbm.inc" + +.bss +keyvec: .res 2 + + +.code + +initconio: + jsr SCREEN + inx + stx xsize + iny + sty ysize + +; Save the old vector + + lda KeyStoreVec + sta keyvec + lda KeyStoreVec+1 + sta keyvec+1 + +; Set the new vector. I can only hope that this works for other C128 +; versions... + + lda #<$C6B7 + ldx #>$C6B7 + +SetVec: sei + sta KeyStoreVec + stx KeyStoreVec+1 + cli + rts + +doneconio: + lda keyvec + ldx keyvec+1 + bne SetVec + diff --git a/libsrc/c128/cputc.s b/libsrc/c128/cputc.s new file mode 100644 index 000000000..99253d04a --- /dev/null +++ b/libsrc/c128/cputc.s @@ -0,0 +1,105 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void cputcxy (unsigned char x, unsigned char y, char c); +; void cputc (char c); +; + + .export _cputcxy, _cputc, cputdirect, putchar + .export advance, newline, plot + .import popa, _gotoxy + .import xsize, revers + + .include "c128.inc" + .include "../cbm/cbm.inc" + +_cputcxy: + pha ; Save C + jsr popa ; Get Y + jsr _gotoxy ; Set cursor, drop x + pla ; Restore C + +; Plot a character - also used as internal function + +_cputc: cmp #$0D ; CR? + bne L1 + lda #0 + sta CURS_X + beq plot ; Recalculate pointers + +L1: cmp #$0A ; LF? + bne L2 + ldy CURS_Y + iny + bne newline ; Recalculate pointers + +; Printable char of some sort + +L2: cmp #' ' + bcc cputdirect ; Other control char + tay + bmi L10 + cmp #$60 + bcc L3 + and #$DF + bne cputdirect ; Branch always +L3: and #$3F + +cputdirect: + jsr putchar ; Write the character to the screen + +; Advance cursor position + +advance: + iny + cpy xsize + bne L9 + ldy #0 ; new line +newline: + clc + lda xsize + adc SCREEN_PTR + sta SCREEN_PTR + bcc L4 + inc SCREEN_PTR+1 +L4: clc + lda xsize + adc CRAM_PTR + sta CRAM_PTR + bcc L5 + inc CRAM_PTR+1 +L5: inc CURS_Y +L9: sty CURS_X + rts + +; Handle character if high bit set + +L10: and #$7F + cmp #$7E ; PI? + bne L11 + lda #$5E ; Load screen code for PI + bne cputdirect +L11: ora #$40 + bne cputdirect + + + +; Set cursor position, calculate RAM pointers + +plot: ldy CURS_X + ldx CURS_Y + clc + jmp PLOT ; Set the new cursor + + + +; Write one character to the screen without doing anything else, return X +; position in Y + +putchar: + ora revers ; Set revers bit + ldy CURS_X + sta (SCREEN_PTR),y ; Set char + lda CHARCOLOR + sta (CRAM_PTR),y ; Set color + rts diff --git a/libsrc/c128/crt0.s b/libsrc/c128/crt0.s new file mode 100644 index 000000000..f10078d32 --- /dev/null +++ b/libsrc/c128/crt0.s @@ -0,0 +1,141 @@ +; +; Startup code for cc65 (C128 version) +; +; This must be the *first* file on the linker command line +; + + .export _exit + .import __hinit, initconio, doneconio, zerobss + .import push0, doatexit, _main + + .include "c128.inc" + .include "../cbm/cbm.inc" + +; ------------------------------------------------------------------------ +; Define and export the ZP variables for the C64 runtime + + .exportzp sp, sreg, regsave + .exportzp ptr1, ptr2, ptr3, ptr4 + .exportzp tmp1, tmp2, tmp3, tmp4 + .exportzp regbank, zpspace + +sp = $02 ; stack pointer +sreg = $04 ; secondary register/high 16 bit for longs +regsave = $06 ; slot to save/restore (E)AX into +ptr1 = $0A ; +ptr2 = $0C +ptr3 = $0E +ptr4 = $10 +tmp1 = $12 +tmp2 = $13 +tmp3 = $14 +tmp4 = $15 +regbank = $16 ; 6 byte register bank +zpspace = $1A ; Zero page space allocated + +; ------------------------------------------------------------------------ +; BASIC header with a SYS call + + .org $1BFF + .word Head ; Load address +Head: .word @Next + .word 1000 ; Line number + .byte $9E,"7181" ; SYS 7181 + .byte $00 ; End of BASIC line +@Next: .word 0 ; BASIC end marker + .reloc + +; ------------------------------------------------------------------------ +; Actual code + + ldy #zpspace-1 +L1: lda sp,y + sta zpsave,y ; save the zero page locations we need + dey + bpl L1 + +; Close open files + + jsr CLRCH + +; Switch to second charset + + lda #14 + jsr BSOUT + +; Get the current MMU setting and save it. Set new memory config. + + lda MMU_CR ; Get current memory configuration... + pha ; ...and save it for later + lda #$0E ; Bank0 with kernal ROM + sta MMU_CR + +; Clear the BSS data + + jsr zerobss + +; Save system stuff and setup the stack + + pla ; Get MMU setting + sta mmusave + + tsx + stx spsave ; save system stk ptr + + lda #<$C000 + sta sp + lda #>$C000 + sta sp+1 + +; Initialize the heap + + jsr __hinit + +; Initialize conio stuff + + jsr initconio + +; Pass an empty command line + + jsr push0 ; argc + jsr push0 ; argv + + ldy #4 ; Argument size + jsr _main ; call the users code + +; fall thru to exit... + +_exit: jsr doatexit ; call exit functions + +; Reset the conio stuff + + jsr doneconio + +; Reset stack and the MMU + + ldx spsave ; Patched at runtime + txs + lda mmusave ; Patched at runtime + sta MMU_CR + +; Copy back the zero page stuff + + ldy #zpspace-1 +L2: lda zpsave,y + sta sp,y + dey + bpl L2 + +; Done + + jmp RESTOR + +.data +zpsave: .res zpspace + +.bss +spsave: .res 1 +mmusave:.res 1 + + + diff --git a/libsrc/c128/dbgbreak.s b/libsrc/c128/dbgbreak.s new file mode 100644 index 000000000..472d800fe --- /dev/null +++ b/libsrc/c128/dbgbreak.s @@ -0,0 +1,24 @@ +; +; Ullrich von Bassewitz, 10.08.1998 +; +; unsigned DbgSetBreakVec (unsigned Addr); +; + + .export _DbgSetBreakVec + .import popax, utstax + + .include "../cbm/cbm.inc" + +_DbgSetBreakVec: + jsr popax ; Get the new address + ldy BRKVec + sta BRKVec + lda BRKVec+1 + stx BRKVec+1 + tax + tya + jmp utstax + + + + diff --git a/libsrc/c128/kbhit.s b/libsrc/c128/kbhit.s new file mode 100644 index 000000000..f63b941a9 --- /dev/null +++ b/libsrc/c128/kbhit.s @@ -0,0 +1,21 @@ +; +; Ullrich von Bassewitz, 18.08.1998 +; +; int kbhit (void); +; + + .export _kbhit + .import return0, return1 + + .include "c128.inc" + +_kbhit: + lda KEY_COUNT ; Get number of characters +; ora FKEY_COUNT ; Or with number of chars from function keys + bne L1 + jmp return0 +L1: jmp return1 + + + + diff --git a/libsrc/c128/readjoy.s b/libsrc/c128/readjoy.s new file mode 100644 index 000000000..e4098052d --- /dev/null +++ b/libsrc/c128/readjoy.s @@ -0,0 +1,41 @@ +; +; Ullrich von Bassewitz, 23.09.1998 +; +; unsigned readjoy (unsigned char joy); +; + + .export _readjoy + + .include "c128.inc" + + +.proc _readjoy + + tax ; Joystick number into X + bne joy2 + +; Read joystick 1 + +joy1: lda #$7F + sei + sta CIA1_PRA + lda CIA1_PRB + cli + and #$1F + eor #$1F + rts + +; Read joystick 2 + +joy2: ldx #0 + lda #$E0 + ldy #$FF + sta CIA1_DDRA + lda CIA1_PRA + sty CIA1_DDRA + and #$1F + eor #$1F + rts + +.endproc + diff --git a/libsrc/c64/Makefile b/libsrc/c64/Makefile new file mode 100644 index 000000000..bfa8bc211 --- /dev/null +++ b/libsrc/c64/Makefile @@ -0,0 +1,28 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $< + @$(AS) -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +C_OBJS = + +S_OBJS = crt0.o read.o write.o kbhit.o conio.o clrscr.o mouse.o\ + cputc.o cgetc.o color.o readjoy.o break.o rs232.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f $(C_OBJS:.c=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) + @rm -f crt0.o + diff --git a/libsrc/c64/break.s b/libsrc/c64/break.s new file mode 100644 index 000000000..8b90d4fb5 --- /dev/null +++ b/libsrc/c64/break.s @@ -0,0 +1,108 @@ +; +; Ullrich von Bassewitz, 27.09.1998 +; +; void set_brk (unsigned Addr); +; void reset_brk (void); +; + + .export _set_brk, _reset_brk + .export _brk_a, _brk_x, _brk_y, _brk_sr, _brk_pc + .import _atexit + + .include "c64.inc" + + +.bss +_brk_a: .res 1 +_brk_x: .res 1 +_brk_y: .res 1 +_brk_sr: .res 1 +_brk_pc: .res 2 + +oldvec: .res 2 ; Old vector + + +.data +uservec: jmp $FFFF ; Patched at runtime + + +.code + +; Set the break vector +.proc _set_brk + + sta uservec+1 + stx uservec+2 ; Set the user vector + + lda oldvec + ora oldvec+1 ; Did we save the vector already? + bne L1 ; Jump if we installed the handler already + + lda BRKVec + sta oldvec + lda BRKVec+1 + sta oldvec+1 ; Save the old vector + + lda #<_reset_brk + ldx #>_reset_brk + jsr _atexit ; Install an exit handler + +L1: lda #brk_handler + sta BRKVec+1 + rts + +.endproc + + +; Reset the break vector +.proc _reset_brk + + lda oldvec + sta BRKVec + lda oldvec+1 + sta BRKVec+1 + rts + +.endproc + + + +; Break handler, called if a break occurs + +.proc brk_handler + + pla + sta _brk_y + pla + sta _brk_x + pla + sta _brk_a + pla + and #$EF ; Clear break bit + sta _brk_sr + pla ; PC low + sec + sbc #2 ; Point to start of brk + sta _brk_pc + pla ; PC high + sbc #0 + sta _brk_pc+1 + + jsr uservec ; Call the user's routine + + lda _brk_pc+1 + pha + lda _brk_pc + pha + lda _brk_sr + pha + ldx _brk_x + ldy _brk_y + lda _brk_a + rti ; Jump back... + +.endproc + + diff --git a/libsrc/c64/c64.inc b/libsrc/c64/c64.inc new file mode 100644 index 000000000..315aaf698 --- /dev/null +++ b/libsrc/c64/c64.inc @@ -0,0 +1,194 @@ +; +; C64 generic definitions. Stolen from Elite128 +; + + +; --------------------------------------------------------------------------- +; Zero page, Commodore stuff + +ST = $90 ; IEC status byte + +FNAM_LEN = $B7 ; Length of filename +SECADR = $B9 ; Secondary address +DEVNUM = $BA ; Device number +KEY_COUNT = $C6 ; Number of keys in input buffer +CURS_FLAG = $CC ; 1 = cursor off +CURS_BLINK = $CD ; Blink counter +CURS_CHAR = $CE ; Character under the cursor +CURS_COLOR = $287 ; Color under the cursor +CURS_STATE = $CF ; Cursor blink state +SCREEN_PTR = $D1 ; Pointer to current char in text screen +CURS_X = $D3 ; Cursor column +CURS_Y = $D6 ; Cursor row +CRAM_PTR = $F3 ; Pointer to current char in color RAM + +CHARCOLOR = $286 +PALFLAG = $2A6 ; $01 = PAL, $00 = NTSC + + +; --------------------------------------------------------------------------- +; Kernal routines + +; Direct entries +CLRSCR = $E544 +KBDREAD = $E5B4 +NAMED_OPEN = $F3D5 +NAMED_CLOSE = $F642 +PLOTCHAR = $EA1C ; Char in A, color in X + +; --------------------------------------------------------------------------- +; Vector and other locations + +IRQVec = $0314 +BRKVec = $0316 +NMIVec = $0318 + +; --------------------------------------------------------------------------- +; I/O: VIC + +VIC = $D000 +VIC_SPR0_X = $D000 +VIC_SPR0_Y = $D001 +VIC_SPR1_X = $D002 +VIC_SPR1_Y = $D003 +VIC_SPR2_X = $D004 +VIC_SPR2_Y = $D005 +VIC_SPR3_X = $D006 +VIC_SPR3_Y = $D007 +VIC_SPR4_X = $D008 +VIC_SPR4_Y = $D009 +VIC_SPR5_X = $D00A +VIC_SPR5_Y = $D00B +VIC_SPR6_X = $D00C +VIC_SPR6_Y = $D00D +VIC_SPR7_X = $D00E +VIC_SPR7_Y = $D00F +VIC_SPR_HI_X = $D010 +VIC_SPR_ENA = $D015 +VIC_SPR_EXP_X = $D017 +VIC_SPR_EXP_Y = $D01D +VIC_SPR_MCOLOR = $D01C +VIC_SPR_BG_PRIO = $D01B + +VIC_SPR_MCOLOR0 = $D025 +VIC_SPR_MCOLOR1 = $D026 + +VIC_SPR0_COLOR = $D027 +VIC_SPR1_COLOR = $D028 +VIC_SPR2_COLOR = $D029 +VIC_SPR3_COLOR = $D02A +VIC_SPR4_COLOR = $D02B +VIC_SPR5_COLOR = $D02C +VIC_SPR6_COLOR = $D02D +VIC_SPR7_COLOR = $D02E + +VIC_CTRL1 = $D011 +VIC_CTRL2 = $D016 + +VIC_HLINE = $D012 + +VIC_VIDEO_ADR = $D018 + +VIC_IRR = $D019 ; Interrupt request register +VIC_IMR = $D01A ; Interrupt mask register + +VIC_BORDERCOLOR = $D020 +VIC_BG_COLOR0 = $D021 +VIC_BG_COLOR1 = $D022 +VIC_BG_COLOR2 = $D023 +VIC_BG_COLOR3 = $D024 + +; 128 stuff: +VIC_KBD_128 = $D02F ; Extended kbd bits (visible in 64 mode) +VIC_CLK_128 = $D030 ; Clock rate register (visible in 64 mode) + + +; --------------------------------------------------------------------------- +; I/O: SID + +SID = $D400 +SID_S1Lo = $D400 +SID_S1Hi = $D401 +SID_PB1Lo = $D402 +SID_PB1Hi = $D403 +SID_Ctl1 = $D404 +SID_AD1 = $D405 +SID_SUR1 = $D406 + +SID_S2Lo = $D407 +SID_S2Hi = $D408 +SID_PB2Lo = $D409 +SID_PB2Hi = $D40A +SID_Ctl2 = $D40B +SID_AD2 = $D40C +SID_SUR2 = $D40D + +SID_S3Lo = $D40E +SID_S3Hi = $D40F +SID_PB3Lo = $D410 +SID_PB3Hi = $D411 +SID_Ctl3 = $D412 +SID_AD3 = $D413 +SID_SUR3 = $D414 + +SID_FltLo = $D415 +SID_FltHi = $D416 +SID_FltCtl = $D417 +SID_Amp = $D418 +SID_ADConv1 = $D419 +SID_ADConv2 = $D41A +SID_Noise = $D41B +SID_Read3 = $D41C + +; --------------------------------------------------------------------------- +; I/O: VDC (128 only) + +VDC_INDEX = $D600 +VDC_DATA = $D601 + +; --------------------------------------------------------------------------- +; I/O: CIAs + +CIA1 = $DC00 +CIA1_PRA = $DC00 +CIA1_PRB = $DC01 +CIA1_DDRA = $DC02 +CIA1_DDRB = $DC03 +CIA1_ICR = $DC0D +CIA1_CRA = $DC0E +CIA1_CRB = $DC0F + +CIA2 = $DD00 +CIA2_PRA = $DD00 +CIA2_PRB = $DD01 +CIA2_DDRA = $DD02 +CIA2_DDRB = $DD03 +CIA2_ICR = $DD0D +CIA2_CRA = $DD0E +CIA2_CRB = $DD0F + +; --------------------------------------------------------------------------- +; Super CPU + +SCPU_VIC_Bank1 = $D075 +SCPU_Slow = $D07A +SCPU_Fast = $D07B +SCPU_EnableRegs = $D07E +SCPU_DisableRegs= $D07F +SCPU_Detect = $D0BC + + +; --------------------------------------------------------------------------- +; Processor Port at $01 + +LORAM = $01 ; Enable the basic rom +HIRAM = $02 ; Enable the kernal rom +IOEN = $04 ; Enable I/O +CASSDATA = $08 ; Cassette data +CASSPLAY = $10 ; Cassette: Play +CASSMOT = $20 ; Cassette motor on +TP_FAST = $80 ; Switch Rossmoeller TurboProcess to fast mode + +RAMONLY = $F8 ; (~(LORAM | HIRAM | IOEN)) & $FF + + diff --git a/libsrc/c64/cgetc.s b/libsrc/c64/cgetc.s new file mode 100644 index 000000000..5c4b407c9 --- /dev/null +++ b/libsrc/c64/cgetc.s @@ -0,0 +1,61 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; char cgetc (void); +; + + .export _cgetc + .import cursor + + .include "c64.inc" + +_cgetc: lda KEY_COUNT ; Get number of characters + bne L3 ; Jump if there are already chars waiting + +; Switch on the cursor if needed + + lda CURS_FLAG + pha + lda cursor + jsr setcursor +L1: lda KEY_COUNT + beq L1 + ldx #0 + pla + bne L2 + inx +L2: txa + jsr setcursor + +L3: jsr KBDREAD ; Read char and return in A + ldx #0 + rts + + +; Switch the cursor on or off + +.proc setcursor + + tax ; On or off? + bne seton ; Go set it on + lda CURS_FLAG ; Is the cursor currently off? + bne crs9 ; Jump if yes + lda #1 + sta CURS_FLAG ; Mark it as off + lda CURS_STATE ; Cursor currently displayed? + beq crs8 ; Jump if no + ldy CURS_X ; Get the character column + lda (SCREEN_PTR),y ; Get character + eor #$80 + sta (SCREEN_PTR),y ; Store character back + lda CURS_COLOR + sta (CRAM_PTR),y ; Store color back +crs8: lda #0 + sta CURS_STATE ; Cursor not displayed +crs9: rts + +seton: lda #0 + sta CURS_FLAG + rts + +.endproc diff --git a/libsrc/c64/clrscr.s b/libsrc/c64/clrscr.s new file mode 100644 index 000000000..e0f8aedfb --- /dev/null +++ b/libsrc/c64/clrscr.s @@ -0,0 +1,14 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void clrscr (void); +; + + .export _clrscr + + .include "c64.inc" + +_clrscr = CLRSCR + + + diff --git a/libsrc/c64/color.s b/libsrc/c64/color.s new file mode 100644 index 000000000..d4a4e4f3e --- /dev/null +++ b/libsrc/c64/color.s @@ -0,0 +1,33 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char __fastcall__ textcolor (unsigned char color); +; unsigned char __fastcall__ bgcolor (unsigned char color); +; unsigned char __fastcall__ bordercolor (unsigned char color); +; + + + .export _textcolor, _bgcolor, _bordercolor + + .include "c64.inc" + +_textcolor: + ldx CHARCOLOR ; get old value + sta CHARCOLOR ; set new value + txa + rts + + +_bgcolor: + ldx VIC_BG_COLOR0 ; get old value + sta VIC_BG_COLOR0 ; set new value + txa + rts + + +_bordercolor: + ldx VIC_BG_COLOR0 ; get old value + sta VIC_BORDERCOLOR ; set new value + txa + rts + diff --git a/libsrc/c64/conio.s b/libsrc/c64/conio.s new file mode 100644 index 000000000..f822c945d --- /dev/null +++ b/libsrc/c64/conio.s @@ -0,0 +1,23 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; Low level stuff for screen output/console input +; + + .export initconio + .exportzp CURS_X, CURS_Y + .import xsize, ysize + + .include "../cbm/cbm.inc" + .include "c64.inc" + +.code + +initconio: + jsr SCREEN + stx xsize + sty ysize + rts + + + diff --git a/libsrc/c64/cputc.s b/libsrc/c64/cputc.s new file mode 100644 index 000000000..bb7384a6a --- /dev/null +++ b/libsrc/c64/cputc.s @@ -0,0 +1,105 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void cputcxy (unsigned char x, unsigned char y, char c); +; void cputc (char c); +; + + .export _cputcxy, _cputc, cputdirect, putchar + .export advance, newline, plot + .import popa, _gotoxy + .import xsize, revers + + .include "c64.inc" + .include "../cbm/cbm.inc" + +_cputcxy: + pha ; Save C + jsr popa ; Get Y + jsr _gotoxy ; Set cursor, drop x + pla ; Restore C + +; Plot a character - also used as internal function + +_cputc: cmp #$0D ; CR? + bne L1 + lda #0 + sta CURS_X + beq plot ; Recalculate pointers + +L1: cmp #$0A ; LF? + bne L2 + ldy CURS_Y + iny + bne newline ; Recalculate pointers + +; Printable char of some sort + +L2: cmp #' ' + bcc cputdirect ; Other control char + tay + bmi L10 + cmp #$60 + bcc L3 + and #$DF + bne cputdirect ; Branch always +L3: and #$3F + +cputdirect: + jsr putchar ; Write the character to the screen + +; Advance cursor position + +advance: + iny + cpy xsize + bne L9 + ldy #0 ; new line +newline: + clc + lda xsize + adc SCREEN_PTR + sta SCREEN_PTR + bcc L4 + inc SCREEN_PTR+1 +L4: clc + lda xsize + adc CRAM_PTR + sta CRAM_PTR + bcc L5 + inc CRAM_PTR+1 +L5: inc CURS_Y +L9: sty CURS_X + rts + +; Handle character if high bit set + +L10: and #$7F + cmp #$7E ; PI? + bne L11 + lda #$5E ; Load screen code for PI + bne cputdirect +L11: ora #$40 + bne cputdirect + + + +; Set cursor position, calculate RAM pointers + +plot: ldy CURS_X + ldx CURS_Y + clc + jmp PLOT ; Set the new cursor + + + +; Write one character to the screen without doing anything else, return X +; position in Y + +putchar: + ora revers ; Set revers bit + ldy CURS_X + sta (SCREEN_PTR),y ; Set char + lda CHARCOLOR + sta (CRAM_PTR),y ; Set color + rts diff --git a/libsrc/c64/crt0.s b/libsrc/c64/crt0.s new file mode 100644 index 000000000..6d68f23ae --- /dev/null +++ b/libsrc/c64/crt0.s @@ -0,0 +1,132 @@ +; +; Startup code for cc65 (C64 version) +; +; This must be the *first* file on the linker command line +; + + .export _exit + .import __hinit, initconio, zerobss, push0, doatexit + .import _main + + .include "c64.inc" + .include "../cbm/cbm.inc" + +; ------------------------------------------------------------------------ +; Define and export the ZP variables for the C64 runtime + + .exportzp sp, sreg, regsave + .exportzp ptr1, ptr2, ptr3, ptr4 + .exportzp tmp1, tmp2, tmp3, tmp4 + .exportzp regbank, zpspace + +sp = $02 ; stack pointer +sreg = $04 ; secondary register/high 16 bit for longs +regsave = $06 ; slot to save/restore (E)AX into +ptr1 = $0A ; +ptr2 = $0C +ptr3 = $0E +ptr4 = $10 +tmp1 = $12 +tmp2 = $13 +tmp3 = $14 +tmp4 = $15 +regbank = $16 ; 6 byte register bank +zpspace = $1A ; Zero page space allocated + +; ------------------------------------------------------------------------ +; BASIC header with a SYS call + + .org $7FF + .word Head ; Load address +Head: .word @Next + .word 1000 ; Line number + .byte $9E,"2061" ; SYS 2061 + .byte $00 ; End of BASIC line +@Next: .word 0 ; BASIC end marker + .reloc + +; ------------------------------------------------------------------------ +; Actual code + + ldy #zpspace-1 +L1: lda sp,y + sta zpsave,y ; Save the zero page locations we need + dey + bpl L1 + +; Close open files + + jsr CLRCH + +; Switch to second charset + + lda #14 + jsr BSOUT + +; Clear the BSS data + + jsr zerobss + +; Save system stuff and setup the stack + + tsx + stx spsave ; Save the system stack ptr + + lda $01 + sta mmusave ; Save the memory configuration + lda $01 + and #$F8 + ora #$06 ; Enable kernal+I/O, disable basic + sta $01 + + lda #<$D000 + sta sp + lda #>$D000 + sta sp+1 ; Set argument stack ptr + +; Initialize the heap + + jsr __hinit + +; Initialize conio stuff + + jsr initconio + +; Pass an empty command line + + jsr push0 ; argc + jsr push0 ; argv + + ldy #4 ; Argument size + jsr _main ; call the users code + +; fall thru to exit... + +_exit: jsr doatexit ; call exit functions + + ldx spsave + txs ; Restore stack pointer + lda mmusave + sta $01 ; Restore memory configuration + +; Copy back the zero page stuff + + ldy #zpspace-1 +L2: lda zpsave,y + sta sp,y + dey + bpl L2 + +; Reset changed vectors, back to basic + + jmp RESTOR + + +.data + +zpsave: .res zpspace + +.bss + +spsave: .res 1 +mmusave:.res 1 diff --git a/libsrc/c64/kbhit.s b/libsrc/c64/kbhit.s new file mode 100644 index 000000000..1a54c2e5e --- /dev/null +++ b/libsrc/c64/kbhit.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; int kbhit (void); +; + + .export _kbhit + .import return0, return1 + + .include "c64.inc" + +_kbhit: + lda KEY_COUNT ; Get number of characters + bne L1 + jmp return0 +L1: jmp return1 + + + + diff --git a/libsrc/c64/mouse.s b/libsrc/c64/mouse.s new file mode 100644 index 000000000..bada17de6 --- /dev/null +++ b/libsrc/c64/mouse.s @@ -0,0 +1,384 @@ +; +; Ullrich von Bassewitz, 24.04.1999 +; +; Routines for the 1351 proportional mouse. Parts of the code are from +; the Commodore 1351 mouse users guide. +; + + .export _mouse_init, _mouse_done + .export _mouse_hide, _mouse_show + .export _mouse_box, _mouse_info + .export _mouse_move + + .import popa, popsreg, addysp1 + .importzp sp, sreg + + .include "c64.inc" + +.code + +; -------------------------------------------------------------------------- +; +; void __fastcall__ mouse_init (unsigned char port, unsigned char sprite); +; + +_mouse_init: + tax ; Save sprite number + jsr popa ; Get the port number + + ldy OldIRQ+1 ; Already initialized? + bne Done ; Jump if yes + + stx MouseSprite ; Remember the sprite number + sta MousePort ; Remember the port number + +; Initialize variables + + ldx #0 + stx XPos + stx XPos+1 + stx YPos + stx YPos+1 + stx OldPotX + stx OldPotY + stx XMin + stx XMin+1 + stx YMin + stx YMin+1 + stx YMax+1 + inx ; X = 1 + stx Visible ; Mouse *not* visible + stx XMax+1 ; >320 + ldx #<320 + stx XMax + ldx #200 + stx YMax + +; Remember the old IRQ vector + + lda IRQVec + sta OldIRQ + lda IRQVec+1 + sta OldIRQ+1 + +; Set our own IRQ vector + + lda #MouseIRQ + bne SetIRQ + +; -------------------------------------------------------------------------- +; +; void mouse_done (void); +; + +_mouse_done: + lda OldIRQ ; Initialized? + ldx OldIRQ+1 + beq Done ; Jump if no + ldy #0 + sty OldIRQ+1 ; Reset the initialized flag +SetIRQ: sei ; Disable interrupts + sta IRQVec ; Set the new/old vector + stx IRQVec+1 + cli ; Enable interrupts +Done: rts + +; -------------------------------------------------------------------------- +; +; void mouse_hide (void); +; + +_mouse_hide: + lda Visible ; Get the flag + bne @L1 ; Jump if already invisible + ldx MouseSprite ; Sprite defined? + beq @L1 ; Jump if no + + lda BitMask-1,x ; Get bit mask + eor #$FF ; We must clear the bit + + sei ; Disable interrupts + and VIC_SPR_ENA + sta VIC_SPR_ENA ; Disable sprite + cli ; Enable interrupts + +@L1: inc Visible ; Set the flag to invisible + rts + +; -------------------------------------------------------------------------- +; +; void mouse_show (void); +; + +_mouse_show: + dec Visible ; Get the flag + bne @L1 ; Jump if still invisible + ldx MouseSprite ; Sprite defined? + beq @L1 ; Jump if no + + lda BitMask-1,x ; Get bit mask + sei ; Disable interrupts + ora VIC_SPR_ENA + sta VIC_SPR_ENA ; Enable sprite + cli ; Enable interrupts + +@L1: rts + +; -------------------------------------------------------------------------- +; +; void __fastcall__ mouse_box (int minx, int miny, int maxx, int maxy); +; + +_mouse_box: + sei ; Disable interrupts + + sta YMax + stx YMax+1 ; maxy + + ldy #0 + lda (sp),y + sta XMax + iny + lda (sp),y + sta XMax+1 ; maxx + + iny + lda (sp),y + sta YMin + iny + lda (sp),y + sta YMin+1 ; miny + + iny + lda (sp),y + sta XMin + iny + lda (sp),y + sta XMin+1 ; minx + + cli ; Enable interrupts + + jmp addysp1 ; Drop params, return + +; -------------------------------------------------------------------------- +; +; void mouse_info (...); +; + +_mouse_info: + rts + + +; -------------------------------------------------------------------------- +; +; void __fastcall__ mouse_move (int x, int y); +; + +_mouse_move: + jsr popsreg ; Get X + sei ; Disable interrupts + + sta YPos + stx YPos+1 + lda sreg + ldx sreg+1 + sta XPos + stx XPos+1 ; Set new position + + lda Visible ; Mouse visible? + bne @L9 ; Jump if no + lda MouseSprite ; Sprite defined? + beq @L9 + + jsr MoveSprite ; Move the sprite to the mouse pos + +@L9: cli ; Enable interrupts + rts + +; -------------------------------------------------------------------------- +; +; Mouse interrupt handler +; + +MouseIRQ: + cld + lda SID_ADConv1 ; Get mouse X movement + ldy OldPotX + jsr MoveCheck ; Calculate movement vector + sty OldPotX + +; Calculate the new X coordinate (--> a/y) + + clc + adc XPos + tay ; Remember low byte + txa + adc XPos+1 + +; Limit the X coordinate to the bounding box + + cpy XMin+1 + bne @L1 + cmp XMin +@L1: bpl @L2 + ldy XMin + lda XMin+1 + jmp @L4 + +@L2: cpy XMax+1 + bne @L3 + cmp XMax + beq @L4 +@L3: bmi @L4 + ldy XMax + lda XMax+1 +@L4: sty XPos + sta XPos+1 + +; Calculate the Y movement vector + + lda SID_ADConv2 ; Get mouse Y movement + ldy OldPotY + jsr MoveCheck ; Calculate movement + sty OldPotY + +; Calculate the new Y coordinate (--> a/y) + + clc + adc YPos + tay ; Remember low byte + txa + adc YPos+1 + +; Limit the Y coordinate to the bounding box + + cpy YMin+1 + bne @L5 + cmp YMin +@L5: bpl @L6 + ldy YMin + lda YMin+1 + jmp @L8 + +@L6: cpy YMax+1 + bne @L7 + cmp YMax + beq @L8 +@L7: bmi @L8 + ldy YMax + lda YMax+1 +@L8: sty YPos + sta YPos+1 + +; Jump to the next IRQ handler + + jmp (OldIRQ) + + +; -------------------------------------------------------------------------- +; +; Move check routine, called for both coordinates. +; +; Entry: y = old value of pot register +; a = current value of pot register +; Exit: y = value to use for old value +; x/a = delta value for position +; + +MoveCheck: + sty OldValue + sta NewValue + ldx #$00 + + sec ; a = mod64 (new - old) + sbc OldValue + and #%01111111 + cmp #%01000000 ; if (a > 0) + bcs @L1 ; + lsr a ; a /= 2; + beq @L2 ; if (a != 0) + ldy NewValue ; y = NewValue + rts ; return + +@L1: ora #%11000000 ; else or in high order bits + cmp #$FF ; if (a != -1) + beq @L2 + sec + ror a ; a /= 2 + ldx #$FF ; high byte = -1 + ldy NewValue + rts + +@L2: lda #0 + rts + +; -------------------------------------------------------------------------- +; +; Move the mouse sprite to the current mouse position. Must be called +; with interrupts off. +; + +MoveSprite: + lda Visible ; Mouse pointer visible? + bne @L9 ; Jump if no + ldx MouseSprite ; Sprite defined? + beq @L9 ; Jump if no + ldy BitMask-1,x ; Get high bit mask + txa + asl a ; Index*2 + tax + +; Set the X position + + lda XPos+1 ; Negative? + bmi @L2 ; Jump if yes + beq @L1 + tya ; Load high position bit +@L1: ora VIC_SPR_HI_X ; Set high bit + sta VIC_SPR_HI_X + lda XPos + sta VIC_SPR0_X,x ; Set low byte + +; Set the Y position + +@L2: ldy YPos+1 ; Negative or too large? + bne @L9 ; Jump if yes + lda YPos + sta VIC_SPR0_Y,x ; Set Y position + +; Done + +@L9: rts + +; -------------------------------------------------------------------------- +; Data + +.bss + +OldIRQ: .res 2 ; Old IRQ vector +MousePort: .res 1 ; Port used for the mouse +MouseSprite: .res 1 ; Number of sprite to control +OldValue: .res 1 ; Temp for MoveCheck routine +NewValue: .res 1 ; Temp for MoveCheck routine + +Visible: .res 1 ; Is the mouse visible? +OldPotX: .res 1 ; Old hw counter values +OldPotY: .res 1 + +XPos: .res 2 ; Current mouse position, X +YPos: .res 2 ; Current mouse position, Y + +XMin: .res 2 ; X1 value of bounding box +YMin: .res 2 ; Y1 value of bounding box +XMax: .res 2 ; X2 value of bounding box +YMax: .res 2 ; Y2 value of bounding box + +.data + +BitMask: .byte $01, $02, $04, $08, $10, $20, $40, $80 + + + + + diff --git a/libsrc/c64/read.s b/libsrc/c64/read.s new file mode 100644 index 000000000..1aa40ad1e --- /dev/null +++ b/libsrc/c64/read.s @@ -0,0 +1,48 @@ +; +; Ullrich von Bassewitz, 30.05.1998 +; +; int read (int fd, void* buf, int count); +; +; THIS IS A HACK! +; + + .export _read + .import popax + .importzp ptr1, ptr2, ptr3 + + .include "../cbm/cbm.inc" + +_read: jsr popax ; get count + sta ptr2 + stx ptr2+1 ; save it for later + jsr popax ; get buf + sta ptr1 + stx ptr1+1 + jsr popax ; get fd and discard it + lda #0 + sta ptr3 + sta ptr3+1 ; set count + +L1: lda ptr2 + ora ptr2+1 ; count zero? + beq L9 + jsr BASIN + ldy #0 + sta (ptr1),y ; save char + inc ptr1 + bne L2 + inc ptr1+1 +L2: inc ptr3 ; increment count + bne L3 + inc ptr3+1 +L3: cmp #$0D ; CR? + bne L1 + +; Done, return the count + +L9: lda ptr3 + ldx ptr3+1 + rts + + + diff --git a/libsrc/c64/readjoy.s b/libsrc/c64/readjoy.s new file mode 100644 index 000000000..e5bb2f167 --- /dev/null +++ b/libsrc/c64/readjoy.s @@ -0,0 +1,41 @@ +; +; Ullrich von Bassewitz, 23.09.1998 +; +; unsigned readjoy (unsigned char joy); +; + + .export _readjoy + + .include "c64.inc" + + +.proc _readjoy + + tax ; Joystick number into X + bne joy2 + +; Read joystick 1 + +joy1: lda #$7F + sei + sta CIA1_PRA + lda CIA1_PRB + cli + and #$1F + eor #$1F + rts + +; Read joystick 2 + +joy2: ldx #0 + lda #$E0 + ldy #$FF + sta CIA1_DDRA + lda CIA1_PRA + sty CIA1_DDRA + and #$1F + eor #$1F + rts + +.endproc + diff --git a/libsrc/c64/rs232.s b/libsrc/c64/rs232.s new file mode 100644 index 000000000..e3c406fa8 --- /dev/null +++ b/libsrc/c64/rs232.s @@ -0,0 +1,699 @@ +; +; SwiftLink/Turbo-232 v0.90 device driver, by Craig Bruce, 14-Apr-1998. +; +; This software is Public Domain. It is in Buddy assembler format. +; +; This device driver uses the SwiftLink RS-232 Serial Cartridge, available from +; Creative Micro Designs, Inc, and also supports the extensions of the Turbo232 +; Serial Cartridge. Both devices are based on the 6551 ACIA chip. It also +; supports the "hacked" SwiftLink with a 1.8432 MHz crystal. +; +; The code assumes that the kernal + I/O are in context. On the C128, call +; it from Bank 15. On the C64, don't flip out the Kernal unless a suitable +; NMI catcher is put into the RAM under then Kernal. For the SuperCPU, the +; interrupt handling assumes that the 65816 is in 6502-emulation mode. +; +;-------------------------------------------------------------------------- +; +; Adapted for the use with the cc65 runtime library by +; Ullrich von Bassewitz (uz@musoftware.de) 02-May-1999. +; +; All external functions are C callable, the return value is an error code. +; + + + .importzp ptr1, ptr2, tmp1, tmp2 + .import popa, popax + .export _rs232_init, _rs232_params, _rs232_done, _rs232_get + .export _rs232_put, _rs232_pause, _rs232_unpause, _rs232_status + + +useC64 = 1 +.if useC64 + NmiExit = $febc ;exit address for nmi +.else + NmiExit = $ff33 ;exit address for nmi +.endif + +ACIA = $DE00 + + + +;---------------------------------------------------------------------------- +; +; Global variables +; + +.bss +DropCnt: .res 4 ; Number of bytes lost from rx buffer full +Initialized: .res 1 ; Flag indicating driver is initialized +Stopped: .res 1 ; Flow-stopped flag +RtsOff: .res 1 ; +Errors: .res 1 ; Number of bytes received in error, low byte +Turbo232: .res 1 ; Flag indicating turbo-232 +HackedFlag: .res 1 ; Flag indicating hacked-crystal swiftlink +CpuSpeed: .res 1 ; In MHz +RecvHead: .res 1 ; Head of receive buffer +RecvTail: .res 1 ; Tail of receive buffer +RecvFreeCnt: .res 1 ; Number of bytes in receive buffer +SendHead: .res 1 ; Head of send buffer +SendTail: .res 1 ; Tail of send buffer +SendFreeCnt: .res 1 ; Number of bytes free in send buffer +BaudCode: .res 1 ; Current baud in effect + +; Send and receive buffers: 256 bytes each +RecvBuf: .res 256 +SendBuf: .res 256 + +.data +NmiContinue: .byte $4c ; JMP instruction for NMI save -- continue +NmiSave: .res 2 ; normal NMI handler + +; Switftlink register offsets +RegData = 0 ; Data register +RegStatus = 1 ; Status register +RegCommand = 2 ; Command register +RegControl = 3 ; Control register +RegClock = 7 ; Turbo232 external baud-rate generator + +; Error codes. Beware: The codes must match the codes in the C header file +ErrNotInitialized = $01 +ErrBaudTooFast = $02 +ErrBaudNotAvail = $03 +ErrNoData = $04 +ErrOverflow = $05 + + +.code + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_init (char hacked); +; /* Initialize the serial port, install the interrupt handler. The parameter +; * must be true (non zero) for a hacked swiftlink and false (zero) otherwise. +; */ +; + +_rs232_init: + bit Initialized ;** shut down if started + bpl @L1 + pha + jsr _rs232_done + pla + +;** set hacked-crystal + +@L1: sta HackedFlag + +;** check for turbo-232 + + lda #$00 + sta ACIA+RegControl + tax + lda ACIA+RegClock + beq @L3 + dex +@L3: stx Turbo232 + +;** get C128/C64 cpu speed +.if useC64 + lda #1 + sta CpuSpeed +.else + lda $d030 + and #$01 + clc + adc #1 + sta CpuSpeed +.endif + +;** check for super-cpu at 20 MHz + + bit $d0bc + bmi @L4 + bit $d0b8 + bvs @L4 + lda #20 + sta CpuSpeed + +;** initialize buffers & control + +@L4: lda #0 + sta RecvHead + sta SendHead + sta RecvTail + sta SendTail + sta Errors + sta Stopped + lda #255 + sta RecvFreeCnt + sta SendFreeCnt + +;** set up nmi's + + lda $318 + ldy $319 + sta NmiSave+0 + sty NmiSave+1 + lda #NmiHandler + sta $318 + sty $319 + +;** set default to 2400-8N1, enable interrupts + + lda ACIA+RegData + lda ACIA+RegStatus + lda #$18 + bit HackedFlag + bpl @L5 + lda #$1a +@L5: sta ACIA+RegControl + + lda #$01 + sta RtsOff + ora #$08 + sta ACIA+RegCommand + lda #$06 + sta BaudCode + +;** return + lda #$ff + sta Initialized + lda #$00 + tax + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_params (unsigned char params, unsigned char parity); +; /* Set the port parameters. Use a combination of the #defined values above. */ +; +; Set communication parameters. +; +; baud rates stops word | parity +; --------------------- ----- ----- | --------- +; $00=50 $08=9600 $00=1 $00=8 | $00=none +; $01=110 $09=19200 $80=2 $20=7 | $20=odd +; $02=134.5 $0a=38400 $40=6 | $60=even +; $03=300 $0b=57600 $60=5 | $A0=mark +; $04=600 $0c=115200 | $E0=space +; $05=1200 $0d=230400 +; $06=2400 $0e=future +; $07=4800 $0f=future +; + +_rs232_params: + jsr CheckInitialized ;** check initialized + bcc @L1 + rts + +; Save new parity + +@L1: and #%11100000 + ora #%00000001 + sta tmp2 + +; Check cpu speed against baud rate + + jsr popa + sta tmp1 + and #$0f + cmp #$0c + bcc @L3 + ldx CpuSpeed + cpx #1+1 + bcc @L2 + cmp #$0c + beq @L3 + cpx #4 + bcs @L3 +@L2: lda #ErrBaudTooFast + bne @L9 + +; Set baud/parameters + +@L3: lda tmp1 + and #$0f + tax + lda NormBauds,x + bit HackedFlag + bpl @L4 + lda HackBauds,x +@L4: cmp #$ff + bne @L5 + lda #ErrBaudNotAvail + bne @L9 + +@L5: tax + and #$30 + beq @L6 + bit Turbo232 + bmi @L6 + lda #ErrBaudNotAvail + bne @L9 + +@L6: lda tmp1 + and #$0f + sta BaudCode + lda tmp1 + and #%11100000 + ora #%00010000 + sta tmp1 + txa + and #$0f + ora tmp1 + sta ACIA+RegControl + txa + and #%00110000 + beq @L7 + lsr + lsr + lsr + lsr + eor #%00000011 + sta ACIA+RegClock + +; Set new parity + +@L7: lda tmp2 + sta RtsOff + ora #%00001000 + sta ACIA+RegCommand + lda #0 +@L9: ldx #0 + rts + +.rodata + +NormBauds: + .byte $ff,$ff,$ff,$05,$06,$07,$08,$0a,$0c,$0e,$0f,$10,$20,$30,$ff,$ff +HackBauds: + .byte $01,$03,$04,$06,$07,$08,$0a,$0c,$0e,$0f,$ff,$ff,$00,$ff,$ff,$ff + ;in: 0 1 2 3 4 5 6 7 8 9 a b c d e f + ;baud50 110 134 3 6 12 24 48 96 19 38 57 115 230 exp exp + ;out masks: $0F=Baud, val$FF=err + ; $30=t232ExtBaud: $00=none, $10=57.6, $20=115.2, $30=230.4 +.code + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_done (void); +; /* Close the port, deinstall the interrupt hander. You MUST call this function +; * before terminating the program, otherwise the machine may crash later. If +; * in doubt, install an exit handler using atexit(). The function will do +; * nothing, if it was already called. +; */ +; + + +_rs232_done: + bit Initialized ;** check initialized + bpl @L9 + +; Stop interrupts, drop DTR + + lda RtsOff + and #%11100010 + ora #%00000010 + sta ACIA+RegCommand + +; Restore NMI vector + + lda NmiSave+0 + ldy NmiSave+1 + sta $318 + sty $319 + +; Flag uninitialized + +@L9: lda #$00 + sta Initialized + tax + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_get (char* B); +; /* Get a character from the serial port. If no characters are available, the +; * function will return RS_ERR_NO_DATA, so this is not a fatal error. +; */ +; + +_rs232_get: + jsr CheckInitialized ; Check if initialized + bcc @L1 + rts + +; Check for bytes to send + +@L1: sta ptr1 + stx ptr1+1 ; Store pointer to received char + ldx SendFreeCnt + cpx #$ff + beq @L2 + lda #$00 + jsr TryToSend + +; Check for buffer empty + +@L2: lda RecvFreeCnt + cmp #$ff + bne @L3 + lda #ErrNoData + ldx #0 + rts + +; Check for flow stopped & enough free: release flow control + +@L3: ldx Stopped + beq @L4 + cmp #63 + bcc @L4 + lda #$00 + sta Stopped + lda RtsOff + ora #%00001000 + sta ACIA+RegCommand + +; Get byte from buffer + +@L4: ldx RecvHead + lda RecvBuf,x + inc RecvHead + inc RecvFreeCnt + ldx #$00 + sta (ptr1,x) + txa ; Return code = 0 + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_put (char B); +; /* Send a character via the serial port. There is a transmit buffer, but +; * transmitting is not done via interrupt. The function returns +; * RS_ERR_OVERFLOW if there is no space left in the transmit buffer. +; */ +; + +_rs232_put: + jsr CheckInitialized ; Check initialized + bcc @L1 + rts + +; Try to send + +@L1: ldx SendFreeCnt + cpx #$ff + beq @L2 + pha + lda #$00 + jsr TryToSend + pla + +; Put byte into send buffer & send + +@L2: ldx SendFreeCnt + bne @L3 + lda #ErrOverflow + ldx #$00 + rts + +@L3: ldx SendTail + sta SendBuf,x + inc SendTail + dec SendFreeCnt + lda #$ff + jsr TryToSend + lda #$00 + tax + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_pause (void); +; /* Assert flow control and disable interrupts. */ +; + +_rs232_pause: +; Check initialized + jsr CheckInitialized + bcc @L1 + rts + +; Assert flow control + +@L1: lda RtsOff + sta Stopped + sta ACIA+RegCommand + +; Delay for flow stop to be received + + ldx BaudCode + lda PauseTimes,x + jsr DelayMs + +; Stop rx interrupts + + lda RtsOff + ora #$02 + sta ACIA+RegCommand + lda #0 + tax + rts + + +.rodata +; Delay times: 32 byte-receive times in milliseconds, or 100 max. +; Formula = 320,000 / baud +PauseTimes: + .byte 100,100,100,100,100,100,100,067,034,017,009,006,003,002,001,001 + ;in: 0 1 2 3 4 5 6 7 8 9 a b c d e f + ;baud50 110 134 3 6 12 24 48 96 19 38 57 115 230 exp exp +.code + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_unpause (void); +; /* Re-enable interrupts and release flow control */ +; + +_rs232_unpause: +; Check initialized + jsr CheckInitialized + bcc @L1 + rts + +; Re-enable rx interrupts & release flow control + +@L1: lda #$00 + sta Stopped + lda RtsOff + ora #%00001000 + sta ACIA+RegCommand + +; Poll for stalled char & exit + + jsr PollReceive + lda #0 + tax + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_status (unsigned char* status, +; unsigned char* errors); +; /* Return the serial port status. */ +; + +_rs232_status: + sta ptr2 + stx ptr2+1 + jsr popax + sta ptr1 + stx ptr1+1 + jsr CheckInitialized + bcs @L9 + +; Get status + + lda ACIA+RegStatus + ldy #0 + sta (ptr1),y + jsr PollReceive ; bug-recovery hack + lda Errors + sta (ptr2),y + tya + tax +@L9: rts + +;---------------------------------------------------------------------------- +; +; NMI handler +; C128 NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=33, ROMexit=30 +; C64 NMI overhead=76 cycles: int=7, maxLatency=6, ROMenter=34, ROMexit=29 +; +; timing: normal=76+43+9=128 cycles, assertFlow=76+52+9=137 cycles +; +; C128 @ 115.2k: 177 cycles avail (fast) +; C64 @ 57.6k: 177 cycles avail, worstAvail=177-43? = 134 +; SCPU @ 230.4k: 868 cycles avail: for a joke! +; + +NmiHandler: +.if useC64 + pha + lda ACIA+RegStatus ;(4) ;status ;check for byte received + and #$08 ;(2) + beq @L9 ;(2*) + cld + txa + pha + tya + pha +.else + lda ACIA+RegStatus ;(4) ;status ;check for byte received + and #$08 ;(2) + beq NmiNorm ;(2*) +.endif + lda ACIA+RegStatus ;(4) opt ;status ;check for receive errors + and #$07 ;(2) opt + beq @L1 ;(3*)opt + inc Errors ;(5^)opt +@L1: lda ACIA+RegData ;(4) ;data ;get byte and put into receive buffer + ldy RecvTail ;(4) + ldx RecvFreeCnt ;(4) + beq @L3 ;(2*) + sta RecvBuf,y ;(5) + inc RecvTail ;(6) + dec RecvFreeCnt ;(6) + cpx #33 ;(2) ;check for buffer space low + bcc @L2 ;(2*) + jmp NmiExit ;(3) + +; Assert flow control + +@L2: lda RtsOff ;(3) ;assert flow control if buffer space too low + sta ACIA+RegCommand ;(4) ;command + sta Stopped ;(3) + jmp NmiExit ;(3) + +; Drop this char + +@L3: inc DropCnt+0 ;not time-critical + bne @L4 + inc DropCnt+1 + bne @L4 + inc DropCnt+2 + bne @L4 + inc DropCnt+3 +@L4: jmp NmiExit + +@L9: +.if useC64 + pla +.endif + jmp NmiContinue + +;---------------------------------------------------------------------------- +; +; CheckInitialized - internal check if initialized +; Set carry and an error code if not initialized, clear carry and do not +; change any registers if initialized. +; + +CheckInitialized: + bit Initialized + bmi @L1 + lda #ErrNotInitialized + ldx #0 + sec + rts + +@L1: clc + rts + +;---------------------------------------------------------------------------- +; Try to send a byte. Internal routine. A = TryHard + +TryToSend: + sta tmp1 ; Remember tryHard flag +@L0: lda SendFreeCnt + cmp #$ff + beq @L3 ; Bail out + +; Check for flow stopped + +@L1: lda Stopped + bne @L3 ; Bail out + +;** check that swiftlink is ready to send + +@L2: lda ACIA+RegStatus + and #$10 + bne @L4 + bit tmp1 ;keep trying if must try hard + bmi @L0 +@L3: rts + +;** send byte and try again + +@L4: ldx SendHead + lda SendBuf,x + sta ACIA+RegData + inc SendHead + inc SendFreeCnt + jmp @L0 + + +;---------------------------------------------------------------------------- +; +; PollReceive - poll for rx char +; This function is useful in odd cases where the 6551 has a character in +; it but it fails to raise an NMI. It might be edge-triggering conditions? +; Actually, I'm not entirely sure that this condition can still arrise, but +; calling this function does no harm. +; + +PollReceive: + lda #$08 + and ACIA+RegStatus + beq @L9 + and ACIA+RegStatus + beq @L9 + lda ACIA+RegData + ldx RecvFreeCnt + beq @L9 + ldx RecvTail + sta RecvBuf,x + inc RecvTail + dec RecvFreeCnt +@L9: rts + +;---------------------------------------------------------------------------- +; +; DelayMs : delay for given number of milliseconds +; This implementation isn't very rigerous; it merely delays for the +; approximate number of clock cycles for the processor speed. +; Algorithm: +; repeat for number of milliseconds: +; repeat for number of MHz of cpu speed: +; delay for 1017 clock cycles +; + +DelayMs: ;( .A=milliseconds ) +@L1: ldy CpuSpeed +@L2: ldx #203 ;(2) +@L3: dex ;(2) + bne @L3 ;(3) // 1017 cycles + dey + bne @L2 + sec + sbc #1 + bne @L1 + rts + +.end + + + diff --git a/libsrc/c64/write.s b/libsrc/c64/write.s new file mode 100644 index 000000000..9a574f32c --- /dev/null +++ b/libsrc/c64/write.s @@ -0,0 +1,47 @@ +; +; Ullrich von Bassewitz, 30.05.1998 +; +; int write (int fd, const void* buf, int count); +; +; THIS IS A HACK! +; + + .export _write + .import popax + .importzp ptr1, ptr2, ptr3 + + .include "../cbm/cbm.inc" + +_write: jsr popax ; get count + sta ptr2 + stx ptr2+1 ; save it for later + sta ptr3 + stx ptr3+1 ; save for function result + jsr popax ; get buf + sta ptr1 + stx ptr1+1 + jsr popax ; get fd and discard it + +L1: lda ptr2 + ora ptr2+1 ; count zero? + beq L9 + ldy #0 + lda (ptr1),y + jsr BSOUT + inc ptr1 + bne L2 + inc ptr1+1 +L2: lda ptr2 + bne L3 + dec ptr2 + dec ptr2+1 + jmp L1 +L3: dec ptr2 + jmp L1 + +; No error, return count + +L9: lda ptr3 + ldx ptr3+1 + rts + diff --git a/libsrc/cbm/Makefile b/libsrc/cbm/Makefile new file mode 100644 index 000000000..9093a8b33 --- /dev/null +++ b/libsrc/cbm/Makefile @@ -0,0 +1,32 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $< + @$(AS) -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +C_OBJS = + +S_OBJS = ctype.o getenv.o gotoxy.o gotox.o gotoy.o where.o\ + clock.o chline.o cvline.o cclear.o revers.o\ + c_readst.o c_close.o c_open.o c_ckout.o c_clrch.o c_bsout.o\ + c_basin.o c_clall.o c_iobase.o c_setnam.o c_setlfs.o c_acptr.o\ + c_ciout.o c_untlk.o c_unlsn.o c_listen.o c_talk.o c_load.o\ + oserror.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f *~ + @rm -f $(C_OBJS:.c=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) + diff --git a/libsrc/cbm/c_acptr.s b/libsrc/cbm/c_acptr.s new file mode 100644 index 000000000..d77d6f1ce --- /dev/null +++ b/libsrc/cbm/c_acptr.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; unsigned char __fastcall__ cbm_acptr (void); +; + + .include "cbm.inc" + + .export _cbm_acptr + +_cbm_acptr: + jsr ACPTR + ldx #0 + rts + + diff --git a/libsrc/cbm/c_basin.s b/libsrc/cbm/c_basin.s new file mode 100644 index 000000000..281f433ef --- /dev/null +++ b/libsrc/cbm/c_basin.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; unsigned char __fastcall__ cbm_basin (void); +; + + .include "cbm.inc" + + .export _cbm_basin + +_cbm_basin: + jsr BASIN + ldx #0 + rts + + diff --git a/libsrc/cbm/c_bsout.s b/libsrc/cbm/c_bsout.s new file mode 100644 index 000000000..a2c682b1c --- /dev/null +++ b/libsrc/cbm/c_bsout.s @@ -0,0 +1,14 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_bsout (unsigned char C); +; + + .include "cbm.inc" + + .export _cbm_bsout + +_cbm_bsout = BSOUT + + + diff --git a/libsrc/cbm/c_ciout.s b/libsrc/cbm/c_ciout.s new file mode 100644 index 000000000..b2c149fb1 --- /dev/null +++ b/libsrc/cbm/c_ciout.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_ciout (unsigned char C); +; + + .include "cbm.inc" + + .export _cbm_ciout + +_cbm_ciout = CIOUT + + diff --git a/libsrc/cbm/c_ckout.s b/libsrc/cbm/c_ckout.s new file mode 100644 index 000000000..7fffe9f70 --- /dev/null +++ b/libsrc/cbm/c_ckout.s @@ -0,0 +1,24 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; unsigned __fastcall__ cbm_ckout (unsigned char FN); +; + + .include "cbm.inc" + + .export _cbm_ckout + +_cbm_ckout: + tax + jsr CKOUT + ldx #0 + bcc @Ok + inx + rts +@Ok: txa + rts + + + + + diff --git a/libsrc/cbm/c_clall.s b/libsrc/cbm/c_clall.s new file mode 100644 index 000000000..d7c5625ee --- /dev/null +++ b/libsrc/cbm/c_clall.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_clall (void); +; + + .include "cbm.inc" + + .export _cbm_clall + +_cbm_clall = CLALL + + diff --git a/libsrc/cbm/c_close.s b/libsrc/cbm/c_close.s new file mode 100644 index 000000000..72cbded77 --- /dev/null +++ b/libsrc/cbm/c_close.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_close (unsigned char FN); +; + + .include "cbm.inc" + + .export _cbm_close + +_cbm_close: + clc + jmp CLOSE + + diff --git a/libsrc/cbm/c_clrch.s b/libsrc/cbm/c_clrch.s new file mode 100644 index 000000000..ba76b588a --- /dev/null +++ b/libsrc/cbm/c_clrch.s @@ -0,0 +1,12 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_clrch (void); +; + + .include "cbm.inc" + + .export _cbm_clrch + +_cbm_clrch = CLRCH + diff --git a/libsrc/cbm/c_iobase.s b/libsrc/cbm/c_iobase.s new file mode 100644 index 000000000..58fb1598b --- /dev/null +++ b/libsrc/cbm/c_iobase.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; unsigned __fastcall__ cbm_iobase (void); +; + + .include "cbm.inc" + + .export _cbm_iobase + +_cbm_iobase: + jsr IOBASE + txa + pha + tya + tax + pla + rts + + diff --git a/libsrc/cbm/c_listen.s b/libsrc/cbm/c_listen.s new file mode 100644 index 000000000..a5cc4e0cb --- /dev/null +++ b/libsrc/cbm/c_listen.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_listen (unsigned char dev); +; + + .include "cbm.inc" + + .export _cbm_listen + +_cbm_listen = LISTEN + + + + + diff --git a/libsrc/cbm/c_load.s b/libsrc/cbm/c_load.s new file mode 100644 index 000000000..16aec28df --- /dev/null +++ b/libsrc/cbm/c_load.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; unsigned __fastcall__ cbm_load (unsigned char flag, unsigned addr); +; + + .include "cbm.inc" + + .export _cbm_load + .import popa + .importzp ptr1 + +_cbm_load: + sta ptr1 + stx ptr1+1 + jsr popa ; get flag + ldx ptr1 + ldy ptr1+1 + jsr LOAD + ldx #0 + bcc @Ok + inx + rts +@Ok: txa + rts + diff --git a/libsrc/cbm/c_open.s b/libsrc/cbm/c_open.s new file mode 100644 index 000000000..559f90771 --- /dev/null +++ b/libsrc/cbm/c_open.s @@ -0,0 +1,19 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; unsigned __fastcall__ cbm_open (void); +; + + .include "cbm.inc" + + .export _cbm_open + +_cbm_open: + jsr OPEN + ldx #0 + bcc @Ok + inx + rts +@Ok: txa + rts + diff --git a/libsrc/cbm/c_readst.s b/libsrc/cbm/c_readst.s new file mode 100644 index 000000000..bb1b42a3d --- /dev/null +++ b/libsrc/cbm/c_readst.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; unsigned __fastcall__ cbm_readst (void); +; + + .include "cbm.inc" + + .export _cbm_readst + +_cbm_readst: + jsr READST + ldx #0 + rts + diff --git a/libsrc/cbm/c_setlfs.s b/libsrc/cbm/c_setlfs.s new file mode 100644 index 000000000..a8bc6f841 --- /dev/null +++ b/libsrc/cbm/c_setlfs.s @@ -0,0 +1,23 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_setlfs (unsigned char LFN, +; unsigned char DEV, +; unsigned char SA); +; + + .include "cbm.inc" + + .export _cbm_setlfs + .import popa + .importzp tmp1 + +_cbm_setlfs: + sta tmp1 ; Save SA + jsr popa ; Get DEV + tax + jsr popa ; Get LFN + ldy tmp1 ; Get SA + jmp SETLFS + + diff --git a/libsrc/cbm/c_setnam.s b/libsrc/cbm/c_setnam.s new file mode 100644 index 000000000..ed597139c --- /dev/null +++ b/libsrc/cbm/c_setnam.s @@ -0,0 +1,24 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_setnam (const char* Name); +; + + .include "cbm.inc" + + .export _cbm_setnam + .importzp ptr1 + +_cbm_setnam: + sta ptr1 ; Store pointer to file name + stx ptr1+1 + ldy #$FF +@Loop: iny ; Get length of name + lda (ptr1),y + bne @Loop + + tya ; Length + ldx ptr1 + ldy ptr1+1 + jmp SETNAM + diff --git a/libsrc/cbm/c_talk.s b/libsrc/cbm/c_talk.s new file mode 100644 index 000000000..d9d709913 --- /dev/null +++ b/libsrc/cbm/c_talk.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_talk (unsigned char dev); +; + + .include "cbm.inc" + + .export _cbm_talk + +_cbm_talk = TALK + + + + + + + diff --git a/libsrc/cbm/c_unlsn.s b/libsrc/cbm/c_unlsn.s new file mode 100644 index 000000000..bcf4e0be0 --- /dev/null +++ b/libsrc/cbm/c_unlsn.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_unlsn (void); +; + + .include "cbm.inc" + + .export _cbm_unlsn + +_cbm_unlsn = UNLSN + + + + + diff --git a/libsrc/cbm/c_untlk.s b/libsrc/cbm/c_untlk.s new file mode 100644 index 000000000..4417188b3 --- /dev/null +++ b/libsrc/cbm/c_untlk.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 03.06.1999 +; +; void __fastcall__ cbm_untlk (void); +; + + .include "cbm.inc" + + .export _cbm_untlk + +_cbm_untlk = UNTLK + + diff --git a/libsrc/cbm/cbm.inc b/libsrc/cbm/cbm.inc new file mode 100644 index 000000000..26644c5ba --- /dev/null +++ b/libsrc/cbm/cbm.inc @@ -0,0 +1,46 @@ +; +; Subroutines available in the CBM jump table +; + + +CINT = $FF81 +IOINIT = $FF84 +RAMTAS = $FF87 +RESTOR = $FF8A +VECTOR = $FF8D +SETMSG = $FF90 +SECOND = $FF93 +TKSA = $FF96 +MEMTOP = $FF99 +MEMBOT = $FF9C +SCNKEY = $FF9F +SETTMO = $FFA2 +ACPTR = $FFA5 +CIOUT = $FFA8 +UNTLK = $FFAB +UNLSN = $FFAE +LISTEN = $FFB1 +TALK = $FFB4 +READST = $FFB7 +SETLFS = $FFBA +SETNAM = $FFBD +OPEN = $FFC0 +CLOSE = $FFC3 +CHKIN = $FFC6 +CKOUT = $FFC9 +CLRCH = $FFCC +BASIN = $FFCF +BSOUT = $FFD2 +LOAD = $FFD5 +SAVE = $FFD8 +SETTIM = $FFDB +RDTIM = $FFDE +STOP = $FFE1 +GETIN = $FFE4 +CLALL = $FFE7 +UDTIM = $FFEA +SCREEN = $FFED +PLOT = $FFF0 +IOBASE = $FFF3 + + diff --git a/libsrc/cbm/cclear.s b/libsrc/cbm/cclear.s new file mode 100644 index 000000000..586459cb0 --- /dev/null +++ b/libsrc/cbm/cclear.s @@ -0,0 +1,30 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void cclearxy (unsigned char x, unsigned char y, unsigned char length); +; void cclear (unsigned char length); +; + + .export _cclearxy, _cclear + .import popa, _gotoxy, cputdirect + .importzp tmp1 + +_cclearxy: + pha ; Save the length + jsr popa ; Get y + jsr _gotoxy ; Call this one, will pop params + pla ; Restore the length and run into _cclear + +_cclear: + cmp #0 ; Is the length zero? + beq L9 ; Jump if done + sta tmp1 +L1: lda #$20 ; Blank - screen code + jsr cputdirect ; Direct output + dec tmp1 + bne L1 +L9: rts + + + + diff --git a/libsrc/cbm/chline.s b/libsrc/cbm/chline.s new file mode 100644 index 000000000..26be1c7a1 --- /dev/null +++ b/libsrc/cbm/chline.s @@ -0,0 +1,30 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void chlinexy (unsigned char x, unsigned char y, unsigned char length); +; void chline (unsigned char length); +; + + .export _chlinexy, _chline + .import popa, _gotoxy, cputdirect + .importzp tmp1 + +_chlinexy: + pha ; Save the length + jsr popa ; Get y + jsr _gotoxy ; Call this one, will pop params + pla ; Restore the length + +_chline: + cmp #0 ; Is the length zero? + beq L9 ; Jump if done + sta tmp1 +L1: lda #64 ; Horizontal line, screen code + jsr cputdirect ; Direct output + dec tmp1 + bne L1 +L9: rts + + + + diff --git a/libsrc/cbm/clock.s b/libsrc/cbm/clock.s new file mode 100644 index 000000000..8df03dc56 --- /dev/null +++ b/libsrc/cbm/clock.s @@ -0,0 +1,22 @@ +; +; Ullrich von Bassewitz, 21.09.1998 +; +; clock_t clock (void); +; + + .export _clock + .importzp sreg + + .include "cbm.inc" + + +.proc _clock + + lda #0 ; Byte 3 is always zero + sta ::sreg+1 + jsr RDTIM + sty ::sreg + rts ; Don't set CC, this has no meaning here + +.endproc + diff --git a/libsrc/cbm/ctype.s b/libsrc/cbm/ctype.s new file mode 100644 index 000000000..e5f770300 --- /dev/null +++ b/libsrc/cbm/ctype.s @@ -0,0 +1,311 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; Character specification table. +; + +; The tables are readonly, put them into the code segment + +.code + +; Value that must be added to a lower case char to make it an upper case +; char (example: for ASCII, this must be $E0). + + + .export __cdiff + +__cdiff: + .byte $80 + + +; The following 256 byte wide table specifies attributes for the isxxx type +; of functions. Doing it by a table means some overhead in space, but it +; has major advantages: +; +; * It is fast. If it were'nt for the slow parameter passing of cc65, one +; could even define macros for the isxxx functions (this is usually +; done on other platforms). +; +; * It is highly portable. The only unportable part is the table itself, +; all real code goes into the common library. +; +; * We save some code in the isxxx functions. +; +; +; Bit assignments: +; +; 0 - Lower case char +; 1 - Upper case char +; 2 - Numeric digit +; 3 - Hex digit (both, lower and upper) +; 4 - Control character +; 5 - The space character itself +; 6 - Other whitespace (that is: '\f', '\n', '\r', '\t' and '\v') +; 7 - Space or tab character + + +; The table is taken from Craig S. Bruce technical docs for the ACE os + + .export __ctype + +__ctype: + .byte $10 ; 0/00 ___rvs_@___ + .byte $10 ; 1/01 ___rvs_a___ + .byte $10 ; 2/02 ___rvs_b___ + .byte $10 ; 3/03 ___rvs_c___ + .byte $10 ; 4/04 ___rvs_d___ + .byte $10 ; 5/05 ___rvs_e___ + .byte $10 ; 6/06 ___rvs_f___ + .byte $10 ; 7/07 _BEL/rvs_g_ + .byte $10 ; 8/08 ___rvs_h___ + .byte $D0 ; 9/09 _TAB/rvs_i_ + .byte $50 ; 10/0a _BOL/rvs_j_ + .byte $10 ; 11/0b ___rvs_k___ + .byte $10 ; 12/0c ___rvs_l___ + .byte $50 ; 13/0d _CR_/rvs_m_ + .byte $10 ; 14/0e ___rvs_n___ + .byte $10 ; 15/0f ___rvs_o___ + .byte $10 ; 16/10 ___rvs_p___ + .byte $50 ; 17/11 _VT_/rvs_q_ + .byte $10 ; 18/12 ___rvs_r___ + .byte $10 ; 19/13 ___rvs_s___ + .byte $50 ; 20/14 _BS_/rvs_t_ + .byte $10 ; 21/15 ___rvs_u___ + .byte $10 ; 22/16 ___rvs_v___ + .byte $10 ; 23/17 ___rvs_w___ + .byte $10 ; 24/18 ___rvs_x___ + .byte $10 ; 25/19 ___rvs_y___ + .byte $10 ; 26/1a ___rvs_z___ + .byte $10 ; 27/1b ___rvs_[___ + .byte $10 ; 28/1c ___rvs_\___ + .byte $10 ; 29/1d ___rvs_]___ + .byte $10 ; 30/1e ___rvs_^___ + .byte $10 ; 31/1f _rvs_under_ + .byte $A0 ; 32/20 ___SPACE___ + .byte $00 ; 33/21 _____!_____ + .byte $00 ; 34/22 _____"_____ + .byte $00 ; 35/23 _____#_____ + .byte $00 ; 36/24 _____$_____ + .byte $00 ; 37/25 _____%_____ + .byte $00 ; 38/26 _____&_____ + .byte $00 ; 39/27 _____'_____ + .byte $00 ; 40/28 _____(_____ + .byte $00 ; 41/29 _____)_____ + .byte $00 ; 42/2a _____*_____ + .byte $00 ; 43/2b _____+_____ + .byte $00 ; 44/2c _____,_____ + .byte $00 ; 45/2d _____-_____ + .byte $00 ; 46/2e _____._____ + .byte $00 ; 47/2f _____/_____ + .byte $0C ; 48/30 _____0_____ + .byte $0C ; 49/31 _____1_____ + .byte $0C ; 50/32 _____2_____ + .byte $0C ; 51/33 _____3_____ + .byte $0C ; 52/34 _____4_____ + .byte $0C ; 53/35 _____5_____ + .byte $0C ; 54/36 _____6_____ + .byte $0C ; 55/37 _____7_____ + .byte $0C ; 56/38 _____8_____ + .byte $0C ; 57/39 _____9_____ + .byte $00 ; 58/3a _____:_____ + .byte $00 ; 59/3b _____;_____ + .byte $00 ; 60/3c _____<_____ + .byte $00 ; 61/3d _____=_____ + .byte $00 ; 62/3e _____>_____ + .byte $00 ; 63/3f _____?_____ + + .byte $00 ; 64/40 _____@_____ + .byte $09 ; 65/41 _____a_____ + .byte $09 ; 66/42 _____b_____ + .byte $09 ; 67/43 _____c_____ + .byte $09 ; 68/44 _____d_____ + .byte $09 ; 69/45 _____e_____ + .byte $09 ; 70/46 _____f_____ + .byte $01 ; 71/47 _____g_____ + .byte $01 ; 72/48 _____h_____ + .byte $01 ; 73/49 _____i_____ + .byte $01 ; 74/4a _____j_____ + .byte $01 ; 75/4b _____k_____ + .byte $01 ; 76/4c _____l_____ + .byte $01 ; 77/4d _____m_____ + .byte $01 ; 78/4e _____n_____ + .byte $01 ; 79/4f _____o_____ + .byte $01 ; 80/50 _____p_____ + .byte $01 ; 81/51 _____q_____ + .byte $01 ; 82/52 _____r_____ + .byte $01 ; 83/53 _____s_____ + .byte $01 ; 84/54 _____t_____ + .byte $01 ; 85/55 _____u_____ + .byte $01 ; 86/56 _____v_____ + .byte $01 ; 87/57 _____w_____ + .byte $01 ; 88/58 _____x_____ + .byte $01 ; 89/59 _____y_____ + .byte $01 ; 90/5a _____z_____ + .byte $00 ; 91/5b _____[_____ + .byte $00 ; 92/5c _____\_____ + .byte $00 ; 93/5d _____]_____ + .byte $00 ; 94/5e _____^_____ + .byte $00 ; 95/5f _UNDERLINE_ + .byte $00 ; 96/60 _A`_grave__ + .byte $00 ; 97/61 _A'_acute__ + .byte $00 ; 98/62 _A^_circum_ + .byte $00 ; 99/63 _A~_tilde__ + .byte $00 ; 100/64 _A"_dieres_ + .byte $00 ; 101/65 _A__ring___ + .byte $00 ; 102/66 _AE________ + .byte $00 ; 103/67 _C,cedilla_ + .byte $00 ; 104/68 _E`_grave__ + .byte $00 ; 105/69 _E'_acute__ + .byte $00 ; 106/6a _E^_circum_ + .byte $00 ; 107/6b _E"_dieres_ + .byte $00 ; 108/6c _I`_grave__ + .byte $00 ; 109/6d _I'_acute__ + .byte $00 ; 110/6e _I^_circum_ + .byte $00 ; 111/6f _I"_dieres_ + .byte $00 ; 112/70 _D-_Eth_lr_ + .byte $00 ; 113/71 _N~_tilde__ + .byte $00 ; 114/72 _O`_grave__ + .byte $00 ; 115/73 _O'_acute__ + .byte $00 ; 116/74 _O^_circum_ + .byte $00 ; 117/75 _O~_tilde__ + .byte $00 ; 118/76 _O"_dieres_ + .byte $00 ; 119/77 __multiply_ + .byte $00 ; 120/78 _O/_slash__ + .byte $00 ; 121/79 _U`_grave__ + .byte $00 ; 122/7a _U'_acute__ + .byte $00 ; 123/7b _U^_circum_ + .byte $00 ; 124/7c _U"_dieres_ + .byte $00 ; 125/7d _Y'_acute__ + .byte $00 ; 126/7e _cap_thorn_ + .byte $00 ; 127/7f _Es-sed_B__ + + .byte $00 ; 128/80 __bullet___ + .byte $00 ; 129/81 __v_line___ + .byte $00 ; 130/82 __h_line___ + .byte $00 ; 131/83 ___cross___ + .byte $00 ; 132/84 _tl_corner_ + .byte $00 ; 133/85 _tr_corner_ + .byte $00 ; 134/86 _bl_corner_ + .byte $00 ; 135/87 _br_corner_ + .byte $00 ; 136/88 ___l_tee___ + .byte $00 ; 137/89 ___r_tee___ + .byte $00 ; 138/8a ___t_tee___ + .byte $00 ; 139/8b ___b_tee___ + .byte $00 ; 140/8c ___heart___ + .byte $00 ; 141/8d __diamond__ + .byte $00 ; 142/8e ___club____ + .byte $00 ; 143/8f ___spade___ + .byte $00 ; 144/90 _s_circle__ + .byte $00 ; 145/91 __circle___ + .byte $00 ; 146/92 ___pound___ + .byte $10 ; 147/93 _CLS/check_ + .byte $00 ; 148/94 ____pi_____ + .byte $00 ; 149/95 ____+/-____ + .byte $00 ; 150/96 __divide___ + .byte $00 ; 151/97 __degree___ + .byte $00 ; 152/98 _c_checker_ + .byte $00 ; 153/99 _f_checker_ + .byte $00 ; 154/9a _solid_sq__ + .byte $00 ; 155/9b __cr_char__ + .byte $00 ; 156/9c _up_arrow__ + .byte $00 ; 157/9d _down_arro_ + .byte $00 ; 158/9e _left_arro_ + .byte $00 ; 159/9f _right_arr_ + .byte $00 ; 160/a0 _req space_ + .byte $00 ; 161/a1 _!_invertd_ + .byte $00 ; 162/a2 ___cent____ + .byte $00 ; 163/a3 ___pound___ + .byte $00 ; 164/a4 __currency_ + .byte $00 ; 165/a5 ____yen____ + .byte $00 ; 166/a6 _|_broken__ + .byte $00 ; 167/a7 __section__ + .byte $00 ; 168/a8 __umulaut__ + .byte $00 ; 169/a9 _copyright_ + .byte $00 ; 170/aa __fem_ord__ + .byte $00 ; 171/ab _l_ang_quo_ + .byte $00 ; 172/ac ____not____ + .byte $00 ; 173/ad _syl_hyphn_ + .byte $00 ; 174/ae _registerd_ + .byte $00 ; 175/af _overline__ + .byte $00 ; 176/b0 __degrees__ + .byte $00 ; 177/b1 ____+/-____ + .byte $00 ; 178/b2 _2_supersc_ + .byte $00 ; 179/b3 _3_supersc_ + .byte $00 ; 180/b4 ___acute___ + .byte $00 ; 181/b5 ____mu_____ + .byte $00 ; 182/b6 _paragraph_ + .byte $00 ; 183/b7 __mid_dot__ + .byte $00 ; 184/b8 __cedilla__ + .byte $00 ; 185/b9 _1_supersc_ + .byte $00 ; 186/ba __mas_ord__ + .byte $00 ; 187/bb _r_ang_quo_ + .byte $00 ; 188/bc ____1/4____ + .byte $00 ; 189/bd ____1/2____ + .byte $00 ; 190/be ____3/4____ + .byte $00 ; 191/bf _?_invertd_ + + .byte $00 ; 192/c0 _____`_____ + .byte $0A ; 193/c1 _____A_____ + .byte $0A ; 194/c2 _____B_____ + .byte $0A ; 195/c3 _____C_____ + .byte $0A ; 196/c4 _____D_____ + .byte $0A ; 197/c5 _____E_____ + .byte $0A ; 198/c6 _____F_____ + .byte $02 ; 199/c7 _____G_____ + .byte $02 ; 200/c8 _____H_____ + .byte $02 ; 201/c9 _____I_____ + .byte $02 ; 202/ca _____J_____ + .byte $02 ; 203/cb _____K_____ + .byte $02 ; 204/cc _____L_____ + .byte $02 ; 205/cd _____M_____ + .byte $02 ; 206/ce _____N_____ + .byte $02 ; 207/cf _____O_____ + .byte $02 ; 208/d0 _____P_____ + .byte $02 ; 209/d1 _____Q_____ + .byte $02 ; 210/d2 _____R_____ + .byte $02 ; 211/d3 _____S_____ + .byte $02 ; 212/d4 _____T_____ + .byte $02 ; 213/d5 _____U_____ + .byte $02 ; 214/d6 _____V_____ + .byte $02 ; 215/d7 _____W_____ + .byte $02 ; 216/d8 _____X_____ + .byte $02 ; 217/d9 _____Y_____ + .byte $02 ; 218/da _____Z_____ + .byte $00 ; 219/db _____{_____ + .byte $00 ; 220/dc _____|_____ + .byte $00 ; 221/dd _____}_____ + .byte $00 ; 222/de _____~_____ + .byte $00 ; 223/df ___HOUSE___ + .byte $00 ; 224/e0 _a`_grave__ + .byte $00 ; 225/e1 _a'_acute__ + .byte $00 ; 226/e2 _a^_circum_ + .byte $00 ; 227/e3 _a~_tilde__ + .byte $00 ; 228/e4 _a"_dieres_ + .byte $00 ; 229/e5 _a__ring___ + .byte $00 ; 230/e6 _ae________ + .byte $00 ; 231/e7 _c,cedilla_ + .byte $00 ; 232/e8 _e`_grave__ + .byte $00 ; 233/e9 _e'_acute__ + .byte $00 ; 234/ea _e^_circum_ + .byte $00 ; 235/eb _e"_dieres_ + .byte $00 ; 236/ec _i`_grave__ + .byte $00 ; 237/ed _i'_acute__ + .byte $00 ; 238/ee _i^_circum_ + .byte $00 ; 239/ef _i"_dieres_ + .byte $00 ; 240/f0 _o^x_Eth_s_ + .byte $00 ; 241/f1 _n~_tilda__ + .byte $00 ; 242/f2 _o`_grave__ + .byte $00 ; 243/f3 _o'_acute__ + .byte $00 ; 244/f4 _o^_circum_ + .byte $00 ; 245/f5 _o~_tilde__ + .byte $00 ; 246/f6 _o"_dieres_ + .byte $00 ; 247/f7 __divide___ + .byte $00 ; 248/f8 _o/_slash__ + .byte $00 ; 249/f9 _u`_grave__ + .byte $00 ; 250/fa _u'_acute__ + .byte $00 ; 251/fb _u^_circum_ + .byte $00 ; 252/fc _u"_dieres_ + .byte $00 ; 253/fd _y'_acute__ + .byte $00 ; 254/fe _sm_thorn__ + .byte $00 ; 255/ff _y"_dieres_ + diff --git a/libsrc/cbm/cvline.s b/libsrc/cbm/cvline.s new file mode 100644 index 000000000..151a4925a --- /dev/null +++ b/libsrc/cbm/cvline.s @@ -0,0 +1,30 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void cvlinexy (unsigned char x, unsigned char y, unsigned char length); +; void cvline (unsigned char length); +; + + .export _cvlinexy, _cvline + .import popa, _gotoxy, putchar, newline + .importzp tmp1 + +_cvlinexy: + pha ; Save the length + jsr popa ; Get y + jsr _gotoxy ; Call this one, will pop params + pla ; Restore the length and run into _cvline + +_cvline: + cmp #0 ; Is the length zero? + beq L9 ; Jump if done + sta tmp1 +L1: lda #93 ; Vertical bar + jsr putchar ; Write, no cursor advance + jsr newline ; Advance cursor to next line + dec tmp1 + bne L1 +L9: rts + + + diff --git a/libsrc/cbm/getenv.s b/libsrc/cbm/getenv.s new file mode 100644 index 000000000..ea527dd13 --- /dev/null +++ b/libsrc/cbm/getenv.s @@ -0,0 +1,11 @@ +; +; Ullrich von Bassewitz, 17.06.1998 +; +; char* getenv (const char* name); +; + + .export _getenv + .import return0 + +_getenv = return0 ; "not found" + diff --git a/libsrc/cbm/gotox.s b/libsrc/cbm/gotox.s new file mode 100644 index 000000000..25de6c3f4 --- /dev/null +++ b/libsrc/cbm/gotox.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; void gotox (unsigned char x); +; + + .export _gotox + .import plot + .importzp CURS_X + +_gotox: sta CURS_X ; Set new position + jmp plot ; And activate it + + + diff --git a/libsrc/cbm/gotoxy.s b/libsrc/cbm/gotoxy.s new file mode 100644 index 000000000..cce95aee5 --- /dev/null +++ b/libsrc/cbm/gotoxy.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void gotoxy (unsigned char x, unsigned char y); +; + + .export _gotoxy + .import popa, plot + .importzp CURS_X, CURS_Y + +_gotoxy: + sta CURS_Y ; Set Y + jsr popa ; Get X + sta CURS_X ; Set X + jmp plot ; Set the cursor position + + diff --git a/libsrc/cbm/gotoy.s b/libsrc/cbm/gotoy.s new file mode 100644 index 000000000..7779b54f9 --- /dev/null +++ b/libsrc/cbm/gotoy.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 0.08.1998 +; +; void gotoy (unsigned char y); +; + + .export _gotoy + .import plot + .importzp CURS_Y + +_gotoy: sta CURS_Y ; Set the new position + jmp plot ; And activate it + diff --git a/libsrc/cbm/oserror.s b/libsrc/cbm/oserror.s new file mode 100644 index 000000000..0a7ed6e27 --- /dev/null +++ b/libsrc/cbm/oserror.s @@ -0,0 +1,77 @@ +; +; Ullrich von Bassewitz, 17.05.2000 +; +; int __fastcall__ _osmaperrno (unsigned char oserror); +; /* Map a system specific error into a system independent code */ +; + + .export __osmaperrno + .include "../common/errno.inc" + +.code + +__osmaperrno: + ldx #ErrTabSize +@L1: cmp ErrTab-2,x ; Search for the error code + beq @L2 ; Jump if found + dex + dex + bne @L1 ; Next entry + +; Code not found, return EINVAL + + lda #EINVAL + rts + +; Found the code + +@L2: lda ErrTab-1,x + ldx #$00 ; High byte always zero + rts + +.rodata + +ErrTab: + .byte 1, EMFILE ; Too many open files + .byte 2, EINVAL ; File is open + .byte 3, EINVAL ; File not open + .byte 4, ENOENT ; File not found + .byte 5, ENODEV ; Device not present + .byte 6, EINVAL ; File not input + .byte 7, EINVAL ; File not output + .byte 8, EINVAL ; Filename missing + .byte 9, ENODEV ; Ilegal device +; .byte 20, ; Read error +; .byte 21, ; Read error +; .byte 22, ; Read error +; .byte 23, ; Read error +; .byte 24, ; Read error +; .byte 25, ; Write error + .byte 26, EACCES ; Write protect on +; .byte 27, ; Read error +; .byte 28, ; Write error +; .byte 29, ; Disk ID mismatch +; .byte 30, ; Syntax error +; .byte 31, ; Syntax error +; .byte 32, ; Syntax error + .byte 33, EINVAL ; Syntax error (invalid file name) + .byte 34, EINVAL ; Syntax error (no file given) +; .byte 39, ; Syntax error +; .byte 50, ; Record not present +; .byte 51, ; Overflow in record +; .byte 52, ; File too large + .byte 60, EINVAL ; Write file open + .byte 61, EINVAL ; File not open + .byte 62, ENOENT ; File not found + .byte 63, EEXIST ; File exists + .byte 64, EINVAL ; File type mismatch +; .byte 65, ; No block +; .byte 66, ; Illegal track or sector +; .byte 67, ; Illegal system track or sector + .byte 70, EBUSY ; No channel +; .byte 71, ; Directory error +; .byte 72, ; Disk full +; .byte 73, ; DOS version mismatch + +ErrTabSize = (* - ErrTab) diff --git a/libsrc/cbm/revers.s b/libsrc/cbm/revers.s new file mode 100644 index 000000000..64a0029ea --- /dev/null +++ b/libsrc/cbm/revers.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; unsigned char revers (unsigned char onoff); +; + + .export _revers + .export revers + +_revers: + ldx #$00 ; Assume revers off + tay ; Test onoff + beq L1 ; Jump if off + ldx #$80 ; Load on value +L1: ldy #$00 ; Assume old value is zero + lda revers ; Load old value + stx revers ; Set new value + beq L2 ; Jump if old value zero + iny ; Make old value = 1 +L2: ldx #$00 ; Load high byte of result + tya ; Load low byte, set CC + rts + +.bss + +revers: .res 1 diff --git a/libsrc/cbm/where.s b/libsrc/cbm/where.s new file mode 100644 index 000000000..d9c0b61ed --- /dev/null +++ b/libsrc/cbm/where.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char wherex (void); +; unsigned char wherey (void); + + .export _wherex, _wherey + + .include "cbm.inc" + +_wherex: + sec + jsr PLOT ; Get cursor position + tya + rts + +_wherey: + sec + jsr PLOT ; Get cursor position + txa + rts + + + + + diff --git a/libsrc/cbm610/Makefile b/libsrc/cbm610/Makefile new file mode 100644 index 000000000..cdc5f65a6 --- /dev/null +++ b/libsrc/cbm610/Makefile @@ -0,0 +1,29 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $< + @$(AS) -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +C_OBJS = + +S_OBJS = crt0.o kbhit.o conio.o clrscr.o cputc.o cgetc.o\ + color.o break.o banking.o crtc.o pokesys.o\ + kscnkey.o kplot.o kudtim.o kirq.o rs232.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f $(C_OBJS:.c=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) + @rm -f crt0.o + diff --git a/libsrc/cbm610/banking.s b/libsrc/cbm610/banking.s new file mode 100644 index 000000000..57c47547c --- /dev/null +++ b/libsrc/cbm610/banking.s @@ -0,0 +1,41 @@ +; +; Ullrich von Bassewitz, 28.09.1998 +; +; Banking routines for the 610. +; + + .export set_bank, sys_bank, restore_bank + .importzp ptr1 + + .include "zeropage.inc" + +.code + +.proc sys_bank + pha + lda IndReg + sta IndSegSave + lda #$0F + sta IndReg + pla + rts +.endproc + +.proc set_bank + pha + lda IndReg + sta IndSegSave + pla + sta IndReg + rts +.endproc + +.proc restore_bank + pha + lda IndSegSave + sta IndReg + pla + rts +.endproc + + diff --git a/libsrc/cbm610/break.s b/libsrc/cbm610/break.s new file mode 100644 index 000000000..66492be71 --- /dev/null +++ b/libsrc/cbm610/break.s @@ -0,0 +1,117 @@ +; +; Ullrich von Bassewitz, 27.09.1998 +; +; void set_brk (unsigned Addr); +; void reset_brk (void); +; + + .export _set_brk, _reset_brk + .export _brk_a, _brk_x, _brk_y, _brk_sr, _brk_pc + .import _atexit + + .include "zeropage.inc" + .include "page3.inc" + + +.bss +_brk_a: .res 1 +_brk_x: .res 1 +_brk_y: .res 1 +_brk_sr: .res 1 +_brk_pc: .res 2 +_brk_01: .res 1 + +oldvec: .res 2 ; Old vector + + +.data +uservec: jmp $FFFF ; Patched at runtime + + +.code + +; Set the break vector +.proc _set_brk + + sta uservec+1 + stx uservec+2 ; Set the user vector + + lda oldvec + ora oldvec+1 ; Did we save the vector already? + bne L1 ; Jump if we installed the handler already + + lda BRKVec + sta oldvec + lda BRKVec+1 + sta oldvec+1 ; Save the old vector + + lda #<_reset_brk + ldx #>_reset_brk + jsr _atexit ; Install an exit handler + +L1: lda #brk_handler + sta BRKVec+1 + rts + +.endproc + + +; Reset the break vector +.proc _reset_brk + + lda oldvec + sta BRKVec + lda oldvec+1 + sta BRKVec+1 + rts + +.endproc + + + +; Break handler, called if a break occurs + +.proc brk_handler + + pla + sta _brk_y + pla + sta _brk_x + pla + sta _brk_a + pla + and #$EF ; Clear break bit + sta _brk_sr + pla ; PC low + sec + sbc #2 ; Point to start of brk + sta _brk_pc + pla ; PC high + sbc #0 + sta _brk_pc+1 + lda IndReg + sta _brk_01 + lda ExecReg + sta IndReg + + jsr uservec ; Call the user's routine + + lda _brk_01 + sta IndReg + + lda _brk_pc+1 + pha + lda _brk_pc + pha + lda _brk_sr + pha + ldx _brk_x + ldy _brk_y + lda _brk_a + rti ; Jump back... + +.endproc + + diff --git a/libsrc/cbm610/cbm610.inc b/libsrc/cbm610/cbm610.inc new file mode 100644 index 000000000..a7430ab95 --- /dev/null +++ b/libsrc/cbm610/cbm610.inc @@ -0,0 +1,17 @@ +; +; CBM610 generic definitions. +; + + +; --------------------------------------------------------------------------- +; Vector and other locations + +FUNKEY_VEC = $03B5 + +; --------------------------------------------------------------------------- +; I/O + +CRTC = $D800 +CRTC_ADDR = $00 +CRTC_DATA = $01 + diff --git a/libsrc/cbm610/cgetc.s b/libsrc/cbm610/cgetc.s new file mode 100644 index 000000000..c801c3ab8 --- /dev/null +++ b/libsrc/cbm610/cgetc.s @@ -0,0 +1,49 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; char cgetc (void); +; + + .export _cgetc + .import plot, write_crtc + .import cursor + + .include "zeropage.inc" + .include "page3.inc" + + +_cgetc: lda KeyIndex ; Get number of characters + bne L2 ; Jump if there are already chars waiting + +; Switch on the cursor if needed + + lda cursor + beq L1 ; Jump if no cursor + + jsr plot ; Set the current cursor position + ldy #10 + lda Config ; Cursor format + jsr write_crtc ; Set the cursor formar + +L1: lda KeyIndex + beq L1 + + ldy #10 + lda #$20 ; Cursor off + jsr write_crtc + +L2: ldx #$00 ; Get index + ldy KeyBuf ; Get first character in the buffer + sei +L3: lda KeyBuf+1,x ; Move up the remaining chars + sta KeyBuf,x + inx + cpx KeyIndex + bne L3 + dec KeyIndex + cli + + ldx #$00 ; High byte + tya ; First char from buffer + rts + diff --git a/libsrc/cbm610/clrscr.s b/libsrc/cbm610/clrscr.s new file mode 100644 index 000000000..22ca5e9b9 --- /dev/null +++ b/libsrc/cbm610/clrscr.s @@ -0,0 +1,42 @@ +; +; Ullrich von Bassewitz, 22.09.1998 +; +; void clrscr (void); +; + + .export _clrscr + .import plot + + .include "zeropage.inc" + +.proc _clrscr + + lda #0 + sta CURS_X + sta CURS_Y + jsr plot ; Set cursor to top left corner + + lda IndReg + pha + lda #$0F + sta IndReg ; Switch to the system bank + + ldx #8 + ldy #$00 + lda #$20 ; Screencode for blank +L1: sta (CharPtr),y + iny + bne L1 + inc CharPtr+1 + dex + bne L1 + + pla + sta IndReg ; Restore old indirect segment + + jmp plot ; Set screen pointer again + +.endproc + + + diff --git a/libsrc/cbm610/color.s b/libsrc/cbm610/color.s new file mode 100644 index 000000000..f36fdf11e --- /dev/null +++ b/libsrc/cbm610/color.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char __fastcall__ textcolor (unsigned char color); +; unsigned char __fastcall__ bgcolor (unsigned char color); +; unsigned char __fastcall__ bordercolor (unsigned char color); +; + + .export _textcolor, _bgcolor, _bordercolor + .import return0, return1 + + .include "cbm610.inc" + +_textcolor = return1 + +_bgcolor = return0 + +_bordercolor = return0 + + diff --git a/libsrc/cbm610/conio.s b/libsrc/cbm610/conio.s new file mode 100644 index 000000000..c1c58ef38 --- /dev/null +++ b/libsrc/cbm610/conio.s @@ -0,0 +1,19 @@ +; +; Ullrich von Bassewitz, 22.09.1998 +; +; Low level stuff for screen output/console input +; + + .export initconio + .import xsize, ysize + + .include "cbm610.inc" + +initconio: + lda #80 + sta xsize + lda #25 + sta ysize + rts + + diff --git a/libsrc/cbm610/cputc.s b/libsrc/cbm610/cputc.s new file mode 100644 index 000000000..4e8d94233 --- /dev/null +++ b/libsrc/cbm610/cputc.s @@ -0,0 +1,102 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void cputcxy (unsigned char x, unsigned char y, char c); +; void cputc (char c); +; + + .export _cputcxy, _cputc, cputdirect, putchar + .export advance, newline, plot + .exportzp CURS_X, CURS_Y + .import _gotoxy + .import popa + .import xsize, revers + + .include "cbm610.inc" + .include "zeropage.inc" + .include "../cbm/cbm.inc" + +_cputcxy: + pha ; Save C + jsr popa ; Get Y + jsr _gotoxy ; Set cursor, drop x + pla ; Restore C + +; Plot a character - also used as internal function + +_cputc: cmp #$0D ; CR? + bne L1 + lda #0 + sta CURS_X + beq plot ; Recalculate pointers + +L1: cmp #$0A ; LF? + bne L2 + ldy CURS_Y + iny + bne newline ; Recalculate pointers + +; Printable char of some sort + +L2: cmp #' ' + bcc cputdirect ; Other control char + tay + bmi L10 + cmp #$60 + bcc L3 + and #$DF + bne cputdirect ; Branch always +L3: and #$3F + +cputdirect: + jsr putchar ; Write the character to the screen + +; Advance cursor position + +advance: + iny + cpy xsize + bne L9 + ldy #0 ; new line +newline: + clc + lda xsize + adc CharPtr + sta CharPtr + bcc L4 + inc CharPtr+1 +L4: inc CURS_Y +L9: sty CURS_X + rts + +; Handle character if high bit set + +L10: and #$7F + cmp #$7E ; PI? + bne L11 + lda #$5E ; Load screen code for PI + bne cputdirect +L11: ora #$40 + bne cputdirect ; Branch always + +; Set cursor position, calculate RAM pointers + +plot: ldy CURS_X + ldx CURS_Y + clc + jmp PLOT + +; Write one character to the screen without doing anything else, return X +; position in Y + +putchar: + ldx IndReg + ldy #$0F + sty IndReg + ora revers ; Set revers bit + ldy CURS_X + sta (CharPtr),y ; Set char + stx IndReg + rts + + diff --git a/libsrc/cbm610/crt0.s b/libsrc/cbm610/crt0.s new file mode 100644 index 000000000..3ab5257e0 --- /dev/null +++ b/libsrc/cbm610/crt0.s @@ -0,0 +1,411 @@ +; +; Startup code for cc65 (Plus/4 version) +; +; This must be the *first* file on the linker command line +; + + .export _exit + .import __hinit, push0, doatexit, _main + .import initconio + .import __BSS_RUN__, __BSS_SIZE__ + .import irq, nmi + .import k_irq, k_nmi, k_plot, k_udtim, k_scnkey + + .include "zeropage.inc" + .include "io.inc" + + +; ------------------------------------------------------------------------ +; Define and export the ZP variables for the CBM610 runtime + + .exportzp sp, sreg, regsave + .exportzp ptr1, ptr2, ptr3, ptr4 + .exportzp tmp1, tmp2, tmp3, tmp4 + .exportzp regbank, zpspace + .exportzp crtc, sid, IPCcia, cia, acia, tpi1, tpi2 + .exportzp ktab1, ktab2, ktab3, ktab4, time, RecvBuf, SendBuf + +sp = $02 ; stack pointer +sreg = $04 ; secondary register/high 16 bit for longs +regsave = $06 ; slot to save/restore (E)AX into +ptr1 = $0A ; +ptr2 = $0C +ptr3 = $0E +ptr4 = $10 +tmp1 = $12 +tmp2 = $13 +tmp3 = $14 +tmp4 = $15 +regbank = $16 ; 6 byte register bank +zpspace = $1A ; Zero page space allocated + +; ------------------------------------------------------------------------ +; BASIC header and a small BASIC program. Since it is not possible to start +; programs in other banks using SYS, the BASIC program will write a small +; machine code program into memory at $100 and start that machine code +; program. The machine code program will then start the machine language +; code in bank 1, which will initialize the system by copying stuff from +; the system bank, and start the application. +; +; Here's the basic program that's in the following lines: +; +; 10 for i=0 to 4 +; 20 read j +; 30 poke 256+i,j +; 40 next i +; 50 sys 256 +; 60 data 120,169,1,133,0 +; +; The machine program in the data lines is: +; +; sei +; lda #$01 +; sta $00 <-- Switch to bank 1 after this command +; +; Initialization is not only complex because of the jumping from one bank +; into another. but also because we want to save memory, and because of +; this, we will use the system memory ($00-$3FF) for initialization stuff +; that is overwritten later. +; + +; To make things more simple, make the code of this module absolute. + + .org $0001 +Head: .byte $03,$00,$11,$00,$0a,$00,$81,$20,$49,$b2,$30,$20,$a4,$20,$34,$00 + .byte $19,$00,$14,$00,$87,$20,$4a,$00,$27,$00,$1e,$00,$97,$20,$32,$35 + .byte $36,$aa,$49,$2c,$4a,$00,$2f,$00,$28,$00,$82,$20,$49,$00,$39,$00 + .byte $32,$00,$9e,$20,$32,$35,$36,$00,$4f,$00,$3c,$00,$83,$20,$31,$32 + .byte $30,$2c,$31,$36,$39,$2c,$31,$2c,$31,$33,$33,$2c,$30,$00,$00,$00 + +; Since we need some vectors to access stuff in the system bank for our own, +; we will include them here, starting from $60: + + .res $60-* + +crtc: .word $d800 +sid: .word $da00 +IPCcia: .word $db00 +cia: .word $dc00 +acia: .word $dd00 +tpi1: .word $de00 +tpi2: .word $df00 +ktab1: .word $ea29 +ktab2: .word $ea89 +ktab3: .word $eae9 +ktab4: .word $eb49 +time: .dword $0000 +RecvBuf: .word $0100 ; RS232 received buffer +SendBuf: .word $0200 ; RS232 send buffer + + +; The code in the target bank when switching back will be put at the bottom +; of the stack. We will jump here to switch segments. The range $F2..$FF is +; not used by any kernal routine. + + .res $F8-* +Back: ldx spsave + txs + lda IndReg + sta ExecReg + +; The following code is a copy of the code that is poked in the system bank +; memory by the basic header program, it's only for documentation and not +; actually used here: + + sei + lda #$01 + sta ExecReg + +; This is the actual starting point of our code after switching banks for +; startup. Beware: The following code will get overwritten as soon as we +; use the stack (since it's in page 1)! + + tsx + stx spsave ; Save the system stackpointer + ldx #$FF + txs ; Set up our own stack + +; Set the interrupt, NMI and other vectors + + ldy #vectable_size +L0: lda vectable-1,y + sta $FF80,y + dey + bne L0 + +; Switch the indirect segment to the system bank + + lda #$0F + sta IndReg + +; Copy the kernal zero page ($90-$F2) from the system bank + + lda #$90 + sta ptr1 + lda #$00 + sta ptr1+1 + ldy #$62-1 +L1: lda (ptr1),y + sta $90,y + dey + bpl L1 + +; Copy the page 3 vectors in place + + ldy #$00 +L2: lda p3vectable,y + sta $300,y + iny + cpy #p3vectable_size + bne L2 + +; Copy the rest of page 3 from the system bank + + lda #$00 + sta ptr1 + lda #$03 + sta ptr1+1 +L3: lda (ptr1),y + sta $300,y + iny + bne L3 + +; Set the indirect segment to bank we're executing in + + lda ExecReg + sta IndReg + +; Zero the BSS segment. We will do that here instead calling the routine +; in the common library, since we have the memory anyway, and this way, +; it's reused later. + + lda #<__BSS_RUN__ + sta ptr1 + lda #>__BSS_RUN__ + sta ptr1+1 + lda #0 + tay + +; Clear full pages + + ldx #>__BSS_SIZE__ + beq Z2 +Z1: sta (ptr1),y + iny + bne Z1 + inc ptr1+1 ; Next page + dex + bne Z1 + +; Clear the remaining page + +Z2: ldx #<__BSS_SIZE__ + beq Z4 +Z3: sta (ptr1),y + iny + dex + bne Z3 +Z4: + +; Setup the C stack + + lda #<$FF81 + sta sp + lda #>$FF81 + sta sp+1 + +; We expect to be in page 2 now + +.if (* < $1FD) + jmp $200 + .res $200-* +.endif +.if (* < $200) + .res $200-*,$EA +.endif +.if (* >= $2F0) +.error "Code range invalid" +.endif + +; This code is in page 2, so we may now start calling subroutines safely, +; since the code we execute is no longer in the stack page. + + jsr __hinit ; Initialize the heap + jsr initconio ; Initialize conio stuff + +; Create the (empty) command line for the program + + jsr push0 ; argc + jsr push0 ; argv + +; Execute the program code + + jmp Start + +; ------------------------------------------------------------------------ +; Additional data that we need for initialization and that's overwritten +; later + +vectable: + jmp $0000 ; CINT + jmp $0000 ; IOINIT + jmp $0000 ; RAMTAS + jmp $0000 ; RESTOR + jmp $0000 ; VECTOR + jmp $0000 ; SETMSG + jmp $0000 ; SECOND + jmp $0000 ; TKSA + jmp $0000 ; MEMTOP + jmp $0000 ; MEMBOT + jmp k_scnkey ; SCNKEY + jmp $0000 ; SETTMO + jmp $0000 ; ACPTR + jmp $0000 ; CIOUT + jmp $0000 ; UNTLK + jmp $0000 ; UNLSN + jmp $0000 ; LISTEN + jmp $0000 ; TALK + jmp $0000 ; READST + jmp k_setlfs ; SETLFS + jmp k_setnam ; SETNAM + jmp $0000 ; OPEN + jmp $0000 ; CLOSE + jmp $0000 ; CHKIN + jmp $0000 ; CKOUT + jmp $0000 ; CLRCH + jmp $0000 ; BASIN + jmp $0000 ; BSOUT + jmp $0000 ; LOAD + jmp $0000 ; SAVE + jmp k_settim ; SETTIM + jmp k_rdtim ; RDTIM + jmp $0000 ; STOP + jmp $0000 ; GETIN + jmp $0000 ; CLALL + jmp k_udtim ; UDTIM + jmp k_screen ; SCREEN + jmp k_plot ; PLOT + jmp k_iobase ; IOBASE + sta ExecReg + rts + .byte $01 ; Filler + .word nmi + .word 0 ; Reset - not used + .word irq +vectable_size = * - vectable + +p3vectable: + .word k_irq ; IRQ user vector + .word k_brk ; BRK user vector + .word k_nmi ; NMI user vector +p3vectable_size = * - p3vectable + + +; ------------------------------------------------------------------------ +; This is the program code after setup. It starts at $400 + + .res $400-* + +Start: + +; Enable interrupts + + cli + +; Call the user code + + ldy #4 ; Argument size + jsr _main ; call the users code + +; Fall thru to exit. + +_exit: jsr doatexit ; call exit functions + +; Clear the start of the zero page, since it will be interpreted as a +; (garbage) BASIC program otherwise. This is also the default entry for +; the break vector. + +k_brk: sei + lda #$00 + ldx #$3E +Clear: sta $02,x + dex + bne Clear + +; Setup the welcome code at the stack bottom in the system bank. Use +; the F4/F5 vector to access the system bank + + lda #$0F + sta IndReg + ldy #$00 + sty $F4 + iny + sty $F5 + ldy #reset_size-1 +@L1: lda reset,y + sta ($F4),y + dey + bne @L1 + jmp Back + +; ------------------------------------------------------------------------ +; Code that is copied into the system bank at $100 when switching back + +reset: cli + jmp $8000 ; BASIC cold start +reset_size = * - reset + +; ------------------------------------------------------------------------ +; Code for a few simpler kernal calls goes here + +k_iobase: + ldx cia + ldy cia+1 + rts + +k_screen: + ldx #80 ; Columns + ldy #25 ; Lines + rts + +k_setlfs: + sta LogicalAdr + stx FirstAdr + sty SecondAdr + rts + +k_setnam: + sta FileNameLen + lda $00,x + sta FileNameAdrLo + lda $01,x + sta FileNameAdrHi + lda $02,x + sta FileNameAdrSeg + rts + +k_rdtim: + sei + lda time+0 + ldx time+1 + ldy time+2 + cli + rts + +k_settim: + sei + sta time+0 + stx time+1 + sty time+2 + cli + rts + +; ------------------------------------------------------------------------- +; Data area - switch back to relocatable mode + + .reloc + +.data +spsave: .res 1 + + diff --git a/libsrc/cbm610/crtc.s b/libsrc/cbm610/crtc.s new file mode 100644 index 000000000..18670b9df --- /dev/null +++ b/libsrc/cbm610/crtc.s @@ -0,0 +1,58 @@ +; +; Ullrich von Bassewitz, 28.09.1998 +; +; Write to the CRTC. +; + + .export write_crtc, read_crtc + .importzp crtc + + .include "cbm610.inc" + .include "zeropage.inc" + + +; Write a value to the CRTC. The index is in Y, the value in A + +.proc write_crtc + sta sedt1 + lda IndReg + pha + lda #$0F + sta IndReg + tya + ldy #$00 + sei + sta (crtc),y + iny + lda sedt1 + sta (crtc),y + cli + pla + sta IndReg + lda sedt1 + rts +.endproc + + +.proc read_crtc + sty sedt1 + lda IndReg + pha + lda #$0F + sta IndReg + lda sedt1 + ldy #$00 + sei + sta (crtc),y + iny + lda (crtc),y + cli + tay + pla + sta IndReg + tya + ldy sedt1 + rts +.endproc + + diff --git a/libsrc/cbm610/io.inc b/libsrc/cbm610/io.inc new file mode 100644 index 000000000..4d7d29bf4 --- /dev/null +++ b/libsrc/cbm610/io.inc @@ -0,0 +1,108 @@ +; +; I/O definitions for the CBM 610 +; +; Taken from a kernal disassembly done by myself in 1987. +; +; Ullrich von Bassewitz, 28.09.1998 + + +; I/O $d800: CRTC 6545 + +; crtc = $d800 + + CAdrReg = $00 + CDataReg = $01 + + + +; I/O $da00: SID 6581 + +; sid = $da00 + + Osc1 = $00 + Osc2 = $07 + Osc3 = $0e + + FreqLo = $00 + FreqHi = $01 + PulseF = $02 + PulseC = $03 + OscCtl = $04 + AtkDcy = $05 + SusRel = $06 + + FiCtlLo = $15 + FiCtlHi = $16 + Resonance = $17 + Volume = $18 + PotX = $19 + PotY = $1A + Random = $1B + Env3 = $1C + + + +; I/O $db00: CIA 6526 Inter Process Communication + +; IPCcia = $db00 + + PortA = $00 + PortB = $01 + DDRA = $02 + DDRB = $03 + TimALo = $04 + TimAHi = $05 + TimBLo = $06 + TimBHi = $07 + TOD10 = $08 + TODsec = $09 + TODmin = $0A + TODhour = $0B + SerDataReg = $0C + IntCtrReg = $0D + CtrlA = $0E + CtrlB = $0F + + + +; I/O $dc00: CIA 6526 + +; cia = $dc00 + + + +; I/O $dd00: ACIA 6551 + +; acia = $dd00 + + ADataReg = $00 + AStatusReg = $01 + ACmdReg = $02 + ACtrlReg = $03 + + + +; I/O $de00: Triport #1 6525 + +; tpi1 = $de00 + + tpiPortA = $00 + tpiPortB = $01 + tpiPortC = $02 + tpiIntLatch = $02 + tpiDDRA = $03 + tpiDDRB = $04 + tpiDDRC = $05 + tpiIntMask = $05 + tpiCtrlReg = $06 + tpiActIntReg = $07 + + + +; I/O $df00: Triport #2 6525 + +; tpi2 = $df00 + + + + diff --git a/libsrc/cbm610/kbhit.s b/libsrc/cbm610/kbhit.s new file mode 100644 index 000000000..5ef5e63ae --- /dev/null +++ b/libsrc/cbm610/kbhit.s @@ -0,0 +1,23 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; int kbhit (void); +; + + .export _kbhit + .import return0, return1 + + .include "zeropage.inc" + +.proc _kbhit + lda KeyIndex ; Get number of characters + bne L1 + jmp return0 +L1: jmp return1 +.endproc + + + + + + diff --git a/libsrc/cbm610/kirq.s b/libsrc/cbm610/kirq.s new file mode 100644 index 000000000..63630720b --- /dev/null +++ b/libsrc/cbm610/kirq.s @@ -0,0 +1,100 @@ +; +; Ullrich von Bassewitz, 28.09.1998 +; +; IRQ routine for the 610. +; + + .export irq, nmi, k_irq, k_nmi + .import k_scnkey, k_udtim, k_rs232 + .importzp tpi1 + + .include "zeropage.inc" + .include "io.inc" + .include "page3.inc" + + +; ------------------------------------------------------------------------- +; This is the mapping of the active irq register of the 6525 (tpi1): +; +; Bit 7 6 5 4 3 2 1 0 +; | | | | ^ 50 Hz +; | | | ^ SRQ IEEE 488 +; | | ^ cia +; | ^ IRQB ext. Port +; ^ acia + + + +; ------------------------------------------------------------------------- +; IRQ entry point + +.proc irq + + pha + txa + pha + tya + pha + tsx + lda $104,x ; Get the flags from the stack + and #$10 ; Test break flag + bne L1 + jmp (IRQVec) +L1: jmp (BRKVec) + +.endproc + +; ------------------------------------------------------------------------- +; NMI entry point + +.proc nmi + + jmp (NMIVec) + +.endproc + + +; ------------------------------------------------------------------------- +; Kernal irq entry point. The IRQvec points here (usually). + +k_irq: + lda IndReg ; Ind. Segment retten + pha + cld + lda #$0F + sta IndReg + ldy #tpiActIntReg + lda (tpi1),y ; Interrupt Register 6525 + beq noirq + +; ------------------------------------------------------------------------- +; 50/60Hz interrupt + + cmp #%00000001 ; ticker irq? + bne irq1 + jsr k_scnkey ; Poll the keyboard + jsr k_udtim ; Bump the time + +; ------------------------------------------------------------------------- +; UART interrupt + +irq1: cmp #%00010000 ; interrupt from uart? + bne irqend + jsr k_rs232 ; Read character from uart + +; ------------------------------------------------------------------------- +; Done + +irqend: ldy #tpiActIntReg + sta (tpi1),y ; Clear interrupt + +noirq: pla + sta IndReg + pla + tay + pla + tax + pla +k_nmi: rti + + diff --git a/libsrc/cbm610/kplot.s b/libsrc/cbm610/kplot.s new file mode 100644 index 000000000..e7f633db1 --- /dev/null +++ b/libsrc/cbm610/kplot.s @@ -0,0 +1,76 @@ +; +; Ullrich von Bassewitz, 28.09.1998 +; +; PLOT routine for the 610. +; + + .export k_plot + .importzp crtc + + .include "zeropage.inc" + + +.proc k_plot + + bcc set + ldx CURS_Y + ldy CURS_X + rts + +set: stx CURS_Y + sty CURS_X + lda LineLSBTab,x + sta CharPtr + lda LineMSBTab,x + sta CharPtr+1 + + lda IndReg + pha + lda #$0F + sta IndReg + + ldy #$00 + clc + sei + sta (crtc),y + lda CharPtr + adc CURS_X + iny + sta (crtc),y + dey + lda #$0E + sta (crtc),y + iny + lda (crtc),y + and #$F8 + sta sedt1 + lda CharPtr+1 + adc #$00 + and #$07 + ora sedt1 + sta (crtc),y + cli + + pla + sta IndReg + rts +.endproc + +; ------------------------------------------------------------------------- +; Low bytes of the start address of the screen lines + +.rodata + +LineLSBTab: + .byte $00,$50,$A0,$F0,$40,$90,$E0,$30 + .byte $80,$D0,$20,$70,$C0,$10,$60,$B0 + .byte $00,$50,$A0,$F0,$40,$90,$E0,$30 + .byte $80 +; ------------------------------------------------------------------------- +; High bytes of the start address of the screen lines + +LineMSBTab: + .byte $D0,$D0,$D0,$D0,$D1,$D1,$D1,$D2 + .byte $D2,$D2,$D3,$D3,$D3,$D4,$D4,$D4 + .byte $D5,$D5,$D5,$D5,$D6,$D6,$D6,$D7 + .byte $D7 diff --git a/libsrc/cbm610/kscnkey.s b/libsrc/cbm610/kscnkey.s new file mode 100644 index 000000000..8d0447924 --- /dev/null +++ b/libsrc/cbm610/kscnkey.s @@ -0,0 +1,146 @@ +; +; Ullrich von Bassewitz, 28.09.1998 +; +; Keyboard polling stuff for the 610. +; + + .export k_scnkey + .importzp tpi2, ktab1, ktab2, ktab3, ktab4 + + .include "zeropage.inc" + .include "io.inc" + .include "page3.inc" + + +.proc k_scnkey + + lda #$FF + sta ModKey + sta NorKey + lda #$00 + sta KbdScanBuf + ldy #tpiPortB + sta (tpi2),y + ldy #tpiPortA + sta (tpi2),y + jsr Poll + and #$3F + eor #$3F + bne L1 + jmp NoKey + +L1: lda #$FF + ldy #tpiPortA + sta (tpi2),y + asl a + ldy #tpiPortB + sta (tpi2),y + jsr Poll + pha + sta ModKey + ora #$30 + bne L3 ; Branch always + +L2: jsr Poll +L3: ldx #$05 + ldy #$00 +L4: lsr a + bcc L5 + inc KbdScanBuf + dex + bpl L4 + sec + ldy #tpiPortB + lda (tpi2),y + rol a + sta (tpi2),y + ldy #tpiPortA + lda (tpi2),y + rol a + sta (tpi2),y + bcs L2 + pla + bcc NoKey ; Branch always + +L5: ldy KbdScanBuf + sty NorKey + pla + asl a + asl a + asl a + bcc L6 + bmi L7 + lda (ktab2),y ; Shifted normal key + ldx GrafMode + beq L8 + lda (ktab3),y ; Shifted key in graph mode + bne L8 + +L6: lda (ktab4),y ; Key with ctrl pressed + bne L8 +L7: lda (ktab1),y ; Normal key +L8: tax + cpx #$FF ; Valid key? + beq Done + cpy LastIndex + beq Repeat + ldx #$13 + stx RepeatDelay + ldx KeyIndex + cpx #$09 + beq NoKey + cpy #$59 + bne PutKey + cpx #$08 + beq NoKey + sta KeyBuf,x + inx + bne PutKey + +NoKey: ldy #$FF +Done: sty LastIndex +End: lda #$7F + ldy #tpiPortA + sta (tpi2),y + ldy #tpiPortB + lda #$FF + sta (tpi2),y + rts + +Repeat: dec RepeatDelay + bpl End + inc RepeatDelay + dec RepeatCount + bpl End + inc RepeatCount + ldx KeyIndex + bne End + +PutKey: sta KeyBuf,x + inx + stx KeyIndex + ldx #$03 + stx RepeatCount + bne Done + +.endproc + + +; Poll the keyboard port until it's stable + +.proc Poll + ldy #tpiPortC +L1: lda (tpi2),y + sta KeySave + lda (tpi2),y + cmp KeySave + bne L1 + rts +.endproc + + +.bss + +KeySave: .res 1 + + diff --git a/libsrc/cbm610/kudtim.s b/libsrc/cbm610/kudtim.s new file mode 100644 index 000000000..e100a84c6 --- /dev/null +++ b/libsrc/cbm610/kudtim.s @@ -0,0 +1,25 @@ +; +; Ullrich von Bassewitz, 28.09.1998 +; +; udtim routine for the 610. We will not check for the stop key here, since +; C programs will not use it. +; + + .export k_udtim + .importzp time + + +.proc k_udtim + + inc time + bne L9 + inc time+1 + bne L9 + inc time+2 + bne L9 + inc time+3 +L9: rts + +.endproc + + diff --git a/libsrc/cbm610/page3.inc b/libsrc/cbm610/page3.inc new file mode 100644 index 000000000..7095a1348 --- /dev/null +++ b/libsrc/cbm610/page3.inc @@ -0,0 +1,94 @@ +; +; Page 3 variables for the CBM 610 +; +; Taken from a kernal disassembly done by myself in 1987. +; +; Ullrich von Bassewitz, 28.09.1998 + + +; +; system ram vectors +; + +IRQVec = $0300 +BRKVec = $0302 +NMIVec = $0304 +openVec = $0306 +closeVec = $0308 +chkinVec = $030A +ckoutVec = $030C +clrchVec = $030E +basinVec = $0310 +bsoutVec = $0312 +stopVec = $0314 +getinVec = $0316 +clallVec = $0318 +loadVec = $031A +saveVec = $031C +usrcmd = $031E +escvec = $0320 +ctrlvec = $0322 +secndVec = $0324 +tksaVec = $0326 +acptrVec = $0328 +cioutVec = $032A +untlkVec = $032C +unlsnVec = $032E +listnVec = $0330 +talkVec = $0332 + +; +; +; + +LogicalAdrTable = $0334 +FirstAdrTable = $033E +SecondAdrTable = $0348 +SysMemBot = $0352 +SysMemTop = $0355 +UsrMemBot = $0358 +UsrMemTop = $035B +TimOut = $035E +VerifyFlag = $035F +DevTabIndex = $0360 +MsgFlag = $0361 +CassBufPtr = $0362 +t1 = $0363 +t2 = $0364 +XSave = $0365 +SaveX = $0366 +SaveXt = $0367 +temp = $0368 +alarm = $0369 +TapeVec = $036A +LoadStAdr = $036F +CassMotFlag = $0375 +m6551Ctrl = $0376 +m6551Cmd = $0377 +rs232status = $037A +dcddsr = $037B +rs232head = $037C +rs232tail = $037D +PgmKeyEnd = $0380 +PgmKeySeg = $0382 +PgmKeySize = $0383 +rvsFlag = $0397 +linetmp = $0398 +LastPrtChar = $0399 +InsertFlag = $039A +ScrollFlag = $039B +FktTemp = $039C +PgmKeyIdx = $039D +LogScrollFlag = $039E +BellMode = $039F ; Bell on/off 00 = an +SegSave = $03A0 +TabStopTable = $03A1 ; 80 bits for tabstops +KeyBuf = $03AB ; Keyboard buffer +funvec = $03B5 ; Vector for function key handline +FunKeyTmp = $03B7 +sedt3 = $03B9 +MoniSegSave = $03f0 +wstvec = $03F8 +WstFlag = $03FA ; Warm start flag + + diff --git a/libsrc/cbm610/pokesys.s b/libsrc/cbm610/pokesys.s new file mode 100644 index 000000000..ebd4ec5cf --- /dev/null +++ b/libsrc/cbm610/pokesys.s @@ -0,0 +1,38 @@ +; +; Ullrich von Bassewitz, 29.09.1998 +; +; void pokebsys (unsigned Addr, unsigned char Val); +; void pokewsys (unsigned Addr, unsigned Val); + + .export _pokebsys, _pokewsys + .import popsreg + .importzp sreg, tmp1 + + .include "zeropage.inc" + + +_pokebsys: + jsr popsreg ; Get the address + ldx IndReg + ldy #$0F + sty IndReg ; Switch to the system bank + ldy #$00 + sta (sreg),y + stx IndReg + rts + +_pokewsys: + stx tmp1 ; Save high byte + jsr popsreg ; Get the address + ldx IndReg + ldy #$0F + sty IndReg ; Switch to the system bank + ldy #$00 + sta (sreg),y + iny + lda tmp1 + sta (sreg),y + stx IndReg + rts + + diff --git a/libsrc/cbm610/rs232.s b/libsrc/cbm610/rs232.s new file mode 100644 index 000000000..1ed4c615f --- /dev/null +++ b/libsrc/cbm610/rs232.s @@ -0,0 +1,631 @@ +; +; SwiftLink/Turbo-232 v0.90 device driver, by Craig Bruce, 14-Apr-1998. +; +; This software is Public Domain. It is in Buddy assembler format. +; +; This device driver uses the SwiftLink RS-232 Serial Cartridge, available from +; Creative Micro Designs, Inc, and also supports the extensions of the Turbo232 +; Serial Cartridge. Both devices are based on the 6551 ACIA chip. It also +; supports the "hacked" SwiftLink with a 1.8432 MHz crystal. +; +; The code assumes that the kernal + I/O are in context. On the C128, call +; it from Bank 15. On the C64, don't flip out the Kernal unless a suitable +; NMI catcher is put into the RAM under then Kernal. For the SuperCPU, the +; interrupt handling assumes that the 65816 is in 6502-emulation mode. +; +;-------------------------------------------------------------------------- +; +; Adapted for the use with the cc65 runtime library by +; Ullrich von Bassewitz (uz@musoftware.de) 02-May-1999. +; +; All external functions are C callable, the return value is an error code. +; + + + .importzp ptr1, ptr2, tmp1, tmp2 + .importzp acia, RecvBuf, SendBuf + .import popa, popax + .import sys_bank, restore_bank + .export _rs232_init, _rs232_params, _rs232_done, _rs232_get + .export _rs232_put, _rs232_pause, _rs232_unpause, _rs232_status + .export k_rs232 + + .include "zeropage.inc" + + +;---------------------------------------------------------------------------- +; +; Global variables +; + +.bss +DropCnt: .res 4 ; Number of bytes lost from rx buffer full +Initialized: .res 1 ; Flag indicating driver is initialized +Stopped: .res 1 ; Flow-stopped flag +RtsOff: .res 1 ; +Errors: .res 1 ; Number of bytes received in error, low byte +BaudCode: .res 1 ; Current baud in effect + +; Segment, the RS232 buffers are in +BufferSeg = 2 + +; UART register offsets +RegData = 0 ; Data register +RegStatus = 1 ; Status register +RegCommand = 2 ; Command register +RegControl = 3 ; Control register + +; Error codes. Beware: The codes must match the codes in the C header file +ErrNotInitialized = $01 +ErrBaudTooFast = $02 +ErrBaudNotAvail = $03 +ErrNoData = $04 +ErrOverflow = $05 + + +.code + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_init (char hacked); +; /* Initialize the serial port, install the interrupt handler. The parameter +; * must be true (non zero) for a hacked swiftlink and false (zero) otherwise. +; */ +; + +_rs232_init: + bit Initialized ;** shut down if started + bpl @L1 + pha + jsr _rs232_done + pla + +; Initialize buffers & control + +@L1: lda #0 + sta RecvHead + sta SendHead + sta RecvTail + sta SendTail + sta Errors + sta Stopped + lda #255 + sta RecvFreeCnt + sta SendFreeCnt + +; Set default to 2400-8N1, enable interrupts + + jsr sys_bank ; Switch indirect to system bank + + ldy #RegData + lda (acia),y + ldy #RegStatus + lda (acia),y + lda #$18 + ldy #RegControl + sta (acia),y + + lda #$01 + sta RtsOff + ora #$08 + ldy #RegCommand + sta (acia),y + lda #$06 + sta BaudCode + + jsr restore_bank + + lda #$ff + sta Initialized + lda #$00 + tax + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_params (unsigned char params, unsigned char parity); +; /* Set the port parameters. Use a combination of the #defined values above. */ +; +; Set communication parameters. +; +; baud rates stops word | parity +; --------------------- ----- ----- | --------- +; $00=50 $08=9600 $00=1 $00=8 | $00=none +; $01=110 $09=19200 $80=2 $20=7 | $20=odd +; $02=134.5 $0a=38400 $40=6 | $60=even +; $03=300 $0b=57600 $60=5 | $A0=mark +; $04=600 $0c=115200 | $E0=space +; $05=1200 $0d=230400 +; $06=2400 $0e=future +; $07=4800 $0f=future +; + +_rs232_params: + jsr CheckInitialized ;** check initialized + bcc @L1 + rts + +; Save new parity + +@L1: and #%11100000 + ora #%00000001 + sta tmp2 + +; Set baud/parameters + + jsr popa + sta tmp1 + and #$0f + tax + lda Bauds,x + cmp #$ff + bne @L5 + lda #ErrBaudNotAvail + bne @L9 + +@L5: jsr sys_bank ; Indirect segment to system bank + tax + lda tmp1 + and #$0f + sta BaudCode + lda tmp1 + and #%11100000 + ora #%00010000 + sta tmp1 + txa + and #$0f + ora tmp1 + ldy #RegControl + sta (acia),y + +; Set new parity + +@L7: lda tmp2 + sta RtsOff + ora #%00001000 + ldy #RegCommand + sta (acia),y + jsr restore_bank ; Restore indirect bank + lda #0 +@L9: ldx #0 + rts + +.rodata +Bauds: + .byte $01,$03,$04,$06,$07,$08,$0a,$0c,$0e,$0f,$ff,$ff,$ff,$ff,$ff,$ff + ;in: 0 1 2 3 4 5 6 7 8 9 a b c d e f + ;baud50 110 134 3 6 12 24 48 96 19 38 57 115 230 exp exp + ;out masks: $0F=Baud, val$FF=err +.code + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_done (void); +; /* Close the port, deinstall the interrupt hander. You MUST call this function +; * before terminating the program, otherwise the machine may crash later. If +; * in doubt, install an exit handler using atexit(). The function will do +; * nothing, if it was already called. +; */ +; + + +_rs232_done: + bit Initialized ;** check initialized + bpl @L9 + +; Stop interrupts, drop DTR + + lda RtsOff + and #%11100010 + ora #%00000010 + ldx IndReg + ldy #$0F + sty IndReg ; Set indirect to system bank + ldy #RegCommand + sta (acia),y + stx IndReg ; Restore old indirect bank + +; Flag uninitialized + +@L9: lda #$00 + sta Initialized + tax + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_get (char* B); +; /* Get a character from the serial port. If no characters are available, the +; * function will return RS_ERR_NO_DATA, so this is not a fatal error. +; */ +; + +_rs232_get: + jsr CheckInitialized ; Check if initialized + bcc @L1 + rts + +; Check for bytes to send + +@L1: sta ptr1 + stx ptr1+1 ; Store pointer to received char + ldx SendFreeCnt + cpx #$ff + beq @L2 + lda #$00 + jsr TryToSend + +; Check for buffer empty + +@L2: lda RecvFreeCnt + cmp #$ff + bne @L3 + lda #ErrNoData + ldx #0 + rts + +; Check for flow stopped & enough free: release flow control + +@L3: ldx Stopped + beq @L4 + cmp #63 + bcc @L4 + lda #$00 + sta Stopped + lda RtsOff + ora #%00001000 + ldx IndReg + ldy #$0F ; Set indirect to system bank + sty IndReg + ldy #RegCommand + sta (acia),y + stx IndReg + +; Get byte from buffer + +@L4: ldx IndReg + lda #BufferSeg ; Set indirect to buffer bank + sta IndReg + ldy RecvHead + lda (RecvBuf),y + stx IndReg ; Restore indirect bank + inc RecvHead + inc RecvFreeCnt + ldx #$00 + sta (ptr1,x) + txa ; Return code = 0 + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_put (char B); +; /* Send a character via the serial port. There is a transmit buffer, but +; * transmitting is not done via interrupt. The function returns +; * RS_ERR_OVERFLOW if there is no space left in the transmit buffer. +; */ +; + +_rs232_put: + jsr CheckInitialized ; Check initialized + bcc @L1 + rts + +; Try to send + +@L1: ldx SendFreeCnt + cpx #$ff + beq @L2 + pha + lda #$00 + jsr TryToSend + pla + +; Put byte into send buffer & send + +@L2: ldx SendFreeCnt + bne @L3 + lda #ErrOverflow + ldx #$00 + rts + +; There is enough room (character still in A) + +@L3: ldx IndReg + ldy #BufferSeg ; Set indirect to buffer segment + sty IndReg + ldy SendTail + sta (SendBuf),y + stx IndReg ; Restore indirect bank + inc SendTail + dec SendFreeCnt + lda #$ff + jsr TryToSend + lda #$00 + tax + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_pause (void); +; /* Assert flow control and disable interrupts. */ +; + +_rs232_pause: +; Check initialized + jsr CheckInitialized + bcc @L1 + rts + +; Assert flow control + +@L1: lda RtsOff + sta Stopped + jsr sys_bank ; Set indirect to system bank + ldy #RegCommand + sta (acia),y + +; Delay for flow stop to be received + + ldx BaudCode + lda PauseTimes,x + jsr DelayMs + +; Stop rx interrupts + + lda RtsOff + ora #$02 + ldy #RegCommand + sta (acia),y + jsr restore_bank ; Restore indirect segment + lda #0 + tax + rts + + +.rodata +; Delay times: 32 byte-receive times in milliseconds, or 100 max. +; Formula = 320,000 / baud +PauseTimes: + .byte 100,100,100,100,100,100,100,067,034,017,009,006,003,002,001,001 + ;in: 0 1 2 3 4 5 6 7 8 9 a b c d e f + ;baud50 110 134 3 6 12 24 48 96 19 38 57 115 230 exp exp +.code + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_unpause (void); +; /* Re-enable interrupts and release flow control */ +; + +_rs232_unpause: +; Check initialized + jsr CheckInitialized + bcc @L1 + rts + +; Re-enable rx interrupts & release flow control + +@L1: lda #$00 + sta Stopped + lda RtsOff + ora #%00001000 + ldx IndReg + ldy #$0F + sty IndReg ; Set indirect to system bank + ldy #RegCommand + sta (acia),y + stx IndReg ; Restore indirect bank + +; Poll for stalled char & exit + + jsr PollReceive + lda #0 + tax + rts + +;---------------------------------------------------------------------------- +; +; unsigned char __fastcall__ rs232_status (unsigned char* status, +; unsigned char* errors); +; /* Return the serial port status. */ +; + +_rs232_status: + sta ptr2 + stx ptr2+1 + jsr popax + sta ptr1 + stx ptr1+1 + jsr CheckInitialized + bcs @L9 + +; Get status + + ldx IndReg ; Save indirect segment + lda #$0F + sta IndReg ; Set system bank as indirect segment + ldy #RegStatus + lda (acia),y ; Read status register + stx IndReg + ldy #0 + sta (ptr1),y + jsr PollReceive ; bug-recovery hack + lda Errors + ldy #0 + sta (ptr2),y + tya + tax +@L9: rts + +;---------------------------------------------------------------------------- +; +; RS232 interrupt handler. +; The RS232 handler will be called with the system bank as indirect bank +; and all registers saved. +; + +k_rs232: + ldy #RegStatus + lda (acia),y ; check for byte received + and #$08 + beq @L9 ; Nothing to receive + lda (acia),y ; check for receive errors + and #$07 + beq @L1 + inc Errors +@L1: ldy #RegData + lda (acia),y ; get byte and put into receive buffer + ldx RecvFreeCnt + beq @L3 + ldy #BufferSeg + sty IndReg + ldy RecvTail + sta (RecvBuf),y ; Store received character + lda #$0F + sta IndReg ; Restore indirect segment + inc RecvTail + dec RecvFreeCnt + cpx #33 ; check for buffer space low + bcs @L9 + +; Assert flow control + +@L2: lda RtsOff ; assert flow control if buffer space too low + ldy #RegCommand + sta (acia),y + sta Stopped + rts + +; Drop this char + +@L3: inc DropCnt+0 ;not time-critical + bne @L9 + inc DropCnt+1 + bne @L9 + inc DropCnt+2 + bne @L9 + inc DropCnt+3 +@L9: rts + + +;---------------------------------------------------------------------------- +; +; CheckInitialized - internal check if initialized +; Set carry and an error code if not initialized, clear carry and do not +; change any registers if initialized. +; + +CheckInitialized: + bit Initialized + bmi @L1 + lda #ErrNotInitialized + ldx #0 + sec + rts + +@L1: clc + rts + +;---------------------------------------------------------------------------- +; Try to send a byte. Internal routine. A = TryHard + +TryToSend: + sta tmp1 ; Remember tryHard flag + ldx IndReg ; Save indirect segment + lda #$0F + sta IndReg ; Set system segment as indirect segment +@L0: lda SendFreeCnt + cmp #$ff + beq @L3 ; Bail out + +; Check for flow stopped + +@L1: lda Stopped + bne @L3 ; Bail out + +; Check that the UART is ready to send + +@L2: ldy #RegStatus + lda (acia),y + and #$10 + bne @L4 + bit tmp1 ; Keep trying if must try hard + bmi @L0 +@L3: stx IndReg ; Restore indirect segment + rts + +; Send byte and try again + +@L4: lda #BufferSeg + sta IndReg + ldy SendHead + lda (SendBuf),y + ldy #$0F + sty IndReg + ldy #RegData + sta (acia),y + inc SendHead + inc SendFreeCnt + jmp @L0 + + +;---------------------------------------------------------------------------- +; +; PollReceive - poll for rx char +; This function is useful in odd cases where the 6551 has a character in +; it but it fails to raise an NMI. It might be edge-triggering conditions? +; Actually, I'm not entirely sure that this condition can still arrise, but +; calling this function does no harm. +; + +PollReceive: + ldx IndReg ; Save indirect segment + lda #$0F + sta IndReg ; Set system bank as indirect segment + ldy #RegStatus + lda (acia),y + and #$08 + beq @L9 + lda (acia),y ; Read a second time? ### + and #$08 + beq @L9 + ldy #RegData + lda (acia),y + ldy RecvFreeCnt + beq @L9 + ldy #BufferSeg + sty IndReg + ldy RecvTail + sta (RecvBuf),y + inc RecvTail + dec RecvFreeCnt +@L9: stx IndReg ; Restore indirect segment + rts + +;---------------------------------------------------------------------------- +; +; DelayMs : delay for given number of milliseconds +; This implementation isn't very rigerous; it merely delays for the +; approximate number of clock cycles for the processor speed. +; Algorithm: +; repeat for number of milliseconds: +; repeat for number of MHz of cpu speed: +; delay for 1017 clock cycles +; + +DelayMs: ;( .A=milliseconds ) +@L1: ldy #2 ; 2MHz +@L2: ldx #203 ;(2) +@L3: dex ;(2) + bne @L3 ;(3) // 1017 cycles + dey + bne @L2 + sec + sbc #1 + bne @L1 + rts + +.end + + + diff --git a/libsrc/cbm610/zeropage.inc b/libsrc/cbm610/zeropage.inc new file mode 100644 index 000000000..5e9ba5d62 --- /dev/null +++ b/libsrc/cbm610/zeropage.inc @@ -0,0 +1,100 @@ +; +; Zero page variables for the CBM 610 +; +; Taken from a kernal disassembly done by myself in 1987. +; +; Ullrich von Bassewitz, 28.09.1998 + + +ExecReg = $0000 +IndReg = $0001 + +; Up to $20 and $60-8F used by runtime and fixed values +; ----------------------------------- + +KbdScanBuf = $20 ; Intermediate for keyboard scan +; RS232 stuff +RecvHead = $21 ; Head of receive buffer +RecvTail = $22 ; Tail of receive buffer +RecvFreeCnt = $23 ; Number of bytes in receive buffer +SendHead = $24 ; Head of send buffer +SendTail = $25 ; Tail of send buffer +SendFreeCnt = $26 ; Number of bytes free in send buffer + +FileNameAdrLo = $90 +FileNameAdrHi = $91 +FileNameAdrSeg = $92 +SaveAdrLow = $93 +SaveAdrHi = $94 +SaveAdrSeg = $95 +EndAdrLow = $96 +EndAdrHi = $97 +EndAdrSeg = $98 +StartAdrLow = $99 +StartAdrHi = $9A +StartAdrSeg = $9B +Status = $9C +FileNameLen = $9D +LogicalAdr = $9E +FirstAdr = $9F +SecondAdr = $A0 +DefInpDev = $A1 +DefOutDev = $A2 +TapeBufPtr = $A3 +TapeBufPtrSeg = $A5 +rs232BufPtr = $A6 +rs232BufPtrSeg = $A8 +StopKeyFlag = $A9 +CTemp = $AA +snsw1 = $AB +SegChgPtr = $AC +PChighSave = $AE +PClowSave = $AF +SRSave = $B0 +ACSave = $B1 +XRSave = $B2 +YRSave = $B3 +SPSave = $B4 +IndSegSave = $B5 +IRQSaveHi = $B7 +IRQSaveLo = $B8 +Adr1 = $B9 +Adr2 = $BB +MoniCntr = $BD +MoniTmp = $BE +MoniDevNr = $BF +PgmKeyBuf = $C0 +PgmKeyPtr = $C2 +sedsal = $C4 +sedeal = $C6 +CharPtr = $C8 +CURS_Y = $CA +CURS_X = $CB +GrafMode = $CC +LastIndex = $CD +LastLine = $CE +LastCol = $CF +crsw = $D0 +KeyIndex = $D1 +QuoteSw = $D2 +Insrt = $D3 +Config = $D4 +LastLinePos = $D5 +PgmKeyIndex = $D6 +RepeatCount = $D7 +RepeatDelay = $D8 +sedt1 = $D9 ; Temp +sedt2 = $DA ; Temp, frequently used +PrtData = $DB +ScreenTop = $DC +ScreenBot = $DD +ScreenLeft = $DE +ScreenRight = $DF +ModKey = $E0 +NorKey = $E1 +BitTable = $E2 + + + + + diff --git a/libsrc/common/.cvsignore b/libsrc/common/.cvsignore new file mode 100644 index 000000000..1f88d3044 --- /dev/null +++ b/libsrc/common/.cvsignore @@ -0,0 +1,40 @@ +fclose.s +fgets.s +fprintf.s +strdup.s +calloc.s +_fopen.s +fputs.s +fread.s +fwrite.s +gets.s +realloc.s +bsearch.s +printf.s +_hextab.s +malloc.s +free.s +vfprintf.s +fdopen.s +_afailed.s +fopen.s +fgetc.s +fputc.s +puts.s +_printf.s +vprintf.s +vsprintf.s +sprintf.s +abort.s +errormsg.s +_hadd.s +cprintf.s +vcprintf.s +freopen.s +perror.s +qsort.s +strxfrm.s +strtok.s +locale.s +putchar.s +getchar.s diff --git a/libsrc/common/Makefile b/libsrc/common/Makefile new file mode 100644 index 000000000..2bbeede0b --- /dev/null +++ b/libsrc/common/Makefile @@ -0,0 +1,39 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $< + @$(AS) -g -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +C_OBJS = fclose.o fgets.o fprintf.o strdup.o calloc.o _fopen.o\ + fputs.o fread.o fwrite.o gets.o realloc.o bsearch.o strxfrm.o\ + printf.o _hextab.o malloc.o free.o vfprintf.o fdopen.o strtok.o\ + _afailed.o fopen.o fgetc.o fputc.o puts.o gets.o perror.o getchar.o\ + _printf.o vprintf.o vsprintf.o sprintf.o abort.o qsort.o putchar.o\ + errormsg.o _hadd.o cprintf.o vcprintf.o freopen.o locale.o + +S_OBJS = isalpha.o isdigit.o _file.o fmisc.o strlower.o strchr.o tolower.o\ + toupper.o errno.o strcpy.o strlen.o strcat.o strcmp.o itoa.o\ + strupper.o isalpha.o isalnum.o isgraph.o islower.o isupper.o\ + isprint.o ispunct.o isspace.o isxdigit.o isblank.o strrchr.o\ + _stksize.o _heap.o stricmp.o strncmp.o strncpy.o atoi.o setjmp.o\ + longjmp.o rand.o atexit.o memset.o memcpy.o memchr.o memcmp.o\ + ltoa.o strcspn.o strncat.o strpbrk.o strspn.o abs.o labs.o jmpvec.o\ + _fdesc.o stkcheck.o zerobss.o copydata.o _swap.o strstr.o strcoll.o\ + _sys.o getcpu.o _oserror.o strerror.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f *~ + @rm -f $(C_OBJS:.o=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) diff --git a/libsrc/common/_afailed.c b/libsrc/common/_afailed.c new file mode 100644 index 000000000..6600bf860 --- /dev/null +++ b/libsrc/common/_afailed.c @@ -0,0 +1,21 @@ +/* + * _afailed.c + * + * Ullrich von Bassewitz, 06.06.1998 + */ + + + +#include +#include + + + +void _afailed (char* file, unsigned line) +{ + fprintf (stderr, "ASSERTION FAILED IN %s(%u)\n", file, line); + exit (2); +} + + + diff --git a/libsrc/common/_fdesc.s b/libsrc/common/_fdesc.s new file mode 100644 index 000000000..2ffadd5e8 --- /dev/null +++ b/libsrc/common/_fdesc.s @@ -0,0 +1,39 @@ +; +; Ullrich von Bassewitz, 17.06.1998 +; +; int _fdesc (void); +; /* Find a free descriptor slot */ + + + .export __fdesc + .import return0, __filetab + .importzp tmp1 + +__fdesc: + ldy #0 +L1: lda __filetab+1,y ; load flags + beq L2 ; jump if empty (== CLOSED) + iny + iny + cpy #16 ; Done? + bne L1 + +; File table is full + + jmp return0 + +; Free slot found + +L2: sty tmp1 ; Offset + lda #<__filetab + ldx #>__filetab + clc + adc tmp1 + tay + txa + adc #0 + tax + tya + rts + + diff --git a/libsrc/common/_file.h b/libsrc/common/_file.h new file mode 100644 index 000000000..8a27796c2 --- /dev/null +++ b/libsrc/common/_file.h @@ -0,0 +1,48 @@ +/* + * _file.h + * + * Ullrich von Bassewitz, 02.06.1998 + * + */ + + + +#ifndef __FILE_H +#define __FILE_H + + + +#include + + + +/* Definition of struct _FILE */ +struct _FILE { + char f_fd; + char f_flags; +}; + +/* File table. Beware: FOPEN_MAX is hardcoded in the ASM files! */ +extern FILE _filetab [FOPEN_MAX]; + +/* Flags field */ +#define _FCLOSED 0x00 +#define _FOPEN 0x01 +#define _FEOF 0x02 +#define _FERROR 0x04 + + + +FILE* _fopen (const char* name, const char* mode, FILE* f); +/* Open the specified file and fill the descriptor values into f */ + +FILE* _fdesc (void); +/* Find a free FILE descriptor */ + + + +/* End of _file.h */ +#endif + + + diff --git a/libsrc/common/_file.s b/libsrc/common/_file.s new file mode 100644 index 000000000..19e6ac202 --- /dev/null +++ b/libsrc/common/_file.s @@ -0,0 +1,28 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; Data for the stdio file stream. +; + + .export __filetab, _stdin, _stdout, _stderr + +.data + +__filetab: +in: .byte 0, 1 ; stdin +out: .byte 1, 1 ; stdout +err: .byte 2, 1 ; stderr + .byte 0, 0 ; free slot + .byte 0, 0 ; free slot + .byte 0, 0 ; free slot + .byte 0, 0 ; free slot + .byte 0, 0 ; free slot + +_stdin: + .word in + +_stdout: + .word out + +_stderr: + .word err diff --git a/libsrc/common/_fopen.c b/libsrc/common/_fopen.c new file mode 100644 index 000000000..3ae53a59b --- /dev/null +++ b/libsrc/common/_fopen.c @@ -0,0 +1,73 @@ +/* + * _fopen.c + * + * Ullrich von bassewitz, 17.06.1997 + */ + + + +#include +#include +#include "_file.h" + + + +static unsigned char amode_to_bmode (const char* mode) +/* Convert ASCII mode (like for fopen) to binary mode (for open) */ +{ + char c; + char flag = 0; + unsigned char binmode = 0; + + while (c = *mode++) { + switch(c) { + case 'w': + binmode = O_WRONLY; + break; + case 'r': + binmode = O_RDONLY; + break; + case '+': + binmode = O_RDWR; + break; + /* a,b missing */ + } + } + if (binmode == 0) { + _errno = EINVAL; + } + return binmode; +} + + + +FILE* _fopen (const char* name, const char* mode, FILE* f) +/* Open the specified file and fill the descriptor values into f */ +{ + int fd; + unsigned char binmode; + + + /* Convert ASCII mode to binary mode */ + if ((binmode = amode_to_bmode (mode)) == 0) { + /* Invalid mode, _errno already set */ + return 0; + } + + /* Open the file */ + fd = open (name, binmode); + if (fd == -1) { + /* Error - _oserror is set */ + return 0; + } + + /* Remember fd, mark the file as opened */ + f->f_fd = fd; + f->f_flags = _FOPEN; + + /* Return the file descriptor */ + return f; +} + + + diff --git a/libsrc/common/_hadd.c b/libsrc/common/_hadd.c new file mode 100644 index 000000000..190875d64 --- /dev/null +++ b/libsrc/common/_hadd.c @@ -0,0 +1,106 @@ +/* + * _hadd.c + * + * Ullrich von Bassewitz, 19.06.1998 + */ + + + +#include +#include "_heap.h" + + + +void _hadd (void* mem, size_t size) +/* Add an arbitrary memory block to the heap. This function is used by + * free(), but it does also allow usage of otherwise unused memory + * blocks as heap space. The given block is entered in the free list + * without any checks, so beware! + */ +{ + struct freeblock* f; + struct freeblock* left; + struct freeblock* right; + + if (size >= sizeof (struct freeblock)) { + + /* Set the admin data */ + f = (struct freeblock*) mem; + f->size = size; + + /* Check if the freelist is empty */ + if (_hfirst == 0) { + + /* The freelist is empty until now, insert the block */ + f->prev = 0; + f->next = 0; + _hfirst = f; + _hlast = f; + + } else { + + /* We have to search the free list. As we are doing so, we check + * if it is possible to combine this block with another already + * existing block. Beware: The block may be the "missing link" + * between *two* other blocks. + */ + left = 0; + right = _hfirst; + while (right && f > right) { + left = right; + right = right->next; + } + + + /* Ok, the current block must be inserted between left and right (but + * beware: one of the two may be zero!). Also check for the condition + * that we have to merge two or three blocks. + */ + if (right) { + /* Check if we must merge the block with the right one */ + if (((int) f) + size == (int) right) { + /* Merge with the right block */ + f->size += right->size; + if (f->next = right->next) { + f->next->prev = f; + } else { + /* This is now the last block */ + _hlast = f; + } + } else { + /* No merge, just set the link */ + f->next = right; + right->prev = f; + } + } else { + f->next = 0; + /* Special case: This is the new freelist end */ + _hlast = f; + } + if (left) { + /* Check if we must merge the block with the left one */ + if ((int) f == ((int) left) + left->size) { + /* Merge with the left block */ + left->size += f->size; + if (left->next = f->next) { + left->next->prev = left; + } else { + /* This is now the last block */ + _hlast = left; + } + } else { + /* No merge, just set the link */ + left->next = f; + f->prev = left; + } + } else { + f->prev = 0; + /* Special case: This is the new freelist start */ + _hfirst = f; + } + } + } +} + + + diff --git a/libsrc/common/_heap.h b/libsrc/common/_heap.h new file mode 100644 index 000000000..3f8d7800f --- /dev/null +++ b/libsrc/common/_heap.h @@ -0,0 +1,43 @@ +/* + * _heap.h + * + * Ullrich von Bassewitz, 03.06.1998 + * + */ + + + +#ifndef __HEAP_H +#define __HEAP_H + + + +/* Space needed for administering used blocks */ +#define HEAP_ADMIN_SPACE sizeof (unsigned) + +/* The data type used to implement the free list. + * Beware: Field order is significant! + */ +struct freeblock { + unsigned size; + struct freeblock* next; + struct freeblock* prev; +}; + + + +/* Variables that describe the heap */ +extern unsigned* _horg; /* Bottom of heap */ +extern unsigned* _hptr; /* Current top */ +extern unsigned* _hend; /* Upper limit */ +extern struct freeblock* _hfirst; /* First free block in list */ +extern struct freeblock* _hlast; /* Last free block in list */ + + + +/* End of _heap.h */ + +#endif + + + diff --git a/libsrc/common/_heap.s b/libsrc/common/_heap.s new file mode 100644 index 000000000..427ecaea1 --- /dev/null +++ b/libsrc/common/_heap.s @@ -0,0 +1,45 @@ +; +; Ullrich von Bassewitz, 03.06.1998 +; +; Heap variables and initialization. +; + + .export __horg, __hptr, __hend, __hfirst, __hlast + .export __hinit + .import __BSS_RUN__, __BSS_SIZE__, __stksize + .importzp sp + +.data + +__horg: + .word __BSS_RUN__+__BSS_SIZE__ ; Linker calculates this symbol +__hptr: + .word __BSS_RUN__+__BSS_SIZE__ ; Dito +__hend: + .word __BSS_RUN__+__BSS_SIZE__ +__hfirst: + .word 0 +__hlast: + .word 0 + + +; +; Initialization. Must be called from startup! +; + +.code + +__hinit: + sec + lda sp + sbc __stksize + sta __hend + lda sp+1 + sbc __stksize+1 + sta __hend+1 + rts + + + + + diff --git a/libsrc/common/_hextab.c b/libsrc/common/_hextab.c new file mode 100644 index 000000000..fc82f17d7 --- /dev/null +++ b/libsrc/common/_hextab.c @@ -0,0 +1,18 @@ +/* + * Ullrich von Bassewitz, 11.08.1998 + * + * Hex conversion table. Must be in C since the compiler will convert + * to the correct character set for the target platform. + */ + + + +/* Data in this module is read-only, put it into the RODATA segment */ +#pragma dataseg ("RODATA") + +const unsigned char _hextab [16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + + diff --git a/libsrc/common/_oserror.s b/libsrc/common/_oserror.s new file mode 100644 index 000000000..19073933a --- /dev/null +++ b/libsrc/common/_oserror.s @@ -0,0 +1,14 @@ +; +; Ullrich von Bassewitz, 16.05.2000 +; +; extern unsigned char _oserror; +; /* Operating system specific errors from the low level functions */ + + + .export __oserror + +.bss + +__oserror: + .res 1 + diff --git a/libsrc/common/_printf.c b/libsrc/common/_printf.c new file mode 100644 index 000000000..5bb72e3d9 --- /dev/null +++ b/libsrc/common/_printf.c @@ -0,0 +1,253 @@ +/* + * Helper function for the printf family. + */ + + + +#include +#include +#include +#include +#include +#include "_printf.h" + + + +/* Use static variables for locals */ +#pragma staticlocals (1); + + + +int _printf (struct outdesc* d, char* f, va_list ap) +{ + outfunc fout; /* Output function */ + unsigned char type; /* variable argument type */ + char str [20]; /* string buffer */ + char c; /* Current format char */ + char leftjust; /* left justify string */ + char addsign; /* always add + or - */ + char addblank; /* add blank instead of + */ + char altform; /* alternate form? */ + char padchar; /* pad with space or zeroes? */ + char islong; /* l modifier found */ + unsigned arglen; /* length of argument string */ + unsigned prec; /* Precision */ + unsigned width; /* Width of output field */ + int i; /* Integer value */ + long l; /* Long value */ + char* sptr; /* pointer to argument string */ + register char* s; /* work pointer to argument string */ + + /* Remember the format string in a register variable for shorter code */ + register char* format = f; + + /* Remember the output function in a local variable for speed and size */ + fout = d->fout; + + /* */ + d->ccount = 0; + while (c = *format++) { + + if (c != '%') { + fout (d, &c, 1); + continue; + } + + /* %%? */ + if (*format == '%') { + fout (d, format, 1); + ++format; + continue; + } + + /* format is: %[flags][width][.precision][mod]type */ + + /* flags */ + leftjust = addsign = addblank = altform = 0; + do { + switch (c = *format) { + + case '-': + leftjust = 1; + break; + + case '+': + addsign = 1; + break; + + case '#': + altform = 1; + break; + + case ' ': + addblank = 1; + break; + + default: + goto flags_done; + + } + ++format; + } while (1); +flags_done: + + /* width */ + padchar = ' '; + if (*format == '0') { + padchar = '0'; + ++format; + } + if (*format == '*') { + width = va_arg (ap, int); + ++format; + } else { + width = 0; + while (isdigit (c = *format)) { + width = width * 10 + (c - '0'); + ++format; + } + } + + /* precision */ + prec = 0; + if (*format == '.') { + ++format; + if (*format == '*') { + prec = va_arg (ap, int); + ++format; + } else { + while (isdigit (c = *format)) { + prec = prec * 10 + (c - '0'); + ++format; + } + } + } + + /* modifiers */ + islong = 0; + while (strchr ("FNhlL", c = *format)) { + switch (c) { + + case 'l': + islong = 1; + break; + + } + ++format; + } + + /* Check the format specifier */ + sptr = s = str; + type = *format++; + switch (type) { + + case 'c': + str [0] = va_arg (ap, char); + str [1] = 0; + break; + + case 'd': + case 'i': + if (addsign) { + *s++ = '+'; + } else if (addblank) { + *s++ = ' '; + } + if (islong) { + ltoa (va_arg (ap, long), s, 10); + } else { + itoa (va_arg (ap, int), s, 10); + } + break; + + case 'n': + *va_arg (ap, int*) = d->ccount; + continue; + + case 'o': + if (islong) { + l = va_arg (ap, unsigned long); + if (altform && (l || prec)) { + *s++ = '0'; + } + ultoa (l, s, 8); + } else { + i = va_arg (ap, unsigned); + if (altform && (i || prec)) { + *s++ = '0'; + } + utoa (i, s, 8); + } + break; + + case 's': + sptr = va_arg (ap, char*); + break; + + case 'u': + if (islong) { + ultoa (va_arg (ap, unsigned long), str, 10); + } else { + utoa (va_arg (ap, unsigned), str, 10); + } + break; + + case 'x': + case 'X': + if (altform) { + *s++ = '0'; + *s++ = 'X'; + } + if (islong) { + ultoa (va_arg (ap, unsigned long), s, 16); + } else { + utoa (va_arg (ap, unsigned), s, 16); + } + if (type == 'x') { + strlower (str); + } + break; + + default: + /* Unknown type char - skip it */ + continue; + + } + + /* Do argument string formatting */ + arglen = strlen (sptr); + if (prec && prec < arglen) { + arglen = prec; + } + if (width > arglen) { + width -= arglen; /* padcount */ + } else { + width = 0; + } + + /* Do padding on the left side if needed */ + if (!leftjust) { + /* argument right justified */ + while (width) { + fout (d, &padchar, 1); + --width; + } + } + + /* Output the argument string */ + fout (d, sptr, arglen); + + /* Output right padding bytes if needed */ + if (leftjust) { + /* argument left justified */ + while (width) { + fout (d, &padchar, 1); + --width; + } + } + + } +} + + + diff --git a/libsrc/common/_printf.h b/libsrc/common/_printf.h new file mode 100644 index 000000000..d4d5ed607 --- /dev/null +++ b/libsrc/common/_printf.h @@ -0,0 +1,41 @@ +/* + * _printf.h + * + * (C) Copyright 1998 Ullrich von Bassewitz (uz@ibb.schwaben.com) + * + */ + + + +#ifndef __PRINTF_H +#define __PRINTF_H + + + +/* Forward */ +struct outdesc; + +/* Type of the function that is called to output data */ +typedef void (*outfunc) (struct outdesc* desc, char* buf, unsigned count); + + + +struct outdesc { + outfunc fout; /* Routine used to output data */ + int ccount; /* Character counter */ + void* ptr; /* Data internal to print routine */ + unsigned uns; /* Data internal to print routine */ +}; + + + +/* Internal formatting routine */ +int _printf (struct outdesc* d, char* format, va_list ap); + + + +/* End of _printf.h */ +#endif + + + diff --git a/libsrc/common/_stksize.s b/libsrc/common/_stksize.s new file mode 100644 index 000000000..9d8d6079d --- /dev/null +++ b/libsrc/common/_stksize.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 03.06.1998 +; +; Stack default size definition +; + + .export __stksize + +.data + +__stksize: + .word $800 ; 2K + + + diff --git a/libsrc/common/_swap.s b/libsrc/common/_swap.s new file mode 100644 index 000000000..7f3f98fa0 --- /dev/null +++ b/libsrc/common/_swap.s @@ -0,0 +1,62 @@ +; +; Ullrich von Bassewitz, 09.12.1998 +; +; void __fastcall__ _swap (void* p, void* q, size_t size); +; + + .export __swap + .import popax + .importzp ptr1, ptr2, ptr3 + + +__swap: sta ptr3 ; Save size + stx ptr3+1 + + jsr popax ; Get q + sta ptr2 + stx ptr2+1 + + jsr popax ; Get p + sta ptr1 + stx ptr1+1 + +; Prepare for swap + + ldy #$00 + +; Swap 256 byte blocks + + ldx ptr3+1 + beq @L2 + +@L1: lda (ptr1),y + tax + lda (ptr2),y + sta (ptr1),y + txa + sta (ptr2),y + iny + bne @L1 + dec ptr3+1 + bne @L1 + +; Swap remaining bytes (Y is zero) + +@L2: ldx ptr3 + beq @L9 + +@L3: lda (ptr1),y + tax + lda (ptr2),y + sta (ptr1),y + txa + sta (ptr2),y + iny + dec ptr3 + bne @L3 + +; Done + +@L9: rts + + diff --git a/libsrc/common/_sys.s b/libsrc/common/_sys.s new file mode 100644 index 000000000..c92c1d8d7 --- /dev/null +++ b/libsrc/common/_sys.s @@ -0,0 +1,72 @@ +; +; void __fastcall__ _sys (struct regs* r); +; +; Ullrich von Bassewitz, 16.12.1998 +; + + .export __sys + .import jmpvec + .importzp ptr1 + + +__sys: sta ptr1 + stx ptr1+1 ; Save the pointer to r + +; Fetch the PC and store it into the jump vector + + ldy #5 + lda (ptr1),y + sta jmpvec+2 + dey + lda (ptr1),y + sta jmpvec+1 + +; Get the flags, mask unnecessary bits and push them. Push a + + dey + lda (ptr1),y + and #%11001011 + pha + ldy #0 + lda (ptr1),y + pha + +; Get and assign X and Y + + iny + lda (ptr1),y + tay + iny + lda (ptr1),y + tay + +; Set a and the flags, call the machine code routine + + pla + plp + jsr jmpvec + +; Back from the routine. Save the flags and a + + php + pha + +; Put the register values into the regs structure + + tya + ldy #2 + sta (ptr1),y + dey + txa + sta (ptr1),y + dey + pla + sta (ptr1),y + ldy #3 + pla + sta (ptr1),y + +; Done + + rts + diff --git a/libsrc/common/abort.c b/libsrc/common/abort.c new file mode 100644 index 000000000..6af3a59d0 --- /dev/null +++ b/libsrc/common/abort.c @@ -0,0 +1,21 @@ +/* + * abort.c + * + * Ullrich von Bassewitz, 02.06.1998 + */ + + + +#include +#include + + + +void abort (void) +{ + fputs ("ABNORMAL PROGRAM TERMINATION\n", stderr); + exit (3); +} + + + diff --git a/libsrc/common/abs.s b/libsrc/common/abs.s new file mode 100644 index 000000000..89161ab59 --- /dev/null +++ b/libsrc/common/abs.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 17.06.1998 +; +; int abs (int x); +; + + .export _abs + .import negax + +_abs: dex + inx ; test hi byte + bpl L1 + jmp negax ; Negate if negative +L1: rts + + + diff --git a/libsrc/common/atexit.s b/libsrc/common/atexit.s new file mode 100644 index 000000000..5d94d36fe --- /dev/null +++ b/libsrc/common/atexit.s @@ -0,0 +1,69 @@ +; +; Ullrich von Bassewitz, 06.06.1998 +; +; int atexit (void (*f) (void)); +; + +; The exit functions + + .export _atexit, doatexit + .import __errno, jmpvec + +.bss +ecount: .byte 0 ; Really an index, inc'ed by 2 +efunc: .word 0,0,0,0,0 ; 5 exit functions +maxcount = * - efunc + + +.code + +_atexit: + ldy ecount + cpy #maxcount ; slot available? + beq E0 ; jump if no + +; Enter the function into the table + + sta efunc,y + iny + txa + sta efunc,y + iny + sty ecount + +; Done, return zero + + lda #0 + tax + rts + +; Error, no space left + +E0: lda #$FF + sta __errno ; Use -1 until codes are defined ### + sta __errno+1 + tax + rts + +; Function called from exit + +doatexit: + ldy ecount ; get index + beq L9 ; jump if done + dey + lda efunc,y + sta jmpvec+2 + dey + lda efunc,y + sta jmpvec+1 + sty ecount + ldy #0 ; number of function parms + jsr jmpvec + jmp doatexit ; next one + +L9: rts + + + + + diff --git a/libsrc/common/atoi.s b/libsrc/common/atoi.s new file mode 100644 index 000000000..cb2a72b48 --- /dev/null +++ b/libsrc/common/atoi.s @@ -0,0 +1,156 @@ +; +; Ullrich von Bassewitz, 05.06.1998 +; +; int atoi (const char* s); +; long atol (const char* s); +; + + .export _atoi, _atol + .import __ctype + .importzp sreg, ptr1, ptr2, tmp1 + +; +; Conversion routine (32 bit) +; + +_atoi: +_atol: sta ptr1 ; Store s + stx ptr1+1 + ldy #0 + sty ptr2 + sty ptr2+1 ; initial value (32 bit) + sty sreg + sty sreg+1 + +; Skip whitespace + +L1: lda (ptr1),y + tax + lda __ctype,x ; get character classification + and #$80 ; tab or space? + beq L2 ; jump if no + iny + bne L1 + inc ptr1+1 + bne L1 ; branch always + +; Check for a sign. The character is in X + +L2: txa ; get char + ldx #0 ; flag: positive + cmp #'+' ; ### portable? + beq L3 + cmp #'-' ; ### portable? + bne L5 + dex ; flag: negative +L3: iny + bne L5 + inc ptr1+1 + +; Store the sign flag and setup for conversion + +L5: stx tmp1 ; remember sign flag + +L6: lda (ptr1),y ; get next char + tax + lda __ctype,x ; get character classification + and #$04 ; digit? + beq L8 ; done + +; Multiply ptr2 (the converted value) by 10 + + jsr mul2 ; * 2 + + lda sreg+1 + pha + lda sreg + pha + lda ptr2+1 + pha + lda ptr2 + pha ; Save value + + jsr mul2 ; * 4 + jsr mul2 ; * 8 + + clc + pla + adc ptr2 + sta ptr2 + pla + adc ptr2+1 + sta ptr2+1 + pla + adc sreg + sta sreg + pla + adc sreg+1 + sta sreg+1 ; x*2 + x*8 = x*10 + +; Get the character back and add it + + txa ; get char back + sec + sbc #'0' ; make numeric value + clc + adc ptr2 + sta ptr2 + bcc L7 + inc ptr2+1 + bne L7 + inc sreg + bne L7 + inc sreg+1 + +; Next character + +L7: iny + bne L6 + inc ptr1+1 + bne L6 + +; Conversion done. Be shure to negate the value if needed. + +L8: lda ptr2 + ldx ptr2+1 + ldy tmp1 ; sign + beq L9 + +; Negate the 32 bit value in ptr2/sreg + + sec + lda ptr2 + eor #$FF + adc #0 + sta ptr2 + lda ptr2+1 + eor #$FF + adc #0 + sta ptr2+1 + lda sreg + eor #$FF + adc #0 + sta sreg + lda sreg+1 + eor #$FF + adc #0 + sta sreg+1 + +; Done, load the low 16 bit into A/X + +L9: lda ptr2 + ldx ptr2+1 ; get value + rts + +; +; Helper functions +; + +mul2: + asl ptr2 + rol ptr2+1 + rol sreg + rol sreg+1 ; * 2 + rts + + diff --git a/libsrc/common/bsearch.c b/libsrc/common/bsearch.c new file mode 100644 index 000000000..47cbc4e1c --- /dev/null +++ b/libsrc/common/bsearch.c @@ -0,0 +1,48 @@ +/* + * bsearch.c + * + * Ullrich von Bassewitz, 17.06.1998 + */ + + + +#include + + + +void* bsearch (void* key, void* base, size_t n, size_t size, int (*cmp) (void*, void*)) +{ + int current; + int result; + int found = 0; + int first = 0; + int last = n - 1; + + /* Binary search */ + while (first <= last) { + + /* Set current to mid of range */ + current = (last + first) / 2; + + /* Do a compare */ + result = cmp ((void*) (((int) base) + current*size), key); + if (result < 0) { + first = current + 1; + } else { + last = current - 1; + if (result == 0) { + /* Found one entry that matches the search key. However there may be + * more than one entry with the same key value and ANSI guarantees + * that we return the first of a row of items with the same key. + */ + found = 1; + } + } + } + + /* Did we find the entry? */ + return (void*) (found? ((int) base) + first*size : 0); +} + + + diff --git a/libsrc/common/calloc.c b/libsrc/common/calloc.c new file mode 100644 index 000000000..70b984938 --- /dev/null +++ b/libsrc/common/calloc.c @@ -0,0 +1,25 @@ +/* + * calloc.c + * + * Ullrich von Bassewitz, 06.06.1998 + */ + + + +#include +#include + + + +void* calloc (size_t count, size_t size) +{ + void* mem; + size *= count; + if (mem = malloc (size)) { + memset (mem, 0, size); + } + return mem; +} + + + diff --git a/libsrc/common/copydata.s b/libsrc/common/copydata.s new file mode 100644 index 000000000..b554540be --- /dev/null +++ b/libsrc/common/copydata.s @@ -0,0 +1,53 @@ +; +; Ullrich von Bassewitz, 07.12.1998 +; +; Copy the data segment from the LOAD to the RUN location +; + + .export copydata + .import __DATA_LOAD__, __DATA_RUN__, __DATA_SIZE__ + .importzp ptr1, ptr2 + + +copydata: + lda #<__DATA_LOAD__ ; Source pointer + sta ptr1 + lda #>__DATA_LOAD__ + sta ptr1+1 + + lda #<__DATA_RUN__ ; Target pointer + sta ptr2 + lda #>__DATA_RUN__ + sta ptr2+1 + + ldy #$00 + ldx #>__DATA_SIZE__ ; Get page count + beq @L2 ; No full pages + +; Copy full pages + +@L1: lda (ptr1),y + sta (ptr2),y + iny + bne @L1 + inc ptr1+1 + inc ptr2+2 ; Bump pointers + dex + bne @L1 + +; Copy last page (remember: y contains zero) + +@L2: ldx #<__DATA_SIZE__ ; Get remaining bytes + beq @L4 + +@L3: lda (ptr1),y + sta (ptr2),y + iny + dex + bne @L3 + +; Done + +@L4: rts + + diff --git a/libsrc/common/cprintf.c b/libsrc/common/cprintf.c new file mode 100644 index 000000000..a9d46ecbd --- /dev/null +++ b/libsrc/common/cprintf.c @@ -0,0 +1,26 @@ +/* + * cprintf.c + * + * Ullrich von Bassewitz. 11.08.1998 + */ + + + +#include +#include + + + +int cprintf (char* format, ...) +{ + va_list ap; + va_start (ap, format); + + /* Do formatting and output. Since we know, that va_end is empty, we don't + * call it here, saving an extra variable and some code. + */ + return vcprintf ((char*) va_fix (ap, 1), ap); +} + + + diff --git a/libsrc/common/errno.inc b/libsrc/common/errno.inc new file mode 100644 index 000000000..197a35574 --- /dev/null +++ b/libsrc/common/errno.inc @@ -0,0 +1,24 @@ +; +; Ullrich von Bassewitz, 16.05.2000 +; + +; Error codes, must match the values in the C headers + +ENOENT = 1 ; No such file or directory +ENOMEM = 2 ; Out of memory +EACCES = 3 ; Permission denied +ENODEV = 4 ; No such device +EMFILE = 5 ; Too many open files +EBUSY = 6 ; Device or resource busy +EINVAL = 7 ; Invalid argument +ENOSPC = 8 ; No space left on device +EEXIST = 9 ; File exists +EAGAIN = 10 ; Try again +EIO = 11 ; I/O error +EINTR = 12 ; Interrupted system call +ENOSYS = 13 ; Function not implemented +ESPIPE = 14 ; Illegal seek +EUNKNOWN = 15 ; Unknown OS specific error - must be last! + +EMAX = 15 ; Highest error code + diff --git a/libsrc/common/errno.s b/libsrc/common/errno.s new file mode 100644 index 000000000..1ab845397 --- /dev/null +++ b/libsrc/common/errno.s @@ -0,0 +1,33 @@ +; +; Ullrich von Bassewitz, 06.06.1998 +; +; int _errno; +; +; void _maperrno(void); +; /* Map an OS error to a system independent error code */ +; + + .export __maperrno + .export __errno + .import __oserror + .import __osmaperrno + + +.code + +__maperrno: + lda __oserror ; Get the error code + beq @L1 ; Jump if no error + ldx #$00 ; Clear error + stx __oserror + jsr __osmaperrno ; Map the code + sta __errno + stx __errno+1 +@L1: rts + + +.bss + +__errno: + .word 0 + diff --git a/libsrc/common/errormsg.c b/libsrc/common/errormsg.c new file mode 100644 index 000000000..4c15ac4cd --- /dev/null +++ b/libsrc/common/errormsg.c @@ -0,0 +1,33 @@ +/* + * errormsg.c + * + * Ullrich von Bassewitz, 17.05.2000 + * + * Must be a C function, since we have otherwise problems with the different + * character sets. + */ + + + +/* Place the following data into the readonly data segment */ +#pragma dataseg ("RODATA") + +const char* _sys_errlist[] = { + "Unknown error", /* 0 */ + "No such file or directory", /* 1 */ + "Out of memory", /* 2 */ + "Permission denied", /* 3 */ + "No such device", /* 4 */ + "Too many open files", /* 5 */ + "Device or resource busy", /* 6 */ + "Invalid argument", /* 7 */ + "No space left on device", /* 8 */ + "File exists", /* 9 */ + "Try again", /* 10 */ + "I/O error", /* 11 */ + "Interrupted system call", /* 12 */ + "Function not implemented", /* 13 */ + "Illegal seek", /* 14 */ +}; + + diff --git a/libsrc/common/fclose.c b/libsrc/common/fclose.c new file mode 100644 index 000000000..0ab2d422d --- /dev/null +++ b/libsrc/common/fclose.c @@ -0,0 +1,27 @@ +/* + * int fclose (FILE* f); + */ + + + +#include +#include +#include "_file.h" + + + +int fclose (FILE* f) +{ + if ((f->f_flags & _FOPEN) == 0) { + /* File is not open */ + _errno = EINVAL; /* File not input */ + return -1; + } + + /* Reset the flags and close the file */ + f->f_flags = _FCLOSED; + return close (f->f_fd); +} + + + diff --git a/libsrc/common/fdopen.c b/libsrc/common/fdopen.c new file mode 100644 index 000000000..896c98332 --- /dev/null +++ b/libsrc/common/fdopen.c @@ -0,0 +1,37 @@ +/* + * fopen.c + * + * Ullrich von Bassewitz, 17.06.1998 + */ + + + +#include +#include +#include +#include "_file.h" + + + +FILE* fdopen (int handle, char* /*mode*/) +{ + FILE* f; + + /* Find a free file slot */ + if (!(f = _fdesc ())) { + /* No slots */ + errno = EMFILE; /* Too many files */ + return 0; + } + + /* Insert the handle, and return the descriptor */ + f->f_fd = handle; + f->f_flags = _FOPEN; + + /* Return the file descriptor */ + return f; +} + + + + diff --git a/libsrc/common/fgetc.c b/libsrc/common/fgetc.c new file mode 100644 index 000000000..97e6dcb0b --- /dev/null +++ b/libsrc/common/fgetc.c @@ -0,0 +1,46 @@ +/* + * Ullrich von Bassewitz, 11.08.1998 + * + * int fgetc (FILE* f); + */ + + + +#include +#include +#include +#include "_file.h" + + + +int fgetc (FILE* f) +{ + char c; + + /* Check if the file is open or if there is an error condition */ + if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { + return -1; + } + + /* Read the byte */ + switch (read (f->f_fd, &c, 1)) { + + case -1: + /* Error */ + f->f_flags |= _FERROR; + return -1; + + case 0: + /* EOF */ + f->f_flags |= _FEOF; + return -1; + + default: + /* Char read */ + return ((int) c) & 0xFF; + + } +} + + + diff --git a/libsrc/common/fgets.c b/libsrc/common/fgets.c new file mode 100644 index 000000000..10c32bde2 --- /dev/null +++ b/libsrc/common/fgets.c @@ -0,0 +1,59 @@ +/* + * Ullrich von Bassewitz, 11.08.1998 + * + * char* fgets (char* s, int size, FILE* f); + */ + + + +#include +#include +#include "_file.h" + + + +char* fgets (char* s, unsigned size, FILE* f) +{ + int i, c; + + /* We do not handle the case "size == 0" here */ + i = 0; --size; + while (i < size) { + + /* Get next character */ + c = fgetc (f); + if (c == -1) { + s [i] = 0; + /* Error or EOF */ + if (f->f_flags & _FERROR) { + /* ERROR */ + return 0; + } else { + /* EOF */ + if (i) { + return s; + } else { + return 0; + } + } + } + + /* One char more */ + s [i++] = c; + + /* Stop at end of line */ + if (c == '\n') { + break; + } + } + + /* Replace newline by NUL */ + s [i-1] = '\0'; + + /* Done */ + return s; +} + + + + diff --git a/libsrc/common/fmisc.s b/libsrc/common/fmisc.s new file mode 100644 index 000000000..185a4ec9c --- /dev/null +++ b/libsrc/common/fmisc.s @@ -0,0 +1,81 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; Several small file stream functions +; + + .export _clearerr, _feof, _ferror, _fileno, _fflush + .import return0 + .import __errno + .importzp ptr1 + +; +; Get the FILE* parameter, check if the file is open +; + +getf: sta ptr1 + stx ptr1+1 + ldy #1 + lda (ptr1),y ; get f->f_flags + and #$01 ; file open? + beq @L1 ; jump if no + clc ; ok + rts +@L1: sec + rts + +; +; void clearerr (FILE* f); +; + +_clearerr: + jsr getf + bcs err + lda (ptr1),y + and #$F9 + sta (ptr1),y +err: rts + +; +; int feof (FILE* f); +; + +_feof: + jsr getf +; bcs err + lda (ptr1),y + and #$02 + ldx #0 + rts + +; +; int ferror (FILE* f); +; + +_ferror: + jsr getf +; bcs err + lda (ptr1),y + and #$04 + ldx #0 + rts + +; +; int fileno (FILE* f); +; + +_fileno: + jsr getf +; bcs err + dey + lda (ptr1),y + ldx #0 + rts + +; +; int __fastcall__ fflush (FILE* f); +; + +_fflush = return0 + + diff --git a/libsrc/common/fmode.inc b/libsrc/common/fmode.inc new file mode 100644 index 000000000..2ec109055 --- /dev/null +++ b/libsrc/common/fmode.inc @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 05.06.1999 +; + +; File mode constants, must match the values in the C headers + + +O_RDONLY = $01 +O_WRONLY = $02 +O_RDWR = $03 +O_CREAT = $04 +O_TRUNC = $10 +O_APPEND = $20 + + diff --git a/libsrc/common/fopen.c b/libsrc/common/fopen.c new file mode 100644 index 000000000..f5c36527d --- /dev/null +++ b/libsrc/common/fopen.c @@ -0,0 +1,32 @@ +/* + * fopen.c + * + * Ullrich von Bassewitz, 17.06.1998 + */ + + + +#include +#include +#include +#include "_file.h" + + + +FILE* fopen (const char* name, const char* mode) +{ + FILE* f; + + /* Find a free file slot */ + if (!(f = _fdesc ())) { + /* No slots */ + _errno = EMFILE; /* Too many files */ + return 0; + } + + /* Open the file and return the descriptor */ + return _fopen (name, mode, f); +} + + + diff --git a/libsrc/common/fprintf.c b/libsrc/common/fprintf.c new file mode 100644 index 000000000..afa111f79 --- /dev/null +++ b/libsrc/common/fprintf.c @@ -0,0 +1,26 @@ +/* + * fprintf.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include + + + +int fprintf (FILE* /*F*/, char* format, ...) +{ + va_list ap; + va_start (ap, format); + + /* Do formatting and output. Since we know, that va_end is empty, we don't + * call it here, saving an extra variable and some code. + */ + return vfprintf ((FILE*) va_fix (ap, 1), (char*) va_fix (ap, 2), ap); +} + + + diff --git a/libsrc/common/fputc.c b/libsrc/common/fputc.c new file mode 100644 index 000000000..012cc052d --- /dev/null +++ b/libsrc/common/fputc.c @@ -0,0 +1,34 @@ +/* + * fputc.c + * + * Ullrich von Bassewitz, 02.06.1998 + */ + + + +#include +#include +#include "_file.h" + + + +int fputc (int c, FILE* f) +{ + /* Check if the file is open or if there is an error condition */ + if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { + return -1; + } + + /* Write the byte (knows about byte order!) */ + if (write (f->f_fd, &c, 1) <= 0) { + /* Error */ + f->f_flags |= _FERROR; + return -1; + } + + /* Return the byte written */ + return c & 0xFF; +} + + + diff --git a/libsrc/common/fputs.c b/libsrc/common/fputs.c new file mode 100644 index 000000000..f4dcbbf9d --- /dev/null +++ b/libsrc/common/fputs.c @@ -0,0 +1,28 @@ +/* + * int fputs (const char* s, FILE* f); + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include +#include +#include "_file.h" + + + +int fputs (char* s, FILE* f) +{ + /* Check if the file is open or if there is an error condition */ + if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { + return -1; + } + + /* Write the string */ + return write (f->f_fd, s, strlen (s)); +} + + + diff --git a/libsrc/common/fread.c b/libsrc/common/fread.c new file mode 100644 index 000000000..a8b402af3 --- /dev/null +++ b/libsrc/common/fread.c @@ -0,0 +1,61 @@ +/* + * fread.c + * + * Ullrich von Bassewitz, 02.06.1998 + */ + + + +#include +#include +#include +#include "_file.h" + + + +size_t fread (void* buf, size_t size, size_t count, FILE* f) +{ + int bytes; + + /* Is the file open? */ + if ((f->f_flags & _FOPEN) == 0) { + _errno = EINVAL; /* File not open */ + return (size_t) -1; + } + + /* Did we have an error or EOF? */ + if ((f->f_flags & (_FERROR | _FEOF)) != 0) { + /* Cannot read from stream */ + return 0; + } + + /* How many bytes to read? */ + bytes = size * count; + + if (bytes) { + /* Read the data. */ + bytes = read (f->f_fd, buf, bytes); + if (bytes == -1) { + /* Read error */ + f->f_flags |= _FERROR; + return (size_t) -1; + } + if (bytes == 0) { + /* End of file */ + f->f_flags |= _FEOF; + return (size_t) -1; + } + + /* Unfortunately, we cannot avoid the divide here... */ + return bytes / size; + + } else { + + /* 0 bytes read */ + return count; + + } +} + + + diff --git a/libsrc/common/free.c b/libsrc/common/free.c new file mode 100644 index 000000000..686f4600a --- /dev/null +++ b/libsrc/common/free.c @@ -0,0 +1,65 @@ +/* + * free.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include "_heap.h" + + + +void free (void* block) +/* Release an allocated memory block. The function will accept NULL pointers + * (and do nothing in this case). + */ +{ + unsigned* b; + unsigned size; + struct freeblock* f; + + + /* Allow NULL arguments */ + if (block == 0) { + return; + } + + /* Get a pointer to the real memory block, then get the size */ + b = (unsigned*) block; + size = *--b; + + /* Check if the block is at the top of the heap */ + if (((int) b) + size == (int) _hptr) { + + /* Decrease _hptr to release the block */ + _hptr = (unsigned*) (((int) _hptr) - size); + + /* Check if the last block in the freelist is now at heap top. If so, + * remove this block from the freelist. + */ + if (f = _hlast) { + if (((int) f) + f->size == (int) _hptr) { + /* Remove the last block */ + _hptr = (unsigned*) (((int) _hptr) - f->size); + if (_hlast = f->prev) { + /* Block before is now last block */ + f->prev->next = 0; + } else { + /* The freelist is empty now */ + _hfirst = 0; + } + } + } + + } else { + + /* Not at heap top, enter the block into the free list */ + _hadd (b, size); + + } +} + + + diff --git a/libsrc/common/freopen.c b/libsrc/common/freopen.c new file mode 100644 index 000000000..bbad5b326 --- /dev/null +++ b/libsrc/common/freopen.c @@ -0,0 +1,38 @@ +/* + * freopen.c + * + * Ullrich von Bassewitz, 17.06.1998 + */ + + + +#include +#include +#include +#include "_file.h" + + + +FILE* freopen (char* name, char* mode, FILE* f) +{ + /* Check if the file is open, if so, close it */ + if ((f->f_flags & _FOPEN) == 0) { + /* File is not open */ + _errno = EINVAL; /* File not input */ + return 0; + } + + /* Close the file. Don't bother setting the flag, it will get + * overwritten by _fopen. + */ + if (close (f->f_fd) < 0) { + /* An error occured, _oserror is set */ + return 0; + } + + /* Open the file and return the descriptor */ + return _fopen (name, mode, f); +} + + + diff --git a/libsrc/common/fwrite.c b/libsrc/common/fwrite.c new file mode 100644 index 000000000..7978d2d2c --- /dev/null +++ b/libsrc/common/fwrite.c @@ -0,0 +1,51 @@ +/* + * fwrite.c + * + * Ullrich von Bassewitz, 04.06.1998 + */ + + + +#include +#include +#include +#include "_file.h" + + + +size_t fwrite (void* buf, size_t size, size_t count, FILE* f) +{ + int bytes; + + /* Is the file open? */ + if ((f->f_flags & _FOPEN) == 0) { + _errno = EINVAL; /* File not open */ + return -1; + } + + /* Did we have an error */ + if ((f->f_flags & _FERROR) != 0) { + /* Cannot write to stream */ + return 0; + } + + /* How many bytes to write? */ + bytes = size * count; + + if (bytes) { + /* Write the data. */ + if (write (f->f_fd, buf, bytes) == -1) { + /* Write error */ + f->f_flags |= _FERROR; + return -1; + } + } + + /* Don't waste time with expensive calculations, assume the write was + * complete and return the count of items. + */ + return count; +} + + + diff --git a/libsrc/common/getchar.c b/libsrc/common/getchar.c new file mode 100644 index 000000000..a058ab022 --- /dev/null +++ b/libsrc/common/getchar.c @@ -0,0 +1,22 @@ +/* + * getchar.c + * + * Ullrich von Bassewitz, 11.12.1998 + */ + + + +#include + +/* This is usually declared as a macro */ +#undef getchar + + + +int getchar (void) +{ + return fgetc (stdin); +} + + + diff --git a/libsrc/common/getcpu.s b/libsrc/common/getcpu.s new file mode 100644 index 000000000..a65aac376 --- /dev/null +++ b/libsrc/common/getcpu.s @@ -0,0 +1,37 @@ +; +; Ullrich von Bassewitz, 02.04.1999 +; +; unsigned char getcpu (void); +; + + .export _getcpu + +; --------------------------------------------------------------------------- +; Subroutine to detect an 816. Returns +; +; - carry clear and 0 in A for a NMOS 6502 CPU +; - carry set and 1 in A for some CMOS 6502 CPU +; - carry set and 2 in A for a 65816 +; +; This function uses a $1A opcode which is a INA on the 816 and ignored +; (interpreted as a NOP) on a NMOS 6502. There are several CMOS versions +; of the 6502, but all of them interpret unknown opcodes as NOP so this is +; just what we want. + +.p816 ; Enable 65816 instructions + +_getcpu: + lda #0 + inc a ; .byte $1A + cmp #1 + bcc @L9 + +; This is at least a 65C02, check for a 65816 + + xba ; .byte $eb, put $01 in B accu + dec a ; .byte $3a, A=$00 if 65C02 + xba ; .byte $eb, get $01 back if 65816 + inc a ; .byte $1a, make $01/$02 +@L9: ldx #0 ; Load high byte of word + rts + diff --git a/libsrc/common/gets.c b/libsrc/common/gets.c new file mode 100644 index 000000000..4177522d4 --- /dev/null +++ b/libsrc/common/gets.c @@ -0,0 +1,52 @@ +/* + * gets.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include "_file.h" + + + +char* gets (char* s) +{ + int i, c; + + i = 0; + do { + + /* Get next character */ + c = fgetc (stdin); + if (c == -1) { + /* Error or EOF */ + s [i] = 0; + if (stdin->f_flags & _FERROR) { + /* ERROR */ + return 0; + } else { + /* EOF */ + if (i) { + return s; + } else { + return 0; + } + } + } + + /* One char more */ + s [i++] = c; + + } while (c != '\n'); + + /* Replace newline by NUL */ + s [i-1] = '\0'; + + /* Done */ + return s; +} + + + diff --git a/libsrc/common/isalnum.s b/libsrc/common/isalnum.s new file mode 100644 index 000000000..e8654ad9f --- /dev/null +++ b/libsrc/common/isalnum.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int isalnum (int c); +; + + .export _isalnum + .import __ctype + +_isalnum: + tay + lda __ctype,y ; Get character classification + and #$07 ; Mask character/digit bits + rts + diff --git a/libsrc/common/isalpha.s b/libsrc/common/isalpha.s new file mode 100644 index 000000000..420d6b3dc --- /dev/null +++ b/libsrc/common/isalpha.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int isalpha (int c); +; + + .export _isalpha + .import __ctype + +_isalpha: + tay + lda __ctype,y ; Get character classification + and #$03 ; Mask character bits + rts + diff --git a/libsrc/common/isblank.s b/libsrc/common/isblank.s new file mode 100644 index 000000000..f3564ec63 --- /dev/null +++ b/libsrc/common/isblank.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int isblank (int c); +; +; cc65 (and GNU) extension. +; + + .export _isblank + .import __ctype + +_isblank: + tay + lda __ctype,y ; Get character classification + and #$80 ; Mask blank bit + rts + diff --git a/libsrc/common/iscntrl.s b/libsrc/common/iscntrl.s new file mode 100644 index 000000000..6d29cb586 --- /dev/null +++ b/libsrc/common/iscntrl.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int iscntrl (int c); +; + + .export _iscntrl + .import __ctype + +_iscntrl: + tay + lda __ctype,y ; Get character classification + and #$10 ; Mask control character bit + rts + + diff --git a/libsrc/common/isdigit.s b/libsrc/common/isdigit.s new file mode 100644 index 000000000..d8cc6d522 --- /dev/null +++ b/libsrc/common/isdigit.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int isdigit (int c); +; + + .export _isdigit + .import __ctype + +_isdigit: + tay + lda __ctype,y ; Get character classification + and #$04 ; Mask digit bit + rts + diff --git a/libsrc/common/isgraph.s b/libsrc/common/isgraph.s new file mode 100644 index 000000000..91f482c56 --- /dev/null +++ b/libsrc/common/isgraph.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int isgraph (int c); +; + + .export _isgraph + .import __ctype + +_isgraph: + tay + lda __ctype,y ; Get character classification + eor #$30 ; NOT control and NOT space + and #$30 ; Mask character bits + rts + diff --git a/libsrc/common/islower.s b/libsrc/common/islower.s new file mode 100644 index 000000000..565f0f853 --- /dev/null +++ b/libsrc/common/islower.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int islower (int c); +; + + .export _islower + .import __ctype + +_islower: + tay + lda __ctype,y ; Get character classification + and #$01 ; Mask lower char bit + rts + diff --git a/libsrc/common/isprint.s b/libsrc/common/isprint.s new file mode 100644 index 000000000..e82e61312 --- /dev/null +++ b/libsrc/common/isprint.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int isprint (int c); +; + + .export _isprint + .import __ctype + +_isprint: + tay + lda __ctype,y ; Get character classification + eor #$10 ; NOT a control char + and #$10 ; Mask control char bit + rts + diff --git a/libsrc/common/ispunct.s b/libsrc/common/ispunct.s new file mode 100644 index 000000000..71664f0d8 --- /dev/null +++ b/libsrc/common/ispunct.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int ispunct (int c); +; + + .export _ispunct + .import __ctype + +_ispunct: + tay + lda __ctype,y ; Get character classification + eor #$37 ; NOT (space | control | digit | char) + and #$37 ; Mask relevant bits + rts + diff --git a/libsrc/common/isspace.s b/libsrc/common/isspace.s new file mode 100644 index 000000000..1c6cc2e8a --- /dev/null +++ b/libsrc/common/isspace.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int isspace (int c); +; + + .export _isspace + .import __ctype + +_isspace: + tay + lda __ctype,y ; Get character classification + and #$60 ; Mask space bits + rts + diff --git a/libsrc/common/isupper.s b/libsrc/common/isupper.s new file mode 100644 index 000000000..bb5ad07a2 --- /dev/null +++ b/libsrc/common/isupper.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int isupper (int c); +; + + .export _isupper + .import __ctype + +_isupper: + tay + lda __ctype,y ; Get character classification + and #$02 ; Mask upper char bit + rts + diff --git a/libsrc/common/isxdigit.s b/libsrc/common/isxdigit.s new file mode 100644 index 000000000..a3184d17b --- /dev/null +++ b/libsrc/common/isxdigit.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int isxdigit (int c); +; + + .export _isxdigit + .import __ctype + +_isxdigit: + tay + lda __ctype,y ; Get character classification + and #$08 ; Mask xdigit bit + rts + diff --git a/libsrc/common/itoa.s b/libsrc/common/itoa.s new file mode 100644 index 000000000..37544c434 --- /dev/null +++ b/libsrc/common/itoa.s @@ -0,0 +1,146 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; char* itoa (int value, char* s, int radix); +; char* utoa (unsigned value, char* s, int radix); +; + + .export _itoa, _utoa + .import addysp1 + .import __hextab + .importzp sp, sreg, ptr2, ptr3, tmp1 + +.rodata +specval: + .byte '-', '3', '2', '7', '6', '8', 0 +.code + +; +; Common subroutine to pop the parameters and put them into core +; + +dopop: sta tmp1 ; will loose high byte + ldy #0 + lda (sp),y + sta ptr2 + sta ptr3 + iny + lda (sp),y + sta ptr2+1 + sta ptr3+1 + iny + lda (sp),y + sta sreg + iny + lda (sp),y + sta sreg+1 + jmp addysp1 ; Bump stack pointer + +; +; itoa +; + +_itoa: jsr dopop ; pop the arguments + +; We must handle $8000 in a special way, since it is the only negative +; number that has no positive 16-bit counterpart + + ldy tmp1 ; get radix + cpy #10 + bne utoa + cmp #$00 + bne L2 + cpx #$80 + bne L2 + + ldy #6 +L1: lda specval,y ; copy -32768 + sta (ptr2),y + dey + bpl L1 + jmp L10 + +; Check if the value is negative. If so, write a - sign and negate the +; number. + +L2: lda sreg+1 ; get high byte + bpl utoa + lda #'-' + ldy #0 + sta (ptr2),y ; store sign + inc ptr2 + bne L3 + inc ptr2+1 + +L3: lda sreg + eor #$FF + clc + adc #$01 + sta sreg + lda sreg+1 + eor #$FF + adc #$00 + sta sreg+1 + jmp utoa + +; +; utoa +; + +_utoa: jsr dopop ; pop the arguments + +; Convert to string by dividing and push the result onto the stack + +utoa: lda #$00 + pha ; sentinel + +; Divide sreg/tmp1 -> sreg, remainder in a + +L5: ldy #16 ; 16 bit + lda #0 ; remainder +L6: asl sreg + rol sreg+1 + rol a + cmp tmp1 + bcc L7 + sbc tmp1 + inc sreg +L7: dey + bne L6 + + tay ; get remainder into y + lda __hextab,y ; get hex character + pha ; save char value on stack + + lda sreg + ora sreg+1 + bne L5 + +; Get the characters from the stack into the string + + ldy #0 +L9: pla + sta (ptr2),y + beq L10 ; jump if sentinel + iny + bne L9 ; jump always + +; Done! Return the target string + +L10: lda ptr3 + ldx ptr3+1 + rts + + + + + + + + + + + + + + diff --git a/libsrc/common/jmpvec.s b/libsrc/common/jmpvec.s new file mode 100644 index 000000000..d9631583e --- /dev/null +++ b/libsrc/common/jmpvec.s @@ -0,0 +1,14 @@ +; +; General purpose jump vector in the data segment that is patched at +; runtime and used by several routines. +; +; Ullrich von Bassewitz, 16.12.1998 +; + + .export jmpvec + + +.data + +jmpvec: jmp $FFFF + diff --git a/libsrc/common/labs.s b/libsrc/common/labs.s new file mode 100644 index 000000000..d73617b95 --- /dev/null +++ b/libsrc/common/labs.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 17.06.1998 +; +; long labs (long x); +; + + .export _labs + .import negeax, tsteax + .importzp sreg + +_labs: ldy sreg+1 ; test hi byte + bpl L1 + jmp negeax ; Negate if negative +L1: rts + diff --git a/libsrc/common/locale.c b/libsrc/common/locale.c new file mode 100644 index 000000000..ad5fe1c23 --- /dev/null +++ b/libsrc/common/locale.c @@ -0,0 +1,78 @@ +/* + * locale.c + * + * Ullrich von Bassewitz, 11.12.1998 + */ + + + +#include +#include + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Data in this module is read-only, put it into the RODATA segment */ +#pragma dataseg ("RODATA"); + +/* For memory efficiency use a separate empty string */ +static const char EmptyString [] = ""; + +static struct lconv lc = { + EmptyString, /* currency_symbol */ + ".", /* decimal_point */ + EmptyString, /* grouping */ + EmptyString, /* int_curr_symbol */ + EmptyString, /* mon_decimal_point */ + EmptyString, /* mon_grouping */ + EmptyString, /* mon_thousands_sep */ + EmptyString, /* negative_sign */ + EmptyString, /* positive_sign */ + EmptyString, /* thousands_sep */ + CHAR_MAX, /* frac_digits */ + CHAR_MAX, /* int_frac_digits */ + CHAR_MAX, /* n_cs_precedes */ + CHAR_MAX, /* n_sep_by_space */ + CHAR_MAX, /* n_sign_posn */ + CHAR_MAX, /* p_cs_precedes */ + CHAR_MAX, /* p_sep_by_space */ + CHAR_MAX, /* p_sign_posn */ +}; + +/* Restore the old data segment name */ +#pragma dataseg ("DATA"); + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +struct lconv* localeconv (void) +{ + return &lc; +} + + + +char* setlocale (int, const char* locale) +{ + if (locale == 0 || (locale [0] == 'C' && locale [1] == '\0') || locale [0] == '\0') { + /* No change, or value already set, our locale is the "C" locale */ + return "C"; + } else { + /* Cannot set this one */ + return 0; + } +} + + + + diff --git a/libsrc/common/longjmp.s b/libsrc/common/longjmp.s new file mode 100644 index 000000000..6f74f7331 --- /dev/null +++ b/libsrc/common/longjmp.s @@ -0,0 +1,48 @@ +; +; Ullrich von Bassewitz, 06.06.1998 +; +; void longjmp (jmp_buf buf, int retval); +; + + .export _longjmp + .import popax + .importzp sp, ptr1, ptr2 + +_longjmp: + sta ptr2 ; Save retval + stx ptr2+1 + jsr popax ; get buf + sta ptr1 + stx ptr1+1 + ldy #0 + +; Get the old parameter stack + + lda (ptr1),y + iny + sta sp + lda (ptr1),y + iny + sta sp+1 + +; Get the old stack pointer + + lda (ptr1),y + iny + tax + txs + +; Get the return address and push it on the stack + + lda (ptr1),y + iny + pha + lda (ptr1),y + pha + +; Load the return value and return to the caller + + lda ptr2 + ldx ptr2+1 + rts + diff --git a/libsrc/common/ltoa.s b/libsrc/common/ltoa.s new file mode 100644 index 000000000..c0a342056 --- /dev/null +++ b/libsrc/common/ltoa.s @@ -0,0 +1,160 @@ +; +; Ullrich von Bassewitz, 11.06.1998 +; +; char* ltoa (long value, char* s, int radix); +; char* ultoa (unsigned long value, char* s, int radix); +; + + .export _ltoa, _ultoa + .import popax + .import __hextab + .importzp sreg, ptr1, ptr2, ptr3, tmp1 + + + +.rodata +specval: + .byte '-', '2', '1', '4', '7', '4', '8', '3', '6', '4', '8', 0 +.code + +; +; Common subroutine to pop the parameters and put them into core +; + +dopop: sta tmp1 ; will loose high byte + jsr popax ; get s + sta ptr1 + stx ptr1+1 + sta sreg ; save for return + stx sreg+1 + jsr popax ; get low word of value + sta ptr2 + stx ptr2+1 + jsr popax ; get high word of value + sta ptr3 + stx ptr3+1 + rts + +; +; ltoa +; + +_ltoa: jsr dopop ; pop the arguments + +; We must handle $80000000 in a special way, since it is the only negative +; number that has no positive 32-bit counterpart + + ldx ptr3+1 ; get high byte + ldy tmp1 ; get radix + cpy #10 + bne ultoa + lda ptr3 + ora ptr2+1 + ora ptr2 + bne L2 + cpx #$80 + bne L2 + + ldy #11 +L1: lda specval,y ; copy -2147483648 + sta (ptr1),y + dey + bpl L1 + jmp L10 + +; Check if the value is negative. If so, write a - sign and negate the +; number. + +L2: txa ; get high byte + bpl ultoa + lda #'-' + ldy #0 + sta (ptr1),y ; store sign + inc ptr1 + bne L3 + inc ptr1+1 + +L3: lda ptr2 ; negate val + eor #$FF + clc + adc #$01 + sta ptr2 + lda ptr2+1 + eor #$FF + adc #$00 + sta ptr2+1 + lda ptr3 + eor #$FF + adc #$00 + sta ptr3 + lda ptr3+1 + eor #$FF + adc #$00 + sta ptr3+1 + jmp ultoa + +; +; utoa +; + +_ultoa: jsr dopop ; pop the arguments + +; Convert to string by dividing and push the result onto the stack + +ultoa: lda #$00 + pha ; sentinel + +; Divide val/tmp1 -> val, remainder in a + +L5: ldy #32 ; 32 bit + lda #0 ; remainder +L6: asl ptr2 + rol ptr2+1 + rol ptr3 + rol ptr3+1 + rol a + cmp tmp1 + bcc L7 + sbc tmp1 + inc ptr2 +L7: dey + bne L6 + + tay ; get remainder into y + lda __hextab,y ; get hex character + pha ; save char value on stack + + lda ptr2 + ora ptr2+1 + ora ptr3 + ora ptr3+1 + bne L5 + +; Get the characters from the stack into the string + + ldy #0 +L9: pla + sta (ptr1),y + beq L10 ; jump if sentinel + iny + bne L9 ; jump always + +; Done! Return the target string + +L10: lda sreg + ldx sreg+1 + rts + + + + + + + + + + + + + + diff --git a/libsrc/common/malloc.c b/libsrc/common/malloc.c new file mode 100644 index 000000000..d7722d595 --- /dev/null +++ b/libsrc/common/malloc.c @@ -0,0 +1,125 @@ +/* + * malloc.c + * + * Ullrich von Bassewitz, 03.06.1998 + */ + + + +#include +#include "_heap.h" + + + +void* malloc (size_t size) +/* Allocate memory from the given heap. The function returns a pointer to the + * allocated memory block or a NULL pointer if not enough memory is available. + * Allocating a zero size block is not allowed. + */ +{ + struct freeblock* f; + unsigned* p; + + + /* Check for a size of zero, then add the administration space and round + * up the size if needed. + */ + if (size == 0) { + return 0; + } + size += HEAP_ADMIN_SPACE; + if (size < sizeof (struct freeblock)) { + size = sizeof (struct freeblock); + } + + /* Search the freelist for a block that is big enough */ + f = _hfirst; + while (f && f->size < size) { + f = f->next; + } + + /* Did we find one? */ + if (f) { + + /* We found a block big enough. If the block can hold just the + * requested size, use the block in full. Beware: When slicing blocks, + * there must be space enough to create a new one! If this is not the + * case, then use the complete block. + */ + if (f->size - size < sizeof (struct freeblock)) { + + /* Use the actual size */ + size = f->size; + + /* Remove the block from the free list */ + if (f->prev) { + /* We have a previous block */ + f->prev->next = f->next; + } else { + /* This is the first block, correct the freelist pointer */ + _hfirst = f->next; + } + if (f->next) { + /* We have a next block */ + f->next->prev = f->prev; + } else { + /* This is the last block, correct the freelist pointer */ + _hlast = f->prev; + } + + } else { + + /* We must slice the block found */ + struct freeblock* newblock; + newblock = (struct freeblock*) ((unsigned) f) + size; + + /* Insert the new block (the remaining space) instead of the + * old one. + */ + newblock->size = f->size - size; /* Remaining size */ + newblock->next = f->next; + newblock->prev = f->prev; + if (f->prev) { + /* We have a previous block */ + f->prev->next = newblock; + } else { + /* This is the first block, correct the freelist pointer */ + _hfirst = newblock; + } + if (f->next) { + /* We have a next block */ + f->next->prev = newblock; + } else { + /* This is the last block, correct the freelist pointer */ + _hlast = newblock; + } + + } + + /* Setup the pointer for the bock */ + p = (unsigned*) f; + + } else { + + /* We did not find a block big enough. Try to use new space from the + * heap top. + */ + if (((unsigned) _hend) - ((unsigned) _hptr) < size) { + /* Out of heap space */ + return 0; + } + + + /* There is enough space left, take it from the heap top */ + p = _hptr; + _hptr = (unsigned*) (((unsigned) _hptr) + size); + + } + + /* New block is now in p. Fill in the size and return the user pointer */ + *p++ = size; + return p; +} + + + diff --git a/libsrc/common/memchr.s b/libsrc/common/memchr.s new file mode 100644 index 000000000..59186ae45 --- /dev/null +++ b/libsrc/common/memchr.s @@ -0,0 +1,57 @@ +; +; Ullrich von Bassewitz, 09.06.1998 +; +; void* memchr (const void* p, int c, size_t n); +; + + .export _memchr + .import popax, return0 + .importzp ptr1, ptr2, tmp1 + + +_memchr: + sta ptr2 ; Save n + stx ptr2+1 + jsr popax ; get c + sta tmp1 + jsr popax ; get p + sta ptr1 + stx ptr1+1 + + ldy #0 + lda tmp1 ; get c + ldx ptr2 ; use X as low counter byte + beq L3 ; check high byte + +; Search for the char + +L1: cmp (ptr1),y + beq L5 ; jump if found + iny + bne L2 + inc ptr1+1 +L2: cpx #0 + beq L3 + dex + jmp L1 +L3: ldx ptr2+1 ; Check high byte + beq L4 ; Jump if counter off + dec ptr2+1 + ldx #$FF + bne L1 ; branch always + +; Character not found, return zero + +L4: jmp return0 + +; Character found, calculate pointer + +L5: ldx ptr1+1 ; get high byte of pointer + tya ; low byte offset + clc + adc ptr1 + bcc L6 + inx +L6: rts + + diff --git a/libsrc/common/memcmp.s b/libsrc/common/memcmp.s new file mode 100644 index 000000000..4647c3176 --- /dev/null +++ b/libsrc/common/memcmp.s @@ -0,0 +1,54 @@ +; +; Ullrich von Bassewitz, 09.06.1998 +; +; int memcmp (const void* p1, const void* p2, size_t count); +; + + .export _memcmp + .import popax, return0 + .importzp ptr1, ptr2, ptr3 + +_memcmp: + sta ptr3 ; Save count + sta ptr3+1 + jsr popax ; get p2 + sta ptr2 + stx ptr2+1 + jsr popax ; get p1 + sta ptr1 + stx ptr1+1 + + ldy #0 + ldx ptr3 ; use X as low counter byte + beq L3 + +L1: lda (ptr1),y + cmp (ptr2),y + bne L5 + iny + bne L2 + inc ptr1+1 + inc ptr2+1 +L2: txa + beq L3 + dex + jmp L1 +L3: lda ptr3+1 ; check high byte + beq L4 + dec ptr3+1 + dex ; X = $FF + bne L1 ; branch always + +; Memory areas are equal + +L4: jmp return0 + +; Not equal, check which one is greater + +L5: bcs L6 + ldx #$FF + rts + +L6: ldx #$01 + rts + diff --git a/libsrc/common/memcpy.s b/libsrc/common/memcpy.s new file mode 100644 index 000000000..4ef141df0 --- /dev/null +++ b/libsrc/common/memcpy.s @@ -0,0 +1,113 @@ +; +; void* memcpy (void* dest, const void* src, size_t n); +; void* memmove (void* dest, const void* src, size_t n); +; +; Ullrich von Bassewitz, 10.12.1998 +; + + .export _memcpy, _memmove + .import popax + .importzp ptr1, ptr2, ptr3, tmp1, tmp2 + +; ---------------------------------------------------------------------- +_memcpy: + jsr getparms ; Get the parameters from stack + +; Copy upwards + +copyup: ldy #0 ; set up to move 256 + ldx tmp2 ; hi byte of n + beq @L2 + +@L1: lda (ptr1),y ; get a byte + sta (ptr2),y ; store it + iny + bne @L1 + inc ptr1+1 ; bump ptrs + inc ptr2+1 + dex + bne @L1 ; do another block + +@L2: ldx tmp1 ; get low byte of n + beq done ; jump if done + +@L3: lda (ptr1),y ; get a byte + sta (ptr2),y ; store it + iny + dex + bne @L3 + +done: lda ptr3 + ldx ptr3+1 ; get function result (dest) + rts + + +; ---------------------------------------------------------------------- +_memmove: + jsr getparms ; Get the parameters from stack + + cpx ptr1+1 ; dest > src? + bne @L1 + cmp ptr1 +@L1: beq done ; Both pointers are equal - nothing to copy + bcc copyup ; Copy upwards + +; Copy downwards + + clc + lda ptr1+1 + adc tmp2 + sta ptr1+1 + + clc + lda ptr2+1 + adc tmp2 + sta ptr2+1 + +; Copy the incomplete page + + ldy tmp1 ; Get low byte of count + beq @L3 + +@L2: dey + lda (ptr1),y + sta (ptr2),y + tya ; Test Y + bne @L2 ; Jump if not zero + +; Copy complete pages + +@L3: ldx tmp2 ; Get hi byte of count + beq done + +@L4: dec ptr1+1 + dec ptr2+1 +@L5: dey + lda (ptr1),y + sta (ptr2),y + tya + bne @L5 + dex + bne @L4 + +; Done + + beq done + +; ---------------------------------------------------------------------- +; Get the parameters from stack + +getparms: + sta tmp1 ; Save n + stx tmp2 + jsr popax ; src + sta ptr1 + stx ptr1+1 + jsr popax ; dest + sta ptr2 + stx ptr2+1 ; save work copy + sta ptr3 + stx ptr3+1 ; save function result + rts + + diff --git a/libsrc/common/memset.s b/libsrc/common/memset.s new file mode 100644 index 000000000..18fc9966e --- /dev/null +++ b/libsrc/common/memset.s @@ -0,0 +1,52 @@ +; +; void* memset (void* ptr, int c, size_t n); +; +; Ullrich von Bassewitz, 29.05.1998 +; + + .export _memset + .import popax + .importzp ptr1, ptr2, tmp1, tmp2, tmp3 + +_memset: + sta tmp1 ; Save n + stx tmp2 + jsr popax ; Get c + sta tmp3 ; Save c + jsr popax ; Get ptr + sta ptr1 + stx ptr1+1 ; Save work copy + sta ptr2 + stx ptr2+1 ; Save a copy for the function result + lda tmp3 + + ldy #0 + ldx tmp2 ; Get high byte of n + beq L2 ; Jump if zero + +; Set 256 byte blocks + +L1: sta (ptr1),y ; Set one byte + iny + sta (ptr1),y ; Unroll this a bit to make it faster + iny + bne L1 + inc ptr1+1 + dex ; Next 256 byte block + bne L1 ; Repeat if any + +; Set the remaining bytes if any + +L2: ldx tmp1 ; Get the low byte of n + beq L9 ; Low byte is zero + +L3: sta (ptr1),y ; Set one byte + iny + dex ; Done? + bne L3 + +L9: lda ptr2 ; Load function result + ldx ptr2+1 + rts + + diff --git a/libsrc/common/perror.c b/libsrc/common/perror.c new file mode 100644 index 000000000..814152e29 --- /dev/null +++ b/libsrc/common/perror.c @@ -0,0 +1,24 @@ +/* + * perror.c + * + * Ullrich von Bassewitz, 01.10.1998 + */ + + + +#include +#include +#include + + + +void perror (const char* msg) +{ + if (msg) { + fprintf (stderr, "%s: ", msg); + } + fprintf (stderr, "%s\n", strerror (errno)); +} + + + diff --git a/libsrc/common/printf.c b/libsrc/common/printf.c new file mode 100644 index 000000000..7f0fcb4a0 --- /dev/null +++ b/libsrc/common/printf.c @@ -0,0 +1,26 @@ +/* + * printf.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include + + + +int printf (char* format, ...) +{ + va_list ap; + va_start (ap, format); + + /* Do formatting and output. Since we know, that va_end is empty, we don't + * call it here, saving an extra variable and some code. + */ + return vfprintf (stdout, (char*) va_fix (ap, 1), ap); +} + + + diff --git a/libsrc/common/putchar.c b/libsrc/common/putchar.c new file mode 100644 index 000000000..95bee07d7 --- /dev/null +++ b/libsrc/common/putchar.c @@ -0,0 +1,22 @@ +/* + * putchar.c + * + * Ullrich von Bassewitz, 11.12.1998 + */ + + + +#include + +/* This is usually declared as a macro */ +#undef putchar + + + +int putchar (int c) +{ + return fputc (c, stdout); +} + + + diff --git a/libsrc/common/puts.c b/libsrc/common/puts.c new file mode 100644 index 000000000..e3522c7b3 --- /dev/null +++ b/libsrc/common/puts.c @@ -0,0 +1,32 @@ +/* + * puts.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include +#include +#include "_file.h" + + + +int puts (const char* s) +{ + static char nl = '\n'; + + /* Assume stdout is always open */ + if (write (stdout->f_fd, s, strlen (s)) < 0 || + write (stdout->f_fd, &nl, 1) < 0) { + stdout->f_flags |= _FERROR; + return -1; + } + + /* Done */ + return 0; +} + + + diff --git a/libsrc/common/qsort.c b/libsrc/common/qsort.c new file mode 100644 index 000000000..88da4b28a --- /dev/null +++ b/libsrc/common/qsort.c @@ -0,0 +1,68 @@ +/* + * qsort.c + * + * Ullrich von Bassewitz, 09.12.1998 + */ + + + +#include + + + +static void QuickSort (void* Base, int Lo, int Hi, size_t Size, + int (*Compare)(const void*, const void*)) +/* Internal recursive function. Works with ints, but this shouldn't be + * a problem. + */ +{ + int I, J; + + /* Get a char pointer */ + unsigned char* B = Base; + + /* Quicksort */ + while (Hi > Lo) { + I = Lo + Size; + J = Hi; + while (I <= J) { + while (I <= J && Compare (B + Lo, B + I) >= 0) { + I += Size; + } + while (I <= J && Compare (B + Lo, B + J) < 0) { + J -= Size; + } + if (I <= J) { + _swap (B + I, B + J, Size); + I += Size; + J -= Size; + } + } + if (J != Lo) { + _swap (B + J, B + Lo, Size); + } + if (((unsigned) J) * 2 > (Hi + Lo)) { + QuickSort (Base, J + Size, Hi, Size, Compare); + Hi = J - Size; + } else { + QuickSort (Base, Lo, J - Size, Size, Compare); + Lo = J + Size; + } + } +} + + + +void qsort (void* base, size_t nmemb, size_t size, + int (*compare)(const void*, const void*)) +/* Quicksort implementation */ +{ + if (nmemb > 1) { + QuickSort (base, 0, (nmemb-1) * size, size, compare); + } +} + + + + + diff --git a/libsrc/common/rand.s b/libsrc/common/rand.s new file mode 100644 index 000000000..51959619b --- /dev/null +++ b/libsrc/common/rand.s @@ -0,0 +1,63 @@ +; +; Randum number generator +; +; Written and donated by Sidney Cadot - sidney@ch.twi.tudelft.nl +; +; May be distributed with the cc65 runtime using the same license. +; +; +; int rand (void); +; void srand (unsigned seed); +; +; Uses 4-byte state. +; Multiplier must be 1 (mod 4) +; Added value must be 1 (mod 2) +; This guarantees max. period (2**32) +; Bits 8-22 are returned (positive 2-byte int) +; where 0 is LSB, 31 is MSB. +; This is better as lower bits exhibit easily +; detectable patterns. +; + + .export _rand, _srand + +.bss + +rand: .res 4 ; Seed + +.code + +_rand: clc + lda rand+0 ; SEED *= $01010101 + adc rand+1 + sta rand+1 + adc rand+2 + sta rand+2 + adc rand+3 + sta rand+3 + clc + lda rand+0 ; SEED += $31415927 + adc #$27 + sta rand+0 + lda rand+1 + adc #$59 + sta rand+1 + pha + lda rand+2 + adc #$41 + sta rand+2 + and #$7f ; Suppress sign bit (make it positive) + tax + lda rand+3 + adc #$31 + sta rand+3 + pla ; return bit 8-22 in (X,A) + rts + +_srand: sta rand+0 ; Store the seed + stx rand+1 + lda #0 + sta rand+2 ; Set MSW to zero + sta rand+3 + rts + diff --git a/libsrc/common/realloc.c b/libsrc/common/realloc.c new file mode 100644 index 000000000..32c05f46f --- /dev/null +++ b/libsrc/common/realloc.c @@ -0,0 +1,78 @@ +/* + * realloc.c + * + * Ullrich von Bassewitz, 06.06.1998 + */ + + + +#include +#include +#include "_heap.h" + + + +void* realloc (void* block, size_t size) +{ + unsigned* b; + unsigned* newblock; + unsigned oldsize; + int diff; + + /* Check the block parameter */ + if (!block) { + /* Block is NULL, same as malloc */ + return malloc (size); + } + + /* Check the size parameter */ + if (size == 0) { + /* Block is not NULL, but size is: free the block */ + free (block); + return 0; + } + + /* Make the internal used size from the given size */ + size += HEAP_ADMIN_SPACE; + if (size < sizeof (struct freeblock)) { + size = sizeof (struct freeblock); + } + + /* Get a pointer to the real block, get the old block size */ + b = (unsigned*) (((int) block) - 2); + oldsize = *b; + + /* Get the size difference as a signed quantity */ + diff = size - oldsize; + + /* Is the block at the current heap top? */ + if (((int) b) + oldsize == ((int) _hptr)) { + /* Check if we've enough memory at the heap top */ + int newhptr; + newhptr = ((int) _hptr) + diff; + if (newhptr <= ((int) _hend)) { + /* Ok, there's space enough */ + _hptr = (unsigned*) newhptr; + *b = size; + return block; + } + } + + /* The given block was not located on top of the heap, or there's no + * room left. Try to allocate a new block and copy the data. + */ + if (newblock = malloc (size)) { + memcpy (newblock, block, oldsize - 2); + free (block); + } + return newblock; +} + + + + + + + + + diff --git a/libsrc/common/setjmp.s b/libsrc/common/setjmp.s new file mode 100644 index 000000000..38ca80e68 --- /dev/null +++ b/libsrc/common/setjmp.s @@ -0,0 +1,52 @@ +; +; Ullrich von Bassewitz, 06.06.1998 +; +; int setjmp (jmp_buf buf); +; + + .export __setjmp + .importzp sp, ptr1 + +__setjmp: + sta ptr1 ; Save buf + stx ptr1+1 + ldy #0 + +; The parameter stack is now empty, put it into buf + + lda sp + sta (ptr1),y + iny + lda sp+1 + sta (ptr1),y + iny + +; Put the return stack pointer next + + tsx + inx + inx ; drop return address + txa + sta (ptr1),y + iny + +; Last thing is the return address. + + pla + tax + pla + sta (ptr1),y ; high byte first + iny + pha + txa + sta (ptr1),y + pha + +; Return zero + + lda #0 + tax + rts + + + diff --git a/libsrc/common/sprintf.c b/libsrc/common/sprintf.c new file mode 100644 index 000000000..26b03486d --- /dev/null +++ b/libsrc/common/sprintf.c @@ -0,0 +1,26 @@ +/* + * sprintf.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include + + + +int sprintf (char* /*buf*/, char* format, ...) +{ + va_list ap; + va_start (ap, format); + + /* Do formatting and output. Since we know, that va_end is empty, we don't + * call it here, saving an extra variable and some code. + */ + return vsprintf ((char*) va_fix (ap, 1), (char*) va_fix (ap, 2), ap); +} + + + diff --git a/libsrc/common/stkcheck.s b/libsrc/common/stkcheck.s new file mode 100644 index 000000000..3dff40381 --- /dev/null +++ b/libsrc/common/stkcheck.s @@ -0,0 +1,48 @@ +; +; Ullrich von Bassewitz, 18.08.1998 +; +; Stack checker +; + + + .export _stkcheck, __stksafety + .import pushax, exit + .import __hend + .importzp sp + +.data +__stksafety: + .word 64 ; + +.code +_stkcheck: + clc + lda __hend + adc __stksafety + tax ; Remember low byte + lda __hend+1 + adc __stksafety+1 + + cmp sp+1 + bcc Ok + bne L1 + cpx sp + bcc Ok + +; Stack overflow + +L1: inc sp+1 ; Create 256 bytes of space + ldx #0 + lda #4 + jsr pushax + jmp exit + +; All is well + +Ok: rts + + + + + + diff --git a/libsrc/common/strcat.s b/libsrc/common/strcat.s new file mode 100644 index 000000000..7297579bb --- /dev/null +++ b/libsrc/common/strcat.s @@ -0,0 +1,57 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; char* strcat (char* dest, const char* src); +; + + .export _strcat + .import popax + .importzp ptr1, ptr2, ptr3 + +_strcat: + sta ptr1 ; Save src + stx ptr1+1 + jsr popax ; Get dest + sta ptr2 + stx ptr2+1 + sta ptr3 ; Remember for function return + stx ptr3+1 + ldy #0 + +; find end of dest + +sc1: lda (ptr2),y + beq sc2 + iny + bne sc1 + inc ptr2+1 + bne sc1 + +; end found, get offset in y into pointer + +sc2: tya + clc + adc ptr2 + sta ptr2 + bcc sc3 + inc ptr2+1 + +; copy src + +sc3: ldy #0 +sc4: lda (ptr1),y + sta (ptr2),y + beq sc5 + iny + bne sc4 + inc ptr1+1 + inc ptr2+1 + bne sc4 + +; done, return pointer to dest + +sc5: lda ptr3 + ldx ptr3+1 + rts + + diff --git a/libsrc/common/strchr.s b/libsrc/common/strchr.s new file mode 100644 index 000000000..7a5c767d0 --- /dev/null +++ b/libsrc/common/strchr.s @@ -0,0 +1,41 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; const char* strchr (const char* s, int c); +; + + .export _strchr + .import popax + .importzp ptr1, tmp1 + +_strchr: + sta tmp1 ; Save c + jsr popax ; get s + sta ptr1 + stx ptr1+1 + ldy #0 + +scloop: lda (ptr1),y ; get next char + beq strz ; jump if end of string + cmp tmp1 ; found? + beq strf ; jump if yes + iny + bne scloop + inc ptr1+1 + bne scloop ; jump always + +; found, calculate pointer to c + +strf: ldx ptr1+1 ; get high byte of pointer + tya ; low byte offset + clc + adc ptr1 + bcc str1 + inx +str1: rts + +; not found, return zero + +strz: tax ; return 0 + rts + diff --git a/libsrc/common/strcmp.s b/libsrc/common/strcmp.s new file mode 100644 index 000000000..db85d9bc3 --- /dev/null +++ b/libsrc/common/strcmp.s @@ -0,0 +1,35 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; int strcmp (const char* s1, const char* s2); +; + + .export _strcmp + .import popax + .importzp ptr1, ptr2 + +_strcmp: + sta ptr2 ; Save s2 + stx ptr2+1 + jsr popax ; Get s1 + sta ptr1 + stx ptr1+1 + ldy #0 + +loop: lda (ptr1),y + cmp (ptr2),y + bne L1 + tax ; end of strings? + beq L3 + iny + bne loop + inc ptr1+1 + inc ptr2+1 + bne loop + +L1: bcs L2 + ldx #$FF + rts + +L2: ldx #$01 +L3: rts diff --git a/libsrc/common/strcoll.s b/libsrc/common/strcoll.s new file mode 100644 index 000000000..664d1f483 --- /dev/null +++ b/libsrc/common/strcoll.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 11.12.1998 +; +; int strcoll (const char* s1, const char* s2); +; +; Since we don't have locales, this function is equivalent to strcmp. +; + + .export _strcoll + .import _strcmp + +_strcoll = _strcmp + diff --git a/libsrc/common/strcpy.s b/libsrc/common/strcpy.s new file mode 100644 index 000000000..a0194db9c --- /dev/null +++ b/libsrc/common/strcpy.s @@ -0,0 +1,33 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; char* strcpy (char* dest, const char* src); +; + + .export _strcpy + .import popax + .importzp ptr1, ptr2, ptr3 + +_strcpy: + sta ptr1 ; Save src + stx ptr1+1 + jsr popax ; Get dest + sta ptr2 + stx ptr2+1 + sta ptr3 ; remember for function return + stx ptr3+1 + ldy #$00 + +L1: lda (ptr1),y + sta (ptr2),y + beq L9 + iny + bne L1 + inc ptr1+1 + inc ptr2+1 + bne L1 + +L9: lda ptr3 + ldx ptr3+1 + rts + diff --git a/libsrc/common/strcspn.s b/libsrc/common/strcspn.s new file mode 100644 index 000000000..6c1256101 --- /dev/null +++ b/libsrc/common/strcspn.s @@ -0,0 +1,54 @@ +; +; Ullrich von Bassewitz, 11.06.1998 +; +; size_t strcspn (const char* s1, const char* s2); +; + + .export _strcspn + .import popax + .importzp ptr1, ptr2, tmp1, tmp2, tmp3 + +_strcspn: + sta ptr2 ; Save s2 + stx ptr2+1 + jsr popax ; Get s1 + sta ptr1 + stx ptr1+1 + ldx #0 ; low counter byte + stx tmp1 ; high counter byte + ldy #$00 + +L1: lda (ptr1),y ; get next char from s1 + beq L6 ; jump if done + sta tmp2 ; save char + iny + bne L2 + inc ptr1+1 +L2: sty tmp3 ; save index into s1 + + ldy #0 ; get index into s2 +L3: lda (ptr2),y ; + beq L4 ; jump if done + cmp tmp2 + beq L6 + iny + bne L3 + +; The character was not found in s2. Increment the counter and start over + +L4: ldy tmp3 ; reload index + inx + bne L1 + inc tmp1 + bne L1 + +; The character was found, or we reached the end of s1. Return count of +; characters + +L6: txa ; get low counter byte + ldx tmp1 ; get high counter byte + rts + + + + diff --git a/libsrc/common/strdup.c b/libsrc/common/strdup.c new file mode 100644 index 000000000..42a01afc8 --- /dev/null +++ b/libsrc/common/strdup.c @@ -0,0 +1,25 @@ +/* + * strdup.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include + + + +char* strdup (char* s) +{ + char* p; + p = malloc (strlen (s) + 1); + if (p) { + strcpy (p, s); + } + return p; +} + + + diff --git a/libsrc/common/strerror.s b/libsrc/common/strerror.s new file mode 100644 index 000000000..6149c42fc --- /dev/null +++ b/libsrc/common/strerror.s @@ -0,0 +1,29 @@ +; +; Ullrich von Bassewitz, 17.05.2000 +; +; char* __fastcall__ strerror (int errcode); +; /* Map an error number to an error message */ +; + + .export _strerror + .import __sys_errlist + + .include "errno.inc" + +_strerror: + cpx #$00 ; High byte must be zero + bne @L1 ; Jump if invalid error + cmp #EMAX ; Valid error code (map EUNKNOWN to 0)? + bcc @L2 ; Jump if ok +@L1: lda #$00 ; "Unknown error" +@L2: asl a ; * 2 + tay + +; Load the pointer to the error message and return + + lda __sys_errlist+1,y + tax + lda __sys_errlist,y + rts + + diff --git a/libsrc/common/stricmp.s b/libsrc/common/stricmp.s new file mode 100644 index 000000000..62e2f847c --- /dev/null +++ b/libsrc/common/stricmp.s @@ -0,0 +1,59 @@ +; +; Ullrich von Bassewitz, 03.06.1998 +; +; int stricmp (const char* s1, const char* s2); /* DOS way */ +; int strcasecmp (const char* s1, const char* s2); /* UNIX way */ +; + + .export _stricmp, _strcasecmp + .import popax + .import __ctype, __cdiff + .importzp ptr1, ptr2, tmp1 + + +_stricmp: +_strcasecmp: + sta ptr2 ; Save s2 + stx ptr2+1 + jsr popax ; get s1 + sta ptr1 + stx ptr1+1 + ldy #0 + +loop: lda (ptr2),y ; get char from second string + tax + lda __ctype,x ; get character classification + and #$01 ; lower case char? + beq L1 ; jump if no + txa ; get character back + clc + adc __cdiff ; make upper case char + tax ; +L1: stx tmp1 ; remember upper case equivalent + + lda (ptr1),y ; get character from first string + tax + lda __ctype,x ; get character classification + and #$01 ; lower case char? + beq L2 ; jump if no + txa ; get character back + clc + adc __cdiff ; make upper case char + tax + +L2: cpx tmp1 ; compare characters + bne L3 + txa ; end of strings? + beq L5 ; a/x both zero + iny + bne loop + inc ptr1+1 + inc ptr2+1 + bne loop + +L3: bcs L4 + ldx #$FF + rts + +L4: ldx #$01 +L5: rts diff --git a/libsrc/common/strlen.s b/libsrc/common/strlen.s new file mode 100644 index 000000000..6cef2eaf5 --- /dev/null +++ b/libsrc/common/strlen.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; int strlen (const char* s); +; + + .export _strlen + .importzp ptr1 + +_strlen: + sta ptr1 ; Save s + stx ptr1+1 + ldx #0 ; YX used as counter + ldy #0 + +L1: lda (ptr1),y + beq L9 + iny + bne L1 + inc ptr1+1 + inx + bne L1 + +L9: tya ; get low byte of counter, hi's all set + rts + diff --git a/libsrc/common/strlower.s b/libsrc/common/strlower.s new file mode 100644 index 000000000..49e906bea --- /dev/null +++ b/libsrc/common/strlower.s @@ -0,0 +1,45 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; char* strlower (char* s); +; char* strlwr (char* s); +; +; Non-ANSI +; + + .export _strlower, _strlwr + .import popax + .import __ctype, __cdiff + .importzp ptr1, ptr2 + +_strlower: +_strlwr: + sta ptr1 ; Save s (working copy) + stx ptr1+1 + sta ptr2 + sta ptr2+2 ; save function result + ldy #0 + +loop: lda (ptr1),y ; get character + beq L9 ; jump if done + tax + lda __ctype,x ; get character classification + and #$02 ; upper case char? + beq L1 ; jump if no + txa ; get character back into accu + sec + sbc __cdiff ; make lower case char + sta (ptr1),y ; store back +L1: iny ; next char + bne loop + inc ptr1+1 ; handle offset overflow + bne loop ; branch always + +; Done, return the argument string + +L9: lda ptr2 + ldx ptr2+1 + rts + + + diff --git a/libsrc/common/strncat.s b/libsrc/common/strncat.s new file mode 100644 index 000000000..5171ad9b4 --- /dev/null +++ b/libsrc/common/strncat.s @@ -0,0 +1,72 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; char* strncat (char* dest, const char* src, size_t n); +; + + .export _strncat + .import popax + .importzp ptr1, ptr2, ptr3, tmp1, tmp2 + +_strncat: + eor #$FF ; one's complement to count upwards + sta tmp1 + txa + eor #$FF + sta tmp2 + jsr popax ; get src + sta ptr1 + stx ptr1+1 + jsr popax ; get dest + sta ptr2 + stx ptr2+1 + sta ptr3 ; remember for function return + stx ptr3+1 + ldy #0 + +; find end of dest + +L1: lda (ptr2),y + beq L2 + iny + bne L1 + inc ptr2+1 + bne L1 + +; end found, get offset in y into pointer + +L2: tya + clc + adc ptr2 + sta ptr2 + bcc L3 + inc ptr2+1 + +; copy src. We've put the ones complement of the count into the counter, so +; we'll increment the counter on top of the loop + +L3: ldy #0 + ldx tmp1 ; low counter byte + +L4: inx + bne L5 + inc tmp2 + beq L6 ; jump if done +L5: lda (ptr1),y + sta (ptr2),y + beq L7 + iny + bne L4 + inc ptr1+1 + inc ptr2+1 + bne L4 + +; done, set the trailing zero and return pointer to dest + +L6: lda #0 + sta (ptr2),y +L7: lda ptr3 + ldx ptr3+1 + rts + + diff --git a/libsrc/common/strncmp.s b/libsrc/common/strncmp.s new file mode 100644 index 000000000..3af44fa69 --- /dev/null +++ b/libsrc/common/strncmp.s @@ -0,0 +1,81 @@ +; +; Ullrich von Bassewitz, 25.05.2000 +; +; int strncmp (const char* s1, const char* s2, unsigned n); +; + + .export _strncmp + .import popax + .importzp ptr1, ptr2, ptr3 + + +_strncmp: + +; Convert the given counter value in a/x from a downward counter into an +; upward counter, so we can increment the counter in the loop below instead +; of decrementing it. This adds some overhead now, but is cheaper than +; executing a more complex test in each iteration of the loop. We do also +; correct the value by one, so we can do the test on top of the loop. + + eor #$FF + sta ptr3 + txa + eor #$FF + sta ptr3+1 + +; Get the remaining arguments + + jsr popax ; get s2 + sta ptr2 + stx ptr2+1 + jsr popax ; get s1 + sta ptr1 + stx ptr1+1 + +; Loop setup + + ldy #0 + +; Start of compare loop. Check the counter. + +Loop: inc ptr3 + beq IncHi ; Increment high byte + +; Compare a byte from the strings + +Comp: lda (ptr1),y + cmp (ptr2),y + bne NotEqual ; Jump if strings different + tax ; End of strings? + beq Equal1 ; Jump if EOS reached, a/x == 0 + +; Increment the pointers + + iny + bne Loop + inc ptr1+1 + inc ptr2+1 + bne Loop ; Branch always + +; Increment hi byte + +IncHi: inc ptr3+1 + bne Comp ; Jump if counter not zero + +; Exit code if strings are equal. a/x not set + +Equal: lda #$00 + tax +Equal1: rts + +; Exit code if strings not equal + +NotEqual: + bcs L1 + ldx #$FF ; Make result negative + rts + +L1: ldx #$01 ; Make result positive + rts + + diff --git a/libsrc/common/strncpy.s b/libsrc/common/strncpy.s new file mode 100644 index 000000000..7c5ee159f --- /dev/null +++ b/libsrc/common/strncpy.s @@ -0,0 +1,98 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; char* strncpy (char* dest, const char* src, unsigned size); +; + + .export _strncpy + .import popax + .importzp ptr1, ptr2, ptr3, tmp1, tmp2 + +_strncpy: + sta tmp1 ; Save size + stx tmp2 + jsr popax ; get src + sta ptr1 + stx ptr1+1 + jsr popax ; get dest + sta ptr2 + stx ptr2+1 + sta ptr3 ; remember for function return + stx ptr3+1 + + ldy #$00 + ldx tmp1 ; Low byte of size + beq L1 + +; Copy the first chunk < 256 + + jsr CopyChunk + bcs L3 ; Jump if end of string found + +; Copy full 256 byte chunks + +L1: lda tmp2 ; High byte of size + beq L3 + ldx #$00 ; Copy 256 bytes +L2: jsr CopyChunk + bcs L3 + dec tmp2 + bne L2 + beq L9 + +; Fill the remaining space with zeroes. If we come here, the value in X +; is the low byte of the fill count, tmp2 holds the high byte. Y is the index +; into the target string. + +L3: tax ; Test low byte + beq L4 + jsr FillChunk + +L4: lda tmp2 ; Test high byte + beq L9 +L5: jsr FillChunk + dec tmp2 + bne L5 + +; Done - return a pointer to the string + +L9: lda ptr3 + ldx ptr3+1 + rts + + +; ------------------------------------------------------------------- +; Copy byte count in X from ptr1 to ptr2 + +.proc CopyChunk +L1: lda (ptr1),y + sta (ptr2),y + beq L3 + iny + bne L2 + inc ptr1+1 + inc ptr2+1 +L2: dex + bne L1 + clc + rts +L3: sec + rts +.endproc + + +; ------------------------------------------------------------------- +; Fill byte count in X with zeroes + +.proc FillChunk + lda #$00 +L1: sta (ptr1),y + iny + bne L2 + inc ptr1+1 +L2: dex + bne L1 + rts +.endproc + + diff --git a/libsrc/common/strpbrk.s b/libsrc/common/strpbrk.s new file mode 100644 index 000000000..e34468b53 --- /dev/null +++ b/libsrc/common/strpbrk.s @@ -0,0 +1,60 @@ +; +; Ullrich von Bassewitz, 11.06.1998 +; +; char* strpbrk (const char* s1, const char* s2); +; + + .export _strpbrk + .import popax, return0 + .importzp ptr1, ptr2, tmp1, tmp2, tmp3 + +_strpbrk: + jsr popax ; get s2 + sta ptr2 + stx ptr2+1 + jsr popax ; get s1 + sta ptr1 + stx ptr1+1 + ldy #$00 + +L1: lda (ptr1),y ; get next char from s1 + beq L9 ; jump if done + sta tmp2 ; save char + iny + bne L2 + inc ptr1+1 +L2: sty tmp3 ; save index into s1 + + ldy #0 ; get index into s2 +L3: lda (ptr2),y ; + beq L4 ; jump if done + cmp tmp2 + beq L6 + iny + bne L3 + +; The character was not found in s2. Increment the counter and start over + +L4: ldy tmp3 ; reload index + inx + bne L1 + inc tmp1 + bne L1 + +; A character was found. Calculate a pointer to this char in s1 and return it. + +L6: ldx ptr1+1 + lda tmp3 ; get y offset + clc + adc ptr1 + bcc L7 + inx +L7: rts + +; None of the characters in s2 was found - return NULL + +L9: jmp return0 + + + + diff --git a/libsrc/common/strrchr.s b/libsrc/common/strrchr.s new file mode 100644 index 000000000..bf09fbd7b --- /dev/null +++ b/libsrc/common/strrchr.s @@ -0,0 +1,47 @@ +; +; Ullrich von Bassewitz, 31.05.1998 +; +; char* strrchr (const char* s, int c); +; + + .export _strrchr + .import popax + .importzp ptr1, ptr2, tmp1 + +_strrchr: + sta tmp1 ; Save c + jsr popax ; get s + sta ptr1 + stx ptr1+1 + lda #0 ; function result = NULL + sta ptr2 + sta ptr2+1 + tay + +L1: lda (ptr1),y ; get next char + beq L3 ; jump if end of string + cmp tmp1 ; found? + bne L2 ; jump if no + +; Remember a pointer to the character + + tya + clc + adc ptr1 + sta ptr2 + lda ptr1+1 + adc #$00 + sta ptr2+1 + +; Next char + +L2: iny + bne L1 + inc ptr1+1 + bne L1 ; jump always + +; Return the pointer to the last occurrence + +L3: lda ptr2 + ldx ptr2+1 + rts diff --git a/libsrc/common/strspn.s b/libsrc/common/strspn.s new file mode 100644 index 000000000..884a3376a --- /dev/null +++ b/libsrc/common/strspn.s @@ -0,0 +1,56 @@ +; +; Ullrich von Bassewitz, 11.06.1998 +; +; size_t strspn (const char* s1, const char* s2); +; + + .export _strspn + .import popax + .importzp ptr1, ptr2, tmp1, tmp2, tmp3 + +_strspn: + sta ptr2 ; Save s2 + stx ptr2+1 + jsr popax ; get s1 + sta ptr1 + stx ptr1+1 + ldx #0 ; low counter byte + stx tmp1 ; high counter byte + ldy #$00 + +L1: lda (ptr1),y ; get next char from s1 + beq L6 ; jump if done + sta tmp2 ; save char + iny + bne L2 + inc ptr1+1 +L2: sty tmp3 ; save index into s1 + + ldy #0 ; get index into s2 +L3: lda (ptr2),y ; + beq L6 ; jump if done + cmp tmp2 + beq L4 + iny + bne L3 + +; The character was found in s2. Increment the counter and start over + +L4: ldy tmp3 ; reload index + inx + bne L1 + inc tmp1 + bne L1 + +; The character was not found, or we reached the end of s1. Return count of +; characters + +L6: txa ; get low counter byte + ldx tmp1 ; get high counter byte + rts + + + + + + diff --git a/libsrc/common/strstr.s b/libsrc/common/strstr.s new file mode 100644 index 000000000..62e1944ec --- /dev/null +++ b/libsrc/common/strstr.s @@ -0,0 +1,97 @@ +; +; Ullrich von Bassewitz, 11.12.1998 +; +; char* strstr (const char* haystack, const char* needle); +; + + .export _strstr + .import popax + .importzp ptr1, ptr2, ptr3, ptr4, tmp1 + +_strstr: + sta ptr2 ; Save needle + stx ptr2+1 + sta ptr4 ; Setup temp copy for later + + jsr popax ; Get haystack + sta ptr1 + stx ptr1+1 ; Save haystack + +; If needle is empty, return haystack + + ldy #$00 + lda (ptr2),y ; Get first byte of needle + beq @Found ; Needle is empty --> we're done + +; Search for the beginning of the string (this is not an optimal search +; strategy [in fact, it's pretty dumb], but it's simple to implement). + + sta tmp1 ; Save start of needle +@L1: lda (ptr1),y ; Get next char from haystack + beq @NotFound ; Jump if end + cmp tmp1 ; Start of needle found? + beq @L2 ; Jump if so + iny ; Next char + bne @L1 + inc ptr1+1 ; Bump high byte + bne @L1 ; Branch always + +; We found the start of needle in haystack + +@L2: tya ; Get offset + clc + adc ptr1 + sta ptr1 ; Make ptr1 point to start + bcc @L3 + inc ptr1+1 + +; ptr1 points to the start of needle now. Setup temporary pointers for the +; search. The low byte of ptr4 is already set. + +@L3: sta ptr3 + lda ptr1+1 + sta ptr3+1 + lda ptr2+1 + sta ptr4+1 + ldy #1 ; First char is identical, so start on second + +; Do the compare + +@L4: lda (ptr4),y ; Get char from needle + beq @Found ; Jump if end of needle (-> found) + cmp (ptr3),y ; Compare with haystack + bne @L5 ; Jump if not equal + iny ; Next char + bne @L4 + inc ptr3+1 + inc ptr4+1 ; Bump hi byte of pointers + bne @L4 ; Next char (branch always) + +; The strings did not compare equal, search next start of needle + +@L5: ldy #1 ; Start after this char + bne @L1 ; Branch always + +; We found the start of needle + +@Found: lda ptr1 + ldx ptr1+1 + rts + +; We reached end of haystack without finding needle + +@NotFound: + lda #$00 ; return NULL + tax + rts + + + + + + + + + + + diff --git a/libsrc/common/strtok.c b/libsrc/common/strtok.c new file mode 100644 index 000000000..d3f8b56f4 --- /dev/null +++ b/libsrc/common/strtok.c @@ -0,0 +1,77 @@ +/* + * strtok.c + * + * Ullrich von Bassewitz, 11.12.1998 + */ + + + +#include + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Memory location that holds the last input */ +static char* Last = 0; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +char* strtok (char* s1, const char* s2) +{ + char c; + char* start; + + /* Use the stored location if called with a NULL pointer */ + if (s1 == 0) { + s1 = Last; + } + + /* If s1 is empty, there are no more tokens. Return 0 in this case. */ + if (*s1 == '\0') { + return 0; + } + + /* Search the address of the first element in s1 that equals none + * of the characters in s2. + */ + while ((c = *s1) && strchr (s2, c) != 0) { + ++s1; + } + if (c == '\0') { + /* No more tokens found */ + Last = s1; + return 0; + } + + /* Remember the start of the token */ + start = s1; + + /* Search for the end of the token */ + while ((c = *s1) && strchr (s2, c) == 0) { + ++s1; + } + if (c == '\0') { + /* Last element */ + Last = s1; + } else { + *s1 = '\0'; + Last = s1 + 1; + } + + /* Return the start of the token */ + return start; +} + + + diff --git a/libsrc/common/strupper.s b/libsrc/common/strupper.s new file mode 100644 index 000000000..80019ac9a --- /dev/null +++ b/libsrc/common/strupper.s @@ -0,0 +1,45 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; char* strupper (char* s); +; char* strupr (char* s); +; +; Non-ANSI +; + + .export _strupper, _strupr + .import popax + .import __ctype, __cdiff + .importzp ptr1, ptr2 + +_strupper: +_strupr: + sta ptr1 ; Save s (working copy) + stx ptr1+1 + sta ptr2 + sta ptr2+2 ; save function result + ldy #0 + +loop: lda (ptr1),y ; get character + beq L9 ; jump if done + tax + lda __ctype,x ; get character classification + and #$01 ; lower case char? + beq L1 ; jump if no + txa ; get character back into accu + clc + adc __cdiff ; make upper case char + sta (ptr1),y ; store back +L1: iny ; next char + bne loop + inc ptr1+1 ; handle offset overflow + bne loop ; branch always + +; Done, return the argument string + +L9: lda ptr2 + ldx ptr2+1 + rts + + + diff --git a/libsrc/common/strxfrm.c b/libsrc/common/strxfrm.c new file mode 100644 index 000000000..47d9a9951 --- /dev/null +++ b/libsrc/common/strxfrm.c @@ -0,0 +1,20 @@ +/* + * strxfrm.c + * + * Ullrich von Bassewitz, 11.12.1998 + */ + + + +#include + + + +size_t strxfrm (char* dest, const char* src, size_t count) +{ + strncpy (dest, src, count); + return strlen (src); +} + + + diff --git a/libsrc/common/tolower.s b/libsrc/common/tolower.s new file mode 100644 index 000000000..5ecc92f09 --- /dev/null +++ b/libsrc/common/tolower.s @@ -0,0 +1,22 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int tolower (int c); +; + + .export _tolower + .import __ctype, __cdiff + +_tolower: + tay ; Get C into Y + lda __ctype,y ; Get character classification + and #$02 ; Is this an upper case char? + beq L1 ; Jump if no + tya ; Get char back into A + sec + sbc __cdiff ; make lower case char + rts ; CC are set + +L1: tya ; Get char back into A + rts ; CC are set + diff --git a/libsrc/common/toupper.s b/libsrc/common/toupper.s new file mode 100644 index 000000000..43ff0812d --- /dev/null +++ b/libsrc/common/toupper.s @@ -0,0 +1,22 @@ +; +; Ullrich von Bassewitz, 02.06.1998 +; +; int toupper (int c); +; + + .export _toupper + .import __ctype, __cdiff + +_toupper: + tay ; Get c into Y + lda __ctype,y ; Get character classification + and #$01 ; Mask lower char bit + beq L1 ; Jump if not lower char + tya ; Get C back into A + clc + adc __cdiff ; make upper case char + rts ; CC are set + +L1: tya ; Get C back + rts ; CC are set + diff --git a/libsrc/common/vcprintf.c b/libsrc/common/vcprintf.c new file mode 100644 index 000000000..97e8214d4 --- /dev/null +++ b/libsrc/common/vcprintf.c @@ -0,0 +1,44 @@ +/* + * vcprintf.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include +#include "_printf.h" + + + +static void out (struct outdesc* d, char* buf, unsigned count) +/* Routine used for writing */ +{ + /* Fast screen output */ + d->ccount += count; + while (count) { + cputc (*buf); + ++buf; + --count; + } +} + + + +int vcprintf (char* format, va_list ap) +{ + struct outdesc d; + + /* Setup descriptor */ + d.fout = out; + + /* Do formatting and output */ + _printf (&d, format, ap); + + /* Return bytes written */ + return d.ccount; +} + + + diff --git a/libsrc/common/vfprintf.c b/libsrc/common/vfprintf.c new file mode 100644 index 000000000..a150aaa0c --- /dev/null +++ b/libsrc/common/vfprintf.c @@ -0,0 +1,44 @@ +/* + * vfprintf.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include +#include "_printf.h" + + + +static void out (struct outdesc* d, char* buf, unsigned count) +/* Routine used for writing */ +{ + /* Write to the file */ + if (fwrite (buf, count, 1, (FILE*) d->ptr) == -1) { + d->ccount = -1; + } else { + d->ccount += count; + } +} + + + +int vfprintf (FILE* f, char* format, va_list ap) +{ + struct outdesc d; + + /* Setup descriptor */ + d.fout = out; + d.ptr = f; + + /* Do formatting and output */ + _printf (&d, format, ap); + + /* Return bytes written */ + return d.ccount; +} + + + diff --git a/libsrc/common/vprintf.c b/libsrc/common/vprintf.c new file mode 100644 index 000000000..923d3e5c1 --- /dev/null +++ b/libsrc/common/vprintf.c @@ -0,0 +1,21 @@ +/* + * vprintf.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include +#include "_printf.h" + + + +int vprintf (char* format, va_list ap) +{ + return vfprintf (stdout, format, ap); +} + + + diff --git a/libsrc/common/vsprintf.c b/libsrc/common/vsprintf.c new file mode 100644 index 000000000..bb97a88ab --- /dev/null +++ b/libsrc/common/vsprintf.c @@ -0,0 +1,45 @@ +/* + * vsprintf.c + * + * Ullrich von Bassewitz, 11.08.1998 + */ + + + +#include +#include +#include "_printf.h" + + + +static void out (struct outdesc* d, char* buf, unsigned count) +/* Routine used for writing */ +{ + /* String - be shure to check the size */ + while (count-- && d->ccount < d->uns) { + ((char*) d->ptr) [d->ccount] = *buf; + ++buf; + ++d->ccount; + } +} + + + +int vsprintf (char* buf, char* format, va_list ap) +{ + struct outdesc d; + + /* Setup descriptor */ + d.fout = out; + d.ptr = buf; + d.uns = 0x7FFF; + + /* Do formatting and output */ + _printf (&d, format, ap); + + /* Return bytes written */ + return d.ccount; +} + + + diff --git a/libsrc/common/zerobss.s b/libsrc/common/zerobss.s new file mode 100644 index 000000000..9e1d00524 --- /dev/null +++ b/libsrc/common/zerobss.s @@ -0,0 +1,47 @@ +; +; Ullrich von Bassewitz, 17.09.1998 +; +; Zero the bss segment. +; + + .export zerobss + .import __BSS_RUN__, __BSS_SIZE__ + .importzp ptr1 + + +.code + +zerobss: + lda #<__BSS_RUN__ + sta ptr1 + lda #>__BSS_RUN__ + sta ptr1+1 + lda #0 + tay + +; Clear full pages + +L1: ldx #>__BSS_SIZE__ + beq L3 +L2: sta (ptr1),y + iny + bne L2 + inc ptr1+1 + dex + bne L2 + +; Clear remaining page (y is zero on entry) + +L3: ldx #<__BSS_SIZE__ + beq L5 +L4: sta (ptr1),y + iny + dex + bne L4 + +; Done + +L5: rts + + + diff --git a/libsrc/conio/Makefile b/libsrc/conio/Makefile new file mode 100644 index 000000000..2fc751aba --- /dev/null +++ b/libsrc/conio/Makefile @@ -0,0 +1,26 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $< + @$(AS) -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +C_OBJS = + +S_OBJS = cputs.o cursor.o cputhex.o scrsize.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f *~ + @rm -f $(C_OBJS:.o=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) diff --git a/libsrc/conio/cputhex.s b/libsrc/conio/cputhex.s new file mode 100644 index 000000000..1aaeed6b2 --- /dev/null +++ b/libsrc/conio/cputhex.s @@ -0,0 +1,39 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void cputhex8 (unsigned char val); +; void cputhex16 (unsigned val); +; + + .export _cputhex8, _cputhex16 + .import _cputc + .import __hextab + + +_cputhex16: + pha ; Save low byte + txa ; Get high byte into A + jsr _cputhex8 ; Output high byte + pla ; Restore low byte and run into _cputhex8 + +_cputhex8: + pha ; Save the value + lsr a + lsr a + lsr a + lsr a + tay + lda __hextab,y + jsr _cputc + pla + and #$0F + tay + lda __hextab,y + jmp _cputc + + + + + + + diff --git a/libsrc/conio/cputs.s b/libsrc/conio/cputs.s new file mode 100644 index 000000000..1d8662607 --- /dev/null +++ b/libsrc/conio/cputs.s @@ -0,0 +1,36 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void cputsxy (unsigned char x, unsigned char y, char* s); +; void cputs (char* s); +; + + .export _cputsxy, _cputs + .import popa, _gotoxy, _cputc + .importzp ptr1, tmp1 + +_cputsxy: + sta ptr1 ; Save s for later + stx ptr1+1 + jsr popa ; Get Y + jsr _gotoxy ; Set cursor, pop x + jmp L0 ; Same as cputs... + +_cputs: sta ptr1 ; Save s + stx ptr1+1 +L0: ldy #0 +L1: lda (ptr1),y + beq L9 ; Jump if done + iny + sty tmp1 ; Save offset + jsr _cputc ; Output char, advance cursor + ldy tmp1 ; Get offset + bne L1 ; Next char + inc ptr1+1 ; Bump high byte + bne L1 + +; Done + +L9: rts + + diff --git a/libsrc/conio/cursor.s b/libsrc/conio/cursor.s new file mode 100644 index 000000000..1dd500e68 --- /dev/null +++ b/libsrc/conio/cursor.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 17.06.1998 +; +; unsigned char cursor (unsigned char onoff); +; + + .export _cursor + .export cursor + + +.proc _cursor + + tay ; onoff into Y + ldx #0 ; High byte of result + lda cursor ; Get old value + sty cursor ; Set new value + rts + +.endproc + + +.bss + +cursor: .res 1 + + diff --git a/libsrc/conio/scrsize.s b/libsrc/conio/scrsize.s new file mode 100644 index 000000000..4be59964a --- /dev/null +++ b/libsrc/conio/scrsize.s @@ -0,0 +1,38 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; void screensize (unsigned char* x, unsigned char* y); +; + + .export _screensize + .export xsize, ysize + + .import popax + .importzp ptr1, ptr2 + +.proc _screensize + + sta ptr1 ; Store the y pointer + stx ptr1+1 + + jsr popax ; get the x pointer + sta ptr2 + stx ptr2+1 + + ldy #0 + lda xsize + sta (ptr2),y + lda ysize + sta (ptr1),y + rts + +.endproc + + +.bss + +xsize: .res 1 +ysize: .res 1 + + + diff --git a/libsrc/dbg/.cvsignore b/libsrc/dbg/.cvsignore new file mode 100644 index 000000000..4966575f6 --- /dev/null +++ b/libsrc/dbg/.cvsignore @@ -0,0 +1 @@ +dbg.s diff --git a/libsrc/dbg/Makefile b/libsrc/dbg/Makefile new file mode 100644 index 000000000..1dcf8b116 --- /dev/null +++ b/libsrc/dbg/Makefile @@ -0,0 +1,26 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $< + @$(AS) -g -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +C_OBJS = dbg.o + +S_OBJS = asmtab.o dbgdasm.o dbgdump.o dbgisram.o dbgsupp.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f *~ + @rm -f $(C_OBJS:.o=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) diff --git a/libsrc/dbg/asmtab.s b/libsrc/dbg/asmtab.s new file mode 100644 index 000000000..d09448521 --- /dev/null +++ b/libsrc/dbg/asmtab.s @@ -0,0 +1,61 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; Tables needed for the line assembler/disassembler. +; + + .export OffsetTab + .export AdrFlagTab + .export SymbolTab1, SymbolTab2 + .export MnemoTab1, MnemoTab2 + +; ------------------------------------------------------------------------- +; Assembler tables + +.rodata + + +OffsetTab: + .byte $40,$02,$45,$03,$D0,$08,$40,$09 + .byte $30,$22,$45,$33,$D0,$08,$40,$09 + .byte $40,$02,$45,$33,$D0,$08,$40,$09 + .byte $40,$02,$45,$B3,$D0,$08,$40,$09 + .byte $00,$22,$44,$33,$D0,$8C,$44,$00 + .byte $11,$22,$44,$33,$D0,$8C,$44,$9A + .byte $10,$22,$44,$33,$D0,$08,$40,$09 + .byte $10,$22,$44,$33,$D0,$08,$40,$09 + .byte $62,$13,$78,$A9 + +AdrFlagTab: + .byte $00,$21,$81,$82,$00,$00,$59,$4D + .byte $91,$92,$86,$4A,$85,$9D + +SymbolTab1: + .byte $2C,$29,$2C,$23,$28,$24 + +SymbolTab2: + .byte $59,$00,$58,$24,$24,$00 + +MnemoTab1: + .byte $1C,$8A,$1C,$23,$5D,$8B,$1B,$A1 + .byte $9D,$8A,$1D,$23,$9D,$8B,$1D,$A1 + .byte $00,$29,$19,$AE,$69,$A8,$19,$23 + .byte $24,$53,$1B,$23,$24,$53,$19,$A1 + .byte $00,$1A,$5B,$5B,$A5,$69,$24,$24 + .byte $AE,$AE,$A8,$AD,$29,$00,$7C,$00 + .byte $15,$9C,$6D,$9C,$A5,$69,$29,$53 + .byte $84,$13,$34,$11,$A5,$69,$23,$A0 + +MnemoTab2: + .byte $D8,$62,$5A,$48,$26,$62,$94,$88 + .byte $54,$44,$C8,$54,$68,$44,$E8,$94 + .byte $00,$B4,$08,$84,$74,$B4,$28,$6E + .byte $74,$F4,$CC,$4A,$72,$F2,$A4,$8A + .byte $00,$AA,$A2,$A2,$74,$74,$74,$72 + .byte $44,$68,$B2,$32,$B2,$00,$22,$00 + .byte $1A,$1A,$26,$26,$72,$72,$88,$C8 + .byte $C4,$CA,$26,$48,$44,$44,$A2,$C8 + + + + diff --git a/libsrc/dbg/dbg.c b/libsrc/dbg/dbg.c new file mode 100644 index 000000000..e657b0c04 --- /dev/null +++ b/libsrc/dbg/dbg.c @@ -0,0 +1,1511 @@ +/* + * dbg.c + * + * Ullrich von Bassewitz, 08.08.1998 + * + */ + + + +#include +#include +#include +#include +#include +#include <6502.h> +#include + + + +/*****************************************************************************/ +/* Function forwards */ +/*****************************************************************************/ + + + +/* Forwards for handler functions */ +static char AsmHandler (void); +static char RegHandler (void); +static char StackHandler (void); +static char CStackHandler (void); +static char DumpHandler (void); +static char HelpHandler (void); + +/* Forwards for other functions */ +static void DisplayPrompt (char* s); +static void SingleStep (char StepInto); +static void RedrawStatic (char Frame); +static void Redraw (char Frame); +static char GetKeyUpdate (void); + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + +/* Color definitions */ +#ifdef __PLUS4__ +# define COLOR_BORDER (BCOLOR_DARKBLUE | CATTR_LUMA6) +# define COLOR_BACKGROUND COLOR_WHITE +# define COLOR_TEXTHIGH COLOR_BLACK +# define COLOR_TEXTLOW COLOR_GRAY1 +# define COLOR_FRAMEHIGH COLOR_BLACK +# define COLOR_FRAMELOW COLOR_GRAY2 +#else +# ifdef COLOR_GRAY3 +# define COLOR_BORDER COLOR_BLACK +# define COLOR_BACKGROUND COLOR_BLACK +# define COLOR_TEXTHIGH COLOR_WHITE +# define COLOR_TEXTLOW COLOR_GRAY3 +# define COLOR_FRAMEHIGH COLOR_WHITE +# define COLOR_FRAMELOW COLOR_GRAY3 +# else +# ifdef __APPLE2__ +# define COLOR_BORDER COLOR_BLACK +# define COLOR_BACKGROUND COLOR_BLACK +# define COLOR_TEXTHIGH COLOR_BLACK +# define COLOR_TEXTLOW COLOR_BLACK +# define COLOR_FRAMEHIGH COLOR_BLACK +# define COLOR_FRAMELOW COLOR_BLACK +# else +# define COLOR_BORDER COLOR_BLACK +# define COLOR_BACKGROUND COLOR_BLACK +# define COLOR_TEXTHIGH COLOR_WHITE +# define COLOR_TEXTLOW COLOR_WHITE +# define COLOR_FRAMEHIGH COLOR_WHITE +# define COLOR_FRAMELOW COLOR_WHITE +# endif +# endif +#endif + +/* Screen definitions */ +#ifdef __CBM610__ +# define BIGSCREEN +# define MAX_X 80 +# define MAX_Y 25 +# define DUMP_BYTES 16 +#else +# ifdef __APPLE2__ +# define MAX_X 40 +# define MAX_Y 24 +# define DUMP_BYTES 8 +# else +# ifdef __ATARI__ +# define MAX_X 40 +# define MAX_Y 24 +# define DUMP_BYTES 8 +# else +# define MAX_X 40 +# define MAX_Y 25 +# define DUMP_BYTES 8 +# endif +# endif +#endif + + + +/* Key definitions */ +#ifndef CH_F1 +# define CH_F1 '?' +#endif +#ifndef CH_F2 +# define CH_F2 0 +#endif +#ifndef CH_F3 +# define CH_F3 0 +#endif +#ifndef CH_F4 +# define CH_F4 0 +#endif +#ifndef CH_F5 +# define CH_F5 0 +#endif +#ifndef CH_F6 +# define CH_F6 0 +#endif +#ifndef CH_F7 +# define CH_F7 0 +#endif +#ifndef CH_F8 +# define CH_F8 0 +#endif + + + +/* Defines for opcodes */ +#define OPC_BRK 0x00 +#define OPC_BPL 0x10 +#define OPC_JSR 0x20 +#define OPC_BMI 0x30 +#define OPC_RTI 0x40 +#define OPC_JMP 0x4C +#define OPC_BVC 0x50 +#define OPC_RTS 0x60 +#define OPC_JMPIND 0x6C +#define OPC_BVS 0x70 +#define OPC_BCC 0x90 +#define OPC_BCS 0xB0 +#define OPC_BNE 0xD0 +#define OPC_BEQ 0xF0 + + + +/* Register values that are used also in the assembler stuff */ +extern unsigned char DbgSP; /* Stack pointer */ +extern unsigned DbgCS; /* C stack pointer */ +extern unsigned DbgHI; /* High 16 bit of primary reg */ + + + +/* Descriptor for one text line */ +typedef struct { + unsigned char x; + unsigned char y; + char* text; +} TextDesc; + +/* Window descriptor */ +typedef struct { + unsigned char fd_tl; /* Top left char */ + unsigned char fd_tr; /* Top right char */ + unsigned char fd_bl; /* Bottom left char */ + unsigned char fd_br; /* Bottom right char */ + unsigned char fd_x1, fd_y1; /* Upper left corner */ + unsigned char fd_x2, fd_y2; /* Lower right corner */ + unsigned char fd_width, fd_height; /* Redundant but faster */ + unsigned char fd_visible; /* Is the window currently visible? */ + char (*fd_func) (void); /* Handler function */ + unsigned char fd_textcount; /* Number of text lines to print */ + TextDesc* fd_text; /* Static text in the window */ +} FrameDesc; + + + +/* Texts for the windows */ +static TextDesc RegText [] = { + { 1, 0, "PC" }, + { 1, 1, "SR" }, + { 1, 2, "A" }, + { 1, 3, "X" }, + { 1, 4, "Y" }, + { 1, 5, "SP" }, + { 1, 6, "CS" }, + { 1, 7, "HI" } +}; +static TextDesc HelpText [] = { + { 1, 0, "F1 Help" }, + { 1, 1, "F2 Toggle breakpoint" }, + { 1, 2, "F3 Run until subroutine returns" }, + { 1, 3, "F4 Run to cursor" }, + { 1, 4, "F7 Step into" }, + { 1, 5, "F8 Step over" }, + { 1, 6, "1-5 Select active window" }, + { 1, 7, "+ Page down" }, + { 1, 8, "- Page up" }, + { 1, 9, "Cursor Move up/down" }, + { 1, 10, "c Continue" }, + { 1, 11, "f Follow instruction" }, + { 1, 12, "o Goto origin" }, + { 1, 13, "p Use as new PC value" }, + { 1, 14, "r Redraw screen" }, + { 1, 15, "q Quit" }, + { 1, 16, "s Skip next instruction" }, +}; + + +/* Window data */ +static FrameDesc AsmFrame = { + CH_ULCORNER, CH_TTEE, CH_LTEE, CH_CROSS, + 0, 0, MAX_X - 10, 15, + MAX_X - 11, 14, + 1, + AsmHandler, + 0, 0 +}; +static FrameDesc RegFrame = { + CH_TTEE, CH_URCORNER, CH_LTEE, CH_RTEE, + MAX_X - 10, 0, MAX_X - 1, 9, + 8, 8, + 1, + RegHandler, + sizeof (RegText) / sizeof (RegText [0]), RegText +}; +static FrameDesc StackFrame = { + CH_LTEE, CH_RTEE, CH_CROSS, CH_RTEE, + MAX_X - 10, 9, MAX_X - 1, 15, + 8, 5, + 1, + StackHandler, + 0, 0 +}; +static FrameDesc CStackFrame = { + CH_CROSS, CH_RTEE, CH_BTEE, CH_LRCORNER, + MAX_X - 10, 15, MAX_X - 1, MAX_Y - 1, + 8, MAX_Y - 17, + 1, + CStackHandler, + 0, 0 +}; +static FrameDesc DumpFrame = { + CH_LTEE, CH_CROSS, CH_LLCORNER, CH_BTEE, + 0, 15, MAX_X - 10, MAX_Y-1, + MAX_X - 11, MAX_Y - 17, + 1, + DumpHandler, + 0, 0 +}; +static FrameDesc HelpFrame = { + CH_ULCORNER, CH_URCORNER, CH_LLCORNER, CH_LRCORNER, + 0, 0, MAX_X - 1, MAX_Y-1, + MAX_X - 2, MAX_Y - 2, + 0, + HelpHandler, + sizeof (HelpText) / sizeof (HelpText [0]), HelpText +}; +static FrameDesc* Frames [] = { + &AsmFrame, + &RegFrame, + &StackFrame, + &CStackFrame, + &DumpFrame, + &HelpFrame +}; + +/* Number of active frame, -1 = none */ +static int ActiveFrame = -1; + +/* Window names */ +#define WIN_ASM 0 +#define WIN_REG 1 +#define WIN_STACK 2 +#define WIN_CSTACK 3 +#define WIN_DUMP 4 +#define WIN_HELP 5 + +/* Other window data */ +static unsigned AsmAddr; /* Start address of output */ +static unsigned DumpAddr; /* Start address of output */ +static unsigned CStackAddr; /* Start address of output */ +static unsigned char StackAddr; /* Start address of output */ + + + +/* Prompt line data */ +static char* ActivePrompt = 0; /* Last prompt line displayed */ +static char PromptColor; /* Color behind prompt */ +static char PromptLength; /* Length of current prompt string */ + + + +/* Values for the bk_use field of struct BreakPoint */ +#define BRK_EMPTY 0x00 +#define BRK_USER 0x01 +#define BRK_TMP 0x80 + +/* Structure describing a breakpoint */ +typedef struct { + unsigned bk_addr; /* Address, 0 if unused */ + unsigned char bk_opc; /* Opcode */ + unsigned char bk_use; /* 1 if in use, 0 otherwise */ +} BreakPoint; + + + +/* Temporary breakpoints - also accessed from the assembler source */ +#define MAX_USERBREAKS 10 +unsigned char DbgBreakCount = 0; +BreakPoint DbgBreaks [MAX_USERBREAKS+2]; + + + +/*****************************************************************************/ +/* Forwards for functions in the assembler source */ +/*****************************************************************************/ + + + +BreakPoint* DbgGetBreakSlot (void); +/* Search for a free breakpoint slot. Return a pointer to the slot or 0 */ + +BreakPoint* DbgIsBreak (unsigned Addr); +/* Check if there is a user breakpoint at the given address, if so, return + * a pointer to the slot, else return 0. + */ + + + +/*****************************************************************************/ +/* Frame/window drawing code */ +/*****************************************************************************/ + + + +static void DrawFrame (FrameDesc* F, char Active) +/* Draw one window frame */ +{ + TextDesc* T; + unsigned char Count; + unsigned char tl, tr, bl, br; + unsigned char x1, y1, width; + unsigned char OldColor; + + /* Determine the characters for the corners, set frame color */ + if (Active) { + OldColor = textcolor (COLOR_FRAMEHIGH); + tl = CH_ULCORNER; + tr = CH_URCORNER; + bl = CH_LLCORNER; + br = CH_LRCORNER; + } else { + OldColor = textcolor (COLOR_FRAMELOW); + tl = F->fd_tl; + tr = F->fd_tr; + bl = F->fd_bl; + br = F->fd_br; + } + + /* Get the coordinates into locals for faster access */ + x1 = F->fd_x1; + y1 = F->fd_y1; + width = F->fd_width; + + /* Top line */ + cputcxy (x1, y1, tl); + chline (width); + cputc (tr); + + /* Left line */ + cvlinexy (x1, ++y1, F->fd_height); + + /* Bottom line */ + cputc (bl); + chline (width); + cputc (br); + + /* Right line */ + cvlinexy (F->fd_x2, y1, F->fd_height); + + /* If the window has static text associated, print the text */ + textcolor (COLOR_TEXTLOW); + Count = F->fd_textcount; + T = F->fd_text; + while (Count--) { + cputsxy (x1 + T->x, y1 + T->y, T->text); + ++T; + } + + /* Set the old color */ + textcolor (OldColor); +} + + + +static void DrawFrames (void) +/* Draw all frames */ +{ + unsigned char I; + FrameDesc* F; + + /* Build the frame layout of the screen */ + for (I = 0; I < sizeof (Frames) / sizeof (Frames [0]); ++I) { + F = Frames [I]; + if (F->fd_visible) { + DrawFrame (F, 0); + } + } +} + + + +static void ActivateFrame (int Num, unsigned char Clear) +/* Activate a new frame, deactivate the old one */ +{ + unsigned char y; + FrameDesc* F; + + if (ActiveFrame != Num) { + + /* Deactivate the old one */ + if (ActiveFrame >= 0) { + DrawFrame (Frames [ActiveFrame], 0); + } + + /* Activate the new one */ + if ((ActiveFrame = Num) >= 0) { + F = Frames [ActiveFrame]; + /* Clear the frame if requested */ + if (Clear) { + for (y = F->fd_y1+1; y < F->fd_y2; ++y) { + cclearxy (F->fd_x1+1, y, F->fd_width); + } + } + DrawFrame (F, 1); + } + + /* Redraw the current prompt line */ + DisplayPrompt (ActivePrompt); + + } +} + + + +/*****************************************************************************/ +/* Prompt line */ +/*****************************************************************************/ + + + +static void DisplayPrompt (char* s) +/* Display a prompt */ +{ + unsigned char OldColor; + + /* Remember the current color */ + OldColor = textcolor (COLOR_TEXTHIGH); + + /* Clear the old prompt if there is one */ + if (ActivePrompt) { + textcolor (PromptColor); + chlinexy ((MAX_X - PromptLength) / 2, MAX_Y-1, PromptLength); + } + + /* Get the new prompt data */ + ActivePrompt = s; + PromptColor = OldColor; + PromptLength = strlen (ActivePrompt); + + /* Display the new prompt */ + textcolor (COLOR_TEXTHIGH); + cputsxy ((MAX_X - PromptLength) / 2, MAX_Y-1, ActivePrompt); + + /* Restore the old color */ + textcolor (PromptColor); +} + + + +static void HelpPrompt (void) +/* Display a prompt line mentioning the help key */ +{ + DisplayPrompt ("Press F1 for help"); +} + + + +static void AnyKeyPrompt (void) +{ + DisplayPrompt ("Press any key to continue"); +} + + + +static char Input (char* Prompt, char* Buf, unsigned char Count) +/* Read input from the user, return 1 on success, 0 if aborted */ +{ + int Frame; + unsigned char OldColor; + unsigned char OldCursor; + unsigned char x1; + unsigned char i; + unsigned char done; + char c; + + /* Clear the current prompt line */ + cclearxy (0, MAX_Y-1, MAX_X); + + /* Display the new prompt */ + OldColor = textcolor (COLOR_TEXTHIGH); + cputsxy (0, MAX_Y-1, Prompt); + textcolor (COLOR_TEXTLOW); + + /* Remember where we are, enable the cursor */ + x1 = wherex (); + OldCursor = cursor (1); + + /* Get input and handle it */ + i = done = 0; + do { + c = cgetc (); + if (isalnum (c) && i < Count) { + Buf [i] = c; + cputcxy (x1 + i, MAX_Y-1, c); + ++i; + } else if (i > 0 && c == CH_DEL) { + --i; + cputcxy (x1 + i, MAX_Y-1, ' '); + gotoxy (x1 + i, MAX_Y-1); + } else if (c == '\n') { + Buf [i] = '\0'; + done = 1; + } else if (c == CH_ESC) { + /* Abort */ + done = 2; + } + } while (!done); + + /* Reset settings, display old prompt line */ + cursor (OldCursor); + textcolor (OldColor); + DrawFrames (); + Frame = ActiveFrame; + ActiveFrame = -1; + ActivateFrame (Frame, 0); + + return (done == 1); +} + + + +static int InputHex (char* Prompt, unsigned* Val) +/* Prompt for a hexadecimal value */ +{ + char Buf [5]; + char* P; + char C; + unsigned V; + + /* Read input from the user (4 digits max), check input */ + if (Input (Prompt, Buf, sizeof (Buf)-1) && isxdigit (Buf [0])) { + + /* Check the characters and convert to hex */ + P = Buf; + V = 0; + while ((C = *P) && isxdigit (C)) { + V <<= 4; + if (isdigit (C)) { + C -= '0'; + } else { + C = toupper (C) - ('A' - 10); + } + V += C; + ++P; + } + + /* Assign the value */ + *Val = V; + + /* Success */ + return 1; + + } else { + + /* Failure */ + return 0; + + } +} + + + +static int InputGoto (unsigned* Addr) +/* Prompt "Goto" and read an address */ +{ + return InputHex ("Goto: ", Addr); +} + + + +static void ErrorPrompt (char* Msg) +/* Display an error message and wait for a key */ +{ + /* Save the current prompt */ + char* OldPrompt = ActivePrompt; + + /* Display the new one */ + DisplayPrompt (Msg); + + /* Wait for a key and discard it */ + cgetc (); + + /* Restore the old prompt */ + DisplayPrompt (OldPrompt); +} + + + +static void BreakInRomError (void) +/* Print an error message if we cannot set a breakpoint */ +{ + ErrorPrompt ("Cannot set breakpoint - press a key"); +} + + + +/*****************************************************************************/ +/* Breakpoint handling */ +/*****************************************************************************/ + + + +static void DbgSetTmpBreak (unsigned Addr) +/* Set a breakpoint */ +{ + BreakPoint* B = DbgGetBreakSlot (); + B->bk_addr = Addr; + B->bk_use = BRK_TMP; +} + + + +static void DbgToggleUserBreak (unsigned Addr) +/* Set a breakpoint */ +{ + BreakPoint* B = DbgIsBreak (Addr); + + if (B) { + /* We have a breakpoint, remove it */ + B->bk_use = BRK_EMPTY; + --DbgBreakCount; + } else { + /* We don't have a breakpoint, set one */ + if (DbgBreakCount >= MAX_USERBREAKS) { + ErrorPrompt ("Too many breakpoints - press a key"); + } else { + /* Test if we can set a breakpoint at that address */ + if (!DbgIsRAM (Addr)) { + BreakInRomError (); + } else { + /* Set the breakpoint */ + B = DbgGetBreakSlot (); + B->bk_addr = Addr; + B->bk_use = BRK_USER; + ++DbgBreakCount; + } + } + } +} + + + +static void DbgResetTmpBreaks (void) +/* Reset all temporary breakpoints */ +{ + unsigned char i; + BreakPoint* B = DbgBreaks; + + for (i = 0; i < MAX_USERBREAKS; ++i) { + if (B->bk_use == BRK_TMP) { + B->bk_use = BRK_EMPTY; + } + ++B; + } +} + + + +static int DbgTmpBreaksOk (void) +/* Check if the temporary breakpoints can be set, if so, return 1, if not, + * reset them all and return 0. + */ +{ + unsigned char i; + BreakPoint* B = DbgBreaks; + for (i = 0; i < MAX_USERBREAKS; ++i) { + if (B->bk_use == BRK_TMP && !DbgIsRAM (B->bk_addr)) { + BreakInRomError (); + DbgResetTmpBreaks (); + return 0; + } + ++B; + } + return 1; +} + + + +/*****************************************************************************/ +/* Assembler window stuff */ +/*****************************************************************************/ + + + +static unsigned AsmBack (unsigned mem, unsigned char lines) +/* Go back in the assembler window the given number of lines (calculate + * new start address). + */ +{ + unsigned cur; + unsigned adr [32]; + unsigned char in; + + unsigned offs = 6; + while (1) { + in = 0; + cur = mem - (lines * 3) - offs; + while (1) { + cur += DbgDisAsmLen (cur); + adr [in] = cur; + in = (in + 1) & 0x1F; + if (cur >= mem) { + if (cur == mem || offs == 12) { + /* Found */ + return adr [(in - lines - 1) & 0x1F]; + } else { + /* The requested address is inside an instruction, go back + * one more byte and try again. + */ + ++offs; + break; + } + } + } + } +} + + + +static unsigned UpdateAsm (void) +/* Update the assembler window starting at the given address */ +{ + char buf [MAX_X]; + unsigned char len; + unsigned char y; + unsigned char width = AsmFrame.fd_width; + unsigned char x = AsmFrame.fd_x1 + 1; + unsigned m = AsmBack (AsmAddr, 2); + + for (y = AsmFrame.fd_y1+1; y < AsmFrame.fd_y2; ++y) { + len = DbgDisAsm (m, buf, width); + if (m == brk_pc) { + buf [4] = '-'; + buf [5] = '>'; + } + if (DbgIsBreak (m)) { + buf [5] = '*'; + } + if (m == AsmAddr) { + revers (1); + cputsxy (1, y, buf); + revers (0); + } else { + cputsxy (1, y, buf); + } + m += len; + } + return m; +} + + + +static unsigned AsmArg16 (void) +/* Return a 16 bit argument */ +{ + return *(unsigned*)(AsmAddr+1); +} + + + +static void AsmFollow (void) +/* Follow the current instruction */ +{ + switch (*(unsigned char*) AsmAddr) { + + case OPC_JMP: + case OPC_JSR: + AsmAddr = AsmArg16 (); + break; + + case OPC_JMPIND: + AsmAddr = *(unsigned*)AsmArg16 (); + break; + + case OPC_BPL: + case OPC_BMI: + case OPC_BVC: + case OPC_BVS: + case OPC_BCC: + case OPC_BCS: + case OPC_BNE: + case OPC_BEQ: + AsmAddr = AsmAddr + 2 + *(signed char*)(AsmAddr+1); + break; + + case OPC_RTS: + AsmAddr = (*(unsigned*) (DbgSP + 0x101) + 1); + break; + + case OPC_RTI: + AsmAddr = *(unsigned*) (DbgSP + 0x102); + break; + + } +} + + + +static void AsmHome (void) +/* Set the cursor to home position */ +{ + AsmAddr = brk_pc; +} + + + +static void InitAsm (void) +/* Initialize the asm window */ +{ + AsmHome (); + UpdateAsm (); +} + + + +static char AsmHandler (void) +/* Get characters and handle them */ +{ + char c; + unsigned Last; + + while (1) { + + /* Update the window contents */ + Last = UpdateAsm (); + + /* Read and handle input */ + switch (c = GetKeyUpdate ()) { + + case '+': + AsmAddr = Last; + break; + + case '-': + AsmAddr = AsmBack (AsmAddr, AsmFrame.fd_height); + break; + + case CH_F2: + DbgToggleUserBreak (AsmAddr); + break; + + case 'f': + AsmFollow (); + break; + + case 'g': + InputGoto (&AsmAddr); + break; + + case 'o': + AsmHome (); + break; + + case 'p': + brk_pc = AsmAddr; + break; + + case CH_CURS_UP: + AsmAddr = AsmBack (AsmAddr, 1); + break; + + case CH_CURS_DOWN: + AsmAddr += DbgDisAsmLen (AsmAddr); + break; + + default: + return c; + + } + } +} + + + +/*****************************************************************************/ +/* Register window stuff */ +/*****************************************************************************/ + + + +static unsigned UpdateReg (void) +/* Update the register window */ +{ + unsigned char x1 = RegFrame.fd_x1 + 5; + unsigned char x2 = x1 + 2; + unsigned char y = RegFrame.fd_y1; + + /* Print the register contents */ + gotoxy (x1, ++y); cputhex16 (brk_pc); + gotoxy (x2, ++y); cputhex8 (brk_sr); + gotoxy (x2, ++y); cputhex8 (brk_a); + gotoxy (x2, ++y); cputhex8 (brk_x); + gotoxy (x2, ++y); cputhex8 (brk_y); + gotoxy (x2, ++y); cputhex8 (DbgSP); + gotoxy (x1, ++y); cputhex16 (DbgCS); + gotoxy (x1, ++y); cputhex16 (DbgHI); + + /* Not needed */ + return 0; +} + + + +static void InitReg (void) +/* Initialize the register window */ +{ + UpdateReg (); +} + + + +static char RegHandler (void) +/* Get characters and handle them */ +{ + return GetKeyUpdate (); +} + + + +/*****************************************************************************/ +/* Stack window stuff */ +/*****************************************************************************/ + + + +static unsigned UpdateStack (void) +/* Update the stack window */ +{ + unsigned char mem = StackAddr; + unsigned char x1 = StackFrame.fd_x1 + 1; + unsigned char x2 = x1 + 6; + unsigned char y; + + for (y = StackFrame.fd_y2-1; y > StackFrame.fd_y1; --y) { + gotoxy (x1, y); + cputhex8 (mem); + gotoxy (x2, y); + cputhex8 (* (unsigned char*) (mem + 0x100)); + ++mem; + } + return mem; +} + + + +static void StackHome (void) +/* Set the cursor to home position */ +{ + StackAddr = DbgSP + 1; +} + + + +static void InitStack (void) +/* Initialize the stack window */ +{ + StackHome (); + UpdateStack (); +} + + + +static char StackHandler (void) +/* Get characters and handle them */ +{ + char c; + unsigned char BytesPerPage = StackFrame.fd_height; + + while (1) { + + /* Read and handle input */ + switch (c = GetKeyUpdate ()) { + + case '+': + StackAddr += BytesPerPage; + break; + + case '-': + StackAddr -= BytesPerPage; + break; + + case 'o': + StackHome (); + break; + + case CH_CURS_UP: + --StackAddr; + break; + + case CH_CURS_DOWN: + ++StackAddr; + break; + + default: + return c; + + } + + /* Update the window contents */ + UpdateStack (); + } +} + + + +/*****************************************************************************/ +/* C Stack window stuff */ +/*****************************************************************************/ + + + +static unsigned UpdateCStack (void) +/* Update the C stack window */ +{ + unsigned mem = CStackAddr; + unsigned char x = CStackFrame.fd_x1 + 5; + unsigned char y; + + for (y = CStackFrame.fd_y2-1; y > CStackFrame.fd_y1; --y) { + gotoxy (x, y); + cputhex16 (* (unsigned*)mem); + mem += 2; + } + cputsxy (CStackFrame.fd_x1+1, CStackFrame.fd_y2-1, "->"); + return mem; +} + + + +static void CStackHome (void) +/* Set the cursor to home position */ +{ + CStackAddr = DbgCS; +} + + + +static void InitCStack (void) +/* Initialize the C stack window */ +{ + CStackHome (); + UpdateCStack (); +} + + + +static char CStackHandler (void) +/* Get characters and handle them */ +{ + char c; + unsigned char BytesPerPage = CStackFrame.fd_height * 2; + + while (1) { + + /* Read and handle input */ + switch (c = GetKeyUpdate ()) { + + case '+': + CStackAddr += BytesPerPage; + break; + + case '-': + CStackAddr -= BytesPerPage; + break; + + case 'o': + CStackHome (); + break; + + case CH_CURS_UP: + CStackAddr -= 2; + break; + + case CH_CURS_DOWN: + CStackAddr += 2; + break; + + default: + return c; + + } + + /* Update the window contents */ + UpdateCStack (); + } +} + + + +/*****************************************************************************/ +/* Dump window stuff */ +/*****************************************************************************/ + + + +static unsigned UpdateDump (void) +/* Update the dump window */ +{ + char Buf [MAX_X]; + unsigned char y; + unsigned mem = DumpAddr; + unsigned char x = DumpFrame.fd_x1 + 1; + unsigned char* p = (unsigned char*) mem; + + for (y = DumpFrame.fd_y1+1; y < DumpFrame.fd_y2; ++y) { + cputsxy (x, y, DbgMemDump (mem, Buf, DUMP_BYTES)); + mem += DUMP_BYTES; + } + return mem; +} + + + +static void DumpHome (void) +/* Set the cursor to home position */ +{ + DumpAddr = 0; +} + + + +static char DumpHandler (void) +/* Get characters and handle them */ +{ + char c; + unsigned BytesPerPage = DumpFrame.fd_height * 8; + + while (1) { + + /* Read and handle input */ + switch (c = GetKeyUpdate ()) { + + case '+': + DumpAddr += BytesPerPage; + break; + + case '-': + DumpAddr -= BytesPerPage; + break; + + case 'g': + InputGoto (&DumpAddr); + break; + + case 'o': + DumpHome (); + break; + + case CH_CURS_UP: + DumpAddr -= 8; + break; + + case CH_CURS_DOWN: + DumpAddr += 8; + break; + + default: + return c; + + } + + /* Update the window contents */ + UpdateDump (); + } +} + + + +/*****************************************************************************/ +/* Help window stuff */ +/*****************************************************************************/ + + + +static char HelpHandler (void) +/* Get characters and handle them */ +{ + /* Activate the frame */ + int OldActive = ActiveFrame; + ActivateFrame (WIN_HELP, 1); + + /* Say that we're waiting for a key */ + AnyKeyPrompt (); + + /* Get a character and discard it */ + cgetc (); + + /* Redraw the old stuff */ + Redraw (OldActive); + + /* Done, return no char */ + return 0; +} + + + +/*****************************************************************************/ +/* Singlestep */ +/*****************************************************************************/ + + + +static unsigned GetArg16 (void) +/* Read an argument */ +{ + return *(unsigned*)(brk_pc+1); +} + + + +static unsigned GetStack16 (unsigned char Offs) +/* Fetch a 16 bit value from stack top */ +{ + return *(unsigned*)(DbgSP+Offs+0x101); +} + + + +static void SetRTSBreak (void) +/* Set a breakpoint at the return target */ +{ + DbgSetTmpBreak (GetStack16 (0) + 1); +} + + + +static void SingleStep (char StepInto) +{ + signed char Offs; + + switch (*(unsigned char*) brk_pc) { + + case OPC_JMP: + /* Set breakpoint at target */ + DbgSetTmpBreak (GetArg16 ()); + return; + + case OPC_JMPIND: + /* Indirect jump, ignore CPU error when crossing page */ + DbgSetTmpBreak (*(unsigned*)GetArg16 ()); + return; + + case OPC_BPL: + case OPC_BMI: + case OPC_BVC: + case OPC_BVS: + case OPC_BCC: + case OPC_BCS: + case OPC_BNE: + case OPC_BEQ: + /* Be sure not to set the breakpoint twice if this is a jump to + * the following instruction. + */ + Offs = *(signed char*)(brk_pc+1); + if (Offs) { + DbgSetTmpBreak (brk_pc + Offs + 2); + } + break; + + case OPC_RTS: + /* Set a breakpoint at the return target */ + SetRTSBreak (); + return; + + case OPC_RTI: + /* Set a breakpoint at the return target */ + DbgSetTmpBreak (GetStack16 (1)); + return; + + case OPC_JSR: + if (StepInto) { + /* Set breakpoint at target */ + DbgSetTmpBreak (GetArg16 ()); + return; + } + break; + } + + /* Place a breakpoint behind the instruction */ + DbgSetTmpBreak (brk_pc + DbgDisAsmLen (brk_pc)); +} + + + +/*****************************************************************************/ +/* High level window handling */ +/*****************************************************************************/ + + + +static void RedrawStatic (char Frame) +/* Redraw static display stuff */ +{ + /* Reset the active frame */ + ActiveFrame = -1; + + /* Clear the screen hide the cursor */ + bordercolor (COLOR_BORDER); + bgcolor (COLOR_BACKGROUND); + clrscr (); + cursor (0); + + /* Build the frame layout of the screen */ + textcolor (COLOR_FRAMELOW); + DrawFrames (); + + /* Draw the prompt line */ + HelpPrompt (); + + /* Activate the active frame */ + ActivateFrame (Frame, 0); +} + + + +static void Redraw (char Frame) +/* Redraw the display in case it's garbled */ +{ + /* Redraw the static stuff */ + RedrawStatic (Frame); + + /* Init the window contents */ + UpdateAsm (); + UpdateReg (); + UpdateStack (); + UpdateCStack (); + UpdateDump (); +} + + + +static char GetKeyUpdate (void) +/* Wait for a key updating the windows in the background */ +{ + static unsigned char Win; + + /* While there are no keys... */ + while (!kbhit ()) { + + switch (Win) { + + case 0: + UpdateAsm (); + break; + + case 1: + UpdateStack (); + break; + + case 2: + UpdateCStack (); + break; + + case 3: + UpdateDump (); + break; + } + + Win = (Win + 1) & 0x03; + + } + + /* We have a key - return it */ + return cgetc (); +} + + + +/*****************************************************************************/ +/* Externally visible functions */ +/*****************************************************************************/ + + + +void DbgEntry (void) +/* Start up the debugger */ +{ + static unsigned char FirstTime = 1; + char c; + char done; + + /* If this is the first call, setup the display */ + if (FirstTime) { + FirstTime = 0; + + /* Draw the window, default active frame is ASM frame */ + RedrawStatic (WIN_ASM); + InitAsm (); + InitReg (); + InitStack (); + InitCStack (); + UpdateDump (); + } + + /* Only initialize variables here, don't do a display update. The actual + * display update will be done while waiting for user input. + */ + AsmHome (); + UpdateReg (); /* Must update this (static later) */ + StackHome (); + CStackHome (); + DumpHome (); + + /* Wait for user input */ + done = 0; + while (!done) { + c = Frames [ActiveFrame]->fd_func (); + switch (c) { + + case '1': + case '2': + case '3': + case '4': + case '5': + ActivateFrame (c - '1', 0); + break; + + case CH_F1: + HelpHandler (); + break; + + case CH_F3: + /* Go until return */ + SetRTSBreak (); + done = 1; + break; + + case CH_F4: + /* Go to cursor, only possible if cursor not at current PC */ + if (AsmAddr != brk_pc) { + DbgSetTmpBreak (AsmAddr); + done = 1; + } + break; + + case ' ': + case '\n': + case CH_F7: + case CH_F8: + SingleStep (c == CH_F7 || c == ' '); + if (DbgTmpBreaksOk ()) { + /* Could set breakpoints */ + done = 1; + } + break; + + case 'c': + case 0: + done = 1; + break; + + case 's': + /* Skip instruction */ + brk_pc += DbgDisAsmLen (brk_pc); + InitAsm (); + break; + + case 'r': + /* Redraw screen */ + Redraw (ActiveFrame); + break; + + case 'q': + /* Quit program */ + clrscr (); + exit (1); + + } + } +} + + diff --git a/libsrc/dbg/dbgdasm.s b/libsrc/dbg/dbgdasm.s new file mode 100644 index 000000000..80c90e303 --- /dev/null +++ b/libsrc/dbg/dbgdasm.s @@ -0,0 +1,295 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; unsigned DbgDisAsm (char* buf, unsigned addr); +; unsigned DbgDisAsm (unsigned addr); +; +; +; Part of this code is taken from the Plus/4 machine language monitor +; (TEDMon). +; + + .import utsta0, popax + .import __hextab, OffsetTab, AdrFlagTab + .import SymbolTab1, SymbolTab2, MnemoTab1, MnemoTab2 + + + +; ------------------------------------------------------------------------- +; Equates for better readability + + .importzp sreg, tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3 + +BufIndex = tmp1 ; Index into output buffer +OperandLen = tmp2 ; Length of operand +BufLen = tmp3 ; Length of output buffer +AdrFlagBuf = tmp4 ; Flag for addressing mode +YSave = sreg ; Temp storage +XSave = sreg+1 ; Dito +BufPtr = ptr1 ; Pointer to output buffer +MemPtr = ptr2 ; Pointer to memory to disassemble +MnemoBuf = ptr3 ; Buffer for decoding mnemonic + + +; ------------------------------------------------------------------------- +; Main entries + + .export _DbgDisAsm, _DbgDisAsmLen + +.proc _DbgDisAsm + sta BufLen ; Save the buffer length + jsr popax ; Get the buffer pointer + sta BufPtr + stx BufPtr+1 + jsr popax ; Get the address + sta MemPtr + stx MemPtr+1 + lda #0 + sta BufIndex ; Initialize index into buffer + jsr DisAssLine ; Disassemble one line into the buffer + + lda BufLen ; Get requested length + sec + sbc BufIndex + beq L2 + tax ; Count into X + ldy BufIndex + lda #$20 ; Get a space +L1: sta (BufPtr),y + iny + dex + bne L1 +L2: lda #0 ; Add C string terminator + sta (BufPtr),y + beq disassret + +.endproc + + +_DbgDisAsmLen: + sta MemPtr ; Save address + stx MemPtr+1 + ldy #$00 + lda (MemPtr),y ; Get the opcode from memory... + jsr AnalyzeOPCode ; ...and analyze it +disassret: + ldx OperandLen ; Get length of operand + inx ; Adjust for opcode byte + txa + jmp utsta0 ; Set condition codes + +; ------------------------------------------------------------------------- +; Helper functions + + +Put3Spaces: + jsr PutSpace +Put2Spaces: + jsr PutSpace +PutSpace: + lda #$20 +PutChar: + sty YSave ; Save Y + ldy BufIndex ; Get current line pointer + cpy BufLen ; Be sure not to overflow the buffer + bcs PC9 + sta (BufPtr),y ; store character + iny ; bump index + sty BufIndex +PC9: ldy YSave ; get old value + rts + +; Print the 16 bit hex value in X/Y + +PutHex16: + txa + jsr PutHex8 + tya + +; Print 8 bit value in A, save X and Y + +PutHex8: + stx XSave + sty YSave + ldy BufIndex + pha + lsr a + lsr a + lsr a + lsr a + tax + lda __hextab,x + sta (BufPtr),y + iny + pla + and #$0F + tax + lda __hextab,x + sta (BufPtr),y + iny + sty BufIndex + ldy YSave + ldx XSave + rts + +; ------------------------------------------------------------------------- +; Eine Zeile disassemblieren + +DisAssLine: + ldy MemPtr + ldx MemPtr+1 + jsr PutHex16 ; Print the address + jsr Put2Spaces ; Add some space + ldy #$00 + lda (MemPtr),y ; Get the opcode from memory... + jsr AnalyzeOPCode ; ...and analyze it + pha ; Save mnemonic + ldx OperandLen ; Number of bytes + +; Print the bytes that make up the instruction + + inx +L2083: dex + bpl L208C ; Print the instruction bytes + jsr Put3Spaces ; If none left, print spaces instead + jmp L2094 +L208C: lda (MemPtr),y ; Get a byte from memory + jsr PutHex8 ; ...and print it + jsr PutSpace ; Add some space + +L2094: iny ; Next one... + cpy #$03 ; Maximum is three + bcc L2083 ; + + jsr Put2Spaces ; Add some space after bytes + +; Print the assembler mnemonic + + pla ; Get mnemonic code + ldx #$03 + jsr PutMnemo ; Print the mnemonic + ldx #$06 + +; Print the operand + +L20A4: cpx #$03 + bne L20BA + ldy OperandLen + beq L20BA + +L20AC: lda AdrFlagBuf + cmp #$E8 ; Branch? + lda (MemPtr),y ; Get branch offset + bcs GetBranchAdr ; If branch: Calculate address + jsr PutHex8 ; Otherwise print 8bit value + dey + bne L20AC + +L20BA: asl AdrFlagBuf + bcc L20CC + lda SymbolTab1-1,x + jsr PutChar + lda SymbolTab2-1,x + beq L20CC + jsr PutChar + +L20CC: dex + bne L20A4 + rts + +; If the instruction is a branch, calculate the absolute address of the +; branch target and print it. + +GetBranchAdr: + jsr L20DD + clc + adc #$01 + bne L20D9 + inx ; Bump high byte +L20D9: tay + jmp PutHex16 ; Output address + +L20DD: ldx MemPtr+1 + tay + bpl L20E3 + dex +L20E3: adc MemPtr + bcc L20E8 + inx ; Bump high byte +L20E8: rts + +; ------------------------------------------------------------------------- +; Subroutine to analyze an opcode byte in A. Will return a byte that +; encodes the mnemonic, and will set the number of bytes needed for this +; instruction in OperandLen + +AnalyzeOPCode: + tay + lsr a + bcc L20F8 + lsr a + bcs L2107 + cmp #$22 + beq L2107 + and #$07 + ora #$80 +L20F8: lsr a + tax + lda OffsetTab,x + bcs L2103 + lsr a + lsr a + lsr a + lsr a +L2103: and #$0F + bne L210B +L2107: ldy #$80 + lda #$00 +L210B: tax + lda AdrFlagTab,x + sta AdrFlagBuf + and #$03 + sta OperandLen + tya + and #$8F + tax + tya + ldy #$03 + cpx #$8A + beq L212B + +L2120: lsr a + bcc L212B + lsr a +L2124: lsr a + ora #$20 + dey + bne L2124 + iny +L212B: dey + bne L2120 + rts + +; ------------------------------------------------------------------------- +; Print the mnemonic with code in A (that code was returned by +; AnalyzeOpcode). + +PutMnemo: + tay + lda MnemoTab1,y + sta MnemoBuf + lda MnemoTab2,y + sta MnemoBuf+1 +L213A: lda #$00 + ldy #$05 ; 3*5 bits in two bytes +L213E: asl MnemoBuf+1 + rol MnemoBuf + rol a + dey + bne L213E + adc #$3F + jsr PutChar + dex + bne L213A + jmp PutSpace + diff --git a/libsrc/dbg/dbgdump.s b/libsrc/dbg/dbgdump.s new file mode 100644 index 000000000..ff8818f96 --- /dev/null +++ b/libsrc/dbg/dbgdump.s @@ -0,0 +1,81 @@ +; +; Ullrich von Bassewitz, 11.08.1998 +; +; char* DbgMemDump (unsigend Addr, char* Buf, unsigned char Length); +; + + .export _DbgMemDump + .import addysp1 + .import __hextab + .importzp sp, tmp2, tmp3, tmp4, ptr3, ptr4 + +_DbgMemDump: + ldy #0 + lda (sp),y ; Get length + sta tmp4 + iny + lda (sp),y ; Get the string buffer + sta ptr3 + iny + lda (sp),y + sta ptr3+1 + iny + lda (sp),y ; Get the address + sta ptr4 + iny + lda (sp),y + sta ptr4+1 + jsr addysp1 ; Drop the parameters + + lda #0 + sta tmp2 ; String index + sta tmp3 ; Byte index + +; Print the address + + lda ptr4+1 ; Hi address byte + jsr dump ; Print address + lda ptr4 ; Lo address byte + jsr dump + jsr putspace ; Add a space + +dump1: dec tmp4 ; Bytes left? + bmi dump9 ; Jump if no + jsr putspace ; Add a space + ldy tmp3 + inc tmp3 + lda (ptr4),y + jsr dump + jmp dump1 + +dump9: lda #0 + ldy tmp2 + sta (ptr3),y ; Add string terminator + lda ptr3 + ldx ptr3+1 ; We assume this is not zero + rts + +; Dump one hex byte + +dump: pha + lsr a + lsr a + lsr a + lsr a + tax + lda __hextab,x + jsr putc + pla + and #$0F + tax + lda __hextab,x +putc: ldy tmp2 + inc tmp2 + sta (ptr3),y + rts + +putspace: + lda #$20 + bne putc + + diff --git a/libsrc/dbg/dbgisram.s b/libsrc/dbg/dbgisram.s new file mode 100644 index 000000000..f231b3fbb --- /dev/null +++ b/libsrc/dbg/dbgisram.s @@ -0,0 +1,55 @@ +; +; Ullrich von Bassewitz, 10.08.1998 +; +; int DbgIsRAM (unsigned Addr); +; + + .export _DbgIsRAM + .import popax, return0, return1 + .importzp ptr1 + +_DbgIsRAM: + sta ptr1 ; Store the address + stx ptr1+1 + + ldy #0 + php ; Save I flag + sei ; Disable interrupts + + lda (ptr1),y ; Get old value + pha ; ...and save it + + ldx #3 +L1: lda TestVal,x + jsr CheckCell + bne L2 + dex + bpl L1 + +; This seems to be RAM + + pla + sta (ptr1),y ; Restore old value + plp ; Restore old I flag + jmp return1 + +; No RAM at this address + +L2: pla + sta (ptr1),y ; Restore old value + plp ; Restore old I flag + jmp return0 + +; Check one memory cell + +CheckCell: + sta (ptr1),y + cmp (ptr1),y ; Could we write it? + rts + + +.rodata +TestVal: + .byte $55, $AA, $33, $CC + + diff --git a/libsrc/dbg/dbgsupp.s b/libsrc/dbg/dbgsupp.s new file mode 100644 index 000000000..64ec3c861 --- /dev/null +++ b/libsrc/dbg/dbgsupp.s @@ -0,0 +1,201 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; Support routines for the debugger +; + + .export _DbgInit + .export _DbgSP, _DbgCS, _DbgHI + .import popax, return0, _DbgEntry, _set_brk, _end_brk + .import _DbgBreaks + .import _brk_pc + .importzp sp, sreg, ptr1, tmp1, zpspace + +; C callable function, will install the debugger + +_DbgInit: + lda #DbgBreak + jmp _set_brk + + +; Entry for the break vector. + +DbgBreak: + pla + sta retsav + pla + sta retsav+1 + + cli + tsx ; Stack pointer + stx _DbgSP + + jsr DbgSwapZP ; Swap stuff + lda #DbgStack + sta sp+1 + jsr ResetDbgBreaks ; Reset temporary breakpoints + jsr _DbgEntry ; Call C code + jsr SetDbgBreaks ; Set temporary breakpoints + jsr DbgSwapZP ; Swap stuff back + + lda retsav+1 + pha + lda retsav + pha + rts + + + +; Stack used when in debugger mode + +.bss + .res 256 +DbgStack: + +; Swap space for the the C temporaries + +CTemp: +_DbgCS: .res 2 ; sp +_DbgHI: .res 2 ; sreg + .res 22 ; Other stuff +_DbgSP: .res 1 +retsav: .res 2 ; Save buffer for return address + +.code + +; Swap the C temporaries + +DbgSwapZP: + ldy #zpspace-1 +Swap1: ldx CTemp,y + lda sp,y ; ###### + sta CTemp,y + txa + sta sp,y + dey + bpl Swap1 + rts + +; ---------------------------------------------------------------------------- +; Utility functions + + +; Set/reset the breakpoints. We must do that here since the breakpoints +; may be in the runtime stuff, causing the C part to fail before it has +; reset the breakpoints. See declaration of struct breakpoint in the C +; source + +MaxBreaks = 48 ; 4*12 + +ResetDbgBreaks: + ldy #0 + ldx #0 +L4: lda _DbgBreaks+3,x ; Get bk_use + beq L6 ; Jump if not set + bpl L5 ; Jump if user breakpoint + lda #0 + sta _DbgBreaks+3,x ; Clear if temp breakpoint +L5: lda _DbgBreaks+1,x ; PC hi + sta ptr1+1 + lda _DbgBreaks,x ; PC lo + sta ptr1 + lda _DbgBreaks+2,x ; Old OPC + sta (ptr1),y ; Reset the breakpoint +L6: inx + inx + inx + inx + cpx #MaxBreaks ; Done? + bne L4 + rts + +SetDbgBreaks: + ldx #0 + ldy #0 +L7: lda _DbgBreaks+3,x ; Get bk_use + beq L8 ; Jump if not set + lda _DbgBreaks+1,x ; PC hi + sta ptr1+1 + lda _DbgBreaks,x ; PC lo + sta ptr1 + lda (ptr1),y ; Get the breakpoint OPC... + sta _DbgBreaks+2,x ; ...and save it + lda #$00 ; Load BRK opcode + sta (ptr1),y +L8: inx + inx + inx + inx + cpx #MaxBreaks ; Done? + bne L7 + rts + +; Get a free breakpoint slot or return 0 + + .export _DbgGetBreakSlot + +_DbgGetBreakSlot: + ldx #0 +L10: lda _DbgBreaks+3,x ; Get bk_use + beq L11 ; Jump if not set + inx + inx + inx + inx + cpx #MaxBreaks ; Done? + bne L10 + jmp return0 ; No free slot + +L11: stx tmp1 + lda #<_DbgBreaks + ldx #>_DbgBreaks + clc + adc tmp1 + bcc L12 + inx +L12: ldy #1 ; Force != 0 + rts + + +; Check if a given address has a user breakpoint set, if found, return the +; slot, otherwise return 0. + + .export _DbgIsBreak + +_DbgIsBreak: + jsr popax ; Get address + sta ptr1 + stx ptr1+1 + ldx #0 +L20: lda _DbgBreaks+3,x ; Get bk_use + beq L21 ; Jump if not set + bmi L21 ; Jump if temp breakpoint + lda _DbgBreaks,x ; Low byte of address + cmp ptr1 + bne L21 + lda _DbgBreaks+1,x ; High byte of address + cmp ptr1+1 + beq L22 +L21: inx + inx + inx + inx + cpx #MaxBreaks ; Done? + bne L20 + jmp return0 ; Not found + +L22: stx tmp1 + lda #<_DbgBreaks + ldx #>_DbgBreaks + clc + adc tmp1 + bcc L23 + inx +L23: ldy #1 ; Force != 0 + rts + + + diff --git a/libsrc/geos/Makefile b/libsrc/geos/Makefile new file mode 100644 index 000000000..30b48a2f2 --- /dev/null +++ b/libsrc/geos/Makefile @@ -0,0 +1,35 @@ +# +# Makefile for GEOS lib +# for cc65 +# +# Maciej 'YTM/Alliance' Witkowiak + +export CC = ../../../src/cc65 +export CFLAGS = -O +export AS = ../../../src/ca65/ca65 +export ASFLAGS = +AR = ../../src/ar65/ar65 + + +OBJ_DIRS=disk dlgbox file graph menuicon memory mousesprite process system + +all: + @for i in devel $(OBJ_DIRS); do $(MAKE) -C $$i; done + @mv devel/crt0.o ../geos.o + @for i in $(OBJ_DIRS); do $(AR) a ../geos.lib $$i/*.o; done + +rebuild: zap all clean + + +.PHONY: clean +clean: + @for i in $(OBJ_DIRS); do \ + cd $$i; \ + $(MAKE) clean; \ + cd ..; \ + done + +.PHONY: zap +zap: clean + @rm -f ../geos.lib ../geos.o + diff --git a/libsrc/geos/devel/Makefile b/libsrc/geos/devel/Makefile new file mode 100644 index 000000000..e7c140ad5 --- /dev/null +++ b/libsrc/geos/devel/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for GEOS lib +# for cc65 +# +# + +%.o: %.s + @echo $< + @$(AS) -o $@ $(AFLAGS) $< + + +S_OBJS = crt0.o + +all: $(S_OBJS) + +clean: + @rm -f *.~ $(S_OBJS) core \ No newline at end of file diff --git a/libsrc/geos/devel/crt0.s b/libsrc/geos/devel/crt0.s new file mode 100644 index 000000000..00cac2d4c --- /dev/null +++ b/libsrc/geos/devel/crt0.s @@ -0,0 +1,77 @@ +; +; This must be the *second* file on the linker command line +; (.cvt header must be the *first* one) + +; Maciej 'YTM/Alliance' Witkowiak +; 26.10.99, 10.3.2000 + +; no __hinit + + .export _exit + .import pushax + .import _main + .import zerobss, doatexit + +; ------------------------------------------------------------------------ +; Define and export the ZP variables for the C64 runtime + + .exportzp sp, sreg, regsave, regbank + .exportzp ptr1, ptr2, ptr3, ptr4 + .exportzp tmp1, tmp2, tmp3, tmp4 + +sp = $72 ; stack pointer +sreg = $74 ; secondary register/high 16 bit for longs +regsave = $76 ; slot to save/restore (E)AX into +ptr1 = $7A ; +ptr2 = $7C +ptr3 = $7E +ptr4 = $70 +tmp1 = $fb +tmp2 = $fc +tmp3 = $fd +tmp4 = $fe + +regbank = $a3 ; 6 bytes hopefully not used by Kernal + +; ------------------------------------------------------------------------ + +; .org $0400-508 ; $0400 - length of .cvt header +; .include "cvthead.s" + + .reloc + +; ------------------------------------------------------------------------ +; Actual code + +; Clear the BSS data + + jsr zerobss + +; Setup stack + + lda #<$7900 + sta sp + lda #>$7900 + sta sp+1 ; Set argument stack ptr + +; Initialize the heap + +;;! jsr __hinit + +; Pass an empty command line + + lda #0 + tax + jsr pushax ; argc + jsr pushax ; argv + + ldy #4 ; Argument size + jsr _main ; call the users code + jmp $c1c3 ; jump to GEOS MainLoop + +; exit must be called from the code! + +_exit: + jsr doatexit ; call exit functions + + jmp $c22c ; EnterDeskTop diff --git a/libsrc/geos/devel/cvthead.s b/libsrc/geos/devel/cvthead.s new file mode 100644 index 000000000..a7b2f564d --- /dev/null +++ b/libsrc/geos/devel/cvthead.s @@ -0,0 +1,110 @@ + +; NOTE THAT EASIER AND SAFER WAY OF GETTING HEADER IS TO GENERATE IT !!! + +; Maciej 'YTM/Alliance' Witkowiak +; 28.02.2000 + +; This is .cvt header for GEOS files, it is recognized by Convert v2.5 for GEOS +; and Star Commander (when copying GEOS files to/from .d64 images) +; This is only an example, and you should customize file header values (such as +; Author, Class, Date, filename) either here for all GEOS apps or manually later +; in GEOS environment using specialized apps or disk editor + +; currently only SEQUENTIAL structure is supported, no overlays + +; defineable values are marked with ';**' in comment line, please be careful with +; string lengths + + +; .org $0400-508 ; $0400 - length of .cvt header + + .segment "HEADER" + + .include "../inc/const.inc" + +ProgType = APPLICATION ;** may be one of: + ; APPLICATION + ; ASSEMBLY + ; DESK_ACC (unusable, unless you will fix end address in header before run) + ; PRINTER (unusable, unless you change $0400 to $7900 here and in crt0.s) + ; INPUT_DEVICE (like above but change $0400 to $fe80, you have $017a bytes) + ; AUTO_EXEC (you need to fit in $0400-$4fff area) + ; INPUT_128 (like INPUT_DEVICE but change $0400 to $fd80, you have $017a bytes) + + .byte USR | $80 ; DOS filetype + .word 0 ; T&S, will be fixed by converter + + .byte "filename" ;** DOS filename (16 chars with $a0 padding) + .byte $a0,$a0,$a0,$a0,$a0,$a0,$a0,$a0 + + .word 0 ; header T&S + + .byte SEQUENTIAL ; GEOS structure + .byte ProgType ; GEOS filetype + .byte 00 ;** year 2000=00 or 100? + .byte 02 ;** month + .byte 28 ;** day + .byte 18 ;** hour + .byte 58 ;** minute + + .word 0 ; size in blocks, will be fixed by converter + + .byte "PRG formatted GEOS file V1.0" + ; converter stamp + .res $c4 ; some bytes are left + + .byte 3, 21, 63 | $80 ; icon picture header, 63 bytes follow + + ;** hey, uberhacker! edit icon here!!! ;-)) + .byte %11111111, %11111111, %11111111 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %11111111, %11111111, %11111111 + + .byte USR | $80 ;again DOS type + .byte ProgType ;again GEOS type + .byte SEQUENTIAL ;structure + .word $0400 ;ProgStart + .word $0400-1 ;ProgEnd (needs proper value for DESK_ACC) + .word $0400 ;ProgExec + + .byte "Filename" ;**GEOS class (12 chars) + .byte $20,$20,$20,$20 ; padding with spaces to 12 + + .byte "V1.0",0 ;**version + .word 0 + + .byte %01000000 ;**40/80 columns capability +; B7 B6 +; 0 0 - runs under GEOS128 but only in 40 column mode +; 0 1 - runs under GEOS128 in both 40/80 column modes +; 1 0 - does not run under GEOS128 +; 1 1 - runs under GEOS128 but only in 80 column mode + + + .byte "Author" ;**author's name (63 chars) + .byte 0 ; +terminator + .res (63-7) ; padding to 63 + + .byte "Compiled with cc65" ;**note (95 chars) + .byte 0 ; +terminator + .res (95-18) ; padding to 95 + + ; end of header, code follows diff --git a/libsrc/geos/disk/Makefile b/libsrc/geos/disk/Makefile new file mode 100644 index 000000000..9020128e1 --- /dev/null +++ b/libsrc/geos/disk/Makefile @@ -0,0 +1,20 @@ +# +# Makefile for GEOS lib +# for cc65 +# + +%.o: %.s + @echo $< + @$(AS) -o $@ $(AFLAGS) $< + + +S_OBJS = blkalloc.o calcblksfree.o changediskdevice.o chkdkgeos.o enterturbo.o exitturbo.o\ + findbambit.o freeblock.o getblock.o getdirhead.o getptrcurdknm.o newdisk.o\ + nxtblkalloc.o opendisk.o purgeturbo.o putblock.o putdirhead.o readblock.o\ + readbuff.o setnextfree.o setgeosdisk.o writeblock.o writebuff.o verwriteblock.o\ + gettrse.o + +all: $(S_OBJS) + +clean: + @rm -f *.~ $(S_OBJS) core \ No newline at end of file diff --git a/libsrc/geos/disk/blkalloc.s b/libsrc/geos/disk/blkalloc.s new file mode 100644 index 000000000..a8f0638eb --- /dev/null +++ b/libsrc/geos/disk/blkalloc.s @@ -0,0 +1,24 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char BlkAlloc (struct tr_se output[], int length); + + .import popax + .export _BlkAlloc + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_BlkAlloc: + sta r2L + stx r2H + jsr popax + sta r4L + stx r4H + jsr BlkAlloc + stx errno + txa + rts diff --git a/libsrc/geos/disk/calcblksfree.s b/libsrc/geos/disk/calcblksfree.s new file mode 100644 index 000000000..9e0247e34 --- /dev/null +++ b/libsrc/geos/disk/calcblksfree.s @@ -0,0 +1,19 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; int CalcBlksFree (void); + + .export _CalcBlksFree + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_CalcBlksFree: + jsr CalcBlksFree + stx errno + lda r4L + ldx r4H + rts diff --git a/libsrc/geos/disk/changediskdevice.s b/libsrc/geos/disk/changediskdevice.s new file mode 100644 index 000000000..204b46076 --- /dev/null +++ b/libsrc/geos/disk/changediskdevice.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char ChangeDiskDevice (char newDriveNumber); + + .export _ChangeDiskDevice + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_ChangeDiskDevice: + jsr ChangeDiskDevice + stx errno + txa + rts diff --git a/libsrc/geos/disk/chkdkgeos.s b/libsrc/geos/disk/chkdkgeos.s new file mode 100644 index 000000000..01496076b --- /dev/null +++ b/libsrc/geos/disk/chkdkgeos.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char ChkDkGEOS (void); + + .export _ChkDkGEOS + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_ChkDkGEOS: + jsr ChkDkGEOS + stx errno + lda isGEOS + rts diff --git a/libsrc/geos/disk/enterturbo.s b/libsrc/geos/disk/enterturbo.s new file mode 100644 index 000000000..1236fa239 --- /dev/null +++ b/libsrc/geos/disk/enterturbo.s @@ -0,0 +1,13 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; void EnterTurbo (void); + + .export _EnterTurbo + + .include "../inc/jumptab.inc" + +_EnterTurbo = EnterTurbo diff --git a/libsrc/geos/disk/exitturbo.s b/libsrc/geos/disk/exitturbo.s new file mode 100644 index 000000000..521f70f75 --- /dev/null +++ b/libsrc/geos/disk/exitturbo.s @@ -0,0 +1,13 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; void ExitTurbo (void); + + .export _ExitTurbo + + .include "../inc/jumptab.inc" + +_ExitTurbo = ExitTurbo diff --git a/libsrc/geos/disk/findbambit.s b/libsrc/geos/disk/findbambit.s new file mode 100644 index 000000000..66a689885 --- /dev/null +++ b/libsrc/geos/disk/findbambit.s @@ -0,0 +1,25 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char FindBAMBit (struct tr_se *TS); +; (might be called inUSE (if (!inUSE(block)))) + + .import gettrse + .export _FindBAMBit + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_FindBAMBit: + jsr gettrse + sta r6L + stx r6H + jsr FindBAMBit + bne inUse + lda #0 + rts +inUse: lda #$ff + rts diff --git a/libsrc/geos/disk/freeblock.s b/libsrc/geos/disk/freeblock.s new file mode 100644 index 000000000..57b84b781 --- /dev/null +++ b/libsrc/geos/disk/freeblock.s @@ -0,0 +1,22 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char FreeBlock (struct tr_se *TS); + + .import gettrse + .export _FreeBlock + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_FreeBlock: + jsr gettrse + sta r6L + stx r6H + jsr FreeBlock + stx errno + txa + rts diff --git a/libsrc/geos/disk/getblock.s b/libsrc/geos/disk/getblock.s new file mode 100644 index 000000000..2ba5f3fa5 --- /dev/null +++ b/libsrc/geos/disk/getblock.s @@ -0,0 +1,26 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char GetBlock (struct tr_se *myTS, char *buffer); + + .import popax + .import gettrse + .export _GetBlock + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_GetBlock: + sta r4L + stx r4H + jsr popax + jsr gettrse + sta r1L + stx r1H + jsr GetBlock + stx errno + txa + rts diff --git a/libsrc/geos/disk/getdirhead.s b/libsrc/geos/disk/getdirhead.s new file mode 100644 index 000000000..e3a452237 --- /dev/null +++ b/libsrc/geos/disk/getdirhead.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char GetDirHead (void); + + .export _GetDirHead + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_GetDirHead: + jsr GetDirHead + stx errno + txa + rts diff --git a/libsrc/geos/disk/getptrcurdknm.s b/libsrc/geos/disk/getptrcurdknm.s new file mode 100644 index 000000000..c36068c2d --- /dev/null +++ b/libsrc/geos/disk/getptrcurdknm.s @@ -0,0 +1,34 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; void GetPtrCurDkNm (char *curName); +; (fills curName[17] with current disk's name) + + .importzp ptr4, ptr3 + .export _GetPtrCurDkNm + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_GetPtrCurDkNm: + sta ptr3 + stx ptr3+1 + ldx #ptr4 + jsr GetPtrCurDkNm + ldy #0 + txa + bne fin +namelp: lda (ptr4),y + cmp #$a0 + beq fin + sta (ptr3),y + iny + cpy #16 + bne namelp +fin: lda #0 + sta (ptr3),y + stx errno + rts diff --git a/libsrc/geos/disk/gettrse.s b/libsrc/geos/disk/gettrse.s new file mode 100644 index 000000000..525df08af --- /dev/null +++ b/libsrc/geos/disk/gettrse.s @@ -0,0 +1,17 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.1.00 + + .export gettrse + .importzp ptr4 +gettrse: + sta ptr4 + stx ptr4+1 + ldy #1 + lda (ptr4),y + tax + dey + lda (ptr4),y + rts diff --git a/libsrc/geos/disk/newdisk.s b/libsrc/geos/disk/newdisk.s new file mode 100644 index 000000000..7a539c016 --- /dev/null +++ b/libsrc/geos/disk/newdisk.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char NewDisk (void); + + .export _NewDisk + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_NewDisk: + jsr NewDisk + stx errno + txa + rts diff --git a/libsrc/geos/disk/nxtblkalloc.s b/libsrc/geos/disk/nxtblkalloc.s new file mode 100644 index 000000000..8bab0e47a --- /dev/null +++ b/libsrc/geos/disk/nxtblkalloc.s @@ -0,0 +1,30 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char NxtBlkAlloc (struct tr_se *startTS, struct tr_se output[], int length ); + + .import popax + .import gettrse + .importzp ptr4 + .export _NxtBlkAlloc + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_NxtBlkAlloc: + sta r2L + stx r2H + jsr popax + sta r4L + stx r4H + jsr popax + jsr gettrse + sta r3L + stx r3H + jsr NxtBlkAlloc + stx errno + txa + rts diff --git a/libsrc/geos/disk/opendisk.s b/libsrc/geos/disk/opendisk.s new file mode 100644 index 000000000..7f0c83d4a --- /dev/null +++ b/libsrc/geos/disk/opendisk.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char OpenDisk (void); + + .export _OpenDisk + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_OpenDisk: + jsr OpenDisk + stx errno + txa + rts diff --git a/libsrc/geos/disk/purgeturbo.s b/libsrc/geos/disk/purgeturbo.s new file mode 100644 index 000000000..d6a286218 --- /dev/null +++ b/libsrc/geos/disk/purgeturbo.s @@ -0,0 +1,13 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; void PurgeTurbo (void); + + .export _PurgeTurbo + + .include "../inc/jumptab.inc" + +_PurgeTurbo = PurgeTurbo diff --git a/libsrc/geos/disk/putblock.s b/libsrc/geos/disk/putblock.s new file mode 100644 index 000000000..c857bf768 --- /dev/null +++ b/libsrc/geos/disk/putblock.s @@ -0,0 +1,26 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char PutBlock (struct tr_se *myTS, char *buffer); + + .import popax + .import gettrse + .export _PutBlock + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_PutBlock: + sta r4L + stx r4H + jsr popax + jsr gettrse + sta r1L + stx r1H + jsr PutBlock + stx errno + txa + rts diff --git a/libsrc/geos/disk/putdirhead.s b/libsrc/geos/disk/putdirhead.s new file mode 100644 index 000000000..24577865f --- /dev/null +++ b/libsrc/geos/disk/putdirhead.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char PutDirHead (void); + + .export _PutDirHead + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_PutDirHead: + jsr PutDirHead + stx errno + txa + rts diff --git a/libsrc/geos/disk/readblock.s b/libsrc/geos/disk/readblock.s new file mode 100644 index 000000000..5c319e2ce --- /dev/null +++ b/libsrc/geos/disk/readblock.s @@ -0,0 +1,26 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char ReadBlock (struct tr_se myTS, char *buffer); + + .import popax + .import gettrse + .export _ReadBlock + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_ReadBlock: + sta r4L + stx r4H + jsr popax + jsr gettrse + sta r1L + stx r1H + jsr ReadBlock + stx errno + txa + rts diff --git a/libsrc/geos/disk/readbuff.s b/libsrc/geos/disk/readbuff.s new file mode 100644 index 000000000..4d6cda0a9 --- /dev/null +++ b/libsrc/geos/disk/readbuff.s @@ -0,0 +1,22 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 26.10.99 + +; char ReadBuff (struct tr_se); + + .import gettrse + .export _ReadBuff + + .include "../inc/diskdrv.inc" + .include "../inc/geossym.inc" + +_ReadBuff: + jsr gettrse + sta r1L + stx r1H + jsr ReadBuff + stx errno + txa + rts diff --git a/libsrc/geos/disk/setgeosdisk.s b/libsrc/geos/disk/setgeosdisk.s new file mode 100644 index 000000000..cf1f32713 --- /dev/null +++ b/libsrc/geos/disk/setgeosdisk.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char SetGEOSDisk (void); + + .export _SetGEOSDisk + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_SetGEOSDisk: + jsr SetGEOSDisk + stx errno + txa + rts diff --git a/libsrc/geos/disk/setnextfree.s b/libsrc/geos/disk/setnextfree.s new file mode 100644 index 000000000..df698b1b9 --- /dev/null +++ b/libsrc/geos/disk/setnextfree.s @@ -0,0 +1,23 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; struct tr_se SetNextFree (struct tr_se *startTS); + + .import gettrse + .export _SetNextFree + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_SetNextFree: + jsr gettrse + sta r3L + stx r3H + jsr SetNextFree + stx errno + lda r3L + ldx r3H + rts diff --git a/libsrc/geos/disk/verwriteblock.s b/libsrc/geos/disk/verwriteblock.s new file mode 100644 index 000000000..88601eef6 --- /dev/null +++ b/libsrc/geos/disk/verwriteblock.s @@ -0,0 +1,26 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char VerWriteBlock (struct tr_se *myTS, char *buffer); + + .import popax + .import gettrse + .export _VerWriteBlock + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_VerWriteBlock: + sta r4L + stx r4H + jsr popax + jsr gettrse + sta r1L + stx r1H + jsr VerWriteBlock + stx errno + txa + rts diff --git a/libsrc/geos/disk/writeblock.s b/libsrc/geos/disk/writeblock.s new file mode 100644 index 000000000..4574f2f9f --- /dev/null +++ b/libsrc/geos/disk/writeblock.s @@ -0,0 +1,26 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char WriteBlock (struct tr_se *myTS, char *buffer); + + .import popax + .import gettrse + .export _WriteBlock + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_WriteBlock: + sta r4L + stx r4H + jsr popax + jsr gettrse + sta r1L + stx r1H + jsr WriteBlock + stx errno + txa + rts diff --git a/libsrc/geos/disk/writebuff.s b/libsrc/geos/disk/writebuff.s new file mode 100644 index 000000000..ecd04bc5b --- /dev/null +++ b/libsrc/geos/disk/writebuff.s @@ -0,0 +1,22 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 26.10.99 + +; char WriteBuff (struct tr_se*); + + .import gettrse + .export _WriteBuff + + .include "../inc/diskdrv.inc" + .include "../inc/geossym.inc" + +_WriteBuff: + jsr gettrse + sta r1L + stx r1H + jsr WriteBuff + stx errno + txa + rts diff --git a/libsrc/geos/dlgbox/Makefile b/libsrc/geos/dlgbox/Makefile new file mode 100644 index 000000000..3f8cb8e0f --- /dev/null +++ b/libsrc/geos/dlgbox/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for GEOS lib +# for cc65 +# +# + +%.o: %.s + @echo $< + @$(AS) -o $@ $(AFLAGS) $< + + +S_OBJS = dodlgbox.o rstrfrmdialogue.o\ + dbget2lines.o dlgboxyesno.o dlgboxokcancel.o dlgboxok.o dlgboxgetstring.o\ + dlgboxfileselect.o + +all: $(S_OBJS) + +clean: + @rm -f *.~ $(S_OBJS) core \ No newline at end of file diff --git a/libsrc/geos/dlgbox/dbget2lines.s b/libsrc/geos/dlgbox/dbget2lines.s new file mode 100644 index 000000000..1258dbb48 --- /dev/null +++ b/libsrc/geos/dlgbox/dbget2lines.s @@ -0,0 +1,17 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + + .export DB_get2lines + .importzp ptr3,ptr4 + .import popax + +DB_get2lines: + sta ptr4 + stx ptr4+1 + jsr popax + sta ptr3 + stx ptr3+1 + rts diff --git a/libsrc/geos/dlgbox/dlgboxfileselect.s b/libsrc/geos/dlgbox/dlgboxfileselect.s new file mode 100644 index 000000000..8efc44772 --- /dev/null +++ b/libsrc/geos/dlgbox/dlgboxfileselect.s @@ -0,0 +1,62 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char DlgBoxFileSelect (char *class, char ftype, char *fname); + + .export _DlgBoxFileSelect + .import popa, popax + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + .include "../inc/const.inc" + .include "../inc/geosmac.ca65.inc" + +_DlgBoxFileSelect: +; sta r5L +; stx r5H +; jsr popa +; sta r7L +; jsr popax +; sta r10L +; stx r10H + + sta tmp_r5 + stx tmp_r5+1 + jsr popa + sta tmp_r7L + jsr popax + sta tmp_r10 + stx tmp_r10+1 + +DB_FS_reload: + MoveW tmp_r5, r5 + MoveW tmp_r10, r10 + MoveB tmp_r7L, r7L + + lda #paramStrFileSelect + sta r0L + stx r0H + jsr DoDlgBox + lda r0L + cmp #DISK + bne DB_FS_Fin + jsr OpenDisk + txa + beq DB_FS_reload +DB_FS_Fin: rts + +paramStrFileSelect: + .byte DEF_DB_POS | 1 + .byte DBGETFILES, 4, 4 + .byte OPEN, DBI_X_2, DBI_Y_0+16 + .byte DISK, DBI_X_2, DBI_Y_0+32+1 + .byte CANCEL, DBI_X_2, DBI_Y_0+64+3 + .byte NULL + +tmp_r5: .word 0 +tmp_r7L: .byte 0 +tmp_r10: .word 0 diff --git a/libsrc/geos/dlgbox/dlgboxgetstring.s b/libsrc/geos/dlgbox/dlgboxgetstring.s new file mode 100644 index 000000000..8398bd7e3 --- /dev/null +++ b/libsrc/geos/dlgbox/dlgboxgetstring.s @@ -0,0 +1,40 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char DlgBoxGetString (char *string, char strlen, char *line1,char *line2); + + .export _DlgBoxGetString + .import DB_get2lines + .importzp ptr2, ptr3, ptr4 + .import popa, popax + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + .include "../inc/const.inc" + +_DlgBoxGetString: + jsr DB_get2lines + jsr popa + sta DB_strlen + jsr popax + sta ptr2 + stx ptr2+1 + lda #paramStrGetString + sta r0L + stx r0H + jsr DoDlgBox + lda r0L + rts + +paramStrGetString: + .byte DEF_DB_POS | 1 + .byte DBVARSTR, TXT_LN_X, TXT_LN_1_Y, ptr3 + .byte DBVARSTR, TXT_LN_X, TXT_LN_2_Y, ptr4 + .byte DBGETSTRING, TXT_LN_X, TXT_LN_3_Y, ptr2 +DB_strlen: .byte 17 + .byte CANCEL, DBI_X_2, DBI_Y_2 + .byte NULL diff --git a/libsrc/geos/dlgbox/dlgboxok.s b/libsrc/geos/dlgbox/dlgboxok.s new file mode 100644 index 000000000..8b9b96197 --- /dev/null +++ b/libsrc/geos/dlgbox/dlgboxok.s @@ -0,0 +1,32 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char DlgBoxOk (char *line1,char *line2); + + .export _DlgBoxOk + .import DB_get2lines + .importzp ptr3, ptr4 + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + .include "../inc/const.inc" + +_DlgBoxOk: + jsr DB_get2lines + lda #paramStrOk + sta r0L + stx r0H + jsr DoDlgBox + lda r0L + rts + +paramStrOk: + .byte DEF_DB_POS | 1 + .byte DBVARSTR, TXT_LN_X, TXT_LN_2_Y, ptr3 + .byte DBVARSTR, TXT_LN_X, TXT_LN_3_Y, ptr4 + .byte OK, DBI_X_0, DBI_Y_2 + .byte NULL diff --git a/libsrc/geos/dlgbox/dlgboxokcancel.s b/libsrc/geos/dlgbox/dlgboxokcancel.s new file mode 100644 index 000000000..14a4e936e --- /dev/null +++ b/libsrc/geos/dlgbox/dlgboxokcancel.s @@ -0,0 +1,33 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char DlgBoxOkCancel (char *line1,char *line2); + + .export _DlgBoxOkCancel + .import DB_get2lines + .importzp ptr3, ptr4 + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + .include "../inc/const.inc" + +_DlgBoxOkCancel: + jsr DB_get2lines + lda #paramStrOkCancel + sta r0L + stx r0H + jsr DoDlgBox + lda r0L + rts + +paramStrOkCancel: + .byte DEF_DB_POS | 1 + .byte DBVARSTR, TXT_LN_X, TXT_LN_2_Y, ptr3 + .byte DBVARSTR, TXT_LN_X, TXT_LN_3_Y, ptr4 + .byte OK, DBI_X_0, DBI_Y_2 + .byte CANCEL, DBI_X_2, DBI_Y_2 + .byte NULL diff --git a/libsrc/geos/dlgbox/dlgboxyesno.s b/libsrc/geos/dlgbox/dlgboxyesno.s new file mode 100644 index 000000000..7c1d934d0 --- /dev/null +++ b/libsrc/geos/dlgbox/dlgboxyesno.s @@ -0,0 +1,33 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char DlgBoxYesNo (char *line1,char *line2); + + .export _DlgBoxYesNo + .import DB_get2lines + .importzp ptr3, ptr4 + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + .include "../inc/const.inc" + +_DlgBoxYesNo: + jsr DB_get2lines + lda #paramStrYesNo + sta r0L + stx r0H + jsr DoDlgBox + lda r0L + rts + +paramStrYesNo: + .byte DEF_DB_POS | 1 + .byte DBVARSTR, TXT_LN_X, TXT_LN_2_Y, ptr3 + .byte DBVARSTR, TXT_LN_X, TXT_LN_3_Y, ptr4 + .byte YES, DBI_X_0, DBI_Y_2 + .byte NO, DBI_X_2, DBI_Y_2 + .byte NULL diff --git a/libsrc/geos/dlgbox/dodlgbox.s b/libsrc/geos/dlgbox/dodlgbox.s new file mode 100644 index 000000000..b17a4f686 --- /dev/null +++ b/libsrc/geos/dlgbox/dodlgbox.s @@ -0,0 +1,19 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char DoDlgBox (char *myParamString); + + .export _DoDlgBox + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_DoDlgBox: + sta r0L + stx r0H + jsr DoDlgBox + lda r0L + rts diff --git a/libsrc/geos/dlgbox/rstrfrmdialogue.s b/libsrc/geos/dlgbox/rstrfrmdialogue.s new file mode 100644 index 000000000..585e5b396 --- /dev/null +++ b/libsrc/geos/dlgbox/rstrfrmdialogue.s @@ -0,0 +1,13 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char RstrFrmDialogue (void); + + .export _RstrFrmDialogue + + .include "../inc/jumptab.inc" + +_RstrFrmDialogue = RstrFrmDialogue diff --git a/libsrc/geos/file/Makefile b/libsrc/geos/file/Makefile new file mode 100644 index 000000000..01299b30b --- /dev/null +++ b/libsrc/geos/file/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for GEOS lib +# for cc65 +# + +%.o: %.s + @echo $< + @$(AS) -o $@ $(AFLAGS) $< + + +S_OBJS = get1stdirentry.o getnxtdirentry.o\ + openrecordfile.o closerecordfile.o nextrecord.o previousrecord.o pointrecord.o\ + deleterecord.o insertrecord.o appendrecord.o readrecord.o writerecord.o\ + updaterecordfile.o\ + findfile.o followchain.o getfhdrinfo.o readfile.o savefile.o freefile.o\ + deletefile.o renamefile.o findftypes.o readbyte.o + +all: $(S_OBJS) + +clean: + @rm -f *.~ $(S_OBJS) core \ No newline at end of file diff --git a/libsrc/geos/file/appendrecord.s b/libsrc/geos/file/appendrecord.s new file mode 100644 index 000000000..243fdf27f --- /dev/null +++ b/libsrc/geos/file/appendrecord.s @@ -0,0 +1,19 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char AppendRecord (void); + + .export _AppendRecord + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_AppendRecord: + + jsr AppendRecord + stx errno + txa + rts diff --git a/libsrc/geos/file/closerecordfile.s b/libsrc/geos/file/closerecordfile.s new file mode 100644 index 000000000..b8454d065 --- /dev/null +++ b/libsrc/geos/file/closerecordfile.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char CloseRecordFile (void); + + .export _CloseRecordFile + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_CloseRecordFile: + jsr CloseRecordFile + stx errno + txa + rts diff --git a/libsrc/geos/file/deletefile.s b/libsrc/geos/file/deletefile.s new file mode 100644 index 000000000..919e55e32 --- /dev/null +++ b/libsrc/geos/file/deletefile.s @@ -0,0 +1,20 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char DeleteFile (char *myName); + + .export _DeleteFile + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_DeleteFile: + sta r0L + stx r0H + jsr DeleteFile + stx errno + txa + rts diff --git a/libsrc/geos/file/deleterecord.s b/libsrc/geos/file/deleterecord.s new file mode 100644 index 000000000..8691b6aa1 --- /dev/null +++ b/libsrc/geos/file/deleterecord.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char DeleteRecord (void); + + .export _DeleteRecord + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_DeleteRecord: + jsr DeleteRecord + stx errno + txa + rts diff --git a/libsrc/geos/file/findfile.s b/libsrc/geos/file/findfile.s new file mode 100644 index 000000000..65008d69b --- /dev/null +++ b/libsrc/geos/file/findfile.s @@ -0,0 +1,20 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char FindFile (char *myName); + + .export _FindFile + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_FindFile: + sta r6L + stx r6H + jsr FindFile + stx errno + txa + rts diff --git a/libsrc/geos/file/findftypes.s b/libsrc/geos/file/findftypes.s new file mode 100644 index 000000000..66e270c96 --- /dev/null +++ b/libsrc/geos/file/findftypes.s @@ -0,0 +1,28 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char FindFTypes (char *buffer, char fileType, char fileMax, char *Class); + + .export _FindFTypes + .import popax, popa + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_FindFTypes: + sta r10L + stx r10H + jsr popa + sta r7H + jsr popa + sta r7L + jsr popax + sta r6L + stx r6H + jsr FindFTypes + stx errno + txa + rts diff --git a/libsrc/geos/file/followchain.s b/libsrc/geos/file/followchain.s new file mode 100644 index 000000000..a76d9c6ea --- /dev/null +++ b/libsrc/geos/file/followchain.s @@ -0,0 +1,26 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char FollowChain (struct tr_se *myTrSe, char *buffer); + + .export _FollowChain + .import popax + .import gettrse + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_FollowChain: + sta r3L + stx r3H + jsr popax + jsr gettrse + sta r1L + stx r1H + jsr FollowChain + stx errno + txa + rts diff --git a/libsrc/geos/file/freefile.s b/libsrc/geos/file/freefile.s new file mode 100644 index 000000000..dd02d28e9 --- /dev/null +++ b/libsrc/geos/file/freefile.s @@ -0,0 +1,20 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char FreeFile (struct trse myTrSe[]); + + .export _FreeFile + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_FreeFile: + sta r9L + stx r9H + jsr FreeFile + stx errno + txa + rts diff --git a/libsrc/geos/file/get1stdirentry.s b/libsrc/geos/file/get1stdirentry.s new file mode 100644 index 000000000..7542c7dc1 --- /dev/null +++ b/libsrc/geos/file/get1stdirentry.s @@ -0,0 +1,19 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 26.10.99 + +; struct filehandle* Get1stDirEntry (void); + + .export _Get1stDirEntry + + .include "../inc/diskdrv.inc" + .include "../inc/geossym.inc" + +_Get1stDirEntry: + jsr Get1stDirEntry + stx errno + lda r5L + ldx r5H + rts diff --git a/libsrc/geos/file/getfhdrinfo.s b/libsrc/geos/file/getfhdrinfo.s new file mode 100644 index 000000000..815a70767 --- /dev/null +++ b/libsrc/geos/file/getfhdrinfo.s @@ -0,0 +1,20 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char GetFHdrInfo (struct filehandle *myFile); + + .export _GetFHdrInfo + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_GetFHdrInfo: + sta r9L + stx r9H + jsr GetFHdrInfo + stx errno + txa + rts diff --git a/libsrc/geos/file/getnxtdirentry.s b/libsrc/geos/file/getnxtdirentry.s new file mode 100644 index 000000000..d67f3fc1f --- /dev/null +++ b/libsrc/geos/file/getnxtdirentry.s @@ -0,0 +1,19 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 26.10.99 + +; struct filehandle* GetNxtDirEntry (void); + + .export _GetNxtDirEntry + + .include "../inc/diskdrv.inc" + .include "../inc/geossym.inc" + +_GetNxtDirEntry: + jsr GetNxtDirEntry + stx errno + lda r5L + ldx r5H + rts diff --git a/libsrc/geos/file/insertrecord.s b/libsrc/geos/file/insertrecord.s new file mode 100644 index 000000000..5b94de9e8 --- /dev/null +++ b/libsrc/geos/file/insertrecord.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char InsertRecord (void); + + .export _InsertRecord + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_InsertRecord: + jsr InsertRecord + stx errno + txa + rts diff --git a/libsrc/geos/file/nextrecord.s b/libsrc/geos/file/nextrecord.s new file mode 100644 index 000000000..473806ea4 --- /dev/null +++ b/libsrc/geos/file/nextrecord.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char NextRecord (void); + + .export _NextRecord + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_NextRecord: + jsr NextRecord + stx errno + txa + rts diff --git a/libsrc/geos/file/openrecordfile.s b/libsrc/geos/file/openrecordfile.s new file mode 100644 index 000000000..9251e6745 --- /dev/null +++ b/libsrc/geos/file/openrecordfile.s @@ -0,0 +1,20 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char OpenRecordFile (char *myName); + + .export _OpenRecordFile + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_OpenRecordFile: + sta r0L + stx r0H + jsr OpenRecordFile + stx errno + txa + rts diff --git a/libsrc/geos/file/pointrecord.s b/libsrc/geos/file/pointrecord.s new file mode 100644 index 000000000..bfd5b8b46 --- /dev/null +++ b/libsrc/geos/file/pointrecord.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char PointRecord (char recordNum); + + .export _PointRecord + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_PointRecord: + jsr PointRecord + stx errno + txa + rts diff --git a/libsrc/geos/file/previousrecord.s b/libsrc/geos/file/previousrecord.s new file mode 100644 index 000000000..2c84ba5ba --- /dev/null +++ b/libsrc/geos/file/previousrecord.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char PreviousRecord (void); + + .export _PreviousRecord + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_PreviousRecord: + jsr PreviousRecord + stx errno + txa + rts diff --git a/libsrc/geos/file/readbyte.s b/libsrc/geos/file/readbyte.s new file mode 100644 index 000000000..e618ebf40 --- /dev/null +++ b/libsrc/geos/file/readbyte.s @@ -0,0 +1,17 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char ReadByte (void); + + .export _ReadByte + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_ReadByte: + jsr ReadByte + stx errno + rts diff --git a/libsrc/geos/file/readfile.s b/libsrc/geos/file/readfile.s new file mode 100644 index 000000000..333d096c3 --- /dev/null +++ b/libsrc/geos/file/readfile.s @@ -0,0 +1,29 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char ReadFile (struct tr_se *myTS, char *buffer, int length); + + .export _ReadFile + .import popax + .import gettrse + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_ReadFile: + sta r2L + stx r2H + jsr popax + sta r7L + stx r7H + jsr popax + jsr gettrse + sta r1L + stx r1H + jsr ReadFile + stx errno + txa + rts diff --git a/libsrc/geos/file/readrecord.s b/libsrc/geos/file/readrecord.s new file mode 100644 index 000000000..4ccda9b71 --- /dev/null +++ b/libsrc/geos/file/readrecord.s @@ -0,0 +1,24 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char ReadRecord (char *buffer, int length); + + .export _ReadRecord + .import popax + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_ReadRecord: + sta r2L + stx r2H + jsr popax + sta r7L + stx r7H + jsr ReadRecord + stx errno + txa + rts diff --git a/libsrc/geos/file/renamefile.s b/libsrc/geos/file/renamefile.s new file mode 100644 index 000000000..dfa885598 --- /dev/null +++ b/libsrc/geos/file/renamefile.s @@ -0,0 +1,24 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char RenameFile (char *source, char *target); + + .export _RenameFile + .import popax + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_RenameFile: + sta r0L + stx r0H + jsr popax + sta r6L + stx r6H + jsr RenameFile + stx errno + txa + rts diff --git a/libsrc/geos/file/savefile.s b/libsrc/geos/file/savefile.s new file mode 100644 index 000000000..e0f360833 --- /dev/null +++ b/libsrc/geos/file/savefile.s @@ -0,0 +1,20 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char SaveFile (struct fileheader *myHeader); + + .export _SaveFile + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_SaveFile: + sta r9L + stx r9H + jsr SaveFile + stx errno + txa + rts diff --git a/libsrc/geos/file/updaterecordfile.s b/libsrc/geos/file/updaterecordfile.s new file mode 100644 index 000000000..996002203 --- /dev/null +++ b/libsrc/geos/file/updaterecordfile.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char UpdateRecordFile (void); + + .export _UpdateRecordFile + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_UpdateRecordFile: + jsr UpdateRecordFile + stx errno + txa + rts diff --git a/libsrc/geos/file/writerecord.s b/libsrc/geos/file/writerecord.s new file mode 100644 index 000000000..7f59482ce --- /dev/null +++ b/libsrc/geos/file/writerecord.s @@ -0,0 +1,24 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; char WriteRecord (char *buffer, int length); + + .export _WriteRecord + .import popax + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_WriteRecord: + sta r2L + stx r2H + jsr popax + sta r7L + stx r7H + jsr WriteRecord + stx errno + txa + rts diff --git a/libsrc/geos/graph/Makefile b/libsrc/geos/graph/Makefile new file mode 100644 index 000000000..1abf1a912 --- /dev/null +++ b/libsrc/geos/graph/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for GEOS lib +# for cc65 +# +# + +%.o: %.s + @echo $< + @$(AS) -o $@ $(AFLAGS) $< + + +S_OBJS = drawline.o drawpoint.o framerectangle.o hlineregs.o horizontalline.o\ + imprintrectangle.o invertline.o invertrectangle.o pointregs.o recoverline.o\ + recoverrectangle.o rectangle.o initdrawwindow.o setpattern.o testpoint.o verticalline.o\ + putchar.o putdecimal.o putstring.o usesystemfont.o\ + getcharwidth.o loadcharset.o bitmapup.o bitmapregs.o bitmapclip.o bitotherclip.o\ + graphicsstring.o getintcharint.o + +all: $(S_OBJS) + +clean: + @rm -f *.~ $(S_OBJS) core \ No newline at end of file diff --git a/libsrc/geos/graph/bitmapclip.s b/libsrc/geos/graph/bitmapclip.s new file mode 100644 index 000000000..9876efe8f --- /dev/null +++ b/libsrc/geos/graph/bitmapclip.s @@ -0,0 +1,25 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; void BitmapClip (char skipl, char skipr, int skipy, struct iconpic *myGfx); + + .import popa, popax + .import BitmapRegs + .export _BitmapClip + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_BitmapClip: + jsr BitmapRegs + jsr popax + sta r12L + stx r12H + jsr popa + sta r11H + jsr popa + sta r11L + jmp BitmapClip diff --git a/libsrc/geos/graph/bitmapregs.s b/libsrc/geos/graph/bitmapregs.s new file mode 100644 index 000000000..068239b1a --- /dev/null +++ b/libsrc/geos/graph/bitmapregs.s @@ -0,0 +1,22 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + + .importzp ptr4 + + .export BitmapRegs + + .include "../inc/geossym.inc" + +BitmapRegs: ;a/x is a struct iconpic* + sta ptr4 + stx ptr4+1 + ldy #0 +bmpLp: lda (ptr4),y + sta r0L,y + iny + cpy #6 + bne bmpLp + rts diff --git a/libsrc/geos/graph/bitmapup.s b/libsrc/geos/graph/bitmapup.s new file mode 100644 index 000000000..fbaa5f463 --- /dev/null +++ b/libsrc/geos/graph/bitmapup.s @@ -0,0 +1,17 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; void BitmapUp (struct iconpic *myGfx); + + + .import BitmapRegs + .export _BitmapUp + + .include "../inc/jumptab.inc" + +_BitmapUp: + jsr BitmapRegs + jmp BitmapUp diff --git a/libsrc/geos/graph/bitotherclip.s b/libsrc/geos/graph/bitotherclip.s new file mode 100644 index 000000000..6da0f4e97 --- /dev/null +++ b/libsrc/geos/graph/bitotherclip.s @@ -0,0 +1,37 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; void BitOtherClip (void *proc1, void* proc2, char skipl, char skipr, int skipy, +; struct iconpic *myGfx); + +; both proc1, proc2 should be: char __fastcall something (void); +; proc1 is called before reading a byte (.A returns next data) +; proc2 is called before reading each byte which is not pattern (code >219) + + + .import popa, popax + .import BitOtherRegs + .export _BitOtherClip + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_BitOtherClip: + jsr BitOtherRegs + jsr popax + sta r12L + stx r12H + jsr popa + sta r11H + jsr popa + sta r11L + jsr popax + sta r14L + stx r14H + jsr popax + sta r13L + stx r13H + jmp BitOtherClip diff --git a/libsrc/geos/graph/drawline.s b/libsrc/geos/graph/drawline.s new file mode 100644 index 000000000..baa68bce6 --- /dev/null +++ b/libsrc/geos/graph/drawline.s @@ -0,0 +1,23 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void DrawLine (struct window *mywindow); + + .import _InitDrawWindow + .export _DrawLine + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + .include "../inc/geosmac.ca65.inc" + +_DrawLine: + tay + PushW r2 + tya + jsr _InitDrawWindow + MoveW r2, r11 + PopW r2 + jmp DrawLine diff --git a/libsrc/geos/graph/drawpoint.s b/libsrc/geos/graph/drawpoint.s new file mode 100644 index 000000000..842a9b96f --- /dev/null +++ b/libsrc/geos/graph/drawpoint.s @@ -0,0 +1,17 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void DrawPoint (struct pixel *mypixel); + + + .import PointRegs + .export _DrawPoint + + .include "../inc/jumptab.inc" + +_DrawPoint: + jsr PointRegs + jmp DrawPoint diff --git a/libsrc/geos/graph/framerectangle.s b/libsrc/geos/graph/framerectangle.s new file mode 100644 index 000000000..e9b73784a --- /dev/null +++ b/libsrc/geos/graph/framerectangle.s @@ -0,0 +1,13 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void FrameRectangle (char pattern); + + .export _FrameRectangle + + .include "../inc/jumptab.inc" + +_FrameRectangle = FrameRectangle diff --git a/libsrc/geos/graph/getcharwidth.s b/libsrc/geos/graph/getcharwidth.s new file mode 100644 index 000000000..e0f08864b --- /dev/null +++ b/libsrc/geos/graph/getcharwidth.s @@ -0,0 +1,13 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; char GetCharWidth (char character); + + .export _GetCharWidth + + .include "../inc/jumptab.inc" + +_GetCharWidth = GetCharWidth diff --git a/libsrc/geos/graph/getintcharint.s b/libsrc/geos/graph/getintcharint.s new file mode 100644 index 000000000..fdf5af764 --- /dev/null +++ b/libsrc/geos/graph/getintcharint.s @@ -0,0 +1,21 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 11.03.2000 + + .import popa, popax + .export getintcharint + + .include "../inc/geossym.inc" + +getintcharint: + sta r11L + stx r11H + jsr popa + sta r1H + jsr popax + sta r0L + stx r0H + rts + diff --git a/libsrc/geos/graph/graphicsstring.s b/libsrc/geos/graph/graphicsstring.s new file mode 100644 index 000000000..3e751fc95 --- /dev/null +++ b/libsrc/geos/graph/graphicsstring.s @@ -0,0 +1,17 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 25.12.99 + +; void GraphicsString (char *myString); + + .export _GraphicsString + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_GraphicsString: + sta r0L + stx r0H + jmp GraphicsString diff --git a/libsrc/geos/graph/hlineregs.s b/libsrc/geos/graph/hlineregs.s new file mode 100644 index 000000000..8b5f2345a --- /dev/null +++ b/libsrc/geos/graph/hlineregs.s @@ -0,0 +1,22 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + + + .import popax, popa + + .export HLineRegs + + .include "../inc/geossym.inc" + +HLineRegs: + stx r4H + sta r4L + jsr popax + stx r3H + sta r3L + jsr popa + sta r11L + rts diff --git a/libsrc/geos/graph/horizontalline.s b/libsrc/geos/graph/horizontalline.s new file mode 100644 index 000000000..fa68ac73f --- /dev/null +++ b/libsrc/geos/graph/horizontalline.s @@ -0,0 +1,19 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void HorizontalLine (char pattern, char y, int xstart, int xend); + + .import popa + .import HLineRegs + + .export _HorizontalLine + + .include "../inc/jumptab.inc" + +_HorizontalLine: + jsr HLineRegs + jsr popa + jmp HorizontalLine diff --git a/libsrc/geos/graph/imprintrectangle.s b/libsrc/geos/graph/imprintrectangle.s new file mode 100644 index 000000000..02b896ad3 --- /dev/null +++ b/libsrc/geos/graph/imprintrectangle.s @@ -0,0 +1,14 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; void ImprintRectangle (void); + + .export _ImprintRectangle + + .include "../inc/jumptab.inc" + +_ImprintRectangle = ImprintRectangle + diff --git a/libsrc/geos/graph/initdrawwindow.s b/libsrc/geos/graph/initdrawwindow.s new file mode 100644 index 000000000..307af72ea --- /dev/null +++ b/libsrc/geos/graph/initdrawwindow.s @@ -0,0 +1,25 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 +; 11.03.2000 + +; void InitDrawWindow (struct window *myWindow); + + .importzp ptr4 + + .export _InitDrawWindow + + .include "../inc/geossym.inc" + +_InitDrawWindow: ;a/x is a struct window* + sta ptr4 + stx ptr4+1 + ldy #0 +copyWin: lda (ptr4),y + sta r2L,y + iny + cpy #6 + bne copyWin + rts diff --git a/libsrc/geos/graph/invertline.s b/libsrc/geos/graph/invertline.s new file mode 100644 index 000000000..dcae0b46e --- /dev/null +++ b/libsrc/geos/graph/invertline.s @@ -0,0 +1,16 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void InvertLine (char y, int xstart, int xend); + + .import HLineRegs + .export _InvertLine + + .include "../inc/jumptab.inc" + +_InvertLine: + jsr HLineRegs + jmp InvertLine diff --git a/libsrc/geos/graph/invertrectangle.s b/libsrc/geos/graph/invertrectangle.s new file mode 100644 index 000000000..0ef9d8580 --- /dev/null +++ b/libsrc/geos/graph/invertrectangle.s @@ -0,0 +1,14 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void InvertRectangle (void); + + .export _InvertRectangle + + .include "../inc/jumptab.inc" + +_InvertRectangle = InvertRectangle + diff --git a/libsrc/geos/graph/loadcharset.s b/libsrc/geos/graph/loadcharset.s new file mode 100644 index 000000000..d61b7f98b --- /dev/null +++ b/libsrc/geos/graph/loadcharset.s @@ -0,0 +1,17 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 21.12.99 + +; void LoadCharSet (struct fontdesc *myFont); + + .export _LoadCharSet + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_LoadCharSet: + sta r0L + stx r0H + jmp LoadCharSet diff --git a/libsrc/geos/graph/pointregs.s b/libsrc/geos/graph/pointregs.s new file mode 100644 index 000000000..f87667aa3 --- /dev/null +++ b/libsrc/geos/graph/pointregs.s @@ -0,0 +1,25 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + + .importzp ptr4 + + .export PointRegs + + .include "../inc/geossym.inc" + +PointRegs: ;a/x is a struct pixel* + sta ptr4 + stx ptr4+1 + ldy #0 + lda (ptr4),y + sta r3L + iny + lda (ptr4),y + sta r3H + iny + lda (ptr4),y + sta r11L + rts diff --git a/libsrc/geos/graph/putchar.s b/libsrc/geos/graph/putchar.s new file mode 100644 index 000000000..ed885800e --- /dev/null +++ b/libsrc/geos/graph/putchar.s @@ -0,0 +1,21 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 30.10.99 + +; void PutChar (char character, char y, int x); + + .import popa + .export _PutChar + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_PutChar: + sta r11L + stx r11H + jsr popa + sta r1H + jsr popa + jmp PutChar diff --git a/libsrc/geos/graph/putdecimal.s b/libsrc/geos/graph/putdecimal.s new file mode 100644 index 000000000..77e0ed2ed --- /dev/null +++ b/libsrc/geos/graph/putdecimal.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 30.10.99 + +; void PutDecimal (char style, int value, char y, int x); + + .import popa, popax + .import getintcharint + .export _PutDecimal + + .include "../inc/jumptab.inc" + +_PutDecimal: + jsr getintcharint + jsr popa + jmp PutDecimal diff --git a/libsrc/geos/graph/putstring.s b/libsrc/geos/graph/putstring.s new file mode 100644 index 000000000..53b685d0d --- /dev/null +++ b/libsrc/geos/graph/putstring.s @@ -0,0 +1,17 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 30.10.99 + +; void PutString (char *mytxt, char y, int x); + + .import popax, popa + .import getintcharint + .export _PutString + + .include "../inc/jumptab.inc" + +_PutString: + jsr getintcharint + jmp PutString diff --git a/libsrc/geos/graph/recoverline.s b/libsrc/geos/graph/recoverline.s new file mode 100644 index 000000000..1a59ae247 --- /dev/null +++ b/libsrc/geos/graph/recoverline.s @@ -0,0 +1,18 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void RecoverLine (char y, int xstart, int xend); + + .import HLineRegs + + .export _RecoverLine + + .include "../inc/jumptab.inc" + +_RecoverLine: + jsr HLineRegs + jmp RecoverLine + diff --git a/libsrc/geos/graph/recoverrectangle.s b/libsrc/geos/graph/recoverrectangle.s new file mode 100644 index 000000000..426b83d75 --- /dev/null +++ b/libsrc/geos/graph/recoverrectangle.s @@ -0,0 +1,14 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void RecoverRectangle (void); + + .export _RecoverRectangle + + .include "../inc/jumptab.inc" + +_RecoverRectangle = RecoverRectangle + diff --git a/libsrc/geos/graph/rectangle.s b/libsrc/geos/graph/rectangle.s new file mode 100644 index 000000000..c25a4e64d --- /dev/null +++ b/libsrc/geos/graph/rectangle.s @@ -0,0 +1,13 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void Rectangle (void); + + .export _Rectangle + + .include "../inc/jumptab.inc" + +_Rectangle = Rectangle diff --git a/libsrc/geos/graph/setpattern.s b/libsrc/geos/graph/setpattern.s new file mode 100644 index 000000000..e5c80bf10 --- /dev/null +++ b/libsrc/geos/graph/setpattern.s @@ -0,0 +1,13 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void SetPattern (char pattern); + + .export _SetPattern + + .include "../inc/jumptab.inc" + +_SetPattern = SetPattern diff --git a/libsrc/geos/graph/testpoint.s b/libsrc/geos/graph/testpoint.s new file mode 100644 index 000000000..f068e77b5 --- /dev/null +++ b/libsrc/geos/graph/testpoint.s @@ -0,0 +1,21 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; char TestPoint (struct pixel *mypixel); + + .import PointRegs + .export _TestPoint + + .include "../inc/jumptab.inc" + +_TestPoint: + jsr PointRegs + jsr TestPoint + bcc goFalse + lda #$ff + rts +goFalse: lda #0 + rts diff --git a/libsrc/geos/graph/usesystemfont.s b/libsrc/geos/graph/usesystemfont.s new file mode 100644 index 000000000..09072733e --- /dev/null +++ b/libsrc/geos/graph/usesystemfont.s @@ -0,0 +1,13 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 30.10.99 + +; void UseSystemFont (void); + + .export _UseSystemFont + + .include "../inc/jumptab.inc" + +_UseSystemFont = UseSystemFont diff --git a/libsrc/geos/graph/verticalline.s b/libsrc/geos/graph/verticalline.s new file mode 100644 index 000000000..5ce077082 --- /dev/null +++ b/libsrc/geos/graph/verticalline.s @@ -0,0 +1,24 @@ + +; +; Maciej 'YTM/Alliance' Witkowiak +; +; 29.10.99 + +; void VerticalLine (char pattern, char ystart, char yend, int x); + + .import popa + + .export _VerticalLine + + .include "../inc/jumptab.inc" + .include "../inc/geossym.inc" + +_VerticalLine: + stx r4H + sta r4L + jsr popa + sta r3H + jsr popa + sta r3L + jsr popa + jmp VerticalLine diff --git a/libsrc/geos/inc/const.inc b/libsrc/geos/inc/const.inc new file mode 100644 index 000000000..dcbf8e479 --- /dev/null +++ b/libsrc/geos/inc/const.inc @@ -0,0 +1,418 @@ + +;GeosConst - various system constans sorted by function +;reassembled by Maciej 'YTM/Alliance' Witkowiak +;4-2-99, 18-3-99 + +NULL = 0 +FALSE = NULL +TRUE = $ff + +MOUSE_SPRNUM = 0 + +DISK_DRV_LGH = $0d80 + +; +;filetypes +; GEOS +NOT_GEOS = 0 +BASIC = 1 +ASSEMBLY = 2 +DATA = 3 +SYSTEM = 4 +DESK_ACC = 5 +APPLICATION = 6 +APPL_DATA = 7 +FONT = 8 +PRINTER = 9 +INPUT_DEVICE = 10 +DISK_DEVICE = 11 +SYSTEM_BOOT = 12 +TEMPORARY = 13 +AUTO_EXEC = 14 +INPUT_128 = 15 +NUMFILETYPES = 16 +; structure +SEQUENTIAL = 0 +VLIR = 1 +; DOS +DEL = 0 +SEQ = 1 +PRG = 2 +USR = 3 +REL = 4 +CBM = 5 + +;drivetypes +DRV_NULL = 0 +DRV_1541 = 1 +DRV_1571 = 2 +DRV_1581 = 3 +DRV_NETWORK = 15 + +;various disk +REL_FILE_NUM = 9 +CMND_FILE_NUM = 15 +MAX_CMND_STR = 32 +DIR_1581_TRACK = 40 +DIR_ACC_CHAN = 13 +DIR_TRACK = 18 +N_TRACKS = 35 +DK_NM_ID_LEN = 18 +TRACK = 9 +SECTOR = 12 +TOTAL_BLOCKS = 664 + +;colours +BLACK = 0 +WHITE = 1 +RED = 2 +CYAN = 3 +PURPLE = 4 +GREEN = 5 +BLUE = 6 +YELLOW = 7 +ORANGE = 8 +BROWN = 9 +LTRED = 10 +DKGREY = 11 +GREY = 12 +MEDGREY = 12 +LTGREEN = 13 +LTBLUE = 14 +LTGREY = 15 + +;vic memory banks +GRBANK0 = %11 +GRBANK1 = %10 +GRBANK2 = %01 +GRBANK3 = %00 + +;screen +VIC_X_POS_OFF = 24 +VIC_Y_POS_OFF = 50 +SC_BYTE_WIDTH = 40 +SC_PIX_HEIGHT = 200 +SC_PIX_WIDTH = 320 +SC_SIZE = 8000 +;128 screen size constants +SCREENBYTEWIDTH = 80 +SCREENPIXELWIDTH = 640 + + +;control characters +EOF = 0 +BACKSPACE = 8 +FORWARDSPACE = 9 +TAB = 9 +LF = 10 +HOME = 11 +PAGE_BREAK = 12 +UPLINE = 12 +CR = 13 +ULINEON = 14 +ULINEOFF = 15 +ESC_GRAPHICS = 16 +ESC_RULER = 17 +REV_ON = 18 +REV_OFF = 19 +GOTOX = 20 +GOTOY = 21 +GOTOXY = 22 +NEWCARDSET = 23 +BOLDON = 24 +ITALICON = 25 +OUTLINEON = 26 +PLAINTEXT = 27 + +;keyboard +KEY_F1 = 1 +KEY_F2 = 2 +KEY_F3 = 3 +KEY_F4 = 4 +KEY_F5 = 5 +KEY_F6 = 6 +KEY_NOSCRL = 7 +KEY_ENTER = 11 +KEY_F7 = 14 +KEY_F8 = 15 +KEY_UP = 16 +KEY_DOWN = 17 +KEY_HOME = 18 +KEY_CLEAR = 19 +KEY_LARROW = 20 +KEY_UPARROR = 21 +KEY_STOP = 22 +KEY_RUN = 23 +KEY_BPS = 24 +KEY_HELP = 25 +KEY_ALT = 26 +KEY_ESC = 27 +KEY_INSERT = 28 +KEY_DELETE = 29 +KEY_RIGHT = 30 +KEY_INVALID = 31 +KEY_LEFT = BACKSPACE + +;DialogBox +; icons +OK = 1 +CANCEL = 2 +YES = 3 +NO = 4 +OPEN = 5 +DISK = 6 +; commands +DBTXTSTR = 11 +DBVARSTR = 12 +DBGETSTRING = 13 +DBSYSOPV = 14 +DBGRPHSTR = 15 +DBGETFILES = 16 +DBOPVEC = 17 +DBUSRICON = 18 +DB_USR_ROUT = 19 +; tabulation in standard window +DBI_X_0 = 1 +DBI_X_1 = 9 +DBI_X_2 = 17 +DBI_Y_0 = 8 +DBI_Y_1 = 40 +DBI_Y_2 = 72 +; standard window +SET_DB_POS = 0 +DEF_DB_POS = $80 +DEF_DB_TOP = 32 +DEF_DB_BOT = 127 +DEF_DB_LEFT = 64 +DEF_DB_RIGHT = 255 +; text tabulation +TXT_LN_1_Y = 16 +TXT_LN_2_Y = 32 +TXT_LN_3_Y = 48 +TXT_LN_4_Y = 64 +TXT_LN_5_Y = 80 +TXT_LN_X = 16 +; ??? +SYSDBI_HEIGHT = 16 +SYSDBI_WIDTH = 6 + +;GraphicsString - commands +MOVEPENTO = 1 +LINETO = 2 +RECTANGLETO = 3 +NEWPATTERN = 5 +ESC_PUTSTRING = 6 +FRAME_RECTO = 7 +PEN_X_DELTA = 8 +PEN_Y_DELTA = 9 +PEN_XY_DELTA = 10 + + +;DoMenu - menutypes +MENU_ACTION = $00 +DYN_SUB_MENU = $40 +SUB_MENU = $80 +HORIZONTAL = %00000000 +VERTICAL = %10000000 + +;Errors +ANY_FAULT = %11110000 +NO_BLOCKS = 1 +INV_TRACK = 2 +INSUFF_SPACE = 3 +FULL_DIRECTORY = 4 +FILE_NOT_FOUND = 5 +BAD_BAM = 6 +UNOPENED_VLIR = 7 +INV_RECORD = 8 +OUT_OF_RECORDS = 9 +STRUCT_MISMAT = 10 +BFR_OVERFLOW = 11 +CANCEL_ERR = 12 +DEV_NOT_FOUND = 13 +INCOMPATIBLE = 14 +HDR_NOT_THERE = $20 +NO_SYNC = $21 +DBLK_NOT_THERE = $22 +DAT_CHKSUM_ERR = $23 +WR_VER_ERR = $25 +WR_PR_ON = $26 +HDR_CHKSUM_ERR = $27 +DSK_ID_MISMAT = $29 +BYTE_DEC_ERR = $2e +DOS_MISMATCH = $73 + +;Offsets +; ??? +OFF_INDEX_PTR = 1 +; icons +OFF_NM_ICNS = 0 +OFF_IC_XMOUSE = 1 +OFF_IC_YMOUSE = 3 +OFF_PIC_ICON = 0 +OFF_X_ICON_POS = 2 +OFF_Y_ICON_POS = 3 +OFF_WDTH_ICON = 4 +OFF_HEIGHT_ICON = 5 +OFF_SRV_RT_ICON = 6 +OFF_NX_ICON = 8 +; menu +OFF_MY_TOP = 0 +OFF_MY_BOT = 1 +OFF_MX_LEFT = 2 +OFF_MX_RIGHT = 4 +OFF_NUM_M_ITEMS = 6 +OFF_1ST_M_ITEM = 7 +; dialog box +OFF_DB_FORM = 0 +OFF_DB_TOP = 1 +OFF_DB_BOT = 2 +OFF_DB_LEFT = 3 +OFF_DB_RIGHT = 5 +OFF_DB_1STCMD = 7 +; directory +; disk header +OFF_TO_BAM = 4 +OFF_DISK_NAME = 144 +OFF_GS_DTYPE = 189 +OFF_OP_TR_SC = 171 +OFF_GS_ID = 173 +; dir entry +FRST_FILE_ENTRY = 2 +OFF_CFILE_TYPE = 0 +OFF_DE_TR_SC = 1 +OFF_FNAME = 3 +OFF_GHDR_PTR = 19 +OFF_GSTRUC_TYPE = 21 +OFF_GFILE_TYPE = 22 +OFF_YEAR = 23 +OFF_SIZE = 28 +OFF_NXT_FILE = 32 +; file header +O_GHIC_WIDTH = 2 +O_GHIC_HEIGHT = 3 +O_GHIC_PIC = 4 +O_GHCMDR_TYPE = 68 +O_GHGEOS_TYPE = 69 +O_GHSTR_TYPE = 70 +O_GHST_ADDR = 71 +O_GHEND_ADDR = 73 +O_GHST_VEC = 75 +O_GHFNAME = 77 +O_128_FLAGS = 96 +O_GH_AUTHOR = 97 +O_GHP_DISK = 97 +O_GHP_FNAME = 117 +O_GHINFO_TXT = $a0 + +;values for config - C128 mmu +CIOIN = $7E ;60K RAM, 4K I/O space in +CRAM64K = $7F ;64K RAM +CKRNLBASIOIN = $40 ;kernal, I/O and basic ROM's mapped into memory +CKRNLIOIN = $4E ;Kernal ROM and I/O space mapped in + +;values of faultData - pointer position vs. mouseTop/Bottom/Left/Right +; bit numbers +OFFTOP_BIT = 7 +OFFBOTTOM_BIT = 6 +OFFLEFT_BIT = 5 +OFFRIGHT_BIT = 4 +OFFMENU_BIT = 3 +; masks +SET_OFFTOP = %10000000 +SET_OFFBOTTOM = %01000000 +SET_OFFLEFT = %00100000 +SET_OFFRIGHT = %00010000 +SET_OFFMENU = %00001000 + +;values of currentMode +; bit numbers +UNDERLINE_BIT = 7 +BOLD_BIT = 6 +REVERSE_BIT = 5 +ITALIC_BIT = 4 +OUTLINE_BIT = 3 +SUPERSCRIPT_BIT = 2 +SUBSCRIPT_BIT = 1 +; masks +SET_UNDERLINE = %10000000 +SET_BOLD = %01000000 +SET_REVERSE = %00100000 +SET_ITALIC = %00010000 +SET_OUTLINE = %00001000 +SET_SUPERSCRIPT = %00000100 +SET_SUBSCRIPT = %00000010 +SET_PLAINTEXT = %00000000 + +;Process control variable +; bit numbers +RUNABLE_BIT = 7 +BLOCKED_BIT = 6 +FROZEN_BIT = 5 +NOTIMER_BIT = 4 +; masks +SET_RUNABLE = %10000000 +SET_BLOCKED = %01000000 +SET_FROZEN = %00100000 +SET_NOTIMER = %00010000 + +;mouseOn +; bit numbers +MOUSEON_BIT = 7 +MENUON_BIT = 6 +ICONSON_BIT = 5 +; masks +SET_MSE_ON = %10000000 +SET_MENUON = %01000000 +SET_ICONSON = %00100000 + +;pressFlag +; bit numbers +KEYPRESS_BIT = 7 +INPUT_BIT = 6 +MOUSE_BIT = 5 +; masks +SET_KEYPRESS = %10000000 +SET_INPUTCHG = %01000000 +SET_MOUSE = %00100000 + +;dispBufferOn +ST_WRGS_FORE = $20 +ST_WR_BACK = $40 +ST_WR_FORE = $80 + +;alarmSetFlag +ALARMMASK = %00000100 + +;PutDecimal + ;leading zeroes +SET_NOSURPRESS = %00000000 +SET_SURPRESS = %01000000 + ;justification +SET_RIGHTJUST = %00000000 +SET_LEFTJUST = %10000000 + +;icons, menus status flags +ST_FLASH = $80 +ST_INVERT = $40 +ST_LD_AT_ADDR = $01 +ST_LD_DATA = $80 +ST_PR_DATA = $40 +ST_WR_PR = $40 + +;??? +ADD1_W = $2000 +DOUBLE_B = $80 +DOUBLE_W = $8000 + +CLR_SAVE = %01000000 +CONSTRAINED = %01000000 +UN_CONSTRAINED = %00000000 +FG_SAVE = %10000000 + +FUTURE1 = 7 +FUTURE2 = 8 +FUTURE3 = 9 +FUTURE4 = 10 +USELAST = 127 +SHORTCUT = 128 diff --git a/libsrc/geos/inc/diskdrv.inc b/libsrc/geos/inc/diskdrv.inc new file mode 100644 index 000000000..ce106d761 --- /dev/null +++ b/libsrc/geos/inc/diskdrv.inc @@ -0,0 +1,42 @@ + +;GEOS Disk Driver JumpTab +;reassembled by Maciej 'YTM/Alliance' Witkowiak +;4-2-99 + +;pointers +_InitForIO = $9000 +_DoneWithIO = $9002 +_ExitTurbo = $9004 +_PurgeTurbo = $9006 +_EnterTurbo = $9008 +_ChangeDiskDevice = $900a +_NewDisk = $900c +_ReadBlock = $900e +_WriteBlock = $9010 +_VerWriteBlock = $9012 +_OpenDisk = $9014 +_GetBlock = $9016 +_PutBlock = $9018 +_GetDirHead = $901a +_PutDirHead = $901c +_GetFreeDirBlk = $901e +_CalcBlksFree = $9020 +_FreeBlock = $9022 +_SetNextFree = $9024 +_FindBAMBit = $9026 +_NxtBlkAlloc = $9028 +_BlkAlloc = $902a +_ChkDkGEOS = $902c +_SetGEOSDisk = $902e + +;jump table +Get1stDirEntry = $9030 +GetNxtDirEntry = $9033 +GetBorder = $9036 +AddDirBlock = $9039 +ReadBuff = $903c +WriteBuff = $903f +;??? = $9042 +;??? = $9045 +AllocateBlock = $9048 +ReadLink = $904b diff --git a/libsrc/geos/inc/geosmac.ca65.inc b/libsrc/geos/inc/geosmac.ca65.inc new file mode 100644 index 000000000..e233ef977 --- /dev/null +++ b/libsrc/geos/inc/geosmac.ca65.inc @@ -0,0 +1,263 @@ + +;GEOS macros +;reassembled for 6502TASM/MMS by Maciej 'YTM/Alliance' Witkowiak +;4-2-99 + +;28-6-99 - ca65 port +;macro 'sub' renamed to 'ssub' due to 65816 mnemonics + + + .macro LoadB dest, value + lda #value + sta dest + .endmacro + + .macro LoadW dest, value + lda #>value + sta dest+1 + .if (>value)<>(value)=0 + bcc Skip + inc dest+1 + .else + lda #>value + adc dest+1 + sta dest+1 + .endif +Skip: + .endmacro + + .macro ssub source + sec + sbc source + .endmacro + + .macro SubB source, dest + lda dest + ssub source + sta dest + .endmacro + + .macro SubW source, dest + SubB source+0, dest+0 + lda dest+1 + sbc source+1 + sta dest+1 + .endmacro + + .macro SubVW value, dest + sec + lda dest+0 + sbc #value + sta dest+1 + .endmacro + + .macro CmpB source, dest + lda source + cmp dest + .endmacro + + .macro CmpBI source, immed + lda source + cmp #immed + .endmacro + + .macro CmpW source, dest +.local Skip + CmpB source+1, dest+1 + bne Skip + CmpB source+0, dest+0 +Skip: + .endmacro + + .macro CmpWI source, immed +.local Skip + CmpBI source+1, >immed + bne Skip + CmpBI source+0, _reset_brk + jsr _atexit ; Install an exit handler + +L1: lda #brk_handler + sta BRKVec+1 + rts + +.endproc + + +; Reset the break vector +.proc _reset_brk + + lda oldvec + sta BRKVec + lda oldvec+1 + sta BRKVec+1 + rts + +.endproc + + + +; Break handler, called if a break occurs + +.proc brk_handler + + pla + sta _brk_y + pla + sta _brk_x + pla + sta _brk_a + pla + and #$EF ; Clear break bit + sta _brk_sr + pla ; PC low + sec + sbc #2 ; Point to start of brk + sta _brk_pc + pla ; PC high + sbc #0 + sta _brk_pc+1 + + jsr uservec ; Call the user's routine + + lda _brk_pc+1 + pha + lda _brk_pc + pha + lda _brk_sr + pha + ldx _brk_x + ldy _brk_y + lda _brk_a + rti ; Jump back... + +.endproc + + + diff --git a/libsrc/pet/cgetc.s b/libsrc/pet/cgetc.s new file mode 100644 index 000000000..4113e1305 --- /dev/null +++ b/libsrc/pet/cgetc.s @@ -0,0 +1,68 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; char cgetc (void); +; + + .export _cgetc + .import cursor + + .include "pet.inc" + +_cgetc: lda KEY_COUNT ; Get number of characters + bne L3 ; Jump if there are already chars waiting + +; Switch on the cursor if needed + + lda CURS_FLAG + pha + lda cursor + jsr setcursor +L1: lda KEY_COUNT + beq L1 + ldx #0 + pla + bne L2 + inx +L2: txa + jsr setcursor + +; Fetch the character from the keyboard buffer + +L3: sei + ldy KEY_BUF + ldx #$00 +L4: lda KEY_BUF+1,x + sta KEY_BUF,x + inx + cpx KEY_COUNT + bne L4 + dec KEY_COUNT + cli + ldx #$00 ; Clear high byte + tya + rts + +; Switch the cursor on or off + +setcursor: + tax ; On or off? + bne seton ; Go set it on + lda CURS_FLAG ; Is the cursor currently off? + bne crs9 ; Jump if yes + lda #1 + sta CURS_FLAG ; Mark it as off + lda CURS_STATE ; Cursor currently displayed? + beq crs8 ; Jump if no + ldy CURS_X ; Get the character column + lda (SCREEN_PTR),y ; Get character + eor #$80 + sta (SCREEN_PTR),y ; Store character back +crs8: lda #0 + sta CURS_STATE ; Cursor not displayed +crs9: rts + +seton: lda #0 + sta CURS_FLAG + rts + diff --git a/libsrc/pet/clrscr.s b/libsrc/pet/clrscr.s new file mode 100644 index 000000000..2cabcc084 --- /dev/null +++ b/libsrc/pet/clrscr.s @@ -0,0 +1,50 @@ +; +; Ullrich von Bassewitz, 26.11.1998 +; +; void clrscr (void); +; + + .export _clrscr + .import plot + .importzp ptr1 + .import xsize + + .include "pet.inc" + +_clrscr: + +; Set the screen base address + + lda #$00 + sta ptr1 + lda #$80 + sta ptr1+1 + +; Determine, how many pages to fill + + ldx #4 + lda xsize + cmp #40 + beq L1 + ldx #8 + +; Clear the screen + +L1: lda #$20 ; Screen code for blank + ldy #$00 +L2: sta (ptr1),y + iny + bne L2 + inc ptr1+1 + dex + bne L2 + +; Set the cursor to 0/0 + + lda #0 + sta CURS_X + sta CURS_Y + jmp plot + + rts + diff --git a/libsrc/pet/color.s b/libsrc/pet/color.s new file mode 100644 index 000000000..6fbca5682 --- /dev/null +++ b/libsrc/pet/color.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char __fastcall__ textcolor (unsigned char color); +; unsigned char __fastcall__ bgcolor (unsigned char color); +; unsigned char __fastcall__ bordercolor (unsigned char color); +; + + .export _textcolor, _bgcolor, _bordercolor + .import return0, return1 + +_textcolor = return1 + +_bgcolor = return0 + +_bordercolor = return0 + + diff --git a/libsrc/pet/conio.s b/libsrc/pet/conio.s new file mode 100644 index 000000000..19e753333 --- /dev/null +++ b/libsrc/pet/conio.s @@ -0,0 +1,24 @@ +; +; Ullrich von Bassewitz, 26.11.1998 +; +; Low level stuff for screen output/console input +; + + .export initconio + .import xsize, ysize + .exportzp CURS_X, CURS_Y + + .include "pet.inc" + +.code + +initconio: + ldx SCR_LINELEN + inx ; Variable is one less + stx xsize + lda #25 + sta ysize + rts + + + diff --git a/libsrc/pet/cputc.s b/libsrc/pet/cputc.s new file mode 100644 index 000000000..3c8b21d14 --- /dev/null +++ b/libsrc/pet/cputc.s @@ -0,0 +1,120 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void cputcxy (unsigned char x, unsigned char y, char c); +; void cputc (char c); +; + + .export _cputcxy, _cputc, cputdirect, putchar + .export advance, newline, plot + .import popa, _gotoxy + .import xsize, revers + + .include "pet.inc" + .include "../cbm/cbm.inc" + +_cputcxy: + pha ; Save C + jsr popa ; Get Y + jsr _gotoxy ; Set cursor, drop x + pla ; Restore C + +; Plot a character - also used as internal function + +_cputc: cmp #$0D ; CR? + bne L1 + lda #0 + sta CURS_X + beq plot ; Recalculate pointers + +L1: cmp #$0A ; LF? + bne L2 + ldy CURS_Y + iny + bne newline ; Recalculate pointers + +; Printable char of some sort + +L2: cmp #' ' + bcc cputdirect ; Other control char + tay + bmi L10 + cmp #$60 + bcc L3 + and #$DF + bne cputdirect ; Branch always +L3: and #$3F + +cputdirect: + jsr putchar ; Write the character to the screen + +; Advance cursor position + +advance: + iny + cpy xsize + bne L9 + ldy #0 ; new line +newline: + clc + lda xsize + adc SCREEN_PTR + sta SCREEN_PTR + bcc L4 + inc SCREEN_PTR+1 +L4: inc CURS_Y +L9: sty CURS_X + rts + +; Handle character if high bit set + +L10: and #$7F + cmp #$7E ; PI? + bne L11 + lda #$5E ; Load screen code for PI + bne cputdirect +L11: ora #$40 + bne cputdirect + + + +; Set cursor position, calculate RAM pointers + +plot: ldy CURS_Y + lda ScrLo,y + sta SCREEN_PTR + lda ScrHi,y + ldy xsize + cpy #40 + beq @L1 + asl SCREEN_PTR ; 80 column mode + rol a +@L1: ora #$80 ; Screen at $8000 + sta SCREEN_PTR+1 + rts + + +; Write one character to the screen without doing anything else, return X +; position in Y + +putchar: + ora revers ; Set revers bit + ldy CURS_X + sta (SCREEN_PTR),y ; Set char + rts + +; Screen address tables - offset to real screen + +.rodata + +ScrLo: .byte $00, $28, $50, $78, $A0, $C8, $F0, $18 + .byte $40, $68, $90, $B8, $E0, $08, $30, $58 + .byte $80, $A8, $D0, $F8, $20, $48, $70, $98 + .byte $C0 + +ScrHi: .byte $00, $00, $00, $00, $00, $00, $00, $01 + .byte $01, $01, $01, $01, $01, $02, $02, $02 + .byte $02, $02, $02, $02, $03, $03, $03, $03 + .byte $03 + + diff --git a/libsrc/pet/crt0.s b/libsrc/pet/crt0.s new file mode 100644 index 000000000..58e67eb05 --- /dev/null +++ b/libsrc/pet/crt0.s @@ -0,0 +1,125 @@ +; +; Startup code for cc65 (PET version) +; +; This must be the *first* file on the linker command line +; + + .export _exit + .import __hinit, initconio, zerobss, push0, doatexit + .import _main + + .include "pet.inc" + .include "../cbm/cbm.inc" + +; ------------------------------------------------------------------------ +; Define and export the ZP variables for the C64 runtime + + .exportzp sp, sreg, regsave + .exportzp ptr1, ptr2, ptr3, ptr4 + .exportzp tmp1, tmp2, tmp3, tmp4 + .exportzp regbank, zpspace + +sp = $02 ; stack pointer +sreg = $04 ; secondary register/high 16 bit for longs +regsave = $06 ; slot to save/restore (E)AX into +ptr1 = $0A ; +ptr2 = $0C +ptr3 = $0E +ptr4 = $10 +tmp1 = $12 +tmp2 = $13 +tmp3 = $14 +tmp4 = $15 +regbank = $16 ; 6 byte register bank +zpspace = $1A ; Zero page space allocated + +; ------------------------------------------------------------------------ +; BASIC header with a SYS call + + .org $3FF + .word Head ; Load address +Head: .word @Next + .word 1000 ; Line number + .byte $9E,"1037" ; SYS 1037 + .byte $00 ; End of BASIC line +@Next: .word 0 ; BASIC end marker + .reloc + +; ------------------------------------------------------------------------ +; Actual code + + ldy #zpspace-1 +L1: lda sp,y + sta zpsave,y ; Save the zero page locations we need + dey + bpl L1 + +; Close open files + + jsr CLRCH + +; Switch to second charset + + lda #14 +; sta $E84C ; See PET FAQ + jsr BSOUT + +; Clear the BSS data + + jsr zerobss + +; Save system stuff and setup the stack + + tsx + stx spsave ; Save the system stack ptr + + lda MEMSIZE + sta sp + lda MEMSIZE+1 + sta sp+1 ; Set argument stack ptr + +; Initialize the heap + + jsr __hinit + +; Initialize conio stuff + + jsr initconio + +; Pass an empty command line + + jsr push0 ; argc + jsr push0 ; argv + + ldy #4 ; Argument size + jsr _main ; call the users code + +; fall thru to exit... + +_exit: jsr doatexit ; call exit functions + + ldx spsave + txs ; Restore stack pointer + +; Copy back the zero page stuff + + ldy #zpspace-1 +L2: lda zpsave,y + sta sp,y + dey + bpl L2 + +; Back to basic + + rts + + +.data + +zpsave: .res zpspace + +.bss + +spsave: .res 1 +mmusave:.res 1 + diff --git a/libsrc/pet/kbhit.s b/libsrc/pet/kbhit.s new file mode 100644 index 000000000..272bad778 --- /dev/null +++ b/libsrc/pet/kbhit.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 26.11.1998 +; +; int kbhit (void); +; + + .export _kbhit + .import return0, return1 + + .include "pet.inc" + +_kbhit: + lda KEY_COUNT ; Get number of characters + bne L1 + jmp return0 +L1: jmp return1 + + + + diff --git a/libsrc/pet/pet.inc b/libsrc/pet/pet.inc new file mode 100644 index 000000000..4dcb73947 --- /dev/null +++ b/libsrc/pet/pet.inc @@ -0,0 +1,32 @@ +; +; C64 generic definitions. Stolen from Elite128 +; + + +; --------------------------------------------------------------------------- +; Zero page, Commodore stuff + +MEMSIZE = $34 ; Size of memory installed +ST = $96 ; IEC status byte +SECADR = $D3 ; Secondary address +DEVNUM = $D4 ; Device number +KEY_COUNT = $9E ; Number of keys in input buffer +CURS_FLAG = $A7 ; 1 = cursor off +CURS_BLINK = $A8 ; Blink counter +CURS_CHAR = $A9 ; Character under the cursor +CURS_STATE = $AA ; Cursor blink state +SCREEN_PTR = $C4 ; Pointer to current char in text screen +CURS_X = $C6 ; Cursor column +CURS_Y = $D8 ; Cursor row +SCR_LINELEN = $D5 ; Screen line length + +KEY_BUF = $26F ; Keyboard buffer + + +; --------------------------------------------------------------------------- +; Vector and other locations + +IRQVec = $0090 +BRKVec = $0092 +NMIVec = $0094 + diff --git a/libsrc/plus4/Makefile b/libsrc/plus4/Makefile new file mode 100644 index 000000000..ae624cf9a --- /dev/null +++ b/libsrc/plus4/Makefile @@ -0,0 +1,28 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +%.o: %.c + @echo $< + @$(CC) $(CFLAGS) $< + @$(AS) -o $@ $(AFLAGS) $(*).s + +%.o: %.s + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +C_OBJS = + +S_OBJS = crt0.o kbhit.o conio.o clrscr.o cputc.o cgetc.o\ + color.o readjoy.o break.o + +all: $(C_OBJS) $(S_OBJS) + +clean: + @rm -f $(C_OBJS:.c=.s) + @rm -f $(C_OBJS) + @rm -f $(S_OBJS) + @rm -f crt0.o + diff --git a/libsrc/plus4/break.s b/libsrc/plus4/break.s new file mode 100644 index 000000000..436553426 --- /dev/null +++ b/libsrc/plus4/break.s @@ -0,0 +1,108 @@ +; +; Ullrich von Bassewitz, 27.09.1998 +; +; void set_brk (unsigned Addr); +; void reset_brk (void); +; + + .export _set_brk, _reset_brk + .export _brk_a, _brk_x, _brk_y, _brk_sr, _brk_pc + .import _atexit + + .include "plus4.inc" + + +.bss +_brk_a: .res 1 +_brk_x: .res 1 +_brk_y: .res 1 +_brk_sr: .res 1 +_brk_pc: .res 2 + +oldvec: .res 2 ; Old vector + + +.data +uservec: jmp $FFFF ; Patched at runtime + + +.code + +; Set the break vector +.proc _set_brk + + sta uservec+1 + stx uservec+2 ; Set the user vector + + lda oldvec + ora oldvec+1 ; Did we save the vector already? + bne L1 ; Jump if we installed the handler already + + lda BRKVec + sta oldvec + lda BRKVec+1 + sta oldvec+1 ; Save the old vector + + lda #<_reset_brk + ldx #>_reset_brk + jsr _atexit ; Install an exit handler + +L1: lda #brk_handler + sta BRKVec+1 + rts + +.endproc + + +; Reset the break vector +.proc _reset_brk + + lda oldvec + sta BRKVec + lda oldvec+1 + sta BRKVec+1 + rts + +.endproc + + + +; Break handler, called if a break occurs + +.proc brk_handler + + pla + sta _brk_y + pla + sta _brk_x + pla + sta _brk_a + pla + and #$EF ; Clear break bit + sta _brk_sr + pla ; PC low + sec + sbc #2 ; Point to start of brk + sta _brk_pc + pla ; PC high + sbc #0 + sta _brk_pc+1 + + jsr uservec ; Call the user's routine + + lda _brk_pc+1 + pha + lda _brk_pc + pha + lda _brk_sr + pha + ldx _brk_x + ldy _brk_y + lda _brk_a + rti ; Jump back... + +.endproc + + diff --git a/libsrc/plus4/cgetc.s b/libsrc/plus4/cgetc.s new file mode 100644 index 000000000..493bbf195 --- /dev/null +++ b/libsrc/plus4/cgetc.s @@ -0,0 +1,48 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; char cgetc (void); +; + + .export _cgetc + .import cursor + + .include "plus4.inc" + + +_cgetc: lda KEY_COUNT ; Get number of characters + ora FKEY_COUNT ; Or with number of function key chars + bne L2 ; Jump if there are already chars waiting + +; Switch on the cursor if needed + + ldy CURS_X + lda (CRAM_PTR),y ; Get current char + pha ; And save it + lda CHARCOLOR + sta (CRAM_PTR),y + + lda cursor + beq L1 ; Jump if no cursor + tya + clc + adc SCREEN_PTR + sta TED_CURSLO + lda SCREEN_PTR+1 + adc #$00 + sbc #$0B ; + carry = $C00 (screen address) + sta TED_CURSHI + +L1: lda KEY_COUNT + ora FKEY_COUNT + beq L1 + pla + sta (CRAM_PTR),y + lda #$ff + sta TED_CURSLO ; Cursor off + sta TED_CURSHI + +L2: jsr KBDREAD ; Read char and return in A + ldx #0 + rts + diff --git a/libsrc/plus4/clrscr.s b/libsrc/plus4/clrscr.s new file mode 100644 index 000000000..4b6c48355 --- /dev/null +++ b/libsrc/plus4/clrscr.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void clrscr (void); +; + + .export _clrscr + + .include "plus4.inc" + +_clrscr = CLRSCR + + + + diff --git a/libsrc/plus4/color.s b/libsrc/plus4/color.s new file mode 100644 index 000000000..9d7107de8 --- /dev/null +++ b/libsrc/plus4/color.s @@ -0,0 +1,33 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char __fastcall__ textcolor (unsigned char color); +; unsigned char __fastcall__ bgcolor (unsigned char color); +; unsigned char __fastcall__ bordercolor (unsigned char color); +; + + .export _textcolor, _bgcolor, _bordercolor + + .include "plus4.inc" + +_textcolor: + ldx CHARCOLOR ; get old value + sta CHARCOLOR ; set new value + txa + rts + + +_bgcolor: + ldx TED_BGCOLOR ; get old value + sta TED_BGCOLOR ; set new value + txa + rts + + +_bordercolor: + ldx TED_BORDERCOLOR ; get old value + sta TED_BORDERCOLOR ; set new value + txa + rts + + diff --git a/libsrc/plus4/conio.s b/libsrc/plus4/conio.s new file mode 100644 index 000000000..11d5307f6 --- /dev/null +++ b/libsrc/plus4/conio.s @@ -0,0 +1,41 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; Low level stuff for screen output/console input +; + + .export initconio, doneconio + .exportzp CURS_X, CURS_Y + .import xsize, ysize + + .include "plus4.inc" + .include "../cbm/cbm.inc" + +.code + +initconio: + jsr SCREEN + stx xsize + sty ysize + ldy #15 +L1: lda fnkeys,y + sta FKEY_SPACE,y + dey + bpl L1 + rts + + +doneconio: + ldx #$39 ; Copy the original function keys +L2: lda FKEY_ORIG,x + sta FKEY_SPACE,x + dex + bpl L2 + rts + +; Function key table, readonly + +.rodata +fnkeys: .byte $01, $01, $01, $01, $01, $01, $01, $01 + .byte 133, 137, 134, 138, 135, 139, 136, 140 + diff --git a/libsrc/plus4/cputc.s b/libsrc/plus4/cputc.s new file mode 100644 index 000000000..4bf9d2e86 --- /dev/null +++ b/libsrc/plus4/cputc.s @@ -0,0 +1,105 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; void cputcxy (unsigned char x, unsigned char y, char c); +; void cputc (char c); +; + + .export _cputcxy, _cputc, cputdirect, putchar + .export advance, newline, plot + .import popa, _gotoxy + .import xsize, revers + + .include "plus4.inc" + .include "../cbm/cbm.inc" + +_cputcxy: + pha ; Save C + jsr popa ; Get Y + jsr _gotoxy ; Set cursor, drop x + pla ; Restore C + +; Plot a character - also used as internal function + +_cputc: cmp #$0D ; CR? + bne L1 + lda #0 + sta CURS_X + beq plot ; Recalculate pointers + +L1: cmp #$0A ; LF? + bne L2 + ldy CURS_Y + iny + bne newline ; Recalculate pointers + +; Printable char of some sort + +L2: cmp #' ' + bcc cputdirect ; Other control char + tay + bmi L10 + cmp #$60 + bcc L3 + and #$DF + bne cputdirect ; Branch always +L3: and #$3F + +cputdirect: + jsr putchar ; Write the character to the screen + +; Advance cursor position + +advance: + iny + cpy xsize + bne L9 + ldy #0 ; new line +newline: + clc + lda xsize + adc SCREEN_PTR + sta SCREEN_PTR + bcc L4 + inc SCREEN_PTR+1 +L4: clc + lda xsize + adc CRAM_PTR + sta CRAM_PTR + bcc L5 + inc CRAM_PTR+1 +L5: inc CURS_Y +L9: sty CURS_X + rts + +; Handle character if high bit set + +L10: and #$7F + cmp #$7E ; PI? + bne L11 + lda #$5E ; Load screen code for PI + bne cputdirect +L11: ora #$40 + bne cputdirect + + + +; Set cursor position, calculate RAM pointers + +plot: ldy CURS_X + ldx CURS_Y + clc + jmp PLOT ; Set the new cursor + + + +; Write one character to the screen without doing anything else, return X +; position in Y + +putchar: + ora revers ; Set revers bit + ldy CURS_X + sta (SCREEN_PTR),y ; Set char + lda CHARCOLOR + sta (CRAM_PTR),y ; Set color + rts diff --git a/libsrc/plus4/crt0.s b/libsrc/plus4/crt0.s new file mode 100644 index 000000000..72fce209d --- /dev/null +++ b/libsrc/plus4/crt0.s @@ -0,0 +1,129 @@ +; +; Startup code for cc65 (Plus/4 version) +; +; This must be the *first* file on the linker command line +; + + .export _exit + .import __hinit, push0, doatexit, _main + .import initconio, doneconio, zerobss + + .include "plus4.inc" + .include "../cbm/cbm.inc" + +; ------------------------------------------------------------------------ +; Define and export the ZP variables for the C64 runtime + + .exportzp sp, sreg, regsave + .exportzp ptr1, ptr2, ptr3, ptr4 + .exportzp tmp1, tmp2, tmp3, tmp4 + .exportzp regbank, zpspace + +sp = $02 ; stack pointer +sreg = $04 ; secondary register/high 16 bit for longs +regsave = $06 ; slot to save/restore (E)AX into +ptr1 = $0A ; +ptr2 = $0C +ptr3 = $0E +ptr4 = $10 +tmp1 = $12 +tmp2 = $13 +tmp3 = $14 +tmp4 = $15 +regbank = $16 ; 6 byte register bank +zpspace = $1A ; Zero page space allocated + +; ------------------------------------------------------------------------ +; BASIC header with a SYS call + + .org $0FFF + .word Head ; Load address +Head: .word @Next + .word 1000 ; Line number + .byte $9E,"4109" ; SYS 4109 + .byte $00 ; End of BASIC line +@Next: .word 0 ; BASIC end marker + .reloc + +; ------------------------------------------------------------------------ +; Actual code + + ldy #zpspace-1 +L1: lda sp,y + sta zpsave,y ; save the zero page locations we need + dey + bpl L1 + +; Close open files + + jsr CLRCH + +; Switch to second charset + + lda #14 + jsr BSOUT + +; Clear the BSS data + + jsr zerobss + +; Save system stuff and setup the stack + + tsx + stx spsave ; save system stk ptr + + sec + jsr MEMTOP ; Get top memory + cpy #$80 ; We can only use the low 32K :-( + bcc MemOk + ldy #$80 + ldx #$00 +MemOk: stx sp + sty sp+1 ; set argument stack ptr + +; Initialize the heap + + jsr __hinit + +; Initialize conio stuff + + jsr initconio + +; Pass an empty command line + + jsr push0 ; argc + jsr push0 ; argv + + ldy #4 ; Argument size + jsr _main ; call the users code + +; fall thru to exit... + +_exit: jsr doatexit ; call exit functions + ldx spsave + txs + +; Reset the conio stuff + + jsr doneconio + +; Copy back the zero page stuff + + ldy #zpspace-1 +L2: lda zpsave,y + sta sp,y + dey + bpl L2 + +; Reset changed vectors + + jmp RESTOR + + +.data +zpsave: .res zpspace + +.bss +spsave: .res 1 + + diff --git a/libsrc/plus4/kbhit.s b/libsrc/plus4/kbhit.s new file mode 100644 index 000000000..2a804cd5b --- /dev/null +++ b/libsrc/plus4/kbhit.s @@ -0,0 +1,21 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; int kbhit (void); +; + + .export _kbhit + .import return0, return1 + + .include "plus4.inc" + +_kbhit: + lda KEY_COUNT ; Get number of characters + ora FKEY_COUNT ; Or with number of chars from function keys + bne L1 + jmp return0 +L1: jmp return1 + + + + diff --git a/libsrc/plus4/plus4.inc b/libsrc/plus4/plus4.inc new file mode 100644 index 000000000..5b8ab265b --- /dev/null +++ b/libsrc/plus4/plus4.inc @@ -0,0 +1,66 @@ +; +; Plus/4 generic definitions. +; + + +; --------------------------------------------------------------------------- +; Zero page, Commodore stuff + +ST = $90 ; IEC status byte + +FNAM_LEN = $AB ; Length of filename +SECADR = $AD ; Secondary address +DEVNUM = $AE ; Device number +KEY_COUNT = $EF ; Number of keys in input buffer +CURS_X = $CA ; Cursor column +CURS_Y = $CD ; Cursor row +SCREEN_PTR = $C8 ; Pointer to current char in text screen +CRAM_PTR = $EA ; Pointer to current char in color RAM + +CHARCOLOR = $53B +FKEY_COUNT = $55D ; Characters for function key +FKEY_SPACE = $55F ; Function key definitions +FKEY_ORIG = $F3D2 ; Original definitions + +; --------------------------------------------------------------------------- +; Kernal routines + +; Direct entries +CLRSCR = $D88B +KBDREAD = $D8C1 + +; --------------------------------------------------------------------------- +; Vector and other locations + +IRQVec = $0314 +BRKVec = $0316 +NMIVec = $0318 + +; --------------------------------------------------------------------------- +; I/O + +TED_T1LO = $FF00 +TED_T1HI = $FF01 +TED_T2LO = $FF02 +TED_T2HI = $FF03 +TED_T3LO = $FF04 +TED_T4HI = $FF05 +TED_KBD = $FF08 +TED_CURSHI = $FF0C +TED_CURSLO = $FF0D +TED_V1FRQLO = $FF0E +TED_V2FRQLO = $FF0F +TED_V2FRQHI = $FF10 +TED_BGCOLOR = $FF15 +TED_COLOR1 = $FF16 +TED_COLOR2 = $FF17 +TED_COLOR3 = $FF18 +TED_BORDERCOLOR = $FF19 +TED_VLINEHI = $FF1C +TED_VLINELO = $FF1D +TED_HPOS = $FF1E +TED_ROMSEL = $FF3E +TED_RAMSEL = $FF3F + + + diff --git a/libsrc/plus4/readjoy.s b/libsrc/plus4/readjoy.s new file mode 100644 index 000000000..338558947 --- /dev/null +++ b/libsrc/plus4/readjoy.s @@ -0,0 +1,29 @@ +; +; Ullrich von Bassewitz, 23.09.1998 +; +; unsigned readjoy (unsigned char joy); +; + + .export _readjoy + + .include "plus4.inc" + + +.proc _readjoy + + ldy #$FA ; Load index for joystick #1 + tax ; Test joystick number + beq L1 + ldy #$FB ; Load index for joystick #2 +L1: sei + sty TED_KBD + lda TED_KBD + cli + ldx #$00 ; Clear high byte + and #$1F + eor #$1F + rts + +.endproc + + diff --git a/libsrc/runtime/Makefile b/libsrc/runtime/Makefile new file mode 100644 index 000000000..775d60fc9 --- /dev/null +++ b/libsrc/runtime/Makefile @@ -0,0 +1,42 @@ +# +# makefile for CC65 runtime library +# + +.SUFFIXES: .o .s .c + +.c.s: + @echo $< + @$(CC) $(CFLAGS) $< + +.s.o: + @echo $< + @$(AS) -g -o $@ $(AFLAGS) $< + +OBJS = runtime.o mul.o div.o push.o inc.o dec.o shl.o shr.o add.o\ + sub.o rsub.o or.o xor.o and.o neg.o bneg.o compl.o icmp.o\ + call.o swap.o switch.o gt.o ugt.o ge.o makebool.o ldau0sp.o\ + uge.o lt.o ult.o le.o ule.o eq.o ne.o test.o subeqsp.o\ + udiv.o umod.o mod.o shelp.o aslax1.o asrax1.o shrax1.o\ + aslax2.o asrax2.o shrax2.o aslax3.o asrax3.o shrax3.o\ + enter.o leave.o leaysp.o popsreg.o ldai.o ldaxi.o ldauisp.o\ + ldaui.o pushw.o pushb.o staxsp.o ldaxsp.o addeqsp.o\ + ldasp.o ldausp.o bpushbsp.o pushwsp.o pushbsp.o + +LOBJS = lruntime.o lconvert.o ladd.o lsub.o lrsub.o leq.o lne.o\ + lneg.o lbneg.o lcompl.o lpush.o land.o lor.o lxor.o ldeaxi.o\ + ltest.o llt.o lle.o lge.o lgt.o lsave.o asleax1.o laddeqsp.o\ + asreax1.o shreax1.o asleax2.o asreax2.o shreax2.o lsubeqsp.o\ + asleax3.o asreax3.o shreax3.o lmul.o lshelp.o ludiv.o lumod.o\ + ldiv.o lmod.o lswitch.o steaxsp.o lshr.o lshl.o lcmp.o lugt.o\ + luge.o lult.o lule.o linc.o ldec.o lswap.o lpop.o ldeax.o\ + lsubeq.o laddeq.o + +all: $(OBJS) $(LOBJS) + +clean: + @rm -f *~ + @rm -f $(COBJS:.o=.s) + @rm -f $(OBJS) + @rm -f $(LOBJS) + + diff --git a/libsrc/runtime/add.s b/libsrc/runtime/add.s new file mode 100644 index 000000000..5d83ef9a2 --- /dev/null +++ b/libsrc/runtime/add.s @@ -0,0 +1,47 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: add ints +; + +; Make this as fast as possible, even if it needs more space since it's +; called a lot! + + .export tosadda0, tosaddax + .importzp sp, tmp1 + +tosadda0: + ldx #0 +tosaddax: + ldy #0 + clc + adc (sp),y ; lo byte + sta tmp1 ; save it + txa + iny + adc (sp),y ; hi byte + tax + clc + lda sp + adc #2 + sta sp + bcc L1 + inc sp+1 +L1: txa ; Test high byte + bmi L2 + bne L3 + lda tmp1 ; Get low byte + rts + +; Value is negative + +L2: lda tmp1 ; Get low byte + ldy #$FF ; Force negative + rts + +; Value is positive != 0 + +L3: lda tmp1 ; Get low byte + ldy #1 + rts + diff --git a/libsrc/runtime/addeqsp.s b/libsrc/runtime/addeqsp.s new file mode 100644 index 000000000..d667e36d9 --- /dev/null +++ b/libsrc/runtime/addeqsp.s @@ -0,0 +1,24 @@ +; +; Ullrich von Bassewitz, 08.10.1998 +; +; CC65 runtime: += operator for ints on the stack +; + + .export addeq0sp, addeqysp + .importzp sp + +addeq0sp: + ldy #0 +addeqysp: + clc + adc (sp),y + sta (sp),y + pha + iny + txa + adc (sp),y + sta (sp),y + tax + pla + rts + diff --git a/libsrc/runtime/and.s b/libsrc/runtime/and.s new file mode 100644 index 000000000..cf1d10ead --- /dev/null +++ b/libsrc/runtime/and.s @@ -0,0 +1,23 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: and on ints +; + + .export tosanda0, tosandax + .import addysp1 + .importzp sp, ptr4 + +tosanda0: + ldx #$00 +tosandax: + ldy #0 + and (sp),y + sta ptr4 + iny + txa + and (sp),y + tax + lda ptr4 + jmp addysp1 ; drop TOS, set condition codes + diff --git a/libsrc/runtime/aslax1.s b/libsrc/runtime/aslax1.s new file mode 100644 index 000000000..eddb5272a --- /dev/null +++ b/libsrc/runtime/aslax1.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register +; + + .export aslax1, shlax1 + .importzp tmp1 + +aslax1: +shlax1: stx tmp1 + asl A + rol tmp1 + ldx tmp1 + rts + + diff --git a/libsrc/runtime/aslax2.s b/libsrc/runtime/aslax2.s new file mode 100644 index 000000000..e6e56b0d0 --- /dev/null +++ b/libsrc/runtime/aslax2.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register by 4 +; + + .export aslax2, shlax2 + .importzp tmp1 + +aslax2: +shlax2: stx tmp1 + asl a + rol tmp1 + asl a + rol tmp1 + ldx tmp1 + rts + diff --git a/libsrc/runtime/aslax3.s b/libsrc/runtime/aslax3.s new file mode 100644 index 000000000..9c9bbb806 --- /dev/null +++ b/libsrc/runtime/aslax3.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register by 8 +; + + .export aslax3, shlax3 + .importzp tmp1 + +aslax3: +shlax3: stx tmp1 + asl a + rol tmp1 + asl a + rol tmp1 + asl a + rol tmp1 + ldx tmp1 + rts + diff --git a/libsrc/runtime/asleax1.s b/libsrc/runtime/asleax1.s new file mode 100644 index 000000000..8f30a18a3 --- /dev/null +++ b/libsrc/runtime/asleax1.s @@ -0,0 +1,19 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the 32 bit primary register by 2 +; + + .export asleax1, shleax1 + .importzp sreg, tmp1 + +asleax1: +shleax1: + stx tmp1 + asl a + rol tmp1 + rol sreg + rol sreg+1 + ldx tmp1 + rts + diff --git a/libsrc/runtime/asleax2.s b/libsrc/runtime/asleax2.s new file mode 100644 index 000000000..772a37d9b --- /dev/null +++ b/libsrc/runtime/asleax2.s @@ -0,0 +1,23 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the 32 bit primary register by 4 +; + + .export asleax2, shleax2 + .importzp sreg, tmp1 + +asleax2: +shleax2: + stx tmp1 + asl a + rol tmp1 + rol sreg + rol sreg+1 + asl a + rol tmp1 + rol sreg + rol sreg+1 + ldx tmp1 + rts + diff --git a/libsrc/runtime/asleax3.s b/libsrc/runtime/asleax3.s new file mode 100644 index 000000000..1c45320eb --- /dev/null +++ b/libsrc/runtime/asleax3.s @@ -0,0 +1,27 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the 32 bit primary register by 8 +; + + .export asleax3, shleax3 + .importzp sreg, tmp1 + +asleax3: +shleax3: + stx tmp1 + asl a + rol tmp1 + rol sreg + rol sreg+1 + asl a + rol tmp1 + rol sreg + rol sreg+1 + asl a + rol tmp1 + rol sreg + rol sreg+1 + ldx tmp1 + rts + diff --git a/libsrc/runtime/asrax1.s b/libsrc/runtime/asrax1.s new file mode 100644 index 000000000..bf7646aef --- /dev/null +++ b/libsrc/runtime/asrax1.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register +; + + .export asrax1 + .importzp tmp1 + +asrax1: stx tmp1 + cpx #$80 ; Put bit 7 into carry + ror tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/asrax2.s b/libsrc/runtime/asrax2.s new file mode 100644 index 000000000..b6cb3f7f4 --- /dev/null +++ b/libsrc/runtime/asrax2.s @@ -0,0 +1,19 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register by 4 +; + + .export asrax2 + .importzp tmp1 + +asrax2: stx tmp1 + cpx #$80 ; Put bit 7 into carry + ror tmp1 + ror a + cpx #$80 + ror tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/asrax3.s b/libsrc/runtime/asrax3.s new file mode 100644 index 000000000..e3286d431 --- /dev/null +++ b/libsrc/runtime/asrax3.s @@ -0,0 +1,24 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register by 8 +; + + .export asrax3 + .importzp tmp1 + +asrax3: stx tmp1 + cpx #$80 ; Put bit 7 into carry + ror tmp1 + ror a + ldx tmp1 + cpx #$80 + ror tmp1 + ror a + ldx tmp1 + cpx #$80 + ror tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/asreax1.s b/libsrc/runtime/asreax1.s new file mode 100644 index 000000000..ab02ed9d2 --- /dev/null +++ b/libsrc/runtime/asreax1.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register +; + + .export asreax1 + .importzp sreg, tmp1 + +asreax1: + stx tmp1 + ldx sreg+1 + cpx #$80 ; Get bit 7 into carry + ror sreg+1 + ror sreg + ror tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/asreax2.s b/libsrc/runtime/asreax2.s new file mode 100644 index 000000000..40c94c0b5 --- /dev/null +++ b/libsrc/runtime/asreax2.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the 32 bit primary register by 4 +; + + .export asreax2 + .importzp sreg, tmp1 + +asreax2: + stx tmp1 + ldx sreg+1 + cpx #$80 ; Get bit 7 into carry + ror sreg+1 + ror sreg + ror tmp1 + ror a + ldx sreg+1 + cpx #$80 ; Get bit 7 into carry + ror sreg+1 + ror sreg + ror tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/asreax3.s b/libsrc/runtime/asreax3.s new file mode 100644 index 000000000..64f005fbf --- /dev/null +++ b/libsrc/runtime/asreax3.s @@ -0,0 +1,32 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the 32 bit primary register by 8 +; + + .export asreax3 + .importzp sreg, tmp1 + +asreax3: + stx tmp1 + ldx sreg+1 + cpx #$80 ; Get bit 7 into carry + ror sreg+1 + ror sreg + ror tmp1 + ror a + ldx sreg+1 + cpx #$80 ; Get bit 7 into carry + ror sreg+1 + ror sreg + ror tmp1 + ror a + ldx sreg+1 + cpx #$80 ; Get bit 7 into carry + ror sreg+1 + ror sreg + ror tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/bneg.s b/libsrc/runtime/bneg.s new file mode 100644 index 000000000..034d716c9 --- /dev/null +++ b/libsrc/runtime/bneg.s @@ -0,0 +1,21 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: boolean negation +; + + .export bnega, bnegax + .import return0, return1 + +bnegax: cpx #0 + bne L0 +bnega: cmp #0 + bne L0 +L1: ldx #0 + lda #1 + rts + +L0: ldx #0 + txa + rts + diff --git a/libsrc/runtime/bpushbsp.s b/libsrc/runtime/bpushbsp.s new file mode 100644 index 000000000..f4d8d532f --- /dev/null +++ b/libsrc/runtime/bpushbsp.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load a from stack slot and push as byte +; + + .export bpushbsp, bpushbysp + .import pusha + .importzp sp + +bpushbsp: + ldy #0 +bpushbysp: + lda (sp),y + jmp pusha + + + diff --git a/libsrc/runtime/call.s b/libsrc/runtime/call.s new file mode 100644 index 000000000..0600177cd --- /dev/null +++ b/libsrc/runtime/call.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: call function via pointer in ax +; + + .export callax + .importzp ptr1 + +callax: sta ptr1 + stx ptr1+1 + jmp (ptr1) ; jump there + diff --git a/libsrc/runtime/compl.s b/libsrc/runtime/compl.s new file mode 100644 index 000000000..5e9626321 --- /dev/null +++ b/libsrc/runtime/compl.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: integer complement +; + + .export complax + +complax: + eor #$FF ; Not A + pha + txa + eor #$FF ; Not X + tax + pla + rts + diff --git a/libsrc/runtime/dec.s b/libsrc/runtime/dec.s new file mode 100644 index 000000000..784e73f06 --- /dev/null +++ b/libsrc/runtime/dec.s @@ -0,0 +1,31 @@ +; +; Ullrich von Bassewitz, 29.12.1999 +; +; CC65 runtime: Decrement ax by constant or value in Y +; + + .export decaxy + .export decax2, decax1 + .importzp tmp1 + + +decaxy: sty tmp1 + sec + sbc tmp1 + bcs *+3 + dex + rts + +decax2: sec + sbc #2 + bcs *+3 + dex + rts + +decax1: sec + sbc #1 + bcs *+3 + dex + rts + + diff --git a/libsrc/runtime/div.s b/libsrc/runtime/div.s new file mode 100644 index 000000000..b995b9e87 --- /dev/null +++ b/libsrc/runtime/div.s @@ -0,0 +1,22 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: division for signed ints +; + +; When negating values, we will ignore the possibility here, that one of the +; values if $8000, in which case the negate will fail. + + .export tosdiva0, tosdivax + .import popsargs, udiv16, adjsres + .importzp sreg + +tosdiva0: + ldx #0 +tosdivax: + jsr popsargs ; Get arguments from stack, adjust sign + jsr udiv16 ; Do the division + lda sreg ; Result is in sreg, remainder in ptr1 + ldx sreg+1 + jmp adjsres ; Adjust the sign of the result if needed + diff --git a/libsrc/runtime/enter.s b/libsrc/runtime/enter.s new file mode 100644 index 000000000..a246d5e93 --- /dev/null +++ b/libsrc/runtime/enter.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: function prologue +; + + .export enter + .importzp sp + +enter: tya ; get arg size + ldy sp + bne L1 + dec sp+1 +L1: dec sp + ldy #0 + sta (sp),y ; Store the arg count + rts + diff --git a/libsrc/runtime/eq.s b/libsrc/runtime/eq.s new file mode 100644 index 000000000..28e52055e --- /dev/null +++ b/libsrc/runtime/eq.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare == for ints +; + + .export toseq00, toseqa0, toseqax + .import tosicmp, booleq + .importzp sp, tmp1 + +toseq00: + lda #$00 +toseqa0: + ldx #$00 +toseqax: + jsr tosicmp ; Set flags + jmp booleq ; Convert to boolean diff --git a/libsrc/runtime/ge.s b/libsrc/runtime/ge.s new file mode 100644 index 000000000..17d4bb98b --- /dev/null +++ b/libsrc/runtime/ge.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare >= for signed ints +; + + .export tosge00, tosgea0, tosgeax + .import tosicmp, boolge + + +tosge00: + lda #$00 +tosgea0: + ldx #$00 +tosgeax: + jsr tosicmp ; Set flags + jmp boolge ; Convert to boolean diff --git a/libsrc/runtime/gt.s b/libsrc/runtime/gt.s new file mode 100644 index 000000000..b6ba7b61c --- /dev/null +++ b/libsrc/runtime/gt.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare > for signed ints +; + + .export tosgt00, tosgta0, tosgtax + .import tosicmp, boolgt + + +tosgt00: + lda #$00 +tosgta0: + ldx #$00 +tosgtax: + jsr tosicmp ; Set the flags + jmp boolgt ; Convert to boolean + diff --git a/libsrc/runtime/icmp.s b/libsrc/runtime/icmp.s new file mode 100644 index 000000000..e3b2bba6c --- /dev/null +++ b/libsrc/runtime/icmp.s @@ -0,0 +1,44 @@ +; +; Ullrich von Bassewitz, 10.12.1998 +; +; Integer compare function - used by the compare operators +; + + .export tosicmp + .import incsp2 + .importzp sp, sreg + + +tosicmp: + sta sreg + stx sreg+1 ; Save ax + + ldy #$01 + lda (sp),y ; Get high byte + tax + dey + lda (sp),y ; Get low byte + +; Inline incsp2 for better performance + + inc sp ; 5 + bne @L1 ; 3 + inc sp+1 ; (5) +@L1: inc sp ; 5 + bne @L2 ; 3 + inc sp+1 ; (5) + +; Do the compare. + +@L2: cpx sreg+1 ; Compare high byte + bne @L3 + cmp sreg ; Compare low byte + beq @L3 + bcs @L4 + lda #$FF ; Set the N flag +@L3: rts + +@L4: lda #$01 ; Clear the N flag + rts + + diff --git a/libsrc/runtime/inc.s b/libsrc/runtime/inc.s new file mode 100644 index 000000000..043d27d3b --- /dev/null +++ b/libsrc/runtime/inc.s @@ -0,0 +1,48 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: Increment ax by constant or value in Y +; + + .export incaxy + .export incax8, incax7, incax6, incax5 + .export incax4, incax3, incax2, incax1 + .importzp tmp1 + + +incax8: ldy #8 + bne incaxy + +incax7: ldy #7 + bne incaxy + +incax6: ldy #6 + bne incaxy + +incax5: ldy #5 + bne incaxy + +incax4: ldy #4 + bne incaxy + +incax3: ldy #3 +; bne incaxy +incaxy: sty tmp1 + clc + adc tmp1 + bcc *+3 + inx + rts + +incax2: clc + adc #2 + bcc *+3 + inx + rts + +incax1: clc + adc #1 + bcc *+3 + inx + rts + diff --git a/libsrc/runtime/ladd.s b/libsrc/runtime/ladd.s new file mode 100644 index 000000000..fabedc9a1 --- /dev/null +++ b/libsrc/runtime/ladd.s @@ -0,0 +1,32 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: long add +; + + .export tosaddeax + .import addysp1 + .importzp sp, sreg, tmp1 + +; EAX = TOS + EAX + +tosaddeax: + ldy #0 + clc + adc (sp),y ; byte 0 + sta tmp1 ; use as temp storage + iny + txa + adc (sp),y ; byte 1 + tax + iny + lda sreg + adc (sp),y ; byte 2 + sta sreg + iny + lda sreg+1 + adc (sp),y ; byte 3 + sta sreg+1 + lda tmp1 ; load byte 0 + jmp addysp1 ; drop TOS + diff --git a/libsrc/runtime/laddeq.s b/libsrc/runtime/laddeq.s new file mode 100644 index 000000000..6478017f3 --- /dev/null +++ b/libsrc/runtime/laddeq.s @@ -0,0 +1,53 @@ +; +; Ullrich von Bassewitz, 07.04.2000 +; +; CC65 runtime: += operator +; +; On entry, the low byte of the address of the variable to increment is +; in ptr1, the high byte is in Y, and the increment is in eax. +; + + .export laddeq1, laddeqa, laddeq + .importzp sreg, ptr1, tmp1 + + +laddeq1: + lda #$01 + +laddeqa: + ldx #$00 + stx sreg + stx sreg+1 + +laddeq: sty ptr1+1 ; Store high byte of address + ldy #$00 ; Address low byte + clc + + adc (ptr1),y + sta (ptr1),y + pha ; Save byte 0 of result for later + + iny ; Address byte 1 + txa + adc (ptr1),y ; Load byte 1 + sta (ptr1),y + tax + + iny ; Address byte 2 + lda sreg + adc (ptr1),y + sta (ptr1),y + sta sreg + + iny ; Address byte 3 + lda sreg+1 + adc (ptr1),y + sta (ptr1),y + sta sreg+1 + + pla ; Retrieve byte 0 of result + + rts ; Done + + + diff --git a/libsrc/runtime/laddeqsp.s b/libsrc/runtime/laddeqsp.s new file mode 100644 index 000000000..e5a1f04a9 --- /dev/null +++ b/libsrc/runtime/laddeqsp.s @@ -0,0 +1,34 @@ +; +; Ullrich von Bassewitz, 08.10.1998 +; +; CC65 runtime: += operator for longs on the stack +; + + .export laddeq0sp, laddeqysp + .importzp sp, sreg + +laddeq0sp: + ldy #0 +laddeqysp: + clc + adc (sp),y + sta (sp),y + pha + iny + txa + adc (sp),y + sta (sp),y + tax + iny + lda sreg + adc (sp),y + sta (sp),y + sta sreg + iny + lda sreg+1 + adc (sp),y + sta (sp),y + sta sreg+1 + pla + rts + diff --git a/libsrc/runtime/land.s b/libsrc/runtime/land.s new file mode 100644 index 000000000..1ee2a296d --- /dev/null +++ b/libsrc/runtime/land.s @@ -0,0 +1,30 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: and on longs +; + + .export tosandeax + .import addysp1 + .importzp sp, sreg, tmp1 + +tosandeax: + ldy #0 + and (sp),y ; byte 0 + sta tmp1 + iny + txa + and (sp),y ; byte 1 + tax + iny + lda sreg + and (sp),y ; byte 2 + sta sreg + iny + lda sreg+1 + and (sp),y ; byte 3 + sta sreg+1 + + lda tmp1 + jmp addysp1 + diff --git a/libsrc/runtime/lbneg.s b/libsrc/runtime/lbneg.s new file mode 100644 index 000000000..cd508e587 --- /dev/null +++ b/libsrc/runtime/lbneg.s @@ -0,0 +1,22 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: boolean negation for longs +; + + .export bnegeax + .import return0, return1 + .importzp sreg + +bnegeax: + cmp #0 + bne L1 + cpx #0 + bne L1 + lda sreg + bne L1 + lda sreg+1 + bne L1 + jmp return1 +L1: jmp return0 + diff --git a/libsrc/runtime/lcmp.s b/libsrc/runtime/lcmp.s new file mode 100644 index 000000000..c2cb9ffe3 --- /dev/null +++ b/libsrc/runtime/lcmp.s @@ -0,0 +1,50 @@ +; +; Ullrich von Bassewitz, 10.12.1998 +; +; Long int compare function - used by the compare operators +; + + .export lcmp + .import incsp4 + .importzp sp, sreg, ptr1 + + +lcmp: sta ptr1 + stx ptr1+1 ; EAX now in sreg:ptr1 + + ldy #$03 + lda (sp),y + cmp sreg+1 + bne L4 + + dey + lda (sp),y + cmp sreg + bne L1 + + dey + lda (sp),y + cmp ptr1+1 + bne L1 + + dey + lda (sp),y + cmp ptr1 + +L1: php ; Save flags + jsr incsp4 ; Drop TOS + plp ; Restore the flags + beq L2 + bcs L3 + lda #$FF ; Set the N flag +L2: rts + +L3: lda #$01 ; Clear the N flag + rts + +L4: php ; Save flags + jsr incsp4 ; Drop TOS + plp ; Restore flags + rts + + diff --git a/libsrc/runtime/lcompl.s b/libsrc/runtime/lcompl.s new file mode 100644 index 000000000..fff31d9af --- /dev/null +++ b/libsrc/runtime/lcompl.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: long complement +; + + .export compleax + .importzp sreg + +; eax = ~eax + +compleax: + eor #$FF + pha + txa + eor #$FF + tax + lda sreg + eor #$FF + sta sreg + lda sreg+1 + eor #$FF + sta sreg+1 + pla + rts + diff --git a/libsrc/runtime/lconvert.s b/libsrc/runtime/lconvert.s new file mode 100644 index 000000000..f33c75415 --- /dev/null +++ b/libsrc/runtime/lconvert.s @@ -0,0 +1,88 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: long conversion routines +; + +; +; Convert TOS from long to int by cutting of the high 16bit +; + .export tosint, tosulong, toslong, axulong, axlong + .import incsp2, decsp2 + .importzp sp, sreg + +tosint: pha + ldy #0 + lda (sp),y ; sp+1 + ldy #2 + sta (sp),y + ldy #1 + lda (sp),y + ldy #3 + sta (sp),y + pla + jmp incsp2 ; Drop 16 bit + +; +; Convert TOS from int to long +; + +tosulong: + pha + jsr decsp2 ; Make room + ldy #2 + lda (sp),y + ldy #0 + sta (sp),y + ldy #3 + lda (sp),y + ldy #1 + sta (sp),y + lda #0 ; Zero extend +toslong2: + iny + sta (sp),y + iny + sta (sp),y + pla + rts + +toslong: + pha + jsr decsp2 ; Make room + ldy #2 + lda (sp),y + ldy #0 + sta (sp),y + ldy #3 + lda (sp),y + bmi toslong1 + ldy #1 + sta (sp),y + lda #$00 ; Positive, high word is zero + bne toslong2 +toslong1: + ldy #1 + sta (sp),y + lda #$FF + bne toslong2 + +; +; Convert AX from int to long in EAX +; + +axulong: + ldy #0 + sty sreg + sty sreg+1 + rts + +axlong: cpx #$80 ; Positive? + bcc axulong ; Yes, handle like unsigned type + ldy #$ff + sty sreg + sty sreg+1 + rts + + + diff --git a/libsrc/runtime/ldai.s b/libsrc/runtime/ldai.s new file mode 100644 index 000000000..0dc1ef28d --- /dev/null +++ b/libsrc/runtime/ldai.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load a indirect from address in ax +; + + .export ldai, ldaidx + .importzp ptr1 + +ldai: ldy #$00 +ldaidx: sta ptr1 + stx ptr1+1 + ldx #$00 + lda (ptr1),y + bpl L9 + dex +L9: rts + diff --git a/libsrc/runtime/ldasp.s b/libsrc/runtime/ldasp.s new file mode 100644 index 000000000..ae0886803 --- /dev/null +++ b/libsrc/runtime/ldasp.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load a from offset in stack +; + + .export ldasp, ldaysp + .importzp sp + +ldasp: ldy #0 +ldaysp: ldx #0 + lda (sp),y + bpl L9 ; Jump if positive + dex +L9: rts + + diff --git a/libsrc/runtime/ldau0sp.s b/libsrc/runtime/ldau0sp.s new file mode 100644 index 000000000..e967f7a82 --- /dev/null +++ b/libsrc/runtime/ldau0sp.s @@ -0,0 +1,21 @@ +; +; Ullrich von Bassewitz, 11.04.1999 +; +; CC65 runtime: Load an unsigned char indirect from pointer somewhere in stack +; + + .export ldau00sp, ldau0ysp + .importzp sp, ptr1 + +ldau00sp: + ldy #1 +ldau0ysp: + lda (sp),y + sta ptr1+1 + dey + lda (sp),y + sta ptr1 + ldx #0 + lda (ptr1,x) + rts + diff --git a/libsrc/runtime/ldaui.s b/libsrc/runtime/ldaui.s new file mode 100644 index 000000000..b61ff1f3e --- /dev/null +++ b/libsrc/runtime/ldaui.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load a unsigned indirect from address in ax +; + + .export ldaui, ldauidx + .importzp ptr1 + +ldaui: + ldy #0 +ldauidx: + sta ptr1 + stx ptr1+1 + ldx #0 + lda (ptr1),y + rts + diff --git a/libsrc/runtime/ldauisp.s b/libsrc/runtime/ldauisp.s new file mode 100644 index 000000000..7e153291c --- /dev/null +++ b/libsrc/runtime/ldauisp.s @@ -0,0 +1,24 @@ +; +; Ullrich von Bassewitz, 11.04.1999 +; +; CC65 runtime: Load an unsigned char indirect from pointer somewhere in stack +; + + .export ldaui0sp, ldauiysp + .importzp sp, ptr1 + +ldaui0sp: + ldy #1 +ldauiysp: + lda (sp),y + sta ptr1+1 + dey + lda (sp),y + sta ptr1 + txa + tay + ldx #0 + lda (ptr1),y + rts + + diff --git a/libsrc/runtime/ldausp.s b/libsrc/runtime/ldausp.s new file mode 100644 index 000000000..aaa47c272 --- /dev/null +++ b/libsrc/runtime/ldausp.s @@ -0,0 +1,16 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load a unsigned from offset in stack +; + + .export ldausp, ldauysp + .importzp sp + +ldausp: ldy #0 +ldauysp: + ldx #0 + lda (sp),y + rts + + diff --git a/libsrc/runtime/ldaxi.s b/libsrc/runtime/ldaxi.s new file mode 100644 index 000000000..a03c610e4 --- /dev/null +++ b/libsrc/runtime/ldaxi.s @@ -0,0 +1,19 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load ax indirect from address in ax +; + + .export ldaxi, ldaxidx + .importzp ptr1 + +ldaxi: ldy #1 +ldaxidx: + sta ptr1 + stx ptr1+1 + lda (ptr1),y + tax + dey + lda (ptr1),y + rts + diff --git a/libsrc/runtime/ldaxsp.s b/libsrc/runtime/ldaxsp.s new file mode 100644 index 000000000..b72e237c5 --- /dev/null +++ b/libsrc/runtime/ldaxsp.s @@ -0,0 +1,28 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load ax from offset in stack +; + + .export ldax0sp, ldaxysp + .importzp sp + +; Beware: The optimizer knows about the value in Y after return! + +ldax0sp: + ldy #1 +ldaxysp: + lda (sp),y ; get high byte + tax ; and save it + bne L1 ; Try to generate FAST code + dey ; point to lo byte + lda (sp),y ; load low byte + rts + +L1: php ; Save Z flag + dey + lda (sp),y + plp + rts + + diff --git a/libsrc/runtime/ldeax.s b/libsrc/runtime/ldeax.s new file mode 100644 index 000000000..9912a5211 --- /dev/null +++ b/libsrc/runtime/ldeax.s @@ -0,0 +1,38 @@ +; +; Ullrich von Bassewitz, 29.12.1999 +; +; CC65 runtime: Load eax from immidiate value following the call +; + + .export ldeax + .importzp sreg, ptr4 + + +ldeax: pla ; Low byte of return address + sta ptr4 + pla ; high byte of return address + sta ptr4+1 + ldy #4 ; high byte of value + lda (ptr4),y + sta sreg+1 + dey + lda (ptr4),y + sta sreg + dey + lda (ptr4),y + tax + dey + lda (ptr4),y + tay ; Save low byte + clc + lda #4 + adc ptr4 + sta ptr4 + lda ptr4+1 + adc #$00 + pha ; High byte of new return address + lda ptr4 + pha ; Low byte of new return address + tya ; Low byte of fetched value + rts + diff --git a/libsrc/runtime/ldeaxi.s b/libsrc/runtime/ldeaxi.s new file mode 100644 index 000000000..a1f9e64f9 --- /dev/null +++ b/libsrc/runtime/ldeaxi.s @@ -0,0 +1,25 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load eax indirect from address in ax +; + + .export ldeaxidx, ldeaxi + .importzp sreg, ptr1 + +ldeaxi: ldy #3 +ldeaxidx: + sta ptr1 + stx ptr1+1 + lda (ptr1),y + dey + sta sreg+1 + lda (ptr1),y + dey + sta sreg + lda (ptr1),y + dey + tax + lda (ptr1),y + rts + diff --git a/libsrc/runtime/ldec.s b/libsrc/runtime/ldec.s new file mode 100644 index 000000000..404019b81 --- /dev/null +++ b/libsrc/runtime/ldec.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 29.12.1999 +; +; CC65 runtime: Decrement eax by value in Y +; + + .export deceaxy + .importzp ptr4, sreg + +deceaxy: + sty ptr4 + sec + sbc ptr4 + sta ptr4 + txa + sbc #0 + tax + lda sreg + sbc #0 + sta sreg + lda sreg+1 + sbc #0 + sta sreg+1 + lda ptr4 + rts + diff --git a/libsrc/runtime/ldiv.s b/libsrc/runtime/ldiv.s new file mode 100644 index 000000000..f08354d24 --- /dev/null +++ b/libsrc/runtime/ldiv.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 17.08.1998 +; +; CC65 runtime: division for signed long ints +; + +; When negating values, we will ignore the possibility here, that one of the +; values if $80000000, in which case the negate will fail. + + .export tosdiveax + .import poplsargs, udiv32, adjlsres + .importzp ptr1 + +tosdiveax: + jsr poplsargs ; Get arguments from stack, adjust sign + jsr udiv32 ; Do the division + lda ptr1 ; Result is in (ptr1:sreg) + ldx ptr1+1 + jmp adjlsres ; Adjust the sign of the result if needed + diff --git a/libsrc/runtime/le.s b/libsrc/runtime/le.s new file mode 100644 index 000000000..a615b3f0a --- /dev/null +++ b/libsrc/runtime/le.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare <= for signed ints +; + + .export tosle00, toslea0, tosleax + .import tosicmp, boolle + +tosle00: + lda #$00 +toslea0: + ldx #$00 +tosleax: + jsr tosicmp ; Set flags + jmp boolle ; Convert to boolean + diff --git a/libsrc/runtime/leave.s b/libsrc/runtime/leave.s new file mode 100644 index 000000000..4846b9efd --- /dev/null +++ b/libsrc/runtime/leave.s @@ -0,0 +1,37 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: function epilogue +; + +; exit a function. pop stack and rts. The function comes in different +; flavours that provide default values for the return val, or drop a local +; stack frame with size in y. + + .export leave00, leave0, leavey00, leavey0, leavey + .export leave + .import addysp + .importzp sp + +leave00: + lda #0 +leave0: ldx #0 + beq leave + +leavey00: + lda #0 ; "return 0" +leavey0: + ldx #0 ; return < 256 +leavey: + jsr addysp ; drop stack frame +leave: pha ; save A a sec + ldy #0 + lda (sp),y ; that's the pushed arg size + sec ; Count the byte, the count's stored in + adc sp + sta sp + bcc L1 + inc sp+1 +L1: pla ; Get return value back + rts + diff --git a/libsrc/runtime/leaysp.s b/libsrc/runtime/leaysp.s new file mode 100644 index 000000000..2e5c16174 --- /dev/null +++ b/libsrc/runtime/leaysp.s @@ -0,0 +1,33 @@ +; +; Ullrich von Bassewitz, 21.08.1998 +; +; CC65 runtime: Load effective address with offset in Y relative to SP +; + + .export lea0sp, leaysp, plea0sp, pleaysp + .import pushax + .importzp sp + +lea0sp: ldy #0 ; Load offset zero +leaysp: tya + ldx sp+1 ; Get high byte + clc + adc sp + bcc L8 + inx +L8: rts + + +plea0sp: + ldy #0 +pleaysp: + tya + ldx sp+1 ; Get high byte + clc + adc sp + bcc L9 + inx +L9: jmp pushax + + + diff --git a/libsrc/runtime/leq.s b/libsrc/runtime/leq.s new file mode 100644 index 000000000..5f8d6d33d --- /dev/null +++ b/libsrc/runtime/leq.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: long equal +; + + .export toseqeax + .import lcmp, booleq + +toseqeax: + jsr lcmp ; Set flags + jmp booleq ; Convert to boolean + + + diff --git a/libsrc/runtime/lge.s b/libsrc/runtime/lge.s new file mode 100644 index 000000000..f98d9fb0e --- /dev/null +++ b/libsrc/runtime/lge.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: Compare >= for long ints +; + + .export tosgeeax + .import lcmp, boolge + +tosgeeax: + jsr lcmp ; Set the flags + jmp boolge ; Convert to boolean + diff --git a/libsrc/runtime/lgt.s b/libsrc/runtime/lgt.s new file mode 100644 index 000000000..de08c4787 --- /dev/null +++ b/libsrc/runtime/lgt.s @@ -0,0 +1,14 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: Compare > for long ints +; + + .export tosgteax + .import lcmp, boolgt + +tosgteax: + jsr lcmp ; Set the flags + jmp boolgt ; Convert to boolean + + diff --git a/libsrc/runtime/linc.s b/libsrc/runtime/linc.s new file mode 100644 index 000000000..1922a0709 --- /dev/null +++ b/libsrc/runtime/linc.s @@ -0,0 +1,22 @@ +; +; Ullrich von Bassewitz, 29.12.1999 +; +; CC65 runtime: Increment eax by value in Y +; + + .export inceaxy + .importzp ptr4, sreg + +inceaxy: + sty ptr4 + clc + adc ptr4 + bcc inceax9 + inx + bne inceax9 + inc sreg + bne inceax9 + inc sreg+1 +inceax9: + rts + diff --git a/libsrc/runtime/lle.s b/libsrc/runtime/lle.s new file mode 100644 index 000000000..06d6e0abc --- /dev/null +++ b/libsrc/runtime/lle.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: Compare <= for long ints +; + + .export tosleeax + .import lcmp, boolle + +tosleeax: + jsr lcmp ; Set the flags + jmp boolle ; Convert to boolean + diff --git a/libsrc/runtime/llt.s b/libsrc/runtime/llt.s new file mode 100644 index 000000000..565421204 --- /dev/null +++ b/libsrc/runtime/llt.s @@ -0,0 +1,12 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: Compare < for long ints +; + + .export toslteax + .import lcmp, boollt + +toslteax: + jsr lcmp ; Set the flags + jmp boollt ; Convert to boolean diff --git a/libsrc/runtime/lmod.s b/libsrc/runtime/lmod.s new file mode 100644 index 000000000..8c62d27c0 --- /dev/null +++ b/libsrc/runtime/lmod.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: modulo operation for long signed ints +; + +; When negating values, we will ignore the possibility here, that one of the +; values if $8000, in which case the negate will fail. + + .export tosmodeax + .import poplsargs, udiv32, adjlsres + .importzp sreg, ptr1, ptr2, tmp3, tmp4 + +tosmodeax: + jsr poplsargs ; Get arguments from stack, adjust sign + jsr udiv32 ; Do the division + lda ptr1 ; Remainder is in (ptr2:tmp3:tmp4) + lda ptr2 + ldx ptr2+1 + ldy tmp3 + sty sreg + ldy tmp4 + sty sreg+1 + jmp adjlsres ; Adjust the sign of the result if needed + + diff --git a/libsrc/runtime/lmul.s b/libsrc/runtime/lmul.s new file mode 100644 index 000000000..1a909a3d6 --- /dev/null +++ b/libsrc/runtime/lmul.s @@ -0,0 +1,63 @@ +; +; Ullrich von Bassewitz, 13.08.1998 +; +; CC65 runtime: multiplication for long (unsigned) ints +; + + .export tosumuleax, tosmuleax + .import addysp1 + .importzp sp, sreg, tmp1, tmp2, tmp3, tmp4, ptr1, ptr3, ptr4 + +tosmuleax: +tosumuleax: +mul32: sta ptr1 + stx ptr1+1 ; op2 now in ptr1/sreg + ldy #0 + lda (sp),y + sta ptr3 + iny + lda (sp),y + sta ptr3+1 + iny + lda (sp),y + sta ptr4 + iny + lda (sp),y + sta ptr4+1 ; op1 in pre3/ptr4 + jsr addysp1 ; Drop TOS + +; Do (ptr1:sreg)*(ptr3:ptr4) --> EAX. + + lda #0 + sta tmp4 + sta tmp3 + sta tmp2 + ldy #32 +L0: lsr tmp4 + ror tmp3 + ror tmp2 + ror a + ror sreg+1 + ror sreg + ror ptr1+1 + ror ptr1 + bcc L1 + clc + adc ptr3 + pha + lda ptr3+1 + adc tmp2 + sta tmp2 + lda ptr4 + adc tmp3 + sta tmp3 + lda ptr4+1 + adc tmp4 + sta tmp4 + pla +L1: dey + bpl L0 + lda ptr1 ; Load the low result word + ldx ptr1+1 + rts + diff --git a/libsrc/runtime/lne.s b/libsrc/runtime/lne.s new file mode 100644 index 000000000..d948fc271 --- /dev/null +++ b/libsrc/runtime/lne.s @@ -0,0 +1,14 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: long not equal +; + + .export tosneeax + .import lcmp, boolne + +tosneeax: + jsr lcmp ; Set flags + jmp boolne ; Convert to boolean + + diff --git a/libsrc/runtime/lneg.s b/libsrc/runtime/lneg.s new file mode 100644 index 000000000..12b744b48 --- /dev/null +++ b/libsrc/runtime/lneg.s @@ -0,0 +1,31 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: negation on longs +; + +; +; eax = -eax +; + .export negeax + .importzp sreg + +negeax: clc + eor #$FF + adc #1 + pha + txa + eor #$FF + adc #0 + tax + lda sreg + eor #$FF + adc #0 + sta sreg + lda sreg+1 + eor #$FF + adc #0 + sta sreg+1 + pla + rts + diff --git a/libsrc/runtime/lor.s b/libsrc/runtime/lor.s new file mode 100644 index 000000000..62051a211 --- /dev/null +++ b/libsrc/runtime/lor.s @@ -0,0 +1,30 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: or on longs +; + + .export tosoreax + .import addysp1 + .importzp sp, sreg, tmp1 + +tosoreax: + ldy #0 + ora (sp),y ; byte 0 + sta tmp1 + iny + txa + ora (sp),y ; byte 1 + tax + iny + lda sreg + ora (sp),y ; byte 2 + sta sreg + iny + lda sreg+1 + ora (sp),y ; byte 3 + sta sreg+1 + + lda tmp1 + jmp addysp1 + diff --git a/libsrc/runtime/lpop.s b/libsrc/runtime/lpop.s new file mode 100644 index 000000000..ff0d59b9b --- /dev/null +++ b/libsrc/runtime/lpop.s @@ -0,0 +1,25 @@ +; +; Ullrich von Bassewitz, 29.12.1999 +; +; CC65 runtime: long pop +; + + .export popeax + .import incsp4 + .importzp sp, sreg + + +popeax: ldy #3 + lda (sp),y + sta sreg+1 + dey + lda (sp),y + sta sreg + dey + lda (sp),y + tax + dey + lda (sp),y + jmp incsp4 + + diff --git a/libsrc/runtime/lpush.s b/libsrc/runtime/lpush.s new file mode 100644 index 000000000..7724f499b --- /dev/null +++ b/libsrc/runtime/lpush.s @@ -0,0 +1,34 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: long push +; + +; +; push eax on stack +; + .export push0ax, pusheax + .import decsp4 + .importzp sp, sreg + +push0ax: + ldy #0 + sty sreg + sty sreg+1 +pusheax: + jsr decsp4 + pha + ldy #0 + sta (sp),y + iny + txa + sta (sp),y + iny + lda sreg + sta (sp),y + iny + lda sreg+1 + sta (sp),y + pla + rts + diff --git a/libsrc/runtime/lrsub.s b/libsrc/runtime/lrsub.s new file mode 100644 index 000000000..607b7fed2 --- /dev/null +++ b/libsrc/runtime/lrsub.s @@ -0,0 +1,33 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: long sub reversed +; + +; +; EAX = EAX - TOS +; + .export tosrsubeax + .import addysp1 + .importzp sp, sreg, tmp1 + +tosrsubeax: + ldy #0 + sec + sbc (sp),y ; byte 0 + sta tmp1 ; use as temp storage + txa + iny + sbc (sp),y ; byte 1 + tax + iny + lda sreg + sbc (sp),y ; byte 2 + sta sreg + iny + lda sreg+1 + sbc (sp),y ; byte 3 + sta sreg+1 + lda tmp1 + jmp addysp1 ; drop TOS + diff --git a/libsrc/runtime/lruntime.s b/libsrc/runtime/lruntime.s new file mode 100644 index 000000000..756c1418f --- /dev/null +++ b/libsrc/runtime/lruntime.s @@ -0,0 +1,100 @@ +; +; lruntime.s +; +; Ullrich von Bassewitz, 22.06.1998 +; + +; Runtime support for longs. + + .import popax, pusheax, staspic + .importzp sp, sreg, tmp2, ptr1 + +; +; leax (sp),y +; + .export ldeax0sp, ldeaxysp + +ldeax0sp: + ldy #3 +ldeaxysp: + lda (sp),y + sta sreg+1 + dey + lda (sp),y + sta sreg + dey + lda (sp),y + tax + dey + lda (sp),y + rts + +; +; push a long from (sp),y +; + .export pushlysp + +pushlysp: + iny + iny + lda (sp),y + iny + sta sreg + lda (sp),y + sta sreg+1 + dey + dey + lda (sp),y + dey + tax + lda (sp),y + jmp pusheax + +; +; eax --> ((sp)); pop +; + .export steaxspp + +steaxspp: + pha + txa + pha + jsr popax ; get address + sta ptr1 + stx ptr1+1 + ldy #3 + lda sreg+1 + sta (ptr1),y + dey + lda sreg + sta (ptr1),y + dey + pla + tax + sta (ptr1),y + dey + pla + sta (ptr1),y + rts + +; +; eax --> ((sp)),y +; + .export steaxspidx + +steaxspidx: + jsr staspic ; Get pointer, store a + pha + iny + lda tmp2 + sta (ptr1),y + iny + tax + lda sreg + sta (ptr1),y + iny + lda sreg+1 + sta (ptr1),y + pla + rts + diff --git a/libsrc/runtime/lsave.s b/libsrc/runtime/lsave.s new file mode 100644 index 000000000..e9ccfc32f --- /dev/null +++ b/libsrc/runtime/lsave.s @@ -0,0 +1,28 @@ +; +; Ullrich von Bassewitz, 08.08.1998 +; +; CC65 runtime: save ax into temp storage/restore ax from temp storage +; + + .export saveeax, resteax + .importzp sreg, regsave + +saveeax: + sta regsave + stx regsave+1 + lda sreg + sta regsave+2 + lda sreg+1 + sta regsave+3 + lda regsave + rts + +resteax: + lda regsave+3 + sta sreg+1 + lda regsave+2 + sta sreg + ldx regsave+1 + lda regsave + rts + diff --git a/libsrc/runtime/lshelp.s b/libsrc/runtime/lshelp.s new file mode 100644 index 000000000..9d8f366d2 --- /dev/null +++ b/libsrc/runtime/lshelp.s @@ -0,0 +1,76 @@ +; +; Ullrich von Bassewitz, 13.08.1998 +; +; CC65 runtime: helper stuff for mod/div/mul with long signed ints +; + +; When negating values, we will ignore the possibility here, that one of the +; values if $80000000, in which case the negate will fail. + + .export poplsargs, adjlsres + .import getlop, negeax + .importzp sreg, tmp1, ptr1, ptr3, ptr4 + +poplsargs: + jsr getlop ; Get the operands + +; Calculate the sign of the result, that is sign(op1) * sign(op2) and +; remember it. + + lda sreg+1 + eor ptr4+1 + sta tmp1 ; Save it across call + +; Make both operands positive + + lda sreg+1 ; Is the operand negative? + bpl L1 ; Jump if not + + clc ; Make it positive + lda ptr1 + eor #$FF + adc #$01 + sta ptr1 + lda ptr1+1 + eor #$FF + adc #$00 + sta ptr1+1 + lda sreg + eor #$FF + adc #$00 + sta sreg + lda sreg+1 + eor #$FF + adc #$00 + sta sreg+1 + +L1: lda ptr4+1 ; Is the operand nagative? + bpl L2 ; Jump if not + + clc ; Make it positive + lda ptr3 + eor #$FF + adc #$01 + sta ptr3 + lda ptr3+1 + eor #$FF + adc #$00 + sta ptr3+1 + lda ptr4 + eor #$FF + adc #$00 + sta ptr4 + lda ptr4+1 + eor #$FF + adc #$00 + sta ptr4+1 + +L2: rts + +; Adjust the result of a mod/div/mul operation + +adjlsres: + ldy tmp1 ; Check if we must adjust the sign + bpl L2 + jmp negeax ; Netage value + diff --git a/libsrc/runtime/lshl.s b/libsrc/runtime/lshl.s new file mode 100644 index 000000000..d481fa69c --- /dev/null +++ b/libsrc/runtime/lshl.s @@ -0,0 +1,95 @@ +; +; Ullrich von Bassewitz, 20.09.1998 +; +; CC65 runtime: left shift support for longs +; + + + .export tosasleax, tosshleax + .import addysp1 + .importzp sp, sreg, ptr1, ptr2 + + +tosshleax: +tosasleax: + +; Get the lhs from stack into ptr1/ptr2 + + pha + ldy #0 + lda (sp),y + sta ptr1 + iny + lda (sp),y + sta ptr1+1 + iny + lda (sp),y + sta ptr2 + iny + lda (sp),y + sta ptr2+1 + pla + jsr addysp1 + +; Check for shift overflow or zero shift + + tay ; Low byte of shift count into y + txa ; Get byte 2 + ora sreg + ora sreg+1 ; Check high 24 bit + bne @L6 ; Shift count too large + cpy #32 + bcs @L6 + + cpy #0 ; Shift count zero? + beq @L5 + +; We must shift. Shift by multiples of eight if possible + + tya +@L1: cmp #8 + bcc @L3 + sbc #8 + ldx ptr2 + stx ptr2+1 + ldx ptr1+1 + stx ptr2 + ldx ptr1 + stx ptr1+1 + ldx #0 + stx ptr1 + beq @L1 + +; Shift count is now less than eight. Do a real shift. + +@L3: tay ; Shift count to Y + lda ptr1 ; Get one byte into A for speed + cpy #0 + beq @L4a ; Jump if done +@L4: asl a + rol ptr1+1 + rol ptr2 + rol ptr2+1 + dey + bne @L4 + +; Put the result in place + +@L4a: ldx ptr2 + stx sreg + ldx ptr2+1 + stx sreg+1 + ldx ptr1+1 +@L5: rts + +; Jump here if shift count overflow + +@L6: lda #0 + sta sreg+1 + sta sreg + tax + rts + + + + diff --git a/libsrc/runtime/lshr.s b/libsrc/runtime/lshr.s new file mode 100644 index 000000000..2e3e05101 --- /dev/null +++ b/libsrc/runtime/lshr.s @@ -0,0 +1,185 @@ +; +; Ullrich von Bassewitz, 20.09.1998 +; +; CC65 runtime: right shift support for longs +; + + + .export tosasreax, tosshreax + .import addysp1 + .importzp sp, sreg, ptr1, ptr2 + +; -------------------------------------------------------------------- +; signed shift + +.proc tosasreax + + jsr getlhs ; Get the lhs from the stack + + jsr checkovf ; Check for overflow + bcs L6 ; Jump if shift count too large + + cpy #0 ; Shift count zero? + beq L5 + +; We must shift. Shift by multiples of eight if possible + + tya +L1: cmp #8 + bcc L3 + sbc #8 + ldx ptr1+1 + stx ptr1 + ldx ptr2 + stx ptr1+1 + ldy #0 + ldx ptr2+1 + stx ptr2 + bpl L2 + dey ; Get sign +L2: sty ptr2+1 + jmp L1 + +; Shift count is now less than eight. Do a real shift. + +L3: tay ; Shift count to Y + lda ptr2+1 ; Get one byte into A for speed + cpy #0 + beq L4a ; Jump if done +L4: cmp #$80 ; Get sign bit into C + ror a + ror ptr2 + ror ptr1+1 + ror ptr1 + dey + bne L4 + +; Put the result in place + +L4a: sta sreg+1 + lda ptr2 + sta sreg + ldx ptr1+1 + lda ptr1 +L5: rts + +; Jump here if shift count overflow + +L6: ldx #0 + lda ptr2+1 ; Check sign + bpl L7 + dex +L7: stx sreg+1 + stx sreg + txa + rts + +.endproc + +; -------------------------------------------------------------------- +; unsigned shift + +.proc tosshreax + + jsr getlhs ; Get the lhs from the stack + + jsr checkovf ; Check for overflow + bcs L6 ; Jump if shift count too large + + cpy #0 ; Shift count zero? + beq L5 + +; We must shift. Shift by multiples of eight if possible + + tya +L1: cmp #8 + bcc L3 + sbc #8 + ldx ptr1+1 + stx ptr1 + ldx ptr2 + stx ptr1+1 + ldx ptr2+1 + stx ptr2 + ldx #0 + stx ptr2+1 + beq L1 + +; Shift count is now less than eight. Do a real shift. + +L3: tay ; Shift count to Y + lda ptr2+1 ; Get one byte into A for speed + cpy #0 + beq L4a ; Jump if done +L4: lsr a + ror ptr2 + ror ptr1+1 + ror ptr1 + dey + bne L4 + +; Put the result in place + +L4a: sta sreg+1 + lda ptr2 + sta sreg + ldx ptr1+1 + lda ptr1 +L5: rts + +; Jump here if shift count overflow + +L6: lda #0 + sta sreg+1 + sta sreg + tax + rts + +.endproc + +; -------------------------------------------------------------------- +; Helpers + +.proc getlhs ; Get the lhs from stack into ptr1/ptr2 + + pha + ldy #0 + lda (sp),y + sta ptr1 + iny + lda (sp),y + sta ptr1+1 + iny + lda (sp),y + sta ptr2 + iny + lda (sp),y + sta ptr2+1 + pla + jmp addysp1 + +.endproc + + +.proc checkovf ; Check for shift overflow + + tay ; Low byte of shift count into y + txa ; Get byte 2 + ora sreg + ora sreg+1 ; Check high 24 bit + bne TooLarge ; Shift count too large + cpy #32 + bcc Ok +TooLarge: + sec +Ok: rts + +.endproc + + + + + + + + diff --git a/libsrc/runtime/lsub.s b/libsrc/runtime/lsub.s new file mode 100644 index 000000000..8854e9e50 --- /dev/null +++ b/libsrc/runtime/lsub.s @@ -0,0 +1,36 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: long sub +; + +; +; EAX = TOS - EAX +; + .export tossubeax + .import addysp1 + .importzp sp, sreg, tmp1, tmp2 + +tossubeax: + ldy #0 + sec + sta tmp1 + lda (sp),y + sbc tmp1 ; byte 0 + sta tmp2 ; use as temp storage + iny + stx tmp1 + lda (sp),y + sbc tmp1 ; byte 1 + tax + iny + lda (sp),y + sbc sreg ; byte 2 + sta sreg + iny + lda (sp),y + sbc sreg+1 ; byte 3 + sta sreg+1 + lda tmp2 ; load byte 0 + jmp addysp1 ; drop TOS + diff --git a/libsrc/runtime/lsubeq.s b/libsrc/runtime/lsubeq.s new file mode 100644 index 000000000..974626c14 --- /dev/null +++ b/libsrc/runtime/lsubeq.s @@ -0,0 +1,57 @@ +; +; Ullrich von Bassewitz, 07.04.2000 +; +; CC65 runtime: -= operator +; +; On entry, the low byte of the address of the variable to decrement is +; in ptr1, the high byte is in Y, and the decrement is in eax. +; + + .export lsubeq1, lsubeqa, lsubeq + .importzp sreg, ptr1, tmp1 + + +lsubeq1: + lda #$01 + +lsubeqa: + ldx #$00 + stx sreg + stx sreg+1 + +lsubeq: sty ptr1+1 ; Store high byte of address + ldy #$00 ; Address low byte + sec + + sta tmp1 + lda (ptr1),y ; Load byte 0 + sbc tmp1 + sta (ptr1),y + pha ; Save byte 0 of result for later + + iny ; Address byte 1 + stx tmp1 + lda (ptr1),y ; Load byte 1 + sbc tmp1 + sta (ptr1),y + tax + + iny ; Address byte 2 + lda (ptr1),y + sbc sreg + sta (ptr1),y + sta sreg + + iny ; Address byte 3 + lda (ptr1),y + sbc sreg+1 + sta (ptr1),y + sta sreg+1 + + pla ; Retrieve byte 0 of result + + rts ; Done + + + + diff --git a/libsrc/runtime/lsubeqsp.s b/libsrc/runtime/lsubeqsp.s new file mode 100644 index 000000000..6474f4059 --- /dev/null +++ b/libsrc/runtime/lsubeqsp.s @@ -0,0 +1,37 @@ +; +; Ullrich von Bassewitz, 08.10.1998 +; +; CC65 runtime: -= operator for longs on the stack +; + + .export lsubeq0sp, lsubeqysp + .importzp sp, sreg, tmp1, tmp2 + +lsubeq0sp: + ldy #0 +lsubeqysp: + sec + sta tmp1 + stx tmp2 + lda (sp),y + sbc tmp1 + sta (sp),y + pha + iny + lda (sp),y + sbc tmp2 + sta (sp),y + tax + iny + lda (sp),y + sbc sreg + sta (sp),y + sta sreg + iny + lda (sp),y + sbc sreg+1 + sta (sp),y + sta sreg+1 + pla + rts + diff --git a/libsrc/runtime/lswap.s b/libsrc/runtime/lswap.s new file mode 100644 index 000000000..0e03cf2f7 --- /dev/null +++ b/libsrc/runtime/lswap.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 29.12.1999 +; +; CC65 runtime: Exchange lo and hi part of eax +; + + .export swapeax + .importzp sreg + +swapeax: + ldy sreg + sta sreg + lda sreg+1 + stx sreg+1 + tax + tya + rts + diff --git a/libsrc/runtime/lswitch.s b/libsrc/runtime/lswitch.s new file mode 100644 index 000000000..9173b1554 --- /dev/null +++ b/libsrc/runtime/lswitch.s @@ -0,0 +1,87 @@ +; +; Ullrich von Bassewitz, 17.08.1998 +; +; CC65 runtime: switch statement with long selector +; + +; Subroutine to handle a switch statement with an int selector. The table +; is located at the return address from the function. It contains the negative +; of the case label count as first word, followed by three words for each case +; label, the first two being the value, and the second one the label to jump +; to in case of a match. The default case is located at the end of the table. + + .export lswitch + .importzp sreg, ptr1, ptr2, ptr3 + +lswitch: + sta ptr1 + stx ptr1+1 ; Save AX + clc + pla + adc #1 + sta ptr2 + pla + adc #0 + sta ptr2+1 ; Get pointer to table + + ldy #0 + lda (ptr2),y + sta ptr3 + iny + lda (ptr2),y + sta ptr3+1 ; Remember the count of labels + + ldy #0 + clc ; Skip the label count + lda ptr2 + adc #2 + sta ptr2 + bcc L2 + inc ptr2+1 + bne L2 ; Branch always + +; Search for the label + +L0: lda (ptr2),y + cmp ptr1 + bne L1 + iny + lda (ptr2),y + cmp ptr1+1 + bne L1 + iny + lda (ptr2),y + cmp sreg + bne L1 + iny + lda (ptr2),y + cmp sreg+1 + beq L3 +L1: clc + lda ptr2 + adc #6 ; Skip table entry + sta ptr2 + bcc L2 + inc ptr2+1 + +; Check if there are any labels left + +L2: inc ptr3 + bne L0 + inc ptr3+1 + bne L0 + +; Out of labels + + jmp (ptr2) + +; Label found + +L3: ldy #4 ; Jump label offset + lda (ptr2),y + sta ptr3 + iny + lda (ptr2),y + sta ptr3+1 + jmp (ptr3) + diff --git a/libsrc/runtime/lt.s b/libsrc/runtime/lt.s new file mode 100644 index 000000000..1b681f588 --- /dev/null +++ b/libsrc/runtime/lt.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare < for signed ints +; + + .export toslt00, toslta0, tosltax + .import tosicmp, boollt + +toslt00: + lda #$00 +toslta0: + ldx #$00 +tosltax: + jsr tosicmp ; Set flags + jmp boollt ; Convert to boolean + diff --git a/libsrc/runtime/ltest.s b/libsrc/runtime/ltest.s new file mode 100644 index 000000000..2c6630042 --- /dev/null +++ b/libsrc/runtime/ltest.s @@ -0,0 +1,22 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: test long in eax +; + + .export utsteax, tsteax + .importzp sreg, tmp1 + +tsteax: +utsteax: + tay ; Save value + stx tmp1 + ora tmp1 + ora sreg + ora sreg+1 + beq L9 + tya + ldy #1 ; Force NE +L9: rts + + diff --git a/libsrc/runtime/ludiv.s b/libsrc/runtime/ludiv.s new file mode 100644 index 000000000..1d8dc730a --- /dev/null +++ b/libsrc/runtime/ludiv.s @@ -0,0 +1,93 @@ +; +; Ullrich von Bassewitz, 17.08.1998 +; +; CC65 runtime: division for long unsigned ints +; + + .export tosudiveax, getlop, udiv32 + .import addysp1 + .importzp sp, sreg, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4 + +tosudiveax: + jsr getlop ; Get the paramameters + jsr udiv32 ; Do the division + lda ptr1 ; Result is in ptr1:sreg + ldx ptr1+1 + rts + +; Pop the parameters for the long division and put it into the relevant +; memory cells. Called from the signed divisions also. + +getlop: sta ptr3 ; Put right operand in place + stx ptr3+1 + lda sreg + sta ptr4 + lda sreg+1 + sta ptr4+1 + + ldy #0 ; Put left operand in place + lda (sp),y + sta ptr1 + iny + lda (sp),y + sta ptr1+1 + iny + lda (sp),y + sta sreg + iny + lda (sp),y + sta sreg+1 + jmp addysp1 ; Drop parameters + +; Do (ptr1:sreg) / (ptr3:ptr4) --> (ptr1:sreg), remainder in (ptr2:tmp3:tmp4) +; This is also the entry point for the signed division + +udiv32: lda #0 + sta ptr2+1 + sta tmp3 + sta tmp4 +; sta ptr1+1 + ldy #32 +L0: asl ptr1 + rol ptr1+1 + rol sreg + rol sreg+1 + rol a + rol ptr2+1 + rol tmp3 + rol tmp4 + +; Do a subtraction. we do not have enough space to store the intermediate +; result, so we may have to do the subtraction twice. + + pha + cmp ptr3 + lda ptr2+1 + sbc ptr3+1 + lda tmp3 + sbc ptr4 + lda tmp4 + sbc ptr4+1 + bcc L1 + +; Overflow, do the subtraction again, this time store the result + + sta ptr4+1 ; We have the high byte already + pla + sbc ptr3 ; byte 0 + pha + lda ptr2+1 + sbc ptr3+1 + sta ptr2+1 ; byte 1 + lda tmp3 + sbc ptr4 + sta tmp3 ; byte 2 + inc ptr1 ; Set result bit + +L1: pla + dey + bne L0 + sta ptr2 + rts + + diff --git a/libsrc/runtime/luge.s b/libsrc/runtime/luge.s new file mode 100644 index 000000000..803640c65 --- /dev/null +++ b/libsrc/runtime/luge.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 10.12.1998 +; +; CC65 runtime: Compare >= for long unsigneds +; + + .export tosugeeax + .import lcmp, booluge + +tosugeeax: + jsr lcmp ; Set the flags + jmp booluge ; Convert to boolean + diff --git a/libsrc/runtime/lugt.s b/libsrc/runtime/lugt.s new file mode 100644 index 000000000..eb4712197 --- /dev/null +++ b/libsrc/runtime/lugt.s @@ -0,0 +1,14 @@ +; +; Ullrich von Bassewitz, 10.12.1998 +; +; CC65 runtime: Compare > for long unsigneds +; + + .export tosugteax + .import lcmp, boolugt + +tosugteax: + jsr lcmp ; Set the flags + jmp boolugt ; Convert to boolean + + diff --git a/libsrc/runtime/lule.s b/libsrc/runtime/lule.s new file mode 100644 index 000000000..7b080df0a --- /dev/null +++ b/libsrc/runtime/lule.s @@ -0,0 +1,13 @@ +; +; Ullrich von Bassewitz, 10.12.1998 +; +; CC65 runtime: Compare <= for long unsigneds +; + + .export tosuleeax + .import lcmp, boolule + +tosuleeax: + jsr lcmp ; Set the flags + jmp boolule ; Convert to boolean + diff --git a/libsrc/runtime/lult.s b/libsrc/runtime/lult.s new file mode 100644 index 000000000..85b8b9ab9 --- /dev/null +++ b/libsrc/runtime/lult.s @@ -0,0 +1,12 @@ +; +; Ullrich von Bassewitz, 10.12.1998 +; +; CC65 runtime: Compare < for long unsigneds +; + + .export tosulteax + .import lcmp, boolult + +tosulteax: + jsr lcmp ; Set the flags + jmp boolult ; Convert to boolean diff --git a/libsrc/runtime/lumod.s b/libsrc/runtime/lumod.s new file mode 100644 index 000000000..526db7f10 --- /dev/null +++ b/libsrc/runtime/lumod.s @@ -0,0 +1,21 @@ +; +; Ullrich von Bassewitz, 27.09.1998 +; +; CC65 runtime: modulo operation for long unsigned ints +; + + .export tosumodeax + .import getlop, udiv32 + .importzp sreg, tmp3, tmp4, ptr2 + +tosumodeax: + jsr getlop ; Get the paramameters + jsr udiv32 ; Do the division + lda tmp3 ; Remainder is in ptr2:tmp3:tmp4 + sta sreg + lda tmp4 + sta sreg + lda ptr2 + ldx ptr2+1 + rts + diff --git a/libsrc/runtime/lxor.s b/libsrc/runtime/lxor.s new file mode 100644 index 000000000..f772ebb89 --- /dev/null +++ b/libsrc/runtime/lxor.s @@ -0,0 +1,32 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: xor on longs +; + + .export tosxoreax + .import addysp1 + .importzp sp, sreg, tmp1 + +tosxoreax: + ldy #0 + eor (sp),y ; byte 0 + sta tmp1 + iny + txa + eor (sp),y ; byte 1 + tax + iny + lda sreg + eor (sp),y ; byte 2 + sta sreg + iny + lda sreg+1 + eor (sp),y ; byte 3 + sta sreg+1 + + lda tmp1 + jmp addysp1 + + + diff --git a/libsrc/runtime/makebool.s b/libsrc/runtime/makebool.s new file mode 100644 index 000000000..3429ff0bc --- /dev/null +++ b/libsrc/runtime/makebool.s @@ -0,0 +1,60 @@ +; +; Ullrich von Bassewitz, 05.10.1998 +; +; CC65 runtime: Make boolean according to flags +; + + .export boolne, booleq, boollt, boolle, boolgt, boolge + .export boolult, boolule, boolugt, booluge + + +boolne: bne ret1 + ldx #$00 + txa + rts + + +booleq: beq ret1 + ldx #$00 + txa + rts + + +boolle: beq ret1 +boollt: bmi ret1 + ldx #$00 + txa + rts + + +boolgt: beq L0 +boolge: bpl ret1 +L0: ldx #$00 + txa + rts + + +boolule: + beq ret1 +boolult: + bcc ret1 + ldx #$00 + txa + rts + + +boolugt: + beq L1 +booluge: + bcs ret1 +L1: ldx #$00 + txa + rts + + +ret1: ldx #$00 + lda #$01 + rts + + + diff --git a/libsrc/runtime/mod.s b/libsrc/runtime/mod.s new file mode 100644 index 000000000..9e2a73ed8 --- /dev/null +++ b/libsrc/runtime/mod.s @@ -0,0 +1,23 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: modulo operation for signed ints +; + +; When negating values, we will ignore the possibility here, that one of the +; values if $8000, in which case the negate will fail. + + .export tosmoda0, tosmodax + .import popsargs, udiv16, adjsres + .importzp ptr1 + +tosmoda0: + ldx #0 +tosmodax: + jsr popsargs ; Get arguments from stack, adjust sign + jsr udiv16 ; Do the division + lda ptr1 ; Result is in sreg, remainder in ptr1 + ldx ptr1+1 + jmp adjsres ; Adjust the sign of the result if needed + + diff --git a/libsrc/runtime/mul.s b/libsrc/runtime/mul.s new file mode 100644 index 000000000..67caf52cf --- /dev/null +++ b/libsrc/runtime/mul.s @@ -0,0 +1,43 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: multiplication for ints +; + + .export tosumula0, tosumulax, tosmula0, tosmulax + .import popsreg + .importzp sreg, tmp1, ptr4 + +tosmula0: +tosumula0: + ldx #0 +tosmulax: +tosumulax: +mul16: sta ptr4 + stx ptr4+1 ; Save right operand + jsr popsreg ; Get left operand + +; Do ptr4*sreg --> AX (see mult-div.s from "The Fridge"). + + lda #0 + sta tmp1 + ldx sreg+1 ; Get into register for speed + ldy #16 ; Number of bits +L0: lsr tmp1 + ror a + ror ptr4+1 + ror ptr4 + bcc L1 + clc + adc sreg + pha + txa ; hi byte of left op + adc tmp1 + sta tmp1 + pla +L1: dey + bpl L0 + lda ptr4 ; Load the result + ldx ptr4+1 + rts ; Done + diff --git a/libsrc/runtime/ne.s b/libsrc/runtime/ne.s new file mode 100644 index 000000000..ce9b8171d --- /dev/null +++ b/libsrc/runtime/ne.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare != for ints +; + + .export tosne00, tosnea0, tosneax + .import tosicmp, boolne + +tosne00: + lda #$00 +tosnea0: + ldx #$00 +tosneax: + jsr tosicmp ; Set flags + jmp boolne ; Convert to boolean + + diff --git a/libsrc/runtime/neg.s b/libsrc/runtime/neg.s new file mode 100644 index 000000000..62807c7e9 --- /dev/null +++ b/libsrc/runtime/neg.s @@ -0,0 +1,21 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: negation on ints +; + + .export negax + +negax: clc + eor #$FF + adc #1 + pha + txa + eor #$FF + adc #0 + tax + pla + rts + + + diff --git a/libsrc/runtime/or.s b/libsrc/runtime/or.s new file mode 100644 index 000000000..f95354921 --- /dev/null +++ b/libsrc/runtime/or.s @@ -0,0 +1,23 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: or on ints +; + + .export tosora0, tosorax + .import addysp1 + .importzp sp, tmp1 + +tosora0: + ldx #$00 +tosorax: + ldy #0 + ora (sp),y + sta tmp1 + iny + txa + ora (sp),y + tax + lda tmp1 + jmp addysp1 ; drop TOS, set condition codes + diff --git a/libsrc/runtime/popsreg.s b/libsrc/runtime/popsreg.s new file mode 100644 index 000000000..a79f9dc41 --- /dev/null +++ b/libsrc/runtime/popsreg.s @@ -0,0 +1,22 @@ +; +; Ullrich von Bassewitz, 21.08.1998 +; +; CC65 runtime: Pop TOS into sreg +; + + .export popsreg + .import incsp2 + .importzp sp, sreg + +popsreg: + pha ; save A + ldy #0 + lda (sp),y ; get lo byte + sta sreg ; store it + iny + lda (sp),y ; get hi byte + sta sreg+1 ; store it + pla ; get A back + jmp incsp2 ; bump stack and return + + diff --git a/libsrc/runtime/push.s b/libsrc/runtime/push.s new file mode 100644 index 000000000..0f2ff3c05 --- /dev/null +++ b/libsrc/runtime/push.s @@ -0,0 +1,83 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: Push ints onto the stack +; + +; +; push/pop things on stack +; + .export push0, push1, push2, push3, push4, push5 + .export push6, push7, pusha0, pushaFF, pushax + .export pusha, pushaysp, pushc0, pushc1, pushc2 + .importzp sp + +pushaFF: + ldx #$FF + bne pushax + +push7: lda #7 + bne pusha0 +push6: lda #6 + bne pusha0 +push5: lda #5 + bne pusha0 +push4: lda #4 + bne pusha0 +push3: lda #3 + bne pusha0 +push2: lda #2 + bne pusha0 +push1: lda #1 + bne pusha0 +push0: lda #0 +; beq pusha0 +pusha0: ldx #0 + +; This function is used *a lot*, so don't call any subroutines here. +; Beware: The value in ax must not be changed by this function! +; Beware^2: The optimizer knows about the value of Y after the function +; returns! + +pushax: ldy sp + beq @L1 + dey + beq @L2 + dey +@L0: sty sp + ldy #0 ; get index + sta (sp),y ; store lo byte + pha ; save it + txa ; get hi byte + iny ; bump idx + sta (sp),y ; store hi byte + pla ; get A back + rts ; done + +@L1: dey +@L2: dey + dec sp+1 + bne @L0 ; Branch always + +; Push for chars, same warning as above: The optimizer expects the value +; 0 in the Y register after this function. + +pushc2: lda #2 + bne pusha +pushc1: lda #1 + bne pusha +pushc0: lda #0 + beq pusha +pushaysp: + lda (sp),y +pusha: ldy sp + beq @L1 + dec sp + ldy #0 + sta (sp),y + rts + +@L1: dec sp+1 + dec sp + sta (sp),y + rts diff --git a/libsrc/runtime/pushb.s b/libsrc/runtime/pushb.s new file mode 100644 index 000000000..a0b37faa2 --- /dev/null +++ b/libsrc/runtime/pushb.s @@ -0,0 +1,24 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Push word from stack +; + + .export pushb, pushbidx + .import pushax + .importzp ptr1 + +pushbidx: + sty ptr1 + clc + adc ptr1 + bcc pushb + inx +pushb: sta ptr1 + stx ptr1+1 + ldx #0 ; Load index/high byte + lda (ptr1,x) + bpl L1 + dex ; Make high byte FF +L1: jmp pushax + diff --git a/libsrc/runtime/pushbsp.s b/libsrc/runtime/pushbsp.s new file mode 100644 index 000000000..2ae062506 --- /dev/null +++ b/libsrc/runtime/pushbsp.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load a from stack slot and push as word +; + + .export pushbsp, pushbysp + .import pusha0 + .importzp sp + +pushbsp: + ldy #0 +pushbysp: + lda (sp),y ; get lo byte + jmp pusha0 ; promote to unsigned and push + + diff --git a/libsrc/runtime/pushw.s b/libsrc/runtime/pushw.s new file mode 100644 index 000000000..a560bef18 --- /dev/null +++ b/libsrc/runtime/pushw.s @@ -0,0 +1,25 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Push word from stack +; + + .export pushw, pushwidx + .import pushax + .importzp ptr1 + +pushwidx: + sty ptr1 + clc + adc ptr1 + bcc pushw + inx +pushw: sta ptr1 + stx ptr1+1 + ldy #1 + lda (ptr1),y + tax + dey + lda (ptr1),y + jmp pushax + diff --git a/libsrc/runtime/pushwsp.s b/libsrc/runtime/pushwsp.s new file mode 100644 index 000000000..1c940e038 --- /dev/null +++ b/libsrc/runtime/pushwsp.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Load word from stack slot and push +; + + .export pushwysp, pushw0sp + .import pushax + .importzp sp + +pushw0sp: + ldy #1 +pushwysp: + lda (sp),y ; get hi byte + tax + dey + lda (sp),y ; get lo byte + jmp pushax ; push that + + diff --git a/libsrc/runtime/rsub.s b/libsrc/runtime/rsub.s new file mode 100644 index 000000000..2b722e013 --- /dev/null +++ b/libsrc/runtime/rsub.s @@ -0,0 +1,29 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: sub ints reversed +; + + .export tosrsuba0, tosrsubax + .import addysp1 + .importzp sp, tmp1 + +; +; AX = AX - TOS +; + +tosrsuba0: + ldx #0 +tosrsubax: + ldy #0 + sec + sbc (sp),y ; lo byte + sta tmp1 ; save lo byte + txa + iny + sbc (sp),y ; hi byte + tax + lda tmp1 + jmp addysp1 ; drop TOS, set condition codes + + diff --git a/libsrc/runtime/runtime.s b/libsrc/runtime/runtime.s new file mode 100644 index 000000000..095da2588 --- /dev/null +++ b/libsrc/runtime/runtime.s @@ -0,0 +1,265 @@ +; +; Runtime code for cc65. +; + + + .import ldai, ldaxi, pushax + .importzp sp, tmp1, tmp2, tmp3, ptr1, ptr4 + + +; Pop a from stack + .export popa + +popa: ldy #0 + lda (sp),y ; Read byte + inc sp + bne *+4 + inc sp+1 + rts + +; +; pop a from stack and load x with zero +; + + .export popa0 + +popa0: ldy #0 + lda (sp),y ; load low byte + ldx #0 + beq incsp2 + +; +; pop a/x from stack. This function will run directly into incsp2 +; + + .export popax ; pop stack into AX + +popax: ldy #1 + lda (sp),y ; get hi byte + tax ; into x + dey + lda (sp),y ; get lo byte + +; +; routines for inc/dec'ing sp +; + + .export addysp, addysp1 + .export incsp1, incsp2, incsp3, incsp4 + .export incsp5, incsp6, incsp7, incsp8 + +; do this by hand, cause it gets used a lot + +incsp2: ldy sp ; 3 + iny ; 2 + beq @L1 ; 2 + iny ; 2 + beq @L2 ; 2 + sty sp ; 3 + rts + +@L1: iny ; 2 +@L2: sty sp ; 3 + inc sp+1 ; 5 + rts + +; Hand optimize this one also... + +incsp1: inc sp + bne *+4 + inc sp+1 + rts + +incsp3: ldy #3 + bne addysp + +incsp4: ldy #4 + bne addysp + +incsp5: ldy #5 + bne addysp + +incsp6: ldy #6 + bne addysp + +incsp7: ldy #7 + bne addysp + +incsp8: ldy #8 + bne addysp + +addysp1: + iny +addysp: pha ; save A + clc + tya ; get the value + adc sp ; add lo byte + sta sp ; put it back + bcc addysp_1 ; if no carry, we're done + inc sp+1 ; inc hi byte +addysp_1: + pla ; get A back + rts + + +; +; + .export subysp ; sub Y from SP + .export decsp1, decsp2, decsp3, decsp4 + .export decsp5, decsp6, decsp7, decsp8 + +; Do this one by hand, cause it gets used a lot + +decsp2: ldy sp + beq @L1 + dey + beq @L2 + dey + sty sp + rts + +@L1: dey +@L2: dey + sty sp + dec sp+1 + rts + +; Decrement by 1 also done as fast as possible + +decsp1: ldy sp + bne *+4 + dec sp+1 + dec sp + rts + +decsp3: ldy #3 + bne subysp + +decsp4: ldy #4 + bne subysp + +decsp5: ldy #5 + bne subysp + +decsp6: ldy #6 + bne subysp + +decsp7: ldy #7 + bne subysp + +decsp8: ldy #8 +; bne subysp + +subysp: pha ; save A + sty tmp1 ; save the value + lda sp ; get lo byte + sec + sbc tmp1 ; sub y val + sta sp ; put it back + bcs *+4 + dec sp+1 + pla ; get A back + rts ; done + +; +; Various kinds of store operators +; +; store AX at SP@@(Y) + + .export staxspidx, staspidx, staspic +staxspidx: + jsr staspic ; use common part + pha + iny + lda tmp2 + sta (ptr4),y + tax + pla + rts +staspidx: + jsr staspic ; use common part + ldx tmp2 + rts + +staspic: + sta tmp1 + stx tmp2 + sty tmp3 + jsr popax ; get the pointer + sta ptr4 + stx ptr4+1 + ldy tmp3 + lda tmp1 + sta (ptr4),y + rts + +; ax --> (sp),y + + + .export staxspp ; store AX thru (sp), and pop +staxspp: + ldy #0 + pha + lda (sp),y + sta ptr1 + iny + lda (sp),y + sta ptr1+1 + txa + sta (ptr1),y + pla + dey + sta (ptr1),y + jmp incsp2 ; Drop address + + + .export staspp ; store A thru (sp), and pop +staspp: + ldy #1 + pha + lda (sp),y + sta ptr1+1 + dey + lda (sp),y + sta ptr1 + pla + sta (ptr1),y + jmp incsp2 ; Drop address + + +; +; Boolean function return entries. +; + + .export return0, return1 + +return1: + ldx #0 + lda #1 + rts + +return0: + lda #0 + tax + rts + +; +; random stuff +; + +; (a/x) 16--> (--sp) + + .export pushwaxi +pushwaxi: ; push word at (ax) + jsr ldaxi + jmp pushax + +; (a/x) 8--> (--sp) + + .export pushbaxi ; push byte at (ax) +pushbaxi: + jsr ldai + jmp pushax + + + diff --git a/libsrc/runtime/shelp.s b/libsrc/runtime/shelp.s new file mode 100644 index 000000000..631ff4783 --- /dev/null +++ b/libsrc/runtime/shelp.s @@ -0,0 +1,48 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: helper stuff for mod/div/mul with signed ints +; + +; When negating values, we will ignore the possibility here, that one of the +; values if $8000, in which case the negate will fail. + + .export popsargs, adjsres + .import negax, popax + .importzp sreg, tmp1, tmp2, ptr4 + +popsargs: + stx tmp1 ; Remember sign + cpx #0 + bpl L1 + jsr negax ; Negate accumulator +L1: sta ptr4 + stx ptr4+1 ; Save right operand + + jsr popax + stx tmp2 ; Remember sign + cpx #0 + bpl L2 + jsr negax +L2: sta sreg + stx sreg+1 + +; Calculate the sign of the result, that is sign(op1) * sign(op2) + + lda tmp1 + eor tmp2 + sta tmp2 ; Save it across call +L3: rts + +; Adjust the result of a mod/div/mul operation + +adjsres: + +; Check if we must adjust the sign + + ldy tmp2 + bpl L3 + jmp negax ; Adjust sign + + + diff --git a/libsrc/runtime/shl.s b/libsrc/runtime/shl.s new file mode 100644 index 000000000..73f483142 --- /dev/null +++ b/libsrc/runtime/shl.s @@ -0,0 +1,69 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: left shift support for ints +; + + .export tosasla0, tosaslax, tosshla0, tosshlax + .import popsreg, return0 + .importzp sreg + +tosshla0: +tosasla0: + ldx #0 +tosshlax: +tosaslax: + jsr popsreg ; get TOS into sreg + cpx #0 + bne TooLarge + cmp #16 + bcs TooLarge + + cmp #8 ; Shift count greater 8? + beq L3 ; Jump if exactly 8 + bcc L1 ; Jump if no + +; Shift count is greater 8. Do the first 8 bits the fast way + + ldy sreg + sty sreg+1 + stx sreg ; Low byte = 0 + sec + sbc #8 + +; Shift count less than 8 if we come here + +L1: tay ; Shift count --> Y + beq Zero ; Done if shift count zero + + lda sreg ; get low byte for faster shift + +; Do the actual shift + +L2: asl a + rol sreg+1 + dey + bne L2 + +; Done with shift + + ldx sreg+1 + rts + +; Shift count == 8 + +L3: txa ; X == 0, now A == 0 + ldx sreg + rts + +; Shift count was zero + +Zero: lda sreg + ldx sreg+1 + rts + +; Shift count too large, result is zero + +TooLarge: + jmp return0 + diff --git a/libsrc/runtime/shr.s b/libsrc/runtime/shr.s new file mode 100644 index 000000000..2b51500bc --- /dev/null +++ b/libsrc/runtime/shr.s @@ -0,0 +1,127 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: right shift support for ints +; + + +; -------------------------------------------------------------------- +; signed shift + + .export tosasra0, tosasrax + .import popsreg, return0 + .importzp sreg + +tosasra0: + ldx #0 +tosasrax: + jsr popsreg ; get TOS into sreg + cpx #0 + bne TooLarge + cmp #16 + bcs TooLarge + + cmp #8 ; Shift count greater 8? + beq L4 ; Jump if exactly 8 + bcc L2 ; Jump if no + +; Shift count is greater 8. Do the first 8 bits the fast way + + ldy #0 + ldx sreg+1 + stx sreg + bpl L1 + dey ; Create correct sign bits +L1: sty sreg+1 + sec + sbc #8 + +; Shift count less than 8 if we come here + +L2: tay ; Shift count --> Y + beq Zero ; Done if shift count zero + + lda sreg ; get low byte for faster shift + ldx sreg+1 ; get high byte + +; Do the actual shift + +L3: cpx #$80 ; get bit 7 into carry + ror sreg+1 + ror a + dey + bne L3 + +; Done with shift + + ldx sreg+1 + rts + +; Shift count == 8 + +L4: lda sreg+1 ; X is zero + bpl *+3 + dex ; X == 0xFF + rts + +; Shift count was zero + +Zero: lda sreg + ldx sreg+1 + rts + +; Shift count too large, result is zero + +TooLarge: + jmp return0 + + +; -------------------------------------------------------------------- +; unsigned shift + + .export tosshra0, tosshrax + +tosshra0: + ldx #0 +tosshrax: + jsr popsreg ; get TOS into sreg + cpx #0 + bne TooLarge + cmp #16 + bcs TooLarge + + cmp #8 ; Shift count greater 8? + beq L8 ; Jump if exactly 8 + bcc L6 ; Jump if no + +; Shift count is greater 8. Do the first 8 bits the fast way + + sbc #8 ; Carry already set + ldy sreg+1 + sty sreg + stx sreg+1 ; High byte = 0 + +; Shift count less than 8 if we come here + +L6: tay ; Shift count --> Y + beq Zero ; Done if shift count zero + + lda sreg ; get low byte for faster shift + +; Do the actual shift + +L7: lsr sreg+1 + ror a + dey + bne L7 + +; Done with shift + + ldx sreg+1 + rts + +; Shift count == 8 + +L8: lda sreg+1 ; X is zero + rts + diff --git a/libsrc/runtime/shrax1.s b/libsrc/runtime/shrax1.s new file mode 100644 index 000000000..f7b6b7256 --- /dev/null +++ b/libsrc/runtime/shrax1.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register +; + + .export shrax1 + .importzp tmp1 + +shrax1: stx tmp1 + lsr tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/shrax2.s b/libsrc/runtime/shrax2.s new file mode 100644 index 000000000..074ab8e3e --- /dev/null +++ b/libsrc/runtime/shrax2.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register by 4 +; + + .export shrax2 + .importzp tmp1 + +shrax2: stx tmp1 + lsr tmp1 + ror a + lsr tmp1 + ror a + ldx tmp1 + rts + + diff --git a/libsrc/runtime/shrax3.s b/libsrc/runtime/shrax3.s new file mode 100644 index 000000000..07e1e1f07 --- /dev/null +++ b/libsrc/runtime/shrax3.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the primary register by 8 +; + + .export shrax3 + .importzp tmp1 + +shrax3: stx tmp1 + lsr tmp1 + ror a + lsr tmp1 + ror a + lsr tmp1 + ror a + ldx tmp1 + rts + + diff --git a/libsrc/runtime/shreax1.s b/libsrc/runtime/shreax1.s new file mode 100644 index 000000000..f4d808f55 --- /dev/null +++ b/libsrc/runtime/shreax1.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the 32 bit primary register by 2 +; + + .export shreax1 + .importzp sreg, tmp1 + +shreax1: + stx tmp1 + lsr sreg+1 + ror sreg + ror tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/shreax2.s b/libsrc/runtime/shreax2.s new file mode 100644 index 000000000..1f2a23da8 --- /dev/null +++ b/libsrc/runtime/shreax2.s @@ -0,0 +1,22 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the 32 bit primary register by 4 +; + + .export shreax2 + .importzp sreg, tmp1 + +shreax2: + stx tmp1 + lsr sreg+1 + ror sreg + ror tmp1 + ror a + lsr sreg+1 + ror sreg + ror tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/shreax3.s b/libsrc/runtime/shreax3.s new file mode 100644 index 000000000..8111253a0 --- /dev/null +++ b/libsrc/runtime/shreax3.s @@ -0,0 +1,26 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Scale the 32 bit primary register by 8 +; + + .export shreax3 + .importzp sreg, tmp1 + +shreax3: + stx tmp1 + lsr sreg+1 + ror sreg + ror tmp1 + ror a + lsr sreg+1 + ror sreg + ror tmp1 + ror a + lsr sreg+1 + ror sreg + ror tmp1 + ror a + ldx tmp1 + rts + diff --git a/libsrc/runtime/staxsp.s b/libsrc/runtime/staxsp.s new file mode 100644 index 000000000..07828e36f --- /dev/null +++ b/libsrc/runtime/staxsp.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Store ax at (sp),y +; + + .export staxysp, stax0sp + .importzp sp + +stax0sp: + ldy #0 +staxysp: + sta (sp),y + iny + pha + txa + sta (sp),y + pla + rts + diff --git a/libsrc/runtime/steaxsp.s b/libsrc/runtime/steaxsp.s new file mode 100644 index 000000000..de134e2d3 --- /dev/null +++ b/libsrc/runtime/steaxsp.s @@ -0,0 +1,27 @@ +; +; Ullrich von Bassewitz, 31.08.1998 +; +; CC65 runtime: Store eax at (sp),y +; + + .export steaxysp, steax0sp + .importzp sp, sreg + +steax0sp: + ldy #0 +steaxysp: + sta (sp),y + iny + pha + txa + sta (sp),y + iny + lda sreg + sta (sp),y + iny + lda sreg+1 + sta (sp),y + pla + rts + + diff --git a/libsrc/runtime/sub.s b/libsrc/runtime/sub.s new file mode 100644 index 000000000..9108b2710 --- /dev/null +++ b/libsrc/runtime/sub.s @@ -0,0 +1,31 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: sub ints +; + + .export tossuba0, tossubax + .import addysp1 + .importzp sp, ptr1 + +; +; AX = TOS - AX +; + +tossuba0: + ldx #0 +tossubax: + ldy #0 + sta ptr1 + stx ptr1+1 + lda (sp),y ; lo byte + sec + sbc ptr1 + sta ptr1 ; save lo byte + iny + lda (sp),y + sbc ptr1+1 + tax + lda ptr1 + jmp addysp1 ; drop TOS, set condition codes + diff --git a/libsrc/runtime/subeqsp.s b/libsrc/runtime/subeqsp.s new file mode 100644 index 000000000..a6f75798f --- /dev/null +++ b/libsrc/runtime/subeqsp.s @@ -0,0 +1,27 @@ +; +; Ullrich von Bassewitz, 08.10.1998 +; +; CC65 runtime: -= operator for ints on the stack +; + + .export subeq0sp, subeqysp + .importzp sp, tmp1, tmp2 + +subeq0sp: + ldy #0 +subeqysp: + sec + sta tmp1 + stx tmp2 + lda (sp),y + sbc tmp1 + sta (sp),y + pha + iny + lda (sp),y + sbc tmp2 + sta (sp),y + tax + pla + rts + diff --git a/libsrc/runtime/swap.s b/libsrc/runtime/swap.s new file mode 100644 index 000000000..e2f882f7e --- /dev/null +++ b/libsrc/runtime/swap.s @@ -0,0 +1,25 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: swap ax with TOS +; + + .export swapstk + .importzp sp, ptr4 + +swapstk: + sta ptr4 + stx ptr4+1 + ldy #1 ; index + lda (sp),y + tax + lda ptr4+1 + sta (sp),y + dey + lda (sp),y + pha + lda ptr4 + sta (sp),y + pla + rts ; whew! + diff --git a/libsrc/runtime/switch.s b/libsrc/runtime/switch.s new file mode 100644 index 000000000..22dc4b7f3 --- /dev/null +++ b/libsrc/runtime/switch.s @@ -0,0 +1,88 @@ +; +; Ullrich von Bassewitz, 17.08.1998 +; +; CC65 runtime: switch statement with int selector +; + +; Subroutine to handle a switch statement with an int selector. The table +; is located at the return address from the function. It contains the negative +; of the case label count as first word, followed by two words for each case +; label, the first one being the value, and the second one the label to jump +; to in case of a match. The default case is located at the end of the table. + + .export switch + .importzp ptr1, ptr2, ptr3 + +switch: sta ptr1 + stx ptr1+1 ; Save AX + clc + pla + adc #1 + sta ptr2 + pla + adc #0 + sta ptr2+1 ; Get pointer to table + + ldy #0 + lda (ptr2),y + sta ptr3 + iny + lda (ptr2),y + sta ptr3+1 ; Remember the count of labels + + ldy #0 + clc ; Skip the label count + lda ptr2 + adc #2 + sta ptr2 + bcc L2 + inc ptr2+1 + bne L2 ; Branch always + +; Search for the label + +L0: lda (ptr2),y + iny + cmp ptr1 + beq L4 +L1: iny + iny + iny ; Overflow only here + bne L2 + inc ptr2+1 ; Bump high byte + +; Check if there are any labels left + +L2: inc ptr3 + bne L0 + inc ptr3+1 + bne L0 + +; Out of labels + + tya + clc + adc ptr2 + sta ptr2 + bcc L3 + inc ptr2+1 +L3: jmp (ptr2) + + +; Check high byte + +L4: lda (ptr2),y + cmp ptr1+1 + bne L1 + +; Label found + + iny + lda (ptr2),y + sta ptr3 + iny + lda (ptr2),y + sta ptr3+1 + jmp (ptr3) + + diff --git a/libsrc/runtime/test.s b/libsrc/runtime/test.s new file mode 100644 index 000000000..a1dc39668 --- /dev/null +++ b/libsrc/runtime/test.s @@ -0,0 +1,17 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: test int in ax +; + + .export utsta0, utstax, tsta0, tstax + +tsta0: +utsta0: ldx #0 +tstax: +utstax: cpx #0 + bne L1 + cmp #0 +L1: rts + + diff --git a/libsrc/runtime/udiv.s b/libsrc/runtime/udiv.s new file mode 100644 index 000000000..e82d5bf21 --- /dev/null +++ b/libsrc/runtime/udiv.s @@ -0,0 +1,54 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: division for unsigned ints +; + + .export tosudiva0, tosudivax, udiv16 + .import popsreg + .importzp sreg, ptr1, ptr4 + +tosudiva0: + ldx #0 +tosudivax: + sta ptr4 + stx ptr4+1 ; Save right operand + jsr popsreg ; Get left operand + +; Do the division + + jsr udiv16 + +; Result is in sreg, remainder in ptr1 + + lda sreg + ldx sreg+1 + rts + +; Do (sreg/ptr4) -> sreg (see mult-div.s from "The Fridge"). +; This is also the entry point for the signed division + +udiv16: lda #0 + sta ptr1+1 + ldy #16 +L0: asl sreg + rol sreg+1 + rol a + rol ptr1+1 + pha + cmp ptr4 + lda ptr1+1 + sbc ptr4+1 + bcc L1 + sta ptr1+1 + pla + sbc ptr4 + pha + inc sreg +L1: pla + dey + bne L0 + sta ptr1 + rts + + diff --git a/libsrc/runtime/uge.s b/libsrc/runtime/uge.s new file mode 100644 index 000000000..96e949717 --- /dev/null +++ b/libsrc/runtime/uge.s @@ -0,0 +1,20 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare >= for unsigned ints +; + + .export tosuge00, tosugea0, tosugeax + .import tosicmp, booluge + + +tosuge00: + lda #$00 +tosugea0: + ldx #$00 +tosugeax: + jsr tosicmp ; Set flags + jmp booluge ; Convert to boolean + + + diff --git a/libsrc/runtime/ugt.s b/libsrc/runtime/ugt.s new file mode 100644 index 000000000..a8befbfca --- /dev/null +++ b/libsrc/runtime/ugt.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare > for unsigned ints +; + + .export tosugt00, tosugta0, tosugtax + .import tosicmp, boolugt + + +tosugt00: + lda #$00 +tosugta0: + ldx #$00 +tosugtax: + jsr tosicmp ; Set flags + jmp boolugt ; Convert to boolean + diff --git a/libsrc/runtime/ule.s b/libsrc/runtime/ule.s new file mode 100644 index 000000000..5e0817d49 --- /dev/null +++ b/libsrc/runtime/ule.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare <= for unsigned ints +; + + .export tosule00, tosulea0, tosuleax + .import tosicmp, boolule + +tosule00: + lda #$00 +tosulea0: + ldx #$00 +tosuleax: + jsr tosicmp ; Set flags + jmp boolule ; Convert to boolean + + diff --git a/libsrc/runtime/ult.s b/libsrc/runtime/ult.s new file mode 100644 index 000000000..d2895d08d --- /dev/null +++ b/libsrc/runtime/ult.s @@ -0,0 +1,18 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; CC65 runtime: Compare < for unsigned ints +; + + .export tosult00, tosulta0, tosultax + .import tosicmp, boolult, return0 + + +tosult00 = return0 ; This is always false + +tosulta0: + ldx #$00 +tosultax: + jsr tosicmp ; Set flags + jmp boolult ; Convert to boolean + diff --git a/libsrc/runtime/umod.s b/libsrc/runtime/umod.s new file mode 100644 index 000000000..b465d90f0 --- /dev/null +++ b/libsrc/runtime/umod.s @@ -0,0 +1,28 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; CC65 runtime: modulo operation for unsigned ints +; + + .export tosumoda0, tosumodax + .import popsreg, udiv16 + .importzp ptr1, ptr4 + +tosumoda0: + ldx #0 +tosumodax: + sta ptr4 + stx ptr4+1 ; Save right operand + jsr popsreg ; Get right operand + +; Do the division + + jsr udiv16 + +; Result is in sreg, remainder in ptr1 + + lda ptr1 + ldx ptr1+1 + rts + + diff --git a/libsrc/runtime/xor.s b/libsrc/runtime/xor.s new file mode 100644 index 000000000..9027cb1d6 --- /dev/null +++ b/libsrc/runtime/xor.s @@ -0,0 +1,23 @@ +; +; Ullrich von Bassewitz, 05.08.1998 +; +; CC65 runtime: xor on ints +; + + .export tosxora0, tosxorax + .import addysp1 + .importzp sp, tmp1 + +tosxora0: + ldx #$00 +tosxorax: + ldy #0 + eor (sp),y + sta tmp1 + iny + txa + eor (sp),y + tax + lda tmp1 + jmp addysp1 ; drop TOS, set condition codes + diff --git a/samples/.cvsignore b/samples/.cvsignore new file mode 100644 index 000000000..65f1ae933 --- /dev/null +++ b/samples/.cvsignore @@ -0,0 +1,7 @@ +nachtm +hello +sieve +*.map +*.d64 +*.s +*.lbl diff --git a/samples/Makefile b/samples/Makefile new file mode 100644 index 000000000..72074e82b --- /dev/null +++ b/samples/Makefile @@ -0,0 +1,54 @@ +# +# Makefile for cc65 samples +# + +# Enter the target system here +SYS = c64 + +CRT0 = ../lib/$(SYS).o +CLIB = ../lib/$(SYS).lib +CC = ../cc65/cc65 +CL = ../cl65/cl65 +AS = ../binutils/ca65/ca65 +LD = ../binutils/ld65/ld65 +C1541 = c1541 + + +.c.o: + @echo $< + @$(CL) -c -Oirs -t $(SYS) -I../include/ $< + +.s.o: + @echo $< + @$(CL) -c $(basename $<).s + + +.PHONY: all +all: nachtm hello sieve + +nachtm: $(CRT0) nachtm.o $(CLIB) + @$(LD) -t $(SYS) -m nachtm.map -Ln nachtm.lbl -o $@ $^ + +hello: $(CRT0) hello.o $(CLIB) + @$(LD) -t $(SYS) -m hello.map -Ln hello.lbl -o $@ $^ + +sieve: $(CRT0) sieve.o $(CLIB) + @$(LD) -t $(SYS) -m sieve.map -Ln sieve.lbl -o $@ $^ + +.PHONY: disk +disk: c64.d64 + +c64.d64: all + $(C1541) < c1541.rsp + +.PHONY: clean +clean: + rm -f *~ *.map *.o *.s *.lbl + +.PHONY: zap +zap: clean + rm -f nachtm hello + + + + diff --git a/samples/c1541.rsp b/samples/c1541.rsp new file mode 100644 index 000000000..0c46decc9 --- /dev/null +++ b/samples/c1541.rsp @@ -0,0 +1,9 @@ +att c64.d64 + +delete nachtm +delete hello +write nachtm +write hello +quit + + diff --git a/samples/hello.c b/samples/hello.c new file mode 100644 index 000000000..cdbfab73a --- /dev/null +++ b/samples/hello.c @@ -0,0 +1,82 @@ +/* + * Fancy hello world program using cc65. + * + * Ullrich von Bassewitz (ullrich@von-bassewitz.de) + * + */ + + + +#include +#include +#include +#include + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +static const char Text [] = "Hello world!"; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int main (void) +{ + unsigned char XSize, YSize; + + /* Set screen colors, hide the cursor */ + textcolor (COLOR_WHITE); + bordercolor (COLOR_BLACK); + bgcolor (COLOR_BLACK); + cursor (0); + + /* Clear the screen, put cursor in upper left corner */ + clrscr (); + + /* Ask for the screen size */ + screensize (&XSize, &YSize); + + /* Draw a border around the screen */ + + /* Top line */ + cputc (CH_ULCORNER); + chline (XSize - 2); + cputc (CH_URCORNER); + + /* Vertical line, left side */ + cvlinexy (0, 1, YSize - 2); + + /* Bottom line */ + cputc (CH_LLCORNER); + chline (XSize - 2); + cputc (CH_LRCORNER); + + /* Vertical line, right side */ + cvlinexy (XSize - 1, 1, YSize - 2); + + /* Write the greeting in the mid of the screen */ + gotoxy ((XSize - strlen (Text)) / 2, YSize / 2); + cprintf ("%s", Text); + + /* Wait for the user to press a key */ + (void) cgetc (); + + /* Clear the screen again */ + clrscr (); + + /* Done */ + return EXIT_SUCCESS; +} + + + diff --git a/samples/nachtm.c b/samples/nachtm.c new file mode 100644 index 000000000..816aa9fd7 --- /dev/null +++ b/samples/nachtm.c @@ -0,0 +1,1185 @@ +/* + * "Eine kleine Nachtmusik" by Wolfgang Amadeus Mozart, KV 525 + * + * First version in 1987 by + * Joachim von Bassewitz (joachim@von-bassewitz.de) and + * Ullrich von Bassewitz (ullrich@von-bassewitz.de). + * + * C conversion in 1998 by + * Ullrich von Bassewitz (ullrich@von-bassewitz.de) + * + */ + + + +#include +#include +#include +#include +#include + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Tables with voice data. + * + * Bit Description + * ------------------------------------------- + * 15 Pause bit. + * 12-14 Octave + * 8-11 Tone (index into frequency table) + * 7 Unused. Was thought as a control bit in the original version to + * change SID parameters, but this was never implemented. + * 0-6 Length of the tone in ticks. + * + */ + + + +static int Voice1 [] = { + 0x5708,0x8004,0x5204,0x5708,0x8004,0x5204,0x5704,0x5204,0x5704,0x5B04, + 0x6208,0x8008,0x6008,0x8004,0x5904,0x6008,0x8004,0x5904,0x6004,0x5904, + 0x5604,0x5904,0x5208,0x8008,0x5704,0x8004,0x570C,0x5B01,0x5B01,0x5B01, + 0x5B01,0x5904,0x5704,0x5704,0x5604,0x560C,0x5901,0x5901,0x5901,0x5901, + 0x6004,0x5604,0x5901,0x5901,0x5901,0x5901,0x5704,0x570C,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5904,0x5704,0x5701,0x5701,0x5701,0x5701,0x5604,0x560C, + 0x5901,0x5901,0x5901,0x5901,0x6004,0x5604,0x5704,0x5704,0x5601,0x5601, + 0x5601,0x5601,0x5401,0x5401,0x5602,0x5704,0x5704,0x5901,0x5901,0x5901, + 0x5901,0x5701,0x5701,0x5902,0x5B04,0x5B04,0x6001,0x6001,0x6001,0x6001, + 0x5B01,0x5B01,0x6001,0x6001,0x6208,0x8008,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5410,0x5008,0x5008,0x4B08,0x4B08,0x4908,0x4908,0x4701, + 0x4701,0x4701,0x4701,0x4604,0x4404,0x4604,0x4704,0x8004,0x4904,0x8004, + 0x4B04,0x800C,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5410,0x5201, + 0x5201,0x5201,0x5201,0x5004,0x5004,0x5004,0x5001,0x5001,0x5001,0x5001, + 0x4B04,0x4B04,0x4B04,0x4B01,0x4B01,0x4B01,0x4B01,0x4904,0x4904,0x4904, + 0x4701,0x4701,0x4701,0x4701,0x4601,0x4601,0x4601,0x4601,0x4401,0x4401, + 0x4401,0x4401,0x4604,0x4701,0x4701,0x4701,0x4701,0x4701,0x4701,0x4701, + 0x4701,0x4701,0x4701,0x4701,0x4701,0x4701,0x4701,0x4701,0x4701,0x4704, + 0x4701,0x4701,0x4601,0x4701,0x4901,0x4901,0x4901,0x4901,0x4604,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B04,0x4B01,0x4B01,0x4901,0x4B01, + 0x5001,0x5001,0x5001,0x5001,0x4904,0x5210,0x5408,0x5608,0x5708,0x5908, + 0x5B08,0x6108,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x5904,0x6101,0x6101,0x6101,0x6101,0x6101, + 0x6101,0x5902,0x6101,0x6101,0x6101,0x6101,0x6101,0x6101,0x5902,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x5904,0x6101,0x6101,0x6101,0x6101,0x6101,0x6101,0x5902,0x6101, + 0x6101,0x6101,0x6101,0x6101,0x6101,0x5902,0x6204,0x6208,0x6208,0x6208, + 0x6201,0x6201,0x6201,0x6201,0x6204,0x6208,0x6208,0x6208,0x6204,0x6104, + 0x5904,0x6204,0x5904,0x6004,0x5904,0x6204,0x5904,0x6104,0x4904,0x4904, + 0x4904,0x4908,0x8008,0x590A,0x5702,0x5602,0x5402,0x5204,0x8004,0x5B04, + 0x8004,0x5704,0x8004,0x5404,0x8004,0x5B04,0x800C,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5401,0x5401,0x5201, + 0x5201,0x5101,0x5101,0x4B04,0x8004,0x5704,0x8004,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5408,0x8008,0x5901,0x5901,0x5901,0x5901,0x5901, + 0x5901,0x5901,0x5901,0x5901,0x5901,0x5701,0x5701,0x5601,0x5601,0x5401, + 0x5401,0x5204,0x8004,0x5B04,0x8004,0x5704,0x8004,0x5404,0x8004,0x5908, + 0x8004,0x5704,0x6104,0x6204,0x8004,0x5B04,0x5B01,0x5B01,0x5B01,0x5B01, + 0x5904,0x8004,0x5104,0x5208,0x8004,0x5904,0x6201,0x6201,0x6201,0x6201, + 0x6101,0x6101,0x6101,0x6101,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5B01, + 0x5B01,0x5B01,0x5B01,0x5904,0x8004,0x5904,0x5904,0x5904,0x5904,0x5904, + 0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x8004,0x5904,0x6204,0x6104,0x5B04, + 0x5904,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x8004,0x5904,0x5904,0x5904, + 0x5904,0x5904,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x8008,0x5B0A,0x5901, + 0x5901,0x5701,0x5701,0x5601,0x5601,0x5708,0x8008,0x590A,0x5701,0x5701, + 0x5601,0x5601,0x5401,0x5401,0x5608,0x8008,0x5B01,0x5B01,0x5B01,0x5B01, + 0x6101,0x6101,0x6201,0x6201,0x6104,0x5B04,0x5B01,0x5B01,0x5B01,0x5B01, + 0x5904,0x5604,0x5904,0x5901,0x5901,0x5901,0x5901,0x5704,0x5604,0x5404, + 0x5208,0x8004,0x5904,0x6201,0x6201,0x6201,0x6201,0x6101,0x6101,0x6101, + 0x6101,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5B01,0x5B01,0x5B01,0x5B01, + 0x5904,0x8004,0x5904,0x5904,0x5904,0x5904,0x5904,0x5B01,0x5B01,0x5B01, + 0x5B01,0x5904,0x8004,0x5904,0x6201,0x6201,0x6201,0x6201,0x6101,0x6101, + 0x6101,0x6101,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5B01,0x5B01,0x5B01, + 0x5B01,0x5904,0x8004,0x5904,0x5904,0x5904,0x5904,0x5904,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5904,0x8008,0x5B0A,0x5901,0x5901,0x5701,0x5701,0x5601, + 0x5601,0x5708,0x8008,0x590A,0x5701,0x5701,0x5601,0x5601,0x5401,0x5401, + 0x5608,0x8008,0x5B01,0x5B01,0x5B01,0x5B01,0x6101,0x6101,0x6201,0x6201, + 0x6104,0x5B04,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5604,0x5904,0x5901, + 0x5901,0x5901,0x5901,0x5704,0x5604,0x5404,0x5204,0x4904,0x4B04,0x5104, + 0x5204,0x5204,0x5401,0x5401,0x5401,0x5401,0x5201,0x5201,0x5401,0x5401, + 0x5604,0x5104,0x5204,0x5404,0x5604,0x5604,0x5701,0x5701,0x5701,0x5701, + 0x5601,0x5601,0x5701,0x5701,0x5904,0x5904,0x5A04,0x5802,0x5A02,0x5B08, + 0x8008,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x5404,0x5201,0x5201,0x5201,0x5201,0x5101,0x5101, + 0x5101,0x5101,0x5B01,0x5B01,0x5B01,0x5B01,0x5901,0x5901,0x5901,0x5901, + 0x6204,0x8004,0x6604,0x8004,0x6204,0x800C,0x5708,0x8004,0x5204,0x5708, + 0x8004,0x5204,0x5704,0x5204,0x5704,0x5B04,0x6208,0x8008,0x6008,0x8004, + 0x5904,0x6008,0x8004,0x5904,0x6004,0x5904,0x5604,0x5904,0x5208,0x8008, + 0x5704,0x8004,0x570C,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5704,0x5704, + 0x5604,0x560C,0x5901,0x5901,0x5901,0x5901,0x6004,0x5604,0x5901,0x5901, + 0x5901,0x5901,0x5704,0x570C,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5704, + 0x5701,0x5701,0x5701,0x5701,0x5604,0x560C,0x5901,0x5901,0x5901,0x5901, + 0x6004,0x5604,0x5704,0x5704,0x5601,0x5601,0x5601,0x5601,0x5401,0x5401, + 0x5602,0x5704,0x5704,0x5901,0x5901,0x5901,0x5901,0x5701,0x5701,0x5902, + 0x5B04,0x5B04,0x6001,0x6001,0x6001,0x6001,0x5B01,0x5B01,0x6001,0x6001, + 0x6208,0x8008,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5410,0x5008, + 0x5008,0x4B08,0x4B08,0x4908,0x4908,0x4701,0x4701,0x4701,0x4701,0x4604, + 0x4404,0x4604,0x4704,0x8004,0x4904,0x8004,0x4B04,0x800C,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5410,0x5201,0x5201,0x5201,0x5201,0x5004, + 0x5004,0x5004,0x5001,0x5001,0x5001,0x5001,0x4B04,0x4B04,0x4B04,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4904,0x4904,0x4904,0x4701,0x4701,0x4701,0x4701, + 0x4601,0x4601,0x4601,0x4601,0x4401,0x4401,0x4401,0x4401,0x4604,0x4701, + 0x4701,0x4701,0x4701,0x4701,0x4701,0x4701,0x4701,0x4701,0x4701,0x4701, + 0x4701,0x4701,0x4701,0x4701,0x4701,0x4704,0x4701,0x4701,0x4601,0x4701, + 0x4901,0x4901,0x4901,0x4901,0x4604,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B04,0x4B01,0x4B01,0x4901,0x4B01,0x5001,0x5001,0x5001,0x5001, + 0x4904,0x5210,0x5408,0x5608,0x5708,0x5908,0x5B08,0x6108,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x5904,0x6101,0x6101,0x6101,0x6101,0x6101,0x6101,0x5902,0x6101,0x6101, + 0x6101,0x6101,0x6101,0x6101,0x5902,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x5904,0x6101,0x6101, + 0x6101,0x6101,0x6101,0x6101,0x5902,0x6101,0x6101,0x6101,0x6101,0x6101, + 0x6101,0x5902,0x6204,0x6208,0x6208,0x6208,0x6201,0x6201,0x6201,0x6201, + 0x6204,0x6208,0x6208,0x6208,0x6204,0x6104,0x5904,0x6204,0x5904,0x6004, + 0x5904,0x6204,0x5904,0x6104,0x4904,0x4904,0x4904,0x4908,0x8008,0x590A, + 0x5702,0x5602,0x5402,0x5204,0x8004,0x5B04,0x8004,0x5704,0x8004,0x5404, + 0x8004,0x5B04,0x800C,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5401,0x5401,0x5201,0x5201,0x5101,0x5101,0x4B04, + 0x8004,0x5704,0x8004,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5408, + 0x8008,0x5901,0x5901,0x5901,0x5901,0x5901,0x5901,0x5901,0x5901,0x5901, + 0x5901,0x5701,0x5701,0x5601,0x5601,0x5401,0x5401,0x5204,0x8004,0x5B04, + 0x8004,0x5704,0x8004,0x5404,0x8004,0x5908,0x8004,0x5704,0x6104,0x6204, + 0x8004,0x5B04,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x8004,0x5104,0x5208, + 0x8004,0x5904,0x6201,0x6201,0x6201,0x6201,0x6101,0x6101,0x6101,0x6101, + 0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5B01,0x5B01,0x5B01,0x5B01,0x5904, + 0x8004,0x5904,0x5904,0x5904,0x5904,0x5904,0x5B01,0x5B01,0x5B01,0x5B01, + 0x5904,0x8004,0x5904,0x6204,0x6104,0x5B04,0x5904,0x5B01,0x5B01,0x5B01, + 0x5B01,0x5904,0x8004,0x5904,0x5904,0x5904,0x5904,0x5904,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5904,0x8008,0x5B0A,0x5901,0x5901,0x5701,0x5701,0x5601, + 0x5601,0x5708,0x8008,0x590A,0x5701,0x5701,0x5601,0x5601,0x5401,0x5401, + 0x5608,0x8008,0x5B01,0x5B01,0x5B01,0x5B01,0x6101,0x6101,0x6201,0x6201, + 0x6104,0x5B04,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5604,0x5904,0x5901, + 0x5901,0x5901,0x5901,0x5704,0x5604,0x5404,0x5208,0x8004,0x5904,0x6201, + 0x6201,0x6201,0x6201,0x6101,0x6101,0x6101,0x6101,0x5B01,0x5B01,0x5B01, + 0x5B01,0x5904,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x8004,0x5904,0x5904, + 0x5904,0x5904,0x5904,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x8004,0x5904, + 0x6201,0x6201,0x6201,0x6201,0x6101,0x6101,0x6101,0x6101,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5904,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x8004,0x5904, + 0x5904,0x5904,0x5904,0x5904,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x8008, + 0x5B0A,0x5901,0x5901,0x5701,0x5701,0x5601,0x5601,0x5708,0x8008,0x590A, + 0x5701,0x5701,0x5601,0x5601,0x5401,0x5401,0x5608,0x8008,0x5B01,0x5B01, + 0x5B01,0x5B01,0x6101,0x6101,0x6201,0x6201,0x6104,0x5B04,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5904,0x5604,0x5904,0x5901,0x5901,0x5901,0x5901,0x5704, + 0x5604,0x5404,0x5204,0x4904,0x4B04,0x5104,0x5204,0x5204,0x5401,0x5401, + 0x5401,0x5401,0x5201,0x5201,0x5401,0x5401,0x5604,0x5104,0x5204,0x5404, + 0x5604,0x5604,0x5701,0x5701,0x5701,0x5701,0x5601,0x5601,0x5701,0x5701, + 0x5904,0x5904,0x5A04,0x5802,0x5A02,0x5B08,0x8008,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x5404, + 0x5201,0x5201,0x5201,0x5201,0x5101,0x5101,0x5101,0x5101,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5901,0x5901,0x5901,0x5901,0x6204,0x8004,0x6604,0x8004, + 0x6204,0x800C,0x5208,0x8004,0x4904,0x5208,0x8004,0x4904,0x5204,0x4904, + 0x5204,0x5604,0x5908,0x8008,0x5908,0x8004,0x5604,0x5908,0x8004,0x5604, + 0x5904,0x5604,0x5304,0x5604,0x4B08,0x8014,0x5704,0x6001,0x6001,0x6001, + 0x6001,0x5B01,0x5B01,0x5B01,0x5B01,0x5901,0x5901,0x5901,0x5901,0x5704, + 0x5901,0x5901,0x5901,0x5901,0x5704,0x8004,0x5704,0x5704,0x5704,0x5704, + 0x5704,0x5901,0x5901,0x5901,0x5901,0x5704,0x8004,0x5704,0x6001,0x6001, + 0x6001,0x6001,0x5B01,0x5B01,0x5B01,0x5B01,0x5901,0x5901,0x5901,0x5901, + 0x5704,0x5901,0x5901,0x5901,0x5901,0x5704,0x8004,0x5704,0x5704,0x5704, + 0x5704,0x5704,0x5901,0x5901,0x5901,0x5901,0x5704,0x8004,0x5704,0x6001, + 0x6001,0x6001,0x6001,0x5B01,0x5B01,0x5B01,0x5B01,0x5901,0x5901,0x5901, + 0x5901,0x5704,0x5901,0x5901,0x5901,0x5901,0x5804,0x8004,0x5804,0x5804, + 0x5804,0x5804,0x5804,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x8004,0x5904, + 0x6001,0x6001,0x6001,0x6001,0x5A01,0x5A01,0x5A01,0x5A01,0x5901,0x5901, + 0x5901,0x5901,0x5704,0x5701,0x5701,0x5701,0x5701,0x5604,0x8004,0x5604, + 0x5604,0x5604,0x5604,0x5604,0x5901,0x5901,0x5901,0x5901,0x5704,0x8004, + 0x5304,0x5704,0x5504,0x5304,0x5204,0x5201,0x5201,0x5201,0x5201,0x5104, + 0x8004,0x5104,0x5104,0x5104,0x5104,0x5104,0x5401,0x5401,0x5401,0x5401, + 0x5204,0x8004,0x4201,0x4201,0x4201,0x4201,0x4401,0x4401,0x4401,0x4401, + 0x4601,0x4601,0x4601,0x4601,0x4701,0x4701,0x4701,0x4701,0x4901,0x4901, + 0x4901,0x4901,0x5001,0x5001,0x5001,0x5001,0x4A04,0x8004,0x4601,0x4601, + 0x4601,0x4601,0x4701,0x4701,0x4701,0x4701,0x4901,0x4901,0x4901,0x4901, + 0x4A01,0x4A01,0x4A01,0x4A01,0x5101,0x5101,0x5101,0x5101,0x5401,0x5401, + 0x5401,0x5401,0x5204,0x8004,0x5201,0x5201,0x5201,0x5201,0x5401,0x5401, + 0x5401,0x5401,0x5601,0x5601,0x5601,0x5601,0x5701,0x5701,0x5701,0x5701, + 0x5901,0x5901,0x5901,0x5901,0x5A01,0x5A01,0x5A01,0x5A01,0x5A01,0x5A01, + 0x5A01,0x5A01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01, + 0x6001,0x6001,0x6001,0x6001,0x6001,0x6001,0x6001,0x6001,0x6108,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6210,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5401,0x5401,0x5602,0x5708,0x8004,0x5204, + 0x5708,0x8004,0x5204,0x5704,0x5204,0x5704,0x5B04,0x6208,0x8008,0x6008, + 0x8004,0x5904,0x6008,0x8004,0x5904,0x6004,0x5904,0x5604,0x5904,0x5208, + 0x8008,0x5704,0x8004,0x570C,0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5704, + 0x5701,0x5701,0x5701,0x5701,0x5604,0x560C,0x5901,0x5901,0x5901,0x5901, + 0x6004,0x5604,0x5901,0x5901,0x5901,0x5901,0x5704,0x570C,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5904,0x5704,0x5701,0x5701,0x5701,0x5701,0x5604,0x560C, + 0x5901,0x5901,0x5901,0x5901,0x6004,0x5604,0x5704,0x5704,0x5601,0x5601, + 0x5601,0x5601,0x5401,0x5401,0x5602,0x5704,0x5704,0x5901,0x5901,0x5901, + 0x5901,0x5701,0x5701,0x5902,0x5B04,0x5B04,0x6001,0x6001,0x6001,0x6001, + 0x5B01,0x5B01,0x6001,0x6001,0x6208,0x8008,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5410,0x5008,0x5008,0x4B08,0x4B08,0x4908,0x4908,0x4701, + 0x4701,0x4701,0x4701,0x4604,0x4404,0x4604,0x4704,0x8004,0x4904,0x8004, + 0x4B04,0x800C,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5410,0x5201, + 0x5201,0x5201,0x5201,0x5004,0x5004,0x5004,0x5001,0x5001,0x5001,0x5001, + 0x4B04,0x4B04,0x4B04,0x4B01,0x4B01,0x4B01,0x4B01,0x4904,0x4904,0x4904, + 0x4701,0x4701,0x4701,0x4701,0x4604,0x4404,0x4604,0x4714,0x4701,0x4701, + 0x4601,0x4701,0x4901,0x4901,0x4901,0x4901,0x4604,0x4B14,0x4B01,0x4B01, + 0x4901,0x4B01,0x5001,0x5001,0x5001,0x5001,0x4904,0x5210,0x5408,0x5608, + 0x5708,0x5908,0x5B08,0x5108,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x5904,0x6101,0x6101,0x6101, + 0x6101,0x6101,0x6101,0x5902,0x5101,0x5101,0x5101,0x5101,0x5101,0x5101, + 0x5902,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x5904,0x6101,0x6101,0x6101,0x6101,0x6101,0x6101, + 0x5902,0x5101,0x5101,0x5101,0x5101,0x5101,0x5101,0x5902,0x6204,0x5904, + 0x6104,0x5904,0x6204,0x5904,0x6104,0x5904,0x6204,0x4204,0x4204,0x4204, + 0x4208,0x8008,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5001,0x5001,0x4B01,0x4B01,0x4901,0x4901,0x4704,0x8004, + 0x5404,0x8004,0x5004,0x8004,0x4904,0x8004,0x5204,0x800C,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5901,0x5901, + 0x5701,0x5701,0x5601,0x5601,0x5404,0x8004,0x6004,0x8004,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5B01,0x5B01,0x5908,0x8008,0x8004,0x6204,0x6204,0x6204, + 0x6204,0x6204,0x6204,0x6204,0x6204,0x6204,0x6204,0x6204,0x6201,0x6201, + 0x6201,0x6201,0x6001,0x6001,0x6001,0x6001,0x5901,0x5901,0x5901,0x5901, + 0x5604,0x5601,0x5601,0x5601,0x5601,0x5704,0x8004,0x5404,0x5401,0x5401, + 0x5401,0x5401,0x5204,0x8004,0x4604,0x4708,0x8004,0x5204,0x5701,0x5701, + 0x5701,0x5701,0x5601,0x5601,0x5601,0x5601,0x5401,0x5401,0x5401,0x5401, + 0x5204,0x5401,0x5401,0x5401,0x5401,0x5204,0x8004,0x5204,0x5204,0x5204, + 0x5204,0x5204,0x5401,0x5401,0x5401,0x5401,0x5204,0x8004,0x5204,0x5704, + 0x5604,0x5404,0x5204,0x5401,0x5401,0x5401,0x5401,0x5204,0x8004,0x5204, + 0x5204,0x5204,0x5204,0x5204,0x5401,0x5401,0x5401,0x5401,0x5204,0x8008, + 0x540A,0x5201,0x5201,0x5001,0x5001,0x4B01,0x4B01,0x5008,0x8008,0x520A, + 0x5001,0x5001,0x4B01,0x4B01,0x4901,0x4901,0x4B08,0x8008,0x5401,0x5401, + 0x5401,0x5401,0x5601,0x5601,0x5701,0x5701,0x5604,0x5404,0x5401,0x5401, + 0x5401,0x5401,0x5204,0x4B04,0x5204,0x5201,0x5201,0x5201,0x5201,0x5004, + 0x4B04,0x4904,0x4708,0x8004,0x5204,0x5701,0x5701,0x5701,0x5701,0x5601, + 0x5601,0x5601,0x5601,0x5401,0x5401,0x5401,0x5401,0x5204,0x5401,0x5401, + 0x5401,0x5401,0x5204,0x8004,0x5204,0x5204,0x5204,0x5204,0x5204,0x5401, + 0x5401,0x5401,0x5401,0x5204,0x8004,0x5204,0x5701,0x5701,0x5701,0x5701, + 0x5601,0x5601,0x5601,0x5601,0x5401,0x5401,0x5401,0x5401,0x5204,0x5401, + 0x5401,0x5401,0x5401,0x5204,0x8004,0x5204,0x5204,0x5204,0x5204,0x5204, + 0x5401,0x5401,0x5401,0x5401,0x5204,0x8008,0x640A,0x6201,0x6201,0x6001, + 0x6001,0x5B01,0x5B01,0x6008,0x8008,0x620A,0x6001,0x6001,0x5B01,0x5B01, + 0x5901,0x5901,0x5B08,0x8008,0x5401,0x5401,0x5401,0x5401,0x5601,0x5601, + 0x5701,0x5701,0x5604,0x5404,0x5204,0x5704,0x5B04,0x6204,0x6201,0x6201, + 0x6201,0x6201,0x6004,0x5B04,0x5904,0x5704,0x4204,0x4404,0x4604,0x4704, + 0x4704,0x4901,0x4901,0x4901,0x4901,0x4701,0x4701,0x4901,0x4901,0x4B04, + 0x4604,0x4704,0x4904,0x4B04,0x4B04,0x5001,0x5001,0x5001,0x5001,0x4B01, + 0x4B01,0x5001,0x5001,0x5204,0x5204,0x5301,0x5301,0x5301,0x5301,0x5101, + 0x5101,0x5301,0x5301,0x5408,0x8008,0x440C,0x4904,0x4704,0x4604,0x4404, + 0x4204,0x5201,0x5201,0x5201,0x5201,0x5101,0x5101,0x5101,0x5101,0x5001, + 0x5001,0x5001,0x5001,0x4B01,0x4B01,0x4B01,0x4B01,0x5201,0x5201,0x5201, + 0x5201,0x5101,0x5101,0x5101,0x5101,0x5001,0x5001,0x5001,0x5001,0x4B04, + 0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401, + 0x4401,0x4401,0x4904,0x4701,0x4701,0x4701,0x4701,0x4601,0x4601,0x4601, + 0x4601,0x4401,0x4401,0x4401,0x4401,0x4204,0x5201,0x5201,0x5201,0x5201, + 0x5401,0x5401,0x5401,0x5401,0x5601,0x5601,0x5601,0x5601,0x5701,0x5701, + 0x5701,0x5701,0x5201,0x5201,0x5201,0x5201,0x5401,0x5401,0x5401,0x5401, + 0x5601,0x5601,0x5601,0x5601,0x5704,0x5908,0x8008,0x6208,0x8008,0x5708, + 0x8004,0x5204,0x4B04,0x4704,0x4B04,0x5204,0x5704,0x5204,0x5704,0x5B04, + 0x6208,0x5608,0x5708,0x8004,0x5204,0x4B04,0x4704,0x4B04,0x5204,0x5704, + 0x5204,0x5704,0x5B04,0x6208,0x5608,0x5708,0x8008,0x5708,0x8008,0x5708, + 0x4706,0x4702,0x4708,0x8008,0x5208,0x8004,0x4904,0x5208,0x8004,0x4904, + 0x5204,0x4904,0x5204,0x5604,0x5908,0x8008,0x5908,0x8004,0x5604,0x5908, + 0x8004,0x5604,0x5904,0x5604,0x5304,0x5604,0x4B08,0x8014,0x5704,0x6001, + 0x6001,0x6001,0x6001,0x5B01,0x5B01,0x5B01,0x5B01,0x5901,0x5901,0x5901, + 0x5901,0x5704,0x5901,0x5901,0x5901,0x5901,0x5704,0x8004,0x5704,0x5704, + 0x5704,0x5704,0x5704,0x5901,0x5901,0x5901,0x5901,0x5704,0x8004,0x5704, + 0x6001,0x6001,0x6001,0x6001,0x5B01,0x5B01,0x5B01,0x5B01,0x5901,0x5901, + 0x5901,0x5901,0x5704,0x5901,0x5901,0x5901,0x5901,0x5704,0x8004,0x5704, + 0x5704,0x5704,0x5704,0x5704,0x5901,0x5901,0x5901,0x5901,0x5704,0x8004, + 0x5704,0x6001,0x6001,0x6001,0x6001,0x5B01,0x5B01,0x5B01,0x5B01,0x5901, + 0x5901,0x5901,0x5901,0x5704,0x5901,0x5901,0x5901,0x5901,0x5804,0x8004, + 0x5804,0x5804,0x5804,0x5804,0x5804,0x5B01,0x5B01,0x5B01,0x5B01,0x5904, + 0x8004,0x5904,0x6001,0x6001,0x6001,0x6001,0x5A01,0x5A01,0x5A01,0x5A01, + 0x5901,0x5901,0x5901,0x5901,0x5704,0x5701,0x5701,0x5701,0x5701,0x5604, + 0x8004,0x5604,0x5604,0x5604,0x5604,0x5604,0x5901,0x5901,0x5901,0x5901, + 0x5704,0x8004,0x5304,0x5704,0x5504,0x5304,0x5204,0x5201,0x5201,0x5201, + 0x5201,0x5104,0x8004,0x5104,0x5104,0x5104,0x5104,0x5104,0x5401,0x5401, + 0x5401,0x5401,0x5204,0x8004,0x4201,0x4201,0x4201,0x4201,0x4401,0x4401, + 0x4401,0x4401,0x4601,0x4601,0x4601,0x4601,0x4701,0x4701,0x4701,0x4701, + 0x4901,0x4901,0x4901,0x4901,0x5001,0x5001,0x5001,0x5001,0x4A04,0x8004, + 0x4601,0x4601,0x4601,0x4601,0x4701,0x4701,0x4701,0x4701,0x4901,0x4901, + 0x4901,0x4901,0x4A01,0x4A01,0x4A01,0x4A01,0x5101,0x5101,0x5101,0x5101, + 0x5401,0x5401,0x5401,0x5401,0x5204,0x8004,0x5201,0x5201,0x5201,0x5201, + 0x5401,0x5401,0x5401,0x5401,0x5601,0x5601,0x5601,0x5601,0x5701,0x5701, + 0x5701,0x5701,0x5901,0x5901,0x5901,0x5901,0x5A01,0x5A01,0x5A01,0x5A01, + 0x5A01,0x5A01,0x5A01,0x5A01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01, + 0x5B01,0x5B01,0x6001,0x6001,0x6001,0x6001,0x6001,0x6001,0x6001,0x6001, + 0x6108,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6210,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5401,0x5401,0x5602,0x5708, + 0x8004,0x5204,0x5708,0x8004,0x5204,0x5704,0x5204,0x5704,0x5B04,0x6208, + 0x8008,0x6008,0x8004,0x5904,0x6008,0x8004,0x5904,0x6004,0x5904,0x5604, + 0x5904,0x5208,0x8008,0x5704,0x8004,0x570C,0x5B01,0x5B01,0x5B01,0x5B01, + 0x5904,0x5704,0x5701,0x5701,0x5701,0x5701,0x5604,0x560C,0x5901,0x5901, + 0x5901,0x5901,0x6004,0x5604,0x5901,0x5901,0x5901,0x5901,0x5704,0x570C, + 0x5B01,0x5B01,0x5B01,0x5B01,0x5904,0x5704,0x5701,0x5701,0x5701,0x5701, + 0x5604,0x560C,0x5901,0x5901,0x5901,0x5901,0x6004,0x5604,0x5704,0x5704, + 0x5601,0x5601,0x5601,0x5601,0x5401,0x5401,0x5602,0x5704,0x5704,0x5901, + 0x5901,0x5901,0x5901,0x5701,0x5701,0x5902,0x5B04,0x5B04,0x6001,0x6001, + 0x6001,0x6001,0x5B01,0x5B01,0x6001,0x6001,0x6208,0x8008,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5410,0x5008,0x5008,0x4B08,0x4B08,0x4908, + 0x4908,0x4701,0x4701,0x4701,0x4701,0x4604,0x4404,0x4604,0x4704,0x8004, + 0x4904,0x8004,0x4B04,0x800C,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5410,0x5201,0x5201,0x5201,0x5201,0x5004,0x5004,0x5004,0x5001,0x5001, + 0x5001,0x5001,0x4B04,0x4B04,0x4B04,0x4B01,0x4B01,0x4B01,0x4B01,0x4904, + 0x4904,0x4904,0x4701,0x4701,0x4701,0x4701,0x4604,0x4404,0x4604,0x4714, + 0x4701,0x4701,0x4601,0x4701,0x4901,0x4901,0x4901,0x4901,0x4604,0x4B14, + 0x4B01,0x4B01,0x4901,0x4B01,0x5001,0x5001,0x5001,0x5001,0x4904,0x5210, + 0x5408,0x5608,0x5708,0x5908,0x5B08,0x5108,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x5904,0x6101, + 0x6101,0x6101,0x6101,0x6101,0x6101,0x5902,0x5101,0x5101,0x5101,0x5101, + 0x5101,0x5101,0x5902,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201,0x6201, + 0x6201,0x6201,0x6201,0x6201,0x6201,0x5904,0x6101,0x6101,0x6101,0x6101, + 0x6101,0x6101,0x5902,0x5101,0x5101,0x5101,0x5101,0x5101,0x5101,0x5902, + 0x6204,0x5904,0x6104,0x5904,0x6204,0x5904,0x6104,0x5904,0x6204,0x4204, + 0x4204,0x4204,0x4208,0x8008,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5201,0x5001,0x5001,0x4B01,0x4B01,0x4901,0x4901, + 0x4704,0x8004,0x5404,0x8004,0x5004,0x8004,0x4904,0x8004,0x5204,0x800C, + 0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01, + 0x5901,0x5901,0x5701,0x5701,0x5601,0x5601,0x5404,0x8004,0x6004,0x8004, + 0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01, + 0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5B01,0x5908,0x8008,0x8004,0x6204, + 0x6204,0x6204,0x6204,0x6204,0x6204,0x6204,0x6204,0x6204,0x6204,0x6204, + 0x6201,0x6201,0x6201,0x6201,0x6001,0x6001,0x6001,0x6001,0x5901,0x5901, + 0x5901,0x5901,0x5604,0x5601,0x5601,0x5601,0x5601,0x5704,0x8004,0x5404, + 0x5401,0x5401,0x5401,0x5401,0x5204,0x8004,0x4604,0x4708,0x8004,0x5204, + 0x5701,0x5701,0x5701,0x5701,0x5601,0x5601,0x5601,0x5601,0x5401,0x5401, + 0x5401,0x5401,0x5204,0x5401,0x5401,0x5401,0x5401,0x5204,0x8004,0x5204, + 0x5204,0x5204,0x5204,0x5204,0x5401,0x5401,0x5401,0x5401,0x5204,0x8004, + 0x5204,0x5704,0x5604,0x5404,0x5204,0x5401,0x5401,0x5401,0x5401,0x5204, + 0x8004,0x5204,0x5204,0x5204,0x5204,0x5204,0x5401,0x5401,0x5401,0x5401, + 0x5204,0x8008,0x540A,0x5201,0x5201,0x5001,0x5001,0x4B01,0x4B01,0x5008, + 0x8008,0x520A,0x5001,0x5001,0x4B01,0x4B01,0x4901,0x4901,0x4B08,0x8008, + 0x5401,0x5401,0x5401,0x5401,0x5601,0x5601,0x5701,0x5701,0x5604,0x5404, + 0x5401,0x5401,0x5401,0x5401,0x5204,0x4B04,0x5204,0x5201,0x5201,0x5201, + 0x5201,0x5004,0x4B04,0x4904,0x4708,0x8004,0x5204,0x5701,0x5701,0x5701, + 0x5701,0x5601,0x5601,0x5601,0x5601,0x5401,0x5401,0x5401,0x5401,0x5204, + 0x5401,0x5401,0x5401,0x5401,0x5204,0x8004,0x5204,0x5204,0x5204,0x5204, + 0x5204,0x5401,0x5401,0x5401,0x5401,0x5204,0x8004,0x5204,0x5701,0x5701, + 0x5701,0x5701,0x5601,0x5601,0x5601,0x5601,0x5401,0x5401,0x5401,0x5401, + 0x5204,0x5401,0x5401,0x5401,0x5401,0x5204,0x8004,0x5204,0x5204,0x5204, + 0x5204,0x5204,0x5401,0x5401,0x5401,0x5401,0x5204,0x8008,0x640A,0x6201, + 0x6201,0x6001,0x6001,0x5B01,0x5B01,0x6008,0x8008,0x620A,0x6001,0x6001, + 0x5B01,0x5B01,0x5901,0x5901,0x5B08,0x8008,0x5401,0x5401,0x5401,0x5401, + 0x5601,0x5601,0x5701,0x5701,0x5604,0x5404,0x5204,0x5704,0x5B04,0x6204, + 0x6201,0x6201,0x6201,0x6201,0x6004,0x5B04,0x5904,0x5704,0x4204,0x4404, + 0x4604,0x4704,0x4704,0x4901,0x4901,0x4901,0x4901,0x4701,0x4701,0x4901, + 0x4901,0x4B04,0x4604,0x4704,0x4904,0x4B04,0x4B04,0x5001,0x5001,0x5001, + 0x5001,0x4B01,0x4B01,0x5001,0x5001,0x5204,0x5204,0x5301,0x5301,0x5301, + 0x5301,0x5101,0x5101,0x5301,0x5301,0x5408,0x8008,0x440C,0x4904,0x4704, + 0x4604,0x4404,0x4204,0x5201,0x5201,0x5201,0x5201,0x5101,0x5101,0x5101, + 0x5101,0x5001,0x5001,0x5001,0x5001,0x4B01,0x4B01,0x4B01,0x4B01,0x5201, + 0x5201,0x5201,0x5201,0x5101,0x5101,0x5101,0x5101,0x5001,0x5001,0x5001, + 0x5001,0x4B04,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401, + 0x4401,0x4401,0x4401,0x4401,0x4904,0x4701,0x4701,0x4701,0x4701,0x4601, + 0x4601,0x4601,0x4601,0x4401,0x4401,0x4401,0x4401,0x4204,0x5201,0x5201, + 0x5201,0x5201,0x5401,0x5401,0x5401,0x5401,0x5601,0x5601,0x5601,0x5601, + 0x5701,0x5701,0x5701,0x5701,0x5201,0x5201,0x5201,0x5201,0x5401,0x5401, + 0x5401,0x5401,0x5601,0x5601,0x5601,0x5601,0x5704,0x5908,0x8008,0x6208, + 0x8008,0x5708,0x8004,0x5204,0x4B04,0x4704,0x4B04,0x5204,0x5704,0x5204, + 0x5704,0x5B04,0x6208,0x5608,0x5708,0x8004,0x5204,0x4B04,0x4704,0x4B04, + 0x5204,0x5704,0x5204,0x5704,0x5B04,0x6208,0x5608,0x5708,0x8008,0x5708, + 0x8008,0x5708,0x4706,0x4702,0x4708,0x8008, + 0x0000 +}; + +static int Voice2 [] = { + 0x4708,0x8004,0x4204,0x4708,0x8004,0x4204,0x4704,0x4204,0x4704,0x4B04, + 0x5208,0x8008,0x5008,0x8004,0x4904,0x5008,0x8004,0x4904,0x5004,0x4904, + 0x4604,0x4904,0x4208,0x8008,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4704,0x4904,0x4904,0x4904,0x4904,0x4901,0x4901,0x4901,0x4901, + 0x5004,0x4601,0x4601,0x4601,0x4601,0x4904,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4704,0x4704,0x4704,0x4904,0x4904,0x4904,0x4904,0x4901,0x4901, + 0x4901,0x4901,0x5004,0x4601,0x4601,0x4601,0x4601,0x4904,0x4210,0x4210, + 0x4210,0x4208,0x8028,0x3610,0x3710,0x4008,0x4008,0x3908,0x3208,0x3204, + 0x8004,0x4204,0x8004,0x4208,0x8028,0x4601,0x4601,0x4601,0x4601,0x4601, + 0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601, + 0x4601,0x4710,0x4008,0x4004,0x4004,0x3908,0x3904,0x3904,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4904,0x4904,0x4704,0x4704,0x4604,0x4604,0x4704,0x4704,0x4404,0x4404, + 0x4210,0x4410,0x4210,0x4410,0x4604,0x4704,0x4904,0x4704,0x4604,0x4704, + 0x4904,0x4604,0x4B04,0x4904,0x4704,0x4904,0x4B04,0x4904,0x4804,0x4B04, + 0x4904,0x4904,0x4904,0x4904,0x4904,0x4904,0x4904,0x4904,0x4904,0x3904, + 0x3904,0x3904,0x3908,0x8020,0x4604,0x8004,0x3B04,0x8004,0x4704,0x8004, + 0x4404,0x8004,0x4104,0x800C,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401, + 0x4401,0x4401,0x4204,0x8004,0x4404,0x8004,0x4201,0x4201,0x4201,0x4201, + 0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201, + 0x4201,0x4201,0x4108,0x8010,0x4101,0x4101,0x4101,0x4101,0x4101,0x4101, + 0x4101,0x4101,0x4204,0x8004,0x4604,0x8004,0x3B04,0x8004,0x3704,0x8004, + 0x3408,0x8004,0x4404,0x4704,0x4604,0x8004,0x5704,0x5701,0x5701,0x5701, + 0x5701,0x5604,0x8004,0x4704,0x4204,0x4204,0x4404,0x4404,0x4604,0x4604, + 0x4204,0x4204,0x4104,0x4104,0x4204,0x4204,0x4404,0x4404,0x4104,0x4104, + 0x4204,0x4204,0x4404,0x4404,0x4604,0x4604,0x4204,0x4204,0x4104,0x4104, + 0x4204,0x4204,0x4404,0x4404,0x4104,0x4104,0x4204,0x4204,0x4604,0x4404, + 0x4304,0x3B04,0x4104,0x4304,0x4404,0x4704,0x4404,0x4204,0x4104,0x3904, + 0x3B04,0x4104,0x5204,0x5204,0x5204,0x5204,0x5204,0x5204,0x5204,0x5204, + 0x5204,0x4904,0x4904,0x4904,0x4904,0x4904,0x4904,0x4704,0x4604,0x4204, + 0x4404,0x4404,0x4604,0x4604,0x4204,0x4204,0x4104,0x4104,0x4204,0x4204, + 0x4404,0x4404,0x4104,0x4104,0x4204,0x4204,0x4404,0x4404,0x4604,0x4604, + 0x4204,0x4204,0x4104,0x4104,0x4204,0x4204,0x4404,0x4404,0x4104,0x4104, + 0x4204,0x4204,0x4604,0x4404,0x4304,0x3B04,0x4104,0x4304,0x4404,0x4704, + 0x4404,0x4204,0x4104,0x3904,0x3B04,0x4104,0x4204,0x5204,0x5204,0x5204, + 0x5204,0x5204,0x5204,0x5204,0x5204,0x4904,0x4904,0x4904,0x4904,0x4904, + 0x4904,0x4704,0x4604,0x3904,0x3B04,0x4104,0x4204,0x4204,0x4404,0x4404, + 0x4604,0x4104,0x4204,0x4404,0x4604,0x4604,0x4704,0x4704,0x4904,0x4904, + 0x4A04,0x4A04,0x4B08,0x8008,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401, + 0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401, + 0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4201,0x4201, + 0x4201,0x4201,0x4104,0x5204,0x8004,0x5204,0x8004,0x5204,0x800C,0x4708, + 0x8004,0x4204,0x4708,0x8004,0x4204,0x4704,0x4204,0x4704,0x4B04,0x5208, + 0x8008,0x5008,0x8004,0x4904,0x5008,0x8004,0x4904,0x5004,0x4904,0x4604, + 0x4904,0x4208,0x8008,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4904,0x4904,0x4904,0x4904,0x4901,0x4901,0x4901,0x4901,0x5004, + 0x4601,0x4601,0x4601,0x4601,0x4904,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4704,0x4704,0x4904,0x4904,0x4904,0x4904,0x4901,0x4901,0x4901, + 0x4901,0x5004,0x4601,0x4601,0x4601,0x4601,0x4904,0x4210,0x4210,0x4210, + 0x4208,0x8028,0x3610,0x3710,0x4008,0x4008,0x3908,0x3208,0x3204,0x8004, + 0x4204,0x8004,0x4208,0x8028,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601, + 0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601, + 0x4710,0x4008,0x4004,0x4004,0x3908,0x3904,0x3904,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4904, + 0x4904,0x4704,0x4704,0x4604,0x4604,0x4704,0x4704,0x4404,0x4404,0x4210, + 0x4410,0x4210,0x4410,0x4604,0x4704,0x4904,0x4704,0x4604,0x4704,0x4904, + 0x4604,0x4B04,0x4904,0x4704,0x4904,0x4B04,0x4904,0x4804,0x4B04,0x4904, + 0x4904,0x4904,0x4904,0x4904,0x4904,0x4904,0x4904,0x4904,0x3904,0x3904, + 0x3904,0x3908,0x8020,0x4604,0x8004,0x3B04,0x8004,0x4704,0x8004,0x4404, + 0x8004,0x4104,0x800C,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401, + 0x4401,0x4204,0x8004,0x4404,0x8004,0x4201,0x4201,0x4201,0x4201,0x4201, + 0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201, + 0x4201,0x4108,0x8010,0x4101,0x4101,0x4101,0x4101,0x4101,0x4101,0x4101, + 0x4101,0x4204,0x8004,0x4604,0x8004,0x3B04,0x8004,0x3704,0x8004,0x3408, + 0x8004,0x4404,0x4704,0x4604,0x8004,0x5704,0x5701,0x5701,0x5701,0x5701, + 0x5604,0x8004,0x4704,0x4204,0x4204,0x4404,0x4404,0x4604,0x4604,0x4204, + 0x4204,0x4104,0x4104,0x4204,0x4204,0x4404,0x4404,0x4104,0x4104,0x4204, + 0x4204,0x4404,0x4404,0x4604,0x4604,0x4204,0x4204,0x4104,0x4104,0x4204, + 0x4204,0x4404,0x4404,0x4104,0x4104,0x4204,0x4204,0x4604,0x4404,0x4304, + 0x3B04,0x4104,0x4304,0x4404,0x4704,0x4404,0x4204,0x4104,0x3904,0x3B04, + 0x4104,0x5204,0x5204,0x5204,0x5204,0x5204,0x5204,0x5204,0x5204,0x5204, + 0x4904,0x4904,0x4904,0x4904,0x4904,0x4904,0x4704,0x4604,0x4204,0x4404, + 0x4404,0x4604,0x4604,0x4204,0x4204,0x4104,0x4104,0x4204,0x4204,0x4404, + 0x4404,0x4104,0x4104,0x4204,0x4204,0x4404,0x4404,0x4604,0x4604,0x4204, + 0x4204,0x4104,0x4104,0x4204,0x4204,0x4404,0x4404,0x4104,0x4104,0x4204, + 0x4204,0x4604,0x4404,0x4304,0x3B04,0x4104,0x4304,0x4404,0x4704,0x4404, + 0x4204,0x4104,0x3904,0x3B04,0x4104,0x4204,0x5204,0x5204,0x5204,0x5204, + 0x5204,0x5204,0x5204,0x5204,0x4904,0x4904,0x4904,0x4904,0x4904,0x4904, + 0x4704,0x4604,0x3904,0x3B04,0x4104,0x4204,0x4204,0x4404,0x4404,0x4604, + 0x4104,0x4204,0x4404,0x4604,0x4604,0x4704,0x4704,0x4904,0x4904,0x4A04, + 0x4A04,0x4B08,0x8008,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401, + 0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401, + 0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4401,0x4201,0x4201,0x4201, + 0x4201,0x4104,0x5204,0x8004,0x5204,0x8004,0x5204,0x800C,0x4208,0x8004, + 0x3904,0x4208,0x8004,0x3904,0x4204,0x3904,0x4204,0x4604,0x4908,0x8008, + 0x4908,0x8004,0x4604,0x4908,0x8004,0x4604,0x4904,0x4604,0x4304,0x4604, + 0x3B08,0x8008,0x4004,0x4004,0x4204,0x4204,0x4404,0x4404,0x4004,0x4004, + 0x3B04,0x3B04,0x4004,0x4004,0x4204,0x4204,0x3B04,0x3B04,0x4004,0x4004, + 0x4204,0x4204,0x4404,0x4404,0x4004,0x4004,0x3B04,0x3B04,0x4004,0x4004, + 0x4204,0x4204,0x3B04,0x3B04,0x4004,0x4004,0x4204,0x4204,0x4404,0x4404, + 0x4004,0x4004,0x3B04,0x3B04,0x4004,0x4004,0x4204,0x4204,0x3B04,0x3B04, + 0x3904,0x3904,0x3B04,0x3B04,0x4004,0x4004,0x3904,0x3904,0x3904,0x3904, + 0x3A04,0x3A04,0x4004,0x4004,0x3904,0x3904,0x3704,0x3704,0x3604,0x3604, + 0x3704,0x3704,0x4604,0x4604,0x4704,0x4704,0x4604,0x4604,0x4704,0x3704, + 0x3604,0x3704,0x3908,0x8004,0x3201,0x3201,0x3201,0x3201,0x3401,0x3401, + 0x3401,0x3401,0x3601,0x3601,0x3601,0x3601,0x3701,0x3701,0x3701,0x3701, + 0x3901,0x3901,0x3901,0x3901,0x4001,0x4001,0x4001,0x4001,0x3A04,0x8004, + 0x3601,0x3601,0x3601,0x3601,0x3701,0x3701,0x3701,0x3701,0x3901,0x3901, + 0x3901,0x3901,0x3A01,0x3A01,0x3A01,0x3A01,0x4101,0x4101,0x4101,0x4101, + 0x4401,0x4401,0x4401,0x4401,0x4204,0x8038,0x4B04,0x4B04,0x4B01,0x4B01, + 0x4B01,0x4B01,0x5004,0x4904,0x4904,0x4901,0x4901,0x4901,0x4901,0x4B04, + 0x4704,0x4704,0x4701,0x4701,0x4701,0x4701,0x4B04,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4904,0x4704,0x4204,0x3B08,0x8004,0x4204,0x4708,0x8004,0x4204, + 0x4704,0x4204,0x4704,0x4B04,0x5208,0x8008,0x5008,0x8004,0x4904,0x5008, + 0x8004,0x4904,0x5004,0x4904,0x4604,0x4904,0x4208,0x8008,0x4704,0x4704, + 0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4701,0x4701,0x4701,0x4701,0x4B04,0x4401,0x4401,0x4401,0x4401,0x4704, + 0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4704,0x4701,0x4701,0x4701,0x4701,0x4B04,0x4401,0x4401,0x4401, + 0x4401,0x4704,0x4210,0x4210,0x4210,0x4208,0x8028,0x3610,0x3710,0x4008, + 0x4008,0x3908,0x3908,0x3204,0x8004,0x4204,0x8004,0x4208,0x8028,0x4601, + 0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601, + 0x4601,0x4601,0x4601,0x4601,0x4601,0x4710,0x4008,0x4004,0x4004,0x3908, + 0x3904,0x3904,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x4704, + 0x4704,0x4704,0x4704,0x4704,0x4904,0x4904,0x4704,0x4704,0x4604,0x4604, + 0x4704,0x4704,0x4404,0x4404,0x4210,0x4410,0x4210,0x4410,0x4208,0x4408, + 0x4208,0x4408,0x4204,0x3204,0x3204,0x3204,0x3208,0x8020,0x3B04,0x8004, + 0x3404,0x8004,0x4004,0x8004,0x3904,0x8004,0x3604,0x800C,0x4901,0x4901, + 0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4704,0x8004,0x4904,0x8004, + 0x4B10,0x4908,0x8008,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5001,0x5001,0x4B01,0x4B01,0x4901,0x4901,0x4704, + 0x8004,0x5404,0x8004,0x5004,0x8004,0x4904,0x8004,0x5208,0x8004,0x5004, + 0x5001,0x5001,0x5001,0x5001,0x4B04,0x8004,0x5004,0x5001,0x5001,0x5001, + 0x5001,0x4B04,0x8004,0x4004,0x3704,0x3704,0x3904,0x3904,0x3B04,0x3B04, + 0x3704,0x3704,0x3604,0x3604,0x3704,0x3704,0x3904,0x3904,0x3604,0x3604, + 0x3704,0x3704,0x3904,0x3904,0x3B04,0x3B04,0x3704,0x3704,0x3604,0x3604, + 0x3704,0x3704,0x3904,0x3904,0x3604,0x3604,0x3704,0x4704,0x4B04,0x4904, + 0x4804,0x4404,0x4604,0x4804,0x4904,0x5004,0x4904,0x4704,0x4604,0x4204, + 0x4404,0x4604,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4204,0x4204,0x4204,0x4204,0x4204,0x4204,0x4004,0x3B04,0x3704, + 0x3904,0x3904,0x3B04,0x3B04,0x3704,0x3704,0x3604,0x3604,0x3704,0x3704, + 0x3904,0x3904,0x3604,0x3604,0x3704,0x4704,0x4904,0x4904,0x4B04,0x4B04, + 0x4704,0x4704,0x4604,0x4604,0x4704,0x4704,0x4904,0x4904,0x4604,0x4604, + 0x4704,0x4704,0x4B04,0x4904,0x4804,0x4404,0x4604,0x4804,0x4904,0x5004, + 0x4904,0x4704,0x4604,0x4204,0x4404,0x4604,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4704,0x4704,0x4704,0x4701,0x4701,0x4701,0x4701,0x4904,0x5004, + 0x5004,0x5004,0x5004,0x5004,0x4B04,0x4904,0x4204,0x4404,0x4604,0x4704, + 0x4704,0x4904,0x4904,0x4B04,0x3604,0x3704,0x3904,0x3B04,0x3B04,0x4004, + 0x4004,0x4004,0x4004,0x4104,0x4104,0x4208,0x8008,0x3901,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901, + 0x3901,0x3701,0x3701,0x3701,0x3701,0x3604,0x3708,0x8018,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901, + 0x3901,0x3901,0x3701,0x3701,0x3701,0x3701,0x3604,0x3708,0x8008,0x4708, + 0x8008,0x4408,0x8008,0x4708,0x8008,0x3710,0x3710,0x3710,0x3708,0x3908, + 0x3710,0x3710,0x3710,0x3708,0x3908,0x3704,0x3B04,0x4204,0x4704,0x4B04, + 0x4704,0x5204,0x4B04,0x4708,0x3706,0x3702,0x3708,0x8008,0x4208,0x8004, + 0x3904,0x4208,0x8004,0x3904,0x4204,0x3904,0x4204,0x4604,0x4908,0x8008, + 0x4908,0x8004,0x4604,0x4908,0x8004,0x4604,0x4904,0x4604,0x4304,0x4604, + 0x3B08,0x8008,0x4004,0x4004,0x4204,0x4204,0x4404,0x4404,0x4004,0x4004, + 0x3B04,0x3B04,0x4004,0x4004,0x4204,0x4204,0x3B04,0x3B04,0x4004,0x4004, + 0x4204,0x4204,0x4404,0x4404,0x4004,0x4004,0x3B04,0x3B04,0x4004,0x4004, + 0x4204,0x4204,0x3B04,0x3B04,0x4004,0x4004,0x4204,0x4204,0x4404,0x4404, + 0x4004,0x4004,0x3B04,0x3B04,0x4004,0x4004,0x4204,0x4204,0x3B04,0x3B04, + 0x3904,0x3904,0x3B04,0x3B04,0x4004,0x4004,0x3904,0x3904,0x3904,0x3904, + 0x3A04,0x3A04,0x4004,0x4004,0x3904,0x3904,0x3704,0x3704,0x3604,0x3604, + 0x3704,0x3704,0x4604,0x4604,0x4704,0x4704,0x4604,0x4604,0x4704,0x3704, + 0x3604,0x3704,0x3908,0x8004,0x3201,0x3201,0x3201,0x3201,0x3401,0x3401, + 0x3401,0x3401,0x3601,0x3601,0x3601,0x3601,0x3701,0x3701,0x3701,0x3701, + 0x3901,0x3901,0x3901,0x3901,0x4001,0x4001,0x4001,0x4001,0x3A04,0x8004, + 0x3601,0x3601,0x3601,0x3601,0x3701,0x3701,0x3701,0x3701,0x3901,0x3901, + 0x3901,0x3901,0x3A01,0x3A01,0x3A01,0x3A01,0x4101,0x4101,0x4101,0x4101, + 0x4401,0x4401,0x4401,0x4401,0x4204,0x8038,0x4B04,0x4B04,0x4B01,0x4B01, + 0x4B01,0x4B01,0x5004,0x4904,0x4904,0x4901,0x4901,0x4901,0x4901,0x4B04, + 0x4704,0x4704,0x4701,0x4701,0x4701,0x4701,0x4B04,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4904,0x4704,0x4204,0x3B08,0x8004,0x4204,0x4708,0x8004,0x4204, + 0x4704,0x4204,0x4704,0x4B04,0x5208,0x8008,0x5008,0x8004,0x4904,0x5008, + 0x8004,0x4904,0x5004,0x4904,0x4604,0x4904,0x4208,0x8008,0x4704,0x4704, + 0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4701,0x4701,0x4701,0x4701,0x4B04,0x4401,0x4401,0x4401,0x4401,0x4704, + 0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4704,0x4701,0x4701,0x4701,0x4701,0x4B04,0x4401,0x4401,0x4401, + 0x4401,0x4704,0x4210,0x4210,0x4210,0x4208,0x8028,0x3610,0x3710,0x4008, + 0x4008,0x3908,0x3908,0x3204,0x8004,0x4204,0x8004,0x4208,0x8028,0x4601, + 0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601,0x4601, + 0x4601,0x4601,0x4601,0x4601,0x4601,0x4710,0x4008,0x4004,0x4004,0x3908, + 0x3904,0x3904,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x4704, + 0x4704,0x4704,0x4704,0x4704,0x4904,0x4904,0x4704,0x4704,0x4604,0x4604, + 0x4704,0x4704,0x4404,0x4404,0x4210,0x4410,0x4210,0x4410,0x4208,0x4408, + 0x4208,0x4408,0x4204,0x3204,0x3204,0x3204,0x3208,0x8020,0x3B04,0x8004, + 0x3404,0x8004,0x4004,0x8004,0x3904,0x8004,0x3604,0x800C,0x4901,0x4901, + 0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4704,0x8004,0x4904,0x8004, + 0x4B10,0x4908,0x8008,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201,0x5201, + 0x5201,0x5201,0x5201,0x5001,0x5001,0x4B01,0x4B01,0x4901,0x4901,0x4704, + 0x8004,0x5404,0x8004,0x5004,0x8004,0x4904,0x8004,0x5208,0x8004,0x5004, + 0x5001,0x5001,0x5001,0x5001,0x4B04,0x8004,0x5004,0x5001,0x5001,0x5001, + 0x5001,0x4B04,0x8004,0x4004,0x3704,0x3704,0x3904,0x3904,0x3B04,0x3B04, + 0x3704,0x3704,0x3604,0x3604,0x3704,0x3704,0x3904,0x3904,0x3604,0x3604, + 0x3704,0x3704,0x3904,0x3904,0x3B04,0x3B04,0x3704,0x3704,0x3604,0x3604, + 0x3704,0x3704,0x3904,0x3904,0x3604,0x3604,0x3704,0x4704,0x4B04,0x4904, + 0x4804,0x4404,0x4604,0x4804,0x4904,0x5004,0x4904,0x4704,0x4604,0x4204, + 0x4404,0x4604,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4204,0x4204,0x4204,0x4204,0x4204,0x4204,0x4004,0x3B04,0x3704, + 0x3904,0x3904,0x3B04,0x3B04,0x3704,0x3704,0x3604,0x3604,0x3704,0x3704, + 0x3904,0x3904,0x3604,0x3604,0x3704,0x4704,0x4904,0x4904,0x4B04,0x4B04, + 0x4704,0x4704,0x4604,0x4604,0x4704,0x4704,0x4904,0x4904,0x4604,0x4604, + 0x4704,0x4704,0x4B04,0x4904,0x4804,0x4404,0x4604,0x4804,0x4904,0x5004, + 0x4904,0x4704,0x4604,0x4204,0x4404,0x4604,0x4704,0x4704,0x4704,0x4704, + 0x4704,0x4704,0x4704,0x4704,0x4701,0x4701,0x4701,0x4701,0x4904,0x5004, + 0x5004,0x5004,0x5004,0x5004,0x4B04,0x4904,0x4204,0x4404,0x4604,0x4704, + 0x4704,0x4904,0x4904,0x4B04,0x3604,0x3704,0x3904,0x3B04,0x3B04,0x4004, + 0x4004,0x4004,0x4004,0x4104,0x4104,0x4208,0x8008,0x3901,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901, + 0x3901,0x3701,0x3701,0x3701,0x3701,0x3604,0x3708,0x8018,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901, + 0x3901,0x3901,0x3701,0x3701,0x3701,0x3701,0x3604,0x3708,0x8008,0x4708, + 0x8008,0x4408,0x8008,0x4708,0x8008,0x3710,0x3710,0x3710,0x3708,0x3908, + 0x3710,0x3710,0x3710,0x3708,0x3908,0x3704,0x3B04,0x4204,0x4704,0x4B04, + 0x4704,0x5204,0x4B04,0x4708,0x3706,0x3702,0x3708,0x8008, + 0x0000 +}; + +static int Voice3 [] = { + 0x3708,0x8004,0x3204,0x3708,0x8004,0x3204,0x3704,0x3204,0x3704,0x3B04, + 0x3208,0x8008,0x4008,0x8004,0x3904,0x4008,0x8004,0x3904,0x4004,0x3904, + 0x3604,0x4904,0x4208,0x8008,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3904,0x3904, + 0x3B04,0x3B04,0x3604,0x3604,0x3704,0x3704,0x3904,0x3904,0x3B08,0x8028, + 0x3210,0x3410,0x3008,0x3008,0x3208,0x3208,0x2B04,0x8004,0x3204,0x8004, + 0x3708,0x8008,0x4B10,0x5010,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201, + 0x3201,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201, + 0x3410,0x3008,0x3008,0x3208,0x3208,0x3B14,0x3B02,0x3901,0x3B01,0x4004, + 0x3904,0x4714,0x4702,0x4601,0x4701,0x4904,0x4604,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3604,0x3604,0x3704, + 0x3704,0x3404,0x3404,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204, + 0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204, + 0x3404,0x3604,0x3404,0x3204,0x3404,0x3604,0x3204,0x3704,0x3904,0x3B04, + 0x3904,0x3704,0x3904,0x3B04,0x3804,0x3904,0x3904,0x3904,0x3904,0x3904, + 0x3904,0x3904,0x3904,0x3904,0x2904,0x2904,0x2904,0x2908,0x8008,0x490A, + 0x4702,0x4602,0x4402,0x4204,0x8004,0x3304,0x8004,0x3404,0x8004,0x3204, + 0x8004,0x3104,0x8004,0x2904,0x800C,0x2A01,0x2A01,0x2A01,0x2A01,0x2A01, + 0x2A01,0x2A01,0x2A01,0x2B04,0x8004,0x2704,0x8004,0x2908,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901, + 0x3801,0x3801,0x3801,0x3801,0x3701,0x3701,0x3701,0x3701,0x3401,0x3401, + 0x3401,0x3401,0x3204,0x8004,0x3404,0x8004,0x3604,0x8004,0x3304,0x8004, + 0x3404,0x8004,0x3204,0x8004,0x3104,0x8004,0x2904,0x8004,0x2B04,0x8004, + 0x3704,0x8004,0x3904,0x8004,0x2904,0x8004,0x4604,0x4604,0x4704,0x4704, + 0x4904,0x4904,0x4604,0x4604,0x4404,0x4404,0x4604,0x4604,0x4704,0x4704, + 0x4404,0x4404,0x4604,0x4604,0x4704,0x4704,0x4904,0x4904,0x4604,0x4604, + 0x4404,0x4404,0x4604,0x4604,0x4704,0x4704,0x4404,0x4404,0x4608,0x8008, + 0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5408,0x8008,0x5401,0x5401, + 0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401, + 0x5401,0x5401,0x5401,0x5401,0x5204,0x5604,0x5704,0x5904,0x5701,0x5701, + 0x5701,0x5701,0x5901,0x5901,0x5B01,0x5B01,0x5904,0x5704,0x5701,0x5701, + 0x5701,0x5701,0x5604,0x5204,0x5604,0x5601,0x5601,0x5601,0x5601,0x5404, + 0x5204,0x5104,0x5204,0x4604,0x4704,0x4704,0x4904,0x4904,0x4604,0x4604, + 0x4404,0x4404,0x4604,0x4604,0x4704,0x4704,0x4404,0x4404,0x4604,0x4604, + 0x4704,0x4704,0x4904,0x4904,0x4604,0x4604,0x4404,0x4404,0x4604,0x4604, + 0x4704,0x4704,0x4404,0x4404,0x4608,0x8008,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5408,0x8008,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401, + 0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401, + 0x5204,0x5504,0x5704,0x5904,0x5701,0x5701,0x5701,0x5701,0x5901,0x5901, + 0x5B01,0x5B01,0x5904,0x5704,0x5701,0x5701,0x5701,0x5701,0x5604,0x5204, + 0x5604,0x5601,0x5601,0x5601,0x5601,0x5404,0x5204,0x5104,0x3204,0x2904, + 0x2B04,0x3104,0x3204,0x3204,0x3404,0x3404,0x3604,0x3104,0x3204,0x3404, + 0x3604,0x3604,0x3704,0x3704,0x3904,0x3904,0x3A04,0x3A04,0x3B08,0x8008, + 0x3710,0x3910,0x4204,0x8004,0x4204,0x8004,0x4204,0x800C,0x3708,0x8004, + 0x3204,0x3708,0x8004,0x3204,0x3704,0x3204,0x3704,0x3B04,0x3208,0x8008, + 0x4008,0x8004,0x3904,0x4008,0x8004,0x3904,0x4004,0x3904,0x3604,0x4904, + 0x4208,0x8008,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3904,0x3904,0x3B04,0x3B04, + 0x3604,0x3604,0x3704,0x3704,0x3904,0x3904,0x3B08,0x8028,0x3210,0x3410, + 0x3008,0x3008,0x3208,0x3208,0x2B04,0x8004,0x3204,0x8004,0x3708,0x8008, + 0x4B10,0x5010,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201, + 0x3201,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201,0x3201,0x3410,0x3008, + 0x3008,0x3208,0x3208,0x3B14,0x3B02,0x3901,0x3B01,0x4004,0x3904,0x4714, + 0x4702,0x4601,0x4701,0x4904,0x4604,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3604,0x3604,0x3704,0x3704,0x3404, + 0x3404,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204, + 0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3204,0x3404,0x3604, + 0x3404,0x3204,0x3404,0x3604,0x3204,0x3704,0x3904,0x3B04,0x3904,0x3704, + 0x3904,0x3B04,0x3804,0x3904,0x3904,0x3904,0x3904,0x3904,0x3904,0x3904, + 0x3904,0x3904,0x2904,0x2904,0x2904,0x2908,0x8008,0x490A,0x4702,0x4602, + 0x4402,0x4204,0x8004,0x3304,0x8004,0x3404,0x8004,0x3204,0x8004,0x3104, + 0x8004,0x2904,0x800C,0x2A01,0x2A01,0x2A01,0x2A01,0x2A01,0x2A01,0x2A01, + 0x2A01,0x2B04,0x8004,0x2704,0x8004,0x2908,0x3901,0x3901,0x3901,0x3901, + 0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3901,0x3801,0x3801, + 0x3801,0x3801,0x3701,0x3701,0x3701,0x3701,0x3401,0x3401,0x3401,0x3401, + 0x3204,0x8004,0x3404,0x8004,0x3604,0x8004,0x3304,0x8004,0x3404,0x8004, + 0x3204,0x8004,0x3104,0x8004,0x2904,0x8004,0x2B04,0x8004,0x3704,0x8004, + 0x3904,0x8004,0x2904,0x8004,0x4604,0x4604,0x4704,0x4704,0x4904,0x4904, + 0x4604,0x4604,0x4404,0x4404,0x4604,0x4604,0x4704,0x4704,0x4404,0x4404, + 0x4604,0x4604,0x4704,0x4704,0x4904,0x4904,0x4604,0x4604,0x4404,0x4404, + 0x4604,0x4604,0x4704,0x4704,0x4404,0x4404,0x4608,0x8008,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5408,0x8008,0x5401,0x5401,0x5401,0x5401, + 0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401, + 0x5401,0x5401,0x5204,0x5604,0x5704,0x5904,0x5701,0x5701,0x5701,0x5701, + 0x5901,0x5901,0x5B01,0x5B01,0x5904,0x5704,0x5701,0x5701,0x5701,0x5701, + 0x5604,0x5204,0x5604,0x5601,0x5601,0x5601,0x5601,0x5404,0x5204,0x5104, + 0x5204,0x4604,0x4704,0x4704,0x4904,0x4904,0x4604,0x4604,0x4404,0x4404, + 0x4604,0x4604,0x4704,0x4704,0x4404,0x4404,0x4604,0x4604,0x4704,0x4704, + 0x4904,0x4904,0x4604,0x4604,0x4404,0x4404,0x4604,0x4604,0x4704,0x4704, + 0x4404,0x4404,0x4608,0x8008,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601,0x5601, + 0x5408,0x8008,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401, + 0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5401,0x5204,0x5504, + 0x5704,0x5904,0x5701,0x5701,0x5701,0x5701,0x5901,0x5901,0x5B01,0x5B01, + 0x5904,0x5704,0x5701,0x5701,0x5701,0x5701,0x5604,0x5204,0x5604,0x5601, + 0x5601,0x5601,0x5601,0x5404,0x5204,0x5104,0x3204,0x2904,0x2B04,0x3104, + 0x3204,0x3204,0x3404,0x3404,0x3604,0x3104,0x3204,0x3404,0x3604,0x3604, + 0x3704,0x3704,0x3904,0x3904,0x3A04,0x3A04,0x3B08,0x8008,0x3710,0x3910, + 0x4204,0x8004,0x4204,0x8004,0x4204,0x800C,0x3208,0x8004,0x2904,0x3208, + 0x8004,0x2904,0x3204,0x2904,0x3204,0x3604,0x4908,0x8008,0x3908,0x8004, + 0x3604,0x3908,0x8004,0x3604,0x3904,0x3604,0x3304,0x3604,0x2B08,0x8008, + 0x4404,0x4404,0x4504,0x4504,0x4704,0x4704,0x4404,0x4404,0x4204,0x4204, + 0x4404,0x4404,0x4504,0x4504,0x4204,0x4204,0x4404,0x4404,0x4504,0x4504, + 0x4704,0x4704,0x4404,0x4404,0x4204,0x4204,0x4404,0x4404,0x4504,0x4504, + 0x4204,0x4204,0x4404,0x4404,0x4504,0x4504,0x4704,0x4704,0x4404,0x4404, + 0x4204,0x4204,0x4404,0x4404,0x4504,0x4504,0x4204,0x4204,0x4004,0x4004, + 0x4204,0x4204,0x4404,0x4404,0x4004,0x4004,0x4004,0x4004,0x4204,0x4204, + 0x4304,0x4304,0x4004,0x4004,0x3A04,0x3A04,0x3904,0x3904,0x3A04,0x3A04, + 0x4904,0x4904,0x4A04,0x4A04,0x4904,0x4904,0x4A04,0x4A04,0x4904,0x4704, + 0x4608,0x8004,0x2201,0x2201,0x2201,0x2201,0x2401,0x2401,0x2401,0x2401, + 0x2601,0x2601,0x2601,0x2601,0x2701,0x2701,0x2701,0x2701,0x2901,0x2901, + 0x2901,0x2901,0x3001,0x3001,0x3001,0x3001,0x2A04,0x8004,0x2601,0x2601, + 0x2601,0x2601,0x2701,0x2701,0x2701,0x2701,0x2901,0x2901,0x2901,0x2901, + 0x2A01,0x2A01,0x2A01,0x2A01,0x2101,0x2101,0x2101,0x2101,0x3401,0x3401, + 0x3401,0x3401,0x3204,0x8004,0x4204,0x4404,0x4504,0x4704,0x4904,0x4A08, + 0x4B08,0x5008,0x5108,0x5204,0x5204,0x5201,0x5201,0x5201,0x5201,0x5404, + 0x5004,0x5004,0x5001,0x5001,0x5001,0x5001,0x5204,0x4B04,0x4B04,0x4B01, + 0x4B01,0x4B01,0x4B01,0x5204,0x5201,0x5201,0x5201,0x5201,0x5004,0x4B04, + 0x4904,0x4708,0x8004,0x3204,0x3708,0x8004,0x3204,0x3704,0x3204,0x3704, + 0x3B04,0x4208,0x8008,0x4008,0x8004,0x3904,0x4008,0x8004,0x3904,0x4004, + 0x3904,0x3604,0x3904,0x3208,0x8008,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3904, + 0x3904,0x3B04,0x3B04,0x3704,0x3704,0x3904,0x3904,0x3B04,0x3B04,0x4008, + 0x8008,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x5010,0x4908,0x4908, + 0x4708,0x4708,0x4408,0x4408,0x4008,0x3908,0x4204,0x8004,0x4604,0x8004, + 0x4704,0x800C,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x5010,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4904,0x4904,0x4904,0x4901,0x4901,0x4901,0x4901, + 0x4704,0x4704,0x4704,0x4408,0x4404,0x4404,0x4008,0x4004,0x4004,0x3B14, + 0x3B01,0x3B01,0x3901,0x3B01,0x4001,0x4001,0x4001,0x4001,0x3904,0x4714, + 0x4701,0x4701,0x4601,0x4701,0x4901,0x4901,0x4901,0x4901,0x4604,0x4B10, + 0x5010,0x4B08,0x5210,0x5708,0x5610,0x5710,0x5610,0x5710,0x5608,0x5708, + 0x5608,0x5708,0x5604,0x4204,0x4204,0x4204,0x4208,0x8008,0x4201,0x4201, + 0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4001,0x4001, + 0x3B01,0x3B01,0x3901,0x3901,0x3704,0x8004,0x4404,0x8004,0x4004,0x8004, + 0x3904,0x8004,0x4204,0x800C,0x4B0A,0x4902,0x4702,0x4602,0x5404,0x8004, + 0x5004,0x8004,0x3208,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201, + 0x4201,0x4201,0x4201,0x4201,0x4201,0x4101,0x4101,0x4101,0x4101,0x4001, + 0x4001,0x4001,0x4001,0x3901,0x3901,0x3901,0x3901,0x3704,0x8004,0x3904, + 0x8004,0x3B04,0x8004,0x3804,0x8004,0x3904,0x8004,0x3704,0x8004,0x3604, + 0x8004,0x3204,0x8004,0x3404,0x8004,0x3004,0x8004,0x3204,0x8004,0x3204, + 0x8004,0x2708,0x8018,0x3208,0x8018,0x2708,0x8018,0x3208,0x8018,0x3B08, + 0x8008,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4908,0x8008,0x4901, + 0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4901, + 0x4901,0x4901,0x4901,0x4901,0x4901,0x4704,0x4B04,0x5004,0x5204,0x5001, + 0x5001,0x5001,0x5001,0x5201,0x5201,0x5401,0x5401,0x5204,0x5004,0x5001, + 0x5001,0x5001,0x5001,0x4B04,0x4704,0x4B04,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4904,0x4704,0x4604,0x4704,0x3B04,0x4004,0x4004,0x4204,0x4204,0x3B04, + 0x3B04,0x3904,0x3904,0x3B04,0x3B04,0x4004,0x4004,0x3904,0x3904,0x3B04, + 0x4B04,0x5004,0x5004,0x5204,0x5204,0x4B04,0x4B04,0x4904,0x4904,0x4B04, + 0x4B04,0x5004,0x5004,0x4904,0x4904,0x8004,0x3704,0x3B04,0x3904,0x3804, + 0x3404,0x3604,0x3804,0x3904,0x4004,0x3904,0x3704,0x3604,0x3204,0x3404, + 0x3604,0x3704,0x3704,0x3904,0x3B04,0x4004,0x4004,0x4004,0x4004,0x4204, + 0x4204,0x4204,0x4204,0x3204,0x3204,0x3204,0x3204,0x3704,0x3204,0x3404, + 0x3604,0x3704,0x3704,0x3904,0x3904,0x3B04,0x3604,0x3704,0x3904,0x3B04, + 0x3B04,0x4004,0x4004,0x4204,0x4204,0x4304,0x4304,0x4408,0x8008,0x3001, + 0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001, + 0x3001,0x3001,0x3001,0x3001,0x3001,0x3210,0x2708,0x8018,0x3001,0x3001, + 0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001, + 0x3001,0x3001,0x3001,0x3001,0x3210,0x2708,0x8018,0x3008,0x8008,0x3208, + 0x8008,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704, + 0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704, + 0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704, + 0x2704,0x2704,0x2704,0x2704,0x2B04,0x3204,0x3704,0x3B04,0x3704,0x4204, + 0x3B04,0x3708,0x2706,0x2702,0x2708,0x8008,0x3208,0x8004,0x2904,0x3208, + 0x8004,0x2904,0x3204,0x2904,0x3204,0x3604,0x4908,0x8008,0x3908,0x8004, + 0x3604,0x3908,0x8004,0x3604,0x3904,0x3604,0x3304,0x3604,0x2B08,0x8008, + 0x4404,0x4404,0x4504,0x4504,0x4704,0x4704,0x4404,0x4404,0x4204,0x4204, + 0x4404,0x4404,0x4504,0x4504,0x4204,0x4204,0x4404,0x4404,0x4504,0x4504, + 0x4704,0x4704,0x4404,0x4404,0x4204,0x4204,0x4404,0x4404,0x4504,0x4504, + 0x4204,0x4204,0x4404,0x4404,0x4504,0x4504,0x4704,0x4704,0x4404,0x4404, + 0x4204,0x4204,0x4404,0x4404,0x4504,0x4504,0x4204,0x4204,0x4004,0x4004, + 0x4204,0x4204,0x4404,0x4404,0x4004,0x4004,0x4004,0x4004,0x4204,0x4204, + 0x4304,0x4304,0x4004,0x4004,0x3A04,0x3A04,0x3904,0x3904,0x3A04,0x3A04, + 0x4904,0x4904,0x4A04,0x4A04,0x4904,0x4904,0x4A04,0x4A04,0x4904,0x4704, + 0x4608,0x8004,0x2201,0x2201,0x2201,0x2201,0x2401,0x2401,0x2401,0x2401, + 0x2601,0x2601,0x2601,0x2601,0x2701,0x2701,0x2701,0x2701,0x2901,0x2901, + 0x2901,0x2901,0x3001,0x3001,0x3001,0x3001,0x2A04,0x8004,0x2601,0x2601, + 0x2601,0x2601,0x2701,0x2701,0x2701,0x2701,0x2901,0x2901,0x2901,0x2901, + 0x2A01,0x2A01,0x2A01,0x2A01,0x2101,0x2101,0x2101,0x2101,0x3401,0x3401, + 0x3401,0x3401,0x3204,0x8004,0x4204,0x4404,0x4504,0x4704,0x4904,0x4A08, + 0x4B08,0x5008,0x5108,0x5204,0x5204,0x5201,0x5201,0x5201,0x5201,0x5404, + 0x5004,0x5004,0x5001,0x5001,0x5001,0x5001,0x5204,0x4B04,0x4B04,0x4B01, + 0x4B01,0x4B01,0x4B01,0x5204,0x5201,0x5201,0x5201,0x5201,0x5004,0x4B04, + 0x4904,0x4708,0x8004,0x3204,0x3708,0x8004,0x3204,0x3704,0x3204,0x3704, + 0x3B04,0x4208,0x8008,0x4008,0x8004,0x3904,0x4008,0x8004,0x3904,0x4004, + 0x3904,0x3604,0x3904,0x3208,0x8008,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704, + 0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3704,0x3904, + 0x3904,0x3B04,0x3B04,0x3704,0x3704,0x3904,0x3904,0x3B04,0x3B04,0x4008, + 0x8008,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x5010,0x4908,0x4908, + 0x4708,0x4708,0x4408,0x4408,0x4008,0x3908,0x4204,0x8004,0x4604,0x8004, + 0x4704,0x800C,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x5010,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4904,0x4904,0x4904,0x4901,0x4901,0x4901,0x4901, + 0x4704,0x4704,0x4704,0x4408,0x4404,0x4404,0x4008,0x4004,0x4004,0x3B14, + 0x3B01,0x3B01,0x3901,0x3B01,0x4001,0x4001,0x4001,0x4001,0x3904,0x4714, + 0x4701,0x4701,0x4601,0x4701,0x4901,0x4901,0x4901,0x4901,0x4604,0x4B10, + 0x5010,0x4B08,0x5210,0x5708,0x5610,0x5710,0x5610,0x5710,0x5608,0x5708, + 0x5608,0x5708,0x5604,0x4204,0x4204,0x4204,0x4208,0x8008,0x4201,0x4201, + 0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4001,0x4001, + 0x3B01,0x3B01,0x3901,0x3901,0x3704,0x8004,0x4404,0x8004,0x4004,0x8004, + 0x3904,0x8004,0x4204,0x800C,0x4B0A,0x4902,0x4702,0x4602,0x5404,0x8004, + 0x5004,0x8004,0x3208,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201,0x4201, + 0x4201,0x4201,0x4201,0x4201,0x4201,0x4101,0x4101,0x4101,0x4101,0x4001, + 0x4001,0x4001,0x4001,0x3901,0x3901,0x3901,0x3901,0x3704,0x8004,0x3904, + 0x8004,0x3B04,0x8004,0x3804,0x8004,0x3904,0x8004,0x3704,0x8004,0x3604, + 0x8004,0x3204,0x8004,0x3404,0x8004,0x3004,0x8004,0x3204,0x8004,0x3204, + 0x8004,0x2708,0x8018,0x3208,0x8018,0x2708,0x8018,0x3208,0x8018,0x3B08, + 0x8008,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4B01,0x4908,0x8008,0x4901, + 0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4901,0x4901, + 0x4901,0x4901,0x4901,0x4901,0x4901,0x4704,0x4B04,0x5004,0x5204,0x5001, + 0x5001,0x5001,0x5001,0x5201,0x5201,0x5401,0x5401,0x5204,0x5004,0x5001, + 0x5001,0x5001,0x5001,0x4B04,0x4704,0x4B04,0x4B01,0x4B01,0x4B01,0x4B01, + 0x4904,0x4704,0x4604,0x4704,0x3B04,0x4004,0x4004,0x4204,0x4204,0x3B04, + 0x3B04,0x3904,0x3904,0x3B04,0x3B04,0x4004,0x4004,0x3904,0x3904,0x3B04, + 0x4B04,0x5004,0x5004,0x5204,0x5204,0x4B04,0x4B04,0x4904,0x4904,0x4B04, + 0x4B04,0x5004,0x5004,0x4904,0x4904,0x8004,0x3704,0x3B04,0x3904,0x3804, + 0x3404,0x3604,0x3804,0x3904,0x4004,0x3904,0x3704,0x3604,0x3204,0x3404, + 0x3604,0x3704,0x3704,0x3904,0x3B04,0x4004,0x4004,0x4004,0x4004,0x4204, + 0x4204,0x4204,0x4204,0x3204,0x3204,0x3204,0x3204,0x3704,0x3204,0x3404, + 0x3604,0x3704,0x3704,0x3904,0x3904,0x3B04,0x3604,0x3704,0x3904,0x3B04, + 0x3B04,0x4004,0x4004,0x4204,0x4204,0x4304,0x4304,0x4408,0x8008,0x3001, + 0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001, + 0x3001,0x3001,0x3001,0x3001,0x3001,0x3210,0x2708,0x8018,0x3001,0x3001, + 0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001,0x3001, + 0x3001,0x3001,0x3001,0x3001,0x3210,0x2708,0x8018,0x3008,0x8008,0x3208, + 0x8008,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704, + 0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704, + 0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704,0x2704, + 0x2704,0x2704,0x2704,0x2704,0x2B04,0x3204,0x3704,0x3B04,0x3704,0x4204, + 0x3B04,0x3708,0x2706,0x2702,0x2708,0x8008, + 0x0000 +}; + + + +/* Screen sizes */ +#ifdef __CBM610__ +# define MAX_X 80 +#else +# define MAX_X 40 +#endif + + + +#ifdef __C64__ +static unsigned long FreqTab [12] = { +#ifndef NTSC + /* PAL */ + 0x008B38, 0x009381, 0x009C45, 0x00A590, 0x00AF68, 0x00B9D6, + 0x00C4E4, 0x00D099, 0x00DCFF, 0x00EA24, 0x00F810, 0x0106D1, +#else + /* NTSC */ + 0x00861E, 0x008E19, 0x00968B, 0x009F7F, 0x00A8FA, 0x00B307, + 0x00BDAD, 0x00C8F4, 0x00D4E6, 0x00E18F, 0x00EEF9, 0x00FD2F, +#endif +}; +#endif + +#ifdef __C128__ +static unsigned long FreqTab [12] = { + 0x00892B, 0x009153, 0x0099F7, 0x00A31E, 0x00ACD2, 0x00B718, + 0x00C1FD, 0x00CD85, 0x00D9BD, 0x00E6B0, 0x00F467, 0x0102F0, +}; +#endif + +#ifdef __CBM610__ +static unsigned long FreqTab [12] = { + 0x004495, 0x0048AA, 0x004CFB, 0x00518F, 0x005669, 0x005B8C, + 0x0060FE, 0x0066C3, 0x006CDE, 0x007358, 0x007A34, 0x008178, +}; +#endif + + + +typedef struct { + unsigned char DoneMask; /* Set this if we're done */ + unsigned char Trigger; /* Trigger value */ + unsigned char Ticks; /* Ticks for this tone */ + unsigned Freq; /* Actual frequency value */ + int* Data; /* Pointer to data */ + struct __sid_voice* Voice; /* Pointer to sid registers */ +} VoiceCtrl; + +/* Control structs for all three voices */ +static VoiceCtrl V1 = { + 0x01, 0x11, 0, 0, Voice1, &SID.v1 +}; +static VoiceCtrl V2 = { + 0x02, 0x41, 0, 0, Voice2, &SID.v2 +}; +static VoiceCtrl V3 = { + 0x04, 0x11, 0, 0, Voice3, &SID.v3 +}; + +/* Pointers to the structs for easy reference */ +static VoiceCtrl* V [3] = { + &V1, &V2, &V3 +}; + +/* Variable that contains the time of the next clock tick to play a note */ +static unsigned char NextClock; + +/* Start- and runtime */ +static clock_t StartTime; + +/* Number of ticks for each tone */ +#define TICKS_PER_TONE 4 + +/* Done flag. Contains one bit for each voice. Will contain 0x07 if all + * voices have finished playing. + */ +static unsigned char Done; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void MakeTeeLine (unsigned char Y) +/* Make a divider line */ +{ + cputcxy (0, Y, CH_LTEE); + chline (MAX_X - 2); + cputc (CH_RTEE); +} + + + +static void MakeNiceScreen (void) +/* Make a nice screen */ +{ + typedef struct { + unsigned char X; + unsigned char Y; + char* Msg; + } TextDesc; + static TextDesc Text [] = { + { (MAX_X / 2) - 11, 2, "Wolfgang Amadeus Mozart" }, + { (MAX_X / 2) - 12, 4, "\"Eine kleine Nachtmusik\"" }, + { (MAX_X / 2) - 4, 5, "(KV 525)" }, + { (MAX_X / 2) - 14, 9, "Ported to the SID in 1987 by" }, + { (MAX_X / 2) - 10, 11, "Joachim von Bassewitz" }, + { (MAX_X / 2) - 13, 12, "(joachim@von-bassewitz.de)" }, + { (MAX_X / 2) - 1, 13, "and" }, + { (MAX_X / 2) - 10, 14, "Ullrich von Bassewitz" }, + { (MAX_X / 2) - 13, 15, "(ullrich@von-bassewitz.de)" }, + { (MAX_X / 2) - 9, 18, "C Implementation by" }, + { (MAX_X / 2) - 10, 19, "Ullrich von Bassewitz" }, + { (MAX_X / 2) - 11, 23, "Press any key to quit..." }, + }; + + TextDesc* T; + unsigned char I; + + /* Clear the screen hide the cursor, set colors */ +#ifdef __CBM610__ + textcolor (COLOR_WHITE); +#else + textcolor (COLOR_GRAY3); +#endif + bordercolor (COLOR_BLACK); + bgcolor (COLOR_BLACK); + clrscr (); + cursor (0); + + /* Top line */ + cputcxy (0, 0, CH_ULCORNER); + chline (MAX_X - 2); + cputc (CH_URCORNER); + + /* Left line */ + cvlinexy (0, 1, 23); + + /* Bottom line */ + cputc (CH_LLCORNER); + chline (MAX_X - 2); + cputc (CH_LRCORNER); + + /* Right line */ + cvlinexy (MAX_X - 1, 1, 23); + + /* Several divider lines */ + MakeTeeLine (7); + MakeTeeLine (22); + + /* Write something into the frame */ + for (I = 0, T = Text; I < sizeof (Text) / sizeof (Text [0]); ++I) { + cputsxy (T->X, T->Y, T->Msg); + ++T; + } +} + + + +static void TimeSync (void) +/* Sync the time for the next tone */ +{ + static unsigned char Clock; + + do { + Clock = clock (); + } while (Clock != NextClock); + NextClock = Clock + TICKS_PER_TONE; +} + + + +static void DisplayTime (void) +/* Display the running time */ +{ + clock_t Time = (clock () - StartTime) / CLOCKS_PER_TICK; + unsigned Sec = Time % 60; + unsigned Min = Time / 60; + + gotoxy (1, 0); + cprintf ("%02d:%02d", Min, Sec); +} + + + +/* On the 610, the SID is in another bank (the system bank), so we cannot + * just write to the memory space. + */ +#ifdef __CBM610__ +# define outb(addr,val) pokebsys ((unsigned)(addr), val) +# define outw(addr,val) pokewsys ((unsigned)(addr), val) +#else +# define outb(addr,val) (*(addr)) = (val) +# define outw(addr,val) (*(addr)) = (val) +#endif + + + +int main (void) +{ + unsigned char I; + unsigned char Tone; + unsigned char Octave; + unsigned Val; + struct __sid_voice* Voice; + VoiceCtrl* VC; + + /* Initialize the debugger */ + DbgInit (0); + + /* Make a nice screen */ + MakeNiceScreen (); + + /* Initialize the SID */ + outw (&SID.v1.pw, 0x0800); + outb (&SID.v1.ad, 0x33); + outb (&SID.v1.sr, 0xF0); + outw (&SID.v2.pw, 0x0800); + outb (&SID.v2.ad, 0x77); + outb (&SID.v2.sr, 0x74); + outw (&SID.v3.pw, 0x0800); + outb (&SID.v3.ad, 0x77); + outb (&SID.v3.sr, 0xD2); + outw (&SID.flt_freq, 0xF0F0); + outb (&SID.flt_ctrl, 0xF2); + outb (&SID.amp, 0x5F); + + /* Sync the clock */ + NextClock = StartTime = clock (); + NextClock += TICKS_PER_TONE; + + /* Play each voice until all three are done */ + while (Done != 0x07) { + + /* Display the time in the lower left corner */ + DisplayTime (); + + /* Wait for the next run */ + TimeSync (); + + /* Check for a key */ + if (kbhit ()) { + if (cgetc () == 'd') { + /* Start the debugger */ + BREAK (); + } else { + /* Stop playing music */ + break; + } + } + + /* Play all three voices */ + for (I = 0; I < 3; ++I) { + + /* Get a pointer to this voice */ + VC = V [I]; + Voice = VC->Voice; + + /* Is this voice done? */ + if (Done & VC->DoneMask) { + /* Voice already done */ + continue; + } + + /* Do we have any more ticks to play? */ + if (VC->Ticks == 0) { + /* We need new data */ + if ((Val = *VC->Data) == 0) { + /* End of data. Mark the voice as done */ + Done |= VC->DoneMask; + continue; + } + ++VC->Data; + + /* Get the ticks from the data */ + VC->Ticks = (Val & 0x7F) - 1; + + /* Check if this is a tone or a pause */ + if (Val & 0x8000) { + /* This is a pause. Remember it and shut off the SID */ + outb (&Voice->ctrl, VC->Trigger & 0xFE); + } else { + /* This is a tone. Extract the attributes. */ + Tone = (Val >> 8) & 0x0F; + Octave = ((Val >> 12) & 0x07) ^ 0x07; + /* Calculate the frequency */ + VC->Freq = FreqTab [Tone] >> Octave; + /* Set the frequency */ + outw (&Voice->freq, VC->Freq); + /* Start the tone */ + outb (&Voice->ctrl, VC->Trigger); + } + } else { + /* Decrement the ticks. If this is the last tick of a tone, + * reset bit 0 of the trigger value and write it back to the + * SID to start the release phase. + */ + if (--(VC->Ticks) == 0) { + outb (&Voice->ctrl, VC->Trigger & 0xFE); + } + } + } + } + + /* Reset the SID */ + outb (&SID.v1.ctrl, 0x00); + outb (&SID.v2.ctrl, 0x00); + outb (&SID.v3.ctrl, 0x00); + + /* Clear the screen */ + clrscr (); + + /* If we have a character, remove it from the buffer */ + if (kbhit ()) { + cgetc (); + } + + /* Done */ + return 0; +} + + + diff --git a/samples/sieve.c b/samples/sieve.c new file mode 100644 index 000000000..24e5601cb --- /dev/null +++ b/samples/sieve.c @@ -0,0 +1,67 @@ +/* + * Calculate all primes up to a specific number. + */ + + + +#include +#include + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +#define COUNT 8192 /* Up to what number? */ +#define SQRT_COUNT 91 /* Sqrt of COUNT */ + +static unsigned char Sieve[COUNT]; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int main (void) +{ + /* This is an example where register variables make sense */ + register unsigned char* S; + register unsigned I; + register unsigned J; + + /* Execute the sieve */ + I = 2; + while (I < SQRT_COUNT) { + if (Sieve[I] == 0) { + /* Prime number - mark multiples */ + S = &Sieve[J = I*2]; + while (J < COUNT) { + *S = 1; + S += I; + J += I; + } + } + ++I; + } + + /* Print the result */ + for (I = 2; I < COUNT; ++I) { + if (Sieve[I] == 0) { + printf ("%4d\n", I); + } + if (kbhit() && cgetc() == 'q') { + break; + } + } + + return 0; +} + + + diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 000000000..a8dca6f3e --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1 @@ +*.obj diff --git a/src/ar65/.cvsignore b/src/ar65/.cvsignore new file mode 100644 index 000000000..765a70274 --- /dev/null +++ b/src/ar65/.cvsignore @@ -0,0 +1,3 @@ +.depend +ar65 + diff --git a/src/ar65/add.c b/src/ar65/add.c new file mode 100644 index 000000000..1ca0ff14f --- /dev/null +++ b/src/ar65/add.c @@ -0,0 +1,83 @@ +/*****************************************************************************/ +/* */ +/* add.c */ +/* */ +/* Object file adding for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "error.h" +#include "objfile.h" +#include "library.h" +#include "add.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void AddObjFiles (int argc, char* argv []) +/* Add object files to a library */ +{ + int I; + + /* Check the argument count */ + if (argc <= 0) { + Error ("No library name given"); + } + if (argc <= 1) { + Error ("No object files to add"); + } + + /* Open the library, read the index */ + LibOpen (argv [0], 0, 1); + + /* Add the object files */ + I = 1; + while (I < argc) { + ObjAdd (argv [I]); + ++I; + } + + /* Create a new library file and close the old one */ + LibClose (); + + /* Successful end */ + exit (EXIT_SUCCESS); +} + + + + diff --git a/src/ar65/add.h b/src/ar65/add.h new file mode 100644 index 000000000..38bf8682a --- /dev/null +++ b/src/ar65/add.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +/* */ +/* add.h */ +/* */ +/* Object file adding for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ADD_H +#define ADD_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void AddObjFiles (int argc, char* argv []); +/* Add object files to a library */ + + + +/* End of add.h */ + +#endif + + + + diff --git a/src/ar65/del.c b/src/ar65/del.c new file mode 100644 index 000000000..a94be258f --- /dev/null +++ b/src/ar65/del.c @@ -0,0 +1,84 @@ +/*****************************************************************************/ +/* */ +/* del.h */ +/* */ +/* Object file deleting for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "error.h" +#include "objdata.h" +#include "library.h" +#include "del.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void DelObjFiles (int argc, char* argv []) +/* Delete modules from a library */ +{ + int I; + + /* Check the argument count */ + if (argc <= 0) { + Error ("No library name given"); + } + if (argc <= 1) { + Error ("No modules to delete"); + } + + /* Open the library, read the index */ + LibOpen (argv [0], 1, 1); + + /* Delete the modules */ + I = 1; + while (I < argc) { + /* Delete the module from the list */ + DelObjData (argv [I]); + ++I; + } + + /* Create a new library file and close the old one */ + LibClose (); + + /* Successful end */ + exit (EXIT_SUCCESS); +} + + + + diff --git a/src/ar65/del.h b/src/ar65/del.h new file mode 100644 index 000000000..18f6c3cc0 --- /dev/null +++ b/src/ar65/del.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +/* */ +/* del.h */ +/* */ +/* Object file deleting for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef DEL_H +#define DEL_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void DelObjFiles (int argc, char* argv []); +/* Delete modules from a library */ + + + +/* End of del.h */ + +#endif + + + + diff --git a/src/ar65/error.c b/src/ar65/error.c new file mode 100644 index 000000000..a8b7a83b7 --- /dev/null +++ b/src/ar65/error.c @@ -0,0 +1,106 @@ +/*****************************************************************************/ +/* */ +/* global.c */ +/* */ +/* Error handling for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "error.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Messages for internal compiler errors */ +const char _MsgCheckFailed [] = + "Check failed: `%s' (= %d), file `%s', line %u\n"; +const char _MsgPrecondition [] = + "Precondition violated: `%s' (= %d), file `%s', line %u\n"; +const char _MsgFail [] = + "%s, file `%s', line %u\n"; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Warning (const char* Format, ...) +/* Print a warning message */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "Warning: "); + vfprintf (stderr, Format, ap); + putc ('\n', stderr); + va_end (ap); +} + + + +void Error (const char* Format, ...) +/* Print an error message and die */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "Error: "); + vfprintf (stderr, Format, ap); + putc ('\n', stderr); + va_end (ap); + exit (EXIT_FAILURE); +} + + + +void Internal (const char* Format, ...) +/* Print an internal error message and die */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "Internal error: "); + vfprintf (stderr, Format, ap); + putc ('\n', stderr); + va_end (ap); + exit (EXIT_FAILURE); +} + + + diff --git a/src/ar65/error.h b/src/ar65/error.h new file mode 100644 index 000000000..be36010f5 --- /dev/null +++ b/src/ar65/error.h @@ -0,0 +1,87 @@ +/*****************************************************************************/ +/* */ +/* global.h */ +/* */ +/* Error handling for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ERROR_H +#define ERROR_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Messages for internal compiler errors */ +extern const char _MsgCheckFailed []; +extern const char _MsgPrecondition []; +extern const char _MsgFail []; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Warning (const char* Format, ...); +/* Print a warning message */ + +void Error (const char* Format, ...); +/* Print an error message and die */ + +void Internal (const char* Format, ...); +/* Print an internal error message and die */ + +#define CHECK(c) \ + if (!(c)) \ + Internal (_MsgCheckFailed, #c, c, __FILE__, __LINE__) + +#define PRECONDITION(c) \ + if (!(c)) \ + Internal (_MsgPrecondition, #c, c, __FILE__, __LINE__) + +#define FAIL(s) \ + Internal (_MsgFail, s, __FILE__, __LINE__) + + + +/* End of error.h */ + +#endif + + + diff --git a/src/ar65/exports.c b/src/ar65/exports.c new file mode 100644 index 000000000..9c1ad9d14 --- /dev/null +++ b/src/ar65/exports.c @@ -0,0 +1,149 @@ +/*****************************************************************************/ +/* */ +/* exports.c */ +/* */ +/* Duplicate export checking for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "../common/hashstr.h" + +#include "mem.h" +#include "error.h" +#include "objdata.h" +#include "exports.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* A hash table entry */ +typedef struct HashEntry_ HashEntry; +struct HashEntry_ { + HashEntry* Next; /* Next in list */ + unsigned Module; /* Module index */ + char Name [1]; /* Name of identifier */ +}; + +/* Hash table */ +#define HASHTAB_SIZE 4783 +static HashEntry* HashTab [HASHTAB_SIZE]; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static HashEntry* NewHashEntry (const char* Name, unsigned Module) +/* Create and return a new hash entry */ +{ + /* Get the length of the name */ + unsigned Len = strlen (Name); + + /* Get memory for the struct */ + HashEntry* H = Xmalloc (sizeof (HashEntry) + Len); + + /* Initialize the fields and return it */ + H->Next = 0; + H->Module = Module; + memcpy (H->Name, Name, Len); + H->Name [Len] = '\0'; + return H; +} + + + +void ExpInsert (const char* Name, unsigned Module) +/* Insert an exported identifier and check if it's already in the list */ +{ + HashEntry* L; + + /* Create a hash value for the given name */ + unsigned HashVal = HashStr (Name) % HASHTAB_SIZE; + + /* Create a new hash entry */ + HashEntry* H = NewHashEntry (Name, Module); + + /* Search through the list in that slot and print matching duplicates */ + if (HashTab [HashVal] == 0) { + /* The slot is empty */ + HashTab [HashVal] = H; + return; + } + L = HashTab [HashVal]; + while (1) { + if (strcmp (L->Name, Name) == 0) { + /* Duplicate entry */ + Warning ("External symbol `%s' in module `%s' is duplicated in " + "module `%s'", + Name, GetObjName (L->Module), GetObjName (Module)); + } + if (L->Next == 0) { + break; + } else { + L = L->Next; + } + } + L->Next = H; +} + + + +int ExpFind (const char* Name) +/* Check for an identifier in the list. Return -1 if not found, otherwise + * return the number of the module, that exports the identifer. + */ +{ + /* Get a pointer to the list with the symbols hash value */ + HashEntry* L = HashTab [HashStr (Name) % HASHTAB_SIZE]; + while (L) { + /* Search through the list in that slot */ + if (strcmp (L->Name, Name) == 0) { + /* Entry found */ + return L->Module; + } + L = L->Next; + } + + /* Not found */ + return -1; +} + + + diff --git a/src/ar65/exports.h b/src/ar65/exports.h new file mode 100644 index 000000000..4ae8fc1f2 --- /dev/null +++ b/src/ar65/exports.h @@ -0,0 +1,62 @@ +/*****************************************************************************/ +/* */ +/* exports.h */ +/* */ +/* Duplicate export checking for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef EXPORTS_H +#define EXPORTS_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ExpInsert (const char* Name, unsigned Module); +/* Insert an exported identifier and check if it's already in the list */ + +int ExpFind (const char* Name); +/* Check for an identifier in the list. Return -1 if not found, otherwise + * return the number of the module, that exports the identifer. + */ + + + +/* End of exports.h */ + +#endif + + + diff --git a/src/ar65/extract.c b/src/ar65/extract.c new file mode 100644 index 000000000..aba4f6b34 --- /dev/null +++ b/src/ar65/extract.c @@ -0,0 +1,83 @@ +/*****************************************************************************/ +/* */ +/* extract.c */ +/* */ +/* Object file extraction for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "mem.h" +#include "error.h" +#include "objfile.h" +#include "library.h" +#include "extract.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ExtractObjFiles (int argc, char* argv []) +/* Extract object files from a library */ +{ + int I; + + /* Check the argument count */ + if (argc <= 0) { + Error ("No library name given"); + } + if (argc <= 1) { + Error ("No object files to add"); + } + + /* Open the library, read the index */ + LibOpen (argv [0], 1, 0); + + /* Extract the object files */ + I = 1; + while (I < argc) { + ObjExtract (argv [I]); + ++I; + } + + /* Create a new library file and close the old one */ + LibClose (); + + /* Successful end */ + exit (EXIT_SUCCESS); +} + + + diff --git a/src/ar65/extract.h b/src/ar65/extract.h new file mode 100644 index 000000000..c413fd8bc --- /dev/null +++ b/src/ar65/extract.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +/* */ +/* extract.h */ +/* */ +/* Object file extraction for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef EXTRACT_H +#define EXTRACT_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ExtractObjFiles (int argc, char* argv []); +/* Extract object files from a library */ + + + +/* End of extract.h */ + +#endif + + + + diff --git a/src/ar65/fileio.c b/src/ar65/fileio.c new file mode 100644 index 000000000..696b4ff20 --- /dev/null +++ b/src/ar65/fileio.c @@ -0,0 +1,210 @@ +/*****************************************************************************/ +/* */ +/* fileio.c */ +/* */ +/* File I/O for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "error.h" +#include "mem.h" +#include "fileio.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Write8 (FILE* F, unsigned char Val) +/* Write an 8 bit value to the file */ +{ + if (putc (Val, F) == EOF) { + Error ("Write error (disk full?)"); + } +} + + + +void Write16 (FILE* F, unsigned Val) +/* Write a 16 bit value to the file */ +{ + Write8 (F, (unsigned char) Val); + Write8 (F, (unsigned char) (Val >> 8)); +} + + + +void Write32 (FILE* F, unsigned long Val) +/* Write a 32 bit value to the file */ +{ + Write8 (F, (unsigned char) Val); + Write8 (F, (unsigned char) (Val >> 8)); + Write8 (F, (unsigned char) (Val >> 16)); + Write8 (F, (unsigned char) (Val >> 24)); +} + + + +void WriteStr (FILE* F, const char* S) +/* Write a string to the file */ +{ + unsigned Len = strlen (S); + if (Len > 255) { + Internal ("String too long"); + } + Write8 (F, (unsigned char) Len); + WriteData (F, S, Len); +} + + + +void WriteData (FILE* F, const void* Data, unsigned Size) +/* Write data to the file */ +{ + if (fwrite (Data, 1, Size, F) != Size) { + Error ("Write error (disk full?)"); + } +} + + + +unsigned Read8 (FILE* F) +/* Read an 8 bit value from the file */ +{ + int C = getc (F); + if (C == EOF) { + Error ("Read error (file corrupt?)"); + } + return C; +} + + + +unsigned Read16 (FILE* F) +/* Read a 16 bit value from the file */ +{ + unsigned Lo = Read8 (F); + unsigned Hi = Read8 (F); + return (Hi << 8) | Lo; +} + + + +unsigned long Read32 (FILE* F) +/* Read a 32 bit value from the file */ +{ + unsigned long Lo = Read16 (F); + unsigned long Hi = Read16 (F); + return (Hi << 16) | Lo; +} + + + +char* ReadStr (FILE* F) +/* Read a string from the file (the memory will be malloc'ed) */ +{ + /* Read the length byte */ + unsigned Len = Read8 (F); + + /* Allocate memory and read the string itself */ + char* S = Xmalloc (Len + 1); + ReadData (F, S, Len); + + /* Terminate the string and return it */ + S [Len] = '\0'; + return S; +} + + + +void* ReadData (FILE* F, void* Data, unsigned Size) +/* Read data from the file */ +{ + if (fread (Data, 1, Size, F) != Size) { + Error ("Read error (file corrupt?)"); + } + return Data; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ar65/fileio.h b/src/ar65/fileio.h new file mode 100644 index 000000000..e38c46082 --- /dev/null +++ b/src/ar65/fileio.h @@ -0,0 +1,88 @@ +/*****************************************************************************/ +/* */ +/* fileio.h */ +/* */ +/* File I/O for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef FILEIO_H +#define FILEIO_H + + + +#include + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Write8 (FILE* F, unsigned char Val); +/* Write an 8 bit value to the file */ + +void Write16 (FILE* F, unsigned Val); +/* Write a 16 bit value to the file */ + +void Write32 (FILE* F, unsigned long Val); +/* Write a 32 bit value to the file */ + +void WriteStr (FILE* F, const char* S); +/* Write a string to the file */ + +void WriteData (FILE* F, const void* Data, unsigned Size); +/* Write data to the file */ + +unsigned Read8 (FILE* F); +/* Read an 8 bit value from the file */ + +unsigned Read16 (FILE* F); +/* Read a 16 bit value from the file */ + +unsigned long Read32 (FILE* F); +/* Read a 32 bit value from the file */ + +char* ReadStr (FILE* F); +/* Read a string from the file (the memory will be malloc'ed) */ + +void* ReadData (FILE* F, void* Data, unsigned Size); +/* Read data from the file */ + + + +/* End of fileio.h */ + +#endif + + + diff --git a/src/ar65/global.c b/src/ar65/global.c new file mode 100644 index 000000000..cc37aa28f --- /dev/null +++ b/src/ar65/global.c @@ -0,0 +1,51 @@ +/*****************************************************************************/ +/* */ +/* global.c */ +/* */ +/* Global variables for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "global.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +const char* ProgName = "ar65"; /* Program name */ + +int Verbose = 0; /* Verbose operation flag */ + + + diff --git a/src/ar65/global.h b/src/ar65/global.h new file mode 100644 index 000000000..2a01feab8 --- /dev/null +++ b/src/ar65/global.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +/* */ +/* global.h */ +/* */ +/* Global variables for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef GLOBAL_H +#define GLOBAL_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +extern const char* ProgName; /* Program name */ + +extern int Verbose; /* Verbose operation flag */ + + + +/* End of global.h */ + +#endif + + + diff --git a/src/ar65/library.c b/src/ar65/library.c new file mode 100644 index 000000000..6fd3e7d46 --- /dev/null +++ b/src/ar65/library.c @@ -0,0 +1,470 @@ +/*****************************************************************************/ +/* */ +/* library.c */ +/* */ +/* Library data structures and helpers for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "../common/libdefs.h" +#include "../common/symdefs.h" +#include "../common/exprdefs.h" +#include "../common/filepos.h" +#include "../common/bitops.h" + +#include "mem.h" +#include "error.h" +#include "global.h" +#include "fileio.h" +#include "objdata.h" +#include "exports.h" +#include "library.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* File descriptor for the library file */ +FILE* NewLib = 0; +static FILE* Lib = 0; +static const char* LibName = 0; + +/* The library header */ +static LibHeader Header = { + LIB_MAGIC, + LIB_VERSION +}; + + + +/*****************************************************************************/ +/* Writing file data structures */ +/*****************************************************************************/ + + + +static void ReadHeader (void) +/* Read the header of a library file */ +{ + /* Seek to position zero */ + fseek (Lib, 0, SEEK_SET); + + /* Read the header fields, checking magic and version */ + Header.Magic = Read32 (Lib); + if (Header.Magic != LIB_MAGIC) { + Error ("`%s' is not a valid library file", LibName); + } + Header.Version = Read16 (Lib); + if (Header.Version != LIB_VERSION) { + Error ("Wrong data version in `%s'", LibName); + } + Header.Flags = Read16 (Lib); + Header.IndexOffs = Read32 (Lib); +} + + + +static void ReadIndexEntry (void) +/* Read one entry in the index */ +{ + /* Create a new entry and insert it into the list */ + ObjData* O = NewObjData (); + + /* Module name/flags/MTime/Start/Size */ + O->Name = ReadStr (Lib); + O->Flags = Read16 (Lib); + O->MTime = Read32 (Lib); + O->Start = Read32 (Lib); + O->Size = Read32 (Lib); + + /* Exports */ + O->ExportSize = Read16 (Lib); + O->Exports = Xmalloc (O->ExportSize); + ReadData (Lib, O->Exports, O->ExportSize); + + /* Imports */ + O->ImportSize = Read16 (Lib); + O->Imports = Xmalloc (O->ImportSize); + ReadData (Lib, O->Imports, O->ImportSize); +} + + + +static void ReadIndex (void) +/* Read the index of a library file */ +{ + unsigned Count; + + /* Seek to the start of the index */ + fseek (Lib, Header.IndexOffs, SEEK_SET); + + /* Read the object file count and calculate the cross ref size */ + Count = Read16 (Lib); + + /* Read all entries in the index */ + while (Count--) { + ReadIndexEntry (); + } +} + + + +/*****************************************************************************/ +/* Writing file data structures */ +/*****************************************************************************/ + + + +static void WriteHeader (void) +/* Write the header to the library file */ +{ + /* Seek to position zero */ + fseek (NewLib, 0, SEEK_SET); + + /* Write the header fields */ + Write32 (NewLib, Header.Magic); + Write16 (NewLib, Header.Version); + Write16 (NewLib, Header.Flags); + Write32 (NewLib, Header.IndexOffs); +} + + + +static void WriteIndexEntry (ObjData* O) +/* Write one index entry */ +{ + /* Module name/flags/MTime/start/size */ + WriteStr (NewLib, O->Name); + Write16 (NewLib, O->Flags & ~OBJ_HAVEDATA); + Write32 (NewLib, O->MTime); + Write32 (NewLib, O->Start); + Write32 (NewLib, O->Size); + + /* Exports */ + Write16 (NewLib, O->ExportSize); + WriteData (NewLib, O->Exports, O->ExportSize); + + /* Imports */ + Write16 (NewLib, O->ImportSize); + WriteData (NewLib, O->Imports, O->ImportSize); +} + + + +static void WriteIndex (void) +/* Write the index of a library file */ +{ + ObjData* O; + + /* Sync I/O in case the last operation was a read */ + fseek (NewLib, 0, SEEK_CUR); + + /* Remember the current offset in the header */ + Header.IndexOffs = ftell (NewLib); + + /* Write the object file count */ + Write16 (NewLib, ObjCount); + + /* Write the object files */ + O = ObjRoot; + while (O) { + WriteIndexEntry (O); + O = O->Next; + } +} + + + +/*****************************************************************************/ +/* High level stuff */ +/*****************************************************************************/ + + + +void LibOpen (const char* Name, int MustExist, int NeedTemp) +/* Open an existing library and a temporary copy. If MustExist is true, the + * old library is expected to exist. If NeedTemp is true, a temporary library + * is created. + */ +{ + /* Remember the name */ + LibName = StrDup (Name); + + /* Open the existing library for reading */ + Lib = fopen (Name, "rb"); + if (Lib == 0) { + + /* File does not exist */ + if (MustExist) { + Error ("Library `%s' does not exist", Name); + } else { + Warning ("Library `%s' not found - will be created", Name); + } + + } else { + + /* We have an existing file: Read the header */ + ReadHeader (); + + /* Now read the existing index */ + ReadIndex (); + + } + + if (NeedTemp) { + /* Create the temporary library */ + NewLib = tmpfile (); + if (NewLib == 0) { + Error ("Cannot create temporary file: %s", strerror (errno)); + } + + /* Write a dummy header to the temp file */ + WriteHeader (); + } +} + + + +unsigned long LibCopyTo (FILE* F, unsigned long Bytes) +/* Copy data from F to the temp library file, return the start position in + * the temporary library file. + */ +{ + unsigned char Buf [4096]; + + /* Remember the position */ + unsigned long Pos = ftell (NewLib); + + /* Copy loop */ + while (Bytes) { + unsigned Count = (Bytes > sizeof (Buf))? sizeof (Buf) : Bytes; + ReadData (F, Buf, Count); + WriteData (NewLib, Buf, Count); + Bytes -= Count; + } + + /* Return the start position */ + return Pos; +} + + + +void LibCopyFrom (unsigned long Pos, unsigned long Bytes, FILE* F) +/* Copy data from the library file into another file */ +{ + unsigned char Buf [4096]; + + /* Seek to the correct position */ + fseek (Lib, Pos, SEEK_SET); + + /* Copy loop */ + while (Bytes) { + unsigned Count = (Bytes > sizeof (Buf))? sizeof (Buf) : Bytes; + ReadData (Lib, Buf, Count); + WriteData (F, Buf, Count); + Bytes -= Count; + } +} + + + +static void SkipExpr (unsigned char** Buf) +/* Skip an expression in Buf */ +{ + /* Get the operation and skip it */ + unsigned char Op = **Buf; + ++(*Buf); + + /* Filter leaf nodes */ + switch (Op) { + + case EXPR_NULL: + return; + + case EXPR_LITERAL: + /* 32 bit literal value */ + *Buf += 4; + return; + + case EXPR_SYMBOL: + /* 16 bit symbol index */ + *Buf += 2; + return; + + case EXPR_SEGMENT: + /* 8 bit segment number */ + *Buf += 1; + return; + } + + /* What's left are unary and binary nodes */ + SkipExpr (Buf); /* Skip left */ + SkipExpr (Buf); /* Skip right */ +} + + + +static void LibCheckExports (ObjData* O) +/* Insert all exports from the given object file into the global list + * checking for duplicates. + */ +{ + char Name [256]; + + /* Get a pointer to the buffer */ + unsigned char* Exports = O->Exports; + + /* First two bytes are export count */ + unsigned Lo = *Exports++; + unsigned Hi = *Exports++; + unsigned Count = (Hi << 8) + Lo; + + /* Read the exports */ + if (Verbose > 1) { + printf ("Module `%s' (%u exports):\n", O->Name, Count); + } + while (Count--) { + + unsigned char Len; + + /* Get the export tag */ + unsigned char Tag = *Exports++; + + /* Next thing is name of symbol */ + Len = *Exports++; + memcpy (Name, Exports, Len); + Name [Len] = '\0'; + Exports += Len; + + /* Skip value of symbol */ + if (Tag & EXP_EXPR) { + /* Expression tree */ + SkipExpr (&Exports); + } else { + /* Constant 32 bit value */ + Exports += 4; + } + + /* Skip the position */ + Exports += POS_SIZE; + + /* Insert the name into the hash table */ + if (Verbose > 1) { + printf (" %s\n", Name); + } + ExpInsert (Name, O->Index); + } +} + + + +void LibClose (void) +/* Write remaining data, close both files and copy the temp file to the old + * filename + */ +{ + /* Do we have a temporary library? */ + if (NewLib) { + + unsigned I; + unsigned char Buf [4096]; + size_t Count; + + /* Index the object files and make an array containing the objects */ + MakeObjPool (); + + /* Walk through the object file list, inserting exports into the + * export list checking for duplicates. Copy any data that is still + * in the old library into the new one. + */ + for (I = 0; I < ObjCount; ++I) { + + /* Get a pointer to the object */ + ObjData* O = ObjPool [I]; + + /* Check exports, make global export table */ + LibCheckExports (O); + + /* Copy data if needed */ + if ((O->Flags & OBJ_HAVEDATA) == 0) { + /* Data is still in the old library */ + fseek (Lib, O->Start, SEEK_SET); + O->Start = ftell (NewLib); + LibCopyTo (Lib, O->Size); + O->Flags |= OBJ_HAVEDATA; + } + } + + /* Write the index */ + WriteIndex (); + + /* Write the updated header */ + WriteHeader (); + + /* Close the file */ + if (Lib && fclose (Lib) != 0) { + Error ("Error closing library: %s", strerror (errno)); + } + + /* Reopen the library and truncate it */ + Lib = fopen (LibName, "wb"); + if (Lib == 0) { + Error ("Cannot open library `%s' for writing: %s", + LibName, strerror (errno)); + } + + /* Copy the new library to the new one */ + fseek (NewLib, 0, SEEK_SET); + while ((Count = fread (Buf, 1, sizeof (Buf), NewLib)) != 0) { + if (fwrite (Buf, 1, Count, Lib) != Count) { + Error ("Cannot write to `%s': %s", LibName, strerror (errno)); + } + } + } + + /* Close both files */ + if (Lib && fclose (Lib) != 0) { + Error ("Problem closing `%s': %s", LibName, strerror (errno)); + } + if (NewLib && fclose (NewLib) != 0) { + Error ("Problem closing temporary library file: %s", strerror (errno)); + } +} + + + diff --git a/src/ar65/library.h b/src/ar65/library.h new file mode 100644 index 000000000..bd4b6edbd --- /dev/null +++ b/src/ar65/library.h @@ -0,0 +1,88 @@ +/*****************************************************************************/ +/* */ +/* library.h */ +/* */ +/* Library data structures and helpers for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef LIBRARY_H +#define LIBRARY_H + + + +#include + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* File descriptor for the new library file */ +extern FILE* NewLib; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void LibOpen (const char* Name, int MustExist, int NeedTemp); +/* Open an existing library and a temporary copy. If MustExist is true, the + * old library is expected to exist. If NeedTemp is true, a temporary library + * is created. + */ + +unsigned long LibCopyTo (FILE* F, unsigned long Bytes); +/* Copy data from F to the temp library file, return the start position in + * the temporary library file. + */ + +void LibCopyFrom (unsigned long Pos, unsigned long Bytes, FILE* F); +/* Copy data from the library file into another file */ + +void LibClose (void); +/* Write remaining data, close both files and copy the temp file to the old + * filename + */ + + + +/* End of library.h */ + +#endif + + + diff --git a/src/ar65/list.c b/src/ar65/list.c new file mode 100644 index 000000000..c54f65efe --- /dev/null +++ b/src/ar65/list.c @@ -0,0 +1,83 @@ +/*****************************************************************************/ +/* */ +/* list.c */ +/* */ +/* Module listing for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "error.h" +#include "objdata.h" +#include "library.h" +#include "list.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ListObjFiles (int argc, char* argv []) +/* List modules in a library */ +{ + ObjData* O; + + /* Check the argument count */ + if (argc <= 0) { + Error ("No library name given"); + } + if (argc > 2) { + Error ("Too many arguments"); + } + + /* Open the library, read the index */ + LibOpen (argv [0], 1, 0); + + /* List the modules */ + O = ObjRoot; + while (O) { + printf ("%s\n", O->Name); + O = O->Next; + } + + /* Create a new library file and close the old one */ + LibClose (); + + /* Successful end */ + exit (EXIT_SUCCESS); +} + + + diff --git a/src/ar65/list.h b/src/ar65/list.h new file mode 100644 index 000000000..09ac5080e --- /dev/null +++ b/src/ar65/list.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +/* */ +/* list.h */ +/* */ +/* Module listing for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef LIST_H +#define LIST_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ListObjFiles (int argc, char* argv []); +/* List modules in a library */ + + + +/* End of list.h */ + +#endif + + + + diff --git a/src/ar65/main.c b/src/ar65/main.c new file mode 100644 index 000000000..120190173 --- /dev/null +++ b/src/ar65/main.c @@ -0,0 +1,140 @@ +/*****************************************************************************/ +/* */ +/* main.c */ +/* */ +/* Main program for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include + +#include "../common/version.h" + +#include "global.h" +#include "error.h" +#include "mem.h" +#include "add.h" +#include "del.h" +#include "list.h" +#include "extract.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void Usage (void) +/* Print usage information and exit */ +{ + fprintf (stderr, + "Usage: %s lib file|module ...\n" + "Operation is one of:\n" + "\ta\tAdd modules\n" + "\td\tDelete modules\n" + "\tl\tList library contents\n" + "\tx\tExtract modules\n" + "\tV\tPrint the archiver version\n", + ProgName); + exit (EXIT_FAILURE); +} + + + +int main (int argc, char* argv []) +/* Assembler main program */ +{ + int I; + + /* We must have a file name */ + if (argc < 2) { + Usage (); + } + + /* Check the parameters */ + I = 1; + while (I < argc) { + + /* Get the argument */ + const char* Arg = argv [I]; + + /* Check for an option */ + if (strlen (Arg) != 1) { + Usage (); + } + switch (Arg [0]) { + + case 'a': + AddObjFiles (argc - I - 1, &argv [I+1]); + break; + + case 'd': + DelObjFiles (argc - I - 1, &argv [I+1]); + break; + + case 'l': + ListObjFiles (argc - I - 1, &argv [I+1]); + break; + + case 'v': + ++Verbose; + break; + + case 'x': + ExtractObjFiles (argc - I - 1, &argv [I+1]); + break; + + case 'V': + fprintf (stderr, + "ar65 V%u.%u.%u - (C) Copyright 1998-1999 Ullrich von Bassewitz\n", + VER_MAJOR, VER_MINOR, VER_PATCH); + break; + + default: + fprintf (stderr, "Unknown option: %s\n", Arg); + Usage (); + + } + + /* Next argument */ + ++I; + } + + /* Return an apropriate exit code */ + return EXIT_SUCCESS; +} + + + diff --git a/src/ar65/make/gcc.mak b/src/ar65/make/gcc.mak new file mode 100644 index 000000000..fae3bd38c --- /dev/null +++ b/src/ar65/make/gcc.mak @@ -0,0 +1,56 @@ +# +# gcc Makefile for ar65 +# + +CFLAGS = -g -O2 -Wall +CC = gcc +LDFLAGS = + +OBJS = add.o \ + del.o \ + error.o \ + exports.o \ + extract.o \ + fileio.o \ + global.o \ + library.o \ + list.o \ + main.o \ + mem.o \ + objdata.o \ + objfile.o + +LIBS = ../common/common.a + + +EXECS = ar65 + +.PHONY: all +ifeq (.depend,$(wildcard .depend)) +all : $(EXECS) +include .depend +else +all: depend + @$(MAKE) -f make/gcc.mak all +endif + + + +ar65: $(OBJS) $(LIBS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) + +clean: + rm -f *~ core + +zap: clean + rm -f *.o $(EXECS) .depend + +# ------------------------------------------------------------------------------ +# Make the dependencies + +.PHONY: depend dep +depend dep: $(OBJS:.o=.c) + @echo "Creating dependency information" + $(CC) -MM $^ > .depend + + diff --git a/src/ar65/make/watcom.mak b/src/ar65/make/watcom.mak new file mode 100644 index 000000000..7be93c97e --- /dev/null +++ b/src/ar65/make/watcom.mak @@ -0,0 +1,123 @@ +# +# ar65 Makefile for the Watcom compiler +# + +# ------------------------------------------------------------------------------ +# Generic stuff + +.AUTODEPEND +.SUFFIXES .ASM .C .CC .CPP +.SWAP + +AR = WLIB +LD = WLINK + +!if !$d(TARGET) +!if $d(__OS2__) +TARGET = OS2 +!else +TARGET = NT +!endif +!endif + +# target specific macros. +!if $(TARGET)==OS2 + +# --------------------- OS2 --------------------- +SYSTEM = os2v2 +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS32 + +# -------------------- DOS4G -------------------- +SYSTEM = dos4g +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS + +# --------------------- DOS --------------------- +SYSTEM = dos +CC = WCC +CCCFG = -bt=$(TARGET) -d1 -onatx -zp2 -2 -ml -zq -w2 + +!elif $(TARGET)==NT + +# --------------------- NT ---------------------- +SYSTEM = nt +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!else +!error +!endif + +# ------------------------------------------------------------------------------ +# Implicit rules + +.c.obj: + $(CC) $(CCCFG) $< + + +# ------------------------------------------------------------------------------ +# All library OBJ files + +OBJS = add.obj \ + del.obj \ + error.obj \ + exports.obj \ + extract.obj \ + fileio.obj \ + global.obj \ + library.obj \ + list.obj \ + main.obj \ + mem.obj \ + objdata.obj \ + objfile.obj + +LIBS = ..\common\common.lib + + +# ------------------------------------------------------------------------------ +# Main targets + +all: ar65 + +ar65: ar65.exe + + +# ------------------------------------------------------------------------------ +# Other targets + + +ar65.exe: $(OBJS) $(LIBS) + $(LD) system $(SYSTEM) @&&| +DEBUG ALL +OPTION QUIET +NAME $< +FILE add.obj +FILE del.obj +FILE error.obj +FILE exports.obj +FILE extract.obj +FILE fileio.obj +FILE global.obj +FILE library.obj +FILE list.obj +FILE main.obj +FILE mem.obj +FILE objdata.obj +FILE objfile.obj +LIBRARY ..\common\common.lib +| + + +clean: + @if exist *.obj del *.obj + @if exist ar65.exe del ar65.exe + +strip: + @-wstrip ar65.exe + diff --git a/src/ar65/mem.c b/src/ar65/mem.c new file mode 100644 index 000000000..7d5d175c7 --- /dev/null +++ b/src/ar65/mem.c @@ -0,0 +1,84 @@ +/*****************************************************************************/ +/* */ +/* mem.c */ +/* */ +/* Memory allocation for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "error.h" +#include "mem.h" + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void* Xmalloc (size_t size) +/* Allocate memory, check for out of memory condition. Do some debugging */ +{ + void* p; + + p = malloc (size); + if (p == 0 && size != 0) { + Error ("Out of memory"); + } + + /* Return a pointer to the block */ + return p; +} + + + +void Xfree (const void* block) +/* Free the block, do some debugging */ +{ + free ((void*) block); +} + + + +char* StrDup (const char* s) +/* Duplicate a string on the heap. The function checks for out of memory */ +{ + unsigned len; + + len = strlen (s) + 1; + return memcpy (Xmalloc (len), s, len); +} + + + diff --git a/src/ar65/mem.h b/src/ar65/mem.h new file mode 100644 index 000000000..a896131d4 --- /dev/null +++ b/src/ar65/mem.h @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* */ +/* mem.h */ +/* */ +/* Memory allocation for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef MEM_H +#define MEM_H + + + +#include + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void* Xmalloc (size_t size); +/* Allocate memory, check for out of memory condition. Do some debugging */ + +void Xfree (const void* block); +/* Free the block, do some debugging */ + +char* StrDup (const char* s); +/* Duplicate a string on the heap. The function checks for out of memory */ + + + +/* End of mem.h */ + +#endif + + + diff --git a/src/ar65/objdata.c b/src/ar65/objdata.c new file mode 100644 index 000000000..0fd32d362 --- /dev/null +++ b/src/ar65/objdata.c @@ -0,0 +1,207 @@ +/*****************************************************************************/ +/* */ +/* objdata.c */ +/* */ +/* Handling object file data for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "mem.h" +#include "error.h" +#include "objdata.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Object data list management */ +unsigned ObjCount = 0; /* Count of object files in the list */ +ObjData* ObjRoot = 0; /* List of object files */ +ObjData* ObjLast = 0; /* Last entry in list */ +ObjData** ObjPool = 0; /* Object files as array */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +ObjData* NewObjData (void) +/* Allocate a new structure on the heap, insert it into the list, return it */ +{ + /* Allocate memory */ + ObjData* O = Xmalloc (sizeof (ObjData)); + + /* Initialize the data */ + O->Next = 0; + O->Name = 0; + O->Index = ~0; + O->Flags = 0; + O->MTime = 0; + O->Start = 0; + O->Size = 0; + O->ImportSize = 0; + O->Imports = 0; + O->ExportSize = 0; + O->Exports = 0; + + /* Link it into the list */ + if (ObjLast) { + ObjLast->Next = O; + ObjLast = O; + } else { + /* First entry */ + ObjRoot = ObjLast = O; + } + + /* One object file more now */ + ++ObjCount; + + /* Return the new entry */ + return O; +} + + + +void FreeObjData (ObjData* O) +/* Free a complete struct */ +{ + Xfree (O->Name); + Xfree (O->Imports); + Xfree (O->Exports); + Xfree (O); +} + + + +ObjData* FindObjData (const char* Module) +/* Search for the module with the given name and return it. Return NULL if the + * module is not in the list. + */ +{ + /* Hmm. Maybe we should hash the module names? */ + ObjData* O = ObjRoot; + while (O) { + if (strcmp (O->Name, Module) == 0) { + return O; + } + O = O->Next; + } + return 0; +} + + + +void DelObjData (const char* Module) +/* Delete the object module from the list */ +{ + ObjData* O = ObjRoot; + ObjData* Last = 0; + while (O) { + if (strcmp (O->Name, Module) == 0) { + /* Found the module, remove it from the list */ + if (Last == 0) { + /* This was the first entry in the list */ + ObjRoot = O->Next; + } else { + Last->Next = O->Next; + } + if (ObjLast == O) { + /* O was the last object in the list */ + ObjLast = Last; + } + --ObjCount; + + /* Free the entry */ + FreeObjData (O); + + /* Done */ + return; + } + Last = O; + O = O->Next; + } + + /* Not found! */ + Warning ("Module `%s' not found in library", Module); +} + + + +void MakeObjPool (void) +/* Allocate memory, index the entries and make the ObjPool valid */ +{ + ObjData* O; + unsigned Index; + + /* Allocate memory for the pool */ + ObjPool = Xmalloc (ObjCount * sizeof (ObjData*)); + + /* Setup the pointers and index the objects */ + Index = 0; + O = ObjRoot; + while (O) { + + /* Safety */ + CHECK (Index < ObjCount); + + /* Set the Index */ + O->Index = Index; + + /* Set the pool pointer */ + ObjPool [Index] = O; + + /* Next object */ + ++Index; + O = O->Next; + } +} + + + +const char* GetObjName (unsigned Index) +/* Get the name of a module by index */ +{ + PRECONDITION (ObjPool != 0 && Index < ObjCount && ObjPool [Index] != 0); + return ObjPool [Index]->Name; +} + + + + + diff --git a/src/ar65/objdata.h b/src/ar65/objdata.h new file mode 100644 index 000000000..66acb7688 --- /dev/null +++ b/src/ar65/objdata.h @@ -0,0 +1,111 @@ +/*****************************************************************************/ +/* */ +/* objdata.h */ +/* */ +/* Handling object file data for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OBJDATA_H +#define OBJDATA_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Values for the Flags field */ +#define OBJ_HAVEDATA 0x0001 /* The object data is in the tmp file */ +#define OBJ_MARKED 0x0002 /* Generic marker bit */ + + +/* Internal structure holding object file data */ +typedef struct ObjData_ ObjData; +struct ObjData_ { + ObjData* Next; /* Linked list of all objects */ + char* Name; /* Module name */ + unsigned Index; /* Module index */ + unsigned Flags; + unsigned long MTime; /* Modifiation time of object file */ + unsigned long Start; /* Start offset of data in library */ + unsigned long Size; /* Size of data in library */ + unsigned long ImportSize; /* Size of imports */ + void* Imports; /* Imports as raw data */ + unsigned long ExportSize; /* Size of exports */ + void* Exports; /* Exports as raw data */ +}; + + + +/* Object data list management */ +extern unsigned ObjCount; /* Count of files in the list */ +extern ObjData* ObjRoot; /* List of object files */ +extern ObjData* ObjLast; /* Last entry in list */ +extern ObjData** ObjPool; /* Object files as array */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +ObjData* NewObjData (void); +/* Allocate a new structure on the heap, insert it into the list, return it */ + +void FreeObjData (ObjData* O); +/* Free a complete struct */ + +ObjData* FindObjData (const char* Module); +/* Search for the module with the given name and return it. Return NULL if the + * module is not in the list. + */ + +void DelObjData (const char* Module); +/* Delete the object module from the list */ + +void MakeObjPool (void); +/* Allocate memory, index the entries and make the ObjPool valid */ + +const char* GetObjName (unsigned Index); +/* Get the name of a module by index */ + + + +/* End of objdata.h */ + +#endif + + + diff --git a/src/ar65/objfile.c b/src/ar65/objfile.c new file mode 100644 index 000000000..c485c6e75 --- /dev/null +++ b/src/ar65/objfile.c @@ -0,0 +1,288 @@ +/*****************************************************************************/ +/* */ +/* objfile.c */ +/* */ +/* Object file handling for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#ifdef __WATCOMC__ +/* Watcom has the file in the wrong directory */ +# include +#else +# include /* FreeBSD needs this */ +# include +#endif +#include +#include + +#include "error.h" +#include "mem.h" +#include "objdata.h" +#include "fileio.h" +#include "library.h" +#include "objfile.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static const char* GetModule (const char* Name) +/* Get a module name from the file name */ +{ + /* Make a module name from the file name */ + const char* Module = Name + strlen (Name); + while (Module > Name) { + --Module; + if (*Module == '/' || *Module == '\\') { + ++Module; + break; + } + } + if (*Module == 0) { + Error ("Cannot make module name from `%s'", Name); + } + return Module; +} + + + +void ObjReadHeader (FILE* Obj, ObjHeader* H, const char* Name) +/* Read the header of the object file checking the signature */ +{ + H->Magic = Read32 (Obj); + if (H->Magic != OBJ_MAGIC) { + Error ("`%s' is not an object file", Name); + } + H->Version = Read16 (Obj); + if (H->Version != OBJ_VERSION) { + Error ("Object file `%s' has wrong version", Name); + } + H->Flags = Read16 (Obj); + H->OptionOffs = Read32 (Obj); + H->OptionSize = Read32 (Obj); + H->FileOffs = Read32 (Obj); + H->FileSize = Read32 (Obj); + H->SegOffs = Read32 (Obj); + H->SegSize = Read32 (Obj); + H->ImportOffs = Read32 (Obj); + H->ImportSize = Read32 (Obj); + H->ExportOffs = Read32 (Obj); + H->ExportSize = Read32 (Obj); + H->DbgSymOffs = Read32 (Obj); + H->DbgSymSize = Read32 (Obj); +} + + + +void ObjWriteHeader (FILE* Obj, ObjHeader* H) +/* Write the header of the object file */ +{ + Write32 (Obj, H->Magic); + Write16 (Obj, H->Version); + Write16 (Obj, H->Flags); + Write32 (Obj, H->OptionOffs); + Write32 (Obj, H->OptionSize); + Write32 (Obj, H->FileOffs); + Write32 (Obj, H->FileSize); + Write32 (Obj, H->SegOffs); + Write32 (Obj, H->SegSize); + Write32 (Obj, H->ImportOffs); + Write32 (Obj, H->ImportSize); + Write32 (Obj, H->ExportOffs); + Write32 (Obj, H->ExportSize); + Write32 (Obj, H->DbgSymOffs); + Write32 (Obj, H->DbgSymSize); +} + + + +void ObjAdd (const char* Name) +/* Add an object file to the library */ +{ + struct stat StatBuf; + const char* Module; + ObjHeader H; + ObjData* O; + + /* Open the object file */ + FILE* Obj = fopen (Name, "rb"); + if (Obj == 0) { + Error ("Could not open `%s': %s", Name, strerror (errno)); + } + + /* Get the modification time of the object file */ + if (fstat (fileno (Obj), &StatBuf) != 0) { + Error ("Cannot stat object file `%s': %s", Name, strerror (errno)); + } + + /* Read and check the header */ + ObjReadHeader (Obj, &H, Name); + + /* Make a module name from the file name */ + Module = GetModule (Name); + + /* Check if we already have a module with this name */ + O = FindObjData (Module); + if (O == 0) { + /* Not found, create a new entry */ + O = NewObjData (); + } else { + /* Found - check the file modification times of the internal copy + * and the external one. + */ + if (difftime ((time_t)O->MTime, StatBuf.st_mtime) > 0.0) { + Warning ("Replacing module `%s' by older version", O->Name); + } + } + + /* Initialize the object module data structure */ + O->Name = StrDup (Module); + O->Flags = OBJ_HAVEDATA; + O->MTime = StatBuf.st_mtime; + O->ImportSize = H.ImportSize; + O->Imports = Xmalloc (O->ImportSize); + O->ExportSize = H.ExportSize; + O->Exports = Xmalloc (O->ExportSize); + + /* Read imports and exports */ + fseek (Obj, H.ImportOffs, SEEK_SET); + ReadData (Obj, O->Imports, O->ImportSize); + fseek (Obj, H.ExportOffs, SEEK_SET); + ReadData (Obj, O->Exports, O->ExportSize); + + /* Skip the object file header */ + O->Start = ftell (NewLib); + fseek (NewLib, OBJ_HDR_SIZE, SEEK_CUR); + + /* Copy the remaining sections */ + fseek (Obj, H.DbgSymOffs, SEEK_SET); + H.DbgSymOffs = LibCopyTo (Obj, H.DbgSymSize) - O->Start; + fseek (Obj, H.OptionOffs, SEEK_SET); + H.OptionOffs = LibCopyTo (Obj, H.OptionSize) - O->Start; + fseek (Obj, H.SegOffs, SEEK_SET); + H.SegOffs = LibCopyTo (Obj, H.SegSize) - O->Start; + fseek (Obj, H.FileOffs, SEEK_SET); + H.FileOffs = LibCopyTo (Obj, H.FileSize) - O->Start; + + /* Calculate the amount of data written */ + O->Size = ftell (NewLib) - O->Start; + + /* Clear the remaining header fields */ + H.ImportOffs = H.ImportSize = 0; + H.ExportOffs = H.ExportSize = 0; + + /* Seek back and write the updated header */ + fseek (NewLib, O->Start, SEEK_SET); + ObjWriteHeader (NewLib, &H); + + /* Now seek again to end of file */ + fseek (NewLib, 0, SEEK_END); + + /* Done, close the file (we read it only, so no error check) */ + fclose (Obj); +} + + + +void ObjExtract (const char* Name) +/* Extract a module from the library */ +{ + unsigned long ImportStart; + unsigned long ExportStart; + struct utimbuf U; + ObjHeader H; + FILE* Obj; + + + /* Make a module name from the file name */ + const char* Module = GetModule (Name); + + /* Try to find the module in the library */ + ObjData* O = FindObjData (Module); + + /* Bail out if the module does not exist */ + if (O == 0) { + Error ("Module `%s' not found in library", Module); + } + + /* Open the output file */ + Obj = fopen (Name, "w+b"); + if (Obj == 0) { + Error ("Cannot open target file `%s': %s", Name, strerror (errno)); + } + + /* Copy the first four segments including the header to the new file */ + LibCopyFrom (O->Start, O->Size, Obj); + + /* Write imports and exports */ + ImportStart = ftell (Obj); + WriteData (Obj, O->Imports, O->ImportSize); + ExportStart = ftell (Obj); + WriteData (Obj, O->Exports, O->ExportSize); + + /* Seek back and read the header */ + fseek (Obj, 0, SEEK_SET); + ObjReadHeader (Obj, &H, Name); + + /* Update the header fields */ + H.ImportOffs = ImportStart; + H.ImportSize = O->ImportSize; + H.ExportOffs = ExportStart; + H.ExportSize = O->ExportSize; + + /* Write the changed header */ + fseek (Obj, 0, SEEK_SET); + ObjWriteHeader (Obj, &H); + + /* Close the file */ + if (fclose (Obj) != 0) { + Error ("Problem closing object file `%s': %s", Name, strerror (errno)); + } + + /* Set access and modification time */ + U.actime = O->MTime; + U.modtime = O->MTime; + if (utime (Name, &U) != 0) { + Error ("Cannot set mod time on `%s': %s", Name, strerror (errno)); + } +} + + + + + + diff --git a/src/ar65/objfile.h b/src/ar65/objfile.h new file mode 100644 index 000000000..50b05f9a7 --- /dev/null +++ b/src/ar65/objfile.h @@ -0,0 +1,72 @@ +/*****************************************************************************/ +/* */ +/* objfile.h */ +/* */ +/* Object file handling for the ar65 archiver */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OBJFILE_H +#define OBJFILE_H + + + +#include + +#include "../common/objdefs.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ObjReadHeader (FILE* Obj, ObjHeader* H, const char* Name); +/* Read the header of the object file checking the signature */ + +void ObjWriteHeader (FILE* Obj, ObjHeader* H); +/* Write the header of the object file */ + +void ObjAdd (const char* Name); +/* Add an object file to the library */ + +void ObjExtract (const char* Name); +/* Extract a module from the library */ + + + +/* End of objfile.h */ + +#endif + + + diff --git a/src/ca65/.cvsignore b/src/ca65/.cvsignore new file mode 100644 index 000000000..d745ea83a --- /dev/null +++ b/src/ca65/.cvsignore @@ -0,0 +1,2 @@ +.depend +ca65 diff --git a/src/ca65/condasm.c b/src/ca65/condasm.c new file mode 100644 index 000000000..831802c37 --- /dev/null +++ b/src/ca65/condasm.c @@ -0,0 +1,418 @@ +/*****************************************************************************/ +/* */ +/* condasm.c */ +/* */ +/* Conditional assembly support for ca65 */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "error.h" +#include "expr.h" +#include "scanner.h" +#include "symtab.h" +#include "condasm.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Maximum count of nested .ifs */ +#define MAX_IFS 32 + +/* Set of bitmapped flags for the if descriptor */ +enum { + ifNone = 0x0000, /* No flag */ + ifCond = 0x0001, /* IF condition was true */ + ifElse = 0x0002, /* We had a .ELSE branch */ + ifNeedTerm = 0x0004 /* Need .ENDIF termination */ +}; + + + +/*****************************************************************************/ +/* struct IfDesc */ +/*****************************************************************************/ + + + +/* One .IF descriptor */ +typedef struct IfDesc_ IfDesc; +struct IfDesc_ { + unsigned Flags; /* Bitmapped flags, see above */ + FilePos Pos; /* File position of the .IF */ + const char* Name; /* Name of the directive */ +}; + +/* The .IF stack */ +static IfDesc IfStack [MAX_IFS]; +static unsigned IfCount = 0; + + + +static IfDesc* AllocIf (const char* Directive, int NeedTerm) +/* Alloc a new element from the .IF stack */ +{ + IfDesc* ID; + + /* Check for stack overflow */ + if (IfCount >= MAX_IFS) { + Error (ERR_IF_NESTING); + } + + /* Alloc one element */ + ID = &IfStack [IfCount++]; + + /* Initialize elements */ + ID->Flags = NeedTerm? ifNeedTerm : ifNone; + ID->Pos = CurPos; + ID->Name = Directive; + + /* Return the result */ + return ID; +} + + + +static IfDesc* GetCurrentIf (void) +/* Return the current .IF descriptor */ +{ + if (IfCount == 0) { + return 0; + } else { + return &IfStack [IfCount-1]; + } +} + + + +static void FreeIf (void) +/* Free all .IF descriptors until we reach one with the NeedTerm bit set */ +{ + int Done = 0; + do { + IfDesc* D = GetCurrentIf(); + if (D == 0) { + Error (ERR_UNEXPECTED, ".ENDIF"); + } else { + Done = (D->Flags & ifNeedTerm) != 0; + --IfCount; + } + } while (!Done); +} + + + +static int GetCurrentIfCond (void) +/* Return the current condition based on all conditions on the stack */ +{ + unsigned Count; + for (Count = 0; Count < IfCount; ++Count) { + if ((IfStack[Count].Flags & ifCond) == 0) { + return 0; + } + } + return 1; +} + + + +static void SetIfCond (IfDesc* ID, int C) +/* Set the .IF condition */ +{ + if (C) { + ID->Flags |= ifCond; + } else { + ID->Flags &= ~ifCond; + } +} + + + +static void InvertIfCond (IfDesc* ID) +/* Invert the current condition */ +{ + ID->Flags ^= ifCond; +} + + + +static int GetElse (const IfDesc* ID) +/* Return true if we had a .ELSE */ +{ + return (ID->Flags & ifElse) != 0; +} + + + +static void SetElse (IfDesc* ID, int E) +/* Set the .ELSE flag */ +{ + if (E) { + ID->Flags |= ifElse; + } else { + ID->Flags &= ~ifElse; + } +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void DoConditionals (void) +/* Catch all for conditional directives */ +{ + IfDesc* D; + + int IfCond = GetCurrentIfCond (); + do { + + switch (Tok) { + + case TOK_ELSE: + D = GetCurrentIf (); + if (D == 0) { + Error (ERR_UNEXPECTED, ".ELSE"); + } else if (GetElse(D)) { + /* We already had a .ELSE ! */ + Error (ERR_DUPLICATE_ELSE); + } else { + /* Allow an .ELSE */ + InvertIfCond (D); + SetElse (D, 1); + D->Pos = CurPos; + D->Name = ".ELSE"; + IfCond = GetCurrentIfCond (); + } + NextTok (); + break; + + case TOK_ELSEIF: + D = GetCurrentIf (); + if (D == 0) { + Error (ERR_UNEXPECTED, ".ELSEIF"); + } else if (GetElse(D)) { + /* We already had a .ELSE */ + Error (ERR_DUPLICATE_ELSE); + } else { + /* Handle as if there was an .ELSE first */ + InvertIfCond (D); + SetElse (D, 1); + + /* Allocate and prepare a new descriptor */ + D = AllocIf (".ELSEIF", 0); + NextTok (); + + /* Ignore the new condition if we are inside a false .ELSE + * branch. This way we won't get any errors about undefined + * symbols or similar... + */ + if (IfCond == 0) { + SetIfCond (D, ConstExpression ()); + } + + /* Get the new overall condition */ + IfCond = GetCurrentIfCond (); + } + break; + + case TOK_ENDIF: + /* We're done with this .IF.. - remove the descriptor(s) */ + FreeIf (); + + /* Be sure not to read the next token until the .IF stack + * has been cleanup up, since we may be at end of file. + */ + NextTok (); + + /* Get the new overall condition */ + IfCond = GetCurrentIfCond (); + break; + + case TOK_IF: + D = AllocIf (".IF", 1); + NextTok (); + if (IfCond) { + SetIfCond (D, ConstExpression ()); + } + IfCond = GetCurrentIfCond (); + break; + + case TOK_IFBLANK: + D = AllocIf (".IFBLANK", 1); + NextTok (); + if (IfCond) { + SetIfCond (D, Tok == TOK_SEP); + } + IfCond = GetCurrentIfCond (); + break; + + case TOK_IFCONST: + D = AllocIf (".IFCONST", 1); + NextTok (); + if (IfCond) { + ExprNode* Expr = Expression(); + SetIfCond (D, IsConstExpr (Expr)); + FreeExpr (Expr); + } + IfCond = GetCurrentIfCond (); + break; + + case TOK_IFDEF: + D = AllocIf (".IFDEF", 1); + NextTok (); + if (IfCond) { + if (Tok != TOK_IDENT) { + ErrorSkip (ERR_IDENT_EXPECTED); + } else { + SetIfCond (D, SymIsDef (SVal)); + NextTok (); + } + } + IfCond = GetCurrentIfCond (); + break; + + case TOK_IFNBLANK: + D = AllocIf (".IFNBLANK", 1); + NextTok (); + if (IfCond) { + SetIfCond (D, Tok != TOK_SEP); + } + IfCond = GetCurrentIfCond (); + break; + + case TOK_IFNCONST: + D = AllocIf (".IFNCONST", 1); + NextTok (); + if (IfCond) { + ExprNode* Expr = Expression(); + SetIfCond (D, !IsConstExpr (Expr)); + FreeExpr (Expr); + } + IfCond = GetCurrentIfCond (); + break; + + case TOK_IFNDEF: + D = AllocIf (".IFNDEF", 1); + NextTok (); + if (IfCond) { + if (Tok != TOK_IDENT) { + ErrorSkip (ERR_IDENT_EXPECTED); + } else { + SetIfCond (D, !SymIsDef (SVal)); + NextTok (); + } + } + IfCond = GetCurrentIfCond (); + break; + + case TOK_IFNREF: + D = AllocIf (".IFNREF", 1); + NextTok (); + if (IfCond) { + if (Tok != TOK_IDENT) { + ErrorSkip (ERR_IDENT_EXPECTED); + } else { + SetIfCond (D, !SymIsRef (SVal)); + NextTok (); + } + } + IfCond = GetCurrentIfCond (); + break; + + case TOK_IFP02: + break; + + case TOK_IFP816: + break; + + case TOK_IFPC02: + break; + + case TOK_IFREF: + D = AllocIf (".IFREF", 1); + NextTok (); + if (IfCond) { + if (Tok != TOK_IDENT) { + ErrorSkip (ERR_IDENT_EXPECTED); + } else { + SetIfCond (D, SymIsRef (SVal)); + NextTok (); + } + } + IfCond = GetCurrentIfCond (); + break; + + default: + // Skip tokens + NextTok (); + + } + + } while (IfCond == 0 && Tok != TOK_EOF); +} + + + +void CheckOpenIfs (void) +/* Called from the scanner before closing an input file. Will check for any + * open .ifs in this file. + */ +{ + while (1) { + /* Get the current file number and check if the topmost entry on the + * .IF stack was inserted with this file number + */ + IfDesc* D = GetCurrentIf (); + if (D == 0) { + /* There are no open .IFs */ + break; + } + + if (D->Pos.Name != CurPos.Name) { + /* The .if is from another file, bail out */ + break; + } + + /* Start of .if is in the file we're about to leave */ + PError (&D->Pos, ERR_OPEN_IF); + FreeIf (); + } +} + + + diff --git a/src/ca65/condasm.h b/src/ca65/condasm.h new file mode 100644 index 000000000..3994b3929 --- /dev/null +++ b/src/ca65/condasm.h @@ -0,0 +1,62 @@ +/*****************************************************************************/ +/* */ +/* condasm.h */ +/* */ +/* Conditional assembly support for ca65 */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef CONDASM_H +#define CONDASM_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void DoConditionals (void); +/* Catch all for conditional directives */ + +void CheckOpenIfs (void); +/* Called from the scanner before closing an input file. Will check for any + * open .ifs in this file. + */ + + + +/* End of condasm.h */ + +#endif + + + diff --git a/src/ca65/ea.c b/src/ca65/ea.c new file mode 100644 index 000000000..8bc8e810d --- /dev/null +++ b/src/ca65/ea.c @@ -0,0 +1,198 @@ +/*****************************************************************************/ +/* */ +/* ea.c */ +/* */ +/* Effective address parsing for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "error.h" +#include "expr.h" +#include "instr.h" +#include "scanner.h" +#include "ea.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void GetEA (unsigned long* AddrMode, ExprNode** Expr, ExprNode** Bank) +/* Parse an effective address, return the possible modes in AddrMode, and the + * expression involved (if any) in Expr. + */ +{ + /* Clear the expressions */ + *Bank = *Expr = 0; + + + if (Tok == TOK_SEP) { + + *AddrMode = AM_IMPLICIT; + + } else if (Tok == TOK_HASH) { + + /* #val */ + NextTok (); + *Expr = Expression (); + *AddrMode = AM_IMM; + + } else if (Tok == TOK_A) { + + NextTok (); + *AddrMode = AM_ACCU; + + } else if (Tok == TOK_LBRACK) { + + /* [dir] or [dir],y */ + NextTok (); + *Expr = Expression (); + Consume (TOK_RBRACK, ERR_RBRACK_EXPECTED); + if (Tok == TOK_COMMA) { + /* [dir],y */ + NextTok (); + Consume (TOK_Y, ERR_Y_EXPECTED); + *AddrMode = AM_DIR_IND_LONG_Y; + } else { + /* [dir] */ + *AddrMode = AM_DIR_IND_LONG; + } + + } else if (Tok == TOK_LPAREN) { + + /* One of the indirect modes */ + NextTok (); + *Expr = Expression (); + + if (Tok == TOK_COMMA) { + + /* (expr,X) or (rel,S),y */ + NextTok (); + if (Tok == TOK_X) { + /* (adr,x) */ + NextTok (); + *AddrMode = AM_ABS_X_IND | AM_DIR_X_IND; + ConsumeRParen (); + } else if (Tok == TOK_S) { + /* (rel,s),y */ + NextTok (); + *AddrMode = AM_STACK_REL_IND_Y; + ConsumeRParen (); + ConsumeComma (); + Consume (TOK_Y, ERR_Y_EXPECTED); + } else { + Error (ERR_SYNTAX); + } + + } else { + + /* (adr) or (adr),y */ + ConsumeRParen (); + if (Tok == TOK_COMMA) { + /* (adr),y */ + NextTok (); + Consume (TOK_Y, ERR_Y_EXPECTED); + *AddrMode = AM_DIR_IND_Y; + } else { + /* (adr) */ + *AddrMode = AM_ABS_IND | AM_DIR_IND; + } + } + + } else { + + /* Remaining stuff: + * + * adr + * bank.adr + * adr,x + * bank.adr,x + * adr,y + * adr,s + */ + *Expr = Expression (); + + if (Tok == TOK_DOT) { + + /* Expr was a bank specification: bank.adr or bank.adr,x */ + *Bank = *Expr; + NextTok (); + *Expr = Expression (); + if (Tok == TOK_COMMA) { + /* bank.adr,x */ + NextTok (); + Consume (TOK_X, ERR_X_EXPECTED); + *AddrMode = AM_ABS_LONG_X; + } else { + /* bank.adr */ + *AddrMode = AM_ABS_LONG; + } + + } else { + + if (Tok == TOK_COMMA) { + + NextTok (); + switch (Tok) { + + case TOK_X: + *AddrMode = AM_ABS_X | AM_DIR_X; + NextTok (); + break; + + case TOK_Y: + *AddrMode = AM_ABS_Y | AM_DIR_Y; + NextTok (); + break; + + case TOK_S: + *AddrMode = AM_STACK_REL; + NextTok (); + break; + + default: + Error (ERR_SYNTAX); + + } + + } else { + + *AddrMode = AM_ABS | AM_DIR; + + } + } + } +} + + + diff --git a/src/ca65/ea.h b/src/ca65/ea.h new file mode 100644 index 000000000..8d3c88a8d --- /dev/null +++ b/src/ca65/ea.h @@ -0,0 +1,69 @@ +/*****************************************************************************/ +/* */ +/* ea.h */ +/* */ +/* Effective address parsing for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef EA_H +#define EA_H + + + +#include "expr.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void GetEA (unsigned long* AddrMode, ExprNode** Expr, ExprNode** Bank); +/* Parse an effective address, return the possible modes in AddrMode, and the + * expression involved (if any) in Expr. + */ + + + +/* End of ea.h */ + +#endif + + + diff --git a/src/ca65/error.c b/src/ca65/error.c new file mode 100644 index 000000000..5dea2d6e5 --- /dev/null +++ b/src/ca65/error.c @@ -0,0 +1,291 @@ +/*****************************************************************************/ +/* */ +/* error.c */ +/* */ +/* Error handling for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "scanner.h" +#include "error.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Warning level */ +unsigned WarnLevel = 1; + +/* Messages for internal compiler errors */ +const char _MsgCheckFailed [] = + "Check failed: `%s' (= %d), file `%s', line %u\n"; +const char _MsgPrecondition [] = + "Precondition violated: `%s' (= %d), file `%s', line %u\n"; +const char _MsgFail [] = + "%s, file `%s', line %u\n"; + + +/* Statistics */ +unsigned ErrorCount = 0; +unsigned WarningCount = 0; + + + +/*****************************************************************************/ +/* Warnings */ +/*****************************************************************************/ + + + +void WarningMsg (const FilePos* Pos, unsigned WarnNum, va_list ap) +/* Print warning message. */ +{ + static const struct { + unsigned char Level; + const char* Msg; + } Warnings [WARN_COUNT-1] = { + { 1, "Mask error" }, + { 2, "Symbol `%s' is defined but never used" }, + { 2, "Symbol `%s' is imported but never used" }, + { 1, "Cannot track processor status byte" }, + }; + + if (Warnings [WarnNum-1].Level <= WarnLevel) { + fprintf (stderr, "%s(%lu): Warning #%u: ", + GetFileName (Pos->Name), Pos->Line, WarnNum); + vfprintf (stderr, Warnings [WarnNum-1].Msg, ap); + fprintf (stderr, "\n"); + ++WarningCount; + } +} + + + +void Warning (unsigned WarnNum, ...) +/* Print warning message. */ +{ + va_list ap; + va_start (ap, WarnNum); + WarningMsg (&CurPos, WarnNum, ap); + va_end (ap); +} + + + +void PWarning (const FilePos* Pos, unsigned WarnNum, ...) +/* Print warning message giving an explicit file and position. */ +{ + va_list ap; + va_start (ap, WarnNum); + WarningMsg (Pos, WarnNum, ap); + va_end (ap); +} + + + +/*****************************************************************************/ +/* Errors */ +/*****************************************************************************/ + + + +void ErrorMsg (const FilePos* Pos, unsigned ErrNum, va_list ap) +/* Print an error message */ +{ + static const char* Msgs [ERR_COUNT-1] = { + "Command/operation not implemented", + "Cannot open include file `%s': %s", + "Include nesting too deep", + "Invalid input character: %02X", + "Hex digit expected", + "Digit expected", + "`0' or `1' expected", + "Numerical overflow", + "Control statement expected", + "Too many characters", + "`:' expected", + "`(' expected", + "`)' expected", + "`]' expected", + "`,' expected", + "Boolean switch value expected (on/off/+/-)", + "`Y' expected", + "`X' expected", + "Integer constant expected", + "String constant expected", + "Character constant expected", + "Constant expression expected", + "Identifier expected", + "`.endmacro' expected", + "Option key expected", + "Command is only valid in 65816 mode", + "User error: %s", + "String constant too long", + "Newline in string constant", + "Illegal character constant", + "Illegal addressing mode", + "Illegal character to start local symbols", + "Illegal use of local symbol", + "Illegal segment name: `%s'", + "Illegal segment attribute", + "Illegal macro package name", + "Illegal emulation feature", + "Syntax error", + "Symbol `%s' is already defined", + "Undefined symbol `%s'", + "Symbol `%s' is marked as import", + "Symbol `%s' is marked as export", + "Exported symbol `%s' is undefined", + "Exported values must be constant", + ".IF nesting too deep", + "Unexpected end of line", + "Unexpected `%s'", + "Division by zero", + "Modulo operation with zero", + "Range error", + "Too many macro parameters", + "Macro parameter expected", + "Circular reference in symbol definition", + "Symbol redeclaration mismatch", + "Alignment value must be a power of 2", + "Duplicate `.ELSE'", + "Conditional assembly branch was never closed", + "Lexical level was not terminated correctly", + "Segment attribute mismatch", + "CPU not supported", + "Counter underflow", + "Undefined label", + }; + + fprintf (stderr, "%s(%lu): Error #%u: ", + GetFileName (Pos->Name), Pos->Line, ErrNum); + vfprintf (stderr, Msgs [ErrNum-1], ap); + fprintf (stderr, "\n"); + ++ErrorCount; +} + + + +void Error (unsigned ErrNum, ...) +/* Print an error message */ +{ + va_list ap; + va_start (ap, ErrNum); + ErrorMsg (&CurPos, ErrNum, ap); + va_end (ap); +} + + + +void PError (const FilePos* Pos, unsigned ErrNum, ...) +/* Print an error message giving an explicit file and position. */ +{ + va_list ap; + va_start (ap, ErrNum); + ErrorMsg (Pos, ErrNum, ap); + va_end (ap); +} + + + +void ErrorSkip (unsigned ErrNum, ...) +/* Print an error message and skip the rest of the line */ +{ + va_list ap; + va_start (ap, ErrNum); + ErrorMsg (&CurPos, ErrNum, ap); + va_end (ap); + + SkipUntilSep (); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Fatal (unsigned FatNum, ...) +/* Print a message about a fatal error and die */ +{ + static const char* Msgs [FAT_COUNT-1] = { + "Maximum number of input files reached", + "Out of memory", + "Too many segments", + "String too long", + "Cannot open input file `%s': %s", + "Cannot stat input file `%s': %s", + "Cannot open output file `%s': %s", + "Cannot write to output file `%s': %s", + "Cannot open listing file: %s", + "Cannot write to listing file: %s", + "Cannot read from listing file: %s", + "Macro nesting too deep", + "Too many symbols", + }; + va_list ap; + + va_start (ap, FatNum); + fprintf (stderr, "Fatal #%u: ", FatNum); + vfprintf (stderr, Msgs [FatNum-1], ap); + fprintf (stderr, "\n"); + va_end (ap); + + /* And die... */ + exit (EXIT_FAILURE); +} + + + +void Internal (const char* Format, ...) +/* Print a message about an internal compiler error and die. */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "Internal assembler error\n"); + vfprintf (stderr, Format, ap); + va_end (ap); + fprintf (stderr, "\n"); + + exit (EXIT_FAILURE); +} + + + diff --git a/src/ca65/error.h b/src/ca65/error.h new file mode 100644 index 000000000..7dba96924 --- /dev/null +++ b/src/ca65/error.h @@ -0,0 +1,210 @@ +/*****************************************************************************/ +/* */ +/* error.h */ +/* */ +/* Error handling for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ERROR_H +#define ERROR_H + + + +#include "scanner.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Warning numbers */ +enum Warnings { + WARN_NONE, /* No warning */ + WARN_MASK_ERROR, + WARN_SYM_NOT_REFERENCED, + WARN_IMPORT_NOT_REFERENCED, + WARN_CANNOT_TRACK_STATUS, + WARN_COUNT /* Warning count */ +}; + +/* Error numbers */ +enum Errors { + ERR_NONE, /* No error */ + ERR_NOT_IMPLEMENTED, /* Command/operation not implemented */ + ERR_CANNOT_OPEN_INCLUDE, + ERR_INCLUDE_NESTING, + ERR_INVALID_CHAR, + ERR_HEX_DIGIT_EXPECTED, + ERR_DIGIT_EXPECTED, + ERR_01_EXPECTED, + ERR_NUM_OVERFLOW, + ERR_PSEUDO_EXPECTED, + ERR_TOO_MANY_CHARS, + ERR_COLON_EXPECTED, + ERR_LPAREN_EXPECTED, + ERR_RPAREN_EXPECTED, + ERR_RBRACK_EXPECTED, + ERR_COMMA_EXPECTED, + ERR_ONOFF_EXPECTED, + ERR_Y_EXPECTED, + ERR_X_EXPECTED, + ERR_INTCON_EXPECTED, + ERR_STRCON_EXPECTED, + ERR_CHARCON_EXPECTED, + ERR_CONSTEXPR_EXPECTED, + ERR_IDENT_EXPECTED, + ERR_ENDMACRO_EXPECTED, + ERR_OPTION_KEY_EXPECTED, + ERR_816_MODE_ONLY, + ERR_USER, + ERR_STRING_TOO_LONG, + ERR_NEWLINE_IN_STRING, + ERR_ILLEGAL_CHARCON, + ERR_ILLEGAL_ADDR_MODE, + ERR_ILLEGAL_LOCALSTART, + ERR_ILLEGAL_LOCAL_USE, + ERR_ILLEGAL_SEGMENT, + ERR_ILLEGAL_SEG_ATTR, + ERR_ILLEGAL_MACPACK, + ERR_ILLEGAL_FEATURE, + ERR_SYNTAX, + ERR_SYM_ALREADY_DEFINED, + ERR_SYM_UNDEFINED, + ERR_SYM_ALREADY_IMPORT, + ERR_SYM_ALREADY_EXPORT, + ERR_EXPORT_UNDEFINED, + ERR_EXPORT_MUST_BE_CONST, + ERR_IF_NESTING, + ERR_UNEXPECTED_EOL, + ERR_UNEXPECTED, + ERR_DIV_BY_ZERO, + ERR_MOD_BY_ZERO, + ERR_RANGE, + ERR_TOO_MANY_PARAMS, + ERR_MACRO_PARAM_EXPECTED, + ERR_CIRCULAR_REFERENCE, + ERR_SYM_REDECL_MISMATCH, + ERR_ALIGN, + ERR_DUPLICATE_ELSE, + ERR_OPEN_IF, + ERR_OPEN_PROC, + ERR_SEG_ATTR_MISMATCH, + ERR_CPU_NOT_SUPPORTED, + ERR_COUNTER_UNDERFLOW, + ERR_UNDEFINED_LABEL, + ERR_COUNT /* Error count */ +}; + +/* Fatal errors */ +enum Fatals { + FAT_NONE, + FAT_MAX_INPUT_FILES, + FAT_OUT_OF_MEMORY, + FAT_TOO_MANY_SEGMENTS, + FAT_STRING_TOO_LONG, + FAT_CANNOT_OPEN_INPUT, + FAT_CANNOT_STAT_INPUT, + FAT_CANNOT_OPEN_OUTPUT, + FAT_CANNOT_WRITE_OUTPUT, + FAT_CANNOT_OPEN_LISTING, + FAT_CANNOT_WRITE_LISTING, + FAT_CANNOT_READ_LISTING, + FAT_MACRO_NESTING, + FAT_TOO_MANY_SYMBOLS, + FAT_COUNT /* Fatal error count */ +}; + + + +/* Warning levels */ +extern unsigned WarnLevel; + +/* Messages for internal compiler errors */ +extern const char _MsgCheckFailed []; +extern const char _MsgPrecondition []; +extern const char _MsgFail []; + +/* Statistics */ +extern unsigned ErrorCount; +extern unsigned WarningCount; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Warning (unsigned WarnNum, ...); +/* Print warning message. */ + +void PWarning (const FilePos* Pos, unsigned WarnNum, ...); +/* Print warning message giving an explicit file and position. */ + +void Error (unsigned ErrNum, ...); +/* Print an error message */ + +void PError (const FilePos* Pos, unsigned ErrNum, ...); +/* Print an error message giving an explicit file and position. */ + +void ErrorSkip (unsigned ErrNum, ...); +/* Print an error message and skip the rest of the line */ + +void Fatal (unsigned FatNum, ...); +/* Print a message about a fatal error and die */ + +void Internal (const char* Format, ...); +/* Print a message about an internal compiler error and die. */ + +#define CHECK(c) \ + if (!(c)) \ + Internal (_MsgCheckFailed, #c, c, __FILE__, __LINE__) + +#define PRECONDITION(c) \ + if (!(c)) \ + Internal (_MsgPrecondition, #c, c, __FILE__, __LINE__) + +#define FAIL(s) \ + Internal (_MsgFail, s, __FILE__, __LINE__) + + + +/* End of error.h */ + +#endif + + + + diff --git a/src/ca65/expr.c b/src/ca65/expr.c new file mode 100644 index 000000000..6131e39a0 --- /dev/null +++ b/src/ca65/expr.c @@ -0,0 +1,1566 @@ +/*****************************************************************************/ +/* */ +/* expr.c */ +/* */ +/* Expression evaluation for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "../common/exprdefs.h" + +#include "error.h" +#include "global.h" +#include "instr.h" +#include "mem.h" +#include "objcode.h" +#include "objfile.h" +#include "scanner.h" +#include "symtab.h" +#include "toknode.h" +#include "ulabel.h" +#include "expr.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Since all expressions are first packed into expression trees, and each + * expression tree node is allocated on the heap, we add some type of special + * purpose memory allocation here: Instead of freeing the nodes, we save some + * number of freed nodes for later and remember them in a single linked list + * using the Left link. + */ +#define MAX_FREE_NODES 64 +static ExprNode* FreeExprNodes = 0; +static unsigned FreeNodeCount = 0; + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +static ExprNode* NewExprNode (void) +/* Create a new expression node */ +{ + ExprNode* N; + + /* Do we have some nodes in the list already? */ + if (FreeExprNodes) { + /* Use first node from list */ + N = FreeExprNodes; + FreeExprNodes = N->Left; + } else { + /* Allocate fresh memory */ + N = Xmalloc (sizeof (ExprNode)); + } + N->Op = EXPR_NULL; + N->Left = N->Right = 0; + N->Obj = 0; + + return N; +} + + + +static void FreeExprNode (ExprNode* E) +/* Free a node */ +{ + if (E) { + if (FreeNodeCount < MAX_FREE_NODES) { + /* Remember this node for later */ + E->Left = FreeExprNodes; + FreeExprNodes = E; + } else { + /* Free the memory */ + Xfree (E); + } + } +} + + + +/*****************************************************************************/ +/* Dump an expression tree on stdout for debugging */ +/*****************************************************************************/ + + + +static void InternalDumpExpr (ExprNode* Expr) +/* Dump an expression in UPN */ +{ + if (Expr == 0) { + return; + } + InternalDumpExpr (Expr->Left); + InternalDumpExpr (Expr->Right); + + switch (Expr->Op) { + + case EXPR_LITERAL: + case EXPR_ULABEL: + printf (" $%04lX", Expr->V.Val & 0xFFFF); + break; + + case EXPR_SYMBOL: + printf (" %s", GetSymName (Expr->V.Sym)); + break; + + case EXPR_SEGMENT: + printf (" SEG"); + break; + + case EXPR_PLUS: + printf (" +"); + break; + + case EXPR_MINUS: + printf (" -"); + break; + + case EXPR_MUL: + printf (" *"); + break; + + case EXPR_DIV: + printf (" /"); + break; + + case EXPR_MOD: + printf (" %%"); + break; + + case EXPR_OR: + printf (" OR"); + break; + + case EXPR_XOR: + printf (" XOR"); + break; + + case EXPR_AND: + printf (" AND"); + break; + + case EXPR_SHL: + printf (" SHL"); + break; + + case EXPR_SHR: + printf (" SHR"); + break; + + case EXPR_EQ: + printf (" ="); + break; + + case EXPR_NE: + printf ("<>"); + break; + + case EXPR_LT: + printf (" <"); + break; + + case EXPR_GT: + printf (" >"); + break; + + case EXPR_UNARY_MINUS: + printf (" NEG"); + break; + + case EXPR_NOT: + printf (" ~"); + break; + + case EXPR_LOBYTE: + printf (" LO"); + break; + + case EXPR_HIBYTE: + printf (" HI"); + break; + + case EXPR_SWAP: + printf (" SWAP"); + break; + + case EXPR_BAND: + printf (" BOOL_AND"); + break; + + case EXPR_BOR: + printf (" BOOL_OR"); + break; + + case EXPR_BXOR: + printf (" BOOL_XOR"); + break; + + case EXPR_BNOT: + printf (" BOOL_NOT"); + break; + + default: + Internal ("Unknown Op type: %u", Expr->Op); + + } +} + + + +void DumpExpr (ExprNode* Expr) +/* Dump an expression tree to stdout */ +{ + InternalDumpExpr (Expr); + printf ("\n"); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static ExprNode* Expr0 (void); + + + +int IsByteRange (long Val) +/* Return true if this is a byte value */ +{ + return (Val & ~0xFFL) == 0; +} + + + +int IsWordRange (long Val) +/* Return true if this is a word value */ +{ + return (Val & ~0xFFFF) == 0; +} + + + +static int FuncBlank (void) +/* Handle the .BLANK builtin function */ +{ + /* Assume no tokens if the closing brace follows (this is not correct in + * all cases, since the token may be the closing brace, but this will + * give a syntax error anyway and may not be handled by .BLANK. + */ + if (Tok == TOK_RPAREN) { + /* No tokens */ + return 1; + } else { + /* Skip any tokens */ + int Braces = 0; + while (Tok != TOK_SEP && Tok != TOK_EOF) { + if (Tok == TOK_LPAREN) { + ++Braces; + } else if (Tok == TOK_RPAREN) { + if (Braces == 0) { + /* Done */ + break; + } else { + --Braces; + } + } + NextTok (); + } + return 0; + } +} + + + +static int FuncConst (void) +/* Handle the .CONST builtin function */ +{ + /* Read an expression */ + ExprNode* Expr = Expression (); + + /* Check the constness of the expression */ + int Result = IsConstExpr (Expr); + + /* Free the expression */ + FreeExpr (Expr); + + /* Done */ + return Result; +} + + + +static int FuncDefined (void) +/* Handle the .DEFINED builtin function */ +{ + int Result = 0; + + if (Tok != TOK_IDENT) { + Error (ERR_IDENT_EXPECTED); + if (Tok != TOK_RPAREN) { + NextTok (); + } + } else { + Result = SymIsDef (SVal); + NextTok (); + } + + /* Done */ + return Result; +} + + + +static int DoMatch (enum TC EqualityLevel) +/* Handle the .MATCH and .XMATCH builtin functions */ +{ + int Result; + TokNode* Root = 0; + TokNode* Last = 0; + TokNode* Node = 0; + + /* A list of tokens follows. Read this list and remember it building a + * single linked list of tokens including attributes. The list is + * terminated by a comma. + */ + while (Tok != TOK_COMMA) { + + /* We may not end-of-line of end-of-file here */ + if (Tok == TOK_SEP || Tok == TOK_EOF) { + Error (ERR_UNEXPECTED_EOL); + return 0; + } + + /* Get a node with this token */ + Node = NewTokNode (); + + /* Insert the node into the list */ + if (Last == 0) { + Root = Node; + } else { + Last->Next = Node; + } + Last = Node; + + /* Skip the token */ + NextTok (); + } + + /* Skip the comma */ + NextTok (); + + /* Read the second list which is terminated by the right parenthesis and + * compare each token against the one in the first list. + */ + Result = 1; + Node = Root; + while (Tok != TOK_RPAREN) { + + /* We may not end-of-line of end-of-file here */ + if (Tok == TOK_SEP || Tok == TOK_EOF) { + Error (ERR_UNEXPECTED_EOL); + return 0; + } + + /* Compare the tokens if the result is not already known */ + if (Result != 0) { + if (Node == 0) { + /* The second list is larger than the first one */ + Result = 0; + } else if (TokCmp (Node) < EqualityLevel) { + /* Tokens do not match */ + Result = 0; + } + } + + /* Next token in first list */ + if (Node) { + Node = Node->Next; + } + + /* Next token in current list */ + NextTok (); + } + + /* Check if there are remaining tokens in the first list */ + if (Node != 0) { + Result = 0; + } + + /* Free the token list */ + while (Root) { + Node = Root; + Root = Root->Next; + FreeTokNode (Node); + } + + /* Done, return the result */ + return Result; +} + + + +static int FuncMatch (void) +/* Handle the .MATCH function */ +{ + return DoMatch (tcSameToken); +} + + + +static int FuncReferenced (void) +/* Handle the .REFERENCED builtin function */ +{ + int Result = 0; + + if (Tok != TOK_IDENT) { + Error (ERR_IDENT_EXPECTED); + if (Tok != TOK_RPAREN) { + NextTok (); + } + } else { + Result = SymIsRef (SVal); + NextTok (); + } + + /* Done */ + return Result; +} + + + +static int FuncXMatch (void) +/* Handle the .XMATCH function */ +{ + return DoMatch (tcIdentical); +} + + + +static ExprNode* Function (int (*F) (void)) +/* Handle builtin functions */ +{ + long Result; + + /* Skip the keyword */ + NextTok (); + + /* Expression must be enclosed in braces */ + if (Tok != TOK_LPAREN) { + Error (ERR_LPAREN_EXPECTED); + SkipUntilSep (); + return LiteralExpr (0); + } + NextTok (); + + /* Call the function itself */ + Result = (F () != 0); + + /* Closing brace must follow */ + ConsumeRParen (); + + /* Return an expression node with the boolean code */ + return LiteralExpr (Result); +} + + + +static ExprNode* Factor (void) +{ + ExprNode* N; + SymEntry* S; + + switch (Tok) { + + case TOK_INTCON: + case TOK_CHARCON: + N = LiteralExpr (IVal); + NextTok (); + break; + + case TOK_NAMESPACE: + NextTok (); + if (Tok != TOK_IDENT) { + Error (ERR_IDENT_EXPECTED); + N = LiteralExpr (0); /* Dummy */ + } else { + S = SymRefGlobal (SVal); + if (SymIsConst (S)) { + /* Use the literal value instead */ + N = LiteralExpr (GetSymVal (S)); + } else { + /* Create symbol node */ + N = NewExprNode (); + N->Op = EXPR_SYMBOL; + N->V.Sym = S; + } + NextTok (); + } + break; + + case TOK_IDENT: + S = SymRef (SVal); + if (SymIsConst (S)) { + /* Use the literal value instead */ + N = LiteralExpr (GetSymVal (S)); + } else { + /* Create symbol node */ + N = NewExprNode (); + N->Op = EXPR_SYMBOL; + N->V.Sym = S; + } + NextTok (); + break; + + case TOK_ULABEL: + N = ULabRef (IVal); + NextTok (); + break; + + case TOK_MINUS: + NextTok (); + N = NewExprNode (); + N->Left = Factor (); + N->Op = EXPR_UNARY_MINUS; + break; + + case TOK_NOT: + NextTok (); + N = NewExprNode (); + N->Left = Factor (); + N->Op = EXPR_NOT; + break; + + case TOK_STAR: + case TOK_PC: + NextTok (); + N = CurrentPC (); + break; + + case TOK_LT: + NextTok (); + N = NewExprNode (); + N->Left = Factor (); + N->Op = EXPR_LOBYTE; + break; + + case TOK_GT: + NextTok (); + N = NewExprNode (); + N->Left = Factor (); + N->Op = EXPR_HIBYTE; + break; + + case TOK_LPAREN: + NextTok (); + N = Expr0 (); + ConsumeRParen (); + break; + + case TOK_BLANK: + N = Function (FuncBlank); + break; + + case TOK_CONST: + N = Function (FuncConst); + break; + + case TOK_CPU: + N = LiteralExpr (GetCPU()); + NextTok (); + break; + + case TOK_DEFINED: + N = Function (FuncDefined); + break; + + case TOK_MATCH: + N = Function (FuncMatch); + break; + + case TOK_REFERENCED: + N = Function (FuncReferenced); + break; + + case TOK_XMATCH: + N = Function (FuncXMatch); + break; + + default: + N = LiteralExpr (0); /* Dummy */ + Error (ERR_SYNTAX); + NextTok (); + break; + } + return N; +} + + + +static ExprNode* Term (void) +{ + ExprNode* Root; + + /* Read left hand side */ + Root = Factor (); + + /* Handle multiplicative operations */ + while (Tok == TOK_MUL || Tok == TOK_DIV || Tok == TOK_MOD || + Tok == TOK_AND || Tok == TOK_XOR || Tok == TOK_SHL || + Tok == TOK_SHR) { + + /* Create a new node and insert the left expression */ + ExprNode* Left = Root; + Root = NewExprNode (); + Root->Left = Left; + + /* Determine the operator token */ + switch (Tok) { + case TOK_MUL: Root->Op = EXPR_MUL; break; + case TOK_DIV: Root->Op = EXPR_DIV; break; + case TOK_MOD: Root->Op = EXPR_MOD; break; + case TOK_AND: Root->Op = EXPR_AND; break; + case TOK_XOR: Root->Op = EXPR_XOR; break; + case TOK_SHL: Root->Op = EXPR_SHL; break; + case TOK_SHR: Root->Op = EXPR_SHR; break; + default: Internal ("Invalid token"); + } + NextTok (); + + /* Parse the right hand side */ + Root->Right = Factor (); + + } + + /* Return the expression tree we've created */ + return Root; +} + + + +static ExprNode* SimpleExpr (void) +{ + ExprNode* Root; + + /* Read left hand side */ + Root = Term (); + + /* Handle additive operations */ + while (Tok == TOK_PLUS || Tok == TOK_MINUS || Tok == TOK_OR) { + + /* Create a new node and insert the left expression */ + ExprNode* Left = Root; + Root = NewExprNode (); + Root->Left = Left; + + /* Determine the operator token */ + switch (Tok) { + case TOK_PLUS: Root->Op = EXPR_PLUS; break; + case TOK_MINUS: Root->Op = EXPR_MINUS; break; + case TOK_OR: Root->Op = EXPR_OR; break; + default: Internal ("Invalid token"); + } + NextTok (); + + /* Parse the right hand side */ + Root->Right = Term (); + + } + + /* Return the expression tree we've created */ + return Root; +} + + + +static ExprNode* BoolExpr (void) +/* Evaluate a boolean expression */ +{ + /* Read left hand side */ + ExprNode* Root = SimpleExpr (); + + /* Handle booleans */ + while (Tok == TOK_EQ || Tok == TOK_NE || Tok == TOK_LT || + Tok == TOK_GT || Tok == TOK_LE || Tok == TOK_GE) { + + /* Create a new node and insert the left expression */ + ExprNode* Left = Root; + Root = NewExprNode (); + Root->Left = Left; + + /* Determine the operator token */ + switch (Tok) { + case TOK_EQ: Root->Op = EXPR_EQ; break; + case TOK_NE: Root->Op = EXPR_NE; break; + case TOK_LT: Root->Op = EXPR_LT; break; + case TOK_GT: Root->Op = EXPR_GT; break; + case TOK_LE: Root->Op = EXPR_LE; break; + case TOK_GE: Root->Op = EXPR_GE; break; + default: Internal ("Invalid token"); + } + NextTok (); + + /* Parse the right hand side */ + Root->Right = SimpleExpr (); + + } + + /* Return the expression tree we've created */ + return Root; +} + + + +static ExprNode* Expr2 (void) +/* Boolean operators: AND and XOR */ +{ + /* Read left hand side */ + ExprNode* Root = BoolExpr (); + + /* Handle booleans */ + while (Tok == TOK_BAND || Tok == TOK_BXOR) { + + /* Create a new node and insert the left expression */ + ExprNode* Left = Root; + Root = NewExprNode (); + Root->Left = Left; + + /* Determine the operator token */ + switch (Tok) { + case TOK_BAND: Root->Op = EXPR_BAND; break; + case TOK_BXOR: Root->Op = EXPR_BXOR; break; + default: Internal ("Invalid token"); + } + NextTok (); + + /* Parse the right hand side */ + Root->Right = BoolExpr (); + + } + + /* Return the expression tree we've created */ + return Root; +} + + + +static ExprNode* Expr1 (void) +/* Boolean operators: OR */ +{ + /* Read left hand side */ + ExprNode* Root = Expr2 (); + + /* Handle booleans */ + while (Tok == TOK_BOR) { + + /* Create a new node and insert the left expression */ + ExprNode* Left = Root; + Root = NewExprNode (); + Root->Left = Left; + + /* Determine the operator token */ + switch (Tok) { + case TOK_BOR: Root->Op = EXPR_BOR; break; + default: Internal ("Invalid token"); + } + NextTok (); + + /* Parse the right hand side */ + Root->Right = Expr2 (); + + } + + /* Return the expression tree we've created */ + return Root; +} + + + +static ExprNode* Expr0 (void) +/* Boolean operators: NOT */ +{ + ExprNode* Root; + + /* Handle booleans */ + if (Tok == TOK_BNOT) { + + /* Create a new node */ + Root = NewExprNode (); + + /* Determine the operator token */ + switch (Tok) { + case TOK_BNOT: Root->Op = EXPR_BNOT; break; + default: Internal ("Invalid token"); + } + NextTok (); + + /* Parse the left hand side, allow more BNOTs */ + Root->Left = Expr0 (); + + } else { + + /* Read left hand side */ + Root = Expr1 (); + + } + + /* Return the expression tree we've created */ + return Root; +} + + + +static ExprNode* SimplifyExpr (ExprNode* Root) +/* Try to simplify the given expression tree */ +{ + if (Root) { + SimplifyExpr (Root->Left); + SimplifyExpr (Root->Right); + if (IsConstExpr (Root)) { + /* The complete expression is constant */ + Root->V.Val = GetExprVal (Root); + Root->Op = EXPR_LITERAL; + FreeExpr (Root->Left); + FreeExpr (Root->Right); + Root->Left = Root->Right = 0; + } + } + return Root; +} + + + +ExprNode* Expression (void) +/* Evaluate an expression, build the expression tree on the heap and return + * a pointer to the root of the tree. + */ +{ + return SimplifyExpr (Expr0 ()); +} + + + +long ConstExpression (void) +/* Parse an expression. Check if the expression is const, and print an error + * message if not. Return the value of the expression, or a dummy, if it is + * not constant. + */ +{ + /* Read the expression, and call finalize (exception here, since we + * expect a const). + */ + ExprNode* Expr = FinalizeExpr (Expression ()); + + /* Return the value */ + if (IsConstExpr (Expr)) { + return GetExprVal (Expr); + } else { + Error (ERR_CONSTEXPR_EXPECTED); + return 0; + } +} + + + +ExprNode* LiteralExpr (long Val) +/* Return an expression tree that encodes the given literal value */ +{ + ExprNode* Expr = NewExprNode (); + Expr->Op = EXPR_LITERAL; + Expr->V.Val = Val; + return Expr; +} + + + +ExprNode* CurrentPC (void) +/* Return the current program counter as expression */ +{ + ExprNode* Left; + ExprNode* Root; + + if (RelocMode) { + /* Create SegmentBase + Offset */ + Left = NewExprNode (); + Left->Op = EXPR_SEGMENT; + Left->V.SegNum = GetSegNum (); + + Root = NewExprNode (); + Root->Left = Left; + Root->Right = LiteralExpr (GetPC ()); + Root->Op = EXPR_PLUS; + } else { + /* Absolute mode, just return PC value */ + Root = LiteralExpr (GetPC ()); + } + + return Root; +} + + + +ExprNode* SwapExpr (ExprNode* Expr) +/* Return an extended expression with lo and hi bytes swapped */ +{ + ExprNode* N = NewExprNode (); + N->Op = EXPR_SWAP; + N->Left = Expr; + return N; +} + + + +ExprNode* BranchExpr (unsigned Offs) +/* Return an expression that encodes the difference between current PC plus + * offset and the target expression (that is, Expression() - (*+Offs) ). + */ +{ + ExprNode* N; + ExprNode* Root; + ExprNode* Left; + + /* Create *+Offs */ + if (RelocMode) { + Left = NewExprNode (); + Left->Op = EXPR_SEGMENT; + Left->V.SegNum = GetSegNum (); + + N = NewExprNode (); + N->Left = Left; + N->Right = LiteralExpr (GetPC () + Offs); + N->Op = EXPR_PLUS; + } else { + N = LiteralExpr (GetPC () + Offs); + } + + /* Create the root node */ + Root = NewExprNode (); + Root->Left = Expression (); + Root->Right = N; + Root->Op = EXPR_MINUS; + + /* Return the result */ + return SimplifyExpr (Root); +} + + + +ExprNode* ULabelExpr (unsigned Num) +/* Return an expression for an unnamed label with the given index */ +{ + /* Get an expression node */ + ExprNode* Node = NewExprNode (); + + /* Set the values */ + Node->Op = EXPR_ULABEL; + Node->V.Val = Num; + + /* Return the new node */ + return Node; +} + + + +void FreeExpr (ExprNode* Root) +/* Free the expression, Root is pointing to. */ +{ + if (Root) { + FreeExpr (Root->Left); + FreeExpr (Root->Right); + FreeExprNode (Root); + } +} + + + +ExprNode* ForceWordExpr (ExprNode* Expr) +/* Force the given expression into a word and return the result. */ +{ + /* And the expression by $FFFF to force it into word size */ + ExprNode* Root = NewExprNode (); + Root->Left = Expr; + Root->Op = EXPR_AND; + Root->Right = LiteralExpr (0xFFFF); + + /* Return the result */ + return Root; +} + + + +int IsConstExpr (ExprNode* Root) +/* Return true if the given expression is a constant expression, that is, one + * with no references to external symbols. + */ +{ + int Const; + SymEntry* Sym; + + if (EXPR_IS_LEAF (Root->Op)) { + switch (Root->Op) { + + case EXPR_LITERAL: + return 1; + + case EXPR_SYMBOL: + Sym = Root->V.Sym; + if (SymHasUserMark (Sym)) { + if (Verbose) { + DumpExpr (Root); + } + PError (GetSymPos (Sym), ERR_CIRCULAR_REFERENCE); + Const = 0; + } else { + SymMarkUser (Sym); + Const = SymIsConst (Sym); + SymUnmarkUser (Sym); + } + return Const; + + default: + return 0; + + } + } else if (EXPR_IS_UNARY (Root->Op)) { + + return IsConstExpr (Root->Left); + + } else { + + /* We must handle shortcut boolean expressions here */ + switch (Root->Op) { + + case EXPR_BAND: + if (IsConstExpr (Root->Left)) { + /* lhs is const, if it is zero, don't eval right */ + if (GetExprVal (Root->Left) == 0) { + return 1; + } else { + return IsConstExpr (Root->Right); + } + } else { + /* lhs not const --> tree not const */ + return 0; + } + break; + + case EXPR_BOR: + if (IsConstExpr (Root->Left)) { + /* lhs is const, if it is not zero, don't eval right */ + if (GetExprVal (Root->Left) != 0) { + return 1; + } else { + return IsConstExpr (Root->Right); + } + } else { + /* lhs not const --> tree not const */ + return 0; + } + break; + + default: + /* All others are handled normal */ + return IsConstExpr (Root->Left) && IsConstExpr (Root->Right); + } + } +} + + + +static void CheckByteExpr (const ExprNode* N, int* IsByte) +/* Internal routine that is recursively called to check if there is a zeropage + * symbol in the expression tree. + */ +{ + if (N) { + switch (N->Op & EXPR_TYPEMASK) { + + case EXPR_LEAFNODE: + switch (N->Op) { + + case EXPR_SYMBOL: + if (SymIsZP (N->V.Sym)) { + *IsByte = 1; + } + break; + + case EXPR_SEGMENT: + if (GetSegType (N->V.SegNum) == SEGTYPE_ZP) { + *IsByte = 1; + } + break; + + } + break; + + case EXPR_UNARYNODE: + CheckByteExpr (N->Left, IsByte); + break; + + case EXPR_BINARYNODE: + CheckByteExpr (N->Left, IsByte); + CheckByteExpr (N->Right, IsByte); + break; + + default: + Internal ("Unknown expression op: %02X", N->Op); + } + } +} + + + +int IsByteExpr (ExprNode* Root) +/* Return true if this is a byte expression */ +{ + int IsByte; + + if (IsConstExpr (Root)) { + if (Root->Op != EXPR_LITERAL) { + SimplifyExpr (Root); + } + return IsByteRange (GetExprVal (Root)); + } else if (Root->Op == EXPR_LOBYTE || Root->Op == EXPR_HIBYTE) { + /* Symbol forced to have byte range */ + IsByte = 1; + } else { + /* We have undefined symbols in the expression. Assume that the + * expression is a byte expression if there is at least one symbol + * declared as zeropage in it. Being wrong here is not a very big + * problem since the linker knows about all symbols and detects + * error like mixing absolute and zeropage labels. + */ + IsByte = 0; + CheckByteExpr (Root, &IsByte); + } + return IsByte; +} + + + +long GetExprVal (ExprNode* Expr) +/* Get the value of a constant expression */ +{ + long Right, Left; + + switch (Expr->Op) { + + case EXPR_LITERAL: + return Expr->V.Val; + + case EXPR_SYMBOL: + return GetSymVal (Expr->V.Sym); + + case EXPR_PLUS: + return GetExprVal (Expr->Left) + GetExprVal (Expr->Right); + + case EXPR_MINUS: + return GetExprVal (Expr->Left) - GetExprVal (Expr->Right); + + case EXPR_MUL: + return GetExprVal (Expr->Left) * GetExprVal (Expr->Right); + + case EXPR_DIV: + Left = GetExprVal (Expr->Left); + Right = GetExprVal (Expr->Right); + if (Right == 0) { + Error (ERR_DIV_BY_ZERO); + return 0; + } + return Left / Right; + + case EXPR_MOD: + Left = GetExprVal (Expr->Left); + Right = GetExprVal (Expr->Right); + if (Right == 0) { + Error (ERR_MOD_BY_ZERO); + return 0; + } + return Left % Right; + + case EXPR_OR: + return GetExprVal (Expr->Left) | GetExprVal (Expr->Right); + + case EXPR_XOR: + return GetExprVal (Expr->Left) ^ GetExprVal (Expr->Right); + + case EXPR_AND: + return GetExprVal (Expr->Left) & GetExprVal (Expr->Right); + + case EXPR_SHL: + return GetExprVal (Expr->Left) << GetExprVal (Expr->Right); + + case EXPR_SHR: + return GetExprVal (Expr->Left) >> GetExprVal (Expr->Right); + + case EXPR_EQ: + return (GetExprVal (Expr->Left) == GetExprVal (Expr->Right)); + + case EXPR_NE: + return (GetExprVal (Expr->Left) != GetExprVal (Expr->Right)); + + case EXPR_LT: + return (GetExprVal (Expr->Left) < GetExprVal (Expr->Right)); + + case EXPR_GT: + return (GetExprVal (Expr->Left) > GetExprVal (Expr->Right)); + + case EXPR_LE: + return (GetExprVal (Expr->Left) <= GetExprVal (Expr->Right)); + + case EXPR_GE: + return (GetExprVal (Expr->Left) >= GetExprVal (Expr->Right)); + + case EXPR_UNARY_MINUS: + return -GetExprVal (Expr->Left); + + case EXPR_NOT: + return ~GetExprVal (Expr->Left); + + case EXPR_LOBYTE: + return GetExprVal (Expr->Left) & 0xFF; + + case EXPR_HIBYTE: + return (GetExprVal (Expr->Left) >> 8) & 0xFF; + + case EXPR_SWAP: + Left = GetExprVal (Expr->Left); + return ((Left >> 8) & 0x00FF) | ((Left << 8) & 0xFF00); + + case EXPR_BAND: + return GetExprVal (Expr->Left) && GetExprVal (Expr->Right); + + case EXPR_BOR: + return GetExprVal (Expr->Left) || GetExprVal (Expr->Right); + + case EXPR_BXOR: + return (GetExprVal (Expr->Left) != 0) ^ (GetExprVal (Expr->Right) != 0); + + case EXPR_BNOT: + return !GetExprVal (Expr->Left); + + case EXPR_ULABEL: + Internal ("GetExprVal called for EXPR_ULABEL"); + /* NOTREACHED */ + return 0; + + default: + Internal ("Unknown Op type: %u", Expr->Op); + /* NOTREACHED */ + return 0; + } +} + + + +static ExprNode* RemoveSyms (ExprNode* Expr, int MustClone) +/* Remove resolved symbols from the tree by cloning symbol expressions */ +{ + /* Accept NULL pointers */ + if (Expr == 0) { + return 0; + } + + /* Special node handling */ + switch (Expr->Op) { + + case EXPR_SYMBOL: + if (SymHasExpr (Expr->V.Sym)) { + /* The symbol has an expression tree */ + SymEntry* Sym = Expr->V.Sym; + if (SymHasUserMark (Sym)) { + /* Circular definition */ + if (Verbose) { + DumpExpr (Expr); + } + PError (GetSymPos (Sym), ERR_CIRCULAR_REFERENCE); + return LiteralExpr (0); /* Return a dummy value */ + } + SymMarkUser (Sym); + Expr = RemoveSyms (GetSymExpr (Sym), 1); + SymUnmarkUser (Sym); + return Expr; + } else if (SymIsConst (Expr->V.Sym)) { + /* The symbol is a constant */ + return LiteralExpr (GetSymVal (Expr->V.Sym)); + } + break; + + case EXPR_ULABEL: + if (ULabCanResolve ()) { + ExprNode* NewExpr = ULabResolve (Expr->V.Val); + FreeExpr (Expr); + Expr = NewExpr; + } + break; + + } + + /* Clone the current node if needed */ + if (MustClone) { + + /* Create a new node */ + ExprNode* Clone = NewExprNode (); + + /* Clone the operation */ + Clone->Op = Expr->Op; + + /* Clone the attribute if needed */ + switch (Expr->Op) { + + case EXPR_LITERAL: + case EXPR_ULABEL: + Clone->V.Val = Expr->V.Val; + break; + + case EXPR_SYMBOL: + Clone->V.Sym = Expr->V.Sym; + break; + + case EXPR_SEGMENT: + Clone->V.SegNum = Expr->V.SegNum; + break; + + } + + /* Clone the tree nodes */ + Clone->Left = RemoveSyms (Expr->Left, MustClone); + Clone->Right = RemoveSyms (Expr->Right, MustClone); + + /* Done */ + return Clone; + + } else { + + /* Nothing to clone */ + Expr->Left = RemoveSyms (Expr->Left, MustClone); + Expr->Right = RemoveSyms (Expr->Right, MustClone); + + /* Done */ + return Expr; + + } +} + + + +static ExprNode* ConstExtract (ExprNode* Expr, long* Val, int Sign) +/* Extract and evaluate all constant factors in an subtree that has only + * additions and subtractions. + */ +{ + if (Expr->Op == EXPR_LITERAL) { + if (Sign < 0) { + *Val -= Expr->V.Val; + } else { + *Val += Expr->V.Val; + } + FreeExprNode (Expr); + return 0; + } + + if (Expr->Op == EXPR_PLUS || Expr->Op == EXPR_MINUS) { + ExprNode* Left; + ExprNode* Right; + Left = ConstExtract (Expr->Left, Val, Sign); + if (Expr->Op == EXPR_MINUS) { + Sign = -Sign; + } + Right = ConstExtract (Expr->Right, Val, Sign); + if (Left == 0 && Right == 0) { + FreeExprNode (Expr); + return 0; + } else if (Left == 0) { + FreeExprNode (Expr); + return Right; + } else if (Right == 0) { + FreeExprNode (Expr); + return Left; + } else { + /* Check for SEG - SEG which is now possible */ + if (Left->Op == EXPR_SEGMENT && Right->Op == EXPR_SEGMENT && + Left->V.SegNum == Right->V.SegNum) { + /* SEG - SEG, remove it completely */ + FreeExprNode (Left); + FreeExprNode (Right); + FreeExprNode (Expr); + return 0; + } else { + Expr->Left = Left; + Expr->Right = Right; + return Expr; + } + } + } + + /* Some other sort of node, finalize the terms */ + if (Expr->Left) { + Expr->Left = FinalizeExpr (Expr->Left); + } + if (Expr->Right) { + Expr->Right = FinalizeExpr (Expr->Right); + } + + return Expr; +} + + + +ExprNode* FinalizeExpr (ExprNode* Expr) +/* Resolve any symbols by cloning the symbol expression tree instead of the + * symbol reference, then try to simplify the expression as much as possible. + * This function must only be called if all symbols are resolved (no undefined + * symbol errors). + */ +{ + long Val = 0; + ExprNode* N; + + Expr = RemoveSyms (Expr, 0); + Expr = ConstExtract (Expr, &Val, 1); + if (Expr == 0) { + /* Reduced to a literal value */ + Expr = LiteralExpr (Val); + } else if (Val) { + /* Extracted a value */ + N = NewExprNode (); + N->Op = EXPR_PLUS; + N->Left = Expr; + N->Right = LiteralExpr (Val); + Expr = N; + } + return Expr; +} + + + +ExprNode* CloneExpr (ExprNode* Expr) +/* Clone the given expression tree. The function will simply clone symbol + * nodes, it will not resolve them. + */ +{ + ExprNode* Clone; + + /* Accept NULL pointers */ + if (Expr == 0) { + return 0; + } + + /* Get a new node */ + Clone = NewExprNode (); + + /* Clone the operation */ + Clone->Op = Expr->Op; + + /* Clone the attribute if needed */ + switch (Expr->Op) { + + case EXPR_LITERAL: + case EXPR_ULABEL: + Clone->V.Val = Expr->V.Val; + break; + + case EXPR_SYMBOL: + Clone->V.Sym = Expr->V.Sym; + break; + + case EXPR_SEGMENT: + Clone->V.SegNum = Expr->V.SegNum; + break; + + } + + /* Clone the tree nodes */ + Clone->Left = CloneExpr (Expr->Left); + Clone->Right = CloneExpr (Expr->Right); + + /* Done */ + return Clone; +} + + + +void WriteExpr (ExprNode* Expr) +/* Write the given expression to the object file */ +{ + /* Null expressions are encoded by a type byte of zero */ + if (Expr == 0) { + ObjWrite8 (0); + return; + } + + /* Write the expression code */ + ObjWrite8 (Expr->Op); + + /* If the is a leafnode, write the expression attribute, otherwise + * write the expression operands. + */ + switch (Expr->Op) { + + case EXPR_LITERAL: + ObjWrite32 (Expr->V.Val); + break; + + case EXPR_SYMBOL: + /* Maybe we should use a code here? */ + CHECK (SymIsImport (Expr->V.Sym)); /* Safety */ + ObjWrite16 (GetSymIndex (Expr->V.Sym)); + break; + + case EXPR_SEGMENT: + ObjWrite8 (Expr->V.SegNum); + break; + + case EXPR_ULABEL: + Internal ("WriteExpr: Cannot write EXPR_ULABEL nodes"); + break; + + default: + /* Not a leaf node */ + WriteExpr (Expr->Left); + WriteExpr (Expr->Right); + break; + + } +} + + + diff --git a/src/ca65/expr.h b/src/ca65/expr.h new file mode 100644 index 000000000..e80d3133a --- /dev/null +++ b/src/ca65/expr.h @@ -0,0 +1,127 @@ +/*****************************************************************************/ +/* */ +/* expr.h */ +/* */ +/* Expression evaluation for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef EXPR_H +#define EXPR_H + + + +#include "../common/exprdefs.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +ExprNode* Expression (void); +/* Evaluate an expression, build the expression tree on the heap and return + * a pointer to the root of the tree. + */ + +long ConstExpression (void); +/* Parse an expression. Check if the expression is const, and print an error + * message if not. Return the value of the expression, or a dummy, if it is + * not constant. + */ + +void FreeExpr (ExprNode* Root); +/* Free the expression tree, Root is pointing to. */ + +ExprNode* LiteralExpr (long Val); +/* Return an expression tree that encodes the given literal value */ + +ExprNode* CurrentPC (void); +/* Return the current program counter as expression */ + +ExprNode* SwapExpr (ExprNode* Expr); +/* Return an extended expression with lo and hi bytes swapped */ + +ExprNode* BranchExpr (unsigned Offs); +/* Return an expression that encodes the difference between current PC plus + * offset and the target expression (that is, Expression() - (*+Offs) ). + */ + +ExprNode* ULabelExpr (unsigned Num); +/* Return an expression for an unnamed label with the given index */ + +ExprNode* ForceWordExpr (ExprNode* Expr); +/* Force the given expression into a word and return the result. */ + +int IsConstExpr (ExprNode* Root); +/* Return true if the given expression is a constant expression, that is, one + * with no references to external symbols. + */ + +int IsByteExpr (ExprNode* Root); +/* Return true if this is a byte expression */ + +long GetExprVal (ExprNode* Expr); +/* Get the value of a constant expression */ + +int IsByteRange (long Val); +/* Return true if this is a byte value */ + +int IsWordRange (long Val); +/* Return true if this is a word value */ + +void DumpExpr (ExprNode* Expr); +/* Dump an expression tree to stdout */ + +ExprNode* FinalizeExpr (ExprNode* Expr); +/* Resolve any symbols by cloning the symbol expression tree instead of the + * symbol reference, then try to simplify the expression as much as possible. + * This function must only be called if all symbols are resolved (no undefined + * symbol errors). + */ + +ExprNode* CloneExpr (ExprNode* Expr); +/* Clone the given expression tree. The function will simply clone symbol + * nodes, it will not resolve them. + */ + +void WriteExpr (ExprNode* Expr); +/* Write the given expression to the object file */ + + + +/* End of expr.h */ + +#endif + + + diff --git a/src/ca65/fname.c b/src/ca65/fname.c new file mode 100644 index 000000000..e764d5202 --- /dev/null +++ b/src/ca65/fname.c @@ -0,0 +1,74 @@ +/*****************************************************************************/ +/* */ +/* fname.c */ +/* */ +/* File name handling utilities */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "mem.h" +#include "fname.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +char* MakeFilename (const char* Origin, const char* Ext) +/* Make a new file name from Origin and Ext. If Origin has an extension, it + * is removed and Ext is appended. If Origin has no extension, Ext is simply + * appended. The result is placed in a malloc'ed buffer and returned. + * The function may be used to create "foo.o" from "foo.s". + */ +{ + /* Construct the name */ + char* Result; + const char* P = strrchr (Origin, '.'); + if (P == 0) { + /* No dot, add the extension */ + Result = Xmalloc (strlen (Origin) + strlen (Ext) + 1); + strcpy (Result, Origin); + strcat (Result, Ext); + } else { + Result = Xmalloc (P - Origin + strlen (Ext) + 1); + memcpy (Result, Origin, P - Origin); + strcpy (Result + (P - Origin), Ext); + } + return Result; +} + + + + diff --git a/src/ca65/fname.h b/src/ca65/fname.h new file mode 100644 index 000000000..671bf8043 --- /dev/null +++ b/src/ca65/fname.h @@ -0,0 +1,61 @@ +/*****************************************************************************/ +/* */ +/* fname.h */ +/* */ +/* File name handling utilities */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef FNAME_H +#define FNAME_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +char* MakeFilename (const char* Origin, const char* Ext); +/* Make a new file name from Origin and Ext. If Origin has an extension, it + * is removed and Ext is appended. If Origin has no extension, Ext is simply + * appended. The result is placed in a malloc'ed buffer and returned. + * The function may be used to create "foo.o" from "foo.s". + */ + + + +/* End of fname.h */ + +#endif + + + diff --git a/src/ca65/fragment.c b/src/ca65/fragment.c new file mode 100644 index 000000000..2d269be21 --- /dev/null +++ b/src/ca65/fragment.c @@ -0,0 +1,51 @@ +/*****************************************************************************/ +/* */ +/* fragment.c */ +/* */ +/* Data fragments for the ca65 crossassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "fragment.h" + + + +/*****************************************************************************/ +/* struct Fragment */ +/*****************************************************************************/ + + + +/* List of all fragments */ +Fragment* FragList = 0; +Fragment* FragLast = 0; + + + diff --git a/src/ca65/fragment.h b/src/ca65/fragment.h new file mode 100644 index 000000000..e2ef06517 --- /dev/null +++ b/src/ca65/fragment.h @@ -0,0 +1,78 @@ +/*****************************************************************************/ +/* */ +/* fragment.h */ +/* */ +/* Data fragments for the ca65 crossassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef FRAGMENT_H +#define FRAGMENT_H + + + +#include "../common/exprdefs.h" +#include "../common/filepos.h" + + + +/*****************************************************************************/ +/* struct Fragment */ +/*****************************************************************************/ + + + +typedef struct Fragment_ Fragment; +struct Fragment_ { + Fragment* List; /* List of all fragments */ + Fragment* Next; /* Fragment list in one segment */ + Fragment* LineList; /* List of fragments for one src line */ + FilePos Pos; /* File position for this fragment */ + unsigned short Len; /* Length for this fragment */ + unsigned char Type; /* Fragment type */ + union { + unsigned char Data [4]; /* Literal values */ + ExprNode* Expr; /* Expression */ + } V; +}; + + + +/* List of all fragments */ +extern Fragment* FragList; +extern Fragment* FragLast; + + + +/* End of fragment.h */ +#endif + + + diff --git a/src/ca65/global.c b/src/ca65/global.c new file mode 100644 index 000000000..24fb34f28 --- /dev/null +++ b/src/ca65/global.c @@ -0,0 +1,76 @@ +/*****************************************************************************/ +/* */ +/* global.c */ +/* */ +/* Global variables for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "global.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +const char* ProgName = "ca65"; /* Program name */ + +/* File names */ +const char* InFile = 0; /* Name of input file */ +const char* OutFile = 0; /* Name of output file */ +const char* ListFile = 0; /* Name of listing file */ + +/* Default extensions */ +const char ObjExt[] = ".o"; /* Default object extension */ +const char ListExt[] = ".lst"; /* Default listing extension */ + +char LocalStart = '@'; /* This char starts local symbols */ + +unsigned char IgnoreCase = 0; /* Ignore case on identifiers? */ +unsigned char AutoImport = 0; /* Mark unresolveds as import */ +unsigned char Verbose = 0; /* Verbose operation flag */ +unsigned char SmartMode = 0; /* Smart mode */ +unsigned char DbgSyms = 0; /* Add debug symbols */ +unsigned char Listing = 0; /* Create listing file */ +unsigned char LineCont = 0; /* Allow line continuation */ + +/* Emulation features */ +unsigned char DollarIsPC = 0; /* Allow the $ symbol as current PC */ +unsigned char NoColonLabels = 0; /* Allow labels without a colon */ +unsigned char LooseStringTerm = 0; /* Allow ' as string terminator */ +unsigned char AtInIdents = 0; /* Allow '@' in identifiers */ +unsigned char DollarInIdents = 0; /* Allow '$' in identifiers */ + + + + diff --git a/src/ca65/global.h b/src/ca65/global.h new file mode 100644 index 000000000..4bd7f9af0 --- /dev/null +++ b/src/ca65/global.h @@ -0,0 +1,82 @@ +/*****************************************************************************/ +/* */ +/* global.h */ +/* */ +/* Global variables for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef GLOBAL_H +#define GLOBAL_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +extern const char* ProgName; /* Program name */ + +/* File names */ +extern const char* InFile; /* Name of input file */ +extern const char* OutFile; /* Name of output file */ +extern const char* ListFile; /* Name of listing file */ + +/* Default extensions */ +extern const char ObjExt[]; /* Default object extension */ +extern const char ListExt[]; /* Default listing extension */ + +extern char LocalStart; /* This char starts local symbols */ + +extern unsigned char IgnoreCase; /* Ignore case on identifiers? */ +extern unsigned char AutoImport; /* Mark unresolveds as import */ +extern unsigned char Verbose; /* Verbose operation flag */ +extern unsigned char SmartMode; /* Smart mode */ +extern unsigned char DbgSyms; /* Add debug symbols */ +extern unsigned char Listing; /* Create listing file */ +extern unsigned char LineCont; /* Allow line continuation */ + +/* Emulation features */ +extern unsigned char DollarIsPC; /* Allow the $ symbol as current PC */ +extern unsigned char NoColonLabels; /* Allow labels without a colon */ +extern unsigned char LooseStringTerm;/* Allow ' as string terminator */ +extern unsigned char AtInIdents; /* Allow '@' in identifiers */ +extern unsigned char DollarInIdents; /* Allow '$' in identifiers */ + + + +/* End of global.h */ + +#endif + + + diff --git a/src/ca65/instr.c b/src/ca65/instr.c new file mode 100644 index 000000000..50c74923b --- /dev/null +++ b/src/ca65/instr.c @@ -0,0 +1,748 @@ +/*****************************************************************************/ +/* */ +/* instr.c */ +/* */ +/* Instruction encoding for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "../common/bitops.h" + +#include "ea.h" +#include "error.h" +#include "expr.h" +#include "global.h" +#include "objcode.h" +#include "scanner.h" +#include "instr.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Forwards for handler functions */ +static void PutPCRel8 (const InsDesc* Ins); +static void PutPCRel16 (const InsDesc* Ins); +static void PutBlockMove (const InsDesc* Ins); +static void PutREP (const InsDesc* Ins); +static void PutSEP (const InsDesc* Ins); +static void PutAll (const InsDesc* Ins); + + + +/* Instruction table for the 6502 */ +#define INS_COUNT_6502 56 +static const struct { + unsigned Count; + InsDesc Ins[INS_COUNT_6502]; +} InsTab6502 = { + INS_COUNT_6502, + { + { "ADC", 0x080A26C, 0x60, 0, PutAll }, + { "AND", 0x080A26C, 0x20, 0, PutAll }, + { "ASL", 0x000006e, 0x02, 1, PutAll }, + { "BCC", 0x0020000, 0x90, 0, PutPCRel8 }, + { "BCS", 0x0020000, 0xb0, 0, PutPCRel8 }, + { "BEQ", 0x0020000, 0xf0, 0, PutPCRel8 }, + { "BIT", 0x000000C, 0x00, 2, PutAll }, + { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, + { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, + { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, + { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, + { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, + { "CLC", 0x0000001, 0x18, 0, PutAll }, + { "CLD", 0x0000001, 0xd8, 0, PutAll }, + { "CLI", 0x0000001, 0x58, 0, PutAll }, + { "CLV", 0x0000001, 0xb8, 0, PutAll }, + { "CMP", 0x080A26C, 0xc0, 0, PutAll }, + { "CPX", 0x080000C, 0xe0, 1, PutAll }, + { "CPY", 0x080000C, 0xc0, 1, PutAll }, + { "DEC", 0x000006C, 0x00, 3, PutAll }, + { "DEX", 0x0000001, 0xca, 0, PutAll }, + { "DEY", 0x0000001, 0x88, 0, PutAll }, + { "EOR", 0x080A26C, 0x40, 0, PutAll }, + { "INC", 0x000006c, 0x00, 4, PutAll }, + { "INX", 0x0000001, 0xe8, 0, PutAll }, + { "INY", 0x0000001, 0xc8, 0, PutAll }, + { "JMP", 0x0000808, 0x4c, 6, PutAll }, + { "JSR", 0x0000008, 0x20, 7, PutAll }, + { "LDA", 0x080A26C, 0xa0, 0, PutAll }, + { "LDX", 0x080030C, 0xa2, 1, PutAll }, + { "LDY", 0x080006C, 0xa0, 1, PutAll }, + { "LSR", 0x000006F, 0x42, 1, PutAll }, + { "NOP", 0x0000001, 0xea, 0, PutAll }, + { "ORA", 0x080A26C, 0x00, 0, PutAll }, + { "PHA", 0x0000001, 0x48, 0, PutAll }, + { "PHP", 0x0000001, 0x08, 0, PutAll }, + { "PLA", 0x0000001, 0x68, 0, PutAll }, + { "PLP", 0x0000001, 0x28, 0, PutAll }, + { "ROL", 0x000006F, 0x22, 1, PutAll }, + { "ROR", 0x000006F, 0x62, 1, PutAll }, + { "RTI", 0x0000001, 0x40, 0, PutAll }, + { "RTS", 0x0000001, 0x60, 0, PutAll }, + { "SBC", 0x080A26C, 0xe0, 0, PutAll }, + { "SEC", 0x0000001, 0x38, 0, PutAll }, + { "SED", 0x0000001, 0xf8, 0, PutAll }, + { "SEI", 0x0000001, 0x78, 0, PutAll }, + { "STA", 0x000A26C, 0x80, 0, PutAll }, + { "STX", 0x000010c, 0x82, 1, PutAll }, + { "STY", 0x000002c, 0x80, 1, PutAll }, + { "TAX", 0x0000001, 0xaa, 0, PutAll }, + { "TAY", 0x0000001, 0xa8, 0, PutAll }, + { "TSX", 0x0000001, 0xba, 0, PutAll }, + { "TXA", 0x0000001, 0x8a, 0, PutAll }, + { "TXS", 0x0000001, 0x9a, 0, PutAll }, + { "TYA", 0x0000001, 0x98, 0, PutAll } + } +}; + +/* Instruction table for the 65SC02 */ +#define INS_COUNT_65SC02 66 +static const struct { + unsigned Count; + InsDesc Ins[INS_COUNT_65SC02]; +} InsTab65SC02 = { + INS_COUNT_65SC02, + { + { "ADC", 0x080A66C, 0x60, 0, PutAll }, + { "AND", 0x080A66C, 0x20, 0, PutAll }, + { "ASL", 0x000006e, 0x02, 1, PutAll }, + { "BCC", 0x0020000, 0x90, 0, PutPCRel8 }, + { "BCS", 0x0020000, 0xb0, 0, PutPCRel8 }, + { "BEQ", 0x0020000, 0xf0, 0, PutPCRel8 }, + { "BIT", 0x080006C, 0x00, 2, PutAll }, + { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, + { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, + { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, + { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, + { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, + { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, + { "CLC", 0x0000001, 0x18, 0, PutAll }, + { "CLD", 0x0000001, 0xd8, 0, PutAll }, + { "CLI", 0x0000001, 0x58, 0, PutAll }, + { "CLV", 0x0000001, 0xb8, 0, PutAll }, + { "CMP", 0x080A66C, 0xc0, 0, PutAll }, + { "CPX", 0x080000C, 0xe0, 1, PutAll }, + { "CPY", 0x080000C, 0xc0, 1, PutAll }, + { "DEA", 0x0000001, 0x00, 3, PutAll }, /* == DEC */ + { "DEC", 0x000006F, 0x00, 3, PutAll }, + { "DEX", 0x0000001, 0xca, 0, PutAll }, + { "DEY", 0x0000001, 0x88, 0, PutAll }, + { "EOR", 0x080A66C, 0x40, 0, PutAll }, + { "INA", 0x0000001, 0x00, 4, PutAll }, /* == INC */ + { "INC", 0x000006f, 0x00, 4, PutAll }, + { "INX", 0x0000001, 0xe8, 0, PutAll }, + { "INY", 0x0000001, 0xc8, 0, PutAll }, + { "JMP", 0x0010808, 0x4c, 6, PutAll }, + { "JSR", 0x0000008, 0x20, 7, PutAll }, + { "LDA", 0x080A66C, 0xa0, 0, PutAll }, + { "LDX", 0x080030C, 0xa2, 1, PutAll }, + { "LDY", 0x080006C, 0xa0, 1, PutAll }, + { "LSR", 0x000006F, 0x42, 1, PutAll }, + { "NOP", 0x0000001, 0xea, 0, PutAll }, + { "ORA", 0x080A66C, 0x00, 0, PutAll }, + { "PHA", 0x0000001, 0x48, 0, PutAll }, + { "PHP", 0x0000001, 0x08, 0, PutAll }, + { "PHX", 0x0000001, 0xda, 0, PutAll }, + { "PHY", 0x0000001, 0x5a, 0, PutAll }, + { "PLA", 0x0000001, 0x68, 0, PutAll }, + { "PLP", 0x0000001, 0x28, 0, PutAll }, + { "PLX", 0x0000001, 0xfa, 0, PutAll }, + { "PLY", 0x0000001, 0x7a, 0, PutAll }, + { "ROL", 0x000006F, 0x22, 1, PutAll }, + { "ROR", 0x000006F, 0x62, 1, PutAll }, + { "RTI", 0x0000001, 0x40, 0, PutAll }, + { "RTS", 0x0000001, 0x60, 0, PutAll }, + { "SBC", 0x080A66C, 0xe0, 0, PutAll }, + { "SEC", 0x0000001, 0x38, 0, PutAll }, + { "SED", 0x0000001, 0xf8, 0, PutAll }, + { "SEI", 0x0000001, 0x78, 0, PutAll }, + { "STA", 0x000A66C, 0x80, 0, PutAll }, + { "STX", 0x000010c, 0x82, 1, PutAll }, + { "STY", 0x000002c, 0x80, 1, PutAll }, + { "STZ", 0x000006c, 0x04, 5, PutAll }, + { "TAX", 0x0000001, 0xaa, 0, PutAll }, + { "TAY", 0x0000001, 0xa8, 0, PutAll }, + { "TRB", 0x000000c, 0x10, 1, PutAll }, + { "TSB", 0x000000c, 0x00, 1, PutAll }, + { "TSX", 0x0000001, 0xba, 0, PutAll }, + { "TXA", 0x0000001, 0x8a, 0, PutAll }, + { "TXS", 0x0000001, 0x9a, 0, PutAll }, + { "TYA", 0x0000001, 0x98, 0, PutAll } + } +}; + +/* Instruction table for the 65816 */ +#define INS_COUNT_65816 101 +static const struct { + unsigned Count; + InsDesc Ins[INS_COUNT_65816]; +} InsTab65816 = { + INS_COUNT_65816, + { + { "ADC", 0x0b8f6fc, 0x60, 0, PutAll }, + { "AND", 0x0b8f6fc, 0x20, 0, PutAll }, + { "ASL", 0x000006e, 0x02, 1, PutAll }, + { "BCC", 0x0020000, 0x90, 0, PutPCRel8 }, + { "BCS", 0x0020000, 0xb0, 0, PutPCRel8 }, + { "BEQ", 0x0020000, 0xf0, 0, PutPCRel8 }, + { "BGE", 0x0020000, 0xb0, 0, PutPCRel8 }, /* == BCS */ + { "BIT", 0x0a0006c, 0x00, 2, PutAll }, + { "BLT", 0x0020000, 0x90, 0, PutPCRel8 }, /* == BCC */ + { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, + { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, + { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, + { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, + { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRL", 0x0040000, 0x82, 0, PutPCRel16 }, + { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, + { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, + { "CLC", 0x0000001, 0x18, 0, PutAll }, + { "CLD", 0x0000001, 0xd8, 0, PutAll }, + { "CLI", 0x0000001, 0x58, 0, PutAll }, + { "CLV", 0x0000001, 0xb8, 0, PutAll }, + { "CMP", 0x0b8f6fc, 0xc0, 0, PutAll }, + { "COP", 0x0000004, 0x02, 6, PutAll }, + { "CPA", 0x0b8f6fc, 0xc0, 0, PutAll }, /* == CMP */ + { "CPX", 0x0c0000c, 0xe0, 1, PutAll }, + { "CPY", 0x0c0000c, 0xc0, 1, PutAll }, + { "DEA", 0x0000001, 0x00, 3, PutAll }, /* == DEC */ + { "DEC", 0x000006F, 0x00, 3, PutAll }, + { "DEX", 0x0000001, 0xca, 0, PutAll }, + { "DEY", 0x0000001, 0x88, 0, PutAll }, + { "EOR", 0x0b8f6fc, 0x40, 0, PutAll }, + { "INA", 0x0000001, 0x00, 4, PutAll }, /* == INC */ + { "INC", 0x000006F, 0x00, 4, PutAll }, + { "INX", 0x0000001, 0xe8, 0, PutAll }, + { "INY", 0x0000001, 0xc8, 0, PutAll }, + { "JML", 0x0000810, 0x5c, 1, PutAll }, + { "JMP", 0x0010818, 0x4c, 6, PutAll }, + { "JSL", 0x0000010, 0x20, 7, PutAll }, + { "JSR", 0x0010018, 0x20, 7, PutAll }, + { "LDA", 0x0b8f6fc, 0xa0, 0, PutAll }, + { "LDX", 0x0c0030c, 0xa2, 1, PutAll }, + { "LDY", 0x0c0006c, 0xa0, 1, PutAll }, + { "LSR", 0x000006F, 0x42, 1, PutAll }, + { "MVN", 0x1000000, 0x54, 0, PutBlockMove }, + { "MVP", 0x1000000, 0x44, 0, PutBlockMove }, + { "NOP", 0x0000001, 0xea, 0, PutAll }, + { "ORA", 0x0b8f6fc, 0x00, 0, PutAll }, + { "PEA", 0x0000008, 0xf4, 6, PutAll }, + { "PEI", 0x0800000, 0xd4, 0, PutAll }, + { "PER", 0x0040000, 0x62, 0, PutPCRel16 }, + { "PHA", 0x0000001, 0x48, 0, PutAll }, + { "PHB", 0x0000001, 0x8b, 0, PutAll }, + { "PHD", 0x0000001, 0x0b, 0, PutAll }, + { "PHK", 0x0000001, 0x4b, 0, PutAll }, + { "PHP", 0x0000001, 0x08, 0, PutAll }, + { "PHX", 0x0000001, 0xda, 0, PutAll }, + { "PHY", 0x0000001, 0x5a, 0, PutAll }, + { "PLA", 0x0000001, 0x68, 0, PutAll }, + { "PLB", 0x0000001, 0xab, 0, PutAll }, + { "PLD", 0x0000001, 0x2b, 0, PutAll }, + { "PLP", 0x0000001, 0x28, 0, PutAll }, + { "PLX", 0x0000001, 0xfa, 0, PutAll }, + { "PLY", 0x0000001, 0x7a, 0, PutAll }, + { "REP", 0x0800000, 0xc2, 1, PutREP }, + { "ROL", 0x000006F, 0x22, 1, PutAll }, + { "ROR", 0x000006F, 0x62, 1, PutAll }, + { "RTI", 0x0000001, 0x40, 0, PutAll }, + { "RTL", 0x0000001, 0x6b, 0, PutAll }, + { "RTS", 0x0000001, 0x60, 0, PutAll }, + { "SBC", 0x0b8f6fc, 0xe0, 0, PutAll }, + { "SEC", 0x0000001, 0x38, 0, PutAll }, + { "SED", 0x0000001, 0xf8, 0, PutAll }, + { "SEI", 0x0000001, 0x78, 0, PutAll }, + { "SEP", 0x0800000, 0xe2, 1, PutSEP }, + { "STA", 0x018f6fc, 0x80, 0, PutAll }, + { "STP", 0x0000001, 0xdb, 0, PutAll }, + { "STX", 0x000010c, 0x82, 1, PutAll }, + { "STY", 0x000002c, 0x80, 1, PutAll }, + { "STZ", 0x000006c, 0x04, 5, PutAll }, + { "SWA", 0x0000001, 0xeb, 0, PutAll }, /* == XBA */ + { "TAD", 0x0000001, 0x5b, 0, PutAll }, /* == TCD */ + { "TAS", 0x0000001, 0x1b, 0, PutAll }, /* == TCS */ + { "TAX", 0x0000001, 0xaa, 0, PutAll }, + { "TAY", 0x0000001, 0xa8, 0, PutAll }, + { "TCD", 0x0000001, 0x5b, 0, PutAll }, + { "TCS", 0x0000001, 0x1b, 0, PutAll }, + { "TDA", 0x0000001, 0x7b, 0, PutAll }, /* == TDC */ + { "TDC", 0x0000001, 0x7b, 0, PutAll }, + { "TRB", 0x000000c, 0x10, 1, PutAll }, + { "TSA", 0x0000001, 0x3b, 0, PutAll }, /* == TSC */ + { "TSB", 0x000000c, 0x00, 1, PutAll }, + { "TSC", 0x0000001, 0x3b, 0, PutAll }, + { "TSX", 0x0000001, 0xba, 0, PutAll }, + { "TXA", 0x0000001, 0x8a, 0, PutAll }, + { "TXS", 0x0000001, 0x9a, 0, PutAll }, + { "TXY", 0x0000001, 0x9b, 0, PutAll }, + { "TYA", 0x0000001, 0x98, 0, PutAll }, + { "TYX", 0x0000001, 0xbb, 0, PutAll }, + { "WAI", 0x0000001, 0xcb, 0, PutAll }, + { "XBA", 0x0000001, 0xeb, 0, PutAll }, + { "XCE", 0x0000001, 0xfb, 0, PutAll } + } +}; + +#ifdef SUNPLUS +/* Table for the SUNPLUS CPU */ +#include "sunplus.inc" +#endif + + + +/* The current CPU and an array with instruction tables */ +static enum CPUType CPU = CPU_6502; +static const InsTable* InsTabs[CPU_COUNT] = { + (const InsTable*) &InsTab6502, + (const InsTable*) &InsTab65SC02, + (const InsTable*) &InsTab65816, +#ifdef SUNPLUS + (const InsTable*) &InsTabSunPlus, +#else + NULL, +#endif +}; +const InsTable* InsTab = (const InsTable*) &InsTab6502; + +/* Table to build the effective opcode from a base opcode and an addressing + * mode. + */ +unsigned char EATab [9][AMI_COUNT] = { + { /* Table 0 */ + 0x00, 0x00, 0x05, 0x0D, 0x0F, 0x15, 0x1D, 0x1F, + 0x00, 0x19, 0x12, 0x00, 0x07, 0x11, 0x17, 0x01, + 0x00, 0x00, 0x00, 0x03, 0x13, 0x09, 0x00, 0x09, + 0x00 + }, + { /* Table 1 */ + 0x08, 0x08, 0x04, 0x0C, 0x00, 0x14, 0x1C, 0x00, + 0x14, 0x1C, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }, + { /* Table 2 */ + 0x00, 0x00, 0x24, 0x2C, 0x0F, 0x34, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, + 0x00 + }, + { /* Table 3 */ + 0x3A, 0x3A, 0xC6, 0xCE, 0x00, 0xD6, 0xDE, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }, + { /* Table 4 */ + 0x1A, 0x1A, 0xE6, 0xEE, 0x00, 0xF6, 0xFE, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }, + { /* Table 5 */ + 0x00, 0x00, 0x60, 0x98, 0x00, 0x70, 0x9E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }, + { /* Table 6 */ + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }, + { /* Table 7 */ + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }, + { /* Table 8 */ + 0x00, 0x40, 0x01, 0x41, 0x00, 0x09, 0x49, 0x00, + 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00 + }, +}; + +/* Table that encodes the additional bytes for each instruction */ +unsigned char ExtBytes [AMI_COUNT] = { + 0, /* Implicit */ + 0, /* Accu */ + 1, /* Direct */ + 2, /* Absolute */ + 3, /* Absolute long */ + 1, /* Direct,X */ + 2, /* Absolute,X */ + 3, /* Absolute long,X */ + 1, /* Direct,Y */ + 2, /* Absolute,Y */ + 1, /* (Direct) */ + 2, /* (Absolute) */ + 1, /* [Direct] */ + 1, /* (Direct),Y */ + 1, /* [Direct],Y */ + 1, /* (Direct,X) */ + 2, /* (Absolute,X) */ + 1, /* Relative short */ + 2, /* Relative long */ + 1, /* r,s */ + 1, /* (r,s),y */ + 1, /* Immidiate accu */ + 1, /* Immidiate index */ + 1, /* Immidiate byte */ + 2 /* Blockmove */ +}; + + + +/*****************************************************************************/ +/* Handler functions */ +/*****************************************************************************/ + + + +static long PutImmed8 (const InsDesc* Ins) +/* Parse and emit an immediate 8 bit instruction. Return the value of the + * operand if it's available and const. + */ +{ + ExprNode* Expr; + ExprNode* Bank; + unsigned long AddrMode; + unsigned char OpCode; + long Val = -1; + + /* Get the addressing mode used */ + GetEA (&AddrMode, &Expr, &Bank); + + /* From the possible addressing modes, remove the ones that are invalid + * for this instruction or CPU. + */ + AddrMode &= Ins->AddrMode; + + /* If we have possible zero page addressing modes, and the expression + * involved (if any) is not in byte range, remove the zero page addressing + * modes. + */ + if (Expr && (AddrMode & AM_ZP) && !IsByteExpr (Expr)) { + AddrMode &= ~AM_ZP; + } + + /* Check if we have any adressing modes left */ + if (AddrMode == 0) { + Error (ERR_ILLEGAL_ADDR_MODE); + return -1; + } + AddrMode = BitFind (AddrMode); + + /* Build the opcode */ + OpCode = Ins->BaseCode | EATab [Ins->ExtCode][AddrMode]; + + /* If we have an expression and it's const, get it's value */ + if (Expr && IsConstExpr (Expr)) { + Val = GetExprVal (Expr); + } + + /* Check how many extension bytes are needed and output the instruction */ + switch (ExtBytes [AddrMode]) { + + case 1: + Emit1 (OpCode, Expr); + break; + + default: + Internal ("Invalid operand byte count: %u", ExtBytes [AddrMode]); + } + + /* Return the expression value */ + return Val; +} + + + +static void PutPCRel8 (const InsDesc* Ins) +/* Handle branches with a 8 bit distance */ +{ + EmitPCRel (Ins->BaseCode, BranchExpr (2), 1); +} + + + +static void PutPCRel16 (const InsDesc* Ins) +/* Handle branches with an 16 bit distance and PER */ +{ + EmitPCRel (Ins->BaseCode, BranchExpr (3), 2); +} + + + +static void PutBlockMove (const InsDesc* Ins) +/* Handle the blockmove instructions */ +{ + Emit0 (Ins->BaseCode); + EmitByte (Expression ()); + ConsumeComma (); + EmitByte (Expression ()); +} + + + +static void PutREP (const InsDesc* Ins) +/* Emit a REP instruction, track register sizes */ +{ + /* Use the generic handler */ + long Val = PutImmed8 (Ins); + + /* We track the status only for the 816 CPU and in smart mode */ + if (CPU == CPU_65816 && SmartMode) { + + /* Check the range for Val. */ + if (Val < 0) { + /* We had an error */ + Warning (WARN_CANNOT_TRACK_STATUS); + } else { + if (Val & 0x10) { + /* Index registers to 16 bit */ + ExtBytes [AMI_IMM_INDEX] = 2; + } + if (Val & 0x20) { + /* Accu to 16 bit */ + ExtBytes [AMI_IMM_ACCU] = 2; + } + } + } +} + + + +static void PutSEP (const InsDesc* Ins) +/* Emit a SEP instruction, track register sizes */ +{ + /* Use the generic handler */ + long Val = PutImmed8 (Ins); + + /* We track the status only for the 816 CPU and in smart mode */ + if (CPU == CPU_65816 && SmartMode) { + + /* Check the range for Val. */ + if (Val < 0) { + /* We had an error */ + Warning (WARN_CANNOT_TRACK_STATUS); + } else { + if (Val & 0x10) { + /* Index registers to 8 bit */ + ExtBytes [AMI_IMM_INDEX] = 1; + } + if (Val & 0x20) { + /* Accu to 8 bit */ + ExtBytes [AMI_IMM_ACCU] = 1; + } + } + } +} + + + +static void PutAll (const InsDesc* Ins) +/* Handle all other instructions */ +{ + ExprNode* Expr; + ExprNode* Bank; + unsigned long AddrModeSet; + unsigned char OpCode; + unsigned AddrMode; + unsigned long AddrModeBit; + + /* Get the addressing mode used */ + GetEA (&AddrModeSet, &Expr, &Bank); + + /* From the possible addressing modes, remove the ones that are invalid + * for this instruction or CPU. + */ + AddrModeSet &= Ins->AddrMode; + + /* If we have possible zero page addressing modes, and the expression + * involved (if any) is not in byte range, remove the zero page addressing + * modes. + */ + if (Expr && (AddrModeSet & AM_ZP) && !IsByteExpr (Expr)) { + AddrModeSet &= ~AM_ZP; + } + + /* Check if we have any adressing modes left */ + if (AddrModeSet == 0) { + Error (ERR_ILLEGAL_ADDR_MODE); + return; + } + AddrMode = BitFind (AddrModeSet); + + /* Build the opcode */ + OpCode = Ins->BaseCode | EATab [Ins->ExtCode][AddrMode]; + + /* Check how many extension bytes are needed and output the instruction */ + switch (ExtBytes [AddrMode]) { + + case 0: + Emit0 (OpCode); + break; + + case 1: + Emit1 (OpCode, Expr); + break; + + case 2: + AddrModeBit = (1L << AddrMode); + if (CPU == CPU_65816 && (AddrModeBit & (AM_ABS | AM_ABS_X | AM_ABS_Y))) { + /* This is a 16 bit mode that uses an address. If in 65816, + * mode, force this address into 16 bit range to allow + * addressing inside a 64K segment. + */ + Emit2 (OpCode, ForceWordExpr (Expr)); + } else { + Emit2 (OpCode, Expr); + } + break; + + case 3: + if (Bank) { + /* Separate bank given */ + Emit3b (OpCode, Expr, Bank); + } else { + /* One far argument */ + Emit3 (OpCode, Expr); + } + break; + + default: + Internal ("Invalid operand byte count: %u", ExtBytes [AddrMode]); + + } +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static int CmpName (const void* Key, const void* Instr) +/* Compare function for bsearch */ +{ + return strcmp ((const char*)Key, ((const InsDesc*) Instr)->Mnemonic); +} + + + +void SetCPU (enum CPUType NewCPU) +/* Set a new CPU */ +{ + /* Make sure the parameter is correct */ + CHECK (NewCPU < CPU_COUNT); + + /* Check if we have support for the new CPU, if so, use it */ + if (InsTabs[NewCPU]) { + CPU = NewCPU; + InsTab = InsTabs[CPU]; + } else { + Error (ERR_CPU_NOT_SUPPORTED); + } +} + + + +enum CPUType GetCPU (void) +/* Return the current CPU */ +{ + return CPU; +} + + + +int FindInstruction (const char* Ident) +/* Check if Ident is a valid mnemonic. If so, return the index in the + * instruction table. If not, return -1. + */ +{ + const InsDesc* I; + char Key [sizeof (I->Mnemonic)]; + + /* Accept only strings with the right length */ + if (strlen (Ident) != sizeof (I->Mnemonic)-1) { + /* Wrong length */ + return -1; + } + + /* Make a copy, and uppercase that copy */ + Key [0] = toupper (Ident [0]); + Key [1] = toupper (Ident [1]); + Key [2] = toupper (Ident [2]); + Key [3] = '\0'; + + /* Search for the key */ + I = bsearch (Key, InsTab->Ins, InsTab->Count, sizeof (InsDesc), CmpName); + if (I == 0) { + /* Not found */ + return -1; + } else { + /* Found, return the entry */ + return I - InsTab->Ins; + } +} + + + +void HandleInstruction (unsigned Index) +/* Handle the mnemonic with the given index */ +{ + /* Safety check */ + PRECONDITION (Index < InsTab->Count); + + /* Skip the mnemonic token */ + NextTok (); + + /* Call the handler */ + InsTab->Ins[Index].Emit (&InsTab->Ins[Index]); +} + + + diff --git a/src/ca65/instr.h b/src/ca65/instr.h new file mode 100644 index 000000000..7d30dd409 --- /dev/null +++ b/src/ca65/instr.h @@ -0,0 +1,156 @@ +/*****************************************************************************/ +/* */ +/* instr.h */ +/* */ +/* Instruction encoding for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef INSTR_H +#define INSTR_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Supported CPUs */ +enum CPUType { + CPU_6502, + CPU_65C02, + CPU_65816, + CPU_SUNPLUS, /* Not in the freeware version - sorry */ + CPU_COUNT /* Count of different CPUs */ +}; + +/* Constants for the addressing mode. If an opcode is available in zero page + * and absolut adressing mode, both bits are set. When checking for valid + * modes, the zeropage bit is checked first. Similar, the implicit bit is set + * on accu adressing modes, so the 'A' for accu adressing is not needed (but + * may be specified). + * When assembling for the 6502 or 65C02, all addressing modes that are not + * available on these CPUs are removed before doing any checks. + */ +#define AM_IMPLICIT 0x00000003UL +#define AM_ACCU 0x00000002UL +#define AM_DIR 0x00000004UL +#define AM_ABS 0x00000008UL +#define AM_ABS_LONG 0x00000010UL +#define AM_DIR_X 0x00000020UL +#define AM_ABS_X 0x00000040UL +#define AM_ABS_LONG_X 0x00000080UL +#define AM_DIR_Y 0x00000100UL +#define AM_ABS_Y 0x00000200UL +#define AM_DIR_IND 0x00000400UL +#define AM_ABS_IND 0x00000800UL +#define AM_DIR_IND_LONG 0x00001000UL +#define AM_DIR_IND_Y 0x00002000UL +#define AM_DIR_IND_LONG_Y 0x00004000UL +#define AM_DIR_X_IND 0x00008000UL +#define AM_ABS_X_IND 0x00010000UL +#define AM_REL 0x00020000UL +#define AM_REL_LONG 0x00040000UL +#define AM_STACK_REL 0x00080000UL +#define AM_STACK_REL_IND_Y 0x00100000UL +#define AM_IMM 0x00E00000UL +#define AM_BLOCKMOVE 0x01000000UL + +/* Bitmask for all ZP operations that have correspondent ABS ops */ +#define AM_ZP (AM_DIR | AM_DIR_X | AM_DIR_Y | AM_DIR_IND | AM_DIR_X_IND) + +/* Bit numbers and count */ +#define AMI_IMM_ACCU 21 +#define AMI_IMM_INDEX 22 +#define AMI_COUNT 25 + + + +/* Description for one instruction */ +typedef struct InsDesc_ InsDesc; +struct InsDesc_ { + char Mnemonic [4]; + unsigned long AddrMode; /* Valid adressing modes */ + unsigned char BaseCode; /* Base opcode */ + unsigned char ExtCode; /* Number of ext code table */ + void (*Emit) (const InsDesc*);/* Handler function */ +}; + +/* An instruction table */ +typedef struct InsTable_ InsTable; +struct InsTable_ { + unsigned Count; /* Number of intstructions */ + InsDesc Ins[1]; /* Varying length */ +}; + +/* The instruction table for the currently active CPU */ +extern const InsTable* InsTab; + +/* Table to build the effective opcode from a base opcode and an addressing + * mode. + */ +extern unsigned char EATab [9][AMI_COUNT]; + +/* Table that encodes the additional bytes for each instruction */ +extern unsigned char ExtBytes [AMI_COUNT]; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void SetCPU (enum CPUType NewCPU); +/* Set a new CPU */ + +enum CPUType GetCPU (void); +/* Return the current CPU */ + +int FindInstruction (const char* Ident); +/* Check if Ident is a valid mnemonic. If so, return the index in the + * instruction table. If not, return -1. + */ + +void HandleInstruction (unsigned Index); +/* Handle the mnemonic with the given index */ + + + +/* End of instr.h */ + +#endif + + + + diff --git a/src/ca65/listing.c b/src/ca65/listing.c new file mode 100644 index 000000000..8177c6e15 --- /dev/null +++ b/src/ca65/listing.c @@ -0,0 +1,437 @@ +/*****************************************************************************/ +/* */ +/* listing.c */ +/* */ +/* Listing support for the ca65 crossassembler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "../common/segdefs.h" +#include "../common/version.h" + +#include "error.h" +#include "fname.h" +#include "global.h" +#include "mem.h" +#include "objcode.h" +#include "listing.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Single linked list of lines */ +ListLine* LineList = 0; /* List of listing lines */ +ListLine* LineCur = 0; /* Current listing line */ +ListLine* LineLast = 0; /* Last (current) listing line */ + +/* Page and other formatting */ +int PageLength = -1; /* Length of a listing page */ +static unsigned PageNumber = 1; /* Current listing page number */ +static unsigned PageLines = 0; /* Current line on page */ +static unsigned ListBytes = 12; /* Number of bytes to list for one line */ + +/* Switch the listing on/off */ +static int ListingEnabled = 1; /* Enabled if > 0 */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void NewListingLine (const char* Line, unsigned char File, unsigned char Depth) +/* Create a new ListLine struct and insert it */ +{ + /* Store only if listing is enabled */ + if (Listing) { + + ListLine* L; + + /* Get the length of the line */ + unsigned Len = strlen (Line); + + /* Ignore trailing newlines */ + while (Len > 0 && Line[Len-1] == '\n') { + --Len; + } + + /* Allocate memory */ + L = Xmalloc (sizeof (ListLine) + Len); + + /* Initialize the fields. */ + L->Next = 0; + L->FragList = 0; + L->FragLast = 0; + L->PC = GetPC (); + L->Reloc = RelocMode; + L->File = File; + L->Depth = Depth; + L->Output = (ListingEnabled > 0); + L->ListBytes = (unsigned char) ListBytes; + memcpy (L->Line, Line, Len); + L->Line [Len] = '\0'; + + /* Insert the line into the list of lines */ + if (LineList == 0) { + LineList = L; + } else { + LineLast->Next = L; + } + LineLast = L; + } +} + + + +void EnableListing (void) +/* Enable output of lines to the listing */ +{ + if (Listing) { + /* If we're about to enable the listing, do this for the current line + * also, so we will see the source line that did this. + */ + if (ListingEnabled++ == 0) { + LineCur->Output = 1; + } + } +} + + + +void DisableListing (void) +/* Disable output of lines to the listing */ +{ + if (Listing) { + if (ListingEnabled == 0) { + /* Cannot switch the listing off once more */ + Error (ERR_COUNTER_UNDERFLOW); + } else { + --ListingEnabled; + } + } +} + + + +void SetListBytes (int Bytes) +/* Set the maximum number of bytes listed for one line */ +{ + if (Bytes < 0) { + Bytes = 0; /* Encode "unlimited" as zero */ + } + ListBytes = Bytes; +} + + + +void InitListingLine (void) +/* Initialize the current listing line */ +{ + if (Listing) { + /* Make the last loaded line the current line */ + LineCur = LineLast; + + /* Set the values for this line */ + CHECK (LineCur != 0); + LineCur->PC = GetPC (); + LineCur->Reloc = RelocMode; + LineCur->Output = (ListingEnabled > 0); + LineCur->ListBytes = (unsigned char) ListBytes; + } +} + + + +static char* AddHex (char* S, unsigned Val) +/* Add a hex byte in ASCII to the given string and return the new pointer */ +{ + static const char HexTab [16] = { + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' + }; + + *S++ = HexTab [(Val >> 4) & 0x0F]; + *S++ = HexTab [Val & 0x0F]; + + return S; +} + + + +static void PrintPageHeader (FILE* F, const ListLine* L) +/* Print the header for a new page. It is assumed that the given line is the + * last line of the previous page. + */ +{ + /* Print the header on the new page */ + fprintf (F, + "ca65 V%u.%u.%u - (C) Copyright 1998-2000 Ullrich von Bassewitz\n" + "Main file : %s\n" + "Current file: %s\n" + "\n", + VER_MAJOR, VER_MINOR, VER_PATCH, + InFile, + GetFileName (L->File)); + + /* Count pages, reset lines */ + ++PageNumber; + PageLines = 4; +} + + + +static void PrintLine (FILE* F, const char* Header, const char* Line, const ListLine* L) +/* Print one line to the listing file, adding a newline and counting lines */ +{ + /* Print the given line */ + fprintf (F, "%s%s\n", Header, Line); + + /* Increment the current line */ + ++PageLines; + + /* Switch to a new page if needed. Do not switch, if the current line is + * the last one, to avoid pages that consist of just the header. + */ + if (PageLength > 0 && PageLines >= PageLength && L->Next != 0) { + /* Do a formfeed */ + putc ('\f', F); + /* Print the header on the new page */ + PrintPageHeader (F, L); + } +} + + + +static char* AddMult (char* S, char C, unsigned Count) +/* Add multiple instances of character C to S, return updated S. */ +{ + memset (S, C, Count); + return S + Count; +} + + + +static char* MakeLineHeader (char* H, const ListLine* L) +/* Prepare the line header */ +{ + char Mode; + char Depth; + + /* Setup the PC mode */ + Mode = (L->Reloc)? 'r' : ' '; + + /* Set up the include depth */ + Depth = (L->Depth < 10)? L->Depth + '0' : '+'; + + /* Format the line */ + sprintf (H, "%06lX%c %c", L->PC, Mode, Depth); + memset (H+9, ' ', LINE_HEADER_LEN-9); + + /* Return the buffer */ + return H; +} + + + +void CreateListing (void) +/* Create the listing */ +{ + FILE* F; + Fragment* Frag; + ListLine* L; + char HeaderBuf [LINE_HEADER_LEN+1]; + + /* Create the name of the listing file if needed */ + if (ListFile == 0) { + ListFile = MakeFilename (InFile, ListExt); + } + + /* Open the real listing file */ + F = fopen (ListFile, "w"); + if (F == 0) { + Fatal (FAT_CANNOT_OPEN_LISTING, strerror (errno)); + } + + /* Reset variables, print the header for the first page */ + PageNumber = 0; + PrintPageHeader (F, LineList); + + /* Terminate the header buffer. The last byte will never get overwritten */ + HeaderBuf [LINE_HEADER_LEN] = '\0'; + + /* Walk through all listing lines */ + L = LineList; + while (L) { + + char* Buf; + char* B; + unsigned Count; + unsigned I; + char* Line; + + /* If we should not output this line, go to the next */ + if (L->Output == 0) { + L = L->Next; + continue; + } + + /* If we don't have a fragment list for this line, things are easy */ + if (L->FragList == 0) { + PrintLine (F, MakeLineHeader (HeaderBuf, L), L->Line, L); + L = L->Next; + continue; + } + + /* Count the number of bytes in the complete fragment list */ + Count = 0; + Frag = L->FragList; + while (Frag) { + Count += Frag->Len; + Frag = Frag->LineList; + } + + /* Allocate memory for the given number of bytes */ + Buf = Xmalloc (Count*2+1); + + /* Copy an ASCII representation of the bytes into the buffer */ + B = Buf; + Frag = L->FragList; + while (Frag) { + + /* Write data depending on the type */ + switch (Frag->Type) { + + case FRAG_LITERAL: + for (I = 0; I < Frag->Len; ++I) { + B = AddHex (B, Frag->V.Data[I]); + } + break; + + case FRAG_EXPR: + case FRAG_SEXPR: + B = AddMult (B, 'r', Frag->Len*2); + break; + + case FRAG_FILL: + B = AddMult (B, 'x', Frag->Len*2); + break; + + default: + Internal ("Invalid fragment type: %u", Frag->Type); + + } + + /* Next fragment */ + Frag = Frag->LineList; + + } + + /* Limit the number of bytes actually printed */ + if (L->ListBytes != 0) { + /* Not unlimited */ + if (Count > L->ListBytes) { + Count = L->ListBytes; + } + } + + /* Output the data. The format of a listing line is: + * + * PPPPPPm I 11 22 33 44 + * + * where + * + * PPPPPP is the PC + * m is the mode ('r' or empty) + * I is the include level + * 11 .. are code or data bytes + */ + Line = L->Line; + B = Buf; + while (Count) { + + unsigned Chunk; + char* P; + + /* Prepare the line header */ + MakeLineHeader (HeaderBuf, L); + + /* Get the number of bytes for the next line */ + Chunk = Count; + if (Chunk > 4) { + Chunk = 4; + } + Count -= Chunk; + + /* Increment the program counter. Since we don't need the PC stored + * in the LineList object for anything else, just increment this + * variable. + */ + L->PC += Chunk; + + /* Copy the bytes into the line */ + P = HeaderBuf + 11; + for (I = 0; I < Chunk; ++I) { + *P++ = *B++; + *P++ = *B++; + *P++ = ' '; + } + + /* Output this line */ + PrintLine (F, HeaderBuf, Line, L); + + /* Don't output a line twice */ + Line = ""; + + } + + /* Delete the temporary buffer */ + Xfree (Buf); + + /* Next line */ + L = L->Next; + + } + + /* Close the listing file */ + (void) fclose (F); +} + + + diff --git a/src/ca65/listing.h b/src/ca65/listing.h new file mode 100644 index 000000000..51b3e809b --- /dev/null +++ b/src/ca65/listing.h @@ -0,0 +1,116 @@ +/*****************************************************************************/ +/* */ +/* listing.h */ +/* */ +/* Listing support for the ca65 crossassembler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef LISTING_H +#define LISTING_H + + + +#include "fragment.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Length of the header of a listing line */ +#define LINE_HEADER_LEN 24 + +/* One listing line as it is stored in memory */ +typedef struct ListLine_ ListLine; +struct ListLine_ { + ListLine* Next; /* Pointer to next line */ + Fragment* FragList; /* List of fragments for this line */ + Fragment* FragLast; /* Last entry in fragment list */ + unsigned long PC; /* Program counter for this line */ + unsigned char Reloc; /* Relocatable mode? */ + unsigned char File; /* From which file is the line? */ + unsigned char Depth; /* Include depth */ + unsigned char Output; /* Should we output this line? */ + unsigned char ListBytes; /* How many bytes at max? */ + char Line[1]; /* Line with dynamic length */ +}; + +/* Single linked list of lines */ +extern ListLine* LineList; /* List of listing lines */ +extern ListLine* LineCur; /* Current listing line */ +extern ListLine* LineLast; /* Last listing line */ + +/* Page formatting */ +#define MIN_PAGE_LEN 32 +#define MAX_PAGE_LEN 127 +extern int PageLength; /* Length of a listing page */ + +/* Byte for one listing line */ +#define MIN_LIST_BYTES 4 +#define MAX_LIST_BYTES 255 + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void NewListingLine (const char* Line, unsigned char File, unsigned char Depth); +/* Create a new ListLine struct */ + +void EnableListing (void); +/* Enable output of lines to the listing */ + +void DisableListing (void); +/* Disable output of lines to the listing */ + +void SetListBytes (int Bytes); +/* Set the maximum number of bytes listed for one line */ + +void InitListingLine (void); +/* Initialize the current listing line */ + +void CreateListing (void); +/* Create the listing */ + + + +/* End of listing.h */ + +#endif + + + diff --git a/src/ca65/macpack.c b/src/ca65/macpack.c new file mode 100644 index 000000000..b730b11be --- /dev/null +++ b/src/ca65/macpack.c @@ -0,0 +1,154 @@ +/*****************************************************************************/ +/* */ +/* macpack.c */ +/* */ +/* Predefined macro packages for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "error.h" +#include "scanner.h" +#include "macpack.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Predefined packages */ +static const char MacGeneric [] = /* Generic macros */ + ".macro add Arg\n" + " clc\n" + " adc Arg\n" + ".endmacro\n\n" + ".macro sub Arg\n" + " sec\n" + " sbc Arg\n" + ".endmacro\n\n"; + + + +static const char MacLongBranch [] = /* Long branch macros */ + ".macro jeq Target\n" + " .if .def(Target) .and ((*+2)-(Target) <= 127)\n" + " beq Target\n" + " .else\n" + " bne *+5\n" + " jmp Target\n" + " .endif\n" + ".endmacro\n\n" + ".macro jne Target\n" + " .if .def(Target) .and ((*+2)-(Target) <= 127)\n" + " bne Target\n" + " .else\n" + " beq *+5\n" + " jmp Target\n" + " .endif\n" + ".endmacro\n\n" + ".macro jmi Target\n" + " .if .def(Target) .and ((*+2)-(Target) <= 127)\n" + " bmi Target\n" + " .else\n" + " bpl *+5\n" + " jmp Target\n" + " .endif\n" + ".endmacro\n\n" + ".macro jpl Target\n" + " .if .def(Target) .and ((*+2)-(Target) <= 127)\n" + " bpl Target\n" + " .else\n" + " bmi *+5\n" + " jmp Target\n" + " .endif\n" + ".endmacro\n\n" + ".macro jcs Target\n" + " .if .def(Target) .and ((*+2)-(Target) <= 127)\n" + " bcs Target\n" + " .else\n" + " bcc *+5\n" + " jmp Target\n" + " .endif\n" + ".endmacro\n\n" + ".macro jcc Target\n" + " .if .def(Target) .and ((*+2)-(Target) <= 127)\n" + " bcc Target\n" + " .else\n" + " bcs *+5\n" + " jmp Target\n" + " .endif\n" + ".endmacro\n\n" + ".macro jvs Target\n" + " .if .def(Target) .and ((*+2)-(Target) <= 127)\n" + " bvs Target\n" + " .else\n" + " bvc *+5\n" + " jmp Target\n" + " .endif\n" + ".endmacro\n\n" + ".macro jvc Target\n" + " .if .def(Target) .and ((*+2)-(Target) <= 127)\n" + " bvc Target\n" + " .else\n" + " bvs *+5\n" + " jmp Target\n" + " .endif\n" + ".endmacro\n\n"; + + + +/* Table with pointers to the different packages */ +static const char* MacPackages [] = { + MacGeneric, + MacLongBranch, +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void InsertMacPack (unsigned Id) +/* Insert the macro package with the given id in the input stream */ +{ + /* Check the parameter */ + CHECK (Id < sizeof (MacPackages) / sizeof (MacPackages [0])); + + /* Insert the package */ + NewInputData (MacPackages [Id], 0); +} + + + diff --git a/src/ca65/macpack.h b/src/ca65/macpack.h new file mode 100644 index 000000000..e29292259 --- /dev/null +++ b/src/ca65/macpack.h @@ -0,0 +1,69 @@ +/*****************************************************************************/ +/* */ +/* macpack.h */ +/* */ +/* Predefined macro packages for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef MACPACK_H +#define MACPACK_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Constants for the predefined packages */ +#define MAC_GENERIC 0 +#define MAC_LONGBRANCH 1 + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void InsertMacPack (unsigned Id); +/* Insert the macro package with the given id in the input stream */ + + + +/* End of macpack.h */ + +#endif + + + diff --git a/src/ca65/macro.c b/src/ca65/macro.c new file mode 100644 index 000000000..73d8a0018 --- /dev/null +++ b/src/ca65/macro.c @@ -0,0 +1,769 @@ +/*****************************************************************************/ +/* */ +/* macro.c */ +/* */ +/* Macros for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "../common/hashstr.h" + +#include "mem.h" +#include "error.h" +#include "scanner.h" +#include "toknode.h" +#include "pseudo.h" +#include "macro.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Struct that describes an identifer (macro param, local list) */ +typedef struct IdDesc_ IdDesc; +struct IdDesc_ { + IdDesc* Next; /* Linked list */ + char Id [1]; /* Identifier, dynamically allocated */ +}; + + + +/* Struct that describes a macro definition */ +typedef struct Macro_ Macro; +struct Macro_ { + Macro* Next; /* Next macro with same hash */ + Macro* List; /* List of all macros */ + unsigned LocalCount; /* Count of local symbols */ + IdDesc* Locals; /* List of local symbols */ + unsigned ParamCount; /* Parameter count of macro */ + IdDesc* Params; /* Identifiers of macro parameters */ + unsigned TokCount; /* Number of tokens for this macro */ + TokNode* TokRoot; /* Root of token list */ + TokNode* TokLast; /* Pointer to last token in list */ + unsigned char Style; /* Macro style */ + char Name [1]; /* Macro name, dynamically allocated */ +}; + +/* Macro hash table */ +#define HASHTAB_SIZE 117 +static Macro* MacroTab [HASHTAB_SIZE]; + +/* Global macro data */ +static Macro* MacroRoot = 0; /* List of all macros */ + +/* Structs that holds data for a macro expansion */ +typedef struct MacExp_ MacExp; +struct MacExp_ { + MacExp* Next; /* Pointer to next expansion */ + Macro* M; /* Which macro do we expand? */ + TokNode* Exp; /* Pointer to current token */ + TokNode* Final; /* Pointer to final token */ + unsigned LocalStart; /* Start of counter for local symbol names */ + unsigned ParamCount; /* Number of actual parameters */ + TokNode** Params; /* List of actual parameters */ + TokNode* ParamExp; /* Node for expanding parameters */ +}; + +/* Data for macro expansions */ +#define MAX_MACRO_EXPANSIONS 255 +static unsigned MacroNesting = 0; +static MacExp* CurMac = 0; +static unsigned LocalName = 0; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static IdDesc* NewIdDesc (const char* Id) +/* Create a new IdDesc, initialize and return it */ +{ + /* Allocate memory */ + unsigned Len = strlen (Id); + IdDesc* I = Xmalloc (sizeof (IdDesc) + Len); + + /* Initialize the struct */ + I->Next = 0; + memcpy (I->Id, Id, Len); + I->Id [Len] = '\0'; + + /* Return the new struct */ + return I; +} + + + +static Macro* NewMacro (const char* Name, unsigned HashVal, unsigned char Style) +/* Generate a new macro entry, initialize and return it */ +{ + /* Allocate memory */ + unsigned Len = strlen (Name); + Macro* M = Xmalloc (sizeof (Macro) + Len); + + /* Initialize the macro struct */ + M->LocalCount = 0; + M->ParamCount = 0; + M->Params = 0; + M->TokCount = 0; + M->TokRoot = 0; + M->TokLast = 0; + M->Style = Style; + memcpy (M->Name, Name, Len); + M->Name [Len] = '\0'; + + /* Insert the macro into the global macro list */ + M->List = MacroRoot; + MacroRoot = M; + + /* Insert the macro into the hash table */ + M->Next = MacroTab [HashVal]; + MacroTab [HashVal] = M; + + /* Return the new macro struct */ + return M; +} + + + +static MacExp* NewMacExp (Macro* M) +/* Create a new expansion structure for the given macro */ +{ + unsigned I; + + /* Allocate memory */ + MacExp* E = Xmalloc (sizeof (MacExp)); + + /* Initialize the data */ + E->M = M; + E->Exp = M->TokRoot; + E->Final = 0; + E->LocalStart = LocalName; + LocalName += M->LocalCount; + E->ParamCount = 0; + E->Params = Xmalloc (M->ParamCount * sizeof (TokNode*)); + E->ParamExp = 0; + for (I = 0; I < M->ParamCount; ++I) { + E->Params [I] = 0; + } + + /* And return it... */ + return E; +} + + + +static void MacInsertExp (MacExp* E) +/* Insert a macro expansion into the list */ +{ + E->Next = CurMac; + CurMac = E; + ++MacroNesting; +} + + + +static void FreeMacExp (void) +/* Remove and free the current macro expansion */ +{ + unsigned I; + MacExp* E; + + /* Free the parameter list */ + for (I = 0; I < CurMac->ParamCount; ++I) { + Xfree (CurMac->Params [I]); + } + Xfree (CurMac->Params); + + /* Free the final token if we have one */ + if (CurMac->Final) { + FreeTokNode (CurMac->Final); + } + + /* Reset the list pointer */ + E = CurMac; + CurMac = E->Next; + --MacroNesting; + + /* Free the structure itself */ + Xfree (E); +} + + + +static void MacSkipDef (unsigned Style) +/* Skip a macro definition */ +{ + if (Style == MAC_STYLE_CLASSIC) { + /* Skip tokens until we reach the final .endmacro */ + while (Tok != TOK_ENDMACRO && Tok != TOK_EOF) { + NextTok (); + } + if (Tok != TOK_EOF) { + SkipUntilSep (); + } else { + Error (ERR_ENDMACRO_EXPECTED); + } + } else { + /* Skip until end of line */ + SkipUntilSep (); + } +} + + + +static Macro* MacFind (const char* Name, unsigned HashVal) +/* Search for a macro in the hash table */ +{ + /* Search for the identifier */ + Macro* M = MacroTab [HashVal]; + while (M) { + if (strcmp (Name, M->Name) == 0) { + return M; + } + M = M->Next; + } + return 0; +} + + + +void MacDef (unsigned Style) +/* Parse a macro definition */ +{ + Macro* M; + TokNode* T; + unsigned HashVal; + int HaveParams; + + /* We expect a macro name here */ + if (Tok != TOK_IDENT) { + Error (ERR_IDENT_EXPECTED); + MacSkipDef (Style); + return; + } + + /* Generate the hash value */ + HashVal = HashStr (SVal) % HASHTAB_SIZE; + + /* Did we already define that macro? */ + if (MacFind (SVal, HashVal) != 0) { + /* Macro is already defined */ + Error (ERR_SYM_ALREADY_DEFINED, SVal); + /* Skip tokens until we reach the final .endmacro */ + MacSkipDef (Style); + return; + } + + /* Define the macro */ + M = NewMacro (SVal, HashVal, Style); + NextTok (); + + /* If we have a DEFINE style macro, we may have parameters in braces, + * otherwise we may have parameters without braces. + */ + if (Style == MAC_STYLE_CLASSIC) { + HaveParams = 1; + } else { + if (Tok == TOK_LPAREN) { + HaveParams = 1; + NextTok (); + } else { + HaveParams = 0; + } + } + + /* Parse the parameter list */ + if (HaveParams) { + + while (Tok == TOK_IDENT) { + + /* Create a struct holding the identifier */ + IdDesc* I = NewIdDesc (SVal); + + /* Insert the struct into the list, checking for duplicate idents */ + if (M->ParamCount == 0) { + M->Params = I; + } else { + IdDesc* List = M->Params; + while (1) { + if (strcmp (List->Id, SVal) == 0) { + Error (ERR_SYM_ALREADY_DEFINED, SVal); + } + if (List->Next == 0) { + break; + } else { + List = List->Next; + } + } + List->Next = I; + } + ++M->ParamCount; + + /* Skip the name */ + NextTok (); + + /* Maybe there are more params... */ + if (Tok == TOK_COMMA) { + NextTok (); + } else { + break; + } + } + } + + /* For class macros, we expect a separator token, for define style macros, + * we expect the closing paren. + */ + if (Style == MAC_STYLE_CLASSIC) { + ConsumeSep (); + } else if (HaveParams) { + ConsumeRParen (); + } + + /* Preparse the macro body. We will read the tokens until we reach end of + * file, or a .endmacro (or end of line for DEFINE style macros) and store + * them into an token list internal to the macro. For classic macros, there + * the .LOCAL command is detected and removed at this time. + */ + while (1) { + + /* Check for end of macro */ + if (Style == MAC_STYLE_CLASSIC) { + /* In classic macros, only .endmacro is allowed */ + if (Tok == TOK_ENDMACRO) { + /* Done */ + break; + } + /* May not have end of file in a macro definition */ + if (Tok == TOK_EOF) { + Error (ERR_ENDMACRO_EXPECTED); + return; + } + } else { + /* Accept a newline or end of file for new style macros */ + if (Tok == TOK_SEP || Tok == TOK_EOF) { + break; + } + } + + /* Check for a .LOCAL declaration */ + if (Tok == TOK_LOCAL && Style == MAC_STYLE_CLASSIC) { + + while (1) { + + IdDesc* I; + + /* Skip .local or comma */ + NextTok (); + + /* Need an identifer */ + if (Tok != TOK_IDENT) { + Error (ERR_IDENT_EXPECTED); + SkipUntilSep (); + break; + } + + /* Put the identifier into the locals list and skip it */ + I = NewIdDesc (SVal); + I->Next = M->Locals; + M->Locals = I; + ++M->LocalCount; + NextTok (); + + /* Check for end of list */ + if (Tok != TOK_COMMA) { + break; + } + + } + + /* We need end of line after the locals */ + ConsumeSep (); + continue; + } + + /* Create a token node for the current token */ + T = NewTokNode (); + + /* If the token is an ident, check if it is a local parameter */ + if (Tok == TOK_IDENT) { + unsigned Count = 0; + IdDesc* I = M->Params; + while (I) { + if (strcmp (I->Id, SVal) == 0) { + /* Local param name, replace it */ + T->Tok = TOK_MACPARAM; + T->IVal = Count; + break; + } + ++Count; + I = I->Next; + } + } + + /* Insert the new token in the list */ + if (M->TokCount == 0) { + /* First token */ + M->TokRoot = M->TokLast = T; + } else { + /* We have already tokens */ + M->TokLast->Next = T; + M->TokLast = T; + } + ++M->TokCount; + + /* Read the next token */ + NextTok (); + } + + /* Skip the .endmacro for a classic macro */ + if (Style == MAC_STYLE_CLASSIC) { + NextTok (); + } +} + + + +static void StartExpClassic (Macro* M) +/* Start expanding the classic macro M */ +{ + MacExp* E; + + /* Skip the macro name */ + NextTok (); + + /* Create a structure holding expansion data */ + E = NewMacExp (M); + + /* Read the actual parameters */ + while (Tok != TOK_SEP && Tok != TOK_EOF) { + + TokNode* Last; + + /* Check for maximum parameter count */ + if (E->ParamCount >= M->ParamCount) { + Error (ERR_TOO_MANY_PARAMS); + SkipUntilSep (); + break; + } + + /* Read tokens for one parameter, accept empty params */ + Last = 0; + while (Tok != TOK_COMMA && Tok != TOK_SEP) { + + TokNode* T; + + /* Check for end of file */ + if (Tok == TOK_EOF) { + Error (ERR_SYNTAX); + return; + } + + /* Get the next token in a node */ + T = NewTokNode (); + + /* Insert it into the list */ + if (Last == 0) { + E->Params [E->ParamCount] = T; + } else { + Last->Next = T; + } + Last = T; + + /* And skip it... */ + NextTok (); + } + + /* One parameter more */ + ++E->ParamCount; + + /* Check for a comma */ + if (Tok == TOK_COMMA) { + NextTok (); + } else { + break; + } + } + + /* Insert the newly created structure into the expansion list */ + MacInsertExp (E); +} + + + +static void StartExpDefine (Macro* M) +/* Start expanding a DEFINE style macro */ +{ + /* Create a structure holding expansion data */ + MacExp* E = NewMacExp (M); + + /* A define style macro must be called with as many actual parameters + * as there are formal ones. Get the parameter count. + */ + unsigned Count = M->ParamCount; + + /* Skip the current token */ + NextTok (); + + /* Read the actual parameters */ + while (Count--) { + + TokNode* Last; + + /* Check if there is really a parameter */ + if (Tok == TOK_SEP || Tok == TOK_EOF || Tok == TOK_COMMA) { + Error (ERR_MACRO_PARAM_EXPECTED); + SkipUntilSep (); + return; + } + + /* Read tokens for one parameter */ + Last = 0; + do { + + TokNode* T; + + /* Get the next token in a node */ + T = NewTokNode (); + + /* Insert it into the list */ + if (Last == 0) { + E->Params [E->ParamCount] = T; + } else { + Last->Next = T; + } + Last = T; + + /* And skip it... */ + NextTok (); + + } while (Tok != TOK_COMMA && Tok != TOK_SEP && Tok != TOK_EOF); + + /* One parameter more */ + ++E->ParamCount; + + /* Check for a comma */ + if (Count > 0) { + if (Tok == TOK_COMMA) { + NextTok (); + } else { + Error (ERR_COMMA_EXPECTED); + } + } + } + + /* Macro expansion will overwrite the current token. This is a problem + * for define style macros since these are called from the scanner level. + * To avoid it, remember the current token and re-insert it if macro + * expansion is done. + */ + E->Final = NewTokNode (); + + /* Insert the newly created structure into the expansion list */ + MacInsertExp (E); +} + + + +void MacExpandStart (void) +/* Start expanding the macro in SVal */ +{ + Macro* M; + + /* Beware of runoff macros */ + if (MacroNesting == MAX_MACRO_EXPANSIONS) { + Fatal (FAT_MACRO_NESTING); + } + + /* Search for the macro */ + M = MacFind (SVal, HashStr (SVal) % HASHTAB_SIZE); + CHECK (M != 0); + + /* Call the apropriate subroutine */ + switch (M->Style) { + case MAC_STYLE_CLASSIC: StartExpClassic (M); break; + case MAC_STYLE_DEFINE: StartExpDefine (M); break; + default: Internal ("Invalid macro style: %d", M->Style); + } +} + + + +int MacExpand (void) +/* If we're currently expanding a macro, set the the scanner token and + * attribute to the next value and return true. If we are not expanding + * a macro, return false. + */ +{ + if (MacroNesting == 0) { + /* Not expanding a macro */ + return 0; + } + + /* We're expanding a macro. Check if we are expanding one of the + * macro parameters. + */ + if (CurMac->ParamExp) { + + /* Ok, use token from parameter list */ + TokSet (CurMac->ParamExp); + + /* Set pointer to next token */ + CurMac->ParamExp = CurMac->ParamExp->Next; + + /* Done */ + return 1; + + } else if (CurMac->Exp) { + + /* We're not expanding a parameter, use next macro token */ + TokSet (CurMac->Exp); + + /* Set pointer to next token */ + CurMac->Exp = CurMac->Exp->Next; + + /* Is it a request for actual parameter count? */ + if (Tok == TOK_PARAMCOUNT) { + Tok = TOK_INTCON; + IVal = CurMac->ParamCount; + return 1; + } + + /* Is it an .exitmacro command? */ + if (Tok == TOK_EXITMACRO) { + /* Forced exit from macro expansion */ + FreeMacExp (); + return MacExpand (); + } + + /* Is it the name of a macro parameter? */ + if (Tok == TOK_MACPARAM) { + + /* Start to expand the parameter token list */ + CurMac->ParamExp = CurMac->Params [IVal]; + + /* Recursive call to expand the parameter */ + return MacExpand (); + } + + /* If it's an identifier, it may in fact be a local symbol */ + if (Tok == TOK_IDENT && CurMac->M->LocalCount) { + /* Search for the local symbol in the list */ + unsigned Index = 0; + IdDesc* I = CurMac->M->Locals; + while (I) { + if (strcmp (SVal, I->Id) == 0) { + /* This is in fact a local symbol, change the name */ + sprintf (SVal, "___%04X__", CurMac->LocalStart + Index); + break; + } + /* Next symbol */ + ++Index; + I = I->Next; + } + + /* Done */ + return 1; + } + + /* The token was successfully set */ + return 1; + + } else if (CurMac->Final) { + + /* Set the final token and remove it */ + TokSet (CurMac->Final); + FreeTokNode (CurMac->Final); + CurMac->Final = 0; + + /* The token was successfully set */ + return 1; + + } else { + + /* End of macro expansion */ + FreeMacExp (); + return MacExpand (); + + } +} + + + +void MacAbort (void) +/* Abort the current macro expansion */ +{ + /* Must have an expansion */ + CHECK (CurMac != 0); + + /* Free current structure */ + FreeMacExp (); +} + + + +int IsMacro (const char* Name) +/* Return true if the given name is the name of a macro */ +{ + return MacFind (SVal, HashStr (SVal) % HASHTAB_SIZE) != 0; +} + + + +int IsDefine (const char* Name) +/* Return true if the given name is the name of a define style macro */ +{ + Macro* M = MacFind (SVal, HashStr (SVal) % HASHTAB_SIZE); + return (M != 0 && M->Style == MAC_STYLE_DEFINE); +} + + + +int InMacExpansion (void) +/* Return true if we're currently expanding a macro */ +{ + return MacroNesting != 0; +} + + + + + + diff --git a/src/ca65/macro.h b/src/ca65/macro.h new file mode 100644 index 000000000..defcd9ad8 --- /dev/null +++ b/src/ca65/macro.h @@ -0,0 +1,90 @@ +/*****************************************************************************/ +/* */ +/* macro.h */ +/* */ +/* Macros for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef MACRO_H +#define MACRO_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Macro styles */ +#define MAC_STYLE_CLASSIC 0 +#define MAC_STYLE_DEFINE 1 + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void MacDef (unsigned Style); +/* Parse a macro definition */ + +void MacExpandStart (void); +/* Start expanding the macro in SVal */ + +int MacExpand (void); +/* If we're currently expanding a macro, set the the scanner token and + * attribute to the next value and return true. If we are not expanding + * a macro, return false. + */ + +void MacAbort (void); +/* Abort the current macro expansion */ + +int IsMacro (const char* Name); +/* Return true if the given name is the name of a macro */ + +int IsDefine (const char* Name); +/* Return true if the given name is the name of a define style macro */ + +int InMacExpansion (void); +/* Return true if we're currently expanding a macro */ + + + +/* End of macro.h */ + +#endif + + + diff --git a/src/ca65/main.c b/src/ca65/main.c new file mode 100644 index 000000000..1c65f09d6 --- /dev/null +++ b/src/ca65/main.c @@ -0,0 +1,564 @@ +/*****************************************************************************/ +/* */ +/* main.c */ +/* */ +/* Main program for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include +#include + +#include "../common/version.h" + +#include "error.h" +#include "expr.h" +#include "global.h" +#include "instr.h" +#include "listing.h" +#include "macro.h" +#include "mem.h" +#include "objcode.h" +#include "objfile.h" +#include "options.h" +#include "pseudo.h" +#include "scanner.h" +#include "symtab.h" +#include "ulabel.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void Usage (void) +/* Print usage information and exit */ +{ + fprintf (stderr, + "Usage: %s [options] file\n" + "Options:\n" + "\t-g\t\tAdd debug info to object file\n" + "\t-i\t\tIgnore case of symbols\n" + "\t-l\t\tCreate a listing if assembly was ok\n" + "\t-o name\t\tName the output file\n" + "\t-s\t\tEnable smart mode\n" + "\t-v\t\tIncrease verbosity\n" + "\t-D name[=value]\tDefine a symbol\n" + "\t-U\t\tMark unresolved symbols as import\n" + "\t-V\t\tPrint the assembler version\n" + "\t-W n\t\tSet warning level n\n" + "\t--cpu type\tSet cpu type\n" + "\t--pagelength n\tSet the page length for the listing\n" + "\t--smart\t\tEnable smart mode\n", + ProgName); + exit (EXIT_FAILURE); +} + + + +static void UnknownOption (const char* Arg) +/* Print an error about an unknown option. Print usage information and exit */ +{ + fprintf (stderr, "Unknown option: %s\n", Arg); + Usage (); +} + + + +static void NeedArg (const char* Arg) +/* Print an error about a missing option argument and exit. */ +{ + fprintf (stderr, "Option requires an argument: %s\n", Arg); + exit (EXIT_FAILURE); +} + + + +static void InvSym (const char* Def) +/* Print an error about an invalid symbol definition and die */ +{ + fprintf (stderr, "Invalid symbol definition: `%s'\n", Def); + exit (EXIT_FAILURE); +} + + + +static const char* GetArg (int* ArgNum, char* argv [], unsigned Len) +/* Get an option argument */ +{ + const char* Arg = argv [*ArgNum]; + if (Arg [Len] != '\0') { + /* Argument appended */ + return Arg + Len; + } else { + /* Separate argument */ + Arg = argv [*ArgNum + 1]; + if (Arg == 0) { + /* End of arguments */ + NeedArg (argv [*ArgNum]); + } + ++(*ArgNum); + return Arg; + } +} + + + +static void SetOptions (void) +/* Set the option for the translator */ +{ + char Buf [256]; + + /* Set the translator */ + sprintf (Buf, "ca65 V%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH); + OptTranslator (Buf); + + /* Set date and time */ + OptDateTime ((unsigned long) time(0)); +} + + + +static void DefineSymbol (const char* Def) +/* Define a symbol from the command line */ +{ + const char* P; + unsigned I; + long Val; + char SymName [MAX_STR_LEN+1]; + + /* The symbol must start with a character or underline */ + if (Def [0] != '_' && !isalpha (Def [0])) { + InvSym (Def); + } + P = Def; + + /* Copy the symbol, checking the rest */ + I = 0; + while (isalnum (*P) || *P == '_') { + if (I <= MAX_STR_LEN) { + SymName [I++] = *P; + } + ++P; + } + SymName [I] = '\0'; + + /* Do we have a value given? */ + if (*P != '=') { + if (*P != '\0') { + InvSym (Def); + } + Val = 0; + } else { + /* We have a value */ + ++P; + if (*P == '$') { + ++P; + if (sscanf (P, "%lx", &Val) != 1) { + InvSym (Def); + } + } else { + if (sscanf (P, "%li", &Val) != 1) { + InvSym (Def); + } + } + } + + /* Check if have already a symbol with this name */ + if (SymIsDef (SymName)) { + fprintf (stderr, "`%s' is already defined\n", SymName); + exit (EXIT_FAILURE); + } + + /* Define the symbol */ + SymDef (SymName, LiteralExpr (Val), 0); +} + + + +static void OptCPU (const char* Opt, const char* Arg) +/* Handle the --cpu option */ +{ + if (Arg == 0) { + NeedArg (Opt); + } + if (strcmp (Arg, "6502") == 0) { + SetCPU (CPU_6502); + } else if (strcmp (Arg, "65C02") == 0) { + SetCPU (CPU_65C02); + } else if (strcmp (Arg, "65816") == 0) { + SetCPU (CPU_65816); +#ifdef SUNPLUS + } else if (strcmp (Arg, "sunplus") == 0) { + SetCPU (CPU_SUNPLUS); +#endif + } else { + fprintf (stderr, "Invalid CPU: `%s'\n", Arg); + exit (EXIT_FAILURE); + } +} + + + +static void OptPageLength (const char* Opt, const char* Arg) +/* Handle the --pagelength option */ +{ + int Len; + if (Arg == 0) { + NeedArg (Opt); + } + Len = atoi (Arg); + if (Len != -1 && (Len < MIN_PAGE_LEN || Len > MAX_PAGE_LEN)) { + fprintf (stderr, "Invalid page length: %d\n", Len); + exit (EXIT_FAILURE); + } + PageLength = Len; +} + + + +static void OptSmart (const char* Opt) +/* Handle the -s/--smart options */ +{ + SmartMode = 1; +} + + + +static void LongOption (int* ArgNum, char* argv []) +/* Handle a long command line option */ +{ + const char* Opt = argv [*ArgNum]; + const char* Arg = argv [*ArgNum+1]; + + if (strcmp (Opt, "--cpu") == 0) { + OptCPU (Opt, Arg); + ++(*ArgNum); + } else if (strcmp (Opt, "--pagelength") == 0) { + OptPageLength (Opt, Arg); + ++(*ArgNum); + } else if (strcmp (Opt, "--smart") == 0) { + OptSmart (Opt); + } else { + UnknownOption (Opt); + } +} + + + +static void OneLine (void) +/* Assemble one line */ +{ + char Ident [MAX_STR_LEN+1]; + int Done = 0; + + /* Initialize the listing line */ + InitListingLine (); + + if (Tok == TOK_COLON) { + /* An unnamed label */ + ULabDef (); + NextTok (); + } + + /* Assemble the line */ + if (Tok == TOK_IDENT) { + + /* Is it a macro? */ + if (IsMacro (SVal)) { + + /* Yes, start a macro expansion */ + MacExpandStart (); + Done = 1; + + } else { + + /* No, label. Remember the identifier, then skip it */ + int HadWS = WS; /* Did we have whitespace before the ident? */ + strcpy (Ident, SVal); + NextTok (); + + /* If a colon follows, this is a label definition. If there + * is no colon, it's an assignment. + */ + if (Tok == TOK_EQ) { + /* Skip the '=' */ + NextTok (); + /* Define the symbol with the expression following the + * '=' + */ + SymDef (Ident, Expression (), 0); + /* Don't allow anything after a symbol definition */ + Done = 1; + } else { + /* Define a label */ + SymDef (Ident, CurrentPC (), IsZPSeg ()); + /* Skip the colon. If NoColonLabels is enabled, allow labels + * without a colon if there is no whitespace before the + * identifier. + */ + if (Tok != TOK_COLON) { + if (HadWS || !NoColonLabels) { + Error (ERR_COLON_EXPECTED); + } + if (Tok == TOK_NAMESPACE) { + /* Smart :: handling */ + NextTok (); + } + } else { + /* Skip the colon */ + NextTok (); + } + } + } + } + + if (!Done) { + + if (TokIsPseudo (Tok)) { + /* A control command, IVal is index into table */ + HandlePseudo (); + } else if (Tok == TOK_MNEMO) { + /* A mnemonic - assemble one instruction */ + HandleInstruction (IVal); + } else if (Tok == TOK_IDENT && IsMacro (SVal)) { + /* A macro expansion */ + MacExpandStart (); + } + } + + /* Calling InitListingLine again here is part of a hack that introduces + * enough magic to make the PC output in the listing work. + */ + InitListingLine (); + + /* Line separator must come here */ + ConsumeSep (); +} + + + +static void Assemble (void) +/* Start the ball rolling ... */ +{ + /* Prime the pump */ + NextTok (); + + /* Assemble lines until end of file */ + while (Tok != TOK_EOF) { + OneLine (); + } +} + + + +static void CreateObjFile (void) +/* Create the object file */ +{ + /* Open the object, write the header */ + ObjOpen (); + + /* Write the object file options */ + WriteOptions (); + + /* Write the list of input files */ + WriteFiles (); + + /* Write the segment data to the file */ + WriteSegments (); + + /* Write the import list */ + WriteImports (); + + /* Write the export list */ + WriteExports (); + + /* Write debug symbols if requested */ + WriteDbgSyms (); + + /* Write an updated header and close the file */ + ObjClose (); +} + + + +int main (int argc, char* argv []) +/* Assembler main program */ +{ + int I; + + /* Set the program name */ + ProgName = argv [0]; + + /* We must have a file name */ + if (argc < 2) { + Usage (); + } + + /* Enter the base lexical level. We must do that here, since we may + * define symbols using -D. + */ + SymEnterLevel (); + + /* Check the parameters */ + I = 1; + while (I < argc) { + + /* Get the argument */ + const char* Arg = argv [I]; + + /* Check for an option */ + if (Arg [0] == '-') { + switch (Arg [1]) { + + case '-': + LongOption (&I, argv); + break; + + case 'g': + DbgSyms = 1; + break; + + case 'i': + IgnoreCase = 1; + break; + + case 'l': + Listing = 1; + break; + + case 'o': + OutFile = GetArg (&I, argv, 2); + break; + + case 's': + OptSmart (Arg); + break; + + case 'v': + ++Verbose; + break; + + case 'D': + DefineSymbol (GetArg (&I, argv, 2)); + break; + + case 'U': + AutoImport = 1; + break; + + case 'V': + fprintf (stderr, + "ca65 V%u.%u.%u - (C) Copyright 1998-2000 Ullrich von Bassewitz\n", + VER_MAJOR, VER_MINOR, VER_PATCH); + break; + + case 'W': + WarnLevel = atoi (GetArg (&I, argv, 2)); + break; + + default: + UnknownOption (Arg); + break; + + } + } else { + /* Filename. Check if we already had one */ + if (InFile) { + fprintf (stderr, "Don't know what to do with `%s'\n", Arg); + Usage (); + } else { + InFile = Arg; + } + } + + /* Next argument */ + ++I; + } + + /* Do we have an input file? */ + if (InFile == 0) { + fprintf (stderr, "No input file\n"); + exit (EXIT_FAILURE); + } + + /* Initialize the scanner, open the input file */ + InitScanner (InFile); + + /* Define the default options */ + SetOptions (); + + /* Assemble the input */ + Assemble (); + + /* If we didn't have any errors, check the unnamed labels */ + if (ErrorCount == 0) { + ULabCheck (); + } + + /* If we didn't have any errors, check the symbol table */ + if (ErrorCount == 0) { + SymCheck (); + } + + /* If we didn't have any errors, check and resolve the segment data */ + if (ErrorCount == 0) { + SegCheck (); + } + + /* Dump the data */ + if (Verbose >= 2) { + SymDump (stdout); + SegDump (); + } + + /* If we didn't have any errors, create the object and listing files */ + if (ErrorCount == 0) { + CreateObjFile (); + if (Listing) { + CreateListing (); + } + } + + /* Close the input file */ + DoneScanner (); + + /* Return an apropriate exit code */ + return (ErrorCount == 0)? EXIT_SUCCESS : EXIT_FAILURE; +} + + + diff --git a/src/ca65/make/gcc.mak b/src/ca65/make/gcc.mak new file mode 100644 index 000000000..8d5c69413 --- /dev/null +++ b/src/ca65/make/gcc.mak @@ -0,0 +1,63 @@ +# +# gcc Makefile for a65, link65 & libr65 +# + +CFLAGS = -g -O2 -Wall +CC = gcc +LDFLAGS = + +OBJS = condasm.o \ + ea.o \ + error.o \ + expr.o \ + fname.o \ + fragment.o \ + global.o \ + instr.o \ + listing.o \ + macpack.o \ + macro.o \ + main.o \ + mem.o \ + objcode.o \ + objfile.o \ + options.o \ + pseudo.o \ + scanner.o \ + strexpr.o \ + symtab.o \ + toknode.o \ + ulabel.o + +LIBS = ../common/common.a + +EXECS = ca65 + +.PHONY: all +ifeq (.depend,$(wildcard .depend)) +all : $(EXECS) +include .depend +else +all: depend + @$(MAKE) -f make/gcc.mak all +endif + + + +ca65: $(OBJS) $(LIBS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) + +clean: + rm -f *~ core *.lst + +zap: clean + rm -f *.o $(EXECS) .depend + +# ------------------------------------------------------------------------------ +# Make the dependencies + +.PHONY: depend dep +depend dep: $(OBJS:.o=.c) + @echo "Creating dependency information" + $(CC) -MM $^ > .depend + diff --git a/src/ca65/make/watcom.mak b/src/ca65/make/watcom.mak new file mode 100644 index 000000000..7ba0b5e0c --- /dev/null +++ b/src/ca65/make/watcom.mak @@ -0,0 +1,140 @@ +# +# CA65 Makefile for the Watcom compiler +# + +# ------------------------------------------------------------------------------ +# Generic stuff + +.AUTODEPEND +.SUFFIXES .ASM .C .CC .CPP +.SWAP + +AR = WLIB +LD = WLINK + +!if !$d(TARGET) +!if $d(__OS2__) +TARGET = OS2 +!else +TARGET = NT +!endif +!endif + +# target specific macros. +!if $(TARGET)==OS2 + +# --------------------- OS2 --------------------- +SYSTEM = os2v2 +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS32 + +# -------------------- DOS4G -------------------- +SYSTEM = dos4g +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS + +# --------------------- DOS --------------------- +SYSTEM = dos +CC = WCC +CCCFG = -bt=$(TARGET) -d1 -onatx -zp2 -2 -ml -zq -w2 + +!elif $(TARGET)==NT + +# --------------------- NT ---------------------- +SYSTEM = nt +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!else +!error +!endif + +# ------------------------------------------------------------------------------ +# Implicit rules + +.c.obj: + $(CC) $(CCCFG) $< + + +# ------------------------------------------------------------------------------ +# All library OBJ files + +OBJS = condasm.obj \ + ea.obj \ + error.obj \ + expr.obj \ + fname.obj \ + fragment.obj \ + global.obj \ + instr.obj \ + listing.obj \ + macpack.obj \ + macro.obj \ + main.obj \ + mem.obj \ + objcode.obj \ + objfile.obj \ + options.obj \ + pseudo.obj \ + scanner.obj \ + strexpr.obj \ + symtab.obj \ + toknode.obj \ + ulabel.obj + +LIBS = ..\common\common.lib + + +# ------------------------------------------------------------------------------ +# Main targets + +all: ca65 + +ca65: ca65.exe + + +# ------------------------------------------------------------------------------ +# Other targets + + +ca65.exe: $(OBJS) $(LIBS) + $(LD) system $(SYSTEM) @&&| +DEBUG ALL +OPTION QUIET +NAME $< +FILE condasm.obj +FILE ea.obj +FILE error.obj +FILE expr.obj +FILE fname.obj +FILE fragment.obj +FILE global.obj +FILE instr.obj +FILE listing.obj +FILE macpack.obj +FILE macro.obj +FILE main.obj +FILE mem.obj +FILE objcode.obj +FILE objfile.obj +FILE options.obj +FILE pseudo.obj +FILE scanner.obj +FILE strexpr.obj +FILE symtab.obj +FILE toknode.obj +FILE ulabel.obj +LIBRARY ..\common\common.lib +| + +clean: + @if exist *.obj del *.obj + @if exist ca65.exe del ca65.exe + +strip: + @-wstrip ca65.exe + diff --git a/src/ca65/mem.c b/src/ca65/mem.c new file mode 100644 index 000000000..7f10aa859 --- /dev/null +++ b/src/ca65/mem.c @@ -0,0 +1,84 @@ +/*****************************************************************************/ +/* */ +/* mem.c */ +/* */ +/* Memory allocation for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "error.h" +#include "mem.h" + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void* Xmalloc (size_t size) +/* Allocate memory, check for out of memory condition. Do some debugging */ +{ + void* p; + + p = malloc (size); + if (p == 0 && size != 0) { + Fatal (FAT_OUT_OF_MEMORY); + } + + /* Return a pointer to the block */ + return p; +} + + + +void Xfree (const void* block) +/* Free the block, do some debugging */ +{ + free ((void*) block); +} + + + +char* StrDup (const char* s) +/* Duplicate a string on the heap. The function checks for out of memory */ +{ + unsigned len; + + len = strlen (s) + 1; + return memcpy (Xmalloc (len), s, len); +} + + + diff --git a/src/ca65/mem.h b/src/ca65/mem.h new file mode 100644 index 000000000..4a6871c37 --- /dev/null +++ b/src/ca65/mem.h @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* */ +/* mem.h */ +/* */ +/* Memory allocation for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef MEM_H +#define MEM_H + + + +#include + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void* Xmalloc (size_t size); +/* Allocate memory, check for out of memory condition. Do some debugging */ + +void Xfree (const void* block); +/* Free the block, do some debugging */ + +char* StrDup (const char* s); +/* Duplicate a string on the heap. The function checks for out of memory */ + + + +/* End of mem.h */ + +#endif + + + diff --git a/src/ca65/objcode.c b/src/ca65/objcode.c new file mode 100644 index 000000000..029e01e3c --- /dev/null +++ b/src/ca65/objcode.c @@ -0,0 +1,842 @@ +/*****************************************************************************/ +/* */ +/* objcode.c */ +/* */ +/* Objectcode management for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "../common/segdefs.h" + +#include "error.h" +#include "fragment.h" +#include "global.h" +#include "listing.h" +#include "mem.h" +#include "objfile.h" +#include "scanner.h" +#include "symtab.h" +#include "objcode.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Are we in absolute mode or in relocatable mode? */ +int RelocMode = 1; +unsigned long AbsPC = 0; /* PC if in absolute mode */ + + + +typedef struct Segment_ Segment; +struct Segment_ { + Segment* List; /* List of all segments */ + Fragment* Root; /* Root of fragment list */ + Fragment* Last; /* Pointer to last fragment */ + unsigned char Align; /* Segment alignment */ + unsigned char SegType; /* True if zero page segment */ + unsigned long PC; + unsigned Num; /* Segment number */ + char* Name; /* Segment name */ +}; + + + +/* Predefined segments */ +static Segment NullSeg = { + 0, 0, 0, 0, SEGTYPE_ABS, 0, 5, "NULL" +}; +static Segment ZeropageSeg = { + &NullSeg, 0, 0, 0, SEGTYPE_ZP, 0, 4, "ZEROPAGE" +}; +static Segment DataSeg = { + &ZeropageSeg, 0, 0, 0, SEGTYPE_ABS, 0, 3, "DATA" +}; +static Segment BssSeg = { + &DataSeg, 0, 0, 0, SEGTYPE_ABS, 0, 2, "BSS" +}; +static Segment RODataSeg = { + &BssSeg, 0, 0, 0, SEGTYPE_ABS, 0, 1, "RODATA" +}; +static Segment CodeSeg = { + &RODataSeg, 0, 0, 0, SEGTYPE_ABS, 0, 0, "CODE" +}; + +/* Number of segments */ +static unsigned SegmentCount = 6; + +/* List of all segments */ +static Segment* SegmentList = &CodeSeg; +static Segment* SegmentLast = &NullSeg; + +/* Currently active segment */ +static Segment* ActiveSeg = &CodeSeg; + + + +/*****************************************************************************/ +/* Segment management */ +/*****************************************************************************/ + + + +static Segment* NewSegment (const char* Name, unsigned SegType) +/* Create a new segment, insert it into the global list and return it */ +{ + Segment* S; + const char* N; + + /* Check for too many segments */ + if (SegmentCount >= 256) { + Fatal (FAT_TOO_MANY_SEGMENTS); + } + + /* Check the segment name for invalid names */ + N = Name; + if ((*N != '_' && !isalpha (*N)) || strlen (Name) > 80) { + Error (ERR_ILLEGAL_SEGMENT, Name); + } + do { + if (*N != '_' && !isalnum (*N)) { + Error (ERR_ILLEGAL_SEGMENT, Name); + break; + } + ++N; + } while (*N); + + /* Create a new segment */ + S = Xmalloc (sizeof (*S)); + + /* Initialize it */ + S->List = 0; + S->Root = 0; + S->Last = 0; + S->Align = 0; + S->SegType = SegType; + S->PC = 0; + S->Num = SegmentCount++; + S->Name = StrDup (Name); + + /* Insert it into the segment list */ + SegmentLast->List = S; + SegmentLast = S; + + /* And return it... */ + return S; +} + + + +void UseCodeSeg (void) +/* Use the code segment */ +{ + ActiveSeg = &CodeSeg; +} + + + +void UseRODataSeg (void) +/* Use the r/o data segment */ +{ + ActiveSeg = &RODataSeg; +} + + + +void UseDataSeg (void) +/* Use the data segment */ +{ + ActiveSeg = &DataSeg; +} + + + +void UseBssSeg (void) +/* Use the BSS segment */ +{ + ActiveSeg = &BssSeg; +} + + + +void UseZeropageSeg (void) +/* Use the zero page segment */ +{ + ActiveSeg = &ZeropageSeg; +} + + + +void UseNullSeg (void) +/* Use the null segment */ +{ + ActiveSeg = &NullSeg; +} + + + +void UseSeg (const char* Name, unsigned SegType) +/* Use the segment with the given name */ +{ + Segment* Seg = SegmentList; + while (Seg) { + if (strcmp (Seg->Name, Name) == 0) { + /* We found this segment. Check if the type is identical */ + if (SegType != SEGTYPE_DEFAULT && Seg->SegType != SegType) { + Error (ERR_SEG_ATTR_MISMATCH); + /* Use the new attribute to avoid errors */ + Seg->SegType = SegType; + } + ActiveSeg = Seg; + return; + } + /* Check next segment */ + Seg = Seg->List; + } + + /* Segment is not in list, create a new one */ + if (SegType == SEGTYPE_DEFAULT) { + SegType = SEGTYPE_ABS; + } + Seg = NewSegment (Name, SegType); + ActiveSeg = Seg; +} + + + +unsigned long GetPC (void) +/* Get the program counter of the current segment */ +{ + return RelocMode? ActiveSeg->PC : AbsPC; +} + + + +void SetAbsPC (unsigned long PC) +/* Set the program counter in absolute mode */ +{ + RelocMode = 0; + AbsPC = PC; +} + + + +unsigned GetSegNum (void) +/* Get the number of the current segment */ +{ + return ActiveSeg->Num; +} + + + +void SegAlign (unsigned Power, int Val) +/* Align the PC segment to 2^Power. If Val is -1, emit fill fragments (the + * actual fill value will be determined by the linker), otherwise use the + * given value. + */ +{ + unsigned char Data [4]; + unsigned long Align = (1UL << Power) - 1; + unsigned long NewPC = (ActiveSeg->PC + Align) & ~Align; + unsigned long Count = NewPC - ActiveSeg->PC; + + if (Val != -1) { + /* User defined fill value */ + memset (Data, Val, sizeof (Data)); + while (Count) { + if (Count > sizeof (Data)) { + EmitData (Data, sizeof (Data)); + Count -= sizeof (Data); + } else { + EmitData (Data, Count); + Count = 0; + } + } + } else { + /* Linker defined fill value */ + EmitFill (Count); + } + + /* Remember the alignment in the header */ + if (ActiveSeg->Align < Power) { + ActiveSeg->Align = Power; + } +} + + + +int IsZPSeg (void) +/* Return true if the current segment is a zeropage segment */ +{ + return (ActiveSeg->SegType == SEGTYPE_ZP); +} + + + +int IsFarSeg (void) +/* Return true if the current segment is a far segment */ +{ + return (ActiveSeg->SegType == SEGTYPE_FAR); +} + + + +unsigned GetSegType (unsigned SegNum) +/* Return the type of the segment with the given number */ +{ + /* Search for the segment */ + Segment* S = SegmentList; + while (S && SegNum) { + --SegNum; + S = S->List; + } + + /* Did we find it? */ + if (S == 0) { + FAIL ("Invalid segment number"); + } + + /* Return the segment type */ + return S->SegType; +} + + + +void SegCheck (void) +/* Check the segments for range and other errors */ +{ + Segment* S = SegmentList; + while (S) { + Fragment* F = S->Root; + while (F) { + if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) { + F->V.Expr = FinalizeExpr (F->V.Expr); + if (IsConstExpr (F->V.Expr)) { + /* We are able to evaluate the expression. Get the value + * and check for range errors. + */ + unsigned I; + long Val = GetExprVal (F->V.Expr); + int Abs = (F->Type != FRAG_SEXPR); + + if (F->Len == 1) { + if (Abs) { + /* Absolute value */ + if (Val > 255) { + PError (&F->Pos, ERR_RANGE); + } + } else { + /* PC relative value */ + if (Val < -128 || Val > 127) { + PError (&F->Pos, ERR_RANGE); + } + } + } else if (F->Len == 2) { + if (Abs) { + /* Absolute value */ + if (Val > 65535) { + PError (&F->Pos, ERR_RANGE); + } + } else { + /* PC relative value */ + if (Val < -32768 || Val > 32767) { + PError (&F->Pos, ERR_RANGE); + } + } + } + + /* Convert the fragment into a literal fragment */ + for (I = 0; I < F->Len; ++I) { + F->V.Data [I] = Val & 0xFF; + Val >>= 8; + } + F->Type = FRAG_LITERAL; + } else { + /* We cannot evaluate the expression now, leave the job for + * the linker. However, we are able to check for explicit + * byte expressions and we will do so. + */ + if (F->Type == FRAG_EXPR && F->Len == 1 && !IsByteExpr (F->V.Expr)) { + PError (&F->Pos, ERR_RANGE); + } + } + } + F = F->Next; + } + S = S->List; + } +} + + + +void SegDump (void) +/* Dump the contents of all segments */ +{ + unsigned X = 0; + Segment* S = SegmentList; + printf ("\n"); + while (S) { + unsigned I; + Fragment* F; + int State = -1; + printf ("New segment: %s", S->Name); + F = S->Root; + while (F) { + if (F->Type == FRAG_LITERAL) { + if (State != 0) { + printf ("\n Literal:"); + X = 15; + State = 0; + } + for (I = 0; I < F->Len; ++I) { + printf (" %02X", F->V.Data [I]); + X += 3; + } + } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) { + State = 1; + printf ("\n Expression (%u): ", F->Len); + DumpExpr (F->V.Expr); + } else if (F->Type == FRAG_FILL) { + State = 1; + printf ("\n Fill bytes (%u)", F->Len); + } else { + Internal ("Unknown fragment type: %u", F->Type); + } + if (X > 65) { + State = -1; + } + F = F->Next; + } + printf ("\n End PC = $%04X\n", (unsigned)(S->PC & 0xFFFF)); + S = S->List; + } + printf ("\n"); +} + + + +static void WriteOneSeg (Segment* Seg) +/* Write one segment to the object file */ +{ + Fragment* Frag; + Fragment* F; + unsigned long Size; + + /* Write the segment name followed by the byte count in this segment */ + ObjWriteStr (Seg->Name); + ObjWrite32 (Seg->PC); + ObjWrite8 (Seg->Align); + ObjWrite8 (Seg->SegType); + + /* Now walk through the fragment list for this segment and write the + * fragments. + */ + Frag = Seg->Root; + while (Frag) { + + /* Write data depending on the type */ + switch (Frag->Type) { + + case FRAG_LITERAL: + /* To make the object file somewhat smaller, write all literal + * data of this and the following fragments preceeded by the + * length. + */ + F = Frag; + Size = 0; + while (F && F->Type == FRAG_LITERAL) { + Size += F->Len; + F = F->Next; + } + if (Size < 0x100) { + ObjWrite8 (FRAG_LITERAL8); + ObjWrite8 (Size); + } else if (Size < 0x10000) { + ObjWrite8 (FRAG_LITERAL16); + ObjWrite16 (Size); + } else if (Size < 0x1000000) { + ObjWrite8 (FRAG_LITERAL24); + ObjWrite24 (Size); + } else { + ObjWrite8 (FRAG_LITERAL32); + ObjWrite32 (Size); + } + + /* Now write the literal data */ + F = Frag; + while (F && F->Type == FRAG_LITERAL) { + ObjWriteData (F->V.Data, F->Len); + Frag = F; + F = F->Next; + } + break; + + case FRAG_EXPR: + switch (Frag->Len) { + case 1: ObjWrite8 (FRAG_EXPR8); break; + case 2: ObjWrite8 (FRAG_EXPR16); break; + case 3: ObjWrite8 (FRAG_EXPR24); break; + case 4: ObjWrite8 (FRAG_EXPR32); break; + default: Internal ("Invalid fragment size: %u", Frag->Len); + } + WriteExpr (Frag->V.Expr); + break; + + case FRAG_SEXPR: + switch (Frag->Len) { + case 1: ObjWrite8 (FRAG_SEXPR8); break; + case 2: ObjWrite8 (FRAG_SEXPR16); break; + case 3: ObjWrite8 (FRAG_SEXPR24); break; + case 4: ObjWrite8 (FRAG_SEXPR32); break; + default: Internal ("Invalid fragment size: %u", Frag->Len); + } + WriteExpr (Frag->V.Expr); + break; + + case FRAG_FILL: + ObjWrite8 (FRAG_FILL); + ObjWrite16 (Frag->Len); + break; + + default: + Internal ("Invalid fragment type: %u", Frag->Type); + + } + + /* Write the file position of this fragment */ + ObjWritePos (&Frag->Pos); + + /* Next fragment */ + Frag = Frag->Next; + } +} + + + +void WriteSegments (void) +/* Write the segment data to the object file */ +{ + Segment* Seg; + + /* Tell the object file module that we're about to start the seg list */ + ObjStartSegments (); + + /* First thing is segment count */ + ObjWrite8 (SegmentCount); + + /* Now walk through all segments and write them to the object file */ + Seg = SegmentList; + while (Seg) { + /* Write one segment */ + WriteOneSeg (Seg); + /* Next segment */ + Seg = Seg->List; + } + + /* Done writing segments */ + ObjEndSegments (); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void IncPC (unsigned Value) +/* Increment the PC counter */ +{ + ActiveSeg->PC += Value; + if (!RelocMode) { + AbsPC += Value; + } +} + + + +static Fragment* NewFragment (unsigned char Type, unsigned short Len) +/* Create, initialize and return a new fragment. The fragment will be inserted + * into the current segment. + */ +{ + Fragment* F; + + /* Create a new fragment */ + F = Xmalloc (sizeof (*F)); + + /* Initialize it */ + F->List = 0; + F->Next = 0; + F->LineList = 0; + F->Pos = CurPos; + F->Len = Len; + F->Type = Type; + + /* Insert it into the list of all segments */ + if (FragList == 0) { + FragList = F; + } else { + FragLast->List = F; + } + FragLast = F; + + /* Insert it into the current segment */ + if (ActiveSeg->Root) { + ActiveSeg->Last->Next = F; + ActiveSeg->Last = F; + } else { + ActiveSeg->Root = ActiveSeg->Last = F; + } + + /* Add this fragment to the current listing line */ + if (LineCur) { + if (LineCur->FragList == 0) { + LineCur->FragList = F; + /* First fragment - set the PC + LineCur->PC = GetPC (); + LineCur->Reloc = RelocMode; +*/ + } else { + LineCur->FragLast->LineList = F; + } + LineCur->FragLast = F; + } + + /* Increment the program counter */ + IncPC (Len); + + /* And return it */ + return F; +} + + + +void Emit0 (unsigned char OPC) +/* Emit an instruction with a zero sized operand */ +{ + /* First fragment, wrong type or out of space, create new one */ + Fragment* F = NewFragment (FRAG_LITERAL, 1); + F->V.Data [0] = OPC; +} + + + +void Emit1 (unsigned char OPC, ExprNode* Value) +/* Emit an instruction with an one byte argument */ +{ + Emit0 (OPC); + EmitByte (Value); +} + + + +void Emit2 (unsigned char OPC, ExprNode* Value) +/* Emit an instruction with a two byte argument */ +{ + Emit0 (OPC); + EmitWord (Value); +} + + + +void Emit3 (unsigned char OPC, ExprNode* Expr) +/* Emit an instruction with a three byte argument */ +{ + Emit0 (OPC); + EmitFarAddr (Expr); +} + + + +void Emit3b (unsigned char OPC, ExprNode* Expr, ExprNode* Bank) +/* Emit an instruction with a three byte argument and separate bank */ +{ + Emit0 (OPC); + EmitWord (Expr); + EmitByte (Bank); +} + + + +void EmitPCRel (unsigned char OPC, ExprNode* Expr, unsigned Size) +/* Emit an opcode with a PC relative argument of one or two bytes */ +{ + Fragment* F; + Emit0 (OPC); + F = NewFragment (FRAG_SEXPR, Size); + F->V.Expr = Expr; +} + + + +void EmitData (const unsigned char* Data, unsigned Size) +/* Emit data into the current segment */ +{ + /* Create lots of fragments for the data */ + while (Size) { + Fragment* F; + + /* Determine the length of the next fragment */ + unsigned Len = Size; + if (Len > sizeof (F->V.Data)) { + Len = sizeof (F->V.Data); + } + + /* Create a new fragment */ + F = NewFragment (FRAG_LITERAL, Len); + + /* Copy the data */ + memcpy (F->V.Data, Data, Len); + + /* Next chunk */ + Data += Len; + Size -= Len; + + } +} + + + +void EmitByte (ExprNode* Expr) +/* Emit one byte */ +{ + if (IsConstExpr (Expr)) { + /* Constant expression, emit literal byte */ + long Val = GetExprVal (Expr); + FreeExpr (Expr); + if ((Val & ~0xFF) != 0) { + Error (ERR_RANGE); + } + Emit0 (Val & 0xFF); + } else { + /* Create a new fragment */ + Fragment* F = NewFragment (FRAG_EXPR, 1); + + /* Set the data */ + F->V.Expr = Expr; + } +} + + + +void EmitWord (ExprNode* Expr) +/* Emit one word */ +{ + if (IsConstExpr (Expr)) { + /* Constant expression, emit literal byte */ + long Val = GetExprVal (Expr); + FreeExpr (Expr); + if ((Val & ~0xFFFF) != 0) { + Error (ERR_RANGE); + } + Emit0 (Val & 0xFF); + Emit0 ((Val >> 8) & 0xFF); + } else { + /* Create a new fragment */ + Fragment* F = NewFragment (FRAG_EXPR, 2); + + /* Set the data */ + F->V.Expr = Expr; + } +} + + + +void EmitFarAddr (ExprNode* Expr) +/* Emit a 24 bit expression */ +{ + if (IsConstExpr (Expr)) { + /* Constant expression, emit literal byte */ + long Val = GetExprVal (Expr); + FreeExpr (Expr); + if ((Val & ~0xFFFFFF) != 0) { + Error (ERR_RANGE); + } + Emit0 (Val & 0xFF); + Emit0 ((Val >> 8) & 0xFF); + Emit0 ((Val >> 16) & 0xFF); + } else { + /* Create a new fragment */ + Fragment* F = NewFragment (FRAG_EXPR, 3); + + /* Set the data */ + F->V.Expr = Expr; + } +} + + + +void EmitDWord (ExprNode* Expr) +/* Emit one dword */ +{ + if (IsConstExpr (Expr)) { + /* Constant expression, emit literal byte */ + long Val = GetExprVal (Expr); + FreeExpr (Expr); + Emit0 (Val & 0xFF); + Emit0 ((Val >> 8) & 0xFF); + Emit0 ((Val >> 16) & 0xFF); + Emit0 ((Val >> 24) & 0xFF); + } else { + /* Create a new fragment */ + Fragment* F = NewFragment (FRAG_EXPR, 4); + + /* Set the data */ + F->V.Expr = Expr; + } +} + + + +void EmitFill (unsigned long Count) +/* Emit Count fill bytes */ +{ + while (Count) { + /* Calculate the size of the next chunk */ + unsigned Chunk = (Count > 0xFFFF)? 0xFFFF : (unsigned) Count; + Count -= Chunk; + + /* Emit one chunk */ + NewFragment (FRAG_FILL, Chunk); + } +} + + + diff --git a/src/ca65/objcode.h b/src/ca65/objcode.h new file mode 100644 index 000000000..b4b0d4898 --- /dev/null +++ b/src/ca65/objcode.h @@ -0,0 +1,168 @@ +/*****************************************************************************/ +/* */ +/* objcode.h */ +/* */ +/* Objectcode management for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OBJCODE_H +#define OBJCODE_H + + + +#include "../common/segdefs.h" +#include "expr.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Are we in absolute mode or in relocatable mode? */ +extern int RelocMode; + + + +/*****************************************************************************/ +/* Segment management */ +/*****************************************************************************/ + + + +void UseCodeSeg (void); +/* Use the code segment */ + +void UseRODataSeg (void); +/* Use the r/o data segment */ + +void UseDataSeg (void); +/* Use the data segment */ + +void UseBssSeg (void); +/* Use the BSS segment */ + +void UseZeropageSeg (void); +/* Use the zero page segment */ + +void UseNullSeg (void); +/* Use the null segment */ + +void UseSeg (const char* Name, unsigned SegType); +/* Use the segment with the given name */ + +unsigned GetSegNum (void); +/* Get the number of the current segment */ + +void SegAlign (unsigned Power, int Val); +/* Align the PC segment to 2^Power. If Val is -1, emit fill fragments (the + * actual fill value will be determined by the linker), otherwise use the + * given value. + */ + +int IsZPSeg (void); +/* Return true if the current segment is a zeropage segment */ + +int IsFarSeg (void); +/* Return true if the current segment is a far segment */ + +unsigned GetSegType (unsigned SegNum); +/* Return the type of the segment with the given number */ + +unsigned long GetPC (void); +/* Get the program counter of the current segment */ + +void SetAbsPC (unsigned long AbsPC); +/* Set the program counter in absolute mode */ + +void SegCheck (void); +/* Check the segments for range and other errors */ + +void SegDump (void); +/* Dump the contents of all segments */ + +void WriteSegments (void); +/* Write the segment data to the object file */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Emit0 (unsigned char OPC); +/* Emit an instruction with a zero sized operand */ + +void Emit1 (unsigned char OPC, ExprNode* Value); +/* Emit an instruction with an one byte argument */ + +void Emit2 (unsigned char OPC, ExprNode* Value); +/* Emit an instruction with a two byte argument */ + +void Emit3 (unsigned char OPC, ExprNode* Expr); +/* Emit an instruction with a three byte argument */ + +void Emit3b (unsigned char OPC, ExprNode* Expr, ExprNode* Bank); +/* Emit an instruction with a three byte argument and separate bank */ + +void EmitPCRel (unsigned char OPC, ExprNode* Expr, unsigned Size); +/* Emit an opcode with a PC relative argument of one or two bytes */ + +void EmitData (const unsigned char* Data, unsigned Size); +/* Emit data into the current segment */ + +void EmitByte (ExprNode* Expr); +/* Emit one byte */ + +void EmitWord (ExprNode* Expr); +/* Emit one word */ + +void EmitFarAddr (ExprNode* Expr); +/* Emit a 24 bit expression */ + +void EmitDWord (ExprNode* Expr); +/* Emit one dword */ + +void EmitFill (unsigned long Count); +/* Emit Count fill bytes */ + + + +/* End of objcode.h */ + +#endif + + + diff --git a/src/ca65/objfile.c b/src/ca65/objfile.c new file mode 100644 index 000000000..b3218feb3 --- /dev/null +++ b/src/ca65/objfile.c @@ -0,0 +1,351 @@ +/*****************************************************************************/ +/* */ +/* objfile.c */ +/* */ +/* Object file writing routines for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include + +#include "../common/objdefs.h" + +#include "global.h" +#include "error.h" +#include "fname.h" +#include "mem.h" +#include "objfile.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* File descriptor */ +static FILE* F = 0; + +/* Default extension */ +#define OBJ_EXT ".o" + +/* Header structure */ +static ObjHeader Header = { + OBJ_MAGIC, + OBJ_VERSION +}; + + + +/*****************************************************************************/ +/* Internally used functions */ +/*****************************************************************************/ + + + +static void ObjWriteError (void) +/* Called on a write error. Will try to close and remove the file, then + * print a fatal error. + */ +{ + /* Remember the error */ + int Error = errno; + + /* Force a close of the file, ignoring errors */ + fclose (F); + + /* Try to remove the file, also ignoring errors */ + remove (OutFile); + + /* Now abort with a fatal error */ + Fatal (FAT_CANNOT_WRITE_OUTPUT, OutFile, strerror (Error)); +} + + + +static void ObjWriteHeader (void) +/* Write the object file header to the current file position */ +{ + ObjWrite32 (Header.Magic); + ObjWrite16 (Header.Version); + ObjWrite16 (Header.Flags); + ObjWrite32 (Header.OptionOffs); + ObjWrite32 (Header.OptionSize); + ObjWrite32 (Header.FileOffs); + ObjWrite32 (Header.FileSize); + ObjWrite32 (Header.SegOffs); + ObjWrite32 (Header.SegSize); + ObjWrite32 (Header.ImportOffs); + ObjWrite32 (Header.ImportSize); + ObjWrite32 (Header.ExportOffs); + ObjWrite32 (Header.ExportSize); + ObjWrite32 (Header.DbgSymOffs); + ObjWrite32 (Header.DbgSymSize); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ObjOpen (void) +/* Open the object file for writing, write a dummy header */ +{ + /* Do we have a name for the output file? */ + if (OutFile == 0) { + /* We don't have an output name explicitly given, construct one from + * the name of the input file. + */ + OutFile = MakeFilename (InFile, OBJ_EXT); + } + + /* Create the output file */ + F = fopen (OutFile, "w+b"); + if (F == 0) { + Fatal (FAT_CANNOT_OPEN_OUTPUT, OutFile, strerror (errno)); + } + + /* Write a dummy header */ + ObjWriteHeader (); +} + + + +void ObjClose (void) +/* Write an update header and close the object file. */ +{ + /* Go back to the beginning */ + if (fseek (F, 0, SEEK_SET) != 0) { + ObjWriteError (); + } + + /* If we have debug infos, set the flag in the header */ + if (DbgSyms) { + Header.Flags |= OBJ_FLAGS_DBGINFO; + } + + /* Write the updated header */ + ObjWriteHeader (); + + /* Close the file */ + if (fclose (F) != 0) { + ObjWriteError (); + } +} + + + +void ObjWrite8 (unsigned char V) +/* Write an 8 bit value to the file */ +{ + if (putc (V, F) == EOF) { + ObjWriteError (); + } +} + + + +void ObjWrite16 (unsigned V) +/* Write a 16 bit value to the file */ +{ + ObjWrite8 (V); + ObjWrite8 (V >> 8); +} + + + +void ObjWrite24 (unsigned long V) +/* Write a 24 bit value to the file */ +{ + ObjWrite8 (V); + ObjWrite8 (V >> 8); + ObjWrite8 (V >> 16); +} + + + +void ObjWrite32 (unsigned long V) +/* Write a 32 bit value to the file */ +{ + ObjWrite8 (V); + ObjWrite8 (V >> 8); + ObjWrite8 (V >> 16); + ObjWrite8 (V >> 24); +} + + + +void ObjWriteStr (const char* S) +/* Write a string to the object file */ +{ + unsigned Len = strlen (S); + if (Len > 255) { + Internal ("String too long in ObjWriteStr"); + } + + /* Write the string with a length byte preceeded (this is easier for + * the reading routine than the C format since the length is known in + * advance). + */ + ObjWrite8 ((unsigned char) Len); + ObjWriteData (S, Len); +} + + + +void ObjWriteData (const void* Data, unsigned Size) +/* Write literal data to the file */ +{ + if (fwrite (Data, 1, Size, F) != Size) { + ObjWriteError (); + } +} + + + +void ObjWritePos (const FilePos* Pos) +/* Write a file position to the object file */ +{ + /* Write the line number as 24 bit value to save one byte */ + ObjWrite24 (Pos->Line); + ObjWrite8 (Pos->Col); + if (Pos->Name == 0) { + /* Position is outside file scope, use the main file instead */ + ObjWrite8 (0); + } else { + ObjWrite8 (Pos->Name - 1); + } +} + + + +void ObjStartOptions (void) +/* Mark the start of the option section */ +{ + Header.OptionOffs = ftell (F); +} + + + +void ObjEndOptions (void) +/* Mark the end of the option section */ +{ + Header.OptionSize = ftell (F) - Header.OptionOffs; +} + + + +void ObjStartFiles (void) +/* Mark the start of the files section */ +{ + Header.FileOffs = ftell (F); +} + + + +void ObjEndFiles (void) +/* Mark the end of the files section */ +{ + Header.FileSize = ftell (F) - Header.FileOffs; +} + + + +void ObjStartSegments (void) +/* Mark the start of the segment section */ +{ + Header.SegOffs = ftell (F); +} + + + +void ObjEndSegments (void) +/* Mark the end of the segment section */ +{ + Header.SegSize = ftell (F) - Header.SegOffs; +} + + + +void ObjStartImports (void) +/* Mark the start of the import section */ +{ + Header.ImportOffs = ftell (F); +} + + + +void ObjEndImports (void) +/* Mark the end of the import section */ +{ + Header.ImportSize = ftell (F) - Header.ImportOffs; +} + + + +void ObjStartExports (void) +/* Mark the start of the export section */ +{ + Header.ExportOffs = ftell (F); +} + + + +void ObjEndExports (void) +/* Mark the end of the export section */ +{ + Header.ExportSize = ftell (F) - Header.ExportOffs; +} + + + +void ObjStartDbgSyms (void) +/* Mark the start of the debug symbol section */ +{ + Header.DbgSymOffs = ftell (F); +} + + + +void ObjEndDbgSyms (void) +/* Mark the end of the debug symbol section */ +{ + Header.DbgSymSize = ftell (F) - Header.DbgSymOffs; +} + + + diff --git a/src/ca65/objfile.h b/src/ca65/objfile.h new file mode 100644 index 000000000..27c19fd67 --- /dev/null +++ b/src/ca65/objfile.h @@ -0,0 +1,121 @@ +/*****************************************************************************/ +/* */ +/* objfile.h */ +/* */ +/* Object file writing routines for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OBJFILE_H +#define OBJFILE_H + + + +#include "../common/filepos.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ObjOpen (void); +/* Open the object file for writing, write a dummy header */ + +void ObjClose (void); +/* Write an update header and close the object file. */ + +void ObjWrite8 (unsigned char V); +/* Write an 8 bit value to the file */ + +void ObjWrite16 (unsigned V); +/* Write a 16 bit value to the file */ + +void ObjWrite24 (unsigned long V); +/* Write a 24 bit value to the file */ + +void ObjWrite32 (unsigned long V); +/* Write a 32 bit value to the file */ + +void ObjWriteStr (const char* S); +/* Write a string to the object file */ + +void ObjWriteData (const void* Data, unsigned Size); +/* Write literal data to the file */ + +void ObjWritePos (const FilePos* Pos); +/* Write a file position to the object file */ + +void ObjStartOptions (void); +/* Mark the start of the option section */ + +void ObjEndOptions (void); +/* Mark the end of the option section */ + +void ObjStartFiles (void); +/* Mark the start of the files section */ + +void ObjEndFiles (void); +/* Mark the end of the files section */ + +void ObjStartSegments (void); +/* Mark the start of the segment section */ + +void ObjEndSegments (void); +/* Mark the end of the segment section */ + +void ObjStartImports (void); +/* Mark the start of the import section */ + +void ObjEndImports (void); +/* Mark the end of the import section */ + +void ObjStartExports (void); +/* Mark the start of the export section */ + +void ObjEndExports (void); +/* Mark the end of the export section */ + +void ObjStartDbgSyms (void); +/* Mark the start of the debug symbol section */ + +void ObjEndDbgSyms (void); +/* Mark the end of the debug symbol section */ + + + +/* End of objfile.h */ + +#endif + + + diff --git a/src/ca65/options.c b/src/ca65/options.c new file mode 100644 index 000000000..ca05f0b48 --- /dev/null +++ b/src/ca65/options.c @@ -0,0 +1,202 @@ +/*****************************************************************************/ +/* */ +/* options.c */ +/* */ +/* Object file options for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "../common/optdefs.h" + +#include "mem.h" +#include "error.h" +#include "objfile.h" +#include "options.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Option list */ +static Option* OptRoot = 0; +static Option* OptLast = 0; +static unsigned OptCount = 0; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static Option* NewOption (unsigned char Type) +/* Create a new option, insert it into the list and return it */ +{ + Option* Opt; + + /* Allocate memory */ + Opt = Xmalloc (sizeof (*Opt)); + + /* Initialize fields */ + Opt->Next = 0; + Opt->Type = Type; + Opt->V.Str = 0; + + /* Insert it into the list */ + if (OptRoot == 0) { + OptRoot = Opt; + } else { + OptLast->Next = Opt; + } + OptLast = Opt; + + /* One more option now */ + ++OptCount; + + /* Return the new struct */ + return Opt; +} + + + +void OptStr (unsigned char Type, const char* Text) +/* Add a string option */ +{ + Option* O; + + /* String must have less than 255 bytes */ + if (strlen (Text) > 255) { + Fatal (FAT_STRING_TOO_LONG); + } + O = NewOption (Type); + O->V.Str = StrDup (Text); +} + + + +void OptComment (const char* Comment) +/* Add a comment */ +{ + OptStr (OPT_COMMENT, Comment); +} + + + +void OptAuthor (const char* Author) +/* Add an author statement */ +{ + OptStr (OPT_AUTHOR, Author); +} + + + +void OptTranslator (const char* Translator) +/* Add a translator option */ +{ + OptStr (OPT_TRANSLATOR, Translator); +} + + + +void OptCompiler (const char* Compiler) +/* Add a compiler option */ +{ + OptStr (OPT_COMPILER, Compiler); +} + + + +void OptOS (const char* OS) +/* Add an operating system option */ +{ + OptStr (OPT_OS, OS); +} + + + +void OptDateTime (unsigned long DateTime) +/* Add a date/time option */ +{ + Option* O = NewOption (OPT_DATETIME); + O->V.Val = DateTime; +} + + + +void WriteOptions (void) +/* Write the options to the object file */ +{ + Option* O; + + /* Tell the object file module that we're about to start the options */ + ObjStartOptions (); + + /* Write the option count */ + ObjWrite16 (OptCount); + + /* Walk through the list and write the options */ + O = OptRoot; + while (O) { + + /* Write the type of the option */ + ObjWrite8 (O->Type); + + /* Write the argument */ + switch (O->Type & OPT_ARGMASK) { + + case OPT_ARGSTR: + ObjWriteStr (O->V.Str); + break; + + case OPT_ARGNUM: + ObjWrite32 (O->V.Val); + break; + + default: + Internal ("Invalid option type: $%02X", O->Type & 0xFF); + + } + + /* Next option */ + O = O->Next; + + } + + /* Done writing options */ + ObjEndOptions (); +} + + + diff --git a/src/ca65/options.h b/src/ca65/options.h new file mode 100644 index 000000000..1c6839b48 --- /dev/null +++ b/src/ca65/options.h @@ -0,0 +1,78 @@ +/*****************************************************************************/ +/* */ +/* options.h */ +/* */ +/* Object file options for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OPTIONS_H +#define OPTIONS_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void OptStr (unsigned char Type, const char* Text); +/* Add a string option */ + +void OptComment (const char* Comment); +/* Add a comment */ + +void OptAuthor (const char* Author); +/* Add an author statement */ + +void OptTranslator (const char* Translator); +/* Add a translator option */ + +void OptCompiler (const char* Compiler); +/* Add a compiler option */ + +void OptOS (const char* OS); +/* Add an operating system option */ + +void OptDateTime (unsigned long DateTime); +/* Add a date/time option */ + +void WriteOptions (void); +/* Write the options to the object file */ + + + +/* End of options.h */ + +#endif + + + diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c new file mode 100644 index 000000000..f2ad9b350 --- /dev/null +++ b/src/ca65/pseudo.c @@ -0,0 +1,1190 @@ +/*****************************************************************************/ +/* */ +/* pseudo.c */ +/* */ +/* Pseudo instructions for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include +#include + +#include "../common/bitops.h" + +#include "condasm.h" +#include "error.h" +#include "expr.h" +#include "global.h" +#include "instr.h" +#include "listing.h" +#include "macpack.h" +#include "macro.h" +#include "objcode.h" +#include "options.h" +#include "scanner.h" +#include "strexpr.h" +#include "symtab.h" +#include "pseudo.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Keyword we're about to handle */ +static char Keyword [sizeof (SVal)+1] = "."; + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static void DoUnexpected (void); + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static void SetBoolOption (unsigned char* Flag) +/* Read a on/off/+/- option and set flag accordingly */ +{ + static const char* Keys[] = { + "OFF", + "ON", + }; + + if (Tok == TOK_PLUS) { + *Flag = 1; + NextTok (); + } else if (Tok == TOK_MINUS) { + *Flag = 0; + NextTok (); + } else if (Tok == TOK_IDENT) { + /* Map the keyword to a number */ + switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) { + case 0: *Flag = 0; NextTok (); break; + case 1: *Flag = 1; NextTok (); break; + default: ErrorSkip (ERR_ONOFF_EXPECTED); break; + } + } else if (Tok == TOK_SEP || Tok == TOK_EOF) { + /* Without anything assume switch on */ + *Flag = 1; + } else { + ErrorSkip (ERR_ONOFF_EXPECTED); + } +} + + + +static void ExportImport (void (*SymFunc) (const char*, int), int ZP) +/* Export or import symbols */ +{ + while (1) { + if (Tok != TOK_IDENT) { + ErrorSkip (ERR_IDENT_EXPECTED); + break; + } + SymFunc (SVal, ZP); + NextTok (); + if (Tok == TOK_COMMA) { + NextTok (); + } else { + break; + } + } +} + + + +static long IntArg (long Min, long Max) +/* Read an integer argument and check a range. Accept the token "unlimited" + * and return -1 in this case. + */ +{ + if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) { + NextTok (); + return -1; + } else { + long Val = ConstExpression (); + if (Val < Min || Val > Max) { + Error (ERR_RANGE); + Val = Min; + } + return Val; + } +} + + + +/*****************************************************************************/ +/* Handler functions */ +/*****************************************************************************/ + + + +static void DoA16 (void) +/* Switch the accu to 16 bit mode (assembler only) */ +{ + if (GetCPU() != CPU_65816) { + Error (ERR_816_MODE_ONLY); + } else { + /* Immidiate mode has two extension bytes */ + ExtBytes [AMI_IMM_ACCU] = 2; + } +} + + + +static void DoA8 (void) +/* Switch the accu to 8 bit mode (assembler only) */ +{ + if (GetCPU() != CPU_65816) { + Error (ERR_816_MODE_ONLY); + } else { + /* Immidiate mode has one extension byte */ + ExtBytes [AMI_IMM_ACCU] = 1; + } +} + + + +static void DoAddr (void) +/* Define addresses */ +{ + while (1) { + if (GetCPU() == CPU_65816) { + EmitWord (ForceWordExpr (Expression ())); + } else { + /* Do a range check */ + EmitWord (Expression ()); + } + if (Tok != TOK_COMMA) { + break; + } else { + NextTok (); + } + } +} + + + +static void DoAlign (void) +/* Align the PC to some boundary */ +{ + long Val; + long Align; + unsigned Bit; + + /* Read the alignment value */ + Align = ConstExpression (); + if (Align <= 0 || Align > 0x10000) { + ErrorSkip (ERR_RANGE); + return; + } + + /* Optional value follows */ + if (Tok == TOK_COMMA) { + NextTok (); + Val = ConstExpression (); + /* We need a byte value here */ + if (!IsByteRange (Val)) { + ErrorSkip (ERR_RANGE); + return; + } + } else { + Val = -1; + } + + /* Check if the alignment is a power of two */ + Bit = BitFind (Align); + if (Align != (0x01UL << Bit)) { + Error (ERR_ALIGN); + } else { + SegAlign (Bit, (int) Val); + } +} + + + +static void DoASCIIZ (void) +/* Define text with a zero terminator */ +{ + while (1) { + if (StringExpression () == 0) { + ErrorSkip (ERR_STRCON_EXPECTED); + return; + } + EmitData (SVal, strlen (SVal)); + NextTok (); + if (Tok == TOK_COMMA) { + NextTok (); + } else { + break; + } + } + Emit0 (0); +} + + + +static void DoAutoImport (void) +/* Mark unresolved symbols as imported */ +{ + SetBoolOption (&AutoImport); +} + + + +static void DoBss (void) +/* Switch to the BSS segment */ +{ + UseBssSeg (); +} + + + +static void DoByte (void) +/* Define bytes */ +{ + while (1) { + if (StringExpression () != 0) { + /* A string */ + EmitData (SVal, strlen (SVal)); + NextTok (); + } else { + EmitByte (Expression ()); + } + if (Tok != TOK_COMMA) { + break; + } else { + NextTok (); + /* Do smart handling of dangling comma */ + if (Tok == TOK_SEP) { + Error (ERR_UNEXPECTED_EOL); + break; + } + } + } +} + + + +static void DoCase (void) +/* Switch the IgnoreCase option */ +{ + SetBoolOption (&IgnoreCase); + IgnoreCase = !IgnoreCase; +} + + + +static void DoCode (void) +/* Switch to the code segment */ +{ + UseCodeSeg (); +} + + + +static void DoData (void) +/* Switch to the data segment */ +{ + UseDataSeg (); +} + + + +static void DoDByt (void) +/* Output double bytes */ +{ + while (1) { + EmitWord (SwapExpr (Expression ())); + if (Tok != TOK_COMMA) { + break; + } else { + NextTok (); + } + } +} + + + +static void DoDebugInfo (void) +/* Switch debug info on or off */ +{ + SetBoolOption (&DbgSyms); +} + + + +static void DoDefine (void) +/* Define a one line macro */ +{ + MacDef (MAC_STYLE_DEFINE); +} + + + +static void DoDWord (void) +/* Define dwords */ +{ + while (1) { + EmitDWord (Expression ()); + if (Tok != TOK_COMMA) { + break; + } else { + NextTok (); + } + } +} + + + +static void DoEnd (void) +/* End of assembly */ +{ + ForcedEnd = 1; +} + + + +static void DoEndProc (void) +/* Leave a lexical level */ +{ + SymLeaveLevel (); +} + + + +static void DoError (void) +/* Use error */ +{ + if (StringExpression () == 0) { + ErrorSkip (ERR_STRCON_EXPECTED); + } else { + Error (ERR_USER, SVal); + SkipUntilSep (); + } +} + + + +static void DoExitMacro (void) +/* Exit a macro expansion */ +{ + if (!InMacExpansion ()) { + /* We aren't expanding a macro currently */ + DoUnexpected (); + } else { + MacAbort (); + } +} + + + +static void DoExport (void) +/* Export a symbol */ +{ + ExportImport (SymExport, 0); +} + + + +static void DoExportZP (void) +/* Export a zeropage symbol */ +{ + ExportImport (SymExport, 1); +} + + + +static void DoFarAddr (void) +/* Define far addresses (24 bit) */ +{ + while (1) { + EmitFarAddr (Expression ()); + if (Tok != TOK_COMMA) { + break; + } else { + NextTok (); + } + } +} + + + +static void DoFeature (void) +/* Switch the Feature option */ +{ + int Feature; + + static const char* Keys[] = { + "DOLLAR_IS_PC", + "LABELS_WITHOUT_COLONS", + "LOOSE_STRING_TERM", + "AT_IN_IDENTIFIERS", + "DOLLAR_IN_IDENTIFIERS", + }; + + /* Allow a list of comma separated keywords */ + while (1) { + + /* We expect an identifier */ + if (Tok != TOK_IDENT) { + ErrorSkip (ERR_IDENT_EXPECTED); + return; + } + + /* Map the keyword to a number */ + Feature = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0])); + if (Feature < 0) { + /* Not found */ + ErrorSkip (ERR_ILLEGAL_FEATURE); + return; + } + + /* Skip the keyword */ + NextTok (); + + /* Switch the feature on */ + switch (Feature) { + case 0: DollarIsPC = 1; break; + case 1: NoColonLabels = 1; break; + case 2: LooseStringTerm = 1; break; + case 3: AtInIdents = 1; break; + case 4: DollarInIdents = 1; break; + default: Internal ("Invalid feature: %d", Feature); + } + + /* Allow more than one keyword */ + if (Tok == TOK_COMMA) { + NextTok (); + } else { + break; + } + } +} + + + +static void DoFileOpt (void) +/* Insert a file option */ +{ + long OptNum; + + /* The option type may be given as a keyword or as a number. */ + if (Tok == TOK_IDENT) { + + /* Option given as keyword */ + static const char* Keys [] = { + "AUTHOR", "COMMENT", "COMPILER" + }; + + /* Map the option to a number */ + OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0])); + if (OptNum < 0) { + /* Not found */ + ErrorSkip (ERR_OPTION_KEY_EXPECTED); + return; + } + + /* Skip the keyword */ + NextTok (); + + /* Must be followed by a comma */ + ConsumeComma (); + + /* We accept only string options for now */ + if (StringExpression () == 0) { + ErrorSkip (ERR_STRCON_EXPECTED); + return; + } + + /* Insert the option */ + switch (OptNum) { + + case 0: + /* Author */ + OptAuthor (SVal); + break; + + case 1: + /* Comment */ + OptComment (SVal); + break; + + case 2: + /* Compiler */ + OptCompiler (SVal); + break; + + default: + Internal ("Invalid OptNum: %l", OptNum); + + } + + /* Done */ + NextTok (); + + } else { + + /* Option given as number */ + OptNum = ConstExpression (); + if (!IsByteRange (OptNum)) { + ErrorSkip (ERR_RANGE); + return; + } + + /* Must be followed by a comma */ + ConsumeComma (); + + /* We accept only string options for now */ + if (StringExpression () == 0) { + ErrorSkip (ERR_STRCON_EXPECTED); + return; + } + + /* Insert the option */ + OptStr ((unsigned char) OptNum, SVal); + + /* Done */ + NextTok (); + } +} + + + +static void DoGlobal (void) +/* Declare a global symbol */ +{ + ExportImport (SymGlobal, 0); +} + + + +static void DoGlobalZP (void) +/* Declare a global zeropage symbol */ +{ + ExportImport (SymGlobal, 1); +} + + + +static void DoI16 (void) +/* Switch the index registers to 16 bit mode (assembler only) */ +{ + if (GetCPU() != CPU_65816) { + Error (ERR_816_MODE_ONLY); + } else { + /* Immidiate mode has two extension bytes */ + ExtBytes [AMI_IMM_INDEX] = 2; + } +} + + + +static void DoI8 (void) +/* Switch the index registers to 16 bit mode (assembler only) */ +{ + if (GetCPU() != CPU_65816) { + Error (ERR_816_MODE_ONLY); + } else { + /* Immidiate mode has one extension byte */ + ExtBytes [AMI_IMM_INDEX] = 1; + } +} + + + +static void DoImport (void) +/* Import a symbol */ +{ + ExportImport (SymImport, 0); +} + + + +static void DoImportZP (void) +/* Import a zero page symbol */ +{ + ExportImport (SymImport, 1); +} + + + +static void DoIncBin (void) +/* Include a binary file */ +{ + /* Name must follow */ + if (StringExpression () == 0) { + ErrorSkip (ERR_STRCON_EXPECTED); + } else { + /* Try to open the file */ + FILE* F = fopen (SVal, "rb"); + if (F == 0) { + Error (ERR_CANNOT_OPEN_INCLUDE, SVal, strerror (errno)); + } else { + unsigned char Buf [1024]; + size_t Count; + /* Read chunks and insert them into the output */ + while ((Count = fread (Buf, 1, sizeof (Buf), F)) > 0) { + EmitData (Buf, Count); + } + /* Close the file, ignore errors since it's r/o */ + (void) fclose (F); + } + /* Skip the name */ + NextTok (); + } +} + + + +static void DoInclude (void) +/* Include another file */ +{ + char Name [MAX_STR_LEN+1]; + + /* Name must follow */ + if (StringExpression () == 0) { + ErrorSkip (ERR_STRCON_EXPECTED); + } else { + strcpy (Name, SVal); + NextTok (); + NewInputFile (Name); + } +} + + + +static void DoLineCont (void) +/* Switch the use of line continuations */ +{ + SetBoolOption (&LineCont); +} + + + +static void DoList (void) +/* Enable/disable the listing */ +{ + /* Get the setting */ + unsigned char List; + SetBoolOption (&List); + + /* Manage the counter */ + if (List) { + EnableListing (); + } else { + DisableListing (); + } +} + + + +static void DoListBytes (void) +/* Set maximum number of bytes to list for one line */ +{ + SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES)); +} + + + +static void DoLocalChar (void) +/* Define the character that starts local labels */ +{ + if (Tok != TOK_CHARCON) { + ErrorSkip (ERR_CHARCON_EXPECTED); + } else { + if (IVal != '@' && IVal != '?') { + Error (ERR_ILLEGAL_LOCALSTART); + } else { + LocalStart = IVal; + } + NextTok (); + } +} + + + +static void DoMacPack (void) +/* Insert a macro package */ +{ + /* Macro package names */ + static const char* Keys [] = { + "GENERIC", + "LONGBRANCH", + }; + + int Package; + + /* We expect an identifier */ + if (Tok != TOK_IDENT) { + ErrorSkip (ERR_IDENT_EXPECTED); + return; + } + + /* Map the keyword to a number */ + Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0])); + if (Package < 0) { + /* Not found */ + ErrorSkip (ERR_ILLEGAL_MACPACK); + return; + } + + /* Skip the package name */ + NextTok (); + + /* Insert the package */ + InsertMacPack (Package); +} + + + +static void DoMacro (void) +/* Start a macro definition */ +{ + MacDef (MAC_STYLE_CLASSIC); +} + + + +static void DoNull (void) +/* Switch to the NULL segment */ +{ + UseNullSeg (); +} + + + +static void DoOrg (void) +/* Start absolute code */ +{ + long PC = ConstExpression (); + if (PC < 0 || PC > 0xFFFF) { + Error (ERR_RANGE); + return; + } + SetAbsPC (PC); +} + + + +static void DoOut (void) +/* Output a string */ +{ + if (StringExpression () == 0) { + ErrorSkip (ERR_STRCON_EXPECTED); + } else { + /* Output the string and be sure to flush the output to keep it in + * sync with any error messages if the output is redirected to a file. + */ + printf ("%s\n", SVal); + fflush (stdout); + NextTok (); + } +} + + + +static void DoP02 (void) +/* Switch to 6502 CPU */ +{ + SetCPU (CPU_6502); +} + + + +static void DoPC02 (void) +/* Switch to 65C02 CPU */ +{ + SetCPU (CPU_65C02); +} + + + +static void DoP816 (void) +/* Switch to 65816 CPU */ +{ + SetCPU (CPU_65816); +} + + + +static void DoPageLength (void) +/* Set the page length for the listing */ +{ + PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN); +} + + + +static void DoProc (void) +/* Start a new lexical scope */ +{ + if (Tok == TOK_IDENT) { + /* The new scope has a name */ + SymDef (SVal, CurrentPC (), IsZPSeg ()); + NextTok (); + } + SymEnterLevel (); +} + + + +static void DoReloc (void) +/* Enter relocatable mode */ +{ + RelocMode = 1; +} + + + +static void DoRepeat (void) +/* Repeat some instruction block */ +{ + ErrorSkip (ERR_NOT_IMPLEMENTED); +} + + + +static void DoRes (void) +/* Reserve some number of storage bytes */ +{ + long Count; + long Val; + + Count = ConstExpression (); + if (Count > 0xFFFF || Count < 0) { + ErrorSkip (ERR_RANGE); + return; + } + if (Tok == TOK_COMMA) { + NextTok (); + Val = ConstExpression (); + /* We need a byte value here */ + if (!IsByteRange (Val)) { + ErrorSkip (ERR_RANGE); + return; + } + + /* Emit constant values */ + while (Count--) { + Emit0 ((unsigned char) Val); + } + + } else { + /* Emit fill fragments */ + EmitFill (Count); + } +} + + + +static void DoROData (void) +/* Switch to the r/o data segment */ +{ + UseRODataSeg (); +} + + + +static void DoSegment (void) +/* Switch to another segment */ +{ + static const char* AttrTab [] = { + "ZEROPAGE", "DIRECT", + "ABSOLUTE", + "FAR", "LONG" + }; + char Name [sizeof (SVal)]; + int SegType; + + if (StringExpression () == 0) { + ErrorSkip (ERR_STRCON_EXPECTED); + } else { + + /* Save the name of the segment and skip it */ + strcpy (Name, SVal); + NextTok (); + + /* Check for an optional segment attribute */ + SegType = SEGTYPE_DEFAULT; + if (Tok == TOK_COMMA) { + NextTok (); + if (Tok != TOK_IDENT) { + ErrorSkip (ERR_IDENT_EXPECTED); + } else { + int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0])); + switch (Attr) { + + case 0: + case 1: + /* Zeropage */ + SegType = SEGTYPE_ZP; + break; + + case 2: + /* Absolute */ + SegType = SEGTYPE_ABS; + break; + + case 3: + case 4: + /* Far */ + SegType = SEGTYPE_FAR; + break; + + default: + Error (ERR_ILLEGAL_SEG_ATTR); + } + NextTok (); + } + } + + /* Set the segment */ + UseSeg (Name, SegType); + } +} + + + +static void DoSmart (void) +/* Smart mode on/off */ +{ + SetBoolOption (&SmartMode); +} + + + +static void DoSunPlus (void) +/* Switch to the SUNPLUS CPU */ +{ + SetCPU (CPU_SUNPLUS); +} + + + +static void DoUnexpected (void) +/* Got an unexpected keyword */ +{ + Error (ERR_UNEXPECTED, Keyword); + SkipUntilSep (); +} + + + +static void DoWord (void) +/* Define words */ +{ + while (1) { + EmitWord (Expression ()); + if (Tok != TOK_COMMA) { + break; + } else { + NextTok (); + } + } +} + + + +static void DoZeropage (void) +/* Switch to the zeropage segment */ +{ + UseZeropageSeg (); +} + + + +/*****************************************************************************/ +/* Table data */ +/*****************************************************************************/ + + + +/* Control commands flags */ +enum { + ccNone = 0x0000, /* No special flags */ + ccKeepToken = 0x0001 /* Do not skip the current token */ +}; + +/* Control command table */ +struct CtrlDesc_ { + unsigned Flags; /* Flags for this directive */ + void (*Handler) (void); /* Command handler */ +}; +typedef struct CtrlDesc_ CtrlDesc; + +#define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0])) +static CtrlDesc CtrlCmdTab [] = { + { ccNone, DoA16 }, + { ccNone, DoA8 }, + { ccNone, DoAddr }, /* .ADDR */ + { ccNone, DoAlign }, + { ccNone, DoASCIIZ }, + { ccNone, DoAutoImport }, + { ccNone, DoUnexpected }, /* .BLANK */ + { ccNone, DoBss }, + { ccNone, DoByte }, + { ccNone, DoCase }, + { ccNone, DoCode }, + { ccNone, DoUnexpected }, /* .CONST */ + { ccNone, DoUnexpected }, /* .CPU */ + { ccNone, DoData }, + { ccNone, DoDByt }, + { ccNone, DoDebugInfo }, + { ccNone, DoDefine }, + { ccNone, DoUnexpected }, /* .DEFINED */ + { ccNone, DoDWord }, + { ccKeepToken, DoConditionals }, /* .ELSE */ + { ccKeepToken, DoConditionals }, /* .ELSEIF */ + { ccNone, DoEnd }, + { ccKeepToken, DoConditionals }, /* .ENDIF */ + { ccNone, DoUnexpected }, /* .ENDMACRO */ + { ccNone, DoEndProc }, + { ccNone, DoUnexpected }, /* .ENDREPEAT */ + { ccNone, DoError }, + { ccNone, DoExitMacro }, + { ccNone, DoExport }, + { ccNone, DoExportZP }, + { ccNone, DoFarAddr }, + { ccNone, DoFeature }, + { ccNone, DoFileOpt }, + { ccNone, DoGlobal }, + { ccNone, DoGlobalZP }, + { ccNone, DoI16 }, + { ccNone, DoI8 }, + { ccKeepToken, DoConditionals }, /* .IF */ + { ccKeepToken, DoConditionals }, /* .IFBLANK */ + { ccKeepToken, DoConditionals }, /* .IFCONST */ + { ccKeepToken, DoConditionals }, /* .IFDEF */ + { ccKeepToken, DoConditionals }, /* .IFNBLANK */ + { ccKeepToken, DoConditionals }, /* .IFNCONST */ + { ccKeepToken, DoConditionals }, /* .IFNDEF */ + { ccKeepToken, DoConditionals }, /* .IFNREF */ + { ccKeepToken, DoConditionals }, /* .IFP02 */ + { ccKeepToken, DoConditionals }, /* .IFP816 */ + { ccKeepToken, DoConditionals }, /* .IFPC02 */ + { ccKeepToken, DoConditionals }, /* .IFREF */ + { ccNone, DoImport }, + { ccNone, DoImportZP }, + { ccNone, DoIncBin }, + { ccNone, DoInclude }, + { ccNone, DoLineCont }, + { ccNone, DoList }, + { ccNone, DoListBytes }, + { ccNone, DoUnexpected }, /* .LOCAL */ + { ccNone, DoLocalChar }, + { ccNone, DoMacPack }, + { ccNone, DoMacro }, + { ccNone, DoUnexpected }, /* .MATCH */ + { ccNone, DoNull }, + { ccNone, DoOrg }, + { ccNone, DoOut }, + { ccNone, DoP02 }, + { ccNone, DoP816 }, + { ccNone, DoPageLength }, + { ccNone, DoUnexpected }, /* .PARAMCOUNT */ + { ccNone, DoPC02 }, + { ccNone, DoProc }, + { ccNone, DoUnexpected }, /* .REFERENCED */ + { ccNone, DoReloc }, + { ccNone, DoRepeat }, + { ccNone, DoRes }, + { ccNone, DoROData }, + { ccNone, DoSegment }, + { ccNone, DoSmart }, + { ccNone, DoUnexpected }, /* .STRING */ + { ccNone, DoSunPlus }, + { ccNone, DoWord }, + { ccNone, DoUnexpected }, /* .XMATCH */ + { ccNone, DoZeropage }, +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int TokIsPseudo (unsigned Tok) +/* Return true if the given token is a pseudo instruction token */ +{ + return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO); +} + + + +void HandlePseudo (void) +/* Handle a pseudo instruction */ +{ + CtrlDesc* D; + + /* Calculate the index into the table */ + unsigned Index = Tok - TOK_FIRSTPSEUDO; + + /* Safety check */ + if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) { + Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n", + PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1); + } + CHECK (Index < PSEUDO_COUNT); + + /* Get the pseudo intruction descriptor */ + D = &CtrlCmdTab [Index]; + + /* Remember the instruction, then skip it if needed */ + if ((D->Flags & ccKeepToken) == 0) { + strcpy (Keyword+1, SVal); + NextTok (); + } + + /* Call the handler */ + D->Handler (); +} + + + diff --git a/src/ca65/pseudo.h b/src/ca65/pseudo.h new file mode 100644 index 000000000..5f67b6e9a --- /dev/null +++ b/src/ca65/pseudo.h @@ -0,0 +1,74 @@ +/*****************************************************************************/ +/* */ +/* pseudo.h */ +/* */ +/* Pseudo instructions for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef PSEUDO_H +#define PSEUDO_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Are we inside a .IF condition that has been evaluated to TRUE? */ +extern unsigned char IfCond; + +/* How many .IFs are currently open? */ +extern unsigned OpenIfs; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int TokIsPseudo (unsigned Tok); +/* Return true if the given token is a pseudo instruction token */ + +void HandlePseudo (void); +/* Handle a pseudo instruction */ + + + +/* End of pseudo.h */ + +#endif + + + diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c new file mode 100644 index 000000000..8e5261f9d --- /dev/null +++ b/src/ca65/scanner.c @@ -0,0 +1,1202 @@ +/*****************************************************************************/ +/* */ +/* scanner.c */ +/* */ +/* The scanner for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include +#include +#include + +#include "condasm.h" +#include "error.h" +#include "fname.h" +#include "global.h" +#include "instr.h" +#include "listing.h" +#include "macro.h" +#include "mem.h" +#include "objfile.h" +#include "scanner.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +enum Token Tok = TOK_NONE; /* Current token */ +int WS; /* Flag: Whitespace before token */ +long IVal; /* Integer token attribute */ +char SVal [MAX_STR_LEN+1]; /* String token attribute */ + +FilePos CurPos = { 0, 0, 0 }; /* Name and position in current file */ + + + +/* Struct to handle include files. Note: The length of the input line may + * not exceed 255+1, since the column is stored in the file position struct + * as a character. Increasing this value means changing the FilePos struct, + * and the read and write routines in the assembler and linker. + */ +typedef struct InputFile_ InputFile; +struct InputFile_ { + FILE* F; /* Input file descriptor */ + FilePos Pos; /* Position in file */ + enum Token Tok; /* Last token */ + int C; /* Last character */ + char Line[256]; /* The current input line */ + InputFile* Next; /* Linked list of input files */ +}; + +/* Struct to handle textual input data */ +typedef struct InputData_ InputData; +struct InputData_ { + const char* Data; /* Pointer to the data */ + const char* Pos; /* Pointer to current position */ + int Malloced; /* Memory was malloced */ + enum Token Tok; /* Last token */ + int C; /* Last character */ + InputData* Next; /* Linked list of input data */ +}; + +/* List of input files */ +static struct { + unsigned long MTime; /* Time of last modification */ + unsigned long Size; /* Size of file */ + const char* Name; /* Name of file */ +} Files [MAX_INPUT_FILES]; +static unsigned FileCount = 0; + +/* Current input variables */ +static InputFile* IFile = 0; +static InputData* IData = 0; +static unsigned ICount = 0; /* Count of input files */ +static int C = 0; + +/* Force end of assembly */ +int ForcedEnd = 0; + +/* List of dot keywords with the corresponding tokens */ +struct DotKeyword { + const char* Key; /* MUST be first field */ + enum Token Tok; +} DotKeywords [] = { + { "A16", TOK_A16 }, + { "A8", TOK_A8 }, + { "ADDR", TOK_ADDR }, + { "ALIGN", TOK_ALIGN }, + { "AND", TOK_BAND }, + { "ASCIIZ", TOK_ASCIIZ }, + { "AUTOIMPORT", TOK_AUTOIMPORT }, + { "BITAND", TOK_AND }, + { "BITNOT", TOK_NOT }, + { "BITOR", TOK_OR }, + { "BITXOR", TOK_XOR }, + { "BLANK", TOK_BLANK }, + { "BSS", TOK_BSS }, + { "BYTE", TOK_BYTE }, + { "CASE", TOK_CASE }, + { "CODE", TOK_CODE }, + { "CONST", TOK_CONST }, + { "CPU", TOK_CPU }, + { "DATA", TOK_DATA }, + { "DBYT", TOK_DBYT }, + { "DEBUGINFO", TOK_DEBUGINFO }, + { "DEF", TOK_DEFINED }, + { "DEFINE", TOK_DEFINE }, + { "DEFINED", TOK_DEFINED }, + { "DWORD", TOK_DWORD }, + { "ELSE", TOK_ELSE }, + { "ELSEIF", TOK_ELSEIF }, + { "END", TOK_END }, + { "ENDIF", TOK_ENDIF }, + { "ENDMAC", TOK_ENDMACRO }, + { "ENDMACRO", TOK_ENDMACRO }, + { "ENDPROC", TOK_ENDPROC }, + { "ENDREP", TOK_ENDREP }, + { "ENDREPEAT", TOK_ENDREP }, + { "ERROR", TOK_ERROR }, + { "EXITMAC", TOK_EXITMACRO }, + { "EXITMACRO", TOK_EXITMACRO }, + { "EXPORT", TOK_EXPORT }, + { "EXPORTZP", TOK_EXPORTZP }, + { "FARADDR", TOK_FARADDR }, + { "FEATURE", TOK_FEATURE }, + { "FILEOPT", TOK_FILEOPT }, + { "FOPT", TOK_FILEOPT }, + { "GLOBAL", TOK_GLOBAL }, + { "GLOBALZP", TOK_GLOBALZP }, + { "I16", TOK_I16 }, + { "I8", TOK_I8 }, + { "IF", TOK_IF }, + { "IFBLANK", TOK_IFBLANK }, + { "IFCONST", TOK_IFCONST }, + { "IFDEF", TOK_IFDEF }, + { "IFNBLANK", TOK_IFNBLANK }, + { "IFNCONST", TOK_IFNCONST }, + { "IFNDEF", TOK_IFNDEF }, + { "IFNREF", TOK_IFNREF }, + { "IFP02", TOK_IFP02 }, + { "IFP816", TOK_IFP816 }, + { "IFPC02", TOK_IFPC02 }, + { "IFREF", TOK_IFREF }, + { "IMPORT", TOK_IMPORT }, + { "IMPORTZP", TOK_IMPORTZP }, + { "INCBIN", TOK_INCBIN }, + { "INCLUDE", TOK_INCLUDE }, + { "LINECONT", TOK_LINECONT }, + { "LIST", TOK_LIST }, + { "LISTBYTES", TOK_LISTBYTES }, + { "LOCAL", TOK_LOCAL }, + { "LOCALCHAR", TOK_LOCALCHAR }, + { "MAC", TOK_MACRO }, + { "MACPACK", TOK_MACPACK }, + { "MACRO", TOK_MACRO }, + { "MATCH", TOK_MATCH }, + { "MOD", TOK_MOD }, + { "NOT", TOK_BNOT }, + { "NULL", TOK_NULL }, + { "OR", TOK_BOR }, + { "ORG", TOK_ORG }, + { "OUT", TOK_OUT }, + { "P02", TOK_P02 }, + { "P816", TOK_P816 }, + { "PAGELEN", TOK_PAGELENGTH }, + { "PAGELENGTH", TOK_PAGELENGTH }, + { "PARAMCOUNT", TOK_PARAMCOUNT }, + { "PC02", TOK_PC02 }, + { "PROC", TOK_PROC }, + { "REF", TOK_REFERENCED }, + { "REFERENCED", TOK_REFERENCED }, + { "RELOC", TOK_RELOC }, + { "REPEAT", TOK_REPEAT }, + { "RES", TOK_RES }, + { "RODATA", TOK_RODATA }, + { "SEGMENT", TOK_SEGMENT }, + { "SHL", TOK_SHL }, + { "SHR", TOK_SHR }, + { "SMART", TOK_SMART }, + { "STRING", TOK_STRING }, + { "SUNPLUS", TOK_SUNPLUS }, + { "WORD", TOK_WORD }, + { "XMATCH", TOK_XMATCH }, + { "XOR", TOK_BXOR }, + { "ZEROPAGE", TOK_ZEROPAGE }, +}; + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static void NextChar (void); +/* Read the next character from the input file */ + + + +/*****************************************************************************/ +/* Character classification functions */ +/*****************************************************************************/ + + + +static int IsBlank (int C) +/* Return true if the character is a blank or tab */ +{ + return (C == ' ' || C == '\t'); +} + + + +static int IsDigit (int C) +/* Return true if the character is a digit */ +{ + return isdigit (C); +} + + + +static int IsXDigit (int C) +/* Return true if the character is a hexadecimal digit */ +{ + return isxdigit (C); +} + + + +static int IsDDigit (int C) +/* Return true if the character is a dual digit */ +{ + return (C == '0' || C == '1'); +} + + + +static int IsIdChar (int C) +/* Return true if the character is a valid character for an identifier */ +{ + return isalnum (C) || + (C == '_') || + (C == '@' && AtInIdents) || + (C == '$' && DollarInIdents); +} + + + +static int IsIdStart (int C) +/* Return true if the character may start an identifier */ +{ + return isalpha (C) || C == '_'; +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +const char* GetFileName (unsigned char Name) +/* Get the name of a file where the name index is known */ +{ + PRECONDITION (Name <= FileCount); + if (Name == 0) { + /* Name was defined outside any file scope, use the name of the first + * file instead. Errors are then reported with a file position of + * line zero in the first file. + */ + if (FileCount == 0) { + /* No files defined until now */ + return "(outside file scope)"; + } else { + return Files [0].Name; + } + } else { + return Files [Name-1].Name; + } +} + + + +void NewInputFile (const char* Name) +/* Open a new input file */ +{ + InputFile* I; + FILE* F; + + /* Insert a copy of the filename into the list */ + if (FileCount >= MAX_INPUT_FILES) { + Fatal (FAT_MAX_INPUT_FILES); + } + Files [FileCount].Name = StrDup (Name); + + /* First try to open the file */ + F = fopen (Name, "r"); + if (F == 0) { + + /* Error (fatal error if this is the main file) */ + if (ICount == 0) { + Fatal (FAT_CANNOT_OPEN_INPUT, Name, strerror (errno)); + } else { + Error (ERR_CANNOT_OPEN_INCLUDE, Name, strerror (errno)); + Xfree (Files [FileCount].Name); + } + + } else { + + /* Stat the file and remember the values */ + struct stat Buf; + if (fstat (fileno (F), &Buf) != 0) { + Fatal (FAT_CANNOT_STAT_INPUT, Name, strerror (errno)); + } + Files [FileCount].MTime = Buf.st_mtime; + Files [FileCount].Size = Buf.st_size; + ++FileCount; + + /* Create a new state variable and initialize it */ + I = Xmalloc (sizeof (*I)); + I->F = F; + I->Pos.Line = 0; + I->Pos.Col = 0; + I->Pos.Name = FileCount; + I->Tok = Tok; + I->C = C; + I->Line[0] = '\0'; + + /* Use the new file */ + I->Next = IFile; + IFile = I; + ++ICount; + + /* Prime the pump */ + NextChar (); + } +} + + + +void DoneInputFile (void) +/* Close the current input file */ +{ + InputFile* I; + + /* Restore the old token */ + Tok = IFile->Tok; + C = IFile->C; + + /* Save a pointer to the current struct, then set it back */ + I = IFile; + IFile = I->Next; + + /* Cleanup the current stuff */ + fclose (I->F); + Xfree (I); + --ICount; +} + + + +void NewInputData (const char* Data, int Malloced) +/* Add a chunk of input data to the input stream */ +{ + InputData* I; + + /* Create a new state variable and initialize it */ + I = Xmalloc (sizeof (*I)); + I->Data = Data; + I->Pos = Data; + I->Malloced = Malloced; + I->Tok = Tok; + I->C = C; + + /* Use the new data */ + I->Next = IData; + IData = I; + + /* Prime the pump */ + NextChar (); +} + + + +static void DoneInputData (void) +/* End the current input data stream */ +{ + InputData* I; + + /* Restore the old token */ + Tok = IData->Tok; + C = IData->C; + + /* Save a pointer to the current struct, then set it back */ + I = IData; + IData = I->Next; + + /* Cleanup the current stuff */ + if (I->Malloced) { + Xfree (I->Data); + } + Xfree (I); +} + + + +static unsigned DigitVal (unsigned char C) +/* Convert a digit into it's numerical representation */ +{ + if (IsDigit (C)) { + return C - '0'; + } else { + return toupper (C) - 'A' + 10; + } +} + + + +static void NextChar (void) +/* Read the next character from the input file */ +{ + /* If we have an input data structure, read from there */ + if (IData) { + + C = *IData->Pos++; + if (C == '\0') { + /* End of input data, will set to last file char */ + DoneInputData (); + } + + } else { + + /* Check for end of line, read the next line if needed */ + while (IFile->Line [IFile->Pos.Col] == '\0') { + + /* End of current line reached, read next line */ + if (fgets (IFile->Line, sizeof (IFile->Line), IFile->F) == 0) { + /* End of file. Add an empty line to the listing. This is a + * small hack needed to keep the PC output in sync. + */ + NewListingLine ("", IFile->Pos.Name, ICount); + C = EOF; + return; + } + + /* One more line */ + IFile->Pos.Line++; + IFile->Pos.Col = 0; + + /* Remember the new line for the listing */ + NewListingLine (IFile->Line, IFile->Pos.Name, ICount); + + } + + /* Return the next character from the file */ + C = IFile->Line [IFile->Pos.Col++]; + + } +} + + + +void UpcaseSVal (void) +/* Make SVal upper case */ +{ + unsigned I = 0; + while (SVal [I]) { + SVal [I] = toupper (SVal [I]); + ++I; + } +} + + + +static int CmpDotKeyword (const void* K1, const void* K2) +/* Compare function for the dot keyword search */ +{ + return strcmp (((struct DotKeyword*)K1)->Key, ((struct DotKeyword*)K2)->Key); +} + + + +static unsigned char FindDotKeyword (void) +/* Find the dot keyword in SVal. Return the corresponding token if found, + * return TOK_NONE if not found. + */ +{ + static const struct DotKeyword K = { SVal, 0 }; + struct DotKeyword* R; + + /* If we aren't in ignore case mode, we have to uppercase the keyword */ + if (!IgnoreCase) { + UpcaseSVal (); + } + + /* Search for the keyword */ + R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]), + sizeof (DotKeywords [0]), CmpDotKeyword); + if (R != 0) { + return R->Tok; + } else { + return TOK_NONE; + } +} + + + +static void ReadIdent (void) +/* Read an identifier from the current input position into Ident. It is + * assumed that the first character has already been checked. + */ +{ + /* Read the identifier */ + unsigned I = 0; + do { + if (I < MAX_STR_LEN) { + SVal [I++] = C; + } + NextChar (); + } while (IsIdChar (C)); + SVal [I] = '\0'; + + /* If we should ignore case, convert the identifier to upper case */ + if (IgnoreCase) { + UpcaseSVal (); + } +} + + + +static unsigned ReadStringConst (int StringTerm) +/* Read a string constant into SVal. Check for maximum string length and all + * other stuff. The length of the string is returned. + */ +{ + unsigned I; + + /* Skip the leading string terminator */ + NextChar (); + + /* Read the string */ + I = 0; + while (1) { + if (C == StringTerm) { + break; + } + if (C == '\n' || C == EOF) { + Error (ERR_NEWLINE_IN_STRING); + break; + } + + /* Check for string length, print an error message once */ + if (I == MAX_STR_LEN) { + Error (ERR_STRING_TOO_LONG); + } else if (I < MAX_STR_LEN) { + SVal [I] = C; + } + ++I; + + /* Skip the character */ + NextChar (); + } + + /* Skip the trailing terminator */ + NextChar (); + + /* Terminate the string */ + if (I >= MAX_STR_LEN) { + I = MAX_STR_LEN; + } + SVal [I] = '\0'; + + /* Return the length of the string */ + return I; +} + + + +void NextTok (void) +/* Read the next raw token from the input stream */ +{ + /* If we've a forced end of assembly, don't read further */ + if (ForcedEnd) { + Tok = TOK_EOF; + return; + } + + /* If we're expanding a macro, the tokens come from the macro expansion */ + if (MacExpand ()) { + return; + } + +Again: + /* Skip whitespace, remember if we had some */ + if ((WS = IsBlank (C)) != 0) { + do { + NextChar (); + } while (IsBlank (C)); + } + + /* If we're reading from the file, update the location from where the + * next token will be read. If we're reading from input data, keep the + * current position. + */ + if (IData == 0) { + CurPos = IFile->Pos; + } + + /* Hex number or PC symbol? */ + if (C == '$') { + NextChar (); + + /* Hex digit must follow or DollarIsPC must be enabled */ + if (!IsXDigit (C)) { + if (DollarIsPC) { + Tok = TOK_PC; + return; + } else { + Error (ERR_HEX_DIGIT_EXPECTED); + } + } + + /* Read the number */ + IVal = 0; + while (IsXDigit (C)) { + if (IVal & 0xF0000000) { + Error (ERR_NUM_OVERFLOW); + IVal = 0; + } + IVal = (IVal << 4) + DigitVal (C); + NextChar (); + } + + /* This is an integer constant */ + Tok = TOK_INTCON; + return; + } + + /* Dual number? */ + if (C == '%') { + NextChar (); + + /* 0 or 1 must follow */ + if (!IsDDigit (C)) { + Error (ERR_01_EXPECTED); + } + + /* Read the number */ + IVal = 0; + while (IsDDigit (C)) { + if (IVal & 0x80000000) { + Error (ERR_NUM_OVERFLOW); + IVal = 0; + } + IVal = (IVal << 1) + DigitVal (C); + NextChar (); + } + + /* This is an integer constant */ + Tok = TOK_INTCON; + return; + } + + /* Decimal number? */ + if (IsDigit (C)) { + + /* Read the number */ + IVal = 0; + while (IsDigit (C)) { + if (IVal > (0xFFFFFFFF / 10)) { + Error (ERR_NUM_OVERFLOW); + IVal = 0; + } + IVal = (IVal * 10) + DigitVal (C); + NextChar (); + } + + /* This is an integer constant */ + Tok = TOK_INTCON; + return; + } + + /* Control command? */ + if (C == '.') { + + NextChar (); + + if (!IsIdStart (C)) { + Error (ERR_PSEUDO_EXPECTED); + /* Try to read an identifier */ + goto Again; + } + + /* Read the identifier */ + ReadIdent (); + + /* Search the keyword */ + Tok = FindDotKeyword (); + if (Tok == TOK_NONE) { + /* Not found */ + Error (ERR_PSEUDO_EXPECTED); + goto Again; + } + return; + } + + /* Local symbol? */ + if (C == LocalStart) { + + /* Read the identifier */ + ReadIdent (); + + /* Start character alone is not enough */ + if (SVal [1] == '\0') { + Error (ERR_IDENT_EXPECTED); + goto Again; + } + + /* An identifier */ + Tok = TOK_IDENT; + return; + } + + + /* Identifier or keyword? */ + if (IsIdStart (C)) { + + /* Read the identifier */ + ReadIdent (); + + /* Check for special names */ + if (SVal [1] == '\0') { + switch (toupper (SVal [0])) { + + case 'A': + Tok = TOK_A; + return; + + case 'X': + Tok = TOK_X; + return; + + case 'Y': + Tok = TOK_Y; + return; + + case 'S': + Tok = TOK_S; + return; + + default: + Tok = TOK_IDENT; + return; + } + } + + /* Search for an opcode */ + IVal = FindInstruction (SVal); + if (IVal >= 0) { + /* This is a mnemonic */ + Tok = TOK_MNEMO; + } else if (IsDefine (SVal)) { + /* This is a define style macro - expand it */ + MacExpandStart (); + if (!MacExpand ()) { + goto Again; + } + } else { + /* An identifier */ + Tok = TOK_IDENT; + } + return; + } + + /* Ok, let's do the switch */ +CharAgain: + switch (C) { + + case '+': + NextChar (); + Tok = TOK_PLUS; + return; + + case '-': + NextChar (); + Tok = TOK_MINUS; + return; + + case '/': + NextChar (); + Tok = TOK_DIV; + return; + + case '*': + NextChar (); + Tok = TOK_MUL; + return; + + case '^': + NextChar (); + Tok = TOK_XOR; + return; + + case '&': + NextChar (); + if (C == '&') { + NextChar (); + Tok = TOK_BAND; + } else { + Tok = TOK_AND; + } + return; + + case '|': + NextChar (); + if (C == '|') { + NextChar (); + Tok = TOK_BOR; + } else { + Tok = TOK_OR; + } + return; + + case ':': + NextChar (); + switch (C) { + + case ':': + NextChar (); + Tok = TOK_NAMESPACE; + break; + + case '-': + IVal = 0; + do { + --IVal; + NextChar (); + } while (C == '-'); + Tok = TOK_ULABEL; + break; + + case '+': + IVal = 0; + do { + ++IVal; + NextChar (); + } while (C == '+'); + Tok = TOK_ULABEL; + break; + + default: + Tok = TOK_COLON; + break; + } + return; + + case ',': + NextChar (); + Tok = TOK_COMMA; + return; + + case ';': + NextChar (); + while (C != '\n' && C != EOF) { + NextChar (); + } + goto CharAgain; + + case '#': + NextChar (); + Tok = TOK_HASH; + return; + + case '(': + NextChar (); + Tok = TOK_LPAREN; + return; + + case ')': + NextChar (); + Tok = TOK_RPAREN; + return; + + case '[': + NextChar (); + Tok = TOK_LBRACK; + return; + + case ']': + NextChar (); + Tok = TOK_RBRACK; + return; + + case '<': + NextChar (); + if (C == '=') { + NextChar (); + Tok = TOK_LE; + } else if (C == '<') { + NextChar (); + Tok = TOK_SHL; + } else if (C == '>') { + NextChar (); + Tok = TOK_NE; + } else { + Tok = TOK_LT; + } + return; + + case '=': + NextChar (); + Tok = TOK_EQ; + return; + + case '!': + NextChar (); + Tok = TOK_BNOT; + return; + + case '>': + NextChar (); + if (C == '=') { + NextChar (); + Tok = TOK_GE; + } else if (C == '>') { + NextChar (); + Tok = TOK_SHR; + } else { + Tok = TOK_GT; + } + return; + + case '~': + NextChar (); + Tok = TOK_NOT; + return; + + case '\'': + /* Hack: If we allow ' as terminating character for strings, read + * the following stuff as a string, and check for a one character + * string later. + */ + if (LooseStringTerm) { + if (ReadStringConst ('\'') == 1) { + IVal = SVal[0]; + Tok = TOK_CHARCON; + } else { + Tok = TOK_STRCON; + } + } else { + /* Always a character constant */ + NextChar (); + if (C == '\n' || C == EOF) { + Error (ERR_ILLEGAL_CHARCON); + goto CharAgain; + } + IVal = C; + Tok = TOK_CHARCON; + NextChar (); + if (C != '\'') { + Error (ERR_ILLEGAL_CHARCON); + } else { + NextChar (); + } + } + return; + + case '\"': + ReadStringConst ('\"'); + Tok = TOK_STRCON; + return; + + case '\\': + /* Line continuation? */ + if (LineCont) { + NextChar (); + if (C == '\n') { + /* Handle as white space */ + NextChar (); + C = ' '; + goto Again; + } + } + break; + + case '\n': + NextChar (); + Tok = TOK_SEP; + return; + + case EOF: + /* Check if we have any open .IFs in this file */ + CheckOpenIfs (); + + /* If this was an include file, then close it and handle like a + * separator. Do not close the main file, but return EOF. + */ + if (ICount > 1) { + DoneInputFile (); + } else { + Tok = TOK_EOF; + } + return; + + } + + /* If we go here, we could not identify the current character. Skip it + * and try again. + */ + Error (ERR_INVALID_CHAR, C & 0xFF); + NextChar (); + goto Again; +} + + + +void Consume (enum Token Expected, unsigned ErrMsg) +/* Consume Expected, print an error if we don't find it */ +{ + if (Tok == Expected) { + NextTok (); + } else { + Error (ErrMsg); + } +} + + + +void ConsumeSep (void) +/* Consume a separator token */ +{ + /* Accept an EOF as separator */ + if (Tok != TOK_EOF) { + if (Tok != TOK_SEP) { + Error (ERR_TOO_MANY_CHARS); + SkipUntilSep (); + } else { + NextTok (); + } + } +} + + + +void ConsumeLParen (void) +/* Consume a left paren */ +{ + Consume (TOK_LPAREN, ERR_LPAREN_EXPECTED); +} + + + +void ConsumeRParen (void) +/* Consume a right paren */ +{ + Consume (TOK_RPAREN, ERR_RPAREN_EXPECTED); +} + + + +void ConsumeComma (void) +/* Consume a comma */ +{ + Consume (TOK_COMMA, ERR_COMMA_EXPECTED); +} + + + +void SkipUntilSep (void) +/* Skip tokens until we reach a line separator */ +{ + while (Tok != TOK_SEP && Tok != TOK_EOF) { + NextTok (); + } +} + + + +int TokHasSVal (enum Token Tok) +/* Return true if the given token has an attached SVal */ +{ + return (Tok == TOK_IDENT || Tok == TOK_STRCON); +} + + + +int TokHasIVal (enum Token Tok) +/* Return true if the given token has an attached IVal */ +{ + return (Tok == TOK_INTCON || Tok == TOK_CHARCON || Tok == TOK_MNEMO); +} + + + +int GetSubKey (const char** Keys, unsigned Count) +/* Search for a subkey in a table of keywords. The current token must be an + * identifier and all keys must be in upper case. The identifier will be + * uppercased in the process. The function returns the index of the keyword, + * or -1 if the keyword was not found. + */ +{ + unsigned I; + + /* Must have an identifier */ + PRECONDITION (Tok == TOK_IDENT); + + /* If we aren't in ignore case mode, we have to uppercase the identifier */ + if (!IgnoreCase) { + UpcaseSVal (); + } + + /* Do a linear search (a binary search is not worth the effort) */ + for (I = 0; I < Count; ++I) { + if (strcmp (SVal, Keys [I]) == 0) { + /* Found it */ + return I; + } + } + + /* Not found */ + return -1; +} + + + +void WriteFiles (void) +/* Write the list of input files to the object file */ +{ + unsigned I; + + /* Tell the obj file module that we're about to start the file list */ + ObjStartFiles (); + + /* Write the file count */ + ObjWrite8 (FileCount); + + /* Write the file data */ + for (I = 0; I < FileCount; ++I) { + ObjWrite32 (Files [I].MTime); + ObjWrite32 (Files [I].Size); + ObjWriteStr (Files [I].Name); + } + + /* Done writing files */ + ObjEndFiles (); +} + + + +void InitScanner (const char* InFile) +/* Initialize the scanner, open the given input file */ +{ + /* Open the input file */ + NewInputFile (InFile); +} + + + +void DoneScanner (void) +/* Release scanner resources */ +{ + DoneInputFile (); +} + + + diff --git a/src/ca65/scanner.h b/src/ca65/scanner.h new file mode 100644 index 000000000..813a54f76 --- /dev/null +++ b/src/ca65/scanner.h @@ -0,0 +1,283 @@ +/*****************************************************************************/ +/* */ +/* scanner.h */ +/* */ +/* The scanner for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SCANNER_H +#define SCANNER_H + + + +#include "../common/filepos.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Tokens */ +enum Token { + TOK_NONE, /* Start value, invalid */ + TOK_EOF, /* End of input file */ + TOK_SEP, /* Separator (usually newline) */ + TOK_IDENT, /* An identifier */ + TOK_MNEMO, /* A mnemonic */ + + TOK_INTCON, /* Integer constant */ + TOK_CHARCON, /* Character constant */ + TOK_STRCON, /* String constant */ + + TOK_A, /* A)ccu */ + TOK_X, /* X register */ + TOK_Y, /* Y register */ + TOK_S, /* S register */ + + TOK_ULABEL, /* :++ or :-- */ + + TOK_EQ, /* = */ + TOK_NE, /* <> */ + TOK_LT, /* < */ + TOK_GT, /* > */ + TOK_LE, /* <= */ + TOK_GE, /* >= */ + + TOK_BAND, /* .and */ + TOK_BOR, /* .or */ + TOK_BXOR, /* .xor */ + TOK_BNOT, /* .not */ + + TOK_PLUS, /* + */ + TOK_MINUS, /* - */ + TOK_MUL, /* * */ + TOK_STAR = TOK_MUL, /* Alias */ + TOK_DIV, /* / */ + TOK_MOD, /* ! */ + TOK_OR, /* | */ + TOK_XOR, /* ^ */ + TOK_AND, /* & */ + TOK_SHL, /* << */ + TOK_SHR, /* >> */ + TOK_NOT, /* ~ */ + + TOK_PC, /* $ if enabled */ + TOK_NAMESPACE, /* :: */ + TOK_DOT, /* . */ + TOK_COMMA, /* , */ + TOK_HASH, /* # */ + TOK_COLON, /* : */ + TOK_LPAREN, /* ( */ + TOK_RPAREN, /* ) */ + TOK_LBRACK, /* [ */ + TOK_RBRACK, /* ] */ + + TOK_MACPARAM, /* Macro parameter, not generated by scanner */ + + /* The next ones are tokens for the pseudo instructions. Keep together! */ + TOK_FIRSTPSEUDO, + TOK_A16 = TOK_FIRSTPSEUDO, + TOK_A8, + TOK_ADDR, + TOK_ALIGN, + TOK_ASCIIZ, + TOK_AUTOIMPORT, + TOK_BLANK, + TOK_BSS, + TOK_BYTE, + TOK_CASE, + TOK_CODE, + TOK_CONST, + TOK_CPU, + TOK_DATA, + TOK_DBYT, + TOK_DEBUGINFO, + TOK_DEFINE, + TOK_DEFINED, + TOK_DWORD, + TOK_ELSE, + TOK_ELSEIF, + TOK_END, + TOK_ENDIF, + TOK_ENDMACRO, + TOK_ENDPROC, + TOK_ENDREP, + TOK_ERROR, + TOK_EXITMACRO, + TOK_EXPORT, + TOK_EXPORTZP, + TOK_FARADDR, + TOK_FEATURE, + TOK_FILEOPT, + TOK_GLOBAL, + TOK_GLOBALZP, + TOK_I16, + TOK_I8, + TOK_IF, + TOK_IFBLANK, + TOK_IFCONST, + TOK_IFDEF, + TOK_IFNBLANK, + TOK_IFNCONST, + TOK_IFNDEF, + TOK_IFNREF, + TOK_IFP02, + TOK_IFP816, + TOK_IFPC02, + TOK_IFREF, + TOK_IMPORT, + TOK_IMPORTZP, + TOK_INCBIN, + TOK_INCLUDE, + TOK_LINECONT, + TOK_LIST, + TOK_LISTBYTES, + TOK_LOCAL, + TOK_LOCALCHAR, + TOK_MACPACK, + TOK_MACRO, + TOK_MATCH, + TOK_NULL, + TOK_ORG, + TOK_OUT, + TOK_P02, + TOK_P816, + TOK_PAGELENGTH, + TOK_PARAMCOUNT, + TOK_PC02, + TOK_PROC, + TOK_REFERENCED, + TOK_RELOC, + TOK_REPEAT, + TOK_RES, + TOK_RODATA, + TOK_SEGMENT, + TOK_SMART, + TOK_STRING, + TOK_SUNPLUS, + TOK_WORD, + TOK_XMATCH, + TOK_ZEROPAGE, + TOK_LASTPSEUDO = TOK_ZEROPAGE, + + TOK_COUNT /* Count of tokens */ +}; + + + +/* Scanner variables */ +#define MAX_INPUT_FILES 254 /* No more than this files total */ +#define MAX_STR_LEN 255 /* Maximum length of any string */ +extern enum Token Tok; /* Current token */ +extern int WS; /* Flag: Whitespace before token */ +extern long IVal; /* Integer token attribute */ +extern char SVal [MAX_STR_LEN+1]; /* String token attribute */ + +extern FilePos CurPos; /* Name and position in file */ +extern int ForcedEnd; /* Force end of assembly */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +const char* GetFileName (unsigned char Name); +/* Get the name of a file where the name index is known */ + +void NewInputFile (const char* Name); +/* Open a new input file */ + +void DoneInputFile (void); +/* Close the current input file */ + +void NewInputData (const char* Data, int Malloced); +/* Add a chunk of input data to the input stream */ + +void UpcaseSVal (void); +/* Make SVal upper case */ + +void NextTok (void); +/* Read the next token from the input stream */ + +void Consume (enum Token Expected, unsigned ErrMsg); +/* Consume Token, print an error if we don't find it */ + +void ConsumeSep (void); +/* Consume a separator token */ + +void ConsumeLParen (void); +/* Consume a left paren */ + +void ConsumeRParen (void); +/* Consume a right paren */ + +void ConsumeComma (void); +/* Consume a comma */ + +void SkipUntilSep (void); +/* Skip tokens until we reach a line separator */ + +int TokHasSVal (enum Token Tok); +/* Return true if the given token has an attached SVal */ + +int TokHasIVal (enum Token Tok); +/* Return true if the given token has an attached IVal */ + +int GetSubKey (const char** Keys, unsigned Count); +/* Search for a subkey in a table of keywords. The current token must be an + * identifier and all keys must be in upper case. The identifier will be + * uppercased in the process. The function returns the index of the keyword, + * or -1 if the keyword was not found. + */ + +void WriteFiles (void); +/* Write the list of input files to the object file */ + +void InitScanner (const char* InFile); +/* Initialize the scanner, open the given input file */ + +void DoneScanner (void); +/* Release scanner resources */ + + + +/* End of scanner.h */ + +#endif + + + diff --git a/src/ca65/strexpr.c b/src/ca65/strexpr.c new file mode 100644 index 000000000..e3d30baff --- /dev/null +++ b/src/ca65/strexpr.c @@ -0,0 +1,97 @@ +/*****************************************************************************/ +/* */ +/* strexpr.c */ +/* */ +/* String expressions for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "error.h" +#include "expr.h" +#include "scanner.h" +#include "strexpr.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +const char* StringExpression (void) +/* Evaluate a string expression. If there are no errors, the function will + * place the string into the token attribute buffer SVal and the token will + * be TOK_STRCON. A pointer to the buffer is returned. + * If there was an error, a NULL pointer is returned. + */ +{ + char Buf [sizeof (SVal)]; + + /* Check for a string constant or a function that returns a string */ + switch (Tok) { + + case TOK_STRING: + NextTok (); + ConsumeLParen (); + if (Tok == TOK_IDENT) { + /* Save the identifier, then skip it */ + strcpy (Buf, SVal); + NextTok (); + } else { + /* Numeric expression */ + long Val = ConstExpression (); + sprintf (Buf, "%ld", Val); + } + if (Tok != TOK_RPAREN) { + Error (ERR_RPAREN_EXPECTED); + } + /* Overwrite the token, do not skip it! */ + strcpy (SVal, Buf); + Tok = TOK_STRCON; + break; + + case TOK_STRCON: + /* We already have a string */ + break; + + default: + /* Error - no string constant */ + return 0; + } + + /* Return a pointer to the buffer */ + return SVal; +} + + + diff --git a/src/ca65/strexpr.h b/src/ca65/strexpr.h new file mode 100644 index 000000000..9457b9fe2 --- /dev/null +++ b/src/ca65/strexpr.h @@ -0,0 +1,61 @@ +/*****************************************************************************/ +/* */ +/* strexpr.h */ +/* */ +/* String expressions for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef STREXPR_H +#define STREXPR_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +const char* StringExpression (void); +/* Evaluate a string expression. If there are no errors, the function will + * place the string into the token attribute buffer SVal and the token will + * be TOK_STRCON. A pointer to the buffer is returned. + * If there was an error, a NULL pointer is returned. + */ + + + +/* End of strexpr.h */ + +#endif + + + diff --git a/src/ca65/symentry.h b/src/ca65/symentry.h new file mode 100644 index 000000000..bbc1c89ca --- /dev/null +++ b/src/ca65/symentry.h @@ -0,0 +1,57 @@ +/*****************************************************************************/ +/* */ +/* symentry.h */ +/* */ +/* Symbol table entry forward for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SYMENTRY_H +#define SYMENTRY_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Forward declaration for struct SymEntry */ +typedef struct SymEntry_ SymEntry; + + + +/* End of symentry.h */ + +#endif + + + diff --git a/src/ca65/symtab.c b/src/ca65/symtab.c new file mode 100644 index 000000000..53843eb1c --- /dev/null +++ b/src/ca65/symtab.c @@ -0,0 +1,1127 @@ +/*****************************************************************************/ +/* */ +/* symtab.c */ +/* */ +/* Symbol table for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "../common/symdefs.h" +#include "../common/hashstr.h" + +#include "global.h" +#include "error.h" +#include "mem.h" +#include "expr.h" +#include "objfile.h" +#include "symtab.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Bits for the Flags value in SymEntry */ +#define SF_USER 0x0001 /* User bit */ +#define SF_TRAMPOLINE 0x0002 /* Trampoline entry */ +#define SF_EXPORT 0x0004 /* Export this symbol */ +#define SF_IMPORT 0x0008 /* Import this symbol */ +#define SF_GLOBAL 0x0010 /* Global symbol */ +#define SF_ZP 0x0020 /* Declared as zeropage symbol */ +#define SF_ABS 0x0040 /* Declared as absolute symbol */ +#define SF_INDEXED 0x0800 /* Index is valid */ +#define SF_CONST 0x1000 /* The symbol has a constant value */ +#define SF_MULTDEF 0x2000 /* Multiply defined symbol */ +#define SF_DEFINED 0x4000 /* Defined */ +#define SF_REFERENCED 0x8000 /* Referenced */ + +/* Combined stuff */ +#define SF_UNDEFMASK (SF_REFERENCED | SF_DEFINED | SF_IMPORT) +#define SF_UNDEFVAL (SF_REFERENCED) +#define SF_IMPMASK (SF_TRAMPOLINE | SF_IMPORT | SF_REFERENCED) +#define SF_IMPVAL (SF_IMPORT | SF_REFERENCED) +#define SF_EXPMASK (SF_TRAMPOLINE | SF_EXPORT) +#define SF_EXPVAL (SF_EXPORT) +#define SF_DBGINFOMASK (SF_TRAMPOLINE | SF_DEFINED | SF_EXPORT | SF_IMPORT) +#define SF_DBGINFOVAL (SF_DEFINED) + + + +/* Structure of a symbol table entry */ +struct SymEntry_ { + SymEntry* Left; /* Lexically smaller entry */ + SymEntry* Right; /* Lexically larger entry */ + SymEntry* List; /* List of all entries */ + SymEntry* Locals; /* Root of subtree for local symbols */ + struct SymTable_* SymTab; /* Table this symbol is in, 0 for locals */ + FilePos Pos; /* File position for this symbol */ + unsigned Flags; /* Symbol flags */ + unsigned Index; /* Index of import/export entries */ + union { + struct ExprNode_* Expr; /* Expression if CONST not set */ + long Val; /* Value (if CONST set) */ + SymEntry* Sym; /* Symbol (if trampoline entry) */ + } V; + char Name [1]; /* Dynamic allocation */ +}; + + + +/* Definitions for the hash table */ +#define MAIN_HASHTAB_SIZE 213 +#define SUB_HASHTAB_SIZE 53 +typedef struct SymTable_ SymTable; +struct SymTable_ { + unsigned TableSlots; /* Number of hash table slots */ + unsigned TableEntries; /* Number of entries in the table */ + SymTable* BackLink; /* Link to enclosing scope if any */ + SymEntry* Table [1]; /* Dynamic allocation */ +}; + + + +/* Symbol table variables */ +static SymEntry* SymList = 0; /* List of all symbol table entries */ +static SymEntry* SymLast = 0; /* Pointer to last defined symbol */ +static SymTable* SymTab = 0; /* Pointer to current symbol table */ +static SymTable* RootTab = 0; /* Root symbol table */ +static unsigned ImportCount = 0;/* Counter for import symbols */ +static unsigned ExportCount = 0;/* Counter for export symbols */ + + + +/*****************************************************************************/ +/* Internally used functions */ +/*****************************************************************************/ + + + +static int IsLocal (const char* Name) +/* Return true if Name is the name of a local symbol */ +{ + return (*Name == LocalStart); +} + + + +static SymEntry* NewSymEntry (const char* Name) +/* Allocate a symbol table entry, initialize and return it */ +{ + SymEntry* S; + unsigned Len; + + /* Get the length of the name */ + Len = strlen (Name); + + /* Allocate memory */ + S = Xmalloc (sizeof (SymEntry) + Len); + + /* Initialize the entry */ + S->Left = 0; + S->Right = 0; + S->Locals = 0; + S->SymTab = 0; + S->Flags = 0; + S->V.Expr = 0; + S->Pos = CurPos; + memcpy (S->Name, Name, Len+1); + + /* Insert it into the list of all entries */ + S->List = SymList; + SymList = S; + + /* Return the initialized entry */ + return S; +} + + + +static SymTable* NewSymTable (unsigned Size) +/* Allocate a symbol table on the heap and return it */ +{ + SymTable* S; + + /* Allocate memory */ + S = Xmalloc (sizeof (SymTable) + (Size-1) * sizeof (SymEntry*)); + + /* Set variables and clear hash table entries */ + S->TableSlots = Size; + S->TableEntries = 0; + S->BackLink = 0; + while (Size--) { + S->Table [Size] = 0; + } + + /* Return the prepared struct */ + return S; +} + + + +static int SearchSymTab (SymEntry* T, const char* Name, SymEntry** E) +/* Search in the given table for a name (Hash is the hash value of Name and + * is given as parameter so that it will not get calculated twice if we search + * in more than one table). If we find the symbol, the function will return 0 + * and put the entry pointer into E. If we did not find the symbol, and the + * tree is empty, E is set to NULL. If the tree is not empty, E will be set to + * the last entry, and the result of the function is <0 if the entry should + * be inserted on the left side, and >0 if it should get inserted on the right + * side. + */ +{ + int Cmp; + + /* Is there a tree? */ + if (T == 0) { + *E = 0; + return 1; + } + + /* We have a table, search it */ + while (1) { + /* Choose next entry */ + Cmp = strcmp (Name, T->Name); + if (Cmp < 0 && T->Left) { + T = T->Left; + } else if (Cmp > 0 && T->Right) { + T = T->Right; + } else { + /* Found or end of search */ + break; + } + } + + /* Return the search result */ + *E = T; + return Cmp; +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static SymEntry* SymFind (SymTable* Tab, const char* Name, int AllocNew) +/* Find a new symbol table entry in the given table. If AllocNew is given and + * the entry is not found, create a new one. Return the entry found, or the + * new entry created, or - in case AllocNew is zero - return 0. + */ +{ + SymEntry* S; + int Cmp; + unsigned Hash; + + if (IsLocal (Name)) { + + /* Local symbol, get the table */ + if (!SymLast) { + /* No last global, so there's no local table */ + Error (ERR_ILLEGAL_LOCAL_USE); + if (AllocNew) { + return NewSymEntry (Name); + } else { + return 0; + } + } + + /* Search for the symbol if we have a table */ + Cmp = SearchSymTab (SymLast->Locals, Name, &S); + + /* If we found an entry, return it */ + if (Cmp == 0) { + return S; + } + + if (AllocNew) { + + /* Otherwise create a new entry, insert and return it */ + SymEntry* N = NewSymEntry (Name); + if (S == 0) { + SymLast->Locals = N; + } else if (Cmp < 0) { + S->Left = N; + } else { + S->Right = N; + } + return N; + } + + } else { + + /* Global symbol: Get the hash value for the name */ + Hash = HashStr (Name) % Tab->TableSlots; + + /* Search for the entry */ + Cmp = SearchSymTab (Tab->Table [Hash], Name, &S); + + /* If we found an entry, return it */ + if (Cmp == 0) { + /* Check for a trampoline entry, in this case return the real + * symbol. + */ + if (S->Flags & SF_TRAMPOLINE) { + return S->V.Sym; + } else { + return S; + } + } + + if (AllocNew) { + + /* Otherwise create a new entry, insert and return it */ + SymEntry* N = NewSymEntry (Name); + if (S == 0) { + Tab->Table [Hash] = N; + } else if (Cmp < 0) { + S->Left = N; + } else { + S->Right = N; + } + N->SymTab = Tab; + ++Tab->TableEntries; + return N; + + } + } + + /* We did not find the entry and AllocNew is false. */ + return 0; +} + + + +static SymEntry* SymFindAny (SymTable* Tab, const char* Name) +/* Find a symbol in any table */ +{ + SymEntry* Sym; + do { + /* Search in the current table */ + Sym = SymFind (Tab, Name, 0); + if (Sym) { + /* Found, return it */ + return Sym; + } else { + /* Not found, search in the backlink, if we have one */ + Tab = Tab->BackLink; + } + } while (Sym == 0 && Tab != 0); + + /* Not found */ + return 0; +} + + + +static SymEntry* SymRefInternal (SymTable* Table, const char* Name) +/* Search for the symbol in the given table and return it */ +{ + SymEntry* S; + + /* Try to find the symbol, create a new one if the symbol does not exist */ + S = SymFind (Table, Name, 1); + + /* Mark the symbol as referenced */ + S->Flags |= SF_REFERENCED; + + /* Return it */ + return S; +} + + + +void SymEnterLevel (void) +/* Enter a new lexical level */ +{ + if (RootTab == 0) { + /* Create the main symbol table */ + RootTab = SymTab = NewSymTable (MAIN_HASHTAB_SIZE); + } else { + /* Create a local symbol table */ + SymTable* LocalSyms; + LocalSyms = NewSymTable (SUB_HASHTAB_SIZE); + LocalSyms->BackLink = SymTab; + SymTab = LocalSyms; + } +} + + + +void SymLeaveLevel (void) +/* Leave the current lexical level */ +{ + SymTab = SymTab->BackLink; +} + + + +void SymDef (const char* Name, ExprNode* Expr, int ZP) +/* Define a new symbol */ +{ + SymEntry* S; + + /* Do we have such a symbol? */ + S = SymFind (SymTab, Name, 1); + if (S->Flags & SF_IMPORT) { + /* Defined symbol is marked as imported external symbol */ + Error (ERR_SYM_ALREADY_IMPORT); + return; + } + if (S->Flags & SF_DEFINED) { + /* Multiple definition */ + Error (ERR_SYM_ALREADY_DEFINED, Name); + S->Flags |= SF_MULTDEF; + return; + } + + /* Set the symbol data */ + if (IsConstExpr (Expr)) { + /* Expression is const, store the value */ + S->Flags |= SF_CONST; + S->V.Val = GetExprVal (Expr); + FreeExpr (Expr); + } else { + /* Not const, store the expression */ + S->V.Expr = Expr; + } + S->Flags |= SF_DEFINED; + if (ZP) { + S->Flags |= SF_ZP; + } + + /* If the symbol is a ZP symbol, check if the value is in correct range */ + if (S->Flags & SF_ZP) { + /* Already marked as ZP symbol by some means */ + if (!IsByteExpr (Expr)) { + Error (ERR_RANGE); + } + } + + /* If this is not a local symbol, remember it as the last global one */ + if (!IsLocal (Name)) { + SymLast = S; + } +} + + + +SymEntry* SymRef (const char* Name) +/* Search for the symbol and return it */ +{ + /* Reference the symbol in the current table */ + return SymRefInternal (SymTab, Name); +} + + + +SymEntry* SymRefGlobal (const char* Name) +/* Search for the symbol in the global namespace and return it */ +{ + /* Reference the symbol in the current table */ + return SymRefInternal (RootTab, Name); +} + + + +void SymImport (const char* Name, int ZP) +/* Mark the given symbol as an imported symbol */ +{ + SymEntry* S; + + /* Don't accept local symbols */ + if (IsLocal (Name)) { + Error (ERR_ILLEGAL_LOCAL_USE); + return; + } + + /* Do we have such a symbol? */ + S = SymFind (SymTab, Name, 1); + if (S->Flags & SF_DEFINED) { + Error (ERR_SYM_ALREADY_DEFINED, Name); + S->Flags |= SF_MULTDEF; + return; + } + if (S->Flags & SF_EXPORT) { + /* The symbol is already marked as exported symbol */ + Error (ERR_SYM_ALREADY_EXPORT); + return; + } + + /* If the symbol is marked as global, check the symbol size, then do + * silently remove the global flag + */ + if (S->Flags & SF_GLOBAL) { + if ((ZP != 0) != ((S->Flags & SF_ZP) != 0)) { + Error (ERR_SYM_REDECL_MISMATCH); + } + S->Flags &= ~SF_GLOBAL; + } + + /* Set the symbol data */ + S->Flags |= SF_IMPORT; + if (ZP) { + S->Flags |= SF_ZP; + } +} + + + +void SymExport (const char* Name, int ZP) +/* Mark the given symbol as an exported symbol */ +{ + SymEntry* S; + + /* Don't accept local symbols */ + if (IsLocal (Name)) { + Error (ERR_ILLEGAL_LOCAL_USE); + return; + } + + /* Do we have such a symbol? */ + S = SymFind (SymTab, Name, 1); + if (S->Flags & SF_IMPORT) { + /* The symbol is already marked as imported external symbol */ + Error (ERR_SYM_ALREADY_IMPORT); + return; + } + + /* If the symbol is marked as global, check the symbol size, then do + * silently remove the global flag + */ + if (S->Flags & SF_GLOBAL) { + if ((ZP != 0) != ((S->Flags & SF_ZP) != 0)) { + Error (ERR_SYM_REDECL_MISMATCH); + } + S->Flags &= ~SF_GLOBAL; + } + + /* Set the symbol data */ + S->Flags |= SF_EXPORT | SF_REFERENCED; + if (ZP) { + S->Flags |= SF_ZP; + } +} + + + +void SymGlobal (const char* Name, int ZP) +/* Mark the given symbol as a global symbol, that is, as a symbol that is + * either imported or exported. + */ +{ + SymEntry* S; + + /* Don't accept local symbols */ + if (IsLocal (Name)) { + Error (ERR_ILLEGAL_LOCAL_USE); + return; + } + + /* Search for this symbol, create a new entry if needed */ + S = SymFind (SymTab, Name, 1); + + /* If the symbol is already marked as import or export, check the + * size of the definition, then bail out. */ + if (S->Flags & SF_IMPORT || S->Flags & SF_EXPORT) { + if ((ZP != 0) != ((S->Flags & SF_ZP) != 0)) { + Error (ERR_SYM_REDECL_MISMATCH); + } + return; + } + + /* Mark the symbol */ + S->Flags |= SF_GLOBAL; + if (ZP) { + S->Flags |= SF_ZP; + } +} + + + +int SymIsDef (const char* Name) +/* Return true if the given symbol is already defined */ +{ + SymEntry* S = SymFindAny (SymTab, Name); + return S != 0 && (S->Flags & (SF_DEFINED | SF_IMPORT)) != 0; +} + + + +int SymIsRef (const char* Name) +/* Return true if the given symbol has been referenced */ +{ + SymEntry* S = SymFindAny (SymTab, Name); + return S != 0 && (S->Flags & SF_REFERENCED) != 0; +} + + + +int SymIsConst (SymEntry* S) +/* Return true if the given symbol has a constant value */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + /* Check for constness */ + if (S->Flags & SF_CONST) { + return 1; + } else if ((S->Flags & SF_DEFINED) && IsConstExpr (S->V.Expr)) { + /* Constant expression, remember the value */ + ExprNode* Expr = S->V.Expr; + S->Flags |= SF_CONST; + S->V.Val = GetExprVal (Expr); + FreeExpr (Expr); + return 1; + } + return 0; +} + + + +int SymIsZP (SymEntry* S) +/* Return true if the symbol is explicitly marked as zeropage symbol */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + /* If the symbol is not a global symbol, was not defined before, check the + * enclosing scope for a symbol with the same name, and return the ZP + * attribute of this symbol if we find one. + */ + if (!IsLocal (S->Name) && + (S->Flags & (SF_ZP | SF_ABS | SF_DEFINED | SF_IMPORT)) == 0 && + S->SymTab->BackLink != 0) { + + /* Try to find a symbol with the same name in the enclosing scope */ + SymEntry* E = SymFindAny (S->SymTab->BackLink, S->Name); + + /* If we found one, use the ZP flag */ + if (E && (E->Flags & SF_ZP) != 0) { + S->Flags |= SF_ZP; + } + } + + /* Check the ZP flag */ + return (S->Flags & SF_ZP) != 0; +} + + + +int SymIsImport (SymEntry* S) +/* Return true if the given symbol is marked as import */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + /* Check the import flag */ + return (S->Flags & SF_IMPORT) != 0; +} + + + +int SymHasExpr (SymEntry* S) +/* Return true if the given symbol has an associated expression */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + /* Check the expression */ + return ((S->Flags & SF_DEFINED) != 0 && + (S->Flags & SF_IMPORT) == 0 && + (S->Flags & SF_CONST) == 0); +} + + + +void SymFinalize (SymEntry* S) +/* Finalize a symbol expression if there is one */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + /* Check if we have an expression */ + if (SymHasExpr (S)) { + S->V.Expr = FinalizeExpr (S->V.Expr); + } +} + + + +void SymMarkUser (SymEntry* S) +/* Set a user mark on the specified symbol */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + /* Set the bit */ + S->Flags |= SF_USER; +} + + + +void SymUnmarkUser (SymEntry* S) +/* Remove a user mark from the specified symbol */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + /* Reset the bit */ + S->Flags &= ~SF_USER; +} + + + +int SymHasUserMark (SymEntry* S) +/* Return the state of the user mark for the specified symbol */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + /* Check the bit */ + return (S->Flags & SF_USER) != 0; +} + + + +long GetSymVal (SymEntry* S) +/* Return the symbol value */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + PRECONDITION ((S->Flags & SF_DEFINED) != 0 && (S->Flags & SF_CONST) != 0); + return S->V.Val; +} + + + +ExprNode* GetSymExpr (SymEntry* S) +/* Get the expression for a non-const symbol */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + + PRECONDITION (S != 0 && (S->Flags & SF_CONST) == 0); + return S->V.Expr; +} + + + +const char* GetSymName (SymEntry* S) +/* Return the name of the symbol */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + return S->Name; +} + + + +unsigned GetSymIndex (SymEntry* S) +/* Return the symbol index for the given symbol */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + PRECONDITION (S != 0 && (S->Flags & SF_INDEXED)); + return S->Index; +} + + + +const FilePos* GetSymPos (SymEntry* S) +/* Return the position of first occurence in the source for the given symbol */ +{ + /* Resolve trampoline entries */ + if (S->Flags & SF_TRAMPOLINE) { + S = S->V.Sym; + } + PRECONDITION (S != 0); + return &S->Pos; +} + + + +static void SymCheckUndefined (SymEntry* S) +/* Handle an undefined symbol */ +{ + /* Undefined symbol. It may be... + * + * - An undefined symbol in a nested lexical level. In this + * case, search for the symbol in the higher levels and + * make the entry a trampoline entry if we find one. + * + * - If the symbol is not found, it is a real undefined symbol. + * If the AutoImport flag is set, make it an import. If the + * AutoImport flag is not set, it's an error. + */ + SymEntry* Sym = 0; + if (S->SymTab) { + /* It's a global symbol, get the higher level table */ + SymTable* Tab = S->SymTab->BackLink; + while (Tab) { + Sym = SymFindAny (Tab, S->Name); + if (Sym) { + if (Sym->Flags & (SF_DEFINED | SF_IMPORT)) { + /* We've found a symbol in a higher level that is + * either defined in the source, or an import. + */ + break; + } else { + /* The symbol found is undefined itself. Look further */ + Tab = Sym->SymTab->BackLink; + } + } else { + /* No symbol found */ + break; + } + } + } + if (Sym) { + /* We found the symbol in a higher level. Make S a trampoline + * symbol. Beware: We have to transfer the symbol attributes to + * the real symbol and check for any conflicts. + */ + S->Flags |= SF_TRAMPOLINE; + S->V.Sym = Sym; + + /* Transfer the flags. Note: S may not be imported, since in that + * case it wouldn't be undefined. + */ + if (S->Flags & SF_EXPORT) { + if (Sym->Flags & SF_IMPORT) { + /* The symbol is already marked as imported external symbol */ + PError (&S->Pos, ERR_SYM_ALREADY_IMPORT); + } + Sym->Flags |= S->Flags & (SF_EXPORT | SF_ZP); + } + + /* Transfer the referenced flag */ + Sym->Flags |= (S->Flags & SF_REFERENCED); + + } else { + /* The symbol is definitely undefined */ + if (S->Flags & SF_EXPORT) { + /* We will not auto-import an export */ + PError (&S->Pos, ERR_EXPORT_UNDEFINED, S->Name); + } else { + if (AutoImport) { + /* Mark as import, will be indexed later */ + S->Flags |= SF_IMPORT; + } else { + /* Error */ + PError (&S->Pos, ERR_SYM_UNDEFINED, S->Name); + } + } + } +} + + + +void SymCheck (void) +/* Run through all symbols and check for anomalies and errors */ +{ + SymEntry* S; + + /* Check for open lexical levels */ + if (SymTab->BackLink != 0) { + Error (ERR_OPEN_PROC); + } + + /* First pass: Walk through all symbols, checking for undefined's and + * changing them to trampoline symbols or make them imports. + */ + S = SymList; + while (S) { + /* If the symbol is marked as global, mark it as export, if it is + * already defined, otherwise mark it as import. + */ + if (S->Flags & SF_GLOBAL) { + S->Flags &= ~SF_GLOBAL; + if (S->Flags & SF_DEFINED) { + S->Flags |= SF_EXPORT; + } else { + S->Flags |= SF_IMPORT; + } + } + + /* Handle undefined symbols */ + if ((S->Flags & SF_UNDEFMASK) == SF_UNDEFVAL) { + /* This is an undefined symbol. Handle it. */ + SymCheckUndefined (S); + } + + /* Next symbol */ + S = S->List; + } + + /* Second pass: Walk again through the symbols. Ignore undefined's, since + * we handled them in the last pass, and ignore trampoline symbols, since + * we handled them in the last pass, too. + */ + S = SymList; + while (S) { + if ((S->Flags & SF_TRAMPOLINE) == 0 && + (S->Flags & SF_UNDEFMASK) != SF_UNDEFVAL) { + if ((S->Flags & SF_DEFINED) != 0 && (S->Flags & SF_REFERENCED) == 0) { + /* Symbol was defined but never referenced */ + PWarning (&S->Pos, WARN_SYM_NOT_REFERENCED, S->Name); + } + if (S->Flags & SF_IMPORT) { + if ((S->Flags & SF_REFERENCED) == 0) { + /* Imported symbol is not referenced */ + PWarning (&S->Pos, WARN_IMPORT_NOT_REFERENCED, S->Name); + } else { + /* Give the import an index, count imports */ + S->Index = ImportCount++; + S->Flags |= SF_INDEXED; + } + } + if (S->Flags & SF_EXPORT) { + /* Give the export an index, count exports */ + S->Index = ExportCount++; + S->Flags |= SF_INDEXED; + } + } + + /* Next symbol */ + S = S->List; + } +} + + + +void SymDump (FILE* F) +/* Dump the symbol table */ +{ + SymEntry* S = SymList; + + while (S) { + /* Ignore trampoline symbols */ + if ((S->Flags & SF_TRAMPOLINE) != 0) { + printf ("%-24s %s %s %s %s %s\n", + S->Name, + (S->Flags & SF_DEFINED)? "DEF" : "---", + (S->Flags & SF_REFERENCED)? "REF" : "---", + (S->Flags & SF_IMPORT)? "IMP" : "---", + (S->Flags & SF_EXPORT)? "EXP" : "---", + (S->Flags & SF_ZP)? "ZP" : "--"); + } + /* Next symbol */ + S = S->List; + } +} + + + +void WriteImports (void) +/* Write the imports list to the object file */ +{ + SymEntry* S; + + /* Tell the object file module that we're about to start the imports */ + ObjStartImports (); + + /* Write the import count to the list */ + ObjWrite16 (ImportCount); + + /* Walk throught list and write all imports to the file */ + S = SymList; + while (S) { + if ((S->Flags & SF_IMPMASK) == SF_IMPVAL) { + if (S->Flags & SF_ZP) { + ObjWrite8 (IMP_ZP); + } else { + ObjWrite8 (IMP_ABS); + } + ObjWriteStr (S->Name); + ObjWritePos (&S->Pos); + } + S = S->List; + } + + /* Done writing imports */ + ObjEndImports (); +} + + + +void WriteExports (void) +/* Write the exports list to the object file */ +{ + SymEntry* S; + + /* Tell the object file module that we're about to start the exports */ + ObjStartExports (); + + /* Write the export count to the list */ + ObjWrite16 (ExportCount); + + /* Walk throught list and write all exports to the file */ + S = SymList; + while (S) { + if ((S->Flags & SF_EXPMASK) == SF_EXPVAL) { + unsigned char ExprMask; + + /* Finalize an associated expression if we have one */ + SymFinalize (S); + + /* Check if the symbol is const */ + ExprMask = (SymIsConst (S))? EXP_CONST : EXP_EXPR; + + /* Write the type */ + if (S->Flags & SF_ZP) { + ObjWrite8 (EXP_ZP | ExprMask); + } else { + ObjWrite8 (EXP_ABS | ExprMask); + } + ObjWriteStr (S->Name); + if (ExprMask == EXP_CONST) { + /* Constant value */ + ObjWrite32 (S->V.Val); + } else { + /* Expression involved */ + WriteExpr (S->V.Expr); + } + ObjWritePos (&S->Pos); + } + S = S->List; + } + + /* Done writing exports */ + ObjEndExports (); +} + + + +void WriteDbgSyms (void) +/* Write a list of all symbols to the object file */ +{ + unsigned Count; + SymEntry* S; + + /* Tell the object file module that we're about to start the debug info */ + ObjStartDbgSyms (); + + /* Check if debug info is requested */ + if (DbgSyms) { + + /* Walk through the list and count the symbols */ + Count = 0; + S = SymList; + while (S) { + if ((S->Flags & SF_DBGINFOMASK) == SF_DBGINFOVAL) { + ++Count; + } + S = S->List; + } + + /* Safety check */ + if (Count > 0xFFFF) { + Fatal (FAT_TOO_MANY_SYMBOLS); + } + + /* Write the symbol count to the list */ + ObjWrite16 (Count); + + /* Walk through list and write all symbols to the file */ + S = SymList; + while (S) { + if ((S->Flags & SF_DBGINFOMASK) == SF_DBGINFOVAL) { + unsigned char ExprMask; + + /* Finalize an associated expression if we have one */ + SymFinalize (S); + + /* Check if the symbol is const */ + ExprMask = (SymIsConst (S))? EXP_CONST : EXP_EXPR; + + /* Write the type */ + if (S->Flags & SF_ZP) { + ObjWrite8 (EXP_ZP | ExprMask); + } else { + ObjWrite8 (EXP_ABS | ExprMask); + } + ObjWriteStr (S->Name); + if (ExprMask == EXP_CONST) { + /* Constant value */ + ObjWrite32 (S->V.Val); + } else { + /* Expression involved */ + WriteExpr (S->V.Expr); + } + ObjWritePos (&S->Pos); + } + S = S->List; + } + + } else { + + /* No debug symbols */ + ObjWrite16 (0); + + } + + /* Done writing debug symbols */ + ObjEndDbgSyms (); +} + + + diff --git a/src/ca65/symtab.h b/src/ca65/symtab.h new file mode 100644 index 000000000..1d29ec8dd --- /dev/null +++ b/src/ca65/symtab.h @@ -0,0 +1,145 @@ +/*****************************************************************************/ +/* */ +/* symtab.h */ +/* */ +/* Symbol table for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SYMTAB_H +#define SYMTAB_H + + + +#include + +#include "../common/exprdefs.h" + +#include "symentry.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void SymEnterLevel (void); +/* Enter a new lexical level */ + +void SymLeaveLevel (void); +/* Leave the current lexical level */ + +void SymDef (const char* Name, ExprNode* Expr, int ZP); +/* Define a new symbol */ + +SymEntry* SymRef (const char* Name); +/* Search for the symbol and return it */ + +SymEntry* SymRefGlobal (const char* Name); +/* Search for the symbol in the global namespace and return it */ + +int SymIsDef (const char* Name); +/* Return true if the given symbol is already defined */ + +int SymIsRef (const char* Name); +/* Return true if the given symbol has been referenced */ + +void SymImport (const char* Name, int ZP); +/* Mark the given symbol as an imported symbol */ + +void SymExport (const char* Name, int ZP); +/* Mark the given symbol as an exported symbol */ + +void SymGlobal (const char* Name, int ZP); +/* Mark the given symbol as a global symbol, that is, as a symbol that is + * either imported or exported. + */ + +int SymIsConst (SymEntry* Sym); +/* Return true if the given symbol has a constant value */ + +int SymIsZP (SymEntry* Sym); +/* Return true if the symbol is explicitly marked as zeropage symbol */ + +int SymIsImport (SymEntry* Sym); +/* Return true if the given symbol is marked as import */ + +int SymHasExpr (SymEntry* Sym); +/* Return true if the given symbol has an associated expression */ + +void SymMarkUser (SymEntry* Sym); +/* Set a user mark on the specified symbol */ + +void SymUnmarkUser (SymEntry* Sym); +/* Remove a user mark from the specified symbol */ + +int SymHasUserMark (SymEntry* Sym); +/* Return the state of the user mark for the specified symbol */ + +long GetSymVal (SymEntry* Sym); +/* Return the symbol value */ + +ExprNode* GetSymExpr (SymEntry* Sym); +/* Get the expression for a non-const symbol */ + +const char* GetSymName (SymEntry* Sym); +/* Return the name of the symbol */ + +unsigned GetSymIndex (SymEntry* Sym); +/* Return the symbol index for the given symbol */ + +const FilePos* GetSymPos (SymEntry* Sym); +/* Return the position of first occurence in the source for the given symbol */ + +void SymCheck (void); +/* Run through all symbols and check for anomalies and errors */ + +void SymDump (FILE* F); +/* Dump the symbol table */ + +void WriteImports (void); +/* Write the imports list to the object file */ + +void WriteExports (void); +/* Write the exports list to the object file */ + +void WriteDbgSyms (void); +/* Write a list of all symbols to the object file */ + + + +/* End of symtab.h */ + +#endif + + + diff --git a/src/ca65/toknode.c b/src/ca65/toknode.c new file mode 100644 index 000000000..b1eeb8b64 --- /dev/null +++ b/src/ca65/toknode.c @@ -0,0 +1,117 @@ +/*****************************************************************************/ +/* */ +/* toknode.c */ +/* */ +/* Token list node for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "mem.h" +#include "scanner.h" +#include "toknode.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +TokNode* NewTokNode (void) +/* Create and return a token node with the current token value */ +{ + TokNode* T; + + /* Allocate memory */ + unsigned Len = TokHasSVal (Tok)? strlen (SVal) : 0; + T = Xmalloc (sizeof (TokNode) + Len); + + /* Initialize the token contents */ + T->Next = 0; + T->Tok = Tok; + T->WS = WS; + T->IVal = IVal; + memcpy (T->SVal, SVal, Len); + T->SVal [Len] = '\0'; + + /* Return the node */ + return T; +} + + + +void FreeTokNode (TokNode* T) +/* Free the given token node */ +{ + Xfree (T); +} + + + +void TokSet (TokNode* T) +/* Set the scanner token from the given token node */ +{ + /* Set the values */ + Tok = T->Tok; + WS = T->WS; + IVal = T->IVal; + strcpy (SVal, T->SVal); +} + + + +enum TC TokCmp (const TokNode* T) +/* Compare the token given as parameter against the current token */ +{ + if (T->Tok != Tok) { + /* Different token */ + return tcDifferent; + } + + /* If the token has string attribute, check it */ + if (TokHasSVal (T->Tok)) { + if (strcmp (T->SVal, SVal) != 0) { + return tcSameToken; + } + } else if (TokHasIVal (T->Tok)) { + if (T->IVal != IVal) { + return tcSameToken; + } + } + + /* Tokens are identical */ + return tcIdentical; +} + + + diff --git a/src/ca65/toknode.h b/src/ca65/toknode.h new file mode 100644 index 000000000..e3580e2aa --- /dev/null +++ b/src/ca65/toknode.h @@ -0,0 +1,94 @@ +/*****************************************************************************/ +/* */ +/* toknode.h */ +/* */ +/* Token list node for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef TOKNODE_H +#define TOKNODE_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Struct holding a token */ +typedef struct TokNode_ TokNode; +struct TokNode_ { + TokNode* Next; /* For single linked list */ + enum Token Tok; /* Token value */ + int WS; /* Whitespace before token? */ + long IVal; /* Integer token attribute */ + char SVal [1]; /* String attribute, dyn. allocated */ +}; + + + +/* Return codes for TokCmp - higher numeric code means better match */ +enum TC { + tcDifferent, /* Different tokents */ + tcSameToken, /* Same token, different attribute */ + tcIdentical /* Identical (token + attribute) */ +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +TokNode* NewTokNode (void); +/* Create and return a token node with the current token value */ + +void FreeTokNode (TokNode* T); +/* Free the given token node */ + +void TokSet (TokNode* T); +/* Set the scanner token from the given token node */ + +enum TC TokCmp (const TokNode* T); +/* Compare the token given as parameter against the current token */ + + + +/* End of toknode.h */ + +#endif + + + + diff --git a/src/ca65/ulabel.c b/src/ca65/ulabel.c new file mode 100644 index 000000000..adfe2e5f1 --- /dev/null +++ b/src/ca65/ulabel.c @@ -0,0 +1,242 @@ +/*****************************************************************************/ +/* */ +/* ulabel.c */ +/* */ +/* Unnamed labels for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "../common/filepos.h" + +#include "error.h" +#include "expr.h" +#include "mem.h" +#include "scanner.h" +#include "ulabel.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Struct that describes an unnamed label */ +typedef struct ULabel_ ULabel; +struct ULabel_ { + ULabel* Prev; /* Pointer to previous node in list */ + ULabel* Next; /* Pointer to next node in list */ + FilePos Pos; /* Position of the label in the source */ + ExprNode* Val; /* The label value - may be NULL */ +}; + +/* List management */ +static ULabel* ULabRoot = 0; /* Root of the list */ +static ULabel* ULabLast = 0; /* Last ULabel */ +static ULabel* ULabLastDef = 0; /* Last defined ULabel */ +static unsigned ULabCount = 0; /* Number of labels */ +static unsigned ULabDefCount = 0; /* Number of defined labels */ +static ULabel** ULabList = 0; /* Array with pointers to all labels */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static ULabel* NewULabel (ExprNode* Val) +/* Create a new ULabel and insert it into the list. The function will move + * ULabelLast, but not ULabelLastDef. The created label structure is returned. + */ +{ + /* Allocate memory for the ULabel structure */ + ULabel* L = Xmalloc (sizeof (ULabel)); + + /* Initialize the fields */ + L->Pos = CurPos; + L->Val = Val; + + /* Insert the label into the list */ + L->Next = 0; + if (ULabRoot == 0) { + /* First label */ + L->Prev = 0; + ULabRoot = L; + } else { + ULabLast->Next = L; + L->Prev = ULabLast; + } + ULabLast = L; + + /* One label more */ + ++ULabCount; + + /* Return the created label */ + return L; +} + + + +ExprNode* ULabRef (int Which) +/* Get an unnamed label. If Which is negative, it is a backreference (a + * reference to an already defined label), and the function will return a + * segment relative expression. If Which is positive, it is a forward ref, + * and the function will return a expression node for an unnamed label that + * must be resolved later. + */ +{ + ULabel* L; + + /* Which can never be 0 */ + PRECONDITION (Which != 0); + + /* Which is never really big (usually -3..+3), so a linear search is + * the best we can do here. + */ + L = ULabLastDef; + if (Which < 0) { + /* Backward reference */ + while (Which < -1 && L != 0) { + L = L->Prev; + ++Which; + } + if (L == 0) { + /* Label does not exist */ + Error (ERR_UNDEFINED_LABEL); + /* We must return something valid */ + return CurrentPC(); + } else { + /* Return a copy of the label value */ + return CloneExpr (L->Val); + } + } else { + /* Forward reference. Create labels as needed */ + unsigned LabelNum = ULabDefCount + Which - 1; + while (Which > 0) { + if (L->Next == 0) { + NewULabel (0); + } + L = L->Next; + --Which; + } + /* Return an unnamed label expression */ + return ULabelExpr (LabelNum); + } +} + + + +void ULabDef (void) +/* Define an unnamed label at the current PC */ +{ + /* Create a new label if needed, or use an existing one */ + if (ULabLastDef == 0 || ULabLastDef->Next == 0) { + /* The last label is also the last defined label, we need a new one */ + ULabLastDef = NewULabel (CurrentPC ()); + } else { + /* We do already have the label, but it's undefined until now */ + ULabLastDef = ULabLastDef->Next; + ULabLastDef->Val = CurrentPC (); + ULabLastDef->Pos = CurPos; + } + ++ULabDefCount; +} + + + +int ULabCanResolve (void) +/* Return true if we can resolve arbitrary ULabels. */ +{ + /* We can resolve labels if we have built the necessary access array */ + return (ULabList != 0); +} + + + +ExprNode* ULabResolve (unsigned Index) +/* Return a valid expression for the unnamed label with the given index. This + * is used to resolve unnamed labels when assembly is done, so it is an error + * if a label is still undefined in this phase. + */ +{ + ULabel* L; + + /* Must be in resolve phase and the index must be valid */ + CHECK (ULabList != 0 && Index < ULabCount); + + /* Get the label */ + L = ULabList [Index]; + + /* If the label is open (not defined), return some valid value */ + if (L->Val == 0) { + return LiteralExpr (0); + } else { + return CloneExpr (L->Val); + } +} + + + +void ULabCheck (void) +/* Run through all unnamed labels and check for anomalies and errors */ +{ + ULabel* L; + + /* Check if there are undefined labels */ + if (ULabLastDef) { + L = ULabLastDef->Next; + while (L) { + PError (&L->Pos, ERR_UNDEFINED_LABEL); + L = L->Next; + } + } + + /* Create an array that holds pointers to all labels. This allows us to + * access the labels quickly by index in the resolver phase at the end of + * the assembly. + */ + if (ULabCount) { + unsigned I = 0; + ULabList = Xmalloc (ULabCount * sizeof (ULabel*)); + L = ULabRoot; + while (L) { + ULabList[I] = L; + ++I; + L = L->Next; + } + CHECK (I == ULabCount); + } +} + + + diff --git a/src/ca65/ulabel.h b/src/ca65/ulabel.h new file mode 100644 index 000000000..ea2e46767 --- /dev/null +++ b/src/ca65/ulabel.h @@ -0,0 +1,77 @@ +/*****************************************************************************/ +/* */ +/* ulabel.h */ +/* */ +/* Unnamed labels for the ca65 macroassembler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ULABEL_H +#define ULABEL_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +ExprNode* ULabRef (int Which); +/* Get an unnamed label. If Which is negative, it is a backreference (a + * reference to an already defined label), and the function will return a + * segment relative expression. If Which is positive, it is a forward ref, + * and the function will return a expression node for an unnamed label that + * must be resolved later. + */ + +void ULabDef (void); +/* Define an unnamed label at the current PC */ + +int ULabCanResolve (void); +/* Return true if we can resolve arbitrary ULabels. */ + +ExprNode* ULabResolve (unsigned Index); +/* Return a valid expression for the unnamed label with the given index. This + * is used to resolve unnamed labels when assembly is done, so it is an error + * if a label is still undefined in this phase. + */ + +void ULabCheck (void); +/* Run through all unnamed labels and check for anomalies and errors */ + + + +/* End of ulabel.h */ + +#endif + + + diff --git a/src/cc65/.cvsignore b/src/cc65/.cvsignore new file mode 100644 index 000000000..a1b64faf0 --- /dev/null +++ b/src/cc65/.cvsignore @@ -0,0 +1,29 @@ +.depend +.kdbgrc.cc65 +cc65 +*.com +*.map +xopt +predent +postdent +code-gen.m65 +glb.m65 +cc64 +error.m65 +expr1.m65 +expr2.m65 +save +expr3.m65 +function.m65 +globlvar.m65 +io.m65 +lexer.m65 +main.m65 +optab1.m65 +optab2.m65 +optimize.m65 +preproc.m65 +rwords.m65 +stmt1.m65 +stmt2.m65 +symtab.m65 diff --git a/src/cc65/anonname.c b/src/cc65/anonname.c new file mode 100644 index 000000000..1a52147e2 --- /dev/null +++ b/src/cc65/anonname.c @@ -0,0 +1,59 @@ +/*****************************************************************************/ +/* */ +/* anonname.c */ +/* */ +/* Create names for anonymous variables/types */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "anonname.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +char* AnonName (char* Buf, const char* Spec) +/* Get a name for an anonymous variable or type. The given buffer is expected + * to be IDENTSIZE characters long. A pointer to the buffer is returned. + */ +{ + static unsigned ACount = 0; + sprintf (Buf, "$anon-%s-%04X", Spec, ++ACount); + return Buf; +} + + + diff --git a/src/cc65/anonname.h b/src/cc65/anonname.h new file mode 100644 index 000000000..3f833f0ee --- /dev/null +++ b/src/cc65/anonname.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +/* */ +/* anonname.h */ +/* */ +/* Create names for anonymous variables/types */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ANONNAME_H +#define ANONNAME_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +char* AnonName (char* Buf, const char* Spec); +/* Get a name for an anonymous variable or type. The given buffer is expected + * to be IDENTSIZE characters long. A pointer to the buffer is returned. + */ + + + +/* End of anonname.h */ +#endif + + + diff --git a/src/cc65/asmcode.c b/src/cc65/asmcode.c new file mode 100644 index 000000000..b62d36dcd --- /dev/null +++ b/src/cc65/asmcode.c @@ -0,0 +1,115 @@ +/*****************************************************************************/ +/* */ +/* asmcode.c */ +/* */ +/* Assembler output code handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "asmline.h" +#include "check.h" +#include "global.h" +#include "asmcode.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void AddCodeLine (const char* Format, ...) +/* Add a new line of code to the output */ +{ + va_list ap; + va_start (ap, Format); + NewCodeLine (Format, ap); + va_end (ap); +} + + + +void AddCodeHint (const char* Hint) +/* Add an optimizer hint */ +{ + AddCodeLine ("+%s", Hint); +} + + + +void AddEmptyLine (void) +/* Add an empty line for formatting purposes */ +{ + AddCodeLine (""); +} + + + +CodeMark GetCodePos (void) +/* Get a marker pointing to the current output position */ +{ + /* This function should never be called without any code output */ + CHECK (LastLine != 0); + + return LastLine; +} + + + +void RemoveCode (CodeMark M) +/* Remove all code after the given code marker */ +{ + while (LastLine != M) { + FreeCodeLine (LastLine); + } +} + + + +void WriteOutput (FILE* F) +/* Write the final output to a file */ +{ + Line* L = FirstLine; + while (L) { + /* Don't write optimizer hints if not requested to do so */ + if (L->Line[0] == '+') { + if (Debug) { + fprintf (F, ";%s\n", L->Line); + } + } else { + fprintf (F, "%s\n", L->Line); + } + L = L->Next; + } +} + + + diff --git a/src/cc65/asmcode.h b/src/cc65/asmcode.h new file mode 100644 index 000000000..69652c21a --- /dev/null +++ b/src/cc65/asmcode.h @@ -0,0 +1,86 @@ +/*****************************************************************************/ +/* */ +/* asmcode.h */ +/* */ +/* Assembler output code handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ASMCODE_H +#define ASMCODE_H + + + +#include + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Marker for an assembler code position */ +typedef struct Line_* CodeMark; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void AddCodeLine (const char* Format, ...); +/* Add a new line of code to the output */ + +void AddCodeHint (const char* Hint); +/* Add an optimizer hint */ + +void AddEmptyLine (void); +/* Add an empty line for formatting purposes */ + +CodeMark GetCodePos (void); +/* Get a marker pointing to the current output position */ + +void RemoveCode (CodeMark M); +/* Remove all code after the given code marker */ + +void WriteOutput (FILE* F); +/* Write the final output to a file */ + + + +/* End of asmcode.h */ +#endif + + + diff --git a/src/cc65/asmlabel.c b/src/cc65/asmlabel.c new file mode 100644 index 000000000..a18cf1c76 --- /dev/null +++ b/src/cc65/asmlabel.c @@ -0,0 +1,55 @@ +/*****************************************************************************/ +/* */ +/* asmlabel.c */ +/* */ +/* Generate assembler code labels */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "asmlabel.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned GetLabel (void) +/* Get an unused label. Will never return zero. */ +{ + /* Number to generate unique labels */ + static unsigned NextLabel = 0; + return ++NextLabel; +} + + + diff --git a/src/cc65/asmlabel.h b/src/cc65/asmlabel.h new file mode 100644 index 000000000..9dbc6f989 --- /dev/null +++ b/src/cc65/asmlabel.h @@ -0,0 +1,56 @@ +/*****************************************************************************/ +/* */ +/* asmlabel.h */ +/* */ +/* Generate assembler code labels */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ASMLABEL_H +#define ASMLABEL_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned GetLabel (void); +/* Get an unused assembler label. Will never return zero. */ + + + +/* End of asmlabel.h */ +#endif + + + diff --git a/src/cc65/asmline.c b/src/cc65/asmline.c new file mode 100644 index 000000000..5559e880e --- /dev/null +++ b/src/cc65/asmline.c @@ -0,0 +1,184 @@ +/*****************************************************************************/ +/* */ +/* asmline.h */ +/* */ +/* Internal assembler line structure */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "error.h" +#include "mem.h" +#include "asmline.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Number used to index lines */ +static unsigned long LineIndex = 0; + +/* The line list */ +Line* FirstLine = 0; /* Pointer to first line */ +Line* LastLine = 0; /* Pointer to last line */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static Line* NewLine (const char* Format, va_list ap) +/* Interal routine to create a new line from the given text */ +{ + char Buf [8192]; + int OVF; + unsigned Len; + Line* L; + + + /* Make a string from the given format and arguments */ +#if defined(__WATCOMC__) + OVF = (_vbprintf (Buf, sizeof (Buf), Format, ap) >= sizeof (S)); +#else + /* Assume gcc running on a Unix OS */ + OVF = (vsnprintf (Buf, sizeof (Buf), Format, ap) < 0); +#endif + if (OVF) { + Internal ("String size overflow"); + } + + /* Get the length of the line */ + Len = strlen (Buf); + + /* Allocate memory */ + L = xmalloc (sizeof (Line) + Len); + + /* Partially initialize the struct (the remaining fields are initialized + * by the caller). + */ + L->Flags = 0; + L->Size = 0; + L->Len = Len; + memcpy (L->Line, Buf, Len+1); + + /* Return the new line */ + return L; +} + + + +Line* NewCodeLine (const char* Format, va_list ap) +/* Create a new code line and return it */ +{ + /* Create a new line struct */ + Line* L = NewLine (Format, ap); + + /* Initialize struct fields */ + L->Index = LineIndex++; + + /* Insert the line into the list */ + if (FirstLine == 0) { + /* The list is empty */ + L->Next = L->Prev = 0; + FirstLine = LastLine = L; + } else { + /* There are entries in the list, add the new one at the end */ + LastLine->Next = L; + L->Prev = LastLine; + L->Next = 0; + LastLine = L; + } + + /* Return the new line */ + return L; +} + + + +Line* NewCodeLineAfter (Line* LineBefore, const char* Format, va_list ap) +/* Create a new line, insert it after L and return it. */ +{ + /* Create a new line struct */ + Line* L = NewLine (Format, ap); + + /* Initialize struct fields. We use the same index for the inserted line + * as for its predecessor, since we cannot create new numbers on the + * fly and the index is only used to determine sort order. + */ + L->Index = LineBefore->Index; + + /* Insert the line after its predecessor */ + L->Next = LineBefore->Next; + LineBefore->Next = L; + L->Prev = LineBefore; + if (L->Next) { + L->Next->Prev = L; + } else { + /* This is the last line */ + LastLine = L; + } + + /* Return the new line */ + return L; +} + + + +void FreeCodeLine (Line* L) +/* Remove a line from the list and free it */ +{ + /* Unlink the line */ + if (L->Prev == 0) { + /* No line before this one */ + FirstLine = L->Next; + } else { + L->Prev->Next = L->Next; + } + if (L->Next == 0) { + /* No line after this one */ + LastLine = L->Prev; + } else { + L->Next->Prev = L->Prev; + } + + /* Free the struct */ + xfree (L); +} + + + diff --git a/src/cc65/asmline.h b/src/cc65/asmline.h new file mode 100644 index 000000000..bfec110bf --- /dev/null +++ b/src/cc65/asmline.h @@ -0,0 +1,90 @@ +/*****************************************************************************/ +/* */ +/* asmline.h */ +/* */ +/* Internal assembler line structure */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ASMLINE_H +#define ASMLINE_H + + + +#include + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Structure that contains one line */ +typedef struct Line_ Line; +struct Line_ { + Line* Next; /* Next line on double linked list */ + Line* Prev; /* Revious line in list */ + unsigned Flags; /* Flags for this line */ + unsigned long Index; /* Index of this line */ + unsigned Size; /* Size of this code */ + unsigned Len; /* Length of the line */ + char Line [1]; /* The line itself */ +}; + +/* The line list */ +extern Line* FirstLine; /* Pointer to first line */ +extern Line* LastLine; /* Pointer to last line */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +Line* NewCodeLine (const char* Format, va_list ap); +/* Create a new code line and return it */ + +Line* NewCodeLineAfter (Line* LineBefore, const char* Format, va_list ap); +/* Create a new line, insert it after L and return it. */ + +void FreeCodeLine (Line* L); +/* Remove a line from the list and free it */ + + + +/* End of asmline.h */ +#endif + + + diff --git a/src/cc65/check.c b/src/cc65/check.c new file mode 100644 index 000000000..596eb79b9 --- /dev/null +++ b/src/cc65/check.c @@ -0,0 +1,102 @@ +/*****************************************************************************/ +/* */ +/* check.c */ +/* */ +/* Assert macros for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "error.h" +#include "check.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Predefined messages */ +const char* _MsgInternalError = "Internal error: "; +const char* _MsgAbstractCall = "Call to abstract method"; +const char* _MsgPrecondition = "Precondition violated: "; +const char* _MsgCheckFailed = "Check failed: "; +const char* _MsgProgramAborted = "Program aborted: "; + + + +static void _CheckFailed (const char* msg, const char* cond, + int code, const char* file, unsigned line); + +void (*CheckFailed) (const char* Msg, const char* Cond, int Code, + const char* File, unsigned Line) = _CheckFailed; +/* Function pointer that is called from check if the condition code is true. */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void _CheckFailed (const char* Msg, const char* Cond, + int Code, const char* File, unsigned Line) +{ + /* Log the error */ + if (Code) { + Internal ("%s%s (= %d), file `%s', line %u", Msg, Cond, Code, File, Line); + } else { + Internal ("%s%s, file `%s', line %u", Msg, Cond, File, Line); + } + + /* Use abort() to create a core for debugging */ + abort (); +} + + + +void Check (const char* Msg, const char* Cond, int Code, + const char* File, unsigned Line) +/* This function is called from all check macros (see below). It checks, + * wether the given Code is true (!= 0). If so, it calls the CheckFailed + * vector with the given strings. If not, it simply returns. + */ +{ + if (Code != 0) { + CheckFailed (Msg, Cond, Code, File, Line); + } +} + + + diff --git a/src/cc65/check.h b/src/cc65/check.h new file mode 100644 index 000000000..e650092c8 --- /dev/null +++ b/src/cc65/check.h @@ -0,0 +1,96 @@ +/*****************************************************************************/ +/* */ +/* check.h */ +/* */ +/* Assert macros for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef CHECK_H +#define CHECK_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +extern const char* _MsgInternalError; /* "Internal error: " */ +extern const char* _MsgPrecondition; /* "Precondition violated: " */ +extern const char* _MsgCheckFailed; /* "Check failed: " */ +extern const char* _MsgProgramAborted; /* "Program aborted: " */ + + + +extern void (*CheckFailed) (const char* Msg, const char* Cond, + int Code, const char* File, unsigned Line); +/* Function pointer that is called from check if the condition code is true. */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Check (const char* Msg, const char* Cond, int Code, + const char* File, unsigned Line); +/* This function is called from all check macros (see below). It checks, + * wether the given Code is true (!= 0). If so, it calls the CheckFailed + * vector with the given strings. If not, it simply returns. + */ + + + +#define FAIL(s) CheckFailed (_MsgInternalError, s, 0, __FILE__, __LINE__) +/* Fail macro. Is used if something evil happens, calls checkfailed directly. */ + +#define ABORT(s) CheckFailed (_MsgProgramAborted, s, 0, __FILE__, __LINE__) +/* Use this one instead of FAIL if there is no internal program error but an + * error condition that is caused by the user or operating system (FAIL and + * ABORT are essentially the same but the message differs). + */ + +#define PRECONDITION(c) Check (_MsgPrecondition, #c, !(c), __FILE__, __LINE__) + +#define CHECK(c) Check (_MsgCheckFailed, #c, !(c), __FILE__, __LINE__) + +#define ZCHECK(c) Check (_MsgCheckFailed, #c, c, __FILE__, __LINE__) + + + +/* End of check.h */ +#endif + + + diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c new file mode 100644 index 000000000..b805898f1 --- /dev/null +++ b/src/cc65/codegen.c @@ -0,0 +1,3712 @@ +/* + * codegen.c + * + * Ullrich von Bassewitz, 08.06.1998 + */ + + + +#include +#include + +#include "../common/version.h" + +#include "asmcode.h" +#include "asmlabel.h" +#include "check.h" +#include "error.h" +#include "global.h" +#include "io.h" +#include "litpool.h" +#include "mem.h" +#include "optimize.h" +#include "util.h" +#include "codegen.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Compiler relative stk ptr */ +int oursp = 0; + +/* Current segment */ +static enum { SEG_CODE, SEG_RODATA, SEG_DATA, SEG_BSS } CurSeg = SEG_CODE; + +/* Segment names */ +static char* SegmentNames [4]; +static char* SegmentHints [4] = { + "seg:code", "seg:rodata", "seg:data", "seg:bss" +}; + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +static void typeerror (unsigned type) +/* Print an error message about an invalid operand type */ +{ + Internal ("Invalid type in CF flags: %04X, type = %u", type, type & CF_TYPE); +} + + + +static void CheckLocalOffs (unsigned Offs) +/* Check the offset into the stack for 8bit range */ +{ + if (Offs >= 256) { + /* Too many local vars */ + AddCodeLine (";*** Too many locals"); + Error (ERR_TOO_MANY_LOCALS); + } +} + + + +static char* GetLabelName (unsigned flags, unsigned long label, unsigned offs) +{ + static char lbuf [128]; /* Label name */ + + /* Create the correct label name */ + switch (flags & CF_ADDRMASK) { + + case CF_STATIC: + /* Static memory cell */ + sprintf (lbuf, "L%04X+%u", (unsigned)(label & 0xFFFF), offs); + break; + + case CF_EXTERNAL: + /* External label */ + sprintf (lbuf, "_%s+%u", (char*) label, offs); + break; + + case CF_ABSOLUTE: + /* Absolute address */ + sprintf (lbuf, "$%04X", (unsigned)((label+offs) & 0xFFFF)); + break; + + case CF_REGVAR: + /* Variable in register bank */ + sprintf (lbuf, "regbank+%u", (unsigned)((label+offs) & 0xFFFF)); + break; + + default: + Internal ("Invalid address flags"); + } + + /* Return a pointer to the static buffer */ + return lbuf; +} + + + +/*****************************************************************************/ +/* Pre- and postamble */ +/*****************************************************************************/ + + + +void g_preamble (void) +/* Generate the assembler code preamble */ +{ + AddCodeLine ("; File generated by cc65 v %u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH); + AddEmptyLine (); + + /* Insert some object file options */ + AddCodeLine (".fopt\t\tcompiler,\"cc65 v %u.%u.%u\"", VER_MAJOR, VER_MINOR, VER_PATCH); + AddEmptyLine (); + + /* Allow auto import for runtime library routines */ + AddCodeLine (".autoimport\ton"); + + /* Switch the assembler into case sensible mode */ + AddCodeLine (".case\t\ton"); + + /* Tell the assembler if we want to generate debug info */ + AddCodeLine (".debuginfo\t%s", (DebugInfo != 0)? "on" : "off"); + + /* Import the stack pointer for direct auto variable access */ + AddCodeLine (".importzp\tsp, sreg, regsave, regbank, tmp1, ptr1"); + + /* Define long branch macros */ + AddCodeLine (".macpack\tlongbranch"); + AddEmptyLine (); + + /* Define the ldax macro */ + AddCodeLine (".macro ldax Value"); + AddCodeLine (" lda #<(Value)"); + AddCodeLine (" ldx #>(Value)"); + AddCodeLine (".endmacro"); + AddEmptyLine (); + + /* Define the default names for the segments */ + SegmentNames [SEG_CODE] = xstrdup ("CODE"); + SegmentNames [SEG_RODATA] = xstrdup ("RODATA"); + SegmentNames [SEG_DATA] = xstrdup ("DATA"); + SegmentNames [SEG_BSS] = xstrdup ("BSS"); + + /* Tell the optimizer that this is the end of the preamble */ + AddCodeHint ("end_of_preamble"); +} + + + +void g_postamble (void) +/* Generate assembler code postamble */ +{ + /* Tell the optimizer that this is the start of the postamble */ + AddCodeHint ("start_of_postamble"); +} + + + +/*****************************************************************************/ +/* Segment support */ +/*****************************************************************************/ + + + +static void UseSeg (unsigned NewSeg) +/* Switch to a specific segment */ +{ + if (CurSeg != NewSeg) { + CurSeg = NewSeg; + AddCodeLine (".segment\t\"%s\"", SegmentNames [CurSeg]); + AddCodeHint (SegmentHints [CurSeg]); + } +} + + + +void g_usecode (void) +/* Switch to the code segment */ +{ + UseSeg (SEG_CODE); +} + + + +void g_userodata (void) +/* Switch to the read only data segment */ +{ + UseSeg (SEG_RODATA); +} + + + +void g_usedata (void) +/* Switch to the data segment */ +{ + UseSeg (SEG_DATA); +} + + + +void g_usebss (void) +/* Switch to the bss segment */ +{ + UseSeg (SEG_BSS); +} + + + +void SegName (unsigned Seg, const char* Name) +/* Set the name of a segment */ +{ + /* Free the old name and set a new one */ + xfree (SegmentNames [Seg]); + SegmentNames [Seg] = xstrdup (Name); + + /* If the new segment is the current segment, emit a segment directive + * with the new name. + */ + if (Seg == CurSeg) { + CurSeg = -1; /* Invalidate */ + UseSeg (Seg); + } +} + + + +void g_codename (const char* Name) +/* Set the name of the CODE segment */ +{ + SegName (SEG_CODE, Name); +} + + + +void g_rodataname (const char* Name) +/* Set the name of the RODATA segment */ +{ + SegName (SEG_RODATA, Name); +} + + + +void g_dataname (const char* Name) +/* Set the name of the DATA segment */ +{ + SegName (SEG_DATA, Name); +} + + + +void g_bssname (const char* Name) +/* Set the name of the BSS segment */ +{ + SegName (SEG_BSS, Name); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned sizeofarg (unsigned flags) +/* Return the size of a function argument type that is encoded in flags */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + return (flags & CF_FORCECHAR)? 1 : 2; + + case CF_INT: + return 2; + + case CF_LONG: + return 4; + + default: + typeerror (flags); + /* NOTREACHED */ + return 2; + } +} + + + +int pop (unsigned flags) +/* Pop an argument of the given size */ +{ + return oursp += sizeofarg (flags); +} + + + +int push (unsigned flags) +/* Push an argument of the given size */ +{ + return oursp -= sizeofarg (flags); +} + + + +static unsigned MakeByteOffs (unsigned Flags, unsigned Offs) +/* The value in Offs is an offset to an address in a/x. Make sure, an object + * of the type given in Flags can be loaded or stored into this address by + * adding part of the offset to the address in ax, so that the remaining + * offset fits into an index register. Return the remaining offset. + */ +{ + /* If the offset is too large for a byte register, add the high byte + * of the offset to the primary. Beware: We need a special correction + * if the offset in the low byte will overflow in the operation. + */ + unsigned O = Offs & ~0xFFU; + if ((Offs & 0xFF) > 256 - sizeofarg (Flags)) { + /* We need to add the low byte also */ + O += Offs & 0xFF; + } + + /* Do the correction if we need one */ + if (O != 0) { + g_inc (CF_INT | CF_CONST, O); + Offs -= O; + } + + /* Return the new offset */ + return Offs; +} + + + +/*****************************************************************************/ +/* Functions handling local labels */ +/*****************************************************************************/ + + + +void g_defloclabel (unsigned label) +/* Define a local label */ +{ + AddCodeLine ("L%04X:", label & 0xFFFF); +} + + + +/*****************************************************************************/ +/* Functions handling global labels */ +/*****************************************************************************/ + + + +void g_defgloblabel (const char* Name) +/* Define a global label with the given name */ +{ + AddCodeLine ("_%s:", Name); +} + + + +void g_defexport (const char* Name, int ZP) +/* Export the given label */ +{ + if (ZP) { + AddCodeLine ("\t.exportzp\t_%s", Name); + } else { + AddCodeLine ("\t.export\t\t_%s", Name); + } +} + + + +void g_defimport (const char* Name, int ZP) +/* Import the given label */ +{ + if (ZP) { + AddCodeLine ("\t.importzp\t_%s", Name); + } else { + AddCodeLine ("\t.import\t\t_%s", Name); + } +} + + + +/*****************************************************************************/ +/* Load functions for various registers */ +/*****************************************************************************/ + + + +static void ldaconst (unsigned val) +/* Load a with a constant */ +{ + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); +} + + + +static void ldxconst (unsigned val) +/* Load x with a constant */ +{ + AddCodeLine ("\tldx\t#$%02X", val & 0xFF); +} + + + +static void ldyconst (unsigned val) +/* Load y with a constant */ +{ + AddCodeLine ("\tldy\t#$%02X", val & 0xFF); +} + + + +/*****************************************************************************/ +/* Function entry and exit */ +/*****************************************************************************/ + + + +/* Remember the argument size of a function. The variable is set by g_enter + * and used by g_leave. If the functions gets its argument size by the caller + * (variable param list or function without prototype), g_enter will set the + * value to -1. + */ +static int funcargs; + + +void g_enter (unsigned flags, const char* Name, unsigned argsize) +/* Function prologue */ +{ + g_defgloblabel (Name); /* Define function name as label */ + if ((flags & CF_FIXARGC) != 0) { + /* Just remember the argument size for the leave */ + funcargs = argsize; + } else { + funcargs = -1; + AddCodeLine ("\tjsr\tenter"); + } +} + + + +void g_leave (int flags, int val) +/* Function epilogue */ +{ + int k; + char buf [40]; + + /* How many bytes of locals do we have to drop? */ + k = -oursp; + + /* If we didn't have a variable argument list, don't call leave */ + if (funcargs >= 0) { + + /* Load a function return code if needed */ + if ((flags & CF_CONST) != 0) { + g_getimmed (flags, val, 0); + } + + /* Drop stackframe or leave with rts */ + k += funcargs; + if (k == 0) { + AddCodeLine ("\trts"); + } else if (k <= 8) { + AddCodeLine ("\tjmp\tincsp%d", k); + } else { + CheckLocalOffs (k); + ldyconst (k); + AddCodeLine ("\tjmp\taddysp"); + } + + } else { + + strcpy (buf, "\tjmp\tleave"); + if (k) { + /* We've a stack frame to drop */ + ldyconst (k); + strcat (buf, "y"); + } + if (flags & CF_CONST) { + if ((flags & CF_TYPE) != CF_LONG) { + /* Constant int sized value given for return code */ + if (val == 0) { + /* Special case: return 0 */ + strcat (buf, "00"); + } else if (((val >> 8) & 0xFF) == 0) { + /* Special case: constant with high byte zero */ + ldaconst (val); /* Load low byte */ + strcat (buf, "0"); + } else { + /* Others: arbitrary constant value */ + g_getimmed (flags, val, 0); /* Load value */ + } + } else { + /* Constant long value: No shortcut possible */ + g_getimmed (flags, val, 0); + } + } + + /* Output the jump */ + AddCodeLine (buf); + } + + /* Add an empty line after a function to make the code more readable */ + AddEmptyLine (); +} + + + +/*****************************************************************************/ +/* Register variables */ +/*****************************************************************************/ + + + +void g_save_regvars (int RegOffs, unsigned Bytes) +/* Save register variables */ +{ + /* Don't loop for up to two bytes */ + if (Bytes == 1) { + + AddCodeLine ("\tlda\tregbank%+d", RegOffs); + AddCodeLine ("\tjsr\tpusha"); + + } else if (Bytes == 2) { + + AddCodeLine ("\tlda\tregbank%+d", RegOffs); + AddCodeLine ("\tldx\tregbank%+d", RegOffs+1); + AddCodeLine ("\tjsr\tpushax"); + + } else { + + /* More than two bytes - loop */ + unsigned Label = GetLabel (); + g_space (Bytes); + ldyconst (Bytes - 1); + ldxconst (Bytes); + g_defloclabel (Label); + AddCodeLine ("\tlda\tregbank%+d,x", RegOffs-1); + AddCodeLine ("\tsta\t(sp),y"); + AddCodeLine ("\tdey"); + AddCodeLine ("\tdex"); + AddCodeLine ("\tbne\tL%04X", Label); + + } + + /* We pushed stuff, correct the stack pointer */ + oursp -= Bytes; +} + + + +void g_restore_regvars (int StackOffs, int RegOffs, unsigned Bytes) +/* Restore register variables */ +{ + /* Calculate the actual stack offset and check it */ + StackOffs -= oursp; + CheckLocalOffs (StackOffs); + + /* Don't loop for up to two bytes */ + if (Bytes == 1) { + + ldyconst (StackOffs); + AddCodeLine ("\tlda\t(sp),y"); + AddCodeLine ("\tsta\tregbank%+d", RegOffs); + + } else if (Bytes == 2) { + + ldyconst (StackOffs); + AddCodeLine ("\tlda\t(sp),y"); + AddCodeLine ("\tsta\tregbank%+d", RegOffs); + AddCodeLine ("\tiny"); + AddCodeLine ("\tlda\t(sp),y"); + AddCodeLine ("\tsta\tregbank%+d", RegOffs+1); + + } else { + + /* More than two bytes - loop */ + unsigned Label = GetLabel (); + ldyconst (StackOffs+Bytes-1); + ldxconst (Bytes); + g_defloclabel (Label); + AddCodeLine ("\tlda\t(sp),y"); + AddCodeLine ("\tsta\tregbank%+d,x", RegOffs-1); + AddCodeLine ("\tdey"); + AddCodeLine ("\tdex"); + AddCodeLine ("\tbne\tL%04X", Label); + + } +} + + + +/*****************************************************************************/ +/* Fetching memory cells */ +/*****************************************************************************/ + + + +void g_getimmed (unsigned flags, unsigned long val, unsigned offs) +/* Load a constant into the primary register */ +{ + if ((flags & CF_CONST) != 0) { + + /* Numeric constant */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if ((flags & CF_FORCECHAR) != 0) { + ldaconst (val); + break; + } + /* FALL THROUGH */ + case CF_INT: + ldxconst ((val >> 8) & 0xFF); + ldaconst (val & 0xFF); + break; + + case CF_LONG: + if (val < 0x100) { + AddCodeLine ("\tldx\t#$00"); + AddCodeLine ("\tstx\tsreg+1"); + AddCodeLine ("\tstx\tsreg"); + AddCodeLine ("\tlda\t#$%02X", (unsigned char) val); + } else if ((val & 0xFFFF00FF) == 0) { + AddCodeLine ("\tlda\t#$00"); + AddCodeLine ("\tsta\tsreg+1"); + AddCodeLine ("\tsta\tsreg"); + AddCodeLine ("\tldx\t#$%02X", (unsigned char) (val >> 8)); + } else if ((val & 0xFFFF0000) == 0 && FavourSize == 0) { + AddCodeLine ("\tlda\t#$00"); + AddCodeLine ("\tsta\tsreg+1"); + AddCodeLine ("\tsta\tsreg"); + AddCodeLine ("\tlda\t#$%02X", (unsigned char) val); + AddCodeLine ("\tldx\t#$%02X", (unsigned char) (val >> 8)); + } else if ((val & 0xFFFFFF00) == 0xFFFFFF00) { + AddCodeLine ("\tldx\t#$FF"); + AddCodeLine ("\tstx\tsreg+1"); + AddCodeLine ("\tstx\tsreg"); + if ((val & 0xFF) == 0xFF) { + AddCodeLine ("\ttxa"); + } else { + AddCodeLine ("\tlda\t#$%02X", (unsigned char) val); + } + } else if ((val & 0xFFFF00FF) == 0xFFFF00FF) { + AddCodeLine ("\tlda\t#$FF"); + AddCodeLine ("\tsta\tsreg+1"); + AddCodeLine ("\tsta\tsreg"); + AddCodeLine ("\tldx\t#$%02X", (unsigned char) (val >> 8)); + } else { + /* Call a subroutine that will load following value */ + AddCodeLine ("\tjsr\tldeax"); + AddCodeLine ("\t.dword\t$%08lX", val & 0xFFFFFFFF); + } + break; + + default: + typeerror (flags); + break; + + } + + } else { + + /* Some sort of label */ + const char* Label = GetLabelName (flags, val, offs); + + /* Load the address into the primary */ + AddCodeLine ("\tldax\t%s", Label); + + } +} + + + +void g_getstatic (unsigned flags, unsigned long label, unsigned offs) +/* Fetch an static memory cell into the primary register */ +{ + /* Create the correct label name */ + char* lbuf = GetLabelName (flags, label, offs); + + /* Check the size and generate the correct load operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if ((flags & CF_FORCECHAR) || (flags & CF_TEST)) { + AddCodeLine ("\tlda\t%s", lbuf); /* load A from the label */ + } else { + ldxconst (0); + AddCodeLine ("\tlda\t%s", lbuf); /* load A from the label */ + if (!(flags & CF_UNSIGNED)) { + /* Must sign extend */ + AddCodeLine ("\tbpl\t*+3"); + AddCodeLine ("\tdex"); + AddCodeHint ("x:!"); /* X is invalid now */ + } + } + break; + + case CF_INT: + AddCodeLine ("\tlda\t%s", lbuf); + if (flags & CF_TEST) { + AddCodeLine ("\tora\t%s+1", lbuf); + } else { + AddCodeLine ("\tldx\t%s+1", lbuf); + } + break; + + case CF_LONG: + if (flags & CF_TEST) { + AddCodeLine ("\tlda\t%s+3", lbuf); + AddCodeLine ("\tora\t%s+2", lbuf); + AddCodeLine ("\tora\t%s+1", lbuf); + AddCodeLine ("\tora\t%s+0", lbuf); + } else { + AddCodeLine ("\tlda\t%s+3", lbuf); + AddCodeLine ("\tsta\tsreg+1"); + AddCodeLine ("\tlda\t%s+2", lbuf); + AddCodeLine ("\tsta\tsreg"); + AddCodeLine ("\tldx\t%s+1", lbuf); + AddCodeLine ("\tlda\t%s", lbuf); + } + break; + + default: + typeerror (flags); + + } +} + + + +void g_getlocal (unsigned flags, int offs) +/* Fetch specified local object (local var). */ +{ + offs -= oursp; + CheckLocalOffs (offs); + switch (flags & CF_TYPE) { + + case CF_CHAR: + if ((flags & CF_FORCECHAR) || (flags & CF_TEST)) { + ldyconst (offs); + AddCodeLine ("\tlda\t(sp),y"); + } else { + if (offs == 0) { + AddCodeLine ("\tldx\t#$00"); + AddCodeLine ("\tlda\t(sp,x)"); + } else { + ldyconst (offs); + AddCodeLine ("\tldx\t#$00"); + AddCodeLine ("\tlda\t(sp),y"); + } + if ((flags & CF_UNSIGNED) == 0) { + AddCodeLine ("\tbpl\t*+3"); + AddCodeLine ("\tdex"); + AddCodeHint ("x:!"); /* X is invalid now */ + } + } + break; + + case CF_INT: + CheckLocalOffs (offs + 1); + if (flags & CF_TEST) { + ldyconst (offs + 1); + AddCodeLine ("\tlda\t(sp),y"); + AddCodeLine ("\tdey"); + AddCodeLine ("\tora\t(sp),y"); + } else { + if (FavourSize) { + if (offs) { + ldyconst (offs+1); + AddCodeLine ("\tjsr\tldaxysp"); + } else { + AddCodeLine ("\tjsr\tldax0sp"); + } + } else { + ldyconst (offs + 1); + AddCodeLine ("\tlda\t(sp),y"); + AddCodeLine ("\ttax"); + AddCodeLine ("\tdey"); + AddCodeLine ("\tlda\t(sp),y"); + } + } + break; + + case CF_LONG: + if (offs) { + ldyconst (offs+3); + AddCodeLine ("\tjsr\tldeaxysp"); + } else { + AddCodeLine ("\tjsr\tldeax0sp"); + } + break; + + default: + typeerror (flags); + } +} + + + +void g_getind (unsigned flags, unsigned offs) +/* Fetch the specified object type indirect through the primary register + * into the primary register + */ +{ + /* If the offset is greater than 255, add the part that is > 255 to + * the primary. This way we get an easy addition and use the low byte + * as the offset + */ + offs = MakeByteOffs (flags, offs); + + /* Handle the indirect fetch */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + /* Character sized */ + if (offs) { + ldyconst (offs); + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tldauidx"); + } else { + AddCodeLine ("\tjsr\tldaidx"); + } + } else { + if (flags & CF_UNSIGNED) { + if (FavourSize) { + AddCodeLine ("\tjsr\tldaui"); + } else { + AddCodeLine ("\tsta\tptr1"); + AddCodeLine ("\tstx\tptr1+1"); + AddCodeLine ("\tldx\t#$00"); + AddCodeLine ("\tlda\t(ptr1,x)"); + } + } else { + AddCodeLine ("\tjsr\tldai"); + } + } + break; + + case CF_INT: + if (flags & CF_TEST) { + ldyconst (offs); + AddCodeLine ("\tsta\tptr1"); + AddCodeLine ("\tstx\tptr1+1"); + AddCodeLine ("\tlda\t(ptr1),y"); + AddCodeLine ("\tiny"); + AddCodeLine ("\tora\t(ptr1),y"); + } else { + if (offs == 0) { + AddCodeLine ("\tjsr\tldaxi"); + } else { + ldyconst (offs+1); + AddCodeLine ("\tjsr\tldaxidx"); + } + } + break; + + case CF_LONG: + if (offs == 0) { + AddCodeLine ("\tjsr\tldeaxi"); + } else { + ldyconst (offs+3); + AddCodeLine ("\tjsr\tldeaxidx"); + } + if (flags & CF_TEST) { + AddCodeLine ("\tjsr\ttsteax"); + } + break; + + default: + typeerror (flags); + + } +} + + + +void g_leasp (int offs) +/* Fetch the address of the specified symbol into the primary register */ +{ + /* Calculate the offset relative to sp */ + offs -= oursp; + + /* For value 0 we do direct code */ + if (offs == 0) { + AddCodeLine ("\tlda\tsp"); + AddCodeLine ("\tldx\tsp+1"); + } else { + if (FavourSize) { + ldyconst (offs); /* Load Y with offset value */ + AddCodeLine ("\tjsr\tleaysp"); /* Load effective address */ + } else { + ldaconst (offs); + AddCodeLine ("\tclc"); + AddCodeLine ("\tldx\tsp+1"); + AddCodeLine ("\tadc\tsp"); + AddCodeLine ("\tbcc\t*+3"); + AddCodeLine ("\tinx"); + AddCodeHint ("x:!"); /* Invalidate X */ + } + } +} + + + +/*****************************************************************************/ +/* Store into memory */ +/*****************************************************************************/ + + + +void g_putstatic (unsigned flags, unsigned long label, unsigned offs) +/* Store the primary register into the specified static memory cell */ +{ + /* Create the correct label name */ + char* lbuf = GetLabelName (flags, label, offs); + + /* Check the size and generate the correct store operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + AddCodeLine ("\tsta\t%s", lbuf); + break; + + case CF_INT: + AddCodeLine ("\tsta\t%s", lbuf); + AddCodeLine ("\tstx\t%s+1", lbuf); + break; + + case CF_LONG: + AddCodeLine ("\tsta\t%s", lbuf); + AddCodeLine ("\tstx\t%s+1", lbuf); + AddCodeLine ("\tldy\tsreg"); + AddCodeLine ("\tsty\t%s+2", lbuf); + AddCodeLine ("\tldy\tsreg+1"); + AddCodeLine ("\tsty\t%s+3", lbuf); + break; + + default: + typeerror (flags); + + } +} + + + +void g_putlocal (unsigned flags, int offs) +/* Put data into local object. */ +{ + offs -= oursp; + CheckLocalOffs (offs); + switch (flags & CF_TYPE) { + + case CF_CHAR: + ldyconst (offs); + AddCodeLine ("\tsta\t(sp),y"); + break; + + case CF_INT: + if (offs) { + ldyconst (offs); + AddCodeLine ("\tjsr\tstaxysp"); + } else { + AddCodeLine ("\tjsr\tstax0sp"); + } + break; + + case CF_LONG: + if (offs) { + ldyconst (offs); + AddCodeLine ("\tjsr\tsteaxysp"); + } else { + AddCodeLine ("\tjsr\tsteax0sp"); + } + break; + + default: + typeerror (flags); + + } +} + + + +void g_putind (unsigned flags, unsigned offs) +/* Store the specified object type in the primary register at the address + * on the top of the stack + */ +{ + /* We cannot currently handle more than byte sized offsets */ + if (offs > 256 - sizeofarg (flags)) { + Internal ("g_putind: Large offsets not implemented"); + } + + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (offs) { + ldyconst (offs); + AddCodeLine ("\tjsr\tstaspidx"); + } else { + AddCodeLine ("\tjsr\tstaspp"); + } + break; + + case CF_INT: + if (offs) { + ldyconst (offs); + AddCodeLine ("\tjsr\tstaxspidx"); + } else { + AddCodeLine ("\tjsr\tstaxspp"); + } + break; + + case CF_LONG: + if (offs) { + ldyconst (offs); + AddCodeLine ("\tjsr\tsteaxspidx"); + } else { + AddCodeLine ("\tjsr\tsteaxspp"); + } + break; + + default: + typeerror (flags); + + } + + /* Pop the argument which is always a pointer */ + pop (CF_PTR); +} + + + +/*****************************************************************************/ +/* type conversion and similiar stuff */ +/*****************************************************************************/ + + + +void g_toslong (unsigned flags) +/* Make sure, the value on TOS is a long. Convert if necessary */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\ttosulong"); + } else { + AddCodeLine ("\tjsr\ttoslong"); + } + push (CF_INT); + break; + + case CF_LONG: + break; + + default: + typeerror (flags); + } +} + + + +void g_tosint (unsigned flags) +/* Make sure, the value on TOS is an int. Convert if necessary */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + break; + + case CF_LONG: + AddCodeLine ("\tjsr\ttosint"); + pop (CF_INT); + break; + + default: + typeerror (flags); + } +} + + + +void g_reglong (unsigned flags) +/* Make sure, the value in the primary register a long. Convert if necessary */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + if (flags & CF_UNSIGNED) { + if (FavourSize) { + AddCodeLine ("\tjsr\taxulong"); + } else { + ldyconst (0); + AddCodeLine ("\tsty\tsreg"); + AddCodeLine ("\tsty\tsreg+1"); + } + } else { + AddCodeLine ("\tjsr\taxlong"); + } + break; + + case CF_LONG: + break; + + default: + typeerror (flags); + } +} + + + +unsigned g_typeadjust (unsigned lhs, unsigned rhs) +/* Adjust the integer operands before doing a binary operation. lhs is a flags + * value, that corresponds to the value on TOS, rhs corresponds to the value + * in (e)ax. The return value is the the flags value for the resulting type. + */ +{ + unsigned ltype, rtype; + unsigned result; + + /* Get the type spec from the flags */ + ltype = lhs & CF_TYPE; + rtype = rhs & CF_TYPE; + + /* Check if a conversion is needed */ + if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) { + /* We must promote the primary register to long */ + g_reglong (rhs); + /* Get the new rhs type */ + rhs = (rhs & ~CF_TYPE) | CF_LONG; + rtype = CF_LONG; + } else if (ltype != CF_LONG && (lhs & CF_CONST) == 0 && rtype == CF_LONG) { + /* We must promote the lhs to long */ + if (lhs & CF_REG) { + g_reglong (lhs); + } else { + g_toslong (lhs); + } + /* Get the new rhs type */ + lhs = (lhs & ~CF_TYPE) | CF_LONG; + ltype = CF_LONG; + } + + /* Determine the result type for the operation: + * - The result is const if both operands are const. + * - The result is unsigned if one of the operands is unsigned. + * - The result is long if one of the operands is long. + * - Otherwise the result is int sized. + */ + result = (lhs & CF_CONST) & (rhs & CF_CONST); + result |= (lhs & CF_UNSIGNED) | (rhs & CF_UNSIGNED); + if (rtype == CF_LONG || ltype == CF_LONG) { + result |= CF_LONG; + } else { + result |= CF_INT; + } + return result; +} + + + +unsigned g_typecast (unsigned lhs, unsigned rhs) +/* Cast the value in the primary register to the operand size that is flagged + * by the lhs value. Return the result value. + */ +{ + unsigned ltype, rtype; + + /* Get the type spec from the flags */ + ltype = lhs & CF_TYPE; + rtype = rhs & CF_TYPE; + + /* Check if a conversion is needed */ + if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) { + /* We must promote the primary register to long */ + g_reglong (rhs); + } + + /* Do not need any other action. If the left type is int, and the primary + * register is long, it will be automagically truncated. If the right hand + * side is const, it is not located in the primary register and handled by + * the expression parser code. + */ + + /* Result is const if the right hand side was const */ + lhs |= (rhs & CF_CONST); + + /* The resulting type is that of the left hand side (that's why you called + * this function :-) + */ + return lhs; +} + + + +void g_scale (unsigned flags, long val) +/* Scale the value in the primary register by the given value. If val is positive, + * scale up, is val is negative, scale down. This function is used to scale + * the operands or results of pointer arithmetic by the size of the type, the + * pointer points to. + */ +{ + int p2; + + /* Value may not be zero */ + if (val == 0) { + Internal ("Data type has no size"); + } else if (val > 0) { + + /* Scale up */ + if ((p2 = powerof2 (val)) > 0 && p2 <= 3) { + + /* Factor is 2, 4 or 8, use special function */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + while (p2--) { + AddCodeLine ("\tasl\ta"); + } + break; + } + /* FALLTHROUGH */ + + case CF_INT: + if (FavourSize || p2 >= 3) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tshlax%d", p2); + } else { + AddCodeLine ("\tjsr\taslax%d", p2); + } + } else { + AddCodeLine ("\tstx\ttmp1"); + while (p2--) { + AddCodeLine ("\tasl\ta"); + AddCodeLine ("\trol\ttmp1"); + } + AddCodeLine ("\tldx\ttmp1"); + } + break; + + case CF_LONG: + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tshleax%d", p2); + } else { + AddCodeLine ("\tjsr\tasleax%d", p2); + } + break; + + default: + typeerror (flags); + + } + + } else if (val != 1) { + + /* Use a multiplication instead */ + g_mul (flags | CF_CONST, val); + + } + + } else { + + /* Scale down */ + val = -val; + if ((p2 = powerof2 (val)) > 0 && p2 <= 3) { + + /* Factor is 2, 4 or 8, use special function */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + if (flags & CF_UNSIGNED) { + while (p2--) { + AddCodeLine ("\tlsr\ta"); + } + break; + } else if (p2 <= 2) { + AddCodeLine ("\tcmp\t#$80"); + AddCodeLine ("\tror\ta"); + break; + } + } + /* FALLTHROUGH */ + + case CF_INT: + if (flags & CF_UNSIGNED) { + if (FavourSize || p2 >= 3) { + AddCodeLine ("\tjsr\tlsrax%d", p2); + } else { + AddCodeLine ("\tstx\ttmp1"); + while (p2--) { + AddCodeLine ("\tlsr\ttmp1"); + AddCodeLine ("\tror\ta"); + } + AddCodeLine ("\tldx\ttmp1"); + } + } else { + if (FavourSize || p2 >= 3) { + AddCodeLine ("\tjsr\tasrax%d", p2); + } else { + AddCodeLine ("\tstx\ttmp1"); + while (p2--) { + AddCodeLine ("\tcpx\t#$80"); + AddCodeLine ("\tror\ttmp1"); + AddCodeLine ("\tror\ta"); + } + AddCodeLine ("\tldx\ttmp1"); + } + } + break; + + case CF_LONG: + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tlsreax%d", p2); + } else { + AddCodeLine ("\tjsr\tasreax%d", p2); + } + break; + + default: + typeerror (flags); + + } + + } else if (val != 1) { + + /* Use a division instead */ + g_div (flags | CF_CONST, val); + + } + } +} + + + +/*****************************************************************************/ +/* Adds and subs of variables fix a fixed address */ +/*****************************************************************************/ + + + +void g_addlocal (unsigned flags, int offs) +/* Add a local variable to ax */ +{ + /* Correct the offset and check it */ + offs -= oursp; + CheckLocalOffs (offs); + + switch (flags & CF_TYPE) { + + case CF_CHAR: + AddCodeLine ("\tldy\t#$%02X", offs & 0xFF); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t(sp),y"); + AddCodeLine ("\tbcc\t*+3"); + AddCodeLine ("\tinx"); + AddCodeHint ("x:!"); + break; + + case CF_INT: + AddCodeLine ("\tldy\t#$%02X", offs & 0xFF); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t(sp),y"); + AddCodeLine ("\tpha"); + AddCodeLine ("\ttxa"); + AddCodeLine ("\tiny"); + AddCodeLine ("\tadc\t(sp),y"); + AddCodeLine ("\ttax"); + AddCodeLine ("\tpla"); + break; + + case CF_LONG: + /* Do it the old way */ + g_push (flags, 0); + g_getlocal (flags, offs); + g_add (flags, 0); + break; + + default: + typeerror (flags); + + } +} + + + +void g_addstatic (unsigned flags, unsigned long label, unsigned offs) +/* Add a static variable to ax */ +{ + /* Create the correct label name */ + char* lbuf = GetLabelName (flags, label, offs); + + switch (flags & CF_TYPE) { + + case CF_CHAR: + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t%s", lbuf); + AddCodeLine ("\tbcc\t*+3"); + AddCodeLine ("\tinx"); + AddCodeHint ("x:!"); + break; + + case CF_INT: + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t%s", lbuf); + AddCodeLine ("\ttay"); + AddCodeLine ("\ttxa"); + AddCodeLine ("\tadc\t%s+1", lbuf); + AddCodeLine ("\ttax"); + AddCodeLine ("\ttya"); + break; + + case CF_LONG: + /* Do it the old way */ + g_push (flags, 0); + g_getstatic (flags, label, offs); + g_add (flags, 0); + break; + + default: + typeerror (flags); + + } +} + + + +/*****************************************************************************/ +/* Compares of ax with a variable with fixed address */ +/*****************************************************************************/ + + + +void g_cmplocal (unsigned flags, int offs) +/* Compare a local variable to ax */ +{ + Internal ("g_cmplocal not implemented"); +} + + + +void g_cmpstatic (unsigned flags, unsigned label, unsigned offs) +/* Compare a static variable to ax */ +{ + Internal ("g_cmpstatic not implemented"); +} + + + +/*****************************************************************************/ +/* Special op= functions */ +/*****************************************************************************/ + + + +void g_addeqstatic (unsigned flags, unsigned long label, unsigned offs, + unsigned long val) +/* Emit += for a static variable */ +{ + /* Create the correct label name */ + char* lbuf = GetLabelName (flags, label, offs); + + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tldx\t#$00"); + if (flags & CF_CONST) { + if (val == 1) { + AddCodeLine ("\tinc\t%s", lbuf); + AddCodeLine ("\tlda\t%s", lbuf); + } else { + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t%s", lbuf); + AddCodeLine ("\tsta\t%s", lbuf); + } + } else { + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t%s", lbuf); + AddCodeLine ("\tsta\t%s", lbuf); + } + if ((flags & CF_UNSIGNED) == 0) { + AddCodeLine ("\tbpl\t*+3"); + AddCodeLine ("\tdex"); + AddCodeHint ("x:!"); /* Invalidate X */ + } + break; + } + /* FALLTHROUGH */ + + case CF_INT: + if (flags & CF_CONST) { + if (val == 1) { + label = GetLabel (); + AddCodeLine ("\tinc\t%s", lbuf); + AddCodeLine ("\tbne\tL%04X", label); + AddCodeLine ("\tinc\t%s+1", lbuf); + g_defloclabel (label); + AddCodeLine ("\tlda\t%s", lbuf); /* Hmmm... */ + AddCodeLine ("\tldx\t%s+1", lbuf); + } else { + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t%s", lbuf); + AddCodeLine ("\tsta\t%s", lbuf); + if (val < 0x100) { + label = GetLabel (); + AddCodeLine ("\tbcc\tL%04X", label); + AddCodeLine ("\tinc\t%s+1", lbuf); + g_defloclabel (label); + AddCodeLine ("\tldx\t%s+1", lbuf); + } else { + AddCodeLine ("\tlda\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tadc\t%s+1", lbuf); + AddCodeLine ("\tsta\t%s+1", lbuf); + AddCodeLine ("\ttax"); + AddCodeLine ("\tlda\t%s", lbuf); + } + } + } else { + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t%s", lbuf); + AddCodeLine ("\tsta\t%s", lbuf); + AddCodeLine ("\ttxa"); + AddCodeLine ("\tadc\t%s+1", lbuf); + AddCodeLine ("\tsta\t%s+1", lbuf); + AddCodeLine ("\ttax"); + AddCodeLine ("\tlda\t%s", lbuf); + } + break; + + case CF_LONG: + if (flags & CF_CONST) { + if (val < 0x100) { + AddCodeLine ("\tldy\t#<(%s)", lbuf); + AddCodeLine ("\tsty\tptr1"); + AddCodeLine ("\tldy\t#>(%s+1)", lbuf); + if (val == 1) { + AddCodeLine ("\tjsr\tladdeq1"); + } else { + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tladdeqa"); + } + } else { + g_getstatic (flags, label, offs); + g_inc (flags, val); + g_putstatic (flags, label, offs); + } + } else { + AddCodeLine ("\tldy\t#<(%s)", lbuf); + AddCodeLine ("\tsty\tptr1"); + AddCodeLine ("\tldy\t#>(%s+1)", lbuf); + AddCodeLine ("\tjsr\tladdeq"); + } + break; + + default: + typeerror (flags); + } +} + + + +void g_addeqlocal (unsigned flags, int offs, unsigned long val) +/* Emit += for a local variable */ +{ + /* Calculate the true offset, check it, load it into Y */ + offs -= oursp; + CheckLocalOffs (offs); + + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + if (offs == 0) { + AddCodeLine ("\tldx\t#$00"); + if (flags & CF_CONST) { + AddCodeLine ("\tclc"); + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); + AddCodeLine ("\tadc\t(sp,x)"); + AddCodeLine ("\tsta\t(sp,x)"); + } else { + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t(sp,x)"); + AddCodeLine ("\tsta\t(sp,x)"); + } + } else { + ldyconst (offs); + AddCodeLine ("\tldx\t#$00"); + if (flags & CF_CONST) { + AddCodeLine ("\tclc"); + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); + AddCodeLine ("\tadc\t(sp),y"); + AddCodeLine ("\tsta\t(sp),y"); + } else { + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t(sp),y"); + AddCodeLine ("\tsta\t(sp),y"); + } + } + if ((flags & CF_UNSIGNED) == 0) { + AddCodeLine ("\tbpl\t*+3"); + AddCodeLine ("\tdex"); + AddCodeHint ("x:!"); /* Invalidate X */ + } + break; + } + /* FALLTHROUGH */ + + case CF_INT: + if (flags & CF_CONST) { + g_getimmed (flags, val, 0); + } + if (offs == 0) { + AddCodeLine ("\tjsr\taddeq0sp"); + } else { + ldyconst (offs); + AddCodeLine ("\tjsr\taddeqysp"); + } + break; + + case CF_LONG: + if (flags & CF_CONST) { + g_getimmed (flags, val, 0); + } + if (offs == 0) { + AddCodeLine ("\tjsr\tladdeq0sp"); + } else { + ldyconst (offs); + AddCodeLine ("\tjsr\tladdeqysp"); + } + break; + + default: + typeerror (flags); + } +} + + + +void g_addeqind (unsigned flags, unsigned offs, unsigned long val) +/* Emit += for the location with address in ax */ +{ + /* If the offset is too large for a byte register, add the high byte + * of the offset to the primary. Beware: We need a special correction + * if the offset in the low byte will overflow in the operation. + */ + offs = MakeByteOffs (flags, offs); + + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + AddCodeLine ("\tsta\tptr1"); + AddCodeLine ("\tstx\tptr1+1"); + if (offs == 0) { + AddCodeLine ("\tldx\t#$00"); + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t(ptr1,x)"); + AddCodeLine ("\tsta\t(ptr1,x)"); + } else { + AddCodeLine ("\tldy\t#$%02X", offs); + AddCodeLine ("\tldx\t#$00"); + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t(ptr1),y"); + AddCodeLine ("\tsta\t(ptr1),y"); + } + break; + + case CF_INT: + if (!FavourSize) { + /* Lots of code, use only if size is not important */ + AddCodeLine ("\tsta\tptr1"); + AddCodeLine ("\tstx\tptr1+1"); + AddCodeLine ("\tldy\t#$%02X", offs); + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t(ptr1),y"); + AddCodeLine ("\tsta\t(ptr1),y"); + AddCodeLine ("\tpha"); + AddCodeLine ("\tiny"); + AddCodeLine ("\tlda\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tadc\t(ptr1),y"); + AddCodeLine ("\tsta\t(ptr1),y"); + AddCodeLine ("\ttax"); + AddCodeLine ("\tpla"); + break; + } + /* FALL THROUGH */ + + case CF_LONG: + AddCodeLine ("\tjsr\tpushax"); /* Push the address */ + push (flags); /* Correct the internal sp */ + g_getind (flags, offs); /* Fetch the value */ + g_inc (flags, val); /* Increment value in primary */ + g_putind (flags, offs); /* Store the value back */ + break; + + default: + typeerror (flags); + } +} + + + +void g_subeqstatic (unsigned flags, unsigned long label, unsigned offs, + unsigned long val) +/* Emit -= for a static variable */ +{ + /* Create the correct label name */ + char* lbuf = GetLabelName (flags, label, offs); + + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tldx\t#$00"); + if (flags & CF_CONST) { + if (val == 1) { + AddCodeLine ("\tdec\t%s", lbuf); + AddCodeLine ("\tlda\t%s", lbuf); + } else { + AddCodeLine ("\tsec"); + AddCodeLine ("\tlda\t%s", lbuf); + AddCodeLine ("\tsbc\t#$%02X", val & 0xFF); + AddCodeLine ("\tsta\t%s", lbuf); + } + } else { + AddCodeLine ("\tsec"); + AddCodeLine ("\tsta\ttmp1"); + AddCodeLine ("\tlda\t%s", lbuf); + AddCodeLine ("\tsbc\ttmp1"); + AddCodeLine ("\tsta\t%s", lbuf); + } + if ((flags & CF_UNSIGNED) == 0) { + AddCodeLine ("\tbpl\t*+3"); + AddCodeLine ("\tdex"); + AddCodeHint ("x:!"); /* Invalidate X */ + } + break; + } + /* FALLTHROUGH */ + + case CF_INT: + AddCodeLine ("\tsec"); + if (flags & CF_CONST) { + AddCodeLine ("\tlda\t%s", lbuf); + AddCodeLine ("\tsbc\t#$%02X", val & 0xFF); + AddCodeLine ("\tsta\t%s", lbuf); + if (val < 0x100) { + label = GetLabel (); + AddCodeLine ("\tbcs\tL%04X", label); + AddCodeLine ("\tdec\t%s+1", lbuf); + g_defloclabel (label); + AddCodeLine ("\tldx\t%s+1", lbuf); + } else { + AddCodeLine ("\tlda\t%s+1", lbuf); + AddCodeLine ("\tsbc\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tsta\t%s+1", lbuf); + AddCodeLine ("\ttax"); + AddCodeLine ("\tlda\t%s", lbuf); + } + } else { + AddCodeLine ("\tsta\ttmp1"); + AddCodeLine ("\tlda\t%s", lbuf); + AddCodeLine ("\tsbc\ttmp1"); + AddCodeLine ("\tsta\t%s", lbuf); + AddCodeLine ("\tstx\ttmp1"); + AddCodeLine ("\tlda\t%s+1", lbuf); + AddCodeLine ("\tsbc\ttmp1"); + AddCodeLine ("\tsta\t%s+1", lbuf); + AddCodeLine ("\ttax"); + AddCodeLine ("\tlda\t%s", lbuf); + } + break; + + case CF_LONG: + if (flags & CF_CONST) { + if (val < 0x100) { + AddCodeLine ("\tldy\t#<(%s)", lbuf); + AddCodeLine ("\tsty\tptr1"); + AddCodeLine ("\tldy\t#>(%s+1)", lbuf); + if (val == 1) { + AddCodeLine ("\tjsr\tlsubeq1"); + } else { + AddCodeLine ("\tlda\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tlsubeqa"); + } + } else { + g_getstatic (flags, label, offs); + g_dec (flags, val); + g_putstatic (flags, label, offs); + } + } else { + AddCodeLine ("\tldy\t#<(%s)", lbuf); + AddCodeLine ("\tsty\tptr1"); + AddCodeLine ("\tldy\t#>(%s+1)", lbuf); + AddCodeLine ("\tjsr\tlsubeq"); + } + break; + + default: + typeerror (flags); + } +} + + + +void g_subeqlocal (unsigned flags, int offs, unsigned long val) +/* Emit -= for a local variable */ +{ + /* Calculate the true offset, check it, load it into Y */ + offs -= oursp; + CheckLocalOffs (offs); + + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + ldyconst (offs); + AddCodeLine ("\tldx\t#$00"); + AddCodeLine ("\tsec"); + if (flags & CF_CONST) { + AddCodeLine ("\tlda\t(sp),y"); + AddCodeLine ("\tsbc\t#$%02X", val & 0xFF); + } else { + AddCodeLine ("\tsta\ttmp1"); + AddCodeLine ("\tlda\t(sp),y"); + AddCodeLine ("\tsbc\ttmp1"); + } + AddCodeLine ("\tsta\t(sp),y"); + if ((flags & CF_UNSIGNED) == 0) { + AddCodeLine ("\tbpl\t*+3"); + AddCodeLine ("\tdex"); + AddCodeHint ("x:!"); /* Invalidate X */ + } + break; + } + /* FALLTHROUGH */ + + case CF_INT: + if (flags & CF_CONST) { + g_getimmed (flags, val, 0); + } + if (offs == 0) { + AddCodeLine ("\tjsr\tsubeq0sp"); + } else { + ldyconst (offs); + AddCodeLine ("\tjsr\tsubeqysp"); + } + break; + + case CF_LONG: + if (flags & CF_CONST) { + g_getimmed (flags, val, 0); + } + if (offs == 0) { + AddCodeLine ("\tjsr\tlsubeq0sp"); + } else { + ldyconst (offs); + AddCodeLine ("\tjsr\tlsubeqysp"); + } + break; + + default: + typeerror (flags); + } +} + + + +void g_subeqind (unsigned flags, unsigned offs, unsigned long val) +/* Emit -= for the location with address in ax */ +{ + /* If the offset is too large for a byte register, add the high byte + * of the offset to the primary. Beware: We need a special correction + * if the offset in the low byte will overflow in the operation. + */ + offs = MakeByteOffs (flags, offs); + + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + AddCodeLine ("\tsta\tptr1"); + AddCodeLine ("\tstx\tptr1+1"); + if (offs == 0) { + AddCodeLine ("\tldx\t#$00"); + AddCodeLine ("\tlda\t(ptr1,x)"); + AddCodeLine ("\tsec"); + AddCodeLine ("\tsbc\t#$%02X", val & 0xFF); + AddCodeLine ("\tsta\t(ptr1,x)"); + } else { + AddCodeLine ("\tldy\t#$%02X", offs); + AddCodeLine ("\tldx\t#$00"); + AddCodeLine ("\tlda\t(ptr1),y"); + AddCodeLine ("\tsec"); + AddCodeLine ("\tsbc\t#$%02X", val & 0xFF); + AddCodeLine ("\tsta\t(ptr1),y"); + } + break; + + case CF_INT: + if (!FavourSize) { + /* Lots of code, use only if size is not important */ + AddCodeLine ("\tsta\tptr1"); + AddCodeLine ("\tstx\tptr1+1"); + AddCodeLine ("\tldy\t#$%02X", offs); + AddCodeLine ("\tlda\t(ptr1),y"); + AddCodeLine ("\tsec"); + AddCodeLine ("\tsbc\t#$%02X", val & 0xFF); + AddCodeLine ("\tsta\t(ptr1),y"); + AddCodeLine ("\tpha"); + AddCodeLine ("\tiny"); + AddCodeLine ("\tlda\t(ptr1),y"); + AddCodeLine ("\tsbc\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tsta\t(ptr1),y"); + AddCodeLine ("\ttax"); + AddCodeLine ("\tpla"); + break; + } + /* FALL THROUGH */ + + case CF_LONG: + AddCodeLine ("\tjsr\tpushax"); /* Push the address */ + push (flags); /* Correct the internal sp */ + g_getind (flags, offs); /* Fetch the value */ + g_dec (flags, val); /* Increment value in primary */ + g_putind (flags, offs); /* Store the value back */ + break; + + default: + typeerror (flags); + } +} + + + +/*****************************************************************************/ +/* Add a variable address to the value in ax */ +/*****************************************************************************/ + + + +void g_addaddr_local (unsigned flags, int offs) +/* Add the address of a local variable to ax */ +{ + /* Add the offset */ + offs -= oursp; + if (offs != 0) { + /* We cannot address more then 256 bytes of locals anyway */ + CheckLocalOffs (offs); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t#$%02X", offs & 0xFF); + AddCodeLine ("\tbcc\t*+4"); /* Do also skip the CLC insn below */ + AddCodeLine ("\tinx"); + AddCodeHint ("x:!"); /* Invalidate X */ + } + + /* Add the current stackpointer value */ + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\tsp"); + AddCodeLine ("\ttay"); + AddCodeLine ("\ttxa"); + AddCodeLine ("\tadc\tsp+1"); + AddCodeLine ("\ttax"); + AddCodeLine ("\ttya"); +} + + + +void g_addaddr_static (unsigned flags, unsigned long label, unsigned offs) +/* Add the address of a static variable to ax */ +{ + /* Create the correct label name */ + char* lbuf = GetLabelName (flags, label, offs); + + /* Add the address to the current ax value */ + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t#<(%s)", lbuf); + AddCodeLine ("\ttay"); + AddCodeLine ("\ttxa"); + AddCodeLine ("\tadc\t#>(%s)", lbuf); + AddCodeLine ("\ttax"); + AddCodeLine ("\ttya"); +} + + + +/*****************************************************************************/ +/* */ +/*****************************************************************************/ + + + +void g_save (unsigned flags) +/* Copy primary register to hold register. */ +{ + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tpha"); + break; + } + /* FALLTHROUGH */ + + case CF_INT: + AddCodeLine ("\tsta\tregsave"); + AddCodeLine ("\tstx\tregsave+1"); + break; + + case CF_LONG: + AddCodeLine ("\tjsr\tsaveeax"); + break; + + default: + typeerror (flags); + } +} + + + +void g_restore (unsigned flags) +/* Copy hold register to P. */ +{ + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tpla"); + break; + } + /* FALLTHROUGH */ + + case CF_INT: + AddCodeLine ("\tlda\tregsave"); + AddCodeLine ("\tldx\tregsave+1"); + break; + + case CF_LONG: + AddCodeLine ("\tjsr\tresteax"); + break; + + default: + typeerror (flags); + } +} + + + +void g_cmp (unsigned flags, unsigned long val) +/* Immidiate compare. The primary register will not be changed, Z flag + * will be set. + */ +{ + /* Check the size and determine operation */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + break; + } + /* FALLTHROUGH */ + + case CF_INT: + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + AddCodeLine ("\tbne\t*+4"); + AddCodeLine ("\tcpx\t#$%02X", (val >> 8) & 0xFF); + break; + + case CF_LONG: + Internal ("g_cmp: Long compares not implemented"); + break; + + default: + typeerror (flags); + } +} + + + +static void oper (unsigned flags, unsigned long val, char** subs) +/* Encode a binary operation. subs is a pointer to four groups of three + * strings: + * 0-2 --> Operate on ints + * 3-5 --> Operate on unsigneds + * 6-8 --> Operate on longs + * 9-11 --> Operate on unsigned longs + * + * The first subroutine names in each string group is used to encode an + * operation with a zero constant, the second to encode an operation with + * a 8 bit constant, and the third is used in all other cases. + */ +{ + unsigned offs; + + /* Determine the offset into the array */ + offs = (flags & CF_UNSIGNED)? 3 : 0; + switch (flags & CF_TYPE) { + case CF_CHAR: + case CF_INT: + break; + + case CF_LONG: + offs += 6; + break; + + default: + typeerror (flags); + } + + /* Encode the operation */ + if (flags & CF_CONST) { + /* Constant value given */ + if (val == 0 && subs [offs+0]) { + /* Special case: constant with value zero */ + AddCodeLine ("\tjsr\t%s", subs [offs+0]); + } else if (val < 0x100 && subs [offs+1]) { + /* Special case: constant with high byte zero */ + ldaconst (val); /* Load low byte */ + AddCodeLine ("\tjsr\t%s", subs [offs+1]); + } else { + /* Others: arbitrary constant value */ + g_getimmed (flags, val, 0); /* Load value */ + AddCodeLine ("\tjsr\t%s", subs [offs+2]); + } + } else { + /* Value not constant (is already in (e)ax) */ + AddCodeLine ("\tjsr\t%s", subs [offs+2]); + } + + /* The operation will pop it's argument */ + pop (flags); +} + + + +void g_test (unsigned flags) +/* Force a test to set cond codes right */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\ttax"); + break; + } + /* FALLTHROUGH */ + + case CF_INT: + AddCodeLine ("\tstx\ttmp1"); + AddCodeLine ("\tora\ttmp1"); + break; + + case CF_LONG: + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tutsteax"); + } else { + AddCodeLine ("\tjsr\ttsteax"); + } + break; + + default: + typeerror (flags); + + } +} + + + +void g_push (unsigned flags, unsigned long val) +/* Push the primary register or a constant value onto the stack */ +{ + unsigned char hi; + + if (flags & CF_CONST && (flags & CF_TYPE) != CF_LONG) { + + /* We have a constant 8 or 16 bit value */ + if ((flags & CF_TYPE) == CF_CHAR && (flags & CF_FORCECHAR)) { + + /* Handle as 8 bit value */ + if (FavourSize && val <= 2) { + AddCodeLine ("\tjsr\tpushc%d", (int) val); + } else { + ldaconst (val); + AddCodeLine ("\tjsr\tpusha"); + } + + } else { + + /* Handle as 16 bit value */ + hi = val >> 8; + if (val <= 7) { + AddCodeLine ("\tjsr\tpush%u", val); + } else if (hi == 0 || hi == 0xFF) { + /* Use special function */ + ldaconst (val); + AddCodeLine ("\tjsr\t%s", (hi == 0)? "pusha0" : "pushaFF"); + } else { + /* Long way ... */ + g_getimmed (flags, val, 0); + AddCodeLine ("\tjsr\tpushax"); + } + } + + } else { + + /* Value is not 16 bit or not constant */ + if (flags & CF_CONST) { + /* Constant 32 bit value, load into eax */ + g_getimmed (flags, val, 0); + } + + /* Push the primary register */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + /* Handle as char */ + AddCodeLine ("\tjsr\tpusha"); + break; + } + /* FALL THROUGH */ + case CF_INT: + AddCodeLine ("\tjsr\tpushax"); + break; + + case CF_LONG: + AddCodeLine ("\tjsr\tpusheax"); + break; + + default: + typeerror (flags); + + } + + } + + /* Adjust the stack offset */ + push (flags); +} + + + +void g_swap (unsigned flags) +/* Swap the primary register and the top of the stack. flags give the type + * of *both* values (must have same size). + */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + AddCodeLine ("\tjsr\tswapstk"); + break; + + case CF_LONG: + AddCodeLine ("\tjsr\tswapestk"); + break; + + default: + typeerror (flags); + + } +} + + + +void g_call (unsigned flags, char* lbl, unsigned argsize) +/* Call the specified subroutine name */ +{ + if ((flags & CF_FIXARGC) == 0) { + /* Pass arg count */ + ldyconst (argsize); + } + AddCodeLine ("\tjsr\t_%s", lbl); + oursp += argsize; /* callee pops args */ +} + + + +void g_callind (unsigned flags, unsigned argsize) +/* Call subroutine with address in AX */ +{ + if ((flags & CF_FIXARGC) == 0) { + /* Pass arg count */ + ldyconst (argsize); + } + AddCodeLine ("\tjsr\tcallax"); /* do the call */ + oursp += argsize; /* callee pops args */ +} + + + +void g_jump (unsigned label) +/* Jump to specified internal label number */ +{ + AddCodeLine ("\tjmp\tL%04X", label); +} + + + +void g_switch (unsigned flags) +/* Output switch statement preample */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + AddCodeLine ("\tjsr\tswitch"); + break; + + case CF_LONG: + AddCodeLine ("\tjsr\tlswitch"); + break; + + default: + typeerror (flags); + + } +} + + + +void g_case (unsigned flags, unsigned label, unsigned long val) +/* Create table code for one case selector */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + AddCodeLine ("\t.word\t$%04X, L%04X", val & 0xFFFF, label & 0xFFFF); + break; + + case CF_LONG: + AddCodeLine ("\t.dword\t$%08X", val); + AddCodeLine ("\t.word\tL%04X", label & 0xFFFF); + break; + + default: + typeerror (flags); + + } +} + + + +void g_truejump (unsigned flags, unsigned label) +/* Jump to label if zero flag clear */ +{ + if (flags & CF_SHORT) { + AddCodeLine ("\tbne\tL%04X", label); + } else { + AddCodeLine ("\tjne\tL%04X", label); + } +} + + + +void g_falsejump (unsigned flags, unsigned label) +/* Jump to label if zero flag set */ +{ + if (flags & CF_SHORT) { + AddCodeLine ("\tbeq\tL%04X", label); + } else { + AddCodeLine ("\tjeq\tL%04X", label); + } +} + + + +static void mod_internal (int k, char* verb1, char* verb2) +{ + if (k <= 8) { + AddCodeLine ("\tjsr\t%ssp%c", verb1, k + '0'); + } else { + CheckLocalOffs (k); + ldyconst (k); + AddCodeLine ("\tjsr\t%ssp", verb2); + } +} + + + +void g_space (int space) +/* Create or drop space on the stack */ +{ + if (space < 0) { + mod_internal (-space, "inc", "addy"); + } else if (space > 0) { + mod_internal (space, "dec", "suby"); + } +} + + + +void g_add (unsigned flags, unsigned long val) +/* Primary = TOS + Primary */ +{ + static char* ops [12] = { + 0, "tosadda0", "tosaddax", + 0, "tosadda0", "tosaddax", + 0, 0, "tosaddeax", + 0, 0, "tosaddeax", + }; + + if (flags & CF_CONST) { + flags &= ~CF_FORCECHAR; // Handle chars as ints + g_push (flags & ~CF_CONST, 0); + } + oper (flags, val, ops); +} + + + +void g_sub (unsigned flags, unsigned long val) +/* Primary = TOS - Primary */ +{ + static char* ops [12] = { + 0, "tossuba0", "tossubax", + 0, "tossuba0", "tossubax", + 0, 0, "tossubeax", + 0, 0, "tossubeax", + }; + + if (flags & CF_CONST) { + flags &= ~CF_FORCECHAR; // Handle chars as ints + g_push (flags & ~CF_CONST, 0); + } + oper (flags, val, ops); +} + + + +void g_rsub (unsigned flags, unsigned long val) +/* Primary = Primary - TOS */ +{ + static char* ops [12] = { + 0, "tosrsuba0", "tosrsubax", + 0, "tosrsuba0", "tosrsubax", + 0, 0, "tosrsubeax", + 0, 0, "tosrsubeax", + }; + oper (flags, val, ops); +} + + + +void g_mul (unsigned flags, unsigned long val) +/* Primary = TOS * Primary */ +{ + static char* ops [12] = { + 0, "tosmula0", "tosmulax", + 0, "tosumula0", "tosumulax", + 0, 0, "tosmuleax", + 0, 0, "tosumuleax", + }; + + int p2; + + /* Do strength reduction if the value is constant and a power of two */ + if (flags & CF_CONST && (p2 = powerof2 (val)) >= 0) { + /* Generate a shift instead */ + g_asl (flags, p2); + return; + } + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + /* Handle some special cases */ + switch (val) { + + case 3: + AddCodeLine ("\tsta\ttmp1"); + AddCodeLine ("\tasl\ta"); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\ttmp1"); + return; + + case 5: + AddCodeLine ("\tsta\ttmp1"); + AddCodeLine ("\tasl\ta"); + AddCodeLine ("\tasl\ta"); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\ttmp1"); + return; + + case 10: + AddCodeLine ("\tsta\ttmp1"); + AddCodeLine ("\tasl\ta"); + AddCodeLine ("\tasl\ta"); + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\ttmp1"); + AddCodeLine ("\tasl\ta"); + return; + } + } + /* FALLTHROUGH */ + + case CF_INT: + break; + + case CF_LONG: + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + flags &= ~CF_FORCECHAR; // Handle chars as ints + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_div (unsigned flags, unsigned long val) +/* Primary = TOS / Primary */ +{ + static char* ops [12] = { + 0, "tosdiva0", "tosdivax", + 0, "tosudiva0", "tosudivax", + 0, 0, "tosdiveax", + 0, 0, "tosudiveax", + }; + + /* Do strength reduction if the value is constant and a power of two */ + int p2; + if ((flags & CF_CONST) && (p2 = powerof2 (val)) >= 0) { + /* Generate a shift instead */ + g_asr (flags, p2); + } else { + /* Generate a division */ + if (flags & CF_CONST) { + /* lhs is not on stack */ + flags &= ~CF_FORCECHAR; // Handle chars as ints + g_push (flags & ~CF_CONST, 0); + } + oper (flags, val, ops); + } +} + + + +void g_mod (unsigned flags, unsigned long val) +/* Primary = TOS % Primary */ +{ + static char* ops [12] = { + 0, "tosmoda0", "tosmodax", + 0, "tosumoda0", "tosumodax", + 0, 0, "tosmodeax", + 0, 0, "tosumodeax", + }; + int p2; + + /* Check if we can do some cost reduction */ + if ((flags & CF_CONST) && (flags & CF_UNSIGNED) && val != 0xFFFFFFFF && (p2 = powerof2 (val)) >= 0) { + /* We can do that with an AND operation */ + g_and (flags, val - 1); + } else { + /* Do it the hard way... */ + if (flags & CF_CONST) { + /* lhs is not on stack */ + flags &= ~CF_FORCECHAR; // Handle chars as ints + g_push (flags & ~CF_CONST, 0); + } + oper (flags, val, ops); + } +} + + + +void g_or (unsigned flags, unsigned long val) +/* Primary = TOS | Primary */ +{ + static char* ops [12] = { + 0, "tosora0", "tosorax", + 0, "tosora0", "tosorax", + 0, 0, "tosoreax", + 0, 0, "tosoreax", + }; + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + if ((val & 0xFF) != 0xFF) { + AddCodeLine ("\tora\t#$%02X", val & 0xFF); + } + return; + } + /* FALLTHROUGH */ + + case CF_INT: + if (val <= 0xFF) { + AddCodeLine ("\tora\t#$%02X", val & 0xFF); + return; + } + break; + + case CF_LONG: + if (val <= 0xFF) { + AddCodeLine ("\tora\t#$%02X", val & 0xFF); + return; + } + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_xor (unsigned flags, unsigned long val) +/* Primary = TOS ^ Primary */ +{ + static char* ops [12] = { + 0, "tosxora0", "tosxorax", + 0, "tosxora0", "tosxorax", + 0, 0, "tosxoreax", + 0, 0, "tosxoreax", + }; + + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + if ((val & 0xFF) != 0) { + AddCodeLine ("\teor\t#$%02X", val & 0xFF); + } + return; + } + /* FALLTHROUGH */ + + case CF_INT: + if (val <= 0xFF) { + if (val != 0) { + AddCodeLine ("\teor\t#$%02X", val); + } + return; + } else if ((val & 0xFF) == 0) { + AddCodeLine ("\tpha"); + AddCodeLine ("\ttxa"); + AddCodeLine ("\teor\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\ttax"); + AddCodeLine ("\tpla"); + return; + } + break; + + case CF_LONG: + if (val <= 0xFF) { + if (val != 0) { + AddCodeLine ("\teor\t#$%02X", val & 0xFF); + } + return; + } + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_and (unsigned flags, unsigned long val) +/* Primary = TOS & Primary */ +{ + static char* ops [12] = { + 0, "tosanda0", "tosandax", + 0, "tosanda0", "tosandax", + 0, 0, "tosandeax", + 0, 0, "tosandeax", + }; + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tand\t#$%02X", val & 0xFF); + return; + } + /* FALLTHROUGH */ + case CF_INT: + if ((val & 0xFFFF) != 0xFFFF) { + if (val <= 0xFF) { + ldxconst (0); + if (val == 0) { + ldaconst (0); + } else if (val != 0xFF) { + AddCodeLine ("\tand\t#$%02X", val & 0xFF); + } + } else if ((val & 0xFF00) == 0xFF00) { + AddCodeLine ("\tand\t#$%02X", val & 0xFF); + } else if ((val & 0x00FF) == 0x0000) { + AddCodeLine ("\ttxa"); + AddCodeLine ("\tand\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\ttax"); + ldaconst (0); + } else { + AddCodeLine ("\ttay"); + AddCodeLine ("\ttxa"); + AddCodeLine ("\tand\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\ttax"); + AddCodeLine ("\ttya"); + if ((val & 0x00FF) != 0x00FF) { + AddCodeLine ("\tand\t#$%02X", val & 0xFF); + } + } + } + return; + + case CF_LONG: + if (val <= 0xFF) { + ldxconst (0); + AddCodeLine ("\tstx\tsreg+1"); + AddCodeLine ("\tstx\tsreg"); + if ((val & 0xFF) != 0xFF) { + AddCodeLine ("\tand\t#$%02X", val & 0xFF); + } + return; + } else if (val == 0xFF00) { + ldaconst (0); + AddCodeLine ("\tsta\tsreg+1"); + AddCodeLine ("\tsta\tsreg"); + return; + } + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_asr (unsigned flags, unsigned long val) +/* Primary = TOS >> Primary */ +{ + static char* ops [12] = { + 0, "tosasra0", "tosasrax", + 0, "tosshra0", "tosshrax", + 0, 0, "tosasreax", + 0, 0, "tosshreax", + }; + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + if (val >= 1 && val <= 3) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tshrax%ld", val); + } else { + AddCodeLine ("\tjsr\tasrax%ld", val); + } + return; + } else if (val == 8 && (flags & CF_UNSIGNED)) { + AddCodeLine ("\ttxa"); + ldxconst (0); + return; + } + break; + + case CF_LONG: + if (val >= 1 && val <= 3) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tshreax%ld", val); + } else { + AddCodeLine ("\tjsr\tasreax%ld", val); + } + return; + } else if (val == 8 && (flags & CF_UNSIGNED)) { + AddCodeLine ("\ttxa"); + AddCodeLine ("\tldx\tsreg"); + AddCodeLine ("\tldy\tsreg+1"); + AddCodeLine ("\tsty\tsreg"); + AddCodeLine ("\tldy\t#$00"); + AddCodeLine ("\tsty\tsreg+1"); + return; + } else if (val == 16) { + AddCodeLine ("\tldy\t#$00"); + AddCodeLine ("\tldx\tsreg+1"); + if ((flags & CF_UNSIGNED) == 0) { + AddCodeLine ("\tbpl\t*+3"); + AddCodeLine ("\tdey"); + AddCodeHint ("y:!"); + } + AddCodeLine ("\tlda\tsreg"); + AddCodeLine ("\tsty\tsreg+1"); + AddCodeLine ("\tsty\tsreg"); + return; + } + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_asl (unsigned flags, unsigned long val) +/* Primary = TOS << Primary */ +{ + static char* ops [12] = { + 0, "tosasla0", "tosaslax", + 0, "tosshla0", "tosshlax", + 0, 0, "tosasleax", + 0, 0, "tosshleax", + }; + + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + if (val >= 1 && val <= 3) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tshlax%ld", val); + } else { + AddCodeLine ("\tjsr\taslax%ld", val); + } + return; + } else if (val == 8) { + AddCodeLine ("\ttax"); + AddCodeLine ("\tlda\t#$00"); + return; + } + break; + + case CF_LONG: + if (val >= 1 && val <= 3) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tshleax%ld", val); + } else { + AddCodeLine ("\tjsr\tasleax%ld", val); + } + return; + } else if (val == 8) { + AddCodeLine ("\tldy\tsreg"); + AddCodeLine ("\tsty\tsreg+1"); + AddCodeLine ("\tstx\tsreg"); + AddCodeLine ("\ttax"); + AddCodeLine ("\tlda\t#$00"); + return; + } else if (val == 16) { + AddCodeLine ("\tstx\tsreg+1"); + AddCodeLine ("\tsta\tsreg"); + AddCodeLine ("\tlda\t#$00"); + AddCodeLine ("\ttax"); + return; + } + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_neg (unsigned flags) +/* Primary = -Primary */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + AddCodeLine ("\tjsr\tnegax"); + break; + + case CF_LONG: + AddCodeLine ("\tjsr\tnegeax"); + break; + + default: + typeerror (flags); + } +} + + + +void g_bneg (unsigned flags) +/* Primary = !Primary */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + AddCodeLine ("\tjsr\tbnega"); + break; + + case CF_INT: + AddCodeLine ("\tjsr\tbnegax"); + break; + + case CF_LONG: + AddCodeLine ("\tjsr\tbnegeax"); + break; + + default: + typeerror (flags); + } +} + + + +void g_com (unsigned flags) +/* Primary = ~Primary */ +{ + switch (flags & CF_TYPE) { + + case CF_CHAR: + case CF_INT: + AddCodeLine ("\tjsr\tcomplax"); + break; + + case CF_LONG: + AddCodeLine ("\tjsr\tcompleax"); + break; + + default: + typeerror (flags); + } +} + + + +void g_inc (unsigned flags, unsigned long val) +/* Increment the primary register by a given number */ +{ + /* Don't inc by zero */ + if (val == 0) { + return; + } + + /* Generate code for the supported types */ + flags &= ~CF_CONST; + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t#$%02X", val & 0xFF); + break; + } + /* FALLTHROUGH */ + + case CF_INT: + if (FavourSize) { + /* Use jsr calls */ + if (val <= 8) { + AddCodeLine ("\tjsr\tincax%u", val); + } else if (val <= 255) { + ldyconst (val); + AddCodeLine ("\tjsr\tincaxy"); + } else { + g_add (flags | CF_CONST, val); + } + } else { + /* Inline the code */ + if (val < 0x300) { + if ((val & 0xFF) != 0) { + AddCodeLine ("\tclc"); + AddCodeLine ("\tadc\t#$%02X", (unsigned char) val); + AddCodeLine ("\tbcc\t*+3"); + AddCodeLine ("\tinx"); + /* Tell the optimizer that the X register may be invalid */ + AddCodeHint ("x:!"); + } + if (val >= 0x100) { + AddCodeLine ("\tinx"); + } + if (val >= 0x200) { + AddCodeLine ("\tinx"); + } + } else { + AddCodeLine ("\tclc"); + if ((val & 0xFF) != 0) { + AddCodeLine ("\tadc\t#$%02X", (unsigned char) val); + /* Tell the optimizer that the X register may be invalid */ + AddCodeHint ("x:!"); + } + AddCodeLine ("\tpha"); + AddCodeLine ("\ttxa"); + AddCodeLine ("\tadc\t#$%02X", (unsigned char) (val >> 8)); + AddCodeLine ("\ttax"); + AddCodeLine ("\tpla"); + } + } + break; + + case CF_LONG: + if (val <= 255) { + ldyconst (val); + AddCodeLine ("\tjsr\tinceaxy"); + } else { + g_add (flags | CF_CONST, val); + } + break; + + default: + typeerror (flags); + + } +} + + + +void g_dec (unsigned flags, unsigned long val) +/* Decrement the primary register by a given number */ +{ + /* Generate code for the supported types */ + flags &= ~CF_CONST; + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tsec"); + AddCodeLine ("\tsbc\t#$%02X", val & 0xFF); + break; + } + /* FALLTHROUGH */ + + case CF_INT: + if (val <= 2) { + AddCodeLine ("\tjsr\tdecax%d", (int) val); + } else if (val <= 255) { + ldyconst (val); + AddCodeLine ("\tjsr\tdecaxy"); + } else { + g_sub (flags | CF_CONST, val); + } + break; + + case CF_LONG: + if (val <= 255) { + ldyconst (val); + AddCodeLine ("\tjsr\tdeceaxy"); + } else { + g_sub (flags | CF_CONST, val); + } + break; + + default: + typeerror (flags); + + } +} + + + +/* + * Following are the conditional operators. They compare the TOS against + * the primary and put a literal 1 in the primary if the condition is + * true, otherwise they clear the primary register + */ + + + +void g_eq (unsigned flags, unsigned long val) +/* Test for equal */ +{ + static char* ops [12] = { + "toseq00", "toseqa0", "toseqax", + "toseq00", "toseqa0", "toseqax", + 0, 0, "toseqeax", + 0, 0, "toseqeax", + }; + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tbooleq"); + return; + } + /* FALLTHROUGH */ + + case CF_INT: + AddCodeLine ("\tcpx\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tbne\t*+4"); + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tbooleq"); + return; + + case CF_LONG: + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_ne (unsigned flags, unsigned long val) +/* Test for not equal */ +{ + static char* ops [12] = { + "tosne00", "tosnea0", "tosneax", + "tosne00", "tosnea0", "tosneax", + 0, 0, "tosneeax", + 0, 0, "tosneeax", + }; + + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tboolne"); + return; + } + /* FALLTHROUGH */ + + case CF_INT: + AddCodeLine ("\tcpx\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tbne\t*+4"); + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tboolne"); + return; + + case CF_LONG: + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_lt (unsigned flags, unsigned long val) +/* Test for less than */ +{ + static char* ops [12] = { + "toslt00", "toslta0", "tosltax", + "tosult00", "tosulta0", "tosultax", + 0, 0, "toslteax", + 0, 0, "tosulteax", + }; + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + /* Give a warning in some special cases */ + if ((flags & CF_UNSIGNED) && val == 0) { + Warning (WARN_COND_NEVER_TRUE); + } + + /* Look at the type */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tboolult"); + } else { + AddCodeLine ("\tjsr\tboollt"); + } + return; + } + /* FALLTHROUGH */ + + case CF_INT: + if ((flags & CF_UNSIGNED) == 0 && val == 0) { + /* If we have a signed compare against zero, we only need to + * test the high byte. + */ + AddCodeLine ("\ttxa"); + AddCodeLine ("\tjsr\tboollt"); + return; + } + /* Direct code only for unsigned data types */ + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tcpx\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tbne\t*+4"); + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tboolult"); + return; + } + break; + + case CF_LONG: + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_le (unsigned flags, unsigned long val) +/* Test for less than or equal to */ +{ + static char* ops [12] = { + "tosle00", "toslea0", "tosleax", + "tosule00", "tosulea0", "tosuleax", + 0, 0, "tosleeax", + 0, 0, "tosuleeax", + }; + + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + /* Look at the type */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tboolule"); + } else { + AddCodeLine ("\tjsr\tboolle"); + } + return; + } + /* FALLTHROUGH */ + + case CF_INT: + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tcpx\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tbne\t*+4"); + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tboolule"); + return; + } + break; + + case CF_LONG: + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_gt (unsigned flags, unsigned long val) +/* Test for greater than */ +{ + static char* ops [12] = { + "tosgt00", "tosgta0", "tosgtax", + "tosugt00", "tosugta0", "tosugtax", + 0, 0, "tosgteax", + 0, 0, "tosugteax", + }; + + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + /* Look at the type */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + if (flags & CF_UNSIGNED) { + /* If we have a compare > 0, we will replace it by + * != 0 here, since both are identical but the latter + * is easier to optimize. + */ + if (val & 0xFF) { + AddCodeLine ("\tjsr\tboolugt"); + } else { + AddCodeLine ("\tjsr\tboolne"); + } + } else { + AddCodeLine ("\tjsr\tboolgt"); + } + return; + } + /* FALLTHROUGH */ + + case CF_INT: + if (flags & CF_UNSIGNED) { + /* If we have a compare > 0, we will replace it by + * != 0 here, since both are identical but the latter + * is easier to optimize. + */ + if ((val & 0xFFFF) == 0) { + AddCodeLine ("\tstx\ttmp1"); + AddCodeLine ("\tora\ttmp1"); + AddCodeLine ("\tjsr\tboolne"); + } else { + AddCodeLine ("\tcpx\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tbne\t*+4"); + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tboolugt"); + } + return; + } + break; + + case CF_LONG: + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +void g_ge (unsigned flags, unsigned long val) +/* Test for greater than or equal to */ +{ + static char* ops [12] = { + "tosge00", "tosgea0", "tosgeax", + "tosuge00", "tosugea0", "tosugeax", + 0, 0, "tosgeeax", + 0, 0, "tosugeeax", + }; + + + /* If the right hand side is const, the lhs is not on stack but still + * in the primary register. + */ + if (flags & CF_CONST) { + + /* Give a warning in some special cases */ + if ((flags & CF_UNSIGNED) && val == 0) { + Warning (WARN_COND_ALWAYS_TRUE); + } + + /* Look at the type */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tjsr\tbooluge"); + } else { + AddCodeLine ("\tjsr\tboolge"); + } + return; + } + /* FALLTHROUGH */ + + case CF_INT: + if (flags & CF_UNSIGNED) { + AddCodeLine ("\tcpx\t#$%02X", (val >> 8) & 0xFF); + AddCodeLine ("\tbne\t*+4"); + AddCodeLine ("\tcmp\t#$%02X", val & 0xFF); + AddCodeLine ("\tjsr\tbooluge"); + return; + } + break; + + case CF_LONG: + break; + + default: + typeerror (flags); + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + * into the normal, non-optimized stuff. + */ + g_push (flags & ~CF_CONST, 0); + + } + + /* Use long way over the stack */ + oper (flags, val, ops); +} + + + +/*****************************************************************************/ +/* Allocating static storage */ +/*****************************************************************************/ + + + +void g_res (unsigned n) +/* reserve static storage, n bytes */ +{ + AddCodeLine ("\t.res\t%u", n); +} + + + +void g_defdata (unsigned flags, unsigned long val, unsigned offs) +/* Define data with the size given in flags */ +{ + if (flags & CF_CONST) { + + /* Numeric constant */ + switch (flags & CF_TYPE) { + + case CF_CHAR: + AddCodeLine ("\t.byte\t$%02lX", val & 0xFF); + break; + + case CF_INT: + AddCodeLine ("\t.word\t$%04lX", val & 0xFFFF); + break; + + case CF_LONG: + AddCodeLine ("\t.dword\t$%08lX", val & 0xFFFFFFFF); + break; + + default: + typeerror (flags); + break; + + } + + } else { + + /* Create the correct label name */ + const char* Label = GetLabelName (flags, val, offs); + + /* Labels are always 16 bit */ + AddCodeLine ("\t.word\t%s", Label); + + } +} + + + +void g_defbytes (const unsigned char* Bytes, unsigned Count) +/* output a row of bytes as a constant */ +{ + unsigned Chunk; + char Buf [128]; + char* B; + + /* Output the stuff */ + while (Count) { + + /* How many go into this line? */ + if ((Chunk = Count) > 16) { + Chunk = 16; + } + Count -= Chunk; + + /* Output one line */ + strcpy (Buf, "\t.byte\t"); + B = Buf + 7; + do { + B += sprintf (B, "$%02X", *Bytes++ & 0xFF); + if (--Chunk) { + *B++ = ','; + } + } while (Chunk); + + /* Output the line */ + AddCodeLine (Buf); + } +} + + + +void g_zerobytes (unsigned n) +/* Output n bytes of data initialized with zero */ +{ + AddCodeLine ("\t.res\t%u", n); +} + + + +/*****************************************************************************/ +/* Inlined known functions */ +/*****************************************************************************/ + + + +void g_strlen (unsigned flags, unsigned long val, unsigned offs) +/* Inline the strlen() function */ +{ + /* We need a label in both cases */ + unsigned label = GetLabel (); + + /* Two different encodings */ + if (flags & CF_CONST) { + + /* The address of the string is constant. Create the correct label name */ + char* lbuf = GetLabelName (flags, val, offs); + + /* Generate the strlen code */ + AddCodeLine ("\tldy\t#$FF"); + g_defloclabel (label); + AddCodeLine ("\tiny"); + AddCodeLine ("\tlda\t%s,y", lbuf); + AddCodeLine ("\tbne\tL%04X", label); + AddCodeLine ("\ttya"); + AddCodeLine ("\tldx\t#$00"); + + } else { + + /* Address not constant but in primary */ + if (FavourSize) { + /* This is too much code, so call strlen instead of inlining */ + AddCodeLine ("\tjsr\t_strlen"); + } else { + /* Inline the function */ + AddCodeLine ("\tsta\tptr1"); + AddCodeLine ("\tstx\tptr1+1"); + AddCodeLine ("\tldy\t#$FF"); + g_defloclabel (label); + AddCodeLine ("\tiny"); + AddCodeLine ("\tlda\t(ptr1),y"); + AddCodeLine ("\tbne\tL%04X", label); + AddCodeLine ("\ttya"); + AddCodeLine ("\tldx\t#$00"); + } + } +} + + + diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h new file mode 100644 index 000000000..33bad1d22 --- /dev/null +++ b/src/cc65/codegen.h @@ -0,0 +1,395 @@ +/* + * codegen.h + * + * Ullrich von Bassewitz, 04.06.1998 + */ + + + +#ifndef CODEGEN_H +#define CODEGEN_H + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Code generator flags. + * Note: The type flags are designed so that a smaller type may override a + * larger one by or'ing it into the existing one. + */ +#define CF_NONE 0x0000 /* No special flags */ + +#define CF_TYPE 0x000F /* Mask for operand type */ +#define CF_CHAR 0x0003 /* Operation on characters */ +#define CF_INT 0x0001 /* Operation on ints */ +#define CF_PTR CF_INT /* Alias for readability */ +#define CF_LONG 0x0000 /* Operation on longs */ + +#define CF_UNSIGNED 0x0010 /* Value is unsigned */ +#define CF_CONST 0x0020 /* Constant value available */ +#define CF_CONSTADDR 0x0040 /* Constant address value available */ +#define CF_TEST 0x0080 /* Test value */ +#define CF_FIXARGC 0x0100 /* Function has fixed arg count */ +#define CF_FORCECHAR 0x0200 /* Handle chars as chars, not ints */ +#define CF_SHORT 0x0400 /* Use short addressing */ +#define CF_REG 0x0800 /* Value is in primary register */ + +/* Type of static address */ +#define CF_ADDRMASK 0xF000 /* Type of address */ +#define CF_STATIC 0x0000 /* Static local */ +#define CF_EXTERNAL 0x1000 /* Static external */ +#define CF_ABSOLUTE 0x2000 /* Numeric absolute address */ +#define CF_LOCAL 0x4000 /* Auto variable */ +#define CF_REGVAR 0x8000 /* Register variable */ + + + +/* Compiler relative stackpointer */ +extern int oursp; + + + +/*****************************************************************************/ +/* Pre- and postamble */ +/*****************************************************************************/ + + + +void g_preamble (void); +/* Generate the assembler code preamble */ + +void g_postamble (void); +/* Generate assembler code postamble */ + + + +/*****************************************************************************/ +/* Segment support */ +/*****************************************************************************/ + + + +void g_usecode (void); +/* Switch to the code segment */ + +void g_userodata (void); +/* Switch to the read only data segment */ + +void g_usedata (void); +/* Switch to the data segment */ + +void g_usebss (void); +/* Switch to the bss segment */ + +void g_codename (const char* Name); +/* Set the name of the CODE segment */ + +void g_rodataname (const char* Name); +/* Set the name of the RODATA segment */ + +void g_dataname (const char* Name); +/* Set the name of the DATA segment */ + +void g_bssname (const char* Name); +/* Set the name of the BSS segment */ + + + +/*****************************************************************************/ +/* Functions handling local labels */ +/*****************************************************************************/ + + + +void g_defloclabel (unsigned label); +/* Define a local label */ + + + +/*****************************************************************************/ +/* Functions handling global labels */ +/*****************************************************************************/ + + + +void g_defgloblabel (const char* Name); +/* Define a global label with the given name */ + +void g_defexport (const char* Name, int ZP); +/* Export the given label */ + +void g_defimport (const char* Name, int ZP); +/* Import the given label */ + + + +/*****************************************************************************/ +/* stack */ +/*****************************************************************************/ + + + +int pop (unsigned flags); +/* Pop an argument of the given size */ + +int push (unsigned flags); +/* Push an argument of the given size */ + +unsigned sizeofarg (unsigned flags); +/* Return the size of a function argument type that is encoded in flags */ + + + +/*****************************************************************************/ +/* type conversion and similiar stuff */ +/*****************************************************************************/ + + + +void g_toslong (unsigned flags); +/* Make sure, the value on TOS is a long. Convert if necessary */ + +void g_tosint (unsigned flags); +/* Make sure, the value on TOS is an int. Convert if necessary */ + +void g_reglong (unsigned flags); +/* Make sure, the value in the primary register a long. Convert if necessary */ + +unsigned g_typeadjust (unsigned lhs, unsigned rhs); +/* Adjust the integer operands before doing a binary operation. lhs is a flags + * value, that corresponds to the value on TOS, rhs corresponds to the value + * in (e)ax. The return value is the the flags value for the resulting type. + */ + +unsigned g_typecast (unsigned lhs, unsigned rhs); +/* Cast the value in the primary register to the operand size that is flagged + * by the lhs value. Return the result value. + */ + +void g_scale (unsigned flags, long val); +/* Scale the value in the primary register by the given value. If val is positive, + * scale up, is val is negative, scale down. This function is used to scale + * the operands or results of pointer arithmetic by the size of the type, the + * pointer points to. + */ + + + +/*****************************************************************************/ +/* Function entry and exit */ +/*****************************************************************************/ + + + +void g_enter (unsigned flags, const char* Name, unsigned argsize); +/* Function prologue */ + +void g_leave (int flags, int val); +/* Function epilogue */ + + + +/*****************************************************************************/ +/* Register variables */ +/*****************************************************************************/ + + + +void g_save_regvars (int RegOffs, unsigned Bytes); +/* Save register variables */ + +void g_restore_regvars (int StackOffs, int RegOffs, unsigned Bytes); +/* Restore register variables */ + + + +/*****************************************************************************/ +/* Fetching memory cells */ +/*****************************************************************************/ + + + +void g_getimmed (unsigned flags, unsigned long val, unsigned offs); +void g_getstatic (unsigned flags, unsigned long label, unsigned offs); +void g_getlocal (unsigned flags, int offs); +void g_getind (unsigned flags, unsigned offs); +void g_leasp (int offs); + + + +/*****************************************************************************/ +/* Store into memory */ +/*****************************************************************************/ + + + +void g_putstatic (unsigned flags, unsigned long label, unsigned offs); +/* Store the primary register into the specified static memory cell */ + +void g_putlocal (unsigned flags, int offs); +/* Put data into local object. */ + +void g_putind (unsigned flags, unsigned offs); +/* Store the specified object type in the primary register at the address + * on the top of the stack + */ + + + +/*****************************************************************************/ +/* Adds and subs of variables fix a fixed address */ +/*****************************************************************************/ + + + +void g_addlocal (unsigned flags, int offs); +/* Add a local variable to ax */ + +void g_addstatic (unsigned flags, unsigned long label, unsigned offs); +/* Add a static variable to ax */ + + + +/*****************************************************************************/ +/* Compares of ax with a variable with fixed address */ +/*****************************************************************************/ + + + +void g_cmplocal (unsigned flags, int offs); +/* Compare a local variable to ax */ + +void g_cmpstatic (unsigned flags, unsigned label, unsigned offs); +/* Compare a static variable to ax */ + + + +/*****************************************************************************/ +/* Special op= functions */ +/*****************************************************************************/ + + + +void g_addeqstatic (unsigned flags, unsigned long label, unsigned offs, + unsigned long val); +/* Emit += for a static variable */ + +void g_addeqlocal (unsigned flags, int offs, unsigned long val); +/* Emit += for a local variable */ + +void g_addeqind (unsigned flags, unsigned offs, unsigned long val); +/* Emit += for the location with address in ax */ + +void g_subeqstatic (unsigned flags, unsigned long label, unsigned offs, + unsigned long val); +/* Emit -= for a static variable */ + +void g_subeqlocal (unsigned flags, int offs, unsigned long val); +/* Emit -= for a local variable */ + +void g_subeqind (unsigned flags, unsigned offs, unsigned long val); +/* Emit -= for the location with address in ax */ + + + +/*****************************************************************************/ +/* Add a variable address to the value in ax */ +/*****************************************************************************/ + + + +void g_addaddr_local (unsigned flags, int offs); +/* Add the address of a local variable to ax */ + +void g_addaddr_static (unsigned flags, unsigned long label, unsigned offs); +/* Add the address of a static variable to ax */ + + + +/*****************************************************************************/ +/* */ +/*****************************************************************************/ + + + +void g_save (unsigned flags); +void g_restore (unsigned flags); + +void g_cmp (unsigned flags, unsigned long val); +/* Immidiate compare. The primary register will not be changed, Z flag + * will be set. + */ + +void g_test (unsigned flags); +void g_push (unsigned flags, unsigned long val); +void g_swap (unsigned flags); +void g_call (unsigned flags, char *lbl, unsigned argsize); +void g_callind (unsigned flags, unsigned argsize); +void g_jump (unsigned label); +void g_switch (unsigned flags); + +void g_case (unsigned flags, unsigned label, unsigned long val); +/* Create table code for one case selector */ + +void g_truejump (unsigned flags, unsigned label); +/* Jump to label if zero flag clear */ + +void g_falsejump (unsigned flags, unsigned label); +/* Jump to label if zero flag set */ + +void g_space (int space); +/* Create or drop space on the stack */ + +void g_add (unsigned flags, unsigned long val); +void g_sub (unsigned flags, unsigned long val); +void g_rsub (unsigned flags, unsigned long val); +void g_mul (unsigned flags, unsigned long val); +void g_div (unsigned flags, unsigned long val); +void g_mod (unsigned flags, unsigned long val); +void g_or (unsigned flags, unsigned long val); +void g_xor (unsigned flags, unsigned long val); +void g_and (unsigned flags, unsigned long val); +void g_asr (unsigned flags, unsigned long val); +void g_asl (unsigned flags, unsigned long val); +void g_neg (unsigned flags); +void g_bneg (unsigned flags); +void g_com (unsigned flags); +void g_inc (unsigned flags, unsigned long n); +void g_dec (unsigned flags, unsigned long n); +void g_eq (unsigned flags, unsigned long val); +void g_ne (unsigned flags, unsigned long val); +void g_lt (unsigned flags, unsigned long val); +void g_le (unsigned flags, unsigned long val); +void g_gt (unsigned flags, unsigned long val); +void g_ge (unsigned flags, unsigned long val); +void g_makebool (unsigned flags); +void outdat (int n); +void g_res (unsigned n); + +void g_defdata (unsigned flags, unsigned long val, unsigned offs); +/* Define data with the size given in flags */ + +void g_defbytes (const unsigned char *bytes, unsigned count); +void g_zerobytes (unsigned n); + + + +/*****************************************************************************/ +/* Inlined known functions */ +/*****************************************************************************/ + + + +void g_strlen (unsigned flags, unsigned long val, unsigned offs); +/* Inline the strlen() function */ + + + +/* End of codegen.h */ +#endif + + diff --git a/src/cc65/copyleft.jrd b/src/cc65/copyleft.jrd new file mode 100644 index 000000000..f6d33c4e6 --- /dev/null +++ b/src/cc65/copyleft.jrd @@ -0,0 +1,32 @@ +-*- Mode: Text -*- + + This is the copyright notice for RA65, LINK65, LIBR65, and other +Atari 8-bit programs. Said programs are Copyright 1989, by John R. +Dunning. All rights reserved, with the following exceptions: + + Anyone may copy or redistribute these programs, provided that: + +1: You don't charge anything for the copy. It is permissable to + charge a nominal fee for media, etc. + +2: All source code and documentation for the programs is made + available as part of the distribution. + +3: This copyright notice is preserved verbatim, and included in + the distribution. + + You are allowed to modify these programs, and redistribute the +modified versions, provided that the modifications are clearly noted. + + There is NO WARRANTY with this software, it comes as is, and is +distributed in the hope that it may be useful. + + This copyright notice applies to any program which contains +this text, or the refers to this file. + + This copyright notice is based on the one published by the Free +Software Foundation, sometimes known as the GNU project. The idea +is the same as theirs, ie the software is free, and is intended to +stay that way. Everybody has the right to copy, modify, and re- +distribute this software. Nobody has the right to prevent anyone +else from copying, modifying or redistributing it. diff --git a/src/cc65/ctrans.c b/src/cc65/ctrans.c new file mode 100644 index 000000000..1c7f4841f --- /dev/null +++ b/src/cc65/ctrans.c @@ -0,0 +1,137 @@ +/*****************************************************************************/ +/* */ +/* ctrans.c */ +/* */ +/* Character set translation for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "global.h" +#include "ctrans.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +static unsigned char CTNone [256] = { + /* No system - no translation */ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; + +static unsigned char CTAtari [256] = { + /* ASCII -> ATASCII */ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x7E,0x08,0x7F,0x9B,0x0B,0x7D,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; + +static unsigned char CTPET [256] = { + /* ASCII -> PETSCII */ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x14,0x09,0x0D,0x11,0x93,0x0A,0x0E,0x0F, + 0x10,0x0B,0x12,0x13,0x08,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0x5B,0x5C,0x5D,0x5E,0x5F, + 0xC0,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0xDB,0xDC,0xDD,0xDE,0xDF, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x0C,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; + +static unsigned char* CTab [TGT_COUNT] = { + CTNone, /* No system */ + CTAtari, /* Atari */ + CTPET, /* C64 */ + CTPET, /* C128 */ + CTPET, /* ACE */ + CTPET, /* Plus/4 */ + CTPET, /* CBM610 */ + CTPET, /* PET */ + CTNone, /* NES */ + CTNone, /* Apple2 */ + CTPET, /* GEOS */ +}; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +int ctrans (unsigned char C) +/* Translate a character from source charset into target charset */ +{ + /* Translate for the given system */ + return CTab [Target][C]; +} + + + diff --git a/src/cc65/ctrans.h b/src/cc65/ctrans.h new file mode 100644 index 000000000..01ec28931 --- /dev/null +++ b/src/cc65/ctrans.h @@ -0,0 +1,57 @@ +/*****************************************************************************/ +/* */ +/* ctrans.h */ +/* */ +/* Character set translation for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef CTRANS_H +#define CTRANS_H + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +int ctrans (unsigned char c); +/* Translate a character from source charset into target charset */ + + + +/* End of ctrans.h */ + +#endif + + + diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c new file mode 100644 index 000000000..21d2320de --- /dev/null +++ b/src/cc65/datatype.c @@ -0,0 +1,568 @@ +/*****************************************************************************/ +/* */ +/* datatype.c */ +/* */ +/* Type string handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "check.h" +#include "codegen.h" +#include "datatype.h" +#include "error.h" +#include "funcdesc.h" +#include "global.h" +#include "mem.h" +#include "util.h" +#include "symtab.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Predefined type strings */ +type type_int [] = { T_INT, T_END }; +type type_uint [] = { T_UINT, T_END }; +type type_long [] = { T_LONG, T_END }; +type type_ulong [] = { T_ULONG, T_END }; +type type_void [] = { T_VOID, T_END }; +type type_pschar [] = { T_PTR, T_CHAR, T_END }; +type type_puchar [] = { T_PTR, T_UCHAR, T_END }; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned TypeLen (const type* T) +/* Return the length of the type string */ +{ + const type* Start = T; + while (*T) { + ++T; + } + return T - Start; +} + + + +int TypeCmp (const type* T1, const type* T2) +/* Compare two type strings */ +{ + int A, B, D; + do { + A = *T1++; + B = *T2++; + D = A - B; + } while (D == 0 && A != 0); + return D; +} + + + +type* TypeCpy (type* Dest, const type* Src) +/* Copy a type string */ +{ + type T; + type* Orig = Dest; + do { + T = *Src++; + *Dest++ = T; + } while (T); + return Orig; +} + + + +type* TypeCat (type* Dest, const type* Src) +/* Append Src */ +{ + TypeCpy (Dest + TypeLen (Dest), Src); + return Dest; +} + + + +type* TypeDup (const type* T) +/* Create a copy of the given type on the heap */ +{ + unsigned Len = (TypeLen (T) + 1) * sizeof (type); + return memcpy (xmalloc (Len), T, Len); +} + + + +type* TypeAlloc (unsigned Len) +/* Allocate memory for a type string of length Len. Len *must* include the + * trailing T_END. + */ +{ + return xmalloc (Len * sizeof (type)); +} + + + +void TypeFree (type* T) +/* Free a type string */ +{ + xfree (T); +} + + + +type GetDefaultChar (void) +/* Return the default char type (signed/unsigned) depending on the settings */ +{ + return SignedChars? T_CHAR : T_UCHAR; +} + + + +type* GetCharArrayType (unsigned Len) +/* Return the type for a char array of the given length */ +{ + /* Allocate memory for the type string */ + type* T = TypeAlloc (1 + DECODE_SIZE + 2); + + /* Fill the type string */ + T [0] = T_ARRAY; + T [DECODE_SIZE+1] = GetDefaultChar(); + T [DECODE_SIZE+2] = T_END; + + /* Encode the length in the type string */ + Encode (T+1, Len); + + /* Return the new type */ + return T; +} + + + +type* GetImplicitFuncType (void) +/* Return a type string for an inplicitly declared function */ +{ + /* Get a new function descriptor */ + FuncDesc* F = NewFuncDesc (); + + /* Allocate memory for the type string */ + type* T = TypeAlloc (1 + DECODE_SIZE + 2); + + /* Prepare the function descriptor */ + F->Flags = FD_IMPLICIT | FD_ELLIPSIS; + F->SymTab = &EmptySymTab; + F->StructTab = &EmptySymTab; + F->EnumTab = &EmptySymTab; + + /* Fill the type string */ + T [0] = T_FUNC; + T [DECODE_SIZE+1] = T_INT; + T [DECODE_SIZE+2] = T_END; + + /* Encode the function descriptor into the type string */ + EncodePtr (T+1, F); + + /* Return the new type */ + return T; +} + + + +void PrintType (FILE* F, const type* tarray) +/* Output translation of type array. */ +{ + const type* p; + + for (p = tarray; *p != T_END; ++p) { + if (*p & T_UNSIGNED) { + fprintf (F, "unsigned "); + } + switch (*p) { + case T_VOID: + fprintf (F, "void\n"); + break; + case T_CHAR: + case T_UCHAR: + fprintf (F, "char\n"); + break; + case T_INT: + case T_UINT: + fprintf (F, "int\n"); + break; + case T_SHORT: + case T_USHORT: + fprintf (F, "short\n"); + break; + case T_LONG: + case T_ULONG: + fprintf (F, "long\n"); + break; + case T_FLOAT: + fprintf (F, "float\n"); + break; + case T_DOUBLE: + fprintf (F, "double\n"); + break; + case T_PTR: + fprintf (F, "pointer to "); + break; + case T_ARRAY: + fprintf (F, "array[%lu] of ", Decode (p + 1)); + p += DECODE_SIZE; + break; + case T_STRUCT: + fprintf (F, "struct %s\n", ((SymEntry*) Decode (p + 1))->Name); + p += DECODE_SIZE; + break; + case T_UNION: + fprintf (F, "union %s\n", ((SymEntry*) Decode (p + 1))->Name); + p += DECODE_SIZE; + break; + case T_FUNC: + fprintf (F, "function returning "); + p += DECODE_SIZE; + break; + default: + fprintf (F, "unknown type: %04X\n", *p); + } + } +} + + + +void PrintRawType (FILE* F, const type* Type) +/* Print a type string in raw format (for debugging) */ +{ + while (*Type != T_END) { + fprintf (F, "%04X ", *Type++); + } + fprintf (F, "\n"); +} + + + +void Encode (type* Type, unsigned long Val) +/* Encode p[0] and p[1] so that neither p[0] nore p[1] is zero */ +{ + int I; + for (I = 0; I < DECODE_SIZE; ++I) { + *Type++ = ((type) Val) | 0x8000; + Val >>= 15; + } +} + + + +void EncodePtr (type* Type, void* P) +/* Encode a pointer into a type array */ +{ + Encode (Type, (unsigned long) P); +} + + + +unsigned long Decode (const type* Type) +/* Decode */ +{ + int I; + unsigned long Val = 0; + for (I = DECODE_SIZE-1; I >= 0; I--) { + Val <<= 15; + Val |= (Type[I] & 0x7FFF); + } + return Val; +} + + + +void* DecodePtr (const type* Type) +/* Decode a pointer from a type array */ +{ + return (void*) Decode (Type); +} + + + +int HasEncode (const type* Type) +/* Return true if the given type has encoded data */ +{ + return IsStruct (Type) || IsArray (Type) || IsFunc (Type); +} + + + +void CopyEncode (const type* Source, type* Target) +/* Copy encoded data from Source to Target */ +{ + memcpy (Target, Source, DECODE_SIZE * sizeof (type)); +} + + + +unsigned SizeOf (const type* tarray) +/* Compute size of object represented by type array. */ +{ + SymEntry* Entry; + + switch (*tarray) { + + case T_VOID: + return 0; + + case T_CHAR: + case T_UCHAR: + return 1; + + case T_INT: + case T_UINT: + case T_SHORT: + case T_USHORT: + case T_PTR: + case T_ENUM: + return 2; + + case T_LONG: + case T_ULONG: + return 4; + + case T_ARRAY: + return (Decode (tarray + 1) * SizeOf (tarray + DECODE_SIZE + 1)); + + case T_STRUCT: + case T_UNION: + Entry = DecodePtr (tarray+1); + return Entry->V.S.Size; + + default: + Internal ("Unknown type: %04X", *tarray); + return 0; + + } +} + + + +unsigned PSizeOf (const type* tptr) +/* Compute size of pointer object. */ +{ + /* We are expecting a pointer expression */ + CHECK (*tptr & T_POINTER); + + /* Skip the pointer or array token itself */ + if (*tptr == T_ARRAY) { + return SizeOf (tptr + DECODE_SIZE + 1); + } else { + return SizeOf (tptr + 1); + } +} + + + +unsigned TypeOf (const type* Type) +/* Get the code generator base type of the object */ +{ + FuncDesc* F; + + switch (*Type) { + + case T_CHAR: + return CF_CHAR; + + case T_UCHAR: + return CF_CHAR | CF_UNSIGNED; + + case T_SHORT: + case T_INT: + case T_ENUM: + return CF_INT; + + case T_USHORT: + case T_UINT: + case T_PTR: + case T_ARRAY: + return CF_INT | CF_UNSIGNED; + + case T_LONG: + return CF_LONG; + + case T_ULONG: + return CF_LONG | CF_UNSIGNED; + + case T_FUNC: + F = DecodePtr (Type+1); + return (F->Flags & FD_ELLIPSIS)? 0 : CF_FIXARGC; + + case T_STRUCT: + case T_UNION: + /* Address of ... */ + return CF_INT | CF_UNSIGNED; + + default: + Error (ERR_ILLEGAL_TYPE); + return CF_INT; + } +} + + + +type* Indirect (type* Type) +/* Do one indirection for the given type, that is, return the type where the + * given type points to. + */ +{ + /* We are expecting a pointer expression */ + CHECK (Type[0] & T_POINTER); + + /* Skip the pointer or array token itself */ + if (Type[0] == T_ARRAY) { + return Type + DECODE_SIZE + 1; + } else { + return Type + 1; + } +} + + + +int IsVoid (const type* Type) +/* Return true if this is a void type */ +{ + return (Type[0] == T_VOID && Type[1] == T_END); +} + + + +int IsPtr (const type* Type) +/* Return true if this is a pointer type */ +{ + return (Type[0] & T_POINTER) != 0; +} + + + +int IsChar (const type* Type) +/* Return true if this is a character type */ +{ + return (Type[0] == T_CHAR || Type[0] == T_UCHAR) && Type[1] == T_END; +} + + + +int IsInt (const type* Type) +/* Return true if this is an integer type */ +{ + return (Type[0] & T_INTEGER) != 0; +} + + + +int IsLong (const type* Type) +/* Return true if this is a long type (signed or unsigned) */ +{ + return (Type[0] & T_LONG) == T_LONG; +} + + + +int IsUnsigned (const type* Type) +/* Return true if this is an unsigned type */ +{ + return (Type[0] & T_UNSIGNED) != 0; +} + + + +int IsStruct (const type* Type) +/* Return true if this is a struct type */ +{ + return (Type[0] == T_STRUCT || Type[0] == T_UNION); +} + + + +int IsFunc (const type* Type) +/* Return true if this is a function type */ +{ + return (Type[0] == T_FUNC); +} + + + +int IsFastCallFunc (const type* Type) +/* Return true if this is a function type with __fastcall__ calling conventions */ +{ + FuncDesc* F; + CHECK (*Type == T_FUNC); + F = DecodePtr (Type+1); + return (F->Flags & FD_FASTCALL) != 0; +} + + + +int IsFuncPtr (const type* Type) +/* Return true if this is a function pointer */ +{ + return (Type[0] == T_PTR && Type[1] == T_FUNC); +} + + + +int IsArray (const type* Type) +/* Return true if this is an array type */ +{ + return (Type[0] == T_ARRAY); +} + + + +struct FuncDesc* GetFuncDesc (const type* Type) +/* Get the FuncDesc pointer from a function or pointer-to-function type */ +{ + if (Type[0] == T_PTR) { + /* Pointer to function */ + ++Type; + } + + /* Be sure it's a function type */ + CHECK (Type[0] == T_FUNC); + + /* Decode the function descriptor and return it */ + return DecodePtr (Type+1); +} + + + diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h new file mode 100644 index 000000000..60e618f00 --- /dev/null +++ b/src/cc65/datatype.h @@ -0,0 +1,223 @@ +/*****************************************************************************/ +/* */ +/* datatype.h */ +/* */ +/* Type string handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef DATATYPE_H +#define DATATYPE_H + + + +#include + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Data types */ +#define T_END 0x0000 +#define T_CHAR 0x0011 +#define T_INT 0x0012 +#define T_SHORT 0x0013 +#define T_LONG 0x0014 +#define T_ENUM 0x0015 +#define T_UCHAR 0x0019 +#define T_UINT 0x001A +#define T_USHORT 0x001B +#define T_ULONG 0x001C + +#define T_FLOAT 0x0025 +#define T_DOUBLE 0x0026 + +#define T_VOID 0x0001 /* void parameter list */ +#define T_FUNC 0x0002 /* Function */ + +#define T_UNSIGNED 0x0008 /* Class */ +#define T_INTEGER 0x0010 /* Class */ +#define T_REAL 0x0020 /* Class */ +#define T_POINTER 0x0040 /* Class */ +#define T_PTR 0x0049 +#define T_ARRAY 0x004A +#define T_STRUCT 0x0080 +#define T_UNION 0x0081 +#define T_SMASK 0x003F + + + +/* Forward for a symbol entry */ +struct SymEntry; + +/* Type entry */ +typedef unsigned short type; + +/* Maximum length of a type string */ +#define MAXTYPELEN 30 + +/* type elements needed for Encode/Decode */ +#define DECODE_SIZE 5 + +/* Predefined type strings */ +extern type type_int []; +extern type type_uint []; +extern type type_long []; +extern type type_ulong []; +extern type type_void []; +extern type type_pschar []; +extern type type_puchar []; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned TypeLen (const type* Type); +/* Return the length of the type string */ + +int TypeCmp (const type* T1, const type* T2); +/* Compare two type strings */ + +type* TypeCpy (type* Dest, const type* Src); +/* Copy a type string */ + +type* TypeCat (type* Dest, const type* Src); +/* Append Src */ + +type* TypeDup (const type* Type); +/* Create a copy of the given type on the heap */ + +type* TypeAlloc (unsigned Len); +/* Allocate memory for a type string of length Len. Len *must* include the + * trailing T_END. + */ + +void TypeFree (type* Type); +/* Free a type string */ + +type GetDefaultChar (void); +/* Return the default char type (signed/unsigned) depending on the settings */ + +type* GetCharArrayType (unsigned Len); +/* Return the type for a char array of the given length */ + +type* GetImplicitFuncType (void); +/* Return a type string for an inplicitly declared function */ + +void PrintType (FILE* F, const type* Type); +/* Output translation of type array. */ + +void PrintRawType (FILE* F, const type* Type); +/* Print a type string in raw format (for debugging) */ + +void Encode (type* Type, unsigned long Val); +/* Encode an unsigned long into a type array */ + +void EncodePtr (type* Type, void* P); +/* Encode a pointer into a type array */ + +unsigned long Decode (const type* Type); +/* Decode an unsigned long from a type array */ + +void* DecodePtr (const type* Type); +/* Decode a pointer from a type array */ + +int HasEncode (const type* Type); +/* Return true if the given type has encoded data */ + +void CopyEncode (const type* Source, type* Target); +/* Copy encoded data from Source to Target */ + +unsigned SizeOf (const type* Type); +/* Compute size of object represented by type array. */ + +unsigned PSizeOf (const type* Type); +/* Compute size of pointer object. */ + +unsigned TypeOf (const type* Type); +/* Get the code generator base type of the object */ + +type* Indirect (type* Type); +/* Do one indirection for the given type, that is, return the type where the + * given type points to. + */ + +int IsVoid (const type* Type); +/* Return true if this is a void type */ + +int IsPtr (const type* Type); +/* Return true if this is a pointer type */ + +int IsChar (const type* Type); +/* Return true if this is a character type */ + +int IsInt (const type* Type); +/* Return true if this is an integer type */ + +int IsLong (const type* Type); +/* Return true if this is a long type (signed or unsigned) */ + +int IsUnsigned (const type* Type); +/* Return true if this is an unsigned type */ + +int IsStruct (const type* Type); +/* Return true if this is a struct type */ + +int IsFunc (const type* Type); +/* Return true if this is a function type */ + +int IsFastCallFunc (const type* Type); +/* Return true if this is a function type with __fastcall__ calling conventions */ + +int IsFuncPtr (const type* Type); +/* Return true if this is a function pointer */ + +int IsArray (const type* Type); +/* Return true if this is an array type */ + +struct FuncDesc* GetFuncDesc (const type* Type); +/* Get the FuncDesc pointer from a function or pointer-to-function type */ + + + +/* End of datatype.h */ + +#endif + + + diff --git a/src/cc65/declare.c b/src/cc65/declare.c new file mode 100644 index 000000000..8c40fa81b --- /dev/null +++ b/src/cc65/declare.c @@ -0,0 +1,938 @@ +/* + * declare.c + * + * Ullrich von Bassewitz, 20.06.1998 + */ + + + +#include +#include +#include +#include + +#include "anonname.h" +#include "codegen.h" +#include "datatype.h" +#include "error.h" +#include "expr.h" +#include "funcdesc.h" +#include "function.h" +#include "global.h" +#include "litpool.h" +#include "mem.h" +#include "pragma.h" +#include "scanner.h" +#include "symtab.h" +#include "declare.h" + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static void ParseTypeSpec (DeclSpec* D, int Default); +/* Parse a type specificier */ + + + +/*****************************************************************************/ +/* internal functions */ +/*****************************************************************************/ + + + +static void optional_modifiers (void) +/* Eat optional "const" or "volatile" tokens */ +{ + while (curtok == CONST || curtok == VOLATILE) { + /* Skip it */ + gettok (); + } +} + + + +static void optionalint (void) +/* Eat an optional "int" token */ +{ + if (curtok == INT) { + /* Skip it */ + gettok (); + } +} + + + +static void optionalsigned (void) +/* Eat an optional "signed" token */ +{ + if (curtok == SIGNED) { + /* Skip it */ + gettok (); + } +} + + + +static void InitDeclSpec (DeclSpec* D) +/* Initialize the DeclSpec struct for use */ +{ + D->StorageClass = 0; + D->Type[0] = T_END; + D->Flags = 0; +} + + + +static void InitDeclaration (Declaration* D) +/* Initialize the Declaration struct for use */ +{ + D->Ident[0] = '\0'; + D->Type[0] = T_END; + D->T = D->Type; +} + + + +static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) +/* Parse a storage class */ +{ + /* Assume we're using an explicit storage class */ + D->Flags &= ~DS_DEF_STORAGE; + + /* Check the storage class given */ + switch (curtok) { + + case EXTERN: + D->StorageClass = SC_EXTERN | SC_STATIC; + gettok (); + break; + + case STATIC: + D->StorageClass = SC_STATIC; + gettok (); + break; + + case REGISTER: + D->StorageClass = SC_REGISTER | SC_STATIC; + gettok (); + break; + + case AUTO: + D->StorageClass = SC_AUTO; + gettok (); + break; + + case TYPEDEF: + D->StorageClass = SC_TYPEDEF; + gettok (); + break; + + default: + /* No storage class given, use default */ + D->Flags |= DS_DEF_STORAGE; + D->StorageClass = DefStorage; + break; + } +} + + + +static void ParseEnumDecl (void) +/* Process an enum declaration . */ +{ + int EnumVal; + ident Ident; + + /* Accept forward definitions */ + if (curtok != LCURLY) { + return; + } + + /* Skip the opening curly brace */ + gettok (); + + /* Read the enum tags */ + EnumVal = 0; + while (curtok != RCURLY) { + + /* We expect an identifier */ + if (curtok != IDENT) { + Error (ERR_IDENT_EXPECTED); + continue; + } + + /* Remember the identifier and skip it */ + strcpy (Ident, CurTok.Ident); + gettok (); + + /* Check for an assigned value */ + if (curtok == ASGN) { + struct expent lval; + gettok (); + constexpr (&lval); + EnumVal = lval.e_const; + } + + /* Add an entry to the symbol table */ + AddEnumSym (Ident, EnumVal++); + + /* Check for end of definition */ + if (curtok != COMMA) + break; + gettok (); + } + ConsumeRCurly (); +} + + + +static SymEntry* ParseStructDecl (const char* Name, type StructType) +/* Parse a struct/union declaration. */ +{ + + unsigned Size; + unsigned Offs; + SymTable* FieldTab; + SymEntry* Entry; + + + if (curtok != LCURLY) { + /* Just a forward declaration. Try to find a struct with the given + * name. If there is none, insert a forward declaration into the + * current lexical level. + */ + Entry = FindStructSym (Name); + if (Entry == 0 || Entry->Flags != SC_STRUCT) { + Entry = AddStructSym (Name, 0, 0); + } + return Entry; + } + + /* Add a forward declaration for the struct in the current lexical level */ + Entry = AddStructSym (Name, 0, 0); + + /* Skip the curly brace */ + gettok (); + + /* Enter a new lexical level for the struct */ + EnterStructLevel (); + + /* Parse struct fields */ + Size = 0; + while (curtok != RCURLY) { + + /* Get the type of the entry */ + DeclSpec Spec; + InitDeclSpec (&Spec); + ParseTypeSpec (&Spec, -1); + + /* Read fields with this type */ + while (1) { + + /* Get type and name of the struct field */ + Declaration Decl; + ParseDecl (&Spec, &Decl, 0); + + /* Add a field entry to the table */ + AddLocalSym (Decl.Ident, Decl.Type, SC_SFLD, (StructType == T_STRUCT)? Size : 0); + + /* Calculate offset of next field/size of the union */ + Offs = SizeOf (Decl.Type); + if (StructType == T_STRUCT) { + Size += Offs; + } else { + if (Offs > Size) { + Size = Offs; + } + } + + if (curtok != COMMA) + break; + gettok (); + } + ConsumeSemi (); + } + + /* Skip the closing brace */ + gettok (); + + /* Remember the symbol table and leave the struct level */ + FieldTab = GetSymTab (); + LeaveStructLevel (); + + /* Make a real entry from the forward decl and return it */ + return AddStructSym (Name, Size, FieldTab); +} + + + +static void ParseTypeSpec (DeclSpec* D, int Default) +/* Parse a type specificier */ +{ + ident Ident; + SymEntry* Entry; + type StructType; + + /* Assume have an explicit type */ + D->Flags &= ~DS_DEF_TYPE; + + /* Skip const or volatile modifiers if needed */ + optional_modifiers (); + + /* Look at the data type */ + switch (curtok) { + + case VOID: + gettok (); + D->Type[0] = T_VOID; + D->Type[1] = T_END; + break; + + case CHAR: + gettok (); + D->Type[0] = GetDefaultChar(); + D->Type[1] = T_END; + break; + + case LONG: + gettok (); + if (curtok == UNSIGNED) { + gettok (); + optionalint (); + D->Type[0] = T_ULONG; + D->Type[1] = T_END; + } else { + optionalsigned (); + optionalint (); + D->Type[0] = T_LONG; + D->Type[1] = T_END; + } + break; + + case SHORT: + gettok (); + if (curtok == UNSIGNED) { + gettok (); + optionalint (); + D->Type[0] = T_USHORT; + D->Type[1] = T_END; + } else { + optionalsigned (); + optionalint (); + D->Type[0] = T_SHORT; + D->Type[1] = T_END; + } + break; + + case INT: + gettok (); + D->Type[0] = T_INT; + D->Type[1] = T_END; + break; + + case SIGNED: + gettok (); + switch (curtok) { + + case CHAR: + gettok (); + D->Type[0] = T_CHAR; + D->Type[1] = T_END; + break; + + case SHORT: + gettok (); + optionalint (); + D->Type[0] = T_SHORT; + D->Type[1] = T_END; + break; + + case LONG: + gettok (); + optionalint (); + D->Type[0] = T_LONG; + D->Type[1] = T_END; + break; + + case INT: + gettok (); + /* FALL THROUGH */ + + default: + D->Type[0] = T_INT; + D->Type[1] = T_END; + break; + } + break; + + case UNSIGNED: + gettok (); + switch (curtok) { + + case CHAR: + gettok (); + D->Type[0] = T_UCHAR; + D->Type[1] = T_END; + break; + + case SHORT: + gettok (); + optionalint (); + D->Type[0] = T_USHORT; + D->Type[1] = T_END; + break; + + case LONG: + gettok (); + optionalint (); + D->Type[0] = T_ULONG; + D->Type[1] = T_END; + break; + + case INT: + gettok (); + /* FALL THROUGH */ + + default: + D->Type[0] = T_UINT; + D->Type[1] = T_END; + break; + } + break; + + case STRUCT: + case UNION: + StructType = (curtok == STRUCT)? T_STRUCT : T_UNION; + gettok (); + if (curtok == IDENT) { + strcpy (Ident, CurTok.Ident); + gettok (); + } else { + AnonName (Ident, (StructType == T_STRUCT)? "struct" : "union"); + } + /* Declare the struct in the current scope */ + Entry = ParseStructDecl (Ident, StructType); + /* Encode the struct entry into the type */ + D->Type[0] = StructType; + EncodePtr (D->Type+1, Entry); + D->Type[DECODE_SIZE+1] = T_END; + break; + + case ENUM: + gettok (); + if (curtok != LCURLY) { + /* Named enum */ + Consume (IDENT, ERR_IDENT_EXPECTED); + } + ParseEnumDecl (); + D->Type[0] = T_INT; + D->Type[1] = T_END; + break; + + case IDENT: + Entry = FindSym (CurTok.Ident); + if (Entry && IsTypeDef (Entry)) { + /* It's a typedef */ + gettok (); + TypeCpy (D->Type, Entry->Type); + break; + } + /* FALL THROUGH */ + + default: + if (Default < 0) { + Error (ERR_TYPE_EXPECTED); + D->Type[0] = T_INT; + D->Type[1] = T_END; + } else { + D->Flags |= DS_DEF_TYPE; + D->Type[0] = (type) Default; + D->Type[1] = T_END; + } + break; + } +} + + + +static FuncDesc* ParseFuncDecl (void) +/* Parse the argument list of a function. */ +{ + unsigned UnnamedCount = 0; + unsigned Offs; + SymEntry* Sym; + type* Type; + + /* Create a new function descriptor */ + FuncDesc* F = NewFuncDesc (); + + /* Enter a new lexical level */ + EnterFunctionLevel (); + + /* Check for an empty or void parameter list */ + if (curtok == RPAREN) { + /* Parameter list is empty */ + F->Flags |= (FD_EMPTY | FD_ELLIPSIS); + } else if (curtok == VOID && nxttok == RPAREN) { + /* Parameter list declared as void */ + gettok (); + F->Flags |= FD_VOID_PARAM; + } + + /* Parse params */ + while (curtok != RPAREN) { + + DeclSpec Spec; + Declaration Decl; + + /* Allow an ellipsis as last parameter */ + if (curtok == ELLIPSIS) { + gettok (); + F->Flags |= FD_ELLIPSIS; + break; + } + + /* Read the declaration specifier */ + ParseDeclSpec (&Spec, SC_AUTO, T_INT); + + /* We accept only auto and register as storage class specifiers, but + * we ignore all this and use auto. + */ + if ((Spec.StorageClass & SC_AUTO) == 0 && + (Spec.StorageClass & SC_REGISTER) == 0) { + Error (ERR_ILLEGAL_STORAGE_CLASS); + } + Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; + + /* Allow parameters without a name, but remember if we had some to + * eventually print an error message later. + */ + ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + if (Decl.Ident[0] == '\0') { + + /* Unnamed symbol. Generate a name that is not user accessible, + * then handle the symbol normal. + */ + AnonName (Decl.Ident, "param"); + ++UnnamedCount; + + /* Clear defined bit on nonames */ + Spec.StorageClass &= ~SC_DEF; + } + + /* If the parameter is an array, convert it to a pointer */ + Type = Decl.Type; + if (IsArray (Type)) { + Type += DECODE_SIZE; + Type[0] = T_PTR; + } + + /* Create a symbol table entry */ + AddLocalSym (Decl.Ident, Type, Spec.StorageClass, 0); + + /* Count arguments */ + ++F->ParamCount; + F->ParamSize += SizeOf (Type); + + /* Check for more parameters */ + if (curtok == COMMA) { + gettok (); + } else { + break; + } + } + + /* Skip right paren. We must explicitly check for one here, since some of + * the breaks above bail out without checking. + */ + ConsumeRParen (); + + /* Assign offsets. If the function has a variable parameter list, + * there's one additional byte (the arg size). + */ + Offs = (F->Flags & FD_ELLIPSIS)? 1 : 0; + Sym = GetSymTab()->SymTail; + while (Sym) { + Sym->V.Offs = Offs; + Offs += SizeOf (Sym->Type); + Sym = Sym->PrevSym; + } + + /* Check if this is a function definition */ + if (curtok == LCURLY) { + /* Print an error if in strict ANSI mode and we have unnamed + * parameters. + */ + if (ANSI && UnnamedCount > 0) { + Error (ERR_MISSING_PARAM_NAME); + } + } + + /* Leave the lexical level remembering the symbol tables */ + RememberFunctionLevel (F); + + /* Return the function descriptor */ + return F; +} + + + +static void Decl (Declaration* D, unsigned Mode) +/* Recursively process declarators. Build a type array in reverse order. */ +{ + if (curtok == STAR) { + gettok (); + /* Allow optional const or volatile modifiers */ + optional_modifiers (); + Decl (D, Mode); + *D->T++ = T_PTR; + return; + } else if (curtok == LPAREN) { + gettok (); + Decl (D, Mode); + ConsumeRParen (); + } else if (curtok == FASTCALL) { + /* Remember the current type pointer */ + type* T = D->T; + /* Skip the fastcall token */ + gettok (); + /* Parse the function */ + Decl (D, Mode); + /* Set the fastcall flag */ + if (!IsFunc (T)) { + Error (ERR_ILLEGAL_MODIFIER); + } else { + FuncDesc* F = DecodePtr (T+1); + F->Flags |= FD_FASTCALL; + } + return; + } else { + /* Things depend on Mode now: + * - Mode == DM_NEED_IDENT means: + * we *must* have a type and a variable identifer. + * - Mode == DM_NO_IDENT means: + * we must have a type but no variable identifer + * (if there is one, it's not read). + * - Mode == DM_ACCEPT_IDENT means: + * we *may* have an identifier. If there is an identifier, + * it is read, but it is no error, if there is none. + */ + if (Mode == DM_NO_IDENT) { + D->Ident[0] = '\0'; + } else if (curtok == IDENT) { + strcpy (D->Ident, CurTok.Ident); + gettok (); + } else { + if (Mode == DM_NEED_IDENT) { + Error (ERR_IDENT_EXPECTED); + } + D->Ident[0] = '\0'; + return; + } + } + + while (curtok == LBRACK || curtok == LPAREN) { + if (curtok == LPAREN) { + /* Function declaration */ + FuncDesc* F; + gettok (); + /* Parse the function declaration */ + F = ParseFuncDecl (); + *D->T++ = T_FUNC; + EncodePtr (D->T, F); + D->T += DECODE_SIZE; + } else { + /* Array declaration */ + unsigned long Size = 0; + gettok (); + /* Read the size if it is given */ + if (curtok != RBRACK) { + struct expent lval; + constexpr (&lval); + Size = lval.e_const; + } + ConsumeRBrack (); + *D->T++ = T_ARRAY; + Encode (D->T, Size); + D->T += DECODE_SIZE; + } + } +} + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +type* ParseType (type* Type) +/* Parse a complete type specification */ +{ + DeclSpec Spec; + Declaration Decl; + + /* Get a type without a default */ + InitDeclSpec (&Spec); + ParseTypeSpec (&Spec, -1); + + /* Parse additional declarators */ + InitDeclaration (&Decl); + ParseDecl (&Spec, &Decl, DM_NO_IDENT); + + /* Copy the type to the target buffer */ + TypeCpy (Type, Decl.Type); + + /* Return a pointer to the target buffer */ + return Type; +} + + + +void ParseDecl (const DeclSpec* Spec, Declaration* D, unsigned Mode) +/* Parse a variable, type or function declaration */ +{ + /* Initialize the Declaration struct */ + InitDeclaration (D); + + /* Get additional declarators and the identifier */ + Decl (D, Mode); + + /* Add the base type. */ + TypeCpy (D->T, Spec->Type); + + /* Check the size of the generated type */ + if (!IsFunc (D->Type) && SizeOf (D->Type) >= 0x10000) { + Error (ERR_ILLEGAL_SIZE); + } +} + + + +void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, int DefType) +/* Parse a declaration specification */ +{ + /* Initialize the DeclSpec struct */ + InitDeclSpec (D); + + /* First, get the storage class specifier for this declaration */ + ParseStorageClass (D, DefStorage); + + /* Parse the type specifiers */ + ParseTypeSpec (D, DefType); +} + + + +static void ParseVoidInit (void) +/* Parse an initialization of a void variable (special cc65 extension) */ +{ + struct expent lval; + + /* Allow an arbitrary list of values */ + ConsumeLCurly (); + do { + constexpr (&lval); + switch (lval.e_tptr[0]) { + + case T_CHAR: + case T_UCHAR: + if ((lval.e_flags & E_MCTYPE) == E_TCONST) { + /* Make it byte sized */ + lval.e_const &= 0xFF; + } + DefineData (&lval); + break; + + case T_SHORT: + case T_USHORT: + case T_INT: + case T_UINT: + case T_PTR: + case T_ARRAY: + if ((lval.e_flags & E_MCTYPE) == E_TCONST) { + /* Make it word sized */ + lval.e_const &= 0xFFFF; + } + DefineData (&lval); + break; + + case T_LONG: + case T_ULONG: + DefineData (&lval); + break; + + default: + Error (ERR_ILLEGAL_TYPE); + break; + + } + + if (curtok != COMMA) { + break; + } + gettok (); + + } while (curtok != RCURLY); + + ConsumeRCurly (); +} + + + +static void ParseStructInit (type* Type) +/* Parse initialization of a struct or union */ +{ + SymEntry* Entry; + SymTable* Tab; + + /* Consume the opening curly brace */ + ConsumeLCurly (); + + /* Get a pointer to the struct entry from the type */ + Entry = (SymEntry*) Decode (Type + 1); + + /* Check if this struct definition has a field table. If it doesn't, it + * is an incomplete definition. + */ + Tab = Entry->V.S.SymTab; + if (Tab == 0) { + Error (ERR_INIT_INCOMPLETE_TYPE); + /* Returning here will cause lots of errors, but recovery is difficult */ + return; + } + + /* Get a pointer to the list of symbols */ + Entry = Tab->SymHead; + while (curtok != RCURLY) { + if (Entry == NULL) { + Error (ERR_TOO_MANY_INITIALIZERS); + return; + } + ParseInit (Entry->Type); + Entry = Entry->NextSym; + if (curtok != COMMA) + break; + gettok (); + } + + /* Consume the closing curly brace */ + ConsumeRCurly (); + + /* If there are struct fields left, reserve additional storage */ + while (Entry) { + g_zerobytes (SizeOf (Entry->Type)); + Entry = Entry->NextSym; + } +} + + + + + +void ParseInit (type *tptr) +/* Parse initialization of variables */ +{ + int count; + struct expent lval; + type* t; + const char* str; + int sz; + + switch (*tptr) { + + case T_CHAR: + case T_UCHAR: + constexpr (&lval); + if ((lval.e_flags & E_MCTYPE) == E_TCONST) { + /* Make it byte sized */ + lval.e_const &= 0xFF; + } + assignadjust (tptr, &lval); + DefineData (&lval); + break; + + case T_SHORT: + case T_USHORT: + case T_INT: + case T_UINT: + case T_PTR: + constexpr (&lval); + if ((lval.e_flags & E_MCTYPE) == E_TCONST) { + /* Make it word sized */ + lval.e_const &= 0xFFFF; + } + assignadjust (tptr, &lval); + DefineData (&lval); + break; + + case T_LONG: + case T_ULONG: + constexpr (&lval); + if ((lval.e_flags & E_MCTYPE) == E_TCONST) { + /* Make it long sized */ + lval.e_const &= 0xFFFFFFFF; + } + assignadjust (tptr, &lval); + DefineData (&lval); + break; + + case T_ARRAY: + sz = Decode (tptr + 1); + t = tptr + DECODE_SIZE + 1; + if ((t [0] == T_CHAR || t [0] == T_UCHAR) && curtok == SCONST) { + str = GetLiteral (curval); + count = strlen (str) + 1; + TranslateLiteralPool (curval); /* Translate into target charset */ + g_defbytes (str, count); + ResetLiteralOffs (curval); /* Remove string from pool */ + gettok (); + } else { + ConsumeLCurly (); + count = 0; + while (curtok != RCURLY) { + ParseInit (tptr + DECODE_SIZE + 1); + ++count; + if (curtok != COMMA) + break; + gettok (); + } + ConsumeRCurly (); + } + if (sz == 0) { + Encode (tptr + 1, count); + } else if (count < sz) { + g_zerobytes ((sz - count) * SizeOf (tptr + DECODE_SIZE + 1)); + } else if (count > sz) { + Error (ERR_TOO_MANY_INITIALIZERS); + } + break; + + case T_STRUCT: + case T_UNION: + ParseStructInit (tptr); + break; + + case T_VOID: + if (!ANSI) { + /* Special cc65 extension in non ANSI mode */ + ParseVoidInit (); + break; + } + /* FALLTHROUGH */ + + default: + Error (ERR_ILLEGAL_TYPE); + break; + + } +} + + + diff --git a/src/cc65/declare.h b/src/cc65/declare.h new file mode 100644 index 000000000..57bf41516 --- /dev/null +++ b/src/cc65/declare.h @@ -0,0 +1,79 @@ +/* + * declare.h + * + * Ullrich von Bassewitz, 20.06.1998 + */ + + + +#ifndef DECLARE_H +#define DECLARE_H + + + +#include "scanner.h" +#include "symtab.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Masks for the Flags field in DeclSpec */ +#define DS_DEF_STORAGE 0x0001U /* Default storage class used */ +#define DS_DEF_TYPE 0x0002U /* Default type used */ + +/* Result of ParseDeclSpec */ +typedef struct DeclSpec DeclSpec; +struct DeclSpec { + unsigned StorageClass; /* One of the SC_xxx flags */ + type Type [MAXTYPELEN]; /* Type of the declaration spec */ + unsigned Flags; /* Bitmapped flags */ +}; + +/* Result of ParseDecl */ +typedef struct Declaration Declaration; +struct Declaration { + ident Ident; /* The identifier if any, else empty */ + type Type [MAXTYPELEN]; /* The type */ + + /* Working variables */ + type* T; /* Used to build Type */ +}; + +/* Modes for ParseDecl */ +#define DM_NEED_IDENT 0U /* We must have an identifier */ +#define DM_NO_IDENT 1U /* We won't read an identifier */ +#define DM_ACCEPT_IDENT 2U /* We will accept an id if there is one */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +type* ParseType (type* Type); +/* Parse a complete type specification */ + +void ParseDecl (const DeclSpec* Spec, Declaration* D, unsigned Mode); +/* Parse a variable, type or function declaration */ + +void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, int DefType); +/* Parse a declaration specification */ + +void ParseInit (type* tptr); +/* Parse initialization of variables */ + + + +/* End of declare.h */ + +#endif + + + diff --git a/src/cc65/error.c b/src/cc65/error.c new file mode 100644 index 000000000..4d3250256 --- /dev/null +++ b/src/cc65/error.c @@ -0,0 +1,310 @@ +/*****************************************************************************/ +/* */ +/* error.c */ +/* */ +/* Error handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "global.h" +#include "io.h" +#include "scanner.h" +#include "stmt.h" +#include "error.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +static char* WarnMsg [WARN_COUNT-1] = { + "Unreachable code", + "Condition is never true", + "Condition is always true", + "Converting pointer to integer without a cast", + "Converting integer to pointer without a cast", + "Function call without a prototype", + "Unknown #pragma", + "No case labels", + "Function must be extern", + "Parameter `%s' is never used", + "`%s' is defined but never used", + "Constant is long", + "`/*' found inside a comment", +}; + + + +/* Error messages sorted by ErrTypes */ +static char* ErrMsg [ERR_COUNT-1] = { + "Invalid character (%u)", + "Unexpected newline", + "End-of-file reached in comment starting at line %u", + "Syntax error", + "`\"' expected", + "`:' expected", + "`;' expected", + "`(' expected", + "`)' expected", + "`[' expected", + "`]' expected", + "`{' expected", + "`}' expected", + "Identifier expected", + "Type expected", + "Incompatible types", + "Incompatible pointer types", + "Too many arguments in function call", + "Too few arguments in function call", + "Macro argument count mismatch", + "Duplicate macro parameter: %s", + "Variable identifier expected", + "Integer expression expected", + "Constant expression expected", + "No active loop", + "`\"' or `<' expected", + "Missing terminator or name too long", + "Include file `%s' not found", + "Open failure on include file `%s'", + "Invalid #error directive", + "#error: %s", + "Unexpected `#endif'", + "Unexpected `#else'", + "`#endif' expected", + "Compiler directive expected", + "Symbol `%s' defined more than once", + "String literal expected", + "`while' expected", + "Function must return a value", + "Function cannot return a value", + "Unexpected `continue'", + "Undefined symbol: `%s'", + "Undefined label: `%s'", + "Include nesting too deep", + "Too many local variables", + "Too many initializers", + "Cannot initialize incomplete type", + "Cannot subscript", + "Operation not allowed on these types", + "Struct expected", + "Struct/union has no field named `%s'", + "Struct pointer expected", + "lvalue expected", + "Expression expected", + "Preprocessor expression expected", + "Illegal type", + "Illegal function call", + "Illegal indirection", + "Illegal address", + "Illegal macro call", + "Illegal hex digit", + "Illegal character constant", + "Illegal modifier", + "Illegal storage class", + "Division by zero", + "Modulo operation with zero", + "Range error", + "Symbol is already different kind", + "Too many lexical levels", + "Parameter name omitted", + "Old style function decl used as prototype", + "Declaration for parameter `%s' but no such parameter", + "Cannot take address of a register variable", + "Illegal size of data type", + "__fastcall__ is not allowed for C functions", + "Variable has unknown size", +}; + + + +static char* FatMsg [FAT_COUNT-1] = { + "Too many errors", + "Cannot open output file: %s", + "Cannot write to output file (disk full?)", + "Cannot open input file: %s", + "Out of memory", + "Stack overflow", + "Stack empty", + "Out of string space", + "Too many case labels", +}; + + + +/* Count of errors/warnings */ +unsigned ErrorCount = 0; +unsigned WarningCount = 0; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Warning (unsigned WarnNum, ...) +/* Print warning message. */ +{ + va_list ap; + + if (!NoWarn) { + fprintf (stderr, "%s(%u): Warning #%u: ", fin, curpos, WarnNum); + + va_start (ap, WarnNum); + vfprintf (stderr, WarnMsg [WarnNum-1], ap); + va_end (ap); + fprintf (stderr, "\n"); + + if (Verbose) { + fprintf (stderr, "Line: %s\n", line); + } + } + ++ WarningCount; +} + + + +void PPWarning (unsigned WarnNum, ...) +/* Print warning message. For use within the preprocessor. */ +{ + va_list ap; + + if (!NoWarn) { + fprintf (stderr, "%s(%u): Warning #%u: ", fin, ln, WarnNum); + + va_start (ap, WarnNum); + vfprintf (stderr, WarnMsg [WarnNum-1], ap); + va_end (ap); + fprintf (stderr, "\n"); + } + ++WarningCount; +} + + + +void Error (unsigned ErrNum, ...) +/* Print an error message */ +{ + va_list ap; + + fprintf (stderr, "%s(%u): Error #%u: ", fin, curpos, ErrNum); + + va_start (ap, ErrNum); + vfprintf (stderr, ErrMsg [ErrNum-1], ap); + va_end (ap); + fprintf (stderr, "\n"); + + if (Verbose) { + fprintf (stderr, "Line: %s\n", line); + } + ++ErrorCount; + if (ErrorCount > 10) { + Fatal (FAT_TOO_MANY_ERRORS); + } +} + + + +void PPError (unsigned ErrNum, ...) +/* Print an error message. For use within the preprocessor. */ +{ + va_list ap; + + fprintf (stderr, "%s(%u): Error #%u: ", fin, ln, ErrNum); + + va_start (ap, ErrNum); + vfprintf (stderr, ErrMsg [ErrNum-1], ap); + va_end (ap); + fprintf (stderr, "\n"); + + ++ErrorCount; + if (ErrorCount > 10) { + Fatal (FAT_TOO_MANY_ERRORS); + } +} + + + +void Fatal (unsigned FatNum, ...) +/* Print a message about a fatal error and die */ +{ + va_list ap; + + fprintf (stderr, "%s(%u): Fatal #%u: ", fin, curpos, FatNum); + + va_start (ap, FatNum); + vfprintf (stderr, FatMsg [FatNum-1], ap); + va_end (ap); + fprintf (stderr, "\n"); + + if (Verbose) { + fprintf (stderr, "Line: %s\n", line); + } + exit (EXIT_FAILURE); +} + + + +void Internal (char* Format, ...) +/* Print a message about an internal compiler error and die. */ +{ + va_list ap; + + fprintf (stderr, "%s(%u): Internal compiler error:\n", fin, curpos); + + va_start (ap, Format); + vfprintf (stderr, Format, ap); + va_end (ap); + fprintf (stderr, "\nLine: %s\n", line); + + /* Use abort to create a core dump */ + abort (); +} + + + +void ErrorReport (void) +/* Report errors (called at end of compile) */ +{ + if (ErrorCount == 0 && Verbose) { + printf ("No errors.\n"); + } +} + + + diff --git a/src/cc65/error.h b/src/cc65/error.h new file mode 100644 index 000000000..6e2912c9e --- /dev/null +++ b/src/cc65/error.h @@ -0,0 +1,206 @@ +/*****************************************************************************/ +/* */ +/* error.h */ +/* */ +/* Error handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ERROR_H +#define ERROR_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Warning numbers */ +enum Warnings { + WARN_NONE, /* No warning */ + WARN_UNREACHABLE_CODE, + WARN_COND_NEVER_TRUE, + WARN_COND_ALWAYS_TRUE, + WARN_PTR_TO_INT_CONV, + WARN_INT_TO_PTR_CONV, + WARN_FUNC_WITHOUT_PROTO, + WARN_UNKNOWN_PRAGMA, + WARN_NO_CASE_LABELS, + WARN_FUNC_MUST_BE_EXTERN, + WARN_UNUSED_PARM, + WARN_UNUSED_ITEM, + WARN_CONSTANT_IS_LONG, + WARN_NESTED_COMMENT, + WARN_COUNT /* Warning count */ +}; + +/* Error numbers */ +enum Errors { + ERR_NONE, /* No error */ + ERR_INVALID_CHAR, + ERR_UNEXPECTED_NEWLINE, + ERR_EOF_IN_COMMENT, + ERR_SYNTAX, + ERR_QUOTE_EXPECTED, + ERR_COLON_EXPECTED, + ERR_SEMICOLON_EXPECTED, + ERR_LPAREN_EXPECTED, + ERR_RPAREN_EXPECTED, + ERR_LBRACK_EXPECTED, + ERR_RBRACK_EXPECTED, + ERR_LCURLY_EXPECTED, + ERR_RCURLY_EXPECTED, + ERR_IDENT_EXPECTED, + ERR_TYPE_EXPECTED, + ERR_INCOMPATIBLE_TYPES, + ERR_INCOMPATIBLE_POINTERS, + ERR_TOO_MANY_FUNC_ARGS, + ERR_TOO_FEW_FUNC_ARGS, + ERR_MACRO_ARGCOUNT, + ERR_DUPLICATE_MACRO_ARG, + ERR_VAR_IDENT_EXPECTED, + ERR_INT_EXPR_EXPECTED, + ERR_CONST_EXPR_EXPECTED, + ERR_NO_ACTIVE_LOOP, + ERR_INCLUDE_LTERM_EXPECTED, + ERR_INCLUDE_RTERM_EXPECTED, + ERR_INCLUDE_NOT_FOUND, + ERR_INCLUDE_OPEN_FAILURE, + ERR_INVALID_USER_ERROR, + ERR_USER_ERROR, + ERR_UNEXPECTED_CPP_ENDIF, + ERR_UNEXPECTED_CPP_ELSE, + ERR_CPP_ENDIF_EXPECTED, + ERR_CPP_DIRECTIVE_EXPECTED, + ERR_MULTIPLE_DEFINITION, + ERR_STRLIT_EXPECTED, + ERR_WHILE_EXPECTED, + ERR_MUST_RETURN_VALUE, + ERR_CANNOT_RETURN_VALUE, + ERR_UNEXPECTED_CONTINUE, + ERR_UNDEFINED_SYMBOL, + ERR_UNDEFINED_LABEL, + ERR_INCLUDE_NESTING, + ERR_TOO_MANY_LOCALS, + ERR_TOO_MANY_INITIALIZERS, + ERR_INIT_INCOMPLETE_TYPE, + ERR_CANNOT_SUBSCRIPT, + ERR_OP_NOT_ALLOWED, + ERR_STRUCT_EXPECTED, + ERR_STRUCT_FIELD_MISMATCH, + ERR_STRUCT_PTR_EXPECTED, + ERR_LVALUE_EXPECTED, + ERR_EXPR_EXPECTED, + ERR_CPP_EXPR_EXPECTED, + ERR_ILLEGAL_TYPE, + ERR_ILLEGAL_FUNC_CALL, + ERR_ILLEGAL_INDIRECT, + ERR_ILLEGAL_ADDRESS, + ERR_ILLEGAL_MACRO_CALL, + ERR_ILLEGAL_HEX_DIGIT, + ERR_ILLEGAL_CHARCONST, + ERR_ILLEGAL_MODIFIER, + ERR_ILLEGAL_STORAGE_CLASS, + ERR_DIV_BY_ZERO, + ERR_MOD_BY_ZERO, + ERR_RANGE, + ERR_SYMBOL_KIND, + ERR_LEVEL_NESTING, + ERR_MISSING_PARAM_NAME, + ERR_OLD_STYLE_PROTO, + ERR_PARAM_DECL, + ERR_CANNOT_TAKE_ADDR_OF_REG, + ERR_ILLEGAL_SIZE, + ERR_FASTCALL, + ERR_UNKNOWN_SIZE, + ERR_COUNT /* Error count */ +}; + +/* Fatal errors */ +enum Fatals { + FAT_NONE, + FAT_TOO_MANY_ERRORS, + FAT_CANNOT_OPEN_OUTPUT, + FAT_CANNOT_WRITE_OUTPUT, + FAT_CANNOT_OPEN_INPUT, + FAT_OUT_OF_MEMORY, + FAT_STACK_OVERFLOW, + FAT_STACK_EMPTY, + FAT_OUT_OF_STRSPACE, + FAT_TOO_MANY_CASE_LABELS, + FAT_COUNT /* Fatal error count */ +}; + + + +/* Count of errors/warnings */ +extern unsigned ErrorCount; +extern unsigned WarningCount; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void Warning (unsigned WarnNum, ...); +/* Print warning message. */ + +void PPWarning (unsigned WarnNum, ...); +/* Print warning message. For use within the preprocessor. */ + +void Error (unsigned ErrNum, ...); +/* Print an error message */ + +void PPError (unsigned ErrNum, ...); +/* Print an error message. For use within the preprocessor. */ + +void Fatal (unsigned FatNum, ...); +/* Print a message about a fatal error and die */ + +void Internal (char* Format, ...); +/* Print a message about an internal compiler error and die. */ + +void ErrorReport (void); +/* Report errors (called at end of compile) */ + + + +/* End of error.h */ +#endif + + + + + diff --git a/src/cc65/expr.c b/src/cc65/expr.c new file mode 100644 index 000000000..009edce97 --- /dev/null +++ b/src/cc65/expr.c @@ -0,0 +1,2980 @@ +/* + * expr.c + * + * Ullrich von Bassewitz, 21.06.1998 + */ + + + +#include +#include +#include + +#include "asmcode.h" +#include "asmlabel.h" +#include "check.h" +#include "codegen.h" +#include "datatype.h" +#include "declare.h" +#include "error.h" +#include "funcdesc.h" +#include "function.h" +#include "global.h" +#include "io.h" +#include "litpool.h" +#include "macrotab.h" +#include "mem.h" +#include "preproc.h" +#include "scanner.h" +#include "stdfunc.h" +#include "symtab.h" +#include "expr.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Generator attributes */ +#define GEN_NOPUSH 0x01 /* Don't push lhs */ + +/* Map a generator function and its attributes to a token */ +typedef struct { + unsigned char Tok; /* Token to map to */ + unsigned char Flags; /* Flags for generator function */ + void (*Func) (unsigned, unsigned long); /* Generator func */ +} GenDesc; + +/* Descriptors for the operations */ +static GenDesc GenMUL = { STAR, GEN_NOPUSH, g_mul }; +static GenDesc GenDIV = { DIV, GEN_NOPUSH, g_div }; +static GenDesc GenMOD = { MOD, GEN_NOPUSH, g_mod }; +static GenDesc GenASL = { ASL, GEN_NOPUSH, g_asl }; +static GenDesc GenASR = { ASR, GEN_NOPUSH, g_asr }; +static GenDesc GenLT = { LT, GEN_NOPUSH, g_lt }; +static GenDesc GenLE = { LE, GEN_NOPUSH, g_le }; +static GenDesc GenGE = { GE, GEN_NOPUSH, g_ge }; +static GenDesc GenGT = { GT, GEN_NOPUSH, g_gt }; +static GenDesc GenEQ = { EQ, GEN_NOPUSH, g_eq }; +static GenDesc GenNE = { NE, GEN_NOPUSH, g_ne }; +static GenDesc GenAND = { AMP, GEN_NOPUSH, g_and }; +static GenDesc GenXOR = { XOR, GEN_NOPUSH, g_xor }; +static GenDesc GenOR = { BAR, GEN_NOPUSH, g_or }; +static GenDesc GenPASGN = { PASGN, GEN_NOPUSH, g_add }; +static GenDesc GenSASGN = { SASGN, GEN_NOPUSH, g_sub }; +static GenDesc GenMASGN = { MASGN, GEN_NOPUSH, g_mul }; +static GenDesc GenDASGN = { DASGN, GEN_NOPUSH, g_div }; +static GenDesc GenMOASGN = { MOASGN, GEN_NOPUSH, g_mod }; +static GenDesc GenSLASGN = { SLASGN, GEN_NOPUSH, g_asl }; +static GenDesc GenSRASGN = { SRASGN, GEN_NOPUSH, g_asr }; +static GenDesc GenAASGN = { AASGN, GEN_NOPUSH, g_and }; +static GenDesc GenXOASGN = { XOASGN, GEN_NOPUSH, g_xor }; +static GenDesc GenOASGN = { OASGN, GEN_NOPUSH, g_or }; + + + +/*****************************************************************************/ +/* Function forwards */ +/*****************************************************************************/ + + + +static int hie10 (struct expent* lval); +/* Handle ++, --, !, unary - etc. */ + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static unsigned GlobalModeFlags (unsigned flags) +/* Return the addressing mode flags for the variable with the given flags */ +{ + flags &= E_MCTYPE; + if (flags == E_TGLAB) { + /* External linkage */ + return CF_EXTERNAL; + } else if (flags == E_TREGISTER) { + /* Register variable */ + return CF_REGVAR; + } else { + /* Static */ + return CF_STATIC; + } +} + + + +static int IsNullPtr (struct expent* lval) +/* Return true if this is the NULL pointer constant */ +{ + return (IsInt (lval->e_tptr) && /* Is it an int? */ + lval->e_flags == E_MCONST && /* Is it constant? */ + lval->e_const == 0); /* And is it's value zero? */ +} + + + +static type* promoteint (type* lhst, type* rhst) +/* In an expression with two ints, return the type of the result */ +{ + /* Rules for integer types: + * - If one of the values is a long, the result is long. + * - If one of the values is unsigned, the result is also unsigned. + * - Otherwise the result is an int. + */ + if (IsLong (lhst) || IsLong (rhst)) { + if (IsUnsigned (lhst) || IsUnsigned (rhst)) { + return type_ulong; + } else { + return type_long; + } + } else { + if (IsUnsigned (lhst) || IsUnsigned (rhst)) { + return type_uint; + } else { + return type_int; + } + } +} + + + +static unsigned typeadjust (struct expent* lhs, struct expent* rhs, int NoPush) +/* Adjust the two values for a binary operation. lhs is expected on stack or + * to be constant, rhs is expected to be in the primary register or constant. + * The function will put the type of the result into lhs and return the + * code generator flags for the operation. + * If NoPush is given, it is assumed that the operation does not expect the lhs + * to be on stack, and that lhs is in a register instead. + * Beware: The function does only accept int types. + */ +{ + unsigned ltype, rtype; + unsigned flags; + + /* Get the type strings */ + type* lhst = lhs->e_tptr; + type* rhst = rhs->e_tptr; + + /* Generate type adjustment code if needed */ + ltype = TypeOf (lhst); + if (lhs->e_flags == E_MCONST) { + ltype |= CF_CONST; + } + if (NoPush) { + /* Value is in primary register*/ + ltype |= CF_REG; + } + rtype = TypeOf (rhst); + if (rhs->e_flags == E_MCONST) { + rtype |= CF_CONST; + } + flags = g_typeadjust (ltype, rtype); + + /* Set the type of the result */ + lhs->e_tptr = promoteint (lhst, rhst); + + /* Return the code generator flags */ + return flags; +} + + + +unsigned assignadjust (type* lhst, struct expent* rhs) +/* Adjust the type of the right hand expression so that it can be assigned to + * the type on the left hand side. This function is used for assignment and + * for converting parameters in a function call. It returns the code generator + * flags for the operation. The type string of the right hand side will be + * set to the type of the left hand side. + */ +{ + /* Get the type of the right hand side */ + type* rhst = rhs->e_tptr; + + /* After calling this function, rhs will have the type of the lhs */ + rhs->e_tptr = lhst; + + /* First, do some type checking */ + if (IsVoid (lhst) || IsVoid (rhst)) { + /* If one of the sides are of type void, output a more apropriate + * error message. + */ + Error (ERR_ILLEGAL_TYPE); + } else if (IsInt (lhst)) { + if (IsPtr (rhst)) { + /* Pointer -> int conversion */ + Warning (WARN_PTR_TO_INT_CONV); + } else if (!IsInt (rhst)) { + Error (ERR_INCOMPATIBLE_TYPES); + } else { + /* Adjust the int types. To avoid manipulation of TOS mark lhs + * as const. + */ + unsigned flags = TypeOf (rhst); + if (rhs->e_flags & E_MCONST) { + flags |= CF_CONST; + } + return g_typeadjust (TypeOf (lhst) | CF_CONST, flags); + } + } else if (IsPtr (lhst)) { + if (IsPtr (rhst)) { + /* Pointer to pointer assignment is valid, if: + * - both point to the same types, or + * - the rhs pointer is a void pointer, or + * - the lhs pointer is a void pointer. + */ + type* left = Indirect (lhst); + type* right = Indirect (rhst); + if (!EqualTypes (left, right) && *left != T_VOID && *right != T_VOID) { + Error (ERR_INCOMPATIBLE_POINTERS); + } + } else if (IsInt (rhst)) { + /* Int to pointer assignment is valid only for constant zero */ + if ((rhs->e_flags & E_MCONST) == 0 || rhs->e_const != 0) { + Warning (WARN_INT_TO_PTR_CONV); + } + } else if (IsFuncPtr (lhst) && IsFunc(rhst)) { + /* Assignment of function to function pointer is allowed, provided + * that both functions have the same parameter list. + */ + if (!EqualTypes(Indirect (lhst), rhst)) { + Error (ERR_INCOMPATIBLE_TYPES); + } + } else { + Error (ERR_INCOMPATIBLE_TYPES); + } + } else { + Error (ERR_INCOMPATIBLE_TYPES); + } + + /* Return an int value in all cases where the operands are not both ints */ + return CF_INT; +} + + + +void DefineData (struct expent* lval) +/* Output a data definition for the given expression */ +{ + unsigned flags = lval->e_flags; + + switch (flags & E_MCTYPE) { + + case E_TCONST: + /* Number */ + g_defdata (TypeOf (lval->e_tptr) | CF_CONST, lval->e_const, 0); + break; + + case E_TREGISTER: + /* Register variable. Taking the address is usually not + * allowed. + */ + if (!AllowRegVarAddr) { + Error (ERR_CANNOT_TAKE_ADDR_OF_REG); + } + /* FALLTHROUGH */ + + case E_TGLAB: + case E_TLLAB: + /* Local or global symbol */ + g_defdata (GlobalModeFlags (flags), lval->e_name, lval->e_const); + break; + + case E_TLIT: + /* a literal of some kind */ + g_defdata (CF_STATIC, LiteralLabel, lval->e_const); + break; + + default: + Internal ("Unknown constant type: %04X", flags); + } +} + + + +static void lconst (unsigned flags, struct expent* lval) +/* Load primary reg with some constant value. */ +{ + switch (lval->e_flags & E_MCTYPE) { + + case E_TLOFFS: + g_leasp (lval->e_const); + break; + + case E_TCONST: + /* Number constant */ + g_getimmed (flags | TypeOf (lval->e_tptr) | CF_CONST, lval->e_const, 0); + break; + + case E_TREGISTER: + /* Register variable. Taking the address is usually not + * allowed. + */ + if (!AllowRegVarAddr) { + Error (ERR_CANNOT_TAKE_ADDR_OF_REG); + } + /* FALLTHROUGH */ + + case E_TGLAB: + case E_TLLAB: + /* Local or global symbol, load address */ + flags |= GlobalModeFlags (lval->e_flags); + flags &= ~CF_CONST; + g_getimmed (flags, lval->e_name, lval->e_const); + break; + + case E_TLIT: + /* Literal string */ + g_getimmed (CF_STATIC, LiteralLabel, lval->e_const); + break; + + default: + Internal ("Unknown constant type: %04X", lval->e_flags); + } +} + + + +static int kcalc (int tok, long val1, long val2) +/* Calculate an operation with left and right operand constant. */ +{ + switch (tok) { + case EQ: + return (val1 == val2); + case NE: + return (val1 != val2); + case LT: + return (val1 < val2); + case LE: + return (val1 <= val2); + case GE: + return (val1 >= val2); + case GT: + return (val1 > val2); + case BAR: + return (val1 | val2); + case XOR: + return (val1 ^ val2); + case AMP: + return (val1 & val2); + case ASR: + return (val1 >> val2); + case ASL: + return (val1 << val2); + case STAR: + return (val1 * val2); + case DIV: + if (val2 == 0) { + Error (ERR_DIV_BY_ZERO); + return 0x7FFFFFFF; + } + return (val1 / val2); + case MOD: + if (val2 == 0) { + Error (ERR_MOD_BY_ZERO); + return 0; + } + return (val1 % val2); + default: + Internal ("kcalc: got token 0x%X\n", tok); + return 0; + } +} + + + +static GenDesc* FindGen (int Tok, GenDesc** Table) +{ + GenDesc* G; + while ((G = *Table) != 0) { + if (G->Tok == Tok) { + return G; + } + ++Table; + } + return 0; +} + + + +static int istypeexpr (void) +/* Return true if some sort of variable or type is waiting (helper for cast + * and sizeof() in hie10). + */ +{ + SymEntry* Entry; + + return curtok == LPAREN && ( + (nxttok >= FIRSTTYPE && nxttok <= LASTTYPE) || + (nxttok == CONST) || + (nxttok == IDENT && + (Entry = FindSym (NextTok.Ident)) != 0 && + IsTypeDef (Entry)) + ); +} + + + +static void PushAddr (struct expent* lval) +/* If the expression contains an address that was somehow evaluated, + * push this address on the stack. This is a helper function for all + * sorts of implicit or explicit assignment functions where the lvalue + * must be saved if it's not constant, before evaluating the rhs. + */ +{ + /* Get the address on stack if needed */ + if (lval->e_flags != E_MREG && (lval->e_flags & E_MEXPR)) { + /* Push the address (always a pointer) */ + g_push (CF_PTR, 0); + } +} + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void exprhs (unsigned flags, int k, struct expent *lval) +/* Put the result of an expression into the primary register */ +{ + int f; + + f = lval->e_flags; + if (k) { + /* Dereferenced lvalue */ + flags |= TypeOf (lval->e_tptr); + if (lval->e_test & E_FORCETEST) { + flags |= CF_TEST; + lval->e_test &= ~E_FORCETEST; + } + if (f & E_MGLOBAL) { /* ref to globalvar */ + /* Generate code */ + flags |= GlobalModeFlags (f); + g_getstatic (flags, lval->e_name, lval->e_const); + } else if (f & E_MLOCAL) { + /* ref to localvar */ + g_getlocal (flags, lval->e_const); + } else if (f & E_MCONST) { + /* ref to absolute address */ + g_getstatic (flags | CF_ABSOLUTE, lval->e_const, 0); + } else if (f == E_MEOFFS) { + g_getind (flags, lval->e_const); + } else if (f != E_MREG) { + g_getind (flags, 0); + } + } else if (f == E_MEOFFS) { + /* reference not storable */ + flags |= TypeOf (lval->e_tptr); + g_inc (flags | CF_CONST, lval->e_const); + } else if ((f & E_MEXPR) == 0) { + /* Constant of some sort, load it into the primary */ + lconst (flags, lval); + } + if (lval->e_test & E_FORCETEST) { /* we testing this value? */ + /* debug... */ + AddCodeHint ("forcetest"); + flags |= TypeOf (lval->e_tptr); + g_test (flags); /* yes, force a test */ + lval->e_test &= ~E_FORCETEST; + } +} + + +static void callfunction (struct expent* lval) +/* Perform a function call. Called from hie11, this routine will + * either call the named function, or if the supplied ptr is zero, + * will call the contents of P. + */ +{ + struct expent lval2; + FuncDesc* Func; /* Function descriptor */ + int Ellipsis; /* True if we have an open param list */ + SymEntry* Param; /* Current formal parameter */ + unsigned ParamCount; /* Actual parameter count */ + unsigned ParamSize; /* Number of parameter bytes */ + unsigned Flags; + unsigned CFlags; + CodeMark Mark; + + + /* Get a pointer to the function descriptor from the type string */ + Func = GetFuncDesc (lval->e_tptr); + + /* Initialize vars to keep gcc silent */ + Param = 0; + Mark = 0; + + /* Check if this is a function pointer. If so, save it. If not, check for + * special known library functions that may be inlined. + */ + if (lval->e_flags & E_MEXPR) { + /* Function pointer is in primary register, save it */ + Mark = GetCodePos (); + g_save (CF_PTR); + } else if (InlineStdFuncs && IsStdFunc ((const char*) lval->e_name)) { + /* Inline this function */ + HandleStdFunc (lval); + return; + } + + /* Parse the actual parameter list */ + ParamSize = 0; + ParamCount = 0; + Ellipsis = 0; + while (curtok != RPAREN) { + + /* Add a hint for the optimizer */ + AddCodeHint ("param:start"); + + /* Count arguments */ + ++ParamCount; + + /* Fetch the pointer to the next argument, check for too many args */ + if (ParamCount <= Func->ParamCount) { + if (ParamCount == 1) { + /* First argument */ + Param = Func->SymTab->SymHead; + } else { + /* Next argument */ + Param = Param->NextSym; + CHECK ((Param->Flags & SC_PARAM) != 0); + } + } else if (!Ellipsis) { + /* Too many arguments. Do we have an open param list? */ + if ((Func->Flags & FD_ELLIPSIS) == 0) { + /* End of param list reached, no ellipsis */ + Error (ERR_TOO_MANY_FUNC_ARGS); + } + /* Assume an ellipsis even in case of errors to avoid an error + * message for each other argument. + */ + Ellipsis = 1; + } + + /* Do some optimization: If we have a constant value to push, + * use a special function that may optimize. + */ + CFlags = CF_NONE; + if (!Ellipsis && SizeOf (Param->Type) == 1) { + CFlags = CF_FORCECHAR; + } + Flags = 0; + if (evalexpr (CFlags, hie1, &lval2) == 0) { + /* A constant value */ + Flags |= CF_CONST; + } + + /* If we don't have an argument spec, accept anything, otherwise + * convert the actual argument to the type needed. + */ + if (!Ellipsis) { + /* Promote the argument if needed */ + assignadjust (Param->Type, &lval2); + /* If we have a prototype, chars may be pushed as chars */ + Flags |= CF_FORCECHAR; + } + + /* Use the type of the argument for the push */ + Flags |= TypeOf (lval2.e_tptr); + + /* If this is a fastcall function, don't push the last argument */ + if (ParamCount == Func->ParamCount && (Func->Flags & FD_FASTCALL) != 0) { + /* Just load the argument into the primary. This is only needed if + * we have a constant argument, otherwise the value is already in + * the primary. + */ + if (Flags & CF_CONST) { + exprhs (CF_FORCECHAR, 0, &lval2); + } + } else { + /* Push the argument, count the argument size */ + g_push (Flags, lval2.e_const); + ParamSize += sizeofarg (Flags); + } + + /* Add an optimizer hint */ + AddCodeHint ("param:end"); + + /* Check for end of argument list */ + if (curtok != COMMA) { + break; + } + gettok (); + } + + /* We need the closing bracket here */ + ConsumeRParen (); + + /* Check if we had enough parameters */ + if (ParamCount < Func->ParamCount) { + Error (ERR_TOO_FEW_FUNC_ARGS); + } + + /* */ + if (lval->e_flags & E_MEXPR) { + /* Function called via pointer: Restore it and call function */ + if (ParamSize != 0) { + g_restore (CF_PTR); + } else { + /* We had no parameters - remove save code */ + RemoveCode (Mark); + } + g_callind (TypeOf (lval->e_tptr), ParamSize); + } else { + g_call (TypeOf (lval->e_tptr), (char*) lval->e_name, ParamSize); + } +} + + + +void doasm (void) +/* This function parses ASM statements. The syntax of the ASM directive + * looks like the one defined for C++ (C has no ASM directive), that is, + * a string literal in parenthesis. + */ +{ + /* Skip the ASM */ + gettok (); + + /* Need left parenthesis */ + ConsumeLParen (); + + /* String literal */ + if (curtok != SCONST) { + Error (ERR_STRLIT_EXPECTED); + } else { + /* Write the string directly into the output, followed by a newline */ + AddCodeLine (GetLiteral (curval)); + + /* Reset the string pointer, effectivly clearing the string from the + * string table. Since we're working with one token lookahead, this + * will fail if the next token is also a string token, but that's a + * syntax error anyway, because we expect a right paren. + */ + ResetLiteralOffs (curval); + } + + /* Skip the string token */ + gettok (); + + /* Closing paren needed */ + ConsumeRParen (); +} + + + +static int primary (struct expent* lval) +/* This is the lowest level of the expression parser. */ +{ + int k; + + /* not a test at all, yet */ + lval->e_test = 0; + + /* Character and integer constants. */ + if (curtok == ICONST || curtok == CCONST) { + lval->e_flags = E_MCONST | E_TCONST; + lval->e_tptr = curtype; + lval->e_const = curval; + gettok (); + return 0; + } + + /* Process parenthesized subexpression by calling the whole parser + * recursively. + */ + if (curtok == LPAREN) { + gettok (); + memset (lval, 0, sizeof (*lval)); /* Remove any attributes */ + k = hie0 (lval); + ConsumeRParen (); + return k; + } + + /* All others may only be used if the expression evaluation is not called + * recursively by the preprocessor. + */ + if (Preprocessing) { + /* Illegal expression in PP mode */ + Error (ERR_CPP_EXPR_EXPECTED); + lval->e_flags = E_MCONST; + lval->e_tptr = type_int; + return 0; + } + + /* Identifier? */ + if (curtok == IDENT) { + + SymEntry* Sym; + ident Ident; + + /* Get a pointer to the symbol table entry */ + Sym = FindSym (CurTok.Ident); + + /* Is the symbol known? */ + if (Sym) { + + /* We found the symbol - skip the name token */ + gettok (); + + /* The expression type is the symbol type */ + lval->e_tptr = Sym->Type; + + /* Check for illegal symbol types */ + if ((Sym->Flags & SC_LABEL) == SC_LABEL) { + /* Cannot use labels in expressions */ + Error (ERR_SYMBOL_KIND); + return 1; + } else if (Sym->Flags & SC_TYPE) { + /* Cannot use type symbols */ + Error (ERR_VAR_IDENT_EXPECTED); + /* Assume an int type to make lval valid */ + lval->e_flags = E_MLOCAL | E_TLOFFS; + lval->e_tptr = type_int; + lval->e_const = 0; + return 0; + } + + /* Check for legal symbol types */ + if ((Sym->Flags & SC_ENUM) == SC_ENUM) { + lval->e_flags = E_MCONST; + lval->e_const = Sym->V.EnumVal; + return 0; + } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { + /* Function */ + lval->e_flags = E_MGLOBAL | E_MCONST | E_TGLAB; + lval->e_name = (unsigned long) Sym->Name; + lval->e_const = 0; + } else if ((Sym->Flags & SC_AUTO) == SC_AUTO) { + /* Local variable */ + lval->e_flags = E_MLOCAL | E_TLOFFS; + lval->e_const = Sym->V.Offs; + } else if ((Sym->Flags & SC_STATIC) == SC_STATIC) { + /* Static variable */ + if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) { + lval->e_flags = E_MGLOBAL | E_MCONST | E_TGLAB; + lval->e_name = (unsigned long) Sym->Name; + } else { + lval->e_flags = E_MGLOBAL | E_MCONST | E_TLLAB; + lval->e_name = Sym->V.Label; + } + lval->e_const = 0; + } else if ((Sym->Flags & SC_REGISTER) == SC_REGISTER) { + /* Register variable, zero page based */ + lval->e_flags = E_MGLOBAL | E_MCONST | E_TREGISTER; + lval->e_name = Sym->V.Offs; + lval->e_const = 0; + } else { + /* Local static variable */ + lval->e_flags = E_MGLOBAL | E_MCONST | E_TLLAB; + lval->e_name = Sym->V.Offs; + lval->e_const = 0; + } + + /* The symbol is referenced now */ + Sym->Flags |= SC_REF; + if (IsFunc (lval->e_tptr) || IsArray (lval->e_tptr)) { + return 0; + } + return 1; + } + + /* We did not find the symbol. Remember the name, then skip it */ + strcpy (Ident, CurTok.Ident); + gettok (); + + /* IDENT is either an auto-declared function or an undefined variable. */ + if (curtok == LPAREN) { + /* Declare a function returning int. For that purpose, prepare a + * function signature for a function having an empty param list + * and returning int. + */ + Warning (WARN_FUNC_WITHOUT_PROTO); + Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_EXTERN | SC_REF); + lval->e_tptr = Sym->Type; + lval->e_flags = E_MGLOBAL | E_MCONST | E_TGLAB; + lval->e_name = (unsigned long) Sym->Name; + lval->e_const = 0; + return 0; + + } else { + + /* Undeclared Variable */ + Sym = AddLocalSym (Ident, type_int, SC_AUTO | SC_REF, 0); + lval->e_flags = E_MLOCAL | E_TLOFFS; + lval->e_tptr = type_int; + lval->e_const = 0; + Error (ERR_UNDEFINED_SYMBOL, Ident); + return 1; + + } + } + + /* String literal? */ + if (curtok == SCONST) { + lval->e_flags = E_MCONST | E_TLIT; + lval->e_const = curval; + lval->e_tptr = GetCharArrayType (strlen (GetLiteral (curval))); + gettok (); + return 0; + } + + /* ASM statement? */ + if (curtok == ASM) { + doasm (); + lval->e_tptr = type_void; + lval->e_flags = E_MEXPR; + lval->e_const = 0; + return 0; + } + + /* __AX__ and __EAX__ pseudo values? */ + if (curtok == AX || curtok == EAX) { + lval->e_tptr = (curtok == AX)? type_uint : type_ulong; + lval->e_flags = E_MREG; + lval->e_test &= ~E_CC; + lval->e_const = 0; + gettok (); + return 1; /* May be used as lvalue */ + } + + /* Illegal primary. */ + Error (ERR_EXPR_EXPECTED); + lval->e_flags = E_MCONST; + lval->e_tptr = type_int; + return 0; +} + + + +static int arrayref (int k, struct expent* lval) +/* Handle an array reference */ +{ + unsigned lflags; + unsigned rflags; + int ConstBaseAddr; + int ConstSubAddr; + int l; + struct expent lval2; + CodeMark Mark1; + CodeMark Mark2; + type* tptr1; + type* tptr2; + + + /* Skip the bracket */ + gettok (); + + /* Get the type of left side */ + tptr1 = lval->e_tptr; + + /* We can apply a special treatment for arrays that have a const base + * address. This is true for most arrays and will produce a lot better + * code. Check if this is a const base address. + */ + lflags = lval->e_flags & ~E_MCTYPE; + ConstBaseAddr = (lflags == E_MCONST) || /* Constant numeric address */ + (lflags & E_MGLOBAL) != 0 || /* Static array, or ... */ + lflags == E_MLOCAL; /* Local array */ + + /* If we have a constant base, we delay the address fetch */ + Mark1 = GetCodePos (); + Mark2 = 0; /* Silence gcc */ + if (!ConstBaseAddr) { + /* Get a pointer to the array into the primary */ + exprhs (CF_NONE, k, lval); + + /* Get the array pointer on stack. Do not push more than 16 + * bit, even if this value is greater, since we cannot handle + * other than 16bit stuff when doing indexing. + */ + Mark2 = GetCodePos (); + g_push (CF_PTR, 0); + } + + /* TOS now contains ptr to array elements. Get the subscript. */ + l = hie0 (&lval2); + if (l == 0 && lval2.e_flags == E_MCONST) { + + /* The array subscript is a constant - remove value from stack */ + if (!ConstBaseAddr) { + RemoveCode (Mark2); + pop (CF_PTR); + } else { + /* Get an array pointer into the primary */ + exprhs (CF_NONE, k, lval); + } + + if (IsPtr (tptr1)) { + + /* Scale the subscript value according to element size */ + lval2.e_const *= PSizeOf (tptr1); + + /* Remove code for lhs load */ + RemoveCode (Mark1); + + /* Handle constant base array on stack. Be sure NOT to + * handle pointers the same way, this won't work. + */ + if (IsArray (tptr1) && + ((lval->e_flags & ~E_MCTYPE) == E_MCONST || + (lval->e_flags & ~E_MCTYPE) == E_MLOCAL || + (lval->e_flags & E_MGLOBAL) != 0 || + (lval->e_flags == E_MEOFFS))) { + lval->e_const += lval2.e_const; + + } else { + /* Pointer - load into primary and remember offset */ + if ((lval->e_flags & E_MEXPR) == 0 || k != 0) { + exprhs (CF_NONE, k, lval); + } + lval->e_const = lval2.e_const; + lval->e_flags = E_MEOFFS; + } + + /* Result is of element type */ + lval->e_tptr = Indirect (tptr1); + + /* Done */ + goto end_array; + + } else if ((tptr2 = lval2.e_tptr) [0] & T_POINTER) { + /* Subscript is pointer, get element type */ + lval2.e_tptr = Indirect (tptr2); + + /* Scale the rhs value in the primary register */ + g_scale (TypeOf (tptr1), SizeOf (lval2.e_tptr)); + /* */ + lval->e_tptr = lval2.e_tptr; + } else { + Error (ERR_CANNOT_SUBSCRIPT); + } + + /* Add the subscript. Since arrays are indexed by integers, + * we will ignore the true type of the subscript here and + * use always an int. + */ + g_inc (CF_INT | CF_CONST, lval2.e_const); + + } else { + + /* Array subscript is not constant. Load it into the primary */ + Mark2 = GetCodePos (); + exprhs (CF_NONE, l, &lval2); + + tptr2 = lval2.e_tptr; + if (IsPtr (tptr1)) { + + /* Get the element type */ + lval->e_tptr = Indirect (tptr1); + + /* Indexing is based on int's, so we will just use the integer + * portion of the index (which is in (e)ax, so there's no further + * action required). + */ + g_scale (CF_INT, SizeOf (lval->e_tptr)); + + } else if (IsPtr (tptr2)) { + + /* Get the element type */ + lval2.e_tptr = Indirect (tptr2); + + /* Get the int value on top. If we go here, we're sure, + * both values are 16 bit (the first one was truncated + * if necessary and the second one is a pointer). + * Note: If ConstBaseAddr is true, we don't have a value on + * stack, so to "swap" both, just push the subscript. + */ + if (ConstBaseAddr) { + g_push (CF_INT, 0); + exprhs (CF_NONE, k, lval); + ConstBaseAddr = 0; + } else { + g_swap (CF_INT); + } + + /* Scale it */ + g_scale (TypeOf (tptr1), SizeOf (lval2.e_tptr)); + lval->e_tptr = lval2.e_tptr; + } else { + Error (ERR_CANNOT_SUBSCRIPT); + } + + /* The offset is now in the primary register. It didn't have a + * constant base address for the lhs, the lhs address is already + * on stack, and we must add the offset. If the base address was + * constant, we call special functions to add the address to the + * offset value. + */ + if (!ConstBaseAddr) { + /* Add the subscript. Both values are int sized. */ + g_add (CF_INT, 0); + } else { + + /* If the subscript has itself a constant address, it is often + * a better idea to reverse again the order of the evaluation. + * This will generate better code if the subscript is a byte + * sized variable. But beware: This is only possible if the + * subscript was not scaled, that is, if this was a byte array + * or pointer. + */ + rflags = lval2.e_flags & ~E_MCTYPE; + ConstSubAddr = (rflags == E_MCONST) || /* Constant numeric address */ + (rflags & E_MGLOBAL) != 0 || /* Static array, or ... */ + rflags == E_MLOCAL; /* Local array */ + + if (ConstSubAddr && SizeOf (lval->e_tptr) == 1) { + + type* SavedType; + + /* Reverse the order of evaluation */ + unsigned flags = (SizeOf (lval2.e_tptr) == 1)? CF_CHAR : CF_INT; + RemoveCode (Mark2); + + /* Get a pointer to the array into the primary. We have changed + * e_tptr above but we need the original type to load the + * address, so restore it temporarily. + */ + SavedType = lval->e_tptr; + lval->e_tptr = tptr1; + exprhs (CF_NONE, k, lval); + lval->e_tptr = SavedType; + + /* Add the variable */ + if (rflags == E_MLOCAL) { + g_addlocal (flags, lval2.e_const); + } else { + flags |= GlobalModeFlags (lval2.e_flags); + g_addstatic (flags, lval2.e_name, lval2.e_const); + } + } else { + if (lflags == E_MCONST) { + /* Constant numeric address. Just add it */ + g_inc (CF_INT | CF_UNSIGNED, lval->e_const); + } else if (lflags == E_MLOCAL) { + /* Base address is a local variable address */ + if (IsArray (tptr1)) { + g_addaddr_local (CF_INT, lval->e_const); + } else { + g_addlocal (CF_PTR, lval->e_const); + } + } else { + /* Base address is a static variable address */ + unsigned flags = CF_INT; + flags |= GlobalModeFlags (lval->e_flags); + if (IsArray (tptr1)) { + g_addaddr_static (flags, lval->e_name, lval->e_const); + } else { + g_addstatic (flags, lval->e_name, lval->e_const); + } + } + } + } + } + lval->e_flags = E_MEXPR; +end_array: + ConsumeRBrack (); + return !IsArray (lval->e_tptr); + +} + + + +static int structref (int k, struct expent* lval) +/* Process struct field after . or ->. */ +{ + ident Ident; + SymEntry* Field; + int flags; + + /* Skip the token and check for an identifier */ + gettok (); + if (curtok != IDENT) { + Error (ERR_IDENT_EXPECTED); + lval->e_tptr = type_int; + return 0; + } + + /* Get the symbol table entry and check for a struct field */ + strcpy (Ident, CurTok.Ident); + gettok (); + Field = FindStructField (lval->e_tptr, Ident); + if (Field == 0) { + Error (ERR_STRUCT_FIELD_MISMATCH, Ident); + lval->e_tptr = type_int; + return 0; + } + + /* If we have constant input data, the result is also constant */ + flags = lval->e_flags & ~E_MCTYPE; + if (flags == E_MCONST || + (k == 0 && (flags == E_MLOCAL || + (flags & E_MGLOBAL) != 0 || + lval->e_flags == E_MEOFFS))) { + lval->e_const += Field->V.Offs; + } else { + if ((flags & E_MEXPR) == 0 || k != 0) { + exprhs (CF_NONE, k, lval); + } + lval->e_const = Field->V.Offs; + lval->e_flags = E_MEOFFS; + } + lval->e_tptr = Field->Type; + return !IsArray (Field->Type); +} + + + +static int hie11 (struct expent *lval) +/* Handle compound types (structs and arrays) */ +{ + int k; + type* tptr; + + + k = primary (lval); + if (curtok < LBRACK || curtok > PREF) { + /* Not for us */ + return k; + } + + while (1) { + + if (curtok == LBRACK) { + + /* Array reference */ + k = arrayref (k, lval); + + } else if (curtok == LPAREN) { + + /* Function call. Skip the opening parenthesis */ + gettok (); + tptr = lval->e_tptr; + if (IsFunc (tptr) || IsFuncPtr (tptr)) { + if (IsFuncPtr (tptr)) { + /* Pointer to function. Handle transparently */ + exprhs (CF_NONE, k, lval); /* Function pointer to A/X */ + ++lval->e_tptr; /* Skip T_PTR */ + lval->e_flags |= E_MEXPR; + } + callfunction (lval); + lval->e_flags = E_MEXPR; + lval->e_tptr += DECODE_SIZE + 1; /* Set to result */ + } else { + Error (ERR_ILLEGAL_FUNC_CALL); + } + k = 0; + + } else if (curtok == DOT) { + + if (!IsStruct (lval->e_tptr)) { + Error (ERR_STRUCT_EXPECTED); + } + k = structref (0, lval); + + } else if (curtok == PREF) { + + tptr = lval->e_tptr; + if (tptr[0] != T_PTR || (tptr[1] & T_STRUCT) == 0) { + Error (ERR_STRUCT_PTR_EXPECTED); + } + k = structref (k, lval); + + } else { + return k; + } + } +} + + + +static void store (struct expent* lval) +/* Store primary reg into this reference */ +{ + int f; + unsigned flags; + + f = lval->e_flags; + flags = TypeOf (lval->e_tptr); + if (f & E_MGLOBAL) { + flags |= GlobalModeFlags (f); + if (lval->e_test) { + /* Just testing */ + flags |= CF_TEST; + } + + /* Generate code */ + g_putstatic (flags, lval->e_name, lval->e_const); + + } else if (f & E_MLOCAL) { + g_putlocal (flags, lval->e_const); + } else if (f == E_MEOFFS) { + g_putind (flags, lval->e_const); + } else if (f != E_MREG) { + if (f & E_MEXPR) { + g_putind (flags, 0); + } else { + /* Store into absolute address */ + g_putstatic (flags | CF_ABSOLUTE, lval->e_const, 0); + } + } + + /* Assume that each one of the stores will invalidate CC */ + lval->e_test &= ~E_CC; +} + + + +static void pre_incdec (struct expent* lval, void (*inc) (unsigned, unsigned long)) +/* Handle --i and ++i */ +{ + int k; + unsigned flags; + unsigned long val; + + gettok (); + if ((k = hie10 (lval)) == 0) { + Error (ERR_LVALUE_EXPECTED); + return; + } + + /* Get the data type */ + flags = TypeOf (lval->e_tptr) | CF_FORCECHAR | CF_CONST; + + /* Get the increment value in bytes */ + val = (lval->e_tptr [0] == T_PTR)? PSizeOf (lval->e_tptr) : 1; + + /* We're currently only able to handle some adressing modes */ + if ((lval->e_flags & E_MGLOBAL) == 0 && /* Global address? */ + (lval->e_flags & E_MLOCAL) == 0 && /* Local address? */ + (lval->e_flags & E_MCONST) == 0 && /* Constant address? */ + (lval->e_flags & E_MEXPR) == 0) { /* Address in a/x? */ + + /* Use generic code. Push the address if needed */ + PushAddr (lval); + + /* Fetch the value */ + exprhs (CF_NONE, k, lval); + + /* Increment value in primary */ + inc (flags, val); + + /* Store the result back */ + store (lval); + + } else { + + /* Special code for some addressing modes - use the special += ops */ + if (lval->e_flags & E_MGLOBAL) { + flags |= GlobalModeFlags (lval->e_flags); + if (inc == g_inc) { + g_addeqstatic (flags, lval->e_name, lval->e_const, val); + } else { + g_subeqstatic (flags, lval->e_name, lval->e_const, val); + } + } else if (lval->e_flags & E_MLOCAL) { + /* ref to localvar */ + if (inc == g_inc) { + g_addeqlocal (flags, lval->e_const, val); + } else { + g_subeqlocal (flags, lval->e_const, val); + } + } else if (lval->e_flags & E_MCONST) { + /* ref to absolute address */ + flags |= CF_ABSOLUTE; + if (inc == g_inc) { + g_addeqstatic (flags, lval->e_const, 0, val); + } else { + g_subeqstatic (flags, lval->e_const, 0, val); + } + } else if (lval->e_flags & E_MEXPR) { + /* Address in a/x. */ + if (inc == g_inc) { + g_addeqind (flags, lval->e_const, val); + } else { + g_subeqind (flags, lval->e_const, val); + } + } else { + Internal ("Invalid addressing mode"); + } + + } + + /* Result is an expression */ + lval->e_flags = E_MEXPR; +} + + + +static void post_incdec (struct expent *lval, int k, void (*inc) (unsigned, unsigned long)) +/* Handle i-- and i++ */ +{ + unsigned flags; + + gettok (); + if (k == 0) { + Error (ERR_LVALUE_EXPECTED); + return; + } + + /* Get the data type */ + flags = TypeOf (lval->e_tptr); + + /* Push the address if needed */ + PushAddr (lval); + + /* Fetch the value and save it (since it's the result of the expression) */ + exprhs (CF_NONE, 1, lval); + g_save (flags | CF_FORCECHAR); + + /* If we have a pointer expression, increment by the size of the type */ + if (lval->e_tptr[0] == T_PTR) { + inc (flags | CF_CONST | CF_FORCECHAR, SizeOf (lval->e_tptr + 1)); + } else { + inc (flags | CF_CONST | CF_FORCECHAR, 1); + } + + /* Store the result back */ + store (lval); + + /* Restore the original value */ + g_restore (flags | CF_FORCECHAR); + lval->e_flags = E_MEXPR; +} + + + +static void unaryop (int tok, struct expent* lval) +/* Handle unary -/+ and ~ */ +{ + int k; + unsigned flags; + + gettok (); + k = hie10 (lval); + if (k == 0 && lval->e_flags & E_MCONST) { + /* Value is constant */ + switch (tok) { + case MINUS: lval->e_const = -lval->e_const; break; + case PLUS: break; + case COMP: lval->e_const = ~lval->e_const; break; + default: Internal ("Unexpected token: %d", tok); + } + } else { + /* Value is not constant */ + exprhs (CF_NONE, k, lval); + + /* Get the type of the expression */ + flags = TypeOf (lval->e_tptr); + + /* Handle the operation */ + switch (tok) { + case MINUS: g_neg (flags); break; + case PLUS: break; + case COMP: g_com (flags); break; + default: Internal ("Unexpected token: %d", tok); + } + lval->e_flags = E_MEXPR; + } +} + + + +static int typecast (struct expent* lval) +/* Handle an explicit cast */ +{ + int k; + type Type[MAXTYPELEN]; + unsigned rflags; + + /* Skip the left paren */ + gettok (); + + /* Read the type */ + ParseType (Type); + + /* Closing paren */ + ConsumeRParen (); + + /* Read the expression we have to cast */ + k = hie10 (lval); + + /* Get the type of the expression and honor constant values */ + rflags = TypeOf (lval->e_tptr); + if (lval->e_flags & E_MCONST) { + rflags |= CF_CONST; + } + + /* Do the actual cast. Special handling for void casts */ + if (!IsVoid (Type)) { + /* Mark the lhs as const to avoid a manipulation of TOS */ + g_typecast (TypeOf (Type) | CF_CONST, rflags); + } + + /* Use the new type */ + lval->e_tptr = TypeDup (Type); + + /* Done */ + return k; +} + + + +static int hie10 (struct expent* lval) +/* Handle ++, --, !, unary - etc. */ +{ + int k; + type* t; + + switch (curtok) { + + case INC: + pre_incdec (lval, g_inc); + return 0; + + case DEC: + pre_incdec (lval, g_dec); + return 0; + + case PLUS: + case MINUS: + case COMP: + unaryop (curtok, lval); + return 0; + + case BANG: + gettok (); + if (evalexpr (CF_NONE, hie10, lval) == 0) { + /* Constant expression */ + lval->e_const = !lval->e_const; + } else { + g_bneg (TypeOf (lval->e_tptr)); + lval->e_test |= E_CC; /* bneg will set cc */ + lval->e_flags = E_MEXPR; /* say it's an expr */ + } + return 0; /* expr not storable */ + + case STAR: + gettok (); + if (evalexpr (CF_NONE, hie10, lval) != 0) { + /* Expression is not const, indirect value loaded into primary */ + lval->e_flags = E_MEXPR; + lval->e_const = 0; /* Offset is zero now */ + } + t = lval->e_tptr; + if (IsPtr (t)) { + lval->e_tptr = Indirect (t); + } else { + Error (ERR_ILLEGAL_INDIRECT); + } + return 1; + + case AMP: + gettok (); + k = hie10 (lval); + if (k == 0) { + /* Allow the & operator with an array */ + if (!IsArray (lval->e_tptr)) { + Error (ERR_ILLEGAL_ADDRESS); + } + } else { + t = TypeAlloc (TypeLen (lval->e_tptr) + 2); + t [0] = T_PTR; + TypeCpy (t + 1, lval->e_tptr); + lval->e_tptr = t; + } + return 0; + + case SIZEOF: + gettok (); + if (istypeexpr ()) { + type Type[MAXTYPELEN]; + gettok (); + lval->e_const = SizeOf (ParseType (Type)); + ConsumeRParen (); + } else { + /* Remember the output queue pointer */ + CodeMark Mark = GetCodePos (); + hie10 (lval); + lval->e_const = SizeOf (lval->e_tptr); + /* Remove any generated code */ + RemoveCode (Mark); + } + lval->e_flags = E_MCONST | E_TCONST; + lval->e_tptr = type_uint; + lval->e_test &= ~E_CC; + return 0; + + default: + if (istypeexpr ()) { + /* A cast */ + return typecast (lval); + } + } + + k = hie11 (lval); + switch (curtok) { + case INC: + post_incdec (lval, k, g_inc); + return 0; + + case DEC: + post_incdec (lval, k, g_dec); + return 0; + + default: + return k; + } +} + + + +static int hie_internal (GenDesc** ops, /* List of generators */ + struct expent* lval, /* parent expr's lval */ + int (*hienext) (struct expent*), + int* UsedGen) /* next higher level */ +/* Helper function */ +{ + int k; + struct expent lval2; + CodeMark Mark1; + CodeMark Mark2; + GenDesc* Gen; + int tok; /* The operator token */ + unsigned ltype, type; + int rconst; /* Operand is a constant */ + + + k = hienext (lval); + + *UsedGen = 0; + while ((Gen = FindGen (curtok, ops)) != 0) { + + /* Tell the caller that we handled it's ops */ + *UsedGen = 1; + + /* All operators that call this function expect an int on the lhs */ + if (!IsInt (lval->e_tptr)) { + Error (ERR_INT_EXPR_EXPECTED); + } + + /* Remember the operator token, then skip it */ + tok = curtok; + gettok (); + + /* Get the lhs on stack */ + Mark1 = GetCodePos (); + ltype = TypeOf (lval->e_tptr); + if (k == 0 && lval->e_flags == E_MCONST) { + /* Constant value */ + Mark2 = GetCodePos (); + g_push (ltype | CF_CONST, lval->e_const); + } else { + /* Value not constant */ + exprhs (CF_NONE, k, lval); + Mark2 = GetCodePos (); + g_push (ltype, 0); + } + + /* Get the right hand side */ + rconst = (evalexpr (CF_NONE, hienext, &lval2) == 0); + + /* Check the type of the rhs */ + if (!IsInt (lval2.e_tptr)) { + Error (ERR_INT_EXPR_EXPECTED); + } + + /* Check for const operands */ + if (k == 0 && lval->e_flags == E_MCONST && rconst) { + + /* Both operands are constant, remove the generated code */ + RemoveCode (Mark1); + pop (ltype); + + /* Evaluate the result */ + lval->e_const = kcalc (tok, lval->e_const, lval2.e_const); + + /* Get the type of the result */ + lval->e_tptr = promoteint (lval->e_tptr, lval2.e_tptr); + + } else { + + /* If the right hand side is constant, and the generator function + * expects the lhs in the primary, remove the push of the primary + * now. + */ + unsigned rtype = TypeOf (lval2.e_tptr); + type = 0; + if (rconst) { + /* Second value is constant - check for div */ + type |= CF_CONST; + rtype |= CF_CONST; + if (tok == DIV && lval2.e_const == 0) { + Error (ERR_DIV_BY_ZERO); + } else if (tok == MOD && lval2.e_const == 0) { + Error (ERR_MOD_BY_ZERO); + } + if ((Gen->Flags & GEN_NOPUSH) != 0) { + RemoveCode (Mark2); + pop (ltype); + ltype |= CF_REG; /* Value is in register */ + } + } + + /* Determine the type of the operation result. */ + type |= g_typeadjust (ltype, rtype); + lval->e_tptr = promoteint (lval->e_tptr, lval2.e_tptr); + + /* Generate code */ + Gen->Func (type, lval2.e_const); + lval->e_flags = E_MEXPR; + } + + /* We have a rvalue now */ + k = 0; + } + + return k; +} + + + +static int hie_compare (GenDesc** ops, /* List of generators */ + struct expent* lval, /* parent expr's lval */ + int (*hienext) (struct expent*)) +/* Helper function for the compare operators */ +{ + int k; + struct expent lval2; + CodeMark Mark1; + CodeMark Mark2; + GenDesc* Gen; + int tok; /* The operator token */ + unsigned ltype; + int rconst; /* Operand is a constant */ + + + k = hienext (lval); + + while ((Gen = FindGen (curtok, ops)) != 0) { + + /* Remember the operator token, then skip it */ + tok = curtok; + gettok (); + + /* Get the lhs on stack */ + Mark1 = GetCodePos (); + ltype = TypeOf (lval->e_tptr); + if (k == 0 && lval->e_flags == E_MCONST) { + /* Constant value */ + Mark2 = GetCodePos (); + g_push (ltype | CF_CONST, lval->e_const); + } else { + /* Value not constant */ + exprhs (CF_NONE, k, lval); + Mark2 = GetCodePos (); + g_push (ltype, 0); + } + + /* Get the right hand side */ + rconst = (evalexpr (CF_NONE, hienext, &lval2) == 0); + + /* Make sure, the types are compatible */ + if (IsInt (lval->e_tptr)) { + if (!IsInt (lval2.e_tptr) && !(IsPtr(lval2.e_tptr) && IsNullPtr(lval))) { + Error (ERR_INCOMPATIBLE_TYPES); + } + } else if (IsPtr (lval->e_tptr)) { + if (IsPtr (lval2.e_tptr)) { + /* Both pointers are allowed in comparison if they point to + * the same type, or if one of them is a void pointer. + */ + type* left = Indirect (lval->e_tptr); + type* right = Indirect (lval2.e_tptr); + if (!EqualTypes (left, right) && *left != T_VOID && *right != T_VOID) { + /* Incomatible pointers */ + Error (ERR_INCOMPATIBLE_TYPES); + } + } else if (!IsNullPtr (&lval2)) { + Error (ERR_INCOMPATIBLE_TYPES); + } + } + + /* Check for const operands */ + if (k == 0 && lval->e_flags == E_MCONST && rconst) { + + /* Both operands are constant, remove the generated code */ + RemoveCode (Mark1); + pop (ltype); + + /* Evaluate the result */ + lval->e_const = kcalc (tok, lval->e_const, lval2.e_const); + + } else { + + /* If the right hand side is constant, and the generator function + * expects the lhs in the primary, remove the push of the primary + * now. + */ + unsigned flags = 0; + if (rconst) { + flags |= CF_CONST; + if ((Gen->Flags & GEN_NOPUSH) != 0) { + RemoveCode (Mark2); + pop (ltype); + ltype |= CF_REG; /* Value is in register */ + } + } + + /* Determine the type of the operation result. If the left + * operand is of type char and the right is a constant, or + * if both operands are of type char, we will encode the + * operation as char operation. Otherwise the default + * promotions are used. + */ + if (IsChar (lval->e_tptr) && (IsChar (lval2.e_tptr) || rconst)) { + flags |= CF_CHAR; + if (IsUnsigned (lval->e_tptr) || IsUnsigned (lval2.e_tptr)) { + flags |= CF_UNSIGNED; + } + if (rconst) { + flags |= CF_FORCECHAR; + } + } else { + unsigned rtype = TypeOf (lval2.e_tptr) | (flags & CF_CONST); + flags |= g_typeadjust (ltype, rtype); + } + + /* Generate code */ + Gen->Func (flags, lval2.e_const); + lval->e_flags = E_MEXPR; + } + + /* Result type is always int */ + lval->e_tptr = type_int; + + /* We have a rvalue now, condition codes are set */ + k = 0; + lval->e_test |= E_CC; + } + + return k; +} + + + +static int hie9 (struct expent *lval) +/* Process * and / operators. */ +{ + static GenDesc* hie9_ops [] = { + &GenMUL, &GenDIV, &GenMOD, 0 + }; + int UsedGen; + + return hie_internal (hie9_ops, lval, hie10, &UsedGen); +} + + + +static void parseadd (int k, struct expent* lval) +/* Parse an expression with the binary plus operator. lval contains the + * unprocessed left hand side of the expression and will contain the + * result of the expression on return. + */ +{ + struct expent lval2; + unsigned flags; /* Operation flags */ + CodeMark Mark; /* Remember code position */ + type* lhst; /* Type of left hand side */ + type* rhst; /* Type of right hand side */ + + + /* Skip the PLUS token */ + gettok (); + + /* Get the left hand side type, initialize operation flags */ + lhst = lval->e_tptr; + flags = 0; + + /* Check for constness on both sides */ + if (k == 0 && lval->e_flags == E_MCONST) { + + /* The left hand side is a constant. Good. Get rhs */ + if (evalexpr (CF_NONE, hie9, &lval2) == 0) { + + /* Right hand side is also constant. Get the rhs type */ + rhst = lval2.e_tptr; + + /* Both expressions are constants. Check for pointer arithmetic */ + if (IsPtr (lhst) && IsInt (rhst)) { + /* Left is pointer, right is int, must scale rhs */ + lval->e_const = lval->e_const + lval2.e_const * PSizeOf (lhst); + /* Result type is a pointer */ + } else if (IsInt (lhst) && IsPtr (rhst)) { + /* Left is int, right is pointer, must scale lhs */ + lval->e_const = lval->e_const * PSizeOf (rhst) + lval2.e_const; + /* Result type is a pointer */ + lval->e_tptr = lval2.e_tptr; + } else if (IsInt (lhst) && IsInt (rhst)) { + /* Integer addition */ + lval->e_const += lval2.e_const; + typeadjust (lval, &lval2, 1); + } else { + /* OOPS */ + Error (ERR_OP_NOT_ALLOWED); + } + + /* Result is constant, condition codes not set */ + lval->e_test = E_MCONST; + + } else { + + /* lhs is constant, rhs is not. Get the rhs type. */ + rhst = lval2.e_tptr; + + /* Check for pointer arithmetic */ + if (IsPtr (lhst) && IsInt (rhst)) { + /* Left is pointer, right is int, must scale rhs */ + g_scale (CF_INT, PSizeOf (lhst)); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + } else if (IsInt (lhst) && IsPtr (rhst)) { + /* Left is int, right is pointer, must scale lhs */ + lval->e_const *= PSizeOf (rhst); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + lval->e_tptr = lval2.e_tptr; + } else if (IsInt (lhst) && IsInt (rhst)) { + /* Integer addition */ + flags = typeadjust (lval, &lval2, 1); + } else { + /* OOPS */ + Error (ERR_OP_NOT_ALLOWED); + } + + /* Generate code for the add */ + g_inc (flags | CF_CONST, lval->e_const); + + /* Result is in primary register */ + lval->e_flags = E_MEXPR; + lval->e_test &= ~E_CC; + + } + + } else { + + /* Left hand side is not constant. Get the value onto the stack. */ + exprhs (CF_NONE, k, lval); /* --> primary register */ + Mark = GetCodePos (); + g_push (TypeOf (lval->e_tptr), 0); /* --> stack */ + + /* Evaluate the rhs */ + if (evalexpr (CF_NONE, hie9, &lval2) == 0) { + + /* Right hand side is a constant. Get the rhs type */ + rhst = lval2.e_tptr; + + /* Remove pushed value from stack */ + RemoveCode (Mark); + pop (TypeOf (lval->e_tptr)); + + /* Check for pointer arithmetic */ + if (IsPtr (lhst) && IsInt (rhst)) { + /* Left is pointer, right is int, must scale rhs */ + lval2.e_const *= PSizeOf (lhst); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + } else if (IsInt (lhst) && IsPtr (rhst)) { + /* Left is int, right is pointer, must scale lhs (ptr only) */ + g_scale (CF_INT | CF_CONST, PSizeOf (rhst)); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + lval->e_tptr = lval2.e_tptr; + } else if (IsInt (lhst) && IsInt (rhst)) { + /* Integer addition */ + flags = typeadjust (lval, &lval2, 1); + } else { + /* OOPS */ + Error (ERR_OP_NOT_ALLOWED); + } + + /* Generate code for the add */ + g_inc (flags | CF_CONST, lval2.e_const); + + /* Result is in primary register */ + lval->e_flags = E_MEXPR; + lval->e_test &= ~E_CC; + + } else { + + /* lhs and rhs are not constant. Get the rhs type. */ + rhst = lval2.e_tptr; + + /* Check for pointer arithmetic */ + if (IsPtr (lhst) && IsInt (rhst)) { + /* Left is pointer, right is int, must scale rhs */ + g_scale (CF_INT, PSizeOf (lhst)); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + } else if (IsInt (lhst) && IsPtr (rhst)) { + /* Left is int, right is pointer, must scale lhs */ + g_tosint (TypeOf (rhst)); /* Make sure, TOS is int */ + g_swap (CF_INT); /* Swap TOS and primary */ + g_scale (CF_INT, PSizeOf (rhst)); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + lval->e_tptr = lval2.e_tptr; + } else if (IsInt (lhst) && IsInt (rhst)) { + /* Integer addition */ + flags = typeadjust (lval, &lval2, 0); + } else { + /* OOPS */ + Error (ERR_OP_NOT_ALLOWED); + } + + /* Generate code for the add */ + g_add (flags, 0); + + /* Result is in primary register */ + lval->e_flags = E_MEXPR; + lval->e_test &= ~E_CC; + + } + + } +} + + + +static void parsesub (int k, struct expent* lval) +/* Parse an expression with the binary minus operator. lval contains the + * unprocessed left hand side of the expression and will contain the + * result of the expression on return. + */ +{ + struct expent lval2; + unsigned flags; /* Operation flags */ + type* lhst; /* Type of left hand side */ + type* rhst; /* Type of right hand side */ + CodeMark Mark1; /* Save position of output queue */ + CodeMark Mark2; /* Another position in the queue */ + int rscale; /* Scale factor for the result */ + + + /* Skip the MINUS token */ + gettok (); + + /* Get the left hand side type, initialize operation flags */ + lhst = lval->e_tptr; + flags = 0; + rscale = 1; /* Scale by 1, that is, don't scale */ + + /* Remember the output queue position, then bring the value onto the stack */ + Mark1 = GetCodePos (); + exprhs (CF_NONE, k, lval); /* --> primary register */ + Mark2 = GetCodePos (); + g_push (TypeOf (lhst), 0); /* --> stack */ + + /* Parse the right hand side */ + if (evalexpr (CF_NONE, hie9, &lval2) == 0) { + + /* The right hand side is constant. Get the rhs type. */ + rhst = lval2.e_tptr; + + /* Check left hand side */ + if (k == 0 && lval->e_flags & E_MCONST) { + + /* Both sides are constant, remove generated code */ + RemoveCode (Mark1); + pop (TypeOf (lhst)); /* Clean up the stack */ + + /* Check for pointer arithmetic */ + if (IsPtr (lhst) && IsInt (rhst)) { + /* Left is pointer, right is int, must scale rhs */ + lval->e_const -= lval2.e_const * PSizeOf (lhst); + /* Operate on pointers, result type is a pointer */ + } else if (IsPtr (lhst) && IsPtr (rhst)) { + /* Left is pointer, right is pointer, must scale result */ + if (TypeCmp (Indirect (lhst), Indirect (rhst)) != 0) { + Error (ERR_INCOMPATIBLE_POINTERS); + } else { + lval->e_const = (lval->e_const - lval2.e_const) / PSizeOf (lhst); + } + /* Operate on pointers, result type is an integer */ + lval->e_tptr = type_int; + } else if (IsInt (lhst) && IsInt (rhst)) { + /* Integer subtraction */ + typeadjust (lval, &lval2, 1); + lval->e_const -= lval2.e_const; + } else { + /* OOPS */ + Error (ERR_OP_NOT_ALLOWED); + } + + /* Result is constant, condition codes not set */ + lval->e_flags = E_MCONST; + lval->e_test &= ~E_CC; + + } else { + + /* Left hand side is not constant, right hand side is. + * Remove pushed value from stack. + */ + RemoveCode (Mark2); + pop (TypeOf (lhst)); + + if (IsPtr (lhst) && IsInt (rhst)) { + /* Left is pointer, right is int, must scale rhs */ + lval2.e_const *= PSizeOf (lhst); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + } else if (IsPtr (lhst) && IsPtr (rhst)) { + /* Left is pointer, right is pointer, must scale result */ + if (TypeCmp (Indirect (lhst), Indirect (rhst)) != 0) { + Error (ERR_INCOMPATIBLE_POINTERS); + } else { + rscale = PSizeOf (lhst); + } + /* Operate on pointers, result type is an integer */ + flags = CF_PTR; + lval->e_tptr = type_int; + } else if (IsInt (lhst) && IsInt (rhst)) { + /* Integer subtraction */ + flags = typeadjust (lval, &lval2, 1); + } else { + /* OOPS */ + Error (ERR_OP_NOT_ALLOWED); + } + + /* Do the subtraction */ + g_dec (flags | CF_CONST, lval2.e_const); + + /* If this was a pointer subtraction, we must scale the result */ + if (rscale != 1) { + g_scale (flags, -rscale); + } + + /* Result is in primary register */ + lval->e_flags = E_MEXPR; + lval->e_test &= ~E_CC; + + } + + } else { + + /* Right hand side is not constant. Get the rhs type. */ + rhst = lval2.e_tptr; + + /* Check for pointer arithmetic */ + if (IsPtr (lhst) && IsInt (rhst)) { + /* Left is pointer, right is int, must scale rhs */ + g_scale (CF_INT, PSizeOf (lhst)); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + } else if (IsPtr (lhst) && IsPtr (rhst)) { + /* Left is pointer, right is pointer, must scale result */ + if (TypeCmp (Indirect (lhst), Indirect (rhst)) != 0) { + Error (ERR_INCOMPATIBLE_POINTERS); + } else { + rscale = PSizeOf (lhst); + } + /* Operate on pointers, result type is an integer */ + flags = CF_PTR; + lval->e_tptr = type_int; + } else if (IsInt (lhst) && IsInt (rhst)) { + /* Integer subtraction. If the left hand side descriptor says that + * the lhs is const, we have to remove this mark, since this is no + * longer true, lhs is on stack instead. + */ + if (lval->e_flags == E_MCONST) { + lval->e_flags = E_MEXPR; + } + /* Adjust operand types */ + flags = typeadjust (lval, &lval2, 0); + } else { + /* OOPS */ + Error (ERR_OP_NOT_ALLOWED); + } + + /* Generate code for the sub (the & is a hack here) */ + g_sub (flags & ~CF_CONST, 0); + + /* If this was a pointer subtraction, we must scale the result */ + if (rscale != 1) { + g_scale (flags, -rscale); + } + + /* Result is in primary register */ + lval->e_flags = E_MEXPR; + lval->e_test &= ~E_CC; + } +} + + + +static int hie8 (struct expent* lval) +/* Process + and - binary operators. */ +{ + int k = hie9 (lval); + while (curtok == PLUS || curtok == MINUS) { + + if (curtok == PLUS) { + parseadd (k, lval); + } else { + parsesub (k, lval); + } + k = 0; + } + return k; +} + + + + +static int hie7 (struct expent *lval) +/* Parse << and >>. */ +{ + static GenDesc* hie7_ops [] = { + &GenASL, &GenASR, 0 + }; + int UsedGen; + + return hie_internal (hie7_ops, lval, hie8, &UsedGen); +} + + + +static int hie6 (struct expent *lval) +/* process greater-than type comparators */ +{ + static GenDesc* hie6_ops [] = { + &GenLT, &GenLE, &GenGE, &GenGT, 0 + }; + return hie_compare (hie6_ops, lval, hie7); +} + + + +static int hie5 (struct expent *lval) +{ + static GenDesc* hie5_ops[] = { + &GenEQ, &GenNE, 0 + }; + return hie_compare (hie5_ops, lval, hie6); +} + + + +static int hie4 (struct expent* lval) +/* Handle & (bitwise and) */ +{ + static GenDesc* hie4_ops [] = { + &GenAND, 0 + }; + int UsedGen; + + return hie_internal (hie4_ops, lval, hie5, &UsedGen); +} + + + +static int hie3 (struct expent *lval) +/* Handle ^ (bitwise exclusive or) */ +{ + static GenDesc* hie3_ops [] = { + &GenXOR, 0 + }; + int UsedGen; + + return hie_internal (hie3_ops, lval, hie4, &UsedGen); +} + + + +static int hie2 (struct expent *lval) +/* Handle | (bitwise or) */ +{ + static GenDesc* hie2_ops [] = { + &GenOR, 0 + }; + int UsedGen; + + return hie_internal (hie2_ops, lval, hie3, &UsedGen); +} + + + +static int hieAnd (struct expent* lval, unsigned TrueLab, int* BoolOp) +/* Process "exp && exp" */ +{ + int k; + int lab; + struct expent lval2; + + k = hie2 (lval); + if (curtok == DAMP) { + + /* Tell our caller that we're evaluating a boolean */ + *BoolOp = 1; + + /* Get a label that we will use for false expressions */ + lab = GetLabel (); + + /* If the expr hasn't set condition codes, set the force-test flag */ + if ((lval->e_test & E_CC) == 0) { + lval->e_test |= E_FORCETEST; + } + + /* Load the value */ + exprhs (CF_FORCECHAR, k, lval); + + /* Generate the jump */ + g_falsejump (CF_NONE, lab); + + /* Parse more boolean and's */ + while (curtok == DAMP) { + + /* Skip the && */ + gettok (); + + /* Get rhs */ + k = hie2 (&lval2); + if ((lval2.e_test & E_CC) == 0) { + lval2.e_test |= E_FORCETEST; + } + exprhs (CF_FORCECHAR, k, &lval2); + + /* Do short circuit evaluation */ + if (curtok == DAMP) { + g_falsejump (CF_NONE, lab); + } else { + /* Last expression - will evaluate to true */ + g_truejump (CF_NONE, TrueLab); + } + } + + /* Define the false jump label here */ + g_defloclabel (lab); + + /* Define the label */ + lval->e_flags = E_MEXPR; + lval->e_test |= E_CC; /* Condition codes are set */ + k = 0; + } + return k; +} + + + +static int hieOr (struct expent *lval) +/* Process "exp || exp". */ +{ + int k; + struct expent lval2; + int BoolOp = 0; /* Did we have a boolean op? */ + int AndOp; /* Did we have a && operation? */ + unsigned TrueLab; /* Jump to this label if true */ + unsigned DoneLab; + + /* Get a label */ + TrueLab = GetLabel (); + + /* Call the next level parser */ + k = hieAnd (lval, TrueLab, &BoolOp); + + /* Any boolean or's? */ + if (curtok == DBAR) { + + /* If the expr hasn't set condition codes, set the force-test flag */ + if ((lval->e_test & E_CC) == 0) { + lval->e_test |= E_FORCETEST; + } + + /* Get first expr */ + exprhs (CF_FORCECHAR, k, lval); + + /* For each expression jump to TrueLab if true. Beware: If we + * had && operators, the jump is already in place! + */ + if (!BoolOp) { + g_truejump (CF_NONE, TrueLab); + } + + /* Remember that we had a boolean op */ + BoolOp = 1; + + /* while there's more expr */ + while (curtok == DBAR) { + + /* skip the || */ + gettok (); + + /* Get a subexpr */ + AndOp = 0; + k = hieAnd (&lval2, TrueLab, &AndOp); + if ((lval2.e_test & E_CC) == 0) { + lval2.e_test |= E_FORCETEST; + } + exprhs (CF_FORCECHAR, k, &lval2); + + /* If there is more to come, add shortcut boolean eval. + * Beware: If we had && operators, the jump is already + * in place! + */ +#if 0 +/* Seems this sometimes generates wrong code */ + if (curtok == DBAR && !AndOp) { + g_truejump (CF_NONE, TrueLab); + } +#else + g_truejump (CF_NONE, TrueLab); +#endif + } + lval->e_flags = E_MEXPR; + lval->e_test |= E_CC; /* Condition codes are set */ + k = 0; + } + + /* If we really had boolean ops, generate the end sequence */ + if (BoolOp) { + DoneLab = GetLabel (); + g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */ + g_falsejump (CF_NONE, DoneLab); + g_defloclabel (TrueLab); + g_getimmed (CF_INT | CF_CONST, 1, 0); /* Load TRUE */ + g_defloclabel (DoneLab); + } + return k; +} + + + +static int hieQuest (struct expent *lval) +/* Parse "lvalue ? exp : exp" */ +{ + int k; + int labf; + int labt; + struct expent lval2; /* Expression 2 */ + struct expent lval3; /* Expression 3 */ + type* type2; /* Type of expression 2 */ + type* type3; /* Type of expression 3 */ + type* rtype; /* Type of result */ + CodeMark Mark1; /* Save position in output code */ + CodeMark Mark2; /* Save position in output code */ + + + + k = hieOr (lval); + if (curtok == QUEST) { + gettok (); + if ((lval->e_test & E_CC) == 0) { + /* Condition codes not set, force a test */ + lval->e_test |= E_FORCETEST; + } + exprhs (CF_NONE, k, lval); + labf = GetLabel (); + g_falsejump (CF_NONE, labf); + + /* Parse second and third expression */ + expression1 (&lval2); + labt = GetLabel (); + ConsumeColon (); + g_jump (labt); + g_defloclabel (labf); + expression1 (&lval3); + + /* Check if any conversions are needed, if so, do them. + * Conversion rules for ?: expression are: + * - if both expressions are int expressions, default promotion + * rules for ints apply. + * - if both expressions are pointers of the same type, the + * result of the expression is of this type. + * - if one of the expressions is a pointer and the other is + * a zero constant, the resulting type is that of the pointer + * type. + * - all other cases are flagged by an error. + */ + type2 = lval2.e_tptr; + type3 = lval3.e_tptr; + if (IsInt (type2) && IsInt (type3)) { + + /* Get common type */ + rtype = promoteint (type2, type3); + + /* Convert the third expression to this type if needed */ + g_typecast (TypeOf (rtype), TypeOf (type3)); + + /* Setup a new label so that the expr3 code will jump around + * the type cast code for expr2. + */ + labf = GetLabel (); /* Get new label */ + Mark1 = GetCodePos (); /* Remember current position */ + g_jump (labf); /* Jump around code */ + + /* The jump for expr2 goes here */ + g_defloclabel (labt); + + /* Create the typecast code for expr2 */ + Mark2 = GetCodePos (); /* Remember position */ + g_typecast (TypeOf (rtype), TypeOf (type2)); + + /* If the typecast did not produce code, remove the jump, + * otherwise output the label. + */ + if (GetCodePos() == Mark2) { + RemoveCode (Mark1); /* Remove code */ + } else { + /* We have typecast code, output label */ + g_defloclabel (labf); + labt = 0; /* Mark other label as invalid */ + } + + } else if (IsPtr (type2) && IsPtr (type3)) { + /* Must point to same type */ + if (TypeCmp (Indirect (type2), Indirect (type3)) != 0) { + Error (ERR_INCOMPATIBLE_TYPES); + } + /* Result has the common type */ + rtype = lval2.e_tptr; + } else if (IsPtr (type2) && IsNullPtr (&lval3)) { + /* Result type is pointer, no cast needed */ + rtype = lval2.e_tptr; + } else if (IsNullPtr (&lval2) && IsPtr (type3)) { + /* Result type is pointer, no cast needed */ + rtype = lval3.e_tptr; + } else { + Error (ERR_INCOMPATIBLE_TYPES); + rtype = lval2.e_tptr; /* Doesn't matter here */ + } + + /* If we don't have the label defined until now, do it */ + if (labt) { + g_defloclabel (labt); + } + + /* Setup the target expression */ + lval->e_flags = E_MEXPR; + lval->e_tptr = rtype; + k = 0; + } + return k; +} + + + +static void opeq (GenDesc* Gen, struct expent *lval, int k) +/* Process "op=" operators. */ +{ + struct expent lval2; + unsigned flags; + CodeMark Mark; + int MustScale; + + gettok (); + if (k == 0) { + Error (ERR_LVALUE_EXPECTED); + return; + } + + /* Determine the type of the lhs */ + flags = TypeOf (lval->e_tptr); + MustScale = (Gen->Func == g_add || Gen->Func == g_sub) && + lval->e_tptr [0] == T_PTR; + + /* Get the lhs address on stack (if needed) */ + PushAddr (lval); + + /* Fetch the lhs into the primary register if needed */ + exprhs (CF_NONE, k, lval); + + /* Bring the lhs on stack */ + Mark = GetCodePos (); + g_push (flags, 0); + + /* Evaluate the rhs */ + if (evalexpr (CF_NONE, hie1, &lval2) == 0) { + /* The resulting value is a constant. If the generator has the NOPUSH + * flag set, don't push the lhs. + */ + if (Gen->Flags & GEN_NOPUSH) { + RemoveCode (Mark); + pop (flags); + } + if (MustScale) { + /* lhs is a pointer, scale rhs */ + lval2.e_const *= SizeOf (lval->e_tptr+1); + } + + /* If the lhs is character sized, the operation may be later done + * with characters. + */ + if (SizeOf (lval->e_tptr) == 1) { + flags |= CF_FORCECHAR; + } + + /* Special handling for add and sub - some sort of a hack, but short code */ + if (Gen->Func == g_add) { + g_inc (flags | CF_CONST, lval2.e_const); + } else if (Gen->Func == g_sub) { + g_dec (flags | CF_CONST, lval2.e_const); + } else { + Gen->Func (flags | CF_CONST, lval2.e_const); + } + } else { + /* rhs is not constant and already in the primary register */ + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (lval2.e_tptr), SizeOf (lval->e_tptr+1)); + } + + /* If the lhs is character sized, the operation may be later done + * with characters. + */ + if (SizeOf (lval->e_tptr) == 1) { + flags |= CF_FORCECHAR; + } + + /* Adjust the types of the operands if needed */ + Gen->Func (g_typeadjust (flags, TypeOf (lval2.e_tptr)), 0); + } + store (lval); + lval->e_flags = E_MEXPR; +} + + + +static void addsubeq (GenDesc* Gen, struct expent *lval, int k) +/* Process the += and -= operators */ +{ + struct expent lval2; + unsigned flags; + int MustScale; + + + if (k == 0) { + Error (ERR_LVALUE_EXPECTED); + return; + } + + + /* We're currently only able to handle some adressing modes */ + if ((lval->e_flags & E_MGLOBAL) == 0 && /* Global address? */ + (lval->e_flags & E_MLOCAL) == 0 && /* Local address? */ + (lval->e_flags & E_MCONST) == 0) { /* Constant address? */ + /* Use generic routine */ + opeq (Gen, lval, k); + return; + } + + /* Skip the operator */ + gettok (); + + /* Check if we have a pointer expression and must scale rhs */ + MustScale = (lval->e_tptr [0] == T_PTR); + + /* Determine the code generator flags */ + flags = TypeOf (lval->e_tptr) | CF_FORCECHAR; + + /* Evaluate the rhs */ + if (evalexpr (CF_NONE, hie1, &lval2) == 0) { + /* The resulting value is a constant. */ + if (MustScale) { + /* lhs is a pointer, scale rhs */ + lval2.e_const *= SizeOf (lval->e_tptr+1); + } + flags |= CF_CONST; + } else { + /* rhs is not constant and already in the primary register */ + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (lval2.e_tptr), SizeOf (lval->e_tptr+1)); + } + } + + /* If the lhs is character sized, the operation may be later done + * with characters. + */ + if (SizeOf (lval->e_tptr) == 1) { + flags |= CF_FORCECHAR; + } + + /* Output apropriate code */ + if (lval->e_flags & E_MGLOBAL) { + /* Static variable */ + flags |= GlobalModeFlags (lval->e_flags); + if (Gen->Tok == PASGN) { + g_addeqstatic (flags, lval->e_name, lval->e_const, lval2.e_const); + } else { + g_subeqstatic (flags, lval->e_name, lval->e_const, lval2.e_const); + } + } else if (lval->e_flags & E_MLOCAL) { + /* ref to localvar */ + if (Gen->Tok == PASGN) { + g_addeqlocal (flags, lval->e_const, lval2.e_const); + } else { + g_subeqlocal (flags, lval->e_const, lval2.e_const); + } + } else if (lval->e_flags & E_MCONST) { + /* ref to absolute address */ + flags |= CF_ABSOLUTE; + if (Gen->Tok == PASGN) { + g_addeqstatic (flags, lval->e_const, 0, lval2.e_const); + } else { + g_subeqstatic (flags, lval->e_const, 0, lval2.e_const); + } + } else if (lval->e_flags & E_MEXPR) { + /* Address in a/x. */ + if (Gen->Tok == PASGN) { + g_addeqind (flags, lval->e_const, lval2.e_const); + } else { + g_subeqind (flags, lval->e_const, lval2.e_const); + } + } else { + Internal ("Invalid addressing mode"); + } + + /* Expression is in the primary now */ + lval->e_flags = E_MEXPR; +} + + + +static void Assignment (struct expent* lval) +/* Parse an assignment */ +{ + int k; + struct expent lval2; + unsigned flags; + type* ltype = lval->e_tptr; + + /* cc65 does not have full support for handling structs by value. Since + * assigning structs is one of the more useful operations from this + * familiy, allow it here. + */ + if (IsStruct (ltype)) { + + /* Bring the address of the lhs into the primary and push it */ + exprhs (0, 0, lval); + g_push (CF_PTR | CF_UNSIGNED, 0); + + /* Get the expression on the right of the '=' into the primary */ + k = hie1 (&lval2); + if (k) { + /* Get the address */ + exprhs (0, 0, &lval2); + } else { + /* We need an lvalue */ + Error (ERR_LVALUE_EXPECTED); + } + + /* Push the address (or whatever is in ax in case of errors) */ + g_push (CF_PTR | CF_UNSIGNED, 0); + + /* Check for equality of the structs */ + if (!EqualTypes (ltype, lval2.e_tptr)) { + Error (ERR_INCOMPATIBLE_TYPES); + } + + /* Load the size of the struct into the primary */ + g_getimmed (CF_INT | CF_UNSIGNED | CF_CONST, SizeOf (ltype), 0); + + /* Call the memcpy function */ + g_call (CF_FIXARGC, "memcpy", 4); + + } else { + + /* Get the address on stack if needed */ + PushAddr (lval); + + /* No struct, setup flags for the load */ + flags = SizeOf (ltype) == 1? CF_FORCECHAR : CF_NONE; + + /* Get the expression on the right of the '=' into the primary */ + if (evalexpr (flags, hie1, &lval2) == 0) { + /* Constant expression. Adjust the types */ + assignadjust (ltype, &lval2); + /* Put the value into the primary register */ + lconst (flags, &lval2); + } else { + /* Expression is not constant and already in the primary */ + assignadjust (ltype, &lval2); + } + + /* Generate a store instruction */ + store (lval); + + } + + /* Value is still in primary */ + lval->e_flags = E_MEXPR; +} + + + +int hie1 (struct expent* lval) +/* Parse first level of expression hierarchy. */ +{ + int k; + + k = hieQuest (lval); + switch (curtok) { + + case RPAREN: + case SEMI: + return k; + + case ASGN: + gettok (); + if (k == 0) { + Error (ERR_LVALUE_EXPECTED); + } else { + Assignment (lval); + } + break; + + case PASGN: + addsubeq (&GenPASGN, lval, k); + break; + + case SASGN: + addsubeq (&GenSASGN, lval, k); + break; + + case MASGN: + opeq (&GenMASGN, lval, k); + break; + + case DASGN: + opeq (&GenDASGN, lval, k); + break; + + case MOASGN: + opeq (&GenMOASGN, lval, k); + break; + + case SLASGN: + opeq (&GenSLASGN, lval, k); + break; + + case SRASGN: + opeq (&GenSRASGN, lval, k); + break; + + case AASGN: + opeq (&GenAASGN, lval, k); + break; + + case XOASGN: + opeq (&GenXOASGN, lval, k); + break; + + case OASGN: + opeq (&GenOASGN, lval, k); + break; + + default: + return k; + } + return 0; +} + + + +int hie0 (struct expent *lval) +/* Parse comma operator. */ +{ + int k; + + k = hie1 (lval); + while (curtok == COMMA) { + gettok (); + k = hie1 (lval); + } + return k; +} + + + +int evalexpr (unsigned flags, int (*f) (struct expent*), struct expent* lval) +/* Will evaluate an expression via the given function. If the result is a + * constant, 0 is returned and the value is put in the lval struct. If the + * result is not constant, exprhs is called to bring the value into the + * primary register and 1 is returned. + */ +{ + int k; + + /* Evaluate */ + k = f (lval); + if (k == 0 && lval->e_flags == E_MCONST) { + /* Constant expression */ + return 0; + } else { + /* Not constant, load into the primary */ + exprhs (flags, k, lval); + return 1; + } +} + + + +int expr (int (*func) (), struct expent *lval) +/* Expression parser; func is either hie0 or hie1. */ +{ + int k; + int savsp; + + savsp = oursp; + + k = (*func) (lval); + + /* Do some checks if code generation is still constistent */ + if (savsp != oursp) { + if (Debug) { + fprintf (stderr, "oursp != savesp (%d != %d)\n", oursp, savsp); + } else { + Internal ("oursp != savsp (%d != %d)", oursp, savsp); + } + } + return k; +} + + + +void expression1 (struct expent* lval) +/* Evaluate an expression on level 1 (no comma operator) and put it into + * the primary register + */ +{ + memset (lval, 0, sizeof (*lval)); + exprhs (CF_NONE, expr (hie1, lval), lval); +} + + + +void expression (struct expent* lval) +/* Evaluate an expression and put it into the primary register */ +{ + memset (lval, 0, sizeof (*lval)); + exprhs (CF_NONE, expr (hie0, lval), lval); +} + + + +void constexpr (struct expent* lval) +/* Get a constant value */ +{ + memset (lval, 0, sizeof (*lval)); + if (expr (hie1, lval) != 0 || (lval->e_flags & E_MCONST) == 0) { + Error (ERR_CONST_EXPR_EXPECTED); + /* To avoid any compiler errors, make the expression a valid const */ + lval->e_flags = E_MCONST; + lval->e_tptr = type_int; + lval->e_const = 0; + } +} + + + +void intexpr (struct expent* lval) +/* Get an integer expression */ +{ + expression (lval); + if (!IsInt (lval->e_tptr)) { + Error (ERR_INT_EXPR_EXPECTED); + /* To avoid any compiler errors, make the expression a valid int */ + lval->e_flags = E_MCONST; + lval->e_tptr = type_int; + lval->e_const = 0; + } +} + + + +void boolexpr (struct expent* lval) +/* Get a boolean expression */ +{ + /* Read an expression */ + expression (lval); + + /* If it's an integer, it's ok. If it's not an integer, but a pointer, + * the pointer used in a boolean context is also ok (Ootherwise check if it's a pointer + * expression. + */ + if (!IsInt (lval->e_tptr) && !IsPtr (lval->e_tptr)) { + Error (ERR_INT_EXPR_EXPECTED); + /* To avoid any compiler errors, make the expression a valid int */ + lval->e_flags = E_MCONST; + lval->e_tptr = type_int; + lval->e_const = 0; + } +} + + + +void test (unsigned label, int cond) +/* Generate code to perform test and jump if false. */ +{ + int k; + struct expent lval; + + /* Eat the parenthesis */ + ConsumeLParen (); + + /* Prepare the expression, setup labels */ + memset (&lval, 0, sizeof (lval)); + lval.e_test = E_TEST; + + /* Generate code to eval the expr */ + k = expr (hie0, &lval); + if (k == 0 && lval.e_flags == E_MCONST) { + /* Constant rvalue */ + if (cond == 0 && lval.e_const == 0) { + g_jump (label); + Warning (WARN_UNREACHABLE_CODE); + } else if (cond && lval.e_const) { + g_jump (label); + } + ConsumeRParen (); + return; + } + + /* If the expr hasn't set condition codes, set the force-test flag */ + if ((lval.e_test & E_CC) == 0) { + lval.e_test |= E_FORCETEST; + } + + /* Load the value into the primary register */ + exprhs (CF_FORCECHAR, k, &lval); + + /* Check for the closing brace */ + ConsumeRParen (); + + /* Generate the jump */ + if (cond) { + g_truejump (CF_NONE, label); + } else { + /* Special case (putting this here is a small hack - but hey, the + * compiler itself is one big hack...): If a semicolon follows, we + * don't have a statement and may omit the jump. + */ + if (curtok != SEMI) { + g_falsejump (CF_NONE, label); + } + } +} + + + + diff --git a/src/cc65/expr.h b/src/cc65/expr.h new file mode 100644 index 000000000..7768d3540 --- /dev/null +++ b/src/cc65/expr.h @@ -0,0 +1,125 @@ +/* + * expr.h + * + * Ullrich von Bassewitz, 21.06.1998 + */ + + + +#ifndef EXPR_H +#define EXPR_H + + + +#include "datatype.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Defines for the flags field of the expression descriptor */ +#define E_MREG 0x0110 /* Special: Expression is primary register */ +#define E_MGLOBAL 0x0080 /* Reference to static variable */ +#define E_MLOCAL 0x0040 /* Reference to local variable (stack offset) */ +#define E_MCONST 0x0020 /* Constant value */ +#define E_MEXPR 0x0010 /* Result is in primary register */ +#define E_MEOFFS 0x0011 /* Offset is in primary register, base on stack */ + +#define E_MCTYPE 0x0007 /* Type of a constant */ +#define E_TCONST 0x0000 /* Constant */ +#define E_TGLAB 0x0001 /* Global label */ +#define E_TLIT 0x0002 /* Literal of some kind */ +#define E_TLOFFS 0x0003 /* Constant stack offset */ +#define E_TLLAB 0x0004 /* Local label */ +#define E_TREGISTER 0x0005 /* Register variable */ + +/* Defines for the test field of the expression descriptor */ +#define E_CC 0x0001 /* expr has set cond codes apropos result value */ +#define E_FORCETEST 0x0002 /* if expr has NOT set CC, force a test */ +#define E_LOGL 0x0004 /* expr has left a logical value (1 or 0) in AX */ +#define E_XINV 0x0008 /* flip this bit to invert sense of test */ +#define E_TEST 0x0010 /* We're evaluating a test */ + +/* Describe the result of an expression */ +struct expent { + struct SymEntry* Sym; /* Symbol table entry if known */ + type* e_tptr; /* Type array of expression */ + long e_const; /* Value if expression constant */ + unsigned e_flags; + unsigned e_test; /* */ + unsigned long e_name; /* Name or label number */ +}; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void doasm (void); +/* This function parses ASM statements. The syntax of the ASM directive + * looks like the one defined for C++ (C has no ASM directive), that is, + * a string literal in parenthesis. + */ + +unsigned assignadjust (type* lhst, struct expent* rhs); +/* Adjust the type of the right hand expression so that it can be assigned to + * the type on the left hand side. This function is used for assignment and + * for converting parameters in a function call. It returns the code generator + * flags for the operation. + */ + +void exprhs (unsigned flags, int k, struct expent *lval); +/* Put the result of an expression into the primary register */ + +void expression1 (struct expent* lval); +/* Evaluate an expression on level 1 (no comma operator) and put it into + * the primary register + */ + +void expression (struct expent* lval); +/* Evaluate an expression and put it into the primary register */ + +int evalexpr (unsigned flags, int (*f) (struct expent*), struct expent* lval); +/* Will evaluate an expression via the given function. If the result is a + * constant, 0 is returned and the value is put in the lval struct. If the + * result is not constant, exprhs is called to bring the value into the + * primary register and 1 is returned. + */ + +void constexpr (struct expent* lval); +/* Get a constant value */ + +void intexpr (struct expent* lval); +/* Get an integer expression */ + +void boolexpr (struct expent* lval); +/* Get a boolean expression */ + +void test (unsigned label, int cond); +/* Generate code to perform test and jump if false. */ + +int hie1 (struct expent* lval); +/* Parse first level of expression hierarchy. */ + +int hie0 (struct expent* lval); +/* Parse comma operator (highest level of expression hierarchy) */ + +void DefineData (struct expent* lval); +/* Output a data definition for the given expression */ + + + +/* End of expr.h */ + +#endif + + + + diff --git a/src/cc65/funcdesc.c b/src/cc65/funcdesc.c new file mode 100644 index 000000000..1a1ad262b --- /dev/null +++ b/src/cc65/funcdesc.c @@ -0,0 +1,75 @@ +/*****************************************************************************/ +/* */ +/* funcdesc.c */ +/* */ +/* Function descriptor structure for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "mem.h" +#include "funcdesc.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +FuncDesc* NewFuncDesc (void) +/* Create a new symbol table with the given name */ +{ + /* Create a new function descriptor */ + FuncDesc* F = xmalloc (sizeof (FuncDesc)); + + /* Nullify the fields */ + F->Flags = 0; + F->SymTab = 0; + F->StructTab = 0; + F->EnumTab = 0; + F->ParamCount = 0; + F->ParamSize = 0; + + /* Return the new struct */ + return F; +} + + + +void FreeFuncDesc (FuncDesc* F) +/* Free a function descriptor */ +{ + /* Free the structure */ + xfree (F); +} + + + diff --git a/src/cc65/funcdesc.h b/src/cc65/funcdesc.h new file mode 100644 index 000000000..fddab0674 --- /dev/null +++ b/src/cc65/funcdesc.h @@ -0,0 +1,85 @@ +/*****************************************************************************/ +/* */ +/* funcdesc.h */ +/* */ +/* Function descriptor structure for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef FUNCDESC_H +#define FUNCDESC_H + + + +/*****************************************************************************/ +/* struct FuncDesc */ +/*****************************************************************************/ + + + +/* Masks for the Flags field in FuncDesc */ +#define FD_IMPLICIT 0x0001U /* Implicitly declared function */ +#define FD_EMPTY 0x0002U /* Function with empty param list */ +#define FD_VOID_PARAM 0x0004U /* Function with a void param list */ +#define FD_ELLIPSIS 0x0008U /* Function with variable param list */ +#define FD_FASTCALL 0x0010U /* __fastcall__ function */ + +/* Function descriptor */ +typedef struct FuncDesc FuncDesc; +struct FuncDesc { + unsigned Flags; /* Bitmapped flags FD_... */ + struct SymTable* SymTab; /* Symbol table */ + struct SymTable* StructTab; /* Struct table */ + struct SymTable* EnumTab; /* Enum table */ + unsigned ParamCount; /* Number of parameters */ + unsigned ParamSize; /* Size of the parameters */ +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +FuncDesc* NewFuncDesc (void); +/* Create a new symbol table with the given name */ + +void FreeFuncDesc (FuncDesc* E); +/* Free a function descriptor */ + + + +/* End of funcdesc.h */ +#endif + + + diff --git a/src/cc65/function.c b/src/cc65/function.c new file mode 100644 index 000000000..f1a160b6d --- /dev/null +++ b/src/cc65/function.c @@ -0,0 +1,239 @@ +/*****************************************************************************/ +/* */ +/* function.c */ +/* */ +/* Parse function entry/body/exit */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "asmcode.h" +#include "asmlabel.h" +#include "codegen.h" +#include "error.h" +#include "funcdesc.h" +#include "litpool.h" +#include "locals.h" +#include "mem.h" +#include "scanner.h" +#include "stmt.h" +#include "symtab.h" +#include "function.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Structure that holds all data needed for function activation */ +struct Function { + struct SymEntry* FuncEntry; /* Symbol table entry */ + type* ReturnType; /* Function return type */ + struct FuncDesc* Desc; /* Function descriptor */ + CodeMark EntryCode; /* Backpatch addr for entry code */ + unsigned LocalMax; /* Total space for locals */ + unsigned LocalSize; /* Current space for locals */ + unsigned RetLab; /* Return code label */ +}; + +/* Pointer to current function */ +Function* CurrentFunc = 0; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +static Function* NewFunction (struct SymEntry* Sym) +/* Create a new function activation structure and return it */ +{ + /* Allocate a new structure */ + Function* F = xmalloc (sizeof (Function)); + + /* Initialize the fields */ + F->FuncEntry = Sym; + F->ReturnType = Sym->Type + 1 + DECODE_SIZE; + F->Desc = DecodePtr (Sym->Type + 1); + F->EntryCode = GetCodePos (); + F->LocalMax = 0; + F->LocalSize = 0; + F->RetLab = GetLabel (); + + /* Return the new structure */ + return F; +} + + + +static void FreeFunction (Function* F) +/* Free a function activation structure */ +{ + xfree (F); +} + + + +const char* GetFuncName (const Function* F) +/* Return the name of the current function */ +{ + return F->FuncEntry->Name; +} + + + +unsigned GetParamSize (const Function* F) +/* Return the parameter size for the current function */ +{ + return F->Desc->ParamSize; +} + + + +type* GetReturnType (Function* F) +/* Get the return type for the function */ +{ + return F->ReturnType; +} + + + +int HasVoidReturn (const Function* F) +/* Return true if the function does not have a return value */ +{ + return IsVoid (F->ReturnType); +} + + + +unsigned GetRetLab (const Function* F) +/* Return the return jump label */ +{ + return F->RetLab; +} + + + +unsigned AllocLocalSpace (Function* F, unsigned Size) +/* Allocate space for the function locals, return stack offset */ +{ + /* Remember the current offset */ + unsigned Offs = F->LocalSize; + + /* Add the size */ + F->LocalSize += Size; + if (F->LocalSize > F->LocalMax) { + F->LocalMax = F->LocalSize; + } + + /* Return the offset */ + return Offs; +} + + + +void FreeLocalSpace (Function* F, unsigned Size) +/* Free space allocated for function locals */ +{ + F->LocalSize -= Size; +} + + + +void NewFunc (SymEntry* Func) +/* Parse argument declarations and function body. */ +{ + int isbrk; + + /* Get the function descriptor from the function entry */ + FuncDesc* D = DecodePtr (Func->Type+1); + + /* Allocate the function activation record for the function */ + CurrentFunc = NewFunction (Func); + + /* Reenter the lexical level */ + ReenterFunctionLevel (D); + + /* Function body now defined */ + Func->Flags |= SC_DEF; + + /* C functions cannot currently have __fastcall__ calling conventions */ + if (IsFastCallFunc (Func->Type)) { + Error (ERR_FASTCALL); + } + + /* Need a starting curly brace */ + if (curtok != LCURLY) { + Error (ERR_LCURLY_EXPECTED); + } + + /* Setup register variables */ + InitRegVars (); + + /* Switch to the code segment and generate function entry code */ + g_usecode (); + g_enter (TypeOf (Func->Type), Func->Name, GetParamSize (CurrentFunc)); + + /* Parse the function body */ + oursp = 0; + isbrk = compound (); + + /* If the function did not end with an return statement, create exit code */ + if (!isbrk) { +#if 0 + /* If the function has a return type, flag an error */ + if (!voidfunc) { + Error (ERR_MUST_RETURN_VALUE); + } +#endif + RestoreRegVars (0); + g_leave (CF_NONE, 0); + } + + /* Dump literal data created by the function */ + DumpLiteralPool (); + + /* Cleanup register variables */ + DoneRegVars (); + + /* Leave the lexical level */ + LeaveFunctionLevel (); + + /* Reset the current function pointer */ + FreeFunction (CurrentFunc); + CurrentFunc = 0; +} + + + diff --git a/src/cc65/function.h b/src/cc65/function.h new file mode 100644 index 000000000..fed098b74 --- /dev/null +++ b/src/cc65/function.h @@ -0,0 +1,64 @@ +/* + * function.h + * + * Ullrich von Bassewitz, 07.06.1998 + */ + + + +#ifndef FUNCTION_H +#define FUNCTION_H + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Structure that holds all data needed for function activation */ +typedef struct Function Function; + +/* Function activation data for current function (or NULL) */ +extern Function* CurrentFunc; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +const char* GetFuncName (const Function* F); +/* Return the name of the current function */ + +unsigned GetParamSize (const Function* F); +/* Return the parameter size for the current function */ + +type* GetReturnType (Function* F); +/* Get the return type for the function */ + +int HasVoidReturn (const Function* F); +/* Return true if the function does not have a return value */ + +unsigned GetRetLab (const Function* F); +/* Return the return jump label */ + +unsigned AllocLocalSpace (Function* F, unsigned Size); +/* Allocate space for the function locals, return stack offset */ + +void FreeLocalSpace (Function* F, unsigned Size); +/* Free space allocated for function locals */ + +void NewFunc (struct SymEntry* Func); +/* Parse argument declarations and function body. */ + + + +/* End of function.h */ +#endif + + + diff --git a/src/cc65/global.c b/src/cc65/global.c new file mode 100644 index 000000000..81d26b8e6 --- /dev/null +++ b/src/cc65/global.c @@ -0,0 +1,66 @@ +/*****************************************************************************/ +/* */ +/* global.c */ +/* */ +/* Global variables for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "global.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +unsigned char Target = TGT_NONE; /* Target system */ +unsigned char ANSI = 0; /* Strict ANSI flag */ +unsigned char WriteableStrings = 0; /* Literal strings are r/w */ +unsigned char NoWarn = 0; /* Suppress warnings */ +unsigned char Optimize = 0; /* Optimize flag */ +unsigned char FavourSize = 1; /* Favour size over speed */ +unsigned char InlineStdFuncs = 0; /* Inline some known functions */ +unsigned char EnableRegVars = 0; /* Enable register variables */ +unsigned char AllowRegVarAddr = 0; /* Allow taking addresses of register vars */ +unsigned char RegVarsToCallStack= 0; /* Save reg variables on call stack */ +unsigned char LocalsAreStatic = 0; /* Make local variables static */ +unsigned char SignedChars = 0; /* Make characters signed by default */ +unsigned char Verbose = 0; /* Verbose flag */ +unsigned char IncSource = 0; /* Include source as comments */ +unsigned char DebugInfo = 0; /* Add debug info to the obj */ +unsigned char Debug = 0; /* Debug mode */ + + + + + diff --git a/src/cc65/global.h b/src/cc65/global.h new file mode 100644 index 000000000..c6eaef7c7 --- /dev/null +++ b/src/cc65/global.h @@ -0,0 +1,88 @@ +/*****************************************************************************/ +/* */ +/* global.h */ +/* */ +/* Global variables for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef GLOBAL_H +#define GLOBAL_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Supported systems */ +#define TGT_NONE 0 +#define TGT_ATARI 1 +#define TGT_C64 2 +#define TGT_C128 3 +#define TGT_ACE 4 +#define TGT_PLUS4 5 +#define TGT_CBM610 6 +#define TGT_PET 7 +#define TGT_NES 8 +#define TGT_APPLE2 9 +#define TGT_GEOS 10 +#define TGT_COUNT 11 + + + +extern unsigned char Target; /* Target system */ +extern unsigned char ANSI; /* Strict ANSI flag */ +extern unsigned char WriteableStrings; /* Literal strings are r/w */ +extern unsigned char NoWarn; /* Suppress warnings */ +extern unsigned char Optimize; /* Optimize flag */ +extern unsigned char FavourSize; /* Favour size over speed */ +extern unsigned char InlineStdFuncs; /* Inline some known functions */ +extern unsigned char EnableRegVars; /* Enable register variables */ +extern unsigned char AllowRegVarAddr; /* Allow taking addresses of register vars */ +extern unsigned char RegVarsToCallStack; /* Save reg variables on call stack */ +extern unsigned char LocalsAreStatic; /* Make local variables static */ +extern unsigned char SignedChars; /* Make characters signed by default */ +extern unsigned char Verbose; /* Verbose flag */ +extern unsigned char IncSource; /* Include source as comments */ +extern unsigned char DebugInfo; /* Add debug info to the obj */ +extern unsigned char Debug; /* Debug mode */ + + + +/* End of global.h */ + +#endif + + + + diff --git a/src/cc65/goto.c b/src/cc65/goto.c new file mode 100644 index 000000000..b18637012 --- /dev/null +++ b/src/cc65/goto.c @@ -0,0 +1,91 @@ +/*****************************************************************************/ +/* */ +/* goto.c */ +/* */ +/* Goto and label handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "codegen.h" +#include "error.h" +#include "scanner.h" +#include "symtab.h" +#include "goto.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void DoGoto (void) +/* Process a goto statement. */ +{ + /* Eat the "goto" */ + gettok (); + + /* Label name must follow */ + if (curtok != IDENT) { + + Error (ERR_IDENT_EXPECTED); + + } else { + + /* Add a new label symbol if we don't have one until now */ + SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_REF); + + /* Jump to the label */ + g_jump (Entry->V.Label); + } + + /* Eat the label name */ + gettok (); +} + + + +void DoLabel (void) +/* Define a label. */ +{ + /* Add a label symbol */ + SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_DEF); + + /* Emit the jump label */ + g_defloclabel (Entry->V.Label); + + /* Eat the ident and colon */ + gettok (); + gettok (); +} + + + diff --git a/src/cc65/goto.h b/src/cc65/goto.h new file mode 100644 index 000000000..4492b1e89 --- /dev/null +++ b/src/cc65/goto.h @@ -0,0 +1,59 @@ +/*****************************************************************************/ +/* */ +/* goto.h */ +/* */ +/* Goto and label handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef GOTO_H +#define GOTO_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void DoGoto (void); +/* Process a goto statement. */ + +void DoLabel (void); +/* Define a goto label. */ + + + +/* End of goto.h */ +#endif + + + diff --git a/src/cc65/hashstr.c b/src/cc65/hashstr.c new file mode 100644 index 000000000..23b712119 --- /dev/null +++ b/src/cc65/hashstr.c @@ -0,0 +1,60 @@ +/*****************************************************************************/ +/* */ +/* hashstr.c */ +/* */ +/* Hash function for strings */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "hashstr.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned HashStr (const char* S) +/* Return a hash value for the given string */ +{ + unsigned L, H; + + /* Do the hash */ + H = L = 0; + while (*S) { + H = ((H << 3) ^ ((unsigned char) *S++)) + L++; + } + return H; +} + + + diff --git a/src/cc65/hashstr.h b/src/cc65/hashstr.h new file mode 100644 index 000000000..af7f2796c --- /dev/null +++ b/src/cc65/hashstr.h @@ -0,0 +1,57 @@ +/*****************************************************************************/ +/* */ +/* hashstr.h */ +/* */ +/* Hash function for strings */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef HASHSTR_H +#define HASHSTR_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned HashStr (const char* S); +/* Return a hash value for the given string */ + + + +/* End of hashstr.h */ + +#endif + + + diff --git a/src/cc65/ident.c b/src/cc65/ident.c new file mode 100644 index 000000000..615997ab9 --- /dev/null +++ b/src/cc65/ident.c @@ -0,0 +1,55 @@ +/*****************************************************************************/ +/* */ +/* ident.c */ +/* */ +/* Identifier handling for the cc65 compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "ident.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int IsIdent (char c) +/* Return true if the given char may start an identifier */ +{ + return (isalpha (c) || c == '_'); +} + + + diff --git a/src/cc65/ident.h b/src/cc65/ident.h new file mode 100644 index 000000000..f4db625d7 --- /dev/null +++ b/src/cc65/ident.h @@ -0,0 +1,71 @@ +/*****************************************************************************/ +/* */ +/* ident.h */ +/* */ +/* Identifier handling for the cc65 compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef IDENT_H +#define IDENT_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Maximum length of an identifier and the corresponding char array */ +#define MAX_IDENTLEN 64 +#define IDENTSIZE (MAX_IDENTLEN+1) + +/* Variable that holds an identifer */ +typedef char ident [IDENTSIZE]; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int IsIdent (char c); +/* Return true if the given char may start an identifier */ + + + +/* End of ident.h */ +#endif + + + diff --git a/src/cc65/include.c b/src/cc65/include.c new file mode 100644 index 000000000..2488d7196 --- /dev/null +++ b/src/cc65/include.c @@ -0,0 +1,162 @@ +/* + * include.c - Include file handling for cc65 + * + * Ullrich von Bassewitz, 18.08.1998 + */ + + + +#include +#include +#include + +#include "mem.h" +#include "include.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +static char* SysIncludePath = 0; +static char* UserIncludePath = 0; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +static char* Add (char* Orig, char* New) +/* Create a new path from Orig and New, delete Orig, return the result */ +{ + unsigned Len, NewLen; + char* NewPath; + + /* Check for a trailing path separator and remove it */ + NewLen = strlen (New); + if (NewLen > 0 && (New [NewLen-1] == '\\' || New [NewLen-1] == '/')) { + New [--NewLen] = '\0'; + } + + /* Calculate the length of the combined paths */ + if (Orig) { + Len = strlen (Orig) + NewLen; + } else { + Len = NewLen; + } + + /* Allocate memory for the new string */ + NewPath = xmalloc (Len + 2); + + /* Copy the strings */ + if (Orig) { + strcpy (NewPath, Orig); + } else { + NewPath [0] = '\0'; + } + strcat (NewPath, New); + strcat (NewPath, ";"); + + /* Delete the original path */ + xfree (Orig); + + /* Return the new path */ + return NewPath; +} + + + +static char* Find (char* Path, char* File) +/* Search for a file in a list of directories. If found, return the complete + * name including the path in a malloced data area, if not found, return 0. + */ +{ + char* P; + unsigned Count; + int Max; + char PathName [FILENAME_MAX]; + + /* Initialize variables */ + Max = sizeof (PathName) - strlen (File) - 2; + if (Max < 0) { + return 0; + } + P = Path; + + /* Handle a NULL pointer as replacement for an empty string */ + if (P == 0) { + P = ""; + } + + /* Start the search */ + while (*P) { + /* Copy the next path element into the buffer */ + Count = 0; + while (*P != '\0' && *P != ';' && Count < Max) { + PathName [Count++] = *P++; + } + + /* Add a path separator and the filename */ + if (Count) { + PathName [Count++] = '/'; + } + strcpy (PathName + Count, File); + + /* Check if this file exists */ + if (access (PathName, R_OK) == 0) { + /* The file exists */ + return xstrdup (PathName); + } + + /* Skip a list separator if we have one */ + if (*P == ';') { + ++P; + } + } + + /* Not found */ + return 0; +} + + + +void AddIncludePath (char* NewPath, unsigned Where) +/* Add a new include path to the existing one */ +{ + /* Allow a NULL path */ + if (NewPath) { + if (Where & INC_SYS) { + SysIncludePath = Add (SysIncludePath, NewPath); + } + if (Where & INC_USER) { + UserIncludePath = Add (UserIncludePath, NewPath); + } + } +} + + + +char* FindInclude (char* Name, unsigned Where) +/* Find an include file. Return a pointer to a malloced area that contains + * the complete path, if found, return 0 otherwise. + */ +{ + if (Where & INC_SYS) { + /* Search in the system include directories */ + return Find (SysIncludePath, Name); + } + if (Where & INC_USER) { + /* Search in the user include directories */ + return Find (UserIncludePath, Name); + } + return 0; +} + + + diff --git a/src/cc65/include.h b/src/cc65/include.h new file mode 100644 index 000000000..e8131b3d4 --- /dev/null +++ b/src/cc65/include.h @@ -0,0 +1,45 @@ +/* + * include.h - Include file handling for cc65 + * + * Ullrich von Bassewitz, 18.08.1998 + */ + + + +#ifndef INCLUDE_H +#define INCLUDE_H + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +#define INC_SYS 0x0001 /* Add to system include path */ +#define INC_USER 0x0002 /* Add to user include path */ + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void AddIncludePath (char* NewPath, unsigned Where); +/* Add a new include path to the existing one */ + +char* FindInclude (char* Name, unsigned Where); +/* Find an include file. Return a pointer to a malloced area that contains + * the complete path, if found, return 0 otherwise. + */ + + + +/* End of include.h */ +#endif + + + diff --git a/src/cc65/io.c b/src/cc65/io.c new file mode 100644 index 000000000..517aa9520 --- /dev/null +++ b/src/cc65/io.c @@ -0,0 +1,184 @@ + +/* C I/O functions */ + +#include +#include +#include +#include + +#include "asmcode.h" +#include "global.h" +#include "error.h" +#include "mem.h" +#include "codegen.h" +#include "optimize.h" +#include "io.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Input line stuff */ +char linebuf [LINESIZE]; +char* line = linebuf; +char* lptr = 0; + +/* Input file table and number of open input files */ +struct filent filetab[MAXFILES]; +int ifile = 0; + +/* Current input file stream data */ +FILE* inp = 0; +char* fin = 0; +unsigned ln = 0; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +int nch (void) +/* Get the next char in input stream (the one behind the current one) */ +{ + if (*lptr == '\0') { + return 0; + } else { + return lptr[1] & 0xFF; + } +} + + + +int cgch (void) +/* Get the current character in the input stream and advance line + * pointer (unless already at end of line). + */ +{ + if (*lptr == '\0') { + return (0); + } else { + return (*lptr++ & 0xFF); + } +} + + + +int gch (void) +/* Get the current character in the input stream and advance line + * pointer (no end of line check is performed). + */ +{ + return (*lptr++ & 0xFF); +} + + + +void kill (void) +/* Reset input line pointer, clear input line */ +{ + lptr = line; + *lptr = '\0'; +} + + + +static void CloseInclude (void) +/* Close an include file and switch to the higher level file. Set inp to NULL + * if this was the main file. + */ +{ + struct filent* pftab; + + /* Close the file */ + fclose(inp); + + /* Leave the include file */ + if (ifile > 0) { + xfree (fin); + inp = (pftab = &filetab[--ifile])->f_iocb; + ln = pftab->f_ln; + fin = pftab->f_name; + } else { + inp = 0; + } +} + + + +int readline (void) +/* Get a line from the current input. Returns -1 on end of file. */ +{ + unsigned Len; + unsigned Part; + unsigned Start; + int Done; + + /* Setup the line */ + kill (); + + /* If there is no file open, bail out */ + if (inp == 0) { + return 0; + } + + /* Read lines until we get one with real contents */ + Len = 0; + Done = 0; + while (!Done && Len < LINESIZE) { + + while (fgets (line + Len, LINESIZE - Len, inp) == 0) { + + /* eof */ + kill (); + + /* Leave the current file */ + CloseInclude (); + + /* If this was the last file, bail out */ + if (inp == 0) { + return 0; + } + } + + /* We got a new line */ + ++ln; + + /* Remove the trailing newline if we have one */ + Part = strlen (line + Len); + Start = Len; + Len += Part; + while (Len > 0 && line [Len-1] == '\n') { + --Len; + } + line [Len] = '\0'; + + /* Output the source line in the generated assembler file + * if requested. + */ + if (IncSource && line[Start] != '\0') { + AddCodeLine ("; %s", line+Start); + } + + /* Check if we have a line continuation character at the end. If not, + * we're done. + */ + if (Len > 0 && line[Len-1] == '\\') { + line[Len-1] = '\n'; /* Replace by newline */ + } else { + Done = 1; + } + } + + /* Got a line */ + return 1; +} + + + diff --git a/src/cc65/io.h b/src/cc65/io.h new file mode 100644 index 000000000..aa836700e --- /dev/null +++ b/src/cc65/io.h @@ -0,0 +1,86 @@ +/* + * io.h + * + * Ullrich von Bassewitz, 19.06.1998 + */ + + + +#ifndef IO_H +#define IO_H + + + +#include + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Maximum length of an input line and the corresponding char array */ +#define LINEMAX 4095 +#define LINESIZE LINEMAX+1 + +/* Maximum number of nested input files */ +#define MAXFILES 16 + +/* Input line stuff */ +extern char linebuf [LINESIZE]; +extern char* line; +extern char* lptr; + +/* File table entry */ +struct filent { + FILE* f_iocb; + char* f_name; + int f_ln; +}; + +/* Input file table and number of open input files */ +extern struct filent filetab[MAXFILES]; +extern int ifile; + +/* Current input file stream data */ +extern FILE* inp; /* Input file stream */ +extern char* fin; /* Input file name */ +extern unsigned ln; /* Line number */ + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void kill (void); +/* Reset input line pointer, clear input line */ + +int nch (void); +/* Get the next char in input stream (the one behind the current one) */ + +int cgch (void); +/* Get the current character in the input stream and advance line + * pointer (unless already at end of line). + */ + +int gch (void); +/* Get the current character in the input stream and advance line + * pointer (no end of line check is performed). + */ + +int readline (void); +/* Get a line from the current input. Returns -1 on end of file. */ + + + +/* End of io.h */ + +#endif + + + diff --git a/src/cc65/litpool.c b/src/cc65/litpool.c new file mode 100644 index 000000000..65c896d58 --- /dev/null +++ b/src/cc65/litpool.c @@ -0,0 +1,182 @@ +/*****************************************************************************/ +/* */ +/* litpool.c */ +/* */ +/* Literal string handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "asmlabel.h" +#include "check.h" +#include "ctrans.h" +#include "codegen.h" +#include "error.h" +#include "global.h" +#include "litpool.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +#define LITPOOL_SIZE 4096 /* Max strings per function */ +static unsigned char LiteralPool[LITPOOL_SIZE]; /* The literal pool */ +static unsigned LiteralOffs = 0; /* Current pool offset */ +static unsigned LiteralSpace = 0; /* Space used (stats only) */ + +unsigned LiteralLabel = 1; /* Pool asm label */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void TranslateLiteralPool (unsigned Offs) +/* Translate the literals starting from the given offset into the target + * charset. + */ +{ + while (Offs < LiteralOffs) { + LiteralPool[Offs] = ctrans (LiteralPool[Offs]); + ++Offs; + } +} + + + +void DumpLiteralPool (void) +/* Dump the literal pool */ +{ + /* if nothing there, exit... */ + if (LiteralOffs == 0) { + return; + } + + /* Switch to the data segment */ + if (WriteableStrings) { + g_usedata (); + } else { + g_userodata (); + } + + /* Define the label */ + g_defloclabel (LiteralLabel); + + /* Translate the buffer contents into the target charset */ + TranslateLiteralPool (0); + + /* Output the buffer data */ + g_defbytes (LiteralPool, LiteralOffs); + + /* Switch back to the code segment */ + g_usecode (); + + /* Reset the buffer */ + LiteralSpace += LiteralOffs; /* Count literal bytes emitted */ + LiteralLabel = GetLabel (); /* Get a new pool label */ + LiteralOffs = 0; +} + + + +unsigned GetLiteralOffs (void) +/* Return the current offset into the literal pool */ +{ + return LiteralOffs; +} + + + +void ResetLiteralOffs (unsigned Offs) +/* Reset the offset into the literal pool to some earlier value, effectively + * removing values from the pool. + */ +{ + CHECK (Offs <= LiteralOffs); + LiteralOffs = Offs; +} + + + +void AddLiteralChar (char C) +/* Add one character to the literal pool */ +{ + if (LiteralOffs >= LITPOOL_SIZE) { + Fatal (FAT_OUT_OF_STRSPACE); + } + LiteralPool[LiteralOffs++] = C; +} + + + +unsigned AddLiteral (const char* S) +/* Add a literal string to the literal pool. Return the starting offset into + * the pool + */ +{ + /* Remember the starting offset */ + unsigned Start = LiteralOffs; + + /* Copy the string doing a range check */ + do { + AddLiteralChar (*S); + } while (*S++); + + /* Return the starting offset */ + return Start; +} + + + +const char* GetLiteral (unsigned Offs) +/* Get a pointer to the literal with the given offset in the pool */ +{ + CHECK (Offs < LiteralOffs); + return &LiteralPool[Offs]; +} + + + +void PrintLiteralStats (FILE* F) +/* Print statistics about the literal space used */ +{ + fprintf (F, "Literal space used: %d bytes\n", LiteralSpace); +} + + + diff --git a/src/cc65/litpool.h b/src/cc65/litpool.h new file mode 100644 index 000000000..f168d89cf --- /dev/null +++ b/src/cc65/litpool.h @@ -0,0 +1,93 @@ +/*****************************************************************************/ +/* */ +/* litpool.h */ +/* */ +/* Literal string handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef LITPOOL_H +#define LITPOOL_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +extern unsigned LiteralLabel; /* Pool asm label */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void TranslateLiteralPool (unsigned Offs); +/* Translate the literals starting from the given offset into the target + * charset. + */ + +void DumpLiteralPool (void); +/* Dump the literal pool */ + +unsigned GetLiteralOffs (void); +/* Return the current offset into the literal pool */ + +void ResetLiteralOffs (unsigned Offs); +/* Reset the offset into the literal pool to some earlier value, effectively + * removing values from the pool. + */ + +void AddLiteralChar (char C); +/* Add one character to the literal pool */ + +unsigned AddLiteral (const char* S); +/* Add a literal string to the literal pool. Return the starting offset into + * the pool for this string. + */ + +const char* GetLiteral (unsigned Offs); +/* Get a pointer to the literal with the given offset in the pool */ + +void PrintLiteralStats (FILE* F); +/* Print statistics about the literal space used */ + + + +/* End of litpool.h */ +#endif + + + diff --git a/src/cc65/locals.c b/src/cc65/locals.c new file mode 100644 index 000000000..63b6b8611 --- /dev/null +++ b/src/cc65/locals.c @@ -0,0 +1,469 @@ +/*****************************************************************************/ +/* */ +/* locals.c */ +/* */ +/* Local variable handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "anonname.h" +#include "asmlabel.h" +#include "codegen.h" +#include "declare.h" +#include "expr.h" +#include "function.h" /* ## */ +#include "global.h" +#include "mem.h" +#include "symtab.h" +#include "locals.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Register variable management */ +unsigned MaxRegSpace = 6; /* Maximum space available */ +static int RegOffs = 0; /* Offset into register space */ +static const SymEntry** RegSyms = 0; /* The register variables */ +static unsigned RegSymCount = 0; /* Number of register variables */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void InitRegVars (void) +/* Initialize register variable control data */ +{ + /* If the register space is zero, bail out */ + if (MaxRegSpace == 0) { + return; + } + + /* The maximum number of register variables is equal to the register + * variable space available. So allocate one pointer per byte. This + * will usually waste some space but we don't need to dynamically + * grow the array. + */ + RegSyms = xmalloc (MaxRegSpace * sizeof (RegSyms[0])); + RegOffs = MaxRegSpace; +} + + + +void DoneRegVars (void) +/* Free the register variables */ +{ + xfree (RegSyms); + RegSyms = 0; + RegOffs = MaxRegSpace; + RegSymCount = 0; +} + + + +static int AllocRegVar (const SymEntry* Sym, const type* tarray) +/* Allocate a register variable with the given amount of storage. If the + * allocation was successful, return the offset of the register variable in + * the register bank (zero page storage). If there is no register space left, + * return -1. + */ +{ + /* Maybe register variables are disabled... */ + if (EnableRegVars) { + + /* Get the size of the variable */ + unsigned Size = SizeOf (tarray); + + /* Do we have space left? */ + if (RegOffs >= Size) { + + /* Space left. We allocate the variables from high to low addresses, + * so the adressing is compatible with the saved values on stack. + * This allows shorter code when saving/restoring the variables. + */ + RegOffs -= Size; + RegSyms [RegSymCount++] = Sym; + return RegOffs; + } + } + + /* No space left or no allocation */ + return -1; +} + + + +void DeclareLocals (void) +/* Declare local variables and types. */ +{ + int offs = oursp; /* Current stack offset for variable */ + int AutoSpace = 0; /* Unallocated space on the stack */ + int Size; /* Size of an auto variable */ + int Reg; /* Register variable offset */ + unsigned flags = 0; /* Code generator flags */ + int SymbolSC; /* Storage class for symbol */ + int ldata = 0; /* Local symbol data temp storage */ + + /* Loop until we don't find any more variables */ + while (1) { + + /* Check variable declarations. We need to distinguish between a + * default int type and the end of variable declarations. So we + * will do the following: If there is no explicit storage class + * specifier *and* no explicit type given, it is assume that we + * have reached the end of declarations. + */ + DeclSpec Spec; + ParseDeclSpec (&Spec, SC_AUTO, T_INT); + if ((Spec.Flags & DS_DEF_STORAGE) != 0 && (Spec.Flags & DS_DEF_TYPE) != 0) { + break; + } + + /* Accept type only declarations */ + if (curtok == SEMI) { + /* Type declaration only ### Check struct/union here */ + gettok (); + continue; + } + + /* Parse a comma separated variable list */ + while (1) { + + Declaration Decl; + + /* Remember the storage class for the new symbol */ + SymbolSC = Spec.StorageClass; + + /* Read the declaration */ + ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + + /* If we don't have a name, this was flagged as an error earlier. + * To avoid problems later, use an anonymous name here. + */ + if (Decl.Ident[0] == '\0') { + AnonName (Decl.Ident, "param"); + } + + if (!IsFunc (Decl.Type) && (SymbolSC & SC_TYPEDEF) != SC_TYPEDEF) { + + /* Get the size of the variable */ + Size = SizeOf (Decl.Type); + +#if 0 + /* Check the storage class */ + if ((SymbolSC & SC_REGISTER) && (Reg = AllocRegVar (psym, tarray)) >= 0) { + + /* We will store the current value of the register onto the + * stack, thus making functions with register variables + * reentrant. If we have pending auto variables, emit them + * now. + */ + g_usecode (); + g_space (AutoSpace); + oursp -= AutoSpace; + AutoSpace = 0; + + /* Remember the register bank offset */ + ldata = Reg; + + /* Save the current register value onto the stack */ + g_save_regvars (Reg, Size); + + /* Allow variable initialization */ + if (curtok == ASGN) { + + struct expent lval; + + /* Skip the '=' */ + gettok (); + + /* Get the expression into the primary */ + expression1 (&lval); + + /* Make type adjustments if needed */ + assignadjust (tarray, &lval); + + /* Setup the type flags for the assignment */ + flags = TypeOf (tarray) | CF_REGVAR; + if (Size == 1) { + flags |= CF_FORCECHAR; + } + + /* Store the value into the register */ + g_putstatic (flags, Reg, 0); + + /* Mark the variable as referenced */ + SymbolSC |= SC_REF; + + } + + /* Account for the stack space needed and remember the + * stack offset of the save area. + */ + offs -= Size; + psym->h_lattr = offs; + + } else if (SymbolSC & (SC_AUTO | SC_REGISTER)) { +#endif + if (SymbolSC & (SC_AUTO | SC_REGISTER)) { + + /* Auto variable */ + if (LocalsAreStatic == 0) { + + /* Change SC in case it was register */ + SymbolSC = (SymbolSC & ~SC_REGISTER) | SC_AUTO; + if (curtok == ASGN) { + + struct expent lval; + + /* Switch to the code segment, allocate space for + * uninitialized variables. + */ + g_usecode (); + g_space (AutoSpace); + oursp -= AutoSpace; + AutoSpace = 0; + + /* Skip the '=' */ + gettok (); + + /* Setup the type flags for the assignment */ + flags = Size == 1? CF_FORCECHAR : CF_NONE; + + /* Get the expression into the primary */ + if (evalexpr (flags, hie1, &lval) == 0) { + /* Constant expression. Adjust the types */ + assignadjust (Decl.Type, &lval); + flags |= CF_CONST; + } else { + /* Expression is not constant and in the primary */ + assignadjust (Decl.Type, &lval); + } + + /* Push the value */ + g_push (flags | TypeOf (Decl.Type), lval.e_const); + + /* Mark the variable as referenced */ + SymbolSC |= SC_REF; + + } else { + /* Non-initialized local variable. Just keep track of + * the space needed. + */ + AutoSpace += Size; + } + + /* Allocate space on the stack, assign the offset */ + offs -= Size; + ldata = offs; + + } else { + + /* Static local variables. */ + SymbolSC = (SymbolSC & ~(SC_REGISTER | SC_AUTO)) | SC_STATIC; + + /* Put them into the BSS */ + g_usebss (); + + /* Define the variable label */ + g_defloclabel (ldata = GetLabel ()); + + /* Reserve space for the data */ + g_res (Size); + + /* Allow assignments */ + if (curtok == ASGN) { + + struct expent lval; + + /* Switch to the code segment. */ + g_usecode (); + + /* Skip the '=' */ + gettok (); + + /* Get the expression into the primary */ + expression1 (&lval); + + /* Make type adjustments if needed */ + assignadjust (Decl.Type, &lval); + + /* Setup the type flags for the assignment */ + flags = TypeOf (Decl.Type); + if (Size == 1) { + flags |= CF_FORCECHAR; + } + + /* Store the value into the variable */ + g_putstatic (flags, ldata, 0); + + /* Mark the variable as referenced */ + SymbolSC |= SC_REF; + } + } + + } else if ((SymbolSC & SC_STATIC) == SC_STATIC) { + + /* Static data */ + if (curtok == ASGN) { + + /* Initialization ahead, switch to data segment */ + g_usedata (); + + /* Define the variable label */ + g_defloclabel (ldata = GetLabel ()); + + /* Skip the '=' */ + gettok (); + + /* Allow initialization of static vars */ + ParseInit (Decl.Type); + + /* Mark the variable as referenced */ + SymbolSC |= SC_REF; + + } else { + + /* Uninitialized data, use BSS segment */ + g_usebss (); + + /* Define the variable label */ + g_defloclabel (ldata = GetLabel ()); + + /* Reserve space for the data */ + g_res (Size); + + } + } + + } + + /* If the symbol is not marked as external, it will be defined */ + if ((SymbolSC & SC_EXTERN) == 0) { + SymbolSC |= SC_DEF; + } + + /* Add the symbol to the symbol table */ + AddLocalSym (Decl.Ident, Decl.Type, SymbolSC, ldata); + + if (curtok != COMMA) { + break; + } + gettok (); + } + if (curtok == SEMI) { + gettok (); + } + } + + /* In case we switched away from code segment, switch back now */ + g_usecode (); + + /* Create space for locals */ + g_space (AutoSpace); + oursp -= AutoSpace; +} + + + +void RestoreRegVars (int HaveResult) +/* Restore the register variables for the local function if there are any. + * The parameter tells us if there is a return value in ax, in that case, + * the accumulator must be saved across the restore. + */ +{ + int I, J, Bytes, Offs; + + /* If we don't have register variables in this function, bail out early */ + if (RegSymCount == 0) { + return; + } + + /* Save the accumulator if needed */ + if (!HasVoidReturn (CurrentFunc) && HaveResult) { + g_save (CF_CHAR | CF_FORCECHAR); + } + + /* Walk through all variables. If there are several variables in a row + * (that is, with increasing stack offset), restore them in one chunk. + */ + I = 0; + while (I < RegSymCount) { + + /* Check for more than one variable */ + const SymEntry* Sym = RegSyms[I]; + Offs = Sym->V.Offs; + Bytes = SizeOf (Sym->Type); + J = I+1; + + while (J < RegSymCount) { + + /* Get the next symbol */ + const SymEntry* NextSym = RegSyms [J]; + + /* Get the size */ + int Size = SizeOf (NextSym->Type); + + /* Adjacent variable? */ + if (NextSym->V.Offs + Size != Offs) { + /* No */ + break; + } + + /* Adjacent variable */ + Bytes += Size; + Offs -= Size; + Sym = NextSym; + ++J; + } + + /* Restore the memory range */ + g_restore_regvars (Offs, Sym->V.Offs, Bytes); + + /* Next round */ + I = J; + } + + /* Restore the accumulator if needed */ + if (!HasVoidReturn (CurrentFunc) && HaveResult) { + g_restore (CF_CHAR | CF_FORCECHAR); + } +} + + + diff --git a/src/cc65/locals.h b/src/cc65/locals.h new file mode 100644 index 000000000..710149432 --- /dev/null +++ b/src/cc65/locals.h @@ -0,0 +1,68 @@ +/*****************************************************************************/ +/* */ +/* locals.h */ +/* */ +/* Local variable handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef LOCALS_H +#define LOCALS_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void InitRegVars (void); +/* Initialize register variable control data */ + +void DoneRegVars (void); +/* Free the register variables */ + +void DeclareLocals (void); +/* Declare local variables and types. */ + +void RestoreRegVars (int HaveResult); +/* Restore the register variables for the local function if there are any. + * The parameter tells us if there is a return value in ax, in that case, + * the accumulator must be saved across the restore. + */ + + + +/* End of locals.h */ +#endif + + + diff --git a/src/cc65/loop.c b/src/cc65/loop.c new file mode 100644 index 000000000..ec51dc951 --- /dev/null +++ b/src/cc65/loop.c @@ -0,0 +1,81 @@ +/* + * loop.c + * + * Ullrich von Bassewitz, 20.06.1998 + */ + + + +#include "error.h" +#include "mem.h" +#include "loop.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* The root */ +static struct loopdesc* loopstack = 0; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +struct loopdesc* addloop (unsigned sp, unsigned loop, unsigned label, + unsigned linc, unsigned lstat) +/* Create and add a new loop descriptor */ +{ + struct loopdesc* l; + + /* Allocate a new struct */ + l = xmalloc (sizeof (struct loopdesc)); + + /* Fill in the data */ + l->sp = sp; + l->loop = loop; + l->label = label; + l->linc = linc; + l->lstat = lstat; + + /* Insert it into the list */ + l->next = loopstack; + loopstack = l; + + /* Return a pointer to the struct */ + return l; +} + + + +struct loopdesc* currentloop (void) +/* Return a pointer to the descriptor of the current loop */ +{ + if (loopstack == 0) { + /* Stack is empty */ + Error (ERR_NO_ACTIVE_LOOP); + } + return loopstack; +} + + + +void delloop (void) +/* Remove the current loop */ +{ + struct loopdesc* l; + + l = loopstack; + loopstack = loopstack->next; + xfree (l); +} + + + diff --git a/src/cc65/loop.h b/src/cc65/loop.h new file mode 100644 index 000000000..b5c7561f2 --- /dev/null +++ b/src/cc65/loop.h @@ -0,0 +1,54 @@ +/* + * loop.h + * + * Ullrich von Bassewitz, 20.06.1998 + */ + + + +#ifndef LOOP_H +#define LOOP_H + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +struct loopdesc { + struct loopdesc* next; + unsigned sp; + unsigned loop; + unsigned label; + unsigned linc; + unsigned lstat; +}; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +struct loopdesc* addloop (unsigned sp, unsigned loop, unsigned label, + unsigned linc, unsigned lstat); +/* Create and add a new loop descriptor */ + +struct loopdesc* currentloop (void); +/* Return a pointer to the descriptor of the current loop */ + +void delloop (void); +/* Remove the current loop */ + + + +/* End of loop.h */ + +#endif + + + diff --git a/src/cc65/macrotab.c b/src/cc65/macrotab.c new file mode 100644 index 000000000..fd41abe46 --- /dev/null +++ b/src/cc65/macrotab.c @@ -0,0 +1,329 @@ +/*****************************************************************************/ +/* */ +/* macrotab.h */ +/* */ +/* Preprocessor macro table for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "error.h" +#include "hashstr.h" +#include "mem.h" +#include "macrotab.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* The macro hash table */ +#define MACRO_TAB_SIZE 211 +static Macro* MacroTab[MACRO_TAB_SIZE]; + +/* A table that holds the count of macros that start with a specific character. + * It is used to determine quickly, if an identifier may be a macro or not + * without calculating the hash over the name. + */ +static unsigned short MacroFlagTab[256]; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +Macro* NewMacro (const char* Name) +/* Allocate a macro structure with the given name. The structure is not + * inserted into the macro table. + */ +{ + /* Get the length of the macro name */ + unsigned Len = strlen(Name); + + /* Allocate the structure */ + Macro* M = xmalloc (sizeof(Macro) + Len); + + /* Initialize the data */ + M->Next = 0; + M->ArgCount = -1; /* Flag: Not a function like macro */ + M->MaxArgs = 0; + M->FormalArgs = 0; + M->ActualArgs = 0; + M->Replacement = 0; + memcpy (M->Name, Name, Len+1); + + /* Return the new macro */ + return M; +} + + + +void FreeMacro (Macro* M) +/* Delete a macro definition. The function will NOT remove the macro from the + * table, use UndefineMacro for that. + */ +{ + int I; + + for (I = 0; I < M->ArgCount; ++I) { + xfree (M->FormalArgs[I]); + } + xfree (M->FormalArgs); + xfree (M->ActualArgs); + xfree (M->Replacement); + xfree (M); +} + + + +void AddNumericMacro (const char* Name, long Val) +/* Add a macro for a numeric constant */ +{ + char Buf[64]; + + /* Make a string from the number */ + sprintf (Buf, "%ld", Val); + + /* Handle as text macro */ + AddTextMacro (Name, Buf); +} + + + +void AddTextMacro (const char* Name, const char* Val) +/* Add a macro for a textual constant */ +{ + /* Create a new macro */ + Macro* M = NewMacro (Name); + + /* Set the value as replacement text */ + M->Replacement = xstrdup (Val); + + /* Insert the macro into the macro table */ + InsertMacro (M); +} + + + +void InsertMacro (Macro* M) +/* Insert the given macro into the macro table. This call will also allocate + * the ActualArgs parameter array. + */ +{ + unsigned Hash; + + /* Allocate the ActualArgs parameter array */ + if (M->ArgCount > 0) { + M->ActualArgs = xmalloc (M->ArgCount * sizeof(char*)); + } + + /* Get the hash value of the macro name */ + Hash = HashStr (M->Name) % MACRO_TAB_SIZE; + + /* Insert the macro */ + M->Next = MacroTab[Hash]; + MacroTab[Hash] = M; + + /* Increment the number of macros starting with this char */ + MacroFlagTab[(unsigned)(unsigned char)M->Name[0]]++; +} + + + +int UndefineMacro (const char* Name) +/* Search for the macro with the given name and remove it from the macro + * table if it exists. Return 1 if a macro was found and deleted, return + * 0 otherwise. + */ +{ + /* Get the hash value of the macro name */ + unsigned Hash = HashStr (Name) % MACRO_TAB_SIZE; + + /* Search the hash chain */ + Macro* L = 0; + Macro* M = MacroTab[Hash]; + while (M) { + if (strcmp (M->Name, Name) == 0) { + + /* Found it */ + if (L == 0) { + /* First in chain */ + MacroTab[Hash] = M->Next; + } else { + L->Next = M->Next; + } + + /* Decrement the number of macros starting with this char */ + MacroFlagTab[(unsigned)(unsigned char)M->Name[0]]--; + + /* Delete the macro */ + FreeMacro (M); + + /* Done */ + return 1; + } + + /* Next macro */ + L = M; + M = M->Next; + } + + /* Not found */ + return 0; +} + + + +Macro* FindMacro (const char* Name) +/* Find a macro with the given name. Return the macro definition or NULL */ +{ + /* Get the hash value of the macro name */ + unsigned Hash = HashStr (Name) % MACRO_TAB_SIZE; + + /* Search the hash chain */ + Macro* M = MacroTab[Hash]; + while (M) { + if (strcmp (M->Name, Name) == 0) { + /* Found it */ + return M; + } + + /* Next macro */ + M = M->Next; + } + + /* Not found */ + return 0; +} + + + +int IsMacro (const char* Name) +/* Return true if the given name is the name of a macro, return false otherwise */ +{ + return FindMacro(Name) != 0; +} + + + +int MaybeMacro (unsigned char C) +/* Return true if the given character may be the start of the name of an + * existing macro, return false if not. + */ +{ + return (MacroFlagTab[C] > 0); +} + + + +const char* FindMacroArg (Macro* M, const char* Arg) +/* Search for a formal macro argument. If found, return the actual + * (replacement) argument. If the argument was not found, return NULL. + */ +{ + int I; + for (I = 0; I < M->ArgCount; ++I) { + if (strcmp (M->FormalArgs[I], Arg) == 0) { + /* Found */ + return M->ActualArgs[I]; + } + } + /* Not found */ + return 0; +} + + + +void AddMacroArg (Macro* M, const char* Arg) +/* Add a formal macro argument. */ +{ + /* Check if we have a duplicate macro argument, but add it anyway. + * Beware: Don't use FindMacroArg here, since the actual argument array + * may not be initialized. + */ + int I; + for (I = 0; I < M->ArgCount; ++I) { + if (strcmp (M->FormalArgs[I], Arg) == 0) { + /* Found */ + Error (ERR_DUPLICATE_MACRO_ARG, Arg); + break; + } + } + + /* Check if we have enough room available, otherwise expand the array + * that holds the formal argument list. + */ + if (M->ArgCount >= M->MaxArgs) { + /* We must expand the array */ + char** OldArgs = M->FormalArgs; + M->MaxArgs += 10; + M->FormalArgs = xmalloc (M->MaxArgs * sizeof(char*)); + memcpy (M->FormalArgs, OldArgs, M->ArgCount * sizeof (char*)); + xfree (OldArgs); + } + + /* Add the new argument */ + M->FormalArgs[M->ArgCount++] = xstrdup (Arg); +} + + + +void PrintMacroStats (FILE* F) +/* Print macro statistics to the given text file. */ +{ + unsigned I; + Macro* M; + + fprintf (F, "\n\nMacro Hash Table Summary\n"); + for (I = 0; I < MACRO_TAB_SIZE; ++I) { + fprintf (F, "%3u : ", I); + M = MacroTab [I]; + if (M) { + while (M) { + fprintf (F, "%s ", M->Name); + M = M->Next; + } + fprintf (F, "\n"); + } else { + fprintf (F, "empty\n"); + } + } +} + + + diff --git a/src/cc65/macrotab.h b/src/cc65/macrotab.h new file mode 100644 index 000000000..3f5bbaba6 --- /dev/null +++ b/src/cc65/macrotab.h @@ -0,0 +1,122 @@ +/*****************************************************************************/ +/* */ +/* macrotab.h */ +/* */ +/* Preprocessor macro table for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef MACROTAB_H +#define MACROTAB_H + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +typedef struct Macro_ Macro; +struct Macro_ { + Macro* Next; /* Next macro with same hash value */ + int ArgCount; /* Number of parameters, -1 = no parens */ + unsigned MaxArgs; /* Size of formal argument list */ + char** FormalArgs; /* Formal argument list */ + char const** ActualArgs; /* Actual argument list */ + char* Replacement; /* Replacement text */ + char Name[1]; /* Name, dynamically allocated */ +}; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +Macro* NewMacro (const char* Name); +/* Allocate a macro structure with the given name. The structure is not + * inserted into the macro table. + */ + +void FreeMacro (Macro* M); +/* Delete a macro definition. The function will NOT remove the macro from the + * table, use UndefineMacro for that. + */ + +void AddNumericMacro (const char* Name, long Val); +/* Add a macro for a numeric constant */ + +void AddTextMacro (const char* Name, const char* Val); +/* Add a macro for a textual constant */ + +void InsertMacro (Macro* M); +/* Insert the given macro into the macro table. This call will also allocate + * the ActualArgs parameter array. + */ + +int UndefineMacro (const char* Name); +/* Search for the macro with the given name and remove it from the macro + * table if it exists. Return 1 if a macro was found and deleted, return + * 0 otherwise. + */ + +Macro* FindMacro (const char* Name); +/* Find a macro with the given name. Return the macro definition or NULL */ + +int IsMacro (const char* Name); +/* Return true if the given name is the name of a macro, return false otherwise */ + +int MaybeMacro (unsigned char C); +/* Return true if the given character may be the start of the name of an + * existing macro, return false if not. + */ + +const char* FindMacroArg (Macro* M, const char* Arg); +/* Search for a formal macro argument. If found, return the actual + * (replacement) argument. If the argument was not found, return NULL. + */ + +void AddMacroArg (Macro* M, const char* Arg); +/* Add a formal macro argument. */ + +void PrintMacroStats (FILE* F); +/* Print macro statistics to the given text file. */ + + + +/* End of macrotab.h */ +#endif + + + + diff --git a/src/cc65/main.c b/src/cc65/main.c new file mode 100644 index 000000000..296d29731 --- /dev/null +++ b/src/cc65/main.c @@ -0,0 +1,679 @@ +/* CC65 main program */ + +#include +#include +#include +#include +#include + +#include "../common/version.h" + +#include "asmcode.h" +#include "asmlabel.h" +#include "codegen.h" +#include "datatype.h" +#include "declare.h" +#include "error.h" +#include "expr.h" +#include "function.h" +#include "global.h" +#include "include.h" +#include "io.h" +#include "litpool.h" +#include "macrotab.h" +#include "mem.h" +#include "optimize.h" +#include "pragma.h" +#include "scanner.h" +#include "stmt.h" +#include "symtab.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Names of the target systems sorted by target name */ +static const char* TargetNames [] = { + "none", + "atari", + "c64", + "c128", + "ace", + "plus4", + "cbm610", + "pet", + "nes", + "apple2", + "geos", +}; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +static void usage (int ExitCode) +{ + fputs ("Usage: cc65 [options] file\n" + "\t-d\t\tDebug mode\n" + "\t-g\t\tAdd debug info to object files\n" + "\t-h\t\tPrint this help\n" + "\t-j\t\tDefault characters are signed\n" + "\t-o name\t\tName the output file\n" + "\t-tx\t\tSet target system x\n" + "\t-v\t\tVerbose mode\n" + "\t-A\t\tStrict ANSI mode\n" + "\t-Cl\t\tMake local variables static\n" + "\t-Dsym[=defn]\tDefine a symbol\n" + "\t-I path\t\tSet include directory\n" + "\t-O\t\tOptimize code\n" + "\t-Oi\t\tOptimize code, inline more code\n" + "\t-Or\t\tEnable register variables\n" + "\t-Os\t\tInline some known functions\n" + "\t-T\t\tInclude source as comment\n" + "\t-V\t\tPrint version number\n" + "\t-W\t\tSuppress warnings\n", + stderr); + exit (ExitCode); +} + + + +static char* GetArg (int* ArgNum, char* argv [], unsigned Len) +/* Get an option argument */ +{ + char* Arg = argv [*ArgNum]; + if (Arg [Len] != '\0') { + /* Argument appended */ + return Arg + Len; + } else { + /* Separate argument */ + Arg = argv [*ArgNum + 1]; + if (Arg == 0) { + /* End of arguments */ + fprintf (stderr, "Option requires an argument: %s\n", argv [*ArgNum]); + exit (EXIT_FAILURE); + } + ++(*ArgNum); + return Arg; + } +} + + + +/* Define a CBM system */ +static void cbmsys (const char* sys) +{ + AddNumericMacro ("__CBM__", 1); + AddNumericMacro (sys, 1); +} + + + +static int MapSys (const char* Name) +/* Map a target name to a system code. Return -1 in case of an error */ +{ + unsigned I; + + /* Check for a numeric target */ + if (isdigit (*Name)) { + int Target = atoi (Name); + if (Target >= 0 && Target < TGT_COUNT) { + return Target; + } + } + + /* Check for a target string */ + for (I = 0; I < TGT_COUNT; ++I) { + if (strcmp (TargetNames [I], Name) == 0) { + return I; + } + } + /* Not found */ + return -1; +} + + + +/* Define a target system */ +static void SetSys (const char* Sys) +{ + switch (Target = MapSys (Sys)) { + + case TGT_NONE: + break; + + case TGT_ATARI: + AddNumericMacro ("__ATARI__", 1); + break; + + case TGT_C64: + cbmsys ("__C64__"); + break; + + case TGT_C128: + cbmsys ("__C128__"); + break; + + case TGT_ACE: + cbmsys ("__ACE__"); + break; + + case TGT_PLUS4: + cbmsys ("__PLUS4__"); + break; + + case TGT_CBM610: + cbmsys ("__CBM610__"); + break; + + case TGT_PET: + cbmsys ("__PET__"); + break; + + case TGT_NES: + AddNumericMacro ("__NES__", 1); + break; + + case TGT_APPLE2: + AddNumericMacro ("__APPLE2__", 1); + break; + + case TGT_GEOS: + /* Do not handle as a CBM system */ + AddNumericMacro ("__GEOS__", 1); + break; + + default: + fputs ("Unknown system type\n", stderr); + usage (EXIT_FAILURE); + } +} + + + +static void InvSym (const char* Def) +/* Print an error about an invalid macro definition and die */ +{ + fprintf (stderr, "Invalid macro definition: `%s'\n", Def); + exit (EXIT_FAILURE); +} + + + +static void DefineSym (const char* Def) +/* Define a symbol on the command line */ +{ + const char* P = Def; + + /* The symbol must start with a character or underline */ + if (Def [0] != '_' && !isalpha (Def [0])) { + InvSym (Def); + } + + /* Check the symbol name */ + while (isalnum (*P) || *P == '_') { + ++P; + } + + /* Do we have a value given? */ + if (*P != '=') { + if (*P != '\0') { + InvSym (Def); + } + /* No value given. Define the macro with the value 1 */ + AddNumericMacro (Def, 1); + } else { + /* We have a value, P points to the '=' character. Since the argument + * is const, create a copy and replace the '=' in the copy by a zero + * terminator. + */ + char* Q; + unsigned Len = strlen (Def)+1; + char* S = xmalloc (Len); + memcpy (S, Def, Len); + Q = S + (P - Def); + *Q++ = '\0'; + + /* Define this as a macro */ + AddTextMacro (S, Q); + + /* Release the allocated memory */ + xfree (S); + } +} + + + +static void Parse (void) +/* Process all input text. + * At this level, only static declarations, defines, includes, and function + * definitions are legal.... + */ +{ + int comma; + SymEntry* Entry; + + kill (); + gettok (); /* "prime" the pump */ + gettok (); + while (curtok != CEOF) { + + DeclSpec Spec; + Declaration Decl; + int NeedStorage; + + /* Check for an ASM statement (which is allowed also on global level) */ + if (curtok == ASM) { + doasm (); + ConsumeSemi (); + continue; + } + + /* Check for a #pragma */ + if (curtok == PRAGMA) { + DoPragma (); + continue; + } + + /* Read variable defs and functions */ + ParseDeclSpec (&Spec, SC_EXTERN | SC_STATIC, T_INT); + + /* Don't accept illegal storage classes */ + if (Spec.StorageClass == SC_AUTO || Spec.StorageClass == SC_REGISTER) { + Error (ERR_ILLEGAL_STORAGE_CLASS); + Spec.StorageClass = SC_EXTERN | SC_STATIC; + } + + /* Check if this is only a type declaration */ + if (curtok == SEMI) { + gettok (); + continue; + } + + /* Check if we must reserve storage for the variable. We do + * this if we don't had a storage class given ("int i") or + * if the storage class is explicitly specified as static. + * This means that "extern int i" will not get storage + * allocated. + */ + NeedStorage = (Spec.StorageClass & SC_TYPEDEF) == 0 && + ((Spec.Flags & DS_DEF_STORAGE) != 0 || + (Spec.StorageClass & (SC_STATIC | SC_EXTERN)) == SC_STATIC); + + /* Read declarations for this type */ + Entry = 0; + comma = 0; + while (1) { + + unsigned SymFlags; + + /* Read the next declaration */ + ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + if (Decl.Ident[0] == '\0') { + gettok (); + break; + } + + /* Get the symbol flags */ + SymFlags = Spec.StorageClass; + if (IsFunc (Decl.Type)) { + SymFlags |= SC_FUNC; + } else { + if (NeedStorage) { + /* We will allocate storage, variable is defined */ + SymFlags |= SC_STORAGE | SC_DEF; + } + } + + /* Add an entry to the symbol table */ + Entry = AddGlobalSym (Decl.Ident, Decl.Type, SymFlags); + + /* Reserve storage for the variable if we need to */ + if (SymFlags & SC_STORAGE) { + + /* Get the size of the variable */ + unsigned Size = SizeOf (Decl.Type); + + /* Allow initialization */ + if (curtok == ASGN) { + + /* We cannot initialize types of unknown size, or + * void types in non ANSI mode. + */ + if (Size == 0) { + if (!IsVoid (Decl.Type)) { + if (!IsArray (Decl.Type)) { + /* Size is unknown and not an array */ + Error (ERR_UNKNOWN_SIZE); + } + } else if (ANSI) { + /* We cannot declare variables of type void */ + Error (ERR_ILLEGAL_TYPE); + } + } + + /* Switch to the data segment */ + g_usedata (); + + /* Define a label */ + g_defgloblabel (Entry->Name); + + /* Skip the '=' */ + gettok (); + + /* Parse the initialization */ + ParseInit (Entry->Type); + } else { + + if (IsVoid (Decl.Type)) { + /* We cannot declare variables of type void */ + Error (ERR_ILLEGAL_TYPE); + } else if (Size == 0) { + /* Size is unknown */ + Error (ERR_UNKNOWN_SIZE); + } + + /* Switch to the BSS segment */ + g_usebss (); + + /* Define a label */ + g_defgloblabel (Entry->Name); + + /* Allocate space for uninitialized variable */ + g_res (SizeOf (Entry->Type)); + } + + } + + /* Check for end of declaration list */ + if (curtok == COMMA) { + gettok (); + comma = 1; + } else { + break; + } + } + + /* Function declaration? */ + if (IsFunc (Decl.Type)) { + + /* Function */ + if (!comma) { + + if (curtok == SEMI) { + + /* Prototype only */ + gettok (); + + } else { + if (Entry) { + NewFunc (Entry); + } + } + } + + } else { + + /* Must be followed by a semicolon */ + ConsumeSemi (); + + } + } +} + + + +static void Compile (void) +/* Compiler begins execution here. inp is input fd, output is output fd. */ +{ + char* Path; + + + /* Setup variables */ + filetab[0].f_iocb = inp; + LiteralLabel = GetLabel (); + + /* Add some standard paths to the include search path */ + AddIncludePath ("", INC_USER); /* Current directory */ + AddIncludePath ("include", INC_SYS); +#ifdef CC65_INC + /* Allow modifications of the given string by dup'ing it */ + AddIncludePath (xstrdup (CC65_INC), INC_SYS); +#else + AddIncludePath ("/usr/lib/cc65/include", INC_SYS); +#endif + Path = getenv ("CC65_INC"); + if (Path) { + AddIncludePath (Path, INC_SYS | INC_USER); + } + + /* Add macros that are always defined */ + AddNumericMacro ("__CC65__", (VER_MAJOR * 0x100) + (VER_MINOR * 0x10) + VER_PATCH); + + /* Strict ANSI macro */ + if (ANSI) { + AddNumericMacro ("__STRICT_ANSI__", 1); + } + + /* Optimization macros */ + if (Optimize) { + AddNumericMacro ("__OPT__", 1); + if (FavourSize == 0) { + AddNumericMacro ("__OPT_i__", 1); + } + if (EnableRegVars) { + AddNumericMacro ("__OPT_r__", 1); + } + if (InlineStdFuncs) { + AddNumericMacro ("__OPT_s__", 1); + } + } + + /* Create the base lexical level */ + EnterGlobalLevel (); + + /* Generate the code generator preamble */ + g_preamble (); + + /* Ok, start the ball rolling... */ + Parse (); + + /* Dump literal pool. */ + DumpLiteralPool (); + + /* Write imported/exported symbols */ + EmitExternals (); + + if (Debug) { + PrintLiteralStats (stdout); + PrintMacroStats (stdout); + } + + /* Leave the main lexical level */ + LeaveGlobalLevel (); + + /* Print an error report */ + ErrorReport (); +} + + + +int main (int argc, char **argv) +{ + int i; + char *argp; + char out_name [256]; + char* p; + + /* Initialize the output file name */ + out_name [0] = '\0'; + + fin = NULL; + + /* Parse the command line */ + for (i = 1; i < argc; i++) { + if (*(argp = argv[i]) == '-') { + switch (argp[1]) { + + case 'd': /* debug mode */ + Debug = 1; + break; + + case 'h': + case '?': + usage (EXIT_SUCCESS); + break; + + case 'g': + DebugInfo = 1; + break; + + case 'j': + SignedChars = 1; + break; + + case 'o': + strcpy (out_name, GetArg (&i, argv, 2)); + break; + + case 't': + SetSys (GetArg (&i, argv, 2)); + break; + + case 'v': + ++Verbose; + break; + + case 'A': + ANSI = 1; + break; + + case 'C': + p = argp + 2; + while (*p) { + switch (*p++) { + case 'l': + LocalsAreStatic = 1; + break; + } + } + break; + + case 'D': + DefineSym (GetArg (&i, argv, 2)); + break; + + case 'I': + AddIncludePath (GetArg (&i, argv, 2), INC_SYS | INC_USER); + break; + + case 'O': + Optimize = 1; + p = argp + 2; + while (*p) { + switch (*p++) { + case 'f': + sscanf (p, "%lx", (long*) &OptDisable); + break; + case 'i': + FavourSize = 0; + break; + case 'r': + EnableRegVars = 1; + break; + case 's': + InlineStdFuncs = 1; + break; + } + } + break; + + case 'T': + IncSource = 1; + break; + + case 'V': + fprintf (stderr, "cc65 V%u.%u.%u\n", + VER_MAJOR, VER_MINOR, VER_PATCH); + break; + + case 'W': + NoWarn = 1; + break; + + default: + fprintf (stderr, "Invalid option %s\n", argp); + usage (EXIT_FAILURE); + } + } else { + if (fin) { + fprintf (stderr, "additional file specs ignored\n"); + } else { + fin = xstrdup (argp); + inp = fopen (fin, "r"); + if (inp == 0) { + Fatal (FAT_CANNOT_OPEN_INPUT, strerror (errno)); + } + } + } + } + if (!fin) { + fprintf (stderr, "%s: No input files\n", argv [0]); + exit (EXIT_FAILURE); + } + + /* Create the output file name. We should really have + * some checks for string overflow, but I'll drop this because of the + * additional code size it would need (as in other places). Sigh. + */ + if (out_name [0] == '\0') { + /* No output name given, create default */ + strcpy (out_name, fin); + if ((p = strrchr (out_name, '.'))) { + *p = '\0'; + } + strcat (out_name, ".s"); + } + + /* Go! */ + Compile (); + + /* Create the output file if we didn't had any errors */ + if (ErrorCount == 0 || Debug) { + + FILE* F; + + /* Optimize the output if requested */ + if (Optimize) { + OptDoOpt (); + } + + /* Open the file */ + F = fopen (out_name, "w"); + if (F == 0) { + Fatal (FAT_CANNOT_OPEN_OUTPUT, strerror (errno)); + } + + /* Write the output to the file */ + WriteOutput (F); + + /* Close the file, check for errors */ + if (fclose (F) != 0) { + remove (out_name); + Fatal (FAT_CANNOT_WRITE_OUTPUT); + } + } + + /* Return an apropriate exit code */ + return (ErrorCount > 0)? EXIT_FAILURE : EXIT_SUCCESS; +} + + diff --git a/src/cc65/make/cc65.mak b/src/cc65/make/cc65.mak new file mode 100644 index 000000000..6398d3681 --- /dev/null +++ b/src/cc65/make/cc65.mak @@ -0,0 +1,40 @@ +# +# Makefile for CC65.COM. +# + +.SUFFIXES: .o .obj .m65 .c + +.c.m65: + @echo $< + @cc65 -I../lib65/ -O -t4 $< + +.m65.obj: + ../ra65/ra65 -o $@ ../lib65/ace/global.m65 $< + +C_SRCS = code-gen.c error.c expr1.c expr2.c expr3.c function.c mem.c loop.c\ + globlvar.c io.c scanner.c main.c optimize.c preproc.c\ + stmt.c symtab.c util.c declare.c + +H_SRCS = cc65.h scanner.h error.h mem.h optimize.h code-gen.h function.h\ + preproc.h util.h symtab.h io.h ctrans.h stmt.h declare.h loop.h\ + expr.h + +M65_FILES = ccmisc.m65 extra.m65 rtextra.m65 + +OBJS = code-gen.obj error.obj expr1.obj expr2.obj expr3.obj function.obj \ + globlvar.obj io.obj scanner.obj main.obj\ + optimize.obj preproc.obj stmt.obj symtab.obj declare.obj loop.obj\ + ccmisc.obj extra.obj rtextra.obj ctrans.obj mem.obj util.obj + +cc65.com: $(OBJS) + @../ra65/link65 -t4 -m -o cc65.com ../lib65/ace/crt0.obj $(OBJS)\ + ../lib65/ace.olb + +.PRECIOUS: $(C_SRCS:.c=.m65) + + +$(OBJS) : $(H_SRCS) + +clean : + rm -f $(OBJS) + rm -f $(C_SRCS:.c=.m65) diff --git a/src/cc65/make/gcc.mak b/src/cc65/make/gcc.mak new file mode 100644 index 000000000..c50679a8a --- /dev/null +++ b/src/cc65/make/gcc.mak @@ -0,0 +1,77 @@ +# +# Makefile for cross-compiler version of CC65. +# + + +# Default for the compiler lib search path as compiler define +CDEFS=-DCC65_INC=\"/usr/lib/cc65/include/\" +CFLAGS = -O2 -g -Wall $(CDEFS) +CC=gcc +LDFLAGS= + +OBJS = anonname.o \ + asmcode.o \ + asmlabel.o \ + asmline.o \ + check.o \ + codegen.o \ + ctrans.o \ + datatype.o \ + declare.o \ + error.o \ + expr.o \ + funcdesc.o \ + function.o \ + global.o \ + goto.o \ + hashstr.o \ + ident.o \ + include.o \ + io.o \ + litpool.o \ + locals.o \ + loop.o \ + macrotab.o \ + main.o \ + mem.o \ + optimize.o \ + preproc.o \ + pragma.o \ + scanner.o \ + stdfunc.o \ + stmt.o \ + symentry.o \ + symtab.o \ + util.o + +EXECS = cc65 + + +.PHONY: all +ifeq (.depend,$(wildcard .depend)) +all : $(EXECS) +include .depend +else +all: depend + @$(MAKE) -f make/gcc.mak all +endif + + +cc65: $(OBJS) + $(CC) $(LDFLAGS) -o cc65 $(CFLAGS) $(OBJS) + +clean: + rm -f *~ core *.map + +zap: clean + rm -f *.o $(EXECS) .depend + +# ------------------------------------------------------------------------------ +# Make the dependencies + +.PHONY: depend dep +depend dep: $(OBJS:.o=.c) + @echo "Creating dependency information" + $(CC) -MM $^ > .depend + + diff --git a/src/cc65/make/watcom.mak b/src/cc65/make/watcom.mak new file mode 100644 index 000000000..11966fd9d --- /dev/null +++ b/src/cc65/make/watcom.mak @@ -0,0 +1,157 @@ +# +# CC65 Makefile for the Watcom compiler +# + +# ------------------------------------------------------------------------------ +# Generic stuff + +.AUTODEPEND +.SUFFIXES .ASM .C .CC .CPP +.SWAP + +AR = WLIB +LD = WLINK + +!if !$d(TARGET) +!if $d(__OS2__) +TARGET = OS2 +!else +TARGET = NT +!endif +!endif + +# target specific macros. +!if $(TARGET)==OS2 + +# --------------------- OS2 --------------------- +SYSTEM = os2v2 +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS32 + +# -------------------- DOS4G -------------------- +SYSTEM = dos4g +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS + +# --------------------- DOS --------------------- +SYSTEM = dos +CC = WCC +CCCFG = -bt=$(TARGET) -d1 -onatx -zp2 -2 -ml -zq -w2 + +!elif $(TARGET)==NT + +# --------------------- NT ---------------------- +SYSTEM = nt +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!else +!error +!endif + +# ------------------------------------------------------------------------------ +# Implicit rules + +.c.obj: + $(CC) $(CCCFG) $< + + +# ------------------------------------------------------------------------------ +# All library OBJ files + +OBJS = anonname.obj \ + asmcode.obj \ + asmlabel.obj \ + asmline.obj \ + check.obj \ + codegen.obj \ + ctrans.obj \ + datatype.obj \ + declare.obj \ + error.obj \ + expr.obj \ + funcdesc.obj \ + function.obj \ + global.obj \ + goto.obj \ + hashstr.obj \ + ident.obj \ + include.obj \ + io.obj \ + litpool.obj \ + locals.obj \ + loop.obj \ + macrotab.obj \ + main.obj \ + mem.obj \ + optimize.obj \ + pragma.obj \ + preproc.obj \ + stmt.obj \ + scanner.obj \ + stdfunc.obj \ + symentry.obj \ + symtab.obj \ + util.obj + + +.PRECIOUS $(OBJS:.obj=.c) + +# ------------------------------------------------------------------------------ +# Main targets + +all: cc65 + +cc65: cc65.exe + + +# ------------------------------------------------------------------------------ +# Other targets + + +cc65.exe: $(OBJS) + $(LD) system $(SYSTEM) @&&| +DEBUG ALL +OPTION QUIET +NAME $< +FILE anonname.obj +FILE asmcode.obj +FILE asmlabel.obj +FILE asmline.obj +FILE check.obj +FILE codegen.obj +FILE ctrans.obj +FILE datatype.obj +FILE declare.obj +FILE error.obj +FILE expr.obj +FILE funcact.obj +FILE funcdesc.obj +FILE function.obj +FILE global.obj +FILE goto.obj +FILE hashstr.obj +FILE ident.obj +FILE include.obj +FILE io.obj +FILE litpool.obj +FILE locals.obj +FILE loop.obj +FILE macrotab.obj +FILE main.obj +FILE mem.obj +FILE optimize.obj +FILE pragma.obj +FILE preproc.obj +FILE stmt.obj +FILE scanner.obj +FILE stdfunc.obj +FILE symentry.obj +FILE symtab.obj +FILE util.obj +| + diff --git a/src/cc65/mem.c b/src/cc65/mem.c new file mode 100644 index 000000000..72b2291cc --- /dev/null +++ b/src/cc65/mem.c @@ -0,0 +1,605 @@ +/*****************************************************************************/ +/* */ +/* MEMCHECK.CC */ +/* */ +/* (C) 1995 Ullrich von Bassewitz */ +/* Zwehrenbuehlstrasse 33 */ +/* D-72070 Tuebingen */ +/* EMail: uz@ibb.schwaben.com */ +/* */ +/*****************************************************************************/ + + + +// Poor man's memory checker. Overloads the global operators new and delete +// and does some additional checks if the variable MemCheck is set to true: +// +// * Check if an allocated block is already allocated (heap corrupt) +// * Check if a block that should be freed is allocated +// * Check if there have been writes outside the blocks bounds (by +// adding a signature to the end) +// * Check if new does not provide a NULL pointer. + + + +#include +#include +#include +#include +#ifdef __WATCOMC__ +# include +#endif + +#include "check.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +typedef unsigned long u32; + + + +// Signature of a memory block +static u32 MemSig = 0x12785634; + +// Switch memory checking on or off +int MemCheck = 0; + +// Switch memory filling on or off +int MemFill = 0; + +// Validation on each call? +int MemValidate = 0; + +// Don't really free blocks +int MemDontFree = 0; + +// Logfile for allocations/deallocations +static const char* MemLogFile = 0; +static FILE* LogFile = 0; + +// Statistics +u32 MemNewCount = 0; +u32 MemDelCount = 0; +u32 MemDelNULLCount = 0; +u32 MemNewCheckCount = 0; +u32 MemDelCheckCount = 0; +u32 MemLargestBlock = 0; +u32 MemUsage = 0; +u32 MemMaxUsage = 0; + +// This is the fill value for memory blocks if MemFill is true. On intel +// architectures, this is the code for "INT 3", an instruction that is +// often used by debuggers as a breakpoint. +unsigned char FillVal = 0xCC; + + + +/*****************************************************************************/ +/* struct BlockInfo */ +/*****************************************************************************/ + + + +typedef struct { + unsigned char* Ptr; + u32 Size; +} BlockInfo; + +// +const int FirstChunk = 2000; +const int Delta = 1000; + +// Variables needed +static int IsInitialized = 0; +static int BlockCount = 0; +static int BlockLimit = 0; +static BlockInfo* Blocks = 0; + + + +/*****************************************************************************/ +/* class BlockInfoColl */ +/*****************************************************************************/ + + + +static void MemSetCount (int NewCount) +// Make shure, there is space for NewSize blocks in Blocks +{ + if (NewCount > BlockLimit) { + // OOPS, need realloc + if (BlockLimit == 0 && NewCount <= FirstChunk) { + BlockLimit = FirstChunk; + } else { + BlockLimit = ((NewCount / Delta) + 1) * Delta; + } + Blocks = (BlockInfo*) realloc (Blocks, BlockLimit * sizeof (BlockInfo)); + } + BlockCount = NewCount; +} + + + +static int MemSearch (const unsigned char* Ptr, int* Index) +// Search for the block. Return 1 if the block is found (Index holds the +// block index in this case). Return 0 if the block is not found and return +// in Index the index where the block should be inserted. +{ + // do a binary search + int First = 0; + int Last = BlockCount - 1; + int Current; + int S = 0; + + while (First <= Last) { + + // Set current to mid of range + Current = (Last + First) / 2; + + // Do a compare + if (Blocks [Current].Ptr < Ptr) { + First = Current + 1; + } else { + Last = Current - 1; + if (Blocks [Current].Ptr == Ptr) { + // Found. + S = 1; // function result + // Set condition to terminate loop + First = Current; + } + } + + } + + *Index = First; + return S; +} + + + +static void MemDelBlock (int Index) +// Delete the block with the given index +{ + BlockCount--; + memmove (Blocks+Index, Blocks+Index+1, (BlockCount-Index) * sizeof (BlockInfo)); +} + + + +static void MemInsBlock (int Index, unsigned char* Ptr, u32 Size) +{ + // Set the new size + MemSetCount (BlockCount + 1); + + // We can insert the element. If the item is not inserted at the end + // of the collection, we must create a "hole" + if (Index != BlockCount - 1) { + memmove (Blocks + Index + 1, + Blocks + Index, + (BlockCount - 1 - Index) * sizeof (BlockInfo)); + } + + // store the new data + Blocks [Index].Ptr = Ptr; + Blocks [Index].Size = Size; +} + + + +u32 MemBlocksInUse () +{ + return (u32) BlockCount; +} + + + +static void PrintContents (const void* B, unsigned Size, FILE* F) +// Print the contents of the block +{ + unsigned I; + static const unsigned MaxPrint = 14; + + const unsigned char* P = (const unsigned char*) B; + if (Size > MaxPrint) { + Size = MaxPrint; + } + + // Two characters space + fprintf (F, " "); + + // Print the first few bytes in hex + for (I = 0; I < Size; I++) { + fprintf (F, "%02X ", P [I]); + } + fprintf (F, "%*s ", (MaxPrint-Size)*3, ""); + + // Print the bytes again in ASCII + for (I = 0; I < Size; I++) { + unsigned char C = P [I]; + if (C < ' ' || C > 0x7E) { + C = '.'; + } + putc (C, F); + } +} + + + +void MemLogBlocksInUse (const char* Name) +{ + BlockInfo* Block; + int I; + + FILE* F = fopen (Name, "w+t"); + if (F == 0) { + // This is a debug function, so ignore the error + return; + } + + // Get the block count and log some statistics + fprintf (F, "Blocks currently in use: %8lu\n\n" + "Calls to operator new: %8lu\n" + "Calls to operator delete: %8lu\n" + "Checked calls to new: %8lu\n" + "Checked calls to delete: %8lu\n" + "Calls to delete with a NULL arg: %8lu\n\n" + "Largest block allocated: %8lu\n" + "Maximum memory usage: %8lu\n\n", + (unsigned long) BlockCount, + (unsigned long) MemNewCount, + (unsigned long) MemDelCount, + (unsigned long) MemNewCheckCount, + (unsigned long) MemDelCheckCount, + (unsigned long) MemDelNULLCount, + (unsigned long) MemLargestBlock, + (unsigned long) MemMaxUsage); + + // Print a header + fprintf (F, "Num Address Size Contents\n"); + fprintf (F, "----------------------------------------" + "---------------------------------------\n"); + + // Log the blocks + Block = Blocks; + for (I = 0; I < BlockCount; I++, Block++) { + + // Print a line describing the block (convert pointers to hex values) + fprintf (F, "%-5u %08lX %5lu", + I, (unsigned long) Block->Ptr, (unsigned long) Block->Size); + + // Print the first few bytes of the block + PrintContents (Block->Ptr, Block->Size, F); + + // Check the block signature + if (memcmp (Block->Ptr + Block->Size, &MemSig, sizeof (MemSig)) != 0) { + // Signature overwritten + fprintf (F, " *** Signature overwritten ***\n"); + } else { + fprintf (F, "\n"); + } + + } + + // Close the file + fclose (F); +} + + + +static long MemValidateBlocks () +// Validate all memory blocks. Return the index of a block where the +// validation failed, otherwise return -1. +{ + // Validate the blocks + long I; + BlockInfo* Block = Blocks; + for (I = 0; I < BlockCount; I++, Block++) { + + // Check the block signature + if (memcmp (Block->Ptr + Block->Size, &MemSig, sizeof (MemSig)) != 0) { + // Signature overwritten + return I; + } + } + + // All is well... + return -1; +} + + + +static void MemDone () +// Log the memory blocks if requested. Does *not* delete the block array +// since the startup code may release memory after calling the exit functions +// and in this case we will work with a freed block, if we free the block +// array here +{ + // If the environment variable MEMLOGBLOCKS is set to something, use + // this "something" as a filename to log a list of still allocated blocks + const char* Name = getenv ("MEMLOGBLOCKS"); + if (Name) { + MemLogBlocksInUse (Name); + } +} + + + +static void MemInit () +// Initialize the memory checker. +{ + // Get the defaults for the memory checker + const char* Fill; + MemCheck = getenv ("MEMCHECK") != 0; + MemValidate = getenv ("MEMVALIDATE") != 0; + MemDontFree = getenv ("MEMDONTFREE") != 0; + MemLogFile = getenv ("MEMLOGFILE"); + Fill = getenv ("MEMFILL"); + if (Fill) { + MemFill = 1; + if (isdigit (*Fill)) { + FillVal = atoi (Fill); + } + } + + // Open the logfile if set + if (MemLogFile) { + LogFile = fopen (MemLogFile, "w+t"); + } + + // Register the exit function + atexit (MemDone); + + // Initialized now (maybe set already) + IsInitialized = 1; +} + + + +/*****************************************************************************/ +/* Allocate/free blocks */ +/*****************************************************************************/ + + + +static void* MemAlloc (size_t Size) +{ + unsigned char* Ptr; + + // Last allocated block is remembered here + static void* LastBlock = 0; + + // Initialize the memory checker on the first call + if (IsInitialized == 0) { + MemInit (); + } + + // Count the calls to new + MemNewCount++; + + // Update largest block info + if (Size > MemLargestBlock) { + MemLargestBlock = Size; + } + if (MemCheck) { + + int Index; + + // Count the checked calls + MemNewCheckCount++; + + // If we need to validate all blocks, do that + if (MemValidate) { + long I = MemValidateBlocks (); + if (I != -1) { + // We have a problem. Be shure to switch of MemValidate before + // calling FAIL, otherwise we will get an endless loop... + MemValidate = 0; + FAIL ("MemCheck: Block signature overwritten!"); + } + } + + // Update memory usage + MemUsage += Size; + if (MemUsage > MemMaxUsage) { + MemMaxUsage = MemUsage; + } + + // Get a memory block + Ptr = (unsigned char*) malloc (Size + sizeof (MemSig)); + + // Make a signature at the end of the block + memcpy (Ptr + Size, &MemSig, sizeof (MemSig)); + + // Search for the block + if (MemSearch (Ptr, &Index) != 0) { + // An item with this key exists. This means that the heap is + // corrupted + FAIL ("MemCheck: Duplicate block!"); + } else { + // The returned pointer is not in the collection of already + // allocated blocks, but it may point inside of an already + // allocated block. Check this. + // Note: Index is the index of the item _before the given + // pointer, so simply check the range of the entry with index + // Index. + if (Index > 0) { + // There is a block that's memory address is less than the + // one returned by malloc + const BlockInfo* BB = Blocks + Index - 1; + if (Ptr < BB->Ptr + BB->Size) { + // Pointer points inside the block below - heap corrupted + FAIL ("MemCheck: Heap corrupt!"); + } + } + + // Heap ok, insert the new block + MemInsBlock (Index, Ptr, Size); + } + + } else { + + // No memory checking. Allocate a memory block, but beware: New is + // defined so that "new char [0]" points to a distinct object every + // time it is called, so one cannot return NULL for a size of 0! + Ptr = (unsigned char*) malloc (Size ? Size : 1); + + } + + // Remember the last block + LastBlock = Ptr; + + // Check if we got memory, fail otherwise + if (Ptr == 0) { + FAIL ("MemCheck: Out of memory"); + } + + // Fill the memory block if requested + if (MemFill) { + memset (Ptr, FillVal, Size); + } + + // Log the allocation if requested + if (LogFile) { + // Print a line describing the block (convert pointers to hex values) + fprintf (LogFile, "A %08lX %5lu", + (unsigned long) Ptr, (unsigned long) Size); + + // Print the first few bytes of the block + PrintContents (Ptr, Size, LogFile); + fprintf (LogFile, "\n"); + } + + // Return a pointer to the memory block + return Ptr; +} + + + +static void MemFree (void* P) +{ + // We cannot call delete if the memory system is not initialized + if (IsInitialized == 0) { + FAIL ("MemCheck: Trying to delete a block before the first call to new!"); + } + + // Count the calls to delete + MemDelCount++; + + // Deleting NULL pointers is always ok, nothing has to be done + if (P == 0) { + MemDelNULLCount++; + return; + } + + if (MemCheck) { + + int Index; + unsigned char* Ptr; + + // Count the calls + MemDelCheckCount++; + + // If we need to validate all blocks, do that + if (MemValidate) { + long I = MemValidateBlocks (); + if (I != -1) { + // We have a problem. Be shure to switch of MemValidate before + // calling FAIL, otherwise we will get an endless loop... + MemValidate = 0; + FAIL ("MemCheck: Block signature overwritten!"); + } + } + + // Cast the pointer + Ptr = (unsigned char*) P; + + // Search for the block + if (MemSearch (Ptr, &Index) != 0) { + + // The block exists. + BlockInfo* BI = Blocks + Index; + + // Log the deallocation if requested + if (LogFile) { + // Print a line describing the block (convert pointers to hex values) + fprintf (LogFile, "D %08lX %5lu", + (unsigned long) BI->Ptr, (unsigned long) BI->Size); + + // Print the first few bytes of the block + PrintContents (BI->Ptr, BI->Size, LogFile); + fprintf (LogFile, "\n"); + } + + // Check the signature + if (memcmp (Ptr + BI->Size, &MemSig, sizeof (MemSig)) != 0) { + // Signature overwritten + FAIL ("MemCheck: Block signature overwritten"); + } + + // Fill the memory block if requested + if (MemFill) { + memset (Ptr, FillVal, BI->Size); + } + + // Should the block really be freed? + if (MemDontFree == 0) { + + // Update memory usage + MemUsage -= BI->Size; + + // Delete the entry + MemDelBlock (Index); + + // Delete the memory block + free (P); + + } + + } else { + // Trying to free a block that is not allocated + FAIL ("MemCheck: Trying to free a block that is not allocated"); + } + } else { + + // Free the block without checks + free (P); + + } +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void* xmalloc (size_t Size) +{ + return MemAlloc (Size); +} + + + +void xfree (const void* P) +{ + MemFree ((void*)P); +} + + + +char* xstrdup (const char* S) +{ + unsigned Len = strlen (S) + 1; + return memcpy (xmalloc (Len), S, Len); +} + + + diff --git a/src/cc65/mem.h b/src/cc65/mem.h new file mode 100644 index 000000000..7cbfa8c03 --- /dev/null +++ b/src/cc65/mem.h @@ -0,0 +1,76 @@ +/*****************************************************************************/ +/* */ +/* mem.h */ +/* */ +/* Safe memory allocation for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef MEM_H +#define MEM_H + + + +#include + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +extern size_t memmax; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void* xmalloc (size_t size); +/* Allocate memory, check for out of memory condition. Do some debugging */ + +void xfree (const void* block); +/* Free the block, do some debugging */ + +char* xstrdup (const char* s); +/* Duplicate a string on the heap. The function checks for out of memory */ + + + +/* End of mem.h */ +#endif + + + diff --git a/src/cc65/optimize.c b/src/cc65/optimize.c new file mode 100644 index 000000000..3119311b6 --- /dev/null +++ b/src/cc65/optimize.c @@ -0,0 +1,4182 @@ +/*****************************************************************************/ +/* */ +/* optimize.c */ +/* */ +/* An optimizer for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "asmlabel.h" +#include "asmline.h" +#include "check.h" +#include "error.h" +#include "global.h" +#include "io.h" +#include "mem.h" +#include "optimize.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Bitset of flags that switch the different optimizer passes */ +unsigned long OptDisable = 0; + + + +/* Bitmapped flags for the Flags field in the Line struct */ +#define OF_CODE 0x0001 /* This line is in a code segment */ + +/* Pointer to first code line */ +static Line* FirstCode; + +/* Label list */ +static Line** Labels = 0; /* Pointers to label lines */ +static unsigned LabelCount = 0; /* Count of local labels found */ + +/* A collection of lines */ +typedef struct LineColl_ LineColl; +struct LineColl_ { + unsigned Count; /* Count of lines in the collection */ + unsigned Max; /* Maximum count of lines */ + Line* Lines[1]; /* Lines, dynamically allocated */ +}; + + + +/* Calculate the element count of a table */ +#define COUNT(T) (sizeof (T) / sizeof (T [0])) + +/* Macro to increment and decrement register contents if they're valid */ +#define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF +#define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF + +/* Defines for the conditions in a compare */ +#define CMP_EQ 0 +#define CMP_NE 1 +#define CMP_GT 2 +#define CMP_GE 3 +#define CMP_LT 4 +#define CMP_LE 5 +#define CMP_UGT 6 +#define CMP_UGE 7 +#define CMP_ULT 8 +#define CMP_ULE 9 + +/* Defines for registers */ +#define REG_NONE 0x00 +#define REG_A 0x01 +#define REG_X 0x02 +#define REG_Y 0x04 +#define REG_AX (REG_A | REG_X) +#define REG_ALL (REG_A | REG_X | REG_Y) + +/* Description of the commands */ +static const struct { + const char* Insn; /* Instruction */ + unsigned char FullMatch; /* Match full instuction? */ + unsigned char Use; /* Registers used */ + unsigned char Load; /* Registers loaded */ +} CmdDesc [] = { + { "\tadc\t", 0, REG_A, REG_NONE }, + { "\tand\t", 0, REG_A, REG_NONE }, + { "\tasl\ta", 1, REG_A, REG_NONE }, + { "\tasl\t", 0, REG_NONE, REG_NONE }, + { "\tclc", 1, REG_NONE, REG_NONE }, + { "\tcld", 1, REG_NONE, REG_NONE }, + { "\tcli", 1, REG_NONE, REG_NONE }, + { "\tcmp\t", 0, REG_A, REG_NONE }, + { "\tcpx\t", 0, REG_X, REG_NONE }, + { "\tcpy\t", 0, REG_Y, REG_NONE }, + { "\tdec\t", 0, REG_NONE, REG_NONE }, + { "\tdex", 1, REG_X, REG_NONE }, + { "\tdey", 1, REG_Y, REG_NONE }, + { "\teor\t", 0, REG_A, REG_NONE }, + { "\tinc\t", 0, REG_NONE, REG_NONE }, + { "\tinx", 1, REG_X, REG_NONE }, + { "\tiny", 1, REG_Y, REG_NONE }, + { "\tjsr\tbool", 0, REG_NONE, REG_AX }, + { "\tjsr\tdecaxy", 1, REG_ALL, REG_AX }, + { "\tjsr\tdecax", 0, REG_AX, REG_AX }, + { "\tjsr\tldax0sp", 1, REG_Y, REG_AX }, + { "\tjsr\tldaxysp", 1, REG_Y, REG_AX }, + { "\tjsr\tpusha", 1, REG_A, REG_Y }, + { "\tjsr\tpusha0", 1, REG_A, REG_X | REG_Y }, + { "\tjsr\tpushax", 1, REG_AX, REG_Y }, + { "\tjsr\tpushw0sp", 1, REG_NONE, REG_ALL }, + { "\tjsr\tpushwysp", 1, REG_Y, REG_ALL }, + { "\tjsr\ttosicmp", 1, REG_AX, REG_ALL }, + { "\tlda\t", 0, REG_NONE, REG_A }, + { "\tldax\t", 0, REG_NONE, REG_AX }, + { "\tldx\t", 0, REG_NONE, REG_X }, + { "\tldy\t", 0, REG_NONE, REG_Y }, + { "\tlsr\ta", 1, REG_A, REG_NONE }, + { "\tlsr\t", 0, REG_NONE, REG_NONE }, + { "\tnop", 1, REG_NONE, REG_NONE }, + { "\tora\t", 0, REG_A, REG_NONE }, + { "\tpha", 1, REG_A, REG_NONE }, + { "\tphp", 1, REG_NONE, REG_NONE }, + { "\tpla", 1, REG_NONE, REG_A }, + { "\tplp", 1, REG_NONE, REG_NONE }, + { "\trol\ta", 1, REG_A, REG_A }, + { "\trol\t", 0, REG_NONE, REG_NONE }, + { "\tror\ta", 1, REG_A, REG_A }, + { "\tror\t", 0, REG_NONE, REG_NONE }, + { "\tsbc\t", 0, REG_A, REG_NONE }, + { "\tsec", 1, REG_NONE, REG_NONE }, + { "\tsed", 1, REG_NONE, REG_NONE }, + { "\tsei", 1, REG_NONE, REG_NONE }, + { "\tsta\t", 0, REG_A, REG_NONE }, + { "\tstx\t", 0, REG_X, REG_NONE }, + { "\tsty\t", 0, REG_Y, REG_NONE }, + { "\ttax", 1, REG_A, REG_X }, + { "\ttay", 1, REG_A, REG_Y }, + { "\ttsx", 1, REG_NONE, REG_X }, + { "\ttxa", 1, REG_X, REG_A }, + { "\ttya", 1, REG_Y, REG_A }, +}; + + + +/* Table with the compare suffixes */ +static const char CmpSuffixTab [][4] = { + "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule" +}; + +/* Table used to invert a condition, indexed by condition */ +static const unsigned char CmpInvertTab [] = { + CMP_NE, CMP_EQ, + CMP_LE, CMP_LT, CMP_GE, CMP_GT, + CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT +}; + +/* Table to show which compares are signed (use the N flag) */ +static const char CmpSignedTab [] = { + 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 +}; + + + +/* Lists of branches */ +static const char* ShortBranches [] = { + "\tbeq\t", + "\tbne\t", + "\tbpl\t", + "\tbmi\t", + "\tbcc\t", + "\tbcs\t", + "\tbvc\t", + "\tbvs\t", + 0 +}; +static const char* LongBranches [] = { + "\tjeq\t", + "\tjne\t", + "\tjpl\t", + "\tjmi\t", + "\tjcc\t", + "\tjcs\t", + "\tjvc\t", + "\tjvs\t", + 0 +}; + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static unsigned EstimateSize (Line* L); +/* Estimate the size of an instruction */ + +static int IsLocalLabel (const Line* L); +/* Return true if the line is a local label line */ + +static unsigned GetLabelNum (const char* L); +/* Return the label number of a label line */ + +static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused); +/* Subfunction for RegValUsed. Will be called recursively in case of branches. */ + + + +/*****************************************************************************/ +/* List stuff */ +/*****************************************************************************/ + + + +static Line* NewLineAfter (Line* LineBefore, const char* Format, ...) +/* Create a new line, insert it after L and return it. The new line is marked + * as code line. + */ +{ + Line* L; + + /* Format the new line and add it */ + va_list ap; + va_start (ap, Format); + L = NewCodeLineAfter (LineBefore, Format, ap); + va_end (ap); + + /* Make the line a code line */ + L->Flags = OF_CODE; + + /* Estimate the code size */ + L->Size = EstimateSize (L); + + /* Return the new line */ + return L; +} + + + +static Line* NewLabelAfter (Line* L, unsigned Label) +/* Add a new line with a definition of a local label after the line L */ +{ + char Buf [32]; + + /* Create the label */ + sprintf (Buf, "L%04X:", Label); + + /* Create a new line */ + L = NewLineAfter (L, Buf); + + /* Insert this label into the label list */ + Labels [Label] = L; + + /* Return the new line */ + return L; +} + + + +static void FreeLine (Line* L) +/* Remove a line from the list and free it */ +{ + /* If this is a label line, remove it from the label list */ + if (IsLocalLabel (L)) { + Labels [GetLabelNum (L->Line)] = 0; + } + + /* Unlink the line */ + FreeCodeLine (L); +} + + + +static Line* ReplaceLine (Line* L, const char* Format, ...) +/* Replace one line by another */ +{ + unsigned Len; + char S [256]; + + /* Format the new line */ + va_list ap; + va_start (ap, Format); + vsprintf (S, Format, ap); + va_end (ap); + + /* Get the length of the new line */ + Len = strlen (S); + + /* We can copy the line if the old line has space enough */ + if (Len <= L->Len) { + + /* Just copy the new line, but don't update the length */ + memcpy (L->Line, S, Len); + L->Line [Len] = '\0'; + + } else { + + /* We must allocate new space */ + Line* NewLine = xmalloc (sizeof (Line) + Len); + + /* Set the values in the new struct */ + NewLine->Flags = L->Flags; + NewLine->Index = L->Index; + NewLine->Size = L->Size; /* Hmm ... */ + NewLine->Len = Len; + memcpy (NewLine->Line, S, Len + 1); + + /* Replace the old struct in the list */ + NewLine->Next = L->Next; + if (NewLine->Next) { + NewLine->Next->Prev = NewLine; + } else { + /* Last line */ + LastLine = NewLine; + } + NewLine->Prev = L->Prev; + if (NewLine->Prev) { + NewLine->Prev->Next = NewLine; + } else { + /* First line */ + FirstLine = NewLine; + } + + /* Free the old struct */ + xfree (L); + L = NewLine; + } + + /* Estimate the new size */ + if (L->Flags & OF_CODE) { + L->Size = EstimateSize (L); + } + + /* Return the line */ + return L; +} + + + +static Line* PrevCodeLine (Line* L) +/* Return the previous line containing code */ +{ + L = L->Prev; + while (L) { + if (L->Flags & OF_CODE && L->Line [0] != '+') { + break; + } + L = L->Prev; + } + return L; +} + + + +static Line* NextCodeSegLine (Line* L) +/* Return the next line in the code segment */ +{ + L = L->Next; + while (L) { + if (L->Flags & OF_CODE) { + break; + } + L = L->Next; + } + return L; +} + + + +static Line* NextCodeLine (Line* L) +/* Return the next line containing code */ +{ + L = L->Next; + while (L) { + if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') { + break; + } + L = L->Next; + } + return L; +} + + + +static Line* NextInstruction (Line* L) +/* Return the next line containing code, ignoring labels. */ +{ + do { + L = NextCodeLine (L); + } while (L && (L->Line[0] == '+' || IsLocalLabel(L))); + return L; +} + + + +static void FreeLines (Line* Start, Line* End) +/* Delete all lines from Start to End, both inclusive */ +{ + Line* L; + do { + L = Start; + Start = NextCodeSegLine (Start); + FreeLine (L); + } while (L != End); +} + + + +/*****************************************************************************/ +/* Line Collections */ +/*****************************************************************************/ + + + +static LineColl* NewLineColl (unsigned Size) +/* Create a new line collection and return it */ +{ + /* Allocate memory */ + LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1)); + + /* Initialize members */ + LC->Count = 0; + LC->Max = Size; + + /* Return the new collection */ + return LC; +} + + + +static void FreeLineColl (LineColl* LC) +/* Delete a line collection */ +{ + xfree (LC); +} + + + +static int LCAddLine (LineColl* LC, Line* L) +/* Add a line. Return 0 if no space available, return 1 otherwise */ +{ + /* Check if there is enough space available */ + if (LC->Count >= LC->Max) { + /* No room available */ + return 0; + } + + /* Add the line */ + LC->Lines [LC->Count++] = L; + + /* Done */ + return 1; +} + + + +static int LCHasLine (LineColl* LC, Line* L) +/* Check if the given line is in the collection */ +{ + unsigned I; + for (I = 0; I < LC->Count; ++I) { + if (LC->Lines[I] == L) { + return 1; + } + } + return 0; +} + + + +/*****************************************************************************/ +/* Test a line for several things */ +/*****************************************************************************/ + + + +static int IsLocalLabel (const Line* L) +/* Return true if the line is a local label line */ +{ + return (L->Line [0] == 'L' && isxdigit (L->Line [1])); +} + + + +static int IsLabel (const Line* L) +/* Return true if the line is a label line */ +{ + return (L->Line [0] == 'L' && isxdigit (L->Line [1])) || + (L->Line [0] == '_');; +} + + + +static int IsHintLine (const Line* L) +/* Return true if the line contains an optimizer hint */ +{ + return L->Line [0] == '+'; +} + + + +static int IsSegHint (const Line* L) +/* Return true if the given line contains a segment hint */ +{ + return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0); +} + + + +static int IsHint (const Line* L, const char* Hint) +/* Check if the line contains a given hint */ +{ + return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0); +} + + + +static int IsCondJump (Line* L) +/* Return true if the line contains a conditional jump */ +{ + return (L->Line [0] == '\t' && + (strncmp (L->Line + 1, "beq\t", 4) == 0 || + strncmp (L->Line + 1, "bne\t", 4) == 0 || + strncmp (L->Line + 1, "jeq\t", 4) == 0 || + strncmp (L->Line + 1, "jne\t", 4) == 0)); +} + + + +static int IsXIndAddrMode (Line* L) +/* Return true if the given line does use the X register */ +{ + unsigned Len = strlen (L->Line); + return (strcmp (L->Line + Len - 3, ",x)") == 0 || + strcmp (L->Line + Len - 2, ",x") == 0); +} + + + +static int NoXIndAddrMode (Line* L) +/* Return true if the given line does use the X register */ +{ + return !IsXIndAddrMode (L); +} + + + +static int IsYIndAddrMode (Line* L) +/* Return true if the given line does use the Y register */ +{ + unsigned Len = strlen (L->Line); + return (strcmp (L->Line + Len - 2, ",y") == 0); +} + + + +static Line* FindHint (Line* L, const char* Hint) +/* Search for a line with the given hint */ +{ + while (L) { + if (IsHint (L, Hint)) { + break; + } + L = L->Next; + } + return L; +} + + + +static unsigned GetHexNum (const char* S) +/* Get a hex number from a string */ +{ + unsigned I = 0; + unsigned Val = 0; + while (isxdigit (S [I])) { + int C = (unsigned char) (S [I++]); + if (C >= 'A') { + C -= 'A' - 10; + } else { + C -= '0'; + } + Val = (Val << 4) + C; + } + return Val; +} + + + +static unsigned GetLabelNum (const char* L) +/* Return the label number of a label line */ +{ + CHECK (*L == 'L'); + return GetHexNum (L+1); +} + + + +static Line* GetTargetLine (const char* L) +/* Get the line with the target label of a jump. L must be a pointer to the + * string containing the label number. + */ +{ + Line* Target; + + /* Get the label number of the target */ + unsigned Label = GetLabelNum (L); + CHECK (Label < LabelCount); + + /* Get the line with this label */ + Target = Labels [Label]; + CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0); + + /* And return it */ + return Target; +} + + + +static unsigned GetJumpDistance (Line* L, Line* Target) +/* Get the distance between both lines */ +{ + unsigned Distance = 0; + + if (L != Target) { + if (Target->Index > L->Index) { + /* This is a forward jump. */ + do { + L = NextCodeLine (L); + Distance += L->Size; + } while (L != Target); + } else { + /* This is a backward jump */ + do { + L = PrevCodeLine (L); + Distance += L->Size; + } while (L != Target); + } + } + + /* Return the calculated distance */ + return Distance; +} + + + +static int LineMatch (const Line* L, const char* Start) +/* Check if the start of the line matches Start */ +{ + return strncmp (L->Line, Start, strlen (Start)) == 0; +} + + + +static int LineFullMatch (const Line* L, const char* Start) +/* Check if the matches Start */ +{ + return strcmp (L->Line, Start) == 0; +} + + + +static int LineMatchX (const Line* L, const char** Start) +/* Check the start of the line against a list of patterns. Return the + * number of the pattern that matched, or -1 in case of no match. + */ +{ + unsigned I = 0; + while (*Start) { + if (LineMatch (L, *Start)) { + /* Found */ + return I; + } + ++Start; + ++I; + } + /* Not found */ + return -1; +} + + + +static int LineFullMatchX (const Line* L, const char** Start) +/* Check the the line against a list of patterns. Return the + * number of the pattern that matched, or -1 in case of no match. + */ +{ + unsigned I = 0; + while (*Start) { + if (LineFullMatch (L, *Start)) { + /* Found */ + return I; + } + ++Start; + ++I; + } + /* Not found */ + return -1; +} + + + +static int IsLoadAX (Line* L1, Line* L2) +/* Check if the both lines load a static variable into ax. That is, both lines + * look like + * lda x+0 + * ldx x+0+1 + */ +{ + return LineMatch (L1, "\tlda\t") && + LineMatch (L2, "\tldx\t") && + strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0 && + strcmp (L2->Line+strlen(L1->Line), "+1") == 0; +} + + + +/*****************************************************************************/ +/* Initial optimizer setup */ +/*****************************************************************************/ + + + +static void FindCodeStart (void) +/* Find and remember the first line of actual code */ +{ + Line* L = FindHint (FirstLine, "end_of_preamble"); + FirstCode = L? L->Next : 0; +} + + + +static unsigned EstimateDataSize (Line* L, unsigned Chunk) +/* Estimate the size of a .byte, .word or .dword command */ +{ + unsigned Size = Chunk; + char* S = L->Line; + while ((S = strchr (S, ',')) != 0) { + Size += Chunk; + ++S; + } + return Size; +} + + + +static unsigned EstimateSize (Line* L) +/* Estimate the size of an instruction */ +{ + static const char* Transfers [] = { + "\ttax", + "\ttay", + "\ttsx", + "\ttxa", + "\ttya", + 0 + }; + char OpStart; + + if (L->Line [0] != '\t') { + return 0; + } + if (LineMatch (L, "\tldax\t")) { + /* Immidiate load of both, A and X */ + return 4; + } + if (LineMatch (L, "\tld")) { + OpStart = L->Line [5]; + return (OpStart == '#' || OpStart == '(')? 2 : 3; + } + if (LineMatch (L, "\tst")) { + OpStart = L->Line [5]; + return (OpStart == '(')? 2 : 3; + } + if (LineMatch (L, "\t.byte\t")) { + return EstimateDataSize (L, 1); + } + if (LineMatch (L, "\t.word\t")) { + return EstimateDataSize (L, 2); + } + if (LineMatch (L, "\t.dword\t")) { + return EstimateDataSize (L, 4); + } + if (LineMatchX (L, ShortBranches) >= 0) { + return 2; + } + if (LineMatchX (L, LongBranches) >= 0) { + return 5; + } + if (LineMatchX (L, Transfers) >= 0) { + return 1; + } + return 3; +} + + + +static void MarkCodeLines (void) +/* Mark all lines that are inside a code segment */ +{ + int InCode = 1; + Line* L = FirstCode; + while (L) { + if (IsSegHint (L)) { + InCode = IsHint (L, "seg:code"); + } else if (InCode && L->Line[0] != '\0') { + L->Flags |= OF_CODE; + L->Size = EstimateSize (L); + } + L = L->Next; + } +} + + + +static void CreateLabelList (void) +/* Create a list with pointers to local labels */ +{ + unsigned I; + Line* L; + + + /* Get the next label number. This is also the current label count. + * Make some room for more labels when optimizing code. + */ + LabelCount = GetLabel () + 100; + + /* Allocate memory for the array and clear it */ + Labels = xmalloc (LabelCount * sizeof (Line*)); + for (I = 0; I < LabelCount; ++I) { + Labels [I] = 0; + } + + /* Walk through the code and insert all label lines */ + L = FirstLine; + while (L) { + if (IsLocalLabel (L)) { + unsigned LabelNum = GetLabelNum (L->Line); + CHECK (LabelNum < LabelCount); + Labels [LabelNum] = L; + } + L = L->Next; + } +} + + + +static unsigned AllocLabel (void) +/* Get a new label. The current code does not realloc the label list, so there + * must be room enough in the current list. + */ +{ + unsigned I; + + /* Search for a free slot, start at 1, since 0 is "no label" */ + for (I = 1; I < LabelCount; ++I) { + if (Labels[I] == 0) { + /* Found a free slot */ + return I; + } + } + + /* No label space available */ + Internal ("Out of label space in the optimizer"); + + /* Not reached */ + return 0; +} + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count) +/* Get a number of code lines ignoring hints and other stuff. The function + * returns 1 if we got the lines and 0 if we are at the end of the code + * segment or if we hit a label. + */ +{ + while (Count--) { + + /* Get the next valid line */ + do { + L = NextCodeLine (L); + } while (L && IsHintLine (L)); + + /* Did we get one? */ + if (L == 0 || IsLabel (L)) { + /* Error */ + return 0; + } + + /* Remember the line */ + *Lines++ = L; + } + + /* Success */ + return 1; +} + + + +static int FindCond (const char* Suffix) +/* Map a condition suffix to a code. Return the code or -1 on failure */ +{ + int I; + + /* Linear search */ + for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) { + if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) { + /* Found */ + return I; + } + } + + /* Not found */ + return -1; +} + + + +static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP) +/* Helper function to check for a compare subroutine call followed by a + * conditional branch. Will return the condition found, or -1 if no + * or invalid condition. + */ +{ + char Cond[5]; + const char* Tail; + int C; + + /* Extract the condition from the function name. */ + if ((Cond [0] = JSR->Line [8]) == 'u') { + Cond [1] = JSR->Line [9]; + Cond [2] = JSR->Line [10]; + Cond [3] = '\0'; + Tail = JSR->Line + 11; + } else { + Cond [1] = JSR->Line [9]; + Cond [2] = '\0'; + Tail = JSR->Line + 10; + } + + /* Check if this is indeed an integer function */ + if (strcmp (Tail, "ax") != 0) { + /* No! */ + return -1; + } + + /* Get the condition code */ + C = FindCond (Cond); + if (C < 0) { + /* OOPS! */ + return -1; + } + + /* Invert the code if we jump on condition not met. */ + if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') { + /* Jumps if condition false, invert condition */ + C = CmpInvertTab [C]; + } + + /* Return the condition code */ + return C; +} + + + +static int TosCmpFunc (Line* L) +/* Check if this is a call to one of the TOS compare functions (tosgtax). + * Return the condition code or -1 if not found. + */ +{ + if (LineMatch (L, "\tjsr\ttos") && + strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) { + + /* Ok, found. Get the condition. */ + return FindCond (L->Line+8); + + } else { + + /* Not found */ + return -1; + } +} + + + +static int IsUnsignedCmp (int Code) +/* Check if this is an unsigned compare */ +{ + CHECK (Code >= 0); + return CmpSignedTab [Code] == 0; +} + + + +static void InvertZJump (Line* L) +/* Invert a jeq/jne jump */ +{ + if (L->Line [2] == 'n' && L->Line [3] == 'e') { + /* This was a bne/jne */ + L->Line [2] = 'e'; + L->Line [3] = 'q'; + } else { + /* This was (hopefully) a beq/jeq */ + L->Line [2] = 'n'; + L->Line [3] = 'e'; + } +} + + + +static int FindCmd (Line* L) +{ + int I; + + /* Search for the known patterns */ + for (I = 0; I < COUNT(CmdDesc); ++I) { + if (CmdDesc[I].FullMatch) { + if (LineFullMatch (L, CmdDesc[I].Insn)) { + /* found */ + return I; + } + } else { + if (LineMatch (L, CmdDesc[I].Insn)) { + /* found */ + return I; + } + } + } + /* Not found */ + return -1; +} + + + +static unsigned RVUInt2 (Line* L, + LineColl* LC, /* To remember visited lines */ + unsigned Used, /* Definitely used registers */ + unsigned Unused) /* Definitely unused registers */ +/* Subfunction for RegValUsed. Will be called recursively in case of branches. */ +{ + int I; + + /* Check the following instructions. We classifiy them into primary + * loads (register value not used), neutral (check next instruction), + * and unknown (assume register was used). + */ + while (1) { + + unsigned R; + + /* Get the next line and follow jumps */ + do { + + /* Handle jumps to local labels (continue there) */ + if (LineMatch (L, "\tjmp\tL")) { + /* Get the target of the jump */ + L = GetTargetLine (L->Line+5); + } + + /* Get the next instruction line */ + L = NextInstruction (L); + + /* Bail out if we're done */ + if (L == 0 || IsLabel (L)) { + /* Something is wrong */ + return REG_ALL; + } + + /* Check if we had this line already. If so, bail out, if not, + * add it to the list of known lines. + */ + if (LCHasLine (LC, L) || !LCAddLine (LC, L)) { + goto ExitPoint; + } + + } while (LineMatch (L, "\tjmp\tL")); + + /* Special handling for branches */ + if (LineMatchX (L, ShortBranches) >= 0 || + LineMatchX (L, LongBranches) >= 0) { + const char* Target = L->Line+5; + if (Target[0] == 'L') { + /* Jump to local label. Check the register usage starting at + * the branch target and at the code following the branch. + * All registers that are unused in both execution flows are + * returned as unused. + */ + unsigned U1, U2; + U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused); + U1 = RVUInt1 (L, LC, Used, Unused); + return U1 | U2; /* Used in any of the branches */ + } + } + + /* Search for the instruction in this line */ + I = FindCmd (L); + + /* If we don't find it, assume all other registers are */ + if (I < 0) { + break; + } + + /* Evaluate the use flags, check for addressing modes */ + R = CmdDesc[I].Use; + if (IsXIndAddrMode (L)) { + R |= REG_X; + } else if (IsYIndAddrMode (L)) { + R |= REG_Y; + } + if (R) { + /* Remove registers that were already new loaded */ + R &= ~Unused; + + /* Remember the remaining registers */ + Used |= R; + } + + /* Evaluate the load flags */ + R = CmdDesc[I].Load; + if (R) { + /* Remove registers that were already used */ + R &= ~Used; + + /* Remember the remaining registers */ + Unused |= R; + } + + /* If we know about all registers, bail out */ + if ((Used | Unused) == REG_ALL) { + break; + } + } + +ExitPoint: + /* Return to the caller the complement of all unused registers */ + return ~Unused & REG_ALL; +} + + + +static unsigned RVUInt1 (Line* L, + LineColl* LC, /* To remember visited lines */ + unsigned Used, /* Definitely used registers */ + unsigned Unused) /* Definitely unused registers */ +/* Subfunction for RegValUsed. Will be called recursively in case of branches. */ +{ + /* Remember the current count of the line collection */ + unsigned Count = LC->Count; + + /* Call the worker routine */ + unsigned R = RVUInt2 (L, LC, Used, Unused); + + /* Restore the old count */ + LC->Count = Count; + + /* Return the result */ + return R; +} + + + +static unsigned RegValUsed (Line* Start) +/* Check the next instructions after the one in L for register usage. If + * a register is used as an index, or in a store or other instruction, it + * is assumed to be used. If a register is loaded with a value, before it + * was used by one of the actions described above, it is assumed unused. + * If the end of the lookahead is reached, all registers that are uncertain + * are marked as used. + * The result of the search is returned. + */ +{ + unsigned R; + + /* Create a new line collection and enter the start line */ + LineColl* LC = NewLineColl (256); + LCAddLine (LC, Start); + + /* Call the recursive subfunction */ + R = RVUInt1 (Start, LC, REG_NONE, REG_NONE); + + /* Delete the line collection */ + FreeLineColl (LC); + + /* Return the registers used */ + return R; +} + + + +static int RegAUsed (Line* Start) +/* Check if the value in A is used. */ +{ + return (RegValUsed (Start) & REG_A) != 0; +} + + + +static int RegXUsed (Line* Start) +/* Check if the value in X is used. */ +{ + return (RegValUsed (Start) & REG_X) != 0; +} + + + +static int RegYUsed (Line* Start) +/* Check if the value in Y is used. */ +{ + return (RegValUsed (Start) & REG_Y) != 0; +} + + + +/*****************************************************************************/ +/* Real optimizer routines */ +/*****************************************************************************/ + + + +static void OptCompares1 (void) +/* Try to optimize the integer compare subroutines. */ +{ + Line* L2[10]; /* Line lookahead */ + int Cond; /* Condition to evaluate */ + unsigned Label; /* Local label number */ + unsigned Offs; /* Stack offset */ + Line* DelStart; /* First line to delete */ + + Line* L = FirstCode; + while (L) { + + /* Search for compares of local byte sized variables. This looks + * like: + * + * ldy #$xx + * ldx #$00 + * lda (sp),y + * jsr pushax + * ldy #$yy + * ldx #$00 + * lda (sp),y + * jsr tosugtax + * + * Replace it by a direct compare: + * + * ldy #$xx + * lda (sp),y + * ldy #$yy + * cmp (sp),y + * jsr boolugt + */ + if (LineMatch (L, "\tldy\t#$") && + GetNextCodeLines (L, L2, 7) && + LineFullMatch (L2[0], "\tldx\t#$00") && + LineFullMatch (L2[1], "\tlda\t(sp),y") && + LineFullMatch (L2[2], "\tjsr\tpushax") && + LineMatch (L2[3], "\tldy\t#$") && + LineFullMatch (L2[4], "\tldx\t#$00") && + LineFullMatch (L2[5], "\tlda\t(sp),y") && + (Cond = TosCmpFunc (L2[6])) >= 0) { + + /* Get the stack offset and correct it, since we will remove + * the pushax. + */ + Offs = GetHexNum (L2[3]->Line+7) - 2; + + /* Replace it */ + L = NewLineAfter (L, "\tlda\t(sp),y"); + L = NewLineAfter (L, "\tldy\t#$%02X", Offs); + L = NewLineAfter (L, "\tcmp\t(sp),y"); + L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]); + + /* Remove the old cruft */ + FreeLines (L2[0], L2[6]); + } + + /* Compares of byte sized global variables */ + else if (LineFullMatch (L, "\tldx\t#$00") && + GetNextCodeLines (L, L2, 5) && + LineMatch (L2[0], "\tlda\t") && + LineFullMatch (L2[1], "\tjsr\tpushax") && + LineFullMatch (L2[2], "\tldx\t#$00") && + LineMatch (L2[3], "\tlda\t") && + (Cond = TosCmpFunc (L2[4])) >= 0) { + + /* Replace it */ + if (IsXIndAddrMode (L2[0])) { + /* The load is X indirect, so we may not remove the load + * of the X register. + */ + L = L2[0]; + DelStart = L2[1]; + } else { + L = ReplaceLine (L, L2[0]->Line); + DelStart = L2[0]; + } + L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5); + L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]); + + /* Remove the old cruft */ + FreeLines (DelStart, L2[4]); + + } + + /* Byte sized local to global */ + else if (LineMatch (L, "\tldy\t#$") && + GetNextCodeLines (L, L2, 6) && + LineFullMatch (L2[0], "\tldx\t#$00") && + LineFullMatch (L2[1], "\tlda\t(sp),y") && + LineFullMatch (L2[2], "\tjsr\tpushax") && + LineFullMatch (L2[3], "\tldx\t#$00") && + LineMatch (L2[4], "\tlda\t") && + (Cond = TosCmpFunc (L2[5])) >= 0) { + + /* Replace it */ + L = NewLineAfter (L, L2[1]->Line); + L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5); + L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]); + + /* Remove the old cruft */ + FreeLines (L2[0], L2[5]); + + } + + /* Byte sized global to local */ + else if (LineFullMatch (L, "\tldx\t#$00") && + GetNextCodeLines (L, L2, 6) && + LineMatch (L2[0], "\tlda\t") && + LineFullMatch (L2[1], "\tjsr\tpushax") && + LineMatch (L2[2], "\tldy\t#$") && + LineFullMatch (L2[3], "\tldx\t#$00") && + LineFullMatch (L2[4], "\tlda\t(sp),y") && + (Cond = TosCmpFunc (L2[5])) >= 0) { + + /* Get the stack offset and correct it, since we will remove + * the pushax. + */ + Offs = GetHexNum (L2[2]->Line+7) - 2; + + /* Replace it */ + if (IsXIndAddrMode (L2[0])) { + /* The load is X indirect, so we may not remove the load + * of the X register. + */ + L = L2[0]; + DelStart = L2[1]; + } else { + L = ReplaceLine (L, L2[0]->Line); + DelStart = L2[0]; + } + L = NewLineAfter (L, "\tldy\t#$%02X", Offs); + L = NewLineAfter (L, "\tcmp\t(sp),y"); + L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]); + + /* Remove the old cruft */ + FreeLines (DelStart, L2[5]); + + } + + /* Search for unsigned compares against global variables. This looks + * like: + * + * jsr pushax + * lda _b+0 + * ldx _b+0+1 + * jsr tosugtax + * + * Replace that by a direct compare: + * + * cpx _b+0+1 + * bne L + * cmp _b+0 + * L: + * jsr boolugt + */ + else if (LineFullMatch (L, "\tjsr\tpushax") && + GetNextCodeLines (L, L2, 3) && + IsLoadAX (L2[0], L2[1]) && + (Cond = TosCmpFunc (L2[2])) >= 0 && + IsUnsignedCmp (Cond)) { + + /* Get a free label number */ + Label = AllocLabel (); + + /* Replace the code */ + L = ReplaceLine (L, "\tcpx\t%s", L2[1]->Line+5); + L = NewLineAfter (L, "\tbne\tL%04X", Label); + L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5); + L = NewLabelAfter(L, Label); + L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]); + + /* Remove the old code */ + FreeLines (L2[0], L2[2]); + + } + + L = NextCodeLine (L); + } +} + + + +static void OptDeadJumps (void) +/* Remove jumps to the following instruction */ +{ + static const char* Jumps [] = { + "\tbeq\tL", + "\tbne\tL", + "\tjeq\tL", + "\tjne\tL", + "\tjmp\tL", + 0 + }; + + Line* L = FirstCode; + while (L) { + + /* Get a pointer to the next instruction line */ + Line* NextLine = NextInstruction (L); + + /* Is this line a jump? */ + int I = LineMatchX (L, Jumps); + if (I >= 0) { + /* Yes. Get the target label, skip labels */ + Line* Target = NextInstruction (GetTargetLine (L->Line+5)); + + /* If the target label is the next line, remove the jump */ + if (Target == NextLine) { + FreeLine (L); + } + } + + /* Go to the next line */ + L = NextLine; + } +} + + + +static void OptLoads (void) +/* Remove unnecessary loads of values */ +{ + Line* L2 [10]; + + Line* L = FirstCode; + while (L) { + + /* Check for + * + * ldy #$.. + * lda (sp),y + * tax + * dey + * lda (sp),y + * jsr pushax + * + * and replace it by + * + * ldy #$.. + * jsr pushwysp + * + * or even + * + * jsr pushw0sp + * + * This change will cost 3 cycles (one additional jump inside the + * subroutine), but it saves a lot of code (6 bytes per occurrence), + * so we will accept the overhead. It may even be possible to rewrite + * the library routine to get rid of the additional overhead. + */ + if (LineMatch (L, "\tldy\t#$") && + GetNextCodeLines (L, L2, 5) && + LineFullMatch (L2 [0], "\tlda\t(sp),y") && + LineFullMatch (L2 [1], "\ttax") && + LineFullMatch (L2 [2], "\tdey") && + LineFullMatch (L2 [3], "\tlda\t(sp),y") && + LineFullMatch (L2 [4], "\tjsr\tpushax")) { + + /* Found - replace it */ + if (LineFullMatch (L, "\tldy\t#$01")) { + /* Word at offset zero */ + FreeLine (L); + L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp"); + } else { + ReplaceLine (L2 [4], "\tjsr\tpushwysp"); + } + + /* Delete the remaining lines */ + FreeLines (L2 [0], L2 [3]); + } + + /* Check for + * + * ldy #$xx + * lda (sp),y + * tax + * dey + * lda (sp),y + * ldy #$yy + * jsr ldauidx + * + * and replace it by + * + * ldy #$xx + * ldx #$yy + * jsr ldauiysp + * + * or even + * + * jsr ldaui0sp + * + * This change will cost 2 cycles, but it saves a lot of code (6 bytes + * per occurrence), so we will accept the overhead. It may even be + * possible to rewrite the library routine to get rid of the additional + * overhead. + */ + if (LineMatch (L, "\tldy\t#$") && + GetNextCodeLines (L, L2, 6) && + LineFullMatch (L2 [0], "\tlda\t(sp),y") && + LineFullMatch (L2 [1], "\ttax") && + LineFullMatch (L2 [2], "\tdey") && + LineFullMatch (L2 [3], "\tlda\t(sp),y") && + LineMatch (L2 [4], "\tldy\t#$") && + LineFullMatch (L2 [5], "\tjsr\tldauidx")) { + + /* Found - replace it */ + L2 [4]->Line [3] = 'x'; /* Change to ldx */ + if (LineFullMatch (L, "\tldy\t#$01")) { + /* Word at offset zero */ + FreeLine (L); + L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp"); + } else { + ReplaceLine (L2 [5], "\tjsr\tldauiysp"); + } + + /* Delete the remaining lines */ + FreeLines (L2 [0], L2 [3]); + } + + /* Search for: + * + * lda (sp),y + * jsr pusha + * + * And replace by + * + * jsr pushaysp + */ + if (LineFullMatch (L, "\tlda\t(sp),y") && + GetNextCodeLines (L, L2, 1) && + LineFullMatch (L2 [0], "\tjsr\tpusha")) { + + /* Found, replace it */ + L = ReplaceLine (L, "\tjsr\tpushaysp"); + FreeLine (L2 [0]); + } + + /* All other patterns start with this one: */ + if (!LineFullMatch (L, "\tldx\t#$00")) { + /* Next line */ + goto NextLine; + } + + /* Search for: + * + * ldx #$00 + * jsr pushax + * + * and replace it by: + * + * jsr pusha0 + * + */ + if (GetNextCodeLines (L, L2, 1) && + LineFullMatch (L2 [0], "\tjsr\tpushax")) { + + /* Replace the subroutine call */ + L = ReplaceLine (L, "\tjsr\tpusha0"); + + /* Remove the unnecessary line */ + FreeLine (L2[0]); + } + + /* Search for: + * + * ldx #$00 + * lda ... + * jsr pushax + * + * and replace it by: + * + * lda ... + * jsr pusha0 + * + */ + else if (GetNextCodeLines (L, L2, 2) && + LineMatch (L2 [0], "\tlda\t") && + LineFullMatch (L2 [1], "\tjsr\tpushax")) { + + /* Be sure, X is not used in the load */ + if (NoXIndAddrMode (L2 [0])) { + + /* Replace the subroutine call */ + L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0"); + + /* Remove the unnecessary load */ + FreeLine (L); + + /* L must be valid */ + L = L2 [0]; + } + + } + + /* Search for: + * + * ldx #$00 + * lda ... + * cmp #$.. + * + * and replace it by: + * + * lda ... + * cmp #$.. + */ + else if (GetNextCodeLines (L, L2, 2) && + LineMatch (L2 [0], "\tlda\t") && + LineMatch (L2 [1], "\tcmp\t#$")) { + + /* Be sure, X is not used in the load */ + if (NoXIndAddrMode (L2 [0])) { + + /* Remove the unnecessary load */ + FreeLine (L); + + /* L must be valid */ + L = L2 [0]; + } + } + + /* Search for: + * + * ldx #$00 + * lda ... + * jsr bnega + * + * and replace it by: + * + * lda ... + * jsr bnega + */ + else if (GetNextCodeLines (L, L2, 2) && + LineMatch (L2 [0], "\tlda\t") && + LineFullMatch (L2 [1], "\tjsr\tbnega")) { + + /* Be sure, X is not used in the load */ + if (NoXIndAddrMode (L2 [0])) { + + /* Remove the unnecessary load */ + FreeLine (L); + + /* L must be valid */ + L = L2 [0]; + } + } + +NextLine: + /* Go to the next line */ + L = NextCodeLine (L); + } +} + + + +static void OptRegLoads (void) +/* Remove unnecessary loads of registers */ +{ + unsigned Deletions; + Line* L; + Line* Lx; + + /* Repeat this until there is nothing more to delete */ + do { + Deletions = 0; + L = FirstCode; + while (L) { + + int Delete = 0; + + /* Search for a load of X and check if the value is used later */ + if (LineMatch (L, "\tldx\t") && + !RegXUsed (L) && + !IsCondJump (NextInstruction (L))) { + + /* Remember to delete this line */ + Delete = 1; + } + + /* Search for a load of A and check if the value is used later */ + else if (LineMatch (L, "\tlda\t") && + !RegAUsed (L) && + !IsCondJump (NextInstruction (L))) { + + /* Remember to delete this line */ + Delete = 1; + } + + /* Search for a load of Y and check if the value is used later */ + else if (LineMatch (L, "\tldy\t") && + !RegYUsed (L) && + !IsCondJump (NextInstruction (L))) { + + /* Remember to delete this line */ + Delete = 1; + } + + /* Go to the next line, delete the current if requested */ + Lx = L; + L = NextCodeLine (L); + if (Delete) { + FreeLine (Lx); + ++Deletions; + } + } + } while (Deletions > 0); +} + + + +static int OptPtrOps1 (Line** Start) +/* Optimize several pointer and array constructs - subfunction 1 */ +{ + Line* L2[15]; + Line** L3; + unsigned NeedLoad; + unsigned LinesToRemove; + unsigned Inc; + unsigned Done; + unsigned Offs; + + /* Use a local variable for the working line */ + Line* L = *Start; + + /* Search for (23B/XXT) + * + * lda _b+0 + * ldx _b+0+1 + * sta regsave + * stx regsave+1 + * jsr incax1 + * sta _b+0 + * stx _b+0+1 + * lda regsave + * ldx regsave+1 + * + * and replace it by something like (24B/26T) + * + * lda _b+0 + * sta regsave + * clc + * adc #$01 + * sta _b+0 + * lda _b+0+1 + * sta regsave+1 + * adc #$00 + * sta _b+0+1 + * tax + * lda regsave + */ + if (!LineMatch (L, "\tlda\t") || + !GetNextCodeLines (L, L2, 4) || + !IsLoadAX (L, L2 [0]) || + !LineFullMatch (L2[1], "\tsta\tregsave") || + !LineFullMatch (L2[2], "\tstx\tregsave+1")) { + + /* Not found */ + return 0; + } + + /* */ + if (LineMatch (L2[3], "\tjsr\tincax")) { + /* Get next code lines */ + if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) { + /* Cannot get lines */ + return 0; + } + Inc = GetHexNum (L2[3]->Line+10); + L3 = &L2[4]; + LinesToRemove = 8; + } else { + /* Get next code lines */ + if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) { + /* Cannot get lines */ + return 0; + } + if (LineFullMatch (L2[3], "\tclc") && + LineMatch (L2[4], "\tadc\t#$") && + LineFullMatch (L2[5], "\tbcc\t*+3") && + LineFullMatch (L2[6], "\tinx")) { + /* Inlined increment */ + Inc = GetHexNum (L2[4]->Line+7); + L3 = &L2[7]; + LinesToRemove = 11; + } else { + /* Not found */ + return 0; + } + } + + /* Check for the remainder */ + if (!LineMatch (L3[0], "\tsta\t") || + strcmp (L3[0]->Line+5, L->Line+5) != 0 || + !LineMatch (L3[1], "\tstx\t") || + strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0 || + !LineFullMatch (L3[2], "\tlda\tregsave") || + !LineFullMatch (L3[3], "\tldx\tregsave+1")) { + + /* Not found */ + return 0; + } + + /* Check if AX is actually used following the code above. If not, + * we don't need to load A/X from regsave. Since X will never by + * used without A, check just for A. + */ + NeedLoad = 1; + if (!RegAUsed (L3[3])) { + /* We don't need to load regsave */ + NeedLoad = 0; + } + + /* Special code for register variables */ + Done = 0; + if (LineMatch (L, "\tlda\tregbank+") && + GetNextCodeLines (L3[3], &L3[4], 1) && + Inc == 1) { + + /* Remember the offset into the register bank */ + char Reg[20]; + strcpy (Reg, L->Line+5); + + /* Check for several special sequences */ + if (LineFullMatch (L3[4], "\tjsr\tldaui")) { + /* Load char indirect */ + L = ReplaceLine (L, "\tldx\t#$00"); + L = NewLineAfter (L, "\tlda\t(%s,x)", Reg); + L = NewLineAfter (L, "\tinc\t%s", Reg); + L = NewLineAfter (L, "\tbne\t*+4"); + L = NewLineAfter (L, "\tinc\t%s+1", Reg); + Done = 1; + ++LinesToRemove; + } else if (LineFullMatch (L3[4], "\tsta\tptr1") && + GetNextCodeLines (L3[4], &L3[5], 3) && + LineFullMatch (L3[5], "\tstx\tptr1+1") && + LineFullMatch (L3[6], "\tldx\t#$00") && + LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) { + + /* Load char indirect, inlined */ + L = ReplaceLine (L, "\tldx\t#$00"); + L = NewLineAfter (L, "\tlda\t(%s,x)", Reg); + L = NewLineAfter (L, "\tinc\t%s", Reg); + L = NewLineAfter (L, "\tbne\t*+4"); + L = NewLineAfter (L, "\tinc\t%s+1", Reg); + Done = 1; + LinesToRemove += 4; + + } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) { + if (GetNextCodeLines (L3[4], &L3[5], 2) && + LineMatch (L3[5], "\tlda\t") && + LineFullMatch (L3[6], "\tjsr\tstaspp")) { + + /* Store to pointer */ + L = ReplaceLine (L, L3[5]->Line); + L = NewLineAfter (L, "\tldy\t#$00"); + L = NewLineAfter (L, "\tsta\t(%s),y", Reg); + L = NewLineAfter (L, "\tinc\t%s", Reg); + L = NewLineAfter (L, "\tbne\t*+4"); + L = NewLineAfter (L, "\tinc\t%s+1", Reg); + + Done = 1; + LinesToRemove += 3; + + } else if (GetNextCodeLines (L3[4], &L3[5], 3) && + LineMatch (L3[5], "\tldy\t#$") && + LineFullMatch (L3[6], "\tlda\t(sp),y") && + LineFullMatch (L3[7], "\tjsr\tstaspp")) { + + /* Beware: We have to correct the stack offset, since we will + * remove the pushax instruction! + */ + Offs = GetHexNum (L3[5]->Line+7) - 2; + + /* Store to pointer */ + L = ReplaceLine (L, "\tldy\t#$%02X", Offs); + L = NewLineAfter (L, "\tldx\t#$00"); + L = NewLineAfter (L, "\tlda\t(sp),y"); + L = NewLineAfter (L, "\tsta\t(%s,x)", Reg); + L = NewLineAfter (L, "\tinc\t%s", Reg); + L = NewLineAfter (L, "\tbne\t*+4"); + L = NewLineAfter (L, "\tinc\t%s+1", Reg); + + Done = 1; + LinesToRemove += 4; + } + } + } + + if (Done == 0) { + + /* No register variable - insert the first part of the code */ + if (NeedLoad) { + L = NewLineAfter (L, "\tsta\tptr1"); + } + L = NewLineAfter (L, "\tclc"); + L = NewLineAfter (L, "\tadc\t#$%02X", Inc); + L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5); + L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5); + if (NeedLoad) { + L = NewLineAfter (L, "\tsta\tptr1+1"); + } + L = NewLineAfter (L, "\tadc\t#$00"); + L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5); + + /* Check if we must really load the old value into a/x or if the + * code may be replaced by something else. + */ + if (GetNextCodeLines (L3[3], &L3[4], 1)) { + if (LineFullMatch (L3[4], "\tjsr\tldaui")) { + /* Load char indirect */ + L = NewLineAfter (L, "\tldx\t#$00"); + L = NewLineAfter (L, "\tlda\t(ptr1,x)"); + NeedLoad = 0; + ++LinesToRemove; + } else if (LineFullMatch (L3[4], "\tsta\tptr1") && + GetNextCodeLines (L3[4], &L3[5], 3) && + LineFullMatch (L3[5], "\tstx\tptr1+1") && + LineFullMatch (L3[6], "\tldx\t#$00") && + LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) { + + /* Load char indirect, inlined */ + L = NewLineAfter (L, "\tldx\t#$00"); + L = NewLineAfter (L, "\tlda\t(ptr1,x)"); + NeedLoad = 0; + LinesToRemove += 4; + + } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) { + /* Load word indirect */ + L = NewLineAfter (L, "\tldy\t#$01"); + L = NewLineAfter (L, "\tlda\t(ptr1),y"); + L = NewLineAfter (L, "\ttax"); + L = NewLineAfter (L, "\tdey"); + L = NewLineAfter (L, "\tlda\t(ptr1),y"); + NeedLoad = 0; + ++LinesToRemove; + + } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) { + if (GetNextCodeLines (L3[4], &L3[5], 2) && + LineMatch (L3[5], "\tlda\t") && + LineFullMatch (L3[6], "\tjsr\tstaspp")) { + + /* Store to pointer */ + L = NewLineAfter (L, L3[5]->Line); + L = NewLineAfter (L, "\tldy\t#$00"); + L = NewLineAfter (L, "\tsta\t(ptr1),y"); + + NeedLoad = 0; + LinesToRemove += 3; + } else if (GetNextCodeLines (L3[4], &L3[5], 3) && + LineMatch (L3[5], "\tldy\t#$") && + LineFullMatch (L3[6], "\tlda\t(sp),y") && + LineFullMatch (L3[7], "\tjsr\tstaspp")) { + + /* Beware: We have to correct the stack offset, since we will + * remove the pushax instruction! + */ + sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2); + + /* Store to pointer */ + L = NewLineAfter (L, L3[5]->Line); + L = NewLineAfter (L, L3[6]->Line); + L = NewLineAfter (L, "\tldy\t#$00"); + L = NewLineAfter (L, "\tsta\t(ptr1),y"); + + NeedLoad = 0; + LinesToRemove += 4; + } + + } + } + + /* If we need to load a/x, add the code */ + if (NeedLoad) { + L = NewLineAfter (L, "\ttax"); + L = NewLineAfter (L, "\tlda\tptr1"); + } + } + + /* Remove the code that is no longer needed */ + FreeLines (L2[0], L2[LinesToRemove-1]); + + /* Return the new line and success */ + *Start = NextCodeLine (L); + return 1; +} + + + +static int OptPtrOps2 (Line** Start) +/* Optimize several pointer and array constructs - subfunction 2 */ +{ + Line* L2[25]; + Line** L3; + unsigned NeedLoad; + unsigned LinesToRemove; + unsigned Inc; + unsigned Offs; + + + /* Use a local variable for the working line */ + Line* L = *Start; + + /* Same as subfunction 1 but for local variables. */ + if (LineMatch (L, "\tldy\t#$") == 0) { + return 0; + } + + /* Get the stack offset. The offset points to the high byte, correct that. */ + Offs = GetHexNum (L->Line+7) - 1; + + /* Check for the actual sequences */ + if (GetNextCodeLines (L, L2, 7) && + LineFullMatch (L2[0], "\tjsr\tldaxysp") && + LineFullMatch (L2[1], "\tsta\tregsave") && + LineFullMatch (L2[2], "\tstx\tregsave+1") && + LineMatch (L2[3], "\tjsr\tincax")) { + + /* Non inlined version */ + Inc = GetHexNum (L2[3]->Line+10); + + /* Check for stack offset zero */ + if (LineFullMatch (L2[4], "\tjsr\tstax0sp") && + LineFullMatch (L2[5], "\tlda\tregsave") && + LineFullMatch (L2[6], "\tldx\tregsave+1")) { + + LinesToRemove = 7; + + } else if (GetNextCodeLines (L2[6], &L2[7], 1) && + LineMatch (L2[4], "\tldy\t#$") && + GetHexNum (L2[4]->Line+7) == Offs && + LineFullMatch (L2[5], "\tjsr\tstaxysp") && + LineFullMatch (L2[6], "\tlda\tregsave") && + LineFullMatch (L2[7], "\tldx\tregsave+1")) { + + LinesToRemove = 8; + + } else { + /* Not found */ + return 0; + } + + } else if (GetNextCodeLines (L, L2, 13) && + LineFullMatch (L2[0], "\tlda\t(sp),y") && + LineFullMatch (L2[1], "\ttax") && + LineFullMatch (L2[2], "\tdey") && + LineFullMatch (L2[3], "\tlda\t(sp),y") && + LineFullMatch (L2[4], "\tsta\tregsave") && + LineFullMatch (L2[5], "\tstx\tregsave+1") && + LineFullMatch (L2[6], "\tclc") && + LineMatch (L2[7], "\tadc\t#$") && + LineFullMatch (L2[8], "\tbcc\t*+3") && + LineFullMatch (L2[9], "\tinx")) { + + /* Inlined version */ + Inc = GetHexNum (L2[7]->Line+7); + + /* Check for stack offset zero */ + if (LineFullMatch (L2[10], "\tjsr\tstax0sp") && + LineFullMatch (L2[11], "\tlda\tregsave") && + LineFullMatch (L2[12], "\tldx\tregsave+1")) { + + LinesToRemove = 13; + + } else if (GetNextCodeLines (L2[12], &L2[13], 1) && + LineMatch (L2[10], "\tldy\t#$") && + GetHexNum (L2[10]->Line+7) == Offs && + LineFullMatch (L2[11], "\tjsr\tstaxysp") && + LineFullMatch (L2[12], "\tlda\tregsave") && + LineFullMatch (L2[13], "\tldx\tregsave+1")) { + + LinesToRemove = 14; + + } else { + /* Not found */ + return 0; + } + } else { + /* Not found */ + return 0; + } + + /* Get a pointer to the last line of the preceding sequence */ + L3 = &L2[LinesToRemove-1]; + + /* Check if AX is actually used following the code above. If not, + * we don't need to load A/X from regsave. Since X will never by + * used without A, check just for A. + */ + NeedLoad = 1; + if (!RegAUsed (L3[0])) { + /* We don't need to load regsave */ + NeedLoad = 0; + } + + /* Replace the ldy instruction, offset must point to the low byte */ + sprintf (L->Line+7, "%02X", Offs); + + /* Insert the first part of the code */ + L = NewLineAfter (L, "\tlda\t(sp),y"); + if (NeedLoad) { + L = NewLineAfter (L, "\tsta\tptr1"); + } + L = NewLineAfter (L, "\tclc"); + L = NewLineAfter (L, "\tadc\t#$%02X", Inc); + L = NewLineAfter (L, "\tsta\t(sp),y"); + L = NewLineAfter (L, "\tiny"); + L = NewLineAfter (L, "\tlda\t(sp),y"); + if (NeedLoad) { + L = NewLineAfter (L, "\tsta\tptr1+1"); + } + L = NewLineAfter (L, "\tadc\t#$00"); + L = NewLineAfter (L, "\tsta\t(sp),y"); + + /* Check if we must really load the old value into a/x or if the + * code may be replaced by something else. + */ + if (GetNextCodeLines (L3[0], &L3[1], 1)) { + if (LineFullMatch (L3[1], "\tjsr\tldaui")) { + /* Load char indirect */ + L = NewLineAfter (L, "\tldx\t#$00"); + L = NewLineAfter (L, "\tlda\t(ptr1,x)"); + NeedLoad = 0; + ++LinesToRemove; + } else if (LineFullMatch (L3[1], "\tsta\tptr1") && + GetNextCodeLines (L3[1], &L3[2], 3) && + LineFullMatch (L3[2], "\tstx\tptr1+1") && + LineFullMatch (L3[3], "\tldx\t#$00") && + LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) { + + /* Load char indirect, inlined */ + L = NewLineAfter (L, "\tldx\t#$00"); + L = NewLineAfter (L, "\tlda\t(ptr1,x)"); + NeedLoad = 0; + LinesToRemove += 4; + + } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) { + /* Load word indirect */ + L = NewLineAfter (L, "\tldy\t#$01"); + L = NewLineAfter (L, "\tlda\t(ptr1),y"); + L = NewLineAfter (L, "\ttax"); + L = NewLineAfter (L, "\tdey"); + L = NewLineAfter (L, "\tlda\t(ptr1),y"); + NeedLoad = 0; + ++LinesToRemove; + + } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) { + if (GetNextCodeLines (L3[1], &L3[2], 2) && + LineMatch (L3[2], "\tlda\t") && + LineFullMatch (L3[3], "\tjsr\tstaspp")) { + + /* Store to pointer */ + L = NewLineAfter (L, L3[2]->Line); + L = NewLineAfter (L, "\tldy\t#$00"); + L = NewLineAfter (L, "\tsta\t(ptr1),y"); + + NeedLoad = 0; + LinesToRemove += 3; + } else if (GetNextCodeLines (L3[1], &L3[2], 3) && + LineMatch (L3[2], "\tldy\t#$") && + LineFullMatch (L3[3], "\tlda\t(sp),y") && + LineFullMatch (L3[4], "\tjsr\tstaspp")) { + + /* Beware: We have to correct the stack offset, since we will + * remove the pushax instruction! + */ + sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2); + + /* Store to pointer */ + L = NewLineAfter (L, L3[2]->Line); + L = NewLineAfter (L, L3[3]->Line); + L = NewLineAfter (L, "\tldy\t#$00"); + L = NewLineAfter (L, "\tsta\t(ptr1),y"); + + NeedLoad = 0; + LinesToRemove += 4; + } + } + + } + + /* If we need to load a/x, add the code */ + if (NeedLoad) { + L = NewLineAfter (L, "\ttax"); + L = NewLineAfter (L, "\tlda\tptr1"); + } + + /* Remove the code that is no longer needed */ + FreeLines (L2[0], L2[LinesToRemove-1]); + + /* Return the new line and success */ + *Start = NextCodeLine (L); + return 1; +} + + + +static void OptPtrOps (void) +/* Optimize several pointer and array constructs */ +{ + Line* L2 [10]; + + Line* L = FirstCode; + while (L) { + + if (OptPtrOps1 (&L)) { + continue; + } else if (OptPtrOps2 (&L)) { + continue; + } + + /* Search for the following sequence: + * + * lda regsave + * ldx regsave+1 + * jsr pushax + * lda #$.. + * jsr staspp + * + * and replace it by: + * + * lda #$.. + * ldy #$00 + * sta (regsave),y + * + */ + else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */ + GetNextCodeLines (L, L2, 4) && /* Fetch next lines */ + LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */ + LineFullMatch (L2 [1], "\tjsr\tpushax") && + LineMatch (L2 [2], "\tlda\t#$") && + LineFullMatch (L2 [3], "\tjsr\tstaspp")) { + + /* Found the sequence, replace it */ + L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */ + L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00"); + L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y"); + + /* Free the remaining lines */ + FreeLines (L2 [2], L2 [3]); + } + + /* Search for the following sequence: + * + * lda regsave + * ldx regsave+1 + * jsr ldaui + * + * and replace it by: + * + * ldx #$00 + * lda (regsave,x) + * + */ + else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */ + GetNextCodeLines (L, L2, 2) && /* Fetch next lines */ + LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */ + LineFullMatch (L2 [1], "\tjsr\tldaui")) { + + /* Found the sequence, replace it */ + L = ReplaceLine (L, "\tldx\t#$00"); + L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)"); + + /* Free the remaining lines */ + FreeLine (L2 [1]); + } + + /* + * Search for the following sequence: + * + * lda regsave + * ldx regsave+1 + * jsr pushax + * ldx #$high + * lda #$low + * jsr staxspp + * + * and replace it by: + * + * ldy #$01 + * lda #$high + * sta (regsave),y + * tax + * dey + * lda #$low + * sta (regsave),y + * + */ + else if (LineFullMatch (L, "\tlda\tregsave") && + GetNextCodeLines (L, L2, 5) && + LineFullMatch (L2 [0], "\tldx\tregsave+1") && + LineFullMatch (L2 [1], "\tjsr\tpushax") && + LineMatch (L2 [2], "\tldx\t#$") && + LineMatch (L2 [3], "\tlda\t#$") && + LineFullMatch (L2 [4], "\tjsr\tstaxspp")) { + + /* Found the sequence, replace it */ + L = ReplaceLine (L, "\tldy\t#$01"); + L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line); + L2 [0]->Line [3] = 'a'; + L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y"); + L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line); + L2 [2] = ReplaceLine (L2 [2], "\ttax"); + L2 [3] = ReplaceLine (L2 [3], "\tdey"); + L = NewLineAfter (L2 [4], "\tsta\t(regsave),y"); + } + + /* + * Search for the following sequence: + * + * lda regsave + * ldx regsave+1 + * sta ptr1 + * stx ptr1+1 + * ldx #$00 + * lda (ptr1,x) + * + * and replace it by: + * + * ldx #$00 + * lda (regsave,x) + * + */ + else if (LineFullMatch (L, "\tlda\tregsave") && + GetNextCodeLines (L, L2, 5) && + LineFullMatch (L2 [0], "\tldx\tregsave+1") && + LineFullMatch (L2 [1], "\tsta\tptr1") && + LineFullMatch (L2 [2], "\tstx\tptr1+1") && + LineFullMatch (L2 [3], "\tldx\t#$00") && + LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) { + + /* Found the sequence, replace it */ + L = ReplaceLine (L, "\tldx\t#$00"); + L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)"); + + /* Remove the remaining lines */ + FreeLines (L2 [1], L2 [4]); + } + + /* Search for the following sequence: + * + * jsr pushax + * lda ... + * jsr staspp + * + * and replace it by: + * + * sta ptr1 + * stx ptr1+1 + * lda ... + * ldy #$00 + * sta (ptr1),y + * + */ + else if (LineFullMatch (L, "\tjsr\tpushax") && + GetNextCodeLines (L, L2, 2) && + LineMatch (L2 [0], "\tlda\t") && + LineFullMatch (L2 [1], "\tjsr\tstaspp")) { + + /* Found the sequence, replace it */ + L = ReplaceLine (L, "\tsta\tptr1"); + L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */ + L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1"); + L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00"); + L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y"); + } + + /* Search for the following sequence: + * + * jsr pushax + * lda ... + * ldy #$nn + * jsr staspidx + * + * and replace it by: + * + * sta ptr1 + * stx ptr1+1 + * lda ... + * ldy #$nn + * sta (ptr1),y + * + */ + else if (LineFullMatch (L, "\tjsr\tpushax") && + GetNextCodeLines (L, L2, 3) && + LineMatch (L2 [0], "\tlda\t") && + LineMatch (L2 [1], "\tldy\t#$") && + LineFullMatch (L2 [2], "\tjsr\tstaspidx")) { + + /* Found the sequence, replace it */ + L = ReplaceLine (L, "\tsta\tptr1"); + L = NewLineAfter (L, "\tstx\tptr1+1"); + L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y"); + } + + /* Search for the following sequence: + * + * jsr pushax + * ldy #$.. + * lda (sp),y + * jsr staspp + * + * and replace it by: + * + * sta ptr1 + * stx ptr1+1 + * ldy #$.. + * lda (sp),y + * ldy #$00 + * sta (ptr1),y + * + * Beware: Since we remove a call to a function that changes the stack + * pointer, we have to adjust the stack address for the lda. + * + */ + else if (LineFullMatch (L, "\tjsr\tpushax") && + GetNextCodeLines (L, L2, 3) && + LineMatch (L2 [0], "\tldy\t#$") && + LineFullMatch (L2 [1], "\tlda\t(sp),y") && + LineFullMatch (L2 [2], "\tjsr\tstaspp")) { + + /* Found the sequence, replace it. First create a new load + * instruction for the changed stack offset. + */ + char Buf [30]; + sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2); + L = ReplaceLine (L, "\tsta\tptr1"); + L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */ + L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1"); + L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y"); + L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00"); + L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y"); + } + + /* Search for the following sequence: + * + * jsr pushax + * ldy #$nn + * lda (sp),y + * ldy #$mm + * jsr staspidx + * + * and replace it by: + * + * sta ptr1 + * stx ptr1+1 + * ldy #$nn + * lda (sp),y + * ldy #$mm + * sta (ptr1),y + * + * Beware: Since we remove a call to a function that changes the stack + * pointer, we have to adjust the stack address for the lda. + * + */ + else if (LineFullMatch (L, "\tjsr\tpushax") && + GetNextCodeLines (L, L2, 4) && + LineMatch (L2 [0], "\tldy\t#$") && + LineFullMatch (L2 [1], "\tlda\t(sp),y") && + LineMatch (L2 [2], "\tldy\t#$") && + LineFullMatch (L2 [3], "\tjsr\tstaspidx")) { + + /* Found the sequence, replace it. First create a new load + * instruction for the changed stack offset. + */ + char Buf [30]; + sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2); + L = ReplaceLine (L, "\tsta\tptr1"); + L = NewLineAfter (L, "\tstx\tptr1+1"); + L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */ + L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y"); + } + + /* Search for the following sequence: + * + * ldax _label+0 + * ldy #$.. + * clc + * adc (sp),y + * bcc *+3 + * inx + * sta ptr1 + * stx ptr1+1 + * ldx #$00 + * lda (ptr1,x) + * + * and replace it by: + * + * ldy #$.. + * lda (sp),y + * tay + * ldx #$00 + * lda _label+0,y + * + * The load of X may be omitted if X is not used below. + */ + else if (LineMatch (L, "\tldax\t_") && + GetNextCodeLines (L, L2, 9) && + LineMatch (L2 [0], "\tldy\t#$") && + LineFullMatch (L2 [1], "\tclc") && + LineFullMatch (L2 [2], "\tadc\t(sp),y") && + LineFullMatch (L2 [3], "\tbcc\t*+3") && + LineFullMatch (L2 [4], "\tinx") && + LineFullMatch (L2 [5], "\tsta\tptr1") && + LineFullMatch (L2 [6], "\tstx\tptr1+1") && + LineFullMatch (L2 [7], "\tldx\t#$00") && + LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) { + + /* Found the sequence, replace it */ + char Label [256]; + strcpy (Label, L->Line + 6); /* Remember the label */ + L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */ + L = NewLineAfter (L, "\tlda\t(sp),y"); + L = NewLineAfter (L, "\ttay"); + if (RegXUsed (L2[8])) { + L = NewLineAfter (L, "\tldx\t#$00"); + } + L = NewLineAfter (L, "\tlda\t%s,y", Label); + + /* Remove the remaining stuff. There may be hints between the + * instructions, remove them too + */ + FreeLines (L2[0], L2 [8]); + + } + + /* Check for + * + * ldy #$xx + * lda (sp),y + * tax + * dey + * lda (sp),y + * ldy #$yy + * jsr ldauidx + * + * and replace it by + * + * ldy #$xx + * ldx #$yy + * jsr ldauiysp + * + * or even + * + * jsr ldaui0sp + * + * This change will cost 2 cycles, but it saves a lot of code (6 bytes + * per occurrence), so we will accept the overhead. It may even be + * possible to rewrite the library routine to get rid of the additional + * overhead. + */ + else if (LineMatch (L, "\tldy\t#$") && + GetNextCodeLines (L, L2, 6) && + LineFullMatch (L2 [0], "\tlda\t(sp),y") && + LineFullMatch (L2 [1], "\ttax") && + LineFullMatch (L2 [2], "\tdey") && + LineFullMatch (L2 [3], "\tlda\t(sp),y") && + LineMatch (L2 [4], "\tldy\t#$") && + LineFullMatch (L2 [5], "\tjsr\tldauidx")) { + + /* Found - replace it */ + L2 [4]->Line [3] = 'x'; /* Change to ldx */ + if (LineFullMatch (L, "\tldy\t#$01")) { + /* Word at offset zero */ + FreeLine (L); + L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp"); + } else { + ReplaceLine (L2 [5], "\tjsr\tldauiysp"); + } + + /* Delete the remaining lines */ + FreeLines (L2 [0], L2 [3]); + } + + /* Check for + * + * ldy #$xx + * lda (sp),y + * tax + * dey + * lda (sp),y + * sta ptr1 + * stx ptr1+1 + * ldx #$00 + * lda (ptr1,x) + * + * and replace it by + * + * ldy #$xx + * jsr ldau0ysp + * + * or even + * + * jsr ldau00sp + * + * This change will has an overhead of 10 cycles, but it saves 11(!) + * bytes per invocation. Maybe we should apply only if FavourSize is + * true? + */ + else if (LineMatch (L, "\tldy\t#$") && + GetNextCodeLines (L, L2, 8) && + LineFullMatch (L2 [0], "\tlda\t(sp),y") && + LineFullMatch (L2 [1], "\ttax") && + LineFullMatch (L2 [2], "\tdey") && + LineFullMatch (L2 [3], "\tlda\t(sp),y") && + LineFullMatch (L2 [4], "\tsta\tptr1") && + LineFullMatch (L2 [5], "\tstx\tptr1+1") && + LineFullMatch (L2 [6], "\tldx\t#$00") && + LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) { + + /* Found - replace it */ + if (LineFullMatch (L, "\tldy\t#$01")) { + /* Word at offset zero */ + FreeLine (L); + L = ReplaceLine (L2 [0], "\tjsr\tldau00sp"); + } else { + ReplaceLine (L2 [0], "\tjsr\tldau0ysp"); + } + + /* Delete the remaining lines */ + FreeLines (L2 [1], L2 [7]); + } + + /* Next Line */ + L = NextCodeLine (L); + } +} + + + +static void OptRegVars (void) +/* Optimize register variable uses */ +{ + Line* L2 [10]; + + Line* L = FirstCode; + while (L) { + + /* Search for the following sequence: + * + * lda regbank+n + * ldx regbank+n+1 + * jsr ldaui + * + * and replace it by: + * + * ldx #$00 + * lda (regbank+n,x) + * + */ + if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */ + GetNextCodeLines (L, L2, 2) && /* Fetch next lines */ + LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */ + LineFullMatch (L2 [1], "\tjsr\tldaui") && + L->Line [13] == L2 [0]->Line [13] && /* Offset equal */ + strcmp (L2 [0]->Line + 14, "+1") == 0) { + + char Buf [100]; + sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5); + + /* Found the sequence, replace it */ + L = ReplaceLine (L, "\tldx\t#$00"); + L2 [0] = ReplaceLine (L2 [0], Buf); + + /* Free the remaining lines */ + FreeLine (L2 [1]); + } + + /* Search for the following sequence: + * + * lda regbank+n + * ldx regbank+n+1 + * sta ptr1 + * stx ptr1+1 + * ldx #$00 + * lda (ptr1,x) + * + * and replace it by: + * + * ldx #$00 + * lda (regbank+n,x) + * + */ + else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */ + GetNextCodeLines (L, L2, 5) && /* Fetch next lines */ + LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */ + L->Line [13] == L2 [0]->Line [13] && /* Offset equal */ + strcmp (L2 [0]->Line + 14, "+1") == 0 && + LineFullMatch (L2 [1], "\tsta\tptr1") && + LineFullMatch (L2 [2], "\tstx\tptr1+1") && + LineFullMatch (L2 [3], "\tldx\t#$00") && + LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) { + + char Buf [100]; + sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5); + + /* Found the sequence, replace it */ + L = ReplaceLine (L, "\tldx\t#$00"); + L2 [0] = ReplaceLine (L2 [0], Buf); + + /* Free the remaining lines */ + FreeLines (L2 [1], L2 [4]); + } + + /* Search for the following sequence: + * + * lda regbank+n + * ldx regbank+n+1 + * ldy #$.. + * jsr ldauidx + * + * and replace it by: + * + * ldy #$.. + * ldx #$00 + * lda (regbank+n),y + * + */ + else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */ + GetNextCodeLines (L, L2, 3) && /* Fetch next lines */ + LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */ + L->Line [13] == L2 [0]->Line [13] && /* Offset equal */ + strcmp (L2 [0]->Line + 14, "+1") == 0 && + LineMatch (L2 [1], "\tldy\t#$") && + LineFullMatch (L2 [2], "\tjsr\tldauidx")) { + + char Buf [100]; + sprintf (Buf, "\tlda\t(%s),y", L->Line + 5); + + /* Found the sequence, replace it */ + L = ReplaceLine (L, L2 [1]->Line); + L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00"); + L2 [1] = ReplaceLine (L2 [1], Buf); + + /* Free the remaining lines */ + FreeLine (L2 [2]); + } + + /* Search for the following sequence: + * + * lda regbank+n + * ldx regbank+n+1 + * sta ptr1 + * stx ptr1+1 + * lda ... + * ldy #$mm + * sta (ptr1),y + * + * and replace it by: + * + * lda ... + * ldy #$mm + * sta (regbank+n),y + * + * The source form is not generated by the parser but by the optimizer. + */ + else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */ + GetNextCodeLines (L, L2, 6) && /* Fetch next lines */ + LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */ + L->Line [13] == L2 [0]->Line [13] && /* Offset equal */ + strcmp (L2 [0]->Line + 14, "+1") == 0 && + LineFullMatch (L2 [1], "\tsta\tptr1") && + LineFullMatch (L2 [2], "\tstx\tptr1+1") && + LineMatch (L2 [3], "\tlda\t") && + LineMatch (L2 [4], "\tldy\t#$") && + LineMatch (L2 [5], "\tsta\t(ptr1),y")) { + + char Buf [100]; + sprintf (Buf, "\tsta\t(%s),y", L->Line + 5); + + /* Found the sequence, replace it */ + L2 [5] = ReplaceLine (L2 [5], Buf); + + /* Free the remaining lines */ + FreeLines (L, L2 [2]); + + /* Make the line pointer valid again */ + L = L2 [5]; + } + + /* Search for the following sequence: + * + * lda regbank+n + * ldx regbank+n+1 + * sta ptr1 + * stx ptr1+1 + * ldy #$mm + * lda (sp),y + * ldy #$ll + * sta (ptr1),y + * + * and replace it by: + * + * ldy #$mm + * lda (sp),y + * ldy #$ll + * sta (regbank+n),y + * + * The source form is not generated by the parser but by the optimizer. + */ + else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */ + GetNextCodeLines (L, L2, 7) && /* Fetch next lines */ + LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */ + L->Line [13] == L2 [0]->Line [13] && /* Offset equal */ + strcmp (L2 [0]->Line + 14, "+1") == 0 && + LineFullMatch (L2 [1], "\tsta\tptr1") && + LineFullMatch (L2 [2], "\tstx\tptr1+1") && + LineMatch (L2 [3], "\tldy\t#$") && + LineFullMatch (L2 [4], "\tlda\t(sp),y") && + LineMatch (L2 [5], "\tldy\t#$") && + LineMatch (L2 [6], "\tsta\t(ptr1),y")) { + + char Buf [100]; + sprintf (Buf, "\tsta\t(%s),y", L->Line + 5); + + /* Found the sequence, replace it */ + L2 [6] = ReplaceLine (L2 [6], Buf); + + /* Free the remaining lines */ + FreeLines (L, L2 [2]); + + /* Make the line pointer valid again */ + L = L2 [6]; + } + + /* Next Line */ + L = NextCodeLine (L); + } +} + + + +static void OptDoubleJumps (void) +/* Remove/rearrange jumps that jump to other jumps */ +{ + static const char* Jumps [] = { + "\tjeq\tL", + "\tjne\tL", + "\tbeq\tL", + "\tbne\tL", + "\tjmp\tL", + 0 + }; + + unsigned D; + + Line* L = FirstCode; + while (L) { + + int I; + + /* Is this a jump? */ + while ((I = LineMatchX (L, Jumps)) >= 0) { + + /* Yes. Get the target label */ + Line* Target = GetTargetLine (L->Line + 5); + + /* Target points to the label itself. Skip lines until we reach + * one that is not a label. + */ + Target = NextInstruction (Target); + + /* Be sure, this line is not the same as the one the jump is + * in (this happens if there is an empty loop). + */ + if (Target == L) { + break; + } + D = 0; + if (LineMatch (Target, "\tjmp\t")) { + + /* The target is itself a jump. If this is a short branch, get + * the final target and check if it is in reach. Bail out if + * not. + */ + if (L->Line[1] == 'b') { + Line* FinalTarget = GetTargetLine (Target->Line+5); + FinalTarget = NextInstruction (FinalTarget); + if ((D = GetJumpDistance (L, FinalTarget)) >= 123) { + break; + } + } + + /* Make sure the jump does indeed point to another label. + * It may happen that this is not the case for some endless + * loop (while(1) and similar). + */ + if (strcmp (L->Line+5, Target->Line+5) == 0) { + /* Same label, bail out */ + break; + } + + /* Use the label in the original jump instead */ + L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5); + + } else if (I < 2 && LineMatch (Target, Jumps [I])) { + + /* Conditional jump. Use final label */ + strcpy (L->Line+5, Target->Line+5); + + } else { + break; + } + } + + /* Next line */ + L = NextCodeLine (L); + } +} + + + +static void OptJumpRTS (void) +/* Replace jumps to an RTS by an RTS */ +{ + Line* L = FirstCode; + while (L) { + /* Is this a jump to a numbered label? */ + if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && isdigit (L->Line [6])) { + + /* Yes. Get the target label */ + Line* Target = GetTargetLine (L->Line+5); + + /* Target points to the label itself. Get the next line */ + Target = NextCodeLine (Target); + if (LineFullMatch (Target, "\trts")) { + /* Replace the jump by an RTS */ + L = ReplaceLine (L, "\trts"); + } + } + L = NextCodeLine (L); + } +} + + + +static void OptBoolTransforms (void) +/* Try to remove the boolean transformation subroutines where they aren't + * necessary. + */ +{ + Line* L2 [2]; + unsigned Label; + const char* BranchTarget; + + Line* L = FirstCode; + while (L) { + + /* Search for a boolean transformer followed by a conditional jump. */ + if (LineMatch (L, "\tjsr\tbool") && + GetNextCodeLines (L, L2, 1) && + IsCondJump (L2 [0])) { + + /* Make the boolean transformer unnecessary by changing the + * the conditional jump to evaluate the condition flags that + * are set after the compare directly. Note: jeq jumps if + * the condition is not met, jne jumps if the condition is met. + */ + + /* Get the condition code */ + int Cond = FindCond (L->Line + 9); + if (Cond < 0) { + /* OOPS! */ + goto NextLine; + } + + /* Invert the code if we jump on condition not met. */ + if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* For easier reading, get a pointer to the jump target */ + BranchTarget = L2[0]->Line+5; + + /* Check if we can replace the jump (sometimes we would need two + * conditional jumps, we will not handle that for now since it + * has some complications - both jumps may be far jumps for + * example making the jumps more costly than the bool transformer + * subroutine). If we cannot replace the jump, bail out. + */ + switch (Cond) { + + case CMP_EQ: + L = ReplaceLine (L, "\tjeq\t%s", BranchTarget); + break; + + case CMP_NE: + L = ReplaceLine (L, "\tjne\t%s", BranchTarget); + break; + + case CMP_GT: + Label = AllocLabel (); + L = ReplaceLine (L, "\tbeq\tL%04X", Label); + L = NewLineAfter (L, "\tjpl\t%s", BranchTarget); + L = NewLabelAfter(L, Label); + break; + + case CMP_GE: + L = ReplaceLine (L, "\tjpl\t%s", BranchTarget); + break; + + case CMP_LT: + L = ReplaceLine (L, "\tjmi\t%s", BranchTarget); + break; + + case CMP_LE: + L = ReplaceLine (L, "\tjeq\t%s", BranchTarget); + L = NewLineAfter (L, "\tjmi\t%s", BranchTarget); + break; + + case CMP_UGT: + Label = AllocLabel (); + L = ReplaceLine (L, "\tbeq\tL%04X", Label); + L = NewLineAfter (L, "\tjcs\t%s", BranchTarget); + L = NewLabelAfter(L, Label); + break; + + case CMP_UGE: + L = ReplaceLine (L, "\tjcs\t%s", BranchTarget); + break; + + case CMP_ULT: + L = ReplaceLine (L, "\tjcc\t%s", BranchTarget); + break; + + case CMP_ULE: + L = ReplaceLine (L, "\tjeq\t%s", BranchTarget); + L = NewLineAfter (L, "\tjcc\t%s", BranchTarget); + break; + + default: + Internal ("Unknown jump condition: %u", Cond); + + } + + /* Remove the old stuff */ + FreeLine (L2[0]); + + } + +NextLine: + L = NextCodeLine (L); + } +} + + + +static void OptCompares2 (void) +/* Try to optimize the integer compare subroutines. */ +{ + Line* L2[10]; + unsigned Label; + const char* BranchTarget; + int C; + + Line* L = FirstCode; + while (L) { + + /* Search for + * + * lda x + * ldx x+1 + * cpx #$00 + * bne *+4 + * cmp #$00 + * jne/jeq ... + * + * and replace it by + * + * lda x + * ora x+1 + * jne/jeq ... + */ + if (LineMatch (L, "\tlda\t") && + GetNextCodeLines (L, L2, 5) && + IsLoadAX (L, L2[0]) && + LineFullMatch (L2[1], "\tcpx\t#$00") && + LineFullMatch (L2[2], "\tbne\t*+4") && + LineFullMatch (L2[3], "\tcmp\t#$00") && + IsCondJump (L2[4])) { + + /* Replace the load of X by an ora */ + L2[0]->Line[1] = 'o'; + L2[0]->Line[2] = 'r'; + L2[0]->Line[3] = 'a'; + + /* Remove unneeded stuff */ + FreeLines (L2[1], L2[3]); + + } + + /* Same for local variables: Replace + * + * ldy #$xx + * lda (sp),y + * tax + * dey + * lda (sp),y + * cpx #$00 + * bne *+4 cmp #$00 + * cmp #$00 + * jne/jeq ... + * + * by + * + * ldy #$xx + * lda (sp),y + * dey + * ora (sp),y + * jne/jeq ... + */ + else if (LineMatch (L, "\tldy\t#$") && + GetNextCodeLines (L, L2, 8) && + LineFullMatch (L2[0], "\tlda\t(sp),y") && + LineFullMatch (L2[1], "\ttax") && + LineFullMatch (L2[2], "\tdey") && + LineFullMatch (L2[3], "\tlda\t(sp),y") && + LineFullMatch (L2[4], "\tcpx\t#$00") && + LineFullMatch (L2[5], "\tbne\t*+4") && + LineFullMatch (L2[6], "\tcmp\t#$00") && + IsCondJump (L2[7])) { + + /* Replace the second load by an ora */ + L2[3]->Line[1] = 'o'; + L2[3]->Line[2] = 'r'; + L2[3]->Line[3] = 'a'; + + /* Remove unneeded stuff */ + FreeLine (L2[1]); + FreeLines (L2[4], L2[6]); + + } + + /* Search for the call to a compare subroutine followed by a + * conditional jump. + */ + else if (LineMatch (L, "\tjsr\ttos") && + (L2[0] = NextCodeLine (L)) != 0 && + IsCondJump (L2[0])) { + + /* Extract the condition from the function name and branch */ + C = CheckAndGetIntCmp (L, L2[0]); + if (C < 0) { + /* Something is wrong */ + goto NextLine; + } + + /* Replace the subroutine call by a cheaper one */ + L = ReplaceLine (L, "\tjsr\ttosicmp"); + + /* For easier reading, get a pointer to the jump target */ + BranchTarget = L2[0]->Line+5; + + /* Check if we can replace the jump (sometimes we would need two + * conditional jumps, we will not handle that for now since it + * has some complications - both jumps may be far jumps for + * example making the jumps more costly than the bool transformer + * subroutine). If we cannot replace the jump, bail out. + */ + switch (C) { + + case CMP_EQ: + L = NewLineAfter (L, "\tjeq\t%s", BranchTarget); + break; + + case CMP_NE: + L = NewLineAfter (L, "\tjne\t%s", BranchTarget); + break; + + case CMP_GT: + Label = AllocLabel (); + L = NewLineAfter (L, "\tbeq\tL%04X", Label); + L = NewLineAfter (L, "\tjpl\t%s", BranchTarget); + L = NewLabelAfter(L, Label); + break; + + case CMP_GE: + L = NewLineAfter (L, "\tjpl\t%s", BranchTarget); + break; + + case CMP_LT: + L = NewLineAfter (L, "\tjmi\t%s", BranchTarget); + break; + + case CMP_LE: + L = NewLineAfter (L, "\tjeq\t%s", BranchTarget); + L = NewLineAfter (L, "\tjmi\t%s", BranchTarget); + break; + + case CMP_UGT: + Label = AllocLabel (); + L = NewLineAfter (L, "\tbeq\tL%04X", Label); + L = NewLineAfter (L, "\tjcs\t%s", BranchTarget); + L = NewLabelAfter(L, Label); + break; + + case CMP_UGE: + L = NewLineAfter (L, "\tjcs\t%s", BranchTarget); + break; + + case CMP_ULT: + L = NewLineAfter (L, "\tjcc\t%s", BranchTarget); + break; + + case CMP_ULE: + L = NewLineAfter (L, "\tjeq\t%s", BranchTarget); + L = NewLineAfter (L, "\tjcc\t%s", BranchTarget); + break; + + default: + Internal ("Unknown jump condition: %u", C); + + } + + /* Remove the old stuff */ + FreeLine (L2[0]); + } + +NextLine: + L = NextCodeLine (L); + } +} + + + +static void OptTests (void) +/* Remove unnecessary tests */ +{ + Line* L2 [2]; + + const char* BitOps [] = { + "\tand\t", + "\tora\t", + "\teor\t", + 0 + }; + + /* Search for lda/tay/jne or lda/tay/jeq, remove the tay. + * Search for + * lda ... + * cmp #$00 + * jne/jeq + * Remove the cmp. + */ + Line* L = FirstCode; + while (L) { + + /* Search for lda/tay/jne or lda/tay/jeq, remove the tay. + * Search for + * lda/and/ora/eor + * cmp #$00 + * jne/jeq ... + * Remove the cmp. + */ + if ((LineMatch (L, "\tlda\t") || + LineMatch (L, "\tand\t") || + LineMatch (L, "\tora\t") || + LineMatch (L, "\teor\t")) && + GetNextCodeLines (L, L2, 2) && + (LineFullMatch (L2 [0], "\ttay") || + LineFullMatch (L2 [0], "\tcmp\t#$00")) && + IsCondJump (L2 [1])) { + + /* We can remove the tay */ + FreeLine (L2 [0]); + + } + + /* Search for + * + * and ... + * tax + * jeq/jne + * + * and remove the tax. + */ + else if (LineMatchX (L, BitOps) >= 0 && + GetNextCodeLines (L, L2, 2) && + LineFullMatch (L2[0], "\ttax") && + IsCondJump (L2[1])) { + + /* Remove the tax including a hint line of there is one */ + if (LineFullMatch (L2[0]->Prev, "+forcetest")) { + FreeLine (L2[0]->Prev); + } + FreeLine (L2[0]); + + /* If the line before L loads X, this is useless and may be removed */ + L2[0] = PrevCodeLine (L); + if (LineFullMatch (L2[0], "\tldx\t#$00")) { + FreeLine (L2[0]); + } + + } + + /* Search for the sequence + * + * stx xx + * stx tmp1 + * ora tmp1 + * + * and replace it by + * + * stx xx + * ora xx + */ + else if (LineMatch (L, "\tstx\t") && + GetNextCodeLines (L, L2, 2) && + LineFullMatch (L2[0], "\tstx\ttmp1") && + LineFullMatch (L2[1], "\tora\ttmp1")) { + + /* Found, replace it */ + L = NewLineAfter (L, "\tora\t%s", L->Line+5); + + /* Remove remaining stuff */ + FreeLines (L2[0], L2[1]); + + } + + + /* Next line */ + L = NextCodeLine (L); + } +} + + + +static void OptNeg (void) +/* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */ +{ + Line* L2 [10]; + + Line* L = FirstCode; + while (L) { + + /* Search for the sequence: + * + * lda ... + * jsr bnega + * jeq/jne ... + * + * and replace it by: + * + * lda ... + * jne/jeq ... + */ + if (LineMatch (L, "\tlda\t") && /* Match on start */ + GetNextCodeLines (L, L2, 2) && /* Fetch next lines */ + LineFullMatch (L2 [0], "\tjsr\tbnega") && + IsCondJump (L2 [1])) { + + /* Found the sequence, replace it */ + FreeLine (L2 [0]); + InvertZJump (L2 [1]); + + } + + /* Search for the sequence: + * + * ldy #$xx + * lda (sp),y + * tax + * dey + * lda (sp),y + * jsr bnegax + * jne/jeq ... + * + * and replace it by + * + * ldy #$xx + * lda (sp),y + * dey + * ora (sp),y + * jeq/jne ... + */ + else if (LineMatch (L, "\tldy\t#$") && + GetNextCodeLines (L, L2, 6) && + LineFullMatch (L2[0], "\tlda\t(sp),y") && + LineFullMatch (L2[1], "\ttax") && + LineFullMatch (L2[2], "\tdey") && + LineFullMatch (L2[3], "\tlda\t(sp),y") && + LineFullMatch (L2[4], "\tjsr\tbnegax") && + IsCondJump (L2[5])) { + + L2[1] = ReplaceLine (L2[1], "\tdey"); + L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y"); + FreeLines (L2[3], L2[4]); + InvertZJump (L2[5]); + + } + + /* Search for the sequence: + * + * lda xx + * ldx xx+1 + * jsr bnegax + * jne/jeq ... + * + * and replace it by + * + * lda xx + * ora xx+1 + * jeq/jne ... + */ + else if (LineMatch (L, "\tlda\t") && + GetNextCodeLines (L, L2, 3) && + IsLoadAX (L, L2[0]) && + LineFullMatch (L2[1], "\tjsr\tbnegax") && + IsCondJump (L2[2])) { + + /* Replace the load of X by ora */ + L2[0]->Line[1] = 'o'; + L2[0]->Line[2] = 'r'; + L2[0]->Line[3] = 'a'; + FreeLine (L2[1]); + InvertZJump (L2[2]); + + } + + /* Search for the sequence: + * + * jsr _xxx + * jsr bnega(x) + * jeq/jne ... + * + * and replace it by: + * + * jsr _xxx + * + * jne/jeq ... + */ + else if (LineMatch (L, "\tjsr\t_") && /* Match on start */ + GetNextCodeLines (L, L2, 2) && + LineMatch (L2 [0], "\tjsr\tbnega") && + IsCondJump (L2 [1])) { + + if (LineFullMatch (L2 [0], "\tjsr\tbnega")) { + /* Byte sized */ + L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */ + } else { + /* Word sized */ + L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1"); + NewLineAfter (L2 [0], "\tora\ttmp1"); + } + + /* Invert the jump */ + InvertZJump (L2 [1]); + + } + + /* Next line */ + L = NextCodeLine (L); + } +} + + + +static void OptTriples (void) +/* Replace code triples */ +{ + static const char* Pat1 [] = { + "\tjsr\tldaxysp", + "\tjsr\tldax0sp", + "\tjsr\tldaysp", + "\tjsr\tleaysp", + "\tjsr\tldaxi", + 0 + }; + static const char* Pat2 [] = { + "\tjsr\tpushax", + "\tjsr\tpushax", + "\tjsr\tpushax", + "\tjsr\tpushax", + "\tjsr\tpushax", + 0 + }; + static const char* Replace [] = { + "\tjsr\tpushwysp", + "\tjsr\tpushw0sp", + "\tjsr\tpushbysp", + "\tjsr\tpleaysp", + "\tjsr\tpushw", + }; + + Line* L = FirstCode; + while (L) { + int I = LineFullMatchX (L, Pat1); + if (I >= 0) { + /* We found the first match, get the next line */ + Line* L2 = NextCodeLine (L); + if (L2 && LineFullMatch (L2, Pat2 [I])) { + /* Found. Replace by the short call */ + FreeLine (L2); + L = ReplaceLine (L, Replace [I]); + } + } + /* Next line */ + L = NextCodeLine (L); + } +} + + + +static Line* OptOneBlock (Line* L) +/* Optimize the register contents inside one basic block */ +{ + static const char* Compares [] = { + "\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax", + "\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0", + "\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00", + "\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00", + "\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax", + "\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0", + "\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0", + "\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax", + "\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax", + "\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax", + "\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00", + "\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00", + "\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax", + "\tjsr\ttosugeeax", + 0 + }; + + static const char* MakeBool [] = { + "\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt", + "\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge", + "\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt", + "\tjsr\tbooluge", + 0 + }; + + int A = -1; /* Contents of A register */ + int X = -1; /* Contents of X register */ + int Y = -1; /* Contents of Y register */ + Line* L2; + unsigned NewVal; + int Delete; + + while (L && !IsLabel (L)) { + + /* Handle all instructions. All instructions not tested here have + * no effects on the register contents. + */ + Delete = 0; + if (L->Line [0] == '+') { + /* This is a hint */ + if (LineMatch (L, "+a:")) { + /* Information about a */ + switch (L->Line [3]) { + case '!': A = -1; break; + case '=': A = GetHexNum (L->Line + 4); break; + } + } else if (LineMatch (L, "+x:")) { + /* The code generator tells something about the x register */ + switch (L->Line [3]) { + case '!': X = -1; break; + case '=': X = GetHexNum (L->Line + 4); break; + } + } else if (LineMatch (L, "+y:")) { + /* Information about the y register */ + switch (L->Line [3]) { + case '!': Y = -1; break; + case '=': Y = GetHexNum (L->Line + 4); break; + } + } + } else if (LineMatch (L, "\tadc\t")) { + A = -1; + } else if (LineMatch (L, "\tand\t")) { + A = -1; + } else if (LineFullMatch (L, "\tasl\ta")) { + if (A != -1) { + A = (A << 1) & 0xFF; + } + } else if (LineFullMatch (L, "\tdex")) { + DEC (X, 1); + } else if (LineFullMatch (L, "\tdey")) { + DEC (Y, 1); + } else if (LineMatch (L, "\teor")) { + A = -1; + } else if (LineFullMatch (L, "\tinx")) { + INC (X, 1); + } else if (LineFullMatch (L, "\tiny")) { + INC (Y, 1); + } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) { + /* We know about this function */ + A = X = -1; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\taddeqysp")) { + /* We know about this function */ + A = X = -1; + INC (Y, 1); + } else if (LineFullMatch (L, "\tjsr\tbnega")) { + /* We know about this function */ + A = -1; + X = 0; + } else if (LineFullMatch (L, "\tjsr\tbnegax")) { + /* We know about this function */ + A = -1; + X = 0; + } else if (LineFullMatch (L, "\tjsr\tbnegeax")) { + /* We know about this function */ + A = -1; + X = 0; + } else if (LineFullMatch (L, "\tjsr\tincax1")) { + /* We know about this function */ + A = X = -1; + } else if (LineFullMatch (L, "\tjsr\tincax2")) { + /* We know about this function */ + A = X = -1; + } else if (LineFullMatch (L, "\tjsr\tladdeq")) { + /* We know about this function */ + A = X = -1; + Y = 3; + } else if (LineFullMatch (L, "\tjsr\tladdeqb")) { + /* We know about this function */ + A = X = -1; + Y = 3; + } else if (LineFullMatch (L, "\tjsr\tldau00sp")) { + /* We know about this function */ + A = -1; + X = 0; + Y = 0; + } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) { + /* We know about this function */ + A = -1; + X = 0; + DEC (Y, 1); + } else if (LineFullMatch (L, "\tjsr\tldaui")) { + /* We know about this function */ + A = -1; + X = 0; + Y = 0; + } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) { + A = -1; + Y = X; + X = 0; + } else if (LineFullMatch (L, "\tjsr\tldauidx")) { + /* We know about this function */ + A = -1; + X = 0; + } else if (LineFullMatch (L, "\tjsr\tldauiysp")) { + /* We know about this function */ + A = -1; + Y = X; + X = 0; + } else if (LineFullMatch (L, "\tjsr\tldax0sp")) { + /* We know about this function */ + A = X = -1; + Y = 0; + } else if (LineFullMatch (L, "\tjsr\tldaxi")) { + /* We know about this function */ + A = X = -1; + Y = 0; + } else if (LineFullMatch (L, "\tjsr\tldaxidx")) { + /* We know about this function */ + A = X = -1; + DEC (Y, 1); + } else if (LineFullMatch (L, "\tjsr\tldaxysp")) { + /* We know about this function */ + A = X = -1; + DEC (Y, 1); + } else if (LineFullMatch (L, "\tjsr\tldeaxi")) { + /* We know about this function */ + A = X = -1; + Y = 0; + } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) { + /* We know about this function */ + A = X = -1; + DEC (Y, 3); + } else if (LineFullMatch (L, "\tjsr\tlsubeq")) { + /* We know about this function */ + A = X = -1; + Y = 3; + } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) { + /* We know about this function */ + A = X = -1; + Y = 3; + } else if (LineFullMatch (L, "\tjsr\tpush0")) { + /* We know about this function */ + A = 0; + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpush1")) { + /* We know about this function */ + A = 1; + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpush2")) { + /* We know about this function */ + A = 2; + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpush3")) { + /* We know about this function */ + A = 3; + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpush4")) { + /* We know about this function */ + A = 4; + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpush5")) { + /* We know about this function */ + A = 5; + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpush6")) { + /* We know about this function */ + A = 6; + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpush7")) { + /* We know about this function */ + A = 7; + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpusha")) { + /* We know about this function */ + Y = 0; + } else if (LineFullMatch (L, "\tjsr\tpusha0")) { + /* We know about this function */ + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpusha0")) { + /* We know about this function + * If X is already zero, we may call pushax instead and save two + * cycles. + */ + if (X == 0) { + L = ReplaceLine (L, "\tjsr\tpushax"); + } + X = 0; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpushax")) { + /* We know about this function */ + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpushc0")) { + /* We know about this function */ + A = 0; + Y = 0; + } else if (LineFullMatch (L, "\tjsr\tpushc1")) { + /* We know about this function */ + A = 1; + Y = 0; + } else if (LineFullMatch (L, "\tjsr\tpushc2")) { + /* We know about this function */ + A = 2; + Y = 0; + } else if (LineFullMatch (L, "\tjsr\tpushw")) { + /* We know about this function (calls pushax) */ + A = X = -1; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) { + /* We know about this function(calls pushax) */ + A = X = -1; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpushwidx")) { + /* We know about this function (calls pushax) */ + A = X = -1; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tpushwysp")) { + /* We know about this function (calls pushax) */ + A = X = -1; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tstaspp")) { + /* We know about this function */ + Y = -1; + } else if (LineFullMatch (L, "\tjsr\tstaxspp")) { + /* We know about this function */ + Y = -1; + } else if (LineFullMatch (L, "\tjsr\tstax0sp")) { + /* We know about this function */ + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tstaxysp")) { + /* We know about this function */ + INC (Y, 1); + } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) { + /* We know about this function */ + A = X = -1; + Y = 1; + } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) { + /* We know about this function */ + A = X = -1; + INC (Y, 1); + } else if (LineFullMatch (L, "\tjsr\ttosicmp")) { + /* We know about this function */ + A = X = -1; + Y = 0; + } else if (LineFullMatchX (L, Compares) >= 0) { + A = Y = -1; + X = 0; + } else if (LineFullMatchX (L, MakeBool) >= 0) { + A = -1; + X = 0; + } else if (LineMatch (L, "\tjsr\t")) { + /* Subroutine call, forget all register information */ + A = X = Y = -1; + } else if (LineMatch (L, "\tlda\t")) { + if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) { + /* The value loaded is not used later, remove it */ + Delete = 1; + } else if (LineMatch (L, "\tlda\t(")) { + if (IsXIndAddrMode (L)) { + /* lda (zp,x) - if Y and X are both zero, replace by + * load indirect y and save one cycle in some cases. + */ + if (X == 0 && Y == 0) { + char Buf [256]; + const char* S = L->Line + 6; + char* T = Buf + 6; + strcpy (Buf, "\tlda\t("); + while (*S != ',') { + *T++ = *S++; + } + *T++ = ')'; + *T++ = ','; + *T++ = 'y'; + *T = '\0'; + L = ReplaceLine (L, Buf); + } + } + /* In any case invalidate A */ + A = -1; + } else if (LineMatch (L, "\tlda\t#$")) { + /* Immidiate load into A */ + NewVal = GetHexNum (L->Line + 7); + if (NewVal == A) { + /* Load has no effect */ + Delete = 1; + } else if (NewVal == X) { + /* Requested value is already in X */ + L = ReplaceLine (L, "\ttxa"); + } else if (NewVal == Y) { + /* Requested value is already in Y */ + L = ReplaceLine (L, "\ttya"); + } + /* Anyway, the new value is now in A */ + A = NewVal; + } else { + /* Memory load into A */ + A = -1; + } + } else if (LineMatch (L, "\tldax\t")) { + /* Memory load into A and X */ + A = X = -1; + } else if (LineMatch (L, "\tldx\t")) { + if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) { + /* The value loaded is not used later, remove it */ + Delete = 1; + } else if (LineMatch (L, "\tldx\t#$")) { + /* Immidiate load into X */ + NewVal = GetHexNum (L->Line + 7); + if (NewVal == X) { + /* Load has no effect */ + Delete = 1; + } else if (NewVal == A) { + /* Requested value is already in A */ + L = ReplaceLine (L, "\ttax"); + } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) { + /* Requested value is one more than current contents */ + L = ReplaceLine (L, "\tinx"); + } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) { + /* Requested value is one less than current contents */ + L = ReplaceLine (L, "\tdex"); + } + /* Anyway, the new value is now in X */ + X = NewVal; + } else { + /* Memory load into X */ + X = -1; + } + } else if (LineMatch (L, "\tldy\t")) { + if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) { + /* The value loaded is not used later, remove it */ + Delete = 1; + } else if (LineMatch (L, "\tldy\t#$")) { + /* Immidiate load into Y */ + NewVal = GetHexNum (L->Line + 7); + if (NewVal == Y) { + /* Load has no effect */ + Delete = 1; + } else if (NewVal == A) { + /* Requested value is already in A */ + L = ReplaceLine (L, "\ttay"); + } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) { + /* Requested value is one more than current contents */ + L = ReplaceLine (L, "\tiny"); + } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) { + /* Requested value is one less than current contents */ + L = ReplaceLine (L, "\tdey"); + } + /* Anyway, the new value is now in Y */ + Y = NewVal; + } else { + /* Memory load into Y */ + Y = -1; + } + } else if (LineFullMatch (L, "\tlsr\ta")) { + if (A != -1) { + A >>= 1; + } + } else if (LineMatch (L, "\tora\t#$")) { + if (A != -1) { + A |= GetHexNum (L->Line + 7); + } + } else if (LineMatch (L, "\tora\t")) { + A = -1; + } else if (LineFullMatch (L, "\tpla")) { + A = -1; + } else if (LineFullMatch (L, "\trol\ta")) { + A = -1; + } else if (LineFullMatch (L, "\tror\ta")) { + A = -1; + } else if (LineFullMatch (L, "\trts")) { + A = X = Y = -1; + } else if (LineFullMatch (L, "\trti")) { + A = X = Y = -1; + } else if (LineMatch (L, "\tsbc\t")) { + A = -1; + } else if (LineFullMatch (L, "\ttax")) { + if (A != -1 && X == A) { + /* Load has no effect */ + Delete = 1; + } else { + X = A; + } + } else if (LineFullMatch (L, "\ttay")) { + if (A != -1 && Y == A) { + /* Load has no effect */ + Delete = 1; + } else { + Y = A; + } + } else if (LineFullMatch (L, "\ttsx")) { + X = -1; + } else if (LineFullMatch (L, "\ttxa")) { + if (X != -1 && A == X) { + /* Load has no effect */ + Delete = 1; + } else { + A = X; + } + } else if (LineFullMatch (L, "\ttya")) { + if (Y != -1 && A == Y) { + /* Load has no effect */ + Delete = 1; + } else { + A = Y; + } + } + + /* Set to next line, handle deletions */ + L2 = NextCodeSegLine (L); + if (Delete) { + FreeLine (L); + } + L = L2; + + } + if (L) { + /* Skip the label */ + L = NextCodeSegLine (L); + } + return L; +} + + + +static void OptBlocks (void) +/* Optimize the register contents inside basic blocks */ +{ + Line* L = FirstCode; + while (L) { + L = OptOneBlock (L); + } +} + + + +static void OptJumps (void) +/* Optimize jumps */ +{ + static const char* Jumps [] = { + "\tjeq\tL", + "\tjne\tL", + "\tjmi\tL", + "\tjpl\tL", + "\tjcs\tL", + "\tjcc\tL", + 0 + }; + + Line* L = FirstCode; + while (L) { + int I = LineMatchX (L, Jumps); + if (I >= 0) { + Line* Target = GetTargetLine (L->Line+5); + if (Target->Index > L->Index) { + /* This is a forward jump. Backward jumps are handled + * automagically by the assembler. + */ + unsigned Distance = GetJumpDistance (L, Target); + if (Distance < 123) { /* Safety */ + L->Line [1] = 'b'; /* Make a short branch */ + L->Size = 2; /* Set new size */ + } + } + } + L = NextCodeLine (L); + } +} + + + +static void OptRTS (void) +/* Change sequences of jsr XXX/rts to jmp XXX */ +{ + Line* L = FirstCode; + while (L) { + if (LineMatch (L, "\tjsr\t")) { + /* This is a jsr, get the next instruction */ + Line* L2 = NextCodeLine (L); + if (L2 && LineFullMatch (L2, "\trts")) { + /* We found a sequence */ + FreeLine (L2); + L->Line [2] = 'm'; + L->Line [3] = 'p'; + } + } + /* Try the next line */ + L = NextCodeLine (L); + } +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void OptOnePass (unsigned long Flag, void (*F) (void)) +/* Call one optimizer pass if enabled */ +{ + if ((OptDisable & Flag) == 0) { + F (); + } else if (Verbose || Debug) { + printf ("Optimizer pass %04lX skipped\n", Flag); + } +} + + + +void OptDoOpt (void) +/* Run the optimizer over the collected stuff */ +{ + /* Find and remember the first line of code */ + FindCodeStart (); + + /* Mark all lines inside the code segment */ + MarkCodeLines (); + + /* Create a list of all local labels for fast access */ + CreateLabelList (); + + /* Ok, now start the real optimizations */ + + /* Optimize compares - first step */ + OptOnePass (0x0001, OptCompares1); + + /* Remove dead jumps */ + OptOnePass (0x0002, OptDeadJumps); + + /* Remove unnecessary loads */ + OptOnePass (0x0004, OptLoads); + + /* Remove unnecessary register loads */ + OptOnePass (0x0008, OptRegLoads); + + /* Optimize stores through pointers */ + OptOnePass (0x0010, OptPtrOps); + + /* Optimize use of register variables */ + OptOnePass (0x0020, OptRegVars); + + /* Remove jump cascades - must be used before OptNeg */ + OptOnePass (0x0040, OptDoubleJumps); + + /* Remove unnecessary boolean negates */ + OptOnePass (0x0080, OptNeg); + + /* Replace jumps to an RTS by an RTS */ + OptOnePass (0x0100, OptJumpRTS); + + /* Optimize boolean transforms */ + OptOnePass (0x0200, OptBoolTransforms); + + /* Optimize compares */ + OptOnePass (0x0400, OptCompares2); + + /* Remove unnecessary tests */ + OptOnePass (0x0800, OptTests); + + /* Optimize several triples */ + OptOnePass (0x1000, OptTriples); + + /* Optimize basic blocks */ + OptOnePass (0x2000, OptBlocks); + + /* Remove unnecessary register loads (another pass) */ + OptOnePass (0x0008, OptRegLoads); + + /* Optimize jumps */ + OptOnePass (0x4000, OptJumps); + + /* Optimize jsr/rts sequences */ + OptOnePass (0x8000, OptRTS); +} + + + diff --git a/src/cc65/optimize.h b/src/cc65/optimize.h new file mode 100644 index 000000000..503ccba44 --- /dev/null +++ b/src/cc65/optimize.h @@ -0,0 +1,68 @@ +/*****************************************************************************/ +/* */ +/* optimize.h */ +/* */ +/* An optimizer for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OPTIMIZE_H +#define OPTIMIZE_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Bitset of flags that switch the different optimizer passes */ +extern unsigned long OptDisable; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void OptDoOpt (void); +/* Run the optimizer over the collected stuff */ + + + +/* End of optimize.h */ + +#endif + + + diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c new file mode 100644 index 000000000..2a0f7460c --- /dev/null +++ b/src/cc65/pragma.c @@ -0,0 +1,199 @@ +/*****************************************************************************/ +/* */ +/* pragma.c */ +/* */ +/* Pragma handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "global.h" +#include "error.h" +#include "io.h" +#include "litpool.h" +#include "symtab.h" +#include "preproc.h" +#include "scanner.h" +#include "codegen.h" +#include "expr.h" +#include "pragma.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Tokens for the #pragmas */ +enum { + PR_BSSSEG, + PR_CODESEG, + PR_DATASEG, + PR_REGVARADDR, + PR_RODATASEG, + PR_SIGNEDCHARS, + PR_STATICLOCALS, + PR_ZPSYM, + PR_ILLEGAL +}; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +static void StringPragma (void (*Func) (const char*)) +/* Handle a pragma that expects a string parameter */ +{ + if (curtok != SCONST) { + Error (ERR_STRLIT_EXPECTED); + } else { + /* Get the string */ + const char* Name = GetLiteral (curval); + + /* Call the given function with the string argument */ + Func (Name); + + /* Reset the string pointer, removing the string from the pool */ + ResetLiteralOffs (curval); + } + + /* Skip the string (or error) token */ + gettok (); +} + + + +static void FlagPragma (unsigned char* Flag) +/* Handle a pragma that expects a boolean paramater */ +{ + /* Read a constant expression */ + struct expent val; + constexpr (&val); + + /* Store the value into the flag parameter */ + *Flag = val.e_const; +} + + + +void DoPragma (void) +/* Handle pragmas */ +{ + static const struct tok_elt Pragmas [] = { + { "bssseg", PR_BSSSEG }, + { "codeseg", PR_CODESEG }, + { "dataseg", PR_DATASEG }, + { "regvaraddr", PR_REGVARADDR }, + { "rodataseg", PR_RODATASEG }, + { "signedchars", PR_SIGNEDCHARS }, + { "staticlocals", PR_STATICLOCALS }, + { "zpsym", PR_ZPSYM }, + { 0, PR_ILLEGAL }, + }; + + int Pragma; + + /* Skip the token itself */ + gettok (); + + /* Identifier must follow */ + if (curtok != IDENT) { + Error (ERR_IDENT_EXPECTED); + return; + } + + /* Do we know this pragma? */ + Pragma = searchtok (CurTok.Ident, Pragmas); + if (Pragma == PR_ILLEGAL) { + /* According to the ANSI standard, we're not allowed to generate errors + * for unknown pragmas, however, we're allowed to warn - and we will + * do so. Otherwise one typo may give you hours of bug hunting... + */ + Warning (WARN_UNKNOWN_PRAGMA); + return; + } + + /* Skip the identifier and check for an open paren */ + gettok (); + ConsumeLParen (); + + /* Switch for the different pragmas */ + switch (Pragma) { + + case PR_BSSSEG: + StringPragma (g_bssname); + break; + + case PR_CODESEG: + StringPragma (g_codename); + break; + + case PR_DATASEG: + StringPragma (g_dataname); + break; + + case PR_REGVARADDR: + FlagPragma (&AllowRegVarAddr); + break; + + case PR_RODATASEG: + StringPragma (g_rodataname); + break; + + case PR_SIGNEDCHARS: + FlagPragma (&SignedChars); + break; + + case PR_STATICLOCALS: + FlagPragma (&LocalsAreStatic); + break; + + case PR_ZPSYM: + StringPragma (MakeZPSym); + break; + + default: + Internal ("Invalid pragma"); + } + + /* Closing paren needed */ + ConsumeRParen (); +} + + + diff --git a/src/cc65/pragma.h b/src/cc65/pragma.h new file mode 100644 index 000000000..65794a037 --- /dev/null +++ b/src/cc65/pragma.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +/* */ +/* pragma.h */ +/* */ +/* Pragma handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef PRAGMA_H +#define PRAGMA_H + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void DoPragma (void); +/* Handle pragmas */ + + + +/* End of pragma.h */ +#endif + + + + + diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c new file mode 100644 index 000000000..07ef3bd49 --- /dev/null +++ b/src/cc65/preproc.c @@ -0,0 +1,882 @@ + +/* C pre-processor functions */ + +#include +#include +#include +#include +#include + +#include "codegen.h" +#include "error.h" +#include "expr.h" +#include "global.h" +#include "ident.h" +#include "include.h" +#include "io.h" +#include "macrotab.h" +#include "mem.h" +#include "scanner.h" +#include "util.h" +#include "preproc.h" + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static int Pass1 (char* from, char* to); + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Set when the pp calls expr() recursively */ +unsigned char Preprocessing = 0; + +/* Management data for #if */ +#define N_IFDEF 16 +static int i_ifdef = -1; +static char s_ifdef[N_IFDEF]; + +/* Buffer for macro expansion */ +static char mlinebuf [LINESIZE]; +static char* mline = mlinebuf; +static char* mptr; + +/* Flag: Expand macros in this line */ +static int ExpandMacros = 1; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +static int keepch (char c) +/* Put character c into translation buffer. */ +{ + return (*mptr++ = c); +} + + + +static void keepstr (const char* S) +/* Put string str into translation buffer. */ +{ + while (*S) { + keepch (*S++); + } +} + + + +static void comment (void) +/* Remove comment from line. */ +{ + unsigned StartingLine = ln; + + gch (); + gch (); + while (*lptr != '*' || nch () != '/') { + if (*lptr == '\0') { + if (readline () == 0) { + PPError (ERR_EOF_IN_COMMENT, StartingLine); + return; + } + } else { + if (*lptr == '/' && nch() == '*') { + PPWarning (WARN_NESTED_COMMENT); + } + ++lptr; + } + } + gch (); + gch (); +} + + + +static void skipblank (void) +/* Skip blanks and tabs in the input stream. */ +{ + while (IsBlank (*lptr)) { + ++lptr; + } +} + + + +static char* CopyQuotedString (int Quote, char* Target) +/* Copy a single or double quoted string from lptr to Target. Return the + * new target pointer. Target will not be terminated after the copy. + */ +{ + /* Copy the starting quote */ + *Target++ = gch(); + + /* Copy the characters inside the string */ + while (*lptr != '\0' && *lptr != Quote) { + /* Keep an escaped char */ + if (*lptr == '\\') { + *Target++ = gch(); + } + /* Copy the character */ + *Target++ = cgch(); + } + + /* If we had a terminating quote, copy it */ + if (*lptr) { + *Target++ = gch(); + } + + /* Return the new target pointer */ + return Target; +} + + + +/*****************************************************************************/ +/* Macro stuff */ +/*****************************************************************************/ + + + +static int macname (char *sname) +/* Get macro symbol name. If error, print message and kill line. */ +{ + if (issym (sname) == 0) { + PPError (ERR_IDENT_EXPECTED); + kill (); + return 0; + } else { + return 1; + } +} + + + +static void ExpandMacroArgs (Macro* M) +/* Preprocessor pass 2. Perform macro substitution. */ +{ + int C; + ident Ident; + const char* Replacement; + char* SavePtr; + + /* Save the current line pointer and setup the new ones */ + SavePtr = lptr; + lptr = M->Replacement; + + /* Copy the macro replacement checking for parameters to replace */ + while ((C = *lptr) != '\0') { + /* If the next token is an identifier, check for a macro arg */ + if (IsIdent (C)) { + symname (Ident); + Replacement = FindMacroArg (M, Ident); + if (Replacement) { + /* Macro arg, keep the replacement */ + keepstr (Replacement); + } else { + /* No macro argument, keep the original identifier */ + keepstr (Ident); + } + } else if (C == '#' && IsIdent (nch ())) { + ++lptr; + symname (Ident); + Replacement = FindMacroArg (M, Ident); + if (Replacement) { + keepch ('\"'); + keepstr (Replacement); + keepch ('\"'); + } else { + keepch ('#'); + keepstr (Ident); + } + } else if (IsQuoteChar(C)) { + mptr = CopyQuotedString (C, mptr); + } else { + *mptr++ = *lptr++; + } + } + + /* Reset the line pointer */ + lptr = SavePtr; +} + + + +static int MacroCall (Macro* M) +/* Process a function like macro */ +{ + unsigned ArgCount; /* Macro argument count */ + unsigned ParCount; /* Number of open parenthesis */ + char Buf[LINESIZE]; /* Argument buffer */ + char C; + const char* ArgStart; + char* B; + + /* Expect an argument list */ + skipblank (); + if (*lptr != '(') { + PPError (ERR_ILLEGAL_MACRO_CALL); + return 0; + } + + /* Eat the left paren */ + ++lptr; + + /* Read the actual macro arguments and store pointers to these arguments + * into the array of actual arguments in the macro definition. + */ + ArgCount = 0; + ParCount = 0; + ArgStart = Buf; + B = Buf; + while (1) { + C = *lptr; + if (C == '(') { + *B++ = gch (); + ++ParCount; + } else if (IsQuoteChar(C)) { + B = CopyQuotedString (C, B); + } else if (C == ',' || C == ')') { + if (ParCount == 0) { + /* End of actual argument */ + gch (); + *B++ = '\0'; + while (IsBlank(*ArgStart)) { + ++ArgStart; + } + if (ArgCount < M->ArgCount) { + M->ActualArgs[ArgCount++] = ArgStart; + } else if (C != ')' || *ArgStart != '\0' || M->ArgCount > 0) { + /* Be sure not to count the single empty argument for a + * macro that does not have arguments. + */ + ++ArgCount; + } + + /* Start the next one */ + ArgStart = B; + if (C == ')') { + break; + } + } else { + *B++ = gch (); + if (C == ')') { + --ParCount; + } + } + } else if (IsBlank (C)) { + /* Squeeze runs of blanks */ + *B++ = ' '; + skipblank (); + } else if (C == '\0') { + /* End of line inside macro argument list - read next line */ + if (readline () == 0) { + return 0; + } + } else { + /* Just copy the character */ + *B++ = *lptr++; + } + } + + /* Compare formal argument count with actual */ + if (M->ArgCount != ArgCount) { + PPError (ERR_MACRO_ARGCOUNT); + /* Be sure to make enough empty arguments available */ + while (ArgCount < M->ArgCount) { + M->ActualArgs [ArgCount++] = ""; + } + } + + /* Preprocess the line, replacing macro parameters */ + ExpandMacroArgs (M); + + /* Done */ + return 1; +} + + + +static void ExpandMacro (Macro* M) +/* Expand a macro */ +{ + /* Check if this is a function like macro */ + if (M->ArgCount >= 0) { + /* Function like macro */ + if (MacroCall (M) == 0) { + kill (); + } + } else { + /* Just copy the replacement text */ + keepstr (M->Replacement); + } +} + + + +static void addmac (void) +/* Add a macro to the macro table. */ +{ + char* saveptr; + ident Ident; + char Buf[LINESIZE]; + Macro* M; + + /* Read the macro name */ + skipblank (); + if (!macname (Ident)) { + return; + } + + /* Create a new macro definition */ + M = NewMacro (Ident); + + /* Check if this is a function like macro */ + if (*lptr == '(') { + + /* Skip the left paren */ + gch (); + + /* Set the marker that this is a function like macro */ + M->ArgCount = 0; + + /* Read the formal parameter list */ + while (1) { + skipblank (); + if (*lptr == ')') + break; + if (macname (Ident) == 0) { + return; + } + AddMacroArg (M, Ident); + skipblank (); + if (*lptr != ',') + break; + gch (); + } + if (*lptr != ')') { + PPError (ERR_RPAREN_EXPECTED); + kill (); + return; + } + gch (); + } + + /* Insert the macro into the macro table and allocate the ActualArgs array */ + InsertMacro (M); + + /* Remove whitespace and comments from the line, store the preprocessed + * line into Buf. + */ + skipblank (); + saveptr = mptr; + Pass1 (lptr, Buf); + mptr = saveptr; + + /* Create a copy of the replacement */ + M->Replacement = xstrdup (Buf); +} + + + +/*****************************************************************************/ + +/*****************************************************************************/ + + + +static int Pass1 (char* from, char* to) +/* Preprocessor pass 1. Remove whitespace and comments. */ +{ + int c; + int done; + ident Ident; + int HaveParen; + + lptr = from; + mptr = to; + done = 1; + while ((c = *lptr) != 0) { + if (IsBlank (c)) { + keepch (' '); + skipblank (); + } else if (IsIdent (c)) { + symname (Ident); + if (Preprocessing && strcmp(Ident, "defined") == 0) { + /* Handle the "defined" operator */ + skipblank(); + HaveParen = 0; + if (*lptr == '(') { + HaveParen = 1; + ++lptr; + skipblank(); + } + if (!IsIdent(c)) { + PPError (ERR_IDENT_EXPECTED); + *mptr++ = '0'; + } else { + symname (Ident); + *mptr++ = IsMacro(Ident)? '1' : '0'; + if (HaveParen) { + skipblank(); + if (*lptr != ')') { + PPError (ERR_RPAREN_EXPECTED); + } else { + ++lptr; + } + } + } + } else { + if (MaybeMacro(c)) { + done = 0; + } + keepstr (Ident); + } + } else if (IsQuoteChar(c)) { + mptr = CopyQuotedString (c, mptr); + } else if (c == '/' && nch () == '*') { + keepch (' '); + comment (); + } else if (ANSI == 0 && c == '/' && nch () == '/') { + keepch (' '); + /* Beware: Because line continuation chars are handled when reading + * lines, we may only skip til the end of the source line, which + * may not be the same as the end of the input line. The end of the + * source line is denoted by a lf (\n) character. + */ + do { + ++lptr; + } while (*lptr != '\n' && *lptr != '\0'); + if (*lptr == '\n') { + ++lptr; + } + } else { + *mptr++ = *lptr++; + } + } + keepch ('\0'); + return done; +} + + + +static int Pass2 (char *from, char *to) +/* Preprocessor pass 2. Perform macro substitution. */ +{ + int C; + int no_chg; + ident Ident; + Macro* M; + + lptr = from; + mptr = to; + no_chg = 1; + while ((C = *lptr) != '\0') { + /* If we have an identifier, check if it's a macro */ + if (IsIdent (C)) { + symname (Ident); + M = FindMacro (Ident); + if (M) { + ExpandMacro (M); + no_chg = 0; + } else { + keepstr (Ident); + } + } else if (IsQuoteChar(C)) { + mptr = CopyQuotedString (C, mptr); + } else { + *mptr++ = *lptr++; + } + } + return no_chg; +} + + + +static void xlateline (void) +/* Translate one line. */ +{ + int cnt; + int Done; + char *p; + + Done = Pass1 (line, mline); + if (ExpandMacros == 0) { + Done = 1; + ExpandMacros = 1; /* Reset to default */ + } + cnt = 5; + do { + p = line; + line = mline; + mline = p; + if (Done) + break; + Done = Pass2 (line, mline); + keepch ('\0'); + } while (--cnt); + lptr = line; +} + + + +static void doundef (void) +/* Process #undef directive */ +{ + ident Ident; + + skipblank (); + if (macname (Ident)) { + UndefineMacro (Ident); + } +} + + + +static int setmflag (int skip, int flag, int cond) +/* setmflag( skip, flag, cond ) */ +{ + if (skip) { + s_ifdef[++i_ifdef] = 3; + return (1); + } else { + s_ifdef[++i_ifdef] = 6; + return (flag ^ cond); + } +} + + + +static int doiff (int skip) +/* Process #if directive */ +{ + struct expent lval; + char* S; + + /* We're about to abuse the compiler expression parser to evaluate the + * #if expression. Save the current tokens to come back here later. + */ + Token sv1 = CurTok; + Token sv2 = NextTok; + + /* Remove the #if from the line and add two semicolons as sentinels */ + skipblank (); + S = line; + while ((*S++ = *lptr++) != '\0') ; + strcat (line, ";;"); + lptr = line; + + /* Switch into special preprocessing mode */ + Preprocessing = 1; + + /* Expand macros in this line */ + xlateline (); + + /* Prime the token pump (remove old tokens from the stream) */ + gettok (); + gettok (); + + /* Call the expression parser */ + constexpr (&lval); + + /* End preprocessing mode */ + Preprocessing = 0; + + /* Reset the old tokens */ + CurTok = sv1; + NextTok = sv2; + + /* Set the #if condition according to the expression result */ + return (setmflag (skip, 1, lval.e_const != 0)); +} + + + +static int doifdef (int skip, int flag) +/* Process #ifdef if flag == 1, or #ifndef if flag == 0. */ +{ + ident Ident; + + skipblank (); + if (macname (Ident) == 0) { + return 0; + } else { + return setmflag (skip, flag, IsMacro(Ident)); + } +} + + + +static void doinclude (void) +/* Open an include file. */ +{ + char name [80]; + unsigned count; + char term; + char c; + char *p; + + if (ifile >= MAXFILES) { + PPError (ERR_INCLUDE_NESTING); + goto done; + } + mptr = mline; + skipblank (); + if (!strchr ("\"<", (term = cgch ()))) { + PPError (ERR_INCLUDE_LTERM_EXPECTED); + goto done; + } + if (term == '<') { + term = '>'; /* get right terminator */ + } + + /* Get the name of the include file */ + count = 0; + while ((c = *lptr) && (c != term) && count < sizeof (name)-1) { + name [count++] = c; + ++lptr; + } + if (c != term) { + PPError (ERR_INCLUDE_RTERM_EXPECTED); + goto done; + } + name [count] = '\0'; + + /* Now search for the name */ + p = FindInclude (name, (term == '\"')? INC_USER : INC_SYS); + if (p == 0) { + PPError (ERR_INCLUDE_NOT_FOUND, name); + goto done; + } + + /* Save the existing file info */ + filetab[ifile].f_ln = ln; + filetab[ifile].f_name = fin; + filetab[ifile].f_iocb = inp; + ++ifile; + + /* Assign the name and output it */ + fin = p; + if (Verbose) { + printf ("including '%s'\n", fin); + } + + /* Try to open the include file */ + if ((inp = fopen (fin, "r")) == 0) { + /* oops! restore old file */ + PPError (ERR_INCLUDE_OPEN_FAILURE, fin); + xfree (fin); + --ifile; + inp = filetab[ifile].f_iocb; + fin = filetab[ifile].f_name; + } else { + ln = 0; + } + +done: + /* clear rest of line so next read will come from new file (if open) */ + kill (); +} + + + +static void doerror (void) +/* Print an error */ +{ + skipblank (); + if (*lptr == '\0') { + PPError (ERR_INVALID_USER_ERROR); + } else { + PPError (ERR_USER_ERROR, lptr); + } + + /* clear rest of line */ + kill (); +} + + + +/* C preprocessor. */ + +/* stuff used to bum the keyword dispatching stuff */ +enum { + D_DEFINE, + D_ELSE, + D_ENDIF, + D_ERROR, + D_IF, + D_IFDEF, + D_IFNDEF, + D_INCLUDE, + D_LINE, + D_PRAGMA, + D_UNDEF, + D_ILLEGAL, +}; + +static const struct tok_elt pre_toks[] = { + { "define", D_DEFINE }, + { "else", D_ELSE }, + { "endif", D_ENDIF }, + { "error", D_ERROR }, + { "if", D_IF }, + { "ifdef", D_IFDEF }, + { "ifndef", D_IFNDEF }, + { "include", D_INCLUDE }, + { "line", D_LINE }, + { "pragma", D_PRAGMA }, + { "undef", D_UNDEF }, + { 0, D_ILLEGAL } +}; + + + +int searchtok (const char *sym, const struct tok_elt *toks) +/* Search a token in a table */ +{ + while (toks->toknam && strcmp (toks->toknam, sym)) + ++toks; + return (toks->toknbr); +} + + + +void preprocess (void) +/* Preprocess a line */ +{ + int c; + int Skip; + ident sname; + + /* Process compiler directives, skip empty lines */ + lptr = line; + + /* Skip white space at the beginning of the line */ + skipblank (); + + /* Check for stuff to skip */ + Skip = 0; + while ((c = *lptr) == '\0' || c == '#' || Skip) { + + /* Check for preprocessor lines lines */ + if (c == '#') { + ++lptr; + skipblank (); + if (*lptr == '\0') { + /* ignore the empty preprocessor directive */ + continue; + } + if (!issym (sname)) { + PPError (ERR_CPP_DIRECTIVE_EXPECTED); + kill (); + } else { + switch (searchtok (sname, pre_toks)) { + + case D_DEFINE: + if (!Skip) { + addmac (); + } + break; + + case D_ELSE: + if (s_ifdef[i_ifdef] & 2) { + if (s_ifdef[i_ifdef] & 4) { + Skip = !Skip; + } + s_ifdef[i_ifdef] ^= 2; + } else { + PPError (ERR_UNEXPECTED_CPP_ELSE); + } + break; + + case D_ENDIF: + if (i_ifdef >= 0) { + Skip = s_ifdef[i_ifdef--] & 1; + } else { + PPError (ERR_UNEXPECTED_CPP_ENDIF); + } + break; + + case D_ERROR: + if (!Skip) { + doerror (); + } + break; + + case D_IF: + Skip = doiff (Skip); + break; + + case D_IFDEF: + Skip = doifdef (Skip, 1); + break; + + case D_IFNDEF: + Skip = doifdef (Skip, 0); + break; + + case D_INCLUDE: + if (!Skip) { + doinclude (); + } + break; + + case D_LINE: + /* Not allowed in strict ANSI mode */ + if (ANSI) { + PPError (ERR_CPP_DIRECTIVE_EXPECTED); + kill (); + } + break; + + case D_PRAGMA: + if (!Skip) { + /* Don't expand macros in this line */ + ExpandMacros = 0; + /* #pragma is handled on the scanner level */ + goto Done; + } + break; + + case D_UNDEF: + if (!Skip) { + doundef (); + } + break; + + default: + PPError (ERR_CPP_DIRECTIVE_EXPECTED); + kill (); + } + } + + } + if (readline () == 0) { + if (i_ifdef >= 0) { + PPError (ERR_CPP_ENDIF_EXPECTED); + } + return; + } + skipblank (); + } + +Done: + xlateline (); + if (Verbose > 1) { + printf ("line: %s\n", line); + } +} + diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h new file mode 100644 index 000000000..194d11c35 --- /dev/null +++ b/src/cc65/preproc.h @@ -0,0 +1,51 @@ +/* + * preproc.h + * + * Ullrich von Bassewitz, 07.06.1998 + */ + + + +#ifndef PREPROC_H +#define PREPROC_H + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Token table entry */ +struct tok_elt { + char *toknam; + int toknbr; +}; + +/* Set when the pp calls expr() recursively */ +extern unsigned char Preprocessing; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +int searchtok (const char *sym, const struct tok_elt* toks); +/* Search a token in a table */ + +void preprocess (void); +/* Preprocess a line */ + + + +/* End of preproc.h */ +#endif + + + + + diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c new file mode 100644 index 000000000..5ca35d4c7 --- /dev/null +++ b/src/cc65/scanner.c @@ -0,0 +1,814 @@ +/* + * scanner.c + * + * Ullrich von Bassewitz, 07.06.1998 + */ + + + +#include +#include +#include +#include +#include + +#include "ctrans.h" +#include "datatype.h" +#include "error.h" +#include "function.h" +#include "global.h" +#include "ident.h" +#include "io.h" +#include "litpool.h" +#include "preproc.h" +#include "symtab.h" +#include "util.h" +#include "scanner.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +Token CurTok; /* The current token */ +Token NextTok; /* The next token */ + + + +/* Token types */ +#define TT_C 0 /* ANSI C token */ +#define TT_EXT 1 /* cc65 extension */ + +/* Token table */ +static struct Keyword { + char* Key; /* Keyword name */ + unsigned char Tok; /* The token */ + unsigned char Type; /* Token type */ +} Keywords [] = { + { "__AX__", AX, TT_C }, + { "__EAX__", EAX, TT_C }, + { "__asm__", ASM, TT_C }, + { "__fastcall__", FASTCALL, TT_C }, + { "asm", ASM, TT_EXT }, + { "auto", AUTO, TT_C }, + { "break", BREAK, TT_C }, + { "case", CASE, TT_C }, + { "char", CHAR, TT_C }, + { "const", CONST, TT_C }, + { "continue", CONTINUE, TT_C }, + { "default", DEFAULT, TT_C }, + { "do", DO, TT_C }, + { "double", DOUBLE, TT_C }, + { "else", ELSE, TT_C }, + { "enum", ENUM, TT_C }, + { "extern", EXTERN, TT_C }, + { "fastcall", FASTCALL, TT_EXT }, + { "float", FLOAT, TT_C }, + { "for", FOR, TT_C }, + { "goto", GOTO, TT_C }, + { "if", IF, TT_C }, + { "int", INT, TT_C }, + { "long", LONG, TT_C }, + { "register", REGISTER, TT_C }, + { "return", RETURN, TT_C }, + { "short", SHORT, TT_C }, + { "signed", SIGNED, TT_C }, + { "sizeof", SIZEOF, TT_C }, + { "static", STATIC, TT_C }, + { "struct", STRUCT, TT_C }, + { "switch", SWITCH, TT_C }, + { "typedef", TYPEDEF, TT_C }, + { "union", UNION, TT_C }, + { "unsigned", UNSIGNED, TT_C }, + { "void", VOID, TT_C }, + { "volatile", VOLATILE, TT_C }, + { "while", WHILE, TT_C }, +}; +#define KEY_COUNT (sizeof (Keywords) / sizeof (Keywords [0])) + + + +/* Stuff for determining the type of an integer constant */ +#define IT_INT 0x01 +#define IT_UINT 0x02 +#define IT_LONG 0x04 +#define IT_ULONG 0x08 + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +static int CmpKey (const void* Key, const void* Elem) +/* Compare function for bsearch */ +{ + return strcmp ((const char*) Key, ((const struct Keyword*) Elem)->Key); +} + + + +static int FindKey (char* Key) +/* Find a keyword and return the token. Return IDENT if the token is not a + * keyword. + */ +{ + struct Keyword* K; + K = bsearch (Key, Keywords, KEY_COUNT, sizeof (Keywords [0]), CmpKey); + if (K && (K->Type != TT_EXT || ANSI == 0)) { + return K->Tok; + } else { + return IDENT; + } +} + + + +static int skipwhite (void) +/* Skip white space in the input stream, reading and preprocessing new lines + * if necessary. Return 0 if end of file is reached, return 1 otherwise. + */ +{ + while (1) { + while (*lptr == 0) { + if (readline () == 0) { + return 0; + } + preprocess (); + } + if (*lptr == ' ' || *lptr == '\r') { + ++lptr; + } else { + return 1; + } + } +} + + + +void symname (char *s) +/* Get symbol from input stream */ +{ + unsigned k = 0; + do { + if (k != MAX_IDENTLEN) { + ++k; + *s++ = *lptr; + } + ++lptr; + } while (IsIdent (*lptr) || isdigit (*lptr)); + *s = '\0'; +} + + + +int issym (char *s) +/* Get symbol from input stream or return 0 if not a symbol. */ +{ + if (IsIdent (*lptr)) { + symname (s); + return 1; + } else { + return 0; + } +} + + + +static void unknown (unsigned char c) +/* Error message for unknown character */ +{ + Error (ERR_INVALID_CHAR, c); + gch (); /* Skip */ +} + + + +static unsigned hexval (int c) +/* Convert a hex digit into a value */ +{ + if (!isxdigit (c)) { + Error (ERR_ILLEGAL_HEX_DIGIT); + } + if (isdigit (c)) { + return c - '0'; + } else { + return toupper (c) - 'A' + 10; + } +} + + + +static void SetTok (int tok) +/* set nxttok and bump line ptr */ +{ + nxttok = tok; + ++lptr; +} + + + +static int SignExtendChar (int C) +/* Do correct sign extension of a character */ +{ + if (SignedChars && (C & 0x80) != 0) { + return C | ~0xFF; + } else { + return C & 0xFF; + } +} + + + +static int parsechar (int c) +/* Parse a character. Converts \n into EOL, etc. */ +{ + int i; + int val; + + /* Check for escape chars */ + if (c == '\\') { + switch (c = gch ()) { + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'r': + c = '\r'; + break; + case 'n': + c = '\n'; + break; + case 't': + c = '\t'; + break; + case '\"': + c = '\"'; + break; + case '\'': + c = '\''; + break; + case '\\': + c = '\\'; + break; + case 'x': + case 'X': + /* Hex character constant */ + val = hexval (gch ()) << 4; + c = val | hexval (gch ()); /* Do not translate */ + break; + case '0': + case '1': + /* Octal constant */ + i = 0; + val = c - '0'; + while ((c = *lptr) >= '0' && c <= '7' && i++ < 4) { + val = (val << 3) | (c - '0'); + gch (); + } + c = val; /* Do not translate */ + break; + default: + Error (ERR_ILLEGAL_CHARCONST); + } + } + + /* Do correct sign extension */ + return SignExtendChar (c); +} + + + +static void CharConst (void) +/* Parse a character constant. */ +{ + int c; + + /* Skip the quote */ + ++lptr; + + /* Get character */ + c = parsechar (cgch ()); + + /* Check for closing quote */ + if (cgch () != '\'') { + Error (ERR_QUOTE_EXPECTED); + } + + /* Setup values and attributes */ + nxttok = CCONST; + nxtval = SignExtendChar (ctrans (c)); /* Translate into target charset */ + nxttype = type_int; /* Character constants have type int */ +} + + + +static void StringConst (void) +/* Parse a quoted string */ +{ + nxtval = GetLiteralOffs (); + nxttok = SCONST; + + /* Be sure to concatenate strings */ + while (*lptr == '\"') { + + /* Skip the quote char */ + ++lptr; + + while (*lptr != '\"') { + if (*lptr == 0) { + Error (ERR_UNEXPECTED_NEWLINE); + break; + } + AddLiteralChar (parsechar (gch())); + } + + /* Skip closing quote char if there was one */ + cgch (); + + /* Skip white space, read new input */ + skipwhite (); + + } + + /* Terminate the string */ + AddLiteralChar ('\0'); +} + + + +void gettok (void) +/* Get next token from input stream */ +{ + char c; + ident token; + + /* Current token is the lookahead token */ + CurTok = NextTok; + + /* Remember the starting position of the next token */ + NextTok.Pos = ln; + + /* Skip spaces and read the next line if needed */ + if (skipwhite () == 0) { + /* End of file reached */ + nxttok = CEOF; + return; + } + + /* Determine the next token from the lookahead */ + c = *lptr; + if (isdigit (c)) { + + /* A number */ + int HaveSuffix; /* True if we have a type suffix */ + unsigned types; /* Possible types */ + unsigned base; + unsigned long k; /* Value */ + + k = 0; + base = 10; + types = IT_INT | IT_LONG | IT_ULONG; + + if (c == '0') { + /* Octal or hex constants may also be of type unsigned int */ + types = IT_INT | IT_UINT | IT_LONG | IT_ULONG; + /* gobble 0 and examin next char */ + if (toupper (*++lptr) == 'X') { + base = 16; + nxttype = type_uint; + ++lptr; /* gobble "x" */ + } else { + base = 8; + } + } + while (1) { + c = *lptr; + if (isdigit (c)) { + k = k * base + (c - '0'); + } else if (base == 16 && isxdigit (c)) { + k = (k << 4) + hexval (c); + } else { + break; /* not digit */ + } + ++lptr; /* gobble char */ + } + + /* Check for a suffix */ + HaveSuffix = 1; + c = toupper (*lptr); + if (c == 'U') { + /* Unsigned type */ + ++lptr; + if (toupper (*lptr) != 'L') { + types = IT_UINT | IT_ULONG; + } else { + ++lptr; + types = IT_ULONG; + } + } else if (c == 'L') { + /* Long type */ + ++lptr; + if (toupper (*lptr) != 'U') { + types = IT_LONG | IT_ULONG; + } else { + ++lptr; + types = IT_ULONG; + } + } else { + HaveSuffix = 0; + } + + /* Check the range to determine the type */ + if (k > 0x7FFF) { + /* Out of range for int */ + types &= ~IT_INT; + /* If the value is in the range 0x8000..0xFFFF, unsigned int is not + * allowed, and we don't have a type specifying suffix, emit a + * warning. + */ + if (k <= 0xFFFF && (types & IT_UINT) == 0 && !HaveSuffix) { + Warning (WARN_CONSTANT_IS_LONG); + } + } + if (k > 0xFFFF) { + /* Out of range for unsigned int */ + types &= ~IT_UINT; + } + if (k > 0x7FFFFFFF) { + /* Out of range for long int */ + types &= ~IT_LONG; + } + + /* Now set the type string to the smallest type in types */ + if (types & IT_INT) { + nxttype = type_int; + } else if (types & IT_UINT) { + nxttype = type_uint; + } else if (types & IT_LONG) { + nxttype = type_long; + } else { + nxttype = type_ulong; + } + + /* Set the value and the token */ + nxtval = k; + nxttok = ICONST; + return; + } + + if (issym (token)) { + + /* Check for a keyword */ + if ((nxttok = FindKey (token)) != IDENT) { + /* Reserved word found */ + return; + } + /* No reserved word, check for special symbols */ + if (token [0] == '_') { + /* Special symbols */ + if (strcmp (token, "__FILE__") == 0) { + nxtval = AddLiteral (fin); + nxttok = SCONST; + return; + } else if (strcmp (token, "__LINE__") == 0) { + nxttok = ICONST; + nxtval = ln; + nxttype = type_int; + return; + } else if (strcmp (token, "__fixargs__") == 0) { + nxttok = ICONST; + nxtval = GetParamSize (CurrentFunc); + nxttype = type_uint; + return; + } else if (strcmp (token, "__func__") == 0) { + /* __func__ is only defined in functions */ + if (CurrentFunc) { + nxtval = AddLiteral (GetFuncName (CurrentFunc)); + nxttok = SCONST; + return; + } + } + } + + /* No reserved word but identifier */ + strcpy (NextTok.Ident, token); + NextTok.Tok = IDENT; + return; + } + + /* Monstrous switch statement ahead... */ + switch (c) { + + case '!': + if (*++lptr == '=') { + SetTok (NE); + } else { + nxttok = BANG; + } + break; + + case '\"': + StringConst (); + break; + + case '%': + if (*++lptr == '=') { + SetTok (MOASGN); + } else { + nxttok = MOD; + } + break; + + case '&': + switch (*++lptr) { + case '&': + SetTok (DAMP); + break; + case '=': + SetTok (AASGN); + break; + default: + nxttok = AMP; + } + break; + + case '\'': + CharConst (); + break; + + case '(': + SetTok (LPAREN); + break; + + case ')': + SetTok (RPAREN); + break; + + case '*': + if (*++lptr == '=') { + SetTok (MASGN); + } else { + nxttok = STAR; + } + break; + + case '+': + switch (*++lptr) { + case '+': + SetTok (INC); + break; + case '=': + SetTok (PASGN); + break; + default: + nxttok = PLUS; + } + break; + + case ',': + SetTok (COMMA); + break; + + case '-': + switch (*++lptr) { + case '-': + SetTok (DEC); + break; + case '=': + SetTok (SASGN); + break; + case '>': + SetTok (PREF); + break; + default: + nxttok = MINUS; + } + break; + + case '.': + if (*++lptr == '.') { + if (*++lptr == '.') { + SetTok (ELLIPSIS); + } else { + unknown (*lptr); + } + } else { + nxttok = DOT; + } + break; + + case '/': + if (*++lptr == '=') { + SetTok (DASGN); + } else { + nxttok = DIV; + } + break; + + case ':': + SetTok (COLON); + break; + + case ';': + SetTok (SEMI); + break; + + case '<': + switch (*++lptr) { + case '=': + SetTok (LE); + break; + case '<': + if (*++lptr == '=') { + SetTok (SLASGN); + } else { + nxttok = ASL; + } + break; + default: + nxttok = LT; + } + break; + + case '=': + if (*++lptr == '=') { + SetTok (EQ); + } else { + nxttok = ASGN; + } + break; + + case '>': + switch (*++lptr) { + case '=': + SetTok (GE); + break; + case '>': + if (*++lptr == '=') { + SetTok (SRASGN); + } else { + nxttok = ASR; + } + break; + default: + nxttok = GT; + } + break; + + case '?': + SetTok (QUEST); + break; + + case '[': + SetTok (LBRACK); + break; + + case ']': + SetTok (RBRACK); + break; + + case '^': + if (*++lptr == '=') { + SetTok (XOASGN); + } else { + nxttok = XOR; + } + break; + + case '{': + SetTok (LCURLY); + break; + + case '|': + switch (*++lptr) { + case '|': + SetTok (DBAR); + break; + case '=': + SetTok (OASGN); + break; + default: + nxttok = BAR; + } + break; + + case '}': + SetTok (RCURLY); + break; + + case '~': + SetTok (COMP); + break; + + case '#': + while (*++lptr == ' ') ; /* Skip it and following whitespace */ + if (!issym (token) || strcmp (token, "pragma") != 0) { + /* OOPS - should not happen */ + Error (ERR_CPP_DIRECTIVE_EXPECTED); + } + nxttok = PRAGMA; + break; + + default: + unknown (c); + + } + +} + + + +void Consume (unsigned Token, unsigned char ErrNum) +/* Eat token if it is the next in the input stream, otherwise print an error + * message. + */ +{ + if (curtok == Token) { + gettok (); + } else { + Error (ErrNum); + } +} + + + +void ConsumeColon (void) +/* Check for a colon and skip it. */ +{ + Consume (COLON, ERR_COLON_EXPECTED); +} + + + +void ConsumeSemi (void) +/* Check for a semicolon and skip it. */ +{ + /* Try do be smart about typos... */ + if (curtok == SEMI) { + gettok (); + } else { + Error (ERR_SEMICOLON_EXPECTED); + if (curtok == COLON || curtok == COMMA) { + gettok (); + } + } +} + + + +void ConsumeLParen (void) +/* Check for a left parenthesis and skip it */ +{ + Consume (LPAREN, ERR_LPAREN_EXPECTED); +} + + + +void ConsumeRParen (void) +/* Check for a right parenthesis and skip it */ +{ + Consume (RPAREN, ERR_RPAREN_EXPECTED); +} + + + +void ConsumeLBrack (void) +/* Check for a left bracket and skip it */ +{ + Consume (LBRACK, ERR_LBRACK_EXPECTED); +} + + + +void ConsumeRBrack (void) +/* Check for a right bracket and skip it */ +{ + Consume (RBRACK, ERR_RBRACK_EXPECTED); +} + + + +void ConsumeLCurly (void) +/* Check for a left curly brace and skip it */ +{ + Consume (LCURLY, ERR_LCURLY_EXPECTED); +} + + + +void ConsumeRCurly (void) +/* Check for a right curly brace and skip it */ +{ + Consume (RCURLY, ERR_RCURLY_EXPECTED); +} + + + diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h new file mode 100644 index 000000000..3becee72f --- /dev/null +++ b/src/cc65/scanner.h @@ -0,0 +1,217 @@ +/* + * scanner.h + * + * Ullrich von Bassewitz, 07.06.1998 + */ + + + +#ifndef SCANNER_H +#define SCANNER_H + + + +#include "datatype.h" +#include "ident.h" + + + +/*****************************************************************************/ +/* token definitions */ +/*****************************************************************************/ + + + +#define CEOF 0 + +#define AUTO 10 +#define EXTERN 11 +#define REGISTER 12 +#define STATIC 13 +#define TYPEDEF 14 +#define ENUM 15 +#define CONST 16 +#define VOLATILE 17 + +#define FIRSTTYPE 19 +#define CHAR 19 +#define INT 20 +#define DOUBLE 21 +#define FLOAT 22 +#define LONG 23 +#define UNSIGNED 24 +#define SIGNED 25 +#define SHORT 26 +#define STRUCT 27 +#define UNION 28 +#define VOID 29 +#define LASTTYPE 29 + +#define DO 30 +#define FOR 31 +#define GOTO 32 +#define IF 33 +#define RETURN 34 +#define SWITCH 35 +#define WHILE 36 + +#define ASM 40 +#define CASE 41 +#define DEFAULT 42 +#define BREAK 43 +#define CONTINUE 44 +#define ELSE 45 +#define ELLIPSIS 46 +#define SIZEOF 47 + +#define IDENT 50 +#define SEMI 51 + +/* primary operators */ +#define LBRACK 52 +#define LPAREN 53 +#define DOT 54 +#define PREF 55 + +#define LCURLY 56 +#define RBRACK 57 +#define COMP 58 +#define INC 59 +#define PASGN 60 +#define PLUS 61 +#define COMMA 62 +#define DEC 63 +#define SASGN 64 +#define RCURLY 65 +#define MINUS 66 +#define MASGN 67 +#define STAR 68 +#define DASGN 69 +#define DIV 70 +#define DAMP 71 +#define AASGN 72 +#define AMP 73 +#define NE 74 +#define BANG 75 +#define DBAR 76 +#define OASGN 77 +#define BAR 78 +#define EQ 79 +#define ASGN 80 +#define SLASGN 81 +#define ASL 82 + +/* inequalities */ +#define LE 83 +#define LT 84 +#define GE 85 +#define GT 86 + +#define SRASGN 87 +#define ASR 88 +#define XOASGN 89 +#define XOR 90 +#define MOASGN 91 +#define MOD 92 +#define QUEST 93 +#define COLON 94 +#define RPAREN 95 +#define SCONST 96 +#define ICONST 97 +#define CCONST 98 +#define FCONST 99 + +#define FASTCALL 100 +#define AX 101 +#define EAX 102 + +#define PRAGMA 110 + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +/* Token stuff */ +typedef struct Token_ Token; +struct Token_ { + unsigned Tok; /* The token itself */ + long IVal; /* The integer attribute */ + ident Ident; /* Identifier if IDENT */ + unsigned Pos; /* Source line where the token comes from */ + type* IType; /* Type if integer constant */ +}; + +extern Token CurTok; /* The current token */ +extern Token NextTok; /* The next token */ + +/* Defines to make the old code work */ +#define curtok CurTok.Tok +#define curval CurTok.IVal +#define curpos CurTok.Pos +#define curtype CurTok.IType + +#define nxttok NextTok.Tok +#define nxtval NextTok.IVal +#define nxtpos NextTok.Pos +#define nxttype NextTok.IType + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void symname (char* s); +/* Get symbol from input stream */ + +int issym (char* s); +/* Get symbol from input stream or return 0 if not a symbol. */ + +void gettok (void); +/* Get next token from input stream */ + +void Consume (unsigned Token, unsigned char ErrNum); +/* Eat token if it is the next in the input stream, otherwise print an error + * message. + */ + +void ConsumeColon (void); +/* Check for a colon and skip it. */ + +void ConsumeSemi (void); +/* Check for a semicolon and skip it. */ + +void ConsumeLParen (void); +/* Check for a left parenthesis and skip it */ + +void ConsumeRParen (void); +/* Check for a right parenthesis and skip it */ + +void ConsumeLBrack (void); +/* Check for a left bracket and skip it */ + +void ConsumeRBrack (void); +/* Check for a right bracket and skip it */ + +void ConsumeLCurly (void); +/* Check for a left curly brace and skip it */ + +void ConsumeRCurly (void); +/* Check for a right curly brace and skip it */ + + + +/* End of scanner.h */ +#endif + + + + + + diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c new file mode 100644 index 000000000..23b2d7e14 --- /dev/null +++ b/src/cc65/stdfunc.c @@ -0,0 +1,176 @@ +/*****************************************************************************/ +/* */ +/* stdfunc.c */ +/* */ +/* Handle inlining of known functions for the cc65 compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "check.h" +#include "codegen.h" +#include "error.h" +#include "global.h" +#include "scanner.h" +#include "stdfunc.h" + + + +/*****************************************************************************/ +/* Function forwards */ +/*****************************************************************************/ + + + +static void StdFunc_strlen (struct expent*); + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Table with all known functions and their handlers. Must be sorted + * alphabetically! + */ +static struct FuncDesc { + const char* Name; + void (*Handler) (struct expent*); +} StdFuncs [] = { + { "strlen", StdFunc_strlen }, + +}; +#define FUNC_COUNT (sizeof (StdFuncs) / sizeof (StdFuncs [0])) + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static int CmpFunc (const void* Key, const void* Elem) +/* Compare function for bsearch */ +{ + return strcmp ((const char*) Key, ((const struct FuncDesc*) Elem)->Name); +} + + + +static struct FuncDesc* FindFunc (const char* Name) +/* Find a function with the given name. Return a pointer to the descriptor if + * found, return NULL otherwise. + */ +{ + return bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs [0]), CmpFunc); +} + + + +/*****************************************************************************/ +/* Handle known functions */ +/*****************************************************************************/ + + + +static void StdFunc_strlen (struct expent* lval) +/* Handle the strlen function */ +{ + struct expent pval; + + /* Fetch the parameter */ + int k = hie1 (&pval); + + /* Check if the parameter is a const address */ + unsigned flags = 0; + unsigned pflags = pval.e_flags & ~E_MCTYPE; + if (pflags == E_MCONST) { + /* Constant numeric address */ + flags |= CF_CONST | CF_ABSOLUTE; + } else if (k == 0 && ((pflags & E_MGLOBAL) != 0 || pval.e_flags == E_MEOFFS)) { + /* Global array with or without offset */ + flags |= CF_CONST; + if (pval.e_flags & E_TGLAB) { + /* External linkage */ + flags |= CF_EXTERNAL; + } else { + flags |= CF_STATIC; + } + } else { + /* Not const, load parameter into primary */ + exprhs (CF_NONE, k, &pval); + } + + /* Convert the parameter type to the type needed, check for mismatches */ + assignadjust (SignedChars? type_pschar : type_puchar, &pval); + + /* Generate the strlen code */ + g_strlen (flags, pval.e_name, pval.e_const); + + /* We expect the closing brace */ + ConsumeRParen (); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int IsStdFunc (const char* Name) +/* Determine if the given function is a known standard function that may be + * called in a special way. + */ +{ + /* Look into the table for known names */ + return FindFunc (Name) != 0; +} + + + +void HandleStdFunc (struct expent* lval) +/* Generate code for a known standard function. */ +{ + /* Get a pointer to the table entry */ + struct FuncDesc* F = FindFunc ((const char*) lval->e_name); + CHECK (F != 0); + + /* Call the handler function */ + F->Handler (lval); +} + + + diff --git a/src/cc65/stdfunc.h b/src/cc65/stdfunc.h new file mode 100644 index 000000000..cde09f6eb --- /dev/null +++ b/src/cc65/stdfunc.h @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* */ +/* stdfunc.h */ +/* */ +/* Handle inlining of known functions for the cc65 compiler */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef STDFUNC_H +#define STDFUNC_H + + + +#include "symtab.h" +#include "expr.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int IsStdFunc (const char* Name); +/* Determine if the given function is a known standard function that may be + * called in a special way. + */ + +void HandleStdFunc (struct expent* lval); +/* Generate code for a known standard function. */ + + + +/* End of stdfunc.h */ + +#endif + + + diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c new file mode 100644 index 000000000..d04dc3064 --- /dev/null +++ b/src/cc65/stmt.c @@ -0,0 +1,733 @@ +/* + * stmt.c + * + * Ullrich von Bassewitz, 06.08.1998 + * + * Original by John R. Dunning - see copyleft.jrd + */ + + + +#include +#include + +#include "asmcode.h" +#include "asmlabel.h" +#include "codegen.h" +#include "datatype.h" +#include "error.h" +#include "expr.h" +#include "function.h" +#include "global.h" +#include "goto.h" +#include "litpool.h" +#include "locals.h" +#include "loop.h" +#include "mem.h" +#include "pragma.h" +#include "scanner.h" +#include "symtab.h" +#include "stmt.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Maximum count of cases */ +#define CASE_MAX 257 + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static int statement (void); +/* Forward decl */ + + + +static int doif (void) +/* Handle 'if' statement here */ +{ + int flab1; + int flab2; + int gotbreak; + + /* Skip the if */ + gettok (); + + /* Generate a jump label and parse the condition */ + flab1 = GetLabel (); + test (flab1, 0); + + /* Parse the if body */ + gotbreak = statement (); + + /* Else clause present? */ + if (curtok != ELSE) { + + g_defloclabel (flab1); + /* Since there's no else clause, we're not sure, if the a break + * statement is really executed. + */ + return 0; + + } else { + + /* Skip the else */ + gettok (); + + /* If we had some sort of break statement at the end of the if clause, + * there's no need to generate an additional jump around the else + * clause, since the jump is never reached. + */ + if (!gotbreak) { + flab2 = GetLabel (); + g_jump (flab2); + } else { + /* Mark the label as unused */ + flab2 = 0; + } + g_defloclabel (flab1); + gotbreak &= statement (); + + /* Generate the label for the else clause */ + if (flab2) { + g_defloclabel (flab2); + } + + /* Done */ + return gotbreak; + } +} + + + +static void dowhile (char wtype) +/* Handle 'while' statement here */ +{ + int loop; + int lab; + + gettok (); + loop = GetLabel (); + lab = GetLabel (); + addloop (oursp, loop, lab, 0, 0); + g_defloclabel (loop); + if (wtype == 'w') { + + /* While loop */ + test (lab, 0); + + /* If the statement following the while loop is empty, that is, we have + * something like "while (1) ;", the test function ommitted the jump as + * an optimization. Since we know, the condition codes are set, we can + * do another small optimization here, and use a conditional jump + * instead an absolute one. + */ + if (curtok == SEMI) { + /* Shortcut */ + gettok (); + /* Use a conditional jump */ + g_truejump (CF_NONE, loop); + } else { + /* There is code inside the while loop */ + statement (); + g_jump (loop); + g_defloclabel (lab); + } + + } else { + + /* Do loop */ + statement (); + Consume (WHILE, ERR_WHILE_EXPECTED); + test (loop, 1); + ConsumeSemi (); + g_defloclabel (lab); + + } + delloop (); +} + + + +static void doreturn (void) +/* Handle 'return' statement here */ +{ + struct expent lval; + unsigned etype = 0; /* Type of return expression */ + int HaveVal = 0; /* Do we have a return value in ax? */ + + + gettok (); + if (curtok != SEMI) { + if (HasVoidReturn (CurrentFunc)) { + Error (ERR_CANNOT_RETURN_VALUE); + } + if (evalexpr (CF_NONE, hie0, &lval) == 0) { + /* Constant value */ + etype = CF_CONST; + } else { + /* Value in the primary register */ + HaveVal = 1; + } + + /* Convert the return value to the type of the function result */ + if (!HasVoidReturn (CurrentFunc)) { + etype |= assignadjust (GetReturnType (CurrentFunc), &lval) & ~CF_CONST; + } + } else if (!HasVoidReturn (CurrentFunc)) { + Error (ERR_MUST_RETURN_VALUE); + } + RestoreRegVars (HaveVal); + g_leave (etype, lval.e_const); +} + + + +static void dobreak (void) +/* Handle 'break' statement here */ +{ + struct loopdesc* l; + + gettok (); + if ((l = currentloop ()) == 0) { + /* Error: No current loop */ + return; + } + g_space (oursp - l->sp); + g_jump (l->label); +} + + + +static void docontinue (void) +/* Handle 'continue' statement here */ +{ + struct loopdesc* l; + + gettok (); + if ((l = currentloop ()) == 0) { + /* Error: Not in loop */ + return; + } + do { + if (l->loop) { + break; + } + l = l->next; + } while (l); + if (l == 0) { + Error (ERR_UNEXPECTED_CONTINUE); + return; + } + g_space (oursp - l->sp); + if (l->linc) { + g_jump (l->linc); + } else { + g_jump (l->loop); + } +} + + + +static void cascadeswitch (struct expent* eval) +/* Handle a switch statement for chars with a cmp cascade for the selector */ +{ + unsigned exitlab; /* Exit label */ + unsigned nextlab; /* Next case label */ + unsigned codelab; /* Label that starts the actual selector code */ + int havebreak; /* Remember if we exited with break */ + int lcount; /* Label count */ + unsigned flags; /* Code generator flags */ + struct expent lval; /* Case label expression */ + long val; /* Case label value */ + + + /* Create a loop so we may break out, init labels */ + exitlab = GetLabel (); + addloop (oursp, 0, exitlab, 0, 0); + + /* Setup some variables needed in the loop below */ + flags = TypeOf (eval->e_tptr) | CF_CONST | CF_FORCECHAR; + codelab = nextlab = 0; + havebreak = 1; + + /* Parse the labels */ + lcount = 0; + while (curtok != RCURLY) { + + if (curtok == CASE || curtok == DEFAULT) { + + /* If the code for the previous selector did not end with a + * break statement, we must jump over the next selector test. + */ + if (!havebreak) { + /* Define a label for the code */ + if (codelab == 0) { + codelab = GetLabel (); + } + g_jump (codelab); + } + + /* If we have a cascade label, emit it */ + if (nextlab) { + g_defloclabel (nextlab); + nextlab = 0; + } + + while (curtok == CASE || curtok == DEFAULT) { + + /* Parse the selector */ + if (curtok == CASE) { + + /* Count labels */ + ++lcount; + + /* Skip the "case" token */ + gettok (); + + /* Read the selector expression */ + constexpr (&lval); + if (!IsInt (lval.e_tptr)) { + Error (ERR_ILLEGAL_TYPE); + } + + /* Check the range of the expression */ + val = lval.e_const; + switch (*eval->e_tptr) { + + case T_CHAR: + /* Signed char */ + if (val < -128 || val > 127) { + Error (ERR_RANGE); + } + break; + + case T_UCHAR: + if (val < 0 || val > 255) { + Error (ERR_RANGE); + } + break; + + case T_INT: + if (val < -32768 || val > 32767) { + Error (ERR_RANGE); + } + break; + + case T_UINT: + if (val < 0 || val > 65535) { + Error (ERR_RANGE); + } + break; + + default: + Internal ("Invalid type: %02X", *eval->e_tptr & 0xFF); + } + + /* Skip the colon */ + ConsumeColon (); + + /* Emit a compare */ + g_cmp (flags, val); + + /* If another case follows, we will jump to the code if + * the condition is true. + */ + if (curtok == CASE) { + /* Create a code label if needed */ + if (codelab == 0) { + codelab = GetLabel (); + } + g_falsejump (CF_NONE, codelab); + } else if (curtok != DEFAULT) { + /* No case follows, jump to next selector */ + if (nextlab == 0) { + nextlab = GetLabel (); + } + g_truejump (CF_NONE, nextlab); + } + + } else { + + /* Default case */ + gettok (); + + /* Skip the colon */ + ConsumeColon (); + + /* Handle the pathologic case: DEFAULT followed by CASE */ + if (curtok == CASE) { + if (codelab == 0) { + codelab = GetLabel (); + } + g_jump (codelab); + } + } + + } + + } + + /* Emit a code label if we have one */ + if (codelab) { + g_defloclabel (codelab); + codelab = 0; + } + + /* Parse statements */ + if (curtok != RCURLY) { + havebreak = statement (); + } + } + + /* Check if we have any labels */ + if (lcount == 0) { + Warning (WARN_NO_CASE_LABELS); + } + + /* Eat the closing curly brace */ + gettok (); + + /* Define the exit label and, if there's a next label left, create this + * one, too. + */ + if (nextlab) { + g_defloclabel (nextlab); + } + g_defloclabel (exitlab); + + /* End the loop */ + delloop (); +} + + + +static void tableswitch (struct expent* eval) +/* Handle a switch statement via table based selector */ +{ + /* Entry for one case in a switch statement */ + struct swent { + long sw_const; /* selector value */ + unsigned sw_lab; /* label for this selector */ + }; + + int dlabel; /* for default */ + int lab; /* exit label */ + int label; /* label for case */ + int lcase; /* label for compares */ + int lcount; /* Label count */ + int havebreak; /* Last statement has a break */ + unsigned flags; /* Code generator flags */ + struct expent lval; /* Case label expression */ + struct swent *p; + struct swent *swtab; + + /* Allocate memory for the switch table */ + swtab = xmalloc (CASE_MAX * sizeof (struct swent)); + + /* Create a look so we may break out, init labels */ + havebreak = 0; /* Keep gcc silent */ + dlabel = 0; /* init */ + lab = GetLabel (); /* get exit */ + p = swtab; + addloop (oursp, 0, lab, 0, 0); + + /* Jump behind the code for the CASE labels */ + g_jump (lcase = GetLabel ()); + lcount = 0; + while (curtok != RCURLY) { + if (curtok == CASE || curtok == DEFAULT) { + if (lcount >= CASE_MAX) { + Fatal (FAT_TOO_MANY_CASE_LABELS); + } + label = GetLabel (); + do { + if (curtok == CASE) { + gettok (); + constexpr (&lval); + if (!IsInt (lval.e_tptr)) { + Error (ERR_ILLEGAL_TYPE); + } + p->sw_const = lval.e_const; + p->sw_lab = label; + ++p; + ++lcount; + } else { + gettok (); + dlabel = label; + } + ConsumeColon (); + } while (curtok == CASE || curtok == DEFAULT); + g_defloclabel (label); + havebreak = 0; + } + if (curtok != RCURLY) { + havebreak = statement (); + } + } + + /* Check if we have any labels */ + if (lcount == 0) { + Warning (WARN_NO_CASE_LABELS); + } + + /* Eat the closing curly brace */ + gettok (); + + /* If the last statement doesn't have a break or return, add one */ + if (!havebreak) { + g_jump (lab); + } + + /* Actual selector code goes here */ + g_defloclabel (lcase); + + /* Create the call to the switch subroutine */ + flags = TypeOf (eval->e_tptr); + g_switch (flags); + + /* First entry is negative of label count */ + g_defdata (CF_INT, -((int)lcount)-1, 0); + + /* Create the case selector table */ + AddCodeHint ("casetable"); + p = swtab; + while (lcount) { + g_case (flags, p->sw_lab, p->sw_const); /* Create one label */ + --lcount; + ++p; + } + + if (dlabel) { + g_jump (dlabel); + } + g_defloclabel (lab); + delloop (); + + /* Free the allocated space for the labels */ + xfree (swtab); +} + + + +static void doswitch (void) +/* Handle 'switch' statement here */ +{ + struct expent eval; /* Switch statement expression */ + + /* Eat the "switch" */ + gettok (); + + /* Read the switch expression */ + ConsumeLParen (); + intexpr (&eval); + ConsumeRParen (); + + /* result of expr is in P */ + ConsumeLCurly (); + + /* Now decide which sort of switch we will create: */ + if (IsChar (eval.e_tptr) || (FavourSize == 0 && IsInt (eval.e_tptr))) { + cascadeswitch (&eval); + } else { + tableswitch (&eval); + } +} + + + +static void dofor (void) +/* Handle 'for' statement here */ +{ + int loop; + int lab; + int linc; + int lstat; + struct expent lval1; + struct expent lval2; + struct expent lval3; + + gettok (); + loop = GetLabel (); + lab = GetLabel (); + linc = GetLabel (); + lstat = GetLabel (); + addloop (oursp, loop, lab, linc, lstat); + ConsumeLParen (); + if (curtok != SEMI) { /* exp1 */ + expression (&lval1); + } + ConsumeSemi (); + g_defloclabel (loop); + if (curtok != SEMI) { /* exp2 */ + boolexpr (&lval2); + g_truejump (CF_NONE, lstat); + g_jump (lab); + } else { + g_jump (lstat); + } + ConsumeSemi (); + g_defloclabel (linc); + if (curtok != RPAREN) { /* exp3 */ + expression (&lval3); + } + ConsumeRParen (); + g_jump (loop); + g_defloclabel (lstat); + statement (); + g_jump (linc); + g_defloclabel (lab); + delloop (); +} + + + +static int statement (void) +/* Statement parser. Called whenever syntax requires a statement. + * This routine performs that statement and returns 1 if it is a branch, + * 0 otherwise + */ +{ + struct expent lval; + + /* */ + if (curtok == IDENT && nxttok == COLON) { + + /* Special handling for a label */ + DoLabel (); + + } else { + + switch (curtok) { + + case LCURLY: + return compound (); + + case IF: + return doif (); + + case WHILE: + dowhile ('w'); + break; + + case DO: + dowhile ('d'); + break; + + case SWITCH: + doswitch (); + break; + + case RETURN: + doreturn (); + ConsumeSemi (); + return 1; + + case BREAK: + dobreak (); + ConsumeSemi (); + return 1; + + case CONTINUE: + docontinue (); + ConsumeSemi (); + return 1; + + case FOR: + dofor (); + break; + + case GOTO: + DoGoto (); + ConsumeSemi (); + return 1; + + case SEMI: + /* ignore it. */ + gettok (); + break; + + case PRAGMA: + DoPragma (); + break; + + default: + AddCodeHint ("stmt:start"); + expression (&lval); + AddCodeHint ("stmt:end"); + ConsumeSemi (); + } + } + return 0; +} + + + +int compound (void) +/* Compound statement. Allow any number of statements, inside braces. */ +{ + static unsigned CurrentLevel = 0; + + int isbrk; + int oldsp; + + /* eat LCURLY */ + gettok (); + + /* Remember the stack at block entry */ + oldsp = oursp; + + /* If we're not on function level, enter a new lexical level */ + if (CurrentLevel++ > 0) { + /* A nested block */ + EnterBlockLevel (); + } + + /* Parse local variable declarations if any */ + DeclareLocals (); + + /* Now process statements in the function body */ + isbrk = 0; + while (curtok != RCURLY) { + if (curtok == CEOF) + break; + else { + isbrk = statement (); + } + } + + /* Emit references to imports/exports for this block */ + EmitExternals (); + + /* If this is not the top level compound statement, clean up the stack. + * For a top level statement this will be done by the function exit code. + */ + if (--CurrentLevel != 0) { + /* Some sort of nested block */ + LeaveBlockLevel (); + if (isbrk) { + oursp = oldsp; + } else { + g_space (oursp - oldsp); + oursp = oldsp; + } + } + + /* Eat closing brace */ + ConsumeRCurly (); + + return isbrk; +} + + + diff --git a/src/cc65/stmt.h b/src/cc65/stmt.h new file mode 100644 index 000000000..70bfa99e3 --- /dev/null +++ b/src/cc65/stmt.h @@ -0,0 +1,30 @@ +/* + * stmt.h + * + * Ullrich von Bassewitz, 19.06.1998 + */ + + + +#ifndef STMT_H +#define STMT_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int compound (); +/* Compound statement. Allow any number of statements, inside braces. */ + + + +/* End of stmt.h */ + +#endif + + + diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c new file mode 100644 index 000000000..e2b1a5637 --- /dev/null +++ b/src/cc65/symentry.c @@ -0,0 +1,154 @@ +/*****************************************************************************/ +/* */ +/* symentry.c */ +/* */ +/* Symbol table entries for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "mem.h" +#include "symentry.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +SymEntry* NewSymEntry (const char* Name, unsigned Flags) +/* Create a new symbol table with the given name */ +{ + /* Get the length of the name */ + unsigned Len = strlen (Name); + + /* Allocate memory for the symbol entry */ + SymEntry* E = xmalloc (sizeof (SymEntry) + Len); + + /* Initialize the entry */ + E->NextHash = 0; + E->PrevSym = 0; + E->NextSym = 0; + E->Link = 0; + E->Owner = 0; + E->Flags = Flags; + E->Type = 0; + memcpy (E->Name, Name, Len+1); + + /* Return the new entry */ + return E; +} + + + +void FreeSymEntry (SymEntry* E) +/* Free a symbol entry */ +{ + TypeFree (E->Type); + xfree (E); +} + + + +void DumpSymEntry (FILE* F, const SymEntry* E) +/* Dump the given symbol table entry to the file in readable form */ +{ + static const struct { + const char* Name; + unsigned Val; + } Flags [] = { + /* Beware: Order is important! */ + { "SC_TYPEDEF", SC_TYPEDEF }, + { "SC_SFLD", SC_SFLD }, + { "SC_STRUCT", SC_STRUCT }, + { "SC_AUTO", SC_AUTO }, + { "SC_REGISTER", SC_REGISTER }, + { "SC_STATIC", SC_STATIC }, + { "SC_EXTERN", SC_EXTERN }, + { "SC_ENUM", SC_ENUM }, + { "SC_LABEL", SC_LABEL }, + { "SC_PARAM", SC_PARAM }, + { "SC_FUNC", SC_FUNC }, + { "SC_STORAGE", SC_STORAGE }, + { "SC_DEF", SC_DEF }, + { "SC_REF", SC_REF }, + { "SC_ZEROPAGE", SC_ZEROPAGE }, + }; + + unsigned I; + unsigned SymFlags; + + /* Print the name */ + fprintf (F, "%s:\n", E->Name); + + /* Print the flags */ + SymFlags = E->Flags; + fprintf (F, " Flags: "); + for (I = 0; I < sizeof (Flags) / sizeof (Flags[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Flags[I].Val) == Flags[I].Val) { + SymFlags &= ~Flags[I].Val; + fprintf (F, "%s ", Flags[I].Name); + } + } + if (SymFlags != 0) { + fprintf (F, "%04X", SymFlags); + } + fprintf (F, "\n"); + + /* Print the type */ + fprintf (F, " Type: "); + if (E->Type) { + PrintType (F, E->Type); + } else { + fprintf (F, "(none)\n"); + } +} + + + +int IsTypeDef (const SymEntry* E) +/* Return true if the given entry is a typedef entry */ +{ + return ((E->Flags & SC_TYPEDEF) == SC_TYPEDEF); +} + + + +void ChangeSymType (SymEntry* Entry, type* Type) +/* Change the type of the given symbol */ +{ + TypeFree (Entry->Type); + Entry->Type = TypeDup (Type); +} + + + + diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h new file mode 100644 index 000000000..82f9458b9 --- /dev/null +++ b/src/cc65/symentry.h @@ -0,0 +1,144 @@ +/*****************************************************************************/ +/* */ +/* symentry.h */ +/* */ +/* Symbol table entries for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SYMENTRY_H +#define SYMENTRY_H + + + +#include + +#include "datatype.h" + + + +/*****************************************************************************/ +/* struct SymEntry */ +/*****************************************************************************/ + + + +/* Storage classes and flags */ +#define SC_AUTO 0x0001U +#define SC_REGISTER 0x0002U /* Register variable, is in static storage */ +#define SC_STATIC 0x0004U +#define SC_EXTERN 0x0008U + +#define SC_ENUM 0x0010U /* An enum (numeric constant) */ +#define SC_LABEL 0x0020U /* A goto label */ +#define SC_PARAM 0x0040U /* This is a function parameter */ +#define SC_FUNC 0x0080U /* Function entry */ + +#define SC_STORAGE 0x0100U /* Symbol with associated storage */ +#define SC_DEFAULT 0x0200U /* Flag: default storage class was used */ + +#define SC_DEF 0x0400U /* Symbol is defined */ +#define SC_REF 0x0800U /* Symbol is referenced */ + +#define SC_TYPE 0x1000U /* This is a type, struct, typedef, etc. */ +#define SC_STRUCT 0x1001U /* Struct or union */ +#define SC_SFLD 0x1002U /* Struct or union field */ +#define SC_TYPEDEF 0x1003U /* A typedef */ + +#define SC_ZEROPAGE 0x8000U /* Symbol marked as zeropage */ + + + +/* Symbol table entry */ +typedef struct SymEntry SymEntry; +struct SymEntry { + SymEntry* NextHash; /* Next entry in hash list */ + SymEntry* PrevSym; /* Previous symbol in dl list */ + SymEntry* NextSym; /* Next symbol double linked list */ + SymEntry* Link; /* General purpose single linked list */ + struct SymTable* Owner; /* Symbol table the symbol is in */ + unsigned Flags; /* Symbol flags */ + type* Type; /* Symbol type */ + + /* Data that differs for the different symbol types */ + union { + + /* Offset for locals or struct members */ + int Offs; + + /* Label name for static symbols */ + unsigned Label; + + /* Value for enums */ + int EnumVal; + + /* Data for structs/unions */ + struct { + struct SymTable* SymTab; /* Member symbol table */ + unsigned Size; /* Size of the union/struct */ + } S; + + /* Data for functions */ + struct FuncDesc* Func; /* Function descriptor */ + + } V; + char Name[1]; /* Name, dynamically allocated */ +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +SymEntry* NewSymEntry (const char* Name, unsigned Flags); +/* Create a new symbol table with the given name */ + +void FreeSymEntry (SymEntry* E); +/* Free a symbol entry */ + +void DumpSymEntry (FILE* F, const SymEntry* E); +/* Dump the given symbol table entry to the file in readable form */ + +int IsTypeDef (const SymEntry* E); +/* Return true if the given entry is a typedef entry */ + +void ChangeSymType (SymEntry* Entry, type* Type); +/* Change the type of the given symbol */ + + + +/* End of symentry.h */ +#endif + + + diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c new file mode 100644 index 000000000..6db3fc4a1 --- /dev/null +++ b/src/cc65/symtab.c @@ -0,0 +1,990 @@ +/*****************************************************************************/ +/* */ +/* symtab.c */ +/* */ +/* Symbol table management for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include + +#include "asmcode.h" +#include "asmlabel.h" +#include "check.h" +#include "codegen.h" +#include "datatype.h" +#include "declare.h" +#include "error.h" +#include "funcdesc.h" +#include "global.h" +#include "hashstr.h" +#include "io.h" +#include "mem.h" +#include "symentry.h" +#include "symtab.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* An empty symbol table */ +SymTable EmptySymTab = { + 0, /* PrevTab */ + 0, /* SymHead */ + 0, /* SymTail */ + 0, /* SymCount */ + 1, /* Size */ + { 0 } /* Tab[1] */ +}; + +/* Symbol table sizes */ +#define SYMTAB_SIZE_GLOBAL 211U +#define SYMTAB_SIZE_FUNCTION 29U +#define SYMTAB_SIZE_BLOCK 13U +#define SYMTAB_SIZE_STRUCT 19U +#define SYMTAB_SIZE_LABEL 7U + +/* Predefined lexical levels */ +#define LEX_LEVEL_GLOBAL 1U + +/* The current and root symbol tables */ +static unsigned LexicalLevel = 0; /* For safety checks */ +static SymTable* SymTab0 = 0; +static SymTable* SymTab = 0; +static SymTable* StructTab0 = 0; +static SymTable* StructTab = 0; +static SymTable* EnumTab0 = 0; +static SymTable* EnumTab = 0; +static SymTable* LabelTab = 0; + + + +/*****************************************************************************/ +/* struct SymTable */ +/*****************************************************************************/ + + + +static SymTable* NewSymTable (unsigned Size) +/* Create and return a symbol table for the given lexical level */ +{ + unsigned I; + + /* Allocate memory for the table */ + SymTable* S = xmalloc (sizeof (SymTable) + (Size-1) * sizeof (SymEntry*)); + + /* Initialize the symbol table structure */ + S->PrevTab = 0; + S->SymHead = 0; + S->SymTail = 0; + S->SymCount = 0; + S->Size = Size; + for (I = 0; I < Size; ++I) { + S->Tab[I] = 0; + } + + /* Return the symbol table */ + return S; +} + + + +static void FreeSymTable (SymTable* S) +/* Free the given symbo table including all symbols */ +{ + /* Free all symbols */ + SymEntry* Sym = S->SymHead; + while (Sym) { + SymEntry* NextSym = Sym->NextSym; + FreeSymEntry (Sym); + Sym = NextSym; + } + + /* Free the table itself */ + xfree (S); +} + + + +/*****************************************************************************/ +/* Check symbols in a table */ +/*****************************************************************************/ + + + +static void CheckSymTable (SymTable* Tab) +/* Check a symbol table for open references, unused symbols ... */ +{ + SymEntry* Entry = Tab->SymHead; + while (Entry) { + + /* Get the storage flags for tne entry */ + unsigned Flags = Entry->Flags; + + /* Ignore typedef entries */ + if ((Flags & SC_TYPEDEF) != SC_TYPEDEF) { + + /* Check if the symbol is one with storage, and it if it was + * defined but not used. + */ + if (((Flags & SC_AUTO) || (Flags & SC_STATIC)) && (Flags & SC_EXTERN) == 0) { + if ((Flags & SC_DEF) && !(Flags & SC_REF)) { + if (Flags & SC_PARAM) { + Warning (WARN_UNUSED_PARM, Entry->Name); + } else { + Warning (WARN_UNUSED_ITEM, Entry->Name); + } + } + } + + /* If the entry is a label, check if it was defined in the function */ + if (Flags & SC_LABEL) { + if ((Flags & SC_DEF) == 0) { + /* Undefined label */ + Error (ERR_UNDEFINED_LABEL, Entry->Name); + } else if ((Flags & SC_REF) == 0) { + /* Defined but not used */ + Warning (WARN_UNUSED_ITEM, Entry->Name); + } + } + + } + + /* Next entry */ + Entry = Entry->NextSym; + } +} + + + +/*****************************************************************************/ +/* Handling of lexical levels */ +/*****************************************************************************/ + + + +void EnterGlobalLevel (void) +/* Enter the program global lexical level */ +{ + /* Safety */ + PRECONDITION (++LexicalLevel == LEX_LEVEL_GLOBAL); + + /* Create and assign the symbol table */ + SymTab0 = SymTab = NewSymTable (SYMTAB_SIZE_GLOBAL); + + /* Create and assign the struct table */ + StructTab0 = StructTab = NewSymTable (SYMTAB_SIZE_GLOBAL); + + /* Create and assign the enum table */ + EnumTab0 = EnumTab = NewSymTable (SYMTAB_SIZE_GLOBAL); +} + + + +void LeaveGlobalLevel (void) +/* Leave the program global lexical level */ +{ + /* Safety */ + PRECONDITION (LexicalLevel-- == LEX_LEVEL_GLOBAL); + + /* Check the tables */ + CheckSymTable (SymTab0); + + /* Dump the tables if requested */ + if (Debug) { + PrintSymTable (SymTab0, stdout, "Global symbol table"); + PrintSymTable (StructTab0, stdout, "Global struct table"); + PrintSymTable (EnumTab0, stdout, "Global enum table"); + } + + /* Don't delete the symbol and struct tables! */ + SymTab0 = SymTab = 0; + StructTab0 = StructTab = 0; + EnumTab0 = EnumTab = 0; +} + + + +void EnterFunctionLevel (void) +/* Enter function lexical level */ +{ + SymTable* S; + + /* New lexical level */ + ++LexicalLevel; + + /* Get a new symbol table and make it current */ + S = NewSymTable (SYMTAB_SIZE_FUNCTION); + S->PrevTab = SymTab; + SymTab = S; + + /* Get a new struct table and make it current */ + S = NewSymTable (SYMTAB_SIZE_FUNCTION); + S->PrevTab = StructTab; + StructTab = S; + + /* Get a new enum table and make it current */ + S = NewSymTable (SYMTAB_SIZE_FUNCTION); + S->PrevTab = EnumTab; + EnumTab = S; + + /* Create and assign a new label table */ + LabelTab = NewSymTable (SYMTAB_SIZE_LABEL); +} + + + +void RememberFunctionLevel (struct FuncDesc* F) +/* Remember the symbol tables for the level and leave the level without checks */ +{ + /* Leave the lexical level */ + --LexicalLevel; + + /* Remember the tables */ + F->SymTab = SymTab; + F->StructTab = StructTab; + F->EnumTab = EnumTab; + + /* Don't delete the tables */ + SymTab = SymTab->PrevTab; + StructTab = StructTab->PrevTab; + EnumTab = EnumTab->PrevTab; +} + + + +void ReenterFunctionLevel (struct FuncDesc* F) +/* Reenter the function lexical level using the existing tables from F */ +{ + /* New lexical level */ + ++LexicalLevel; + + /* Make the tables current again */ + F->SymTab->PrevTab = SymTab; + SymTab = F->SymTab; + + F->StructTab->PrevTab = StructTab; + StructTab = F->StructTab; + + F->EnumTab->PrevTab = EnumTab; + EnumTab = F->EnumTab; + + /* Create and assign a new label table */ + LabelTab = NewSymTable (SYMTAB_SIZE_LABEL); +} + + + +void LeaveFunctionLevel (void) +/* Leave function lexical level */ +{ + /* Leave the lexical level */ + --LexicalLevel; + + /* Check the tables */ + CheckSymTable (SymTab); + CheckSymTable (LabelTab); + + /* Drop the label table if it is empty */ + if (LabelTab->SymCount == 0) { + FreeSymTable (LabelTab); + } + + /* Don't delete the tables */ + SymTab = SymTab->PrevTab; + StructTab = StructTab->PrevTab; + EnumTab = EnumTab->PrevTab; + LabelTab = 0; +} + + + +void EnterBlockLevel (void) +/* Enter a nested block in a function */ +{ + SymTable* S; + + /* New lexical level */ + ++LexicalLevel; + + /* Get a new symbol table and make it current */ + S = NewSymTable (SYMTAB_SIZE_BLOCK); + S->PrevTab = SymTab; + SymTab = S; + + /* Get a new struct table and make it current */ + S = NewSymTable (SYMTAB_SIZE_BLOCK); + S->PrevTab = StructTab; + StructTab = S; + + /* Get a new enum table and make it current */ + S = NewSymTable (SYMTAB_SIZE_BLOCK); + S->PrevTab = EnumTab; + EnumTab = S; +} + + + +void LeaveBlockLevel (void) +/* Leave a nested block in a function */ +{ + /* Leave the lexical level */ + --LexicalLevel; + + /* Check the tables */ + CheckSymTable (SymTab); + + /* Don't delete the tables */ + SymTab = SymTab->PrevTab; + StructTab = StructTab->PrevTab; + EnumTab = EnumTab->PrevTab; +} + + + +void EnterStructLevel (void) +/* Enter a nested block for a struct definition */ +{ + SymTable* S; + + /* Get a new symbol table and make it current. Note: Structs and enums + * nested in struct scope are NOT local to the struct but visible in the + * outside scope. So we will NOT create a new struct or enum table. + */ + S = NewSymTable (SYMTAB_SIZE_BLOCK); + S->PrevTab = SymTab; + SymTab = S; +} + + + +void LeaveStructLevel (void) +/* Leave a nested block for a struct definition */ +{ + /* Don't delete the table */ + SymTab = SymTab->PrevTab; +} + + + +/*****************************************************************************/ +/* Find functions */ +/*****************************************************************************/ + + + +static SymEntry* FindSymInTable (const SymTable* T, const char* Name, unsigned Hash) +/* Search for an entry in one table */ +{ + /* Get the start of the hash chain */ + SymEntry* E = T->Tab [Hash % T->Size]; + while (E) { + /* Compare the name */ + if (strcmp (E->Name, Name) == 0) { + /* Found */ + return E; + } + /* Not found, next entry in hash chain */ + E = E->NextHash; + } + + /* Not found */ + return 0; +} + + + +static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) +/* Find the symbol with the given name in the table tree that starts with T */ +{ + /* Get the hash over the name */ + unsigned Hash = HashStr (Name); + + /* Check all symbol tables for the symbol */ + while (Tab) { + /* Try to find the symbol in this table */ + SymEntry* E = FindSymInTable (Tab, Name, Hash); + + /* Bail out if we found it */ + if (E != 0) { + return E; + } + + /* Repeat the search in the next higher lexical level */ + Tab = Tab->PrevTab; + } + + /* Not found */ + return 0; +} + + + +SymEntry* FindSym (const char* Name) +/* Find the symbol with the given name */ +{ + return FindSymInTree (SymTab, Name); +} + + + +SymEntry* FindStructSym (const char* Name) +/* Find the symbol with the given name in the struct table */ +{ + return FindSymInTree (StructTab, Name); +} + + + +SymEntry* FindEnumSym (const char* Name) +/* Find the symbol with the given name in the enum table */ +{ + return FindSymInTree (EnumTab, Name); +} + + + +SymEntry* FindStructField (const type* Type, const char* Name) +/* Find a struct field in the fields list */ +{ + SymEntry* Field = 0; + + /* The given type may actually be a pointer to struct */ + if (Type[0] == T_PTR) { + ++Type; + } + + /* Non-structs do not have any struct fields... */ + if (IsStruct (Type)) { + + const SymTable* Tab; + + /* Get a pointer to the struct/union type */ + const SymEntry* Struct = (const SymEntry*) Decode (Type+1); + CHECK (Struct != 0); + + /* Get the field symbol table from the struct entry. + * Beware: The table may not exist. + */ + Tab = Struct->V.S.SymTab; + + /* Now search in the struct symbol table */ + if (Tab) { + Field = FindSymInTable (Struct->V.S.SymTab, Name, HashStr (Name)); + } + } + + return Field; +} + + + +/*****************************************************************************/ +/* Add stuff to the symbol table */ +/*****************************************************************************/ + + + +static void AddSymEntry (SymTable* T, SymEntry* S) +/* Add a symbol to a symbol table */ +{ + /* Get the hash value for the name */ + unsigned Hash = HashStr (S->Name) % T->Size; + + /* Insert the symbol into the list of all symbols in this level */ + if (T->SymTail) { + T->SymTail->NextSym = S; + } + S->PrevSym = T->SymTail; + T->SymTail = S; + if (T->SymHead == 0) { + /* First symbol */ + T->SymHead = S; + } + T->SymCount++; + + /* Insert the symbol into the hash chain */ + S->NextHash = T->Tab[Hash]; + T->Tab[Hash] = S; + + /* Tell the symbol in which table it is */ + S->Owner = T; +} + + + +SymEntry* AddStructSym (const char* Name, unsigned Size, SymTable* Tab) +/* Add a struct/union entry and return it */ +{ + /* Do we have an entry with this name already? */ + SymEntry* Entry = FindSymInTable (StructTab, Name, HashStr (Name)); + if (Entry) { + + /* We do have an entry. This may be a forward, so check it. */ + if (Entry->Flags != SC_STRUCT) { + /* Existing symbol is not a struct */ + Error (ERR_SYMBOL_KIND); + } else if (Size > 0 && Entry->V.S.Size > 0) { + /* Both structs are definitions. */ + Error (ERR_MULTIPLE_DEFINITION, Name); + } else { + /* Define the struct size if it is given */ + if (Size > 0) { + Entry->V.S.SymTab = Tab; + Entry->V.S.Size = Size; + } + } + + } else { + + /* Create a new entry */ + Entry = NewSymEntry (Name, SC_STRUCT); + + /* Set the struct data */ + Entry->V.S.SymTab = Tab; + Entry->V.S.Size = Size; + + /* Add it to the current table */ + AddSymEntry (StructTab, Entry); + } + + /* Return the entry */ + return Entry; +} + + + +SymEntry* AddEnumSym (const char* Name, int Val) +/* Add an enum symbol to the symbol table and return it */ +{ + /* Do we have an entry with this name already? */ + SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); + if (Entry) { + if (Entry->Flags != SC_ENUM) { + Error (ERR_SYMBOL_KIND); + } else { + Error (ERR_MULTIPLE_DEFINITION, Name); + } + return Entry; + } + + /* Create a new entry */ + Entry = NewSymEntry (Name, SC_ENUM); + + /* Enum values are ints */ + Entry->Type = TypeDup (type_int); + + /* Set the enum data */ + Entry->V.EnumVal = Val; + + /* Add the entry to the symbol table */ + AddSymEntry (SymTab, Entry); + + /* Return the entry */ + return Entry; +} + + + +SymEntry* AddLabelSym (const char* Name, unsigned Flags) +/* Add a goto label to the label table */ +{ + /* Do we have an entry with this name already? */ + SymEntry* Entry = FindSymInTable (LabelTab, Name, HashStr (Name)); + if (Entry) { + + if ((Entry->Flags & SC_DEF) != 0 && (Flags & SC_DEF) != 0) { + /* Trying to define the label more than once */ + Error (ERR_MULTIPLE_DEFINITION, Name); + } + Entry->Flags |= Flags; + + } else { + + /* Create a new entry */ + Entry = NewSymEntry (Name, SC_LABEL | Flags); + + /* Set a new label number */ + Entry->V.Label = GetLabel (); + + /* Add the entry to the label table */ + AddSymEntry (LabelTab, Entry); + + } + + /* Return the entry */ + return Entry; +} + + + +SymEntry* AddLocalSym (const char* Name, type* Type, unsigned Flags, int Offs) +/* Add a local symbol and return the symbol entry */ +{ + SymEntry* Entry; + + /* Functions declared inside of functions do always have external linkage */ + if (Type != 0 && IsFunc (Type)) { + if ((Flags & (SC_DEFAULT | SC_EXTERN)) == 0) { + Warning (WARN_FUNC_MUST_BE_EXTERN); + } + Flags = SC_EXTERN; + } + + /* Do we have an entry with this name already? */ + Entry = FindSymInTable (SymTab, Name, HashStr (Name)); + if (Entry) { + + /* We have a symbol with this name already */ + Error (ERR_MULTIPLE_DEFINITION, Name); + + } else { + + /* Create a new entry */ + Entry = NewSymEntry (Name, Flags); + + /* Set the symbol attributes */ + Entry->Type = TypeDup (Type); + Entry->V.Offs = Offs; + + /* Add the entry to the symbol table */ + AddSymEntry (SymTab, Entry); + + } + + /* Return the entry */ + return Entry; +} + + + +SymEntry* AddGlobalSym (const char* Name, type* Type, unsigned Flags) +/* Add an external or global symbol to the symbol table and return the entry */ +{ + /* Functions must be inserted in the global symbol table */ + SymTable* Tab = IsFunc (Type)? SymTab0 : SymTab; + + /* Do we have an entry with this name already? */ + SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); + if (Entry) { + + type* EType; + + /* We have a symbol with this name already */ + if (Entry->Flags & SC_TYPE) { + Error (ERR_MULTIPLE_DEFINITION, Name); + return Entry; + } + + /* Get the type string of the existing symbol */ + EType = Entry->Type; + + /* If we are handling arrays, the old entry or the new entry may be an + * incomplete declaration. Accept this, and if the exsting entry is + * incomplete, complete it. + */ + if (IsArray (Type) && IsArray (EType)) { + + /* Get the array sizes */ + unsigned Size = Decode (Type + 1); + unsigned ESize = Decode (EType + 1); + + if ((Size != 0 && ESize != 0) || + TypeCmp (Type+DECODE_SIZE+1, EType+DECODE_SIZE+1) != 0) { + /* Types not identical: Duplicate definition */ + Error (ERR_MULTIPLE_DEFINITION, Name); + } else { + /* Check if we have a size in the existing definition */ + if (ESize == 0) { + /* Existing, size not given, use size from new def */ + Encode (EType + 1, Size); + } + } + + } else { + /* New type must be identical */ + if (!EqualTypes (EType, Type) != 0) { + Error (ERR_MULTIPLE_DEFINITION, Name); + } + + /* In case of a function, use the new type descriptor, since it + * contains pointers to the new symbol tables that are needed if + * an actual function definition follows. + */ + if (IsFunc (Type)) { + CopyEncode (Type+1, EType+1); + } + } + + /* Add the new flags */ + Entry->Flags |= Flags; + + } else { + + /* Create a new entry */ + Entry = NewSymEntry (Name, Flags); + + /* Set the symbol attributes */ + Entry->Type = TypeDup (Type); + + /* Add the entry to the symbol table */ + AddSymEntry (Tab, Entry); + } + + /* Return the entry */ + return Entry; +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +SymTable* GetSymTab (void) +/* Return the current symbol table */ +{ + return SymTab; +} + + + +static int EqualSymTables (SymTable* Tab1, SymTable* Tab2) +/* Compare two symbol tables. Return 1 if they are equal and 0 otherwise */ +{ + /* Compare the parameter lists */ + SymEntry* Sym1 = Tab1->SymHead; + SymEntry* Sym2 = Tab2->SymHead; + + /* Compare the fields */ + while (Sym1 && Sym2) { + + /* Compare this field */ + if (!EqualTypes (Sym1->Type, Sym2->Type)) { + /* Field types not equal */ + return 0; + } + + /* Get the pointers to the next fields */ + Sym1 = Sym1->NextSym; + Sym2 = Sym2->NextSym; + } + + /* Check both pointers against NULL to compare the field count */ + return (Sym1 == 0 && Sym2 == 0); +} + + + +int EqualTypes (const type* Type1, const type* Type2) +/* Recursively compare two types. Return 1 if the types match, return 0 + * otherwise. + */ +{ + int v1, v2; + SymEntry* Sym1; + SymEntry* Sym2; + SymTable* Tab1; + SymTable* Tab2; + FuncDesc* F1; + FuncDesc* F2; + + + /* Shortcut here: If the pointers are identical, the types are identical */ + if (Type1 == Type2) { + return 1; + } + + /* Compare two types. Determine, where they differ */ + while (*Type1 == *Type2 && *Type1 != T_END) { + + switch (*Type1) { + + case T_FUNC: + /* Compare the function descriptors */ + F1 = DecodePtr (Type1+1); + F2 = DecodePtr (Type2+1); + if ((F1->Flags & ~FD_IMPLICIT) != (F2->Flags & ~FD_IMPLICIT)) { + /* Flags differ */ + return 0; + } + + /* Compare the parameter lists */ + if (EqualSymTables (F1->SymTab, F2->SymTab) == 0 || + EqualSymTables (F1->StructTab, F2->StructTab) == 0 || + EqualSymTables (F1->EnumTab, F2->EnumTab) == 0) { + /* One of the tables is not identical */ + return 0; + } + + /* Skip the FuncDesc pointers to compare the return type */ + Type1 += DECODE_SIZE; + Type2 += DECODE_SIZE; + break; + + case T_ARRAY: + /* Check member count */ + v1 = Decode (Type1+1); + v2 = Decode (Type2+1); + if (v1 != 0 && v2 != 0 && v1 != v2) { + /* Member count given but different */ + return 0; + } + Type1 += DECODE_SIZE; + Type2 += DECODE_SIZE; + break; + + case T_STRUCT: + case T_UNION: + /* Compare the fields recursively. To do that, we fetch the + * pointer to the struct definition from the type, and compare + * the fields. + */ + Sym1 = DecodePtr (Type1+1); + Sym2 = DecodePtr (Type2+1); + + /* Get the field tables from the struct entry */ + Tab1 = Sym1->V.S.SymTab; + Tab2 = Sym2->V.S.SymTab; + + /* One or both structs may be forward definitions. In this case, + * the symbol tables are both non existant. Assume that the + * structs are equal in this case. + */ + if (Tab1 != 0 && Tab2 != 0) { + + if (EqualSymTables (Tab1, Tab2) == 0) { + /* Field lists are not equal */ + return 0; + } + + } + + /* Structs are equal */ + Type1 += DECODE_SIZE; + Type2 += DECODE_SIZE; + break; + } + ++Type1; + ++Type2; + } + + /* Done, types are equal */ + return 1; +} + + + +void MakeZPSym (const char* Name) +/* Mark the given symbol as zero page symbol */ +{ + /* Get the symbol table entry */ + SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); + + /* Mark the symbol as zeropage */ + if (Entry) { + Entry->Flags |= SC_ZEROPAGE; + } else { + Error (ERR_UNDEFINED_SYMBOL, Name); + } +} + + + +void PrintSymTable (const SymTable* Tab, FILE* F, const char* Header, ...) +/* Write the symbol table to the given file */ +{ + unsigned Len; + const SymEntry* Entry; + + /* Print the header */ + va_list ap; + va_start (ap, Header); + fputc ('\n', F); + Len = vfprintf (F, Header, ap); + va_end (ap); + fputc ('\n', F); + + /* Underline the header */ + while (Len--) { + fputc ('=', F); + } + fputc ('\n', F); + + /* Dump the table */ + Entry = Tab->SymHead; + if (Entry == 0) { + fprintf (F, "(empty)\n"); + } else { + while (Entry) { + DumpSymEntry (F, Entry); + Entry = Entry->NextSym; + } + } + fprintf (F, "\n\n\n"); +} + + + +void EmitExternals (void) +/* Write import/export statements for external symbols */ +{ + SymEntry* Entry; + + AddEmptyLine (); + + Entry = SymTab->SymHead; + while (Entry) { + unsigned Flags = Entry->Flags; + if (Flags & SC_EXTERN) { + /* Only defined or referenced externs */ + if ((Flags & SC_REF) != 0 && (Flags & SC_DEF) == 0) { + /* An import */ + g_defimport (Entry->Name, Flags & SC_ZEROPAGE); + } else if (Flags & SC_DEF) { + /* An export */ + g_defexport (Entry->Name, Flags & SC_ZEROPAGE); + } + } + Entry = Entry->NextSym; + } +} + + + diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h new file mode 100644 index 000000000..996941ebf --- /dev/null +++ b/src/cc65/symtab.h @@ -0,0 +1,187 @@ +/*****************************************************************************/ +/* */ +/* symtab.h */ +/* */ +/* Symbol table management for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* (C) 2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SYMTAB_H +#define SYMTAB_H + + + +#include + +#include "datatype.h" +#include "symentry.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Symbol table */ +typedef struct SymTable SymTable; +struct SymTable { + SymTable* PrevTab; /* Pointer to higher level symbol table */ + SymEntry* SymHead; /* Double linked list of symbols */ + SymEntry* SymTail; /* Double linked list of symbols */ + unsigned SymCount; /* Count of symbols in this table */ + unsigned Size; /* Size of table */ + SymEntry* Tab[1]; /* Actual table, dynamically allocated */ +}; + +/* An empty symbol table */ +extern SymTable EmptySymTab; + +/* Forwards */ +struct FuncDesc; + + + +/*****************************************************************************/ +/* Handling of lexical levels */ +/*****************************************************************************/ + + + +void EnterGlobalLevel (void); +/* Enter the program global lexical level */ + +void LeaveGlobalLevel (void); +/* Leave the program global lexical level */ + +void EnterFunctionLevel (void); +/* Enter function lexical level */ + +void RememberFunctionLevel (struct FuncDesc* F); +/* Remember the symbol tables for the level and leave the level without checks */ + +void ReenterFunctionLevel (struct FuncDesc* F); +/* Reenter the function lexical level using the existing tables from F */ + +void LeaveFunctionLevel (void); +/* Leave function lexical level */ + +void EnterBlockLevel (void); +/* Enter a nested block in a function */ + +void LeaveBlockLevel (void); +/* Leave a nested block in a function */ + +void EnterStructLevel (void); +/* Enter a nested block for a struct definition */ + +void LeaveStructLevel (void); +/* Leave a nested block for a struct definition */ + + + +/*****************************************************************************/ +/* Find functions */ +/*****************************************************************************/ + + + +SymEntry* FindSym (const char* Name); +/* Find the symbol with the given name */ + +SymEntry* FindStructSym (const char* Name); +/* Find the symbol with the given name in the struct table */ + +SymEntry* FindEnumSym (const char* Name); +/* Find the symbol with the given name in the enum table */ + +SymEntry* FindStructField (const type* TypeArray, const char* Name); +/* Find a struct field in the fields list */ + + + +/*****************************************************************************/ +/* Add stuff to the symbol table */ +/*****************************************************************************/ + + + +SymEntry* AddStructSym (const char* Name, unsigned Size, SymTable* Tab); +/* Add a struct/union entry and return it */ + +SymEntry* AddEnumSym (const char* Name, int Val); +/* Add an enum symbol to the symbol table and return it */ + +SymEntry* AddLabelSym (const char* Name, unsigned Flags); +/* Add a goto label to the symbol table */ + +SymEntry* AddLocalSym (const char* Name, type* Type, unsigned Flags, int Offs); +/* Add a local symbol and return the symbol entry */ + +SymEntry* AddGlobalSym (const char* Name, type* Type, unsigned Flags); +/* Add an external or global symbol to the symbol table and return the entry */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +SymTable* GetSymTab (void); +/* Return the current symbol table */ + +int EqualTypes (const type* t1, const type* t2); +/* Recursively compare two types. Return 1 if the types match, return 0 + * otherwise. + */ + +void MakeZPSym (const char* Name); +/* Mark the given symbol as zero page symbol */ + +void PrintSymTable (const SymTable* Tab, FILE* F, const char* Header, ...); +/* Write the symbol table to the given file */ + +void EmitExternals (void); +/* Write import/export statements for external symbols */ + + + +/* End of symtab.h */ + +#endif + + + + + + diff --git a/src/cc65/util.c b/src/cc65/util.c new file mode 100644 index 000000000..c39232dc0 --- /dev/null +++ b/src/cc65/util.c @@ -0,0 +1,63 @@ +/* + * util.c + * + * Ullrich von Bassewitz, 18.06.1998 + */ + + + +#include "util.h" + + + +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +int IsBlank (char c) +/* Return true if c is a space, tab or newline */ +{ + return (c == ' ' || c == '\t' || c == '\n'); +} + + + +int IsQuoteChar (char c) +/* Return true if c is a single or double quote */ +{ + return (c == '"' || c == '\''); +} + + + +int powerof2 (unsigned long val) +/* Return the exponent if val is a power of two. Return -1 if val is not a + * power of two. + */ +{ + int i; + unsigned long mask; + mask = 0x0001; + + for (i = 0; i < 32; ++i) { + if (val == mask) { + return i; + } + mask <<= 1; + } + return -1; +} + + + diff --git a/src/cc65/util.h b/src/cc65/util.h new file mode 100644 index 000000000..240a3ec34 --- /dev/null +++ b/src/cc65/util.h @@ -0,0 +1,38 @@ +/* + * util.h + * + * Ullrich von Bassewitz, 18.06.1998 + */ + + + +#ifndef UTIL_H +#define UTIL_H + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +int IsBlank (char c); +/* Return true if c is a space, tab or newline */ + +int IsQuoteChar (char c); +/* Return true if c is a single or double quote */ + +int powerof2 (unsigned long val); +/* Return the exponent if val is a power of two. Return -1 if val is not a + * power of two. + */ + + + +/* End of util.h */ + +#endif + + + diff --git a/src/cl65/.cvsignore b/src/cl65/.cvsignore new file mode 100644 index 000000000..cd4a303fb --- /dev/null +++ b/src/cl65/.cvsignore @@ -0,0 +1,3 @@ +cl65 +cl65.exe +.depend diff --git a/src/cl65/error.c b/src/cl65/error.c new file mode 100644 index 000000000..87200b081 --- /dev/null +++ b/src/cl65/error.c @@ -0,0 +1,107 @@ +/*****************************************************************************/ +/* */ +/* global.c */ +/* */ +/* Error handling for the cl65 compile and link utility */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "global.h" +#include "error.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Messages for internal compiler errors */ +const char _MsgCheckFailed [] = + "Check failed: `%s' (= %d), file `%s', line %u\n"; +const char _MsgPrecondition [] = + "Precondition violated: `%s' (= %d), file `%s', line %u\n"; +const char _MsgFail [] = + "%s, file `%s', line %u\n"; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Warning (const char* Format, ...) +/* Print a warning message */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "%s: ", ProgName); + vfprintf (stderr, Format, ap); + putc ('\n', stderr); + va_end (ap); +} + + + +void Error (const char* Format, ...) +/* Print an error message and die */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "%s: ", ProgName); + vfprintf (stderr, Format, ap); + putc ('\n', stderr); + va_end (ap); + exit (EXIT_FAILURE); +} + + + +void Internal (const char* Format, ...) +/* Print an internal error message and die */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "%s: Internal error: ", ProgName); + vfprintf (stderr, Format, ap); + putc ('\n', stderr); + va_end (ap); + exit (EXIT_FAILURE); +} + + + diff --git a/src/cl65/error.h b/src/cl65/error.h new file mode 100644 index 000000000..e029eb5c3 --- /dev/null +++ b/src/cl65/error.h @@ -0,0 +1,87 @@ +/*****************************************************************************/ +/* */ +/* global.h */ +/* */ +/* Error handling for the cl65 compile and link utility */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ERROR_H +#define ERROR_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Messages for internal compiler errors */ +extern const char _MsgCheckFailed []; +extern const char _MsgPrecondition []; +extern const char _MsgFail []; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Warning (const char* Format, ...); +/* Print a warning message */ + +void Error (const char* Format, ...); +/* Print an error message and die */ + +void Internal (const char* Format, ...); +/* Print an internal error message and die */ + +#define CHECK(c) \ + if (!(c)) \ + Internal (_MsgCheckFailed, #c, c, __FILE__, __LINE__) + +#define PRECONDITION(c) \ + if (!(c)) \ + Internal (_MsgPrecondition, #c, c, __FILE__, __LINE__) + +#define FAIL(s) \ + Internal (_MsgFail, s, __FILE__, __LINE__) + + + +/* End of error.h */ + +#endif + + + diff --git a/src/cl65/global.c b/src/cl65/global.c new file mode 100644 index 000000000..d56716e51 --- /dev/null +++ b/src/cl65/global.c @@ -0,0 +1,49 @@ +/*****************************************************************************/ +/* */ +/* global.c */ +/* */ +/* Global variables for the cl65 compile and link utility */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "global.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +const char* ProgName = "cl65"; /* Program name */ + + + diff --git a/src/cl65/global.h b/src/cl65/global.h new file mode 100644 index 000000000..499259442 --- /dev/null +++ b/src/cl65/global.h @@ -0,0 +1,56 @@ +/*****************************************************************************/ +/* */ +/* global.h */ +/* */ +/* Global variables for the cl65 compile and link utility */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef GLOBAL_H +#define GLOBAL_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +extern const char* ProgName; /* Program name */ + + + +/* End of global.h */ + +#endif + + + diff --git a/src/cl65/main.c b/src/cl65/main.c new file mode 100644 index 000000000..73201e515 --- /dev/null +++ b/src/cl65/main.c @@ -0,0 +1,853 @@ +/*****************************************************************************/ +/* */ +/* main.c */ +/* */ +/* Main module for the cl65 compile and link utility */ +/* */ +/* */ +/* */ +/* (C) 1999-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include +#include +#ifdef __WATCOMC__ +# include /* DOS, OS/2 and Windows */ +#else +# include "spawn.h" /* All others */ +#endif + +#include "../common/version.h" + +#include "global.h" +#include "error.h" +#include "mem.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Struct that describes a command */ +typedef struct CmdDesc_ CmdDesc; +struct CmdDesc_ { + char* Name; /* The command name */ + + unsigned ArgCount; /* Count of arguments */ + unsigned ArgMax; /* Maximum count of arguments */ + char** Args; /* The arguments */ + + unsigned FileCount; /* Count of files to translate */ + unsigned FileMax; /* Maximum count of files */ + char** Files; /* The files */ +}; + +/* Command descriptors for the different programs */ +static CmdDesc CC65 = { 0, 0, 0, 0, 0, 0, 0 }; +static CmdDesc CA65 = { 0, 0, 0, 0, 0, 0, 0 }; +static CmdDesc LD65 = { 0, 0, 0, 0, 0, 0, 0 }; + +/* File types */ +enum { + FILETYPE_UNKNOWN, + FILETYPE_C, + FILETYPE_ASM, + FILETYPE_OBJ, + FILETYPE_LIB +}; + +/* Default file type, used if type unknown */ +static unsigned DefaultFileType = FILETYPE_UNKNOWN; + +/* Variables controlling the steps we're doing */ +static int DontLink = 0; +static int DontAssemble = 0; + +/* The name of the output file, NULL if none given */ +static const char* OutputName = 0; + +/* The name of the linker configuration file if given */ +static const char* LinkerConfig = 0; + +/* The name of the first input file. This will be used to construct the + * executable file name if no explicit name is given. + */ +static const char* FirstInput = 0; + +/* The target system */ +enum { + TGT_UNKNOWN = -1, + TGT_NONE, + TGT_FIRSTREAL, + TGT_ATARI = TGT_FIRSTREAL, + TGT_C64, + TGT_C128, + TGT_ACE, + TGT_PLUS4, + TGT_CBM610, + TGT_PET, + TGT_NES, + TGT_APPLE2, + TGT_GEOS, + TGT_COUNT +} Target = TGT_UNKNOWN; + +/* Names of the target systems sorted by target name */ +static const char* TargetNames [] = { + "none", + "atari", + "c64", + "c128", + "ace", + "plus4", + "cbm610", + "pet", + "nes", + "apple2", + "geos", +}; + +/* Name of the crt0 object file and the runtime library */ +static char* TargetCRT0 = 0; +static char* TargetLib = 0; + + + +/*****************************************************************************/ +/* String handling */ +/*****************************************************************************/ + + + +static const char* FindExt (const char* Name) +/* Return a pointer to the file extension in Name or NULL if there is none */ +{ + const char* S; + + /* Get the length of the name */ + unsigned Len = strlen (Name); + if (Len < 2) { + return 0; + } + + /* Get a pointer to the last character */ + S = Name + Len - 1; + + /* Search for the dot, beware of subdirectories */ + while (S >= Name && *S != '.' && *S != '\\' && *S != '/') { + --S; + } + + /* Did we find an extension? */ + if (*S == '.') { + return S; + } else { + return 0; + } +} + + + +static char* ForceExt (const char* Name, const char* Ext) +/* Return a new filename with the new extension */ +{ + char* Out; + const char* P = FindExt (Name); + if (P == 0) { + /* No dot, add the extension */ + Out = Xmalloc (strlen (Name) + strlen (Ext) + 1); + strcpy (Out, Name); + strcat (Out, Ext); + } else { + Out = Xmalloc (P - Name + strlen (Ext) + 1); + memcpy (Out, Name, P - Name); + strcpy (Out + (P - Name), Ext); + } + return Out; +} + + + +/*****************************************************************************/ +/* Determine a file type */ +/*****************************************************************************/ + + + +static unsigned GetFileType (const char* File) +/* Determine the type of the given file */ +{ + /* Table mapping extensions to file types */ + static const struct { + const char* Ext; + unsigned Type; + } FileTypes [] = { + { ".c", FILETYPE_C }, + { ".s", FILETYPE_ASM }, + { ".asm", FILETYPE_ASM }, + { ".o", FILETYPE_OBJ }, + { ".obj", FILETYPE_OBJ }, + { ".a", FILETYPE_LIB }, + { ".lib", FILETYPE_LIB }, + }; + + unsigned I; + + /* Determine the file type by the extension */ + const char* Ext = FindExt (File); + + /* Do we have an extension? */ + if (Ext == 0) { + return DefaultFileType; + } + + /* Check for known extensions */ + for (I = 0; I < sizeof (FileTypes) / sizeof (FileTypes [0]); ++I) { + if (strcmp (FileTypes [I].Ext, Ext) == 0) { + /* Found */ + return FileTypes [I].Type; + } + } + + /* Not found, return the default */ + return DefaultFileType; +} + + + +/*****************************************************************************/ +/* Command structure handling */ +/*****************************************************************************/ + + + +static void CmdAddArg (CmdDesc* Cmd, const char* Arg) +/* Add a new argument to the command */ +{ + /* Expand the argument vector if needed */ + if (Cmd->ArgCount == Cmd->ArgMax) { + unsigned NewMax = Cmd->ArgMax + 10; + char** NewArgs = Xmalloc (NewMax * sizeof (char*)); + memcpy (NewArgs, Cmd->Args, Cmd->ArgMax * sizeof (char*)); + Xfree (Cmd->Args); + Cmd->Args = NewArgs; + Cmd->ArgMax = NewMax; + } + + /* Add a copy of the new argument, allow a NULL pointer */ + if (Arg) { + Cmd->Args [Cmd->ArgCount++] = StrDup (Arg); + } else { + Cmd->Args [Cmd->ArgCount++] = 0; + } +} + + + +static void CmdDelArgs (CmdDesc* Cmd, unsigned LastValid) +/* Remove all arguments with an index greater than LastValid */ +{ + while (Cmd->ArgCount > LastValid) { + Cmd->ArgCount--; + Xfree (Cmd->Args [Cmd->ArgCount]); + Cmd->Args [Cmd->ArgCount] = 0; + } +} + + + +static void CmdAddFile (CmdDesc* Cmd, const char* File) +/* Add a new file to the command */ +{ + /* Expand the file vector if needed */ + if (Cmd->FileCount == Cmd->FileMax) { + unsigned NewMax = Cmd->FileMax + 10; + char** NewFiles = Xmalloc (NewMax * sizeof (char*)); + memcpy (NewFiles, Cmd->Files, Cmd->FileMax * sizeof (char*)); + Xfree (Cmd->Files); + Cmd->Files = NewFiles; + Cmd->FileMax = NewMax; + } + + /* Add a copy of the file name, allow a NULL pointer */ + if (File) { + Cmd->Files [Cmd->FileCount++] = StrDup (File); + } else { + Cmd->Files [Cmd->FileCount++] = 0; + } +} + + + +static void CmdInit (CmdDesc* Cmd, const char* Path) +/* Initialize the command using the given path to the executable */ +{ + /* Remember the command */ + Cmd->Name = StrDup (Path); + + /* Use the command name as first argument */ + CmdAddArg (Cmd, Path); +} + + + +static void CmdSetOutput (CmdDesc* Cmd, const char* File) +/* Set the output file in a command desc */ +{ + CmdAddArg (Cmd, "-o"); + CmdAddArg (Cmd, File); +} + + + +static void CmdSetTarget (CmdDesc* Cmd, int Target) +/* Set the output file in a command desc */ +{ + if (Target == TGT_UNKNOWN) { + /* Use C64 as default */ + Target = TGT_C64; + } + + if (Target != TGT_NONE) { + CmdAddArg (Cmd, "-t"); + CmdAddArg (Cmd, TargetNames[Target]); + } +} + + + +/*****************************************************************************/ +/* Target handling */ +/*****************************************************************************/ + + + +static int MapTarget (const char* Name) +/* Map a target name to a system code. Abort on errors */ +{ + int I; + + /* Check for a numeric target */ + if (isdigit (*Name)) { + int Target = atoi (Name); + if (Target >= 0 && Target < TGT_COUNT) { + return Target; + } + } + + /* Check for a target string */ + for (I = 0; I < TGT_COUNT; ++I) { + if (strcmp (TargetNames [I], Name) == 0) { + return I; + } + } + + /* Not found */ + Error ("No such target system: `%s'", Name); + return -1; /* Not reached */ +} + + + +static void SetTargetFiles (void) +/* Set the target system files */ +{ + /* Determine the names of the default startup and library file */ + if (Target >= TGT_FIRSTREAL) { + + /* Get a pointer to the system name and its length */ + const char* TargetName = TargetNames [Target]; + unsigned TargetNameLen = strlen (TargetName); + + /* Set the startup file */ + TargetCRT0 = Xmalloc (TargetNameLen + 2 + 1); + strcpy (TargetCRT0, TargetName); + strcat (TargetCRT0, ".o"); + + /* Set the library file */ + TargetLib = Xmalloc (TargetNameLen + 4 + 1); + strcpy (TargetLib, TargetName); + strcat (TargetLib, ".lib"); + + } +} + + + +static void SetTargetByName (const char* Name) +/* Set the target system by name */ +{ + Target = MapTarget (Name); + SetTargetFiles (); +} + + + +/*****************************************************************************/ +/* Subprocesses */ +/*****************************************************************************/ + + + +static void ExecProgram (CmdDesc* Cmd) +/* Execute a subprocess with the given name/parameters. Exit on errors. */ +{ + /* Call the program */ + int Status = spawnvp (P_WAIT, Cmd->Name, Cmd->Args); + + /* Check the result code */ + if (Status < 0) { + /* Error executing the program */ + Error ("Cannot execute `%s': %s", Cmd->Name, strerror (errno)); + } else if (Status != 0) { + /* Called program had an error */ + exit (Status); + } +} + + + +static void Link (void) +/* Link the resulting executable */ +{ + unsigned I; + + /* If we have a linker config file given, set the linker config file. + * Otherwise set the target system. + */ + if (LinkerConfig) { + CmdAddArg (&LD65, "-C"); + CmdAddArg (&LD65, LinkerConfig); + } else { + if (Target == TGT_UNKNOWN) { + /* Use c64 instead */ + Target = TGT_C64; + } + SetTargetFiles (); + CmdSetTarget (&LD65, Target); + } + + /* Since linking is always the final step, if we have an output file name + * given, set it here. If we don't have an explicit output name given, + * try to build one from the name of the first input file. + */ + if (OutputName) { + + CmdAddArg (&LD65, "-o"); + CmdAddArg (&LD65, OutputName); + + } else if (FirstInput && FindExt (FirstInput)) { /* Only if ext present! */ + + char* Output = ForceExt (FirstInput, ""); + CmdAddArg (&LD65, "-o"); + CmdAddArg (&LD65, Output); + Xfree (Output); + + } + + /* If we have a startup file, add its name as a parameter */ + if (TargetCRT0) { + CmdAddArg (&LD65, TargetCRT0); + } + + /* Add all object files as parameters */ + for (I = 0; I < LD65.FileCount; ++I) { + CmdAddArg (&LD65, LD65.Files [I]); + } + + /* Add the system runtime library */ + if (TargetLib) { + CmdAddArg (&LD65, TargetLib); + } + + /* Terminate the argument list with a NULL pointer */ + CmdAddArg (&LD65, 0); + + /* Call the linker */ + ExecProgram (&LD65); +} + + + +static void Assemble (const char* File) +/* Assemble the given file */ +{ + /* Remember the current assembler argument count */ + unsigned ArgCount = CA65.ArgCount; + + /* If we won't link, this is the final step. In this case, set the + * output name. + */ + if (DontLink && OutputName) { + CmdSetOutput (&CA65, OutputName); + } else { + /* The object file name will be the name of the source file + * with .s replaced by ".o". Add this file to the list of + * linker files. + */ + char* ObjName = ForceExt (File, ".o"); + CmdAddFile (&LD65, ObjName); + Xfree (ObjName); + } + + /* Add the file as argument for the assembler */ + CmdAddArg (&CA65, File); + + /* Add a NULL pointer to terminate the argument list */ + CmdAddArg (&CA65, 0); + + /* Run the assembler */ + ExecProgram (&CA65); + + /* Remove the excess arguments */ + CmdDelArgs (&CA65, ArgCount); +} + + + +static void Compile (const char* File) +/* Compile the given file */ +{ + char* AsmName = 0; + + /* Remember the current assembler argument count */ + unsigned ArgCount = CC65.ArgCount; + + /* Set the target system */ + CmdSetTarget (&CC65, Target); + + /* If we won't link, this is the final step. In this case, set the + * output name. + */ + if (DontAssemble && OutputName) { + CmdSetOutput (&CC65, OutputName); + } else { + /* The assembler file name will be the name of the source file + * with .c replaced by ".s". + */ + AsmName = ForceExt (File, ".s"); + } + + /* Add the file as argument for the compiler */ + CmdAddArg (&CC65, File); + + /* Add a NULL pointer to terminate the argument list */ + CmdAddArg (&CC65, 0); + + /* Run the compiler */ + ExecProgram (&CC65); + + /* Remove the excess arguments */ + CmdDelArgs (&CC65, ArgCount); + + /* If this is not the final step, assemble the generated file, then + * remove it + */ + if (!DontAssemble) { + Assemble (AsmName); + if (remove (AsmName) < 0) { + Warning ("Cannot remove temporary file `%s': %s", + AsmName, strerror (errno)); + } + Xfree (AsmName); + } +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void Usage (void) +/* Print usage information and exit */ +{ + fprintf (stderr, + "Usage: %s [options] file\n" + "Options:\n" + "\t-A\t\tStrict ANSI mode\n" + "\t-C name\t\tUse linker config file\n" + "\t-Cl\t\tMake local variables static\n" + "\t-D sym[=defn]\tDefine a preprocessor symbol\n" + "\t-I path\t\tSet an include directory path\n" + "\t-Ln name\tCreate a VICE label file\n" + "\t-O\t\tOptimize code\n" + "\t-Oi\t\tOptimize code, inline functions\n" + "\t-Or\t\tOptimize code, honour the register keyword\n" + "\t-Os\t\tOptimize code, inline known C funtions\n" + "\t-S\t\tCompile but don't assemble and link\n" + "\t-V\t\tPrint the version number\n" + "\t-W\t\tSuppress warnings\n" + "\t-c\t\tCompiler and assemble but don't link\n" + "\t-d\t\tDebug mode\n" + "\t-g\t\tAdd debug info\n" + "\t-h\t\tHelp (this text)\n" + "\t-m name\t\tCreate a map file\n" + "\t-o name\t\tName the output file\n" + "\t-t system\tSet the target system\n" + "\t-v\t\tVerbose mode\n" + "\t-vm\t\tVerbose map file\n", + ProgName); +} + + + +static const char* GetArg (int* ArgNum, char* argv [], unsigned Len) +/* Get an option argument */ +{ + const char* Arg = argv [*ArgNum]; + if (Arg [Len] != '\0') { + /* Argument appended */ + return Arg + Len; + } else { + /* Separate argument */ + Arg = argv [*ArgNum + 1]; + if (Arg == 0) { + /* End of arguments */ + fprintf (stderr, "Option requires an argument: %s\n", argv [*ArgNum]); + exit (EXIT_FAILURE); + } + ++(*ArgNum); + return Arg; + } +} + + + +static void ArgError (const char* Arg) +/* Print an error about a wrong argument */ +{ + Error ("Unknown option: `%s', use -h for help", Arg); +} + + + +int main (int argc, char* argv []) +/* Utility main program */ +{ + int I; + + /* Initialize the command descriptors */ + CmdInit (&CC65, "cc65"); + CmdInit (&CA65, "ca65"); + CmdInit (&LD65, "ld65"); + + /* Check the parameters */ + I = 1; + while (I < argc) { + + /* Get the argument */ + const char* Arg = argv [I]; + + /* Check for an option */ + if (Arg [0] == '-') { + + switch (Arg [1]) { + + case 'A': + /* Strict ANSI mode (compiler) */ + CmdAddArg (&CC65, "-A"); + break; + + case 'C': + if (Arg[2] == 'l' && Arg[3] == '\0') { + /* Make local variables static */ + CmdAddArg (&CC65, "-Cl"); + } else { + /* Specify linker config file */ + LinkerConfig = GetArg (&I, argv, 2); + } + break; + + case 'D': + /* Define a preprocessor symbol (compiler) */ + CmdAddArg (&CC65, "-D"); + CmdAddArg (&CC65, GetArg (&I, argv, 2)); + break; + + case 'I': + /* Include directory (compiler) */ + CmdAddArg (&CC65, "-I"); + CmdAddArg (&CC65, GetArg (&I, argv, 2)); + break; + + case 'L': + if (Arg[2] == 'n') { + /* VICE label file (linker) */ + CmdAddArg (&LD65, "-Ln"); + CmdAddArg (&LD65, GetArg (&I, argv, 3)); + } else { + ArgError (Arg); + } + break; + + case 'O': + /* Optimize code (compiler, also covers -Oi and others) */ + CmdAddArg (&CC65, Arg); + break; + + case 'S': + /* Dont assemble and link the created files */ + DontLink = DontAssemble = 1; + break; + + case 'T': + /* Include source as comment (compiler) */ + CmdAddArg (&CC65, "-T"); + break; + + case 'V': + /* Print version number */ + fprintf (stderr, + "cl65 V%u.%u.%u - (C) Copyright 1998-99 Ullrich von Bassewitz\n", + VER_MAJOR, VER_MINOR, VER_PATCH); + break; + + case 'W': + /* Suppress warnings - compiler and assembler */ + CmdAddArg (&CC65, "-W"); + CmdAddArg (&CA65, "-W"); + CmdAddArg (&CA65, "0"); + break; + + case 'c': + /* Don't link the resulting files */ + DontLink = 1; + break; + + case 'd': + /* Debug mode (compiler) */ + CmdAddArg (&CC65, "-d"); + break; + + case 'g': + /* Debugging - add to compiler and assembler */ + CmdAddArg (&CC65, "-g"); + CmdAddArg (&CA65, "-g"); + break; + + case 'h': + case '?': + /* Print help - cl65 */ + Usage (); + exit (EXIT_SUCCESS); + break; + + case 'm': + /* Create a map file (linker) */ + CmdAddArg (&LD65, "-m"); + CmdAddArg (&LD65, GetArg (&I, argv, 2)); + break; + + case 'o': + /* Name the output file */ + OutputName = GetArg (&I, argv, 2); + break; + + case 't': + /* Set target system - compiler and linker */ + SetTargetByName (GetArg (&I, argv, 2)); + break; + + case 'v': + if (Arg [2] == 'm') { + /* Verbose map file (linker) */ + CmdAddArg (&LD65, "-vm"); + } else { + /* Verbose mode (compiler, assembler, linker) */ + CmdAddArg (&CC65, "-v"); + CmdAddArg (&CA65, "-v"); + CmdAddArg (&LD65, "-v"); + } + break; + + default: + ArgError (Arg); + } + } else { + + /* Remember the first file name */ + if (FirstInput == 0) { + FirstInput = Arg; + } + + /* Determine the file type by the extension */ + switch (GetFileType (Arg)) { + + case FILETYPE_C: + /* Compile the file */ + Compile (Arg); + break; + + case FILETYPE_ASM: + /* Assemble the file */ + if (!DontAssemble) { + Assemble (Arg); + } + break; + + case FILETYPE_OBJ: + case FILETYPE_LIB: + /* Add to the linker files */ + CmdAddFile (&LD65, Arg); + break; + + default: + Error ("Don't know what to do with `%s'", Arg); + + } + + } + + /* Next argument */ + ++I; + } + + /* Check if we had any input files */ + if (FirstInput == 0) { + Warning ("No input files"); + } + + /* Link the given files if requested and if we have any */ + if (DontLink == 0 && LD65.FileCount > 0) { + Link (); + } + + /* Return an apropriate exit code */ + return EXIT_SUCCESS; +} + + + diff --git a/src/cl65/make/gcc.mak b/src/cl65/make/gcc.mak new file mode 100644 index 000000000..445bdcf5d --- /dev/null +++ b/src/cl65/make/gcc.mak @@ -0,0 +1,47 @@ +# +# Makefile for the cl65 compile&link utility +# + +CC=gcc +CFLAGS = -O2 -g -Wall +LDFLAGS= + +OBJS = error.o \ + global.o \ + main.o \ + mem.o \ + spawn.o + +EXECS = cl65 + + +.PHONY: all +ifeq (.depend,$(wildcard .depend)) +all : $(EXECS) +include .depend +else +all: depend + @$(MAKE) -f make/gcc.mak all +endif + + +cl65: $(OBJS) + $(CC) $(LDFLAGS) -o cl65 $(CFLAGS) $(OBJS) + +clean: + rm -f *~ core + +zap: clean + rm -f *.o $(EXECS) .depend + + +# ------------------------------------------------------------------------------ +# Make the dependencies + +.PHONY: depend dep +depend dep: $(OBJS:.o=.c) + @echo "Creating dependency information" + $(CC) -MM $^ > .depend + + + diff --git a/src/cl65/make/watcom.mak b/src/cl65/make/watcom.mak new file mode 100644 index 000000000..ca5e57644 --- /dev/null +++ b/src/cl65/make/watcom.mak @@ -0,0 +1,95 @@ +# +# CL65 Makefile for the Watcom compiler +# + +# ------------------------------------------------------------------------------ +# Generic stuff + +.AUTODEPEND +.SUFFIXES .ASM .C .CC .CPP +.SWAP + +AR = WLIB +LD = WLINK + +!if !$d(TARGET) +!if $d(__OS2__) +TARGET = OS2 +!else +TARGET = NT +!endif +!endif + +# target specific macros. +!if $(TARGET)==OS2 + +# --------------------- OS2 --------------------- +SYSTEM = os2v2 +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS32 + +# -------------------- DOS4G -------------------- +SYSTEM = dos4g +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS + +# --------------------- DOS --------------------- +SYSTEM = dos +CC = WCC +CCCFG = -bt=$(TARGET) -d1 -onatx -zp2 -2 -ml -zq -w2 + +!elif $(TARGET)==NT + +# --------------------- NT ---------------------- +SYSTEM = nt +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!else +!error +!endif + +# ------------------------------------------------------------------------------ +# Implicit rules + +.c.obj: + $(CC) $(CCCFG) $< + + +# ------------------------------------------------------------------------------ +# All OBJ files + +OBJS = error.obj \ + global.obj \ + main.obj \ + mem.obj + +.PRECIOUS $(OBJS:.obj=.c) + +# ------------------------------------------------------------------------------ +# Main targets + +all: cl65 + +cl65: cl65.exe + + +# ------------------------------------------------------------------------------ +# Other targets + + +cl65.exe: $(OBJS) + $(LD) system $(SYSTEM) @&&| +DEBUG ALL +OPTION QUIET +NAME $< +FILE error.obj +FILE global.obj +FILE main.obj +FILE mem.obj +| + diff --git a/src/cl65/mem.c b/src/cl65/mem.c new file mode 100644 index 000000000..879d3e79e --- /dev/null +++ b/src/cl65/mem.c @@ -0,0 +1,85 @@ +/*****************************************************************************/ +/* */ +/* mem.c */ +/* */ +/* Memory allocation for the cl65 compile and link utility */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "error.h" +#include "mem.h" + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void* Xmalloc (size_t size) +/* Allocate memory, check for out of memory condition. Do some debugging */ +{ + void* p; + + p = malloc (size); + if (p == 0 && size != 0) { + Error ("Out of memory"); + } + + /* Return a pointer to the block */ + return p; +} + + + +void Xfree (const void* block) +/* Free the block, do some debugging */ +{ + free ((void*) block); +} + + + +char* StrDup (const char* s) +/* Duplicate a string on the heap. The function checks for out of memory */ +{ + unsigned len; + + len = strlen (s) + 1; + return memcpy (Xmalloc (len), s, len); +} + + + + diff --git a/src/cl65/mem.h b/src/cl65/mem.h new file mode 100644 index 000000000..f66a56ac6 --- /dev/null +++ b/src/cl65/mem.h @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* */ +/* mem.h */ +/* */ +/* Memory allocation for the cl65 compile and link utility */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef MEM_H +#define MEM_H + + + +#include + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void* Xmalloc (size_t size); +/* Allocate memory, check for out of memory condition. Do some debugging */ + +void Xfree (const void* block); +/* Free the block, do some debugging */ + +char* StrDup (const char* s); +/* Duplicate a string on the heap. The function checks for out of memory */ + + + +/* End of mem.h */ + +#endif + + + diff --git a/src/cl65/spawn.c b/src/cl65/spawn.c new file mode 100644 index 000000000..8a43087c3 --- /dev/null +++ b/src/cl65/spawn.c @@ -0,0 +1,95 @@ +/*****************************************************************************/ +/* */ +/* spawn.c */ +/* */ +/* Execute other external programs */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include +#include + +#include "error.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int spawnvp (int Mode, const char* File, char* const argv []) +/* Execute the given program searching and wait til it terminates. The Mode + * argument is ignored (compatibility only). The result of the function is + * the return code of the program. The function will terminate the program + * on errors. + */ +{ + int Status = 0; + + /* Fork */ + int pid = fork (); + if (pid < 0) { + + /* Error forking */ + Error ("Cannot fork: %s", strerror (errno)); + + } else if (pid == 0) { + + /* The son - exec the program */ + if (execvp (File, argv) < 0) { + Error ("Cannot exec `%s': %s", File, strerror (errno)); + } + + } else { + + /* The father: Wait for the subprocess to terminate */ + if (waitpid (pid, &Status, 0) < 0) { + Error ("Failure waiting for subprocess: %s", strerror (errno)); + } + + /* Examine the child status */ + if (!WIFEXITED (Status)) { + Error ("Subprocess `%s' aborted by signal %d", File, WTERMSIG (Status)); + } + } + + /* Only the father goes here, we place a return here regardless of that + * to avoid compiler warnings. + */ + return WEXITSTATUS (Status); +} + + + diff --git a/src/cl65/spawn.h b/src/cl65/spawn.h new file mode 100644 index 000000000..86f50654a --- /dev/null +++ b/src/cl65/spawn.h @@ -0,0 +1,75 @@ +/*****************************************************************************/ +/* */ +/* spawn.h */ +/* */ +/* Execute other external programs */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SPAWN_H +#define SPAWN_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Mode argument for spawn. This value is ignored by the function and only + * provided for DOS/Windows compatibility. + */ +#ifndef P_WAIT +#define P_WAIT 0 +#endif + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int spawnvp (int Mode, const char* File, char* const argv []); +/* Execute the given program searching and wait til it terminates. The Mode + * argument is ignored (compatibility only). The result of the function is + * the return code of the program. The function will terminate the program + * on errors. + */ + + + +/* End of spawn.h */ +#endif + + + diff --git a/src/common/.cvsignore b/src/common/.cvsignore new file mode 100644 index 000000000..ec0600e2e --- /dev/null +++ b/src/common/.cvsignore @@ -0,0 +1,3 @@ +.depend +common.o +common.lib diff --git a/src/common/bitops.c b/src/common/bitops.c new file mode 100644 index 000000000..071b7102c --- /dev/null +++ b/src/common/bitops.c @@ -0,0 +1,128 @@ +/*****************************************************************************/ +/* */ +/* bitops.c */ +/* */ +/* Single bit operations */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "bitops.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned BitFind (unsigned long Val) +/* Find the first bit that is set in Val. Val must *not* be zero */ +{ + unsigned long Mask; + unsigned Bit; + + /* Search for the bits */ + Mask = 1; + Bit = 0; + while (1) { + if (Val & Mask) { + return Bit; + } + Mask <<= 1; + ++Bit; + } +} + + + +void BitSet (void* Data, unsigned Bit) +/* Set a bit in a char array */ +{ + /* Make a char pointer */ + unsigned char* D = Data; + + /* Set the bit */ + D [Bit / 8] |= 0x01 << (Bit % 8); +} + + + +void BitReset (void* Data, unsigned Bit) +/* Reset a bit in a char array */ +{ + /* Make a char pointer */ + unsigned char* D = Data; + + /* Set the bit */ + D [Bit / 8] &= ~(0x01 << (Bit % 8)); +} + + + +int BitIsSet (void* Data, unsigned Bit) +/* Check if a bit is set in a char array */ +{ + /* Make a char pointer */ + unsigned char* D = Data; + + /* Check the bit state */ + return (D [Bit / 8] & (0x01 << (Bit % 8))) != 0; +} + + + +int BitIsReset (void* Data, unsigned Bit) +/* Check if a bit is reset in a char array */ +{ + /* Make a char pointer */ + unsigned char* D = Data; + + /* Check the bit state */ + return (D [Bit / 8] & (0x01 << (Bit % 8))) == 0; +} + + + +void BitMerge (void* Target, const void* Source, unsigned Size) +/* Merge the bits of two char arrays (that is, do an or for the full array) */ +{ + /* Make char arrays */ + unsigned char* T = Target; + const unsigned char* S = Source; + + /* Merge the arrays */ + while (Size--) { + *T++ |= *S++; + } +} + + + diff --git a/src/common/bitops.h b/src/common/bitops.h new file mode 100644 index 000000000..8786fd016 --- /dev/null +++ b/src/common/bitops.h @@ -0,0 +1,72 @@ +/*****************************************************************************/ +/* */ +/* bitops.h */ +/* */ +/* Single bit operations */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef BITOPS_H +#define BITOPS_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned BitFind (unsigned long Val); +/* Find the first bit that is set in Val. Val must *not* be zero */ + +void BitSet (void* Data, unsigned Bit); +/* Set a bit in a char array */ + +void BitReset (void* Data, unsigned Bit); +/* Reset a bit in a char array */ + +int BitIsSet (void* Data, unsigned Bit); +/* Check if a bit is set in a char array */ + +int BitIsReset (void* Data, unsigned Bit); +/* Check if a bit is reset in a char array */ + +void BitMerge (void* Target, const void* Source, unsigned Size); +/* Merge the bits of two char arrays (that is, do an or for the full array) */ + + + +/* End of bitops.h */ + +#endif + + + diff --git a/src/common/exprdefs.h b/src/common/exprdefs.h new file mode 100644 index 000000000..8eca9ea4f --- /dev/null +++ b/src/common/exprdefs.h @@ -0,0 +1,124 @@ +/*****************************************************************************/ +/* */ +/* exprdefs.h */ +/* */ +/* Expression tree definitions */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef EXPRDEFS_H +#define EXPRDEFS_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Expression type masks */ +#define EXPR_TYPEMASK 0xC0 +#define EXPR_BINARYNODE 0x00 +#define EXPR_UNARYNODE 0x40 +#define EXPR_LEAFNODE 0x80 + +/* Type of expression nodes */ +#define EXPR_NULL 0x00 /* Internal error or NULL node */ + +/* Leaf node codes */ +#define EXPR_LITERAL (EXPR_LEAFNODE | 0x01) +#define EXPR_SYMBOL (EXPR_LEAFNODE | 0x02) +#define EXPR_SEGMENT (EXPR_LEAFNODE | 0x03) +#define EXPR_MEMAREA (EXPR_LEAFNODE | 0x04) /* Linker only */ +#define EXPR_ULABEL (EXPR_LEAFNODE | 0x05) /* Assembler only */ + +/* Binary operations, left and right hand sides are valid */ +#define EXPR_PLUS (EXPR_BINARYNODE | 0x01) +#define EXPR_MINUS (EXPR_BINARYNODE | 0x02) +#define EXPR_MUL (EXPR_BINARYNODE | 0x03) +#define EXPR_DIV (EXPR_BINARYNODE | 0x04) +#define EXPR_MOD (EXPR_BINARYNODE | 0x05) +#define EXPR_OR (EXPR_BINARYNODE | 0x06) +#define EXPR_XOR (EXPR_BINARYNODE | 0x07) +#define EXPR_AND (EXPR_BINARYNODE | 0x08) +#define EXPR_SHL (EXPR_BINARYNODE | 0x09) +#define EXPR_SHR (EXPR_BINARYNODE | 0x0A) +#define EXPR_EQ (EXPR_BINARYNODE | 0x0B) +#define EXPR_NE (EXPR_BINARYNODE | 0x0C) +#define EXPR_LT (EXPR_BINARYNODE | 0x0D) +#define EXPR_GT (EXPR_BINARYNODE | 0x0E) +#define EXPR_LE (EXPR_BINARYNODE | 0x0F) +#define EXPR_GE (EXPR_BINARYNODE | 0x10) +#define EXPR_BAND (EXPR_BINARYNODE | 0x11) +#define EXPR_BOR (EXPR_BINARYNODE | 0x12) +#define EXPR_BXOR (EXPR_BINARYNODE | 0x13) + +/* Unary operations, right hand side is empty */ +#define EXPR_UNARY_MINUS (EXPR_UNARYNODE | 0x01) +#define EXPR_NOT (EXPR_UNARYNODE | 0x02) +#define EXPR_LOBYTE (EXPR_UNARYNODE | 0x03) +#define EXPR_HIBYTE (EXPR_UNARYNODE | 0x04) +#define EXPR_SWAP (EXPR_UNARYNODE | 0x05) +#define EXPR_BNOT (EXPR_UNARYNODE | 0x06) + + + +/* The expression node itself */ +typedef struct ExprNode_ ExprNode; +struct ExprNode_ { + unsigned char Op; /* Operand/Type */ + ExprNode* Left; /* Left leaf */ + ExprNode* Right; /* Right leaf */ + struct ObjData_* Obj; /* Object file reference (linker) */ + union { + long Val; /* If this is a value */ + struct SymEntry_* Sym; /* If this is a symbol */ + unsigned SegNum; /* If this is a segment */ + unsigned ImpNum; /* If this is an import */ + struct Memory_* MemArea; /* If this is a memory area */ + } V; +}; + + + +/* Macros to determine the expression type */ +#define EXPR_IS_LEAF(Op) (((Op) & EXPR_TYPEMASK) == EXPR_LEAFNODE) +#define EXPR_IS_UNARY(Op) (((Op) & EXPR_TYPEMASK) == EXPR_UNARYNODE) +#define EXPR_IS_BINARY(OP) (((Op) & EXPR_TYPEMASK) == EXPR_BINARYNODE) + + + +/* End of exprdefs.h */ + +#endif + + + diff --git a/src/common/filepos.h b/src/common/filepos.h new file mode 100644 index 000000000..8378fd4b8 --- /dev/null +++ b/src/common/filepos.h @@ -0,0 +1,65 @@ +/*****************************************************************************/ +/* */ +/* filepos.h */ +/* */ +/* File position data structure */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef FILEPOS_H +#define FILEPOS_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Size of position in file */ +#define POS_SIZE 5 + +/* Type of a file position */ +typedef struct FilePos_ FilePos; +struct FilePos_ { + unsigned long Line; /* Line */ + unsigned char Col; /* Column */ + unsigned char Name; /* File */ +}; + + + +/* End of filepos.h */ + +#endif + + + diff --git a/src/common/hashstr.c b/src/common/hashstr.c new file mode 100644 index 000000000..0d2eb0572 --- /dev/null +++ b/src/common/hashstr.c @@ -0,0 +1,56 @@ +/*****************************************************************************/ +/* */ +/* hashstr.c */ +/* */ +/* Hash function for strings */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned HashStr (const char* S) +/* Return a hash value for the given string */ +{ + unsigned L, H; + + /* Do the hash */ + H = L = 0; + while (*S) { + H = ((H << 3) ^ ((unsigned char) *S++)) + L++; + } + return H; +} + + + diff --git a/src/common/hashstr.h b/src/common/hashstr.h new file mode 100644 index 000000000..af7f2796c --- /dev/null +++ b/src/common/hashstr.h @@ -0,0 +1,57 @@ +/*****************************************************************************/ +/* */ +/* hashstr.h */ +/* */ +/* Hash function for strings */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef HASHSTR_H +#define HASHSTR_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned HashStr (const char* S); +/* Return a hash value for the given string */ + + + +/* End of hashstr.h */ + +#endif + + + diff --git a/src/common/libdefs.h b/src/common/libdefs.h new file mode 100644 index 000000000..1cda2415c --- /dev/null +++ b/src/common/libdefs.h @@ -0,0 +1,72 @@ +/*****************************************************************************/ +/* */ +/* libdefs.h */ +/* */ +/* Library file definitions */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef LIBDEFS_H +#define LIBDEFS_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Defines for magic and version */ +#define LIB_MAGIC 0x7A55616E +#define LIB_VERSION 0x0004 + +/* Size of an library file header */ +#define LIB_HDR_SIZE 12 + + + +/* Header structure for the library */ +typedef struct LibHeader_ LibHeader; +struct LibHeader_ { + unsigned long Magic; /* 32: Magic number */ + unsigned Version; /* 16: Version number */ + unsigned Flags; /* 16: flags */ + unsigned long IndexOffs; /* 32: Offset to directory */ +}; + + + +/* End of libdefs.h */ + +#endif + + + diff --git a/src/common/make/gcc.mak b/src/common/make/gcc.mak new file mode 100644 index 000000000..4fd29a1d6 --- /dev/null +++ b/src/common/make/gcc.mak @@ -0,0 +1,48 @@ +# +# gcc Makefile for the binutils common stuff +# + +CFLAGS = -g -O2 -Wall +CC = gcc +LDFLAGS = +LIB = common.a + + + +OBJS = bitops.o \ + hashstr.o + + +# ------------------------------------------------------------------------------ +# Dummy targets + +.PHONY: all +ifeq (.depend,$(wildcard .depend)) +all: lib +include .depend +else +all: depend + @$(MAKE) -f make/gcc.mak all +endif + +.PHONY: lib +lib: $(LIB) + +$(LIB): $(OBJS) + $(AR) rs $(LIB) $? + +clean: + rm -f *~ core *.map + +zap: clean + rm -f *.o $(LIB) .depend + +# ------------------------------------------------------------------------------ +# Make the dependencies + +.PHONY: depend dep +depend dep: $(OBJS:.o=.c) + @echo "Creating dependency information" + $(CC) -MM $^ > .depend + + diff --git a/src/common/make/watcom.mak b/src/common/make/watcom.mak new file mode 100644 index 000000000..3c3cd8290 --- /dev/null +++ b/src/common/make/watcom.mak @@ -0,0 +1,92 @@ +# +# CC65 Makefile for the Watcom compiler +# + +# ------------------------------------------------------------------------------ +# Generic stuff + +.AUTODEPEND +.SUFFIXES .ASM .C .CC .CPP +.SWAP + +AR = WLIB +LD = WLINK + +LIB = common.lib + +!if !$d(TARGET) +!if $d(__OS2__) +TARGET = OS2 +!else +TARGET = NT +!endif +!endif + +# target specific macros. +!if $(TARGET)==OS2 + +# --------------------- OS2 --------------------- +SYSTEM = os2v2 +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS32 + +# -------------------- DOS4G -------------------- +SYSTEM = dos4g +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS + +# --------------------- DOS --------------------- +SYSTEM = dos +CC = WCC +CCCFG = -bt=$(TARGET) -d1 -onatx -zp2 -2 -ml -zq -w2 + +!elif $(TARGET)==NT + +# --------------------- NT ---------------------- +SYSTEM = nt +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!else +!error +!endif + +# ------------------------------------------------------------------------------ +# Implicit rules + +.c.obj: + $(CC) $(CCCFG) $< + + +# ------------------------------------------------------------------------------ +# All library OBJ files + +OBJS = bitops.obj \ + hashstr.obj \ + wildargv.obj + + +.PRECIOUS $(OBJS:.obj=.cc) $(LIB) + +# ------------------------------------------------------------------------------ +# Main targets + +all: lib + +lib: $(LIB) + +$(LIB): $(OBJS) + @echo Creating library... + &@$(AR) -q -b -P=32 $(LIB) +-$? + @echo Done! + +clean: + @if exist *.obj del *.obj + @if exist $(LIB) del $(LIB) + + + diff --git a/src/common/objdefs.h b/src/common/objdefs.h new file mode 100644 index 000000000..5a17ea888 --- /dev/null +++ b/src/common/objdefs.h @@ -0,0 +1,86 @@ +/*****************************************************************************/ +/* */ +/* objdefs.h */ +/* */ +/* Object file definitions */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OBJDEFS_H +#define OBJDEFS_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Defines for magic and version */ +#define OBJ_MAGIC 0x616E7A55 +#define OBJ_VERSION 0x0005 + +/* Size of an object file header */ +#define OBJ_HDR_SIZE 56 + +/* Flag bits */ +#define OBJ_FLAGS_DBGINFO 0x0001 /* File has debug info */ + + + +/* Header structure */ +typedef struct ObjHeader_ ObjHeader; +struct ObjHeader_ { + unsigned long Magic; /* 32: Magic number */ + unsigned Version; /* 16: Version number */ + unsigned Flags; /* 16: flags */ + unsigned long OptionOffs; /* 32: Offset to option table */ + unsigned long OptionSize; /* 32: Size of options */ + unsigned long FileOffs; /* 32: Offset to file table */ + unsigned long FileSize; /* 32: Size of files */ + unsigned long SegOffs; /* 32: Offset to segment table */ + unsigned long SegSize; /* 32: Size of segment table */ + unsigned long ImportOffs; /* 32: Offset to import list */ + unsigned long ImportSize; /* 32: Size of import list */ + unsigned long ExportOffs; /* 32: Offset to export list */ + unsigned long ExportSize; /* 32: Size of export list */ + unsigned long DbgSymOffs; /* 32: Offset to list of debug symbols */ + unsigned long DbgSymSize; /* 32: Size of debug symbols */ +}; + + + +/* End of objdefs.h */ + +#endif + + + diff --git a/src/common/optdefs.h b/src/common/optdefs.h new file mode 100644 index 000000000..b256147e9 --- /dev/null +++ b/src/common/optdefs.h @@ -0,0 +1,80 @@ +/*****************************************************************************/ +/* */ +/* optdefs.h */ +/* */ +/* Definitions for object file options */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OPTDEFS_H +#define OPTDEFS_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Type of options */ +#define OPT_ARGMASK 0xC0 /* Mask for argument */ +#define OPT_ARGSTR 0x00 /* String argument */ +#define OPT_ARGNUM 0x40 /* Numerical argument */ + +#define OPT_COMMENT (OPT_ARGSTR+0) /* Generic comment */ +#define OPT_AUTHOR (OPT_ARGSTR+1) /* Author specification */ +#define OPT_TRANSLATOR (OPT_ARGSTR+2) /* Translator specification */ +#define OPT_COMPILER (OPT_ARGSTR+3) /* Compiler specification */ +#define OPT_OS (OPT_ARGSTR+4) /* Operating system specification */ + +#define OPT_DATETIME (OPT_ARGNUM+0) /* Date/time of translation */ + + + +/* Structure to encode options */ +typedef struct Option_ Option; +struct Option_ { + Option* Next; /* For list of options */ + unsigned char Type; /* Type of option */ + union { + const char* Str; /* String attribute */ + unsigned long Val; /* Value attribute */ + } V; +}; + + + +/* End of optdefs.h */ + +#endif + + + diff --git a/src/common/segdefs.h b/src/common/segdefs.h new file mode 100644 index 000000000..5da311bcc --- /dev/null +++ b/src/common/segdefs.h @@ -0,0 +1,83 @@ +/*****************************************************************************/ +/* */ +/* segdefs.h */ +/* */ +/* Segment definitions for the bin65 binary utils */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SEGDEFS_H +#define SEGDEFS_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Available segment types */ +#define SEGTYPE_DEFAULT 0 +#define SEGTYPE_ABS 1 +#define SEGTYPE_ZP 2 +#define SEGTYPE_FAR 3 + +/* Fragment types in the object file */ +#define FRAG_TYPEMASK 0x38 /* Mask the type of the fragment */ +#define FRAG_BYTEMASK 0x07 /* Mask for byte count */ + +#define FRAG_LITERAL 0x00 /* Literal data */ +#define FRAG_LITERAL8 0x01 /* Literal data with 8 bit length */ +#define FRAG_LITERAL16 0x02 /* Literal data with 16 bit length */ +#define FRAG_LITERAL24 0x03 /* Literal data with 24 bit length */ +#define FRAG_LITERAL32 0x04 /* Literal data with 32 bit length */ + +#define FRAG_EXPR 0x08 /* Expression */ +#define FRAG_EXPR8 0x09 /* 8 bit expression */ +#define FRAG_EXPR16 0x0A /* 16 bit expression */ +#define FRAG_EXPR24 0x0B /* 24 bit expression */ +#define FRAG_EXPR32 0x0C /* 32 bit expression */ + +#define FRAG_SEXPR 0x10 /* Signed expression */ +#define FRAG_SEXPR8 0x11 /* 8 bit signed expression */ +#define FRAG_SEXPR16 0x12 /* 16 bit signed expression */ +#define FRAG_SEXPR24 0x13 /* 24 bit signed expression */ +#define FRAG_SEXPR32 0x14 /* 32 bit signed expression */ + +#define FRAG_FILL 0x20 /* Fill bytes */ + + +/* End of segdefs.h */ + +#endif + + + diff --git a/src/common/symdefs.h b/src/common/symdefs.h new file mode 100644 index 000000000..ffd371d3a --- /dev/null +++ b/src/common/symdefs.h @@ -0,0 +1,63 @@ +/*****************************************************************************/ +/* */ +/* symdefs.h */ +/* */ +/* Symbol definitions for the bin65 binary utils */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SYMDEFS_H +#define SYMDEFS_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Object file tags for imports and exports */ +#define IMP_ABS 0x00 /* Import as normal value */ +#define IMP_ZP 0x01 /* Import as zero page symbol */ + +#define EXP_ABS 0x00 /* Export as normal value */ +#define EXP_ZP 0x01 /* Export as zero page value */ +#define EXP_CONST 0x00 /* Mask bit for const values */ +#define EXP_EXPR 0x02 /* Mask bit for expr values */ + + + +/* End of symdefs.h */ + +#endif + + + diff --git a/src/common/version.h b/src/common/version.h new file mode 100644 index 000000000..991f5c03e --- /dev/null +++ b/src/common/version.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +/* */ +/* version.h */ +/* */ +/* Version information for the cc65 compiler package */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef VERSION_H +#define VERSION_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +#define VER_MAJOR 2U +#define VER_MINOR 4U +#define VER_PATCH 4U + + + +/* End of version.h */ + +#endif + + + diff --git a/src/geos/headergen.sh b/src/geos/headergen.sh new file mode 100755 index 000000000..6200f6d99 --- /dev/null +++ b/src/geos/headergen.sh @@ -0,0 +1,199 @@ +#!/bin/sh + +# GEOS .cvt header generator... +# it's a really quick and dirty hack ... big and ugly... + +# by Maciej 'YTM/Alliance' Witkowiak +# 9/10.3.2000 + +case "$1" in + + *.res) + +#include resource file + + . $1 + +#test is arguments are really given, if not - set defaults + +# binary file type +if [ -z "$progtype" ]; then progtype="APPLICATION"; fi + +# filenames +if [ -z "$dosname" ]; then dosname="testapp" ; fi +if [ -z "$dostype" ]; then dostype="USR" ; fi + +# date +if [ -z "$year" ]; then year=`date +%y` ; fi +if [ -z "$month" ]; then month=`date +%m` ; fi +if [ -z "$day" ]; then day=`date +%e` ; fi +if [ -z "$hour" ]; then hour=`date +%k` ; fi +if [ -z "$minute" ]; then minute=`date +%M` ; fi + +# screenmode +if [ -z "$screenmode" ]; then screenmode=0 ; fi + +# names +if [ -z "$class" ]; then class="Programname"; fi +if [ -z "$version" ]; then version="V1.0" ; fi +if [ -z "$author" ]; then author="cc65"; fi +if [ -z "$note" ]; then note="Program compiled with cc65 and GEOSLib."; fi + +# start generator + +cat << __END__ + +; Maciej 'YTM/Alliance' Witkowiak +; 28.02.2000 + +; This is .cvt header for GEOS files, it is recognized by Convert v2.5 for GEOS +; and Star Commander (when copying GEOS files to/from .d64 images) + +; currently only SEQUENTIAL structure is supported, no overlays + +; THIS IS GENERATED FILE, ANY CHANGES WILL BE LOST!!! + + .segment "HEADER" + +; +;filetypes +; GEOS +NOT_GEOS = 0 +BASIC = 1 +ASSEMBLY = 2 +DATA = 3 +SYSTEM = 4 +DESK_ACC = 5 +APPLICATION = 6 +APPL_DATA = 7 +FONT = 8 +PRINTER = 9 +INPUT_DEVICE = 10 +DISK_DEVICE = 11 +SYSTEM_BOOT = 12 +TEMPORARY = 13 +AUTO_EXEC = 14 +INPUT_128 = 15 +NUMFILETYPES = 16 +; structure +SEQUENTIAL = 0 +VLIR = 1 +; DOS +DEL = 0 +SEQ = 1 +PRG = 2 +USR = 3 +REL = 4 +CBM = 5 + +__END__ + +echo -e ProgType\\t\\t=\\t$progtype +echo -e \\n\\t\\t.byte $dostype '| $80'\\t\\t'; DOS filetype' +echo -e \\t\\t.word 0\\t\\t'; T&S, will be fixed by converter' +echo $dosname | awk ' +{ len=length($0); printf "\t\t.byte %c%s%c\n", 34, substr($0,0,16), 34; + if (len<16) { len=15-len; printf "\t\t.byte $a0" + while (len>0) { printf ", $a0"; len-=1 } } + print "" + }' +echo -e \\t\\t.word 0\\t\\t'; header T&S' +echo -e \\t\\t.byte SEQUENTIAL +echo -e \\t\\t.byte ProgType +echo -e \\t\\t.byte $year +echo -e \\t\\t.byte $month +echo -e \\t\\t.byte $day +echo -e \\t\\t.byte $hour +echo -e \\t\\t.byte $minute +echo -e \\n\\t\\t.word 0 +cat << __END__ + + .byte "PRG formatted GEOS file V1.0" + ; converter stamp + .res \$c4 ; some bytes are left + + .byte 3, 21, 63 | \$80 ; icon picture header, 63 bytes follow + + ;** hey, uberhacker! edit icon here!!! ;-)) + .byte %11111111, %11111111, %11111111 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %10000000, %00000000, %00000001 + .byte %11111111, %11111111, %11111111 + +__END__ + +echo -e \\n\\t\\t.byte $dostype '| $80'\\t\\t';DOS filetype again' + +cat << __END__ + .byte ProgType ;again GEOS type + .byte SEQUENTIAL ;structure + .word \$0400 ;ProgStart + .word \$0400-1 ;ProgEnd (needs proper value for DESK_ACC) + .word \$0400 ;ProgExec +__END__ + +echo -e \\t\\t'; GEOS class (11 chars padded with spaces, terminated with space (12th))' +echo $class | awk ' +{ len=length($0); printf "\t\t.byte %c%s%c\n", 34, substr($0,0,12), 34; + if (len<12) { len=11-len; printf "\t\t.byte $20" + while (len>0) { printf ", $20"; len-=1 } } + print "" + }' +echo -e \\t\\t'; version info (4 characters)' +echo -en \\t\\t.byte +echo $version | awk '{ printf " %c%s%c\n", 34, substr($0,1,4), 34}' +echo -e \\t\\t.byte 0\\t\\t\\t';string terminator' +echo -e \\t\\t.word 0 +echo -e \\n\\t\\t.byte $screenmode\\t\\t\\t';40/80 columns capability' +echo -e \\n\\t\\t'; author, up to 62 characters' +echo $author | awk ' +{ printf "\t\t.byte %c%s%c\n\t\t.byte 0\n\t\t.res (63-%i)\n", 34, substr($0,0,62), 34, length($0)+1; }' +echo -e \\n\\t\\t';note (up to 95 chars)' +echo $note | awk ' +{ printf "\t\t.byte %c%s%c\n\t\t.byte 0\n\t\t.res (96-%i)\n", 34, substr($0,0,95), 34, length($0)+1; }' +echo -e \\n\\n';end of .cvt header, real code follows' + + ;; + *) +echo "This is GEOSLib .cvt header generator by Maciej Witkowiak" +echo "Usage:" +echo " headergen resourcefile.res" +echo +echo "Contents of resource file are (case sensitive):" +echo "dostype=[PRG,SEQ,USR]" +echo "dosname=filename" +echo "progtype=[APPLICATION,ASSEMBLY,DESK_ACC,PRINTER,INPUT_DEVICE,AUTO_EXEC,INPUT_128]" +echo " currently only APPLICATION is supported" +echo "year,month,date,hour,minute=XX - if not given current will be used" +echo "screenmode=[0,64,128,192] -" +echo " 0 - GEOS128 only 40 columns" +echo " 64 - GEOS128 both 40/80 columns" +echo " 128 - does not run under GEOS128" +echo " 192 - GEOS128 only 80 columns" +echo "class=Class - GEOS class name" +echo "author=Author - author name" +echo "note=Note - note field" +echo +echo "If any of those parameters is not given, default will be used" +echo "Output should be redirected to a file e.g. cvthead.s" +echo "compiled with ca65 and used in linking process as the first object file -" +echo "even before crt0.o" + ;; +esac diff --git a/src/ld65/.cvsignore b/src/ld65/.cvsignore new file mode 100644 index 000000000..93a652341 --- /dev/null +++ b/src/ld65/.cvsignore @@ -0,0 +1,5 @@ +.depend +ld65 +*.map +*.s + diff --git a/src/ld65/bin.c b/src/ld65/bin.c new file mode 100644 index 000000000..c902aaf86 --- /dev/null +++ b/src/ld65/bin.c @@ -0,0 +1,258 @@ +/*****************************************************************************/ +/* */ +/* bin.c */ +/* */ +/* Module to handle the raw binary format */ +/* */ +/* */ +/* */ +/* (C) 1999-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "global.h" +#include "error.h" +#include "mem.h" +#include "fileio.h" +#include "segments.h" +#include "exports.h" +#include "config.h" +#include "expr.h" +#include "bin.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +struct BinDesc_ { + unsigned Undef; /* Count of undefined externals */ + FILE* F; /* Output file */ + const char* Filename; /* Name of output file */ +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +BinDesc* NewBinDesc (void) +/* Create a new binary format descriptor */ +{ + /* Allocate memory for a new BinDesc struct */ + BinDesc* D = Xmalloc (sizeof (BinDesc)); + + /* Initialize the fields */ + D->Undef = 0; + D->F = 0; + D->Filename = 0; + + /* Return the created struct */ + return D; +} + + + +void FreeBinDesc (BinDesc* D) +/* Free a binary format descriptor */ +{ + Xfree (D); +} + + + +static unsigned BinWriteExpr (ExprNode* E, int Signed, unsigned Size, + unsigned long Offs, void* Data) +/* Called from SegWrite for an expression. Evaluate the expression, check the + * range and write the expression value to the file. + */ +{ + /* There's a predefined function to handle constant expressions */ + return SegWriteConstExpr (((BinDesc*)Data)->F, E, Signed, Size); +} + + + +static void BinWriteMem (BinDesc* D, Memory* M) +/* Write the segments of one memory area to a file */ +{ + /* Get the start address of this memory area */ + unsigned long Addr = M->Start; + + /* Get a pointer to the first segment node */ + MemListNode* N = M->SegList; + while (N) { + + int DoWrite; + + /* Get the segment from the list node */ + SegDesc* S = N->Seg; + + /* Keep the user happy */ + if (Verbose) { + printf (" Writing `%s'\n", S->Name); + } + + /* Writes do only occur in the load area and not for BSS segments */ + DoWrite = (S->Flags & SF_BSS) == 0 && /* No BSS segment */ + S->Load == M && /* LOAD segment */ + S->Seg->Dumped == 0; /* Not already written */ + + /* Check if we would need an alignment */ + if (S->Seg->Align > S->Align) { + /* Segment itself requires larger alignment than configured + * in the linker. + */ + Warning ("Segment `%s' in module `%s' requires larger alignment", + S->Name, S->Seg->AlignObj->Name); + } + + /* Handle ALIGN and OFFSET/START */ + if (S->Flags & SF_ALIGN) { + /* Align the address */ + unsigned long Val, NewAddr; + Val = (0x01UL << S->Align) - 1; + NewAddr = (Addr + Val) & ~Val; + if (DoWrite) { + WriteMult (D->F, M->FillVal, NewAddr-Addr); + } + Addr = NewAddr; + /* Remember the fill value for the segment */ + S->Seg->FillVal = M->FillVal; + } else if (S->Flags & (SF_OFFSET | SF_START)) { + unsigned long NewAddr = S->Addr; + if (S->Flags & SF_OFFSET) { + /* It's an offset, not a fixed address, make an address */ + NewAddr += M->Start; + } + if (DoWrite) { + WriteMult (D->F, M->FillVal, NewAddr-Addr); + } + Addr = NewAddr; + } + + /* Now write the segment to disk if it is not a BSS type segment and + * if the memory area is the load area. + */ + if (DoWrite) { + SegWrite (D->F, S->Seg, BinWriteExpr, D); + } else if (M->Flags & MF_FILL) { + WriteMult (D->F, M->FillVal, S->Seg->Size); + } + S->Seg->Dumped = 1; + + /* Calculate the new address */ + Addr += S->Seg->Size; + + /* Next segment node */ + N = N->Next; + } + + /* If a fill was requested, fill the remaining space */ + if (M->Flags & MF_FILL) { + while (M->FillLevel < M->Size) { + Write8 (D->F, M->FillVal); + ++M->FillLevel; + } + } +} + + + +static int BinUnresolved (const char* Name, void* D) +/* Called if an unresolved symbol is encountered */ +{ + /* Unresolved symbols are an error in binary format. Bump the counter + * and return zero telling the caller that the symbol is indeed + * unresolved. + */ + ((BinDesc*) D)->Undef++; + return 0; +} + + + +void BinWriteTarget (BinDesc* D, struct File_* F) +/* Write a binary output file */ +{ + Memory* M; + + /* Place the filename in the control structure */ + D->Filename = F->Name; + + /* Check for unresolved symbols. The function BinUnresolved is called + * if we get an unresolved symbol. + */ + D->Undef = 0; /* Reset the counter */ + CheckExports (BinUnresolved, D); + if (D->Undef > 0) { + /* We had unresolved symbols, cannot create output file */ + Error ("%u unresolved external(s) found - cannot create output file", D->Undef); + } + + /* Open the file */ + D->F = fopen (F->Name, "wb"); + if (D->F == 0) { + Error ("Cannot open `%s': %s", F->Name, strerror (errno)); + } + + /* Keep the user happy */ + if (Verbose) { + printf ("Opened `%s'...\n", F->Name); + } + + /* Dump all memory areas */ + M = F->MemList; + while (M) { + if (Verbose) { + printf (" Dumping `%s'\n", M->Name); + } + BinWriteMem (D, M); + M = M->FNext; + } + + /* Close the file */ + if (fclose (D->F) != 0) { + Error ("Cannot write to `%s': %s", F->Name, strerror (errno)); + } + + /* Reset the file and filename */ + D->F = 0; + D->Filename = 0; +} + + + diff --git a/src/ld65/bin.h b/src/ld65/bin.h new file mode 100644 index 000000000..5ed64676c --- /dev/null +++ b/src/ld65/bin.h @@ -0,0 +1,78 @@ +/*****************************************************************************/ +/* */ +/* bin.h */ +/* */ +/* Module to handle the raw binary format */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef BIN_H +#define BIN_H + + + +#include "config.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Structure describing the format */ +typedef struct BinDesc_ BinDesc; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +BinDesc* NewBinDesc (void); +/* Create a new binary format descriptor */ + +void FreeBinDesc (BinDesc* D); +/* Free a binary format descriptor */ + +void BinWriteTarget (BinDesc* D, File* F); +/* Write a binary output file */ + + + +/* End of bin.h */ + +#endif + + + diff --git a/src/ld65/binfmt.c b/src/ld65/binfmt.c new file mode 100644 index 000000000..b7c978475 --- /dev/null +++ b/src/ld65/binfmt.c @@ -0,0 +1,89 @@ +/*****************************************************************************/ +/* */ +/* binfmt.c */ +/* */ +/* Binary format definitions for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "error.h" +#include "binfmt.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Default format (depends on target system) */ +unsigned char DefaultBinFmt = BINFMT_BINARY; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int RelocatableBinFmt (unsigned Format) +/* Return true if this is a relocatable format, return false otherwise */ +{ + int Reloc = 0; + + /* Resolve the default format */ + if (Format == BINFMT_DEFAULT) { + Format = DefaultBinFmt; + } + + /* Check the type */ + switch (Format) { + + case BINFMT_BINARY: + Reloc = 0; + break; + + case BINFMT_O65: + Reloc = 1; + break; + + default: + Internal ("Invalid format specifier: %u", Format); + + } + + /* Return the flag */ + return Reloc; +} + + + diff --git a/src/ld65/binfmt.h b/src/ld65/binfmt.h new file mode 100644 index 000000000..f5414172d --- /dev/null +++ b/src/ld65/binfmt.h @@ -0,0 +1,73 @@ +/*****************************************************************************/ +/* */ +/* binfmt.h */ +/* */ +/* Binary format definitions for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef BINFMT_H +#define BINFMT_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Types of available output formats */ +#define BINFMT_DEFAULT 0 /* Default (binary) */ +#define BINFMT_BINARY 1 /* Straight binary format */ +#define BINFMT_O65 2 /* Andre Fachats o65 format */ + +/* Default format (depends on target system) */ +extern unsigned char DefaultBinFmt; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int RelocatableBinFmt (unsigned Format); +/* Return true if this is a relocatable format, return false otherwise */ + + + +/* End of binfmt.h */ + +#endif + + + diff --git a/src/ld65/config.c b/src/ld65/config.c new file mode 100644 index 000000000..20e630670 --- /dev/null +++ b/src/ld65/config.c @@ -0,0 +1,1202 @@ +/*****************************************************************************/ +/* */ +/* config.c */ +/* */ +/* Target configuration file for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include + +#include "../common/bitops.h" + +#include "error.h" +#include "mem.h" +#include "global.h" +#include "bin.h" +#include "o65.h" +#include "binfmt.h" +#include "exports.h" +#include "scanner.h" +#include "config.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* File list */ +static File* FileList; /* Single linked list */ +static unsigned FileCount; /* Number of entries in the list */ + + + +/* Memory list */ +static Memory* MemoryList; /* Single linked list */ +static Memory* MemoryLast; /* Last element in list */ +static unsigned MemoryCount; /* Number of entries in the list */ + +/* Memory attributes */ +#define MA_START 0x0001 +#define MA_SIZE 0x0002 +#define MA_TYPE 0x0004 +#define MA_FILE 0x0008 +#define MA_DEFINE 0x0010 +#define MA_FILL 0x0020 +#define MA_FILLVAL 0x0040 + + + +/* Segment list */ +SegDesc* SegDescList; /* Single linked list */ +unsigned SegDescCount; /* Number of entries in list */ + +/* Segment attributes */ +#define SA_TYPE 0x0001 +#define SA_LOAD 0x0002 +#define SA_RUN 0x0004 +#define SA_ALIGN 0x0008 +#define SA_DEFINE 0x0010 +#define SA_OFFSET 0x0020 +#define SA_START 0x0040 + + + +/* Descriptor holding information about the binary formats */ +static BinDesc* BinFmtDesc = 0; +static O65Desc* O65FmtDesc = 0; + +/* Attributes for the o65 format */ +static unsigned O65Attr = 0; +#define OA_OS 0x0001 +#define OA_TYPE 0x0002 +#define OA_VERSION 0x0004 +#define OA_OSVERSION 0x0008 +#define OA_TEXT 0x0010 +#define OA_DATA 0x0020 +#define OA_BSS 0x0040 +#define OA_ZP 0x0080 + + + +/*****************************************************************************/ +/* Constructors/Destructors */ +/*****************************************************************************/ + + + +static File* NewFile (const char* Name) +/* Create a new file descriptor and insert it into the list */ +{ + /* Get the length of the name */ + unsigned Len = strlen (Name); + + /* Allocate memory */ + File* F = Xmalloc (sizeof (File) + Len); + + /* Initialize the fields */ + F->Flags = 0; + F->Format = BINFMT_DEFAULT; + F->MemList = 0; + F->MemLast = 0; + memcpy (F->Name, Name, Len); + F->Name [Len] = '\0'; + + /* Insert the struct into the list */ + F->Next = FileList; + FileList = F; + ++FileCount; + + /* ...and return it */ + return F; +} + + + +static Memory* NewMemory (const char* Name) +/* Create a new memory section and insert it into the list */ +{ + /* Get the length of the name */ + unsigned Len = strlen (Name); + + /* Check for duplicate names */ + Memory* M = MemoryList; + while (M) { + if (strcmp (M->Name, Name) == 0) { + CfgError ("Memory area `%s' defined twice", Name); + break; + } + M = M->Next; + } + + /* Allocate memory */ + M = Xmalloc (sizeof (Memory) + Len); + + /* Initialize the fields */ + M->Next = 0; + M->FNext = 0; + M->Attr = 0; + M->Flags = 0; + M->Start = 0; + M->Size = 0; + M->FillLevel = 0; + M->FillVal = 0; + M->SegList = 0; + M->SegLast = 0; + M->F = 0; + memcpy (M->Name, Name, Len); + M->Name [Len] = '\0'; + + /* Insert the struct into the list */ + if (MemoryLast == 0) { + /* First element */ + MemoryList = M; + } else { + MemoryLast->Next = M; + } + MemoryLast = M; + ++MemoryCount; + + /* ...and return it */ + return M; +} + + + +static SegDesc* NewSegDesc (const char* Name) +/* Create a segment descriptor */ +{ + Segment* Seg; + + /* Get the length of the name */ + unsigned Len = strlen (Name); + + /* Check for duplicate names */ + SegDesc* S = SegDescList; + while (S) { + if (strcmp (S->Name, Name) == 0) { + CfgError ("Segment `%s' defined twice", Name); + break; + } + S = S->Next; + } + + /* Verify that the given segment does really exist */ + Seg = SegFind (Name); + if (Seg == 0) { + CfgWarning ("Segment `%s' does not exist", Name); + } + + /* Allocate memory */ + S = Xmalloc (sizeof (SegDesc) + Len); + + /* Initialize the fields */ + S->Next = 0; + S->Seg = Seg; + S->Attr = 0; + S->Flags = 0; + S->Align = 0; + memcpy (S->Name, Name, Len); + S->Name [Len] = '\0'; + + /* ...and return it */ + return S; +} + + + +static void FreeSegDesc (SegDesc* S) +/* Free a segment descriptor */ +{ + Xfree (S); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void FlagAttr (unsigned* Flags, unsigned Mask, const char* Name) +/* Check if the item is already defined. Print an error if so. If not, set + * the marker that we have a definition now. + */ +{ + if (*Flags & Mask) { + CfgError ("%s is already defined", Name); + } + *Flags |= Mask; +} + + + +static void AttrCheck (unsigned Attr, unsigned Mask, const char* Name) +/* Check that a mandatory attribute was given */ +{ + if ((Attr & Mask) == 0) { + CfgError ("%s attribute is missing", Name); + } +} + + + +static File* FindFile (const char* Name) +/* Find a file with a given name. */ +{ + File* F = FileList; + while (F) { + if (strcmp (F->Name, Name) == 0) { + return F; + } + F = F->Next; + } + return 0; +} + + + +static File* GetFile (const char* Name) +/* Get a file entry with the given name. Create a new one if needed. */ +{ + File* F = FindFile (Name); + if (F == 0) { + /* Create a new one */ + F = NewFile (Name); + } + return F; +} + + + +static void FileInsert (File* F, Memory* M) +/* Insert the memory area into the files list */ +{ + M->F = F; + if (F->MemList == 0) { + /* First entry */ + F->MemList = M; + } else { + F->MemLast->FNext = M; + } + F->MemLast = M; +} + + + +static void ParseMemory (void) +/* Parse a MEMORY section */ +{ + static const IdentTok Attributes [] = { + { "START", CFGTOK_START }, + { "SIZE", CFGTOK_SIZE }, + { "TYPE", CFGTOK_TYPE }, + { "FILE", CFGTOK_FILE }, + { "DEFINE", CFGTOK_DEFINE }, + { "FILL", CFGTOK_FILL }, + { "FILLVAL", CFGTOK_FILLVAL }, + }; + static const IdentTok Types [] = { + { "RO", CFGTOK_RO }, + { "RW", CFGTOK_RW }, + }; + + while (CfgTok == CFGTOK_IDENT) { + + /* Create a new entry on the heap */ + Memory* M = NewMemory (CfgSVal); + + /* Skip the name and the following colon */ + CfgNextTok (); + CfgConsumeColon (); + + /* Read the attributes */ + while (CfgTok == CFGTOK_IDENT) { + + /* Map the identifier to a token */ + unsigned AttrTok; + CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); + AttrTok = CfgTok; + + /* An optional assignment follows */ + CfgNextTok (); + CfgOptionalAssign (); + + /* Check which attribute was given */ + switch (AttrTok) { + + case CFGTOK_START: + FlagAttr (&M->Attr, MA_START, "START"); + CfgAssureInt (); + M->Start = CfgIVal; + break; + + case CFGTOK_SIZE: + FlagAttr (&M->Attr, MA_SIZE, "SIZE"); + CfgAssureInt (); + M->Size = CfgIVal; + break; + + case CFGTOK_TYPE: + FlagAttr (&M->Attr, MA_TYPE, "TYPE"); + CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type"); + if (CfgTok == CFGTOK_RO) { + M->Flags |= MF_RO; + } + break; + + case CFGTOK_FILE: + FlagAttr (&M->Attr, MA_FILE, "FILE"); + CfgAssureStr (); + /* Get the file entry and insert the memory area */ + FileInsert (GetFile (CfgSVal), M); + break; + + case CFGTOK_DEFINE: + FlagAttr (&M->Attr, MA_DEFINE, "DEFINE"); + /* Map the token to a boolean */ + CfgBoolToken (); + if (CfgTok == CFGTOK_TRUE) { + M->Flags |= MF_DEFINE; + } + break; + + case CFGTOK_FILL: + FlagAttr (&M->Attr, MA_FILL, "FILL"); + /* Map the token to a boolean */ + CfgBoolToken (); + if (CfgTok == CFGTOK_TRUE) { + M->Flags |= MF_FILL; + } + break; + + case CFGTOK_FILLVAL: + FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL"); + CfgAssureInt (); + CfgRangeCheck (0, 0xFF); + M->FillVal = (unsigned char) CfgIVal; + break; + + default: + FAIL ("Unexpected attribute token"); + + } + + /* Skip the attribute value and an optional comma */ + CfgNextTok (); + CfgOptionalComma (); + } + + /* Skip the semicolon */ + CfgConsumeSemi (); + + /* Check for mandatory parameters */ + AttrCheck (M->Attr, MA_START, "START"); + AttrCheck (M->Attr, MA_SIZE, "SIZE"); + + /* If we don't have a file name for output given, use the default + * file name. + */ + if ((M->Attr & MA_FILE) == 0) { + FileInsert (GetFile (OutputName), M); + } + } +} + + + +static void ParseFiles (void) +/* Parse a FILES section */ +{ + static const IdentTok Attributes [] = { + { "FORMAT", CFGTOK_FORMAT }, + }; + static const IdentTok Formats [] = { + { "O65", CFGTOK_O65 }, + { "BIN", CFGTOK_BIN }, + { "BINARY", CFGTOK_BIN }, + }; + + + /* Parse all files */ + while (CfgTok != CFGTOK_RCURLY) { + + File* F; + + /* We expect a string value here */ + CfgAssureStr (); + + /* Search for the file, it must exist */ + F = FindFile (CfgSVal); + if (F == 0) { + CfgError ("No such file: `%s'", CfgSVal); + } + + /* Skip the token and the following colon */ + CfgNextTok (); + CfgConsumeColon (); + + /* Read the attributes */ + while (CfgTok == CFGTOK_IDENT) { + + /* Map the identifier to a token */ + unsigned AttrTok; + CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); + AttrTok = CfgTok; + + /* An optional assignment follows */ + CfgNextTok (); + CfgOptionalAssign (); + + /* Check which attribute was given */ + switch (AttrTok) { + + case CFGTOK_FORMAT: + if (F->Format != BINFMT_DEFAULT) { + /* We've set the format already! */ + Error ("Cannot set a file format twice"); + } + /* Read the format token */ + CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format"); + switch (CfgTok) { + + case CFGTOK_BIN: + F->Format = BINFMT_BINARY; + break; + + case CFGTOK_O65: + F->Format = BINFMT_O65; + break; + + default: + Error ("Unexpected format token"); + } + break; + + default: + FAIL ("Unexpected attribute token"); + + } + + /* Skip the attribute value and an optional comma */ + CfgNextTok (); + CfgOptionalComma (); + } + + /* Skip the semicolon */ + CfgConsumeSemi (); + + } +} + + + +static Memory* CfgFindMemory (const char* Name) +/* Find the memory are with the given name. Return NULL if not found */ +{ + Memory* M = MemoryList; + while (M) { + if (strcmp (M->Name, Name) == 0) { + return M; + } + M = M->Next; + } + return 0; +} + + + +static Memory* CfgGetMemory (const char* Name) +/* Find the memory are with the given name. Print an error on an invalid name */ +{ + Memory* M = CfgFindMemory (Name); + if (M == 0) { + CfgError ("Invalid memory area `%s'", Name); + } + return M; +} + + + +static void SegDescInsert (SegDesc* S) +/* Insert a segment descriptor into the list of segment descriptors */ +{ + /* Insert the struct into the list */ + S->Next = SegDescList; + SegDescList = S; + ++SegDescCount; +} + + + +static void MemoryInsert (Memory* M, SegDesc* S) +/* Insert the segment descriptor into the memory area list */ +{ + /* Create a new node for the entry */ + MemListNode* N = Xmalloc (sizeof (MemListNode)); + N->Seg = S; + N->Next = 0; + + if (M->SegLast == 0) { + /* First entry */ + M->SegList = N; + } else { + M->SegLast->Next = N; + } + M->SegLast = N; +} + + + +static void ParseSegments (void) +/* Parse a SEGMENTS section */ +{ + static const IdentTok Attributes [] = { + { "LOAD", CFGTOK_LOAD }, + { "RUN", CFGTOK_RUN }, + { "TYPE", CFGTOK_TYPE }, + { "ALIGN", CFGTOK_ALIGN }, + { "DEFINE", CFGTOK_DEFINE }, + { "OFFSET", CFGTOK_OFFSET }, + { "START", CFGTOK_START }, + }; + static const IdentTok Types [] = { + { "RO", CFGTOK_RO }, + { "RW", CFGTOK_RW }, + { "BSS", CFGTOK_BSS }, + { "ZP", CFGTOK_ZP }, + { "WP", CFGTOK_WPROT }, + { "WPROT", CFGTOK_WPROT }, + }; + + unsigned Count; + + while (CfgTok == CFGTOK_IDENT) { + + SegDesc* S; + + /* Create a new entry on the heap */ + S = NewSegDesc (CfgSVal); + + /* Skip the name and the following colon */ + CfgNextTok (); + CfgConsumeColon (); + + /* Read the attributes */ + while (CfgTok == CFGTOK_IDENT) { + + /* Map the identifier to a token */ + unsigned AttrTok; + CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); + AttrTok = CfgTok; + + /* An optional assignment follows */ + CfgNextTok (); + CfgOptionalAssign (); + + /* Check which attribute was given */ + switch (AttrTok) { + + case CFGTOK_LOAD: + FlagAttr (&S->Attr, SA_LOAD, "LOAD"); + S->Load = CfgGetMemory (CfgSVal); + break; + + case CFGTOK_RUN: + FlagAttr (&S->Attr, SA_RUN, "RUN"); + S->Run = CfgGetMemory (CfgSVal); + break; + + case CFGTOK_TYPE: + FlagAttr (&S->Attr, SA_TYPE, "TYPE"); + CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type"); + switch (CfgTok) { + case CFGTOK_RO: S->Flags |= SF_RO; break; + case CFGTOK_BSS: S->Flags |= SF_BSS; break; + case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break; + case CFGTOK_WPROT: S->Flags |= (SF_RO | SF_WPROT); break; + } + break; + + case CFGTOK_ALIGN: + CfgAssureInt (); + FlagAttr (&S->Attr, SA_ALIGN, "ALIGN"); + CfgRangeCheck (1, 0x10000); + S->Align = BitFind (CfgIVal); + if ((0x01UL << S->Align) != CfgIVal) { + CfgError ("Alignment must be a power of 2"); + } + S->Flags |= SF_ALIGN; + break; + + case CFGTOK_DEFINE: + FlagAttr (&S->Attr, SA_DEFINE, "DEFINE"); + /* Map the token to a boolean */ + CfgBoolToken (); + if (CfgTok == CFGTOK_TRUE) { + S->Flags |= SF_DEFINE; + } + break; + + case CFGTOK_OFFSET: + CfgAssureInt (); + FlagAttr (&S->Attr, SA_OFFSET, "OFFSET"); + CfgRangeCheck (1, 0x1000000); + S->Addr = CfgIVal; + S->Flags |= SF_OFFSET; + break; + + case CFGTOK_START: + CfgAssureInt (); + FlagAttr (&S->Attr, SA_START, "START"); + CfgRangeCheck (1, 0x1000000); + S->Addr = CfgIVal; + S->Flags |= SF_START; + break; + + default: + FAIL ("Unexpected attribute token"); + + } + + /* Skip the attribute value and an optional comma */ + CfgNextTok (); + CfgOptionalComma (); + } + + /* Skip the semicolon */ + CfgConsumeSemi (); + + /* Check for mandatory parameters */ + AttrCheck (S->Attr, SA_LOAD, "LOAD"); + + /* Set defaults for stuff not given */ + if ((S->Attr & SA_RUN) == 0) { + S->Attr |= SA_RUN; + S->Run = S->Load; + } else { + /* Both attributes given */ + S->Flags |= SF_LOAD_AND_RUN; + } + if ((S->Attr & SA_ALIGN) == 0) { + S->Attr |= SA_ALIGN; + S->Align = 0; + } + + /* If the segment is marked as BSS style, check that there's no + * initialized data in the segment. + */ + if ((S->Flags & SF_BSS) != 0 && !IsBSSType (S->Seg)) { + Warning ("%s(%u): Segment with type `bss' contains initialized data", + CfgGetName (), CfgErrorLine); + } + + /* Don't allow read/write data to be put into a readonly area */ + if ((S->Flags & SF_RO) == 0) { + if (S->Run->Flags & MF_RO) { + CfgError ("Cannot put r/w segment `%s' in r/o memory area `%s'", + S->Name, S->Run->Name); + } + } + + /* Only one of ALIGN, START and OFFSET may be used */ + Count = ((S->Flags & SF_ALIGN) != 0) + + ((S->Flags & SF_OFFSET) != 0) + + ((S->Flags & SF_START) != 0); + if (Count > 1) { + CfgError ("Only one of ALIGN, START, OFFSET may be used"); + } + + /* If this segment does exist in any of the object files, insert the + * descriptor into the list of segment descriptors. Otherwise discard + * it silently, because the segment pointer in the descriptor is + * invalid. + */ + if (S->Seg != 0) { + /* Insert the descriptor into the list of all descriptors */ + SegDescInsert (S); + /* Insert the segment into the memory area list */ + MemoryInsert (S->Run, S); + if ((S->Flags & SF_LOAD_AND_RUN) != 0) { + /* We have a separate RUN area given */ + MemoryInsert (S->Load, S); + } + } else { + /* Segment does not exist, discard the descriptor */ + FreeSegDesc (S); + } + } +} + + + +static void ParseO65 (void) +/* Parse the o65 format section */ +{ + static const IdentTok Attributes [] = { + { "EXPORT", CFGTOK_EXPORT }, + { "IMPORT", CFGTOK_IMPORT }, + { "TYPE", CFGTOK_TYPE }, + { "OS", CFGTOK_OS }, + }; + static const IdentTok Types [] = { + { "SMALL", CFGTOK_SMALL }, + { "LARGE", CFGTOK_LARGE }, + }; + static const IdentTok OperatingSystems [] = { + { "LUNIX", CFGTOK_LUNIX }, + { "OSA65", CFGTOK_OSA65 }, + }; + + while (CfgTok == CFGTOK_IDENT) { + + /* Map the identifier to a token */ + unsigned AttrTok; + CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); + AttrTok = CfgTok; + + /* An optional assignment follows */ + CfgNextTok (); + CfgOptionalAssign (); + + /* Check which attribute was given */ + switch (AttrTok) { + + case CFGTOK_EXPORT: + /* We expect an identifier */ + CfgAssureIdent (); + /* Check if we have this symbol defined already. The entry + * routine will check this also, but we get a more verbose + * error message when checking it here. + */ + if (O65GetExport (O65FmtDesc, CfgSVal) != 0) { + CfgError ("Duplicate exported symbol: `%s'", CfgSVal); + } + /* Insert the symbol into the table */ + O65SetExport (O65FmtDesc, CfgSVal); + break; + + case CFGTOK_IMPORT: + /* We expect an identifier */ + CfgAssureIdent (); + /* Check if we have this symbol defined already. The entry + * routine will check this also, but we get a more verbose + * error message when checking it here. + */ + if (O65GetImport (O65FmtDesc, CfgSVal) != 0) { + CfgError ("Duplicate imported symbol: `%s'", CfgSVal); + } + /* Insert the symbol into the table */ + O65SetImport (O65FmtDesc, CfgSVal); + break; + + case CFGTOK_TYPE: + /* Cannot have this attribute twice */ + FlagAttr (&O65Attr, OA_TYPE, "TYPE"); + /* Get the type of the executable */ + CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type"); + switch (CfgTok) { + + case CFGTOK_SMALL: + /* Default, nothing to do */ + break; + + case CFGTOK_LARGE: + O65SetLargeModel (O65FmtDesc); + break; + + default: + Error ("Unexpected type token"); + } + break; + + case CFGTOK_OS: + /* Cannot use this attribute twice */ + FlagAttr (&O65Attr, OA_OS, "OS"); + /* Get the operating system */ + CfgSpecialToken (OperatingSystems, ENTRY_COUNT (OperatingSystems), "OS type"); + switch (CfgTok) { + + case CFGTOK_LUNIX: + O65SetOS (O65FmtDesc, O65OS_LUNIX); + break; + + case CFGTOK_OSA65: + O65SetOS (O65FmtDesc, O65OS_OSA65); + break; + + default: + Error ("Unexpected OS token"); + } + break; + + default: + FAIL ("Unexpected attribute token"); + + } + + /* Skip the attribute value and an optional comma */ + CfgNextTok (); + CfgOptionalComma (); + } +} + + + +static void ParseFormats (void) +/* Parse a target format section */ +{ + static const IdentTok Formats [] = { + { "O65", CFGTOK_O65 }, + { "BIN", CFGTOK_BIN }, + { "BINARY", CFGTOK_BIN }, + }; + + while (CfgTok == CFGTOK_IDENT) { + + /* Map the identifier to a token */ + unsigned FormatTok; + CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format"); + FormatTok = CfgTok; + + /* Skip the name and the following colon */ + CfgNextTok (); + CfgConsumeColon (); + + /* Parse the format options */ + switch (FormatTok) { + + case CFGTOK_O65: + ParseO65 (); + break; + + case CFGTOK_BIN: + /* No attribibutes available */ + break; + + default: + Error ("Unexpected format token"); + } + + /* Skip the semicolon */ + CfgConsumeSemi (); + } +} + + + +static void ParseConfig (void) +/* Parse the config file */ +{ + static const IdentTok BlockNames [] = { + { "MEMORY", CFGTOK_MEMORY }, + { "FILES", CFGTOK_FILES }, + { "SEGMENTS", CFGTOK_SEGMENTS }, + { "FORMATS", CFGTOK_FORMATS }, + }; + unsigned BlockTok; + + do { + + /* Read the block ident */ + CfgSpecialToken (BlockNames, ENTRY_COUNT (BlockNames), "Block identifier"); + BlockTok = CfgTok; + CfgNextTok (); + + /* Expected a curly brace */ + CfgConsume (CFGTOK_LCURLY, "`{' expected"); + + /* Read the block */ + switch (BlockTok) { + + case CFGTOK_MEMORY: + ParseMemory (); + break; + + case CFGTOK_FILES: + ParseFiles (); + break; + + case CFGTOK_SEGMENTS: + ParseSegments (); + break; + + case CFGTOK_FORMATS: + ParseFormats (); + break; + + default: + FAIL ("Unexpected block token"); + + } + + /* Skip closing brace */ + CfgConsume (CFGTOK_RCURLY, "`}' expected"); + + } while (CfgTok != CFGTOK_EOF); +} + + + +void CfgRead (void) +/* Read the configuration */ +{ + /* Create the descriptors for the binary formats */ + BinFmtDesc = NewBinDesc (); + O65FmtDesc = NewO65Desc (); + + /* If we have a config name given, open the file, otherwise we will read + * from a buffer. + */ + CfgOpenInput (); + + /* Parse the file */ + ParseConfig (); + + /* Close the input file */ + CfgCloseInput (); +} + + + +static void CreateRunDefines (Memory* M, SegDesc* S, unsigned long Addr) +/* Create the defines for a RUN segment */ +{ + char Buf [256]; + + sprintf (Buf, "__%s_RUN__", S->Name); + CreateMemExport (Buf, M, Addr - M->Start); + sprintf (Buf, "__%s_SIZE__", S->Name); + CreateConstExport (Buf, S->Seg->Size); + S->Flags |= SF_RUN_DEF; +} + + + +static void CreateLoadDefines (Memory* M, SegDesc* S, unsigned long Addr) +/* Create the defines for a LOAD segment */ +{ + char Buf [256]; + + sprintf (Buf, "__%s_LOAD__", S->Name); + CreateMemExport (Buf, M, Addr - M->Start); + S->Flags |= SF_LOAD_DEF; +} + + + +void CfgAssignSegments (void) +/* Assign segments, define linker symbols where requested */ +{ + /* Walk through each of the memory sections. Add up the sizes and check + * for an overflow of the section. Assign the start addresses of the + * segments while doing this. + */ + Memory* M = MemoryList; + while (M) { + + /* Get the start address of this memory area */ + unsigned long Addr = M->Start; + + /* Walk through the segments in this memory area */ + MemListNode* N = M->SegList; + while (N) { + + /* Get the segment from the node */ + SegDesc* S = N->Seg; + + /* Handle ALIGN and OFFSET/START */ + if (S->Flags & SF_ALIGN) { + /* Align the address */ + unsigned long Val = (0x01UL << S->Align) - 1; + Addr = (Addr + Val) & ~Val; + } else if (S->Flags & (SF_OFFSET | SF_START)) { + /* Give the segment a fixed starting address */ + unsigned long NewAddr = S->Addr; + if (S->Flags & SF_OFFSET) { + /* An offset was given, no address, make an address */ + NewAddr += M->Start; + } + if (Addr > NewAddr) { + /* Offset already too large */ + if (S->Flags & SF_OFFSET) { + Error ("Offset too small in `%s', segment `%s'", + M->Name, S->Name); + } else { + Error ("Start address too low in `%s', segment `%s'", + M->Name, S->Name); + } + } + Addr = NewAddr; + } + + /* If this is the run area, set the start address of this segment */ + if (S->Run == M) { + S->Seg->PC = Addr; + } + + /* Increment the fill level of the memory area and check for an + * overflow. + */ + M->FillLevel = Addr + S->Seg->Size - M->Start; + if (M->FillLevel > M->Size) { + Error ("Memory area overflow in `%s', segment `%s' (%lu bytes)", + M->Name, S->Name, M->FillLevel - M->Size); + } + + /* If requested, define symbols for the start and size of the + * segment. + */ + if (S->Flags & SF_DEFINE) { + if ((S->Flags & SF_LOAD_AND_RUN) && S->Run == S->Load) { + /* RUN and LOAD given and in one memory area. + * Be careful: We will encounter this code twice, the + * first time when walking the RUN list, second time when + * walking the LOAD list. Be sure to define only the + * relevant symbols on each walk. + */ + if (S->Load == M) { + if ((S->Flags & SF_LOAD_DEF) == 0) { + CreateLoadDefines (M, S, Addr); + } else { + CHECK ((S->Flags & SF_RUN_DEF) == 0); + CreateRunDefines (M, S, Addr); + } + } + } else { + /* RUN and LOAD in different memory areas, or RUN not + * given, so RUN defaults to LOAD. In the latter case, we + * have only one copy of the segment in the area. + */ + if (S->Run == M) { + CreateRunDefines (M, S, Addr); + } + if (S->Load == M) { + CreateLoadDefines (M, S, Addr); + } + } + } + + /* Calculate the new address */ + Addr += S->Seg->Size; + + /* Next segment */ + N = N->Next; + } + + /* If requested, define symbols for start and size of the memory area */ + if (M->Flags & MF_DEFINE) { + char Buf [256]; + sprintf (Buf, "__%s_START__", M->Name); + CreateMemExport (Buf, M, 0); + sprintf (Buf, "__%s_SIZE__", M->Name); + CreateConstExport (Buf, M->Size); + sprintf (Buf, "__%s_LAST__", M->Name); + CreateConstExport (Buf, M->FillLevel); + } + + /* Next memory area */ + M = M->Next; + } +} + + + +void CfgWriteTarget (void) +/* Write the target file(s) */ +{ + Memory* M; + + /* Walk through the files list */ + File* F = FileList; + while (F) { + /* We don't need to look at files with no memory areas */ + if (F->MemList) { + + /* Is there an output file? */ + if (strlen (F->Name) > 0) { + + /* Assign a proper binary format */ + if (F->Format == BINFMT_DEFAULT) { + F->Format = DefaultBinFmt; + } + + /* Call the apropriate routine for the binary format */ + switch (F->Format) { + + case BINFMT_BINARY: + BinWriteTarget (BinFmtDesc, F); + break; + + case BINFMT_O65: + O65WriteTarget (O65FmtDesc, F); + break; + + default: + Internal ("Invalid binary format: %u", F->Format); + + } + + } else { + + /* No output file. Walk through the list and mark all segments + * assigned to the memory areas in this file as dumped. + */ + M = F->MemList; + while (M) { + /* Walk throught the segments */ + MemListNode* N = M->SegList; + while (N) { + /* Mark the segment as dumped */ + N->Seg->Seg->Dumped = 1; + + /* Next segment node */ + N = N->Next; + } + /* Next memory area */ + M = M->FNext; + } + } + } + + /* Next file */ + F = F->Next; + } +} + + + diff --git a/src/ld65/config.h b/src/ld65/config.h new file mode 100644 index 000000000..2f5c8114b --- /dev/null +++ b/src/ld65/config.h @@ -0,0 +1,147 @@ +/*****************************************************************************/ +/* */ +/* config.h */ +/* */ +/* Target configuration file for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef CONFIG_H +#define CONFIG_H + + + +#include "segments.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* File list entry */ +typedef struct File_ File; +struct File_ { + File* Next; /* Pointer to next entry in list */ + unsigned Flags; + unsigned Format; /* Output format */ + struct Memory_* MemList; /* List of memory areas in this file */ + struct Memory_* MemLast; /* Last memory area in this file */ + char Name [1]; /* Name of file */ +}; + +/* Segment list node. Needed because there are two lists (RUN & LOAD) */ +typedef struct MemListNode_ MemListNode; +struct MemListNode_ { + MemListNode* Next; /* Next entry */ + struct SegDesc_* Seg; /* Segment */ +}; + +/* Memory list entry */ +typedef struct Memory_ Memory; +struct Memory_ { + Memory* Next; /* Pointer to next entry in list */ + Memory* FNext; /* Next in file list */ + unsigned Attr; /* Which values are valid? */ + unsigned Flags; /* Set of bitmapped flags */ + unsigned long Start; /* Start address */ + unsigned long Size; /* Length of memory section */ + unsigned long FillLevel; /* Actual fill level of segment */ + unsigned char FillVal; /* Value used to fill rest of seg */ + MemListNode* SegList; /* List of segments for this section */ + MemListNode* SegLast; /* Last segment in this section */ + File* F; /* File that contains the entry */ + char Name [1]; /* Name of the memory section */ +}; + +/* Segment descriptor entry */ +typedef struct SegDesc_ SegDesc; +struct SegDesc_ { + SegDesc* Next; /* Pointer to next entry in list */ + Segment* Seg; /* Pointer to segment structure */ + unsigned Attr; /* Attributes for segment */ + unsigned Flags; /* Set of bitmapped flags */ + Memory* Load; /* Load memory section */ + Memory* Run; /* Run memory section */ + unsigned long Addr; /* Start address or offset into segment */ + unsigned char Align; /* Alignment if given */ + char Name [1]; /* Copy of name */ +}; + +/* Segment list */ +extern SegDesc* SegDescList; /* Single linked list */ +extern unsigned SegDescCount; /* Number of entries in list */ + +/* Memory flags */ +#define MF_DEFINE 0x0001 /* Define start and size */ +#define MF_FILL 0x0002 /* Fill segment */ +#define MF_RO 0x0004 /* Read only memory area */ + +/* Segment flags */ +#define SF_RO 0x0001 /* Read only segment */ +#define SF_BSS 0x0002 /* Segment is BSS style segment */ +#define SF_ZP 0x0004 /* Zeropage segment (o65 only) */ +#define SF_WPROT 0x0008 /* Write protected segment */ +#define SF_DEFINE 0x0010 /* Define start and size */ +#define SF_ALIGN 0x0020 /* Align the segment */ +#define SF_OFFSET 0x0040 /* Segment has offset in memory */ +#define SF_START 0x0080 /* Segment has fixed start address */ +#define SF_LOAD_AND_RUN 0x1000 /* LOAD and RUN given */ +#define SF_RUN_DEF 0x2000 /* RUN symbols already defined */ +#define SF_LOAD_DEF 0x4000 /* LOAD symbols already defined */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void CfgRead (void); +/* Read the configuration */ + +void CfgAssignSegments (void); +/* Assign segments, define linker symbols where requested */ + +void CfgWriteTarget (void); +/* Write the target file(s) */ + + + +/* End of config.h */ + +#endif + + + + diff --git a/src/ld65/dbgsyms.c b/src/ld65/dbgsyms.c new file mode 100644 index 000000000..77d7dd38e --- /dev/null +++ b/src/ld65/dbgsyms.c @@ -0,0 +1,210 @@ +/*****************************************************************************/ +/* */ +/* dbgsyms.c */ +/* */ +/* Debug symbol handing for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "../common/symdefs.h" + +#include "global.h" +#include "mem.h" +#include "error.h" +#include "fileio.h" +#include "objdata.h" +#include "expr.h" +#include "dbgsyms.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* We will collect all debug symbols in the following array and remove + * duplicates before outputing them. + */ +static DbgSym* DbgSymPool [256]; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static DbgSym* NewDbgSym (unsigned char Type, const char* Name, ObjData* O) +/* Create a new DbgSym and return it */ +{ + /* Get the length of the symbol name */ + unsigned Len = strlen (Name); + + /* Allocate memory */ + DbgSym* D = Xmalloc (sizeof (DbgSym) + Len); + + /* Initialize the fields */ + D->Next = 0; + D->Flags = 0; + D->Obj = O; + D->Expr = 0; + D->Type = Type; + memcpy (D->Name, Name, Len); + D->Name [Len] = '\0'; + + /* Return the new entry */ + return D; +} + + + +static DbgSym* GetDbgSym (DbgSym* D, long Val) +/* Check if we find the same debug symbol in the table. If we find it, return + * a pointer to the other occurrence, if we didn't find it, return NULL. + */ +{ + /* Create the hash. We hash over the symbol value */ + unsigned Hash = ((Val >> 24) & 0xFF) ^ + ((Val >> 16) & 0xFF) ^ + ((Val >> 8) & 0xFF) ^ + ((Val >> 0) & 0xFF); + + /* Check for this symbol */ + DbgSym* Sym = DbgSymPool [Hash]; + while (Sym) { + /* Is this symbol identical? */ + if (strcmp (Sym->Name, D->Name) == 0 && EqualExpr (Sym->Expr, D->Expr)) { + /* Found */ + return Sym; + } + + /* Next symbol */ + Sym = Sym->Next; + } + + /* This is the first symbol of it's kind */ + return 0; +} + + + +static void InsertDbgSym (DbgSym* D, long Val) +/* Insert the symbol into the hashed symbol pool */ +{ + /* Create the hash. We hash over the symbol value */ + unsigned Hash = ((Val >> 24) & 0xFF) ^ + ((Val >> 16) & 0xFF) ^ + ((Val >> 8) & 0xFF) ^ + ((Val >> 0) & 0xFF); + + /* Insert the symbol */ + D->Next = DbgSymPool [Hash]; + DbgSymPool [Hash] = D; +} + + + +DbgSym* ReadDbgSym (FILE* F, ObjData* O) +/* Read a debug symbol from a file, insert and return it */ +{ + unsigned char Type; + char Name [256]; + DbgSym* D; + + /* Read the type */ + Type = Read8 (F); + + /* Read the name */ + ReadStr (F, Name); + + /* Create a new export */ + D = NewDbgSym (Type, Name, O); + + /* Read the value */ + if (Type & EXP_EXPR) { + D->Expr = ReadExpr (F, O); + } else { + D->Expr = LiteralExpr (Read32 (F), O); + } + + /* Last is the file position where the definition was done */ + ReadFilePos (F, &D->Pos); + + /* Return the new DbgSym */ + return D; +} + + + +long GetDbgSymVal (DbgSym* D) +/* Get the value of this symbol */ +{ + CHECK (D->Expr != 0); + return GetExprVal (D->Expr); +} + + + +void PrintDbgSymLabels (ObjData* O, FILE* F) +/* Print the debug symbols in a VICE label file */ +{ + unsigned I; + + /* Walk through all debug symbols in this module */ + for (I = 0; I < O->DbgSymCount; ++I) { + + /* Get the next debug symbol */ + DbgSym* D = O->DbgSyms [I]; + + /* Get the symbol value */ + long Val = GetDbgSymVal (D); + + /* Lookup this symbol in the table. If it is found in the table, it was + * already written to the file, so don't emit it twice. If it is not in + * the table, insert and output it. + */ + if (GetDbgSym (D, Val) == 0) { + + /* Emit the VICE label line */ + fprintf (F, "al %06lX .%s\n", Val, D->Name); + + /* Insert the symbol into the table */ + InsertDbgSym (D, Val); + } + } +} + + + diff --git a/src/ld65/dbgsyms.h b/src/ld65/dbgsyms.h new file mode 100644 index 000000000..a8dc29d29 --- /dev/null +++ b/src/ld65/dbgsyms.h @@ -0,0 +1,92 @@ +/*****************************************************************************/ +/* */ +/* dbgsyms.h */ +/* */ +/* Debug symbol handing for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef DBGSYMS_H +#define DBGSYMS_H + + + +#include + +#include "../common/exprdefs.h" +#include "../common/filepos.h" + +#include "objdata.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Debug symbol structure */ +typedef struct DbgSym_ DbgSym; +struct DbgSym_ { + DbgSym* Next; /* Pool linear list link */ + unsigned Flags; /* Generic flags */ + ObjData* Obj; /* Object file that exports the name */ + FilePos Pos; /* File position of definition */ + ExprNode* Expr; /* Expression (0 if not def'd) */ + unsigned char Type; /* Type of symbol */ + char Name [1]; /* Name - dynamically allocated */ +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +DbgSym* ReadDbgSym (FILE* F, ObjData* Obj); +/* Read a debug symbol from a file, insert and return it */ + +long GetDbgSymVal (DbgSym* D); +/* Get the value of this symbol */ + +void PrintDbgSymLabels (ObjData* O, FILE* F); +/* Print the debug symbols in a VICE label file */ + + + +/* End of dbgsyms.h */ + +#endif + + + diff --git a/src/ld65/error.c b/src/ld65/error.c new file mode 100644 index 000000000..0476ab7d6 --- /dev/null +++ b/src/ld65/error.c @@ -0,0 +1,106 @@ +/*****************************************************************************/ +/* */ +/* global.c */ +/* */ +/* Error handling for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "error.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Messages for internal compiler errors */ +const char _MsgCheckFailed [] = + "Check failed: `%s' (= %d), file `%s', line %u\n"; +const char _MsgPrecondition [] = + "Precondition violated: `%s' (= %d), file `%s', line %u\n"; +const char _MsgFail [] = + "%s, file `%s', line %u\n"; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Warning (const char* Format, ...) +/* Print a warning message */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "Warning: "); + vfprintf (stderr, Format, ap); + putc ('\n', stderr); + va_end (ap); +} + + + +void Error (const char* Format, ...) +/* Print an error message and die */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "Error: "); + vfprintf (stderr, Format, ap); + putc ('\n', stderr); + va_end (ap); + exit (EXIT_FAILURE); +} + + + +void Internal (const char* Format, ...) +/* Print an internal error message and die */ +{ + va_list ap; + va_start (ap, Format); + fprintf (stderr, "Internal error: "); + vfprintf (stderr, Format, ap); + putc ('\n', stderr); + va_end (ap); + exit (EXIT_FAILURE); +} + + + diff --git a/src/ld65/error.h b/src/ld65/error.h new file mode 100644 index 000000000..9f213abe2 --- /dev/null +++ b/src/ld65/error.h @@ -0,0 +1,87 @@ +/*****************************************************************************/ +/* */ +/* global.h */ +/* */ +/* Error handling for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef ERROR_H +#define ERROR_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Messages for internal compiler errors */ +extern const char _MsgCheckFailed []; +extern const char _MsgPrecondition []; +extern const char _MsgFail []; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Warning (const char* Format, ...); +/* Print a warning message */ + +void Error (const char* Format, ...); +/* Print an error message and die */ + +void Internal (const char* Format, ...); +/* Print an internal error message and die */ + +#define CHECK(c) \ + if (!(c)) \ + Internal (_MsgCheckFailed, #c, c, __FILE__, __LINE__) + +#define PRECONDITION(c) \ + if (!(c)) \ + Internal (_MsgPrecondition, #c, c, __FILE__, __LINE__) + +#define FAIL(s) \ + Internal (_MsgFail, s, __FILE__, __LINE__) + + + +/* End of error.h */ + +#endif + + + diff --git a/src/ld65/exports.c b/src/ld65/exports.c new file mode 100644 index 000000000..1929a6a7b --- /dev/null +++ b/src/ld65/exports.c @@ -0,0 +1,662 @@ +/*****************************************************************************/ +/* */ +/* exports.c */ +/* */ +/* Exports handing for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "../common/symdefs.h" +#include "../common/hashstr.h" + +#include "global.h" +#include "mem.h" +#include "error.h" +#include "fileio.h" +#include "objdata.h" +#include "expr.h" +#include "exports.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Hash table */ +#define HASHTAB_SIZE 4081 +static Export* HashTab [HASHTAB_SIZE]; + +/* Import management variables */ +static unsigned ImpCount = 0; /* Import count */ +static unsigned ImpOpen = 0; /* Count of open imports */ + +/* Export management variables */ +static unsigned ExpCount = 0; /* Export count */ +static Export** ExpPool = 0; /* Exports array */ + +/* Defines for the flags in Export */ +#define EXP_USERMARK 0x0001 + + + +/*****************************************************************************/ +/* Import handling */ +/*****************************************************************************/ + + + +static Export* NewExport (unsigned char Type, const char* Name, ObjData* Obj); +/* Create a new export and initialize it */ + + + +static Import* NewImport (unsigned char Type, ObjData* Obj) +/* Create a new import and initialize it */ +{ + /* Allocate memory */ + Import* I = Xmalloc (sizeof (Import)); + + /* Initialize the fields */ + I->Next = 0; + I->Obj = Obj; + I->V.Name = 0; + I->Type = Type; + + /* Return the new structure */ + return I; +} + + + +void InsertImport (Import* I) +/* Insert an import into the table */ +{ + Export* E; + unsigned HashVal; + + /* As long as the import is not inserted, V.Name is valid */ + const char* Name = I->V.Name; + + /* Create a hash value for the given name */ + HashVal = HashStr (Name) % HASHTAB_SIZE; + + /* Search through the list in that slot and print matching duplicates */ + if (HashTab [HashVal] == 0) { + /* The slot is empty, we need to insert a dummy export */ + E = HashTab [HashVal] = NewExport (0, Name, 0); + ++ExpCount; + } else { + E = HashTab [HashVal]; + while (1) { + if (strcmp (E->Name, Name) == 0) { + /* We have an entry, L points to it */ + break; + } + if (E->Next == 0) { + /* End of list an entry not found, insert a dummy */ + E->Next = NewExport (0, Name, 0); + E = E->Next; /* Point to dummy */ + ++ExpCount; /* One export more */ + break; + } else { + E = E->Next; + } + } + } + + /* Ok, E now points to a valid exports entry for the given import. Insert + * the import into the imports list and update the counters. + */ + I->V.Exp = E; + I->Next = E->ImpList; + E->ImpList = I; + E->ImpCount++; + ++ImpCount; /* Total import count */ + if (E->Expr == 0) { + /* This is a dummy export */ + ++ImpOpen; + } + + /* Now free the name since it's no longer needed */ + Xfree (Name); +} + + + +Import* ReadImport (FILE* F, ObjData* Obj) +/* Read an import from a file and return it */ +{ + Import* I; + + /* Read the import type and check it */ + unsigned char Type = Read8 (F); + if (Type != IMP_ZP && Type != IMP_ABS) { + Error ("Unknown import type in module `%s': %02X", Obj->Name, Type); + } + + /* Create a new import */ + I = NewImport (Type, Obj); + + /* Read the name */ + I->V.Name = ReadMallocedStr (F); + + /* Read the file position */ + ReadFilePos (F, &I->Pos); + + /* Return the new import */ + return I; +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static Export* NewExport (unsigned char Type, const char* Name, ObjData* Obj) +/* Create a new export and initialize it */ +{ + /* Get the length of the symbol name */ + unsigned Len = strlen (Name); + + /* Allocate memory */ + Export* E = Xmalloc (sizeof (Export) + Len); + + /* Initialize the fields */ + E->Next = 0; + E->Flags = 0; + E->Obj = Obj; + E->ImpCount = 0; + E->ImpList = 0; + E->Expr = 0; + E->Type = Type; + memcpy (E->Name, Name, Len); + E->Name [Len] = '\0'; + + /* Return the new entry */ + return E; +} + + + +void InsertExport (Export* E) +/* Insert an exported identifier and check if it's already in the list */ +{ + Export* L; + Export* Last; + Import* Imp; + unsigned HashVal; + + /* Create a hash value for the given name */ + HashVal = HashStr (E->Name) % HASHTAB_SIZE; + + /* Search through the list in that slot */ + if (HashTab [HashVal] == 0) { + /* The slot is empty */ + HashTab [HashVal] = E; + ++ExpCount; + } else { + + Last = 0; + L = HashTab [HashVal]; + do { + if (strcmp (L->Name, E->Name) == 0) { + /* This may be an unresolved external */ + if (L->Expr == 0) { + + /* This *is* an unresolved external */ + E->Next = L->Next; + E->ImpCount = L->ImpCount; + E->ImpList = L->ImpList; + if (Last) { + Last->Next = E; + } else { + HashTab [HashVal] = E; + } + ImpOpen -= E->ImpCount; /* Decrease open imports now */ + Xfree (L); + /* We must run through the import list and change the + * export pointer now. + */ + Imp = E->ImpList; + while (Imp) { + Imp->V.Exp = E; + Imp = Imp->Next; + } + } else { + /* Duplicate entry, ignore it */ + Warning ("Duplicate external identifier: `%s'", L->Name); + } + return; + } + Last = L; + L = L->Next; + + } while (L); + + /* Insert export at end of queue */ + Last->Next = E; + ++ExpCount; + } +} + + + +Export* ReadExport (FILE* F, ObjData* O) +/* Read an export from a file */ +{ + unsigned char Type; + char Name [256]; + Export* E; + + /* Read the type */ + Type = Read8 (F); + + /* Read the name */ + ReadStr (F, Name); + + /* Create a new export */ + E = NewExport (Type, Name, O); + + /* Read the value */ + if (Type & EXP_EXPR) { + E->Expr = ReadExpr (F, O); + } else { + E->Expr = LiteralExpr (Read32 (F), O); + } + + /* Last is the file position where the definition was done */ + ReadFilePos (F, &E->Pos); + + /* Return the new export */ + return E; +} + + + +Export* CreateConstExport (const char* Name, long Value) +/* Create an export for a literal date */ +{ + /* Create a new export */ + Export* E = NewExport (EXP_ABS, Name, 0); + + /* Assign the value */ + E->Expr = LiteralExpr (Value, 0); + + /* Insert the export */ + InsertExport (E); + + /* Return the new export */ + return E; +} + + + +Export* CreateMemExport (const char* Name, Memory* Mem, unsigned long Offs) +/* Create an relative export for a memory area offset */ +{ + /* Create a new export */ + Export* E = NewExport (EXP_ABS, Name, 0); + + /* Assign the value */ + E->Expr = MemExpr (Mem, Offs, 0); + + /* Insert the export */ + InsertExport (E); + + /* Return the new export */ + return E; +} + + + +static Export* FindExport (const char* Name) +/* Check for an identifier in the list. Return 0 if not found, otherwise + * return a pointer to the export. + */ +{ + /* Get a pointer to the list with the symbols hash value */ + Export* L = HashTab [HashStr (Name) % HASHTAB_SIZE]; + while (L) { + /* Search through the list in that slot */ + if (strcmp (L->Name, Name) == 0) { + /* Entry found */ + return L; + } + L = L->Next; + } + + /* Not found */ + return 0; +} + + + +int IsUnresolved (const char* Name) +/* Check if this symbol is an unresolved export */ +{ + /* Find the export */ + Export* E = FindExport (Name); + + /* Check if it's unresolved */ + return E != 0 && E->Expr == 0; +} + + + +int IsConstExport (const Export* E) +/* Return true if the expression associated with this export is const */ +{ + if (E->Expr == 0) { + /* External symbols cannot be const */ + return 0; + } else { + return IsConstExpr (E->Expr); + } +} + + + +long GetExportVal (const Export* E) +/* Get the value of this export */ +{ + if (E->Expr == 0) { + /* OOPS */ + Internal ("`%s' is an undefined external", E->Name); + } + return GetExprVal (E->Expr); +} + + + +static void CheckSymType (Export* E) +/* Check the types for one export */ +{ + /* External with matching imports */ + Import* Imp = E->ImpList; + int ZP = (E->Type & EXP_ZP) != 0; + while (Imp) { + if (ZP != ((Imp->Type & IMP_ZP) != 0)) { + /* Export is ZP, import is abs or the other way round */ + if (E->Obj) { + /* User defined export */ + Warning ("Type mismatch for `%s', export in " + "%s(%lu), import in %s(%lu)", + E->Name, E->Obj->Files [Imp->Pos.Name], + E->Pos.Line, Imp->Obj->Files [Imp->Pos.Name], + Imp->Pos.Line); + } else { + /* Export created by the linker */ + Warning ("Type mismatch for `%s', imported from %s(%lu)", + E->Name, Imp->Obj->Files [Imp->Pos.Name], + Imp->Pos.Line); + } + } + Imp = Imp->Next; + } +} + + + +static void CheckSymTypes (void) +/* Check for symbol tape mismatches */ +{ + unsigned I; + + /* Print all open imports */ + for (I = 0; I < ExpCount; ++I) { + Export* E = ExpPool [I]; + if (E->Expr != 0 && E->ImpCount > 0) { + /* External with matching imports */ + CheckSymType (E); + } + } +} + + + +static void PrintUnresolved (ExpCheckFunc F, void* Data) +/* Print a list of unresolved symbols. On unresolved symbols, F is + * called (see the comments on ExpCheckFunc in the data section). + */ +{ + unsigned I; + + /* Print all open imports */ + for (I = 0; I < ExpCount; ++I) { + Export* E = ExpPool [I]; + if (E->Expr == 0 && E->ImpCount > 0 && F (E->Name, Data) == 0) { + /* Unresolved external */ + Import* Imp = E->ImpList; + fprintf (stderr, + "Unresolved external `%s' referenced in:\n", + E->Name); + while (Imp) { + const char* Name = Imp->Obj->Files [Imp->Pos.Name]; + fprintf (stderr, " %s(%lu)\n", Name, Imp->Pos.Line); + Imp = Imp->Next; + } + } + } +} + + + +static int CmpExpName (const void* K1, const void* K2) +/* Compare function for qsort */ +{ + return strcmp ((*(Export**)K1)->Name, (*(Export**)K2)->Name); +} + + + +static void CreateExportPool (void) +/* Create an array with pointer to all exports */ +{ + unsigned I, J; + + /* Allocate memory */ + if (ExpPool) { + Xfree (ExpPool); + } + ExpPool = Xmalloc (ExpCount * sizeof (Export*)); + + /* Walk through the list and insert the exports */ + for (I = 0, J = 0; I < sizeof (HashTab) / sizeof (HashTab [0]); ++I) { + Export* E = HashTab [I]; + while (E) { + CHECK (J < ExpCount); + ExpPool [J++] = E; + E = E->Next; + } + } + + /* Sort them by name */ + qsort (ExpPool, ExpCount, sizeof (Export*), CmpExpName); +} + + + +void CheckExports (ExpCheckFunc F, void* Data) +/* Check if there are any unresolved symbols. On unresolved symbols, F is + * called (see the comments on ExpCheckFunc in the data section). + */ +{ + /* Create an export pool */ + CreateExportPool (); + + /* Check for symbol type mismatches */ + CheckSymTypes (); + + /* Check for unresolved externals (check here for special bin formats) */ + if (ImpOpen != 0) { + /* Print all open imports */ + PrintUnresolved (F, Data); + } +} + + + +void PrintExportMap (FILE* F) +/* Print an export map to the given file */ +{ + unsigned I; + unsigned Count; + + /* Print all exports */ + Count = 0; + for (I = 0; I < ExpCount; ++I) { + Export* E = ExpPool [I]; + + /* Print unreferenced symbols only if explictly requested */ + if (VerboseMap || E->ImpCount > 0) { + fprintf (F, + "%-25s %06lX %c%c ", + E->Name, + GetExportVal (E), + E->ImpCount? 'R' : ' ', + (E->Type & EXP_ZP)? 'Z' : ' '); + if (++Count == 2) { + Count = 0; + fprintf (F, "\n"); + } + } + } + fprintf (F, "\n"); +} + + + +void PrintImportMap (FILE* F) +/* Print an import map to the given file */ +{ + unsigned I; + Import* Imp; + + /* Loop over all exports */ + for (I = 0; I < ExpCount; ++I) { + + /* Get the export */ + Export* Exp = ExpPool [I]; + + /* Print the symbol only if there are imports, or if a verbose map + * file is requested. + */ + if (VerboseMap || Exp->ImpCount > 0) { + + /* Get the name of the object file that exports the symbol. + * Beware: There may be no object file if the symbol is a linker + * generated symbol. + */ + const char* ObjName = (Exp->Obj != 0)? Exp->Obj->Name : "linker generated"; + + /* Print the export */ + fprintf (F, + "%s (%s):\n", + Exp->Name, + ObjName); + + /* Print all imports for this symbol */ + Imp = Exp->ImpList; + while (Imp) { + + /* Print the import */ + fprintf (F, + " %-25s %s(%lu)\n", + Imp->Obj->Name, + Imp->Obj->Files [Imp->Pos.Name], + Imp->Pos.Line); + + /* Next import */ + Imp = Imp->Next; + } + } + } + fprintf (F, "\n"); +} + + + +void PrintExportLabels (FILE* F) +/* Print the exports in a VICE label file */ +{ + unsigned I; + + /* Print all exports */ + for (I = 0; I < ExpCount; ++I) { + Export* E = ExpPool [I]; + fprintf (F, "al %06lX .%s\n", GetExportVal (E), E->Name); + } +} + + + +void MarkExport (Export* E) +/* Mark the export */ +{ + E->Flags |= EXP_USERMARK; +} + + + +void UnmarkExport (Export* E) +/* Remove the mark from the export */ +{ + E->Flags &= ~EXP_USERMARK; +} + + + +int ExportHasMark (Export* E) +/* Return true if the export has a mark */ +{ + return (E->Flags & EXP_USERMARK) != 0; +} + + + +void CircularRefError (const Export* E) +/* Print an error about a circular reference using to define the given export */ +{ + Error ("Circular reference for symbol `%s', %s(%lu)", + E->Name, E->Obj->Files [E->Pos.Name], E->Pos.Line); +} + + + diff --git a/src/ld65/exports.h b/src/ld65/exports.h new file mode 100644 index 000000000..46a9646b0 --- /dev/null +++ b/src/ld65/exports.h @@ -0,0 +1,164 @@ +/*****************************************************************************/ +/* */ +/* exports.h */ +/* */ +/* Exports handing for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef EXPORTS_H +#define EXPORTS_H + + + +#include + +#include "../common/exprdefs.h" +#include "../common/filepos.h" + +#include "objdata.h" +#include "config.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Import symbol structure */ +typedef struct Import_ Import; +struct Import_ { + Import* Next; /* Single linked list */ + ObjData* Obj; /* Object file that imports the name */ + FilePos Pos; /* File position of reference */ + union { + struct Export_* Exp; /* Matching export for this import */ + const char* Name; /* Name if not in table */ + } V; + unsigned char Type; /* Type of import */ +}; + + + +/* Export symbol structure */ +typedef struct Export_ Export; +struct Export_ { + Export* Next; /* Hash table link */ + unsigned Flags; /* Generic flags */ + ObjData* Obj; /* Object file that exports the name */ + unsigned ImpCount; /* How many imports for this symbol? */ + Import* ImpList; /* List of imports for this symbol */ + FilePos Pos; /* File position of definition */ + ExprNode* Expr; /* Expression (0 if not def'd) */ + unsigned char Type; /* Type of export */ + char Name [1]; /* Name - dynamically allocated */ +}; + + + +/* Prototype of a function that is called if an undefined symbol is found. It + * may check if the symbol is an external symbol (for binary formats that + * support externals) and will return zero if the symbol could not be + * resolved, or a value != zero if the symbol could be resolved. The + * CheckExports routine will print out the missing symbol in the first case. + */ +typedef int (*ExpCheckFunc) (const char* Name, void* Data); + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +Import* ReadImport (FILE* F, ObjData* Obj); +/* Read an import from a file and insert it into the table */ + +void InsertImport (Import* I); +/* Insert an import into the table */ + +Export* ReadExport (FILE* F, ObjData* Obj); +/* Read an export from a file */ + +void InsertExport (Export* E); +/* Insert an exported identifier and check if it's already in the list */ + +Export* CreateConstExport (const char* Name, long Value); +/* Create an export for a literal date */ + +Export* CreateMemExport (const char* Name, Memory* Mem, unsigned long Offs); +/* Create an relative export for a memory area offset */ + +int IsUnresolved (const char* Name); +/* Check if this symbol is an unresolved export */ + +int IsConstExport (const Export* E); +/* Return true if the expression associated with this export is const */ + +long GetExportVal (const Export* E); +/* Get the value of this export */ + +void CheckExports (ExpCheckFunc F, void* Data); +/* Check if there are any unresolved symbols. On unresolved symbols, F is + * called (see the comments on ExpCheckFunc in the data section). + */ + +void PrintExportMap (FILE* F); +/* Print an export map to the given file */ + +void PrintImportMap (FILE* F); +/* Print an import map to the given file */ + +void PrintExportLabels (FILE* F); +/* Print the exports in a VICE label file */ + +void MarkExport (Export* E); +/* Mark the export */ + +void UnmarkExport (Export* E); +/* Remove the mark from the export */ + +int ExportHasMark (Export* E); +/* Return true if the export has a mark */ + +void CircularRefError (const Export* E); +/* Print an error about a circular reference using to define the given export */ + + + +/* End of exports.h */ + +#endif + + + diff --git a/src/ld65/expr.c b/src/ld65/expr.c new file mode 100644 index 000000000..a47c653b7 --- /dev/null +++ b/src/ld65/expr.c @@ -0,0 +1,622 @@ +/*****************************************************************************/ +/* */ +/* expr.c */ +/* */ +/* Expression evaluation for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "../common/exprdefs.h" + +#include "global.h" +#include "error.h" +#include "mem.h" +#include "fileio.h" +#include "segments.h" +#include "expr.h" + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +static ExprNode* NewExprNode (ObjData* O) +/* Create a new expression node */ +{ + /* Allocate fresh memory */ + ExprNode* N = Xmalloc (sizeof (ExprNode)); + N->Op = EXPR_NULL; + N->Left = 0; + N->Right = 0; + N->Obj = O; + N->V.Val = 0; + + return N; +} + + + +static void FreeExprNode (ExprNode* E) +/* Free a node */ +{ + /* Free the memory */ + Xfree (E); +} + + + +/*****************************************************************************/ +/* Dump an expression tree on stdout for debugging */ +/*****************************************************************************/ + + + +static void InternalDumpExpr (const ExprNode* Expr) +/* Dump an expression in UPN */ +{ + if (Expr == 0) { + return; + } + InternalDumpExpr (Expr->Left); + InternalDumpExpr (Expr->Right); + + switch (Expr->Op) { + + case EXPR_LITERAL: + printf (" $%04lX", Expr->V.Val & 0xFFFF); + break; + + case EXPR_SYMBOL: + printf (" SYM"); + break; + + case EXPR_SEGMENT: + printf (" SEG"); + break; + + case EXPR_PLUS: + printf (" +"); + break; + + case EXPR_MINUS: + printf (" -"); + break; + + case EXPR_MUL: + printf (" *"); + break; + + case EXPR_DIV: + printf (" /"); + break; + + case EXPR_MOD: + printf (" %%"); + break; + + case EXPR_OR: + printf (" OR"); + break; + + case EXPR_XOR: + printf (" XOR"); + break; + + case EXPR_AND: + printf (" AND"); + break; + + case EXPR_SHL: + printf (" SHL"); + break; + + case EXPR_SHR: + printf (" SHR"); + break; + + case EXPR_EQ: + printf (" ="); + break; + + case EXPR_NE: + printf ("<>"); + break; + + case EXPR_LT: + printf (" <"); + break; + + case EXPR_GT: + printf (" >"); + break; + + case EXPR_UNARY_MINUS: + printf (" NEG"); + break; + + case EXPR_NOT: + printf (" ~"); + break; + + case EXPR_LOBYTE: + printf (" LO"); + break; + + case EXPR_HIBYTE: + printf (" HI"); + break; + + case EXPR_SWAP: + printf (" SWAP"); + break; + + case EXPR_BAND: + printf (" BOOL_AND"); + break; + + case EXPR_BOR: + printf (" BOOL_OR"); + break; + + case EXPR_BXOR: + printf (" BOOL_XOR"); + break; + + case EXPR_BNOT: + printf (" BOOL_NOT"); + break; + + default: + Internal ("Unknown Op type: %u", Expr->Op); + + } +} + + + +void DumpExpr (const ExprNode* Expr) +/* Dump an expression tree to stdout */ +{ + InternalDumpExpr (Expr); + printf ("\n"); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void FreeExpr (ExprNode* Root) +/* Free the expression, Root is pointing to. */ +{ + if (Root) { + FreeExpr (Root->Left); + FreeExpr (Root->Right); + FreeExprNode (Root); + } +} + + + +int IsConstExpr (ExprNode* Root) +/* Return true if the given expression is a constant expression, that is, one + * with no references to external symbols. + */ +{ + int Const; + Export* E; + + if (EXPR_IS_LEAF (Root->Op)) { + switch (Root->Op) { + + case EXPR_LITERAL: + return 1; + + case EXPR_SYMBOL: + /* Get the referenced export */ + E = GetExprExport (Root); + /* If this export has a mark set, we've already encountered it. + * This means that the export is used to define it's own value, + * which in turn means, that we have a circular reference. + */ + if (ExportHasMark (E)) { + Error ("Circular reference for symbol `%s', %s(%u)", + E->Name, E->Obj->Files [E->Pos.Name], E->Pos.Line); + Const = 0; + } else { + MarkExport (E); + Const = IsConstExport (E); + UnmarkExport (E); + } + return Const; + + default: + return 0; + + } + } else if (EXPR_IS_UNARY (Root->Op)) { + + return IsConstExpr (Root->Left); + + } else { + + /* We must handle shortcut boolean expressions here */ + switch (Root->Op) { + + case EXPR_BAND: + if (IsConstExpr (Root->Left)) { + /* lhs is const, if it is zero, don't eval right */ + if (GetExprVal (Root->Left) == 0) { + return 1; + } else { + return IsConstExpr (Root->Right); + } + } else { + /* lhs not const --> tree not const */ + return 0; + } + break; + + case EXPR_BOR: + if (IsConstExpr (Root->Left)) { + /* lhs is const, if it is not zero, don't eval right */ + if (GetExprVal (Root->Left) != 0) { + return 1; + } else { + return IsConstExpr (Root->Right); + } + } else { + /* lhs not const --> tree not const */ + return 0; + } + break; + + default: + /* All others are handled normal */ + return IsConstExpr (Root->Left) && IsConstExpr (Root->Right); + } + } +} + + + +Import* GetExprImport (ExprNode* Expr) +/* Get the import data structure for a symbol expression node */ +{ + /* Check that this is really a symbol */ + PRECONDITION (Expr->Op == EXPR_SYMBOL); + + /* Return the import */ + return Expr->Obj->Imports [Expr->V.ImpNum]; +} + + + +Export* GetExprExport (ExprNode* Expr) +/* Get the exported symbol for a symbol expression node */ +{ + /* Check that this is really a symbol */ + PRECONDITION (Expr->Op == EXPR_SYMBOL); + + /* Return the export */ + return Expr->Obj->Imports [Expr->V.ImpNum]->V.Exp; +} + + + +Section* GetExprSection (ExprNode* Expr) +/* Get the segment for a segment expression node */ +{ + /* Check that this is really a segment node */ + PRECONDITION (Expr->Op == EXPR_SEGMENT); + + /* Return the export */ + return Expr->Obj->Sections [Expr->V.SegNum]; +} + + + +long GetExprVal (ExprNode* Expr) +/* Get the value of a constant expression */ +{ + long Right, Left, Val; + Section* S; + Export* E; + + switch (Expr->Op) { + + case EXPR_LITERAL: + return Expr->V.Val; + + case EXPR_SYMBOL: + /* Get the referenced export */ + E = GetExprExport (Expr); + /* If this export has a mark set, we've already encountered it. + * This means that the export is used to define it's own value, + * which in turn means, that we have a circular reference. + */ + if (ExportHasMark (E)) { + CircularRefError (E); + Val = 0; + } else { + MarkExport (E); + Val = GetExportVal (E); + UnmarkExport (E); + } + return Val; + + case EXPR_SEGMENT: + S = GetExprSection (Expr); + return S->Offs + S->Seg->PC; + + case EXPR_MEMAREA: + return Expr->V.MemArea->Start; + + case EXPR_PLUS: + return GetExprVal (Expr->Left) + GetExprVal (Expr->Right); + + case EXPR_MINUS: + return GetExprVal (Expr->Left) - GetExprVal (Expr->Right); + + case EXPR_MUL: + return GetExprVal (Expr->Left) * GetExprVal (Expr->Right); + + case EXPR_DIV: + Left = GetExprVal (Expr->Left); + Right = GetExprVal (Expr->Right); + if (Right == 0) { + Error ("Division by zero"); + } + return Left / Right; + + case EXPR_MOD: + Left = GetExprVal (Expr->Left); + Right = GetExprVal (Expr->Right); + if (Right == 0) { + Error ("Modulo operation with zero"); + } + return Left % Right; + + case EXPR_OR: + return GetExprVal (Expr->Left) | GetExprVal (Expr->Right); + + case EXPR_XOR: + return GetExprVal (Expr->Left) ^ GetExprVal (Expr->Right); + + case EXPR_AND: + return GetExprVal (Expr->Left) & GetExprVal (Expr->Right); + + case EXPR_SHL: + return GetExprVal (Expr->Left) << GetExprVal (Expr->Right); + + case EXPR_SHR: + return GetExprVal (Expr->Left) >> GetExprVal (Expr->Right); + + case EXPR_EQ: + return (GetExprVal (Expr->Left) == GetExprVal (Expr->Right)); + + case EXPR_NE: + return (GetExprVal (Expr->Left) != GetExprVal (Expr->Right)); + + case EXPR_LT: + return (GetExprVal (Expr->Left) < GetExprVal (Expr->Right)); + + case EXPR_GT: + return (GetExprVal (Expr->Left) > GetExprVal (Expr->Right)); + + case EXPR_LE: + return (GetExprVal (Expr->Left) <= GetExprVal (Expr->Right)); + + case EXPR_GE: + return (GetExprVal (Expr->Left) >= GetExprVal (Expr->Right)); + + case EXPR_UNARY_MINUS: + return -GetExprVal (Expr->Left); + + case EXPR_NOT: + return ~GetExprVal (Expr->Left); + + case EXPR_LOBYTE: + return GetExprVal (Expr->Left) & 0xFF; + + case EXPR_HIBYTE: + return (GetExprVal (Expr->Left) >> 8) & 0xFF; + + case EXPR_SWAP: + Left = GetExprVal (Expr->Left); + return ((Left >> 8) & 0x00FF) | ((Left << 8) & 0xFF00); + + case EXPR_BAND: + return GetExprVal (Expr->Left) && GetExprVal (Expr->Right); + + case EXPR_BOR: + return GetExprVal (Expr->Left) || GetExprVal (Expr->Right); + + case EXPR_BXOR: + return (GetExprVal (Expr->Left) != 0) ^ (GetExprVal (Expr->Right) != 0); + + case EXPR_BNOT: + return !GetExprVal (Expr->Left); + + default: + Internal ("Unknown expression Op type: %u", Expr->Op); + /* NOTREACHED */ + return 0; + } +} + + + +ExprNode* LiteralExpr (long Val, ObjData* O) +/* Return an expression tree that encodes the given literal value */ +{ + ExprNode* Expr = NewExprNode (O); + Expr->Op = EXPR_LITERAL; + Expr->V.Val = Val; + return Expr; +} + + + +ExprNode* MemExpr (Memory* Mem, long Offs, ObjData* O) +/* Return an expression tree that encodes an offset into the memory area */ +{ + ExprNode* Root; + + ExprNode* Expr = NewExprNode (O); + Expr->Op = EXPR_MEMAREA; + Expr->V.MemArea = Mem; + + Root = NewExprNode (O); + Root->Op = EXPR_PLUS; + Root->Left = Expr; + Root->Right = LiteralExpr (Offs, O); + + return Root; +} + + + +ExprNode* ReadExpr (FILE* F, ObjData* O) +/* Read an expression from the given file */ +{ + ExprNode* Expr; + + /* Read the node tag and handle NULL nodes */ + unsigned char Op = Read8 (F); + if (Op == EXPR_NULL) { + return 0; + } + + /* Create a new node */ + Expr = NewExprNode (O); + Expr->Op = Op; + + /* Check the tag and handle the different expression types */ + if (EXPR_IS_LEAF (Op)) { + switch (Op) { + + case EXPR_LITERAL: + Expr->V.Val = Read32Signed (F); + break; + + case EXPR_SYMBOL: + /* Read the import number */ + Expr->V.ImpNum = Read16 (F); + break; + + case EXPR_SEGMENT: + /* Read the segment number */ + Expr->V.SegNum = Read8 (F); + break; + + default: + Error ("Invalid expression op: %02X", Op); + + } + + } else { + + /* Not a leaf node */ + Expr->Left = ReadExpr (F, O); + Expr->Right = ReadExpr (F, O); + + } + + /* Return the tree */ + return Expr; +} + + + +int EqualExpr (ExprNode* E1, ExprNode* E2) +/* Check if two expressions are identical. */ +{ + /* If one pointer is NULL, both must be NULL */ + if ((E1 == 0) ^ (E2 == 0)) { + return 0; + } + if (E1 == 0) { + return 1; + } + + /* Both pointers not NULL, check OP */ + if (E1->Op != E2->Op) { + return 0; + } + + /* OPs are identical, check data for leafs, or subtrees */ + switch (E1->Op) { + + case EXPR_LITERAL: + /* Value must be identical */ + return (E1->V.Val == E2->V.Val); + + case EXPR_SYMBOL: + /* Import number must be identical */ + return (E1->V.ImpNum == E2->V.ImpNum); + + case EXPR_SEGMENT: + /* Segment number must be identical */ + return (E1->V.SegNum == E2->V.SegNum); + + case EXPR_MEMAREA: + /* Memory area must be identical */ + return (E1->V.MemArea == E2->V.MemArea); + + default: + /* Not a leaf node */ + return EqualExpr (E1->Left, E2->Left) && EqualExpr (E1->Right, E2->Right); + } + +} + + + + + + + + + + + + + + + + + diff --git a/src/ld65/expr.h b/src/ld65/expr.h new file mode 100644 index 000000000..6305e9ee4 --- /dev/null +++ b/src/ld65/expr.h @@ -0,0 +1,97 @@ +/*****************************************************************************/ +/* */ +/* expr.h */ +/* */ +/* Expression evaluation for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef EXPR_H +#define EXPR_H + + + +#include "../common/exprdefs.h" + +#include "objdata.h" +#include "exports.h" +#include "config.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void FreeExpr (ExprNode* Root); +/* Free the expression tree, Root is pointing to. */ + +int IsConstExpr (ExprNode* Root); +/* Return true if the given expression is a constant expression, that is, one + * with no references to external symbols. + */ + +Import* GetExprImport (ExprNode* Expr); +/* Get the import data structure for a symbol expression node */ + +Export* GetExprExport (ExprNode* Expr); +/* Get the exported symbol for a symbol expression node */ + +Section* GetExprSection (ExprNode* Expr); +/* Get the segment for a segment expression node */ + +long GetExprVal (ExprNode* Expr); +/* Get the value of a constant expression */ + +ExprNode* LiteralExpr (long Val, ObjData* O); +/* Return an expression tree that encodes the given literal value */ + +ExprNode* MemExpr (Memory* Mem, long Offs, ObjData* O); +/* Return an expression tree that encodes an offset into the memory area */ + +void DumpExpr (const ExprNode* Expr); +/* Dump an expression tree to stdout */ + +ExprNode* ReadExpr (FILE* F, ObjData* O); +/* Read an expression from the given file */ + +int EqualExpr (ExprNode* E1, ExprNode* E2); +/* Check if two expressions are identical. */ + + + +/* End of expr.h */ + +#endif + + + diff --git a/src/ld65/extsyms.c b/src/ld65/extsyms.c new file mode 100644 index 000000000..73582b353 --- /dev/null +++ b/src/ld65/extsyms.c @@ -0,0 +1,237 @@ +/*****************************************************************************/ +/* */ +/* extsyms.c */ +/* */ +/* Handle program external symbols for relocatable output formats */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "../common/hashstr.h" + +#include "mem.h" +#include "error.h" +#include "extsyms.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Structure holding an external symbol */ +struct ExtSym_ { + ExtSym* List; /* Next entry in list of all symbols */ + ExtSym* Next; /* Next entry in hash list */ + unsigned Flags; /* Generic flags */ + unsigned Num; /* Number of external symbol */ + char Name [1]; /* Name - dynamically allocated */ +}; + +/* External symbol table structure */ +#define HASHTAB_SIZE 53 +struct ExtSymTab_ { + ExtSym* Root; /* List of symbols */ + ExtSym* Last; /* Pointer to last symbol */ + unsigned Count; /* Number of symbols */ + ExtSym* HashTab [HASHTAB_SIZE]; +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +ExtSym* NewExtSym (ExtSymTab* Tab, const char* Name) +/* Create a new external symbol and insert it into the table */ +{ + /* Get the hash value of the string */ + unsigned Hash = HashStr (Name) % HASHTAB_SIZE; + + /* Get the length of the name */ + unsigned Len = strlen (Name); + + /* Check for duplicates */ + ExtSym* E = GetExtSym (Tab, Name); /* Don't care about duplicate hash here... */ + if (E != 0) { + /* We do already have a symbol with this name */ + Error ("Duplicate external symbol `%s'", Name); + } + + /* Allocate memory for the structure */ + E = Xmalloc (sizeof (ExtSym) + Len); + + /* Initialize the structure */ + E->List = 0; + E->Flags = 0; + E->Num = Tab->Count; + memcpy (E->Name, Name, Len+1); + + /* Insert the entry into the list of all symbols */ + if (Tab->Last == 0) { + /* List is empty */ + Tab->Root = E; + } else { + /* List not empty */ + Tab->Last->List = E; + } + Tab->Last = E; + Tab->Count++; + + /* Insert the symbol into the hash table */ + E->Next = Tab->HashTab [Hash]; + Tab->HashTab [Hash] = E; + + /* Done, return the created entry */ + return E; +} + + + +static void FreeExtSym (ExtSym* E) +/* Free an external symbol structure. Will not unlink the entry, so internal + * use only. + */ +{ + Xfree (E); +} + + + +ExtSymTab* NewExtSymTab (void) +/* Create a new external symbol table */ +{ + unsigned I; + + /* Allocate memory */ + ExtSymTab* Tab = Xmalloc (sizeof (ExtSymTab)); + + /* Initialize the fields */ + Tab->Root = 0; + Tab->Last = 0; + Tab->Count = 0; + for (I = 0; I < HASHTAB_SIZE; ++I) { + Tab->HashTab [I] = 0; + } + + /* Done, return the hash table */ + return Tab; +} + + + +void FreeExtSymTab (ExtSymTab* Tab) +/* Free an external symbol structure */ +{ + /* Free all entries */ + while (Tab->Root) { + ExtSym* E = Tab->Root; + Tab->Root = E->Next; + FreeExtSym (E); + } + + /* Free the struct itself */ + Xfree (Tab); +} + + + +ExtSym* GetExtSym (const ExtSymTab* Tab, const char* Name) +/* Return the entry for the external symbol with the given name. Return NULL + * if there is no such symbol. + */ +{ + /* Hash the name */ + unsigned Hash = HashStr (Name) % HASHTAB_SIZE; + + /* Check the linked list */ + ExtSym* E = Tab->HashTab [Hash]; + while (E) { + if (strcmp (E->Name, Name) == 0) { + /* Found it */ + break; + } + E = E->Next; + } + + /* Return the symbol we found */ + return E; +} + + + +unsigned ExtSymCount (const ExtSymTab* Tab) +/* Return the number of symbols in the table */ +{ + return Tab->Count; +} + + + +const ExtSym* ExtSymList (const ExtSymTab* Tab) +/* Return the start of the symbol list sorted by symbol number. Call + * ExtSymNext for the next symbol. + */ +{ + return Tab->Root; +} + + + +unsigned ExtSymNum (const ExtSym* E) +/* Return the number of an external symbol */ +{ + return E->Num; +} + + + +const char* ExtSymName (const ExtSym* E) +/* Return the symbol name */ +{ + return E->Name; +} + + + +const ExtSym* ExtSymNext (const ExtSym* E) +/* Return the next symbol in the list */ +{ + return E->Next; +} + + + diff --git a/src/ld65/extsyms.h b/src/ld65/extsyms.h new file mode 100644 index 000000000..de6b01bed --- /dev/null +++ b/src/ld65/extsyms.h @@ -0,0 +1,99 @@ +/*****************************************************************************/ +/* */ +/* extsyms.h */ +/* */ +/* Handle program external symbols for relocatable output formats */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef EXTSYMS_H +#define EXTSYMS_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Forward decl for structure holding an external symbol */ +typedef struct ExtSym_ ExtSym; + +/* External symbol table structure */ +typedef struct ExtSymTab_ ExtSymTab; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +ExtSym* NewExtSym (ExtSymTab* Tab, const char* Name); +/* Create a new external symbol and insert it into the list */ + +ExtSymTab* NewExtSymTab (void); +/* Create a new external symbol table */ + +void FreeExtSymTab (ExtSymTab* Tab); +/* Free an external symbol structure */ + +ExtSym* GetExtSym (const ExtSymTab* Tab, const char* Name); +/* Return the entry for the external symbol with the given name. Return NULL + * if there is no such symbol. + */ + +unsigned ExtSymCount (const ExtSymTab* Tab); +/* Return the number of symbols in the table */ + +const ExtSym* ExtSymList (const ExtSymTab* Tab); +/* Return the start of the symbol list sorted by symbol number. Call + * ExtSymNext for the next symbol. + */ + +unsigned ExtSymNum (const ExtSym* E); +/* Return the number of an external symbol */ + +const char* ExtSymName (const ExtSym* E); +/* Return the symbol name */ + +const ExtSym* ExtSymNext (const ExtSym* E); +/* Return the next symbol in the list */ + + + +/* End of extsyms.h */ + +#endif + + + diff --git a/src/ld65/fileio.c b/src/ld65/fileio.c new file mode 100644 index 000000000..263f3ab48 --- /dev/null +++ b/src/ld65/fileio.c @@ -0,0 +1,269 @@ +/*****************************************************************************/ +/* */ +/* fileio.c */ +/* */ +/* File I/O for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "error.h" +#include "mem.h" +#include "fileio.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Write8 (FILE* F, unsigned char Val) +/* Write an 8 bit value to the file */ +{ + if (putc (Val, F) == EOF) { + Error ("Write error (disk full?)"); + } +} + + + +void Write16 (FILE* F, unsigned Val) +/* Write a 16 bit value to the file */ +{ + Write8 (F, (unsigned char) Val); + Write8 (F, (unsigned char) (Val >> 8)); +} + + + +void Write24 (FILE* F, unsigned long Val) +/* Write a 24 bit value to the file */ +{ + Write8 (F, (unsigned char) Val); + Write8 (F, (unsigned char) (Val >> 8)); + Write8 (F, (unsigned char) (Val >> 16)); +} + + + +void Write32 (FILE* F, unsigned long Val) +/* Write a 32 bit value to the file */ +{ + Write8 (F, (unsigned char) Val); + Write8 (F, (unsigned char) (Val >> 8)); + Write8 (F, (unsigned char) (Val >> 16)); + Write8 (F, (unsigned char) (Val >> 24)); +} + + + +void WriteVal (FILE* F, unsigned long Val, unsigned Size) +/* Write a value of the given size to the output file */ +{ + switch (Size) { + + case 1: + Write8 (F, Val); + break; + + case 2: + Write16 (F, Val); + break; + + case 3: + Write24 (F, Val); + break; + + case 4: + Write32 (F, Val); + break; + + default: + Internal ("WriteVal: Invalid size: %u", Size); + + } +} + + + +void WriteStr (FILE* F, const char* S) +/* Write a string to the file */ +{ + unsigned Len = strlen (S); + if (Len > 255) { + Internal ("String too long"); + } + Write8 (F, (unsigned char) Len); + WriteData (F, S, Len); +} + + + +void WriteData (FILE* F, const void* Data, unsigned Size) +/* Write data to the file */ +{ + if (fwrite (Data, 1, Size, F) != Size) { + Error ("Write error (disk full?)"); + } +} + + + +void WriteMult (FILE* F, unsigned char Val, unsigned long Count) +/* Write one byte several times to the file */ +{ + while (Count--) { + Write8 (F, Val); + } +} + + + +unsigned Read8 (FILE* F) +/* Read an 8 bit value from the file */ +{ + int C = getc (F); + if (C == EOF) { + Error ("Read error (file corrupt?)"); + } + return C; +} + + + +unsigned Read16 (FILE* F) +/* Read a 16 bit value from the file */ +{ + unsigned Lo = Read8 (F); + unsigned Hi = Read8 (F); + return (Hi << 8) | Lo; +} + + + +unsigned long Read24 (FILE* F) +/* Read a 24 bit value from the file */ +{ + unsigned long Lo = Read16 (F); + unsigned long Hi = Read8 (F); + return (Hi << 16) | Lo; +} + + + +unsigned long Read32 (FILE* F) +/* Read a 32 bit value from the file */ +{ + unsigned long Lo = Read16 (F); + unsigned long Hi = Read16 (F); + return (Hi << 16) | Lo; +} + + + +long Read32Signed (FILE* F) +/* Read a 32 bit value from the file. Sign extend the value. */ +{ + /* Read a 32 bit value */ + unsigned long V = Read32 (F); + + /* Sign extend the value */ + if (V & 0x80000000UL) { + /* Signed value */ + V |= ~0xFFFFFFFFUL; + } + + /* Return it as a long */ + return (long) V; +} + + + +char* ReadStr (FILE* F, char* Str) +/* Read a string from the file. Str must hold 256 chars at max */ +{ + /* Read the length byte */ + unsigned Len = Read8 (F); + + /* Read the string itself */ + ReadData (F, Str, Len); + + /* Terminate the string and return it */ + Str [Len] = '\0'; + return Str; +} + + + +char* ReadMallocedStr (FILE* F) +/* Read a string from the file into a malloced area */ +{ + /* Read the length byte */ + unsigned Len = Read8 (F); + + /* Allocate memory */ + char* Str = Xmalloc (Len + 1); + + /* Read the string itself */ + ReadData (F, Str, Len); + + /* Terminate the string and return it */ + Str [Len] = '\0'; + return Str; +} + + + +FilePos* ReadFilePos (FILE* F, FilePos* Pos) +/* Read a file position from the file */ +{ + /* The line number is encoded as 24 bit value to save some space */ + Pos->Line = Read24 (F); + Pos->Col = Read8 (F); + Pos->Name = Read8 (F); + return Pos; +} + + + +void* ReadData (FILE* F, void* Data, unsigned Size) +/* Read data from the file */ +{ + if (fread (Data, 1, Size, F) != Size) { + Error ("Read error (file corrupt?)"); + } + return Data; +} + + + diff --git a/src/ld65/fileio.h b/src/ld65/fileio.h new file mode 100644 index 000000000..390d35ed3 --- /dev/null +++ b/src/ld65/fileio.h @@ -0,0 +1,111 @@ +/*****************************************************************************/ +/* */ +/* fileio.h */ +/* */ +/* File I/O for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef FILEIO_H +#define FILEIO_H + + + +#include + +#include "../common/filepos.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void Write8 (FILE* F, unsigned char Val); +/* Write an 8 bit value to the file */ + +void Write16 (FILE* F, unsigned Val); +/* Write a 16 bit value to the file */ + +void Write24 (FILE* F, unsigned long Val); +/* Write a 24 bit value to the file */ + +void Write32 (FILE* F, unsigned long Val); +/* Write a 32 bit value to the file */ + +void WriteVal (FILE* F, unsigned long Val, unsigned Size); +/* Write a value of the given size to the output file */ + +void WriteStr (FILE* F, const char* S); +/* Write a string to the file */ + +void WriteData (FILE* F, const void* Data, unsigned Size); +/* Write data to the file */ + +void WriteMult (FILE* F, unsigned char Val, unsigned long Count); +/* Write one byte several times to the file */ + +unsigned Read8 (FILE* F); +/* Read an 8 bit value from the file */ + +unsigned Read16 (FILE* F); +/* Read a 16 bit value from the file */ + +unsigned long Read24 (FILE* F); +/* Read a 24 bit value from the file */ + +unsigned long Read32 (FILE* F); +/* Read a 32 bit value from the file */ + +long Read32Signed (FILE* F); +/* Read a 32 bit value from the file. Sign extend the value. */ + +char* ReadStr (FILE* F, char* Str); +/* Read a string from the file. Str must hold 256 chars at max */ + +char* ReadMallocedStr (FILE* F); +/* Read a string from the file into a malloced area */ + +FilePos* ReadFilePos (FILE* F, FilePos* Pos); +/* Read a file position from the file */ + +void* ReadData (FILE* F, void* Data, unsigned Size); +/* Read data from the file */ + + + +/* End of fileio.h */ + +#endif + + + diff --git a/src/ld65/global.c b/src/ld65/global.c new file mode 100644 index 000000000..ffad09552 --- /dev/null +++ b/src/ld65/global.c @@ -0,0 +1,59 @@ +/*****************************************************************************/ +/* */ +/* global.c */ +/* */ +/* Global variables for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "global.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +const char* ProgName = "ld65"; /* Program name */ + +const char* OutputName = "a.out"; /* Name of output file */ + +unsigned long StartAddr = 0x200; /* Start address */ + +unsigned char Verbose = 0; /* Verbose operation flag */ +unsigned char VerboseMap = 0; /* Verbose map file */ +const char* MapFileName = 0; /* Name of the map file */ +const char* LabelFileName = 0; /* Name of the label file */ +unsigned char WProtSegs = 0; /* Mark write protected segments */ + + + diff --git a/src/ld65/global.h b/src/ld65/global.h new file mode 100644 index 000000000..39056cfcd --- /dev/null +++ b/src/ld65/global.h @@ -0,0 +1,66 @@ +/*****************************************************************************/ +/* */ +/* global.h */ +/* */ +/* Global variables for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef GLOBAL_H +#define GLOBAL_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +extern const char* ProgName; /* Program name */ + +extern const char* OutputName; /* Name of output file */ + +extern unsigned long StartAddr; /* Start address */ + +extern unsigned char Verbose; /* Verbose operation flag */ +extern unsigned char VerboseMap; /* Verbose map file */ +extern const char* MapFileName; /* Name of the map file */ +extern const char* LabelFileName; /* Name of the label file */ +extern unsigned char WProtSegs; /* Mark write protected segments */ + + + +/* End of global.h */ + +#endif + + + diff --git a/src/ld65/library.c b/src/ld65/library.c new file mode 100644 index 000000000..d9649a2a1 --- /dev/null +++ b/src/ld65/library.c @@ -0,0 +1,285 @@ +/*****************************************************************************/ +/* */ +/* library.c */ +/* */ +/* Library data structures and helpers for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "../common/objdefs.h" +#include "../common/libdefs.h" +#include "../common/symdefs.h" +#include "../common/exprdefs.h" +#include "../common/filepos.h" + +#include "mem.h" +#include "error.h" +#include "fileio.h" +#include "objdata.h" +#include "objfile.h" +#include "exports.h" +#include "library.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Library data */ +static FILE* Lib = 0; +static char* LibName = 0; +static unsigned ModuleCount = 0; +static ObjData** Index = 0; + + + +/*****************************************************************************/ +/* Reading file data structures */ +/*****************************************************************************/ + + + +static void LibReadObjHeader (ObjData* O) +/* Read the header of the object file checking the signature */ +{ + O->Header.Magic = Read32 (Lib); + if (O->Header.Magic != OBJ_MAGIC) { + Error ("Object file `%s' in library `%s' is invalid", O->Name, LibName); + } + O->Header.Version = Read16 (Lib); + if (O->Header.Version != OBJ_VERSION) { + Error ("Object file `%s' in library `%s' has wrong version", + O->Name, LibName); + } + O->Header.Flags = Read16 (Lib); + O->Header.OptionOffs = Read32 (Lib); + O->Header.OptionSize = Read32 (Lib); + O->Header.FileOffs = Read32 (Lib); + O->Header.FileSize = Read32 (Lib); + O->Header.SegOffs = Read32 (Lib); + O->Header.SegSize = Read32 (Lib); + O->Header.ImportOffs = Read32 (Lib); + O->Header.ImportSize = Read32 (Lib); + O->Header.ExportOffs = Read32 (Lib); + O->Header.ExportSize = Read32 (Lib); + O->Header.DbgSymOffs = Read32 (Lib); + O->Header.DbgSymSize = Read32 (Lib); +} + + + +static ObjData* ReadIndexEntry (void) +/* Read one entry in the index */ +{ + unsigned I; + + /* Create a new entry and insert it into the list */ + ObjData* O = NewObjData (); + + /* Module name/flags/MTime/Start/Size */ + O->Name = ReadMallocedStr (Lib); + O->Flags = Read16 (Lib); + Read32 (Lib); /* Skip MTime */ + O->Start = Read32 (Lib); + Read32 (Lib); /* Skip Size */ + + /* Skip the export size, then read the exports */ + Read16 (Lib); + O->ExportCount = Read16 (Lib); + O->Exports = Xmalloc (O->ExportCount * sizeof (Export*)); + for (I = 0; I < O->ExportCount; ++I) { + O->Exports [I] = ReadExport (Lib, O); + } + + /* Skip the import size, then read the imports */ + Read16 (Lib); + O->ImportCount = Read16 (Lib); + O->Imports = Xmalloc (O->ImportCount * sizeof (Import*)); + for (I = 0; I < O->ImportCount; ++I) { + O->Imports [I] = ReadImport (Lib, O); + } + + /* Done */ + return O; +} + + + +static void ReadIndex (void) +/* Read the index of a library file */ +{ + unsigned I; + + /* Read the object file count and allocate memory */ + ModuleCount = Read16 (Lib); + Index = Xmalloc (ModuleCount * sizeof (ObjData*)); + + /* Read all entries in the index */ + for (I = 0; I < ModuleCount; ++I) { + Index [I] = ReadIndexEntry (); + } +} + + + +/*****************************************************************************/ +/* High level stuff */ +/*****************************************************************************/ + + + +static void LibCheckExports (ObjData* O) +/* Check if the exports from this file can satisfy any import requests. If so, + * insert the imports and exports from this file and mark the file as added. + */ +{ + unsigned I; + + /* Check all exports */ + for (I = 0; I < O->ExportCount; ++I) { + if (IsUnresolved (O->Exports [I]->Name)) { + /* We need this module */ + O->Flags |= OBJ_REF; + break; + } + } + + /* If we need this module, insert the imports and exports */ + if (O->Flags & OBJ_REF) { + /* Insert the exports */ + for (I = 0; I < O->ExportCount; ++I) { + InsertExport (O->Exports [I]); + } + /* Insert the imports */ + for (I = 0; I < O->ImportCount; ++I) { + InsertImport (O->Imports [I]); + } + } +} + + + +void LibAdd (FILE* F, const char* Name) +/* Add files from the library to the list if there are references that could + * be satisfied. + */ +{ + int Add; + unsigned I; + LibHeader Header; + + /* Store the parameters, so they're visible for other routines */ + Lib = F; + LibName = StrDup (Name); + + /* Read the remaining header fields (magic is already read) */ + Header.Magic = LIB_MAGIC; + Header.Version = Read16 (Lib); + if (Header.Version != LIB_VERSION) { + Error ("Wrong data version in `%s'", Name); + } + Header.Flags = Read16 (Lib); + Header.IndexOffs = Read32 (Lib); + + /* Seek to the index position and read the index */ + fseek (Lib, Header.IndexOffs, SEEK_SET); + ReadIndex (); + + /* Walk through all library modules and check for each module if there + * are unresolved externals in existing modules that may be resolved + * by adding the module. Repeat this step until no more object files + * were added. + */ + do { + Add = 0; + for (I = 0; I < ModuleCount; ++I) { + ObjData* O = Index [I]; + if ((O->Flags & OBJ_REF) == 0) { + LibCheckExports (O); + if (O->Flags & OBJ_REF) { + /* The routine added the file */ + Add = 1; + } + } + } + } while (Add); + + /* Add the files list and sections for all requested modules */ + for (I = 0; I < ModuleCount; ++I) { + ObjData* O = Index [I]; + if (O->Flags & OBJ_REF) { + + /* Seek to the start of the object file and read the header */ + fseek (Lib, O->Start, SEEK_SET); + LibReadObjHeader (O); + + /* Seek to the start of the files list and read the files list */ + fseek (Lib, O->Start + O->Header.FileOffs, SEEK_SET); + ObjReadFiles (Lib, O); + + /* Seek to the start of the segment list and read the segments */ + fseek (Lib, O->Start + O->Header.SegOffs, SEEK_SET); + ObjReadSections (Lib, O); + + /* Seek to the start of the debug info and read the debug info */ + fseek (Lib, O->Start + O->Header.DbgSymOffs, SEEK_SET); + ObjReadDbgSyms (Lib, O); + + /* We have the data now */ + O->Flags |= OBJ_HAVEDATA; + + } + + /* Add a pointer to the library name */ + O->LibName = LibName; + } + + /* Done. Close the file, release allocated memory */ + fclose (F); + Xfree (Index); + Lib = 0; + LibName = 0; + ModuleCount = 0; + Index = 0; +} + + + + + + + diff --git a/src/ld65/library.h b/src/ld65/library.h new file mode 100644 index 000000000..f4c528121 --- /dev/null +++ b/src/ld65/library.h @@ -0,0 +1,59 @@ +/*****************************************************************************/ +/* */ +/* library.h */ +/* */ +/* Library data structures and helpers for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef LIBRARY_H +#define LIBRARY_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void LibAdd (FILE* F, const char* Name); +/* Add files from the library to the list if there are references that could + * be satisfied. + */ + + + +/* End of library.h */ + +#endif + + + diff --git a/src/ld65/main.c b/src/ld65/main.c new file mode 100644 index 000000000..eb76f35a5 --- /dev/null +++ b/src/ld65/main.c @@ -0,0 +1,391 @@ +/*****************************************************************************/ +/* */ +/* main.c */ +/* */ +/* Main program for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include + +#include "../common/libdefs.h" +#include "../common/objdefs.h" +#include "../common/version.h" + +#include "global.h" +#include "error.h" +#include "mem.h" +#include "target.h" +#include "fileio.h" +#include "scanner.h" +#include "config.h" +#include "objfile.h" +#include "library.h" +#include "exports.h" +#include "segments.h" +#include "mapfile.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +static unsigned ObjFiles = 0; /* Count of object files linked */ +static unsigned LibFiles = 0; /* Count of library files linked */ +static const char* LibPath = 0; /* Search path for modules */ +static unsigned LibPathLen = 0; /* Length of LibPath */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void Usage (void) +/* Print usage information and exit */ +{ + fprintf (stderr, + "Usage: %s [options] module ...\n" + "Options are:\n" + "\t-m name\t\tCreate a map file\n" + "\t-o name\t\tName the default output file\n" + "\t-t type\t\tType of target system\n" + "\t-v\t\tVerbose mode\n" + "\t-vm\t\tVerbose map file\n" + "\t-C name\t\tUse linker config file\n" + "\t-Ln name\tCreate a VICE label file\n" + "\t-Lp\t\tMark write protected segments as such (VICE)\n" + "\t-S addr\t\tSet the default start address\n" + "\t-V\t\tPrint linker version\n", + ProgName); + exit (EXIT_FAILURE); +} + + + +static void UnknownOption (const char* Arg) +/* Print an error about an unknown option. Print usage information and exit */ +{ + fprintf (stderr, "Unknown option: %s\n", Arg); + Usage (); +} + + + +static void InvNumber (const char* Arg) +/* Print an error about an unknown option. Print usage information and exit */ +{ + fprintf (stderr, "Invalid number given in argument: %s\n", Arg); + Usage (); +} + + + +static unsigned long CvtNumber (const char* Arg, const char* Number) +/* Convert a number from a string. Allow '$' and '0x' prefixes for hex + * numbers. + */ +{ + unsigned long Val; + + /* Convert */ + if (*Number == '$') { + ++Number; + if (sscanf (Number, "%lx", &Val) != 1) { + InvNumber (Arg); + } + } else { + if (sscanf (Number, "%li", (long*)&Val) != 1) { + InvNumber (Arg); + } + } + + /* Return the result */ + return Val; +} + + + +static const char* GetArg (int* ArgNum, char* argv [], unsigned Len) +/* Get an option argument */ +{ + const char* Arg = argv [*ArgNum]; + if (Arg [Len] != '\0') { + /* Argument appended */ + return Arg + Len; + } else { + /* Separate argument */ + Arg = argv [*ArgNum + 1]; + if (Arg == 0) { + /* End of arguments */ + fprintf (stderr, "Option requires an argument: %s\n", argv [*ArgNum]); + exit (EXIT_FAILURE); + } + ++(*ArgNum); + return Arg; + } +} + + + +static void LongOption (int* Arg, char* argv []) +/* Handle a long command line option */ +{ + /* For now ... */ + UnknownOption (argv [*Arg]); +} + + + +static int HasPath (const char* Name) +/* Check if the given Name has a path component */ +{ + return strchr (Name, '/') != 0 || strchr (Name, '\\') != 0; +} + + + +static void LinkFile (const char* Name) +/* Handle one file */ +{ + unsigned long Magic; + unsigned Len; + char* NewName = 0; + + /* Try to open the file */ + FILE* F = fopen (Name, "rb"); + if (F == 0) { + /* We couldn't open the file. If the name doesn't have a path, and we + * have a search path given, try the name with the search path + * prepended. + */ + if (LibPathLen > 0 && !HasPath (Name)) { + /* Allocate memory. Account for the trailing zero, and for a + * path separator character eventually needed. + */ + Len = LibPathLen; + NewName = Xmalloc (strlen (Name) + Len + 2); + /* Build the new name */ + memcpy (NewName, LibPath, Len); + if (NewName [Len-1] != '/' && NewName [Len-1] != '\\') { + /* We need an additional path separator */ + NewName [Len++] = '/'; + } + strcpy (NewName + Len, Name); + + /* Now try to open the new file */ + F = fopen (NewName, "rb"); + } + + if (F == 0) { + Error ("Cannot open `%s': %s", Name, strerror (errno)); + } + } + + /* Read the magic word */ + Magic = Read32 (F); + + /* Do we know this type of file? */ + switch (Magic) { + + case OBJ_MAGIC: + ObjAdd (F, Name); + ++ObjFiles; + break; + + case LIB_MAGIC: + LibAdd (F, Name); + ++LibFiles; + break; + + default: + fclose (F); + Error ("File `%s' has unknown type", Name); + + } + + /* If we have allocated memory, free it here. Note: Memory will not always + * be freed if we run into an error, but that's no problem. Adding more + * code to work around it will use more memory than the chunk that's lost. + */ + Xfree (NewName); +} + + + +int main (int argc, char* argv []) +/* Assembler main program */ +{ + int I; + + /* Evaluate the CC65_LIB environment variable */ + LibPath = getenv ("CC65_LIB"); + if (LibPath == 0) { + /* Use some default path */ +#ifdef CC65_LIB + LibPath = CC65_LIB; +#else + LibPath = "/usr/lib/cc65/lib/"; +#endif + } + LibPathLen = strlen (LibPath); + + /* Check the parameters */ + I = 1; + while (I < argc) { + + /* Get the argument */ + const char* Arg = argv [I]; + + /* Check for an option */ + if (Arg [0] == '-') { + + /* An option */ + switch (Arg [1]) { + + case '-': + LongOption (&I, argv); + break; + + case 'm': + MapFileName = GetArg (&I, argv, 2); + break; + + case 'o': + OutputName = GetArg (&I, argv, 2); + break; + + case 't': + if (CfgAvail ()) { + Error ("Cannot use -C/-t twice"); + } + TgtSet (GetArg (&I, argv, 2)); + break; + + case 'v': + switch (Arg [2]) { + case 'm': VerboseMap = 1; break; + case '\0': ++Verbose; break; + default: UnknownOption (Arg); + } + break; + + case 'C': + if (CfgAvail ()) { + Error ("Cannot use -C/-t twice"); + } + CfgSetName (GetArg (&I, argv, 2)); + break; + + case 'L': + switch (Arg [2]) { + case 'n': LabelFileName = GetArg (&I, argv, 3); break; + case 'p': WProtSegs = 1; break; + default: UnknownOption (Arg); + } + break; + + case 'S': + StartAddr = CvtNumber (Arg, GetArg (&I, argv, 2)); + break; + + case 'V': + fprintf (stderr, + "ld65 V%u.%u.%u - (C) Copyright 1998-2000 Ullrich von Bassewitz\n", + VER_MAJOR, VER_MINOR, VER_PATCH); + break; + + default: + UnknownOption (Arg); + break; + } + + } else { + + /* A filename */ + LinkFile (Arg); + + } + + /* Next argument */ + ++I; + } + + /* Check if we had any object files */ + if (ObjFiles == 0) { + fprintf (stderr, "No object files to link\n"); + Usage (); + } + + /* Check if we have a valid configuration */ + if (!CfgAvail ()) { + fprintf (stderr, "Memory configuration missing\n"); + Usage (); + } + + /* Read the config file */ + CfgRead (); + + /* Assign start addresses for the segments, define linker symbols */ + CfgAssignSegments (); + + /* Create the output file */ + CfgWriteTarget (); + + /* Check for segments not written to the output file */ + CheckSegments (); + + /* If requested, create a map file and a label file for VICE */ + if (MapFileName) { + CreateMapFile (); + } + if (LabelFileName) { + CreateLabelFile (); + } + + /* Dump the data for debugging */ + if (Verbose > 1) { + SegDump (); + } + + /* Return an apropriate exit code */ + return EXIT_SUCCESS; +} + + + diff --git a/src/ld65/make/gcc.mak b/src/ld65/make/gcc.mak new file mode 100644 index 000000000..ab0ec916e --- /dev/null +++ b/src/ld65/make/gcc.mak @@ -0,0 +1,66 @@ +# +# gcc Makefile for ld65 +# + +# Default for the compiler lib search path as compiler define +CDEFS=-DCC65_LIB=\"/usr/lib/cc65/lib/\" +CFLAGS = -g -O2 -Wall $(CDEFS) +CC=gcc +LDFLAGS= + +OBJS = bin.o \ + binfmt.o \ + config.o \ + dbgsyms.o \ + error.o \ + exports.o \ + expr.o \ + extsyms.o \ + fileio.o \ + global.o \ + library.o \ + main.o \ + mapfile.o \ + mem.o \ + o65.o \ + objdata.o \ + objfile.o \ + scanner.o \ + segments.o \ + target.o + +LIBS = ../common/common.a + + +EXECS = ld65 + +.PHONY: all +ifeq (.depend,$(wildcard .depend)) +all : $(EXECS) +include .depend +else +all: depend + @$(MAKE) -f make/gcc.mak all +endif + + + +ld65: $(OBJS) $(LIBS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) + +clean: + rm -f *~ core *.map + +zap: clean + rm -f *.o $(EXECS) .depend + + +# ------------------------------------------------------------------------------ +# Make the dependencies + +.PHONY: depend dep +depend dep: $(OBJS:.o=.c) + @echo "Creating dependency information" + $(CC) -MM $^ > .depend + + diff --git a/src/ld65/make/watcom.mak b/src/ld65/make/watcom.mak new file mode 100644 index 000000000..06b3d41cc --- /dev/null +++ b/src/ld65/make/watcom.mak @@ -0,0 +1,136 @@ +# +# ld65 Makefile for the Watcom compiler +# + +# ------------------------------------------------------------------------------ +# Generic stuff + +.AUTODEPEND +.SUFFIXES .ASM .C .CC .CPP +.SWAP + +AR = WLIB +LD = WLINK + +!if !$d(TARGET) +!if $d(__OS2__) +TARGET = OS2 +!else +TARGET = NT +!endif +!endif + +# target specific macros. +!if $(TARGET)==OS2 + +# --------------------- OS2 --------------------- +SYSTEM = os2v2 +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS32 + +# -------------------- DOS4G -------------------- +SYSTEM = dos4g +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!elif $(TARGET)==DOS + +# --------------------- DOS --------------------- +SYSTEM = dos +CC = WCC +CCCFG = -bt=$(TARGET) -d1 -onatx -zp2 -2 -ml -zq -w2 + +!elif $(TARGET)==NT + +# --------------------- NT ---------------------- +SYSTEM = nt +CC = WCC386 +CCCFG = -bt=$(TARGET) -d1 -onatx -zp4 -5 -zq -w2 + +!else +!error +!endif + +# ------------------------------------------------------------------------------ +# Implicit rules + +.c.obj: + $(CC) $(CCCFG) $< + + +# ------------------------------------------------------------------------------ +# All OBJ files + +OBJS = bin.obj \ + binfmt.obj \ + config.obj \ + dbgsyms.obj \ + error.obj \ + exports.obj \ + expr.obj \ + extsyms.obj \ + fileio.obj \ + global.obj \ + library.obj \ + main.obj \ + mapfile.obj \ + mem.obj \ + o65.obj \ + objdata.obj \ + objfile.obj \ + scanner.obj \ + segments.obj \ + target.obj + +LIBS = ..\common\common.lib + + +# ------------------------------------------------------------------------------ +# Main targets + +all: ld65 + +ld65: ld65.exe + + +# ------------------------------------------------------------------------------ +# Other targets + + +ld65.exe: $(OBJS) $(LIBS) + $(LD) system $(SYSTEM) @&&| +DEBUG ALL +OPTION QUIET +NAME $< +FILE bin.obj +FILE binfmt.obj +FILE config.obj +FILE dbgsyms.obj +FILE error.obj +FILE exports.obj +FILE expr.obj +FILE extsyms.obj +FILE fileio.obj +FILE global.obj +FILE library.obj +FILE main.obj +FILE mapfile.obj +FILE mem.obj +FILE o65.obj +FILE objdata.obj +FILE objfile.obj +FILE scanner.obj +FILE segments.obj +FILE target.obj +LIBRARY ..\common\common.lib +| + +clean: + @if exist *.obj del *.obj + @if exist *.obj del ld65.exe + +strip: + @-wstrip ld65.exe + diff --git a/src/ld65/mapfile.c b/src/ld65/mapfile.c new file mode 100644 index 000000000..8e3582dd1 --- /dev/null +++ b/src/ld65/mapfile.c @@ -0,0 +1,170 @@ +/*****************************************************************************/ +/* */ +/* mapfile.c */ +/* */ +/* Map file creation for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include + +#include "global.h" +#include "error.h" +#include "objdata.h" +#include "segments.h" +#include "dbgsyms.h" +#include "exports.h" +#include "config.h" +#include "mapfile.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void CreateMapFile (void) +/* Create a map file */ +{ + ObjData* O; + unsigned I; + + /* Open the map file */ + FILE* F = fopen (MapFileName, "w"); + if (F == 0) { + Error ("Cannot create map file `%s': %s", MapFileName, strerror (errno)); + } + + /* Write a modules list */ + fprintf (F, "Modules list:\n" + "-------------\n"); + O = ObjRoot; + while (O) { + if (O->Flags & OBJ_HAVEDATA) { + /* We've linked this module */ + if (O->LibName) { + /* The file is from a library */ + fprintf (F, "%s(%s):\n", O->LibName, O->Name); + } else { + fprintf (F, "%s:\n", O->Name); + } + for (I = 0; I < O->SectionCount; ++I) { + const Section* S = O->Sections [I]; + /* Don't include zero sized sections if not explicitly + * requested + */ + if (VerboseMap || S->Size > 0) { + fprintf (F, " %-15s Offs = %06lX Size = %06lX\n", + S->Seg->Name, S->Offs, S->Size); + } + } + } + O = O->Next; + } + + /* Write the segment list */ + fprintf (F, "\n\n" + "Segment list:\n" + "-------------\n"); + PrintSegmentMap (F); + + /* Write the exports list */ + fprintf (F, "\n\n" + "Exports list:\n" + "-------------\n"); + PrintExportMap (F); + + /* Write the imports list */ + fprintf (F, "\n\n" + "Imports list:\n" + "-------------\n"); + PrintImportMap (F); + + /* Close the file */ + if (fclose (F) != 0) { + Error ("Error closing map file `%s': %s", MapFileName, strerror (errno)); + } +} + + + +void CreateLabelFile (void) +/* Create a label file */ +{ + ObjData* O; + + /* Open the map file */ + FILE* F = fopen (LabelFileName, "w"); + if (F == 0) { + Error ("Cannot create label file `%s': %s", LabelFileName, strerror (errno)); + } + + /* Print the labels for the export symbols */ + PrintExportLabels (F); + + /* Print debug symbols from all modules we have linked into the output file */ + O = ObjRoot; + while (O) { + if (O->Flags & OBJ_HAVEDATA) { + /* We've linked this module */ + PrintDbgSymLabels (O, F); + + } + O = O->Next; + } + + /* If we should mark write protected areas as such, do it */ + if (WProtSegs) { + SegDesc* S = SegDescList; + while (S) { + /* Is this segment write protected and contains data? */ + if (S->Flags & SF_WPROT && S->Seg->Size > 0) { + /* Write protect the memory area in VICE */ + fprintf (F, "wp %04lX %04lX\n", + S->Seg->PC, + S->Seg->PC + S->Seg->Size - 1); + } + /* Next segment */ + S = S->Next; + } + } + + /* Close the file */ + if (fclose (F) != 0) { + Error ("Error closing map file `%s': %s", LabelFileName, strerror (errno)); + } +} + + + diff --git a/src/ld65/mapfile.h b/src/ld65/mapfile.h new file mode 100644 index 000000000..5e63d418e --- /dev/null +++ b/src/ld65/mapfile.h @@ -0,0 +1,64 @@ +/*****************************************************************************/ +/* */ +/* mapfile.h */ +/* */ +/* Map file creation for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef MAPFILE_H +#define MAPFILE_H + + + +#include + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void CreateMapFile (void); +/* Create a map file */ + +void CreateLabelFile (void); +/* Create a label file */ + + + +/* End of mapfile.h */ + +#endif + + + diff --git a/src/ld65/mem.c b/src/ld65/mem.c new file mode 100644 index 000000000..58a885101 --- /dev/null +++ b/src/ld65/mem.c @@ -0,0 +1,84 @@ +/*****************************************************************************/ +/* */ +/* mem.c */ +/* */ +/* Memory allocation for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "error.h" +#include "mem.h" + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void* Xmalloc (size_t size) +/* Allocate memory, check for out of memory condition. Do some debugging */ +{ + void* p; + + p = malloc (size); + if (p == 0 && size != 0) { + Error ("Out of memory"); + } + + /* Return a pointer to the block */ + return p; +} + + + +void Xfree (const void* block) +/* Free the block, do some debugging */ +{ + free ((void*) block); +} + + + +char* StrDup (const char* s) +/* Duplicate a string on the heap. The function checks for out of memory */ +{ + unsigned len; + + len = strlen (s) + 1; + return memcpy (Xmalloc (len), s, len); +} + + + diff --git a/src/ld65/mem.h b/src/ld65/mem.h new file mode 100644 index 000000000..b4ecf57d0 --- /dev/null +++ b/src/ld65/mem.h @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* */ +/* mem.h */ +/* */ +/* Memory allocation for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef MEM_H +#define MEM_H + + + +#include + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void* Xmalloc (size_t size); +/* Allocate memory, check for out of memory condition. Do some debugging */ + +void Xfree (const void* block); +/* Free the block, do some debugging */ + +char* StrDup (const char* s); +/* Duplicate a string on the heap. The function checks for out of memory */ + + + +/* End of mem.h */ + +#endif + + + diff --git a/src/ld65/o65.c b/src/ld65/o65.c new file mode 100644 index 000000000..ae67ca0af --- /dev/null +++ b/src/ld65/o65.c @@ -0,0 +1,1067 @@ +/*****************************************************************************/ +/* */ +/* o65.c */ +/* */ +/* Module to handle the o65 binary format */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include + +#include "../common/version.h" + +#include "global.h" +#include "error.h" +#include "mem.h" +#include "fileio.h" +#include "exports.h" +#include "expr.h" +#include "o65.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Header mode bits */ +#define MF_SIZE_32BIT 0x2000 /* All size words are 32bit */ +#define MF_CPU_816 0x8000 /* Executable is for 65816 */ + +/* The four o65 segment types. Note: These values are identical to the values + * needed for the segmentID in the o65 spec. + */ +#define O65SEG_UNDEF 0x00 +#define O65SEG_ABS 0x01 +#define O65SEG_TEXT 0x02 +#define O65SEG_DATA 0x03 +#define O65SEG_BSS 0x04 +#define O65SEG_ZP 0x05 + +/* Relocation type codes for the o65 format */ +#define O65RELOC_WORD 0x80 +#define O65RELOC_HIGH 0x40 +#define O65RELOC_LOW 0x20 +#define O65RELOC_SEGADR 0xc0 +#define O65RELOC_SEG 0xa0 + +/* O65 executable file header */ +typedef struct O65Header_ O65Header; +struct O65Header_ { + unsigned Version; /* Version number for o65 format */ + unsigned Mode; /* Mode word */ + unsigned long TextBase; /* Base address of text segment */ + unsigned long TextSize; /* Size of text segment */ + unsigned long DataBase; /* Base of data segment */ + unsigned long DataSize; /* Size of data segment */ + unsigned long BssBase; /* Base of bss segment */ + unsigned long BssSize; /* Size of bss segment */ + unsigned long ZPBase; /* Base of zeropage segment */ + unsigned long ZPSize; /* Size of zeropage segment */ + unsigned long StackSize; /* Requested stack size */ +}; + +/* An o65 option */ +typedef struct O65Option_ O65Option; +struct O65Option_ { + O65Option* Next; /* Next in option list */ + unsigned char Type; /* Type of option */ + unsigned char Len; /* Data length */ + unsigned char Data [1]; /* Data, dynamically allocated */ +}; + +/* A o65 relocation table */ +#define RELOC_BLOCKSIZE 4096 +typedef struct O65RelocTab_ O65RelocTab; +struct O65RelocTab_ { + unsigned Size; /* Size of the table */ + unsigned Fill; /* Amount used */ + unsigned char* Buf; /* Buffer, dynamically allocated */ +}; + +/* Structure describing the format */ +struct O65Desc_ { + O65Header Header; /* File header */ + O65Option* Options; /* List of file options */ + ExtSymTab* Exports; /* Table with exported symbols */ + ExtSymTab* Imports; /* Table with imported symbols */ + unsigned Undef; /* Count of undefined symbols */ + FILE* F; /* The file we're writing to */ + char* Filename; /* Name of the output file */ + O65RelocTab* TextReloc; /* Relocation table for text segment */ + O65RelocTab* DataReloc; /* Relocation table for data segment */ + + unsigned TextCount; /* Number of segments assigned to .text */ + SegDesc** TextSeg; /* Array of text segments */ + unsigned DataCount; /* Number of segments assigned to .data */ + SegDesc** DataSeg; /* Array of data segments */ + unsigned BssCount; /* Number of segments assigned to .bss */ + SegDesc** BssSeg; /* Array of bss segments */ + unsigned ZPCount; /* Number of segments assigned to .zp */ + SegDesc** ZPSeg; /* Array of zp segments */ + + /* Temporary data for writing segments */ + unsigned long SegSize; + O65RelocTab* CurReloc; + long LastOffs; +}; + +/* Structure for parsing expression trees */ +typedef struct ExprDesc_ ExprDesc; +struct ExprDesc_ { + O65Desc* D; /* File format descriptor */ + long Val; /* The offset value */ + int TooComplex; /* Expression too complex */ + Section* SegRef; /* Section referenced if any */ + ExtSym* ExtRef; /* External reference if any */ +}; + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static void WriteSize (const O65Desc* D, unsigned long Val) +/* Write a "size" word to the file */ +{ + if (D->Header.Mode & MF_SIZE_32BIT) { + Write32 (D->F, Val); + } else { + Write16 (D->F, (unsigned) Val); + } +} + + + +static unsigned O65SegType (const SegDesc* S) +/* Map our own segment types into something o65 compatible */ +{ + /* Check the segment type. Readonly segments are assign to the o65 + * text segment, writeable segments that contain data are assigned + * to data, bss and zp segments are handled respectively. + * Beware: Zeropage segments have the SF_BSS flag set, so be sure + * to check SF_ZP first. + */ + if (S->Flags & SF_RO) { + return O65SEG_TEXT; + } else if (S->Flags & SF_ZP) { + return O65SEG_ZP; + } else if (S->Flags & SF_BSS) { + return O65SEG_BSS; + } else { + return O65SEG_DATA; + } +} + + + +/*****************************************************************************/ +/* Expression handling */ +/*****************************************************************************/ + + + +static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign) +/* Extract and evaluate all constant factors in an subtree that has only + * additions and subtractions. If anything other than additions and + * subtractions are found, D->TooComplex is set to true. + */ +{ + Export* E; + + switch (Expr->Op) { + + case EXPR_LITERAL: + if (Sign < 0) { + D->Val -= Expr->V.Val; + } else { + D->Val += Expr->V.Val; + } + break; + + case EXPR_SYMBOL: + /* Get the referenced Export */ + E = GetExprExport (Expr); + /* If this export has a mark set, we've already encountered it. + * This means that the export is used to define it's own value, + * which in turn means, that we have a circular reference. + */ + if (ExportHasMark (E)) { + CircularRefError (E); + } else if (E->Expr == 0) { + /* Dummy export, must be an o65 imported symbol */ + ExtSym* S = O65GetImport (D->D, E->Name); + CHECK (S != 0); + if (D->ExtRef) { + /* We cannot have more than one external reference in o65 */ + D->TooComplex = 1; + } else { + /* Remember the external reference */ + D->ExtRef = S; + } + } else { + MarkExport (E); + O65ParseExpr (E->Expr, D, Sign); + UnmarkExport (E); + } + break; + + case EXPR_SEGMENT: + if (D->SegRef) { + /* We cannot handle more than one segment reference in o65 */ + D->TooComplex = 1; + } else { + /* Remember the segment reference */ + D->SegRef = GetExprSection (Expr); + } + break; + + case EXPR_PLUS: + O65ParseExpr (Expr->Left, D, Sign); + O65ParseExpr (Expr->Right, D, Sign); + break; + + case EXPR_MINUS: + O65ParseExpr (Expr->Left, D, Sign); + O65ParseExpr (Expr->Right, D, -Sign); + break; + + default: + /* Expression contains illegal operators */ + D->TooComplex = 1; + break; + + } +} + + + +/*****************************************************************************/ +/* Relocation tables */ +/*****************************************************************************/ + + + +static O65RelocTab* NewO65RelocTab (void) +/* Create a new relocation table */ +{ + /* Allocate a new structure */ + O65RelocTab* R = Xmalloc (sizeof (O65RelocTab)); + + /* Initialize the data */ + R->Size = RELOC_BLOCKSIZE; + R->Fill = 0; + R->Buf = Xmalloc (RELOC_BLOCKSIZE); + + /* Return the created struct */ + return R; +} + + + +static void FreeO65RelocTab (O65RelocTab* R) +/* Free a relocation table */ +{ + Xfree (R->Buf); + Xfree (R); +} + + + +static void O65RelocPutByte (O65RelocTab* R, unsigned char B) +/* Put the byte into the relocation table */ +{ + /* Do we have enough space in the buffer? */ + if (R->Fill == R->Size) { + /* We need to grow the buffer */ + unsigned char* NewBuf = Xmalloc (R->Size + RELOC_BLOCKSIZE); + memcpy (NewBuf, R->Buf, R->Size); + Xfree (R->Buf); + R->Buf = NewBuf; + } + + /* Put the byte into the buffer */ + R->Buf [R->Fill++] = B; +} + + + +static void O65RelocPutWord (O65RelocTab* R, unsigned W) +/* Put a word into the relocation table */ +{ + O65RelocPutByte (R, W); + O65RelocPutByte (R, W >> 8); +} + + + +static void O65WriteReloc (O65RelocTab* R, FILE* F) +/* Write the relocation table to the given file */ +{ + WriteData (F, R->Buf, R->Fill); +} + + + +/*****************************************************************************/ +/* Option handling */ +/*****************************************************************************/ + + + +static O65Option* NewO65Option (unsigned Type, const void* Data, unsigned DataLen) +/* Allocate and initialize a new option struct */ +{ + O65Option* O; + + /* Check the length */ + CHECK (DataLen <= 253); + + /* Allocate memory */ + O = Xmalloc (sizeof (O65Option) - 1 + DataLen); + + /* Initialize the structure */ + O->Next = 0; + O->Type = Type; + O->Len = DataLen; + memcpy (O->Data, Data, DataLen); + + /* Return the created struct */ + return O; +} + + + +static void FreeO65Option (O65Option* O) +/* Free an O65Option struct */ +{ + Xfree (O); +} + + + +/*****************************************************************************/ +/* Subroutines to write o65 sections */ +/*****************************************************************************/ + + + +static void O65WriteHeader (O65Desc* D) +/* Write the header of the executable to the given file */ +{ + static unsigned char Trailer [5] = { + 0x01, 0x00, 0x6F, 0x36, 0x35 + }; + + O65Option* O; + + + /* Write the fixed header */ + WriteData (D->F, Trailer, sizeof (Trailer)); + Write8 (D->F, D->Header.Version); + Write16 (D->F, D->Header.Mode); + WriteSize (D, D->Header.TextBase); + WriteSize (D, D->Header.TextSize); + WriteSize (D, D->Header.DataBase); + WriteSize (D, D->Header.DataSize); + WriteSize (D, D->Header.BssBase); + WriteSize (D, D->Header.BssSize); + WriteSize (D, D->Header.ZPBase); + WriteSize (D, D->Header.ZPSize); + WriteSize (D, D->Header.StackSize); + + /* Write the options */ + O = D->Options; + while (O) { + Write8 (D->F, O->Len + 2); /* Account for len and type bytes */ + Write8 (D->F, O->Type); + if (O->Len) { + WriteData (D->F, O->Data, O->Len); + } + O = O->Next; + } + + /* Write the end-of-options byte */ + Write8 (D->F, 0); +} + + + +static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size, + unsigned long Offs, void* Data) +/* Called from SegWrite for an expression. Evaluate the expression, check the + * range and write the expression value to the file, update the relocation + * table. + */ +{ + long Diff; + long BinVal; + ExprNode* Expr; + ExprDesc ED; + unsigned char RelocType; + + /* Cast the Data pointer to its real type, an O65Desc */ + O65Desc* D = (O65Desc*) Data; + + /* Check for a constant expression */ + if (IsConstExpr (E)) { + /* Write out the constant expression */ + return SegWriteConstExpr (((O65Desc*)Data)->F, E, Signed, Size); + } + + /* We have a relocatable expression that needs a relocation table entry. + * Calculate the number of bytes between this entry and the last one, and + * setup all necessary intermediate bytes in the relocation table. + */ + Offs += D->SegSize; /* Calulate full offset */ + Diff = ((long) Offs) - D->LastOffs; + while (Diff > 0xFE) { + O65RelocPutByte (D->CurReloc, 0xFF); + Diff -= 0xFE; + } + O65RelocPutByte (D->CurReloc, Diff); + + /* Remember this offset for the next time */ + D->LastOffs = Offs; + + /* Determine the expression to relocate */ + Expr = E; + if (E->Op == EXPR_LOBYTE || E->Op == EXPR_HIBYTE) { + /* Use the real expression */ + Expr = E->Left; + } + + /* Initialize the descriptor for expression parsing */ + ED.D = D; + ED.Val = 0; + ED.TooComplex = 0; + ED.SegRef = 0; + ED.ExtRef = 0; + + /* Recursively collect information about this expression */ + O65ParseExpr (Expr, &ED, 1); + + /* We cannot handle both, an imported symbol and a segment ref */ + if (ED.SegRef != 0 && ED.ExtRef != 0) { + ED.TooComplex = 1; + } + + /* Bail out if we cannot handle the expression */ + if (ED.TooComplex) { + return SEG_EXPR_TOO_COMPLEX; + } + + /* Safety: Check that we are really referencing a symbol or a segment */ + CHECK (ED.SegRef != 0 || ED.ExtRef != 0); + + /* Write out the offset that goes into the segment. */ + BinVal = ED.Val; + if (E->Op == EXPR_LOBYTE) { + BinVal &= 0x00FF; + } else if (E->Op == EXPR_HIBYTE) { + BinVal = (BinVal >> 8) & 0x00FF; + } + WriteVal (D->F, BinVal, Size); + + /* Determine the actual type of relocation entry needed from the + * information gathered about the expression. + */ + if (E->Op == EXPR_LOBYTE) { + RelocType = O65RELOC_LOW; + } else if (E->Op == EXPR_HIBYTE) { + RelocType = O65RELOC_HIGH; + } else { + switch (Size) { + + case 1: + RelocType = O65RELOC_LOW; + break; + + case 2: + RelocType = O65RELOC_WORD; + break; + + case 3: + RelocType = O65RELOC_SEGADR; + break; + + case 4: + /* 4 byte expression not supported by o65 */ + return SEG_EXPR_TOO_COMPLEX; + + default: + Internal ("O65WriteExpr: Invalid expression size: %u", Size); + RelocType = 0; /* Avoid gcc warnings */ + } + } + + /* Determine which segment we're referencing */ + if (ED.ExtRef) { + /* Imported symbol */ + RelocType |= O65SEG_UNDEF; + O65RelocPutByte (D->CurReloc, RelocType); + /* Put the number of the imported symbol into the table */ + O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef)); + } else { + /* Segment reference */ + + + + } + + /* Success */ + return SEG_EXPR_OK; +} + + + +static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite) +/* Write one segment to the o65 output file */ +{ + SegDesc* S; + unsigned I; + + /* Initialize variables */ + D->SegSize = 0; + D->LastOffs = -1; + + /* Write out all segments */ + for (I = 0; I < Count; ++I) { + + /* Get the segment from the list node */ + S = Seg [I]; + + /* Keep the user happy */ + if (Verbose) { + printf (" Writing `%s'\n", S->Name); + } + + /* Write this segment */ + if (DoWrite) { + SegWrite (D->F, S->Seg, O65WriteExpr, D); + } + + /* Mark the segment as dumped */ + S->Seg->Dumped = 1; + + /* Calculate the total size */ + D->SegSize += S->Seg->Size; + } + + /* Terminate the relocation table for the this segment */ + if (D->CurReloc) { + O65RelocPutByte (D->CurReloc, 0); + } + + /* Check the size of the segment for overflow */ + if ((D->Header.Mode & MF_SIZE_32BIT) == 0 && D->SegSize > 0xFFFF) { + Error ("Segment overflow in file `%s'", D->Filename); + } + +} + + + +static void O65WriteTextSeg (O65Desc* D, Memory* M) +/* Write the code segment to the o65 output file */ +{ + /* Initialize variables */ + D->CurReloc = D->TextReloc; + + /* Dump all text segments */ + O65WriteSeg (D, D->TextSeg, D->TextCount, 1); + + /* Set the size of the segment */ + D->Header.TextSize = D->SegSize; +} + + + +static void O65WriteDataSeg (O65Desc* D, Memory* M) +/* Write the data segment to the o65 output file */ +{ + /* Initialize variables */ + D->CurReloc = D->DataReloc; + + /* Dump all data segments */ + O65WriteSeg (D, D->DataSeg, D->DataCount, 1); + + /* Set the size of the segment */ + D->Header.DataSize = D->SegSize; +} + + + +static void O65WriteBssSeg (O65Desc* D, Memory* M) +/* "Write" the bss segments to the o65 output file. This will only update + * the relevant header fields. + */ +{ + /* Initialize variables */ + D->CurReloc = 0; + + /* Dump all data segments */ + O65WriteSeg (D, D->BssSeg, D->BssCount, 0); + + /* Set the size of the segment */ + D->Header.BssSize = D->SegSize; +} + + + +static void O65WriteZPSeg (O65Desc* D, Memory* M) +/* "Write" the zeropage segments to the o65 output file. This will only update + * the relevant header fields. + */ +{ + /* Initialize variables */ + D->CurReloc = 0; + + /* Dump all data segments */ + O65WriteSeg (D, D->ZPSeg, D->ZPCount, 0); + + /* Set the size of the segment */ + D->Header.ZPSize = D->SegSize; +} + + + +static void O65WriteImports (O65Desc* D) +/* Write the list of imported symbols to the O65 file */ +{ + const ExtSym* E; + + /* Write the number of external symbols */ + WriteSize (D, ExtSymCount (D->Imports)); + + /* Write out the symbol names, zero terminated */ + E = ExtSymList (D->Imports); + while (E) { + /* Get the name */ + const char* Name = ExtSymName (E); + /* And write it to the output file */ + WriteData (D->F, Name, strlen (Name) + 1); + /* Next symbol */ + E = ExtSymNext (E); + } +} + + + +static void O65WriteTextReloc (O65Desc* D) +/* Write the relocation for the text segment to the output file */ +{ + O65WriteReloc (D->TextReloc, D->F); +} + + + +static void O65WriteDataReloc (O65Desc* D) +/* Write the relocation for the data segment to the output file */ +{ + O65WriteReloc (D->DataReloc, D->F); +} + + + +static void O65WriteExports (O65Desc* D) +/* Write the list of exports */ +{ + /* For now... */ + WriteSize (D, 0); +} + + + +/*****************************************************************************/ +/* Public code */ +/*****************************************************************************/ + + + +O65Desc* NewO65Desc (void) +/* Create, initialize and return a new O65 descriptor struct */ +{ + /* Allocate a new structure */ + O65Desc* D = Xmalloc (sizeof (O65Desc)); + + /* Initialize the header */ + D->Header.Version = 0; + D->Header.Mode = 0; + D->Header.TextBase = 0; + D->Header.TextSize = 0; + D->Header.DataBase = 0; + D->Header.DataSize = 0; + D->Header.BssBase = 0; + D->Header.BssSize = 0; + D->Header.ZPBase = 0; + D->Header.ZPSize = 0; + D->Header.StackSize = 0; /* Let OS choose a good value */ + + /* Initialize other data */ + D->Options = 0; + D->Exports = NewExtSymTab (); + D->Imports = NewExtSymTab (); + D->Undef = 0; + D->F = 0; + D->Filename = 0; + D->TextReloc = NewO65RelocTab (); + D->DataReloc = NewO65RelocTab (); + D->TextCount = 0; + D->TextSeg = 0; + D->DataCount = 0; + D->DataSeg = 0; + D->BssCount = 0; + D->BssSeg = 0; + D->ZPCount = 0; + D->ZPSeg = 0; + + /* Return the created struct */ + return D; +} + + + +void FreeO65Desc (O65Desc* D) +/* Delete the descriptor struct with cleanup */ +{ + /* Free the segment arrays */ + Xfree (D->ZPSeg); + Xfree (D->BssSeg); + Xfree (D->DataSeg); + Xfree (D->TextSeg); + + /* Free the relocation tables */ + FreeO65RelocTab (D->DataReloc); + FreeO65RelocTab (D->TextReloc); + + /* Free the option list */ + while (D->Options) { + O65Option* O = D->Options; + D->Options = D->Options->Next; + FreeO65Option (O); + } + + /* Free the external symbol tables */ + FreeExtSymTab (D->Exports); + FreeExtSymTab (D->Imports); + + /* Free the struct itself */ + Xfree (D); +} + + + +void O65Set816 (O65Desc* D) +/* Enable 816 mode */ +{ + D->Header.Mode |= MF_CPU_816; +} + + + +void O65SetLargeModel (O65Desc* D) +/* Enable a large memory model executable */ +{ + D->Header.Mode |= MF_SIZE_32BIT; +} + + + +void O65SetAlignment (O65Desc* D, unsigned Align) +/* Set the executable alignment */ +{ + /* Remove all alignment bits from the mode word */ + D->Header.Mode &= ~0x0003; + + /* Set the alignment bits */ + switch (Align) { + case 1: break; + case 2: D->Header.Mode |= 0x01; break; + case 4: D->Header.Mode |= 0x02; break; + case 256: D->Header.Mode |= 0x03; break; + default: Error ("Invalid alignment for O65 format: %u", Align); + } +} + + + +void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen) +/* Set an o65 header option */ +{ + /* Create a new option structure */ + O65Option* O = NewO65Option (Type, Data, DataLen); + + /* Insert it into the linked list */ + O->Next = D->Options; + D->Options = O; +} + + + +void O65SetOS (O65Desc* D, unsigned OS) +/* Set an option describing the target operating system */ +{ + static const unsigned char OSA65 [2] = { O65OS_OSA65, 0 }; + static const unsigned char Lunix [2] = { O65OS_LUNIX, 0 }; + + /* Write the correct option */ + switch (OS) { + + case O65OS_OSA65: + O65SetOption (D, O65OPT_OS, OSA65, sizeof (OSA65)); + break; + + case O65OS_LUNIX: + O65SetOption (D, O65OPT_OS, Lunix, sizeof (Lunix)); + break; + + default: + Internal ("Trying to set invalid O65 operating system: %u", OS); + + } +} + + + +ExtSym* O65GetImport (O65Desc* D, const char* Ident) +/* Return the imported symbol or NULL if not found */ +{ + /* Retrieve the symbol from the table */ + return GetExtSym (D->Imports, Ident); +} + + + +void O65SetImport (O65Desc* D, const char* Ident) +/* Set an imported identifier */ +{ + /* Insert the entry into the table */ + NewExtSym (D->Imports, Ident); +} + + + +ExtSym* O65GetExport (O65Desc* D, const char* Ident) +/* Return the exported symbol or NULL if not found */ +{ + /* Retrieve the symbol from the table */ + return GetExtSym (D->Exports, Ident); +} + + + +void O65SetExport (O65Desc* D, const char* Ident) +/* Set an exported identifier */ +{ + /* Insert the entry into the table */ + NewExtSym (D->Exports, Ident); +} + + + +static void O65SetupSegments (O65Desc* D, Memory* M) +/* Setup segment assignments */ +{ + MemListNode* N; + SegDesc* S; + unsigned TextIdx, DataIdx, BssIdx, ZPIdx; + + /* Initialize the counters */ + D->TextCount = 0; + D->DataCount = 0; + D->BssCount = 0; + D->ZPCount = 0; + + /* Walk through the memory list and count the segment types */ + N = M->SegList; + while (N) { + + /* Get the segment from the list node */ + S = N->Seg; + + /* Check the segment type. */ + switch (O65SegType (S)) { + case O65SEG_TEXT: D->TextCount++; break; + case O65SEG_DATA: D->DataCount++; break; + case O65SEG_BSS: D->BssCount++; break; + case O65SEG_ZP: D->ZPCount++; break; + default: Internal ("Invalid return from O65SegType"); + } + + /* Next segment node */ + N = N->Next; + } + + /* Allocate memory according to the numbers */ + D->TextSeg = Xmalloc (D->TextCount * sizeof (SegDesc*)); + D->DataSeg = Xmalloc (D->DataCount * sizeof (SegDesc*)); + D->BssSeg = Xmalloc (D->BssCount * sizeof (SegDesc*)); + D->ZPSeg = Xmalloc (D->ZPCount * sizeof (SegDesc*)); + + /* Walk again through the list and setup the segment arrays */ + TextIdx = DataIdx = BssIdx = ZPIdx = 0; + N = M->SegList; + while (N) { + + /* Get the segment from the list node */ + S = N->Seg; + + /* Check the segment type. */ + switch (O65SegType (S)) { + case O65SEG_TEXT: D->TextSeg [TextIdx++] = S; break; + case O65SEG_DATA: D->DataSeg [DataIdx++] = S; break; + case O65SEG_BSS: D->BssSeg [BssIdx++] = S; break; + case O65SEG_ZP: D->ZPSeg [ZPIdx++] = S; break; + default: Internal ("Invalid return from O65SegType"); + } + + /* Next segment node */ + N = N->Next; + } +} + + + +static int O65Unresolved (const char* Name, void* D) +/* Called if an unresolved symbol is encountered */ +{ + /* Check if the symbol is an imported o65 symbol */ + if (O65GetImport (D, Name) != 0) { + /* This is an external symbol, relax... */ + return 1; + } else { + /* This is actually an unresolved external. Bump the counter */ + ((O65Desc*) D)->Undef++; + return 0; + } +} + + + +void O65WriteTarget (O65Desc* D, File* F) +/* Write an o65 output file */ +{ + Memory* M; + char OptBuf [256]; /* Buffer for option strings */ + time_t T; + + /* Place the filename in the control structure */ + D->Filename = F->Name; + + /* The o65 format uses only one memory area per file. Check that. */ + M = F->MemList; + if (M->Next != 0) { + Warning ("Cannot handle more than one memory area for o65 format"); + } + + /* Check for unresolved symbols. The function O65Unresolved is called + * if we get an unresolved symbol. + */ + D->Undef = 0; /* Reset the counter */ + CheckExports (O65Unresolved, D); + if (D->Undef > 0) { + /* We had unresolved symbols, cannot create output file */ + Error ("%u unresolved external(s) found - cannot create output file", D->Undef); + } + + /* Setup the segment arrays */ + O65SetupSegments (D, M); + + /* Open the file */ + D->F = fopen (F->Name, "wb"); + if (D->F == 0) { + Error ("Cannot open `%s': %s", F->Name, strerror (errno)); + } + + /* Keep the user happy */ + if (Verbose) { + printf ("Opened `%s'...\n", F->Name); + } + + /* Define some more options: A timestamp and the linker version */ + T = time (0); + strcpy (OptBuf, ctime (&T)); + O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, strlen (OptBuf) + 1); + sprintf (OptBuf, "ld65 V%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH); + O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1); + + /* Write the header */ + O65WriteHeader (D); + + /* Write the text segment */ + O65WriteTextSeg (D, M); + + /* Write the data segment */ + O65WriteDataSeg (D, M); + + /* "Write" the bss segments */ + O65WriteBssSeg (D, M); + + /* "Write" the zeropage segments */ + O65WriteZPSeg (D, M); + + /* Write the undefined references list */ + O65WriteImports (D); + + /* Write the text segment relocation table */ + O65WriteTextReloc (D); + + /* Write the data segment relocation table */ + O65WriteDataReloc (D); + + /* Write the list of exports */ + O65WriteExports (D); + + /* Seek back to the start and write the updated header */ + fseek (D->F, 0, SEEK_SET); + O65WriteHeader (D); + + /* Close the file */ + if (fclose (D->F) != 0) { + Error ("Cannot write to `%s': %s", F->Name, strerror (errno)); + } + + /* Reset the file and filename */ + D->F = 0; + D->Filename = 0; +} + + + + diff --git a/src/ld65/o65.h b/src/ld65/o65.h new file mode 100644 index 000000000..d1e6609dd --- /dev/null +++ b/src/ld65/o65.h @@ -0,0 +1,119 @@ +/*****************************************************************************/ +/* */ +/* o65.h */ +/* */ +/* Module to handle the o65 binary format */ +/* */ +/* */ +/* */ +/* (C) 1999 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef O65_H +#define O65_H + + + +#include + +#include "extsyms.h" +#include "config.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Structure describing the format */ +typedef struct O65Desc_ O65Desc; + +/* Option tags */ +#define O65OPT_FILENAME 0 +#define O65OPT_OS 1 +#define O65OPT_ASM 2 +#define O65OPT_AUTHOR 3 +#define O65OPT_TIMESTAMP 4 + +/* Operating system codes for O65OPT_OS */ +#define O65OS_OSA65 1 +#define O65OS_LUNIX 2 + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +O65Desc* NewO65Desc (void); +/* Create, initialize and return a new O65 descriptor struct */ + +void FreeO65Desc (O65Desc* D); +/* Delete the descriptor struct with cleanup */ + +void O65Set816 (O65Desc* D); +/* Enable 816 mode */ + +void O65SetLargeModel (O65Desc* D); +/* Enable a large memory model executable */ + +void O65SetAlignment (O65Desc* D, unsigned Align); +/* Set the executable alignment */ + +void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen); +/* Set an o65 header option */ + +void O65SetOS (O65Desc* D, unsigned OS); +/* Set an option describing the target operating system */ + +ExtSym* O65GetImport (O65Desc* D, const char* Ident); +/* Return the imported symbol or NULL if not found */ + +void O65SetImport (O65Desc* D, const char* Ident); +/* Set an imported identifier */ + +ExtSym* O65GetExport (O65Desc* D, const char* Ident); +/* Return the exported symbol or NULL if not found */ + +void O65SetExport (O65Desc* D, const char* Ident); +/* Set an exported identifier */ + +void O65WriteTarget (O65Desc* D, File* F); +/* Write an o65 output file */ + + + +/* End of o65.h */ + +#endif + + + diff --git a/src/ld65/objdata.c b/src/ld65/objdata.c new file mode 100644 index 000000000..04028cd8c --- /dev/null +++ b/src/ld65/objdata.c @@ -0,0 +1,112 @@ +/*****************************************************************************/ +/* */ +/* objdata.c */ +/* */ +/* Handling object file data for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +#include "mem.h" +#include "error.h" +#include "objdata.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Object data list management */ +unsigned ObjCount = 0; /* Count of object files in the list */ +ObjData* ObjRoot = 0; /* List of object files */ +ObjData* ObjLast = 0; /* Last entry in list */ +ObjData** ObjPool = 0; /* Object files as array */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +ObjData* NewObjData (void) +/* Allocate a new structure on the heap, insert it into the list, return it */ +{ + /* Allocate memory */ + ObjData* O = Xmalloc (sizeof (ObjData)); + + /* Initialize the data */ + O->Next = 0; + O->Name = 0; + O->LibName = 0; + O->Flags = 0; + O->Start = 0; + O->ExportCount = 0; + O->Exports = 0; + O->ImportCount = 0; + O->Imports = 0; + O->DbgSymCount = 0; + O->DbgSyms = 0; + + /* Link it into the list */ + if (ObjLast) { + ObjLast->Next = O; + ObjLast = O; + } else { + /* First entry */ + ObjRoot = ObjLast = O; + } + + /* One object file more now */ + ++ObjCount; + + /* Return the new entry */ + return O; +} + + + +void FreeObjData (ObjData* O) +/* Free a complete struct */ +{ + Xfree (O->Name); + Xfree (O->Imports); + Xfree (O->Exports); + Xfree (O->DbgSyms); + Xfree (O); +} + + + diff --git a/src/ld65/objdata.h b/src/ld65/objdata.h new file mode 100644 index 000000000..91d994a1d --- /dev/null +++ b/src/ld65/objdata.h @@ -0,0 +1,106 @@ +/*****************************************************************************/ +/* */ +/* objdata.h */ +/* */ +/* Handling object file data for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OBJDATA_H +#define OBJDATA_H + + + +#include "../common/objdefs.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Values for the Flags field */ +#define OBJ_REF 0x0001 /* We have a reference to this file */ +#define OBJ_HAVEDATA 0x0002 /* We have this object file already */ +#define OBJ_MARKED 0x0004 /* Generic marker bit */ + + +/* Internal structure holding object file data */ +typedef struct ObjData_ ObjData; +struct ObjData_ { + ObjData* Next; /* Linked list of all objects */ + char* Name; /* Module name */ + char* LibName; /* Name of library */ + ObjHeader Header; /* Header of file */ + unsigned long Start; /* Start offset of data in library */ + unsigned Flags; + unsigned FileCount; /* Input file count */ + char** Files; /* List of input files */ + unsigned SectionCount; /* Count of sections in this object */ + struct Section_** Sections; /* List of all sections */ + unsigned ExportCount; /* Count of exports */ + struct Export_** Exports; /* List of all exports */ + unsigned ImportCount; /* Count of imports */ + struct Import_** Imports; /* List of all imports */ + unsigned DbgSymCount; /* Count of debug symbols */ + struct DbgSym_** DbgSyms; /* List of debug symbols */ +}; + + + +/* Object data list management */ +extern unsigned ObjCount; /* Count of files in the list */ +extern ObjData* ObjRoot; /* List of object files */ +extern ObjData* ObjLast; /* Last entry in list */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +ObjData* NewObjData (void); +/* Allocate a new structure on the heap, insert it into the list, return it */ + +void FreeObjData (ObjData* O); +/* Free a complete struct */ + + + +/* End of objdata.h */ + +#endif + + + diff --git a/src/ld65/objfile.c b/src/ld65/objfile.c new file mode 100644 index 000000000..92c83084f --- /dev/null +++ b/src/ld65/objfile.c @@ -0,0 +1,225 @@ +/*****************************************************************************/ +/* */ +/* objfile.c */ +/* */ +/* Object file handling for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include + +#include "error.h" +#include "mem.h" +#include "objdata.h" +#include "fileio.h" +#include "segments.h" +#include "exports.h" +#include "dbgsyms.h" +#include "objfile.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static const char* GetModule (const char* Name) +/* Get a module name from the file name */ +{ + /* Make a module name from the file name */ + const char* Module = Name + strlen (Name); + while (Module > Name) { + --Module; + if (*Module == '/' || *Module == '\\') { + ++Module; + break; + } + } + if (*Module == 0) { + Error ("Cannot make module name from `%s'", Name); + } + return Module; +} + + + +static void ObjReadHeader (FILE* Obj, ObjHeader* H, const char* Name) +/* Read the header of the object file checking the signature */ +{ + H->Version = Read16 (Obj); + if (H->Version != OBJ_VERSION) { + Error ("Object file `%s' has wrong version", Name); + } + H->Flags = Read16 (Obj); + H->OptionOffs = Read32 (Obj); + H->OptionSize = Read32 (Obj); + H->FileOffs = Read32 (Obj); + H->FileSize = Read32 (Obj); + H->SegOffs = Read32 (Obj); + H->SegSize = Read32 (Obj); + H->ImportOffs = Read32 (Obj); + H->ImportSize = Read32 (Obj); + H->ExportOffs = Read32 (Obj); + H->ExportSize = Read32 (Obj); + H->DbgSymOffs = Read32 (Obj); + H->DbgSymSize = Read32 (Obj); +} + + + +void ObjReadFiles (FILE* F, ObjData* O) +/* Read the files list from a file at the current position */ +{ + unsigned I; + + O->FileCount = Read8 (F); + O->Files = Xmalloc (O->FileCount * sizeof (char*)); + for (I = 0; I < O->FileCount; ++I) { + /* Skip MTime and size */ + Read32 (F); + Read32 (F); + /* Read the filename */ + O->Files [I] = ReadMallocedStr (F); + } +} + + + +void ObjReadImports (FILE* F, ObjData* O) +/* Read the imports from a file at the current position */ +{ + unsigned I; + + O->ImportCount = Read16 (F); + O->Imports = Xmalloc (O->ImportCount * sizeof (Import*)); + for (I = 0; I < O->ImportCount; ++I) { + O->Imports [I] = ReadImport (F, O); + InsertImport (O->Imports [I]); + } +} + + + +void ObjReadExports (FILE* F, ObjData* O) +/* Read the exports from a file at the current position */ +{ + unsigned I; + + O->ExportCount = Read16 (F); + O->Exports = Xmalloc (O->ExportCount * sizeof (Export*)); + for (I = 0; I < O->ExportCount; ++I) { + O->Exports [I] = ReadExport (F, O); + InsertExport (O->Exports [I]); + } +} + + + +void ObjReadDbgSyms (FILE* F, ObjData* O) +/* Read the debug symbols from a file at the current position */ +{ + unsigned I; + + O->DbgSymCount = Read16 (F); + O->DbgSyms = Xmalloc (O->DbgSymCount * sizeof (DbgSym*)); + for (I = 0; I < O->DbgSymCount; ++I) { + O->DbgSyms [I] = ReadDbgSym (F, O); + } +} + + + +void ObjReadSections (FILE* F, ObjData* O) +/* Read the section data from a file at the current position */ +{ + unsigned I; + + O->SectionCount = Read8 (F); + O->Sections = Xmalloc (O->SectionCount * sizeof (Section*)); + for (I = 0; I < O->SectionCount; ++I) { + O->Sections [I] = ReadSection (F, O); + } +} + + + +void ObjAdd (FILE* Obj, const char* Name) +/* Add an object file to the module list */ +{ + /* Create a new structure for the object file data */ + ObjData* O = NewObjData (); + + /* The magic was already read and checked, so set it in the header */ + O->Header.Magic = OBJ_MAGIC; + + /* Read and check the header */ + ObjReadHeader (Obj, &O->Header, Name); + + /* Initialize the object module data structure */ + O->Name = StrDup (GetModule (Name)); + O->Flags = OBJ_HAVEDATA; + + /* Read the files list from the object file */ + fseek (Obj, O->Header.FileOffs, SEEK_SET); + ObjReadFiles (Obj, O); + + /* Read the imports list from the object file */ + fseek (Obj, O->Header.ImportOffs, SEEK_SET); + ObjReadImports (Obj, O); + + /* Read the object file exports and insert them into the exports list */ + fseek (Obj, O->Header.ExportOffs, SEEK_SET); + ObjReadExports (Obj, O); + + /* Read the object debug symbols from the object file */ + fseek (Obj, O->Header.DbgSymOffs, SEEK_SET); + ObjReadDbgSyms (Obj, O); + + /* Read the segment list from the object file. This must be last, since + * the expressions stored in the code may reference segments or imported + * symbols. + */ + fseek (Obj, O->Header.SegOffs, SEEK_SET); + ObjReadSections (Obj, O); + + /* Mark this object file as needed */ + O->Flags |= OBJ_REF | OBJ_HAVEDATA; + + /* Done, close the file (we read it only, so no error check) */ + fclose (Obj); +} + + + diff --git a/src/ld65/objfile.h b/src/ld65/objfile.h new file mode 100644 index 000000000..f2046bbd3 --- /dev/null +++ b/src/ld65/objfile.h @@ -0,0 +1,80 @@ +/*****************************************************************************/ +/* */ +/* objfile.h */ +/* */ +/* Object file handling for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OBJFILE_H +#define OBJFILE_H + + + +#include + +#include "../common/objdefs.h" + +#include "objdata.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ObjReadFiles (FILE* F, ObjData* O); +/* Read the files list from a file at the current position */ + +void ObjReadImports (FILE* F, ObjData* O); +/* Read the imports from a file at the current position */ + +void ObjReadExports (FILE* F, ObjData* O); +/* Read the exports from a file at the current position */ + +void ObjReadDbgSyms (FILE* F, ObjData* O); +/* Read the debug symbols from a file at the current position */ + +void ObjReadSections (FILE* F, ObjData* O); +/* Read the section data from a file at the current position */ + +void ObjAdd (FILE* F, const char* Name); +/* Add an object file to the module list */ + + + +/* End of objfile.h */ + +#endif + + + diff --git a/src/ld65/scanner.c b/src/ld65/scanner.c new file mode 100644 index 000000000..69db9f23f --- /dev/null +++ b/src/ld65/scanner.c @@ -0,0 +1,535 @@ +/*****************************************************************************/ +/* */ +/* scanner.c */ +/* */ +/* Configuration file scanner for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include +#include + +#include "global.h" +#include "error.h" +#include "scanner.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Current token and attributes */ +unsigned CfgTok; +char CfgSVal [CFG_MAX_IDENT_LEN+1]; +unsigned long CfgIVal; + +/* Error location */ +unsigned CfgErrorLine; +unsigned CfgErrorCol; + +/* Input sources for the configuration */ +static const char* CfgName = 0; +static const char* CfgBuf = 0; + +/* Other input stuff */ +static int C = ' '; +static unsigned InputLine = 1; +static unsigned InputCol = 0; +static FILE* InputFile = 0; + + + +/*****************************************************************************/ +/* Error handling */ +/*****************************************************************************/ + + + +void CfgWarning (const char* Format, ...) +/* Print a warning message adding file name and line number of the config file */ +{ + char Buf [512]; + va_list ap; + va_start (ap, Format); +#ifdef __WATCOMC__ + _vbprintf (Buf, sizeof (Buf), Format, ap); +#else + vsnprintf (Buf, sizeof (Buf), Format, ap); +#endif + Warning ("%s(%u): %s", CfgName, CfgErrorLine, Buf); +} + + + +void CfgError (const char* Format, ...) +/* Print an error message adding file name and line number of the config file */ +{ + char Buf [512]; + va_list ap; + va_start (ap, Format); +#ifdef __WATCOMC__ + _vbprintf (Buf, sizeof (Buf), Format, ap); +#else + vsnprintf (Buf, sizeof (Buf), Format, ap); +#endif + Error ("%s(%u): %s", CfgName, CfgErrorLine, Buf); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void NextChar (void) +/* Read the next character from the input file */ +{ + if (CfgBuf) { + /* Read from buffer */ + C = (unsigned char)(*CfgBuf); + if (C == 0) { + C = EOF; + } else { + ++CfgBuf; + } + } else { + /* Read from the file */ + C = getc (InputFile); + } + + /* Count columns */ + if (C != EOF) { + ++InputCol; + } + + /* Count lines */ + if (C == '\n') { + ++InputLine; + InputCol = 0; + } +} + + + +static unsigned DigitVal (int C) +/* Return the value for a numeric digit */ +{ + if (isdigit (C)) { + return C - '0'; + } else { + return toupper (C) - 'A' + 10; + } +} + + + +void CfgNextTok (void) +/* Read the next token from the input stream */ +{ + unsigned I; + + +Again: + /* Skip whitespace */ + while (isspace (C)) { + NextChar (); + } + + /* Remember the current position */ + CfgErrorLine = InputLine; + CfgErrorCol = InputCol; + + /* Identifier? */ + if (C == '_' || isalpha (C)) { + + /* Read the identifier */ + I = 0; + while (C == '_' || isalnum (C)) { + if (I < CFG_MAX_IDENT_LEN) { + CfgSVal [I++] = C; + } + NextChar (); + } + CfgSVal [I] = '\0'; + CfgTok = CFGTOK_IDENT; + return; + } + + /* Hex number? */ + if (C == '$') { + NextChar (); + if (!isxdigit (C)) { + Error ("%s(%u): Hex digit expected", CfgName, InputLine); + } + CfgIVal = 0; + while (isxdigit (C)) { + CfgIVal = CfgIVal * 16 + DigitVal (C); + NextChar (); + } + CfgTok = CFGTOK_INTCON; + return; + } + + /* Decimal number? */ + if (isdigit (C)) { + CfgIVal = 0; + while (isdigit (C)) { + CfgIVal = CfgIVal * 10 + DigitVal (C); + NextChar (); + } + CfgTok = CFGTOK_INTCON; + return; + } + + /* Other characters */ + switch (C) { + + case '{': + NextChar (); + CfgTok = CFGTOK_LCURLY; + break; + + case '}': + NextChar (); + CfgTok = CFGTOK_RCURLY; + break; + + case ';': + NextChar (); + CfgTok = CFGTOK_SEMI; + break; + + case '.': + NextChar (); + CfgTok = CFGTOK_DOT; + break; + + case ',': + NextChar (); + CfgTok = CFGTOK_COMMA; + break; + + case '=': + NextChar (); + CfgTok = CFGTOK_EQ; + break; + + case ':': + NextChar (); + CfgTok = CFGTOK_COLON; + break; + + case '\"': + NextChar (); + I = 0; + while (C != '\"') { + if (C == EOF || C == '\n') { + Error ("%s(%u): Unterminated string", CfgName, InputLine); + } + if (I < CFG_MAX_IDENT_LEN) { + CfgSVal [I++] = C; + } + NextChar (); + } + NextChar (); + CfgSVal [I] = '\0'; + CfgTok = CFGTOK_STRCON; + break; + + case '#': + /* Comment */ + while (C != '\n' && C != EOF) { + NextChar (); + } + if (C != EOF) { + goto Again; + } + CfgTok = CFGTOK_EOF; + break; + + case '%': + NextChar (); + switch (C) { + + case 'O': + NextChar (); + if (OutputName) { + strncpy (CfgSVal, OutputName, CFG_MAX_IDENT_LEN); + CfgSVal [CFG_MAX_IDENT_LEN] = '\0'; + } else { + CfgSVal [0] = '\0'; + } + CfgTok = CFGTOK_STRCON; + break; + + case 'S': + NextChar (); + CfgIVal = StartAddr; + CfgTok = CFGTOK_INTCON; + break; + + default: + CfgError ("Invalid format specification"); + } + break; + + case EOF: + CfgTok = CFGTOK_EOF; + break; + + default: + Error ("%s(%u): Invalid character `%c'", CfgName, InputLine, C); + + } +} + + + +void CfgConsume (unsigned T, const char* Msg) +/* Skip a token, print an error message if not found */ +{ + if (CfgTok != T) { + CfgError (Msg); + } + CfgNextTok (); +} + + + +void CfgConsumeSemi (void) +/* Consume a semicolon */ +{ + CfgConsume (CFGTOK_SEMI, "`;' expected"); +} + + + +void CfgConsumeColon (void) +/* Consume a colon */ +{ + CfgConsume (CFGTOK_COLON, "`:' expected"); +} + + + +void CfgOptionalComma (void) +/* Consume a comma if there is one */ +{ + if (CfgTok == CFGTOK_COMMA) { + CfgNextTok (); + } +} + + + +void CfgOptionalAssign (void) +/* Consume an equal sign if there is one */ +{ + if (CfgTok == CFGTOK_EQ) { + CfgNextTok (); + } +} + + + +void CfgAssureInt (void) +/* Make sure the next token is an integer */ +{ + if (CfgTok != CFGTOK_INTCON) { + CfgError ("Integer constant expected"); + } +} + + + +void CfgAssureStr (void) +/* Make sure the next token is a string constant */ +{ + if (CfgTok != CFGTOK_STRCON) { + CfgError ("String constant expected"); + } +} + + + +void CfgAssureIdent (void) +/* Make sure the next token is an identifier */ +{ + if (CfgTok != CFGTOK_IDENT) { + CfgError ("Identifier expected"); + } +} + + + +void CfgRangeCheck (unsigned long Lo, unsigned long Hi) +/* Check the range of CfgIVal */ +{ + if (CfgIVal < Lo || CfgIVal > Hi) { + CfgError ("Range error"); + } +} + + + +void CfgSpecialToken (const IdentTok* Table, unsigned Size, const char* Name) +/* Map an identifier to one of the special tokens in the table */ +{ + unsigned I; + + /* We need an identifier */ + if (CfgTok == CFGTOK_IDENT) { + + /* Make it upper case */ + I = 0; + while (CfgSVal [I]) { + CfgSVal [I] = toupper (CfgSVal [I]); + ++I; + } + + /* Linear search */ + for (I = 0; I < Size; ++I) { + if (strcmp (CfgSVal, Table [I].Ident) == 0) { + CfgTok = Table [I].Tok; + return; + } + } + + } + + /* Not found or no identifier */ + Error ("%s(%u): %s expected", CfgName, InputLine, Name); +} + + + +void CfgBoolToken (void) +/* Map an identifier or integer to a boolean token */ +{ + static const IdentTok Booleans [] = { + { "YES", CFGTOK_TRUE }, + { "NO", CFGTOK_FALSE }, + { "TRUE", CFGTOK_TRUE }, + { "FALSE", CFGTOK_FALSE }, + }; + + /* If we have an identifier, map it to a boolean token */ + if (CfgTok == CFGTOK_IDENT) { + CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean"); + } else { + /* We expected an integer here */ + if (CfgTok != CFGTOK_INTCON) { + CfgError ("Boolean value expected"); + } + CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE; + } +} + + + +void CfgSetName (const char* Name) +/* Set a name for a config file */ +{ + CfgName = Name; +} + + + +const char* CfgGetName (void) +/* Get the name of the config file */ +{ + return CfgName? CfgName : ""; +} + + + +void CfgSetBuf (const char* Buf) +/* Set a memory buffer for the config */ +{ + CfgBuf = Buf; +} + + + +int CfgAvail (void) +/* Return true if we have a configuration available */ +{ + return CfgName != 0 || CfgBuf != 0; +} + + + +void CfgOpenInput (void) +/* Open the input file if we have one */ +{ + /* If we have a config name given, open the file, otherwise we will read + * from a buffer. + */ + if (!CfgBuf) { + + /* Open the file */ + InputFile = fopen (CfgName, "r"); + if (InputFile == 0) { + Error ("Cannot open `%s': %s", CfgName, strerror (errno)); + } + + } + + /* Initialize variables */ + C = ' '; + InputLine = 1; + InputCol = 0; + + /* Start the ball rolling ... */ + CfgNextTok (); +} + + + +void CfgCloseInput (void) +/* Close the input file if we have one */ +{ + /* Close the input file if we had one */ + if (InputFile) { + (void) fclose (InputFile); + InputFile = 0; + } +} + + + diff --git a/src/ld65/scanner.h b/src/ld65/scanner.h new file mode 100644 index 000000000..2c0a5ac39 --- /dev/null +++ b/src/ld65/scanner.h @@ -0,0 +1,198 @@ +/*****************************************************************************/ +/* */ +/* scanner.h */ +/* */ +/* Configuration file scanner for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SCANNER_H +#define SCANNER_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Config file tokens */ +#define CFGTOK_NONE 0 +#define CFGTOK_INTCON 1 +#define CFGTOK_STRCON 2 +#define CFGTOK_IDENT 3 +#define CFGTOK_LCURLY 4 +#define CFGTOK_RCURLY 5 +#define CFGTOK_SEMI 6 +#define CFGTOK_COMMA 7 +#define CFGTOK_EQ 8 +#define CFGTOK_COLON 9 +#define CFGTOK_DOT 10 +#define CFGTOK_EOF 11 + +/* Special identifiers */ +#define CFGTOK_MEMORY 20 +#define CFGTOK_FILES 21 +#define CFGTOK_SEGMENTS 22 +#define CFGTOK_FORMATS 23 + +#define CFGTOK_START 30 +#define CFGTOK_SIZE 31 +#define CFGTOK_TYPE 32 +#define CFGTOK_FILE 33 +#define CFGTOK_DEFINE 34 +#define CFGTOK_FILL 35 +#define CFGTOK_FILLVAL 36 +#define CFGTOK_EXPORT 37 +#define CFGTOK_IMPORT 38 +#define CFGTOK_OS 39 +#define CFGTOK_FORMAT 40 + +#define CFGTOK_LOAD 50 +#define CFGTOK_RUN 51 +#define CFGTOK_ALIGN 52 +#define CFGTOK_OFFSET 53 + +#define CFGTOK_RO 60 +#define CFGTOK_RW 61 +#define CFGTOK_BSS 62 +#define CFGTOK_ZP 63 +#define CFGTOK_WPROT 64 + +#define CFGTOK_O65 70 +#define CFGTOK_BIN 71 + +#define CFGTOK_SMALL 80 +#define CFGTOK_LARGE 81 + +#define CFGTOK_TRUE 90 +#define CFGTOK_FALSE 91 + +#define CFGTOK_LUNIX 100 +#define CFGTOK_OSA65 101 + + + +/* Mapping table entry, special identifier --> token */ +typedef struct IdentTok_ IdentTok; +struct IdentTok_ { + const char* Ident; /* Identifier */ + unsigned Tok; /* Token for identifier */ +}; +#define ENTRY_COUNT(s) (sizeof (s) / sizeof (s [0])) + + + +/* Current token and attributes */ +#define CFG_MAX_IDENT_LEN 255 +extern unsigned CfgTok; +extern char CfgSVal [CFG_MAX_IDENT_LEN+1]; +extern unsigned long CfgIVal; + +/* Error location */ +extern unsigned CfgErrorLine; +extern unsigned CfgErrorCol; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void CfgWarning (const char* Format, ...); +/* Print a warning message adding file name and line number of the config file */ + +void CfgError (const char* Format, ...); +/* Print an error message adding file name and line number of the config file */ + +void CfgNextTok (void); +/* Read the next token from the input stream */ + +void CfgConsume (unsigned T, const char* Msg); +/* Skip a token, print an error message if not found */ + +void CfgConsumeSemi (void); +/* Consume a semicolon */ + +void CfgConsumeColon (void); +/* Consume a colon */ + +void CfgOptionalComma (void); +/* Consume a comma if there is one */ + +void CfgOptionalAssign (void); +/* Consume an equal sign if there is one */ + +void CfgAssureInt (void); +/* Make sure the next token is an integer */ + +void CfgAssureStr (void); +/* Make sure the next token is a string constant */ + +void CfgAssureIdent (void); +/* Make sure the next token is an identifier */ + +void CfgRangeCheck (unsigned long Lo, unsigned long Hi); +/* Check the range of CfgIVal */ + +void CfgSpecialToken (const IdentTok* Table, unsigned Size, const char* Name); +/* Map an identifier to one of the special tokens in the table */ + +void CfgBoolToken (void); +/* Map an identifier or integer to a boolean token */ + +void CfgSetName (const char* Name); +/* Set a name for a config file */ + +const char* CfgGetName (void); +/* Get the name of the config file */ + +void CfgSetBuf (const char* Buf); +/* Set a memory buffer for the config */ + +int CfgAvail (void); +/* Return true if we have a configuration available */ + +void CfgOpenInput (void); +/* Open the input file if we have one */ + +void CfgCloseInput (void); +/* Close the input file if we have one */ + + + +/* End of scanner.h */ +#endif + + + diff --git a/src/ld65/segments.c b/src/ld65/segments.c new file mode 100644 index 000000000..94a3149e0 --- /dev/null +++ b/src/ld65/segments.c @@ -0,0 +1,666 @@ +/*****************************************************************************/ +/* */ +/* segments.c */ +/* */ +/* Segment handling for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include + +#include "../common/exprdefs.h" +#include "../common/symdefs.h" +#include "../common/segdefs.h" +#include "../common/hashstr.h" + +#include "mem.h" +#include "global.h" +#include "error.h" +#include "fileio.h" +#include "expr.h" +#include "segments.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Fragment structure */ +typedef struct Fragment_ Fragment; +struct Fragment_ { + Fragment* Next; /* Next fragment in list */ + ObjData* Obj; /* Source of fragment */ + unsigned long Size; /* Size of data/expression */ + ExprNode* Expr; /* Expression if FRAG_EXPR */ + FilePos Pos; /* File position in source */ + unsigned char Type; /* Type of fragment */ + unsigned char LitBuf [1]; /* Dynamically alloc'ed literal buffer */ +}; + + + +/* Hash table */ +#define HASHTAB_SIZE 253 +static Segment* HashTab [HASHTAB_SIZE]; + +static unsigned SegCount = 0; /* Segment count */ +static Segment* SegRoot = 0; /* List of all segments */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static Fragment* NewFragment (unsigned char Type, unsigned long Size, Section* S) +/* Create a new fragment and insert it into the segment S */ +{ + /* Allocate memory */ + Fragment* F = Xmalloc (sizeof (Fragment) - 1 + Size); /* Portable? */ + + /* Initialize the data */ + F->Next = 0; + F->Obj = 0; + F->Size = Size; + F->Expr = 0; + F->Type = Type; + + /* Insert the code fragment into the segment */ + if (S->FragRoot == 0) { + /* First fragment */ + S->FragRoot = F; + } else { + S->FragLast->Next = F; + } + S->FragLast = F; + S->Size += Size; + + /* Return the new fragment */ + return F; +} + + + +static Segment* NewSegment (const char* Name, unsigned char Type) +/* Create a new segment and initialize it */ +{ + /* Get the length of the symbol name */ + unsigned Len = strlen (Name); + + /* Allocate memory */ + Segment* S = Xmalloc (sizeof (Segment) + Len); + + /* Initialize the fields */ + S->Next = 0; + S->SecRoot = 0; + S->SecLast = 0; + S->PC = 0; + S->Size = 0; + S->AlignObj = 0; + S->Align = 0; + S->FillVal = 0; + S->Type = Type; + S->Dumped = 0; + memcpy (S->Name, Name, Len); + S->Name [Len] = '\0'; + + /* Insert the segment into the segment list */ + S->List = SegRoot; + SegRoot = S; + ++SegCount; + + /* Return the new entry */ + return S; +} + + + +static Section* NewSection (Segment* Seg, unsigned char Align, unsigned char Type) +/* Create a new section for the given segment */ +{ + unsigned long V; + + + /* Allocate memory */ + Section* S = Xmalloc (sizeof (Segment)); + + /* Initialize the data */ + S->Next = 0; + S->Seg = Seg; + S->FragRoot = 0; + S->FragLast = 0; + S->Size = 0; + S->Align = Align; + S->Type = Type; + + /* Calculate the alignment bytes needed for the section */ + V = (0x01UL << S->Align) - 1; + S->Fill = ((Seg->Size + V) & ~V) - Seg->Size; + + /* Adjust the segment size and set the section offset */ + Seg->Size += S->Fill; + S->Offs = Seg->Size; /* Current size is offset */ + + /* Insert the section into the segment */ + if (Seg->SecRoot == 0) { + /* First section in this segment */ + Seg->SecRoot = S; + } else { + Seg->SecLast->Next = S; + } + Seg->SecLast = S; + + /* Return the struct */ + return S; +} + + + +static Segment* SegFindInternal (const char* Name, unsigned HashVal) +/* Try to find the segment with the given name, return a pointer to the + * segment structure, or 0 if not found. + */ +{ + Segment* S = HashTab [HashVal]; + while (S) { + if (strcmp (Name, S->Name) == 0) { + /* Found */ + break; + } + S = S->Next; + } + /* Not found */ + return S; +} + + + +Section* ReadSection (FILE* F, ObjData* O) +/* Read a section from a file */ +{ + unsigned HashVal; + char Name [256]; + unsigned long Size; + unsigned char Align; + unsigned char Type; + Segment* S; + Section* Sec; + + /* Read the name */ + ReadStr (F, Name); + + /* Read the size */ + Size = Read32 (F); + + /* Read the alignment */ + Align = Read8 (F); + + /* Read the segment type */ + Type = Read8 (F); + + /* Print some data */ + if (Verbose > 1) { + printf ("Module `%s': Found segment `%s', size = %lu, align = %u, type = %u\n", + O->Name, Name, Size, Align, Type); + } + + /* Create a hash over the name and try to locate the segment in the table */ + HashVal = HashStr (Name) % HASHTAB_SIZE; + S = SegFindInternal (Name, HashVal); + + /* If we don't have that segment already, allocate it using the type of + * the first section. + */ + if (S == 0) { + /* Create a new segment and insert it */ + S = NewSegment (Name, Type); + S->Next = HashTab [HashVal]; + HashTab [HashVal] = S; + } + + /* Allocate the section we will return later */ + Sec = NewSection (S, Align, Type); + + /* Check if the section has the same type as the segment */ + if (Sec->Type != S->Type) { + /* OOPS */ + Error ("Module `%s': Type mismatch for segment `%s'", O->Name, S->Name); + } + + /* Set up the minimum segment alignment */ + if (Sec->Align > S->Align) { + /* Section needs larger alignment, use this one */ + S->Align = Sec->Align; + S->AlignObj = O; + } + + /* Start reading fragments from the file and insert them into the section . */ + while (Size) { + + Fragment* Frag; + + /* Read the fragment type */ + unsigned char Type = Read8 (F); + + /* Handle the different fragment types */ + switch (Type) { + + case FRAG_LITERAL8: + Frag = NewFragment (FRAG_LITERAL, Read8 (F), Sec); + break; + + case FRAG_LITERAL16: + Frag = NewFragment (FRAG_LITERAL, Read16 (F), Sec); + break; + + case FRAG_LITERAL24: + Frag = NewFragment (FRAG_LITERAL, Read24 (F), Sec); + break; + + case FRAG_LITERAL32: + Frag = NewFragment (FRAG_LITERAL, Read32 (F), Sec); + break; + + case FRAG_EXPR8: + case FRAG_EXPR16: + case FRAG_EXPR24: + case FRAG_EXPR32: + case FRAG_SEXPR8: + case FRAG_SEXPR16: + case FRAG_SEXPR24: + case FRAG_SEXPR32: + Frag = NewFragment (Type & FRAG_TYPEMASK, Type & FRAG_BYTEMASK, Sec); + break; + + case FRAG_FILL: + /* Will allocate memory, but we don't care... */ + Frag = NewFragment (FRAG_FILL, Read16 (F), Sec); + break; + + default: + Error ("Unknown fragment type in module `%s', segment `%s': %02X", + O->Name, S->Name, Type); + /* NOTREACHED */ + return 0; + } + + /* Now read the fragment data */ + switch (Frag->Type) { + + case FRAG_LITERAL: + /* Literal data */ + ReadData (F, Frag->LitBuf, Frag->Size); + break; + + case FRAG_EXPR: + case FRAG_SEXPR: + /* An expression */ + Frag->Expr = ReadExpr (F, O); + break; + + } + + /* Read the file position of the fragment */ + ReadFilePos (F, &Frag->Pos); + + /* Remember the module we had this fragment from */ + Frag->Obj = O; + + /* Next one */ + CHECK (Size >= Frag->Size); + Size -= Frag->Size; + } + + /* Increment the segment size by the section size */ + S->Size += Sec->Size; + + /* Return the section */ + return Sec; +} + + + +Segment* SegFind (const char* Name) +/* Return the given segment or NULL if not found. */ +{ + return SegFindInternal (Name, HashStr (Name) % HASHTAB_SIZE); +} + + + +int IsBSSType (Segment* S) +/* Check if the given segment is a BSS style segment, that is, it does not + * contain non-zero data. + */ +{ + /* Loop over all sections */ + Section* Sec = S->SecRoot; + while (Sec) { + /* Loop over all fragments */ + Fragment* F = Sec->FragRoot; + while (F) { + if (F->Type == FRAG_LITERAL) { + unsigned char* Data = F->LitBuf; + unsigned long Count = F->Size; + while (Count--) { + if (*Data++ != 0) { + return 0; + } + } + } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) { + if (GetExprVal (F->Expr) != 0) { + return 0; + } + } + F = F->Next; + } + Sec = Sec->Next; + } + return 1; +} + + + +void SegDump (void) +/* Dump the segments and it's contents */ +{ + unsigned I; + unsigned long Count; + unsigned char* Data; + + Segment* Seg = SegRoot; + while (Seg) { + Section* S = Seg->SecRoot; + printf ("Segment: %s (%lu)\n", Seg->Name, Seg->Size); + while (S) { + Fragment* F = S->FragRoot; + printf (" Section:\n"); + while (F) { + switch (F->Type) { + + case FRAG_LITERAL: + printf (" Literal (%lu bytes):", F->Size); + Count = F->Size; + Data = F->LitBuf; + I = 100; + while (Count--) { + if (I > 75) { + printf ("\n "); + I = 3; + } + printf (" %02X", *Data++); + I += 3; + } + printf ("\n"); + break; + + case FRAG_EXPR: + printf (" Expression (%lu bytes):\n", F->Size); + printf (" "); + DumpExpr (F->Expr); + break; + + case FRAG_SEXPR: + printf (" Signed expression (%lu bytes):\n", F->Size); + printf (" "); + DumpExpr (F->Expr); + break; + + case FRAG_FILL: + printf (" Empty space (%lu bytes)\n", F->Size); + break; + + default: + Internal ("Invalid fragment type: %02X", F->Type); + } + F = F->Next; + } + S = S->Next; + } + Seg = Seg->List; + } +} + + + +unsigned SegWriteConstExpr (FILE* F, ExprNode* E, int Signed, unsigned Size) +/* Write a supposedly constant expression to the target file. Do a range + * check and return one of the SEG_EXPR_xxx codes. + */ +{ + static const unsigned long U_HighRange [4] = { + 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF + }; + static const long S_HighRange [4] = { + 0x0000007F, 0x00007FFF, 0x007FFFFF, 0x7FFFFFFF + }; + static const long S_LowRange [4] = { + 0xFFFFFF80, 0xFFFF8000, 0xFF800000, 0x80000000 + }; + + + /* Get the expression value */ + long Val = GetExprVal (E); + + /* Check the size */ + CHECK (Size >= 1 && Size <= 4); + + /* Check for a range error */ + if (Signed) { + if (Val > S_HighRange [Size-1] || Val < S_LowRange [Size-1]) { + /* Range error */ + return SEG_EXPR_RANGE_ERROR; + } + } else { + if (((unsigned long)Val) > U_HighRange [Size-1]) { + /* Range error */ + return SEG_EXPR_RANGE_ERROR; + } + } + + /* Write the value to the file */ + WriteVal (F, Val, Size); + + /* Success */ + return SEG_EXPR_OK; +} + + + +void SegWrite (FILE* Tgt, Segment* S, SegWriteFunc F, void* Data) +/* Write the data from the given segment to a file. For expressions, F is + * called (see description of SegWriteFunc above). + */ +{ + int Sign; + unsigned long Offs = 0; + + /* Loop over all sections in this segment */ + Section* Sec = S->SecRoot; + while (Sec) { + Fragment* Frag; + + /* If we have fill bytes, write them now */ + WriteMult (Tgt, S->FillVal, Sec->Fill); + + /* Loop over all fragments in this section */ + Frag = Sec->FragRoot; + while (Frag) { + + switch (Frag->Type) { + + case FRAG_LITERAL: + WriteData (Tgt, Frag->LitBuf, Frag->Size); + break; + + case FRAG_EXPR: + case FRAG_SEXPR: + Sign = (Frag->Type == FRAG_SEXPR); + /* Call the users function and evaluate the result */ + switch (F (Frag->Expr, Sign, Frag->Size, Offs, Data)) { + + case SEG_EXPR_OK: + break; + + case SEG_EXPR_RANGE_ERROR: + Error ("Range error in module `%s', line %lu", + Frag->Obj->Files [Frag->Pos.Name], Frag->Pos.Line); + break; + + case SEG_EXPR_TOO_COMPLEX: + Error ("Expression too complex in module `%s', line %lu", + Frag->Obj->Files [Frag->Pos.Name], Frag->Pos.Line); + break; + + default: + Internal ("Invalid return code from SegWriteFunc"); + } + break; + + case FRAG_FILL: + WriteMult (Tgt, S->FillVal, Frag->Size); + break; + + default: + Internal ("Invalid fragment type: %02X", Frag->Type); + } + + /* Update the offset */ + Offs += Frag->Size; + + /* Next fragment */ + Frag = Frag->Next; + } + + /* Next section */ + Sec = Sec->Next; + } +} + + + +static int CmpSegStart (const void* K1, const void* K2) +/* Compare function for qsort */ +{ + /* Get the real segment pointers */ + const Segment* S1 = *(const Segment**)K1; + const Segment* S2 = *(const Segment**)K2; + + /* Compare the start addresses */ + if (S1->PC > S2->PC) { + return 1; + } else if (S1->PC < S2->PC) { + return -1; + } else { + /* Sort segments with equal starts by name */ + return strcmp (S1->Name, S2->Name); + } +} + + + +void PrintSegmentMap (FILE* F) +/* Print a segment map to the given file */ +{ + unsigned I; + Segment* S; + Segment** SegPool; + + /* Allocate memory for the segment pool */ + SegPool = Xmalloc (SegCount * sizeof (Segment*)); + + /* Collect pointers to the segments */ + I = 0; + S = SegRoot; + while (S) { + + /* Check the count for safety */ + CHECK (I < SegCount); + + /* Remember the pointer */ + SegPool [I] = S; + + /* Follow the linked list */ + S = S->List; + + /* Next array index */ + ++I; + } + CHECK (I == SegCount); + + /* Sort the array by increasing start addresses */ + qsort (SegPool, SegCount, sizeof (Segment*), CmpSegStart); + + /* Print a header */ + fprintf (F, "Name Start End Size\n" + "--------------------------------------------\n"); + + /* Print the segments */ + for (I = 0; I < SegCount; ++I) { + + /* Get a pointer to the segment */ + S = SegPool [I]; + + /* Print empty segments only if explicitly requested */ + if (VerboseMap || S->Size > 0) { + /* Print the segment data */ + fprintf (F, "%-20s %06lX %06lX %06lX\n", + S->Name, S->PC, S->PC + S->Size, S->Size); + } + } + + /* Free the segment pool */ + Xfree (SegPool); +} + + + +void CheckSegments (void) +/* Walk through the segment list and check if there are segments that were + * not written to the output file. Output an error if this is the case. + */ +{ + Segment* S = SegRoot; + while (S) { + if (S->Size > 0 && S->Dumped == 0) { + Error ("Missing memory area assignment for segment `%s'", S->Name); + } + S = S->List; + } +} + + + diff --git a/src/ld65/segments.h b/src/ld65/segments.h new file mode 100644 index 000000000..f69e9c2db --- /dev/null +++ b/src/ld65/segments.h @@ -0,0 +1,152 @@ +/*****************************************************************************/ +/* */ +/* segments.h */ +/* */ +/* Segment handling for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef SEGMENTS_H +#define SEGMENTS_H + + + +#include + +#include "../common/exprdefs.h" + +#include "objdata.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Forward for the section structure (a section is a part of a segment) */ +typedef struct Section_ Section; + +/* Segment structure */ +typedef struct Segment_ Segment; +struct Segment_ { + Segment* Next; /* Hash list */ + Segment* List; /* List of all segments */ + Section* SecRoot; /* Section list */ + Section* SecLast; /* Pointer to last section */ + unsigned long PC; /* PC were this segment is located */ + unsigned long Size; /* Size of data so far */ + ObjData* AlignObj; /* Module that requested the alignment */ + unsigned char Align; /* Alignment needed */ + unsigned char FillVal; /* Value to use for fill bytes */ + unsigned char Type; /* Type of segment */ + char Dumped; /* Did we dump this segment? */ + char Name [1]; /* Name, dynamically allocated */ +}; + + + +/* Section structure (a section is a part of a segment) */ +struct Section_ { + Section* Next; /* List of sections in a segment */ + Segment* Seg; /* Segment that contains the section */ + struct Fragment_* FragRoot; /* Fragment list */ + struct Fragment_* FragLast; /* Pointer to last fragment */ + unsigned long Offs; /* Offset into the segment */ + unsigned long Size; /* Size of the section */ + unsigned char Align; /* Alignment */ + unsigned char Fill; /* Fill bytes for alignment */ + unsigned char Type; /* Type of segment */ +}; + + + +/* Prototype for a function that is used to write expressions to the target + * file (used in SegWrite). It returns one of the following values: + */ +#define SEG_EXPR_OK 0 /* Ok */ +#define SEG_EXPR_RANGE_ERROR 1 /* Range error */ +#define SEG_EXPR_TOO_COMPLEX 2 /* Expression too complex */ + +typedef unsigned (*SegWriteFunc) (ExprNode* E, /* The expression to write */ + int Signed, /* Signed expression? */ + unsigned Size, /* Size (=range) */ + unsigned long Offs, /* File offset */ + void* Data); /* Callers data */ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +Section* ReadSection (FILE* F, ObjData* O); +/* Read a section from a file */ + +Segment* SegFind (const char* Name); +/* Return the given segment or NULL if not found. */ + +int IsBSSType (Segment* S); +/* Check if the given segment is a BSS style segment, that is, it does not + * contain non-zero data. + */ + +void SegDump (void); +/* Dump the segments and it's contents */ + +unsigned SegWriteConstExpr (FILE* F, ExprNode* E, int Signed, unsigned Size); +/* Write a supposedly constant expression to the target file. Do a range + * check and return one of the SEG_EXPR_xxx codes. + */ + +void SegWrite (FILE* Tgt, Segment* S, SegWriteFunc F, void* Data); +/* Write the data from the given segment to a file. For expressions, F is + * called (see description of SegWriteFunc above). + */ + +void PrintSegmentMap (FILE* F); +/* Print a segment map to the given file */ + +void CheckSegments (void); +/* Walk through the segment list and check if there are segments that were + * not written to the output file. Output an error if this is the case. + */ + + + +/* End of segments.h */ + +#endif + + + diff --git a/src/ld65/target.c b/src/ld65/target.c new file mode 100644 index 000000000..a97fe32f0 --- /dev/null +++ b/src/ld65/target.c @@ -0,0 +1,351 @@ +/*****************************************************************************/ +/* */ +/* target.c */ +/* */ +/* Target system support for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include +#include +#include +#include + +#include "error.h" +#include "global.h" +#include "binfmt.h" +#include "scanner.h" +#include "config.h" +#include "target.h" + + + +/*****************************************************************************/ +/* Target configurations */ +/*****************************************************************************/ + + + +static const char CfgNone [] = + "MEMORY {" + "RAM: start = %S, size = $10000, file = %O;" + "}" + "SEGMENTS {" + "CODE: load = RAM, type = rw;" + "RODATA: load = RAM, type = rw;" + "DATA: load = RAM, type = rw;" + "BSS: load = RAM, type = bss, define = yes;" + "}"; + +static const char CfgAtari [] = + "MEMORY {" + "HEADER: start = $0000, size = $6, file = %O;" + "RAM: start = $1F00, size = $6100, file = %O;" + "}" + "SEGMENTS {" + "EXEHDR: load = HEADER, type = wprot;" + "CODE: load = RAM, type = wprot, define = yes;" + "RODATA: load = RAM, type = wprot;" + "DATA: load = RAM, type = rw;" + "BSS: load = RAM, type = bss, define = yes;" + "AUTOSTRT: load = RAM, type = wprot;" + "}"; + +static const char CfgC64 [] = + "MEMORY {" + "RAM: start = $7FF, size = $c801, file = %O;" + "}" + "SEGMENTS {" + "CODE: load = RAM, type = wprot;" + "RODATA: load = RAM, type = wprot;" + "DATA: load = RAM, type = rw;" + "BSS: load = RAM, type = bss, define = yes;" + "}"; + +static const char CfgC128 [] = + "MEMORY {" + "RAM: start = $1bff, size = $a401, file = %O;" + "}" + "SEGMENTS {" + "CODE: load = RAM, type = wprot;" + "RODATA: load = RAM, type = wprot;" + "DATA: load = RAM, type = rw;" + "BSS: load = RAM, type = bss, define = yes;" + "}"; + +static const char CfgAce [] = + ""; + +static const char CfgPlus4 [] = + "MEMORY {" + "RAM: start = $0fff, size = $7001, file = %O;" + "}" + "SEGMENTS {" + "CODE: load = RAM, type = wprot;" + "RODATA: load = RAM, type = wprot;" + "DATA: load = RAM, type = rw;" + "BSS: load = RAM, type = bss, define = yes;" + "}"; + +static const char CfgCBM610 [] = + "MEMORY {" + "RAM: start = $0001, size = $FFF0, file = %O;" + "}" + "SEGMENTS {" + "CODE: load = RAM, type = wprot;" + "RODATA: load = RAM, type = wprot;" + "DATA: load = RAM, type = rw;" + "BSS: load = RAM, type = bss, define = yes;" + "}"; + +static const char CfgPET [] = + "MEMORY {" + "RAM: start = $03FF, size = $7BFF, file = %O;" + "}" + "SEGMENTS {" + "CODE: load = RAM, type = wprot;" + "RODATA: load = RAM, type = wprot;" + "DATA: load = RAM, type = rw;" + "BSS: load = RAM, type = bss, define = yes;" + "}"; + +static const char CfgNES [] = + "MEMORY {" + "RAM: start = $0200, size = $0600, file = \"\";" + "ROM: start = $8000, size = $8000, file = %O;" + "}" + "SEGMENTS {" + "CODE: load = ROM, type = ro;" + "RODATA: load = ROM, type = ro;" + "DATA: load = ROM, run = RAM, type = rw, define = yes;" + "BSS: load = RAM, type = bss, define = yes;" + "VECTORS: load = ROM, type = ro, start = $FFFA;" + "}"; + +static const char CfgLunix [] = + "MEMORY {" + "COMBINED: start = $0000, size = $FFFF, file = %O;" + "ZEROPAGE: start = $0000, size = $0100, file = %O;" + "}" + "SEGMENTS {" + "CODE: load = COMBINED, type = wprot;" + "RODATA: load = COMBINED, type = wprot;" + "DATA: load = COMBINED, type = rw, define = yes;" + "BSS: load = COMBINED, type = bss, define = yes;" + "ZEROPAGE: load = ZEROPAGE, type = zp;" + "}" + "FILES {" + "%O: format = o65;" + "}" + "FORMATS {" + "o65: os = lunix, type = small," + "extsym = \"LUNIXKERNAL\", extsym = \"LIB6502\";" + "}"; + +static const char CfgOSA65 [] = + "MEMORY {" + "COMBINED: start = $0000, size = $FFFF, file = %O;" + "ZEROPAGE: start = $0000, size = $0100, file = %O;" + "}" + "SEGMENTS {" + "CODE: load = COMBINED, type = wprot;" + "RODATA: load = COMBINED, type = wprot;" + "DATA: load = COMBINED, type = rw, define = yes;" + "BSS: load = COMBINED, type = bss, define = yes;" + "ZEROPAGE: load = ZEROPAGE, type = zp;" + "}" + "FILES {" + "%O: format = o65;" + "}" + "FORMATS {" + "o65: os = osa65, type = small," + "extsym = \"OSA2KERNAL\", extsym = \"LIB6502\";" + "}"; + +static const char CfgApple2 [] = + "MEMORY {" + "RAM: start = $800, size = $8E00, file = %O;" + "}" + "SEGMENTS { " + "CODE: load = RAM, type = ro;" + "RODATA: load = RAM, type = ro;" + "DATA: load = RAM, type = rw;" + "BSS: load = RAM, type = bss, define = yes;" + "}"; + +static const char CfgGeos [] = + "MEMORY {" + "HEADER: start = $204, size = 508, file = %O;" + "RAM: start = $400, size = $7C00, file = %O;" + "}" + "SEGMENTS { " + "HEADER: load = HEADER, type = ro;" + "CODE: load = RAM, type = ro;" + "RODATA: load = RAM, type = ro;" + "DATA: load = RAM, type = rw;" + "BSS: load = RAM, type = bss, define = yes;" + "}"; + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Supported systems */ +#define TGT_NONE 0 +#define TGT_ATARI 1 /* Atari 8 bit machines */ +#define TGT_C64 2 +#define TGT_C128 3 +#define TGT_ACE 4 +#define TGT_PLUS4 5 +#define TGT_CBM610 6 /* CBM 600/700 family */ +#define TGT_PET 7 /* CBM PET family */ +#define TGT_NES 8 /* Nintendo Entertainment System */ +#define TGT_LUNIX 9 +#define TGT_OSA65 10 +#define TGT_APPLE2 11 +#define TGT_GEOS 12 +#define TGT_COUNT 13 /* Count of supported systems */ + + + +/* Structure describing a target */ +typedef struct TargetCfg_ TargetCfg; +struct TargetCfg_ { + const char* Name; /* Name of the system */ + unsigned char BinFmt; /* Default binary format for the target */ + const char* Cfg; /* Pointer to configuration */ +}; + +static const TargetCfg Targets [TGT_COUNT] = { + { "none", BINFMT_BINARY, CfgNone }, + { "atari", BINFMT_BINARY, CfgAtari }, + { "c64", BINFMT_BINARY, CfgC64 }, + { "c128", BINFMT_BINARY, CfgC128 }, + { "ace", BINFMT_BINARY, CfgAce }, + { "plus4", BINFMT_BINARY, CfgPlus4 }, + { "cbm610", BINFMT_BINARY, CfgCBM610 }, + { "pet", BINFMT_BINARY, CfgPET }, + { "nes", BINFMT_BINARY, CfgNES }, + { "lunix", BINFMT_O65, CfgLunix }, + { "osa65", BINFMT_O65, CfgOSA65 }, + { "apple2", BINFMT_BINARY, CfgApple2 }, + { "geos", BINFMT_BINARY, CfgGeos }, +}; + +/* Selected target system type */ +static const TargetCfg* Target; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static int StrICmp (const char* S1, const char* S2) +/* Compare two strings case insensitive */ +{ + int Diff = 0; + while (1) { + Diff = tolower (*S1) - tolower (*S2); + if (Diff != 0 || *S1 == '\0') { + return Diff; + } + ++S1; + ++S2; + } +} + + + +static int TgtMap (const char* Name) +/* Map a target name to a system code. Return -1 in case of an error */ +{ + unsigned I; + + /* Check for a numeric target */ + if (isdigit (*Name)) { + int Target = atoi (Name); + if (Target >= 0 && Target < TGT_COUNT) { + return Target; + } + } + + /* Check for a target string */ + for (I = 0; I < TGT_COUNT; ++I) { + if (StrICmp (Targets [I].Name, Name) == 0) { + return I; + } + } + + /* Not found */ + return -1; +} + + + +void TgtSet (const char* T) +/* Set the target system, initialize internal stuff for this target */ +{ + /* Map the target to a number */ + int TgtNum = TgtMap (T); + if (TgtNum == -1) { + Error ("Invalid target system: %s", T); + } + Target = &Targets [TgtNum]; + + /* Set the target data */ + DefaultBinFmt = Target->BinFmt; + CfgSetBuf (Target->Cfg); +} + + + +void TgtPrintList (FILE* F) +/* Print a list of the available target systems */ +{ + unsigned I; + + /* Print a header */ + fprintf (F, "Available targets:\n"); + + /* Print a list of the target systems */ + for (I = 0; I < TGT_COUNT; ++I) { + fprintf (F, " %s\n", Targets [I].Name); + } +} + + + diff --git a/src/ld65/target.h b/src/ld65/target.h new file mode 100644 index 000000000..43cf6ab91 --- /dev/null +++ b/src/ld65/target.h @@ -0,0 +1,64 @@ +/*****************************************************************************/ +/* */ +/* target.h */ +/* */ +/* Target system support for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998-2000 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef TARGET_H +#define TARGET_H + + + +#include + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void TgtSet (const char* T); +/* Set the target system, initialize internal stuff for this target */ + +void TgtPrintList (FILE* F); +/* Print a list of the available target systems */ + + + +/* End of target.h */ + +#endif + + + diff --git a/src/ld65/version.h b/src/ld65/version.h new file mode 100644 index 000000000..7aeac124d --- /dev/null +++ b/src/ld65/version.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +/* */ +/* version.h */ +/* */ +/* Version information for the ld65 linker */ +/* */ +/* */ +/* */ +/* (C) 1998 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@musoftware.de */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef VERSION_H +#define VERSION_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +#define VER_MAJOR 2U +#define VER_MINOR 4U +#define VER_PATCH 0U + + + +/* End of version.h */ + +#endif + + + diff --git a/src/make/gcc.mak b/src/make/gcc.mak new file mode 100644 index 000000000..0167f90e6 --- /dev/null +++ b/src/make/gcc.mak @@ -0,0 +1,32 @@ +# +# gcc Makefile for the program sources +# + +CFLAGS = -g -O2 -Wall +CC = gcc +LDFLAGS = + +SUBDIRS = \ + common \ + ar65 \ + ca65 \ + cc65 \ + cl65 \ + ld65 + +.PHONY: all +all: + for i in $(SUBDIRS); do $(MAKE) -C $$i -f make/gcc.mak all; done + +.PHONY: dist +dist: + for i in $(SUBDIRS); do $(MAKE) -C $$i -f make/gcc.mak dist; done + +.PHONY: clean +clean: + for i in $(SUBDIRS); do $(MAKE) -C $$i -f make/gcc.mak clean; done + +.PHONY: zap +zap: + for i in $(SUBDIRS); do $(MAKE) -C $$i -f make/gcc.mak zap; done + diff --git a/src/make/watcom.mak b/src/make/watcom.mak new file mode 100644 index 000000000..d9a341ac3 --- /dev/null +++ b/src/make/watcom.mak @@ -0,0 +1,42 @@ +# +# Watcom Makefile for the cc65 binutils +# + +SUBDIRS = \ + common \ + ar65 \ + ca65 \ + ld65 + +all: + cd common + make -f make\watcom.mak + cd ..\ar65 + make -f make\watcom.mak + cd ..\ca65 + make -f make\watcom.mak + cd ..\ld65 + make -f make\watcom.mak + cd .. + +clean: + cd common + make -f make\watcom.mak clean + cd ..\ar65 + make -f make\watcom.mak clean + cd ..\ca65 + make -f make\watcom.mak clean + cd ..\ld65 + make -f make\watcom.mak clean + cd .. + +strip: + @cd ar65 + @-make -f make\watcom.mak strip + @cd ..\ca65 + @-make -f make\watcom.mak strip + @cd ..\ld65 + @-make -f make\watcom.mak strip + @cd .. + + diff --git a/testcode/compiler/pptest1.c b/testcode/compiler/pptest1.c new file mode 100644 index 000000000..b80c8b7d7 --- /dev/null +++ b/testcode/compiler/pptest1.c @@ -0,0 +1,6 @@ +#define hash_hash # ## # +#define mkstr(a) # a +#define in_between(a) mkstr(a) +#define join(c, d) in_between(c hash_hash d) + +char p[] = join(x, y); // Comment diff --git a/testcode/compiler/pptest2.c b/testcode/compiler/pptest2.c new file mode 100644 index 000000000..0d613cdc3 --- /dev/null +++ b/testcode/compiler/pptest2.c @@ -0,0 +1,19 @@ +#define x 3 +#define f(a) f(x * (a)) +#undef x +#define x 2 +#define g f +#define z z[0] +#define h g(~ +#define m(a) a(w) +#define w 0,1 +#define t(a) a +#define p() int +#define q(x) x +#define r(x,y) x ## y +#define str(x) # x + +f(y+1) + f(f(z)) % t(t(g) (0) + t)(1); +g(x+(3,4)-w) | h 5) & m(f)^m(m); +p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; +char c[2][6] = { str(hello), str() }; diff --git a/testcode/compiler/pptest3.c b/testcode/compiler/pptest3.c new file mode 100644 index 000000000..2c2df87e5 --- /dev/null +++ b/testcode/compiler/pptest3.c @@ -0,0 +1,16 @@ +#define str(s) # s +#define xstr(s) str(s) +#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \ + x ## s, x ## t) +#define INCFILE(n) vers ## n // Comment +#define glue(a,b) a ## b +#define xglue(a,b) glue(a,b) +#define HIGHLOW "hello" +#define LOW LOW ", world" + +debug (1, 2); +fputs (str (strncmp("abc\0d", "abc", '\4') // Comment + == 0) str (: @\n), s); +glue (HIGH, LOW); +xglue (HIGH, LOW); + diff --git a/testcode/compiler/pptest4.c b/testcode/compiler/pptest4.c new file mode 100644 index 000000000..abb0d24b5 --- /dev/null +++ b/testcode/compiler/pptest4.c @@ -0,0 +1,3 @@ +#define t(x,y,z) x ## y ## z +int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), + t(10,,), t(,11,), t(,,12), t(,,) }; diff --git a/util/atari/ataricvt.c b/util/atari/ataricvt.c new file mode 100644 index 000000000..35a8ebb5b --- /dev/null +++ b/util/atari/ataricvt.c @@ -0,0 +1,17 @@ +#include + +int main (void) +{ + int C; + while ((C = getchar ()) != EOF) { + if (C == 0x9B) { + putchar ('\n'); + } else if (C == 0x7F) { + putchar ('\t'); + } else { + putchar (C); + } + } + return 0; +} + -- 2.39.5