From 66501ef2fdbc8bb1aef2dedcdb149993f7834c6b Mon Sep 17 00:00:00 2001
From: Piotr Fusik
Date: Sun, 22 May 2005 14:28:58 +0200
Subject: [PATCH] xasm 3.0.0 release.
---
artistic.txt | 117 ++
doc/xasm.htm | 60 +-
t.bat | 2 -
www/index.html | 59 +-
xasm.asm | 3513 ------------------------------------------------
xasm.d | 3149 +++++++++++++++++++++++++++++++++++++++++++
6 files changed, 3341 insertions(+), 3559 deletions(-)
create mode 100644 artistic.txt
delete mode 100644 t.bat
delete mode 100644 xasm.asm
create mode 100644 xasm.d
diff --git a/artistic.txt b/artistic.txt
new file mode 100644
index 0000000..cd17757
--- /dev/null
+++ b/artistic.txt
@@ -0,0 +1,117 @@
+
+
+
+
+ The "Artistic License"
+
+ Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to make
+reasonable modifications.
+
+Definitions:
+
+ "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes
+ of the Copyright Holder as specified below.
+
+ "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ "You" is you, if you're thinking about copying or distributing
+ this Package.
+
+ "Reasonable copying fee" is whatever you can justify on the
+ basis of media cost, duplication charges, time of people involved,
+ and so on. (You will not be required to justify it to the
+ Copyright Holder, but only to the computing community at large
+ as a market that must bear the fee.)
+
+ "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item.
+ It also means that recipients of the item may redistribute it
+ under the same conditions they received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+derived from the Public Domain or from the Copyright Holder. A Package
+modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and
+when you changed that file, and provided that you do at least ONE of the
+following:
+
+ a) place your modifications in the Public Domain or otherwise make them
+ Freely Available, such as by posting said modifications to Usenet or
+ an equivalent medium, or placing the modifications on a major archive
+ site such as uunet.uu.net, or by allowing the Copyright Holder to include
+ your modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation or organization.
+
+ c) rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and provide
+ a separate manual page for each non-standard executable that clearly
+ documents how it differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library files,
+ together with instructions (in the manual page or equivalent) on where
+ to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of
+ the Package with your modifications.
+
+ c) give non-standard executables non-standard names, and clearly
+ document the differences in manual pages (or equivalent), together
+ with instructions on where to get the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package. You may charge any fee you choose for support of this
+Package. You may not charge a fee for this Package itself. However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you do not advertise this Package as a
+product of your own. You may embed this Package's interpreter within
+an executable of yours (by linking); this shall be construed as a mere
+form of aggregation, provided that the complete Standard Version of the
+interpreter is so embedded.
+
+6. The source code and object code supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whoever generated
+them, and may be sold commercially, and may be aggregated with this
+Package.
+
+7. Aggregation of this Package with a commercial distribution is always
+permitted provided that the use of this Package is embedded; that is,
+when no overt attempt is made to make this Package's interfaces visible
+to the end user of the commercial distribution. Such use shall not be
+construed as a distribution of this Package.
+
+8. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
diff --git a/doc/xasm.htm b/doc/xasm.htm
index bdf1eed..602708e 100644
--- a/doc/xasm.htm
+++ b/doc/xasm.htm
@@ -2,7 +2,7 @@
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-xasm 2.6.1
+xasm 3.0.0
@@ -14,9 +14,9 @@
xasm is a cross-assembler which generates code for the 6502
processor.
source is the name of the source file. If no filename extension
-is given, .ASX is appended. The default action (when invoked without
+is given, .asx is appended. The default action (when invoked without
options) is to assembly source, writing the result to a file with
-the .OBX extension.
+the .obx extension.
OPTIONS
- /c
@@ -29,14 +29,6 @@ Lines skipped due to a false condition are not listed by default.
to labels defined in the source file).
You may use several /d options to define many labels
from the command line.
-- /e
-- Enable setting environment variables pointing at the error location.
-With this option, xasm sets two environment variables:
-ERRFILE and ERRLINE.
-They may be used in a batch file to locate the error and set editor's
-insertion point on it. If there was no error, the variables point at the last
-issued warning. If no warning occured, they are removed from
-the environment.
- /i
- Disable listing included sources. Only main source file will be
listed.
@@ -50,7 +42,8 @@ You may use the null device (/o:nul) to generate no object file.
- /p
- Print fully qualified file names in listing and error messages.
This option is useful for the Code-Genie editor, which can jump to the error
-location only if the full path is given.
+location only if the full path is given.
+This option works only on Windows and is silently ignored on Linux.
- /q
- Suppress info messages.
Prevents xasm from printing its name and the summary (how many lines
@@ -63,17 +56,17 @@ to the listing.
- Warn of unused labels. A warning message will be issued for each label,
whose value is never used.
+Alternatively, you may use Unix-style options, for example:
+xasm -i -d DEBUG=1 -l listing.lst source.asx
+
SYNTAX
-Source files should be plain ASCII files. Although different line
-terminators are supported, CR/LF is recommended because it is the standard
-in the DOS/Windows environment. Lines must be no longer than 256
-characters. xasm is not case-sensitive, so you can mix upper-
-and lower-case for labels and instructions.
+Source files should be plain ASCII files. Supported are LF, CR, CR/LF
+and Atari line terminators. xasm is not case-sensitive, so you can mix
+upper- and lower-case for labels and instructions.
xasm is backward compatible with Quick Assembler.
-If you want to assembly QA sources with xasm, simply convert the text
-file to CR/LF terminators and replace ATASCII specific characters with their
-integer representation. You also have to change all OPT directives,
-but usually you only need to remove them.
+If you want to assembly QA sources with xasm, simply replace ATASCII
+specific characters with their integer representation. You also have to change
+all OPT directives, but usually you only need to remove them.
A label is a symbol that represents a 32-bit signed integer.
You can define a label by putting its name at the beginning of a line
(with no spaces before). If you do not use the EQU directive,
@@ -345,12 +338,15 @@ Examples of DTA:
Specifies another file to be included in the assembly as if the contents
of the referenced file appeared in place of the ICL statement.
The included file may contain other ICL statements.
-The .ASX extension is added if none given.
+The .asx extension is added if none given.
Examples:
icl 'macros.asx'
- icl 'c:\atari\xasm\fileio'
-
+ icl 'lib/fileio'
+
+Note: for portability, you should use only relative paths and slash
+as the separator. This guarantees that your sources will compile under Windows
+and Linux.
END - end assembling file
Remaining part of the file is not assembled. If this statement does
not occur, the assembler stops assembling when it encounters the end
@@ -560,6 +556,19 @@ in all 6502 commands and pseudo-commands, except for
cmd (z),0- = ldy #0 : cmd (z),y : dey
CHANGES
+Version 3.0.0 (2005-05-22)
+
Version 2.6.1 (2005-05-21)
- no more "Arithmetic overflow" and "Division by zero" errors when correctly
@@ -668,8 +677,7 @@ for generating smaller code
- origin setting not required until it is used
- Unix ($0a), Macintosh ($0d) and Atari ($9b) EOLs allowed in the source
- value of 'true' changed to 1
-- setting environment variables on error
-option
+- setting environment variables on error option
- assembling only if source newer than object option
- op-code extracting
- line repeating
diff --git a/t.bat b/t.bat
deleted file mode 100644
index 216150b..0000000
--- a/t.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-tasm xasm /m5 /t /z /l
-tlink xasm /x
diff --git a/www/index.html b/www/index.html
index 83d19f3..e3eb6c0 100644
--- a/www/index.html
+++ b/www/index.html
@@ -3,31 +3,30 @@
xasm
-
+
+
-
+
xasm.atari.org
-xasm is a 6502 cross-assembler for DOS/Windows.
+
xasm is a 6502 cross-assembler.
It is mainly targeted at Atari 8-bit computers,
but it can be also used for programming
Atari 2600/5200/Lynx, Commodore C64, Apple ][ and others.
xasm is freeware.
-Download version 2.6.0
-(22 KB, includes full documentation)
-xasm no longer includes the tools X-BOOT, X-LOAD and X-HEAD.
-If you really need them,
-download version 2.5.2 (38 KB).
-Target audience
-xasm is for people who are fluent in the 6502 assembly language.
-There is no sample code for beginners, no easy-to-use libraries.
-You are warned that programming 8-bit machines in the assembly language
-is a tedious work.
-There are several 6502 cross-assemblers to choose from and new ones
-still appear. Just search the web. Make sure to not overlook
-the cc65 compiler which includes
-a cross-assembler, portable across many host and target platforms.
+Version 3.0.0 is a complete rewrite from the x86 assembly language
+to the D programming language.
+xasm is now available for Linux.
Features
Although xasm does not support user-defined macros, it has a rich
set of built-in facilities.
@@ -71,8 +70,32 @@ do_line
Although xasm comes with no editor, the documentation
describes integration (syntax highlighting and single-keystroke compilation)
with a few general-purpose text editors. Here is a screenshot
-of the Code-Genie editor:
+of the Code-Genie editor:
+Download
+
+Links
+
Hosted by Atari Area
diff --git a/xasm.asm b/xasm.asm
deleted file mode 100644
index 52f8ebe..0000000
--- a/xasm.asm
+++ /dev/null
@@ -1,3513 +0,0 @@
-; xasm 2.6.1
-; by Piotr Fusik
-
-; - No more "Arithmetic overflow" and "Division by zero" errors when correctly using forward-referenced labels
-; (bug found by Marcin Lewandowski)
-; - The following now assembles:
-; ift 0
-; foo equ 1
-; ift foo
-; eif
-; eif
-; (bug found by Adrian Matoga)
-; - Issue errors for non-existing INC @ and DEC @
-; - Negative numbers fixed in the listing
-
-EXE = 1 ; EXE vs COM, not sure if COM still works
-COMPAK = 0 ; 1d00h, only if COM
-
-SET_WIN_TITLE = 0 ; modify title of the console window (lame!)
-
-LABELS_32 = 1 ; 32-bit labels
-FOUR_ZEROS_TO_SPACES = 1 ; make hex representation more readable - 4 digits for 16-bit values
-
-POOR_MAN_MAKE = 0 ; check modification times of the main source file and the object file and skip assembly if unnecessary
-CONVERT_TO_TABS = 0 ; compress listing with tabs (disable with /s)
-
-LONG_FILE_NAMES = 1 ; use Win32 long filenames interface if available
-
-RELOC_ORG = 1 ; support org r:
-
-FUNCTIONS = 0 ; support functions in expressions
-
- IDEAL
- P386
-IF EXE
- MODEL SMALL
-ELSE
- MODEL TINY
-ENDIF
- CODESEG
-
-
-l_icl = 1024
-l_org = 4096
-l_def = 1024
-IF EXE
-l_lab = 53000
-ELSE
-l_lab = 48000
-ENDIF
-
-STRUC com
-c_code db ?
-c_flag db ?
-c_name db ?,?,?
-c_vec dw ?
- ENDS
-
-STRUC icl
-prev dw ?
-handle dw ?
-line dd ?
-flags db ?
-m_eofl = 1
-nam db ?
- ENDS
-
-STRUC lab
-IF LABELS_32
-l_val dd ?
-ELSE
-l_val dw ?
-b_sign = 7
-m_sign = 80h
-ENDIF
-flags db ?
-m_lnus = 40h
-m_ukp1 = 20h
-len db ?
-nam db ?
- ENDS
-
-STRUC movt
-m_lcod db ?
-m_lvec dw ?
-m_scod db ?
-m_svec dw ?
-m_inc db ?
-m_dec db ?
- ENDS
-
-;[flags]
-m_pass = 1
-m_norg = 2
-m_rorg = 4
-m_rqff = 8
-b_hdr = 4
-m_hdr = 10h
-m_pair = 20h
-m_repa = 40h
-b_skit = 7
-m_skit = 80h
-b_enve = 8
-m_enve = 100h
-m_wobj = 200h
-m_times = 400h
-m_first = 800h
-m_5200 = 1000h
-m_repl = 2000h
-b_ski2 = 14
-m_ski2 = 4000h
-b_def = 15
-m_def = 8000h
-
-;[swits]
-m_swc = 1
-m_swd = 2
-m_swe = 4
-m_swi = 8
-m_swl = 10h
-IF POOR_MAN_MAKE
-b_swn = 5
-m_swn = 20h
-m_swo = 40h
-ELSE
-m_swo = 20h
-ENDIF
-m_swp = 2*m_swo
-m_swq = 2*m_swp
-IF CONVERT_TO_TABS
-m_sws = 2*m_swq
-m_swt = 2*m_sws
-ELSE
-m_swt = 2*m_swq
-ENDIF
-m_swu = 2*m_swt
-
-;[flist]
-m_lsti = 8
-m_lstl = 10h
-m_lsto = 20h
-m_lsts = m_lsto+m_lstl+m_lsti
-
-; [fillfl]
-m_fillen = 1 ; fill enabled
-m_fillpo = 2 ; fill possible
-b_fillrq = 2 ; fill requested
-m_fillrq = 4
-
-nhand = -1 ;null handle
-STDERR = 2
-cr equ 13
-eol equ 13,10
-eot equ 13,10,'$'
-
-MACRO lda _rg ;shorter than 'mov (e)ax, _rg'
-_rge SUBSTR <_rg>, 1, 1
-IFIDNI _rge,
- xchg eax, _rg
-ELSE
- xchg ax, _rg
-ENDIF
- ENDM
-
-MACRO sta _rg ;shorter than 'mov _rg, (e)ax'
-_rge SUBSTR <_rg>, 1, 1
-IFIDNI _rge,
- xchg _rg, eax
-ELSE
- xchg _rg, ax
-ENDIF
- ENDM
-
-MACRO dos _func
- IFNB <_func>
- IF _func and 0ff00h
- mov ax, _func
- ELSE
- mov ah, _func
- ENDIF
- ENDIF
- int 21h
- ENDM
-
-MACRO file _func, _errtx
- IFNB <_errtx>
- mov [errmsg], offset _errtx
- ENDIF
- IF _func and 0ff00h
- mov ax, _func
- ELSE
- mov ah, _func
- ENDIF
- call xdisk
- ENDM
-
-MACRO jfile _func, _errtx
- IFNB <_errtx>
- mov [errmsg], offset _errtx
- ENDIF
- IF _func and 0ff00h
- mov ax, _func
- ELSE
- mov ah, _func
- ENDIF
- jmp xdisk
- ENDM
-
-MACRO print _text
- IFNB <_text>
- mov dx, offset _text
- ENDIF
- dos 9
- ENDM
-
-MACRO error _err
- push offset _err
- jmp errln
- ENDM
-
-MACRO testfl _mask
-; testflag is tasm's optimized version of test
- testflag [flags], _mask
- ENDM
-
-MACRO resfl _mask
-; maskflag is tasm's optimized version of and
- maskflag [flags], not (_mask)
- ENDM
-
-MACRO setfl _mask
-; setflag is tasm's optimized version of or
- setflag [flags], _mask
- ENDM
-
-MACRO testsw _mask
- testflag [swits], _mask
- ENDM
-
-MACRO setsw _mask
- setflag [swits], _mask
- ENDM
-
-MACRO jpass1 _dest
- testflag [flags], m_pass
- jz _dest
- ENDM
-
-MACRO jpass2 _dest
- testflag [flags], m_pass
- jnz _dest
- ENDM
-
-MACRO jopcod _dest
- cmp bp, offset var
- jne _dest
- ENDM
-
-MACRO jnopcod _dest
- cmp bp, offset var
- je _dest
- ENDM
-
-MACRO cmd _oper
-_tp SUBSTR <_oper>, 4, 4
-_tp CATSTR <0>, &_tp,
- dw _tp
- IRP _ct,<3,2,1>
-_tp SUBSTR <_oper>, _ct, 1
-% db '&_tp'
- ENDM
-_tp SUBSTR <_oper>, 8
- dw _tp
- ENDM
-
-MACRO opr _oper
-_tp SUBSTR <_oper>, 1, 1
- db _tp
-_tp SUBSTR <_oper>, 2
-_tp CATSTR , _tp
- dw _tp
- ENDM
-
-MACRO undata
- db 6 dup(?)
-lstnum db 5 dup(?)
-lstspa db ?
-lstorg db 26 dup(?)
-line db 258 dup(?)
-tlabel db 256 dup(?)
-obuf db 128 dup(?)
-obufen = $
-objnam db 128 dup(?)
-lstnam db 128 dup(?)
-tabnam db 128 dup(?)
-t_icl db l_icl dup(?)
-t_org db l_org dup(?)
-t_def db l_def dup(?)
- ENDM
-
-;*****************************
-
-IF EXE
-ELSE
- db 100h dup(?)
-ENDIF
-start:
-IF COMPAK
- undata
- db COMPAK+start-$ dup(?)
-ENDIF
-IF EXE
- mov dx, @data
- mov es, dx
- mov ss, dx
- mov sp, offset staken
- mov di, offset tlabel
- mov ax, [16h]
- stosw
- mov si, 81h
- movzx cx, [byte si-1]
- inc cx
- rep movsb
- mov ds, dx
-ENDIF
-IF SET_WIN_TITLE
- mov di, offset hello
- mov ax, 168eh
- xor dx, dx
- int 2fh
- mov [titfin], 0dh
-ENDIF
-; print hello
-IF EXE
- mov si, offset tlabel+3
-ELSE
- mov si, 82h
-ENDIF
- mov [objnam], 0
- mov [lstnam], 0
- mov [tabnam], 0
- mov [(icl t_icl).nam], 0 ; pusta nazwa zrodla
-
-; parsuj argumenty
-parg0: dec si
-parg1: lodsb
- cmp al, ' '
- je parg1
- cmp al, 9
- je parg1
- cmp al, 0dh
- je pargx
- cmp al, '/'
- je popt1
- mov di, offset (icl t_icl).nam
- cmp [byte di], 0
- jnz usg ; juz byla nazwa zrodla
- mov dx, di
- dec si
- call clfname
- call adasx
- mov [fslen], di
- jmp parg0
-
-usg: print hello ; usage - instrukcja
- dos 4c03h
-
-popt1: lodsb
- and al, 0dfh ; mala litera -> duza
- mov di, offset swilet
- mov cx, NUM_SWITCHES
- repne scasb
- jne usg ; nie ma takiego switcha
- bts [swits], cx ; sprawdz bit i ustaw
- jnc popt2
- cmp al, 'D' ; tylko /d dozwolone kilka razy
- jne usg
-popt2: mov di, offset lstnam
- cmp al, 'L'
- je popt4
- mov di, offset tabnam
- cmp al, 'T'
- je popt4
- mov di, offset objnam
- cmp al, 'O'
- je popt3
- cmp al, 'D'
- jne parg1
- mov di, [defen]
-popt3: cmp [byte si], ':' ; /O i /D wymagaja ':'
- jne usg
-popt4: mov ah, al
- lodsb
- cmp al, ':'
- jne parg0
- call clfname ; pobierz nazwe
- cmp ah, 'D'
- jne parg0
-; mov [byte di], 0
- lea cx, [di-3]
- sub cx, [defen]
- jbe usg
- xchg di, [defen]
- inc di
- mov al, '='
- repne scasb
- jne usg
- jmp parg0
-
-pargx:
- cmp [(icl t_icl).nam], 0 ; czy jest nazwa?
- je usg
- testsw m_swq
- jnz nohelo
- mov [usgtxt], '$'
- print hello
-nohelo: mov di, offset lstnam
- mov eax, 'TSL'
- call defnam
- mov di, offset objnam
- mov eax, 'XBO'
- call defnam
-
- mov al, [byte swits]
- and al, m_lstl
- xor al, m_lstl+m_lsto
- mov [flist], al
-
- testsw m_swe
- jz noswe
-IF EXE
- mov ax, [word tlabel]
- jmp prpsp1
-ENDIF
-prpsp: mov ax, [16h]
-prpsp1: mov ds, ax
- cmp ax, [16h]
- jne prpsp
-
- mov ax, [2ch]
- test ax, ax
- jz enver
- dec ax
-IF EXE
- mov [ss:envseg], ax
-ELSE
- mov [cs:envseg], ax
-ENDIF
- mov ds, ax
- mov es, ax
- mov si, 10h
- mov di, si
- xor al, al
-
-renv1: cmp al, [si]
- jz renvx
- cmp [word si], 'RE'
- jne renv2
- cmp [byte si+2], 'R'
- jne renv2
- cmp [byte si+7], '='
- jne renv2
- cmp [dword si+3], 'ELIF'
- je renv3
- cmp [dword si+3], 'ENIL'
- je renv3
-renv2: movsb
- cmp al, [si-1]
- jnz renv2
- jmp renv1
-renv3: lodsb
- test al, al
- jnz renv3
- jmp renv1
-
-enver:
-IF EXE
- push ss
-ELSE
- push cs
-ENDIF
- pop ds
- call prenvt
- dos 4c02h
-
-renvx:
-IF EXE
- push ss
-ELSE
- push cs
-ENDIF
- pop ds
- mov [envofs], di
- stosb
- push ds
- pop es
-
-noswe:
-IF POOR_MAN_MAKE
- btr [swits], b_swn
- jnc noswn
-
- mov dx, offset objnam ; sprawdz czas modyfikacji object'a
- dos 3d00h
- jc noswn ; pewnie nie istnieje
- sta bx ; handle -> bx
- dos 5700h
- mov [word objmod], cx ; zapisz czas
- mov [word high objmod], dx ; zapisz date
- dos 3eh
- setsw m_swn ; sprawdzimy czas modyfikacji source'a
-
-noswn:
-ENDIF
- mov bp, offset var
-
-npass: mov [orgvec], offset t_org-2
- mov [defvec], offset t_def
- mov [fillfl], 0
- mov di, [fslen]
-
-opfile: call fopen
-IF POOR_MAN_MAKE
- btr [swits], b_swn
- jnc main
- sta bx
- dos 5700h ; sprawdz czas modyfikacji
- push dx
- push cx
- pop eax
- cmp eax, [objmod]
- ja main
- mov si, offset oldtxt
- call prline
- dos 4c00h
-ENDIF
-
-main: mov di, offset line
- mov si, [defvec]
- cmp si, [defen]
- jae nodef
-mdef1: movsb
- cmp [byte si], '='
- jne mdef1
- inc si
- mov eax, 'uqe '
- stosd
- stosb
-mdef2: movsb
- cmp [byte si], 0
- jne mdef2
- inc si
- mov [defvec], si
- setfl m_def
- inc [(icl t_icl).line]
- jmp syntax
-nodef:
- btr [flags], b_def
- jnc ndef1
- mov [(icl t_icl).line], 0
- mov [srcen], 0
-ndef1: mov bx, [iclen]
- mov bx, [(icl bx).prev]
- test [(icl bx).flags], m_eofl
- jnz filend ; czy byl juz koniec pliku ?
- ; ... nie - omin ewentualny LF
- call fread1
- jz filend
-
- mov bx, [iclen]
- mov bx, [(icl bx).prev]
- inc [(icl bx).line] ; zwieksz nr linii w pliku
- inc [lines] ; ilosc wszystkich linii
- testsw m_swi
- jz gline1 ; czy /I
- and [flist], not m_lsti ; ... tak
- cmp bx, offset t_icl
- jbe gline1 ; czy includowany plik ?
- or [flist], m_lsti ; ... tak, nie listuj
-
-gline1:
- mov al, [di]
- cmp al, 0dh ; pc cr
- jne gline2
- call fread1
- jz eof
- cmp [byte di], 0ah
- je syntax
- push offset e_nomac
- jmp erron
-gline2: cmp al, 0ah ; unix lf
- je syntax
- cmp al, 9bh ; atari eol
- je syntax
- inc di
- cmp di, offset line+256
- jnb linlon
- call fread1
- jnz gline1
-; eof
-eof: mov bx, [iclen] ; koniec pliku
- or [(icl bx).flags], m_eofl
-
-syntax: mov [byte di], 0dh
- mov [eolpos], di
- mov [lstidx], offset lstorg
- mov [labvec], 0
- mov si, offset line ; asembluj linie
- call rlabel
- jb nolabl
- cmp [skflag], 0
- jnz labelx
- jpass2 deflp2 ; jest etykieta
- call flab0 ; definicja etykiety w pass 1
- jnc ltwice
- mov di, [laben]
- mov [labvec], di
-IF LABELS_32
- movzx eax, [origin]
- stosd
-ELSE
- mov ax, [origin]
- stosw ; domyslnie equ *
-ENDIF
- mov al, m_lnus
- mov ah, dl
- stosw
- mov cx, dx
- sub cl, offset (lab).nam
- lda si
- mov si, offset tlabel
- rep movsb ; przepisz nazwe
- sta si
- mov [laben], di
- cmp di, offset t_lab+l_lab+offset (lab)-offset (lab).nam
- jb labelx
- error e_tlab
-
-ltwice: error e_twice
-
-deflp2: mov bx, [pslab] ; definicja etykiety w pass 2
- mov [labvec], bx
- add [pslab], dx ; oznacz jako minieta
- test [(lab bx).flags], m_lnus
- jz labelx
- testsw m_swu
- jz labelx
- push si
- push offset w_ulab
- call warln
- pop si
-labelx: cmp [byte si], 0dh
- je lstrem
- call spaces
-
-nolabl: lodsb
- cmp al, ' '
- je nolabl
- cmp al, 9
- je nolabl
- cmp al, '*'
- je lstrem
- cmp al, ';'
- je lstrem
- cmp al, '|'
- je lstrem
- cmp al, 0dh
- je lstrem
- cmp al, ':'
- jne s_one
- cmp [skflag], 0
- jnz lstcnd
- call getuns
- jc unknow
- sta cx
- jcxz lstre1
- call spaces
- xor eax, eax
- jmp s_cmd
-
-skip1: lodsd ; sprawdz komende
- dec si
- and eax, 0dfdfdfh
- mov di, offset cndtxt
- mov cx, 5
- repne scasd
- jne lstcnd
- call [word di-4+cndvec-cndtxt]
-lstrem: cmp [skflag], 0
- jz lstre1
-lstcnd: testsw m_swc
- jz main
-
-lstre1: cmp [byte high labvec], 0
- jz lstre2
- call chorg
- call phorg
-lstre2: call lstlin
- jmp main
-
-s_one: dec si
- mov cx, 1
- mov eax, -1
-s_cmd: cmp [skflag], 0
- jnz skip1
- mov [times], cx
- mov [loopctr], eax
- resfl m_times
- setfl m_first
- cmp cx, 1
- je jone
- setfl m_times
-jone: testfl m_norg
- jnz nlorg
- call phorg
-nlorg: mov [cmdvec], si
-
-rdcmd1: resfl m_pair
-
-rdcmd2: mov [scdvec], 0
-rdcmd3: lodsw ; wez trzy litery
- and ax, 0dfdfh
- xchg al, ah
- shl eax, 16
- mov ah, 0dfh
- and ah, [si]
- jopcod lbnox ; jezeli nie cytujemy ...
- mov [obufpt], offset obuf ; (oprozniamy bufor wyjsciowy)
- testfl m_norg
- jz lbnox ; ... i nie bylo ORG ...
- cmp [byte high labvec], 0
- je lbnox ; ... a jest etykieta ...
- cmp eax, 'EQU' shl 8
- call nenorg ; ... to dozwolony jest tylko EQU
-lbnox: inc si
- cmp [byte si], ':' ; czy para instrukcji?
- jne nopair
- jopcod ntopco
- inc si
- mov [scdvec], si
- call get ; w kolejnych trzech literach nie moze byc EOLa
- call get
- call get
- setfl m_pair
-nopair: mov di, offset comtab ; przeszukiwanie polowkowe
- mov bx, 64*size com
-sfcmd1: mov al, [(com di+bx).c_code]
- mov [cod], al
- mov al, [(com di+bx).c_flag]
- cmp eax, [dword (com di+bx).c_flag]
- jb sfcmd2
- je fncmd
- add di, bx
- cmp di, offset comend
- jb sfcmd2
- sub di, bx
-sfcmd2: shr bx, 1
- cmp bl, 3
- ja sfcmd1
- mov bl, 0
- je sfcmd1
- error e_inst
-
-ntrep: error e_crep
-
-ntopco: error e_opcod
-
-ntpair: error e_pair
-
-ntrepa: error e_repa
-
-fncmd: test al, 80h
- jz ckcmd1
- jopcod ntopco ; nie ma opcoda
-ckcmd1: test al, 40h
- jz ckcmd2
- testfl m_times ; nie mozna powtarzac ...
- jnz ntrep
- testfl m_pair ; ... ani parowac
- jnz ntpair
-ckcmd2: test al, 20h ; dyrektywa?
- jz ckcmd3
- test al, 10h ; nie mozna generowac skoku przez dyrektywe
- jz rncmd3 ; (dyrektywa neutralna)
- resfl m_repa ; skok zabroniony
- testfl m_skit
- jz rncmd3
-eskit: error e_skit
-ckcmd3: jopcod ckcmd4
- btr [flags], b_skit
- jnc ckcmd4
- setfl m_ski2
- inc [curorg]
- inc [obufpt]
- mov cx, m_pair+m_times
- mov dx, offset w_skif
- call skirew
-ckcmd4: test al, 08h
- jz ckcmd5
- testfl m_repa ; pseudo instrukcja powtarzania
- jz ntrepa
- mov cx, m_repl
- mov dx, offset w_repl
- call skirew
- jmp rncmd2
-
-ckcmd5: setfl m_repa
- test al, 04h
- mov ax, [origin]
- mov [reporg], ax
- jz rncmd2
- resfl m_ski2
- setfl m_skit ; pseudo instrukcja omijania
- mov [obuf], 2
- mov al, [(com di+bx).c_code]
- call savbyt
-; robimy miejsce na argument skoku
-IF RELOC_ORG
- call incorg
-ELSE
- inc [origin]
-ENDIF
- jmp encmd2
-
-rncmd2: resfl m_repl
- testfl m_pair+m_times
- jz rncmd3
- setfl m_repl ; ustaw repl, jesli koniec linii z para lub licznikiem
-rncmd3: mov al, [cod]
- call [(com di+bx).c_vec] ; wywolaj procedure
- btr [flags], b_ski2
- jnc encmd2
- mov ax, [origin]
- sub ax, [reporg]
- add ax, 80h
- call calcbr
- mov [obuf], al
-encmd2: call oflush
- resfl m_first
- mov cx, [scdvec]
- jcxz encmd3
- mov si, cx
- jmp rdcmd2
-encmd3: call linend
- inc [loopctr]
- dec [times]
- jz main
- mov si, [cmdvec]
- jmp rdcmd1
-
-skirew: jpass1 skiret
- testfl cx
- jz skiret
- testfl m_first
- jz skiret
- pusha
- push dx
- call warln
- popa
-skiret: ret
-
-; ERROR
-errln: call ppline
-erron: call prname
- mov dx, offset errtxt
- mov cx, errtxl
- call prerr
- pop si
- call msgenv
- dos 4c02h
-
-; WARNING
-warln: call ppline
- call prname
- mov dx, offset wartxt
- mov cx, wartxl
- call prerr
- pop ax
- pop si
- push ax
- mov [byte exitcod], 1
-msgenv: call prline
- btr [flags], b_enve
- jnc skiret
-prenvt: mov si, offset envtxt
- jmp prline
-
-gname: mov di, [(icl bx).prev]
- mov dx, offset clitxt
- mov cx, clitxl
- testfl m_def
- jnz gnamex
- lea cx, [bx-1]
- lea dx, [(icl di).nam]
- sub cx, dx
-gnamex: ret
-
-prname: mov bx, [iclen]
- cmp bx, offset t_icl
- jna skiret
- call gname
- push di
- mov [envnam], dx
- sub bx, dx
- mov [envlen], bx
- call prerr
- mov al, ' '
- call putchar
- mov al, '('
- call putchar
- pop bx
- mov eax, [(icl bx).line]
- call numdet
- mov dx, di
- mov [envnum], di
- mov cx, offset dectxt+10
- sub cx, di
- call prerr
- mov al, ')'
- call putchar
- mov al, ' '
- call putchar
- testsw m_swe
- jz skiret
- les di, [dword envofs]
- mov ax, [es:3]
- shl ax, 4
- sub ax, di
- sub ax, offset dectxt+10+19-1
- sub ax, [envlen]
- add ax, [envnum]
- mov [envlen], ax
- js senve
- mov eax, 'FRRE'
- stosd
- mov eax, '=ELI'
- stosd
- mov si, [envnam]
-senv1: movsb
- cmp [byte si], 0 ;'$'
- jne senv1
- xor al, al
- stosb
- mov eax, 'LRRE'
- stosd
- mov eax, '=ENI'
- stosd
- mov si, [envnum]
-senv2: movsb
- cmp [byte si], '$'
- jne senv2
- xor ax, ax
- stosw
- push ds
- pop es
- ret
-
-senve: setfl m_enve
- ret
-
-ppline: mov si, offset line
-prline: mov dx, si
- xor cx, cx
-prli1: inc si
- inc cx
- cmp [byte si-1], 0dh
- jne prli1
- mov bx, STDERR
- dos 40h
- mov al, 0ah
-putchar:
- mov [chbuf], al
- mov dx, offset chbuf
- mov cx, 1
-prerr: mov bx, STDERR
- dos 40h
- ret
-
-miseif: push offset e_meif
- jmp erron
-
-skiten: push offset e_skit
- jmp erron
-
-; End of file
-pofend: pop ax
-filend: call fclose
- cmp bx, offset t_icl
- ja main
- jpass2 fin
-
- cmp [elflag], 1
- jne miseif
- testfl m_skit
- jnz skiten
- setfl m_pass+m_norg+m_rorg+m_rqff+m_hdr+m_wobj
- and [flist], not m_lsto
- jmp npass
-
-fin: mov bx, [ohand]
- mov [errmsg], offset e_wrobj
- call hclose
- testsw m_swt
- jz nlata ; czy /T ?
- cmp [laben], offset t_lab ; ... tak
- jbe nlata ; czy tablica pusta ?
- cmp [byte tabnam], 0 ; ... nie
- jnz oplata ; czy dana nazwa ?
- cmp [lhand], nhand ; ... nie
- jne latt1 ; czy otwarty listing ?
- call opnlst ; ... nie - otworz
- jmp latt2
-latt1: call plseol
- jmp latt2
-oplata: call lclose ; zamknij listing
-IF LONG_FILE_NAMES
- mov si, offset tabnam
-ELSE
- mov dx, offset tabnam
-ENDIF
- call opntab ; otworz tablica
-latt2: mov dx, offset tabtxt
- mov cx, tabtxl
- call putlad
- mov si, offset t_lab
-lata1: mov di, offset lstnum
- mov eax, ' '
- test [(lab si).flags], m_lnus
- jz lata2
- mov al, 'n'
-lata2: test [(lab si).flags], m_ukp1
- jz lata3
- mov ah, '2'
-lata3: stosd
-IF LABELS_32
- mov eax, [(lab si).l_val]
- test eax, eax
- jns lata4
- mov [byte di-1], '-'
- neg eax
-lata4:
- call phdword
-ELSE
- mov ax, [(lab si).l_val]
- test [(lab si).flags], m_sign
- jz lata4
- mov [byte di-1], '-'
- neg ax
-lata4:
- call phword
-ENDIF
- mov al, ' '
- stosb
- mov cx, (offset (lab)-offset (lab).nam) and 0ffh
- add cl, [(lab si).len]
- add si, offset (lab).nam
- rep movsb
- call putlst
- cmp si, [laben]
- jb lata1
-
-nlata: call lclose
- testsw m_swq
- jnz zrbyt
- mov eax, [lines]
- shr eax, 1
- call pridec
- print lintxt
- mov eax, [bytes]
- test eax, eax
- jz zrbyt
- call pridec
- print byttxt
-zrbyt: mov ax, [exitcod]
-; dos ;!!!
-
-; I/O
-xdisk: dos
- jnc cloret
-xderror:
- push [errmsg]
- jmp erron
-
-icler: push offset e_icl
- jmp erron
-
-lclose: mov bx, nhand ; mov bx, [lhand]
- xchg bx, [lhand] ; mov [lhand], nhand
- mov [errmsg], offset e_wrlst
-hclose: cmp bx, nhand
- je cloret
- jfile 3eh
-
-IF LONG_FILE_NAMES
-lfnopen:
- mov ax, 716ch
-lfndos:
- mov [errmsg], cx
- xor cx, cx
- dos
- jnc lfnok
- add ah, -71h
- jnc xderror
- mov dx, si
-lfnok:
- ret
-
-lfncreat:
- mov bx, 2001h
- mov dx, 12h
- call lfnopen
- jnc lfnok
- jfile 3c00h
-ENDIF
-
-fopen: cmp di, offset t_icl+l_icl-2
- jnb icler
- push si
- testsw m_swp
- jz fopen1
- mov si, offset (icl).nam
- add si, [iclen]
- mov di, offset tlabel
-IF LONG_FILE_NAMES
- mov cx, offset e_open
- mov ax, 7160h
- call lfndos
- jnc pathok
- file 60h
-pathok:
-ELSE
- file 60h, e_open
-ENDIF
- mov si, offset tlabel
- mov di, offset (icl).nam
- add di, [iclen]
-fomna: lodsb
- stosb
- test al, al
- jnz fomna
-fopen1: mov bx, [iclen]
- mov [(icl bx).line], 0
- mov [(icl bx).flags], 0
-IF LONG_FILE_NAMES
- lea si, [(icl bx).nam]
- mov [(icl di).prev], bx
- mov [iclen], di
- mov bx, 2000h
- mov dx, 1
- mov cx, offset e_open
- call lfnopen
- jnc openok
- file 3d00h
-openok:
-ELSE
- lea dx, [(icl bx).nam]
- mov [(icl di).prev], bx
- mov [iclen], di
- file 3d00h, e_open
-ENDIF
- pop si
- mov bx, [iclen]
- mov bx, [(icl bx).prev]
- mov [(icl bx).handle], ax
-cloret: ret
-
-fread1: mov dx, di
- mov cx, 1
-fread: mov ah, 3fh
-fsrce: mov bx, [iclen]
- mov bx, [(icl bx).prev]
- mov bx, [(icl bx).handle]
- mov [errmsg], offset e_read
- call xdisk
- test ax, ax
- ret
-
-fclose: mov ah, 3eh
- call fsrce
- mov bx, [iclen]
- cmp bx, [srcen]
- jne fclos1
- mov [srcen], 0
-fclos1: mov bx, [(icl bx).prev]
- mov [iclen], bx
- ret
-
-putwor: mov cx, 2 ; zapisz slowo do pliku
- mov dx, offset oword
- mov [oword], ax
-putblk: jpass1 putx ; zapisz blok
- cmp [ohand], nhand
- jne putb1 ; otwarty object ?
-IF LONG_FILE_NAMES
- push cx dx si
- mov si, offset objnam ; ... nie - otworz
- mov cx, offset e_crobj
- call lfncreat
-ELSE
- mov dx, offset objnam ; ... nie - otworz
- xor cx, cx
- file 3ch, e_crobj
-ENDIF
- mov [ohand], ax
- testsw m_swq
- jnz noobjt
- print objtxt
-noobjt:
-IF LONG_FILE_NAMES
- pop si dx cx
-ELSE
- pop dx cx
-ENDIF
-putb1: mov bx, [ohand]
- file 40h, e_wrobj
- movzx ecx, cx
- add [bytes], ecx
-putx: ret
-
-orgwor: push ax
- call phword
- pop ax
- jmp putwor
-
-chorg: testfl m_norg
-nenorg: jz putx
- error e_norg
-
-tmorgs: error e_orgs
-
-filer: error e_fill
-
-;;internal: error e_fatal
-
-incorg: inc [origin]
-IF RELOC_ORG
- inc [ldorg]
-ENDIF
- ret
-
-savwor: push ax
- call savbyt
- pop ax
- mov al, ah
-
-savbyt: jopcod xopco
- testfl m_wobj
- jz incorg
- btr [fillfl], b_fillrq
- jnc nofill
- push ax
- resfl m_rorg+m_rqff
-IF RELOC_ORG
- mov ax, [ldorg]
-ELSE
- mov ax, [origin]
-ENDIF
- sub ax, [curorg]
- jz fillfi
- jb filer
-fillop: push ax
- mov dx, offset fillbyt
- mov cx, 1
- call putblk
- pop ax
- dec ax
- jnz fillop
-IF RELOC_ORG
- mov ax, [ldorg]
-ELSE
- mov ax, [origin]
-ENDIF
- mov [curorg], ax
-fillfi: pop ax
-nofill:
- mov di, [obufpt]
- stosb
- mov [obufpt], di
-IF RELOC_ORG
- mov ax, [ldorg]
-ELSE
- mov ax, [origin]
-ENDIF
- testfl m_hdr
- jz borg4
- call chorg
- testfl m_rorg
- jnz borg1
- cmp ax, [curorg]
- je borg3
-borg1: add [orgvec], 2
- cmp [orgvec], offset t_org+l_org
- jae tmorgs
- jpass1 borg2
- mov di, offset lstorg
- testfl m_rqff
- jz noff
- mov ax, 0ffffh
- call orgwor
- mov ax, ' >'
- stosw
-IF RELOC_ORG
- mov ax, [ldorg]
-ELSE
- mov ax, [origin]
-ENDIF
-noff: call orgwor
- mov al, '-'
- stosb
- mov bx, [orgvec]
- mov ax, [bx]
- call orgwor
- mov ax, ' >'
- stosw
-IF RELOC_ORG
- mov ax, [ldorg]
-ELSE
- mov ax, [origin]
-ENDIF
- mov [lstidx], di
-borg2: resfl m_rorg+m_rqff
-borg3: jpass2 borg4
- mov di, [orgvec]
- stosw
-borg4: inc ax
- mov [curorg], ax
- setflag [fillfl], m_fillpo
-IF RELOC_ORG
- call incorg
-ELSE
- inc [origin]
-ENDIF
-;; cmp ax, [origin]
-;; je internal
- cmp [obufpt], offset obufen
- jb oflur
- testfl m_skit
- jnz eskit
-
-oflush: mov dx, offset obuf
- mov cx, [obufpt]
- sub cx, dx
- jz oflur
- mov [obufpt], dx
- call putblk
- test [flist], m_lsts
- jnz oflur
- mov bx, offset obuf
- mov di, [lstidx]
-olst1: cmp di, offset line-3
- jae lstxtr
- mov al, [bx]
- inc bx
- call phbyte
- mov al, ' '
- stosb
- loop olst1
-olstx: mov [lstidx], di
-oflur: ret
-lstxtr: cmp di, offset line-1
- jae olstx
- mov ax, ' +'
- stosw
- mov [lstidx], di
-linret: ret
-
-; Stwierdza blad, jesli nie spacja, tab lub eol
-linend: lodsb
- cmp al, 0dh
- je linen1
- cmp al, ' '
- je linen1
- cmp al, 9
- je linen1
- error e_xtra
-; Listuje linie po ostatnim przebiegu
-linen1: cmp [times], 1
- jne linret
-; Listuje linie
-lstlin: test [flist], m_lsts
- jnz linret
- mov di, offset lstspa
- mov bx, [iclen]
- mov bx, [(icl bx).prev]
- mov eax, [(icl bx).line]
- call numdec
- mov al, ' '
-lstl1: dec di
- mov [di], al
- cmp di, offset lstnum
- ja lstl1
- mov di, [lstidx]
- mov cx, offset line
- sub cx, di
- rep stosb
- mov [lstspa], al
-
- mov bx, [iclen]
- cmp bx, [srcen]
- je nlsrc ; czy zmienil sie asemblowany plik ?
- mov [srcen], bx ; ... tak
- cmp [lhand], nhand
- jne lsrc1 ; otwarty listing ?
- call opnlst ; ... nie - otworz
-lsrc1: mov dx, offset srctxt
- mov cx, offset srctxl
- call putlad ; komunikat o nowym source'u
- mov bx, [iclen]
- call gname
- call putlad ; nazwa
- call plseol
-nlsrc:
- mov di, [eolpos]
-IF CONVERT_TO_TABS
- testsw m_sws
- jnz ctrail ; jezeli nie ma /S ...
- mov si, offset lstnum
- mov di, si ; ... zamien spacje na taby
-spata1: xor dl, dl
-spata2: lodsb
- cmp al, 0dh
- je ctrail
- stosb
- cmp al, 9
- je spata1
- dec dx
- cmp al, ' '
- jne spata2
- and dx, 7
- jz spata2
- mov cx, dx
- mov bx, si
-spata3: cmp al, [bx]
- jne spata2
- inc bx
- loop spata3
- mov [byte di-1], 9
- mov si, bx
- jmp spata1
-
-strail: dec di
-ctrail: cmp [byte di-1], ' '
- je strail
- cmp [byte di-1], 9
- je strail
-ENDIF
-putlst: mov ax, 0a0dh
- stosw
- mov dx, offset lstnum
- mov cx, di
- sub cx, dx
-putlad: mov bx, [lhand]
- jfile 40h, e_wrlst
-
-opnlst:
-IF LONG_FILE_NAMES
- mov si, offset lstnam
-ELSE
- mov dx, offset lstnam
-ENDIF
-opntab:
-IF LONG_FILE_NAMES
- mov cx, offset e_crlst
- call lfncreat
-ELSE
- xor cx, cx
- file 3ch, e_crlst
-ENDIF
- mov [lhand], ax
- testsw m_swq
- jnz nolstt
- print lsttxt
-nolstt: mov dx, offset hello
- mov cx, hellen
- jmp putlad
-
-plseol: mov dx, offset eoltxt
- mov cx, 2
- jmp putlad
-
-adasx: mov eax, 'XSA'
-; Dodaj rozszerzenie nazwy, gdy go nie ma
-; we: dx,di-poczatek,koniec nazwy; eax-rozszerzenie
-adext: mov bx, di
-adex1: dec bx
- cmp [byte bx], '\'
- je adex2
- cmp [byte bx], '.'
- je adexr
- cmp bx, dx
- ja adex1
-adex2: mov [byte di-1], '.'
- stosd
-adexr: ret
-
-; Zapisz dziesietnie eax; di-koniec liczby
-numdet: mov di, offset dectxt+10
-numdec: mov ebx, 10
-numde1: cdq
- div ebx
- add dl, '0'
- dec di
- mov [di], dl
- test eax, eax
- jnz numde1
- ret
-
-; Wyswietl dziesietnie eax
-pridec: call numdet
- mov dx, di
-; mov [envnum], di
- print
- ret
-
-; Zapisz hex origin
-phorg: mov di, offset lstorg
- mov ax, [origin]
- call phword ; listuj * hex
- mov al, ' '
- stosb
- mov [lstidx], di
- ret
-
-IF LABELS_32
-phdword:
-IF FOUR_ZEROS_TO_SPACES
- rol eax, 16
- test ax, ax
- jnz phdword0
- mov ax, ' '
- stosw
- stosw
- jmp phdword1
-phdword0:
-ENDIF
- call phword
-phdword1:
- rol eax, 16
-ENDIF
-; Zapisz hex ax od [di]
-phword: push ax
- mov al, ah
- call phbyte
- pop ax
-phbyte: aam 10h
- cmp al, 10
- sbb al, 69h
- das
- xchg al, ah
- cmp al, 10
- sbb al, 69h
- das
- stosw
- ret
-
-; Pobierz nazwe pliku z linii polecen (si) i zapisz do di
-clfname:
- lodsb
- cmp al, '"'
- je clfqt
-clf1:
- stosb
- lodsb
- cmp al, ' '
- je clfx
- cmp al, 9
- je clfx
-; cmp al, '/'
-; je clfx
- cmp al, 0dh
- jne clf1
-clfx: xor al, al
- stosb
- ret
-
-clf2:
- stosb
-clfqt:
- lodsb
- cmp al, 0dh
- je clfx
- cmp al, '"'
- jne clf2
- inc si
- jmp clfx
-
-
-; wpisz domyslna nazwe dla obx lub lst
-defnam: cmp [byte di], 0
- jnz defnr
- mov si, [fslen]
-defn1: dec si
- cmp [byte si], '.'
- jne defn1
- lea cx, [si+1]
- mov si, offset (icl t_icl).nam
- sub cx, si
- rep movsb
- stosd
-defnr: ret
-
-; Pobierz znak (eol=error)
-get: lodsb
- cmp al, 0dh
- je uneol
- ret
-uneol: error e_uneol
-
-; Omin spacje i tabulatory
-spaces: call get
- cmp al, ' '
- je space1
- cmp al, 9
- je space1
- error e_spac
-space1: call get
- cmp al, ' '
- je space1
- cmp al, 9
- je space1
- dec si
-rstret: ret
-
-; Pobierz nazwe pliku
-rfname: call spaces
- mov di, offset (icl).nam
- add di, [iclen]
-; Pobierz lancuch do [di]
-rstr: call get
- cmp al, "'"
- je rstr0
- cmp al, '"'
- jne strer
-rstr0: mov dx, di
- mov ah, al
-rstr1: call get
- stosb
- cmp al, ah
- jne rstr1
- lodsb
- cmp al, ah
- je rstr1
- dec si
- mov [byte di-1], 0
- lea cx, [di-1]
- sub cx, dx
- jnz rstret
-
-strer: error e_str
-
-; Przepisz etykiete do tlabel (wyj: dx-dl.etykiety+offset(lab).nam)
-rlabel: mov di, offset tlabel
- mov [byte di], 0
-rlab1: lodsb
- cmp al, '0'
- jb rlabx
- cmp al, '9'
- jbe rlab2
- cmp al, '_'
- je rlab2
- and al, 0dfh
- cmp al, 'A'
- jb rlabx
- cmp al, 'Z'
- ja rlabx
-rlab2: stosb
- cmp di, offset tlabel+256+offset (lab)-offset (lab).nam
- jb rlab1
-linlon: push offset e_long
- jmp erron
-rlabx: lea dx, [(lab di).nam]
- sub dx, offset tlabel
- dec si
- cmp [byte tlabel], 'A'
- ret
-
-; Czytaj etykiete i szukaj w t_lab
-; wyj: dx-dlugosc etykiety+offset(lab).nam
-; C=0: znaleziona, bx=adres wpisu
-; C=1: nie ma jej
-flabel: call rlabel
- jb ilchar
-flab0: push si
- xor cx, cx
- mov si, offset t_lab
- mov ax, [laben]
- dec ax
-flab1: add si, cx
- cmp ax, si
- jb flabx
- mov cl, [(lab si).len]
- cmp cl, dl
- jne flab1
- add si, offset (lab).nam
- sub cl, offset (lab).nam
- mov di, offset tlabel
- repe cmpsb
- jne flab1
- mov bx, offset tlabel - offset (lab).nam
- add bx, si
- sub bx, di ; c=0
-flabx: pop si
- ret
-
-wropar: error e_wpar
-
-; Czytaj wyrazenie i zwroc jego wartosc w [val]
-; (C=1 wartosc nieokreslona w pass 1)
-IF LABELS_32
-get32:
-ELSE
-spaval: call spaces
-getval:
-ENDIF
- xor bx, bx
- mov [ukp1], bh
- push bx
-
-v_lop:
-v_par1: inc bh
-v_par0: call get
- cmp al, '['
- je v_par1
- mov di, offset opert0
- mov cx, noper0
- repne scasb
- jne v_n1a
- sub di, offset opert0-noper1-noper2
- call goprpa
- dec bh
- push di bx
- inc bh
- jmp v_par0
-
-v_n1a: cmp al, '('
- je wropar
-IF FUNCTIONS
- mov eax, [si-1]
- mov di, offset funtxt
- mov cx, nfun
- repne scasd
- jne notfun
- sub di, offset funtxt
- shr di, 1
- call [funvec+di]
- jmp value1
-notfun:
-ENDIF
- movzx eax, al
- cmp al, '*'
- je valorg
- cmp al, '#'
- je valctr
- cmp al, "'"
- je valchr
- cmp al, '"'
- je valchr
- cmp al, '^'
- je valreg
- cmp al, '{'
- je valquo
- mov di, -1
- cdq ; xor edx, edx
- mov ecx, 16
- cmp al, '$'
- je rdnum3
- mov cl, 2
- cmp al, '%'
- je rdnum3
- mov cl, 10
- cmp al, '9'
- ja vlabel
-
-rdnum1: cmp al, '9'
- jbe rdnum2
- and al, 0dfh
- cmp al, 'A'
- jb value0
- add al, '0'+10-'A'
-rdnum2: sub al, '0'
- cmp al, cl
- jnb value0
- movzx edi, al
- lda edx
- mul ecx
- add eax, edi
- js toobig
- adc edx, edx
- jnz toobig
- sta edx
-rdnum3: lodsb
- jmp rdnum1
-
-vlabel: push bx
- dec si
- call flabel
- jnc vlabfn
- jpass1 vlukp1
- error e_undec
-vlabfn: and [(lab bx).flags], not m_lnus
- jpass1 vlchuk
- cmp bx, [pslab]
- jb vlchuk
- test [(lab bx).flags], m_ukp1
- jz vlukp1
- error e_fref
-vlchuk: test [(lab bx).flags], m_ukp1
- jz vlabkn
-vlukp1: mov [ukp1], 0ffh
-vlabkn:
-IF LABELS_32
- mov eax, [(lab bx).l_val]
-ELSE
- bt [word (lab bx).flags], b_sign
- sbb eax, eax
- mov ax, [(lab bx).l_val]
-ENDIF
- pop bx
- jmp value1
-
-valorg: call chorg
- mov ax, [origin]
- jmp value1
-
-valctr: mov eax, [loopctr]
- test eax, eax
- jns value1
- error e_ctr
-
-valchr: mov dl, al
- call get
- cmp al, dl
- jne valch1
- lodsb
- cmp al, dl
- jne strer
-valch1: cmp dl, [si]
- jne strer
- inc si
- cmp [byte si], '*'
- jne value1
- inc si
- xor al, 80h
- jmp value1
-
-valreg: call get
- sub al, '0'
- cmp al, 4
- ja ilchar
- add al, 0d0h
- testfl m_5200
- jz no5200
- cmp al, 0d3h
- je nopia
- cmp al, 0d2h
- jne no5200
- mov al, 0e8h
-no5200: mov ah, al
- call get
- cmp al, '9'
- jbe valre1
- and al, 0dfh
- cmp al, 'A'
- jb ilchar
- add al, '0'+10-'A'
-valre1: sub al, '0'
- cmp al, 0fh
- ja ilchar
- cmp ah, 0d1h
- ja value1
- jne valre2
- sub ax, 0f0h
-valre2: testfl m_5200
- jz value1
- mov ah, 0c0h
- jmp value1
-
-nopia: error e_5200
-
-valquo: jopcod rcopco
- push bx
- mov [opcosp], sp
- mov bp, offset var2
- jmp rdcmd3
-xopco: mov sp, [opcosp]
- mov bp, offset var
- mov ah, al
- call get
- cmp al, '}'
- jne msopco
- movzx eax, ah
- pop bx
- jmp value1
-
-rcopco: error e_ropco
-
-msopco: error e_mopco
-
-value0: dec si
- test di, di
- js ilchar
- lda edx
-value1: push eax
-v_par2: dec bh
- js mbrack
- lodsb
- cmp al, ']'
- je v_par2
-
- mov ah, [si]
- mov di, offset opert2
- mov cx, noper2
- repne scasw
- je foper2 ; operator 2-znakowy
- mov cx, noper1
- repne scasb
- je foper1 ; operator 1-znakowy
- test bh, bh ; koniec wyrazenia
- jnz mbrack ; musza byc zamkniete nawiasy
- dec si
- mov di, offset opert1
-foper1: sub di, offset opert1
- jmp goper
-foper2: inc si
- sub di, offset opert2
- shr di, 1
- add di, noper1
-goper: call goprpa
- pop eax
-v_com: pop cx
- cmp cx, bx
- jb v_xcm
- pop dx
- cmp dx, offset v_1arg
- jae v_r1a
- sta ecx
- pop eax
-v_r1a: jpass2 v_run
- cmp [ukp1], 1
- jnb v_com
-v_run: push offset v_com
- push dx
- ret
-v_xcm: cmp bl, 1
- jbe v_xit
- push cx eax di bx
- jmp v_lop
-v_xit: mov [dword val], eax
- cmp [ukp1], 1
- cmc
-IF LABELS_32
- ret
-spaval:
- call spaces
-getval:
- call get32
-ENDIF
-
- jc unsret
-wrange: cmp eax, 10000h
- cmc
- jnb unsret
- cmp eax, -0ffffh
- jb orange
- ret
-
-brange: cmp eax, 100h
- jb unsret
- cmp eax, -0ffh
- jb orange
- ret
-
-spauns: call spaces
-getuns: call getval
- pushf
- jnc getun1
- jpass1 getun2
-getun1: test eax, eax
- js orange
-getun2: popf
-unsret: ret
-
-getpos: call getval
- jc unknow
- test eax, eax
- jg unsret
-
-orange: error e_range
-
-mbrack: error e_brack
-
-toobig: error e_nbig
-
-; Procedury operatorow nie moga zmieniac bx ani di
-
-v_sub: neg ecx ; -
-v_add: add eax, ecx ; +
- jno v_ret
-oflow: error e_over
-
-div0: error e_div0
-
-v_mul: mov edx, ecx ; *
- xor ecx, eax
- imul edx
- test ecx, ecx
- js v_mu1
- test edx, edx
- jnz oflow
- test eax, eax
- js oflow
- ret
-v_mu1: inc edx
- jnz oflow
- test eax, eax
- jns oflow
- ret
-
-v_div: jecxz div0 ; /
- cdq
- idiv ecx
- ret
-
-v_mod: jecxz div0 ; %
- cdq
- idiv ecx
- lda edx
- ret
-
-v_sln: neg ecx
-v_sal: test ecx, ecx ; <<
- js v_srn
- jz v_ret
- cmp ecx, 20h
- jb v_sl1
- test eax, eax
- jnz oflow
- ret
-v_sl1: add eax, eax
- jo oflow
- loop v_sl1
-p_skp:
-v_ret: ret
-
-v_srn: neg ecx
-v_sar: test ecx, ecx ; >>
- js v_sln
- cmp ecx, 20h
- jb v_sr1
- mov cl, 1fh
-v_sr1: sar eax, cl
- ret
-
-v_and: and eax, ecx ; &
- ret
-
-v_or: or eax, ecx ; |
- ret
-
-v_xor: xor eax, ecx ; ^
- ret
-
-v_equ: cmp eax, ecx ; =
-v_eq1: je v_one
-v_zer: xor eax, eax
- ret
-v_one: mov eax, 1
- ret
-
-v_neq: cmp eax, ecx ; <> !=
- jne v_one
- jmp v_zer
-
-v_les: cmp eax, ecx ; <
- jl v_one
- jmp v_zer
-
-v_grt: cmp eax, ecx ; >
- jg v_one
- jmp v_zer
-
-v_leq: cmp eax, ecx ; <=
- jle v_one
- jmp v_zer
-
-v_geq: cmp eax, ecx ; >=
- jge v_one
- jmp v_zer
-
-v_anl: jecxz v_zer ; &&
- test eax, eax
- jz v_ret
- jmp v_one
-
-v_orl: or eax, ecx ; ||
- jz v_ret
- jmp v_one
-
-; Operatory 1-argumentowe
-v_1arg:
-v_neg: neg eax
-v_plu: ret
-
-v_low: movzx eax, al
- ret
-
-v_hig: movzx eax, ah
- ret
-
-v_nol: test eax, eax
- jmp v_eq1
-
-v_not: not eax
- ret
-
-IF FUNCTIONS
-; funkcje
-f_abs:
- call value
- test eax, eax
- js v_neg
- ret
-
-f_ang:
-
-f_hyp:
-f_ret:
- ret
-
-f_max:
- call valuco
- push eax
- call valuco
- pop ecx
- cmp eax, ecx
- jge f_ret ; jle?
-f_ecx:
- lda ecx
- ret
-
-f_min:
- call valuco
- push eax
- call valuco
- pop ecx
- cmp eax, ecx
- jge f_ecx ; jle?
- ret
-
-f_sqr:
-
-; TODO
- ret
-ENDIF
-
-goprpa: lea ax, [di+operpa]
- add di, di
- add di, ax
- mov bl, [di]
- mov di, [di+1]
- ret
-
-onemod: jnopcod getadr
- cmp [byte si], '}'
- jne getadr
- jmp xopco
-
-getaim: jnopcod getai0
- cmp [byte si], '}'
- je getai2
-getai0: cmp al, '<'
- pushf
- call getval
- popf
- jb getai2
- je getai1
- mov al, ah
-getai1: movzx eax, al
- mov [dword val], eax
-getai2: mov dx, 1
- jmp getadx
-
-; Pobierz operand rozkazu i rozpoznaj tryb adresowania
-getadr: call spaces
- lodsb
- xor dx, dx
- cmp al, '@'
- je getadx
- cmp al, '#'
- je getaim
- cmp al, '<'
- je getaim
- cmp al, '>'
- je getaim
- mov dl, 8
- cmp al, '('
- je getao1
- dec si
- lodsw
- and al, 0dfh
- mov dl, 2
- cmp ax, ':A'
- je getao2
- inc dx
- cmp ax, ':Z'
- je getao2
- dec si
- dec si
- xor dx, dx
-
-getad1: push dx
- call getuns
- sbb al, al
- jnz getad2
- mov al, [byte high val]
-getad2: pop dx
-getad9: cmp dl, 8
- jae getaid
- cmp dl, 2
- jae getad3
- cmp al, 1
- adc dl, 2
-getad3: lodsw
- and ah, 0dfh
- mov bl, 2
- cmp ax, 'X,'
- je getabi
- mov bl, 4
- cmp ax, 'Y,'
- je getabi
- dec si
- dec si
- jmp getadx
-getabi: add dl, bl
-getaxt: lodsb
- cmp al, '+'
- je getabx
- inc bx
- cmp al, '-'
- je getabx
- dec si
- jmp getadx
-getabx: mov dh, bl
-getadx: lda dx
- mov [word amod], ax
- ret
-
-getao1: jnopcod getad1
- cmp [byte si], ')'
- je getaid
-getao2: jnopcod getad1
- cmp [byte si], ','
- je getad9
- cmp [byte si], '}'
- je getad9
- jmp getad1
-
-getaid: lodsb
- cmp al, ','
- je getaix
- call chkpar
- lodsw
- mov dx, 1009h
- mov bl, 14h
- cmp ax, '0,'
- je getaxt
- xor dh, dh
- mov bl, 4
- and ah, 0dfh
- cmp ax, 'Y,'
- je getaxt
- inc dx
- dec si
- dec si
- jmp getadx
-getaix: lodsw
- mov dh, 8
- cmp ax, ')0'
- je getadx
- xor dh, dh
- and al, 0dfh
- cmp ax, ')X'
- je getadx
- jmp ilchar
-
-p_imp = savbyt
-
-p_ads: call getadr
- mov al, [cod]
- call savbyt
- mov al, 60h
- cmp [cod], 18h
- je p_as1
- mov al, 0e0h
-p_as1: mov [cod], al
- jmp p_ac1
-
-p_acc: call getadr
-p_ac1: mov ax, [word amod]
- cmp al, 7
- jne acc1
- dec [amod]
-acc1: cmp ah, 8
- jb acc3
- mov ax, 0a2h
- je acc2
- mov al, 0a0h
-acc2: call savwor
-acc3: mov al, [amod]
- mov bx, offset acctab
- xlat
- test al, al
- jz ilamod
- or al, [cod]
- cmp al, 89h ; sta #
- jne putsfx
-ilamod: error e_amod
-
-p_srt: call getadr
- cmp al, 6
- jnb ilamod
- cmp al, 1
- je ilamod
- mov bx, offset srttab
- xlat
- or al, [cod]
- cmp al, 0cah
- je ilamod
- cmp al, 0eah
- je ilamod
-putsfx: call putcmd
- mov al, [amod+1]
- and al, 7
- mov bx, offset sfxtab
- xlat
- test al, al
- jnz savbyt
-putret: ret
-
-p_inw: call getadr
- cmp ax, 6
- jnb ilamod
- sub al, 2
- jb ilamod
- mov bx, offset inwtab
- xlat
- push ax
- call putcmd
- inc [val]
- mov ax, 03d0h
- test [amod], 1
- jz p_iw1
- dec ah
-p_iw1: call savwor
- pop ax
- jmp putsfx
-
-p_ldi: call getadr
-p_ld1: mov al, [amod]
- cmp al, 1
- jb ilamod
- cmp al, 4
- jb ldi1
- and al, 0feh
- xor al, [cod]
- cmp al, 0a4h
- jne ilamod
- mov al, [amod]
-ldi1: mov bx, offset lditab
- xlat
-putcod: or al, [cod]
- jmp putsfx
-
-putcmd: call savbyt
- mov al, [amod]
- mov bx, offset lentab
- xlat
- cmp al, 2
- jb putret
- mov eax, [dword val]
- jne savwor
- jpass1 putcm1
- call brange
-putcm1: jmp savbyt
-
-p_sti: call getadr
-p_st1: mov al, [amod]
- cmp al, 2
- jb ilamod
- je cod8
- cmp al, 3
- je cod0
- and al, 0feh
- xor al, [cod]
- cmp al, 80h
- jne ilamod
- or [amod], 1
- mov al, 10h
- jmp putcod
-cod8: mov al, 8
- jmp putcod
-cod0: xor al, al
- jmp putcod
-
-p_cpi: call getadr
- cmp al, 1
- jb ilamod
- cmp al, 4
- jnb ilamod
- cmp al, 2
- jb cod0
- mov al, 0ch
- je putcod
- mov al, 4
- jmp putcod
-
-p_rep:
- mov ax, [origin]
- xchg ax, [reporg]
- jmp bra0
-
-p_bra: call onemod
- and al, 0feh
- cmp al, 2
- jne ilamod
- mov ax, [val]
-bra0: jpass1 bra1
- call chorg
- sub ax, [origin]
- add ax, 7eh
- call calcbr
- mov [byte val], al
-bra1: mov al, [cod]
- call savbyt
- mov al, [byte val]
- jmp savbyt
-
-calcbr: test ah, ah
- jnz toofar
- add al, 80h
- ret
-
-toofar: cmp ax, 8080h
- jae toofa1
- sub ax, 0ffh
- neg ax
-toofa1: neg ax
- mov di, offset brout
- call phword
- error e_bra
-
-p_jsr: call onemod
- mov al, 20h
- jmp p_abs
-
-p_bit: call getadr
- cmp al, 2
- mov al, 2ch
- je putcmd
- cmp [amod], 3
- jne ilamod
- mov al, 24h
- jmp putcmd
-
-p_juc: call getadr
- mov al, [cod]
- mov ah, 3
- call savwor
- jmp p_jp1
-
-p_jmp: call getadr
-p_jp1: cmp [amod], 10
- je chkbug
- jpass1 p_jpu
- cmp [cod], 4ch
- je p_jpu
- testfl m_norg ; nieznany * (przy OPT H-)
- jnz p_jpu
- mov ax, [val] ; moze branch wystarczy?
- sub ax, [origin]
- add ax, 80h
- test ah, ah
- jnz p_jpu
- push si
- push offset w_bras
- call warln
- pop si
-p_jpu: mov al, 4ch
-p_abs: and [amod], 0feh
- cmp [amod], 2
- je p_jpp
- jmp ilamod
-chkbug: jpass1 p_jid
- cmp [byte val], 0ffh
- jne p_jid
- push si
- push offset w_bugjp
- call warln
- pop si
-p_jid: mov al, 6ch
-p_jpp: jmp putcmd
-
-getops: call getadr
- lea di, [op1]
- call stop
- push [word ukp1]
- call getadr
- pop [word ukp1]
- mov [tempsi], si
- lea di, [op2]
- call stop
- movzx bx, [cod]
- add bx, offset movtab
-ldop1: lea si, [op1]
-ldop: lodsd
- mov [dword val], eax
- lodsw
- mov [word amod], ax
- ret
-stop: mov eax, [dword val]
- stosd
- mov ax, [word amod]
- stosw
- ret
-
-mcall1: mov al, [(movt bx).m_lcod]
- mov [cod], al
- push bx
- call [(movt bx).m_lvec]
- pop bx
- ret
-
-mcall2: mov al, [(movt bx).m_scod]
- mov [cod], al
- push bx
- call [(movt bx).m_svec]
- pop bx
- ret
-
-p_mvs: call getops
- call mcall1
- lea si, [op2]
- call ldop
-p_mvx: call mcall2
- mov si, [tempsi]
- ret
-
-p_mws: call getops
- mov ax, [word amod]
- cmp ax, 8
- jae ilamod
- cmp al, 1
- jne p_mw1
- and [dword val], 0ffh
-p_mw1: call mcall1
- lea si, [op2]
- call ldop
- cmp [word amod], 8
- jae ilamod
- call mcall2
- call ldop1
- cmp [amod], 1
- je p_mwi
- inc [val]
- jmp p_mw2
-p_mwi:
- cmp [ukp1], 0
- jnz p_mwh
- mov dx, [val]
- cmp dl, dh
- je p_mw3
- dec dh
- mov al, [(movt bx).m_inc]
- cmp dl, dh
- je p_mw4
- add dh, 2
- cmp dl, dh
- jne p_mwh
- mov al, [(movt bx).m_dec]
-p_mw4: test al, al
- jz p_mwh
- call savbyt
- jmp p_mw3
-
-p_mwh: sar [dword val], 8
-p_mw2: call mcall1
-p_mw3: lea si, [op2]
- call ldop
- inc [val]
- jmp p_mvx
-
-p_opt: call spaces
- xor dx, dx
- jmp opt0
-opt1: shr cx, 1
- bts dx, cx
- jc opter
- call [word di-2+optvec-opttxt]
-opt0: lodsw
- and al, 0dfh
- mov cx, 10
- mov di, offset opttxt
- repne scasw
- je opt1
- test dx, dx
- jz opter
- dec si
- dec si
- ret
-
-opter: error e_opt
-
-optf0:
-; maskflag [fillfl], not (m_fillen+m_fillrq)
- maskflag [fillfl], not m_fillen
- ret
-optf1: setflag [fillfl], m_fillen
- ret
-optg0: resfl m_5200
- ret
-optg1: setfl m_5200
- ret
-opth0: resfl m_hdr+m_rqff
- ret
-opth1: bts [flags], b_hdr
- jc optr
- setfl m_rorg
- ret
-optl0: or [flist], m_lsto
- ret
-optl1: jpass1 optr
- and [flist], not m_lsto
-optr: ret
-opto0: resfl m_wobj
- ret
-opto1: setfl m_wobj
- ret
-
-p_ert: call spaval
- jpass1 equret
- test eax, eax
- jz equret
- error e_user
-
-p_equ: mov di, [labvec]
- test di, di
- jz nolabd
- mov [(lab di).l_val], 0
-IF LABELS_32
- call spaces
- call get32
-ELSE
- and [(lab di).flags], not m_sign
- call spaval
-ENDIF
- mov di, [labvec]
- jnc equ1
- or [(lab di).flags], m_ukp1
-equ1:
-IF LABELS_32
- mov [(lab di).l_val], eax
-ELSE
- mov [(lab di).l_val], ax
- test eax, eax
- jns equ2
- or [(lab di).flags], m_sign
-equ2:
-ENDIF
- test [flist], m_lsts
- jnz equret
- sta dx
- mov di, offset lstorg
- mov ax, ' ='
- test eax, eax
- jns equ3
- mov ah, '-'
- neg dx
-IF LABELS_32
- xor eax, 0ffff0000h
-ENDIF
-equ3: stosw
- lda dx
-IF LABELS_32
- call phdword
-ELSE
- call phword
-ENDIF
- mov [lstidx], di
-equret: ret
-
-nolabd: error e_label
-
-chkhon: testfl m_hdr
- jnz equret
- error e_hoff
-
-p_org: call spaces
- lodsw
- and al, 0dfh
-IF RELOC_ORG
- cmp ax, ':R'
- jne norgr
- call chorg
- call getuns
- jc unknow
- jmp setori
-norgr:
-ENDIF
- cmp ax, ':F'
- je orgff
- cmp ax, ':A'
- je orgaf
- dec si
- dec si
- jmp orget
-orgff: setfl m_rqff
-orgaf: setfl m_rorg
- call chkhon
-orget: call getuns
- jc unknow
- testflag [fillfl], m_fillpo
- jz nforg
- setflag [fillfl], m_fillrq
- testflag [fillfl], m_fillen
- jnz setorg
-nforg: maskflag [fillfl], not m_fillrq
-setorg: resfl m_norg
-IF RELOC_ORG
- mov [ldorg], ax
-setori:
-ENDIF
- mov [origin], ax
- ret
-
-p_rui: call chkhon
- mov ah, 2
- call nforg
- call spauns
- call savwor
- maskflag [fillfl], not m_fillpo
- ret
-
-valuco: call getval
- jc unknow
- call get
- cmp al, ','
- jne badfun
- mov ax, [val]
- ret
-badfun: error e_func
-
-dtan0: cmp al, 'A'
- je dtan1
- cmp al, 'B'
- je dtan1
- cmp al, 'L'
- je dtan1
- cmp al, 'H'
- je dtan1
- cmp al, 'R'
- jne dtab1
- jmp dtar1
-
-dtat0: cmp al, 'C'
- je dtat1j
- cmp al, 'D'
- jne dtab1
-dtat1j: dec si
- mov [cod], al
- jmp dtat1
-
-p_dta: call spaces
-dta1: lodsw
- and al, 0dfh
- cmp ah, '('
- je dtan0
- cmp ah, "'"
- je dtat0
- cmp ah, '"'
- je dtat0
-dtab1: dec si
- dec si
- mov al, ' '
-dtan1: mov [cod], al
-
-dtan2: lodsd
- and eax, 0ffdfdfdfh
- cmp eax, '(NIS'
- jne dtansi
- call valuco
- mov [sinadd], eax
- call valuco
- mov [sinamp], eax
- call getpos
- mov [sinsiz], ax
- mov [sinmin], 0
- dec ax
- mov [sinmax], ax
- call get
- cmp al, ')'
- je presin
- cmp al, ','
- jne badfun
- call valuco
- test eax, eax
- js badfun
- mov [sinmin], ax
- call getuns
- jc unknow
- cmp ax, [sinmin]
- jb badfun
- mov [sinmax], ax
- lodsb
- call chkpar
-presin: finit
- fldpi
- fld st
- faddp st(1), st
- fidiv [sinsiz]
-gensin: fild [sinmin]
- fmul st, st(1)
- fsin
- fimul [sinamp]
- fiadd [sinadd]
- fistp [dword val]
- inc [sinmin]
- mov eax, [dword val]
- call wrange
- jmp dtasto
-
-dtansi: sub si, 4
- call getval
-dtasto: mov al, [cod]
- cmp al, 'A'
- je dtana
- jpass1 dtans
- cmp al, 'L'
- je dtanl
- cmp al, 'H'
- je dtanh
- mov eax, [dword val]
- call brange
- jmp dtans
-
-dtana: mov ax, [val]
- call savwor
- jmp dtanx
-
-dtanl: mov al, [byte low val]
- jmp dtans
-
-dtanh: mov al, [byte high val]
-
-dtans: call savbyt
-dtanx: mov ax, [sinmin]
- cmp ax, [sinmax]
- jbe gensin
- cmp [cod], ' '
- je dtanxt
- lodsb
- cmp al, ','
- je dtan2
-dtanp: push offset dtanxt
-chkpar: cmp al, ')'
- je paret
- error e_paren
-
-unknow: error e_uknow
-
-dtat1: mov di, offset tlabel
- call rstr
- lodsb
- mov ah, 80h
- cmp al, '*'
- je dtat2
- dec si
- xor ah, ah
-dtat2: push si
- mov si, dx
-dtatm: lodsb
- xor al, ah
- cmp [cod], 'D'
- jne ascinx
- mov dl, 60h
- and dl, al
- jz ascin1
- cmp dl, 60h
- je ascinx
- sub al, 60h
-ascin1: add al, 40h
-ascinx: push ax cx
- call savbyt
- pop cx ax
- loop dtatm
- pop si
-dtanxt: lodsb
- cmp al, ','
- je dta1
- dec si
-paret: ret
-
-; Zapisz liczbe rzeczywista
-dtar1:
-dtar2: xor bx, bx
- xor edx, edx
- xor cx, cx
- call getsgn
-dreal1: call getdig
- jnc dreal2
- cmp al, '.'
- je drealp
- test bh, bh
- jnz drealz
-ilchar: error e_char
-dreal2: mov bh, 1
- test al, al
- jz dreal1
- dec cx
-dreal3: inc cx
- call putdig
- call getdig
- jnc dreal3
- cmp al, '.'
- je drealp
- jmp dreale
-dreal5: test edx, edx
- jnz dreal9
- test bl, bl
- jnz dreal9
- dec cx
-dreal9: call putdig
-drealp: call getdig
- jnc dreal5
-dreale: and al, 0dfh
- cmp al, 'E'
- jne drealf
- call getsgn
- call getdig
- jc ilchar
- mov ah, al
- call getdig
- jnc dreal4
- shr ax, 8
- dec si
-dreal4: inc si
- aad
- add di, di
- jnc drealn
- neg ax
-drealn: add cx, ax
-drealf: test edx, edx
- jnz drealx
- test bl, bl
- jnz drealx
-drealz: xor ax, ax
- xor edx, edx
- jmp dreals
-drealx: add cx, 80h
- cmp cx, 20h
- js drealz
- cmp cx, 0e2h
- jnb toobig
- add di, di
- rcr cl, 1
- mov al, 10h
- jc dreal7
- cmp bl, al
- mov al, 1
- jb dreal7
- shrd edx, ebx, 4
- shr bl, 4
- jmp dreal8
-dreal6: shld ebx, edx, 4
- shl edx, 4
-dreal7: cmp bl, al
- jb dreal6
-dreal8: lda cx
- mov ah, bl
- rol edx, 16
-dreals: push edx
- call savwor
- pop ax
- xchg ah, al
- call savwor
- pop ax
- xchg ah, al
- call savwor
- dec si
- lodsb
- cmp al, ','
- je dtar2
- jmp dtanp
-
-putdig: cmp bl, 10h
- jnb rlret
- shld ebx, edx, 4
- shl edx, 4
- add dl, al
- ret
-
-getsgn: call get
- cmp al, '-'
- stc
- je sgnret
- cmp al, '+'
- je sgnret ; C=0
- dec si
-sgnret: rcr di, 1
-rlret: ret
-
-getdig: call get
- cmp al, '0'
- jb rlret
- cmp al, '9'+1
- cmc
- jb rlret
- sub al, '0'
- ret
-
-p_icl: call rfname
- pop ax
- push di
- call linend
- mov dx, offset (icl).nam
- add dx, [iclen]
- pop di
- call adasx
- jmp opfile
-
-p_ins: call rfname
- xor eax, eax
- mov [insofs], eax
- mov [inslen], ax
- lodsb
- cmp al, ','
- jne p_ii2
- push di
- call getval
- jc unknow
- mov [insofs], eax
- lodsb
- cmp al, ','
- jne p_ii1
- call getpos
- mov [inslen], ax
- inc si
-p_ii1: pop di
-p_ii2: dec si
- push si
- call fopen
- mov dx, [word insofs]
- mov cx, [word high insofs]
- mov ax, 4200h
- jcxz p_ip1
- mov al, 2
-p_ip1: call fsrce
-p_in1: mov cx, [inslen]
- jcxz p_in2
- test ch, ch
- jz p_in3
-p_in2: mov cx, 256
-p_in3: mov dx, offset tlabel
- call fread
- jz p_inx
- cwde
- mov bx, [iclen]
- mov bx, [(icl bx).prev]
- add [(icl bx).line], eax
- mov si, offset tlabel
- push ax
- sta cx
-p_inp: lodsb
- push cx
- call savbyt
- pop cx
- loop p_inp
- pop ax
- cmp [inslen], 0
- jz p_in1
- sub [inslen], ax
- jnz p_in1
-p_inx: call fclose
- pop si
- cmp [inslen], 0
- jz rlret
- error e_fshor
-
-p_end: pop ax
- call linend
- jmp filend
-
-p_ift: shl [elflag], 1
- jc etmift
- shl [cmflag], 1
- shl [skflag], 1
-ift1: cmp [skflag], 0
- jnz cndret
- call spaval
- jc unknow
- test eax, eax
- jnz ift2
-ift0: setflag [skflag], 1
- ret
-
-p_els: bts [elflag], 0
- jc cnderr
-ift2: bts [cmflag], 0
- jc ift0
- maskflag [skflag], not 1
-cndret: ret
-
-p_eli: maskflag [skflag], not 1
- testflag [elflag], 1
- jz ift1
-cnderr: cmp [elflag], 1
- je emift
- error e_eifex
-
-p_eif: shr [skflag], 1
- shr [cmflag], 1
- shr [elflag], 1
- jnz cndret
-emift: error e_mift
-
-etmift: error e_tmift
-
-IF EXE
- ENDS
- DATASEG
-; ORG 0
-ENDIF
-; addressing modes:
-; 0-@ 1-# 2-A 3-Z 4-A,X 5-Z,X 6-A,Y 7-Z,Y 8-(Z,X) 9-(Z),Y 10-(A)
-lentab db 1,2,3,2,3,2,3,2,2,2,3
-acctab db 0,9,0dh,5,1dh,15h,19h,19h,1,11h,0
-srttab db 0ah,0,0eh,6,1eh,16h
-lditab db 0,0,0ch,4,1ch,14h,1ch,14h
-inwtab db 0eeh,0e6h,0feh,0f6h
-; pseudo-adr modes: 2-X+ 3-X- 4-Y+ 5-Y-
-; +8-,0) +16-),0
-sfxtab db 0,0,0e8h,0cah,0c8h,088h
-
-movtab movt <0a0h,p_ac1,080h,p_ac1,0,0>
- movt <0a2h,p_ld1,086h,p_st1,0e8h,0cah>
- movt <0a0h,p_ld1,084h,p_st1,0c8h,088h>
-
-comtab = $
- cmd ADC0060p_acc
- cmd ADD8018p_ads
- cmd AND0020p_acc
- cmd ASL0000p_srt
- cmd BCC0090p_bra
- cmd BCS00b0p_bra
- cmd BEQ00f0p_bra
- cmd BIT002cp_bit
- cmd BMI0030p_bra
- cmd BNE00d0p_bra
- cmd BPL0010p_bra
- cmd BRK0000p_imp
- cmd BVC0050p_bra
- cmd BVS0070p_bra
- cmd CLC0018p_imp
- cmd CLD00d8p_imp
- cmd CLI0058p_imp
- cmd CLV00b8p_imp
- cmd CMP00c0p_acc
- cmd CPX00e0p_cpi
- cmd CPY00c0p_cpi
- cmd DEC00c0p_srt
- cmd DEX00cap_imp
- cmd DEY0088p_imp
- cmd DTA8000p_dta
- cmd EIFe000p_eif
- cmd ELIe000p_eli
- cmd ELSe000p_els
- cmd ENDe000p_end
- cmd EOR0040p_acc
- cmd EQUe000p_equ
- cmd ERTe000p_ert
- cmd ICLe000p_icl
- cmd IFTe000p_ift
- cmd INC00e0p_srt
- cmd INIf0e2p_rui
- cmd INSf000p_ins
- cmd INW8000p_inw
- cmd INX00e8p_imp
- cmd INY00c8p_imp
- cmd JCC80b0p_juc
- cmd JCS8090p_juc
- cmd JEQ80d0p_juc
- cmd JMI8010p_juc
- cmd JMP004cp_jmp
- cmd JNE80f0p_juc
- cmd JPL8030p_juc
- cmd JSR0020p_jsr
- cmd JVC8070p_juc
- cmd JVS8050p_juc
- cmd LDA00a0p_acc
- cmd LDX00a2p_ldi
- cmd LDY00a0p_ldi
- cmd LSR0040p_srt
- cmd MVA8000p_mvs
- cmd MVX8008p_mvs
- cmd MVY8010p_mvs
- cmd MWA8000p_mws
- cmd MWX8008p_mws
- cmd MWY8010p_mws
- cmd NOP00eap_imp
- cmd OPTe000p_opt
- cmd ORA0000p_acc
- cmd ORGf000p_org
- cmd PHA0048p_imp
- cmd PHP0008p_imp
- cmd PLA0068p_imp
- cmd PLP0028p_imp
- cmd RCC8890p_rep
- cmd RCS88b0p_rep
- cmd REQ88f0p_rep
- cmd RMI8830p_rep
- cmd RNE88d0p_rep
- cmd ROL0020p_srt
- cmd ROR0060p_srt
- cmd RPL8810p_rep
- cmd RTI0040p_imp
- cmd RTS0060p_imp
- cmd RUNf0e0p_rui
- cmd RVC8850p_rep
- cmd RVS8870p_rep
- cmd SBC00e0p_acc
- cmd SCC8490p_skp
- cmd SCS84b0p_skp
- cmd SEC0038p_imp
- cmd SED00f8p_imp
- cmd SEI0078p_imp
- cmd SEQ84f0p_skp
- cmd SMI8430p_skp
- cmd SNE84d0p_skp
- cmd SPL8410p_skp
- cmd STA0080p_acc
- cmd STX0086p_sti
- cmd STY0084p_sti
- cmd SUB8038p_ads
- cmd SVC8450p_skp
- cmd SVS8470p_skp
- cmd TAX00aap_imp
- cmd TAY00a8p_imp
- cmd TSX00bap_imp
- cmd TXA008ap_imp
- cmd TXS009ap_imp
- cmd TYA0098p_imp
-comend = $
-
-operpa = $
- opr 1ret
- opr 6add
- opr 6sub
- opr 7mul
- opr 7div
- opr 7mod
- opr 7and
- opr 6or
- opr 6xor
- opr 5equ
- opr 5les
- opr 5grt
- opr 7sal
- opr 7sar
- opr 5leq
- opr 5geq
- opr 5neq
- opr 5neq
- opr 5equ
- opr 3anl
- opr 2orl
- opr 8plu
- opr 8neg
- opr 8low
- opr 8hig
- opr 4nol
- opr 8not
-
-opert2 db '<<>><=>=<>!===&&||'
-noper2 = ($-opert2)/2
-opert1 db '+-*/%&|^=<>'
-noper1 = $-opert1
-opert0 db '+-<>!~'
-noper0 = $-opert0
-
-IF FUNCTIONS
-funtxt db 'ABS(ANG(HYP(MAX(MIN(SQR('
-nfun = ($-funtxt)/4
-funvec dw f_abs,f_ang,f_hyp,f_max,f_min,f_sqr
-ENDIF
-
-opttxt db 'F-F+G-G+H-H+L-L+O-O+'
-optvec dw optf0,optf1,optg0,optg1,opth0,opth1,optl0,optl1,opto0,opto1
-
-cndtxt dd 'DNE','TFI','ILE','SLE','FIE'
-cndvec dw pofend,0,p_ift,0,p_eli,0,p_els,0,p_eif
-
-swilet db 'UT'
-IF CONVERT_TO_TABS
- db 'S'
-ENDIF
- db 'QPO'
-IF POOR_MAN_MAKE
- db 'N'
-ENDIF
- db 'LIEDC'
-NUM_SWITCHES = $-swilet
-
-hello db 'xasm 2.6.1'
-IF SET_WIN_TITLE
-titfin db 0,10
-ELSE
- db eol
-ENDIF
-hellen = $-hello
-usgtxt db "Syntax: XASM source [options]",eol
- db "/c Include false conditionals in listing",eol
- db "/d:label=value Define a label",eol
- db "/e Set environment variables ERRFILE and ERRLINE",eol
- db "/i Don't list included files",eol
- db "/l[:filename] Generate listing",eol
-IF POOR_MAN_MAKE
- db "/n Assemble only if source file is newer than object file",eol
-ENDIF
- db "/o:filename Set object file name",eol
- db "/p Print fully qualified file names in listing and error messages",eol
- db "/q Suppress info messages",eol
-IF CONVERT_TO_TABS
- db "/s Don't convert spaces to tabs in listing",eol
-ENDIF
- db "/t[:filename] List label table",eol
- db "/u Warn of unused labels",eot
-IF POOR_MAN_MAKE
-oldtxt db 'Source file is older than object file - not assembling',cr
-ENDIF
-envtxt db 'Can''t modify environment',cr
-objtxt db 'Writing object file...',eot
-lsttxt db 'Writing listing file...'
-eoltxt db eot
-srctxt db 'Source: '
-srctxl = $-srctxt
-tabtxt db 'Label table:',eol
-tabtxl = $-tabtxt
-lintxt db ' lines of source assembled',eot
-byttxt db ' bytes written to the object file',eot
-dectxt db 10 dup(' '),'$'
-wartxt db 'WARNING: '
-wartxl = $-wartxt
-w_bugjp db 'Buggy indirect jump',eol
-w_bras db 'Plain branch instruction would be sufficient',eol
-w_ulab db 'Unused label',eol
-w_skif db 'Skipping only the first instruction',eol
-w_repl db 'Repeating only the last instruction',eol
-errtxt db 'ERROR: '
-errtxl = $-errtxt
-e_open db 'Can''t open file',cr
-e_read db 'File read error',cr
-e_crobj db 'Can''t write to the object file',cr
-e_wrobj db 'Error writing to the object file',cr
-e_crlst db 'Can''t write to the listing file',cr
-e_wrlst db 'Error writing to the listing file',cr
-e_icl db 'Too many files nested',cr
-e_long db 'Line too long',cr
-e_uneol db 'Unexpected end of line',cr
-e_char db 'Illegal character',cr
-e_twice db 'Label declared twice',cr
-e_inst db 'Illegal instruction',cr
-e_nbig db 'Number too big',cr
-e_xtra db 'Extra characters on line',cr
-e_label db 'Label name required',cr
-e_str db 'String error',cr
-e_orgs db 'Too many ORGs',cr
-e_paren db 'Need parenthesis',cr
-e_tlab db 'Too many labels',cr
-e_amod db 'Illegal addressing mode',cr
-e_bra db 'Branch out of range by $'
-brout db ' bytes',cr
-e_func db 'Bad or missing function parameter',cr
-e_spac db 'Space expected',cr
-e_opt db 'Invalid option',cr
-e_over db 'Arithmetic overflow',cr
-e_div0 db 'Divide by zero',cr
-e_range db 'Value out of range',cr
-e_uknow db 'Label not defined before',cr
-e_undec db 'Undeclared label',cr
-e_fref db 'Illegal forward reference',cr
-e_wpar db 'Use square brackets instead',cr
-e_brack db 'Unmatched bracket',cr
-e_user db 'User error',cr
-e_tmift db 'Too many IFTs nested',cr
-e_eifex db 'EIF expected',cr
-e_mift db 'Missing IFT',cr
-e_meif db 'Missing EIF',cr
-e_norg db 'No ORG specified',cr
-e_fshor db 'File is too short',cr
-e_hoff db 'Illegal when Atari file headers disabled',cr
-e_crep db 'Can''t repeat this directive',cr
-e_opcod db 'Can''t get opcode of this',cr
-e_ropco db 'Nested opcodes not supported',cr
-e_mopco db 'Missing ''}''',cr
-e_pair db 'Can''t pair this directive',cr
-e_skit db 'Can''t skip over this',cr
-e_repa db 'No instruction to repeat',cr
-e_5200 db 'There''s no PIA chip in Atari 5200',cr
-e_fill db 'Can''t fill from higher to lower memory location',cr
-e_nomac db 'Sorry, Mac EOLs are no longer supported',cr
-e_ctr db '''#'' is allowed only in repeated lines',cr
-;;e_fatal db 'Internal error. Please report to fox@scene.pl',cr
-clitxt db 'command line'
-clitxl = $-clitxt
-
-fillbyt db 0ffh
-exitcod dw 4c00h
-ohand dw nhand
-lhand dw nhand
-flags dw m_norg+m_rorg+m_rqff+m_hdr+m_wobj
-swits dw 0
-lines dd 0
-bytes dd 0
-srcen dw 0
-iclen dw t_icl
-defen dw t_def
-laben dw t_lab
-pslab dw t_lab
-elflag dd 1
-cmflag dd 0
-skflag dd 0
-loopctr dd -1
-sinmin dw 1
-sinmax dw 0
-sinadd dd ?
-sinamp dd ?
-sinsiz dw ?
-fillfl dw ?
-flist db ?
-fslen dw ?
-times dw ?
-cmdvec dw ?
-scdvec dw ?
-opcosp dw ?
-insofs dd ?
-inslen dw ?
-IF RELOC_ORG
-ldorg dw ?
-ENDIF
-origin dw ?
-curorg dw ?
-orgvec dw ?
-defvec dw ?
-reporg dw ?
-eolpos dw ?
-lstidx dw ?
-labvec dw ?
-obufpt dw ?
-oword dw ?
-IF POOR_MAN_MAKE
-objmod dd ?
-ENDIF
-op1 dd ?
- dw ?
-op2 dd ?
- dw ?
-tempsi dw ?
-errmsg dw ?
-envofs dw ?
-envseg dw ?
-envnam dw ?
-envnum dw ?
-envlen dw ?
-chbuf db ?
-
-var = $
-MACRO bb _name
-_name&o = $-var
-_name equ byte bp+_name&o
- db ?
- ENDM
-
-MACRO bw _name
-_name&o = $-var
-_name equ word bp+_name&o
- dw ?
- ENDM
-
- bw val
- dw ?
- bb amod
- db ?
- bb ukp1
- db ?
- bb cod
-
-var2 db ($-var) dup(?)
-
-IF COMPAK
-ELSE
- undata
-ENDIF
-
-t_lab db l_lab dup(?)
-
-IF EXE
-stak db 400h dup(?)
-staken = $
- ENDS
- STACK 100h
-ENDIF
-
- ENDS
- END start
diff --git a/xasm.d b/xasm.d
new file mode 100644
index 0000000..c503d0f
--- /dev/null
+++ b/xasm.d
@@ -0,0 +1,3149 @@
+// xasm 3.0.0 by Piotr Fusik
+// See the homepage at http://xasm.atari.org.
+// This source code is free, redistributable and modifiable
+// under the terms of the Artistic License (attached as artistic.txt).
+
+private import std.math;
+private import std.stream;
+private import std.string;
+
+version (Windows) {
+
+ extern (Windows) export int GetFullPathNameA(char* lpFileName, int nBufferLength, char* lpBuffer, char** lpFilePart);
+
+ char[] getFullPath(char[] filename) {
+ char[260] fullPath;
+ char* filePath;
+ int r = GetFullPathNameA(cast(char*) (filename ~ "\0"), 260, cast(char*) fullPath, &filePath);
+ if (r > 0) {
+ return fullPath[0 .. r].dup;
+ }
+ return filename;
+ }
+
+ const char[] OPTION_P_DESC = "Print fully qualified file names in listing and error messages";
+
+} else {
+
+ char[] getFullPath(char[] filename) {
+ return filename;
+ }
+
+ const char[] OPTION_P_DESC = "Ignored for compatibility";
+}
+
+const char[] TITLE = "xasm 3.0.0";
+
+char[] sourceFilename = null;
+
+bit[26] options;
+
+char[][26] optionParameters;
+
+char[][] commandLineDefinitions = null;
+
+int exitCode = 0;
+
+int totalLines;
+
+bit pass2 = false;
+
+bit optionFill; // opt f
+
+bit option5200; // opt g
+
+bit optionHeaders; // opt h
+
+bit optionListing; // opt l
+
+bit optionObject; // opt o
+
+char[] line;
+
+int column;
+
+class Location {
+
+ char[] filename;
+
+ int lineNo = 0;
+
+ this(char[] filename) {
+ this.filename = filename;
+ }
+}
+
+Location[] locations;
+
+Location currentLocation;
+
+bit foundEnd;
+
+class AssemblyError : Exception {
+
+ this(char[] msg) {
+ super(msg);
+ }
+}
+
+class Label {
+
+ int value;
+
+ bit unused = true;
+
+ bit unknownInPass1 = false;
+
+ bit passed = false;
+
+ this(int value) {
+ this.value = value;
+ }
+}
+
+Label[char[]] labelTable;
+
+Label currentLabel;
+
+typedef int function(int a, int b) OperatorFunction;
+
+bit inOpcode = false;
+
+struct ValOp {
+
+ int value;
+
+ OperatorFunction func;
+
+ int priority;
+}
+
+ValOp[] valOpStack;
+
+int value;
+
+bit unknownInPass1;
+
+enum AddrMode {
+ ACCUMULATOR = 0,
+ IMMEDIATE = 1,
+ ABSOLUTE = 2,
+ ZEROPAGE = 3,
+ ABSOLUTE_X = 4,
+ ZEROPAGE_X = 5,
+ ABSOLUTE_Y = 6,
+ ZEROPAGE_Y = 7,
+ INDIRECT_X = 8,
+ INDIRECT_Y = 9,
+ INDIRECT = 10,
+ ABS_OR_ZP = 11, // temporarily used in readAddrMode
+ STANDARD_MASK = 15,
+ INCREMENT = 0x20,
+ DECREMENT = 0x30,
+ ZERO = 0x40
+}
+
+AddrMode addrMode;
+
+int origin = -1;
+
+int loadOrigin;
+
+int loadingOrigin;
+
+ushort[] blockEnds;
+
+int blockIndex;
+
+bit repeating; // line
+
+int repeatCounter; // line
+
+bit instructionBegin;
+
+bit pairing;
+
+bit willSkip;
+
+bit skipping;
+
+ushort[] skipOffsets;
+
+int skipOffsetsIndex = 0;
+
+int repeatOffset; // instruction repeat
+
+bit wereManyInstructions;
+
+typedef void function(int move) MoveFunction;
+
+int value1;
+
+AddrMode addrMode1;
+
+int value2;
+
+AddrMode addrMode2;
+
+struct IfContext {
+
+ bit condition;
+
+ bit wasElse;
+
+ bit aConditionMatched;
+}
+
+IfContext[] ifContexts;
+
+Stream listingStream = null;
+
+char[32] listingLine;
+
+int listingColumn;
+
+char[] lastListedFilename = null;
+
+Stream objectStream = null;
+
+int objectBytes = 0;
+
+bit getOption(char letter) {
+ assert(letter >= 'a' && letter <= 'z');
+ return options[letter - 'a'];
+}
+
+void warning(char[] msg) {
+ if (!(line is null)) {
+ stderr.printf("%.*s\n", line);
+ }
+ stderr.printf("%.*s (%d) WARNING: %.*s\n",
+ currentLocation.filename,
+ currentLocation.lineNo, msg
+ );
+ exitCode = 1;
+}
+
+void illegalCharacter() {
+ throw new AssemblyError("Illegal character");
+}
+
+bit eol() {
+ return column >= line.length;
+}
+
+char readChar() {
+ if (eol()) {
+ throw new AssemblyError("Unexpected end of line");
+ }
+ return line[column++];
+}
+
+int readDigit(int base) {
+ if (eol()) return -1;
+ int r = line[column];
+ if (r >= '0' && r <= '9') {
+ r -= '0';
+ } else {
+ r &= 0xdf;
+ if (r >= 'A' && r <= 'Z') {
+ r -= 'A' - 10;
+ } else {
+ return -1;
+ }
+ }
+ if (r < base) {
+ column++;
+ return r;
+ }
+ return -1;
+}
+
+int readNumber(int base) {
+ long r = readDigit(base);
+ if (r < 0) {
+ illegalCharacter();
+ }
+ do {
+ int d = readDigit(base);
+ if (d < 0) {
+ return cast(int) r;
+ }
+ r = r * base + d;
+ } while (r <= 0x7fffffff);
+ throw new AssemblyError("Number too big");
+}
+
+void readSpaces() {
+ switch (readChar()) {
+ case '\t':
+ case ' ':
+ break;
+ default:
+ throw new AssemblyError("Space expected");
+ }
+ while (!eol()) {
+ switch (line[column]) {
+ case '\t':
+ case ' ':
+ column++;
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+char[] readLabel() {
+ char[] label = "";
+ while (!eol()) {
+ char c = line[column++];
+ if (c >= '0' && c <= '9' || c == '_') {
+ label ~= c;
+ continue;
+ }
+ c &= 0xdf;
+ if (c >= 'A' && c <= 'Z') {
+ label ~= c;
+ continue;
+ }
+ column--;
+ break;
+ }
+ return label >= "A" ? label : cast(char[]) null;
+}
+
+void readComma() {
+ if (readChar() != ',') {
+ throw new AssemblyError("Bad or missing function parameter");
+ }
+}
+
+char[] readInstruction() {
+ char[] r = "";
+ for (int i = 0; i < 3; i++) {
+ char c = readChar() & 0xdf;
+ if (c < 'A' || c > 'Z') {
+ throw new AssemblyError("Illegal instruction");
+ }
+ r ~= c;
+ }
+ return r;
+}
+
+char[] readFunction() {
+ if (column + 5 >= line.length) return "";
+ if (line[column + 3] != '(') return "";
+ char[] r = "";
+ for (int i = 0; i < 3; i++) {
+ char c = line[column + i] & 0xdf;
+ if (c < 'A' || c > 'Z') return "";
+ r ~= c;
+ }
+ column += 4;
+ return r;
+}
+
+char[] readFilename() {
+ char[] filename = "";
+ readSpaces();
+ char delimiter = readChar();
+ switch (delimiter) {
+ case '"':
+ case '\'':
+ char c;
+ while ((c = readChar()) != delimiter) {
+ filename ~= c;
+ }
+ return filename;
+ default:
+ illegalCharacter();
+ }
+}
+
+void readStringChar(char c) {
+ if (readChar() != c) {
+ throw new AssemblyError("String error");
+ }
+}
+
+ubyte[] readString() {
+ if (eol()) return null;
+ ubyte[] r;
+ char delimiter = readChar();
+ switch (delimiter) {
+ case '"':
+ case '\'':
+ for (;;) {
+ char c = readChar();
+ if (c == delimiter) {
+ if (eol()) return r;
+ if (line[column] != delimiter) {
+ if (line[column] == '*') {
+ column++;
+ foreach (inout ubyte b; r) {
+ b ^= 0x80;
+ }
+ }
+ return r;
+ }
+ column++;
+ }
+ r ~= cast(ubyte) c;
+ }
+ default:
+ column--;
+ return null;
+ }
+}
+
+void checkNoExtraCharacters() {
+ if (eol()) return;
+ switch (line[column]) {
+ case '\t':
+ case ' ':
+ return;
+ default:
+ throw new AssemblyError("Extra characters on line");
+ }
+}
+
+void checkOriginDefined() {
+ if (origin < 0) {
+ throw new AssemblyError("No ORG specified");
+ }
+}
+
+int operatorPlus(int a, int b) {
+ return b;
+}
+
+int operatorMinus(int a, int b) {
+ return -b;
+}
+
+int operatorLow(int a, int b) {
+ return b & 0xff;
+}
+
+int operatorHigh(int a, int b) {
+ return (b >> 8) & 0xff;
+}
+
+int operatorLogicalNot(int a, int b) {
+ return !b;
+}
+
+int operatorBitwiseNot(int a, int b) {
+ return ~b;
+}
+
+int operatorAdd(int a, int b) {
+ long r = cast(long) a + b;
+ if (r < -0x80000000L || r > 0x7fffffffL) {
+ throw new AssemblyError("Arithmetic overflow");
+ }
+ return a + b;
+}
+
+int operatorSubtract(int a, int b) {
+ long r = cast(long) a - b;
+ if (r < -0x80000000L || r > 0x7fffffffL) {
+ throw new AssemblyError("Arithmetic overflow");
+ }
+ return a - b;
+}
+
+int operatorMultiply(int a, int b) {
+ long r = cast(long) a * b;
+ if (r < -0x80000000L || r > 0x7fffffffL) {
+ throw new AssemblyError("Arithmetic overflow");
+ }
+ return a * b;
+}
+
+int operatorDivide(int a, int b) {
+ if (b == 0) {
+ throw new AssemblyError("Divide by zero");
+ }
+ return a / b;
+}
+
+int operatorModulus(int a, int b) {
+ if (b == 0) {
+ throw new AssemblyError("Divide by zero");
+ }
+ return a % b;
+}
+
+int operatorAnd(int a, int b) {
+ return a & b;
+}
+
+int operatorOr(int a, int b) {
+ return a | b;
+}
+
+int operatorXor(int a, int b) {
+ return a ^ b;
+}
+
+int operatorEqual(int a, int b) {
+ return a == b;
+}
+
+int operatorNotEqual(int a, int b) {
+ return a != b;
+}
+
+int operatorLess(int a, int b) {
+ return a < b;
+}
+
+int operatorGreater(int a, int b) {
+ return a > b;
+}
+
+int operatorLessEqual(int a, int b) {
+ return a <= b;
+}
+
+int operatorGreaterEqual(int a, int b) {
+ return a >= b;
+}
+
+int operatorShiftLeft(int a, int b) {
+ if (b < 0) {
+ return operatorShiftRight(a, -b);
+ }
+ if (a != 0 & b >= 32) {
+ throw new AssemblyError("Arithmetic overflow");
+ }
+ long r = cast(long) a << b;
+ if (r & 0xffffffff00000000L) {
+ throw new AssemblyError("Arithmetic overflow");
+ }
+ return a << b;
+}
+
+int operatorShiftRight(int a, int b) {
+ if (b < 0) {
+ return operatorShiftLeft(a, -b);
+ }
+ if (b >= 32) {
+ b = 31;
+ }
+ return a >> b;
+}
+
+int operatorLogicalAnd(int a, int b) {
+ return a && b;
+}
+
+int operatorLogicalOr(int a, int b) {
+ return a || b;
+}
+
+void pushValOp(int value, OperatorFunction func, int priority) {
+ ValOp valOp;
+ valOp.value = value;
+ valOp.func = func;
+ valOp.priority = priority;
+ valOpStack ~= valOp;
+}
+
+void readValue() {
+ assert(valOpStack.length == 0);
+ unknownInPass1 = false;
+ int priority = 0;
+ do {
+ int operand;
+ char c = readChar();
+ switch (c) {
+ case '[':
+ priority += 10;
+ continue;
+ case '+':
+ pushValOp(0, &operatorPlus, priority + 8);
+ continue;
+ case '-':
+ pushValOp(0, &operatorMinus, priority + 8);
+ continue;
+ case '<':
+ pushValOp(0, &operatorLow, priority + 8);
+ continue;
+ case '>':
+ pushValOp(0, &operatorHigh, priority + 8);
+ continue;
+ case '!':
+ pushValOp(0, &operatorLogicalNot, priority + 4);
+ continue;
+ case '~':
+ pushValOp(0, &operatorBitwiseNot, priority + 8);
+ continue;
+ case '(':
+ throw new AssemblyError("Use square brackets instead");
+ case '*':
+ checkOriginDefined();
+ operand = origin;
+ break;
+ case '#':
+ if (!repeating) {
+ throw new AssemblyError("'#' is allowed only in repeated lines");
+ }
+ operand = repeatCounter;
+ break;
+ case '\'':
+ case '"':
+ operand = readChar();
+ if (operand == c) {
+ readStringChar(c);
+ }
+ readStringChar(c);
+ if (!eol() && line[column] == '*') {
+ column++;
+ operand ^= 0x80;
+ }
+ break;
+ case '^':
+ switch (readChar()) {
+ case '0':
+ operand = option5200 ? 0xc000 : 0xd000;
+ break;
+ case '1':
+ operand = option5200 ? 0xc010 : 0xd010;
+ break;
+ case '2':
+ operand = option5200 ? 0xe800 : 0xd200;
+ break;
+ case '3':
+ if (option5200) {
+ throw new AssemblyError("There's no PIA chip in Atari 5200");
+ }
+ operand = 0xd300;
+ break;
+ case '4':
+ operand = 0xd400;
+ break;
+ default:
+ illegalCharacter();
+ }
+ int d = readDigit(16);
+ if (d < 0) {
+ illegalCharacter();
+ }
+ operand += d;
+ break;
+ case '{':
+ if (inOpcode) {
+ throw new AssemblyError("Nested opcodes not supported");
+ }
+ ValOp[] savedValOpStack = valOpStack;
+ AddrMode savedAddrMode = addrMode;
+ bit savedUnknownInPass1 = unknownInPass1;
+ bit savedInstructionBegin = instructionBegin;
+ valOpStack.length = 0;
+ inOpcode = true;
+ assemblyInstruction(readInstruction());
+ if (readChar() != '}') {
+ throw new AssemblyError("Missing '}'");
+ }
+ assert(!instructionBegin);
+ inOpcode = false;
+ valOpStack = savedValOpStack;
+ addrMode = savedAddrMode;
+ unknownInPass1 = savedUnknownInPass1;
+ instructionBegin = savedInstructionBegin;
+ operand = value;
+ break;
+ case '$':
+ operand = readNumber(16);
+ break;
+ case '%':
+ operand = readNumber(2);
+ break;
+ default:
+ column--;
+ if (c >= '0' && c <= '9') {
+ operand = readNumber(10);
+ break;
+ }
+ char[] label = readLabel();
+ if (label is null) {
+ illegalCharacter();
+ }
+ if (label in labelTable) {
+ Label l = labelTable[label];
+ operand = l.value;
+ l.unused = false;
+ if (pass2) {
+ if (l.passed) {
+ if (l.unknownInPass1) {
+ unknownInPass1 = true;
+ }
+ } else {
+ if (l.unknownInPass1) {
+ throw new AssemblyError("Illegal forward reference");
+ }
+ unknownInPass1 = true;
+ }
+ } else {
+ if (l.unknownInPass1) {
+ unknownInPass1 = true;
+ }
+ }
+ } else {
+ if (pass2) {
+ throw new AssemblyError("Undeclared label: " ~ label);
+ }
+ unknownInPass1 = true;
+ }
+ break;
+ }
+ while (!eol() && line[column] == ']') {
+ column++;
+ priority -= 10;
+ if (priority < 0) {
+ throw new AssemblyError("Unmatched bracket");
+ }
+ }
+ if (eol()) {
+ if (priority != 0) {
+ throw new AssemblyError("Unmatched bracket");
+ }
+ pushValOp(operand, &operatorPlus, 1);
+ } else {
+ switch (line[column++]) {
+ case '+':
+ pushValOp(operand, &operatorAdd, priority + 6);
+ break;
+ case '-':
+ pushValOp(operand, &operatorSubtract, priority + 6);
+ break;
+ case '*':
+ pushValOp(operand, &operatorMultiply, priority + 7);
+ break;
+ case '/':
+ pushValOp(operand, &operatorDivide, priority + 7);
+ break;
+ case '%':
+ pushValOp(operand, &operatorModulus, priority + 7);
+ break;
+ case '<':
+ switch (readChar()) {
+ case '<':
+ pushValOp(operand, &operatorShiftLeft, priority + 7);
+ break;
+ case '=':
+ pushValOp(operand, &operatorLessEqual, priority + 5);
+ break;
+ case '>':
+ pushValOp(operand, &operatorNotEqual, priority + 5);
+ break;
+ default:
+ column--;
+ pushValOp(operand, &operatorLess, priority + 5);
+ break;
+ }
+ break;
+ case '=':
+ switch (readChar()) {
+ default:
+ column--;
+ goto case '=';
+ case '=':
+ pushValOp(operand, &operatorEqual, priority + 5);
+ break;
+ }
+ break;
+ case '>':
+ switch (readChar()) {
+ case '>':
+ pushValOp(operand, &operatorShiftRight, priority + 7);
+ break;
+ case '=':
+ pushValOp(operand, &operatorGreaterEqual, priority + 5);
+ break;
+ default:
+ column--;
+ pushValOp(operand, &operatorGreater, priority + 5);
+ break;
+ }
+ break;
+ case '!':
+ switch (readChar()) {
+ case '=':
+ pushValOp(operand, &operatorNotEqual, priority + 5);
+ break;
+ default:
+ illegalCharacter();
+ }
+ break;
+ case '&':
+ switch (readChar()) {
+ case '&':
+ pushValOp(operand, &operatorLogicalAnd, priority + 3);
+ break;
+ default:
+ column--;
+ pushValOp(operand, &operatorAnd, priority + 7);
+ break;
+ }
+ break;
+ case '|':
+ switch (readChar()) {
+ case '|':
+ pushValOp(operand, &operatorLogicalOr, priority + 2);
+ break;
+ default:
+ column--;
+ pushValOp(operand, &operatorOr, priority + 6);
+ break;
+ }
+ break;
+ case '^':
+ pushValOp(operand, &operatorXor, priority + 6);
+ break;
+ default:
+ column--;
+ if (priority != 0) {
+ throw new AssemblyError("Unmatched bracket");
+ }
+ pushValOp(operand, &operatorPlus, 1);
+ break;
+ }
+ }
+ for (;;) {
+ int sp = valOpStack.length - 1;
+ if (sp <= 0 || valOpStack[sp].priority > valOpStack[sp - 1].priority) {
+ break;
+ }
+ int operand1 = valOpStack[sp - 1].value;
+ OperatorFunction func1 = valOpStack[sp - 1].func;
+ valOpStack[sp - 1] = valOpStack[sp];
+ valOpStack.length = sp;
+ if (pass2 || !unknownInPass1) { // skip operations if unknown operands
+ valOpStack[sp - 1].value = func1(operand1, valOpStack[sp - 1].value);
+ }
+ }
+ } while (valOpStack.length != 1 || valOpStack[0].priority != 1);
+ value = valOpStack[0].value;
+ valOpStack.length = 0;
+}
+
+debug int testValue(char[] l) {
+ line = l;
+ column = 0;
+ readValue();
+ printf("Value of %.*s is %x\n", line, value);
+ return value;
+}
+
+unittest {
+ assert(testValue("123") == 123);
+ assert(testValue("$1234abCd") == 0x1234abcd);
+ assert(testValue("%101") == 5);
+ assert(testValue("^07") == 0xd007);
+ assert(testValue("^1f") == 0xd01f);
+ assert(testValue("^23") == 0xd203);
+ assert(testValue("^31") == 0xd301);
+ assert(testValue("^49") == 0xd409);
+ assert(testValue("!0") == 1);
+ assert(testValue("<$1234") == 0x34);
+ assert(testValue(">$1234567") == 0x45);
+ assert(testValue("1+2") == 3);
+ assert(testValue("1+2*3") == 7);
+ assert(testValue("[1+2]*3") == 9);
+ assert(testValue("{nop}") == 0xea);
+ assert(testValue("{CLC}+{sec}") == 0x50);
+ assert(testValue("{Jsr}") == 0x20);
+ assert(testValue("{bit a:}") == 0x2c);
+ assert(testValue("{bIt $7d}") == 0x24);
+}
+
+void mustBeKnownInPass1() {
+ if (unknownInPass1) {
+ throw new AssemblyError("Label not defined before");
+ }
+}
+
+void readWord() {
+ readValue();
+ if ((!unknownInPass1 || pass2) && (value < -0xffff || value > 0xffff)) {
+ throw new AssemblyError("Value out of range");
+ }
+}
+
+void readUnsignedWord() {
+ readWord();
+ if ((!unknownInPass1 || pass2) && value < 0) {
+ throw new AssemblyError("Value out of range");
+ }
+}
+
+void readKnownPositive() {
+ readValue();
+ mustBeKnownInPass1();
+ if (value <= 0) {
+ throw new AssemblyError("Value out of range");
+ }
+}
+
+void optionalIncDec() {
+ if (eol()) return;
+ switch (line[column]) {
+ case '+':
+ column++;
+ addrMode += AddrMode.INCREMENT;
+ return;
+ case '-':
+ column++;
+ addrMode += AddrMode.DECREMENT;
+ return;
+ default:
+ return;
+ }
+}
+
+void readAddrMode() {
+ readSpaces();
+ char c = readChar();
+ switch (c) {
+ case '@':
+ addrMode = AddrMode.ACCUMULATOR;
+ return;
+ case '#':
+ case '<':
+ case '>':
+ addrMode = AddrMode.IMMEDIATE;
+ if (inOpcode && line[column] == '}') {
+ return;
+ }
+ readWord();
+ switch (c) {
+ case '#':
+ break;
+ case '<':
+ value &= 0xff;
+ break;
+ case '>':
+ value = (value >> 8) & 0xff;
+ break;
+ }
+ return;
+ case '(':
+ if (inOpcode) {
+ switch (readChar()) {
+ case ',':
+ switch (readChar()) {
+ case 'X':
+ case 'x':
+ break;
+ default:
+ illegalCharacter();
+ }
+ if (readChar() != ')') {
+ throw new AssemblyError("Need parenthesis");
+ }
+ addrMode = AddrMode.INDIRECT_X;
+ return;
+ case ')':
+ if (readChar() == ',') {
+ switch (readChar()) {
+ case 'Y':
+ case 'y':
+ break;
+ default:
+ illegalCharacter();
+ }
+ addrMode = AddrMode.INDIRECT_Y;
+ return;
+ } else {
+ column--;
+ addrMode = AddrMode.INDIRECT;
+ return;
+ }
+ default:
+ column--;
+ break;
+ }
+ }
+ readUnsignedWord();
+ switch (readChar()) {
+ case ',':
+ switch (readChar()) {
+ case 'X':
+ case 'x':
+ addrMode = AddrMode.INDIRECT_X;
+ break;
+ case '0':
+ addrMode = cast(AddrMode) (AddrMode.INDIRECT_X + AddrMode.ZERO);
+ break;
+ default:
+ illegalCharacter();
+ }
+ if (readChar() != ')') {
+ throw new AssemblyError("Need parenthesis");
+ }
+ return;
+ case ')':
+ if (eol()) {
+ addrMode = AddrMode.INDIRECT;
+ return;
+ }
+ if (line[column] == ',') {
+ column++;
+ switch (readChar()) {
+ case 'Y':
+ case 'y':
+ addrMode = AddrMode.INDIRECT_Y;
+ break;
+ case '0':
+ addrMode = cast(AddrMode) (AddrMode.INDIRECT_Y + AddrMode.ZERO);
+ break;
+ default:
+ illegalCharacter();
+ }
+ optionalIncDec();
+ return;
+ }
+ addrMode = AddrMode.INDIRECT;
+ return;
+ default:
+ illegalCharacter();
+ }
+ break;
+ case 'A':
+ case 'a':
+ if (!eol() && line[column] == ':') {
+ column++;
+ addrMode = AddrMode.ABSOLUTE;
+ } else {
+ addrMode = AddrMode.ABS_OR_ZP;
+ column--;
+ }
+ break;
+ case 'Z':
+ case 'z':
+ if (!eol() && line[column] == ':') {
+ column++;
+ addrMode = AddrMode.ZEROPAGE;
+ } else {
+ addrMode = AddrMode.ABS_OR_ZP;
+ column--;
+ }
+ break;
+ default:
+ addrMode = AddrMode.ABS_OR_ZP;
+ column--;
+ break;
+ }
+ // absolute or zeropage addressing, optionally indexed
+ if (inOpcode && (addrMode == AddrMode.ABSOLUTE || addrMode == AddrMode.ZEROPAGE)) {
+ switch (readChar()) {
+ case '}':
+ column--;
+ return;
+ case ',':
+ switch (readChar()) {
+ case 'X':
+ case 'x':
+ addrMode += cast(AddrMode) (AddrMode.ABSOLUTE_X - AddrMode.ABSOLUTE);
+ return;
+ case 'Y':
+ case 'y':
+ addrMode += cast(AddrMode) (AddrMode.ABSOLUTE_Y - AddrMode.ABSOLUTE);
+ return;
+ default:
+ illegalCharacter();
+ }
+ return;
+ default:
+ column--;
+ break;
+ }
+ }
+ readUnsignedWord();
+ if (addrMode == AddrMode.ABS_OR_ZP) {
+ if (unknownInPass1 || value > 0xff) {
+ addrMode = AddrMode.ABSOLUTE;
+ } else {
+ addrMode = AddrMode.ZEROPAGE;
+ }
+ }
+ if (eol()) return;
+ if (line[column] == ',') {
+ column++;
+ switch (readChar()) {
+ case 'X':
+ case 'x':
+ addrMode += cast(AddrMode) (AddrMode.ABSOLUTE_X - AddrMode.ABSOLUTE);
+ optionalIncDec();
+ return;
+ case 'Y':
+ case 'y':
+ addrMode += cast(AddrMode) (AddrMode.ABSOLUTE_Y - AddrMode.ABSOLUTE);
+ optionalIncDec();
+ return;
+ default:
+ illegalCharacter();
+ }
+ }
+}
+
+void readAbsoluteAddrMode() {
+ if (inOpcode && readChar() == '}') {
+ column--;
+ } else {
+ readAddrMode();
+ switch (addrMode) {
+ case AddrMode.ABSOLUTE:
+ case AddrMode.ZEROPAGE:
+ break;
+ default:
+ illegalAddrMode();
+ }
+ }
+ addrMode = AddrMode.ABSOLUTE;
+}
+
+debug AddrMode testAddrMode(char[] l) {
+ line = l;
+ column = 0;
+ readAddrMode();
+ printf("Addressing mode of \"%.*s\" is %x\n", line, addrMode);
+ return addrMode;
+}
+
+unittest {
+ assert(testAddrMode(" @") == AddrMode.ACCUMULATOR);
+ assert(testAddrMode(" #0") == AddrMode.IMMEDIATE);
+ assert(testAddrMode(" $abc,x-") == cast(AddrMode) (AddrMode.ABSOLUTE_X + AddrMode.DECREMENT));
+ assert(testAddrMode(" $ab,Y+") == cast(AddrMode) (AddrMode.ZEROPAGE_Y + AddrMode.INCREMENT));
+ assert(testAddrMode(" (0,x)") == AddrMode.INDIRECT_X);
+ assert(testAddrMode(" ($ff),Y+") == cast(AddrMode) (AddrMode.INDIRECT_Y + AddrMode.INCREMENT));
+ assert(testAddrMode(" ($abcd)") == AddrMode.INDIRECT);
+ inOpcode = true;
+ assert(testAddrMode(" a:}") == AddrMode.ABSOLUTE);
+ assert(testAddrMode(" z:}") == AddrMode.ZEROPAGE);
+ assert(testAddrMode(" a:,x}") == AddrMode.ABSOLUTE_X);
+ assert(testAddrMode(" z:,y}") == AddrMode.ZEROPAGE_Y);
+ assert(testAddrMode(" (,X)}") == AddrMode.INDIRECT_X);
+ assert(testAddrMode(" (),y}") == AddrMode.INDIRECT_Y);
+ assert(testAddrMode(" ()}") == AddrMode.INDIRECT);
+ inOpcode = false;
+}
+
+bit inFalseCondition() {
+ foreach (IfContext ic; ifContexts) {
+ if (!ic.condition) return true;
+ }
+ return false;
+}
+
+int filenameExt(char[] filename) {
+ int i = filename.length;
+ while (--i >= 0) {
+ switch (filename[i]) {
+ case '.':
+ return i;
+ case '/':
+ case '\\':
+ return -1;
+ default:
+ break;
+ }
+ }
+ return -1;
+}
+
+unittest {
+ assert(filenameExt("foo.bar") == 3);
+ assert(filenameExt("foo.bar/foo") == -1);
+ assert(filenameExt("foobar") == -1);
+ assert(filenameExt("test\\foo.bar") == 8);
+}
+
+Stream openInputFile(char[] filename) {
+ try {
+ return new BufferedFile(filename, FileMode.In);
+ } catch (OpenException e) {
+ throw new AssemblyError(e.toString());
+ }
+}
+
+Stream openOutputFile(char letter, char[] defaultExt) {
+ char[] filename;
+ if (optionParameters[letter - 'a'] is null) {
+ filename = sourceFilename;
+ int i = filenameExt(filename);
+ if (i > 0) {
+ filename = filename[0 .. i];
+ }
+ filename ~= defaultExt;
+ } else {
+ filename = optionParameters[letter - 'a'];
+ }
+ try {
+ return new BufferedFile(filename, FileMode.OutNew);
+ } catch (OpenException e) {
+ throw new AssemblyError(e.toString());
+ }
+}
+
+void ensureListingFileOpen(char letter, char[] msg) {
+ if (listingStream is null) {
+ listingStream = openOutputFile(letter, ".lst");
+ if (!getOption('q')) {
+ printf(msg);
+ }
+ listingStream.printf(TITLE ~ "\r\n");
+ }
+}
+
+void listNibble(int x) {
+ listingLine[listingColumn++] = x <= 9 ? x + '0' : x + ('A' - 10);
+}
+
+void listByte(ubyte x) {
+ listNibble(x >> 4);
+ listNibble(x & 0xf);
+}
+
+void listWord(ushort x) {
+ listByte(cast(ubyte) (x >> 8));
+ listByte(cast(ubyte) x);
+}
+
+void listLine() {
+ if (!optionListing || !getOption('l') || line is null) {
+ return;
+ }
+ assert(pass2);
+ if (inFalseCondition() && !getOption('c')) {
+ return;
+ }
+ if (getOption('i') && locations.length > 0) {
+ return;
+ }
+ ensureListingFileOpen('l', "Writing listing file...\n");
+ if (currentLocation.filename != lastListedFilename) {
+ listingStream.printf("Source: %.*s\n", currentLocation.filename);
+ lastListedFilename = currentLocation.filename;
+ }
+ int i = 4;
+ int x = currentLocation.lineNo;
+ while (x > 0 && i >= 0) {
+ listingLine[i--] = '0' + x % 10;
+ x /= 10;
+ }
+ while (i >= 0) {
+ listingLine[i--] = ' ';
+ }
+ listingLine[5] = ' ';
+ while (listingColumn < 32) {
+ listingLine[listingColumn++] = ' ';
+ }
+ listingStream.printf("%.32s%.*s\r\n", cast(char*) listingLine, line);
+}
+
+void listCommentLine() {
+ if (currentLabel is null) {
+ listingColumn = 6;
+ } else {
+ assert(!inFalseCondition());
+ checkOriginDefined();
+ }
+ listLine();
+}
+
+void listLabelTable() {
+ if (!(optionParameters['t' - 'a'] is null) && !(listingStream is null)) {
+ listingStream.close();
+ listingStream = null;
+ }
+ ensureListingFileOpen('t', "Writing label table...\n");
+ listingStream.printf("Label table:\r\n");
+ foreach (char[] name; labelTable.keys.sort) {
+ Label l = labelTable[name];
+ listingStream.write(l.unused ? 'n' : ' ');
+ listingStream.write(l.unknownInPass1 ? '2' : ' ');
+ int value = l.value;
+ char sign = ' ';
+ if (value < 0) {
+ sign = '-';
+ value = -value;
+ }
+ listingStream.printf(
+ (l.value & 0xffff0000) != 0 ? " %c%08X %.*s\r\n" : " %c%04X %.*s\r\n",
+ sign, value, name
+ );
+ }
+}
+
+debug ubyte[] objectBuffer;
+
+void objectByte(ubyte b) {
+ debug {
+ objectBuffer ~= b;
+ } else {
+ assert(pass2);
+ if (!optionObject) return;
+ if (objectStream is null) {
+ objectStream = openOutputFile('o', ".obx");
+ if (!getOption('q')) {
+ printf("Writing object file...\n");
+ }
+ }
+ try {
+ objectStream.write(b);
+ } catch (WriteException e) {
+ throw new AssemblyError("Error writing object file");
+ }
+ }
+ objectBytes++;
+}
+
+void objectWord(ushort w) {
+ objectByte(cast(ubyte) w);
+ objectByte(cast(ubyte) (w >> 8));
+}
+
+void putByte(ubyte b) {
+ if (inOpcode) {
+ if (instructionBegin) {
+ value = b;
+ instructionBegin = false;
+ }
+ return;
+ }
+ if (willSkip) {
+ assert(!pass2);
+ willSkip = false;
+ skipping = true;
+ }
+ if (skipping) {
+ assert(!pass2);
+ skipOffsets[skipOffsets.length - 1]++;
+ }
+ if (instructionBegin) {
+ repeatOffset = -2;
+ instructionBegin = false;
+ }
+ repeatOffset--;
+ if (optionFill && loadingOrigin >= 0 && loadingOrigin != loadOrigin) {
+ if (loadingOrigin > loadOrigin) {
+ throw new AssemblyError("Can't fill from higher to lower memory location");
+ }
+ if (pass2) {
+ while (loadingOrigin < loadOrigin) {
+ objectByte(0xff);
+ loadingOrigin++;
+ }
+ }
+ }
+ debug {
+ objectByte(b);
+ }
+ if (pass2) {
+ debug; else {
+ objectByte(b);
+ }
+ if (listingColumn < 29) {
+ listByte(b);
+ listingLine[listingColumn++] = ' ';
+ } else {
+ listingLine[29] = '+';
+ listingColumn = 30;
+ }
+ }
+ if (origin >= 0) {
+ assert(blockIndex >= 0);
+ if (!pass2) {
+ blockEnds[blockIndex] = cast(ushort) loadOrigin;
+ }
+ origin++;
+ loadingOrigin = ++loadOrigin;
+ } else if (optionHeaders) {
+ throw new AssemblyError("No ORG specified");
+ }
+}
+
+void putWord(ushort w) {
+ putByte(cast(ubyte) w);
+ putByte(cast(ubyte) (w >> 8));
+}
+
+void putCommand(ubyte b) {
+ putByte(b);
+ if (inOpcode) return;
+ switch (addrMode & AddrMode.STANDARD_MASK) {
+ case AddrMode.ACCUMULATOR:
+ break;
+ case AddrMode.IMMEDIATE:
+ case AddrMode.ZEROPAGE:
+ case AddrMode.ZEROPAGE_X:
+ case AddrMode.ZEROPAGE_Y:
+ case AddrMode.INDIRECT_X:
+ case AddrMode.INDIRECT_Y:
+ if (pass2 && (value < -0xff || value > 0xff)) {
+ throw new AssemblyError("Value out of range");
+ }
+ putByte(cast(ubyte) value);
+ break;
+ case AddrMode.ABSOLUTE:
+ case AddrMode.ABSOLUTE_X:
+ case AddrMode.ABSOLUTE_Y:
+ case AddrMode.INDIRECT:
+ putWord(value);
+ break;
+ }
+ switch (addrMode) {
+ case cast(AddrMode) (AddrMode.ABSOLUTE_X + AddrMode.INCREMENT):
+ case cast(AddrMode) (AddrMode.ZEROPAGE_X + AddrMode.INCREMENT):
+ putByte(0xe8);
+ break;
+ case cast(AddrMode) (AddrMode.ABSOLUTE_X + AddrMode.DECREMENT):
+ case cast(AddrMode) (AddrMode.ZEROPAGE_X + AddrMode.DECREMENT):
+ putByte(0xca);
+ break;
+ case cast(AddrMode) (AddrMode.ABSOLUTE_Y + AddrMode.INCREMENT):
+ case cast(AddrMode) (AddrMode.ZEROPAGE_Y + AddrMode.INCREMENT):
+ case cast(AddrMode) (AddrMode.INDIRECT_Y + AddrMode.INCREMENT):
+ case cast(AddrMode) (AddrMode.INDIRECT_Y + AddrMode.INCREMENT + AddrMode.ZERO):
+ putByte(0xc8);
+ break;
+ case cast(AddrMode) (AddrMode.ABSOLUTE_Y + AddrMode.DECREMENT):
+ case cast(AddrMode) (AddrMode.ZEROPAGE_Y + AddrMode.DECREMENT):
+ case cast(AddrMode) (AddrMode.INDIRECT_Y + AddrMode.DECREMENT):
+ case cast(AddrMode) (AddrMode.INDIRECT_Y + AddrMode.DECREMENT + AddrMode.ZERO):
+ putByte(0x88);
+ break;
+ default:
+ break;
+ }
+}
+
+void noOpcode() {
+ if (inOpcode) {
+ throw new AssemblyError("Can't get opcode of this");
+ }
+}
+
+void directive() {
+ noOpcode();
+ if (repeating) {
+ throw new AssemblyError("Can't repeat this directive");
+ }
+ if (pairing) {
+ throw new AssemblyError("Can't pair this directive");
+ }
+}
+
+void noRepeatSkipDirective() {
+ directive();
+ if (willSkip) {
+ throw new AssemblyError("Can't skip over this");
+ }
+ repeatOffset = 0;
+}
+
+void illegalAddrMode() {
+ throw new AssemblyError("Illegal addressing mode");
+}
+
+void addrModeForMove(int move) {
+ switch (move) {
+ case 0:
+ readAddrMode();
+ break;
+ case 1:
+ value = value1;
+ addrMode = addrMode1;
+ break;
+ case 2:
+ value = value2;
+ addrMode = addrMode2;
+ break;
+ }
+}
+
+void assemblyAccumulator(ubyte b, ubyte prefix, int move) {
+ addrModeForMove(move);
+ if (prefix != 0) {
+ putByte(prefix);
+ }
+ switch (addrMode & AddrMode.STANDARD_MASK) {
+ case AddrMode.ACCUMULATOR:
+ case AddrMode.INDIRECT:
+ illegalAddrMode();
+ case AddrMode.IMMEDIATE:
+ if (b == 0x80) {
+ // STA #
+ illegalAddrMode();
+ }
+ putCommand(b + 9);
+ break;
+ case AddrMode.ABSOLUTE:
+ putCommand(b + 0xd);
+ break;
+ case AddrMode.ZEROPAGE:
+ putCommand(b + 5);
+ break;
+ case AddrMode.ABSOLUTE_X:
+ putCommand(b + 0x1d);
+ break;
+ case AddrMode.ZEROPAGE_X:
+ putCommand(b + 0x15);
+ break;
+ case AddrMode.ZEROPAGE_Y:
+ addrMode -= 1;
+ goto case AddrMode.ABSOLUTE_Y;
+ case AddrMode.ABSOLUTE_Y:
+ putCommand(b + 0x19);
+ break;
+ case AddrMode.INDIRECT_X:
+ if ((addrMode & AddrMode.ZERO) != 0) {
+ putWord(0x00a2);
+ }
+ putCommand(b + 1);
+ break;
+ case AddrMode.INDIRECT_Y:
+ if ((addrMode & AddrMode.ZERO) != 0) {
+ putWord(0x00a0);
+ }
+ putCommand(b + 0x11);
+ break;
+ }
+}
+
+void assemblyShift(ubyte b) {
+ readAddrMode();
+ switch (addrMode & AddrMode.STANDARD_MASK) {
+ case AddrMode.ACCUMULATOR:
+ if (b == 0xc0 || b == 0xe0) {
+ // INC @, DEC @
+ illegalAddrMode();
+ }
+ putByte(b + 0xa);
+ break;
+ case AddrMode.ABSOLUTE:
+ putCommand(b + 0xe);
+ break;
+ case AddrMode.ZEROPAGE:
+ putCommand(b + 6);
+ break;
+ case AddrMode.ABSOLUTE_X:
+ putCommand(b + 0x1e);
+ break;
+ case AddrMode.ZEROPAGE_X:
+ putCommand(b + 0x16);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void assemblyCompareIndex(ubyte b) {
+ readAddrMode();
+ switch (addrMode) {
+ case AddrMode.IMMEDIATE:
+ putCommand(b);
+ break;
+ case AddrMode.ABSOLUTE:
+ putCommand(b + 0xc);
+ break;
+ case AddrMode.ZEROPAGE:
+ putCommand(b + 4);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void assemblyLda(int move) {
+ assemblyAccumulator(0xa0, 0, move);
+}
+
+void assemblyLdx(int move) {
+ addrModeForMove(move);
+ switch (addrMode & AddrMode.STANDARD_MASK) {
+ case AddrMode.IMMEDIATE:
+ putCommand(0xa2);
+ break;
+ case AddrMode.ABSOLUTE:
+ putCommand(0xae);
+ break;
+ case AddrMode.ZEROPAGE:
+ putCommand(0xa6);
+ break;
+ case AddrMode.ABSOLUTE_Y:
+ putCommand(0xbe);
+ break;
+ case AddrMode.ZEROPAGE_Y:
+ putCommand(0xb6);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void assemblyLdy(int move) {
+ addrModeForMove(move);
+ switch (addrMode & AddrMode.STANDARD_MASK) {
+ case AddrMode.IMMEDIATE:
+ putCommand(0xa0);
+ break;
+ case AddrMode.ABSOLUTE:
+ putCommand(0xac);
+ break;
+ case AddrMode.ZEROPAGE:
+ putCommand(0xa4);
+ break;
+ case AddrMode.ABSOLUTE_X:
+ putCommand(0xbc);
+ break;
+ case AddrMode.ZEROPAGE_X:
+ putCommand(0xb4);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void assemblySta(int move) {
+ assemblyAccumulator(0x80, 0, move);
+}
+
+void assemblyStx(int move) {
+ addrModeForMove(move);
+ switch (addrMode & AddrMode.STANDARD_MASK) {
+ case AddrMode.ABSOLUTE:
+ putCommand(0x8e);
+ break;
+ case AddrMode.ZEROPAGE:
+ putCommand(0x86);
+ break;
+ case AddrMode.ABSOLUTE_Y:
+ addrMode += 1;
+ goto case AddrMode.ZEROPAGE_Y;
+ case AddrMode.ZEROPAGE_Y:
+ putCommand(0x96);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void assemblySty(int move) {
+ addrModeForMove(move);
+ switch (addrMode & AddrMode.STANDARD_MASK) {
+ case AddrMode.ABSOLUTE:
+ putCommand(0x8c);
+ break;
+ case AddrMode.ZEROPAGE:
+ putCommand(0x84);
+ break;
+ case AddrMode.ABSOLUTE_X:
+ addrMode += 1;
+ goto case AddrMode.ZEROPAGE_X;
+ case AddrMode.ZEROPAGE_X:
+ putCommand(0x94);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void assemblyBit() {
+ readAddrMode();
+ switch (addrMode) {
+ case AddrMode.ABSOLUTE:
+ putCommand(0x2c);
+ break;
+ case AddrMode.ZEROPAGE:
+ putCommand(0x24);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void putJump() {
+ switch (addrMode) {
+ case AddrMode.ZEROPAGE:
+ addrMode = AddrMode.ABSOLUTE;
+ goto case AddrMode.ABSOLUTE;
+ case AddrMode.ABSOLUTE:
+ putCommand(0x4c);
+ break;
+ case AddrMode.INDIRECT:
+ if (pass2 && (value & 0xff) == 0xff) {
+ warning("Buggy indirect jump");
+ }
+ putCommand(0x6c);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void assemblyJmp() {
+ readAddrMode();
+ putJump();
+}
+
+void assemblyConditionalJump(ubyte b) {
+ noOpcode();
+ readAddrMode();
+ if ((addrMode == AddrMode.ABSOLUTE || addrMode == AddrMode.ZEROPAGE)
+ && pass2 && origin >= 0 && value - origin - 2 >= -0x80 && value - origin - 2 <= 0x7f) {
+ warning("Plain branch instruction would be sufficient");
+ }
+ putByte(b);
+ putByte(3);
+ putJump();
+}
+
+void assemblyJsr() {
+ readAbsoluteAddrMode();
+ putCommand(0x20);
+}
+
+ubyte calculateBranch(int offset) {
+ if (offset < -0x80 || offset > 0x7f) {
+ int dist;
+ if (offset < 0) {
+ dist = 0x80 - offset;
+ } else {
+ dist = offset - 0x7f;
+ }
+ throw new AssemblyError("Branch out of range by " ~ toString(dist) ~ " bytes");
+ }
+ return cast(ubyte) offset;
+}
+
+void assemblyBranch(ubyte b) {
+ readAbsoluteAddrMode();
+ if (inOpcode) {
+ putByte(b);
+ return;
+ }
+ checkOriginDefined();
+ putByte(b);
+ putByte(pass2 ? calculateBranch(value - origin - 1) : 0);
+}
+
+void assemblyRepeat(ubyte b) {
+ noOpcode();
+ int offset = repeatOffset;
+ if (offset >= 0) {
+ throw new AssemblyError("No instruction to repeat");
+ }
+ if (pass2 && wereManyInstructions) {
+ warning("Repeating only the last instruction");
+ }
+ putByte(b);
+ putByte(calculateBranch(offset));
+}
+
+void assemblySkip(ubyte b) {
+ noOpcode();
+ if (willSkip) {
+ skipOffsets[skipOffsets.length - 1] = 2;
+ willSkip = false;
+ }
+ putByte(b);
+ if (pass2) {
+ putByte(calculateBranch(skipOffsets[skipOffsetsIndex++]));
+ } else {
+ putByte(0);
+ skipOffsets ~= 0;
+ willSkip = true;
+ }
+}
+
+void assemblyInw() {
+ noOpcode();
+ readAddrMode();
+ switch (addrMode) {
+ case AddrMode.ABSOLUTE:
+ putCommand(0xee);
+ putWord(0x03d0);
+ value++;
+ putCommand(0xee);
+ break;
+ case AddrMode.ZEROPAGE:
+ putCommand(0xe6);
+ putWord(0x02d0);
+ value++;
+ putCommand(0xe6);
+ break;
+ case AddrMode.ABSOLUTE_X:
+ putCommand(0xfe);
+ putWord(0x03d0);
+ value++;
+ putCommand(0xfe);
+ break;
+ case AddrMode.ZEROPAGE_X:
+ putCommand(0xf6);
+ putWord(0x02d0);
+ value++;
+ putCommand(0xf6);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void assemblyMove() {
+ noOpcode();
+ readAddrMode();
+ value1 = value;
+ addrMode1 = addrMode;
+ bit unknown1 = unknownInPass1;
+ readAddrMode();
+ value2 = value;
+ addrMode2 = addrMode;
+ unknownInPass1 = unknown1;
+}
+
+void assemblyMoveByte(MoveFunction load, MoveFunction store) {
+ assemblyMove();
+ load(1);
+ store(2);
+}
+
+void assemblyMoveWord(MoveFunction load, MoveFunction store, ubyte inc, ubyte dec) {
+ assemblyMove();
+ switch (addrMode2) {
+ case AddrMode.ABSOLUTE:
+ case AddrMode.ZEROPAGE:
+ case AddrMode.ABSOLUTE_X:
+ case AddrMode.ZEROPAGE_X:
+ case AddrMode.ABSOLUTE_Y:
+ case AddrMode.ZEROPAGE_Y:
+ break;
+ default:
+ illegalAddrMode();
+ }
+ switch (addrMode1) {
+ case AddrMode.IMMEDIATE:
+ int high = value1 >> 8;
+ value1 &= 0xff;
+ load(1);
+ store(2);
+ value2++;
+ if (unknownInPass1) {
+ value1 = high;
+ load(1);
+ } else {
+ if (inc != 0 && cast(ubyte) (value1 + 1) == high) {
+ putByte(inc);
+ } else if (dec != 0 && cast(ubyte) (value1 - 1) == high) {
+ putByte(dec);
+ } else if (value1 != high) {
+ value1 = high;
+ load(1);
+ }
+ }
+ store(2);
+ break;
+ case AddrMode.ABSOLUTE:
+ case AddrMode.ZEROPAGE:
+ case AddrMode.ABSOLUTE_X:
+ case AddrMode.ZEROPAGE_X:
+ case AddrMode.ABSOLUTE_Y:
+ case AddrMode.ZEROPAGE_Y:
+ load(1);
+ store(2);
+ value1++;
+ value2++;
+ load(1);
+ store(2);
+ break;
+ default:
+ illegalAddrMode();
+ }
+}
+
+void storeDtaNumber(int val, char letter) {
+ int limit = 0xffff;
+ if (letter == 'b') limit = 0xff;
+ if ((!unknownInPass1 || pass2) && (val < -limit || val > limit)) {
+ throw new AssemblyError("Value out of range");
+ }
+ switch (letter) {
+ case 'a':
+ putWord(cast(ushort) val);
+ break;
+ case 'b':
+ case 'l':
+ putByte(cast(ubyte) val);
+ break;
+ case 'h':
+ putByte(cast(ubyte) (val >> 8));
+ break;
+ }
+}
+
+void assemblyDtaInteger(char letter) {
+ if (readFunction() == "SIN") {
+ readWord();
+ int sinCenter = value;
+ readComma();
+ readWord();
+ int sinAmp = value;
+ readComma();
+ readKnownPositive();
+ int sinPeriod = value;
+ int sinMin = 0;
+ int sinMax = sinPeriod - 1;
+ switch (readChar()) {
+ case ')':
+ break;
+ case ',':
+ readUnsignedWord();
+ mustBeKnownInPass1();
+ sinMin = value;
+ readComma();
+ readUnsignedWord();
+ mustBeKnownInPass1();
+ sinMax = value;
+ if (readChar() != ')') {
+ illegalCharacter();
+ }
+ break;
+ default:
+ illegalCharacter();
+ }
+ while (sinMin <= sinMax) {
+ int val = sinCenter + cast(int) rint(sinAmp * sin(sinMin * 2 * PI / sinPeriod));
+ storeDtaNumber(val, letter);
+ sinMin++;
+ }
+ return;
+ }
+ readWord();
+ storeDtaNumber(value, letter);
+}
+
+bit realSign;
+
+int realExponent;
+
+long realMantissa;
+
+void putReal() {
+ if (realMantissa == 0) {
+ putWord(0);
+ putWord(0);
+ putWord(0);
+ return;
+ }
+ while (realMantissa < 0x1000000000L) {
+ realMantissa <<= 4;
+ realExponent--;
+ }
+ if ((realExponent & 1) != 0) {
+ if (realMantissa & 0xf) {
+ throw new AssemblyError("Out of precision");
+ }
+ realMantissa >>= 4;
+ }
+ realExponent = (realExponent + 0x89) >> 1;
+ if (realExponent < 64 - 49) {
+ throw new AssemblyError("Out of precision");
+ }
+ if (realExponent > 64 + 49) {
+ throw new AssemblyError("Number too big");
+ }
+ putByte(cast(ubyte) (realSign ? realExponent + 0x80 : realExponent));
+ putByte(cast(ubyte) (realMantissa >> 32));
+ putByte(cast(ubyte) (realMantissa >> 24));
+ putByte(cast(ubyte) (realMantissa >> 16));
+ putByte(cast(ubyte) (realMantissa >> 8));
+ putByte(cast(ubyte) realMantissa);
+}
+
+bit readSign() {
+ switch (readChar()) {
+ case '+':
+ return false;
+ case '-':
+ return true;
+ default:
+ column--;
+ return false;
+ }
+}
+
+void readExponent() {
+ bit sign = readSign();
+ char c = readChar();
+ if (c < '0' || c > '9') {
+ illegalCharacter();
+ }
+ int e = c - '0';
+ c = readChar();
+ if (c >= '0' && c <= '9') {
+ e = 10 * e + c - '0';
+ } else {
+ column--;
+ }
+ realExponent += sign ? -e : e;
+ putReal();
+}
+
+void readFraction() {
+ for (;;) {
+ char c = readChar();
+ if (c >= '0' && c <= '9') {
+ if (c != '0' && realMantissa >= 0x1000000000L) {
+ throw new AssemblyError("Out of precision");
+ }
+ realMantissa <<= 4;
+ realMantissa += c - '0';
+ realExponent--;
+ continue;
+ }
+ if (c == 'E' || c == 'e') {
+ readExponent();
+ return;
+ }
+ column--;
+ putReal();
+ return;
+ }
+}
+
+void assemblyDtaReal() {
+ realSign = readSign();
+ realExponent = 0;
+ realMantissa = 0;
+ char c = readChar();
+ if (c == '.') {
+ readFraction();
+ return;
+ }
+ if (c < '0' || c > '9') {
+ illegalCharacter();
+ }
+ do {
+ if (realMantissa < 0x1000000000L) {
+ realMantissa <<= 4;
+ realMantissa += c - '0';
+ } else {
+ if (c != '0') {
+ throw new AssemblyError("Out of precision");
+ }
+ realExponent++;
+ }
+ c = readChar();
+ } while (c >= '0' && c <= '9');
+ switch (c) {
+ case '.':
+ readFraction();
+ break;
+ case 'E':
+ case 'e':
+ readExponent();
+ break;
+ default:
+ column--;
+ putReal();
+ break;
+ }
+}
+
+void assemblyDtaNumbers(char letter) {
+ if (eol() || line[column] != '(') {
+ column--;
+ assemblyDtaInteger('b');
+ return;
+ }
+ column++;
+ for (;;) {
+ switch (letter) {
+ case 'a':
+ case 'b':
+ case 'h':
+ case 'l':
+ assemblyDtaInteger(letter);
+ break;
+ case 'r':
+ assemblyDtaReal();
+ break;
+
+ }
+ switch (readChar()) {
+ case ')':
+ return;
+ case ',':
+ break;
+ default:
+ illegalCharacter();
+ }
+ }
+}
+
+void assemblyDta() {
+ noOpcode();
+ readSpaces();
+ for (;;) {
+ switch (readChar()) {
+ case 'A':
+ case 'a':
+ assemblyDtaNumbers('a');
+ break;
+ case 'B':
+ case 'b':
+ assemblyDtaNumbers('b');
+ break;
+ case 'C':
+ case 'c':
+ ubyte[] s = readString();
+ if (s is null) {
+ column--;
+ assemblyDtaInteger('b');
+ break;
+ }
+ foreach (ubyte b; s) {
+ putByte(b);
+ }
+ break;
+ case 'D':
+ case 'd':
+ ubyte[] s = readString();
+ if (s is null) {
+ column--;
+ assemblyDtaInteger('b');
+ break;
+ }
+ foreach (ubyte b; s) {
+ switch (b & 0x60) {
+ case 0x00:
+ putByte(b + 0x40);
+ break;
+ case 0x20:
+ case 0x40:
+ putByte(b - 0x20);
+ break;
+ case 0x60:
+ putByte(b);
+ break;
+ }
+ }
+ break;
+ case 'H':
+ case 'h':
+ assemblyDtaNumbers('h');
+ break;
+ case 'L':
+ case 'l':
+ assemblyDtaNumbers('l');
+ break;
+ case 'R':
+ case 'r':
+ assemblyDtaNumbers('r');
+ break;
+ default:
+ column--;
+ assemblyDtaInteger('b');
+ break;
+ }
+ if (eol() || line[column] != ',') {
+ break;
+ }
+ column++;
+ }
+}
+
+void assemblyEqu() {
+ directive();
+ if (currentLabel is null) {
+ throw new AssemblyError("Label name required");
+ }
+ currentLabel.value = 0;
+ readSpaces();
+ readValue();
+ currentLabel.value = value;
+ currentLabel.unknownInPass1 = unknownInPass1;
+ if (optionListing) {
+ listingLine[6] = '=';
+ int val = value;
+ listingLine[7] = ' ';
+ if (val < 0) {
+ listingLine[7] = '-';
+ val = -val;
+ }
+ listingColumn = 8;
+ if ((val & 0xffff0000) != 0) {
+ listWord(cast(ushort) (val >> 16));
+ } else {
+ while (listingColumn < 12) {
+ listingLine[listingColumn++] = ' ';
+ }
+ }
+ listWord(cast(ushort) val);
+ }
+}
+
+void assemblyEnd() {
+ directive();
+ assert(!foundEnd);
+ foundEnd = true;
+}
+
+void assemblyIftEli() {
+ ifContexts[ifContexts.length - 1].condition = true;
+ if (!inFalseCondition()) {
+ readSpaces();
+ readValue();
+ mustBeKnownInPass1();
+ if (value != 0) {
+ ifContexts[ifContexts.length - 1].aConditionMatched = true;
+ } else {
+ listLine();
+ }
+ ifContexts[ifContexts.length - 1].condition = value != 0;
+ }
+}
+
+void checkMissingIft() {
+ if (ifContexts.length == 0) {
+ throw new AssemblyError("Missing IFT");
+ }
+}
+
+void assemblyIft() {
+ directive();
+ ifContexts.length = ifContexts.length + 1;
+ assemblyIftEli();
+}
+
+void assemblyEliEls() {
+ directive();
+ checkMissingIft();
+ if (ifContexts[ifContexts.length - 1].wasElse) {
+ throw new AssemblyError("EIF expected");
+ }
+}
+
+void assemblyEli() {
+ assemblyEliEls();
+ if (ifContexts[ifContexts.length - 1].aConditionMatched) {
+ ifContexts[ifContexts.length - 1].condition = false;
+ return;
+ }
+ assemblyIftEli();
+}
+
+void assemblyEls() {
+ assemblyEliEls();
+ with (ifContexts[ifContexts.length - 1]) {
+ if (condition && aConditionMatched) {
+ listLine();
+ }
+ wasElse = true;
+ condition = !aConditionMatched;
+ }
+}
+
+void assemblyEif() {
+ directive();
+ checkMissingIft();
+ ifContexts.length = ifContexts.length - 1;
+}
+
+void assemblyErt() {
+ directive();
+ readSpaces();
+ readValue();
+ if (pass2 && value != 0) {
+ throw new AssemblyError("User-defined error");
+ }
+}
+
+bit readOption() {
+ switch (readChar()) {
+ case '-':
+ return false;
+ case '+':
+ return true;
+ default:
+ illegalCharacter();
+ }
+}
+
+void assemblyOpt() {
+ directive();
+ readSpaces();
+ while (!eol()) {
+ switch (line[column++]) {
+ case 'F':
+ case 'f':
+ optionFill = readOption();
+ break;
+ case 'G':
+ case 'g':
+ option5200 = readOption();
+ break;
+ case 'H':
+ case 'h':
+ optionHeaders = readOption();
+ break;
+ case 'L':
+ case 'l':
+ optionListing = readOption() && !pass2;
+ break;
+ case 'O':
+ case 'o':
+ optionObject = readOption();
+ break;
+ default:
+ column--;
+ return;
+ }
+ }
+}
+
+void originWord(ushort value, char listingChar) {
+ objectWord(value);
+ listWord(value);
+ listingLine[listingColumn++] = listingChar;
+}
+
+void setOrigin(int addr, bit requestedHeader, bit requestedFFFF) {
+ origin = loadOrigin = addr;
+ if (requestedHeader || loadingOrigin < 0 || (addr != loadingOrigin && !optionFill)) {
+ blockIndex++;
+ if (!pass2) {
+ assert(blockIndex == blockEnds.length);
+ blockEnds ~= cast(ushort) (addr - 1);
+ }
+ if (pass2 && optionHeaders) {
+ if (addr - 1 == blockEnds[blockIndex]) {
+ if (requestedHeader) {
+ throw new AssemblyError("Cannot generate an empty block");
+ }
+ return;
+ }
+ if (requestedFFFF || objectBytes == 0) {
+ assert(requestedHeader || addr != loadingOrigin);
+ originWord(0xffff, '>');
+ listingLine[listingColumn++] = ' ';
+ }
+ if (requestedHeader || addr != loadingOrigin) {
+ originWord(cast(ushort) addr, '-');
+ originWord(blockEnds[blockIndex], '>');
+ listingLine[listingColumn++] = ' ';
+ loadingOrigin = -1;
+ }
+ }
+ }
+}
+
+void checkHeadersOn() {
+ if (!optionHeaders) {
+ throw new AssemblyError("Illegal when Atari file headers disabled");
+ }
+}
+
+void assemblyOrg() {
+ noRepeatSkipDirective();
+ readSpaces();
+ bit requestedFFFF = false;
+ bit requestedHeader = false;
+ if (column + 2 < line.length && line[column + 1] == ':') {
+ switch (line[column]) {
+ case 'F':
+ case 'f':
+ requestedFFFF = true;
+ goto case 'A';
+ case 'A':
+ case 'a':
+ checkHeadersOn();
+ column += 2;
+ requestedHeader = true;
+ break;
+ case 'R':
+ case 'r':
+ column += 2;
+ checkOriginDefined();
+ readUnsignedWord();
+ mustBeKnownInPass1();
+ origin = value;
+ return;
+ default:
+ break;
+ }
+ }
+ readUnsignedWord();
+ mustBeKnownInPass1();
+ setOrigin(value, requestedHeader, requestedFFFF);
+}
+
+void assemblyRunIni(ushort addr) {
+ noRepeatSkipDirective();
+ checkHeadersOn();
+ loadingOrigin = -1; // don't fill
+ setOrigin(addr, false, false);
+ readSpaces();
+ readUnsignedWord();
+ putWord(value);
+ loadingOrigin = -1; // don't fill
+}
+
+void assemblyIcl() {
+ directive();
+ char[] filename = readFilename();
+ checkNoExtraCharacters();
+ listLine();
+ assemblyFile(filename);
+ line = null;
+}
+
+void assemblyIns() {
+ noRepeatSkipDirective();
+ char[] filename = readFilename();
+ int offset = 0;
+ int length = -1;
+ if (!eol() && line[column] == ',') {
+ column++;
+ readValue();
+ mustBeKnownInPass1();
+ offset = value;
+ if (!eol() && line[column] == ',') {
+ column++;
+ readKnownPositive();
+ length = value;
+ }
+ }
+ Stream stream = openInputFile(filename);
+ try {
+ stream.seek(offset, offset >= 0 ? SeekPos.Set : SeekPos.End);
+ } catch (SeekException e) {
+ throw new AssemblyError("Error seeking file");
+ }
+ while (length != 0) {
+ ubyte b;
+ try {
+ stream.read(b);
+ } catch (ReadException e) {
+ if (length > 0) {
+ throw new AssemblyError("File is too short");
+ }
+ break;
+ }
+ putByte(b);
+ if (length > 0) length--;
+ }
+ // stream.close(); bug in Phobos
+}
+
+void assemblyInstruction(char[] instruction) {
+ if (!inOpcode && origin < 0 && !(currentLabel is null) && instruction != "EQU") {
+ throw new AssemblyError("No ORG specified");
+ }
+ instructionBegin = true;
+ switch (instruction) {
+ case "ADC":
+ assemblyAccumulator(0x60, 0, 0);
+ break;
+ case "ADD":
+ assemblyAccumulator(0x60, 0x18, 0);
+ break;
+ case "AND":
+ assemblyAccumulator(0x20, 0, 0);
+ break;
+ case "ASL":
+ assemblyShift(0x00);
+ break;
+ case "BCC":
+ assemblyBranch(0x90);
+ break;
+ case "BCS":
+ assemblyBranch(0xb0);
+ break;
+ case "BEQ":
+ assemblyBranch(0xf0);
+ break;
+ case "BIT":
+ assemblyBit();
+ break;
+ case "BMI":
+ assemblyBranch(0x30);
+ break;
+ case "BNE":
+ assemblyBranch(0xd0);
+ break;
+ case "BPL":
+ assemblyBranch(0x10);
+ break;
+ case "BRK":
+ putByte(0x00);
+ break;
+ case "BVC":
+ assemblyBranch(0x50);
+ break;
+ case "BVS":
+ assemblyBranch(0x70);
+ break;
+ case "CLC":
+ putByte(0x18);
+ break;
+ case "CLD":
+ putByte(0xd8);
+ break;
+ case "CLI":
+ putByte(0x58);
+ break;
+ case "CLV":
+ putByte(0xb8);
+ break;
+ case "CMP":
+ assemblyAccumulator(0xc0, 0, 0);
+ break;
+ case "CPX":
+ assemblyCompareIndex(0xe0);
+ break;
+ case "CPY":
+ assemblyCompareIndex(0xc0);
+ break;
+ case "DEC":
+ assemblyShift(0xc0);
+ break;
+ case "DEX":
+ putByte(0xca);
+ break;
+ case "DEY":
+ putByte(0x88);
+ break;
+ case "DTA":
+ assemblyDta();
+ break;
+ case "EIF":
+ assemblyEif();
+ break;
+ case "ELI":
+ assemblyEli();
+ break;
+ case "ELS":
+ assemblyEls();
+ break;
+ case "END":
+ assemblyEnd();
+ break;
+ case "EOR":
+ assemblyAccumulator(0x40, 0, 0);
+ break;
+ case "EQU":
+ assemblyEqu();
+ break;
+ case "ERT":
+ assemblyErt();
+ break;
+ case "ICL":
+ assemblyIcl();
+ break;
+ case "IFT":
+ assemblyIft();
+ break;
+ case "INC":
+ assemblyShift(0xe0);
+ break;
+ case "INI":
+ assemblyRunIni(0x2e2);
+ break;
+ case "INS":
+ assemblyIns();
+ break;
+ case "INX":
+ putByte(0xe8);
+ break;
+ case "INY":
+ putByte(0xc8);
+ break;
+ case "INW":
+ assemblyInw();
+ break;
+ case "JCC":
+ assemblyConditionalJump(0xb0);
+ break;
+ case "JCS":
+ assemblyConditionalJump(0x90);
+ break;
+ case "JEQ":
+ assemblyConditionalJump(0xd0);
+ break;
+ case "JMI":
+ assemblyConditionalJump(0x10);
+ break;
+ case "JMP":
+ assemblyJmp();
+ break;
+ case "JNE":
+ assemblyConditionalJump(0xf0);
+ break;
+ case "JPL":
+ assemblyConditionalJump(0x30);
+ break;
+ case "JSR":
+ assemblyJsr();
+ break;
+ case "JVC":
+ assemblyConditionalJump(0x70);
+ break;
+ case "JVS":
+ assemblyConditionalJump(0x50);
+ break;
+ case "LDA":
+ assemblyAccumulator(0xa0, 0, 0);
+ break;
+ case "LDX":
+ assemblyLdx(0);
+ break;
+ case "LDY":
+ assemblyLdy(0);
+ break;
+ case "LSR":
+ assemblyShift(0x40);
+ break;
+ case "MVA":
+ assemblyMoveByte(&assemblyLda, &assemblySta);
+ break;
+ case "MVX":
+ assemblyMoveByte(&assemblyLdx, &assemblyStx);
+ break;
+ case "MVY":
+ assemblyMoveByte(&assemblyLdy, &assemblySty);
+ break;
+ case "MWA":
+ assemblyMoveWord(&assemblyLda, &assemblySta, 0, 0);
+ break;
+ case "MWX":
+ assemblyMoveWord(&assemblyLdx, &assemblyStx, 0xe8, 0xca);
+ break;
+ case "MWY":
+ assemblyMoveWord(&assemblyLdy, &assemblySty, 0xc8, 0x88);
+ break;
+ case "NOP":
+ putByte(0xea);
+ break;
+ case "OPT":
+ assemblyOpt();
+ break;
+ case "ORA":
+ assemblyAccumulator(0x00, 0, 0);
+ break;
+ case "ORG":
+ assemblyOrg();
+ break;
+ case "PHA":
+ putByte(0x48);
+ break;
+ case "PHP":
+ putByte(0x08);
+ break;
+ case "PLA":
+ putByte(0x68);
+ break;
+ case "PLP":
+ putByte(0x28);
+ break;
+ case "RCC":
+ assemblyRepeat(0x90);
+ break;
+ case "RCS":
+ assemblyRepeat(0xb0);
+ break;
+ case "REQ":
+ assemblyRepeat(0xf0);
+ break;
+ case "RMI":
+ assemblyRepeat(0x30);
+ break;
+ case "RNE":
+ assemblyRepeat(0xd0);
+ break;
+ case "ROL":
+ assemblyShift(0x20);
+ break;
+ case "ROR":
+ assemblyShift(0x60);
+ break;
+ case "RPL":
+ assemblyRepeat(0x10);
+ break;
+ case "RTI":
+ putByte(0x40);
+ break;
+ case "RTS":
+ putByte(0x60);
+ break;
+ case "RUN":
+ assemblyRunIni(0x2e0);
+ break;
+ case "RVC":
+ assemblyRepeat(0x50);
+ break;
+ case "RVS":
+ assemblyRepeat(0x70);
+ break;
+ case "SBC":
+ assemblyAccumulator(0xe0, 0, 0);
+ break;
+ case "SCC":
+ assemblySkip(0x90);
+ break;
+ case "SCS":
+ assemblySkip(0xb0);
+ break;
+ case "SEC":
+ putByte(0x38);
+ break;
+ case "SED":
+ putByte(0xf8);
+ break;
+ case "SEI":
+ putByte(0x78);
+ break;
+ case "SEQ":
+ assemblySkip(0xf0);
+ break;
+ case "SMI":
+ assemblySkip(0x30);
+ break;
+ case "SNE":
+ assemblySkip(0xd0);
+ break;
+ case "SPL":
+ assemblySkip(0x10);
+ break;
+ case "STA":
+ assemblyAccumulator(0x80, 0, 0);
+ break;
+ case "STX":
+ assemblyStx(0);
+ break;
+ case "STY":
+ assemblySty(0);
+ break;
+ case "SUB":
+ assemblyAccumulator(0xe0, 0x38, 0);
+ break;
+ case "SVC":
+ assemblySkip(0x50);
+ break;
+ case "SVS":
+ assemblySkip(0x70);
+ break;
+ case "TAX":
+ putByte(0xaa);
+ break;
+ case "TAY":
+ putByte(0xa8);
+ break;
+ case "TSX":
+ putByte(0xba);
+ break;
+ case "TXA":
+ putByte(0x8a);
+ break;
+ case "TXS":
+ putByte(0x9a);
+ break;
+ case "TYA":
+ putByte(0x98);
+ break;
+ default:
+ throw new AssemblyError("Illegal instruction");
+ }
+ skipping = false;
+}
+
+debug ubyte[] testInstruction(char[] l) {
+ objectBuffer.length = 0;
+ line = l;
+ column = 0;
+ assemblyInstruction(readInstruction());
+ printf("%.*s assembles to", line);
+ foreach (ubyte b; objectBuffer) {
+ printf(" %02x", b);
+ }
+ printf("\n");
+ return objectBuffer;
+}
+
+unittest {
+ assert(testInstruction("nop") == cast(ubyte[]) x"ea");
+ assert(testInstruction("add (5,0)") == cast(ubyte[]) x"18a2006105");
+ assert(testInstruction("mwa #$abcd $1234") == cast(ubyte[]) x"a9cd8d3412a9ab8d3512");
+ assert(testInstruction("dta 5,d'Foo'*,a($4589)") == cast(ubyte[]) x"05a6efef8945");
+ assert(testInstruction("dta r(1,12,123,1234567890,12345678900000,.5,.03,000.1664534589,1e97)")
+ == cast(ubyte[]) x"400100000000 401200000000 410123000000 441234567890 461234567890 3f5000000000 3f0300000000 3f1664534589 701000000000");
+}
+
+void assemblyPair() {
+ assert(!inOpcode);
+ char[] instruction = readInstruction();
+ if (!eol() && line[column] == ':') {
+ pairing = true;
+ column++;
+ char[] instruction2 = readInstruction();
+ int savedColumn = column;
+ if (willSkip) {
+ warning("Skipping only the first instruction");
+ }
+ assemblyInstruction(instruction);
+ checkNoExtraCharacters();
+ column = savedColumn;
+ wereManyInstructions = false;
+ assemblyInstruction(instruction2);
+ wereManyInstructions = true;
+ } else {
+ pairing = false;
+ assemblyInstruction(instruction);
+ wereManyInstructions = false;
+ }
+}
+
+void assemblyLine() {
+ debug {
+ printf("%.*s\n", line);
+ }
+ currentLocation.lineNo++;
+ totalLines++;
+ column = 0;
+ listingColumn = 6;
+ if (origin >= 0) {
+ listWord(origin);
+ listingLine[listingColumn++] = ' ';
+ }
+ char[] label = readLabel();
+ currentLabel = null;
+ if (!(label is null)) {
+ if (!inFalseCondition()) {
+ if (!pass2) {
+ if (label in labelTable) {
+ throw new AssemblyError("Label declared twice");
+ }
+ currentLabel = new Label(origin);
+ labelTable[label] = currentLabel;
+ } else {
+ assert(label in labelTable);
+ currentLabel = labelTable[label];
+ currentLabel.passed = true;
+ if (currentLabel.unused && getOption('u')) {
+ warning("Unused label");
+ }
+ }
+ }
+ if (eol()) {
+ listCommentLine();
+ return;
+ }
+ readSpaces();
+ }
+ commentOrRep: for (;;) {
+ if (eol()) {
+ listCommentLine();
+ return;
+ }
+ switch (line[column]) {
+ case '\t':
+ case ' ':
+ column++;
+ continue;
+ case '*':
+ case ';':
+ case '|':
+ listCommentLine();
+ return;
+ case ':':
+ if (inFalseCondition()) {
+ listCommentLine();
+ return;
+ }
+ column++;
+ readUnsignedWord();
+ mustBeKnownInPass1();
+ int repeatLimit = value;
+ if (repeatLimit == 0) {
+ listCommentLine();
+ return;
+ }
+ readSpaces();
+ repeating = true;
+ if (repeatLimit == 1) {
+ break;
+ }
+ if (willSkip) {
+ warning("Skipping only the first instruction");
+ }
+ int savedColumn = column;
+ for (repeatCounter = 0; repeatCounter < repeatLimit; repeatCounter++) {
+ column = savedColumn;
+ assemblyPair();
+ }
+ checkNoExtraCharacters();
+ listLine();
+ wereManyInstructions = true;
+ return;
+ default:
+ repeating = false;
+ break commentOrRep;
+ }
+ }
+ if (inFalseCondition()) {
+ switch (readInstruction()) {
+ case "END":
+ assemblyEnd();
+ break;
+ case "IFT":
+ assemblyIft();
+ break;
+ case "ELI":
+ assemblyEli();
+ break;
+ case "ELS":
+ assemblyEls();
+ break;
+ case "EIF":
+ assemblyEif();
+ break;
+ default:
+ listCommentLine();
+ return;
+ }
+ checkNoExtraCharacters();
+ listCommentLine();
+ return;
+ }
+ assemblyPair();
+ checkNoExtraCharacters();
+ listLine();
+}
+
+void assemblyFile(char[] filename) {
+ if (filenameExt(filename) < 0) {
+ filename ~= ".asx";
+ }
+ if (!(currentLocation is null)) {
+ locations ~= currentLocation;
+ }
+ if (getOption('p')) {
+ filename = getFullPath(filename);
+ }
+ currentLocation = new Location(filename);
+ foundEnd = false;
+ Stream stream = openInputFile(filename);
+ line = "";
+ try {
+ readChar: while (!foundEnd) {
+ ubyte c;
+ try {
+ stream.read(c);
+ } catch (ReadException e) {
+ break;
+ }
+ switch (c) {
+ case '\r':
+ assemblyLine();
+ line = "";
+ try {
+ stream.read(c);
+ } catch (ReadException e) {
+ break readChar;
+ }
+ if (c != '\n') {
+ line ~= cast(char) c;
+ }
+ break;
+ case '\n':
+ case '\x9b':
+ assemblyLine();
+ line = "";
+ break;
+ default:
+ line ~= cast(char) c;
+ break;
+ }
+ }
+ if (!foundEnd) {
+ assemblyLine();
+ }
+ foundEnd = false;
+ } finally {
+// stream.close(); // bug in Phobos
+ }
+ if (locations.length > 0) {
+ currentLocation = locations[locations.length - 1];
+ locations.length = locations.length - 1;
+ }
+}
+
+void assemblyPass() {
+ origin = -1;
+ loadOrigin = -1;
+ loadingOrigin = -1;
+ blockIndex = -1;
+ optionFill = false;
+ option5200 = false;
+ optionHeaders = true;
+ optionListing = pass2;
+ optionObject = true;
+ willSkip = false;
+ skipping = false;
+ repeatOffset = 0;
+ wereManyInstructions = false;
+ if (commandLineDefinitions.length > 0) {
+ currentLocation = new Location("command line");
+ foreach (char[] definition; commandLineDefinitions) {
+ int i = find(definition, '=');
+ assert(i >= 0);
+ line = definition[0 .. i] ~ " equ " ~ definition[i + 1 .. definition.length];
+ assemblyLine();
+ }
+ line = null;
+ }
+ currentLocation = null;
+ totalLines = 0;
+ assemblyFile(sourceFilename);
+ if (ifContexts.length != 0) {
+ throw new AssemblyError("Missing EIF");
+ }
+ if (willSkip) {
+ throw new AssemblyError("Can't skip over this");
+ }
+}
+
+bit isOption(char[] arg) {
+ if (arg.length < 2) return false;
+ if (arg[0] == '-') return true;
+ if (arg[0] != '/') return false;
+ if (arg.length == 2) return true;
+ if (arg[2] == ':') return true;
+ return false;
+}
+
+void setOption(char letter) {
+ assert(letter >= 'a' && letter <= 'z');
+ if (options[letter - 'a']) {
+ exitCode = 3;
+ return;
+ }
+ options[letter - 'a'] = true;
+}
+
+int main(char[][] args) {
+ for (int i = 1; i < args.length; i++) {
+ char[] arg = args[i];
+ if (isOption(arg)) {
+ char letter = arg[1];
+ switch (letter) {
+ case 'c':
+ case 'i':
+ case 'p':
+ case 'q':
+ case 'u':
+ if (arg.length != 2) {
+ exitCode = 3;
+ }
+ setOption(letter);
+ break;
+ case 'd':
+ char[] definition = null;
+ if (arg[0] == '/') {
+ if (arg.length >= 3 && arg[2] == ':') {
+ definition = arg[3..arg.length];
+ }
+ } else if (i + 1 < args.length && !isOption(args[i + 1])) {
+ definition = args[++i];
+ }
+ if (definition is null || find(definition, '=') < 0) {
+ exitCode = 3;
+ }
+ commandLineDefinitions ~= definition;
+ break;
+ case 'l':
+ case 't':
+ case 'o':
+ setOption(letter);
+ char[] filename = null;
+ if (arg[0] == '/') {
+ if (arg.length >= 3 && arg[2] == ':') {
+ filename = arg[3..arg.length];
+ }
+ } else if (i + 1 < args.length && !isOption(args[i + 1])) {
+ filename = args[++i];
+ }
+ if (filename is null && (letter == 'o' || arg.length != 2)) {
+ exitCode = 3;
+ }
+ optionParameters[letter - 'a'] = filename;
+ break;
+ default:
+ exitCode = 3;
+ break;
+ }
+ continue;
+ }
+ if (!(sourceFilename is null)) {
+ exitCode = 3;
+ }
+ sourceFilename = arg;
+ }
+ if (sourceFilename is null) {
+ exitCode = 3;
+ }
+ if (!getOption('q')) {
+ printf(TITLE ~ "\n");
+ }
+ if (exitCode != 0) {
+ printf(
+ "Syntax: xasm source [options]\n"
+ "/c Include false conditionals in listing\n"
+ "/d:label=value Define a label\n"
+ "/i Don't list included files\n"
+ "/l[:filename] Generate listing\n"
+ "/o:filename Set object file name\n"
+ "/p " ~ OPTION_P_DESC ~ "\n"
+ "/q Suppress info messages\n"
+ "/t[:filename] List label table\n"
+ "/u Warn of unused labels\n"
+ );
+ return exitCode;
+ }
+ try {
+ assemblyPass();
+ pass2 = true;
+ assemblyPass();
+ if (getOption('t') && labelTable.length > 0) {
+ listLabelTable();
+ }
+ } catch (AssemblyError e) {
+ if (!(line is null)) {
+ stderr.printf("%.*s\n", line);
+ }
+ stderr.printf("%.*s (%d) ERROR: %.*s\n",
+ currentLocation.filename,
+ currentLocation.lineNo, e.msg
+ );
+ exitCode = 2;
+ }
+ if (!(listingStream is null)) {
+ listingStream.close();
+ }
+ if (!(objectStream is null)) {
+ objectStream.close();
+ }
+ if (exitCode <= 1 && !getOption('q')) {
+ printf("%d lines of source assembled\n", totalLines);
+ if (objectBytes > 0) {
+ printf("%d bytes written to the object file\n", objectBytes);
+ }
+ }
+ return exitCode;
+}