summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2013-10-26 11:10:59 (GMT)
committerH. Peter Anvin <hpa@zytor.com>2013-10-26 11:10:59 (GMT)
commit966d15a4d6298e6d388525db56d614063bf3f1c9 (patch)
treeadb93ff343065bd174a0507e5e380d760903cbf6
parente65a52425a997bd404b8aec3cbdb048d8e9e0d73 (diff)
downloadabc80sim-966d15a4d6298e6d388525db56d614063bf3f1c9.zip
abc80sim-966d15a4d6298e6d388525db56d614063bf3f1c9.tar.gz
abc80sim-966d15a4d6298e6d388525db56d614063bf3f1c9.tar.bz2
abc80sim-966d15a4d6298e6d388525db56d614063bf3f1c9.tar.xz
Add printer ROM source code and the assembler for it
This is a different z80asm... Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--printer.asm474
-rw-r--r--z80asm/BUGS3
-rw-r--r--z80asm/COPYING18
-rw-r--r--z80asm/ChangeLog169
-rw-r--r--z80asm/GPL2341
-rw-r--r--z80asm/INSTALL9
-rw-r--r--z80asm/Makefile54
-rw-r--r--z80asm/NEWS4
-rw-r--r--z80asm/README100
-rw-r--r--z80asm/TODO5
-rw-r--r--z80asm/gnulib/getopt.c1199
-rw-r--r--z80asm/gnulib/getopt.h165
-rw-r--r--z80asm/gnulib/getopt1.c185
-rw-r--r--z80asm/gnulib/gettext.h68
-rw-r--r--z80asm/z80asm.c3578
15 files changed, 6372 insertions, 0 deletions
diff --git a/printer.asm b/printer.asm
new file mode 100644
index 0000000..a02ec84
--- /dev/null
+++ b/printer.asm
@@ -0,0 +1,474 @@
+; -*- fundamental -*-
+;
+; Simple option ROM for printer for ABC80; this is not authentic, but
+; was done for the FPGA project. This assumes the following hardware
+; interface:
+;
+; - ABC-bus select code 60 decimal;
+; OUT 0 - (OUT) output data
+; OUT 4 - (C3) reset input FIFO
+; IN 0 - (INP) input data
+; INP 1 - (STAT) bit 5 - Tx busy
+; bit 6 - Rx available
+; bit 7 - flow control
+;
+; Convert FF -> FF FF and send FF 00 on CLOSE.
+;
+selcode: equ 60
+
+ org 0x7800
+
+ ; jp table for PR:
+pr_jptable:
+ jp pr_open ; OPEN
+ jp pr_open ; PREPARE
+ jp pr_close ; CLOSE
+ jp notthere ; INPUT
+ jp pr_print ; PRINT
+ jp notthere ; RDBLK
+ jp notthere ; WRBLK
+ jp notthere ; DELETE
+ jp notthere ; RENAME
+
+prab_jp_init:
+ call prab_init
+ jp 0x6543 ; Initialize DOS
+
+pr_open:
+ call select
+
+ ld a,128+selcode
+ out (3),a
+
+ in a,(1)
+ and a
+ jr z,done
+
+pr_close:
+ call select
+ call wait_busy
+ ld a,0FFh
+ out (0),a
+ call wait_busy
+ xor a
+ out (0),a
+ jr done
+
+pr_print:
+ call select
+ ld d,0FFh
+
+pr_print_loop:
+ ld a,b
+ or c
+ jr z,done
+ call wait_busy
+ ld a,(hl)
+ cp d
+ jr nz,pr_print_not_ff
+ out (0),a
+ call wait_busy
+ ld a,(hl)
+pr_print_not_ff:
+ out (0),a
+
+ dec bc
+ inc hl
+ jr pr_print_loop
+
+select:
+ ld a,(0xFD3F) ; Old select code
+ ld (ram_select),a ; Save old select code
+ ld a,selcode ; Select code
+ ld (0xFD3F),a
+ out (1),a
+ ret
+
+wait_busy:
+ push af
+wb_loop:
+ in a,(1)
+ and 0A0h ; TX busy or TX flow control
+ jr nz,wb_loop
+ pop af
+ ret
+
+notthere:
+ ld a,128+52
+ scf
+ ret
+
+done:
+ xor a
+done_err:
+ push af
+ ld a,(ram_select)
+ ld (0xFD3F),a
+ out (1),a
+ pop af
+ and a
+ ret p
+ cp 128+34 ; ERR 34 = end of file
+ jr nz,not_eof
+ xor a ; ... end of file is signalled by A=0
+not_eof:
+ scf
+ ret
+
+ ; jp table for PRA:
+pra_jptable:
+ jp pra_open ; OPEN
+ jp pra_prepare ; PREPARE
+ jp pra_close ; CLOSE
+ jp pra_input ; INPUT
+ jp pra_print ; PRINT
+ jp notthere ; RDBLK
+ jp notthere ; WRBLK
+ ; Supporting DELETE and RENAME requires fixes to the DOS ROM;
+ ; the DOS ROM contains the BASIC interpreter for these and
+ ; it assumes it only applies to disk files.
+ jp notthere ; DELETE
+ jp notthere ; RENAME
+
+prb_jptable:
+ jp prb_open ; OPEN
+ jp prb_prepare ; PREPARE
+ jp prb_close ; CLOSE
+ jp 0015h ; INPUT
+ jp 001Bh ; PRINT
+ jp prb_rdblk ; RDBLK
+ jp prb_wrblk ; WRBLK
+ ; Supporting DELETE and RENAME requires fixes to the DOS ROM;
+ ; the DOS ROM contains the BASIC interpreter for these and
+ ; it assumes it only applies to disk files.
+ jp notthere ; DELETE
+ jp notthere ; RENAME
+
+pra_open:
+ ld c,0xA0
+ jr prx_open
+prb_open:
+ call prb_setup_buf
+ ld c,0xA1
+ jr prx_open
+pra_prepare:
+ ld c,0xA2
+ jr prx_open
+prb_prepare:
+ call prb_setup_buf
+ ld (ix+14),1
+ ld c,0xA3
+prx_open:
+ push bc
+ ex de,hl ; HL <- filename
+ ld (ix+6),0
+ ld (ix+7),132
+ call select
+ pop bc
+ ld a,c
+ push bc
+ call send_cmd
+ ld bc,11
+ call send_buf
+ pop bc
+ ld a,c
+ push bc
+ call recv_reply
+ and a
+ pop bc
+ jr nz,done_err2
+ bit 0,c ; PRB:?
+ jr z,done_err2
+ ld l,(ix+8)
+ ld h,(ix+9)
+ ld (hl),0xe0 ; Mark DOSBUF in use, drive no 0xE0
+ bit 1,c ; OPEN?
+ call z,prb_rdblk ; If so, read the first block?
+ ex de,hl
+ jr done_err2
+
+prb_close:
+ bit 7,(ix+14)
+ call nz,0023h ; Flush data and write EOF
+ ld (ix+14),0
+ ld h,0xFD
+ ld a,0x41
+ add (ix+12)
+ ld l,a
+ ld (hl),0xFF ; Mark buffer free
+pra_close:
+ call select
+ ld a,0xA7
+ call send_cmd
+ ld a,0xA7
+ call recv_reply
+ jr done_err2
+
+pra_print:
+ call output_common
+done_err2:
+ jp done_err
+
+output_common:
+ call select
+ ld a,0xA6
+ call send_cmd
+ ld a,c
+ call send_byte
+ ld a,b
+ call send_byte
+ call send_buf
+ jp recv_reply
+
+pra_input:
+ call select
+ ld a,0xA4 ; INPUT
+ call send_cmd
+ call recv_reply
+ and a
+ jr nz,done_err2
+ call recv_byte
+ jr c,prai_timeout
+ ld e,a
+ call recv_byte
+ jr c,prai_timeout
+ ld d,a
+ ; Now HL -> target buf; DE -> expected byte count;
+ ; BC -> buffer size
+prai_loop:
+ ld a,d
+ or e
+ jp z,done
+ dec de
+ ld a,b
+ or c
+ jr nz,prai_space
+ ld hl,ram_dummy
+ inc c
+prai_space:
+ dec bc
+ call recv_byte
+ jr c,prai_timeout
+ ld (hl),a
+ inc hl
+ jr prai_loop
+prbr_timeout_pop:
+ pop hl
+prbr_timeout:
+prai_timeout:
+ ld a,128+42
+ jr done_err3
+prbr_protoerr:
+ ld a,128+37
+ jr done_err3
+prb_rdblk:
+ call select
+ ld a,0xA5
+ call send_cmd
+ ld b,253
+ ld a,b
+ call send_byte
+ xor a
+ call send_byte
+ call recv_reply
+ and a
+ jr nz,done_err3
+ call recv_byte
+ jr c,prbr_timeout
+ cp b
+ jr nz,prbr_protoerr
+ call recv_byte
+ jr c,prbr_timeout
+ and a
+ jr nz,prbr_protoerr
+ ld l,(ix+8)
+ ld h,(ix+9)
+ push hl
+prbr_recv:
+ call recv_byte
+ jr c,prbr_timeout_pop
+ ld (hl),a
+ inc hl
+ djnz prbr_recv
+ ld (hl),3 ; Terminate with ETX
+ pop hl ; HL -> buf address
+ jp done
+
+ ; Write a binary output block. Similar to
+ ; pra_print, but with a few different buffer
+ ; management bits.
+prb_wrblk:
+ ld l,(ix+8)
+ ld h,(ix+9)
+ ld bc,253
+ push hl ; HL -> buf address
+ call output_common
+ pop hl ; HL -> buf address
+ and a
+ jr nz,done_err3
+ ld (ix+10),l
+ ld (ix+11),h
+ ld (ix+13),252
+ ld (ix+14),a
+ ld (hl),3
+ ex de,hl ; DE -> buf address
+done_err3:
+ jp done_err
+
+ ; Set up a BUF for PRB:
+prb_setup_buf:
+ push hl
+ ld hl,0xFD51 ; DOSDEV1
+psb_loop:
+ ld a,(hl)
+ inc a
+ jr z,psb_found
+ ld a,l
+ add 10h
+ ld l,a
+ cp 0C1h
+ jr nz,psb_loop
+ pop hl
+ rst 10h
+ defb 128+19 ; Too many files open
+psb_found:
+ push de
+ ld a,l
+ sub 041h
+ ld (ix+12),a ; DOSDEV*16
+ rrca
+ rrca
+ rrca
+ rrca
+ ld h,a
+ ld l,0
+ ld (ix+14),l
+ ld de,(0xFD12) ; Address to DOSBUF1
+ add hl,de
+ ld (ix+8),l
+ ld (ix+9),h
+ ld (ix+10),l
+ ld (ix+11),h
+ ld (ix+13),252
+ ld (hl),3
+ pop de
+ pop hl
+ ret
+
+ ; Send a single byte
+send_byte:
+ call wait_busy
+ out (0),a
+ ret
+
+ ; Send a command header, A = command
+send_cmd:
+ push af
+ out (4),a ; Clear input FIFO
+
+ ld a,255
+ call send_byte
+ pop af
+ ld (ram_cmd),a
+ call send_byte
+ ld a,(ram_serial)
+ inc a
+ ld (ram_serial),a
+ call send_byte
+ push bc
+ push ix
+ pop bc
+ ld a,c
+ call send_byte
+ ld a,b
+ pop bc
+ jr send_byte
+
+ ; Send a buffer HL->data BC=count
+ ; On return HL advanced, BC=0, A clobbered
+send_buf:
+ ld a,b
+ or c
+ ret z
+ ld a,(hl)
+ call send_byte
+ dec bc
+ inc hl
+ jr send_buf
+
+ ; Receive a byte. Return with C flag on timeout.
+recv_byte:
+ push bc
+ ld b,0 ; 256/50 Hz = 5.12 s
+rb_ctr:
+ ld a,(65008)
+ ld c,a
+rb_loop:
+ in a,(1)
+ and 64
+ jr nz,rb_data
+ ld a,(65008)
+ cp c
+ jr z,rb_loop
+ djnz rb_ctr
+ pop bc
+ scf
+ ret
+rb_data:
+ in a,(0)
+ ; C flag is 0 already
+ pop bc
+ ret
+
+ ; Receive a reply header.
+ ; Error, if any, in A. ERR 51 if no reply
+recv_reply:
+ push bc
+ call recv_byte
+ jr c,rr_timeout
+ inc a
+ jr nz,recv_reply
+ call recv_byte
+ jr c,rr_timeout
+ ld bc,(ram_cmd)
+ cp c ; Command
+ jr nz,recv_reply
+ call recv_byte
+ jr c,rr_timeout
+ cp b ; Serial
+ jr nz,recv_reply
+ call recv_byte
+ jr nc,rr_done
+rr_timeout:
+ ld a,51+128
+rr_done:
+ pop bc
+ ret
+
+ram_devlst: equ 7B00h
+ram_select: equ 7B0Eh ; Previous select code
+ram_cmd: equ 7B0Fh ; Latest sent command
+ram_serial: equ 7B10h ; Latest serial number
+ram_dummy: equ 7B11h ; Scratch byte
+
+prab_init:
+ ld hl,prab_device
+ ld de,ram_devlst
+ ld bc,7*2
+ ldir
+ ld hl,(65034) ; Device list
+ ld (ram_devlst+7),hl
+ ld hl,ram_devlst
+ ld (65034),hl
+ call select
+ ld a,0xA9 ; CLOSE ALL NO REPLY
+ call send_cmd
+ jp done
+
+prab_device:
+ defw ram_devlst+7
+ defm "PRA"
+ defw pra_jptable
+ defw 0
+ defm "PRB"
+ defw prb_jptable
diff --git a/z80asm/BUGS b/z80asm/BUGS
new file mode 100644
index 0000000..a748101
--- /dev/null
+++ b/z80asm/BUGS
@@ -0,0 +1,3 @@
+known BUGS in shevek's Z80 assembler:
+- out (c),0
+- shift/rotate (i[xy]+d),[bcdehla]
diff --git a/z80asm/COPYING b/z80asm/COPYING
new file mode 100644
index 0000000..be85328
--- /dev/null
+++ b/z80asm/COPYING
@@ -0,0 +1,18 @@
+COPYING - Copyright information for z80asm, the assembler for the z80 by
+Bas Wijnen.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Version 2 of the GPL is included in the distribution in a file named GPL2.
diff --git a/z80asm/ChangeLog b/z80asm/ChangeLog
new file mode 100644
index 0000000..04ea7c1
--- /dev/null
+++ b/z80asm/ChangeLog
@@ -0,0 +1,169 @@
+2005-11-30 Jan Wilmans <jw@dds.nl>
+
+ * z80asm.c: Added unoffical syntax of ADD A,r as ADD r
+ * z80asm.c: Added unoffical syntax of SUB r as SUB A,r
+
+2005-11-30 Jan Wilmans <jw@dds.nl>
+
+ * z80asm.c: Added new seek command
+
+2005-11-29 Jan Wilmans <jw@dds.nl>
+
+ * z80asm.c: Added two extra verbosity levels.
+
+2005-11-13 Bas Wijnen <shevek@fmf.nl>
+
+ * z80asm.c: Run indent.
+
+2005-10-14 Bas Wijnen <shevek@fmf.nl>
+
+ * z80asm.c (struct reference, compute_ref, new_reference): Removed
+ copy of stack frame from struct reference.
+
+2005-09-14 Bas Wijnen <shevek@fmf.nl>
+
+ * z80asm.c (open_include_file, assemble): Open incbin'd files as
+ binary.
+
+2005-06-16 Bas Wijnen <shevek@fmf.nl>
+
+ * Makefile: Fix incorrect version.
+ * z80asm.c (enum mnemonic, mnemonics, assemble), examples/hello.asm:
+ Rename bininclude to incbin.
+ * z80asm.c (readcommand, assemble), examples/macro.asm: Allow labels
+ before endm and endif.
+
+2005-05-19 Bas Wijnen <shevek@fmf.nl>
+
+ * z80asm.c (struct reference, printerr, compute_ref, new_reference,
+ assemble): Added stack frame to reference.
+ * z80asm.c (labelfilename, parse_commandline): Remove output files
+ when compilation fails.
+ * z80asm.c (use_force, parse_commandline, assemble, main): Implement
+ --force.
+ * z80asm.c (parse_commandline): Make a.bin the default target.
+ * z80asm.c (check_label, rd_label, rd_value, rd_expr,
+ get_include_name, assemble): Give errors about junk at end of
+ line or expression.
+ * z80asm.c (rd_value): Support double quotes for character constants.
+ * z80asm.c (rd_expr_equal): Support = for equality.
+ * z80asm.1: Updated manpage.
+
+2005-05-18 Bas Wijnen <shevek@fmf.nl>
+
+ * z80asm.c (enum mnemonic, enum reftype, struct reference, struct
+ label, struct stack, struct macro_arg, struct macro_line, struct
+ macro, mnemonics, firstmacro, define_macro, sp, stack, readlabel,
+ rd_expr, compute_ref, check_label, rd_label, rd_value, rd_factor,
+ rd_term, rd_expr_shift, rd_expr_unequal, rd_expr_equal, rd_expr_and,
+ rd_expr_xor, rd_expr_or, new_reference, wrt_ref, read_line,
+ get_macro_args, assemble): Added macro support.
+ * z80asm.c (rd_value): Added several radix notations.
+ * examples/macro.asm: New file.
+
+2005-05-17 Bas Wijnen <shevek@fmf.nl>
+
+ * Makefile (clean): Clean examples and headers.
+ * Makefile (dist): Make versioned tarballs.
+ * z80asm.c (buffer, try_use_real_file, flush_to_real_file,
+ parse_commandline, assemble): Generalise writing to non-seekable
+ files.
+ * z80asm.c (reallistfile, parse_commandline, assemble): Use it for
+ list file.
+ * z80asm.c (read_line, assemble): Remove arbitrary line length limit.
+
+2005-05-13 Bas Wijnen <shevek@fmf.nl>
+
+ * examples/Makefile, examples/hello.asm, headers/msx-bios.asm,
+ headers/msx2-bios.asm, headers/msx2+-bios.asm,
+ headers/msxturbor-bios.asm: New files.
+ * z80asm.c (struct includedir, firstincludedir, open_include_file,
+ add_include, parse_commandline, assemble, main): Use include path.
+ * z80asm (parse_commandline): Make stderr default for list and label
+ files.
+ * z80asm.c (enum mnemonic, mnemonics, get_include_name, assemble): Add
+ bininclude directive.
+ * z80asm.c (printerr, assemble): Make error output parsable.
+ * z80asm.1: Updated.
+
+2005-05-11 Bas Wijnen <shevek@fmf.nl>
+
+ * Makefile (top level, dist): Use versions instead of dates.
+ * VERSION: New file.
+ * BUGS, NEWS, README: Removed.
+
+2005-05-10 Bas Wijnen <shevek@fmf.nl>
+
+ * z80asm.1: New file.
+ * z80asm.c (enum mnemonic, mnemonic, assemble): Support END directive.
+ * z80asm.c (compare, indx, readlabel): Use strncasecmp instead of
+ compare.
+ * z80asm.c (assemble): Fix bug with strings in DEFB.
+ * z80asm.c (assemble): Allow backslash-escapes in DEFB strings.
+ * z80asm.c (rd_character): Allow octal escapes in strings.
+
+2005-03-11 Bas Wijnen <shevek@fmf.nl>
+
+ * z80asm.c (rd_r): Fixed index prefix.
+ * z80asm.c (assemble): Fixed register count errors.
+
+2004-10-01 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * z80asm (rd_comma): Fixed possible buffer overflow.
+ * z80asm (parse_commandline): Improved --help output.
+ * z80asm (new_reference): Removed useless ++.
+ * z80asm (wrt_ref, assemble): Improved list output for ds.
+
+2004-10-01 H. Peter Anvin <hpa@users.sourceforge.net>
+
+ * z80asm.c (enum mnemonic, mnemonics, assemble): Added dm/defm as an
+ alias for db/defb.
+ * README: Updated documentation accordingly.
+ * z80asm.c (write_one_byte, wrtb, new_reference, wrt_ref, assemble):
+ Fixed bug regarding addr increments.
+
+2004-09-29 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * z80asm.c: added global variable labelprefix.
+ * z80asm.c (parse_commandline, assemble): Support prefixing labels.
+ * z80asm.c (assemble): fixed bug in label output.
+
+2004-09-29 H. Peter Anvin <hpa@users.sourceforge.net>
+
+ * Makefile: Fixed "make clean", removed -Werror for cygwin.
+ * z80asm.c (rd_out): implemented out (c),0.
+
+2004-09-28 H. Peter Anvin <hpa@users.sourceforge.net>
+
+ * z80asm.c (rd_number): Added ouput parameter endptr. Changed all
+ callers.
+ * z80asm.c (rd_value): Added support for Zilog-style base
+ specification.
+ * README: Updated documentation.
+
+2004-09-22 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * z80asm.c (assemble): Bugfixes to make ld (nn),a and ld sp,nn work.
+
+2003-11-19 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * z80asm.c: Changed expression handling, bugfixes.
+
+2003-10-30 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * z80asm.c: Added binary output as hex to listfile.
+
+2003-10-30 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * z80asm.c: Added support for quoted strings in defb, added comments,
+ added long option support and help/version information.
+ * README: Updated documentation.
+
+2002-05-15 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * z80asm.c (rd_expr): Added equations in expr and bugfix
+ (assemble): Added if/else/endif
+
+2002-04-28 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * assembler.c: Started changelog
diff --git a/z80asm/GPL2 b/z80asm/GPL2
new file mode 100644
index 0000000..a52b16e
--- /dev/null
+++ b/z80asm/GPL2
@@ -0,0 +1,341 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/z80asm/INSTALL b/z80asm/INSTALL
new file mode 100644
index 0000000..94f5637
--- /dev/null
+++ b/z80asm/INSTALL
@@ -0,0 +1,9 @@
+How to install shevek's Z80 assembler
+
+To compile:
+go to the source directory
+type ``make'' (without the quotes)
+On some systems, you may need gmake
+
+To install, copy the file z80asm into your search path, for example in
+/usr/local/bin or in ~/bin (you may need to put this in the search path)
diff --git a/z80asm/Makefile b/z80asm/Makefile
new file mode 100644
index 0000000..b022c61
--- /dev/null
+++ b/z80asm/Makefile
@@ -0,0 +1,54 @@
+# Makefile for the Z80 assembler by shevek
+# Copyright (C) 2002-2005 Bas Wijnen
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+CC = gcc
+LDFLAGS = -O2 -Wall
+CFLAGS = -O2 -Wall -Wwrite-strings -Wcast-qual -Wcast-align \
+ -Wstrict-prototypes -Wmissing-prototypes \
+ -Wmissing-declarations -Wredundant-decls -Wnested-externs \
+ -Winline -Wshadow -g -W -Ignulib
+VERSION ?= $(shell echo -n `cat VERSION | cut -d. -f1`. ; echo $$[`cat VERSION | cut -d. -f2` + 1])
+ifneq (,$(findstring _NT,$(shell uname -s)))
+O = obj
+X = .exe
+else
+O = o
+X =
+endif
+
+all: z80asm$(X)
+
+z80asm$(X): z80asm.c Makefile gnulib/getopt.$(O) gnulib/getopt1.$(O)
+ $(CC) $(CFLAGS) $(LDFLAGS) -DVERSION=\"$(shell cat VERSION)\" \
+ $< gnulib/getopt.$(O) gnulib/getopt1.$(O) -o $@
+
+gnulib/%.$(O): gnulib/%.c gnulib/getopt.h Makefile
+ $(CC) $(CFLAGS) -c $< -o $@
+
+clean:
+ for i in . gnulib examples headers ; do \
+ rm -f $$i/core $$i/*~ $$i/\#* $$i/*.$(O) $$i/*.rom ; \
+ done
+ rm -f z80asm$(X) z80asm.exe
+
+dist: clean
+ echo $(VERSION) > VERSION
+ rm -rf /tmp/z80asm-$(VERSION) /tmp/z80asm
+ tar cf - -C .. z80asm | tar xf - -C /tmp
+ find /tmp/z80asm -name CVS | xargs rm -rf
+ mv /tmp/z80asm /tmp/z80asm-$(VERSION)
+ tar cvzf ../z80asm-$(VERSION).tar.gz -C /tmp z80asm-$(VERSION)
diff --git a/z80asm/NEWS b/z80asm/NEWS
new file mode 100644
index 0000000..2fb5ecf
--- /dev/null
+++ b/z80asm/NEWS
@@ -0,0 +1,4 @@
+List files now include the output in hex.
+defb/db accepts quoted strings in double quotes.
+
+See README for a (hopefully complete) list of features
diff --git a/z80asm/README b/z80asm/README
new file mode 100644
index 0000000..d1570e5
--- /dev/null
+++ b/z80asm/README
@@ -0,0 +1,100 @@
+README for shevek's Z80 assembler
+
+See INSTALL for installation instructions
+
+The assembler works like gcc. You need an external editor to create your
+source file. Then you assemble it to a z80 binary with this assembler.
+
+By default, the assembler uses stdin as its only input file and stdout as
+its output file. Any arguments with no flags are taken as input files.
+If more than one input file is specified, the files are concatenated.
+The following flags are supported:
+
+-h --help give a help text
+
+-V --version give version information
+
+-v --verbose be verbose. specify more than once to be more verbose.
+ If you want verbosity about parsing the commandline,
+ then this should be the the first argument.
+
+-l --list file specify file to write out the input, with lines on
+ which files are read and every source line prefixed
+ with the address. (list file)
+
+-L --label file specify file to write all labels to, in assembler
+ readable format (human readable as well). Every line is
+ of the form:
+ label: equ value
+ so it can be included in source.
+
+-i --input file specify an input file.
+
+-o --output file specify the output file.
+
+Some notes on the assembler directives and mnemonics:
+All text is case insensitive, except when quoted.
+Undocumented opcodes are as much as possible supported. There are some errors
+in the implementation, that are being worked on (see BUGS). Names for
+undocumented opcodes are:
+sll and sli are equal and can both be used.
+ixh, ixl, iyh and iyl can be used.
+
+Assembler directives are:
+defb and db arg,arg,arg,...
+defw and dw arg,arg,arg,...
+defs and ds count,value=0
+org address
+if expression
+else
+else
+...
+endif
+include 'file'
+ Note: the quotes around the file for include are
+ mandatory, but you can choose the quotes yourself.
+ eg, you may use % or even a letter as a quote.
+ The filename does not undergo any expansion, so
+ \, ~, $, etc are passed as written (which means ~
+ will not be your home directory.)
+
+defb/db can also take strings, when enclosed in double quotes:
+defb "This text should be in a buffer", 0
+
+All expressions can use the following operators, in order of precedence:
+(a, b and c denote subexpressions)
+a?b:c If a is not zero, return b, otherwise c
+a|b bitwise or
+a^b bitwise xor
+a&b bitwise and
+a==b a!=b equality
+a<=b a>=b a<b a>b inequality
+a<<b a>>b shift
+a+b a-b addition and subtraction
+a*b a/b a%b multiplication, division and modulo
+~a +a -a bitwise not, no effect and negation
+(a) parenthesis
+
+Literals in expressions may be written as: (case does not matter)
+14, 14d decimal number
+016, 16o, 16q octal number
+0xE, 0Eh hexadecimal number (first character must be 0-9)
+%1110, 1110b binary number
+@c11 base 13 number (specified by 'c' so c+1 == 10)
+'s' ASCII code of 's'
+$ address of first byte of current command
+
+In all expressions, labels may be used. However, there are some expressions
+where the value must be computable at once, and therefore only previously
+defined labels may be used. This is the case for:
+- The argument of org
+- The argument of equ (eg, a label definition)
+- The first argument of ds
+- The argument of if
+In all other expression, labels which are defined later may be used.
+
+Warning: parts that are not compiled because of an if statement are only
+checked to have a correct command. The argument is not parsed. This means
+that if the file passes through the assembler with no warnings or errors, it
+may still not assemble correctly in a different setting (where the if's
+give different results).
diff --git a/z80asm/TODO b/z80asm/TODO
new file mode 100644
index 0000000..0f2ea4d
--- /dev/null
+++ b/z80asm/TODO
@@ -0,0 +1,5 @@
+TODO for shevek's Z80 assembler:
+- make header files containing MSX system constants
+- a flag for warnings about unofficial instructions would be nice
+- an "export" assembler directive to specify the labels to be exported with -L
+- add internationalization support.
diff --git a/z80asm/gnulib/getopt.c b/z80asm/gnulib/getopt.c
new file mode 100644
index 0000000..1347f14
--- /dev/null
+++ b/z80asm/gnulib/getopt.c
@@ -0,0 +1,1199 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to drepper@gnu.org
+ before changing it!
+
+ Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
+ 1996, 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation,
+ Inc.
+
+ This file is part of the GNU C Library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+# include <gnu-versions.h>
+# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+# define ELIDE_CODE
+# endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+# include <stdlib.h>
+# include <unistd.h>
+#endif /* GNU C library. */
+
+#include <string.h>
+
+#ifdef VMS
+# include <unixlib.h>
+#endif
+
+#ifdef _LIBC
+# include <libintl.h>
+#else
+/* This is for other GNU distributions with internationalized messages. */
+# include "gettext.h"
+#endif
+#define _(msgid) gettext (msgid)
+
+#if defined _LIBC && defined USE_IN_LIBIO
+# include <wchar.h>
+#endif
+
+#ifndef attribute_hidden
+# define attribute_hidden
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+ causes problems with re-calling getopt as programs generally don't
+ know that. */
+
+int __getopt_initialized attribute_hidden;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Stored original parameters.
+ XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+extern int __libc_argc;
+extern char **__libc_argv;
+
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+# ifdef USE_NONOPTION_FLAGS
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+# endif
+
+# ifdef USE_NONOPTION_FLAGS
+# define SWAP_FLAGS(ch1, ch2) \
+ if (nonoption_flags_len > 0) \
+ { \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
+ }
+# else
+# define SWAP_FLAGS(ch1, ch2)
+# endif
+#else /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (char **argv)
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ /* First make sure the handling of the `__getopt_nonoption_flags'
+ string can work normally. Our top argument must be in the range
+ of the string. */
+ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+ {
+ /* We must extend the array. The user plays games with us and
+ presents new arguments. */
+ char *new_str = malloc (top + 1);
+ if (new_str == NULL)
+ nonoption_flags_len = nonoption_flags_max_len = 0;
+ else
+ {
+ memset (__mempcpy (new_str, __getopt_nonoption_flags,
+ nonoption_flags_max_len),
+ '\0', top + 1 - nonoption_flags_max_len);
+ nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
+ }
+#endif
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS (bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+static const char *
+_getopt_initialize (int argc, char *const *argv, const char *optstring)
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ if (posixly_correct == NULL
+ && argc == __libc_argc && argv == __libc_argv)
+ {
+ if (nonoption_flags_max_len == 0)
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = nonoption_flags_max_len = strlen (orig_str);
+ if (nonoption_flags_max_len < argc)
+ nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *) malloc (nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ nonoption_flags_max_len = -1;
+ else
+ memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+ '\0', nonoption_flags_max_len - len);
+ }
+ }
+ nonoption_flags_len = nonoption_flags_max_len;
+ }
+ else
+ nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns -1.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (int argc, char *const *argv,
+ const char *optstring, const struct option *longopts,
+ int *longind, int long_only)
+{
+ int print_errors = opterr;
+ if (optstring[0] == ':')
+ print_errors = 0;
+
+ if (argc < 1)
+ return -1;
+
+ optarg = NULL;
+
+ if (optind == 0 || !__getopt_initialized)
+ {
+ if (optind == 0)
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ optstring = _getopt_initialize (argc, argv, optstring);
+ __getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && __getopt_nonoption_flags[optind] == '1'))
+#else
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (last_nonopt > optind)
+ last_nonopt = optind;
+ if (first_nonopt > optind)
+ first_nonopt = optind;
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only
+ && (argv[optind][2] || !strchr (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else if (long_only
+ || pfound->has_arg != p->has_arg
+ || pfound->flag != p->flag
+ || pfound->val != p->val)
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]) >= 0)
+ {
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+#endif
+ }
+ nextchar += strlen (nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+ int n;
+#endif
+
+ if (argv[optind - 1][1] == '-')
+ {
+ /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("\
+%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#else
+ fprintf (stderr, _("\
+%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#endif
+ }
+ else
+ {
+ /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("\
+%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0],
+ pfound->name);
+#else
+ fprintf (stderr, _("\
+%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (n >= 0)
+ {
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#endif
+ }
+
+ nextchar += strlen (nextchar);
+
+ optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]) >= 0)
+ {
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+#endif
+ }
+ nextchar += strlen (nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || strchr (optstring, *nextchar) == NULL)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+ int n;
+#endif
+
+ if (argv[optind][1] == '-')
+ {
+ /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+#else
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+#endif
+ }
+ else
+ {
+ /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+#else
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (n >= 0)
+ {
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#endif
+ }
+ nextchar = (char *) "";
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = strchr (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+ int n;
+#endif
+
+ if (posixly_correct)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+#else
+ fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
+#endif
+ }
+ else
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+#else
+ fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (n >= 0)
+ {
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c) >= 0)
+ {
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]) >= 0)
+ {
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+#endif
+ }
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name) >= 0)
+ {
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#endif
+ }
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]) >= 0)
+ {
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+#endif
+ }
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option requires an argument -- %c\n"),
+ argv[0], c) >= 0)
+ {
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (int argc, char *const *argv, const char *optstring)
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (int argc, char **argv)
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/z80asm/gnulib/getopt.h b/z80asm/gnulib/getopt.h
new file mode 100644
index 0000000..d75535b
--- /dev/null
+++ b/z80asm/gnulib/getopt.h
@@ -0,0 +1,165 @@
+/* Declarations for getopt.
+
+ Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
+ 1999, 2001, 2003 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _GETOPT_H
+
+#ifndef __need_getopt
+# define _GETOPT_H 1
+#endif
+
+/* If __GNU_LIBRARY__ is not already defined, either we are being used
+ standalone, or this is the first header included in the source file.
+ If we are being used with glibc, we need to include <features.h>, but
+ that does not exist if we are standalone. So: if __GNU_LIBRARY__ is
+ not defined, include <ctype.h>, which will pull in <features.h> for us
+ if it's from glibc. (Why ctype.h? It's guaranteed to exist and it
+ doesn't flood the namespace with stuff the way some other headers do.) */
+#if !defined __GNU_LIBRARY__
+# include <ctype.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+#ifndef __need_getopt
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+ const char *name;
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+# define no_argument 0
+# define required_argument 1
+# define optional_argument 2
+#endif /* need getopt */
+
+
+/* Get definitions and prototypes for functions to process the
+ arguments in ARGV (ARGC of them, minus the program name) for
+ options given in OPTS.
+
+ Return the option character from OPTS just read. Return -1 when
+ there are no more options. For unrecognized options, or options
+ missing arguments, `optopt' is set to the option letter, and '?' is
+ returned.
+
+ The OPTS string is a list of characters which are recognized option
+ letters, optionally followed by colons, specifying that that letter
+ takes an argument, to be placed in `optarg'.
+
+ If a letter in OPTS is followed by two colons, its argument is
+ optional. This behavior is specific to the GNU `getopt'.
+
+ The argument `--' causes premature termination of argument
+ scanning, explicitly telling `getopt' that there are no more
+ options.
+
+ If OPTS begins with `--', then non-option arguments are treated as
+ arguments to the option '\0'. This behavior is specific to the GNU
+ `getopt'. */
+
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int ___argc, char *const *___argv, const char *__shortopts);
+
+#ifndef __need_getopt
+extern int getopt_long (int ___argc, char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+extern int getopt_long_only (int ___argc, char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int ___argc, char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind,
+ int __long_only);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Make sure we later can get all the definitions and declarations. */
+#undef __need_getopt
+
+#endif /* getopt.h */
diff --git a/z80asm/gnulib/getopt1.c b/z80asm/gnulib/getopt1.c
new file mode 100644
index 0000000..3288c72
--- /dev/null
+++ b/z80asm/gnulib/getopt1.c
@@ -0,0 +1,185 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+
+ Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996,
+ 1997, 1998, 2003 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef _LIBC
+# include <getopt.h>
+#else
+# include "getopt.h"
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (int argc,
+ char *const *argv,
+ const char *options,
+ const struct option *long_options,
+ int *opt_index)
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (int argc,
+ char *const *argv,
+ const char *options,
+ const struct option *long_options,
+ int *opt_index)
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+# ifdef _LIBC
+libc_hidden_def (getopt_long)
+libc_hidden_def (getopt_long_only)
+# endif
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/z80asm/gnulib/gettext.h b/z80asm/gnulib/gettext.h
new file mode 100644
index 0000000..835732e
--- /dev/null
+++ b/z80asm/gnulib/gettext.h
@@ -0,0 +1,68 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+ Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+/* NLS can be disabled through the configure --disable-nls option. */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions. */
+# include <libintl.h>
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+ chokes if dcgettext is defined as a macro. So include it now, to make
+ later inclusions of <locale.h> a NOP. We don't include <libintl.h>
+ as well because people using "gettext.h" will not include <libintl.h>,
+ and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+ is OK. */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Disabled NLS.
+ The casts to 'const char *' serve the purpose of producing warnings
+ for invalid uses of the value returned from these functions.
+ On pre-ANSI systems without 'const', the config.h file is supposed to
+ contain "#define const". */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+ extraction of messages, but does not call gettext(). The run-time
+ translation is done at a different place in the code.
+ The argument, String, should be a literal string. Concatenated strings
+ and other string expressions won't work.
+ The macro's expansion is not parenthesized, so that it is suitable as
+ initializer for static 'char[]' or 'const char[]' variables. */
+#define gettext_noop(String) String
+
+#endif /* _LIBGETTEXT_H */
diff --git a/z80asm/z80asm.c b/z80asm/z80asm.c
new file mode 100644
index 0000000..a681aac
--- /dev/null
+++ b/z80asm/z80asm.c
@@ -0,0 +1,3578 @@
+/* Z80 assembler by shevek
+
+ Copyright (C) 2002-2005 Bas Wijnen <shevek@fmf.nl>
+ Copyright (C) 2005 Jan Wilmans <jw@dds.nl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <getopt.h>
+#include <unistd.h>
+
+/* defines which are not function-specific */
+#ifndef BUFLEN
+#define BUFLEN 300 /* size of readbuffer for file i/o */
+#endif
+
+#ifndef MAX_INCLUDE
+#define MAX_INCLUDE 200 /* stack size for include command and macros */
+#endif
+
+/* types */
+/* mnemonics. THESE MUST BE IN THE SAME ORDER AS const char *mnemonic[]! */
+enum mnemonic
+{
+ CALL, CPDR, CPIR, DJNZ, HALT, INDR, INIR, LDDR, LDIR, OTDR, OTIR, OUTD,
+ OUTI, PUSH, RETI, RETN, RLCA, RRCA, DEFB, DEFW, DEFS, DEFM,
+ ADC, ADD, AND, BIT, CCF, CPD, CPI, CPL, DAA, DEC, EQU, EXX, INC, IND, INI,
+ LDD, LDI, NEG, NOP, OUT, POP, RES, RET, RLA, RLC, RLD, RRA, RRC, RRD, RST,
+ SBC, SCF, SET, SLA, SLL, SLI, SRA, SRL, SUB, XOR, ORG,
+ CP, DI, EI, EX, IM, IN, JP, JR, LD, OR, RL, RR, DB, DW, DS, DM,
+ INCLUDE, INCBIN, IF, ELSE, ENDIF, END, MACRO, ENDM, SEEK
+};
+
+/* types of reference */
+enum reftype
+{
+ TYPE_BSR, /* bit value (0-7) for bit, set and res */
+ TYPE_DS, /* ds reference (byte count and value) */
+ TYPE_RST, /* rst reference: val & 0x38 == val */
+ TYPE_ABSW, /* absolute word (2 bytes) */
+ TYPE_ABSB, /* absolute byte */
+ TYPE_RELB, /* relative byte */
+ TYPE_LABEL /* equ expression */
+};
+
+/* filetypes that can appear on the input. object files are on the todo list */
+enum filetype
+{
+ FILETYPE_ASM
+};
+
+/* labels (will be malloced) */
+struct label
+{
+ struct label *next, *prev; /* linked list */
+ int value; /* value */
+ int valid; /* if it is valid, or not yet computed */
+ int busy; /* if it is currently being computed */
+ struct reference *ref; /* mallocced memory to value for computation */
+ char name[1]; /* space with name in it */
+};
+
+/* files that were given on the commandline */
+struct infile
+{
+ const char *name;
+ enum filetype type;
+};
+
+/* filenames must be remembered for references */
+struct name
+{
+ struct name *next, *prev;
+ char name[1];
+};
+
+/* the include path */
+struct includedir
+{
+ struct includedir *next;
+ char name[1];
+};
+
+/* macro stuff */
+struct macro_arg
+{
+ struct macro_arg *next;
+ unsigned pos;
+ unsigned which;
+};
+
+struct macro_line
+{
+ struct macro_line *next;
+ char *line;
+ struct macro_arg *args;
+};
+
+struct macro
+{
+ struct macro *next;
+ char *name;
+ unsigned numargs;
+ char **args;
+ struct macro_line *lines;
+};
+
+/* elements on the context stack */
+struct stack
+{
+ const char *name; /* filename (for errors). may be malloced */
+ struct includedir *dir; /* directory where it comes from, if any */
+ FILE *file; /* the handle */
+ int line; /* the current line number (for errors) */
+ int shouldclose; /* if this file should be closed when done */
+ struct label *labels; /* local labels for this stack level */
+ /* if file is NULL, this is a macro entry */
+ struct macro *macro;
+ struct macro_line *macro_line;
+ char **macro_args; /* arguments given to the macro */
+};
+
+/* these structs will be malloced for each reference */
+struct reference
+{
+ struct reference *next, *prev;
+ enum reftype type; /* type of reference */
+ long oseekpos; /* position in outfile for data */
+ long lseekpos; /* position in listfile for data */
+ char delimiter; /* delimiter for parser */
+ int addr, line; /* address and line of reference */
+ int comma; /* comma when reference was set */
+ int count; /* only for ds: number of items */
+ int infile; /* index in infile[], current infile */
+ int done; /* if this reference has been computed */
+ int computed_value; /* value (only valid if done = true) */
+ int level; /* maximum stack level of labels to use */
+ char input[1]; /* variable size buffer containing formula */
+};
+
+/* global variables */
+/* mnemonics, used as argument to indx() in assemble */
+const char *mnemonics[] = {
+ "call", "cpdr", "cpir", "djnz", "halt", "indr", "inir", "lddr", "ldir",
+ "otdr", "otir", "outd", "outi", "push", "reti", "retn", "rlca", "rrca",
+ "defb", "defw", "defs", "defm",
+ "adc", "add", "and", "bit", "ccf", "cpd", "cpi", "cpl", "daa", "dec", "equ",
+ "exx", "inc", "ind", "ini", "ldd", "ldi", "neg", "nop", "out", "pop",
+ "res", "ret", "rla", "rlc", "rld", "rra", "rrc", "rrd", "rst", "sbc",
+ "scf", "set", "sla", "sll", "sli", "sra", "srl", "sub", "xor", "org",
+ "cp", "di", "ei", "ex", "im", "in", "jp", "jr", "ld", "or", "rl", "rr",
+ "db", "dw", "ds", "dm",
+ "include", "incbin", "if", "else", "endif", "end", "macro", "endm",
+ "seek", NULL
+};
+
+/* linked lists */
+static struct reference *firstreference = NULL;
+static struct label *firstlabel = NULL, *lastlabel = NULL;
+static struct name *firstname = NULL;
+static struct includedir *firstincludedir = NULL;
+static struct macro *firstmacro = NULL;
+
+/* files */
+static FILE *realoutputfile, *outfile, *reallistfile, *listfile, *labelfile;
+static const char *realoutputfilename;
+static const char *labelfilename;
+static struct infile *infile;
+/* prefix for labels in labelfile */
+static const char *labelprefix = "";
+/* bools to see if files are opened */
+static int havelist = 0, label = 0;
+/* number of infiles in array */
+static int infilecount;
+
+/* number of errors seen so far */
+static int errors = 0;
+
+/* current line, address and file */
+static int addr = 0, file;
+/* current number of characters in list file, for indentation */
+static int listdepth;
+
+/* use readbyte instead of (hl) if writebyte is true */
+static int writebyte;
+static const char *readbyte;
+/* variables which are filled by rd_* functions and used later,
+ * like readbyte */
+static const char *readword, *indexjmp, *bitsetres;
+
+/* 0, 0xdd or 0xfd depening on which index prefix should be given */
+static int indexed;
+
+/* increased for every -v option on the command line */
+static int verbose = 0;
+
+/* read commas after indx() if comma > 1. increase for every call */
+static int comma;
+
+/* address at start of line (for references) */
+static int baseaddr;
+
+/* set by readword and readbyte, used for new_reference */
+static char mem_delimiter;
+
+/* line currently being parsed */
+static char *buffer = NULL;
+
+/* if a macro is currently being defined */
+static int define_macro = 0;
+
+/* file (and macro) stack */
+static int sp;
+static struct stack stack[MAX_INCLUDE]; /* maximum level of includes */
+
+/* Produce output even with errors. */
+static int use_force = 0;
+
+/* print an error message, including current line and file */
+static void
+printerr (const char *fmt, ...)
+{
+ int msp = (sp < 0) ? 0 : sp;
+ va_list l;
+ va_start (l, fmt);
+ fprintf (stderr, "%s%s:%d: ", stack[msp].dir ? stack[msp].dir->name : "",
+ stack[msp].name, stack[msp].line);
+ vfprintf (stderr, fmt, l);
+ va_end (l);
+ errors++;
+}
+
+/* skip over spaces in string */
+static const char *
+delspc (const char *ptr)
+{
+ while (*ptr && isspace (*ptr))
+ ptr++;
+ if (*ptr == ';')
+ ptr = "";
+ return ptr;
+}
+
+/* read away a comma, error if there is none */
+static void
+rd_comma (const char **p)
+{
+ *p = delspc (*p);
+ if (**p != ',')
+ {
+ printerr ("`,' expected. Remainder of line: %s\n", *p);
+ return;
+ }
+ *p = delspc ((*p) + 1);
+}
+
+/* look ahead for a comma, no error if not found */
+static int
+has_argument (const char **p)
+{
+ const char *q = delspc (*p);
+ return (*q == ',');
+}
+
+/* During assembly, many literals are not parsed. Instead, they are saved
+ * until all labels are read. After that, they are parsed. This function
+ * is used during assembly, to find the place where the command continues. */
+static void
+skipword (const char **pos, char delimiter)
+{
+ int depth = 0;
+ char c;
+ while (1)
+ {
+ switch (c = (*((*pos)++)))
+ {
+ case '\0':
+ if (depth > 0)
+ {
+ printerr ("unexpected end of line\n");
+ }
+ (*pos)--;
+ return;
+ case '(':
+ depth++;
+ break;
+ case ')':
+ if (--depth < 0)
+ {
+ if (delimiter == ')')
+ return;
+ printerr ("unexpected `)'\n");
+ }
+ break;
+ default:
+ if (delimiter == c && depth == 0)
+ {
+ return;
+ }
+ }
+ }
+}
+
+/* callback function for argument parser, used to open output files. */
+static FILE *
+openfile (int *done, /* flag to check that a file is opened only once. */
+ const char *type, /* name of filetype for error message */
+ FILE * def, /* default value, in case "-" is specified */
+ const char *name, /* filename to open */
+ const char *flags) /* open flags */
+{
+ FILE *retval;
+ if (*done)
+ {
+ fprintf (stderr, "Error: more than one %s specified\n", type);
+ exit (1);
+ }
+ *done = 1;
+ if (def && (!name || (name[0] == '-' && name[1] == 0)))
+ {
+ return def;
+ }
+ if (!name || !name[0])
+ {
+ fprintf (stderr, "Error: no %s specified\n", type);
+ exit (1);
+ }
+ if (!(retval = fopen (name, flags)))
+ {
+ fprintf (stderr, "Unable to open %s %s: %s\n",
+ type, name, strerror (errno));
+ exit (1);
+ }
+ return retval;
+}
+
+/* open an included file, searching the path */
+static FILE *
+open_include_file (const char *name, struct includedir **dir,
+ const char *flags)
+{
+ FILE *result;
+ struct includedir *i;
+ /* always try the current directory first */
+ result = fopen (name, flags);
+ if (result)
+ {
+ if (dir)
+ *dir = NULL;
+ return result;
+ }
+ for (i = firstincludedir; i != NULL; i = i->next)
+ {
+ char *tmp = malloc (strlen (i->name) + strlen (name) + 1);
+ if (!tmp)
+ {
+ printerr ("not enough memory trying to open include file\n");
+ return NULL;
+ }
+ strcpy (tmp, i->name);
+ strcat (tmp, name);
+ result = fopen (tmp, flags);
+ free (tmp);
+ if (result)
+ {
+ if (dir)
+ *dir = i;
+ return result;
+ }
+ }
+ return NULL;
+}
+
+/* queue a file to be opened for reading */
+static void
+open_infile (const char *name)
+{
+ infile = realloc (infile, sizeof (struct infile) * (infilecount + 1));
+ if (!infile)
+ {
+ fprintf (stderr, "Error: insufficient memory\n");
+ exit (1);
+ }
+ /* only asm is currently supported */
+ infile[infilecount].type = FILETYPE_ASM;
+ infile[infilecount].name = name;
+ if (verbose >= 5)
+ fprintf (stderr, "queued inputfile %s\n", infile[infilecount].name);
+ infilecount++;
+}
+
+/* add a directory to the include search path */
+static void
+add_include (const char *name)
+{
+ struct includedir *i;
+ i = malloc (sizeof (struct includedir) + strlen (name) + 1);
+ if (!i)
+ {
+ fprintf (stderr, "Error: insufficient memory\n");
+ exit (1);
+ }
+ strcpy (i->name, name);
+ if (name[strlen (name) - 1] != '/')
+ strcat (i->name, "/");
+ i->next = firstincludedir;
+ firstincludedir = i;
+}
+
+static void
+try_use_real_file (FILE * real, FILE ** backup)
+{
+ fpos_t pos;
+ if (fgetpos (real, &pos) == 0)
+ {
+ *backup = real;
+ return;
+ }
+ if (!(*backup = tmpfile ()))
+ {
+ fprintf (stderr, "Error: Unable to open temporary file: %s\n",
+ strerror (errno));
+ exit (1);
+ }
+}
+
+static void
+flush_to_real_file (FILE * real, FILE * tmp)
+{
+ int l, size, len = 0;
+ char buf[BUFLEN];
+ if (tmp == real)
+ {
+ return;
+ }
+ rewind (tmp);
+ while (1)
+ {
+ clearerr (tmp);
+ errno = 0;
+ len = fread (buf, 1, BUFLEN, tmp);
+ if (len == 0 && feof (tmp))
+ break;
+ if (len <= 0)
+ {
+ fprintf (stderr, "error reading temp file: %s\n", strerror (errno));
+ exit (1);
+ }
+ l = 0;
+ while (l < len)
+ {
+ clearerr (real);
+ size = fwrite (&buf[l], 1, len - l, real);
+ if (size <= 0)
+ {
+ fprintf (stderr, "error writing final file: %s\n",
+ strerror (errno));
+ exit (1);
+ }
+ l += size;
+ }
+ }
+}
+
+/* parse commandline arguments */
+static void
+parse_commandline (int argc, char **argv)
+{
+ const struct option opts[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"list", optional_argument, NULL, 'l'},
+ {"label", optional_argument, NULL, 'L'},
+ {"input", required_argument, NULL, 'i'},
+ {"output", required_argument, NULL, 'o'},
+ {"label-prefix", required_argument, NULL, 'p'},
+ {"includepath", required_argument, NULL, 'I'},
+ {"force", no_argument, NULL, 'f'},
+ {NULL, 0, NULL, 0}
+ };
+ const char *short_opts = "hVvl:L:i:o:p:I:f";
+ int done = 0, i, out = 0;
+ infile = NULL;
+ while (!done)
+ {
+ switch (getopt_long (argc, argv, short_opts, opts, NULL))
+ {
+ case 'h':
+ /* split in two, to avoid too long string constant */
+ printf ("Usage: %s [options] [input files]\n"
+ "\n"
+ "Possible options are:\n"
+ "-h\t--help\t\tDisplay this help text and exit.\n"
+ "-V\t--version\tDisplay version information and exit.\n"
+ "-v\t--verbose\tBe verbose. "
+ "Specify again to be more verbose.\n"
+ "-l\t--list\t\tWrite a list file.\n"
+ "-L\t--label\t\tWrite a label file.\n", argv[0]);
+ printf ("-p\t--label-prefix\tprefix all labels with this prefix.\n"
+ "-i\t--input\t\tSpecify an input file (-i may be omitted).\n"
+ "-o\t--output\tSpecify the output file.\n"
+ "-I\t--includepath\tAdd a directory to the include path.\n"
+ "Please send bug reports and feature requests to "
+ "<shevek@fmf.nl>\n");
+ exit (0);
+ case 'V':
+ printf ("Z80 assembler version " VERSION "\n"
+ "Copyright (C) 2002-2005 Bas Wijnen "
+ "<shevek@fmf.nl>.\n"
+ "Copyright (C) 2005 Jan Wilmans "
+ "<jw@dds.nl>.\n"
+ "This program comes with ABSOLUTELY NO WARRANTY.\n"
+ "You may distribute copies of the program under the terms\n"
+ "of the GNU General Public License as published by\n"
+ "the Free Software Foundation; either version 2 of the\n"
+ "License, or (at your option) any later version.\n\n"
+ "For more information about these matters, see the file\n"
+ "named COPYING.\n");
+ exit (0);
+ case 'v':
+ verbose++;
+ if (verbose >= 5)
+ fprintf (stderr, "Verbosity increased to level %d\n", verbose);
+ break;
+ case 'o':
+ realoutputfile
+ = openfile (&out, "output file", stdout, optarg, "wb");
+ realoutputfilename = optarg;
+ if (verbose >= 5)
+ fprintf (stderr, "Opened outputfile\n");
+ break;
+ case 'i':
+ open_infile (optarg);
+ break;
+ case 'l':
+ reallistfile
+ = openfile (&havelist, "list file", stderr, optarg, "w");
+ if (verbose >= 5)
+ fprintf (stderr, "Opened list file\n");
+ break;
+ case 'L':
+ labelfile = openfile (&label, "label file", stderr, optarg, "w");
+ labelfilename = optarg;
+ if (verbose >= 5)
+ fprintf (stderr, "Opened label file\n");
+ break;
+ case 'p':
+ labelprefix = optarg;
+ break;
+ case 'I':
+ add_include (optarg);
+ break;
+ case 'f':
+ use_force = 1;
+ break;
+ case -1:
+ done = 1;
+ break;
+ default:
+ /* errors are handled by getopt_long */
+ break;
+ }
+ }
+ for (i = optind; i < argc; ++i)
+ open_infile (argv[i]);
+ if (!infilecount)
+ open_infile ("-");
+ if (!out)
+ realoutputfile = openfile (&out, "output file", stdout, "a.bin", "wb");
+ try_use_real_file (realoutputfile, &outfile);
+ if (havelist)
+ try_use_real_file (reallistfile, &listfile);
+}
+
+/* find any of the list[] entries as the start of ptr and return index */
+static int
+indx (const char **ptr, const char **list, int error)
+{
+ int i, l;
+ *ptr = delspc (*ptr);
+ if (!**ptr)
+ {
+ if (error)
+ {
+ printerr ("unexpected end of line\n");
+ return 0;
+ }
+ else
+ return 0;
+ }
+ if (comma > 1)
+ rd_comma (ptr);
+ for (i = 0; list[i]; i++)
+ {
+ l = strlen (list[i]);
+ if (list[i][0] && !strncasecmp (*ptr, list[i], l)
+ && (!isalnum ((*ptr)[l]) || !isalnum (list[i][l - 1])))
+ {
+ (*ptr) += l;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (0x%04x): Piece of code found:%s\n",
+ stack[sp].line, addr, list[i]);
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Remainder of line=%s.\n",
+ stack[sp].line, addr, *ptr);
+ comma++;
+ return i + 1;
+ }
+ }
+ if (error)
+ {
+ printerr ("parse error. Remainder of line=%s\n", *ptr);
+ if (verbose >= 3)
+ {
+ fprintf (stderr, "When looking for any of:\n");
+ for (i = 0; list[i]; i++)
+ fprintf (stderr, "%s\t", list[i]);
+ fprintf (stderr, "\n");
+ }
+ }
+ return 0;
+}
+
+/* read a mnemonic */
+static int
+readcommand (const char **p)
+{
+ return indx (p, mnemonics, 0);
+}
+
+static int rd_label (const char **p, int *exists, struct label **previous,
+ int level);
+
+/* try to read a label and optionally store it in the list */
+static void
+readlabel (const char **p, int store)
+{
+ const char *c, *d, *pos, *dummy;
+ int i, j;
+ struct label *buf, *previous, **thefirstlabel;
+ for (d = *p; *d && *d != ';'; ++d)
+ {
+ }
+ for (c = *p; !strchr (" \r\n\t", *c) && c < d; ++c)
+ {
+ }
+ pos = strchr (*p, ':');
+ if (!pos || pos >= c)
+ return;
+ if (pos == *p)
+ {
+ printerr ("`:' found without a label");
+ return;
+ }
+ if (!store)
+ {
+ *p = pos + 1;
+ return;
+ }
+ c = pos + 1;
+ dummy = *p;
+ j = rd_label (&dummy, &i, &previous, sp);
+ if (i || j)
+ {
+ printerr ("duplicate definition of label %s\n", *p);
+ *p = c;
+ return;
+ }
+ if (NULL == (buf = malloc (sizeof (struct label) + c - *p)))
+ {
+ printerr ("not enough memory to store label %s\n", *p);
+ *p = c;
+ return;
+ }
+ strncpy (buf->name, *p, c - *p - 1);
+ buf->name[c - *p - 1] = 0;
+ if (verbose >= 3)
+ fprintf (stderr, "%5d (0x%04x): Label found: %s\n", stack[sp].line,
+ addr, buf->name);
+ *p = c;
+ buf->value = addr;
+ lastlabel = buf;
+ if (buf->name[0] == '.')
+ thefirstlabel = &stack[sp].labels;
+ else
+ thefirstlabel = &firstlabel;
+ if (previous)
+ buf->next = previous->next;
+ else
+ buf->next = *thefirstlabel;
+ buf->prev = previous;
+ buf->valid = 1;
+ buf->busy = 0;
+ buf->ref = NULL;
+ if (buf->prev)
+ buf->prev->next = buf;
+ else
+ *thefirstlabel = buf;
+ if (buf->next)
+ buf->next->prev = buf;
+}
+
+static void new_reference (const char *data, int type, char delimiter,
+ int ds_count);
+
+/* write one byte to the outfile, and add it to the list file as well */
+static void
+write_one_byte (int b, int list)
+{
+ if (verbose >= 4)
+ fprintf (stderr,
+ "%5d (0x%04x): write_one_byte called with argument 0x%02x\n",
+ stack[sp].line, addr, b);
+ b &= 0xff;
+ putc (b, outfile);
+ if (list && havelist)
+ {
+ fprintf (listfile, " %02x", b);
+ listdepth += 3;
+ }
+ addr++;
+}
+
+/* write byte to outfile and possibly some index things as well */
+static void
+wrtb (int b)
+{
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (0x%04x): wrtb called with argument 0x%02x\n",
+ stack[sp].line, addr, b);
+ if (indexed)
+ {
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (0x%04x): writing indexed byte 0x%02x\n",
+ stack[sp].line, addr, indexed);
+ write_one_byte (indexed, 1);
+ indexed = 0;
+ }
+ if (writebyte)
+ {
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (0x%04x): using a xor on byte because there is "
+ "a writebyte.\n", stack[sp].line, addr);
+ b ^= 0x40;
+ }
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (0x%04x): writing byte 0x%02x\n", stack[sp].line,
+ addr, b);
+ if (bitsetres && b != 0xCB)
+ {
+ new_reference (bitsetres, TYPE_BSR, ',', b);
+ bitsetres = NULL;
+ }
+ else
+ {
+ write_one_byte (b, 1);
+ }
+ if (indexjmp)
+ {
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (0x%04x): Making reference for index/jump %s\n",
+ stack[sp].line, addr, indexjmp);
+ new_reference (indexjmp, TYPE_ABSB, ')', 1);
+ indexjmp = NULL;
+ }
+ if (writebyte)
+ {
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (0x%04x): writing argument byte for padding\n",
+ stack[sp].line, addr);
+ writebyte = 0;
+ new_reference (readbyte, TYPE_ABSB, mem_delimiter, 1);
+ }
+}
+
+/* reading expressions. The following operators are supported
+ * in order of precedence, with function name:
+ * expr?expr:expr rd_expr
+ * | rd_expr_or
+ * ^ rd_expr_xor
+ * & rd_expr_and
+ * == != rd_expr_equal
+ * >= <= > < rd_expr_unequal
+ * << >> rd_expr_shift
+ * + - (binary) rd_term
+ * * / % rd_factor
+ * ~ + - (unary) rd_factor
+ */
+
+static int
+rd_number (const char **p, const char **endp, int base)
+{
+ int result = 0, i;
+ char *c, num[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read number of base %d"
+ "(string=%s).\n", stack[sp].line, addr, base, *p);
+ num[base] = '\0';
+ *p = delspc (*p);
+ while (**p && (c = strchr (num, tolower (**p))))
+ {
+ i = c - num;
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): Digit found:%1x.\n", stack[sp].line,
+ addr, i);
+ result = result * base + i;
+ (*p)++;
+ }
+ if (endp)
+ *endp = *p;
+ *p = delspc (*p);
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_number returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ return result;
+}
+
+static int
+rd_otherbasenumber (const char **p)
+{
+ char c;
+ if (verbose >= 6)
+ fprintf (stderr,
+ "%5d (0x%04x): Starting to read basenumber (string=%s).\n",
+ stack[sp].line, addr, *p);
+ (*p)++;
+ if (!**p)
+ {
+ printerr ("unexpected end of line after `@'\n");
+ return 0;
+ }
+ if (**p == '0' || !isalnum (**p))
+ {
+ printerr ("base must be between 1 and z\n");
+ return 0;
+ }
+ c = **p;
+ (*p)++;
+ if (isalpha (**p))
+ return rd_number (p, NULL, tolower (c) - 'a' + 1);
+ return rd_number (p, NULL, c - '0' + 1);
+}
+
+static int
+rd_character (const char **p)
+{
+ int i;
+ if (verbose >= 6)
+ fprintf (stderr,
+ "%5d (0x%04x): Starting to read character (string=%s).\n",
+ stack[sp].line, addr, *p);
+ i = **p;
+ if (!i)
+ {
+ printerr ("unexpected end of line in string constant\n");
+ return 0;
+ }
+ if (i == '\\')
+ {
+ (*p)++;
+ if (**p >= '0' && **p <= '7')
+ {
+ int b, num_digits;
+ i = 0;
+ if ((*p)[1] >= '0' && (*p)[1] <= '7')
+ {
+ if (**p <= '3' && (*p)[2] >= '0' && (*p)[2] <= '7')
+ num_digits = 3;
+ else
+ num_digits = 2;
+ }
+ else
+ num_digits = 1;
+ for (b = 0; b < num_digits; ++b)
+ {
+ int bit = (*p)[num_digits - 1 - b] - '0';
+ i += (1 << (b * 3)) * bit;
+ }
+ *p += num_digits;
+ }
+ else
+ {
+ switch (**p)
+ {
+ case 'n':
+ i = 10;
+ break;
+ case 'r':
+ i = 13;
+ break;
+ case 't':
+ i = 9;
+ break;
+ case 'a':
+ i = 7;
+ break;
+ case '\'':
+ printerr ("empty literal character\n");
+ return 0;
+ case 0:
+ printerr ("unexpected end of line after "
+ "backslash in string constant\n");
+ return 0;
+ default:
+ i = **p;
+ }
+ (*p)++;
+ }
+ }
+ else
+ (*p)++;
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_character returned %d (%c).\n",
+ stack[sp].line, addr, i, i);
+ return i;
+}
+
+static int rd_expr (const char **p, char delimiter, int *valid, int level);
+
+static int
+compute_ref (struct reference *ref, int allow_invalid)
+{
+ const char *ptr;
+ int valid = 0;
+ int backup_addr = addr;
+ int backup_comma = comma;
+ int backup_file = file;
+ int backup_sp = sp;
+ sp = ref->level;
+ addr = ref->addr;
+ comma = ref->comma;
+ file = ref->infile;
+ if (verbose >= 3)
+ fprintf (stderr, "%5d (0x%04x): Making reference to %s (done=%d, "
+ "computed=%d)\n",
+ stack[sp].line, addr, ref->input, ref->done,
+ ref->computed_value);
+ ptr = ref->input;
+ if (!ref->done)
+ {
+ ref->computed_value = rd_expr (&ptr, ref->delimiter,
+ allow_invalid ? &valid : NULL,
+ ref->level);
+ if (valid)
+ ref->done = 1;
+ }
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (0x%04x): Reference is %d (0x%04x).\n",
+ stack[sp].line, addr, ref->computed_value, ref->computed_value);
+ sp = backup_sp;
+ addr = backup_addr;
+ comma = backup_comma;
+ file = backup_file;
+ return ref->computed_value;
+}
+
+static int
+check_label (struct label *labels, const char **p, struct label **ret,
+ struct label **previous, int force_skip)
+{
+ struct label *l;
+ const char *c;
+ unsigned s2;
+ *p = delspc (*p);
+ for (c = *p; isalnum (*c) || *c == '_' || *c == '.'; ++c)
+ {
+ }
+ s2 = c - *p;
+ for (l = labels; l; l = l->next)
+ {
+ unsigned s1, s;
+ int cmp;
+ s1 = strlen (l->name);
+ s = s1 < s2 ? s1 : s2;
+ cmp = strncmp (l->name, *p, s);
+ if (cmp > 0 || (cmp == 0 && s1 > s))
+ {
+ if (force_skip)
+ *p = c;
+ return 0;
+ }
+ if (cmp < 0 || s2 > s)
+ {
+ if (previous)
+ *previous = l;
+ continue;
+ }
+ *p = c;
+ /* if label is not valid, compute it */
+ if (l->ref)
+ {
+ compute_ref (l->ref, 1);
+ if (!l->ref->done)
+ {
+ /* label was not valid, and isn't computable. tell the
+ * caller that it doesn't exist, so it will try again later.
+ * Set ret to show actual existence. */
+ if (verbose >= 6)
+ fprintf (stderr,
+ "%5d (0x%04x): returning invalid label %s.\n",
+ stack[sp].line, addr, l->name);
+ *ret = l;
+ return 0;
+ }
+ }
+ *ret = l;
+ return 1;
+ }
+ if (force_skip)
+ *p = c;
+ return 0;
+}
+
+static int
+rd_label (const char **p, int *exists, struct label **previous, int level)
+{
+ struct label *l = NULL;
+ int s;
+ if (exists)
+ *exists = 0;
+ if (previous)
+ *previous = NULL;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read label (string=%s).\n",
+ stack[sp].line, addr, *p);
+ for (s = level; s >= 0; --s)
+ {
+ if (check_label (stack[s].labels, p, &l,
+ (**p == '.' && s == sp) ? previous : NULL, 0))
+ break;
+ }
+ if (s < 0)
+ {
+ /* not yet found */
+ const char *old_p = *p;
+ if (!check_label (firstlabel, p, &l, **p != '.' ? previous : NULL, 1))
+ {
+ /* label does not exist, or is invalid. This is an error if there
+ * is no existance check. */
+ if (!exists)
+ printerr ("using undefined label %.*s\n", *p - old_p, old_p);
+ /* Return a value to discriminate between non-existing and invalid */
+ if (verbose >= 7)
+ fprintf (stderr, "rd_label returns invalid value\n");
+ return l != NULL;
+ }
+ }
+ if (exists)
+ *exists = 1;
+ if (verbose >= 7)
+ fprintf (stderr, "rd_label returns valid value 0x%x\n", l->value);
+ return l->value;
+}
+
+static int
+rd_value (const char **p, int *valid, int level)
+{
+ int sign = 1, not = 0, base, v;
+ const char *p0, *p1, *p2;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read value (string=%s).\n",
+ stack[sp].line, addr, *p);
+ *p = delspc (*p);
+ while (**p && strchr ("+-~", **p))
+ {
+ if (**p == '-')
+ sign = -sign;
+ else if (**p == '~')
+ not = ~not;
+ (*p)++;
+ *p = delspc (*p);
+ }
+ base = 10; /* Default base for suffixless numbers */
+ switch (**p)
+ {
+ int exist, retval;
+ char quote;
+ case '(':
+ (*p)++;
+ retval = not ^ (sign * rd_expr (p, ')', valid, level));
+ ++*p;
+ return retval;
+ case '0':
+ if ((*p)[1] == 'x')
+ {
+ (*p) += 2;
+ return not ^ (sign * rd_number (p, NULL, 0x10));
+ }
+ base = 8; /* If first digit it 0, assume octal unless suffix */
+ /* fall through */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ p0 = *p;
+ rd_number (p, &p1, 36); /* Advance to end of numeric string */
+ p1--; /* Last character in numeric string */
+ switch (*p1)
+ {
+ case 'h':
+ case 'H':
+ base = 16;
+ break;
+ case 'b':
+ case 'B':
+ base = 2;
+ break;
+ case 'o':
+ case 'O':
+ case 'q':
+ case 'Q':
+ base = 8;
+ break;
+ case 'd':
+ case 'D':
+ base = 10;
+ break;
+ default: /* No suffix */
+ p1++;
+ break;
+ }
+ v = rd_number (&p0, &p2, base);
+ if (p1 != p2)
+ {
+ printerr ("invalid character in number: \'%c\'\n", *p2);
+ }
+ return not ^ (sign * v);
+ case '$':
+ ++*p;
+ *p = delspc (*p);
+ p0 = *p;
+ v = rd_number (&p0, &p2, 0x10);
+ if (p2 == *p)
+ {
+ v = baseaddr;
+ }
+ else
+ *p = p2;
+ return not ^ (sign * v);
+ case '%':
+ (*p)++;
+ return not ^ (sign * rd_number (p, NULL, 2));
+ case '\'':
+ case '"':
+ quote = **p;
+ ++*p;
+ retval = not ^ (sign * rd_character (p));
+ if (**p != quote)
+ {
+ printerr ("missing closing quote (%c)\n", quote);
+ return 0;
+ }
+ ++*p;
+ return retval;
+ case '@':
+ return not ^ (sign * rd_otherbasenumber (p));
+ case '?':
+ rd_label (p, &exist, NULL, level);
+ return not ^ (sign * exist);
+ case '&':
+ {
+ ++*p;
+ switch (**p)
+ {
+ case 'h':
+ case 'H':
+ base = 0x10;
+ break;
+ case 'o':
+ case 'O':
+ base = 010;
+ break;
+ case 'b':
+ case 'B':
+ base = 2;
+ break;
+ default:
+ printerr ("invalid literal starting with &%c\n", **p);
+ return 0;
+ }
+ ++*p;
+ return not ^ (sign * rd_number (p, NULL, base));
+ }
+ default:
+ return not ^ (sign * rd_label (p, valid, NULL, level));
+ }
+}
+
+static int
+rd_factor (const char **p, int *valid, int level)
+{
+ /* read a factor of an expression */
+ int result;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read factor (string=%s).\n",
+ stack[sp].line, addr, *p);
+ result = rd_value (p, valid, level);
+ *p = delspc (*p);
+ while (**p == '*' || **p == '/')
+ {
+ if (**p == '*')
+ {
+ (*p)++;
+ result *= rd_value (p, valid, level);
+ }
+ else if (**p == '/')
+ {
+ (*p)++;
+ result /= rd_value (p, valid, level);
+ }
+ *p = delspc (*p);
+ }
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_factor returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ return result;
+}
+
+static int
+rd_term (const char **p, int *valid, int level)
+{
+ /* read a term of an expression */
+ int result;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read term (string=%s).\n",
+ stack[sp].line, addr, *p);
+ result = rd_factor (p, valid, level);
+ *p = delspc (*p);
+ while (**p == '+' || **p == '-')
+ {
+ if (**p == '+')
+ {
+ (*p)++;
+ result += rd_factor (p, valid, level);
+ }
+ else if (**p == '-')
+ {
+ (*p)++;
+ result -= rd_factor (p, valid, level);
+ }
+ *p = delspc (*p);
+ }
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_term returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_shift (const char **p, int *valid, int level)
+{
+ int result;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read shift expression "
+ "(string=%s).\n", stack[sp].line, addr, *p);
+ result = rd_term (p, valid, level);
+ *p = delspc (*p);
+ while ((**p == '<' || **p == '>') && (*p)[1] == **p)
+ {
+ if (**p == '<')
+ {
+ (*p) += 2;
+ result <<= rd_term (p, valid, level);
+ }
+ else if (**p == '>')
+ {
+ (*p) += 2;
+ result >>= rd_term (p, valid, level);
+ }
+ *p = delspc (*p);
+ }
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_shift returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_unequal (const char **p, int *valid, int level)
+{
+ int result;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read "
+ "unequality expression (string=%s).\n", stack[sp].line, addr,
+ *p);
+ result = rd_expr_shift (p, valid, level);
+ *p = delspc (*p);
+ if (**p == '<' && (*p)[1] == '=')
+ {
+ (*p) += 2;
+ return result <= rd_expr_unequal (p, valid, level);
+ }
+ else if (**p == '>' && (*p)[1] == '=')
+ {
+ (*p) += 2;
+ return result >= rd_expr_unequal (p, valid, level);
+ }
+ if (**p == '<' && (*p)[1] != '<')
+ {
+ (*p)++;
+ return result < rd_expr_unequal (p, valid, level);
+ }
+ else if (**p == '>' && (*p)[1] != '>')
+ {
+ (*p)++;
+ return result > rd_expr_unequal (p, valid, level);
+ }
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_shift returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_equal (const char **p, int *valid, int level)
+{
+ int result;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read equality epression "
+ "(string=%s).\n", stack[sp].line, addr, *p);
+ result = rd_expr_unequal (p, valid, level);
+ *p = delspc (*p);
+ if (**p == '=')
+ {
+ ++*p;
+ if (**p == '=')
+ ++ * p;
+ return result == rd_expr_equal (p, valid, level);
+ }
+ else if (**p == '!' && (*p)[1] == '=')
+ {
+ (*p) += 2;
+ return result != rd_expr_equal (p, valid, level);
+ }
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_equal returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_and (const char **p, int *valid, int level)
+{
+ int result;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read and expression "
+ "(string=%s).\n", stack[sp].line, addr, *p);
+ result = rd_expr_equal (p, valid, level);
+ *p = delspc (*p);
+ if (**p == '&')
+ {
+ (*p)++;
+ result &= rd_expr_and (p, valid, level);
+ }
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_expr_and returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_xor (const char **p, int *valid, int level)
+{
+ int result;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read xor expression "
+ "(string=%s).\n", stack[sp].line, addr, *p);
+ result = rd_expr_and (p, valid, level);
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_expr_xor: rd_expr_and returned %d "
+ "(%04x).\n", stack[sp].line, addr, result, result);
+ *p = delspc (*p);
+ if (**p == '^')
+ {
+ (*p)++;
+ result ^= rd_expr_xor (p, valid, level);
+ }
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_expr_xor returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_or (const char **p, int *valid, int level)
+{
+ int result;
+ if (verbose >= 6)
+ fprintf (stderr, "%5d (0x%04x): Starting to read or expression "
+ "(string=%s).\n", stack[sp].line, addr, *p);
+ result = rd_expr_xor (p, valid, level);
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_expr_or: rd_expr_xor returned %d "
+ "(%04x).\n", stack[sp].line, addr, result, result);
+ *p = delspc (*p);
+ if (**p == '|')
+ {
+ (*p)++;
+ result |= rd_expr_or (p, valid, level);
+ }
+ if (verbose >= 7)
+ fprintf (stderr, "%5d (0x%04x): rd_expr_or returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr (const char **p, char delimiter, int *valid, int level)
+{
+ /* read an expression. delimiter can _not_ be '?' */
+ int result = 0;
+ if (verbose >= 6)
+ fprintf (stderr,
+ "%5d (0x%04x): Starting to read expression (string=%s).\n",
+ stack[sp].line, addr, *p);
+ if (valid)
+ *valid = 1;
+ *p = delspc (*p);
+ if (!**p || **p == delimiter)
+ {
+ printerr ("expression expected (not %s)\n", *p);
+ return 0;
+ }
+ result = rd_expr_or (p, valid, level);
+ *p = delspc (*p);
+ if (**p == '?')
+ {
+ (*p)++;
+ if (result)
+ {
+ result = rd_expr (p, ':', valid, level);
+ if (**p)
+ (*p)++;
+ rd_expr (p, delimiter, valid, level);
+ }
+ else
+ {
+ rd_expr (p, ':', valid, level);
+ if (**p)
+ (*p)++;
+ result = rd_expr (p, delimiter, valid, level);
+ }
+ }
+ *p = delspc (*p);
+ if (**p && **p != delimiter)
+ {
+ printerr ("ignoring junk at end of expression: %s\n", *p);
+ }
+ if (verbose >= 7)
+ {
+ fprintf (stderr, "%5d (0x%04x): rd_expr returned %d (%04x).\n",
+ stack[sp].line, addr, result, result);
+ if (valid && !*valid)
+ fprintf (stderr, "%5d (0x%04x): Returning invalid result.\n",
+ stack[sp].line, addr);
+ }
+ return result;
+}
+
+static void wrt_ref (int val, int type, int count);
+
+/* Create a new reference, to be resolved after assembling (so all labels are
+ * known.) */
+static void
+new_reference (const char *p, int type, char delimiter, int ds_count)
+{
+ struct reference *tmp = NULL;
+ long opos, lpos;
+ int valid, value;
+ const char *c;
+ c = p;
+ value = rd_expr (&c, delimiter, &valid, sp);
+ if (valid)
+ {
+ if (verbose >= 5)
+ {
+ fprintf (stderr, "%5d (0x%04x): Using calculated value %d (%x) "
+ "immediately.\n", stack[sp].line, addr, value, value);
+ }
+ }
+ else
+ {
+ /* the expression is not valid (yet), we need to make a real reference.
+ */
+ if (NULL == (tmp = malloc (sizeof (struct reference) + strlen (p))))
+ {
+ printerr ("unable to allocate memory for reference %s\n", p);
+ return;
+ }
+ opos = ftell (outfile);
+ lpos = havelist ? ftell (listfile) : 0;
+ if (verbose >= 3)
+ fprintf (stderr, "%5d (0x%04x): reference set to %s (delimiter=%c)\n",
+ stack[sp].line, addr, p, delimiter);
+ strcpy (tmp->input, p);
+ tmp->addr = baseaddr;
+ tmp->count = ds_count;
+ tmp->infile = file;
+ tmp->comma = comma;
+ tmp->oseekpos = opos;
+ tmp->lseekpos = lpos;
+ tmp->delimiter = delimiter;
+ tmp->type = type;
+ tmp->next = firstreference;
+ tmp->done = 0;
+ tmp->level = sp;
+ if (type != TYPE_LABEL)
+ {
+ if (firstreference)
+ firstreference->prev = tmp;
+ tmp->prev = NULL;
+ firstreference = tmp;
+ }
+ /* Dummy value which should not give warnings */
+ value = (type == TYPE_RELB) ? ds_count : 0;
+ }
+ if (type != TYPE_LABEL)
+ {
+ wrt_ref (value, type, ds_count);
+ }
+ else
+ {
+ lastlabel->ref = tmp;
+ lastlabel->valid = valid;
+ lastlabel->value = value;
+ }
+}
+
+/* write the last read word to file */
+static void
+write_word (void)
+{
+ new_reference (readword, TYPE_ABSW, mem_delimiter, 1);
+}
+
+/* write the last read byte to file (relative) */
+static void
+write_rel (void)
+{
+ new_reference (readbyte, TYPE_RELB, mem_delimiter, (addr + 1) & 0xffff);
+ writebyte = 0;
+}
+
+/* read a word from input and store it in readword. return 1 on success */
+static int
+rd_word (const char **p, char delimiter)
+{
+ *p = delspc (*p);
+ if (**p == 0)
+ return 0;
+ readword = *p;
+ mem_delimiter = delimiter;
+ skipword (p, delimiter);
+ return 1;
+}
+
+/* read a byte from input and store it in readbyte. return 1 on success */
+static int
+rd_byte (const char **p, char delimiter)
+{
+ *p = delspc (*p);
+ if (**p == 0)
+ return 0;
+ readbyte = *p;
+ writebyte = 1;
+ mem_delimiter = delimiter;
+ skipword (p, delimiter);
+ return 1;
+}
+
+/* read an indexjump byte (for (ix + nn) commands) */
+static void
+rd_index (const char **p)
+{
+ rd_byte (p, ')');
+ indexjmp = readbyte;
+ writebyte = 0;
+ return;
+}
+
+/* read an address from infile and put it in reference table.
+ * so that it will be written here afterwards */
+static void
+rd_wrt_addr (const char **p, char delimiter)
+{
+ if (!rd_word (p, delimiter))
+ return;
+ write_word ();
+}
+
+/* like rd_wrt_addr, but for a relative jump */
+static void
+rd_wrt_jr (const char **p, char delimiter)
+{
+ if (!rd_byte (p, delimiter))
+ return;
+ write_rel ();
+}
+
+/* read (SP), DE, or AF */
+static int
+rd_ex1 (const char **p)
+{
+#define DE 2
+#define AF 3
+ const char *list[] = { "(sp)", "de", "af", NULL };
+ return indx (p, list, 1);
+}
+
+/* read first argument of IN */
+static int
+rd_in (const char **p)
+{
+#define A 8
+ const char *list[] = { "b", "c", "d", "e", "h", "l", "f", "a", NULL };
+ return indx (p, list, 1);
+}
+
+/* read second argument of out (c),x */
+static int
+rd_out (const char **p)
+{
+ const char *list[] = { "b", "c", "d", "e", "h", "l", "0", "a", NULL };
+ return indx (p, list, 1);
+}
+
+/* read (c) or (nn) */
+static int
+rd_nnc (const char **p)
+{
+#define C 1
+ int i;
+ const char *list[] = { "(", NULL };
+ i = indx (p, list, 1);
+ if (!i)
+ return 0;
+ *p = delspc (*p);
+ if (tolower (**p) == 'c')
+ {
+ *p = delspc ((*p) + 1);
+ if (**p != ')')
+ {
+ printerr ("missing closing parenthesis\n");
+ return 0;
+ }
+ (*p)++;
+ return C;
+ }
+ if (tolower (**p) == 'a')
+ {
+ *p = delspc ((*p) + 1);
+ if (**p != ',')
+ {
+ printerr ("missing ','\n");
+ return 0;
+ }
+ *p = delspc ((*p) + 1);
+ }
+ rd_byte (p, ')');
+ writebyte = 0;
+ return 2;
+}
+
+/* read (C) */
+static int
+rd_c (const char **p)
+{
+ const char *list[] = { "(c)", "(bc)", NULL };
+ return indx (p, list, 1);
+}
+
+/* read a or hl */
+static int
+rd_a_hl (const char **p)
+{
+#define HL 2
+ const char *list[] = { "a", "hl", NULL };
+ return indx (p, list, 1);
+}
+
+/* read first argument of ld */
+static int
+rd_ld (const char **p)
+{
+#define ldBC 1
+#define ldDE 2
+#define ldHL 3
+#define ldSP 4
+#define ldIX 5
+#define ldIY 6
+#define ldB 7
+#define ldC 8
+#define ldD 9
+#define ldE 10
+#define ldH 11
+#define ldL 12
+#define ld_HL 13
+#define ldA 14
+#define ldI 15
+#define ldR 16
+#define ld_BC 17
+#define ld_DE 18
+#define ld_IX 19
+#define ld_IY 20
+#define ld_NN 21
+ int i;
+ const char *list[] = {
+ "ixh", "ixl", "iyh", "iyl", "bc", "de", "hl", "sp", "ix",
+ "iy", "b", "c", "d", "e", "h", "l", "(hl)", "a", "i",
+ "r", "(bc)", "(de)", "(ix", "(iy", "(", NULL
+ };
+ i = indx (p, list, 1);
+ if (!i)
+ return 0;
+ if (i <= 2)
+ {
+ indexed = 0xdd;
+ return ldH + (i == 2);
+ }
+ if (i <= 4)
+ {
+ indexed = 0xfd;
+ return ldH + (i == 4);
+ }
+ i -= 4;
+ if (i == ldIX || i == ldIY)
+ {
+ indexed = i == ldIX ? 0xDD : 0xFD;
+ return ldHL;
+ }
+ if (i == ld_IX || i == ld_IY)
+ {
+ rd_index (p);
+ indexed = i == ld_IX ? 0xDD : 0xFD;
+ return ld_HL;
+ }
+ if (i == ld_NN)
+ rd_word (p, ')');
+ return i;
+}
+
+/* read first argument of JP */
+static int
+rd_jp (const char **p)
+{
+ int i;
+ const char *list[] = {
+ "nz", "z", "nc", "c", "po", "pe", "p", "m", "(ix)", "(iy)",
+ "(hl)", NULL
+ };
+ i = indx (p, list, 0);
+ if (i < 9)
+ return i;
+ if (i == 11)
+ return -1;
+ indexed = 0xDD + 0x20 * (i - 9);
+ return -1;
+}
+
+/* read first argument of JR */
+static int
+rd_jr (const char **p)
+{
+ const char *list[] = { "nz", "z", "nc", "c", NULL };
+ return indx (p, list, 0);
+}
+
+/* read A */
+static int
+rd_a (const char **p)
+{
+ const char *list[] = { "a", NULL };
+ return indx (p, list, 1);
+}
+
+/* read bc,de,hl,af */
+static int
+rd_stack (const char **p)
+{
+ int i;
+ const char *list[] = { "bc", "de", "hl", "af", "ix", "iy", NULL };
+ i = indx (p, list, 1);
+ if (i < 5)
+ return i;
+ indexed = 0xDD + 0x20 * (i - 5);
+ return 3;
+}
+
+/* read a or hl(2) or i[xy](2) with variables set */
+static int
+rd_a_hlx (const char **p)
+{
+ int i;
+ const char *list[] = { "a", "hl", "ix", "iy", NULL };
+ i = indx (p, list, 1);
+ if (i < 2)
+ return i;
+ if (i == 2)
+ return 2;
+ indexed = 0xDD + 0x20 * (i - 3);
+ return 2;
+}
+
+/* read b,c,d,e,h,l,(hl),a,(ix+nn),(iy+nn),nn
+ * but now with extra hl or i[xy](15) for add-instruction
+ * and set variables accordingly */
+static int
+rd_r_add (const char **p)
+{
+#define addHL 15
+ int i;
+ const char *list[] = {
+ "ixl", "ixh", "iyl", "iyh", "b", "c", "d", "e", "h", "l",
+ "(hl)", "a", "(ix", "(iy", "hl", "ix", "iy", NULL
+ };
+ i = indx (p, list, 0);
+ if (!i) // not in list ? assume "nn"
+ {
+ rd_byte (p, '\0');
+ return 7;
+ }
+ if (i > 14) // 15,16,17
+ {
+ if (i > 15)
+ indexed = 0xDD + 0x20 * (i - 16);
+ return addHL;
+ }
+ if (i <= 4) // 8-bit access of ix/iy
+ {
+ indexed = 0xdd + 0x20 * (i > 2);
+ return 6 - (i & 1);
+ }
+ i -= 4;
+ if (i < 9)
+ return i;
+ indexed = 0xDD + 0x20 * (i - 9); // 16-bit access of ix/iy
+ rd_index (p);
+ return 7;
+}
+
+/* read bc,de,hl, or sp */
+static int
+rd_rr_ (const char **p)
+{
+ const char *list[] = { "bc", "de", "hl", "sp", NULL };
+ return indx (p, list, 1);
+}
+
+/* read bc,de,hl|ix|iy,sp. hl|ix|iy only if it is already indexed. */
+static int
+rd_rrxx (const char **p)
+{
+ const char *listx[] = { "bc", "de", "ix", "sp", NULL };
+ const char *listy[] = { "bc", "de", "iy", "sp", NULL };
+ const char *list[] = { "bc", "de", "hl", "sp", NULL };
+ switch (indexed)
+ {
+ case 0xDD:
+ return indx (p, listx, 1);
+ case 0xFD:
+ return indx (p, listy, 1);
+ default:
+ return indx (p, list, 1);
+ }
+}
+
+/* read b,c,d,e,h,l,(hl),a,(ix+nn),(iy+nn),nn
+ * and set variables accordingly */
+static int
+rd_r (const char **p)
+{
+ int i;
+ const char *list[] = {
+ "ixl", "ixh", "iyl", "iyh", "b", "c", "d", "e", "h", "l", "(hl)",
+ "a", "(ix", "(iy", NULL
+ };
+ i = indx (p, list, 0);
+ if (!i)
+ {
+ rd_byte (p, '\0');
+ return 7;
+ }
+ if (i <= 4)
+ {
+ indexed = 0xdd + 0x20 * (i > 2);
+ return 6 - (i & 1);
+ }
+ i -= 4;
+ if (i < 9)
+ return i;
+ indexed = 0xDD + 0x20 * (i - 9);
+ rd_index (p);
+ return 7;
+}
+
+/* like rd_r(), but without nn */
+static int
+rd_r_ (const char **p)
+{
+ int i;
+ const char *list[] = {
+ "b", "c", "d", "e", "h", "l", "(hl)", "a", "(ix", "(iy", NULL
+ };
+ i = indx (p, list, 1);
+ if (i < 9)
+ return i;
+ indexed = 0xDD + 0x20 * (i - 9);
+ rd_index (p);
+ return 7;
+}
+
+/* read a number from 0 to 7, for bit, set or res */
+static int
+rd_0_7 (const char **p)
+{
+ *p = delspc (*p);
+ if (**p == 0 || **p == ';')
+ return 0;
+ bitsetres = *p;
+ skipword (p, ',');
+ return 1;
+}
+
+/* read long condition. do not error if not found. */
+static int
+rd_cc (const char **p)
+{
+ const char *list[] = { "nz", "z", "nc", "c", "po", "pe", "p", "m", NULL };
+ return indx (p, list, 0);
+}
+
+/* read long or short register, */
+static int
+rd_r_rr (const char **p)
+{
+ int i;
+ const char *list[] = {
+ "iy", "ix", "sp", "hl", "de", "bc", "", "b", "c", "d", "e", "h",
+ "l", "(hl)", "a", "(ix", "(iy", NULL
+ };
+ i = indx (p, list, 1);
+ if (!i)
+ return 0;
+ if (i < 16 && i > 2)
+ return 7 - i;
+ if (i > 15)
+ {
+ rd_index (p);
+ indexed = 0xDD + (i - 16) * 0x20;
+ return -7;
+ }
+ indexed = 0xDD + (2 - i) * 0x20;
+ return 3;
+}
+
+/* read hl */
+static int
+rd_hl (const char **p)
+{
+ const char *list[] = { "hl", NULL };
+ return indx (p, list, 1);
+}
+
+/* read hl, ix, or iy */
+static int
+rd_hlx (const char **p)
+{
+ int i;
+ const char *list[] = { "hl", "ix", "iy", NULL };
+ i = indx (p, list, 1);
+ switch (i)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ default:
+ indexed = 0xDD + 0x20 * (i - 2);
+ return 1;
+ }
+}
+
+/* read af' */
+static int
+rd_af_ (const char **p)
+{
+ const char *list[] = { "af'", NULL };
+ return indx (p, list, 1);
+}
+
+/* read 0(1), 1(3), or 2(4) */
+static int
+rd_0_2 (const char **p)
+{
+ const char *list[] = { "0", "", "1", "2", NULL };
+ return indx (p, list, 1);
+}
+
+/* read argument of ld (hl), */
+static int
+rd_ld_hl (const char **p)
+{
+ int i;
+ const char *list[] = { "b", "c", "d", "e", "h", "l", "", "a", NULL };
+ i = indx (p, list, 0);
+ if (i)
+ return i;
+ rd_byte (p, '\0');
+ return 7;
+}
+
+/* read argument of ld (nnnn), */
+static int
+rd_ld_nn (const char **p)
+{
+#define ld_nnHL 5
+#define ld_nnA 6
+ int i;
+ const char *list[] = { "bc", "de", "", "sp", "hl", "a", "ix", "iy", NULL };
+ i = indx (p, list, 1);
+ if (i < 7)
+ return i;
+ indexed = 0xdd + 0x20 * (i == 8);
+ return ld_nnHL;
+}
+
+/* read argument of ld a, */
+static int
+rd_lda (const char **p)
+{
+#define A_I 9
+#define A_R 10
+#define A_NN 11
+ int i;
+ const char *list[] = {
+ "(sp)", "(iy", "(de)", "(bc)", "(ix", "b", "c", "d", "e", "h",
+ "l", "(hl)", "a", "i", "r", "(", NULL
+ };
+ i = indx (p, list, 0);
+ if (i == 2 || i == 5)
+ {
+ indexed = (i == 2) ? 0xFD : 0xDD;
+ rd_index (p);
+ return 7;
+ }
+ if (!i)
+ {
+ rd_byte (p, '\0');
+ return 7;
+ }
+ if (i == 16)
+ {
+ rd_word (p, ')');
+ }
+ return i - 5;
+}
+
+/* read argument of b|c|d|e|h|l */
+static int
+rd_ldbcdehla (const char **p)
+{
+ int i;
+ const char *list[] = {
+ "b", "c", "d", "e", "h", "l", "(hl)", "a", "(ix", "(iy", "ixh",
+ "ixl", "iyh", "iyl", NULL
+ };
+ i = indx (p, list, 0);
+ if (i > 10)
+ {
+ int x;
+ x = 0xdd + 0x20 * (i > 12);
+ if (indexed && indexed != x)
+ {
+ printerr ("illegal use of index registers\n");
+ return 0;
+ }
+ indexed = x;
+ return 6 - (i & 1);
+ }
+ if (i > 8)
+ {
+ if (indexed)
+ {
+ printerr ("illegal use of index registers\n");
+ return 0;
+ }
+ indexed = 0xDD + 0x20 * (i == 10);
+ rd_index (p);
+ return 7;
+ }
+ if (i)
+ return i;
+ rd_byte (p, '\0');
+ return 7;
+}
+
+/* read nnnn, or (nnnn) */
+static int
+rd_nn_nn (const char **p)
+{
+#define _NN 1
+ int i;
+ const char *list[] = { "(", NULL };
+ i = indx (p, list, 0);
+ if (i)
+ {
+ rd_word (p, ')');
+ return 1;
+ }
+ rd_word (p, '\0');
+ return 0;
+}
+
+/* read {HL|IX|IY},nnnn, or (nnnn) */
+static int
+rd_sp (const char **p)
+{
+#define SPNN 0
+#define SPHL 1
+ int i;
+ const char *list[] = { "(", "ix", "iy", "hl", NULL };
+ i = indx (p, list, 0);
+ switch (i)
+ {
+ case 0:
+ rd_word (p, '\0');
+ return 0;
+ case 1:
+ rd_word (p, ')');
+ return 2;
+ }
+ if (i == 4)
+ return 1;
+ indexed = 0xDD + 0x20 * (i - 2);
+ return 1;
+}
+
+/* write a reference after it has been computed */
+static void
+wrt_ref (int val, int type, int count)
+{
+ switch (type)
+ {
+ case TYPE_RST:
+ if ((val & 0x38) != val)
+ {
+ printerr ("incorrect RST value %d (0x%02x)\n", val, val);
+ return;
+ }
+ write_one_byte (val + 0xC7, 1);
+ return;
+ case TYPE_ABSW:
+ write_one_byte (val & 0xff, 1);
+ write_one_byte ((val >> 8) & 0xff, 1);
+ return;
+ case TYPE_ABSB:
+ write_one_byte (val & 0xff, 1);
+ return;
+ case TYPE_DS:
+ if (havelist)
+ {
+ fprintf (listfile, " 0x%02x...", val & 0xff);
+ listdepth += 6;
+ }
+ while (count--)
+ {
+ write_one_byte (val & 0xff, 0);
+ }
+ return;
+ case TYPE_BSR:
+ if (val & ~7)
+ {
+ printerr ("incorrect BIT/SET/RES value %d\n", val);
+ return;
+ }
+ write_one_byte (0x08 * val + count, 1);
+ return;
+ case TYPE_RELB:
+ val -= count;
+ if (val < -128 || val > 127)
+ {
+ printerr ("relative jump out of range (%d)\n", val);
+ }
+ write_one_byte (val & 0xff, 1);
+ return;
+ case TYPE_LABEL:
+ printerr ("bug in the assembler: trying to write label reference. "
+ "Please report.\n");
+ return;
+ }
+}
+
+static char *
+get_include_name (const char **ptr)
+{
+ int pos = 0;
+ char quote;
+ char *name;
+ *ptr = delspc (*ptr);
+ name = malloc (strlen (*ptr));
+ if (!name)
+ {
+ printerr ("unable to allocate memory for filename %.*s\n",
+ strlen (*ptr) - 1, *ptr);
+ return NULL;
+ }
+ if (!**ptr)
+ {
+ printerr ("include without filename\n");
+ free (name);
+ return NULL;
+ }
+ quote = *(*ptr)++;
+ while (**ptr != quote)
+ {
+ if (!**ptr)
+ {
+ printerr ("filename without closing quote (%c)\n", quote);
+ free (name);
+ return NULL;
+ }
+ name[pos++] = *(*ptr)++;
+ }
+ name[pos] = 0;
+ ++*ptr;
+ return name;
+}
+
+static int
+read_line (void)
+{
+ unsigned pos, newpos, size;
+ struct macro_arg *arg;
+ if (stack[sp].file)
+ {
+ FILE *f = stack[sp].file;
+ static char short_buffer[BUFLEN + 1];
+ if (buffer && buffer != short_buffer)
+ free (buffer);
+ buffer = NULL;
+ if (!fgets (short_buffer, BUFLEN + 1, f))
+ return 0;
+ if (strlen (short_buffer) < BUFLEN)
+ {
+ buffer = short_buffer;
+ return 1;
+ }
+ size = 2 * BUFLEN;
+ buffer = malloc (size + 1);
+ if (!buffer)
+ {
+ printerr ("out of memory reading line\n");
+ return 0;
+ }
+ memcpy (buffer, short_buffer, BUFLEN + 1);
+ while (1)
+ {
+ char *b;
+ if (!fgets (&buffer[size - BUFLEN], BUFLEN + 1, f)
+ || (buffer[strlen (buffer) - 1] == '\n'))
+ {
+ return 1;
+ }
+ size += BUFLEN;
+ b = realloc (buffer, size + 1);
+ if (!b)
+ {
+ printerr ("out of memory reading line\n");
+ return 0;
+ }
+ buffer = b;
+ }
+ }
+ /* macro line */
+ if (!stack[sp].macro_line)
+ {
+ unsigned i;
+ for (i = 0; i < stack[sp].macro->numargs; ++i)
+ free (stack[sp].macro_args[i]);
+ free (stack[sp].macro_args);
+ return 0;
+ }
+ size = strlen (stack[sp].macro_line->line) + 1;
+ for (arg = stack[sp].macro_line->args; arg; arg = arg->next)
+ size += strlen (stack[sp].macro_args[arg->which]);
+ buffer = malloc (size);
+ if (!buffer)
+ {
+ printerr ("out of memory\n");
+ return 0;
+ }
+ pos = 0;
+ newpos = 0;
+ for (arg = stack[sp].macro_line->args; arg; arg = arg->next)
+ {
+ memcpy (&buffer[newpos], &stack[sp].macro_line->line[pos],
+ arg->pos - pos);
+ newpos += arg->pos - pos;
+ strcpy (&buffer[newpos], stack[sp].macro_args[arg->which]);
+ newpos += strlen (stack[sp].macro_args[arg->which]);
+ pos = arg->pos + 1;
+ }
+ strcpy (&buffer[newpos], &stack[sp].macro_line->line[pos]);
+ stack[sp].macro_line = stack[sp].macro_line->next;
+ return 1;
+}
+
+static unsigned
+get_macro_args (const char **ptr, char ***ret_args, int allow_empty)
+{
+ unsigned numargs = 0;
+ *ret_args = NULL;
+ while (1)
+ {
+ char **args;
+ const char *c;
+ *ptr = delspc (*ptr);
+ if (!**ptr)
+ break;
+ c = *ptr;
+ for (; **ptr && !strchr (" \r\n\t,;", **ptr); ++*ptr)
+ {
+ }
+ if (*ptr == c && !allow_empty)
+ {
+ printerr ("empty macro argument\n");
+ break;
+ }
+ ++numargs;
+ args = realloc (*ret_args, sizeof (char *) * numargs);
+ if (!args)
+ {
+ printerr ("out of memory\n");
+ --numargs;
+ break;
+ }
+ *ret_args = args;
+ args[numargs - 1] = malloc (*ptr - c + 1);
+ if (!args[numargs - 1])
+ {
+ printerr ("out of memory\n");
+ --numargs;
+ break;
+ }
+ memcpy (args[numargs - 1], c, *ptr - c);
+ args[numargs - 1][*ptr - c] = 0;
+ }
+ return numargs;
+}
+
+/* do the actual work */
+static void
+assemble (void)
+{
+ int ifcount = 0, noifcount = 0;
+ const char *ptr;
+ struct label *l;
+ char *bufptr;
+ int r, s; /* registers */
+ /* continue assembling until the last input file is done */
+ for (file = 0; file < infilecount; ++file)
+ {
+ int file_ended = 0;
+ sp = 0; /* clear stack */
+ stack[sp].line = 0;
+ stack[sp].shouldclose = 0;
+ stack[sp].name = infile[file].name;
+ stack[sp].dir = NULL;
+ if (infile[file].name[0] == '-' && infile[file].name[1] == 0)
+ {
+ stack[sp].file = stdin;
+ }
+ else
+ {
+ stack[sp].file = fopen (infile[file].name, "r");
+ if (!stack[sp].file)
+ {
+ printerr ("unable to open %s. skipping\n", infile[file].name);
+ continue;
+ }
+ stack[sp].shouldclose = 1;
+ }
+ if (havelist)
+ fprintf (listfile, "# File %s\n", stack[sp].name);
+ if (buffer)
+ buffer[0] = 0;
+ /* loop until this source file is done */
+ while (1)
+ {
+ int cmd, cont = 1;
+ if (havelist)
+ {
+ if (buffer && buffer[0] != 0)
+ {
+ int i, tabs;
+ ptr = delspc (ptr);
+ if (*ptr != 0)
+ {
+ printerr ("ignoring junk at end of line: %s\n", ptr);
+ }
+ if (listdepth < 8)
+ tabs = 3;
+ else if (listdepth < 16)
+ tabs = 2;
+ else
+ tabs = 1;
+ for (i = 0; i < tabs; ++i)
+ fputc ('\t', listfile);
+ fprintf (listfile, "%s\n", buffer);
+ }
+ listdepth = 4;
+ }
+ /* throw away the rest of the file after end */
+ if (file_ended)
+ {
+ while (read_line ())
+ {
+ if (havelist)
+ fprintf (listfile, "\t\t\t%s\n", buffer);
+ }
+ file_ended = 0;
+ }
+ while (!read_line ())
+ {
+ struct reference *ref;
+ struct label *next;
+ if (verbose >= 6)
+ fprintf (stderr, "finished reading file %s\n",
+ stack[sp].name);
+ if (havelist)
+ {
+ if (stack[sp].file)
+ fprintf (listfile, "# End of file %s\n", stack[sp].name);
+ else
+ fprintf (listfile, "# End of macro %s\n", stack[sp].name);
+ }
+ if (stack[sp].shouldclose)
+ fclose (stack[sp].file);
+ /* the top of stack is about to be popped off, throwing all
+ * local labels out of scope. All references at this level
+ * which aren't computable are errors. */
+ for (ref = firstreference; ref; ref = ref->next)
+ {
+ compute_ref (ref, 1);
+ if (ref->level == sp)
+ --ref->level;
+ }
+ /* Ok, now junk all local labels of the top stack level */
+ for (l = stack[sp].labels; l; l = next)
+ {
+ next = l->next;
+ if (l->ref)
+ free (l->ref);
+ free (l);
+ }
+ stack[sp].labels = NULL;
+ if (!sp--)
+ {
+ cont = 0;
+ break;
+ }
+ }
+ if (!cont)
+ break; /* break to next source file */
+ if (havelist)
+ fprintf (listfile, "%04x", addr);
+ for (bufptr = buffer; (bufptr = strchr (bufptr, '\n'));)
+ *bufptr = ' ';
+ for (bufptr = buffer; (bufptr = strchr (bufptr, '\r'));)
+ *bufptr = ' ';
+ ptr = buffer;
+ lastlabel = NULL;
+ baseaddr = addr;
+ ++stack[sp].line;
+ ptr = delspc (ptr);
+ if (!*ptr)
+ continue;
+ if (!noifcount && !define_macro)
+ readlabel (&ptr, 1);
+ else
+ readlabel (&ptr, 0);
+ ptr = delspc (ptr);
+ if (!*ptr)
+ continue;
+ comma = 0;
+ indexed = 0;
+ indexjmp = 0;
+ writebyte = 0;
+ readbyte = 0;
+ readword = 0;
+ cmd = readcommand (&ptr) - 1;
+ if (noifcount)
+ {
+ switch (cmd)
+ {
+ case IF:
+ noifcount++;
+ break;
+ case ELSE:
+ if (noifcount == 1)
+ {
+ noifcount = 0;
+ ifcount++;
+ }
+ break;
+ case ENDIF:
+ noifcount--;
+ }
+ ptr = "";
+ continue;
+ }
+ if (define_macro)
+ {
+ char *newptr;
+ struct macro_line **current_line;
+ for (current_line = &firstmacro->lines; *current_line;
+ current_line = &(*current_line)->next)
+ {
+ }
+ *current_line = malloc (sizeof (struct macro_line));
+ if (!*current_line)
+ {
+ printerr ("out of memory\n");
+ continue;
+ }
+ (*current_line)->next = NULL;
+ (*current_line)->args = NULL;
+ (*current_line)->line = malloc (strlen (buffer) + 1);
+ if (!(*current_line)->line)
+ {
+ printerr ("out of memory\n");
+ free (*current_line);
+ *current_line = NULL;
+ continue;
+ }
+ ptr = buffer;
+ newptr = (*current_line)->line;
+ while (*ptr)
+ {
+ unsigned p;
+ struct macro_arg **last_arg = &(*current_line)->args;
+ for (p = 0; p < firstmacro->numargs; ++p)
+ {
+ if (strncmp (ptr, firstmacro->args[p],
+ strlen (firstmacro->args[p])) == 0)
+ {
+ struct macro_arg *newarg;
+ newarg = malloc (sizeof (struct macro_arg));
+ if (!newarg)
+ {
+ printerr ("out of memory\n");
+ break;
+ }
+ newarg->next = NULL;
+ *last_arg = newarg;
+ last_arg = &newarg->next;
+ newarg->pos = newptr - (*current_line)->line;
+ newarg->which = p;
+ /* leave one character so two macros following each
+ * other keep their order. */
+ ptr += strlen (firstmacro->args[p]) - 1;
+ break;
+ }
+ }
+ *newptr++ = *ptr++;
+ }
+ *newptr = 0;
+ if (verbose >= 7)
+ fprintf (stderr, "added line to macro (cmd = %d): %s\n", cmd,
+ (*current_line)->line);
+ if (cmd == ENDM)
+ define_macro = 0;
+ continue;
+ }
+ switch (cmd)
+ {
+ int i, have_quote;
+ case ADC:
+ if (!(r = rd_a_hl (&ptr)))
+ break;
+ if (r == HL)
+ {
+ if (!(r = rd_rr_ (&ptr)))
+ break;
+ wrtb (0xED);
+ wrtb (0x4A + 0x10 * --r);
+ break;
+ }
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0x88 + --r);
+ break;
+ case ADD:
+ if (!(r = rd_r_add (&ptr)))
+ break;
+ if (r == addHL)
+ {
+ if (!(r = rd_rrxx (&ptr)))
+ break;
+ wrtb (0x09 + 0x10 * --r); // ADD HL/IX/IY, qq
+ break;
+ }
+ if (has_argument (&ptr))
+ {
+ if (r != A)
+ {
+ printerr ("parse error before: %s\n", ptr);
+ break;
+ }
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0x80 + --r); // ADD A,r
+ break;
+ }
+ wrtb (0x80 + --r); // ADD r
+ break;
+ case AND:
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0xA0 + --r);
+ break;
+ case BIT:
+ if (!rd_0_7 (&ptr))
+ break;
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x40 + (r - 1));
+ break;
+ case CALL:
+ if (!(r = rd_cc (&ptr)))
+ {
+ wrtb (0xCD);
+ }
+ else
+ {
+ wrtb (0xC4 + 8 * --r);
+ rd_comma (&ptr);
+ }
+ rd_wrt_addr (&ptr, '\0');
+ break;
+ case CCF:
+ wrtb (0x3F);
+ break;
+ case CP:
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0xB8 + --r);
+ break;
+ case CPD:
+ wrtb (0xED);
+ wrtb (0xA9);
+ break;
+ case CPDR:
+ wrtb (0xED);
+ wrtb (0xB9);
+ break;
+ case CPI:
+ wrtb (0xED);
+ wrtb (0xA1);
+ break;
+ case CPIR:
+ wrtb (0xED);
+ wrtb (0xB1);
+ break;
+ case CPL:
+ wrtb (0x2F);
+ break;
+ case DAA:
+ wrtb (0x27);
+ break;
+ case DEC:
+ if (!(r = rd_r_rr (&ptr)))
+ break;
+ if (r < 0)
+ {
+ wrtb (0x05 - 8 * ++r);
+ break;
+ }
+ wrtb (0x0B + 0x10 * --r);
+ break;
+ case DI:
+ wrtb (0xF3);
+ break;
+ case DJNZ:
+ wrtb (0x10);
+ rd_wrt_jr (&ptr, '\0');
+ break;
+ case EI:
+ wrtb (0xFB);
+ break;
+ case EQU:
+ if (!lastlabel)
+ {
+ printerr ("EQU without label\n");
+ break;
+ }
+ new_reference (ptr, TYPE_LABEL, 0, 0);
+ if (verbose >= 4)
+ {
+ if (lastlabel->valid)
+ fprintf (stderr, "Assigned value %d to label %s.\n",
+ lastlabel->value, lastlabel->name);
+ else
+ fprintf (stderr,
+ "Scheduled label %s for later computation.\n",
+ lastlabel->name);
+ }
+ ptr = "";
+ break;
+ case EX:
+ if (!(r = rd_ex1 (&ptr)))
+ break;
+ switch (r)
+ {
+ case DE:
+ if (!rd_hl (&ptr))
+ break;
+ wrtb (0xEB);
+ break;
+ case AF:
+ if (!rd_af_ (&ptr))
+ break;
+ wrtb (0x08);
+ break;
+ default:
+ if (!rd_hlx (&ptr))
+ break;
+ wrtb (0xE3);
+ }
+ break;
+ case EXX:
+ wrtb (0xD9);
+ break;
+ case HALT:
+ wrtb (0x76);
+ break;
+ case IM:
+ if (!(r = rd_0_2 (&ptr)))
+ break;
+ wrtb (0xED);
+ wrtb (0x46 + 8 * --r);
+ break;
+ case IN:
+ if (!(r = rd_in (&ptr)))
+ break;
+ if (r == A)
+ {
+ const char *tmp;
+ if (!(r = rd_nnc (&ptr)))
+ break;
+ if (r == C)
+ {
+ wrtb (0xED);
+ wrtb (0x40 + 8 * (A - 1));
+ break;
+ }
+ tmp = readbyte;
+ wrtb (0xDB);
+ new_reference (tmp, TYPE_ABSB, ')', 1);
+ break;
+ }
+ if (!rd_c (&ptr))
+ break;
+ wrtb (0xED);
+ wrtb (0x40 + 8 * --r);
+ break;
+ case INC:
+ if (!(r = rd_r_rr (&ptr)))
+ break;
+ if (r < 0)
+ {
+ wrtb (0x04 - 8 * ++r);
+ break;
+ }
+ wrtb (0x03 + 0x10 * --r);
+ break;
+ case IND:
+ wrtb (0xED);
+ wrtb (0xAA);
+ break;
+ case INDR:
+ wrtb (0xED);
+ wrtb (0xBA);
+ break;
+ case INI:
+ wrtb (0xED);
+ wrtb (0xA2);
+ break;
+ case INIR:
+ wrtb (0xED);
+ wrtb (0xB2);
+ break;
+ case JP:
+ r = rd_jp (&ptr);
+ if (r < 0)
+ {
+ wrtb (0xE9);
+ break;
+ }
+ if (r == 0)
+ {
+ wrtb (0xC3);
+ }
+ else
+ {
+ wrtb (0xC2 + 8 * --r);
+ rd_comma (&ptr);
+ }
+ rd_wrt_addr (&ptr, '\0');
+ break;
+ case JR:
+ r = rd_jr (&ptr);
+ if (r)
+ rd_comma (&ptr);
+ wrtb (0x18 + 8 * r);
+ rd_wrt_jr (&ptr, '\0');
+ break;
+ case LD:
+ if (!(r = rd_ld (&ptr)))
+ break;
+ switch (r)
+ {
+ case ld_BC:
+ case ld_DE:
+ if (!rd_a (&ptr))
+ break;
+ wrtb (0x02 + 0x10 * (r == ld_DE));
+ break;
+ case ld_HL:
+ r = rd_ld_hl (&ptr);
+ wrtb (0x70 + --r);
+ break;
+ case ld_NN:
+ if (!(r = rd_ld_nn (&ptr)))
+ break;
+ if (r == ld_nnA || r == ld_nnHL)
+ {
+ wrtb (0x22 + 0x10 * (r == ld_nnA));
+ write_word ();
+ break;
+ }
+ wrtb (0xED);
+ wrtb (0x43 + 0x10 * --r);
+ write_word ();
+ break;
+ case ldA:
+ if (!(r = rd_lda (&ptr)))
+ break;
+ if (r == A_NN)
+ {
+ wrtb (0x3A);
+ write_word ();
+ break;
+ }
+ if (r == A_I || r == A_R)
+ {
+ wrtb (0xED);
+ wrtb (0x57 + 8 * (r == A_R));
+ break;
+ }
+ if (r < 0)
+ {
+ wrtb (0x0A - 0x10 * ++r);
+ break;
+ }
+ wrtb (0x78 + --r);
+ break;
+ case ldB:
+ case ldC:
+ case ldD:
+ case ldE:
+ case ldH:
+ case ldL:
+ if (!(s = rd_ldbcdehla (&ptr)))
+ break;
+ wrtb (0x40 + 0x08 * (r - 7) + (s - 1));
+ break;
+ case ldBC:
+ case ldDE:
+ s = rd_nn_nn (&ptr);
+ if (s == _NN)
+ {
+ wrtb (0xED);
+ wrtb (0x4B + 0x10 * (r == ldDE));
+ write_word ();
+ break;
+ }
+ wrtb (0x01 + (r == ldDE) * 0x10);
+ write_word ();
+ break;
+ case ldHL:
+ r = rd_nn_nn (&ptr);
+ wrtb (0x21 + (r == _NN) * 9);
+ write_word ();
+ break;
+ case ldI:
+ case ldR:
+ if (!rd_a (&ptr))
+ break;
+ wrtb (0xED);
+ wrtb (0x47 + 0x08 * (r == ldR));
+ break;
+ case ldSP:
+ r = rd_sp (&ptr);
+ if (r == SPHL)
+ {
+ wrtb (0xF9);
+ break;
+ }
+ if (r == SPNN)
+ {
+ wrtb (0x31);
+ write_word ();
+ break;
+ }
+ wrtb (0xED);
+ wrtb (0x7B);
+ write_word ();
+ break;
+ }
+ break;
+ case LDD:
+ wrtb (0xED);
+ wrtb (0xA8);
+ break;
+ case LDDR:
+ wrtb (0xED);
+ wrtb (0xB8);
+ break;
+ case LDI:
+ wrtb (0xED);
+ wrtb (0xA0);
+ break;
+ case LDIR:
+ wrtb (0xED);
+ wrtb (0xB0);
+ break;
+ case NEG:
+ wrtb (0xED);
+ wrtb (0x44);
+ break;
+ case NOP:
+ wrtb (0x00);
+ break;
+ case OR:
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0xB0 + --r);
+ break;
+ case OTDR:
+ wrtb (0xED);
+ wrtb (0xBB);
+ break;
+ case OTIR:
+ wrtb (0xED);
+ wrtb (0xB3);
+ break;
+ case OUT:
+ if (!(r = rd_nnc (&ptr)))
+ break;
+ if (r == C)
+ {
+ if (!(r = rd_out (&ptr)))
+ break;
+ wrtb (0xED);
+ wrtb (0x41 + 8 * --r);
+ break;
+ }
+ if (!rd_a (&ptr))
+ break;
+ {
+ const char *tmp = readbyte;
+ wrtb (0xD3);
+ new_reference (tmp, TYPE_ABSB, ')', 1);
+ }
+ break;
+ case OUTD:
+ wrtb (0xED);
+ wrtb (0xAB);
+ break;
+ case OUTI:
+ wrtb (0xED);
+ wrtb (0xA3);
+ break;
+ case POP:
+ if (!(r = rd_stack (&ptr)))
+ break;
+ wrtb (0xC1 + 0x10 * --r);
+ break;
+ case PUSH:
+ if (!(r = rd_stack (&ptr)))
+ break;
+ wrtb (0xC5 + 0x10 * --r);
+ break;
+ case RES:
+ if (!rd_0_7 (&ptr))
+ break;
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x80 + --r);
+ break;
+ case RET:
+ if (!(r = rd_cc (&ptr)))
+ {
+ wrtb (0xC9);
+ break;
+ }
+ wrtb (0xC0 + 8 * --r);
+ break;
+ case RETI:
+ wrtb (0xED);
+ wrtb (0x4D);
+ break;
+ case RETN:
+ wrtb (0xED);
+ wrtb (0x45);
+ break;
+ case RL:
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x10 + --r);
+ break;
+ case RLA:
+ wrtb (0x17);
+ break;
+ case RLC:
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x00 + --r);
+ break;
+ case RLCA:
+ wrtb (0x07);
+ break;
+ case RLD:
+ wrtb (0xED);
+ wrtb (0x6F);
+ break;
+ case RR:
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x18 + --r);
+ break;
+ case RRA:
+ wrtb (0x1F);
+ break;
+ case RRC:
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x08 + --r);
+ break;
+ case RRCA:
+ wrtb (0x0F);
+ break;
+ case RRD:
+ wrtb (0xED);
+ wrtb (0x67);
+ break;
+ case RST:
+ new_reference (ptr, TYPE_RST, '\0', 1);
+ ptr = "";
+ break;
+ case SBC:
+ if (!(r = rd_a_hl (&ptr)))
+ break;
+ if (r == HL)
+ {
+ if (!(r = rd_rr_ (&ptr)))
+ break;
+ wrtb (0xED);
+ wrtb (0x42 + 0x10 * --r);
+ break;
+ }
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0x98 + --r);
+ break;
+ case SCF:
+ wrtb (0x37);
+ break;
+ case SET:
+ if (!rd_0_7 (&ptr))
+ break;
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0xC0 + --r);
+ break;
+ case SLA:
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x20 + --r);
+ break;
+ case SLI:
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x30 + --r);
+ break;
+ case SRA:
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x28 + --r);
+ break;
+ case SRL:
+ if (!(r = rd_r_ (&ptr)))
+ break;
+ wrtb (0xCB);
+ wrtb (0x38 + --r);
+ break;
+ case SUB:
+ if (!(r = rd_r (&ptr)))
+ break;
+ if (has_argument (&ptr)) // SUB A,r ?
+ {
+ if (r != A)
+ {
+ printerr ("parse error before: %s\n", ptr);
+ break;
+ }
+ if (!(r = rd_r (&ptr)))
+ break;
+ }
+ wrtb (0x90 + --r);
+ break;
+ case XOR:
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0xA8 + --r);
+ break;
+ case DEFB:
+ case DB:
+ case DEFM:
+ case DM:
+ ptr = delspc (ptr);
+ have_quote = (*ptr == '"' || *ptr == '\'');
+ if (!have_quote && !rd_byte (&ptr, ','))
+ break;
+ do
+ {
+ if (have_quote)
+ {
+ int quote = *ptr;
+ if (listfile)
+ {
+ fprintf (listfile, " ..");
+ listdepth += 3;
+ }
+ ++ptr;
+ while (*ptr != quote)
+ {
+ write_one_byte (rd_character (&ptr), 0);
+ if (*ptr == 0)
+ {
+ printerr ("end of line in quoted " "string\n");
+ break;
+ }
+ }
+ if (!*ptr)
+ break;
+ ++ptr;
+ ptr = delspc (ptr);
+ if (!*ptr)
+ break;
+ if (*ptr++ != ',')
+ {
+ printerr ("expected end of line or ',' (not %c)\n",
+ ptr[-1]);
+ break;
+ }
+ ptr = delspc (ptr);
+ continue;
+ }
+ new_reference (readbyte, TYPE_ABSB, ',', 1);
+ ptr = delspc (ptr);
+ }
+ while ((have_quote = (*ptr == '"' || *ptr == '\''))
+ || rd_byte (&ptr, ','));
+ writebyte = 0;
+ break;
+ case DEFW:
+ case DW:
+ if (!(r = rd_word (&ptr, ',')))
+ break;
+ do
+ {
+ new_reference (readword, TYPE_ABSW, ',', 1);
+ }
+ while ((r = rd_word (&ptr, ',')));
+ break;
+ case DEFS:
+ case DS:
+ r = rd_expr (&ptr, ',', NULL, sp);
+ if (r < 0)
+ {
+ printerr ("ds should have its first argument >=0"
+ " (not -0x%x)\n", -r);
+ break;
+ }
+ ptr = delspc (ptr);
+ if (*ptr)
+ {
+ rd_comma (&ptr);
+ readbyte = 0;
+ rd_byte (&ptr, '\0');
+ writebyte = 0;
+ new_reference (readbyte, TYPE_DS, '\0', r);
+ break;
+ }
+ if (havelist)
+ {
+ fprintf (listfile, " 00...");
+ listdepth += 6;
+ }
+ for (i = 0; i < r; i++)
+ {
+ write_one_byte (0, 0);
+ }
+ break;
+ case END:
+ file_ended = 1;
+ break;
+ case ORG:
+ addr = rd_expr (&ptr, '\0', NULL, sp);
+ break;
+ case INCLUDE:
+ if (sp + 1 >= MAX_INCLUDE)
+ {
+ printerr ("stack overflow (circular include?)");
+ if (verbose >= 5)
+ {
+ int x;
+ fprintf (stderr, "Stack dump:\nframe line file\n");
+ for (x = 0; x < MAX_INCLUDE; ++x)
+ fprintf (stderr, "%5d %5d %s\n", x, stack[x].line,
+ stack[x].name);
+ }
+ break;
+ }
+ {
+ struct name *name;
+ char *nm = get_include_name (&ptr);
+ if (!nm)
+ break;
+ name = malloc (sizeof (struct name) + strlen (nm));
+ if (!name)
+ {
+ printerr ("out of memory while allocating name\n");
+ free (nm);
+ break;
+ }
+ strcpy (name->name, nm);
+ free (nm);
+ ++sp;
+ stack[sp].name = name->name;
+ stack[sp].shouldclose = 1;
+ stack[sp].line = 0;
+ stack[sp].file = open_include_file (name->name,
+ &stack[sp].dir, "r");
+ if (!stack[sp].file)
+ {
+ printerr ("unable to open file %s\n", name->name);
+ free (name);
+ --sp;
+ break;
+ }
+ name->next = firstname;
+ name->prev = NULL;
+ if (name->next)
+ name->next->prev = name;
+ firstname = name;
+ if (verbose >= 4)
+ fprintf (stderr, "Reading file %s\n", name->name);
+ }
+ break;
+ case INCBIN:
+ {
+ FILE *incfile;
+ char *name = get_include_name (&ptr);
+ if (!name)
+ break;
+ incfile = open_include_file (name, NULL, "rb");
+ if (!incfile)
+ {
+ printerr ("unable to open binary file %s\n", name);
+ free (name);
+ break;
+ }
+ while (1)
+ {
+ char filebuffer[4096];
+ size_t num = fread (filebuffer, 1, 4096, incfile);
+ if (num == 0)
+ break;
+ if (num != fwrite (filebuffer, 1, num, outfile))
+ {
+ printerr ("error including binary file %s: %s\n",
+ name, strerror (errno));
+ break;
+ }
+ addr += num;
+ }
+ fclose (incfile);
+ free (name);
+ break;
+ }
+ case IF:
+ if (rd_expr (&ptr, '\0', NULL, sp))
+ ifcount++;
+ else
+ noifcount++;
+ break;
+ case ELSE:
+ if (ifcount == 0)
+ {
+ printerr ("else without if\n");
+ break;
+ }
+ noifcount = 1;
+ ifcount--;
+ break;
+ case ENDIF:
+ if (noifcount == 0 && ifcount == 0)
+ {
+ printerr ("endif without if\n");
+ break;
+ }
+ if (noifcount)
+ noifcount--;
+ else
+ ifcount--;
+ break;
+ case MACRO:
+ if (!lastlabel)
+ {
+ printerr ("macro without label\n");
+ break;
+ }
+ if (define_macro)
+ {
+ printerr ("nested macro definition\n");
+ break;
+ }
+ {
+ struct macro *m;
+ for (m = firstmacro; m; m = m->next)
+ {
+ if (strcmp (m->name, lastlabel->name) == 0)
+ {
+ printerr ("duplicate macro definition\n");
+ break;
+ }
+ }
+ m = malloc (sizeof (struct macro));
+ if (!m)
+ {
+ printerr ("out of memory\n");
+ break;
+ }
+ m->name = malloc (strlen (lastlabel->name) + 1);
+ if (!m->name)
+ {
+ printerr ("out of memory\n");
+ free (m);
+ break;
+ }
+ strcpy (m->name, lastlabel->name);
+ if (lastlabel->prev)
+ lastlabel->prev->next = lastlabel->next;
+ else
+ firstlabel = lastlabel->next;
+ if (lastlabel->next)
+ lastlabel->next->prev = lastlabel->prev;
+ free (lastlabel);
+ m->next = firstmacro;
+ firstmacro = m;
+ m->lines = NULL;
+ m->numargs = get_macro_args (&ptr, &m->args, 0);
+ define_macro = 1;
+ }
+ break;
+ case ENDM:
+ if (stack[sp].file)
+ printerr ("endm outside macro definition\n");
+ break;
+ case SEEK:
+ {
+ unsigned int seekaddr = rd_expr (&ptr, '\0', NULL, sp);
+ if (verbose >= 2)
+ {
+ fprintf (stderr, "%s%s:%d: ",
+ stack[sp].dir ? stack[sp].dir->name : "",
+ stack[sp].name, stack[sp].line);
+ fprintf (stderr, "[Message] seeking to 0x%0X \n",
+ seekaddr);
+ }
+ fseek (outfile, seekaddr, SEEK_SET);
+ break;
+ }
+ default:
+ {
+ struct macro *m;
+ for (m = firstmacro; m; m = m->next)
+ {
+ if (strncmp (m->name, ptr, strlen (m->name)) == 0)
+ {
+ unsigned numargs;
+ if (sp + 1 >= MAX_INCLUDE)
+ {
+ printerr ("stack overflow (circular include?)\n");
+ if (verbose >= 5)
+ {
+ int x;
+ fprintf (stderr,
+ "Stack dump:\nframe line file\n");
+ for (x = 0; x < MAX_INCLUDE; ++x)
+ fprintf (stderr, "%5d %5d %s\n", x,
+ stack[x].line, stack[x].name);
+ }
+ break;
+ }
+ ++sp;
+ ptr += strlen (m->name);
+ numargs = get_macro_args (&ptr, &stack[sp].macro_args,
+ 1);
+ if (numargs != m->numargs)
+ {
+ unsigned a;
+ printerr ("invalid number of arguments for macro "
+ "(is %d, must be %d)\n", numargs,
+ m->numargs);
+ for (a = 0; a < numargs; ++a)
+ free (stack[sp].macro_args[a]);
+ free (stack[sp].macro_args);
+ break;
+ }
+ stack[sp].name = m->name;
+ stack[sp].file = NULL;
+ stack[sp].line = 0;
+ stack[sp].macro = m;
+ stack[sp].macro_line = m->lines;
+ stack[sp].shouldclose = 0;
+ stack[sp].dir = NULL;
+ break;
+ }
+ }
+ if (m)
+ break;
+ }
+ printerr ("command or comment expected (was %s)\n", ptr);
+ }
+ }
+ }
+ if (ifcount || noifcount)
+ {
+ printerr ("reached EOF at IF level %d\n", ifcount + noifcount);
+ }
+ if (havelist)
+ {
+ fprintf (listfile, "%04x\n", addr);
+ }
+ {
+ struct reference *next;
+ struct reference *tmp;
+ for (tmp = firstreference; tmp; tmp = next)
+ {
+ int ref;
+ next = tmp->next;
+ fseek (outfile, tmp->oseekpos, SEEK_SET);
+ if (havelist)
+ fseek (listfile, tmp->lseekpos, SEEK_SET);
+ ref = compute_ref (tmp, 0);
+ wrt_ref (ref, tmp->type, tmp->count);
+ free (tmp);
+ }
+ }
+ if (!errors || use_force)
+ {
+ flush_to_real_file (realoutputfile, outfile);
+ if (havelist)
+ flush_to_real_file (reallistfile, listfile);
+ }
+ /* write all labels */
+ if (label)
+ fseek (labelfile, 0, SEEK_END);
+ for (l = firstlabel; l; l = l->next)
+ {
+ if (l->ref)
+ {
+ compute_ref (l->ref, 0);
+ }
+ if (label)
+ {
+ fprintf (labelfile, "%s%s:\tequ $%04x\n", labelprefix, l->name,
+ l->value);
+ }
+ }
+ if (label)
+ fclose (labelfile);
+ while (firstlabel)
+ {
+ l = firstlabel->next;
+ free (firstlabel);
+ firstlabel = l;
+ }
+ fclose (outfile);
+ if (outfile != realoutputfile)
+ fclose (realoutputfile);
+ if (havelist)
+ {
+ fclose (listfile);
+ if (listfile != reallistfile && reallistfile != stderr)
+ fclose (reallistfile);
+ }
+ free (infile);
+}
+
+int
+main (int argc, char **argv)
+{
+ /* default include file location */
+ add_include ("/usr/share/z80asm/headers/");
+ parse_commandline (argc, argv);
+ if (verbose >= 1)
+ fprintf (stderr, "Assembling....\n");
+ assemble ();
+ if (errors)
+ {
+ if (errors == 1)
+ fprintf (stderr, "*** 1 error found ***\n");
+ else
+ fprintf (stderr, "*** %d errors found ***\n", errors);
+ if (realoutputfile == outfile && !use_force)
+ {
+ unlink (realoutputfilename);
+ unlink (labelfilename);
+ }
+ return 1;
+ }
+ else
+ {
+ if (verbose >= 1)
+ fprintf (stderr, "Assembly succesful.\n");
+ return 0;
+ }
+}