├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .main.cpp.swp ├── .travis.yml ├── CIncludeDirectoryCollection.h ├── CTextStream.cpp ├── CTextStream.h ├── CZ80AssemblerTest.cpp ├── LICENSE ├── Makefile ├── Module.h ├── README.md ├── SPASM.idl ├── SPASM.rc ├── SPASM.rgs ├── SPASM.sln ├── SPASM.vcxproj ├── SPASM.vcxproj.filters ├── SPASM.vcxproj.vspscc ├── SPASM.vssscc ├── Test.vbs ├── Z80Assembler.cpp ├── Z80Assembler.h ├── Z80Assembler.rgs ├── Z80Label.cpp ├── Z80Label.h ├── appveyor.yml ├── bitmap.cpp ├── bitmap.h ├── console.cpp ├── console.h ├── directive.cpp ├── directive.h ├── dll.cpp ├── dlldata.c ├── errors.cpp ├── errors.h ├── expand_buf.cpp ├── expand_buf.h ├── export.cpp ├── gmp.h ├── hash.cpp ├── hash.h ├── inc ├── app.inc ├── arrays.inc ├── relocate.inc ├── ti83asm.inc ├── ti83plus.inc ├── ti84pce.inc ├── tokens.inc └── z80ext.inc ├── lib ├── mpir.lib ├── mpir.pdb └── x64 │ ├── mpir.lib │ └── mpir.pdb ├── list.cpp ├── list.h ├── main.cpp ├── modp_ascii.cpp ├── modp_ascii.h ├── opcodes.cpp ├── opcodes.h ├── opcodes_ez80.cpp ├── parser.cpp ├── parser.h ├── pass_one.cpp ├── pass_one.h ├── pass_two.cpp ├── pass_two.h ├── preop.cpp ├── preop.h ├── resource.h ├── spasm.h ├── stdafx.cpp ├── stdafx.h ├── storage.cpp ├── storage.h ├── targetver.h ├── test.asm ├── tests ├── bugs │ ├── empty-macro-name.asm │ ├── extra-long-addinstr.asm │ ├── non-existent-jmp.asm │ ├── pass_one_eol.asm │ └── too-far-relative-jmp.asm ├── dd.asm ├── empty-macro.asm ├── eval-neg.asm ├── jr-ez80-offset.asm ├── macro-string-argument.asm ├── macro-string-argument.bin ├── name-starts-with-reg.asm ├── neg-index-offset.asm ├── space-after-unary.asm ├── test-runner.py └── undoc-ez80.asm ├── utils.cpp ├── utils.h ├── version.h ├── version.sh └── version_base.h /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Build 11 | run: make 12 | - name: Test 13 | run: make check 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # SPASM executable 32 | spasm 33 | 34 | # Debian packages 35 | *.deb 36 | 37 | # Visual Studio IDL generated source 38 | SPASM_i.c 39 | SPASM_i.h 40 | SPASM_h.h 41 | 42 | ####################################################################### 43 | # Visual Studio .gitignore 44 | ####################################################################### 45 | 46 | ## Ignore Visual Studio temporary files, build results, and 47 | ## files generated by popular Visual Studio add-ons. 48 | 49 | # User-specific files 50 | *.suo 51 | *.user 52 | *.userosscache 53 | *.sln.docstates 54 | 55 | # User-specific files (MonoDevelop/Xamarin Studio) 56 | *.userprefs 57 | 58 | # Build results 59 | [Dd]ebug/ 60 | [Dd]ebugPublic/ 61 | [Rr]elease/ 62 | [Rr]eleases/ 63 | x64/ 64 | x86/ 65 | build/ 66 | bld/ 67 | [Bb]in/ 68 | [Oo]bj/ 69 | 70 | # Visual Studio 2015 cache/options directory 71 | .vs/ 72 | # Uncomment if you have tasks that create the project's static files in wwwroot 73 | #wwwroot/ 74 | 75 | # MSTest test Results 76 | [Tt]est[Rr]esult*/ 77 | [Bb]uild[Ll]og.* 78 | 79 | # NUNIT 80 | *.VisualState.xml 81 | TestResult.xml 82 | 83 | # Build Results of an ATL Project 84 | [Dd]ebugPS/ 85 | [Rr]eleasePS/ 86 | dlldata.c 87 | 88 | # DNX 89 | project.lock.json 90 | artifacts/ 91 | 92 | *_i.c 93 | *_p.c 94 | *_i.h 95 | *.ilk 96 | *.meta 97 | *.obj 98 | *.pch 99 | *.pdb 100 | *.pgc 101 | *.pgd 102 | *.rsp 103 | *.sbr 104 | *.tlb 105 | *.tli 106 | *.tlh 107 | *.tmp 108 | *.tmp_proj 109 | *.log 110 | *.vspscc 111 | *.vssscc 112 | .builds 113 | *.pidb 114 | *.svclog 115 | *.scc 116 | 117 | # Chutzpah Test files 118 | _Chutzpah* 119 | 120 | # Visual C++ cache files 121 | ipch/ 122 | *.aps 123 | *.ncb 124 | *.opendb 125 | *.opensdf 126 | *.sdf 127 | *.cachefile 128 | 129 | # Visual Studio profiler 130 | *.psess 131 | *.vsp 132 | *.vspx 133 | *.sap 134 | 135 | # TFS 2012 Local Workspace 136 | $tf/ 137 | 138 | # Guidance Automation Toolkit 139 | *.gpState 140 | 141 | # ReSharper is a .NET coding add-in 142 | _ReSharper*/ 143 | *.[Rr]e[Ss]harper 144 | *.DotSettings.user 145 | 146 | # JustCode is a .NET coding add-in 147 | .JustCode 148 | 149 | # TeamCity is a build add-in 150 | _TeamCity* 151 | 152 | # DotCover is a Code Coverage Tool 153 | *.dotCover 154 | 155 | # NCrunch 156 | _NCrunch_* 157 | .*crunch*.local.xml 158 | nCrunchTemp_* 159 | 160 | # MightyMoose 161 | *.mm.* 162 | AutoTest.Net/ 163 | 164 | # Web workbench (sass) 165 | .sass-cache/ 166 | 167 | # Installshield output folder 168 | [Ee]xpress/ 169 | 170 | # DocProject is a documentation generator add-in 171 | DocProject/buildhelp/ 172 | DocProject/Help/*.HxT 173 | DocProject/Help/*.HxC 174 | DocProject/Help/*.hhc 175 | DocProject/Help/*.hhk 176 | DocProject/Help/*.hhp 177 | DocProject/Help/Html2 178 | DocProject/Help/html 179 | 180 | # Click-Once directory 181 | publish/ 182 | 183 | # Publish Web Output 184 | *.[Pp]ublish.xml 185 | *.azurePubxml 186 | # TODO: Comment the next line if you want to checkin your web deploy settings 187 | # but database connection strings (with potential passwords) will be unencrypted 188 | *.pubxml 189 | *.publishproj 190 | 191 | # NuGet Packages 192 | *.nupkg 193 | # The packages folder can be ignored because of Package Restore 194 | **/packages/* 195 | # except build/, which is used as an MSBuild target. 196 | !**/packages/build/ 197 | # Uncomment if necessary however generally it will be regenerated when needed 198 | #!**/packages/repositories.config 199 | 200 | # Microsoft Azure Build Output 201 | csx/ 202 | *.build.csdef 203 | 204 | # Microsoft Azure Emulator 205 | ecf/ 206 | rcf/ 207 | 208 | # Microsoft Azure ApplicationInsights config file 209 | ApplicationInsights.config 210 | 211 | # Windows Store app package directory 212 | AppPackages/ 213 | BundleArtifacts/ 214 | 215 | # Visual Studio cache files 216 | # files ending in .cache can be ignored 217 | *.[Cc]ache 218 | # but keep track of directories ending in .cache 219 | !*.[Cc]ache/ 220 | 221 | # Others 222 | ClientBin/ 223 | ~$* 224 | *~ 225 | *.dbmdl 226 | *.dbproj.schemaview 227 | *.pfx 228 | *.publishsettings 229 | node_modules/ 230 | orleans.codegen.cs 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | 243 | # SQL Server files 244 | *.mdf 245 | *.ldf 246 | 247 | # Business Intelligence projects 248 | *.rdl.data 249 | *.bim.layout 250 | *.bim_*.settings 251 | 252 | # Microsoft Fakes 253 | FakesAssemblies/ 254 | 255 | # GhostDoc plugin setting file 256 | *.GhostDoc.xml 257 | 258 | # Node.js Tools for Visual Studio 259 | .ntvs_analysis.dat 260 | 261 | # Visual Studio 6 build log 262 | *.plg 263 | 264 | # Visual Studio 6 workspace options file 265 | *.opt 266 | 267 | # Visual Studio LightSwitch build output 268 | **/*.HTMLClient/GeneratedArtifacts 269 | **/*.DesktopClient/GeneratedArtifacts 270 | **/*.DesktopClient/ModelManifest.xml 271 | **/*.Server/GeneratedArtifacts 272 | **/*.Server/ModelManifest.xml 273 | _Pvt_Extensions 274 | 275 | # Paket dependency manager 276 | .paket/paket.exe 277 | 278 | # FAKE - F# Make 279 | .fake/ -------------------------------------------------------------------------------- /.main.cpp.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberthdev/spasm-ng/5f0786d38f064835be674d4b7df42969967bb73c/.main.cpp.swp -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | before_install: 4 | - sudo add-apt-repository ppa:fkrull/deadsnakes -y 5 | - sudo apt-get update -qq 6 | - pip install --user cpp-coveralls 7 | 8 | install: 9 | - sudo apt-get install -y --allow-unauthenticated -qq zlib1g-dev python3.5 10 | - sudo apt-get install -y -qq binutils-mingw-w64-i686 11 | - sudo apt-get install -y -qq gcc-mingw-w64-i686 12 | - sudo apt-get install -y -qq g++-mingw-w64-i686 13 | 14 | script: 15 | - make NO_APPSIGN=1 16 | - make PYTHON=python3.5 check 17 | - make clean 18 | - sudo apt-get install -y -qq libssl-dev libgmp-dev 19 | - make 20 | - echo MinGW Build 21 | - make clean 22 | - make CROSS_COMPILE=i686-w64-mingw32- NO_APPSIGN=1 23 | - make clean 24 | - make 25 | - make PYTHON=python3.5 check 26 | - make PYTHON=python3.5 coverage 27 | 28 | after_success: 29 | - coveralls --gcov-options '\-lp' 30 | 31 | notifications: 32 | irc: 33 | channels: 34 | - "irc.choopa.net#spasm-ng" 35 | on_success: change 36 | on_failure: always 37 | skip_join: true 38 | -------------------------------------------------------------------------------- /CIncludeDirectoryCollection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef SPASM_NG_ENABLE_COM 3 | struct _CopyVariantFromAdaptBstr { 4 | static HRESULT copy(VARIANT* p1, const CAdapt* p2) { 5 | p1->vt = VT_BSTR; 6 | p1->bstrVal = p2->m_T.Copy(); 7 | return (p1->bstrVal ? S_OK : E_OUTOFMEMORY); 8 | } 9 | 10 | static void init(VARIANT* p) { VariantInit(p); } 11 | static void destroy(VARIANT* p) { VariantClear(p); } 12 | }; 13 | 14 | typedef CComEnumOnSTL > > 17 | CComEnumVariantOnVectorOfAdaptBstr; 18 | 19 | struct _CopyBstrFromAdaptBstr { 20 | static HRESULT copy(BSTR* p1, const CAdapt* p2) { 21 | *p1 = SysAllocString(p2->m_T); 22 | return (p1 ? S_OK : E_OUTOFMEMORY); 23 | } 24 | 25 | static void init(BSTR* p) { } 26 | static void destroy(BSTR* p) { SysFreeString(*p); } 27 | }; 28 | 29 | typedef ICollectionOnSTLImpl, 30 | std::vector< CAdapt >, 31 | BSTR, 32 | _CopyBstrFromAdaptBstr, 33 | CComEnumVariantOnVectorOfAdaptBstr> 34 | IIncludeDirectoryCollectionImpl; 35 | 36 | 37 | class ATL_NO_VTABLE CIncludeDirectoryCollection : 38 | public CComObjectRootEx, 39 | public IIncludeDirectoryCollectionImpl 40 | { 41 | public: 42 | BEGIN_COM_MAP(CIncludeDirectoryCollection) 43 | COM_INTERFACE_ENTRY(IIncludeDirectoryCollection) 44 | COM_INTERFACE_ENTRY(IDispatch) 45 | END_COM_MAP() 46 | 47 | STDMETHOD(Add)(BSTR bstrDirectory) 48 | { 49 | m_coll.push_back(CAdapt< CComBSTR >(bstrDirectory)); 50 | return S_OK; 51 | } 52 | 53 | STDMETHOD(Remove)(BSTR bstrDirectory) 54 | { 55 | for (auto it = m_coll.begin(); it != m_coll.end(); it++) 56 | { 57 | if (wcscmp((*it).m_T, bstrDirectory) == 0) 58 | { 59 | m_coll.erase(it); 60 | return S_OK; 61 | } 62 | } 63 | return S_FALSE; 64 | } 65 | 66 | STDMETHOD(Clear)() 67 | { 68 | m_coll.clear(); 69 | return S_OK; 70 | } 71 | }; 72 | #endif -------------------------------------------------------------------------------- /CTextStream.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "CTextStream.h" 4 | 5 | #define MAX_PIPE_SIZE (1024 * 1024) 6 | 7 | #ifdef SPASM_NG_ENABLE_COM 8 | HRESULT CTextStream::FinalConstruct() 9 | { 10 | HANDLE hRead, hWrite; 11 | BOOL fResult = CreatePipe(&hRead, &hWrite, NULL, MAX_PIPE_SIZE); 12 | if (fResult) 13 | { 14 | //DWORD dwMode = PIPE_READMODE_BYTE | PIPE_NOWAIT; 15 | //fResult = SetNamedPipeHandleState(hRead, &dwMode, NULL, NULL); 16 | fResult = SetStdHandle(STD_OUTPUT_HANDLE, hWrite); 17 | m_hRead = hRead; 18 | 19 | stdout->_file = _open_osfhandle((long) hWrite, _O_TEXT); 20 | 21 | } 22 | return S_OK; 23 | } 24 | 25 | 26 | STDMETHODIMP CTextStream::get_AtEndOfLine(VARIANT_BOOL *bEOL) 27 | { 28 | return E_NOTIMPL; 29 | } 30 | 31 | STDMETHODIMP CTextStream::get_AtEndOfStream(VARIANT_BOOL *bEOS) 32 | { 33 | fflush(stdout); 34 | 35 | DWORD dwAvail; 36 | BOOL fResult = PeekNamedPipe(m_hRead, NULL, 0, NULL, &dwAvail, NULL); 37 | *bEOS = (dwAvail == 0) ? VARIANT_TRUE : VARIANT_FALSE; 38 | return S_OK; 39 | } 40 | 41 | STDMETHODIMP CTextStream::Read(LONG cch, LPBSTR lpbstrResult) 42 | { 43 | fflush(stdout); 44 | DWORD dwRead; 45 | CComHeapPtr szText; 46 | int cbText = min(MAX_PIPE_SIZE, cch) * sizeof(TCHAR); 47 | szText.Allocate(cbText); 48 | BOOL fResult = ReadFile(m_hRead, szText, cbText, &dwRead, NULL); 49 | if (fResult) 50 | { 51 | szText[dwRead / sizeof(TCHAR)] = _T('\0'); 52 | *lpbstrResult = SysAllocString(_bstr_t(szText)); 53 | return S_OK; 54 | } 55 | return E_FAIL; 56 | } 57 | 58 | STDMETHODIMP CTextStream::ReadLine(LPBSTR lpbstrLine) 59 | { 60 | fflush(stdout); 61 | 62 | _bstr_t result; 63 | 64 | TCHAR ch[2] = {_T('\0')}; 65 | DWORD dwRead; 66 | BOOL fResult; 67 | do 68 | { 69 | fResult = ReadFile(m_hRead, ch, sizeof(TCHAR), &dwRead, NULL); 70 | if (fResult && dwRead == sizeof(TCHAR) && ch[0] != _T('\r') && ch[0] != _T('\n')) 71 | { 72 | result += ch; 73 | } 74 | } 75 | while (ch[0] != _T('\n') && fResult && dwRead > 0); 76 | 77 | *lpbstrLine = SysAllocString(result); 78 | return S_OK; 79 | } 80 | 81 | STDMETHODIMP CTextStream::ReadAll(LPBSTR lpbstrResult) 82 | { 83 | return Read(LONG_MAX, lpbstrResult); 84 | } 85 | 86 | STDMETHODIMP CTextStream::Write(BSTR bstrText) 87 | { 88 | return E_NOTIMPL; 89 | } 90 | 91 | STDMETHODIMP CTextStream::WriteLine(BSTR bstrLine) 92 | { 93 | return E_NOTIMPL; 94 | } 95 | 96 | STDMETHODIMP CTextStream::WriteBlankLines(LONG nLines) 97 | { 98 | return E_NOTIMPL; 99 | } 100 | 101 | STDMETHODIMP CTextStream::Skip(LONG cch) 102 | { 103 | BSTR bstrLine = NULL; 104 | HRESULT hr = Read(cch, &bstrLine); 105 | return hr; 106 | } 107 | 108 | STDMETHODIMP CTextStream::SkipLine() 109 | { 110 | BSTR bstrLine = NULL; 111 | HRESULT hr = ReadLine(&bstrLine); 112 | return hr; 113 | } 114 | STDMETHODIMP CTextStream::Close() 115 | { 116 | return S_FALSE; 117 | } 118 | #endif -------------------------------------------------------------------------------- /CTextStream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef SPASM_NG_ENABLE_COM 4 | class ATL_NO_VTABLE CTextStream : 5 | public CComObjectRootEx, 6 | public IDispatchImpl 7 | { 8 | public: 9 | BEGIN_COM_MAP(CTextStream) 10 | COM_INTERFACE_ENTRY(ITextStream) 11 | COM_INTERFACE_ENTRY(IDispatch) 12 | END_COM_MAP() 13 | 14 | HRESULT FinalConstruct(); 15 | 16 | STDMETHOD(get_Line)(LONG *plLine) 17 | { 18 | return E_NOTIMPL; 19 | } 20 | STDMETHOD(get_Column)(LONG *plColumn) 21 | { 22 | return E_NOTIMPL; 23 | } 24 | 25 | STDMETHOD(get_AtEndOfLine)(VARIANT_BOOL *bEOL); 26 | STDMETHOD(get_AtEndOfStream)(VARIANT_BOOL *bEOS); 27 | 28 | STDMETHOD(Read)(LONG cch, LPBSTR lpbstrResult); 29 | STDMETHOD(ReadLine)(LPBSTR lpbstrLine); 30 | STDMETHOD(ReadAll)(LPBSTR lpbstrResult); 31 | 32 | STDMETHOD(Write)(BSTR bstrText); 33 | STDMETHOD(WriteLine)(BSTR bstrLine); 34 | STDMETHOD(WriteBlankLines)(LONG nLines); 35 | 36 | STDMETHOD(Skip)(LONG cch); 37 | STDMETHOD(SkipLine)(); 38 | STDMETHOD(Close)(); 39 | 40 | private: 41 | HANDLE m_hRead; 42 | }; 43 | #endif -------------------------------------------------------------------------------- /CZ80AssemblerTest.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "spasm.h" 4 | #include "storage.h" 5 | #include "errors.h" 6 | #include "parser.h" 7 | #include "CTextStream.h" 8 | #include "CIncludeDirectoryCollection.h" 9 | #include "hash.h" 10 | 11 | #include "Module.h" 12 | 13 | #ifdef SPASM_NG_ENABLE_COM 14 | class ATL_NO_VTABLE CZ80Assembler : 15 | public CComObjectRootEx, 16 | public CComCoClass, 17 | public IDispatchImpl 18 | { 19 | public: 20 | DECLARE_REGISTRY_RESOURCEID(IDR_Z80ASSEMBLER) 21 | //DECLARE_CLASSFACTORY_SINGLETON(CZ80Assembler) 22 | 23 | BEGIN_COM_MAP(CZ80Assembler) 24 | COM_INTERFACE_ENTRY(IZ80Assembler) 25 | COM_INTERFACE_ENTRY(IDispatch) 26 | END_COM_MAP() 27 | 28 | HRESULT FinalConstruct() 29 | { 30 | HRESULT hr = m_dict.CreateInstance(__uuidof(Dictionary)); 31 | 32 | CComObject *pObj = NULL; 33 | CComObject::CreateInstance(&pObj); 34 | pObj->AddRef(); 35 | m_pStdOut = pObj; 36 | pObj->Release(); 37 | 38 | CComObject *pDirObj = NULL; 39 | CComObject::CreateInstance(&pDirObj); 40 | pDirObj->AddRef(); 41 | m_pDirectories = pDirObj; 42 | pDirObj->Release(); 43 | 44 | m_fFirstAssembly = TRUE; 45 | m_dwOptions = 0; 46 | 47 | AtlComModuleRevokeClassObjects(&_AtlComModule); 48 | return hr; 49 | } 50 | 51 | void FinalRelease() 52 | { 53 | if (!m_fFirstAssembly) 54 | { 55 | free_storage(); 56 | } 57 | } 58 | 59 | STDMETHOD(get_Defines)(IDictionary **ppDictionary) 60 | { 61 | return m_dict->QueryInterface(ppDictionary); 62 | } 63 | 64 | static void label_enum_callback(void *rawlabel, void *arg) 65 | { 66 | IDictionary *pDict = (IDictionary *) arg; 67 | label_t *label = (label_t *) rawlabel; 68 | 69 | CComVariant name(label->name); 70 | CComVariant value((unsigned int) label->value, VT_UI4); 71 | 72 | pDict->Add(&name, &value); 73 | } 74 | 75 | STDMETHOD(get_Labels)(IDictionary **ppLabels) 76 | { 77 | IDictionaryPtr labels(__uuidof(Dictionary)); 78 | if (label_table != NULL) 79 | { 80 | hash_enum(label_table, label_enum_callback, labels.GetInterfacePtr()); 81 | } 82 | return labels->QueryInterface(ppLabels); 83 | } 84 | 85 | STDMETHOD(get_StdOut)(ITextStream **ppStream) 86 | { 87 | return m_pStdOut->QueryInterface(ppStream); 88 | } 89 | 90 | STDMETHOD(get_InputFile)(LPBSTR lpbstrInputFile) 91 | { 92 | *lpbstrInputFile = SysAllocString(m_bstrInputFile); 93 | return S_OK; 94 | } 95 | 96 | STDMETHOD(put_InputFile)(BSTR bstrInputFile) 97 | { 98 | m_bstrInputFile = bstrInputFile; 99 | return S_OK; 100 | } 101 | 102 | STDMETHODIMP put_OutputFile(BSTR bstrOutputFile) 103 | { 104 | m_bstrOutputFile = bstrOutputFile; 105 | return S_OK; 106 | } 107 | 108 | STDMETHODIMP get_OutputFile(LPBSTR lpbstrOutputFile) 109 | { 110 | *lpbstrOutputFile = SysAllocString(m_bstrOutputFile); 111 | return S_OK; 112 | } 113 | 114 | STDMETHODIMP put_CurrentDirectory(BSTR bstrDirectory) 115 | { 116 | SetCurrentDirectory(_bstr_t(bstrDirectory)); 117 | return S_OK; 118 | } 119 | 120 | STDMETHODIMP get_CurrentDirectory(LPBSTR lpbstrDirectory) 121 | { 122 | TCHAR szBuffer[MAX_PATH]; 123 | GetCurrentDirectory(ARRAYSIZE(szBuffer), szBuffer); 124 | *lpbstrDirectory = SysAllocString(_bstr_t(szBuffer)); 125 | return S_OK; 126 | } 127 | 128 | STDMETHOD(get_Options)(LPDWORD lpdwOptions) 129 | { 130 | *lpdwOptions = m_dwOptions; 131 | return S_OK; 132 | } 133 | 134 | STDMETHOD(put_Options)(DWORD dwOptions) 135 | { 136 | m_dwOptions = dwOptions; 137 | return S_OK; 138 | } 139 | 140 | STDMETHOD(get_CaseSensitive)(VARIANT_BOOL *lpCaseSensitive) 141 | { 142 | *lpCaseSensitive = get_case_sensitive() ? VARIANT_TRUE : VARIANT_FALSE; 143 | return S_OK; 144 | } 145 | 146 | STDMETHOD(put_CaseSensitive)(VARIANT_BOOL caseSensitive) 147 | { 148 | set_case_sensitive(caseSensitive == VARIANT_TRUE ? TRUE : FALSE); 149 | return S_OK; 150 | } 151 | 152 | STDMETHOD(get_IncludeDirectories)(IIncludeDirectoryCollection **ppDirectories) 153 | { 154 | return m_pDirectories->QueryInterface(ppDirectories); 155 | } 156 | 157 | STDMETHOD(Assemble)(VARIANT varInput, IStream **ppOutput) 158 | { 159 | HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, output_buf_size); 160 | output_contents = (unsigned char *) GlobalLock(hGlobal); 161 | 162 | mode = m_dwOptions; 163 | 164 | if (V_VT(&varInput) == VT_BSTR) 165 | { 166 | mode |= MODE_NORMAL | MODE_COMMANDLINE; 167 | mode &= ~MODE_LIST; 168 | 169 | if (m_fFirstAssembly) 170 | { 171 | init_storage(); 172 | } 173 | 174 | CW2CT szInput(V_BSTR(&varInput)); 175 | input_contents = strdup(szInput); 176 | 177 | curr_input_file = strdup("COM Interface"); 178 | } 179 | else 180 | { 181 | mode &= ~MODE_COMMANDLINE; 182 | mode |= MODE_NORMAL; 183 | 184 | curr_input_file = strdup(m_bstrInputFile); 185 | output_filename = strdup(m_bstrOutputFile); 186 | 187 | if (!m_fFirstAssembly) 188 | { 189 | free_storage(); 190 | } 191 | 192 | init_storage(); 193 | } 194 | 195 | // Set up the include directories 196 | CComPtr pEnumUnk; 197 | HRESULT hr = m_pDirectories->get__NewEnum(&pEnumUnk); 198 | 199 | CComQIPtr pEnum = pEnumUnk; 200 | 201 | CComVariant varItem; 202 | ULONG ulFetched; 203 | 204 | while (pEnum->Next(1, &varItem, &ulFetched) == S_OK) 205 | { 206 | include_dirs = list_prepend(include_dirs, (char *) strdup(_bstr_t(V_BSTR(&varItem)))); 207 | } 208 | 209 | AddDefines(); 210 | 211 | int error = run_assembly(); 212 | 213 | list_free(include_dirs, true, NULL); 214 | include_dirs = NULL; 215 | 216 | ClearSPASMErrorSessions(); 217 | 218 | GlobalUnlock(hGlobal); 219 | 220 | CComPtr pStream; 221 | hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); 222 | ULARGE_INTEGER ul; 223 | ul.QuadPart = out_ptr - output_contents; 224 | pStream->SetSize(ul); 225 | 226 | m_fFirstAssembly = FALSE; 227 | if (output_filename != NULL) 228 | { 229 | free(output_filename); 230 | output_filename = NULL; 231 | } 232 | if (curr_input_file) 233 | { 234 | free(curr_input_file); 235 | curr_input_file = NULL; 236 | } 237 | 238 | if (mode & MODE_COMMANDLINE) 239 | { 240 | free(input_contents); 241 | input_contents = NULL; 242 | } 243 | return pStream->QueryInterface(ppOutput); 244 | } 245 | 246 | STDMETHOD(Parse)(BSTR bstrInput, LPBSTR lpbstrOutput) 247 | { 248 | return E_NOTIMPL; 249 | } 250 | 251 | private: 252 | void AddDefines() 253 | { 254 | 255 | CComPtr pEnumUnk; 256 | HRESULT hr = m_dict->_NewEnum(&pEnumUnk); 257 | 258 | CComQIPtr pEnum = pEnumUnk; 259 | 260 | CComVariant varItem; 261 | ULONG ulFetched; 262 | 263 | while (pEnum->Next(1, &varItem, &ulFetched) == S_OK) 264 | { 265 | _bstr_t key = V_BSTR(&varItem); 266 | 267 | _variant_t varValue; 268 | m_dict->get_Item(&varItem, &varValue); 269 | 270 | _bstr_t val = varValue; 271 | 272 | bool redefined; 273 | define_t *define = add_define(strdup(key), &redefined); 274 | set_define(define, val, -1, redefined); 275 | } 276 | } 277 | 278 | BOOL m_fFirstAssembly; 279 | DWORD m_dwOptions; 280 | IDictionaryPtr m_dict; 281 | CComPtr m_pStdOut; 282 | CComPtr m_pDirectories; 283 | _bstr_t m_bstrInputFile; 284 | _bstr_t m_bstrOutputFile; 285 | }; 286 | 287 | OBJECT_ENTRY_AUTO(__uuidof(Z80Assembler), CZ80Assembler) 288 | #endif -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ISWIN = $(or $(findstring Windows,$(OS)),$(findstring mingw,$(CROSS_COMPILE))) 2 | CC = $(CROSS_COMPILE)g++ 3 | LD = $(CROSS_COMPILE)ld 4 | STRIP = $(CROSS_COMPILE)strip 5 | CXXFLAGS+= -Wall -DUSE_REUSABLES $(if $(ISWIN),,-DUNIXVER) -DUSE_BUILTIN_FCREATE 6 | LDFLAGS+= -lm 7 | 8 | DESTDIR ?= /usr/local 9 | 10 | ifdef FORCE_NO_GIT 11 | FORCE_NO_GIT = 1 12 | endif 13 | export FORCE_NO_GIT 14 | 15 | VERSION=$$(./version.sh | head -n 1) 16 | VERSION_DPKG=$$(./version.sh dpkg) 17 | GITREV=$$(./version.sh | grep "Git") 18 | 19 | ifdef NO_APPSIGN 20 | CXXFLAGS += -DNO_APPSIGN 21 | else 22 | LDFLAGS += -lgmp -lcrypto 23 | endif 24 | 25 | export CXXFLAGS 26 | export LDFLAGS 27 | 28 | # Suffix Rules 29 | .SUFFIXES: .cpp 30 | 31 | .cpp.o: 32 | $(CC) $(CXXFLAGS) -c $< 33 | 34 | .cpp: 35 | $(CC) $(CXXFLAGS) $< -o $@ 36 | 37 | SRC = main.cpp opcodes.cpp pass_one.cpp pass_two.cpp utils.cpp export.cpp preop.cpp directive.cpp console.cpp \ 38 | expand_buf.cpp hash.cpp list.cpp parser.cpp storage.cpp errors.cpp bitmap.cpp modp_ascii.cpp opcodes_ez80.cpp 39 | OBJ = $(addsuffix .o, $(basename $(SRC))) 40 | OBJ_FILES = $(addsuffix .o, $(basename $(notdir $(SRC)))) 41 | EXE = $(if $(ISWIN),spasm.exe,spasm) 42 | 43 | $(EXE): $(OBJ) Makefile 44 | $(CC) -o $@ $(OBJ_FILES) $(LDFLAGS) 45 | $(STRIP) $@ 46 | 47 | debug: CXXFLAGS+= -g 48 | debug: STRIP= : 49 | debug: $(EXE) 50 | 51 | debugp: CXXFLAGS+= -g -DDEBUG_PRINT 52 | debugp: STRIP= : 53 | debugp: $(EXE) 54 | 55 | prep-special-build: 56 | $(MAKE) clean 57 | touch prep-special-build 58 | 59 | opt: CXXFLAGS+= -O3 60 | opt: prep-special-build $(OBJ) 61 | touch opt 62 | 63 | static: LDFLAGS+= -static 64 | static: $(EXE) 65 | touch static 66 | 67 | opt-static: opt static 68 | 69 | tar: opt-static 70 | tar czvf spasm-ng_$(VERSION)_binary.tar.gz $(EXE) README.md LICENSE inc/ 71 | 72 | # This is a fake Debian package builder - it uses checkinstall 73 | # to make this work. 74 | debian: opt $(EXE) 75 | echo "SPASM-ng is a z80 assembler with extra features to support development for TI calculators." > description-pak 76 | checkinstall --requires "zlib1g, libssl1.0.0, libgmp10" \ 77 | --pkgname="spasm-ng" --pkgversion="$(VERSION_DPKG)" --pkgrelease="1" \ 78 | --maintainer="alberthdev@users.noreply.github.com" \ 79 | --backup=no --deldoc=yes --deldesc=yes --delspec=yes \ 80 | --install=no --default $(CHECKINSTALL_OPTS) 81 | rm -f description-pak 82 | 83 | install: 84 | cp $(EXE) $(DESTDIR)/bin/$(EXE) 85 | 86 | check: $(EXE) 87 | $(PYTHON) tests/test-runner.py ./$(EXE) 88 | 89 | coverage: CXXFLAGS+=-g -O0 --coverage 90 | coverage: LDFLAGS+=-g -O0 --coverage 91 | coverage: clean check 92 | 93 | clean: 94 | rm -f $(OBJ) $(EXE) description-pak spasm-ng*.deb spasm-ng*.tar.gz 95 | rm -f opt static prep-special-build 96 | rm -f *.gcno *.gcda *.gcov 97 | 98 | version: 99 | @./version.sh set 100 | @echo "The current spasm-ng version is: $(VERSION)" 101 | @test -n "$(GITREV)" && echo "$(GITREV)" || exit 0 102 | -------------------------------------------------------------------------------- /Module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _WINDOWS 4 | #ifndef _TEST 5 | #include "SPASM_h.h" 6 | 7 | #ifdef SPASM_NG_ENABLE_COM 8 | class CSPASMModule : public ATL::CAtlExeModuleT 9 | { 10 | public: 11 | DECLARE_LIBID(LIBID_SPASM) 12 | }; 13 | 14 | extern CSPASMModule _AtlModule; 15 | #endif 16 | 17 | #endif 18 | #endif 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | spasm-ng 2 | ======== 3 | [![Linux Build Status](https://travis-ci.org/alberthdev/spasm-ng.svg?branch=master)](https://travis-ci.org/alberthdev/spasm-ng) 4 | [![Windows Build status](https://ci.appveyor.com/api/projects/status/3bl3ys8kw844acqd/branch/master?svg=true)](https://ci.appveyor.com/project/alberthdev/spasm-ng/branch/master) 5 | [![Coverage Status](https://coveralls.io/repos/alberthdev/spasm-ng/badge.svg?branch=master&service=github)](https://coveralls.io/github/alberthdev/spasm-ng?branch=master) 6 | [![Coverity Status](https://scan.coverity.com/projects/8367/badge.svg)](https://scan.coverity.com/projects/alberthdev-spasm-ng) 7 | [![IRC Channel - #spasm-ng on EFNet](https://img.shields.io/badge/irc%20channel-%23spasm--ng%20on%20EFNet-blue.svg)](http://chat.efnet.org/irc.cgi?adv=1&nick=spasmng&chan=%23spasm-ng) 8 | 9 | SPASM-ng is a z80/eZ80 assembler with extra features to support development 10 | for TI calculators. 11 | 12 | Downloads 13 | --------- 14 | Releases with pre-built binaries may be found here: 15 | 16 | ### [Downloads](../../releases) 17 | 18 | Requirements 19 | ------------ 20 | On Windows, you will need the following to build SPASM-ng: 21 | 22 | * Visual Studio with C++ support 23 | 24 | Visual Studio 2010 or newer is recommended, but it may be possible 25 | to downgrade. See http://stackoverflow.com/a/16196505 for more info. 26 | 27 | Note that if you're using Visual Studio 2010, SP1 or newer is required. 28 | Without the service pack, the build will fail. 29 | 30 | Visual Studio Express editions can be used for building. 31 | This include Express for Desktop, Visual C++ Express, etc. 32 | 33 | If you want to build with COM support, however, you will need the ATL 34 | include files. You can get these by either: 35 | 36 | * using Visual Studio Community (2015+, free w/restrictions); 37 | * using Visual Studio Professional; 38 | * installing Windows SDKs that contain the ATL include files, and 39 | reconfiguring the project accordingly. 40 | 41 | On Linux, Mac OS X, and other \*nix, you will need the following to 42 | build SPASM-ng: 43 | 44 | * C/C++ Compiler (GCC, Clang, etc.) 45 | * GMP Library (found at https://gmplib.org/) 46 | * OpenSSL Library (found at https://www.openssl.org/) 47 | 48 | Note that if you are using Linux, your distribution will likely have 49 | pre-built packages (and development packages) available to install. 50 | You should use these first before attempting to install the above 51 | libraries yourself. 52 | 53 | Suggested packages for Ubuntu/Debian: 54 | 55 | * build-essential 56 | * libssl-dev 57 | * zlib1g-dev 58 | * libgmp-dev 59 | * checkinstall (optional, only if you want to build Debian packages - 60 | .deb files) 61 | 62 | If building without app signing support (see *Building* in this document), 63 | GMP and OpenSSL are not required. 64 | 65 | To run the tests, you will also need Python 3.1+. Versions below 66 | Python 3.1 will not work! 67 | 68 | Building 69 | -------- 70 | On Windows, simply build with the included Visual Studio project file. 71 | 72 | If you wish to build with COM support, you must: 73 | 74 | * define `SPASM_NG_ENABLE_COM` globally within the Visual Studio 75 | project; 76 | * ensure that ATL include files are accessible. 77 | 78 | On Linux, Mac OS X, and other \*nix, simply build by running: 79 | 80 | ```bash 81 | # Git users: run this to update the version 82 | # ONLY RUN THIS IF YOU ARE USING GIT 83 | make version 84 | 85 | # Now build! 86 | make 87 | 88 | # Optional: install SPASM-ng (you may need sudo/su/root): 89 | make install 90 | ``` 91 | 92 | To disable app signing support in your binary (thus eliminating the 93 | dependencies on OpenSSL and GMP), define NO\_APPSIGN when invoking `make`: 94 | 95 | ```bash 96 | make NO_APPSIGN=1 97 | ``` 98 | 99 | For Debian systems, you can run the following to create a Debian 100 | package: 101 | 102 | ```bash 103 | # You should "make clean" before running this! 104 | make clean 105 | 106 | # Create a Debian package (builds opt target): 107 | make debian 108 | ``` 109 | 110 | Other Makefile commands: 111 | 112 | ```bash 113 | # You should "make clean" before running any of this! 114 | make clean 115 | 116 | # Build with -static (static linking, bigger binary due to 117 | # including libraries): 118 | make static 119 | 120 | # Build with -O3 optimization: 121 | make opt 122 | 123 | # Build with both -static and -O3 optimization: 124 | make opt-static 125 | 126 | # Create tarball of spasm-ng binary (builds opt-static target): 127 | make tar 128 | ``` 129 | 130 | Cross-compiling can be enabled by simply setting the cross-compiling 131 | prefix, e.g. 132 | 133 | ```bash 134 | make CROSS_COMPILE=i686-w64-mingw32- 135 | ``` 136 | 137 | Documentation 138 | ------------- 139 | The program accepts standard Z80 assembly and eZ80 assembly. 140 | 141 | SPASM command line help shown below: 142 | 143 | SPASM-ng Z80 Assembler by Spencer Putt and Don Straney 144 | 145 | 146 | spasm [options] 147 | 148 | Options: 149 | -E = Assemble eZ80 code 150 | -T = Generate code listing 151 | -C = Code counter mode 152 | -L = Symbol table mode 153 | -S = Stats mode 154 | -O = Don't write to output file 155 | -I [directory] = Add include directory 156 | -A = Labels are cAse-sensitive 157 | -D[=value] = Create a define 'name' [with 'value'] 158 | -N = Don't use colors for messages 159 | -V = Pipe expression directly into assembly 160 | 161 | Added features for eZ80 code include: 162 | * The .ASSUME ADL={0 or 1} directive for specifying 16-bit or 24-bit code (24-bit is the default). 163 | * The .DL and .LONG directives for including 24-bit data. 164 | * Mode-change suffixes available for all instructions, as seen in the eZ80 manual. 165 | 166 | Issues/Bugs 167 | ----------- 168 | Report issues/bugs to the issue tracker, found here: 169 | 170 | https://github.com/alberthdev/spasm-ng/issues 171 | 172 | Copyright/License 173 | ----------------- 174 | SPASM-ng was originally from the SPASM project, and was forked to fix a 175 | few bugs. It was originally written by Spencer Putt and Don Straney, 176 | with additional development by Chris Shappell and James Montelongo. 177 | 178 | License: 179 | 180 | SPASM-ng - a z80 assembler with extra features to support dev for TI calcs! 181 | Copyright (C) 2015 Spencer Putt and Don Straney 182 | Copyright (C) 2015 Chris Shappell and James Montelongo 183 | Copyright (C) 2015 Albert Huang (fixes to SPASM) 184 | Copyright (C) 2015 Brendan Fletcher (eZ80 support) 185 | 186 | This program is free software; you can redistribute it and/or modify 187 | it under the terms of the GNU General Public License as published by 188 | the Free Software Foundation; either version 2 of the License, or 189 | (at your option) any later version. 190 | 191 | This program is distributed in the hope that it will be useful, 192 | but WITHOUT ANY WARRANTY; without even the implied warranty of 193 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 194 | GNU General Public License for more details. 195 | 196 | You should have received a copy of the GNU General Public License along 197 | with this program; if not, write to the Free Software Foundation, Inc., 198 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 199 | -------------------------------------------------------------------------------- /SPASM.idl: -------------------------------------------------------------------------------- 1 | import "oaidl.idl"; 2 | import "ocidl.idl"; 3 | 4 | /* 5 | [ 6 | object, 7 | uuid(9E52A44E-9B78-4662-B423-9A70A9D76140), 8 | dual, 9 | nonextensible, 10 | pointer_default(unique) 11 | ] 12 | interface IStringCollection : IDispatch 13 | { 14 | [propget] 15 | HRESULT Count([out, retval] LONG *Count); 16 | 17 | [id(DISPID_VALUE), propget] 18 | HRESULT Item([in] LONG Index, [out, retval] BSTR **Item); 19 | 20 | [id(DISPID_NEWENUM), propget, hidden] 21 | HRESULT _NewEnum([out, retval] IUnknown** ppEnum); 22 | 23 | [helpstring("Add a new string")] 24 | HRESULT Add([in] BSTR Name, [out, retval] BSTR **Value); 25 | 26 | [helpstring("Remove a string")] 27 | HRESULT Remove([in] BSTR Name); 28 | 29 | [helpstring("Clears the collection")] 30 | HRESULT Clear(); 31 | } 32 | */ 33 | 34 | 35 | [ 36 | object, 37 | uuid(CC851E27-B58F-40DC-AB58-63AFE27BC4D7), 38 | odl, 39 | dual, 40 | oleautomation, 41 | nonextensible, 42 | pointer_default(unique) 43 | ] 44 | interface IIncludeDirectoryCollection : IDispatch 45 | { 46 | [propget] 47 | HRESULT Count([out, retval] LONG *Count); 48 | 49 | [propget, id(DISPID_VALUE)] 50 | HRESULT Item([in] LONG Index, [out, retval] BSTR *Item); 51 | 52 | [propget, restricted, hidden, id(DISPID_NEWENUM)] 53 | HRESULT _NewEnum([out, retval] IUnknown** ppEnum); 54 | 55 | [helpstring("Add a new string")] 56 | HRESULT Add([in] BSTR Name); 57 | 58 | [helpstring("Remove a string")] 59 | HRESULT Remove([in] BSTR Name); 60 | 61 | [helpstring("Clears the collection")] 62 | HRESULT Clear(); 63 | } 64 | 65 | enum ASSEMBLYOPTIONS { 66 | NORMAL = 1, 67 | CODE_COUNTER = 2, 68 | SYMTABLE = 4, 69 | STATS = 8, 70 | LIST = 16, 71 | COMMANDLINE = 32, 72 | } ; 73 | 74 | [ 75 | uuid(16387CF3-1B28-46C0-ABA9-5DEC7A8FA7AA), 76 | version(1.2), 77 | helpstring("SPASM Z80 Assembler 1.2 Type Library"), 78 | ] 79 | library SPASM 80 | { 81 | importlib("stdole2.tlb"); 82 | importlib("scrrun.dll"); 83 | 84 | [ 85 | object, 86 | uuid(4B8E17F3-2326-453D-AF80-E5CC0CA52373), 87 | dual, 88 | nonextensible, 89 | pointer_default(unique) 90 | ] 91 | interface IZ80Assembler : IDispatch 92 | { 93 | [propget, helpstring("Preprocessor definitions")] 94 | HRESULT Defines([out, retval] Scripting.IDictionary **Defines); 95 | 96 | [propget, helpstring("Current labels for the assembly environment")] 97 | HRESULT Labels([out, retval] Scripting.IDictionary **Labels); 98 | 99 | [propget, helpstring("Output text")] 100 | HRESULT StdOut([out, retval] Scripting.ITextStream **OutputStream); 101 | 102 | [propget, helpstring("Input file")] 103 | HRESULT InputFile([out, retval] BSTR *InputFile); 104 | 105 | [propput, helpstring("Input file")] 106 | HRESULT InputFile([in] BSTR InputFile); 107 | 108 | [propget, helpstring("Output file")] 109 | HRESULT OutputFile([out, retval] BSTR *OutputFile); 110 | 111 | [propput, helpstring("Output file")] 112 | HRESULT OutputFile([in] BSTR OutputFile); 113 | 114 | [propget, helpstring("Assembly options")] 115 | HRESULT Options([out, retval] DWORD *Options); 116 | 117 | [propput, helpstring("Assembly options")] 118 | HRESULT Options([in] DWORD Options); 119 | 120 | [propget, helpstring("Case sensitivity")] 121 | HRESULT CaseSensitive([out, retval] VARIANT_BOOL *IsCaseSensitive); 122 | 123 | [propput, helpstring("Case sensitivity")] 124 | HRESULT CaseSensitive([in] VARIANT_BOOL IsCaseSensitive); 125 | 126 | [propget, helpstring("Current directory")] 127 | HRESULT CurrentDirectory([out, retval] BSTR *CurrentDirectory); 128 | 129 | [propput, helpstring("Current directory")] 130 | HRESULT CurrentDirectory([in] BSTR CurrentDirectory); 131 | 132 | [propget, helpstring("Collection of additional include directories")] 133 | HRESULT IncludeDirectories([out, retval] IIncludeDirectoryCollection **IncludeDirectories); 134 | 135 | [helpstring("Assemble a buffer or filename and return the output stream")] 136 | HRESULT Assemble([in, optional] VARIANT Input, [out, retval] IStream **Output); 137 | } 138 | 139 | [ 140 | uuid(BDD26FAE-A388-4860-9D4E-669809BC8EC2) 141 | ] 142 | coclass Z80Assembler 143 | { 144 | [default] interface IZ80Assembler; 145 | }; 146 | }; 147 | -------------------------------------------------------------------------------- /SPASM.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberthdev/spasm-ng/5f0786d38f064835be674d4b7df42969967bb73c/SPASM.rc -------------------------------------------------------------------------------- /SPASM.rgs: -------------------------------------------------------------------------------- 1 | HKCR 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /SPASM.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual C++ Express 2010 4 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{339D2AC9-17A8-4F03-BCB0-6148573892BC}" 5 | EndProject 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SPASM", "SPASM.vcxproj", "{106FF797-6997-43F1-88E3-7D6C0D8A6F3F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {85D2CCCD-B12D-4A80-BB4E-9233E078D9EA}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {85D2CCCD-B12D-4A80-BB4E-9233E078D9EA}.Debug|x64.ActiveCfg = Debug|x64 18 | {85D2CCCD-B12D-4A80-BB4E-9233E078D9EA}.Release|Win32.ActiveCfg = Release|Win32 19 | {85D2CCCD-B12D-4A80-BB4E-9233E078D9EA}.Release|x64.ActiveCfg = Release|x64 20 | {106FF797-6997-43F1-88E3-7D6C0D8A6F3F}.Debug|Win32.ActiveCfg = Debug|Win32 21 | {106FF797-6997-43F1-88E3-7D6C0D8A6F3F}.Debug|Win32.Build.0 = Debug|Win32 22 | {106FF797-6997-43F1-88E3-7D6C0D8A6F3F}.Debug|x64.ActiveCfg = Debug|x64 23 | {106FF797-6997-43F1-88E3-7D6C0D8A6F3F}.Debug|x64.Build.0 = Debug|x64 24 | {106FF797-6997-43F1-88E3-7D6C0D8A6F3F}.Release|Win32.ActiveCfg = Release|Win32 25 | {106FF797-6997-43F1-88E3-7D6C0D8A6F3F}.Release|Win32.Build.0 = Release|Win32 26 | {106FF797-6997-43F1-88E3-7D6C0D8A6F3F}.Release|x64.ActiveCfg = Release|x64 27 | {106FF797-6997-43F1-88E3-7D6C0D8A6F3F}.Release|x64.Build.0 = Release|x64 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | GlobalSection(TeamFoundationVersionControl) = preSolution 33 | SccNumberOfProjects = 3 34 | SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} 35 | SccTeamFoundationServer = https://tfs.codeplex.com/tfs/tfs07 36 | SccLocalPath0 = . 37 | SccProjectUniqueName1 = SPASM.vcxproj 38 | SccLocalPath1 = . 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /SPASM.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {106FF797-6997-43F1-88E3-7D6C0D8A6F3F} 23 | SAK 24 | SAK 25 | SAK 26 | SAK 27 | AtlProj 28 | SPASM 29 | 30 | 31 | 32 | Application 33 | true 34 | Static 35 | MultiByte 36 | v100 37 | 38 | 39 | Application 40 | true 41 | Dynamic 42 | MultiByte 43 | v110 44 | 45 | 46 | Application 47 | false 48 | Static 49 | MultiByte 50 | true 51 | v100 52 | 53 | 54 | Application 55 | false 56 | Static 57 | MultiByte 58 | true 59 | v110 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | true 79 | true 80 | false 81 | $(ProjectDir)\lib;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) 82 | C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) 83 | 84 | 85 | true 86 | true 87 | false 88 | $(ProjectDir)\lib\x64;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win64;$(LibraryPath) 89 | C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) 90 | 91 | 92 | true 93 | false 94 | false 95 | $(ProjectDir)\lib;$(LibraryPath) 96 | 97 | 98 | true 99 | false 100 | false 101 | $(ProjectDir)\lib\x64;$(LibraryPath) 102 | 103 | 104 | 105 | Use 106 | Level3 107 | Disabled 108 | WIN32;_WINDOWS;USE_REUSABLES;USE_BUILTIN_FCREATE;_CRT_SECURE_NO_WARNINGS;STRSAFE_NO_DEPRECATE;_DEBUG;%(PreprocessorDefinitions) 109 | 110 | 111 | true 112 | 113 | 114 | 115 | 116 | Win32 117 | 118 | 119 | 120 | 121 | 0x0409 122 | $(IntDir);%(AdditionalIncludeDirectories) 123 | _DEBUG;%(PreprocessorDefinitions) 124 | 125 | 126 | Console 127 | true 128 | comsuppw.lib;mpir.lib;%(AdditionalDependencies) 129 | 130 | 131 | Performing registration 132 | "$(TargetPath)" /RegServer 133 | 134 | 135 | mpir.lib 136 | 137 | 138 | 139 | 140 | Level3 141 | Disabled 142 | WIN32;_WINDOWS;USE_REUSABLES;USE_BUILTIN_FCREATE;_CRT_SECURE_NO_WARNINGS;STRSAFE_NO_DEPRECATE;_DEBUG;_WIN64;%(PreprocessorDefinitions) 143 | 144 | 145 | true 146 | Use 147 | 148 | 149 | 150 | 151 | X64 152 | 153 | 154 | 0x0409 155 | $(IntDir);%(AdditionalIncludeDirectories) 156 | _DEBUG;%(PreprocessorDefinitions) 157 | 158 | 159 | Console 160 | true 161 | comsuppw.lib;mpir.lib;%(AdditionalDependencies) 162 | 163 | 164 | Performing registration 165 | "$(TargetPath)" /RegServer 166 | 167 | 168 | 169 | 170 | Use 171 | Level3 172 | Full 173 | WIN32;_WINDOWS;USE_REUSABLES;USE_BUILTIN_FCREATE;_CRT_SECURE_NO_WARNINGS;STRSAFE_NO_DEPRECATE;NDEBUG;%(PreprocessorDefinitions) 174 | 175 | 176 | true 177 | AnySuitable 178 | Speed 179 | true 180 | true 181 | MultiThreaded 182 | 183 | 184 | false 185 | Win32 186 | NDEBUG;%(PreprocessorDefinitions) 187 | true 188 | 189 | 190 | true 191 | 192 | 193 | 0x0409 194 | $(IntDir);%(AdditionalIncludeDirectories) 195 | NDEBUG;%(PreprocessorDefinitions) 196 | 197 | 198 | Console 199 | true 200 | true 201 | true 202 | mpir.lib;%(AdditionalDependencies) 203 | UseLinkTimeCodeGeneration 204 | true 205 | 206 | 207 | Performing registration 208 | "$(TargetPath)" /RegServer 209 | 210 | 211 | 212 | 213 | Use 214 | Level3 215 | MaxSpeed 216 | _WIN64;_WINDOWS;USE_REUSABLES;USE_BUILTIN_FCREATE;_CRT_SECURE_NO_WARNINGS;STRSAFE_NO_DEPRECATE;NDEBUG;%(PreprocessorDefinitions) 217 | 218 | 219 | true 220 | AnySuitable 221 | true 222 | Speed 223 | MultiThreaded 224 | true 225 | 226 | 227 | false 228 | NDEBUG;%(PreprocessorDefinitions) 229 | 230 | 231 | X64 232 | true 233 | 234 | 235 | 0x0409 236 | $(IntDir);%(AdditionalIncludeDirectories) 237 | NDEBUG;%(PreprocessorDefinitions) 238 | 239 | 240 | Console 241 | true 242 | true 243 | true 244 | mpir.lib;%(AdditionalDependencies) 245 | true 246 | 247 | 248 | Performing registration 249 | "$(TargetPath)" /RegServer 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | true 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | false 276 | false 277 | 278 | 279 | NotUsing 280 | false 281 | false 282 | 283 | 284 | 285 | 286 | 287 | 288 | Create 289 | Create 290 | Create 291 | Create 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | X64 337 | 338 | 339 | 340 | 341 | 342 | -------------------------------------------------------------------------------- /SPASM.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {00f5d352-e0a0-4e66-b18e-6a5438b1485c} 18 | False 19 | 20 | 21 | {e4a84b9a-4629-4f9b-817e-776daf4c2553} 22 | 23 | 24 | {d97024e2-4364-4c9c-89e3-b6270d2abe69} 25 | False 26 | 27 | 28 | 29 | 30 | Source Files 31 | 32 | 33 | Generated Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files 76 | 77 | 78 | Source Files 79 | 80 | 81 | Source Files 82 | 83 | 84 | Source Files 85 | 86 | 87 | Source Files 88 | 89 | 90 | Source Files 91 | 92 | 93 | Source Files 94 | 95 | 96 | Source Files 97 | 98 | 99 | 100 | 101 | Header Files 102 | 103 | 104 | Header Files 105 | 106 | 107 | Generated Files 108 | 109 | 110 | Header Files 111 | 112 | 113 | Header Files 114 | 115 | 116 | Header Files 117 | 118 | 119 | Header Files 120 | 121 | 122 | Header Files 123 | 124 | 125 | Header Files 126 | 127 | 128 | Header Files 129 | 130 | 131 | Header Files 132 | 133 | 134 | Header Files 135 | 136 | 137 | Header Files 138 | 139 | 140 | Header Files 141 | 142 | 143 | Header Files 144 | 145 | 146 | Header Files 147 | 148 | 149 | Header Files 150 | 151 | 152 | Header Files 153 | 154 | 155 | Header Files 156 | 157 | 158 | Header Files 159 | 160 | 161 | Header Files 162 | 163 | 164 | Header Files 165 | 166 | 167 | Source Files 168 | 169 | 170 | Header Files 171 | 172 | 173 | 174 | 175 | Resource Files 176 | 177 | 178 | Resource Files 179 | 180 | 181 | Source Files 182 | 183 | 184 | Source Files 185 | 186 | 187 | Test Files 188 | 189 | 190 | Test Files 191 | 192 | 193 | Generated Files 194 | 195 | 196 | 197 | 198 | Resource Files 199 | 200 | 201 | 202 | 203 | IDL Files 204 | 205 | 206 | -------------------------------------------------------------------------------- /SPASM.vcxproj.vspscc: -------------------------------------------------------------------------------- 1 | "" 2 | { 3 | "FILE_VERSION" = "9237" 4 | "ENLISTMENT_CHOICE" = "NEVER" 5 | "PROJECT_FILE_RELATIVE_PATH" = "" 6 | "NUMBER_OF_EXCLUDED_FILES" = "5" 7 | "EXCLUDED_FILE0" = "test.lst" 8 | "EXCLUDED_FILE1" = "SPASM_i.c" 9 | "EXCLUDED_FILE2" = "test.z80" 10 | "EXCLUDED_FILE3" = "SPASM_i.h" 11 | "EXCLUDED_FILE4" = "commands.inc" 12 | "ORIGINAL_PROJECT_FILE_PATH" = "" 13 | "NUMBER_OF_NESTED_PROJECTS" = "0" 14 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" 15 | } 16 | -------------------------------------------------------------------------------- /SPASM.vssscc: -------------------------------------------------------------------------------- 1 | "" 2 | { 3 | "FILE_VERSION" = "9237" 4 | "ENLISTMENT_CHOICE" = "NEVER" 5 | "PROJECT_FILE_RELATIVE_PATH" = "" 6 | "NUMBER_OF_EXCLUDED_FILES" = "0" 7 | "ORIGINAL_PROJECT_FILE_PATH" = "" 8 | "NUMBER_OF_NESTED_PROJECTS" = "0" 9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT" 10 | } 11 | -------------------------------------------------------------------------------- /Test.vbs: -------------------------------------------------------------------------------- 1 | Set asm = CreateObject("Wabbit.SPASM") 2 | 3 | asm.Defines.Add "test", 34 4 | 5 | asm.Assemble " .echo ""hello "",test" 6 | WScript.Echo asm.StdOut.ReadAll 7 | -------------------------------------------------------------------------------- /Z80Assembler.cpp: -------------------------------------------------------------------------------- 1 | // Z80Assembler.cpp : Implementation of CZ80Assembler 2 | 3 | #include "stdafx.h" 4 | #include "Z80Assembler.h" 5 | #include "Z80Label.h" 6 | 7 | #include "spasm.h" 8 | #include "utils.h" 9 | #include "storage.h" 10 | #include "list.h" 11 | #include "hash.h" 12 | 13 | extern hash_t *label_table; 14 | 15 | LONG CZ80Assembler::m_lIndex; 16 | 17 | HRESULT CZ80Assembler::FinalConstruct() 18 | { 19 | curr_input_file = strdup("COM Interface"); 20 | output_filename = strdup("COM Interface"); 21 | 22 | init_storage(); 23 | m_pStmOutput = NULL; 24 | return S_OK; 25 | } 26 | 27 | void CZ80Assembler::FinalRelease() 28 | { 29 | free_storage(); 30 | 31 | if (curr_input_file) { 32 | free(curr_input_file); 33 | } 34 | curr_input_file = NULL; 35 | if (output_filename) { 36 | free(output_filename); 37 | } 38 | output_filename = NULL; 39 | } 40 | 41 | STDMETHODIMP CZ80Assembler::get_Output(IStream **ppOutput) 42 | { 43 | if (ppOutput == NULL) 44 | { 45 | return E_INVALIDARG; 46 | } 47 | 48 | if (m_pStmOutput == NULL) 49 | { 50 | return E_NOT_VALID_STATE; 51 | } 52 | return m_pStmOutput->QueryInterface(IID_PPV_ARGS(ppOutput)); 53 | } 54 | 55 | STDMETHODIMP CZ80Assembler::put_InputFile(BSTR bstrInputFile) 56 | { 57 | m_bstrInputFile = bstrInputFile; 58 | return S_OK; 59 | } 60 | 61 | STDMETHODIMP CZ80Assembler::get_InputFile(LPBSTR lpbstrInputFile) 62 | { 63 | *lpbstrInputFile = SysAllocString(m_bstrInputFile); 64 | return S_OK; 65 | } 66 | 67 | STDMETHODIMP CZ80Assembler::put_OutputFile(BSTR bstrOutputFile) 68 | { 69 | m_bstrOutputFile = bstrOutputFile; 70 | return S_OK; 71 | } 72 | 73 | STDMETHODIMP CZ80Assembler::get_OutputFile(LPBSTR lpbstrOutputFile) 74 | { 75 | *lpbstrOutputFile = SysAllocString(m_bstrOutputFile); 76 | return S_OK; 77 | } 78 | 79 | 80 | STDMETHODIMP CZ80Assembler::ClearDefines() 81 | { 82 | list_free(default_defines, true, NULL); 83 | default_defines = NULL; 84 | return S_OK; 85 | } 86 | 87 | STDMETHODIMP CZ80Assembler::AddDefine(BSTR bstrName, VARIANT varValue) 88 | { 89 | if (V_VT(&varValue) == VT_EMPTY || V_VT(&varValue) == VT_ERROR) 90 | { 91 | V_VT(&varValue) = VT_UI4; 92 | V_UI4(&varValue) = 1; 93 | } 94 | 95 | VARIANT varDefine; 96 | VariantInit(&varDefine); 97 | V_VT(&varDefine) = VT_BSTR; 98 | V_BSTR(&varDefine) = SysAllocString(L""); 99 | HRESULT hr = VariantChangeType(&varDefine, &varValue, 0, VT_BSTR); 100 | if (FAILED(hr)) 101 | { 102 | return hr; 103 | } 104 | 105 | CW2A szName(bstrName); 106 | 107 | bool fRedefined = false; 108 | define_t *define = add_define(strdup(szName), &fRedefined); 109 | 110 | if (define != NULL) 111 | { 112 | CW2A szContents(V_BSTR(&varDefine)); 113 | define->contents = strdup(szContents); 114 | return S_OK; 115 | } 116 | else 117 | { 118 | return E_FAIL; 119 | } 120 | } 121 | 122 | STDMETHODIMP CZ80Assembler::ClearIncludeDirectories() 123 | { 124 | list_free(include_dirs, true, NULL); 125 | include_dirs = NULL; 126 | return S_OK; 127 | } 128 | 129 | STDMETHODIMP CZ80Assembler::AddIncludeDirectory(BSTR bstrDirectory) 130 | { 131 | CW2CT szInput(bstrDirectory); 132 | include_dirs = list_append(include_dirs, strdup(szInput)); 133 | return S_OK; 134 | } 135 | 136 | STDMETHODIMP CZ80Assembler::Assemble(VARIANT varInput, int *lpInt) 137 | { 138 | HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, output_buf_size); 139 | output_contents = (unsigned char *) GlobalLock(hGlobal); 140 | 141 | if (V_VT(&varInput) == VT_BSTR) 142 | { 143 | mode = MODE_NORMAL | MODE_COMMANDLINE; 144 | 145 | CW2CT szInput(V_BSTR(&varInput)); 146 | input_contents = strdup(szInput); 147 | } 148 | else 149 | { 150 | mode = MODE_NORMAL; 151 | 152 | if (curr_input_file) { 153 | free(curr_input_file); 154 | } 155 | curr_input_file = strdup(m_bstrInputFile); 156 | if (output_filename) { 157 | free(output_filename); 158 | } 159 | output_filename = strdup(m_bstrOutputFile); 160 | } 161 | 162 | *lpInt = run_assembly(); 163 | 164 | GlobalUnlock(hGlobal); 165 | 166 | if (m_pStmOutput != NULL) 167 | { 168 | m_pStmOutput->Release(); 169 | } 170 | 171 | LPSTREAM pStream = NULL; 172 | HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); 173 | ULARGE_INTEGER ul; 174 | ul.QuadPart = out_ptr - output_contents; 175 | pStream->SetSize(ul); 176 | 177 | m_pStmOutput = pStream; 178 | 179 | return S_OK; 180 | } 181 | 182 | void CZ80Assembler::get_label_callback(label_t *label, CComSafeArray *lpsa) 183 | { 184 | CComObject *pLabelObj = NULL; 185 | CComObject::CreateInstance(&pLabelObj); 186 | pLabelObj->AddRef(); 187 | pLabelObj->Initialize(label); 188 | 189 | IZ80Label *pLabel; 190 | pLabelObj->QueryInterface(&pLabel); 191 | 192 | pLabelObj->Release(); 193 | 194 | lpsa->SetAt(m_lIndex++, pLabel, FALSE); 195 | } 196 | 197 | STDMETHODIMP CZ80Assembler::get_Labels(LPSAFEARRAY *ppsa) 198 | { 199 | CComSafeArray sa((ULONG) label_table->used); 200 | 201 | m_lIndex = sa.GetLowerBound(0); 202 | hash_enum(label_table, (HASH_ENUM_CALLBACK) get_label_callback, &sa); 203 | 204 | LPSAFEARRAY lpsa; 205 | sa.CopyTo(&lpsa); 206 | 207 | *ppsa = lpsa; 208 | return S_OK; 209 | } 210 | -------------------------------------------------------------------------------- /Z80Assembler.h: -------------------------------------------------------------------------------- 1 | // Z80Assembler.h : Declaration of the CZ80Assembler 2 | 3 | #pragma once 4 | #include "resource.h" // main symbols 5 | #include "list.h" 6 | #include "storage.h" 7 | 8 | #include "SPASM_i.h" 9 | 10 | typedef struct 11 | { 12 | char name[64]; 13 | char value[64]; 14 | } 15 | default_define_pair_t; 16 | 17 | // CZ80Assembler 18 | 19 | class ATL_NO_VTABLE CZ80Assembler : 20 | public CComObjectRootEx, 21 | public CComCoClass, 22 | public IDispatchImpl 23 | { 24 | public: 25 | DECLARE_REGISTRY_RESOURCEID(IDR_Z80ASSEMBLER) 26 | 27 | BEGIN_COM_MAP(CZ80Assembler) 28 | COM_INTERFACE_ENTRY(IZ80Assembler) 29 | COM_INTERFACE_ENTRY(IDispatch) 30 | END_COM_MAP() 31 | 32 | DECLARE_PROTECT_FINAL_CONSTRUCT() 33 | 34 | HRESULT FinalConstruct(); 35 | void FinalRelease(); 36 | 37 | public: 38 | STDMETHOD(get_Output)(IStream **ppStream); 39 | 40 | STDMETHOD(get_InputFile)(LPBSTR lpbstrInput); 41 | STDMETHOD(put_InputFile)(BSTR bstrInput); 42 | 43 | STDMETHOD(get_OutputFile)(LPBSTR lpbstrOutput); 44 | STDMETHOD(put_OutputFile)(BSTR bstrOutput); 45 | 46 | STDMETHOD(ClearDefines)(); 47 | STDMETHOD(AddDefine)(BSTR bstrName, VARIANT varValue); 48 | 49 | STDMETHOD(ClearIncludeDirectories)(); 50 | STDMETHOD(AddIncludeDirectory)(BSTR bstrDirectory); 51 | 52 | STDMETHOD(Assemble)(VARIANT varInput, int *lpReturn); 53 | 54 | STDMETHOD(get_Labels)(LPSAFEARRAY *lpsa); 55 | 56 | 57 | private: 58 | static LONG m_lIndex; 59 | static void get_label_callback(label_t *label, CComSafeArray *lpsa); 60 | 61 | LPSTREAM m_pStmOutput; 62 | 63 | _bstr_t m_bstrInputFile; 64 | _bstr_t m_bstrOutputFile; 65 | 66 | list_t *default_defines; 67 | }; 68 | 69 | OBJECT_ENTRY_AUTO(__uuidof(Z80Assembler), CZ80Assembler) 70 | -------------------------------------------------------------------------------- /Z80Assembler.rgs: -------------------------------------------------------------------------------- 1 | HKCR 2 | { 3 | ForceRemove Wabbit.SPASM.1 = s 'Z80Assembler Class' 4 | { 5 | CLSID = s '{BDD26FAE-A388-4860-9D4E-669809BC8EC2}' 6 | } 7 | ForceRemove Wabbit.SPASM = s 'Z80Assembler Class' 8 | { 9 | CurVer = s 'Wabbit.SPASM.1' 10 | } 11 | NoRemove CLSID 12 | { 13 | ForceRemove {BDD26FAE-A388-4860-9D4E-669809BC8EC2} = s 'Z80Assembler Class' 14 | { 15 | ProgID = s 'Wabbit.SPASM.1' 16 | VersionIndependentProgID = s 'Wabbit.SPASM' 17 | ForceRemove Programmable 18 | LocalServer32 = s '%MODULE%' 19 | { 20 | val ServerExecutable = s '%MODULE_RAW%' 21 | } 22 | TypeLib = s '{16387CF3-1B28-46C0-ABA9-5DEC7A8FA7AA}' 23 | Version = s '1.2' 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Z80Label.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "Z80Label.h" 4 | 5 | STDMETHODIMP CZ80Label::get_Name(BSTR *lpbstr) 6 | { 7 | _bstr_t str(m_label->name); 8 | *lpbstr = SysAllocString(str); 9 | return S_OK; 10 | } 11 | 12 | STDMETHODIMP CZ80Label::get_Value(DWORD *lpdwValue) 13 | { 14 | *lpdwValue = m_label->value; 15 | return S_OK; 16 | } -------------------------------------------------------------------------------- /Z80Label.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SPASM_i.h" 4 | #include "storage.h" 5 | 6 | class ATL_NO_VTABLE CZ80Label : 7 | public CComObjectRootEx, 8 | public IDispatchImpl 9 | { 10 | public: 11 | 12 | DECLARE_REGISTRY_RESOURCEID(IDR_Z80ASSEMBLER) 13 | 14 | BEGIN_COM_MAP(CZ80Label) 15 | COM_INTERFACE_ENTRY(IZ80Label) 16 | COM_INTERFACE_ENTRY(IDispatch) 17 | END_COM_MAP() 18 | 19 | public: 20 | STDMETHOD(get_Name)(BSTR *lpbstrName); 21 | STDMETHOD(get_Value)(DWORD *lpdwValue); 22 | 23 | void Initialize(const label_t *label) 24 | { 25 | m_label = label; 26 | } 27 | private: 28 | const label_t *m_label; 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | build: 2 | parallel: true 3 | project: SPASM.sln 4 | 5 | environment: 6 | matrix: 7 | - platform: Win32 8 | - platform: x64 9 | - platform: mingw 10 | 11 | build_script: 12 | - if NOT "%platform%"=="mingw" msbuild /p:Configuration=Debug 13 | - if NOT "%platform%"=="mingw" msbuild /p:Configuration=Release 14 | - if "%platform%"=="mingw" echo Working around non-existent strip for AppVeyor mingw... 15 | - if "%platform%"=="mingw" copy /b/v/y C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin\strip.exe C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin\i686-w64-mingw32-strip.exe 16 | - if "%platform%"=="mingw" set PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH% 17 | - if "%platform%"=="mingw" mingw32-make CROSS_COMPILE=i686-w64-mingw32- NO_APPSIGN=1 18 | 19 | test_script: 20 | - if "%platform%"=="Win32" C:\Python35\python tests\test-runner.py Debug\SPASM.exe 21 | - if "%platform%"=="Win32" C:\Python35\python tests\test-runner.py Release\SPASM.exe 22 | - if "%platform%"=="x64" C:\Python35\python tests\test-runner.py x64\Debug\SPASM.exe 23 | - if "%platform%"=="x64" C:\Python35\python tests\test-runner.py x64\Release\SPASM.exe 24 | - if "%platform%"=="mingw" C:\Python35\python tests\test-runner.py SPASM.exe 25 | -------------------------------------------------------------------------------- /bitmap.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "spasm.h" 4 | #include "errors.h" 5 | #include "utils.h" 6 | #include "storage.h" 7 | #include "pass_one.h" 8 | #include "parser.h" 9 | 10 | #ifndef WIN32 11 | #include 12 | typedef int32_t LONG; 13 | typedef uint16_t UINT; 14 | typedef uint8_t BYTE; 15 | typedef uint16_t WORD; 16 | // Defined in stdafx.h 17 | //typedef uint32_t DWORD; 18 | #define LOBYTE(w) ((BYTE)(w)) 19 | #define HIBYTE(w) ((BYTE)(((WORD)(w)>>8)&0xFF)) 20 | #define BI_RGB 0 21 | 22 | typedef struct tagRECT { 23 | LONG top, left, right, bottom; 24 | } RECT; 25 | 26 | typedef struct tagRGBQUAD { 27 | BYTE rgbBlue; 28 | BYTE rgbGreen; 29 | BYTE rgbRed; 30 | BYTE rgbReserved; 31 | } __attribute__((packed)) RGBQUAD,*LPRGBQUAD; 32 | 33 | 34 | typedef struct tagBITMAPFILEHEADER { 35 | WORD bfType; 36 | DWORD bfSize; 37 | WORD bfReserved1; 38 | WORD bfReserved2; 39 | DWORD bfOffBits; 40 | } __attribute__((packed)) BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER; 41 | 42 | typedef struct tagBITMAPINFOHEADER{ 43 | DWORD biSize; 44 | LONG biWidth; 45 | LONG biHeight; 46 | WORD biPlanes; 47 | WORD biBitCount; 48 | DWORD biCompression; 49 | DWORD biSizeImage; 50 | LONG biXPelsPerMeter; 51 | LONG biYPelsPerMeter; 52 | DWORD biClrUsed; 53 | DWORD biClrImportant; 54 | } __attribute__((packed)) BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER; 55 | #endif 56 | 57 | #ifdef __BIG_ENDIAN__ 58 | #define _ZL(Z) ((LONG) _DW(Z)) 59 | #define _DW(Z) ((((Z)&0xFF)<<24) + (((Z)&0xFF00)<<8) + (((Z)&0xFF0000)>>8) + (((Z)&0xFF000000)>>24)) 60 | #define _W(Z) ((((Z)&0xFF)<<8) + (((Z)&0xFF00)>>8)) 61 | #else 62 | #define _ZL(Z) (Z) 63 | #define _DW(Z) (Z) 64 | #define _W(Z) (Z) 65 | #endif 66 | 67 | #define MASK(Z) ((1<<(Z))-1) 68 | #define LUMINANCE(R,G,B) (((0.2126/255.0) * (R)) + ((0.7152/255.0) * (G)) + ((0.0722/255.0) * (B))) 69 | 70 | 71 | 72 | static void handle_bitmap_header(const RECT *r, const BITMAPFILEHEADER *bf, const BITMAPINFOHEADER *bi) { 73 | // Handle an optional header 74 | if (define_with_value("__BM_HDR", 1)) { 75 | char *hdr, *p; 76 | define_t *def_hdr_fmt = search_defines("__BM_HDR_FMT"); 77 | if (def_hdr_fmt == NULL) 78 | return; 79 | 80 | hdr = strdup(def_hdr_fmt->contents); 81 | reduce_string(hdr); 82 | 83 | for (p = strtok(hdr, ",;x "); p; p = strtok(NULL, ",;x ")) { 84 | 85 | if (!strcasecmp(p, "w")) { 86 | if (r->right - r->left > 255) 87 | show_warning("Bitmap width overflows 'w'; use 'ww' instead"); 88 | write_out(r->right - r->left); 89 | } 90 | else if (!strcasecmp(p, "ww")) { 91 | write_out((r->right - r->left) & 0xFF); 92 | program_counter++; 93 | write_out((r->right - r->left) >> 8); 94 | } 95 | else if (!strcasecmp(p, "h")) { 96 | if ((r->bottom - r->top) > 255) 97 | show_warning("Bitmap height overflows 'h'; use 'hh' instead"); 98 | write_out((r->bottom - r->top)); 99 | } 100 | else if (!strcasecmp(p, "hh")) { 101 | write_out((r->bottom - r->top) & 0xFF); 102 | program_counter++; 103 | write_out((r->bottom - r->top) >> 8); 104 | } 105 | else if (!strcasecmp(p, "b")) 106 | write_out(parse_f("__BM_SHD")); 107 | else if (!strcasecmp(p, "s") || !strcasecmp(p, "ss")) { 108 | int size = ((r->right - r->left) + 7)/8 * (r->bottom - r->top); 109 | if (define_with_value ("__BM_SHD", 4)) size *=2; 110 | else if (define_with_value ("__BM_SHD", 8)) size *=4; 111 | if (define_with_value ("__BM_MASK", 1)) size += ((r->right - r->left) + 7)/8 * (r->bottom - r->top); 112 | 113 | if (!strcasecmp(p, "s")) { 114 | if (size > 255) 115 | show_warning("Bitmap size overflows 's'; use 'ss' instead"); 116 | write_out(size); 117 | } else { 118 | write_out(size & 0xFF); 119 | program_counter++; 120 | write_out((size >> 8) & 0xFF); 121 | } 122 | 123 | } else { 124 | show_warning("Unknown BM_HEADER token '%s'", p); 125 | write_out(0); 126 | } 127 | program_counter++; 128 | } 129 | 130 | free(hdr); 131 | } 132 | } 133 | 134 | #ifdef _WIN32 135 | static WORD log2(WORD value) 136 | { 137 | WORD l = 0; 138 | while( (value >> l) > 1 ) ++l; 139 | return l; 140 | } 141 | #endif 142 | 143 | bool IsFileBitmap(FILE *file) 144 | { 145 | fseek(file, 0, SEEK_SET); 146 | 147 | BITMAPFILEHEADER bf; 148 | fread (&bf, sizeof (BITMAPFILEHEADER), 1, file); 149 | 150 | if (LOBYTE(_W(bf.bfType)) == 'B' && HIBYTE(_W(bf.bfType)) == 'M') 151 | { 152 | return true; 153 | } 154 | else 155 | { 156 | return false; 157 | } 158 | } 159 | 160 | static void handle_bitmap_internal(FILE *file, const RECT *r, const BITMAPFILEHEADER *bf, const BITMAPINFOHEADER *bi, const LPRGBQUAD bmiColors) 161 | { 162 | //printf("handle_bitmap on: %d %d %d %d\n", r->left, r->top, r->right, r->bottom); 163 | 164 | // Bytes, padded to the nearest 32-bit 165 | const LONG biScanWidth = ((_ZL(bi->biWidth) * _W(bi->biBitCount)) + 31) / 32 * 4; 166 | const DWORD biImageSize = (_DW(bf->bfSize) - _DW(bf->bfOffBits)); 167 | // if (biImageSize % biScanWidth != 0) { 168 | // printf("Scan width calculation incorrect! (image size: %ld, scan: %ld)\n", biImageSize, biScanWidth); 169 | // return; 170 | // } 171 | fseek(file, bf->bfOffBits + (biScanWidth * r->top), SEEK_SET); 172 | int min_w = parse_f("__BM_MIN_W"); 173 | 174 | // Read in the image 175 | #define BITS_ACCESS(zr, zc) pBits[((zr) * biScanWidth) + (zc)] 176 | //BYTE (*pBits)[biScanWidth] = malloc(biScanWidth * (r->bottom - r->top)); 177 | BYTE *pBits = (BYTE *) malloc(biScanWidth * (r->bottom - r->top)); 178 | fread ((BYTE *) pBits, biScanWidth * (r->bottom - r->top), 1, file); 179 | 180 | // Create the mask buffer 181 | const DWORD biOutputRowSize = ((max(r->right - r->left, min_w) + 7) / 8) * 8; 182 | const DWORD biByteSize = (r->bottom - r->top) * biOutputRowSize; 183 | 184 | #define OUTPUT_ACCESS(zr, zc) pOutput[((zr) * biOutputRowSize) + (zc)] 185 | //BYTE (*pOutput)[biOutputRowSize] = malloc(biByteSize); 186 | BYTE *pOutput = (BYTE *) malloc(biByteSize); 187 | memset (pOutput, 0, biByteSize); 188 | 189 | #define MASK_ACCESS(zr, zc) pMask[((zr) * biOutputRowSize) + (zc)] 190 | //BYTE (*pMask)[biOutputRowSize] = malloc(biByteSize); 191 | BYTE *pMask = (BYTE *) malloc(biByteSize); 192 | memset (pMask, 1, biByteSize); 193 | 194 | RGBQUAD rgbMask = {0, 255, 0, 0}; 195 | DWORD value = parse_f ("__BM_MSK_RGB"); 196 | 197 | rgbMask.rgbRed = (value >> 16) & 0xFF; 198 | rgbMask.rgbGreen = (value >> 8) & 0xFF; 199 | rgbMask.rgbBlue = (value) & 0xFF; 200 | 201 | bool HandleInv = false; 202 | RGBQUAD rgbInv; 203 | if ((value = parse_f("__BM_INV_RGB")) != -1) { 204 | rgbInv.rgbRed = (value >> 16) & 0xFF; 205 | rgbInv.rgbGreen = (value >> 8) & 0xFF; 206 | rgbInv.rgbBlue = (value) & 0xFF; 207 | HandleInv = true; 208 | } 209 | WORD biOutputShades = parse_f("__BM_SHD"); 210 | 211 | int row; 212 | for (row = r->bottom - r->top - 1; row >= 0; row--) { 213 | int col, bit; 214 | for ( col = 0, bit = r->left * _W(bi->biBitCount); 215 | bit < (max((r->right * _W(bi->biBitCount)), ((r->left + min_w) * _W(bi->biBitCount)))); 216 | col++, bit += _W(bi->biBitCount)) 217 | { 218 | RGBQUAD rgb = {255, 255, 255, 0}; 219 | 220 | if (bit >= _ZL(bi->biWidth) * _W(bi->biBitCount) || bit >= r->right * _W(bi->biBitCount)) { 221 | // Output the brightest shade, then continue 222 | //pOutput[r->bottom - r->top - 1 - row][col] 223 | OUTPUT_ACCESS(r->bottom - r->top - 1 - row, col) = 0; 224 | continue; 225 | } else if (_W(bi->biBitCount) < 16 || _DW(bi->biClrUsed) != 0) { 226 | BYTE bMask = MASK(_W(bi->biBitCount)) << ((8 - _W(bi->biBitCount)) - (bit % 8)); 227 | 228 | UINT iColor = 0; 229 | int i = 0; 230 | do { 231 | iColor <<= 8; 232 | //iColor += (pBits[row][bit / 8] & bMask) >> (8 - _W(bi->biBitCount) - (bit % 8)); 233 | iColor += (BITS_ACCESS(row, bit / 8) & bMask) >> (8 - _W(bi->biBitCount) - (bit % 8)); 234 | } while (++i < (_W(bi->biBitCount) / 8)); 235 | 236 | rgb = bmiColors[iColor]; 237 | } else { 238 | WORD biCmBitCount = _W(bi->biBitCount) / 3; 239 | DWORD dwData = 0; 240 | int i; 241 | for (i = 0; i < (_W(bi->biBitCount) / 8); i++) { 242 | dwData <<= 8; 243 | //dwData += pBits[row][(bit / 8) + i]; 244 | dwData += BITS_ACCESS(row, (bit / 8) + i); 245 | } 246 | 247 | rgb.rgbRed = (dwData) & MASK(biCmBitCount); 248 | rgb.rgbGreen = (dwData >> biCmBitCount) & MASK(biCmBitCount); 249 | rgb.rgbBlue = (dwData >> (2 * biCmBitCount)) & MASK(biCmBitCount); 250 | } 251 | 252 | double lum = LUMINANCE(rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue); 253 | 254 | if (HandleInv && (memcmp(&rgb, &rgbInv, sizeof(RGBQUAD)) == 0)) { 255 | //pOutput[r->bottom - r->top - 1 - row][col] = biOutputShades - 1; 256 | OUTPUT_ACCESS(r->bottom - r->top - 1 - row, col) = biOutputShades - 1; 257 | } else { 258 | // Calculate the output shade (use 0.99 instead of 1.0 to stick within the range) 259 | //pOutput[r->bottom - r->top - 1 - row][col] = (0.99 - lum) * biOutputShades; 260 | OUTPUT_ACCESS(r->bottom - r->top - 1 - row, col) = (BYTE) ((0.99 - lum) * biOutputShades); 261 | 262 | // Calculate the mask 263 | if (memcmp (&rgb, &rgbMask, sizeof (RGBQUAD)) != 0) 264 | // pMask[r->bottom - r->top - 1 - row][col] = 0; 265 | MASK_ACCESS(r->bottom - r->top - 1 - row, col) = 0; 266 | } 267 | } 268 | } 269 | 270 | int image_order = 1; 271 | int mask_order = 2; 272 | 273 | if (define_with_value ("__BM_MSK_1ST", 1)) { 274 | mask_order = 1; 275 | image_order = 2; 276 | } 277 | 278 | int order; 279 | for (order = 1; order <= 2; order++) { 280 | 281 | if (order == image_order) { 282 | // Write out all image layers 283 | int i; 284 | for (i = log2(biOutputShades) - 1; i >= 0; i--) { 285 | int row; 286 | for (row = 0; row < r->bottom - r->top; row++) { 287 | DWORD col; 288 | int db_out = 0; 289 | for (col = 0; col < biOutputRowSize; col++) { 290 | //db_out |= ((pOutput[row][col] >> i) & 1) << (7 - (col % 8)); 291 | db_out |= ((OUTPUT_ACCESS(row, col) >> i) & 1) << (7 - (col % 8)); 292 | 293 | if (col % 8 == 7) { 294 | write_out(db_out); 295 | program_counter++; 296 | db_out = 0; 297 | } 298 | } 299 | } 300 | } 301 | free (pOutput); 302 | } 303 | 304 | if (order == mask_order) { 305 | // Write out the mask 306 | int mask_chk = 0; 307 | if (define_with_value ("__BM_MSK", 1)) { 308 | int row; 309 | for (row = 0; row < r->bottom - r->top; row++) { 310 | DWORD col; 311 | BYTE db_out = 0; 312 | for (col = 0; col < biOutputRowSize; col++) { 313 | //db_out |= (pMask[row][col] & 1) << (7 - (col % 8)); 314 | db_out |= (MASK_ACCESS(row, col) & 1) << (7 - (col % 8)); 315 | 316 | if (col % 8 == 7) { 317 | if (define_with_value ("__BM_MSK_INV", 1)) 318 | write_out(~db_out); 319 | else 320 | write_out(db_out); 321 | program_counter++; 322 | mask_chk |= db_out; 323 | db_out = 0; 324 | } 325 | } 326 | } 327 | 328 | //if (mask_chk == 0) 329 | //show_warning("Bitmap mask is blank - %d null bytes written", (int) biByteSize/8); 330 | } 331 | } 332 | } 333 | 334 | free (pBits); 335 | free (pMask); 336 | } 337 | 338 | void handle_bitmap(FILE *file) 339 | { 340 | char *base_name = NULL, *suffix = NULL; 341 | BITMAPINFOHEADER bi; 342 | RGBQUAD *bmiColors; 343 | DWORD nColors = 0; 344 | RECT r; 345 | LONG padding = parse_f("__BM_PAD"); 346 | define_t *img_map = search_defines("__BM_MAP"); 347 | int min_w = parse_f("__BM_MIN_W"); 348 | int bmp_num = 1; 349 | 350 | fseek(file, 0, SEEK_SET); 351 | 352 | BITMAPFILEHEADER bf; 353 | fread (&bf, sizeof (BITMAPFILEHEADER), 1, file); 354 | 355 | fread (&bi, sizeof (BITMAPINFOHEADER), 1, file); 356 | 357 | if (_DW(bi.biSize) != sizeof (BITMAPINFOHEADER) || _DW(bi.biCompression) != BI_RGB) { 358 | show_error ("Bitmap file of this type not supported: size: %ld, compression: %ld\n", _DW(bi.biSize), _DW(bi.biCompression)); 359 | return; 360 | } 361 | 362 | if (_W(bi.biBitCount) < 16 || _DW(bi.biClrUsed) != 0) { 363 | // If biClrUsed is 0, then the amount of RGBQUADs listed is the total possible 364 | // as given by biBitCount 365 | nColors = (_DW(bi.biClrUsed == 0)) ? 1 << _W(bi.biBitCount) : _DW(bi.biClrUsed); 366 | } 367 | 368 | // Read in the palette 369 | bmiColors = (LPRGBQUAD) calloc(nColors, sizeof(RGBQUAD)); 370 | fread(bmiColors, sizeof(RGBQUAD), nColors, file); 371 | 372 | if (ftell (file) != _DW(bf.bfOffBits)) { 373 | printf("invalid file structure!\n"); 374 | return; 375 | } 376 | 377 | if (last_label != NULL && last_label->value == program_counter) { 378 | base_name = (char *) malloc(strlen(last_label->name) + 32); 379 | strcpy(base_name, last_label->name); 380 | suffix = base_name + strlen(base_name); 381 | } 382 | 383 | if (img_map == NULL || parse_f("__BM_MAP") == 0) { 384 | int width = _ZL(bi.biWidth); 385 | int height = _ZL(bi.biHeight); 386 | 387 | r.left = 0; 388 | r.top = 0; 389 | r.right = width; 390 | r.bottom = height; 391 | 392 | if (base_name) { 393 | strcpy(suffix, "_WIDTH"); 394 | add_label(strdup(base_name), width); 395 | 396 | strcpy(suffix, "_HEIGHT"); 397 | add_label(strdup(base_name), height); 398 | } 399 | 400 | handle_bitmap_header(&r, &bf, &bi); 401 | 402 | 403 | if (min_w > 0 && width > min_w) { 404 | int w = width; 405 | while (w > 0) { 406 | r.right = r.left + min(min_w, w); 407 | handle_bitmap_internal(file, &r, &bf, &bi, bmiColors); 408 | r.left += min_w; 409 | w -= min_w; 410 | } 411 | } else { 412 | handle_bitmap_internal(file, &r, &bf, &bi, bmiColors); 413 | } 414 | } else { 415 | int left, top, width = 0, height = 0, count = 0, i = 0; 416 | char *p; 417 | char *fmt = strdup(skip_whitespace(img_map->contents)); 418 | reduce_string(fmt); 419 | 420 | for (p = strtok(fmt, ",;x "); p; p = strtok(NULL, ",;x ")) { 421 | if (i == 0) width = parse_f(p); 422 | else if (i == 1) height = parse_f(p); 423 | else if (i == 2) count = parse_f(p) + 1; 424 | i++; 425 | } 426 | free(fmt); 427 | 428 | if (padding == -1) padding = 0; 429 | 430 | if (width == 0 && height == 0) { 431 | show_error("At least either of width or height must be given for an image map"); 432 | goto map_done; 433 | } 434 | 435 | width = (_ZL(bi.biWidth) - (width * 2 * padding)) / width; 436 | height = (_ZL(bi.biHeight) - (height * 2 * padding)) / height; 437 | 438 | if (base_name) { 439 | strcpy(suffix, "_WIDTH"); 440 | add_label(strdup(base_name), width); 441 | 442 | strcpy(suffix, "_HEIGHT"); 443 | add_label(strdup(base_name), height); 444 | } 445 | 446 | for (left = 0; left < _ZL(bi.biWidth); left += width + 2*padding) { 447 | for (top = _ZL(bi.biHeight) - height - 2*padding; top >= 0 ; top -= height + 2*padding) { 448 | int min_w = parse_f("__BM_MIN_W"); 449 | if (count == 1) goto map_done; 450 | if (count != 0) count--; 451 | 452 | if (base_name) { 453 | char num_buf[8]; 454 | sprintf(num_buf, "%d", bmp_num); 455 | strcpy(suffix, num_buf); 456 | add_label(strdup(base_name), program_counter); 457 | } 458 | 459 | bmp_num++; 460 | 461 | r.left = left + padding; 462 | r.top = top + padding; 463 | r.right = r.left + width; 464 | r.bottom = r.top + height; 465 | handle_bitmap_header(&r, &bf, &bi); 466 | 467 | if (min_w > 0 && width > min_w) { 468 | int w = width; 469 | while (w > 0) { 470 | r.right = r.left + min(min_w, w); 471 | handle_bitmap_internal(file, &r, &bf, &bi, bmiColors); 472 | r.left += min_w; 473 | w -= min_w; 474 | } 475 | } else { 476 | handle_bitmap_internal(file, &r, &bf, &bi, bmiColors); 477 | } 478 | } 479 | } 480 | map_done: 481 | if (base_name) { 482 | strcpy(suffix, "_COUNT"); 483 | add_label(strdup(base_name), bmp_num - 1); 484 | } 485 | 486 | set_define(img_map, "0", -1, false); 487 | } 488 | if (base_name) 489 | free(base_name); 490 | free (bmiColors); 491 | } 492 | -------------------------------------------------------------------------------- /bitmap.h: -------------------------------------------------------------------------------- 1 | #ifndef _BITMAP_H 2 | #define _BITMAP_H 3 | 4 | bool IsFileBitmap(FILE *file); 5 | void handle_bitmap (FILE *file); 6 | 7 | #endif -------------------------------------------------------------------------------- /console.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "console.h" 4 | #include "spasm.h" 5 | 6 | //saved console attributes, to be restored on exit 7 | WORD user_attributes; 8 | 9 | void restore_console_attributes_at_exit () { 10 | if (!use_colors) return; 11 | #ifdef WIN32 12 | SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), user_attributes); 13 | #elif !defined(MACVER) 14 | printf ("\x1b[0m"); 15 | #endif 16 | } 17 | 18 | void restore_console_attributes (WORD orig_attributes) { 19 | if (!use_colors) return; 20 | #ifdef WIN32 21 | SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), orig_attributes); 22 | #elif !defined(MACVER) 23 | printf ("\x1b[0m"); 24 | #endif 25 | } 26 | 27 | WORD save_console_attributes () { 28 | #ifdef WIN32 29 | CONSOLE_SCREEN_BUFFER_INFO csbiScreenBufferInfo; 30 | GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &csbiScreenBufferInfo); 31 | return csbiScreenBufferInfo.wAttributes; 32 | #else 33 | return 0; 34 | #endif 35 | } 36 | 37 | BOOL set_console_attributes (unsigned short attr) { 38 | if (!use_colors) return true; 39 | #ifdef WIN32 40 | return SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD)attr); 41 | #elif !defined(MACVER) 42 | printf ("\x1b[1;%dm", attr); 43 | return true; 44 | #endif 45 | } 46 | 47 | -------------------------------------------------------------------------------- /console.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONSOLE_H 2 | #define __CONSOLE_H 3 | 4 | #include "spasm.h" 5 | 6 | #ifdef WIN32 7 | # define COLOR_RED FOREGROUND_RED | FOREGROUND_INTENSITY 8 | # define COLOR_YELLOW FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY 9 | # define COLOR_WHITE FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY 10 | # define COLOR_BLUE FOREGROUND_BLUE | FOREGROUND_INTENSITY 11 | # define COLOR_GREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY 12 | #else 13 | # define COLOR_RED 31 14 | # define COLOR_YELLOW 33 15 | # define COLOR_WHITE 37 16 | # define COLOR_BLUE 34 17 | # define COLOR_GREEN 32 18 | #endif 19 | 20 | WORD save_console_attributes (); 21 | void restore_console_attributes_at_exit (); 22 | void restore_console_attributes (WORD orig_attributes); 23 | BOOL set_console_attributes (unsigned short); 24 | 25 | #endif 26 | 27 | -------------------------------------------------------------------------------- /directive.h: -------------------------------------------------------------------------------- 1 | #ifndef __DIRECTIVE_H 2 | #define __DIRECTIVE_H 3 | 4 | typedef enum _ES_TYPE { 5 | ES_BYTE, 6 | ES_WORD, 7 | ES_LONG, 8 | ES_ECHO, 9 | ES_FCREATE, 10 | ES_NONE, 11 | } ES_TYPE; 12 | 13 | char *handle_directive (const char *ptr); 14 | void show_define (define_t *define); 15 | char *parse_emit_string (const char *, ES_TYPE, void *); 16 | 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /dll.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #ifdef _WINDLL 4 | #include "spasm.h" 5 | #include "utils.h" 6 | #include "storage.h" 7 | #include "list.h" 8 | 9 | list_t *default_defines; 10 | typedef struct { 11 | char name[64]; 12 | char value[64]; 13 | } default_define_pair_t; 14 | 15 | #ifdef LOG 16 | #include 17 | FILE *logfile; 18 | #endif 19 | extern "C" 20 | { 21 | __declspec (dllexport) BOOL __stdcall DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 22 | { 23 | switch (ul_reason_for_call) 24 | { 25 | case DLL_PROCESS_ATTACH: 26 | #ifdef LOG 27 | logfile = fopen("logfile.txt", "a"); 28 | fprintf(logfile, "Log opened\n"); 29 | fflush(logfile); 30 | #endif 31 | /* 32 | AllocConsole(); 33 | SetConsoleTitle("SPASM"); 34 | // The following is a really disgusting hack to make stdin and stdout attach 35 | // to the newly created console using the MSVC++ libraries. I hope other 36 | // operating systems don't need this kind of kludge.. :) 37 | stdout->_file = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT); 38 | stdin->_file = _open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT); 39 | */ 40 | output_contents = (unsigned char *) malloc (output_buf_size); 41 | setvbuf(stdout, output_text, _IOFBF, 65536); 42 | break; 43 | case DLL_THREAD_ATTACH: 44 | break; 45 | case DLL_THREAD_DETACH: 46 | break; 47 | case DLL_PROCESS_DETACH: 48 | #ifdef LOG 49 | fprintf(logfile, "Log closed\n"); 50 | fflush(logfile); 51 | fclose(logfile); 52 | #endif 53 | //stdout->_file = -1; 54 | //stdin->_file = -1; 55 | //FreeConsole(); 56 | free(output_contents); 57 | #ifdef _DEBUG 58 | _CrtDumpMemoryLeaks(); 59 | #endif 60 | break; 61 | } 62 | 63 | 64 | return TRUE; 65 | } 66 | 67 | __declspec (dllexport) int __stdcall 68 | SetInputFile(const char *lpFilename) { 69 | curr_input_file = strdup(lpFilename); 70 | #ifdef LOG 71 | fprintf(logfile, "SetInputFile: %s\n", curr_input_file); 72 | fflush(logfile); 73 | #endif 74 | return EXIT_NORMAL; 75 | } 76 | 77 | __declspec (dllexport) int __stdcall 78 | SetOutputFile(const char *lpFilename) { 79 | output_filename = strdup(lpFilename); 80 | #ifdef LOG 81 | fprintf(logfile, "SetInputFile: %s\n", output_filename); 82 | fflush(logfile); 83 | #endif 84 | return EXIT_NORMAL; 85 | } 86 | 87 | __declspec (dllexport) int __stdcall 88 | ClearDefines() { 89 | list_free(default_defines, true, NULL); 90 | default_defines = NULL; 91 | #ifdef LOG 92 | fprintf(logfile, "ClearDefines"); 93 | fflush(logfile); 94 | #endif 95 | return 0; 96 | } 97 | 98 | __declspec (dllexport) int __stdcall 99 | AddDefine(const char *lpName, const char *lpValue) { 100 | default_define_pair_t *ddp = (default_define_pair_t*)malloc (sizeof(*ddp)); 101 | strncpy(ddp->name, lpName, sizeof(ddp->name)); 102 | if (lpValue == NULL) { 103 | strcpy(ddp->value, "1"); 104 | } else { 105 | strncpy(ddp->value, lpValue, sizeof(ddp->value)); 106 | } 107 | default_defines = list_append(default_defines, ddp); 108 | #ifdef LOG 109 | fprintf(logfile, "AddDefine: %s %d\n", ddp->name, ddp->value); 110 | fflush(logfile); 111 | #endif 112 | return 0; 113 | } 114 | 115 | __declspec (dllexport) int __stdcall 116 | ClearIncludes() { 117 | list_free(include_dirs, true, NULL); 118 | include_dirs = NULL; 119 | #ifdef LOG 120 | fprintf(logfile, "ClearIncludes\n"); 121 | fflush(logfile); 122 | #endif 123 | return 0; 124 | } 125 | 126 | __declspec (dllexport) int __stdcall 127 | AddInclude(const char *lpDirectory) { 128 | include_dirs = list_append(include_dirs, strdup (lpDirectory)); 129 | #ifdef LOG 130 | fprintf(logfile, "AddInclude: %s\n", lpDirectory); 131 | fflush(logfile); 132 | #endif 133 | return 0; 134 | } 135 | 136 | __declspec (dllexport) int __stdcall 137 | RunAssembly() { 138 | list_t *list = default_defines; 139 | int result; 140 | 141 | mode = MODE_NORMAL; 142 | init_storage(); 143 | 144 | while (list) { 145 | char *name = ((default_define_pair_t *) list->data)->name; 146 | char *value = ((default_define_pair_t *) list->data)->value; 147 | add_define (strdup (name), NULL)->contents = strdup (value); 148 | list = list->next; 149 | } 150 | 151 | printf("Input file: %s\n", curr_input_file); 152 | printf("Output file: %s\n", output_filename); 153 | 154 | list = include_dirs; 155 | while (list) { 156 | printf("Include dir: %s\n", (char *) list->data); 157 | list = list->next; 158 | } 159 | 160 | if (curr_input_file == NULL || output_filename == NULL) { 161 | #ifdef LOG 162 | fprintf(logfile, "Unable to assemble file\n"); 163 | fflush(logfile); 164 | #endif 165 | return EXIT_FATAL_ERROR; 166 | } 167 | 168 | result = run_assembly(); 169 | fflush(stdout); 170 | #ifdef LOG 171 | fprintf(logfile, "File assembled with return value: %d\n", result); 172 | fflush(logfile); 173 | #endif 174 | free_storage(); 175 | return result; 176 | } 177 | 178 | __declspec (dllexport) 179 | char* __stdcall GetStdOut() 180 | { 181 | return output_text; 182 | } 183 | 184 | __declspec (dllexport) 185 | int __stdcall RunAssemblyWithArguments(char *szCommand, BYTE *lpResult, int cbResult) { 186 | 187 | output_filename = NULL; 188 | mode = MODE_NORMAL | MODE_COMMANDLINE; 189 | input_contents = (char *) malloc (strlen(szCommand) + 1 + 2); 190 | 191 | strcpy(input_contents, " "); 192 | strcat(input_contents, szCommand); 193 | strcat(input_contents, "\n"); 194 | 195 | curr_input_file = NULL; 196 | int result = run_assembly(); 197 | 198 | if (lpResult != NULL) { 199 | memcpy(lpResult, output_contents, min(out_ptr - output_contents, cbResult)); 200 | } 201 | return min(out_ptr - output_contents, cbResult); 202 | } 203 | 204 | __declspec (dllexport) int __stdcall 205 | ShowMessage() { 206 | MessageBox(NULL, "Hello", "Hello", MB_OK); 207 | return 0; 208 | } 209 | } 210 | 211 | #endif -------------------------------------------------------------------------------- /dlldata.c: -------------------------------------------------------------------------------- 1 | /********************************************************* 2 | DllData file -- generated by MIDL compiler 3 | 4 | DO NOT ALTER THIS FILE 5 | 6 | This file is regenerated by MIDL on every IDL file compile. 7 | 8 | To completely reconstruct this file, delete it and rerun MIDL 9 | on all the IDL files in this DLL, specifying this file for the 10 | /dlldata command line option 11 | 12 | *********************************************************/ 13 | 14 | #define PROXY_DELEGATION 15 | 16 | #include 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | EXTERN_PROXY_FILE( SPASM ) 23 | 24 | 25 | PROXYFILE_LIST_START 26 | /* Start of list */ 27 | REFERENCE_PROXY_FILE( SPASM ), 28 | /* End of list */ 29 | PROXYFILE_LIST_END 30 | 31 | 32 | DLLDATA_ROUTINES( aProxyFileList, GET_DLL_CLSID ) 33 | 34 | #ifdef __cplusplus 35 | } /*extern "C" */ 36 | #endif 37 | 38 | /* end of generated dlldata file */ 39 | -------------------------------------------------------------------------------- /errors.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #define _ERRORS_CPP 4 | #include "errors.h" 5 | 6 | #include "spasm.h" 7 | #include "console.h" 8 | #include "list.h" 9 | 10 | typedef struct tagERRORINSTANCE 11 | { 12 | LPSTR lpszFileName; 13 | int line_num; //-1 for no line 14 | DWORD dwErrorCode; 15 | int nSession; 16 | bool fSuppressErrors; 17 | bool fIsWarning; 18 | int nPrintSession; 19 | LPTSTR lpszErrorText; 20 | LPTSTR lpszAnnotation; 21 | } ERRORINSTANCE, *LPERRORINSTANCE; 22 | 23 | typedef struct _errorlist { 24 | LPERRORINSTANCE data; 25 | struct _errorlist *next; 26 | } errorlist_t; 27 | 28 | errorlist_t *g_ErrorList; 29 | int g_nErrorSession = 0; 30 | 31 | static void PrintSPASMError(const LPERRORINSTANCE lpError) 32 | { 33 | if (mode & MODE_CODE_COUNTER) 34 | { 35 | return; 36 | } 37 | 38 | assert(lpError != NULL); 39 | if ((lpError->dwErrorCode != SPASM_ERR_SUCCESS) || (lpError->lpszErrorText != NULL)) 40 | { 41 | WORD orig_attributes = save_console_attributes(); 42 | set_console_attributes(lpError->fIsWarning ? COLOR_YELLOW : COLOR_RED); 43 | if (lpError->lpszAnnotation != NULL) 44 | { 45 | printf("%s\n", lpError->lpszAnnotation); 46 | } 47 | 48 | printf("%s\n", lpError->lpszErrorText); 49 | #ifdef WINVER 50 | OutputDebugString(lpError->lpszErrorText); 51 | OutputDebugString(_T("\n")); 52 | #endif 53 | restore_console_attributes(orig_attributes); 54 | } 55 | } 56 | 57 | static LPERRORINSTANCE AllocErrorInstance() 58 | { 59 | LPERRORINSTANCE lpErr = (LPERRORINSTANCE) malloc(sizeof(ERRORINSTANCE)); 60 | lpErr->dwErrorCode = SPASM_ERR_SUCCESS; 61 | lpErr->line_num = -1; 62 | lpErr->lpszFileName = NULL; 63 | //lpErr->fSuppressErrors = suppress_errors; 64 | lpErr->nSession = g_nErrorSession; 65 | lpErr->nPrintSession = -1; 66 | lpErr->lpszErrorText = NULL; 67 | lpErr->lpszAnnotation = NULL; 68 | return lpErr; 69 | } 70 | 71 | static void FreeErrorInstance(LPERRORINSTANCE lpErr) 72 | { 73 | if (lpErr->lpszErrorText != NULL) 74 | { 75 | free(lpErr->lpszErrorText); 76 | } 77 | if (lpErr->lpszAnnotation != NULL) 78 | { 79 | free(lpErr->lpszAnnotation); 80 | } 81 | if (lpErr->lpszFileName != NULL) 82 | { 83 | free(lpErr->lpszFileName); 84 | } 85 | free(lpErr); 86 | lpErr = NULL; 87 | } 88 | 89 | int StartSPASMErrorSession(void) 90 | { 91 | //suppress_errors = true; 92 | return ++g_nErrorSession; 93 | } 94 | 95 | int GetSPASMErrorSessionErrorCount(int nSession) 96 | { 97 | int nCount = 0; 98 | list_t *pList = (list_t *) g_ErrorList; 99 | while ((pList != NULL) && ((LPERRORINSTANCE) pList->data)->nSession == nSession) 100 | { 101 | if (((LPERRORINSTANCE) pList->data)->dwErrorCode != SPASM_ERR_SUCCESS) 102 | { 103 | nCount++; 104 | } 105 | pList = pList->next; 106 | } 107 | return nCount; 108 | } 109 | 110 | bool IsSPASMErrorFatal(DWORD dwError) 111 | { 112 | return !(dwError == SPASM_ERR_LOCAL_LABEL_FORWARD_REF || 113 | dwError == SPASM_ERR_LABEL_NOT_FOUND || 114 | dwError == SPASM_ERR_SUCCESS || 115 | dwError == SPASM_ERR_RECURSION_DEPTH); 116 | } 117 | 118 | bool IsSPASMErrorSessionFatal(int nSession) 119 | { 120 | bool fIsFatal = false; 121 | list_t *pList = (list_t *) g_ErrorList; 122 | while ((pList != NULL) && ((LPERRORINSTANCE) pList->data)->nSession == nSession) 123 | { 124 | LPERRORINSTANCE lpError = (LPERRORINSTANCE) pList->data; 125 | DWORD dwError = lpError->dwErrorCode; 126 | if (IsSPASMErrorFatal(dwError)) 127 | { 128 | fIsFatal = true; 129 | break; 130 | } 131 | pList = pList->next; 132 | } 133 | return fIsFatal; 134 | } 135 | 136 | static void ReplayErrorRecursive(const list_t *pList, bool fFatalOnly) 137 | { 138 | if (pList == NULL) 139 | return; 140 | 141 | ReplayErrorRecursive(pList->next, fFatalOnly); 142 | if (((LPERRORINSTANCE) pList->data)->nSession == 1) 143 | { 144 | LPERRORINSTANCE lpError = (LPERRORINSTANCE) pList->data; 145 | if (fFatalOnly) 146 | { 147 | if (!IsSPASMErrorFatal(lpError->dwErrorCode)) 148 | { 149 | return; 150 | } 151 | } 152 | DPRINT("[ReplayErrorRecursive] printing error\n"); 153 | PrintSPASMError(lpError); 154 | } 155 | } 156 | 157 | void ReplaySPASMErrorSession(int nSession, bool fFatalOnly) 158 | { 159 | if (nSession == 1) 160 | { 161 | DPRINT("[ReplaySPASMErrorSession] calling ReplayErrorRecursive\n"); 162 | ReplayErrorRecursive((list_t *) g_ErrorList, fFatalOnly); 163 | } 164 | else 165 | { 166 | list_t *pList = (list_t *) g_ErrorList; 167 | while ((pList != NULL) && ((LPERRORINSTANCE) pList->data)->nSession == nSession) 168 | { 169 | // Move it up to the next error level 170 | ((LPERRORINSTANCE) pList->data)->nSession--; 171 | pList = pList->next; 172 | } 173 | } 174 | } 175 | 176 | void ReplayFatalSPASMErrorSession(int nSession) 177 | { 178 | ReplaySPASMErrorSession(nSession, true); 179 | } 180 | 181 | bool IsErrorInSPASMErrorSession(int nSession, DWORD dwErrorCode) 182 | { 183 | list_t *pList = (list_t *) g_ErrorList; 184 | while ((pList != NULL) && ((LPERRORINSTANCE) pList->data)->nSession == nSession) 185 | { 186 | LPERRORINSTANCE lpError = (LPERRORINSTANCE) pList->data; 187 | if (lpError->dwErrorCode == dwErrorCode) 188 | { 189 | return true; 190 | } 191 | pList = pList->next; 192 | } 193 | return false; 194 | } 195 | 196 | void AddSPASMErrorSessionAnnotation(int nSession, LPCTSTR lpszFormat, ...) 197 | { 198 | va_list valist; 199 | va_start(valist, lpszFormat); 200 | 201 | TCHAR szBuffer[256]; 202 | TCHAR szDescription[128] = _T("An error occurred"); 203 | 204 | StringCchVPrintf(szDescription, ARRAYSIZE(szDescription), lpszFormat, valist); 205 | StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), _T("%s:%d: %s"), 206 | curr_input_file, line_num, szDescription); 207 | 208 | va_end(valist); 209 | 210 | list_t *pList = (list_t *) g_ErrorList; 211 | while (pList != NULL) 212 | { 213 | LPERRORINSTANCE lpErr = (LPERRORINSTANCE) pList->data; 214 | if (lpErr->nSession >= nSession) 215 | { 216 | if (lpErr->lpszAnnotation != NULL) 217 | { 218 | free(lpErr->lpszAnnotation); 219 | } 220 | lpErr->lpszAnnotation = _tcsdup(szBuffer); 221 | } 222 | pList = pList->next; 223 | } 224 | } 225 | void EndSPASMErrorSession(int nSession) { 226 | DPRINT("[EndSPASMErrorSession] called with nSession = %d\n", nSession); 227 | int fatalErrors = CleanupSPASMErrorSession(nSession); 228 | DPRINT("[EndSPASMErrorSession] fatal errs = %d\n", fatalErrors); 229 | 230 | if (nSession == 1 && fatalErrors > 0) 231 | exit(EXIT_FATAL_ERROR); 232 | } 233 | 234 | //returns the number of fatal errors 235 | int CleanupSPASMErrorSession(int nSession) 236 | { 237 | int fatalErrorCount = 0; 238 | list_t *pList = (list_t *) g_ErrorList; 239 | 240 | list_t *pPrev = NULL, *old_list = NULL; 241 | while ((pList != NULL) && ((LPERRORINSTANCE) pList->data)->nSession == nSession) 242 | { 243 | LPERRORINSTANCE lpErr = (LPERRORINSTANCE) pList->data; 244 | 245 | if (pPrev == NULL) 246 | { 247 | g_ErrorList = (errorlist_t *) pList->next; 248 | } 249 | else 250 | { 251 | pPrev->next = pList->next; 252 | } 253 | if(IsSPASMErrorFatal(lpErr->dwErrorCode)) { 254 | fatalErrorCount++; 255 | } 256 | FreeErrorInstance(lpErr); 257 | list_t *pListOld = pList; 258 | pList = pList->next; 259 | list_free_node(pListOld); 260 | 261 | //assert(pList != NULL); 262 | if (pList == NULL) 263 | break; 264 | } 265 | 266 | g_nErrorSession--; 267 | return fatalErrorCount; 268 | } 269 | 270 | 271 | void ClearSPASMErrorSessions() 272 | { 273 | if (g_ErrorList != NULL) 274 | { 275 | list_free((list_t *) g_ErrorList, true, (void (*)(void *)) FreeErrorInstance); 276 | } 277 | g_nErrorSession = 0; 278 | g_ErrorList = NULL; 279 | } 280 | 281 | void FreeSPASMErrorSessions(void) 282 | { 283 | list_t *pList = (list_t *) g_ErrorList; 284 | 285 | list_t *pNext = NULL; 286 | while (pList) 287 | { 288 | LPERRORINSTANCE lpErr = (LPERRORINSTANCE) pList->data; 289 | 290 | pNext = pList->next; 291 | FreeErrorInstance(lpErr); 292 | list_free_node(pList); 293 | pList = pNext; 294 | } 295 | } 296 | 297 | #ifdef _TEST 298 | DWORD GetLastSPASMError() 299 | { 300 | list_t *pList = (list_t *) g_ErrorList; 301 | while (pList != NULL) 302 | { 303 | LPERRORINSTANCE lpError = (LPERRORINSTANCE) pList->data; 304 | if (lpError->dwErrorCode != SPASM_ERR_SUCCESS) 305 | { 306 | return lpError->dwErrorCode; 307 | } 308 | pList = pList->next; 309 | } 310 | return SPASM_ERR_SUCCESS; 311 | } 312 | 313 | int GetLastSPASMErrorLine() 314 | { 315 | list_t *pList = (list_t *) g_ErrorList; 316 | while (pList != NULL) 317 | { 318 | LPERRORINSTANCE lpError = (LPERRORINSTANCE) pList->data; 319 | if (lpError->dwErrorCode != SPASM_ERR_SUCCESS) 320 | { 321 | return lpError->line_num; 322 | } 323 | pList = pList->next; 324 | } 325 | return SPASM_ERR_SUCCESS; 326 | } 327 | #endif 328 | 329 | 330 | static void SetLastSPASMProblem(DWORD dwErrorCode, bool fIsWarning, va_list valist) 331 | { 332 | if (dwErrorCode == SPASM_ERR_SUCCESS) 333 | { 334 | return; 335 | } 336 | 337 | LPERRORINSTANCE lpErr = AllocErrorInstance(); 338 | lpErr->dwErrorCode = dwErrorCode; 339 | lpErr->line_num = line_num; 340 | lpErr->lpszFileName = _strdup(curr_input_file); 341 | //lpErr->fSuppressErrors = suppress_errors; 342 | lpErr->fIsWarning = fIsWarning; 343 | 344 | TCHAR szBuffer[256]; 345 | TCHAR szDescription[128] = _T("An error occurred"); 346 | 347 | for (int i = 0; i < ARRAYSIZE(g_ErrorCodes); i++) 348 | { 349 | if (g_ErrorCodes[i].dwCode == lpErr->dwErrorCode) 350 | { 351 | StringCchVPrintf(szDescription, ARRAYSIZE(szDescription), 352 | g_ErrorCodes[i].lpszDescription, valist); 353 | break; 354 | } 355 | } 356 | 357 | LPCTSTR lpszProblemType = (fIsWarning) ? _T("warning") : _T("error"); 358 | LPCTSTR lpszProblemCode = (fIsWarning) ? _T("SW") : _T("SE"); 359 | 360 | if (lpErr->line_num != -1) 361 | { 362 | StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), _T("%s:%d: %s %s%03X: %s"), 363 | lpErr->lpszFileName, lpErr->line_num, lpszProblemType, lpszProblemCode, lpErr->dwErrorCode, szDescription); 364 | } 365 | else 366 | { 367 | StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), _T("%s: %s %s%03X: %s"), 368 | lpErr->lpszFileName, lpszProblemType, lpszProblemCode, lpErr->dwErrorCode, szDescription); 369 | } 370 | 371 | lpErr->lpszErrorText = _strdup(szBuffer); 372 | 373 | g_ErrorList = (errorlist_t *) list_prepend((list_t *) g_ErrorList, (LPVOID) lpErr); 374 | //if (suppress_errors == false) 375 | //{ 376 | //PrintSPASMError(lpErr); 377 | //} 378 | } 379 | 380 | void SetLastSPASMWarning(DWORD dwErrorCode, ...) 381 | { 382 | va_list valist; 383 | va_start(valist, dwErrorCode); 384 | 385 | SetLastSPASMProblem(dwErrorCode, true, valist); 386 | 387 | va_end(valist); 388 | } 389 | 390 | void SetLastSPASMError(DWORD dwErrorCode, ...) 391 | { 392 | va_list valist; 393 | va_start(valist, dwErrorCode); 394 | 395 | SetLastSPASMProblem(dwErrorCode, false, valist); 396 | 397 | va_end(valist); 398 | } 399 | -------------------------------------------------------------------------------- /errors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct tagSPASMERROR 4 | { 5 | DWORD dwCode; 6 | LPCTSTR lpszDescription; 7 | } 8 | SPASMERROR, *LPSPASMERROR; 9 | 10 | #define SPASM_ERR_SUCCESS 0x000 11 | #define SPASM_ERR_FILE_NOT_FOUND 0x001 12 | #define SPASM_ERR_NO_ACCESS 0x002 13 | 14 | #define SPASM_ERR_LOCAL_LABEL_FORWARD_REF 0x100 15 | #define SPASM_ERR_LOCAL_LABEL_SYNTAX 0x101 16 | #define SPASM_ERR_ARG_USED_WITHOUT_VALUE 0x102 17 | #define SPASM_ERR_OPERATOR_EXPECTED 0x103 18 | #define SPASM_ERR_VALUE_EXPECTED 0x104 19 | #define SPASM_ERR_BAD_VALUE_PREFIX 0x105 20 | #define SPASM_ERR_LABEL_NOT_FOUND 0x106 21 | #define SPASM_ERR_INVALID_ADDRESS 0x107 22 | #define SPASM_ERR_SIZE_MUST_BE_POSITIVE 0x108 23 | #define SPASM_ERR_FILENAME_EXPECTED 0x109 24 | #define SPASM_ERR_INVALID_OPERANDS 0x110 25 | #define SPASM_ERR_UNKNOWN_PREOP 0x111 26 | #define SPASM_ERR_UNKNOWN_DIRECTIVE 0x112 27 | #define SPASM_ERR_UNKNOWN_OPCODE 0x113 28 | #define SPASM_ERR_EQUATE_MISSING_LABEL 0x114 29 | #define SPASM_ERR_EXCEEDED_RECURSION_LIMIT 0x115 30 | #define SPASM_ERR_UNMATCHED_IF 0x116 31 | #define SPASM_ERR_STRAY_PREOP 0x117 32 | #define SPASM_ERR_EXPRESSION_EXPECTED 0x118 33 | #define SPASM_ERR_SYNTAX 0x119 34 | #define SPASM_ERR_JUMP_EXCEEDED 0x120 35 | #define SPASM_ERR_INDEX_OFFSET_EXCEEDED 0x121 36 | #define SPASM_ERR_NAME_EXPECTED 0x122 37 | #define SPASM_ERR_NO_PREVIOUS_DEFINE 0x123 38 | #define SPASM_ERR_ELIF_WITHOUT_IF 0x124 39 | #define SPASM_ERR_INVALID_OPTION 0x125 40 | #define SPASM_ERR_INVALID_ADDINSTR 0x126 41 | #define SPASM_ERR_INVALID_RST_OPERANDS 0x127 42 | #define SPASM_ERR_DEFINE_HAS_NO_VALUE 0x128 43 | #define SPASM_ERR_RECURSION_DEPTH 0x129 44 | #define SPASM_ERR_LABEL_CONFLICT 0x130 45 | #define SPASM_ERR_INVALID_INDEX_OFFSET 0x131 46 | 47 | #define SPASM_ERR_INVALID_DECIMAL_DIGIT 0x200 48 | #define SPASM_ERR_INVALID_HEX_DIGIT 0x201 49 | #define SPASM_ERR_INVALID_BINARY_DIGIT 0x202 50 | #define SPASM_ERR_DIVIDE_BY_ZERO 0x203 51 | #define SPASM_ERR_INTEGER_OVERFLOW 0x204 52 | #define SPASM_ERR_DIVIDE_IMIN1 0x205 53 | 54 | #define SPASM_ERR_FCREATE_NOFILE 0x301 55 | 56 | #define SPASM_ERR_SIGNER_MISSING_LENGTH 0x501 57 | #define SPASM_ERR_SIGNER_PRGM_TYPE 0x502 58 | #define SPASM_ERR_SIGNER_MISSING_PAGES 0x503 59 | #define SPASM_ERR_SIGNER_MISSING_NAME 0x504 60 | #define SPASM_ERR_SIGNER_ROOM_FOR_SIG 0x505 61 | 62 | 63 | #define SPASM_ERR_CUSTOM 0x600 64 | 65 | #define SPASM_WARN_TRUNCATING_8 0x800 66 | #define SPASM_WARN_TRUNCATING_16 0x801 67 | #define SPASM_WARN_SMALL_LAST_PAGE 0x802 68 | #define SPASM_WARN_UNKNOWN_EXTENSION 0x803 69 | #define SPASM_WARN_MACRO_TOO_MANY_ARGS 0x804 70 | #define SPASM_WARN_LABEL_OVER_DEFINE 0x805 71 | 72 | 73 | #define SPASM_WARN_SIGNER_FILE_SIZE_64KB 0x550 74 | #define SPASM_WARN_SIGNER_FILE_SIZE_24KB 0x551 75 | 76 | #ifndef _ERRORS_CPP 77 | extern 78 | #endif 79 | SPASMERROR g_ErrorCodes[] 80 | #ifdef _ERRORS_CPP 81 | = 82 | { 83 | {SPASM_ERR_FILE_NOT_FOUND, _T("Could not find the file '%s'")}, 84 | {SPASM_ERR_NO_ACCESS, _T("Could not access the file '%s'")}, 85 | 86 | {SPASM_ERR_LOCAL_LABEL_SYNTAX, _T("Error in local label's syntax (had leading +/- but no _)")}, 87 | {SPASM_ERR_LOCAL_LABEL_FORWARD_REF, _T("Reference to local label which doesn't exist")}, 88 | {SPASM_ERR_ARG_USED_WITHOUT_VALUE, _T("Argument '%s' used without value")}, 89 | {SPASM_ERR_OPERATOR_EXPECTED, _T("Expecting an operator, found '%c' instead")}, 90 | {SPASM_ERR_VALUE_EXPECTED, _T("Expecting a value, expression ended early")}, 91 | {SPASM_ERR_LABEL_NOT_FOUND, _T("Could not find label or macro '%s'")}, 92 | {SPASM_ERR_BAD_VALUE_PREFIX, _T("Unrecognized value prefix '%c'")}, 93 | {SPASM_ERR_INVALID_ADDRESS, _T("The value '%s' is not a valid Z80 address")}, 94 | {SPASM_ERR_SIZE_MUST_BE_POSITIVE, _T("The value '%s' is a size and must be positive")}, 95 | {SPASM_ERR_FILENAME_EXPECTED, _T("Expecting a filename, none was provided")}, 96 | {SPASM_ERR_INVALID_OPERANDS, _T("The opcode %s was given invalid operands")}, 97 | {SPASM_ERR_INVALID_RST_OPERANDS, _T("The opcode rst was given invalid operands")}, 98 | {SPASM_ERR_UNKNOWN_PREOP, _T("Unknown preprocessor command '#%s'")}, 99 | {SPASM_ERR_UNKNOWN_DIRECTIVE, _T("Unknown assembler directive '.%s'")}, 100 | {SPASM_ERR_UNKNOWN_OPCODE, _T("Unknown opcode '%s'")}, 101 | {SPASM_ERR_EQUATE_MISSING_LABEL, _T("Equate is missing corresponding label")}, 102 | {SPASM_ERR_EXCEEDED_RECURSION_LIMIT,_T("Recursion depth limit exceeded")}, 103 | {SPASM_ERR_EXPRESSION_EXPECTED, _T("Expecting an expression, statement ended early")}, 104 | {SPASM_ERR_SYNTAX, _T("Unknown syntax")}, 105 | {SPASM_ERR_JUMP_EXCEEDED, _T("Relative jump distance exceeded (distance %d)")}, 106 | {SPASM_ERR_INDEX_OFFSET_EXCEEDED, _T("Index register offset exceeded (offset %d)")}, 107 | {SPASM_ERR_NAME_EXPECTED, _T("Expecting a name, expression ended early")}, 108 | {SPASM_ERR_NO_PREVIOUS_DEFINE, _T("No previous define to continue")}, 109 | {SPASM_ERR_ELIF_WITHOUT_IF, _T("Use of #ELIF outside of an #IF expression")}, 110 | {SPASM_ERR_INVALID_OPTION, _T("The option %s does not exist")}, 111 | {SPASM_ERR_INVALID_ADDINSTR, _T("Required information for .ADDINSTR is missing or invalid")}, 112 | {SPASM_ERR_INVALID_RST_OPERANDS, _T("Invalid operands for the RST command")}, 113 | {SPASM_ERR_DEFINE_HAS_NO_VALUE, _T("The define '%s' has been used, but doesn't have a value")}, 114 | {SPASM_ERR_RECURSION_DEPTH, _T("Expression is too deep (only %d levels allowed)")}, 115 | {SPASM_ERR_LABEL_CONFLICT, _T("Conflicting definition of '%s' at %s:%d")}, 116 | {SPASM_ERR_INVALID_INDEX_OFFSET, _T("Index offset is expected to begin with '+' or '-' or be missing")}, 117 | 118 | {SPASM_ERR_INVALID_DECIMAL_DIGIT, _T("Invalid digit '%c' in the decimal number '%s'")}, 119 | {SPASM_ERR_INVALID_HEX_DIGIT, _T("Invalid digit '%c' in the hexadecimal number '%s'")}, 120 | {SPASM_ERR_INVALID_BINARY_DIGIT, _T("Invalid digit '%c' in the binary number '%s'")}, 121 | {SPASM_ERR_DIVIDE_BY_ZERO, _T("Division by zero in evaluation of expression '%s'")}, 122 | {SPASM_ERR_INTEGER_OVERFLOW, _T("Magnitude of literal value '%s' is too large")}, 123 | {SPASM_ERR_DIVIDE_IMIN1, _T("Division of INT_MIN by -1 in expression '%s' is unrepresentable")}, 124 | 125 | {SPASM_ERR_FCREATE_NOFILE, _T("No buffer was selected for the fcreate call")}, 126 | {SPASM_ERR_UNMATCHED_IF, _T("Unbalanced #IF/#ENDIF")}, 127 | {SPASM_ERR_STRAY_PREOP, _T("Stray #%s")}, 128 | 129 | {SPASM_ERR_SIGNER_MISSING_LENGTH, _T("Length field missing")}, 130 | {SPASM_ERR_SIGNER_PRGM_TYPE, _T("Program type field missing or incorrect")}, 131 | {SPASM_ERR_SIGNER_MISSING_PAGES, _T("Page count field missing")}, 132 | {SPASM_ERR_SIGNER_MISSING_NAME, _T("Name field missing")}, 133 | {SPASM_ERR_SIGNER_ROOM_FOR_SIG, _T("Not enough room for signature on last page")}, 134 | 135 | {SPASM_ERR_CUSTOM, _T("%s")}, 136 | 137 | {SPASM_WARN_TRUNCATING_8, _T("Value too large for 8-bits, truncation required")}, 138 | {SPASM_WARN_TRUNCATING_16, _T("Value too large for 16-bits, truncation required")}, 139 | {SPASM_WARN_SMALL_LAST_PAGE, _T("Only %d bytes are used on the last APP page")}, 140 | {SPASM_WARN_UNKNOWN_EXTENSION, _T("Unrecognized file extension, assuming binary")}, 141 | {SPASM_WARN_MACRO_TOO_MANY_ARGS, _T("Macro '%s' was given too many arguments, ignoring extras")}, 142 | {SPASM_WARN_LABEL_OVER_DEFINE, _T("Label %s was used instead define %s")}, 143 | 144 | {SPASM_WARN_SIGNER_FILE_SIZE_24KB, _T("The output file is larger than 24KB")}, 145 | {SPASM_WARN_SIGNER_FILE_SIZE_64KB, _T("The output file is larger than 64KB")}, 146 | } 147 | #endif 148 | ; 149 | 150 | void SetLastSPASMError(DWORD dwErrorCode, ...); 151 | void SetLastSPASMWarning(DWORD dwErrorCode, ...); 152 | //DWORD GetLastSPASMError(); 153 | int StartSPASMErrorSession(void); 154 | int GetSPASMErrorSessionErrorCount(int nSession); 155 | bool IsSPASMErrorSessionFatal(int nSession); 156 | void ReplaySPASMErrorSession(int nSession, bool fFatalOnly = false); 157 | void ReplayFatalSPASMErrorSession(int nSession); 158 | void EndSPASMErrorSession(int errors); 159 | void ClearSPASMErrorSessions(); 160 | int CleanupSPASMErrorSession(int nSession); 161 | void AddSPASMErrorSessionAnnotation(int nSession, LPCTSTR lpszFormat, ...); 162 | bool IsErrorInSPASMErrorSession(int nSession, DWORD dwErrorCode); 163 | void FreeSPASMErrorSessions(void); 164 | #ifdef _TEST 165 | DWORD GetLastSPASMError(); 166 | int GetLastSPASMErrorLine(); 167 | #endif 168 | -------------------------------------------------------------------------------- /expand_buf.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "spasm.h" 4 | 5 | #include "expand_buf.h" 6 | #include "utils.h" 7 | 8 | /* 9 | * Initializes an expandable buffer with a 10 | * certain starting size 11 | */ 12 | //TODO: the size increase is exponential, I am not sure an init_size is necessary 13 | expand_buf *eb_init (size_t init_size) { 14 | expand_buf_t *new_buf = (expand_buf_t *) malloc (sizeof (expand_buf_t)); 15 | 16 | if (init_size == -1) init_size = 64; 17 | 18 | new_buf->start = (unsigned char *) malloc(init_size); 19 | new_buf->end = new_buf->start; 20 | new_buf->size = init_size; 21 | 22 | return new_buf; 23 | } 24 | 25 | 26 | int eb_append(expand_buf_t *eb, const char *text, size_t len) { 27 | return eb_insert(eb, -1, text, len); 28 | } 29 | 30 | /* 31 | * Writes text to an expandable buffer at 32 | * offset in buffer (-1 to append), and 33 | * expands if necessary, given length or 34 | * -1 to find length internally, returns 35 | * new offset 36 | */ 37 | 38 | int eb_insert (expand_buf *buf, int offset, const char *text, size_t length) { 39 | unsigned char *ptr; 40 | 41 | if (text == NULL) 42 | return offset; 43 | 44 | if (offset == -1) 45 | ptr = buf->end; 46 | else 47 | ptr = buf->start + offset; 48 | 49 | if (length == -1) 50 | length = strlen (text); 51 | 52 | if (length == 0) 53 | return 0; 54 | 55 | //if the string's too long for the buffer to hold, expand it 56 | if ((size_t) (buf->end + length - buf->start) > buf->size) { 57 | unsigned char *old_start = buf->start; 58 | if (buf->size == 0) 59 | buf->size = 1; 60 | while ((size_t) (buf->end + length - buf->start) > buf->size) 61 | buf->size *= 2; 62 | buf->start = (unsigned char *) realloc (buf->start, buf->size); 63 | if (buf->start == NULL) { 64 | puts ("Expand buf out of memory."); 65 | exit (1); 66 | } 67 | ptr += buf->start - old_start; 68 | buf->end += buf->start - old_start; 69 | } 70 | 71 | //copy the string to the buffer and move the pointer forward 72 | if (ptr < buf->end) { 73 | //if necessary, move the end of the buffer forwards first 74 | unsigned char *curr_ptr; 75 | for (curr_ptr = buf->end - 1; curr_ptr >= ptr; curr_ptr--) 76 | *(curr_ptr + length) = *curr_ptr; 77 | } 78 | strncpy ((char *) ptr, text, length); 79 | buf->end += length; 80 | 81 | return offset + length; 82 | } 83 | 84 | 85 | /* 86 | * Overwrites text in expandable buffer 87 | * starting at offset - DOES NOT 88 | * do length checking! 89 | */ 90 | 91 | void eb_overwrite (expand_buf *buf, int offset, const char *text, size_t length) { 92 | unsigned char *ptr = buf->start + offset; 93 | 94 | if (length == -1) 95 | length = strlen (text); 96 | 97 | strncpy ((char *) ptr, text, length); 98 | } 99 | 100 | 101 | /* 102 | * Erases text from buffer 103 | */ 104 | 105 | void eb_erase (expand_buf *buf, int offset, size_t length) { 106 | unsigned char *ptr, *curr_ptr; 107 | ptr = buf->start + offset; 108 | for (curr_ptr = ptr; curr_ptr < ptr + length && curr_ptr + length < buf->end; curr_ptr++) 109 | *curr_ptr = *(curr_ptr + length); 110 | 111 | buf->end -= length; 112 | } 113 | 114 | 115 | /* 116 | * Gets the char from the buffer 117 | * at given offset 118 | */ 119 | 120 | char eb_get_char (expand_buf *buf, int offset) { 121 | return *(buf->start + offset); 122 | } 123 | 124 | 125 | /* 126 | * Extracts the contents of the buffer as 127 | * a zero-terminated string 128 | */ 129 | 130 | char *eb_extract (expand_buf *buf) { 131 | return strndup ((char *) buf->start, buf->end - buf->start); 132 | } 133 | 134 | 135 | /* 136 | * Frees a buffer 137 | */ 138 | 139 | void eb_free (expand_buf *eb) { 140 | free (eb->start); 141 | free (eb); 142 | } 143 | 144 | -------------------------------------------------------------------------------- /expand_buf.h: -------------------------------------------------------------------------------- 1 | #ifndef __EXPAND_BUF_H 2 | #define __EXPAND_BUF_H 3 | 4 | typedef struct { 5 | unsigned char *start; 6 | unsigned char *end; 7 | size_t size; 8 | } expand_buf, expand_buf_t; 9 | 10 | expand_buf *eb_init (size_t init_size); 11 | int eb_append(expand_buf_t *buf, const char *text, size_t len); 12 | int eb_insert (expand_buf *buf, int offset, const char *text, size_t length); 13 | void eb_overwrite (expand_buf *buf, int offset, const char *text, size_t length); 14 | void eb_erase (expand_buf *buf, int offset, size_t length); 15 | char eb_get_char (expand_buf *buf, int offset); 16 | char *eb_extract (expand_buf *buf); 17 | 18 | void eb_free (expand_buf *buf); 19 | 20 | #endif 21 | 22 | -------------------------------------------------------------------------------- /hash.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "spasm.h" 4 | #include "hash.h" 5 | #include "storage.h" 6 | 7 | hash_t::hash_t(void (*remove_callback)(void *)) : htt_base() { 8 | this->remove_callback = remove_callback; 9 | } 10 | 11 | /* 12 | * Creates a new hash table with an upper bound size 13 | */ 14 | hash_t *hash_init (void remove_callback(void *)) { 15 | hash_t *ht = new hash_t(remove_callback); 16 | return ht; 17 | } 18 | 19 | 20 | /* 21 | * Inserts a value into a hash table. 22 | * 23 | * `store` is the value to be taken, where its first element is assumed 24 | * to be a store_t providing the key. 25 | */ 26 | void hash_insert (hash_t *ht, void *store) { 27 | store_t *key = (store_t *)store; 28 | (*ht)[key->name] = store; 29 | } 30 | 31 | 32 | /* 33 | * Looks up a value from a hash table 34 | * returns NULL if not found 35 | */ 36 | void *hash_lookup (hash_t *ht, const char *name) { 37 | hash_t::iterator location = ht->find(name); 38 | if (location == ht->end()) { 39 | return NULL; 40 | } else { 41 | return location->second; 42 | } 43 | } 44 | 45 | 46 | /* 47 | * Removes a hash from a hash table 48 | * 49 | * Returns 1 on success, or 0 if the name doesn't exist. 50 | */ 51 | int hash_remove (hash_t *ht, const char *name) { 52 | hash_t::iterator location = ht->find(name); 53 | if (location == ht->end()) { 54 | return 0; 55 | } 56 | 57 | ht->remove_callback(location->second); 58 | ht->erase(location); 59 | return 1; 60 | } 61 | 62 | void hash_enum (hash_t *ht, void enum_callback(void *, void *), void *arg) { 63 | for (hash_t::iterator i = ht->begin(); i != ht->end(); i++) { 64 | enum_callback(i->second, arg); 65 | } 66 | } 67 | 68 | int hash_count (hash_t *ht) { 69 | return ht->size(); 70 | } 71 | 72 | /* 73 | * Free a hash table, removing elements 74 | */ 75 | void hash_free (hash_t *ht) { 76 | // Invoke all the remove callbacks. 77 | // This invalidates all of the map's contents. 78 | for (hash_t::iterator i = ht->begin(); i != ht->end(); i++) { 79 | ht->remove_callback(i->second); 80 | } 81 | delete ht; 82 | } 83 | -------------------------------------------------------------------------------- /hash.h: -------------------------------------------------------------------------------- 1 | #ifndef HASH_H_ 2 | #define HASH_H_ 3 | 4 | #include 5 | #include 6 | #include "list.h" 7 | 8 | typedef struct { 9 | char *name; 10 | } store_t; 11 | 12 | struct comparator 13 | : public std::binary_function 14 | { 15 | bool operator()(const char * _Left, const char *_Right) const 16 | { 17 | if (strcmp(_Left, _Right) < 0) 18 | return true; 19 | else 20 | return false; 21 | } 22 | }; 23 | 24 | typedef std::map htt_base; 25 | 26 | class hash_t : public htt_base { 27 | public: 28 | void (*remove_callback)(void *); 29 | 30 | hash_t(void (*remove_callback)(void *)); 31 | }; 32 | 33 | typedef void (*HASH_ENUM_CALLBACK)(void *, void *); 34 | typedef void (*HASH_REMOVE_CALLBACK)(void *); 35 | 36 | hash_t *hash_init (void remove_callback(void *)); 37 | void hash_insert (hash_t *ht, void *store); 38 | void *hash_lookup (hash_t *ht, const char *name); 39 | int hash_remove (hash_t *ht, const char *name); 40 | void hash_enum (hash_t *ht, void enum_callback(void *, void *), void *arg); 41 | int hash_count (hash_t *ht); 42 | void hash_free (hash_t *ht); 43 | 44 | #endif /*HASH_H_*/ 45 | -------------------------------------------------------------------------------- /inc/app.inc: -------------------------------------------------------------------------------- 1 | ; Fatal error macro. Displays message prefixed with "error" 2 | ; and returns with error code 3 | #define fatal(str,return) .error str 4 | #macro defpage(page, appname) 5 | #if page = 0 6 | #ifndef gpage 7 | #define free_space 0 8 | #define gpage 0 9 | #ifndef appname 10 | .echo "Warning: No name given, using \"Default\"" 11 | #define appname "Default" 12 | #endif 13 | .org $4000 14 | .db $80,$0F,0,0,0,0 15 | .db $80,$12,1,4 16 | .db $80,$21,1 17 | .db $80,$31,1 18 | .db $80,$48 19 | 20 | #define gappname eval(appname) 21 | 22 | _ .db appname 23 | #define apnamlen eval($ - -_) 24 | echo_fill(apnamlen,"-","------------------------------------------") 25 | .echo "Beginning APP \"",appname,"\", courtesy of SPASM macros" 26 | echo_fill(apnamlen,"-","------------------------------------------") 27 | 28 | #if apnamlen <= 8 29 | .fill 8-apnamlen,' ' 30 | #else 31 | #define err "App name is ",apnamlen-8," character(s) too long." 32 | fatal(err,1) 33 | #endif 34 | 35 | .db $80,$81,0 36 | .db $80,$90 37 | .db $03,$26,$09,$04,$04,$06f,$1b,$80 38 | .db $02,$0d,$40 39 | .db $a1,$6b,$99,$f6,$59,$bc,$67 40 | .db $f5,$85,$9c,$09,$6c,$0f,$b4,$03,$9b,$c9 41 | .db $03,$32,$2c,$e0,$03,$20,$e3,$2c,$f4,$2d 42 | .db $73,$b4,$27,$c4,$a0,$72,$54,$b9,$ea,$7c 43 | .db $3b,$aa,$16,$f6,$77,$83,$7a,$ee,$1a,$d4 44 | .db $42,$4c,$6b,$8b,$13,$1f,$bb,$93,$8b,$fc 45 | .db $19,$1c,$3c,$ec,$4d,$e5,$75 46 | .db $80,$7F,0,0,0,0 47 | .dw 0,0,0,0,0,0,0,0 48 | #else 49 | fatal("There was already a page 0.",1) 50 | #endif 51 | #else 52 | #if ($ & $0000FFFF) > $8000 53 | #define err "Page ",gpage," went over bounds by ",$-$8000," bytes." 54 | fatal(err, 1) 55 | #endif 56 | #if page <= gpage 57 | fatal("Your page numbers must increase.") 58 | #endif 59 | #if page > gpage+1 60 | .echo "Warning: Skipping page ",gpage+1," at user request." 61 | #endif 62 | #define free_space eval(free_space + $8000-($ & $0000FFFF)) 63 | .echo "Page ",gpage," free space: ",$8000-($ & $0000FFFF) 64 | .block ($8000-($ & $0000FFFF) + ((page-gpage-1)*$4000)) 65 | #define gpage eval(page) 66 | .org $4000+(gpage*$10000) 67 | #endif 68 | #endmacro 69 | 70 | #macro validate 71 | #if ($ & $0000FFFF) > $8000 72 | #define err "Page ",gpage," went over bounds by ",$-$8000," bytes." 73 | fatal(err, 1) 74 | #else 75 | .echo "Page ",gpage," free space: ",$8000-($ & $0000FFFF) 76 | #define free_space eval(free_space + $8000 - ($ & $0000FFFF)) 77 | .echo "Success: Page sizes validated and \"",gappname,"\" is ready for signing." 78 | .echo " In ",gpage+1," page(s), ",free_space," bytes are available." 79 | #endif 80 | #endmacro 81 | 82 | #macro echo_fill(times, char, base) 83 | #if times > 0 84 | #define base base,char 85 | echo_fill(eval(times-1), char, base) 86 | #else 87 | .echo base 88 | #endif 89 | #endmacro 90 | 91 | #define .defpage defpage( 92 | #define .validate validate -------------------------------------------------------------------------------- /inc/arrays.inc: -------------------------------------------------------------------------------- 1 | #IFNDEF ARRAY_INC 2 | #DEFINE ARRAY_INC 3 | 4 | #macro new_array(name) 5 | #define str_name concat("\"",name,"\"") 6 | 7 | clr() 8 | wr("#define ",str_name,"(ind) #define zind concat(\"ind\")\\ clr()\\ wr(\"#define ",str_name,"(ind) \\\\ #define zind concat(\\\"ind\\\")\\ #if zind=\",zind,\"\\\\ ",str_name,"\",zind,\"\\\\ #else\\\\",str_name," #endif\\\\\")\\ wr(\"\\\\",str_name,"\",zind)\\ #undefine zind\\ run()\\\") 9 | run() 10 | #endmacro 11 | 12 | #ENDIF 13 | -------------------------------------------------------------------------------- /inc/relocate.inc: -------------------------------------------------------------------------------- 1 | #macro relocate(new_location) 2 | #ifdef old_location 3 | .echo "Zelda: ",__file,":",__line,": error: You cannot nest relocate blocks." 4 | #else 5 | #define old_location eval($) 6 | .org new_location 7 | #define g_location eval(new_location) 8 | #endif 9 | #endmacro 10 | 11 | #macro endrelocate() 12 | #ifdef g_location 13 | .org $-g_location + old_location 14 | #undefine g_location 15 | #undefine old_location 16 | #else 17 | .echo "Error line ",__line,": No relocate statements corresponds to this endrelocate." 18 | #endif 19 | #endmacro -------------------------------------------------------------------------------- /inc/z80ext.inc: -------------------------------------------------------------------------------- 1 | .nolist 2 | .ADDINSTR ADC A,(HL+) 238E 2 3 | .ADDINSTR ADC A,(HL-) 2B8E 2 4 | .ADDINSTR ADD A,(HL+) 2386 2 5 | .ADDINSTR ADD A,(HL-) 2B86 2 6 | .ADDINSTR AND (HL+) 23A6 2 7 | .ADDINSTR AND (HL-) 2BA6 2 8 | .ADDINSTR DEC (HL+) 2335 2 9 | .ADDINSTR DEC (HL-) 2B35 2 10 | .ADDINSTR INC (HL+) 2334 2 11 | .ADDINSTR INC (HL-) 2B34 2 12 | .ADDINSTR LD (BC),A 02 1 NOP 1 13 | .ADDINSTR LD (BC+),A 0302 2 NOP 1 14 | .ADDINSTR LD (BC-),A 0B02 2 NOP 1 15 | .ADDINSTR LD (DE),A 12 1 NOP 1 16 | .ADDINSTR LD (DE+),A 1312 2 NOP 1 17 | .ADDINSTR LD (DE-),A 1B12 2 NOP 1 18 | 19 | .ADDINSTR LD (HL+),* 36 3 NOP 1 23 20 | .ADDINSTR LD (HL-),* 36 3 NOP 1 2B 21 | .ADDINSTR LD (HL),A 77 1 NOP 1 22 | .ADDINSTR LD (HL+),A 2377 2 NOP 1 23 | .ADDINSTR LD (HL-),A 2B77 2 NOP 1 24 | .ADDINSTR LD (HL),B 70 1 NOP 1 25 | .ADDINSTR LD (HL+),B 2370 2 NOP 1 26 | .ADDINSTR LD (HL-),B 2B70 2 NOP 1 27 | .ADDINSTR LD (HL),C 71 1 NOP 1 28 | .ADDINSTR LD (HL+),C 2371 2 NOP 1 29 | .ADDINSTR LD (HL-),C 2B71 2 NOP 1 30 | .ADDINSTR LD (HL),D 72 1 NOP 1 31 | .ADDINSTR LD (HL+),D 2372 2 NOP 1 32 | .ADDINSTR LD (HL-),D 2B72 2 NOP 1 33 | .ADDINSTR LD (HL),E 73 1 NOP 1 34 | .ADDINSTR LD (HL+),E 2373 2 NOP 1 35 | .ADDINSTR LD (HL-),E 2B73 2 NOP 1 36 | .ADDINSTR LD (HL),H 74 1 NOP 1 37 | .ADDINSTR LD (HL),L 75 1 NOP 1 38 | 39 | .ADDINSTR LD A,(BC+) 030A 2 NOP 1 40 | .ADDINSTR LD A,(BC-) 0B0A 2 NOP 1 41 | .ADDINSTR LD A,(HL+) 237E 2 NOP 1 42 | .ADDINSTR LD A,(HL-) 2B7E 2 NOP 1 43 | .ADDINSTR LD B,(HL+) 2346 2 NOP 1 44 | .ADDINSTR LD B,(HL-) 2B46 2 NOP 1 45 | .ADDINSTR LD C,(HL+) 234E 2 NOP 1 46 | .ADDINSTR LD C,(HL-) 2B4E 2 NOP 1 47 | .ADDINSTR LD D,(HL+) 2356 2 NOP 1 48 | .ADDINSTR LD D,(HL-) 2B56 2 NOP 1 49 | .ADDINSTR LD E,(HL+) 235E 2 NOP 1 50 | .ADDINSTR LD E,(HL-) 2B5E 2 NOP 1 51 | .ADDINSTR LD HL,A,(HL) 6F66237E 4 NOP 1 52 | .ADDINSTR OR (HL+) 23B6 2 NOP 1 53 | .ADDINSTR OR (HL-) 2BB6 2 NOP 1 54 | .ADDINSTR RL (HL+) 2316CB 3 NOP 1 55 | .ADDINSTR RL (HL-) 2B16CB 3 NOP 1 56 | .ADDINSTR RR (HL+) 231ECB 3 NOP 1 57 | .ADDINSTR RR (HL-) 2B1ECB 3 NOP 1 58 | .ADDINSTR RRC (HL+) 230ECB 3 NOP 1 59 | .ADDINSTR RRC (HL-) 2B0ECB 3 NOP 1 60 | .ADDINSTR SBC A,(HL+) 239E 2 NOP 1 61 | .ADDINSTR SBC A,(HL-) 2B9E 2 NOP 1 62 | .ADDINSTR SLA (HL+) 2326CB 3 NOP 1 63 | .ADDINSTR SLA (HL-) 2B26CB 3 NOP 1 64 | .ADDINSTR SL1 (HL+) 2336CB 3 NOP 1 65 | .ADDINSTR SL1 (HL-) 2B36CB 3 NOP 1 66 | .ADDINSTR SLL (HL+) 2336CB 3 NOP 1 67 | .ADDINSTR SLL (HL-) 2B36CB 3 NOP 1 68 | .ADDINSTR SRA (HL+) 232ECB 3 NOP 1 69 | .ADDINSTR SRA (HL-) 2B2ECB 3 NOP 1 70 | .ADDINSTR SRL (HL+) 233ECB 3 NOP 1 71 | .ADDINSTR SRL (HL-) 2B3ECB 3 NOP 1 72 | .ADDINSTR SUB (HL+) 2396 2 NOP 1 73 | .ADDINSTR SUB (HL-) 2B96 2 NOP 1 74 | .ADDINSTR XOR (HL+) 23AE 2 NOP 1 75 | .ADDINSTR XOR (HL-) 2BAE 2 NOP 1 76 | 77 | .ADDINSTR LD HL,BC 6069 2 NOP 1 78 | .ADDINSTR LD BC,HL 444D 2 NOP 1 79 | .ADDINSTR LD HL,DE 626B 2 NOP 1 80 | .ADDINSTR LD DE,HL 545d 2 NOP 1 81 | .ADDINSTR LD DE,BC 5059 2 NOP 1 82 | .ADDINSTR LD BC,DE 424B 2 NOP 1 83 | 84 | .list -------------------------------------------------------------------------------- /lib/mpir.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberthdev/spasm-ng/5f0786d38f064835be674d4b7df42969967bb73c/lib/mpir.lib -------------------------------------------------------------------------------- /lib/mpir.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberthdev/spasm-ng/5f0786d38f064835be674d4b7df42969967bb73c/lib/mpir.pdb -------------------------------------------------------------------------------- /lib/x64/mpir.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberthdev/spasm-ng/5f0786d38f064835be674d4b7df42969967bb73c/lib/x64/mpir.lib -------------------------------------------------------------------------------- /lib/x64/mpir.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberthdev/spasm-ng/5f0786d38f064835be674d4b7df42969967bb73c/lib/x64/mpir.pdb -------------------------------------------------------------------------------- /list.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "spasm.h" 4 | 5 | #include "list.h" 6 | #include "utils.h" 7 | 8 | /* 9 | * Adds a node to the end of the 10 | * list, returns the new start 11 | * of the list 12 | */ 13 | 14 | EXPORT list_t *list_append (list_t *first_node, void *data) { 15 | list_t *curr_node, *new_node; 16 | 17 | new_node = (list_t *)malloc (sizeof (list_t)); 18 | new_node->data = data; 19 | new_node->next = NULL; 20 | 21 | if (first_node == NULL) 22 | return new_node; 23 | 24 | curr_node = first_node; 25 | while (curr_node->next) 26 | curr_node = curr_node->next; 27 | 28 | curr_node->next = new_node; 29 | return first_node; 30 | } 31 | 32 | 33 | /* 34 | * Adds a node to the beginning 35 | * of the list, returns the new 36 | * start of the list 37 | */ 38 | 39 | EXPORT list_t *list_prepend (list_t *first_node, void *data) { 40 | list_t *new_node; 41 | 42 | new_node = (list_t *)malloc (sizeof (list_t)); 43 | new_node->data = data; 44 | new_node->next = first_node; 45 | return new_node; 46 | } 47 | 48 | 49 | /* 50 | * Inserts a node after prev 51 | */ 52 | EXPORT list_t *list_insert(list_t *prev, void *data) { 53 | list_t *new_next; 54 | 55 | new_next = (list_t *) malloc(sizeof(list_t)); 56 | new_next->data = data; 57 | new_next->next = prev->next; 58 | 59 | prev->next = new_next; 60 | return new_next; 61 | } 62 | 63 | /* 64 | * Removes a node from the list, 65 | * returns the new start of the 66 | * list 67 | */ 68 | 69 | EXPORT list_t *list_remove (list_t *first_node, list_t *remove_node) { 70 | list_t *curr_node; 71 | 72 | // if the node to remove is the first one, then it's easy 73 | if (first_node == remove_node) 74 | return first_node->next; 75 | 76 | // otherwise, find the node before the node to remove 77 | curr_node = first_node; 78 | while (curr_node->next != remove_node && curr_node != NULL) 79 | curr_node = curr_node->next; 80 | 81 | // then move around pointers 82 | if (curr_node != NULL) 83 | curr_node->next = remove_node->next; 84 | return first_node; 85 | } 86 | 87 | 88 | /* 89 | * Frees a list node 90 | */ 91 | 92 | EXPORT void list_free_node (list_t *first_node) { 93 | if (first_node) 94 | free (first_node); 95 | } 96 | 97 | 98 | /* 99 | * Frees an entire list 100 | */ 101 | 102 | EXPORT void list_free (list_t *curr_node, bool free_data, void (*free_callback)(void *)) { 103 | 104 | while (curr_node) { 105 | list_t *next = curr_node->next; 106 | if (free_data && curr_node->data) 107 | { 108 | if (free_callback != NULL) 109 | { 110 | free_callback(curr_node->data); 111 | } 112 | else 113 | { 114 | free(curr_node->data); 115 | } 116 | } 117 | 118 | list_free_node(curr_node); 119 | curr_node = next; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIST_H 2 | #define __LIST_H 3 | 4 | typedef struct _list { 5 | void *data; 6 | struct _list *next; 7 | } list_t; 8 | 9 | #ifdef _WIN32 10 | #define EXPORT __declspec(dllexport) 11 | #else 12 | #define EXPORT 13 | #endif 14 | 15 | EXPORT list_t *list_insert(list_t *, void *); 16 | EXPORT list_t *list_append (list_t *first_node, void *data); 17 | EXPORT list_t *list_prepend (list_t *first_node, void *data); 18 | EXPORT list_t *list_remove (list_t *first_node, list_t *remove_node); 19 | EXPORT void list_free_node (list_t *first_node); 20 | EXPORT void list_free (list_t *curr_node, bool free_data, void (*free_callback)(void *)); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #define __MAIN_C 4 | 5 | #include "pass_one.h" 6 | #include "pass_two.h" 7 | #include "opcodes.h" 8 | #include "storage.h" 9 | #include "spasm.h" 10 | #include "utils.h" 11 | #include "console.h" 12 | #include "errors.h" 13 | #include "Module.h" 14 | 15 | #define LISTING_BUF_SIZE 65536 //initial size of buffer for output listing 16 | 17 | void write_file (const unsigned char *, int, const char *); 18 | 19 | extern expr_t *expr_list, *expr_list_tail; 20 | extern output_t *output_list, *output_list_tail; 21 | 22 | #ifdef _WINDOWS 23 | #ifdef SPASM_NG_ENABLE_COM 24 | CSPASMModule _AtlModule; 25 | #endif 26 | #endif 27 | 28 | /* 29 | * Must have mode set 30 | * Must have input file name 31 | * Must have output file name 32 | * Must have storage initialized 33 | * 34 | * After those conditions are met, go for it! 35 | */ 36 | 37 | int run_assembly() 38 | { 39 | #ifdef _WIN32 40 | struct _timeb time_start, time_end; 41 | _ftime(&time_start); 42 | #else 43 | struct timeb time_start, time_end; 44 | ftime(&time_start); 45 | #endif 46 | exit_code = EXIT_NORMAL; 47 | 48 | /*extern int generic_map[256]; 49 | ZeroMemory(generic_map, 256);*/ 50 | program_counter = 0x0000; 51 | stats_codesize = stats_datasize = stats_mintime = stats_maxtime = 0; 52 | last_label = NULL; 53 | error_occurred = false; 54 | listing_offset = 0; 55 | listing_for_line_done = false; 56 | line_start = NULL; 57 | old_line_num = 0; 58 | in_macro = 0; 59 | line_num = 0; 60 | #ifdef USE_BUILTIN_FCREATE 61 | cur_buf = 0; 62 | #endif 63 | #ifdef USE_REUSABLES 64 | curr_reusable = 0; 65 | total_reusables = 0; 66 | #endif 67 | 68 | expr_list = NULL; 69 | expr_list_tail = NULL; 70 | output_list = NULL; 71 | output_list_tail = NULL; 72 | 73 | assert(curr_input_file != NULL); 74 | 75 | //read in the input file 76 | if (!(mode & MODE_COMMANDLINE)) 77 | input_contents = (char *) get_file_contents (curr_input_file); 78 | 79 | if (!input_contents) { 80 | puts ("Couldn't open input file"); 81 | return EXIT_FATAL_ERROR; 82 | } 83 | 84 | out_ptr = output_contents; 85 | 86 | //along with the listing buffer, if required 87 | if ((mode & MODE_LIST)) { 88 | listing_buf = eb_init (LISTING_BUF_SIZE); 89 | listing_offset = 0; 90 | listing_on = true; 91 | } 92 | 93 | //find the path of the input file 94 | if (is_abs_path(curr_input_file)) { 95 | int i; 96 | 97 | strcpy(temp_path, curr_input_file); 98 | 99 | for (i = strlen(temp_path) - 1; 100 | temp_path[i] != '\\' && temp_path[i] != '/' && i > 0; i--); 101 | if (i >= 0) 102 | temp_path[i] = '\0'; 103 | else 104 | strcpy(temp_path, "."); 105 | } else { 106 | #ifdef WIN32 107 | _getcwd(temp_path, sizeof (temp_path)); 108 | #else 109 | getcwd(temp_path, sizeof (temp_path)); 110 | #endif 111 | } 112 | 113 | //add the the input file's path to the include directories 114 | include_dirs = list_prepend (include_dirs, strdup (temp_path)); 115 | 116 | printf ("Pass one... \n"); 117 | 118 | int first_pass_session = StartSPASMErrorSession(); 119 | run_first_pass ((char *) input_contents); 120 | ReplayFatalSPASMErrorSession(first_pass_session); 121 | EndSPASMErrorSession(first_pass_session); 122 | 123 | 124 | //free include dirs when done 125 | if ((mode & MODE_COMMANDLINE) == 0) 126 | { 127 | release_file_contents(input_contents); 128 | input_contents = NULL; 129 | } 130 | 131 | list_free (include_dirs, true, NULL); 132 | include_dirs = NULL; 133 | 134 | //...and if there's output, run the second pass and write it to the output file 135 | if (mode & MODE_SYMTABLE || mode & MODE_NORMAL || mode & MODE_LIST) 136 | { 137 | printf ("Pass two... \n"); 138 | int second_pass_session = StartSPASMErrorSession(); 139 | run_second_pass (); 140 | ReplaySPASMErrorSession(second_pass_session); 141 | EndSPASMErrorSession(second_pass_session); 142 | 143 | if (mode & MODE_SYMTABLE) { 144 | int symtable_session = StartSPASMErrorSession(); 145 | char* fileName = change_extension(output_filename, "lab"); 146 | write_labels (fileName); 147 | free(fileName); 148 | ReplayFatalSPASMErrorSession(symtable_session); 149 | EndSPASMErrorSession(symtable_session); 150 | } 151 | 152 | //run the output through the appropriate program export and write it to a file 153 | if (mode & MODE_NORMAL && output_filename != NULL) 154 | { 155 | int write_session = StartSPASMErrorSession(); 156 | write_file (output_contents, out_ptr - output_contents, output_filename); 157 | ReplayFatalSPASMErrorSession(write_session); 158 | EndSPASMErrorSession(write_session); 159 | } 160 | 161 | //write the listing file if necessary 162 | if ((mode & MODE_LIST)) { 163 | FILE *file; 164 | char *name; 165 | 166 | //get file name 167 | name = change_extension (output_filename, "lst"); 168 | file = fopen (name, "wb"); 169 | if (!file) { 170 | printf ("Couldn't open listing file '%s'", name); 171 | free (name); 172 | return EXIT_FATAL_ERROR; 173 | } 174 | free (name); 175 | 176 | if (fwrite (listing_buf->start, 1, listing_offset, file) != listing_offset) { 177 | puts ("Error writing to listing file"); 178 | fclose (file); 179 | return EXIT_FATAL_ERROR; 180 | } 181 | 182 | fclose (file); 183 | eb_free(listing_buf); 184 | listing_buf = NULL; 185 | } 186 | 187 | //free the output buffer and all the names of input files 188 | list_free(input_files, true, NULL); 189 | input_files = NULL; 190 | } 191 | 192 | //if there's info to be dumped, do that 193 | if (mode & MODE_CODE_COUNTER) { 194 | fprintf (stdout, "Size: %u\nMin. execution time: %u\nMax. execution time: %u\n", 195 | stats_codesize, stats_mintime, stats_maxtime); 196 | } 197 | 198 | if (mode & MODE_STATS) { 199 | fprintf(stdout, "Number of labels: %u\nNumber of defines: %u\nCode size: %u\nData size: %u\nTotal size: %u\n", 200 | get_num_labels (), get_num_defines (), stats_codesize, stats_datasize, stats_codesize + stats_datasize); 201 | } 202 | 203 | #ifdef _WIN32 204 | _ftime(&time_end); 205 | #else 206 | ftime(&time_end); 207 | #endif 208 | int s_diff = (int) (time_end.time - time_start.time); 209 | int ms_diff = time_end.millitm - time_start.millitm; 210 | if (ms_diff < 0) { 211 | ms_diff += 1000; 212 | s_diff -= 1; 213 | } else if (ms_diff > 1000) { 214 | ms_diff -= 1000; 215 | s_diff += 1; 216 | } 217 | printf("Assembly time: %0.3f seconds\n", (float) s_diff + ((float) ms_diff / 1000.0f)); 218 | return exit_code; 219 | } 220 | void print_help_message(void){ 221 | puts ("SPASM-ng Z80 Assembler by Spencer Putt and Don Straney"); 222 | printf ("Version %s (built on %s @ %s)\n", SPASM_NG_VERSION, __DATE__, __TIME__); 223 | #ifdef SPASM_NG_GITREV 224 | printf ("Git revision %s\n", SPASM_NG_GITREV); 225 | #endif 226 | #ifdef _M_X64 227 | puts ("64-bit Version"); 228 | #endif 229 | #ifdef NO_APPSIGN 230 | printf ("\nApp signing is NOT available in this build of SPASM.\n"); 231 | #endif 232 | puts ("\nspasm [options] \n"); 233 | puts ("Options:\n-E = Assemble eZ80 code\n-T = Generate code listing\n-C = Code counter mode\n-L = Symbol table mode\n-S = Stats mode\n-O = Don't write to output file"); 234 | puts ("-I [directory] = Add include directory\n-A = Labels are cAse-sensitive\n-D[=value] = Create a define 'name' [with 'value']"); 235 | puts ("-N = Don't use colors for messages"); 236 | puts ("-V = Pipe expression directly into assembly"); 237 | puts ("-H = Print this help message"); 238 | 239 | #if defined(_DEBUG) && defined(WIN32) 240 | if (IsDebuggerPresent()) 241 | { 242 | system("PAUSE"); 243 | } 244 | #endif 245 | exit(EXIT_NORMAL); 246 | } 247 | 248 | #if 0 249 | int CALLBACK WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCommandLine, int nCmdShow) 250 | { 251 | return _AtlModule.WinMain(SW_HIDE); 252 | } 253 | #else 254 | int main (int argc, char **argv) 255 | { 256 | int curr_arg = 1; 257 | bool case_sensitive = false; 258 | bool is_storage_initialized = false; 259 | 260 | use_colors = true; 261 | extern WORD user_attributes; 262 | user_attributes = save_console_attributes (); 263 | atexit (restore_console_attributes_at_exit); 264 | 265 | //if there aren't enough args, show info 266 | if (argc < 2) { 267 | print_help_message(); 268 | } 269 | 270 | //init stuff 271 | mode = MODE_NORMAL; 272 | in_macro = 0; 273 | 274 | //otherwise, get any options 275 | curr_input_file = strdup("Commandline"); 276 | char *starting_input_file = curr_input_file; 277 | 278 | while (curr_arg < argc) { 279 | if (argv[curr_arg][0] == '-' 280 | #ifdef _WINDOWS 281 | || argv[curr_arg][0] == '/' 282 | #endif 283 | ) 284 | { 285 | switch (argv[curr_arg][1]) 286 | { 287 | //args for different modes 288 | case 'O': 289 | mode = mode & (~MODE_NORMAL); 290 | break; 291 | case 'T': 292 | mode |= MODE_LIST; 293 | break; 294 | case 'C': 295 | mode |= MODE_CODE_COUNTER; 296 | break; 297 | case 'L': 298 | mode |= MODE_SYMTABLE; 299 | break; 300 | case 'S': 301 | mode |= MODE_STATS; 302 | break; 303 | case 'E': 304 | mode |= MODE_EZ80; 305 | all_opcodes = opcode_list_ez80; 306 | break; 307 | //handle no-colors flag 308 | case 'N': 309 | use_colors = false; 310 | break; 311 | //handle include files too 312 | case 'I': 313 | { 314 | char *dir, *p; 315 | //make sure there's another argument after it for the include path 316 | if (strlen(argv[curr_arg]) > 2) { 317 | dir = strdup (&argv[curr_arg][2]); 318 | } else { 319 | if (curr_arg >= argc - 1) { 320 | printf ("%s used without include path\n", argv[curr_arg]); 321 | break; 322 | } 323 | 324 | dir = strdup (argv[++curr_arg]); 325 | } 326 | 327 | for (p = strtok (dir, ";,"); p; p = strtok (NULL, ";,")) { 328 | include_dirs = list_append (include_dirs, strdup(p)); 329 | } 330 | free(dir); 331 | break; 332 | } 333 | //and the case-sensitive flag 334 | case 'A': 335 | case_sensitive = true; 336 | break; 337 | //handle adding defines 338 | case 'D': 339 | { 340 | char name[256]; 341 | char *ptr; 342 | define_t *define; 343 | 344 | if (!is_storage_initialized) 345 | { 346 | init_storage(); 347 | is_storage_initialized = true; 348 | } 349 | 350 | if (strlen (argv[curr_arg]) > 2) { 351 | ptr = &argv[curr_arg][2]; 352 | } else { 353 | if (curr_arg >= argc - 1) { 354 | printf ("%s used without define name", argv[curr_arg]); 355 | break; 356 | } 357 | 358 | ptr = argv[++curr_arg]; 359 | } 360 | 361 | read_expr (&ptr, name, "="); 362 | 363 | define = add_define (strdup (name), NULL); 364 | if (*skip_whitespace (++ptr) != '\0') 365 | define->contents = strdup (ptr); 366 | else 367 | set_define (define, "1", 1, false); 368 | break; 369 | } 370 | case 'V': 371 | { 372 | char *line; 373 | 374 | //check for something after -V 375 | if (strlen(argv[curr_arg]) > 2) { 376 | line = &argv[curr_arg][2]; 377 | } else { 378 | //if not lets fail 379 | if (curr_arg >= argc - 1) { 380 | printf ("%s used without a line to assemble\n", argv[curr_arg]); 381 | return EXIT_FATAL_ERROR; 382 | } 383 | line = argv[++curr_arg]; 384 | } 385 | 386 | mode |= MODE_COMMANDLINE; 387 | curr_input_file = strdup("-v"); 388 | input_contents = (char *) malloc (strlen(line) + 1 + 2); 389 | output_filename = change_extension (curr_input_file, "bin"); 390 | 391 | strcpy(input_contents, line); 392 | strcat(input_contents, "\n"); 393 | break; 394 | } 395 | case 'H': 396 | case 'h': 397 | print_help_message(); 398 | break; 399 | default: 400 | { 401 | fprintf (stderr, "Unrecognized option %s\n", argv[curr_arg]); 402 | #ifdef SPASM_NG_ENABLE_COM 403 | FreeConsole(); 404 | return _AtlModule.WinMain(SW_HIDE); 405 | #else 406 | return 1; 407 | #endif 408 | } 409 | 410 | } 411 | 412 | } else { 413 | //if it's not a flag, then it must be a filename 414 | if (curr_input_file && (curr_input_file != starting_input_file) && !output_filename) 415 | output_filename = strdup(argv[curr_arg]); 416 | else if ((!curr_input_file) || (curr_input_file == starting_input_file)) 417 | curr_input_file = strdup(argv[curr_arg]); 418 | 419 | } 420 | curr_arg++; 421 | } 422 | 423 | // Update case sensitivity settings 424 | set_case_sensitive (case_sensitive); 425 | 426 | //check on filenames 427 | if (!(mode & MODE_COMMANDLINE) && curr_input_file == starting_input_file) { 428 | puts ("No input file specified"); 429 | free(starting_input_file); 430 | return EXIT_FATAL_ERROR; 431 | } 432 | 433 | if (curr_input_file != starting_input_file) { 434 | free(starting_input_file); 435 | } 436 | 437 | if (!output_filename) { 438 | output_filename = change_extension (curr_input_file, "bin"); 439 | } 440 | 441 | if (!is_storage_initialized) 442 | { 443 | init_storage(); 444 | is_storage_initialized = true; 445 | } 446 | output_contents = (unsigned char *) malloc(output_buf_size); 447 | ClearSPASMErrorSessions(); 448 | 449 | int error = run_assembly(); 450 | 451 | free(output_filename); 452 | output_filename = NULL; 453 | if (curr_input_file) { 454 | free(curr_input_file); 455 | curr_input_file = NULL; 456 | } 457 | if (include_dirs) { 458 | list_free(include_dirs, true, NULL); 459 | } 460 | 461 | free(output_contents); 462 | output_contents = NULL; 463 | ClearSPASMErrorSessions(); 464 | free_storage(); 465 | 466 | #ifdef _WINDOWS 467 | _CrtDumpMemoryLeaks(); 468 | if (IsDebuggerPresent()) 469 | { 470 | system("PAUSE"); 471 | } 472 | #endif 473 | 474 | return error; 475 | } 476 | #endif 477 | -------------------------------------------------------------------------------- /modp_ascii.cpp: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ 2 | /* vi: set expandtab shiftwidth=4 tabstop=4: */ 3 | 4 | /** 5 | * \file modp_ascii.c 6 | *
  7 |  * MODP_ASCII - Ascii transformations (upper/lower, etc)
  8 |  * http://code.google.com/p/stringencoders/
  9 |  *
 10 |  * Copyright © 2007  Nick Galbreath -- nickg [at] modp [dot] com
 11 |  * All rights reserved.
 12 |  *
 13 |  * Redistribution and use in source and binary forms, with or without
 14 |  * modification, are permitted provided that the following conditions are
 15 |  * met:
 16 |  *
 17 |  *   Redistributions of source code must retain the above copyright
 18 |  *   notice, this list of conditions and the following disclaimer.
 19 |  *
 20 |  *   Redistributions in binary form must reproduce the above copyright
 21 |  *   notice, this list of conditions and the following disclaimer in the
 22 |  *   documentation and/or other materials provided with the distribution.
 23 |  *
 24 |  *   Neither the name of the modp.com nor the names of its
 25 |  *   contributors may be used to endorse or promote products derived from
 26 |  *   this software without specific prior written permission.
 27 |  *
 28 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 29 |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 30 |  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 31 |  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 32 |  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 33 |  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 34 |  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 35 |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 36 |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 37 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 38 |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 39 |  *
 40 |  * This is the standard "new" BSD license:
 41 |  * http://www.opensource.org/licenses/bsd-license.php
 42 |  * 
43 | */ 44 | #include "stdafx.h" 45 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) 46 | typedef unsigned char uint8_t; 47 | typedef unsigned int uint32_t; 48 | #else 49 | #include 50 | #endif 51 | #include "modp_ascii.h" 52 | 53 | static const unsigned char gsToUpperMap[256] = { 54 | '\0', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, '\t', 55 | '\n', 0x0b, 0x0c, '\r', 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 56 | 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 57 | 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', '\'', 58 | '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', 59 | '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 60 | '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 61 | 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 62 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 63 | 'Z', '[', '\\', ']', '^', '_', '`', 'A', 'B', 'C', 64 | 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 65 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 66 | 'X', 'Y', 'Z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 67 | 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 68 | 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 69 | 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 70 | 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 71 | 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 72 | 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 73 | 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 74 | 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 75 | 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 76 | 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 77 | 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 78 | 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 79 | 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff 80 | }; 81 | 82 | static const unsigned char gsToLowerMap[256] = { 83 | '\0', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, '\t', 84 | '\n', 0x0b, 0x0c, '\r', 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 85 | 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 86 | 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', '\'', 87 | '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', 88 | '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 89 | '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', 90 | 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 91 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 92 | 'z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 93 | 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 94 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 95 | 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 96 | 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 97 | 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 98 | 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 99 | 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 100 | 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 101 | 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 102 | 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 103 | 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 104 | 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 105 | 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 106 | 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 107 | 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 108 | 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff 109 | }; 110 | 111 | static const unsigned char gsToPrintMap[256] = { 112 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 113 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 114 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 115 | '?', '?', ' ', '!', '"', '#', '$', '%', '&', '\'', 116 | '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', 117 | '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 118 | '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 119 | 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 120 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 121 | 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 122 | 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 123 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 124 | 'x', 'y', 'z', '{', '|', '}', '~', '?', '?', '?', 125 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 126 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 127 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 128 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 129 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 130 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 131 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 132 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 133 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 134 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 135 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 136 | '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 137 | '?', '?', '?', '?', '?', '?' 138 | }; 139 | 140 | void modp_toupper_copy(char* dest, const char* str, size_t len) 141 | { 142 | int i; 143 | uint32_t eax, ebx; 144 | const uint8_t* ustr = (const uint8_t*) str; 145 | const int leftover = len % 4; 146 | const int imax = len / 4; 147 | const uint32_t* s = (const uint32_t*) str; 148 | uint32_t* d = (uint32_t*) dest; 149 | for (i = 0; i != imax; ++i) { 150 | eax = s[i]; 151 | /* 152 | * This is based on the algorithm by Paul Hsieh 153 | * http://www.azillionmonkeys.com/qed/asmexample.html 154 | */ 155 | ebx = (0x7f7f7f7fu & eax) + 0x05050505u; 156 | ebx = (0x7f7f7f7fu & ebx) + 0x1a1a1a1au; 157 | ebx = ((ebx & ~eax) >> 2) & 0x20202020u; 158 | *d++ = eax - ebx; 159 | } 160 | 161 | i = imax*4; 162 | dest = (char*) d; 163 | switch (leftover) { 164 | case 3: *dest++ = (char) gsToUpperMap[ustr[i++]]; 165 | case 2: *dest++ = (char) gsToUpperMap[ustr[i++]]; 166 | case 1: *dest++ = (char) gsToUpperMap[ustr[i]]; 167 | case 0: *dest = '\0'; 168 | } 169 | } 170 | 171 | void modp_tolower_copy(char* dest, const char* str, size_t len) 172 | { 173 | int i; 174 | uint32_t eax, ebx; 175 | const uint8_t* ustr = (const uint8_t*) str; 176 | const int leftover = len % 4; 177 | const int imax = len / 4; 178 | const uint32_t* s = (const uint32_t*) str; 179 | uint32_t* d = (uint32_t*) dest; 180 | for (i = 0; i != imax; ++i) { 181 | eax = s[i]; 182 | /* 183 | * This is based on the algorithm by Paul Hsieh 184 | * http://www.azillionmonkeys.com/qed/asmexample.html 185 | */ 186 | ebx = (0x7f7f7f7fu & eax) + 0x25252525u; 187 | ebx = (0x7f7f7f7fu & ebx) + 0x1a1a1a1au; 188 | ebx = ((ebx & ~eax) >> 2) & 0x20202020u; 189 | *d++ = eax + ebx; 190 | } 191 | 192 | i = imax*4; 193 | dest = (char*) d; 194 | switch (leftover) { 195 | case 3: *dest++ = (char) gsToLowerMap[ustr[i++]]; 196 | case 2: *dest++ = (char) gsToLowerMap[ustr[i++]]; 197 | case 1: *dest++ = (char) gsToLowerMap[ustr[i]]; 198 | case 0: *dest = '\0'; 199 | } 200 | } 201 | 202 | void modp_toupper(char* str, size_t len) 203 | { 204 | modp_toupper_copy(str, str, len); 205 | } 206 | 207 | void modp_tolower(char* str, size_t len) 208 | { 209 | modp_tolower_copy(str, str, len); 210 | } 211 | 212 | void modp_toprint_copy(char* dest, const char* str, size_t len) 213 | { 214 | int i; 215 | uint8_t c1,c2,c3,c4; 216 | 217 | const int leftover = len % 4; 218 | const int imax = len - leftover; 219 | const uint8_t* s = (const uint8_t*) str; 220 | for (i = 0; i != imax ; i+=4) { 221 | /* 222 | * it's important to make these variables 223 | * it helps the optimizer to figure out what to do 224 | */ 225 | c1 = s[i]; c2=s[i+1]; c3=s[i+2]; c4=s[i+3]; 226 | dest[0] = (char) gsToPrintMap[c1]; 227 | dest[1] = (char) gsToPrintMap[c2]; 228 | dest[2] = (char) gsToPrintMap[c3]; 229 | dest[3] = (char) gsToPrintMap[c4]; 230 | dest += 4; 231 | } 232 | 233 | switch (leftover) { 234 | case 3: *dest++ = (char) gsToPrintMap[s[i++]]; 235 | case 2: *dest++ = (char) gsToPrintMap[s[i++]]; 236 | case 1: *dest++ = (char) gsToPrintMap[s[i]]; 237 | case 0: *dest = '\0'; 238 | } 239 | } 240 | 241 | void modp_toprint(char* str, size_t len) 242 | { 243 | modp_toprint_copy(str,str,len); 244 | } 245 | -------------------------------------------------------------------------------- /modp_ascii.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ 2 | /* vi: set expandtab shiftwidth=4 tabstop=4: */ 3 | 4 | /** 5 | * \file modp_ascii.h 6 | *
  7 |  * MODP_ASCII -- Simple ascii manipulation (uppercase, lowercase, etc)
  8 |  * http://code.google.com/p/stringencoders/
  9 |  *
 10 |  * Copyright © 2007, Nick Galbreath -- nickg [at] modp [dot] com
 11 |  * All rights reserved.
 12 |  *
 13 |  * Released under bsd license.  See modp_ascii.c for details.
 14 |  * 
15 | * 16 | */ 17 | 18 | #ifndef COM_MODP_STRINGENCODERS_ASCII 19 | #define COM_MODP_STRINGENCODERS_ASCII 20 | 21 | #ifdef __cplusplus 22 | #define BEGIN_C extern "C" { 23 | #define END_C } 24 | #else 25 | #define BEGIN_C 26 | #define END_C 27 | #endif 28 | 29 | BEGIN_C 30 | 31 | /* 32 | * \param[in,out] str the input string 33 | * \param[in] len the length of input string (the strlen) 34 | */ 35 | void modp_toupper(char* str, int size_t); 36 | 37 | /** \brief make lower case copy of input string 38 | * 39 | * \param[out] output buffer, with at least 'len + 1' bytes allocated 40 | * \param[in] str the input string 41 | * \param[in] len the length of input string (the strlen) 42 | * 43 | * Please make sure dest has been allocation with at least 'len+1' 44 | * bytes. This appends a trailing NULL character at the end of 45 | * dest! 46 | * 47 | * This is based on the algorithm by Paul Hsieh 48 | * http://www.azillionmonkeys.com/qed/asmexample.html 49 | */ 50 | void modp_toupper_copy(char* dest, const char* str, size_t len); 51 | 52 | /** \brief lower case a string in place 53 | * 54 | * \param[in, out] str the input string 55 | * \param[in] len the length of input string (the strlen) 56 | * 57 | */ 58 | void modp_tolower(char* str, size_t size); 59 | 60 | /** \brief make lower case copy of input string 61 | * 62 | * \param[out] output buffer, with at least 'len + 1' bytes allocated 63 | * \param[in] str the input string 64 | * \param[in] len the length of input string (the strlen) 65 | * 66 | * Please make sure dest has been allocation with at least 'len+1' 67 | * bytes. This appends a trailing NULL character at the end of 68 | * dest! 69 | * 70 | * This is based on the algorithm by Paul Hsieh 71 | * http://www.azillionmonkeys.com/qed/asmexample.html 72 | */ 73 | void modp_tolower_copy(char* dest, const char* str, size_t len); 74 | 75 | /** \brief turn a string into 7-bit printable ascii. 76 | * 77 | * By "printable" we means all characters between 32 and 126. 78 | * All other values are turned into '?' 79 | * 80 | * \param[in, out] str the input string 81 | * \param[in] len the length of input string (the strlen) 82 | * 83 | */ 84 | void modp_toprint(char* str, size_t len); 85 | 86 | /** \brief make a printable copy of a string 87 | * 88 | * By "printable" we means all characters between 32 and 126. 89 | * All other values are turned into '?' 90 | * 91 | * \param[out] output buffer, with at least 'len + 1' bytes allocated 92 | * \param[in] str the input string 93 | * \param[in] len the length of input string (the strlen) 94 | * 95 | * Please make sure dest has been allocation with at least 'len+1' 96 | * bytes. This appends a trailing NULL character at the end of 97 | * dest! 98 | */ 99 | void modp_toprint_copy(char* dest, const char* str, size_t len); 100 | 101 | END_C 102 | 103 | #endif /* MODP_ASCII */ 104 | -------------------------------------------------------------------------------- /opcodes.h: -------------------------------------------------------------------------------- 1 | #ifndef __OPCODES_H 2 | #define __OPCODES_H 3 | 4 | typedef struct _instr { 5 | const char *args; 6 | unsigned char instr_data[8]; 7 | int instr_size; 8 | int min_exectime; 9 | int max_exectime; 10 | bool has_end_data; 11 | unsigned char end_data; 12 | int size; 13 | int adl_exectime_penalty; 14 | } instr; 15 | 16 | typedef struct _opcode { 17 | const char *name; 18 | instr *instrs; 19 | int num_instrs; 20 | int use_count; 21 | struct _opcode *next; 22 | bool is_added; 23 | } opcode; 24 | 25 | extern opcode *all_opcodes; 26 | extern opcode opcode_list[]; 27 | extern opcode opcode_list_ez80[]; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /parser.h: -------------------------------------------------------------------------------- 1 | #ifndef __PARSER_H 2 | #define __PARSER_H 3 | 4 | #include "storage.h" 5 | 6 | #define EXPR_DELIMS "+-/*&|<>%^" 7 | 8 | extern bool parser_forward_ref_err; 9 | 10 | bool parse_num (const char *expr, int *value); 11 | char *parse_define (define_t *define); 12 | int parse_f (const char *expr); 13 | bool conv_hex (const char *start, const char *end, int *output_num); 14 | #endif 15 | -------------------------------------------------------------------------------- /pass_one.h: -------------------------------------------------------------------------------- 1 | #ifndef __PASS_ONE_H 2 | #define __PASS_ONE_H 3 | 4 | typedef enum { 5 | SUFFIX_L = 1, 6 | SUFFIX_IL = 2 7 | } suffix_type; 8 | 9 | void run_first_pass (char *ptr); 10 | char *run_first_pass_line (char *ptr); 11 | int write_out (int); 12 | char *handle_opcode_or_macro (char *ptr); 13 | void do_listing_for_line (char *ptr); 14 | 15 | extern bool adl_mode; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /pass_two.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "spasm.h" 4 | #include "pass_one.h" 5 | #include "pass_two.h" 6 | #include "utils.h" 7 | #include "parser.h" 8 | #include "directive.h" 9 | #include "console.h" 10 | #include "errors.h" 11 | 12 | expr_t *expr_list = NULL, *expr_list_tail = NULL; 13 | output_t *output_list = NULL, *output_list_tail = NULL; 14 | 15 | /* 16 | * Tries to evaluate an expression, 17 | * if it succeeds writes the data 18 | * to the output, otherwise writes 19 | * 0s to fill the space and adds 20 | * the expression to the list to be 21 | * parsed in pass two 22 | */ 23 | 24 | void add_pass_two_expr (char *expr, arg_type type, int inst_size, int or_value) { 25 | int value; 26 | 27 | //if we're in code counter or stats mode, where we don't need actual expressions, then just skip this crap 28 | /*if (mode & MODE_CODE_COUNTER) 29 | return;*/ 30 | 31 | int session = StartSPASMErrorSession(); 32 | bool fResult = parse_num (expr, &value); 33 | if ((fResult == false) && (IsSPASMErrorSessionFatal(session) == false)) 34 | { 35 | expr_t *new_expr; 36 | 37 | //if that didn't work, then add it to the pass two expression list 38 | new_expr = (expr_t *)malloc (sizeof (expr_t)); 39 | new_expr->program_counter = program_counter; 40 | new_expr->line_num = line_num; 41 | #ifdef USE_REUSABLES 42 | new_expr->curr_reusable = get_curr_reusable(); 43 | #endif 44 | new_expr->out_ptr = out_ptr; 45 | if (mode & MODE_LIST) 46 | new_expr->listing_offset = listing_offset; 47 | new_expr->type = type; 48 | new_expr->input_file = curr_input_file; 49 | new_expr->listing_on = listing_on; 50 | new_expr->inst_size = inst_size; 51 | new_expr->or_value = or_value; 52 | new_expr->next = NULL; 53 | 54 | if (expr_list_tail) 55 | expr_list_tail->next = new_expr; 56 | expr_list_tail = new_expr; 57 | if (!expr_list) 58 | expr_list = new_expr; 59 | 60 | /* Important stuff here: to prevent forward references 61 | combined with changing #defines (either through 62 | redefinition, or with macro arguments, for example), 63 | all #defines need to be fully expanded in the saved 64 | expression for the second pass */ 65 | 66 | //store the contents of the expanded expression for the second pass 67 | new_expr->expr = expand_expr (expr); 68 | 69 | //and write a blank value in its place 70 | if (type == ARG_ADDR_OFFSET) { 71 | type = ARG_NUM_8; 72 | } 73 | write_arg (0, type, inst_size, 0); 74 | 75 | } 76 | else if (fResult == false) 77 | { 78 | ReplaySPASMErrorSession(session); 79 | } 80 | else 81 | { 82 | //write the value now 83 | write_arg (value, type, inst_size, or_value); 84 | ReplaySPASMErrorSession(session); 85 | } 86 | 87 | EndSPASMErrorSession(session); 88 | } 89 | 90 | 91 | /* 92 | * Adds an expression to be 93 | * echo'ed on the second pass 94 | * 95 | * Allocates a copy 96 | */ 97 | 98 | void add_pass_two_output (char *expr, output_type type) { 99 | output_t *new_output; 100 | 101 | new_output = (output_t *) malloc (sizeof (output_t)); 102 | new_output->program_counter = program_counter; 103 | new_output->line_num = line_num; 104 | #ifdef USE_REUSABLES 105 | new_output->curr_reusable = get_curr_reusable(); 106 | #endif 107 | new_output->type = type; 108 | new_output->input_file = curr_input_file; 109 | new_output->next = NULL; 110 | 111 | if (type == OUTPUT_SHOW) 112 | new_output->expr = strdup (expr); 113 | else 114 | new_output->expr = expand_expr (expr); 115 | 116 | if (output_list_tail) 117 | output_list_tail->next = new_output; 118 | output_list_tail = new_output; 119 | if (!output_list) 120 | output_list = new_output; 121 | } 122 | 123 | 124 | /* 125 | * Goes through the list of 126 | * expressions that couldn't 127 | * be parsed before and evaluates 128 | * them and writes the values 129 | * to the output file 130 | */ 131 | 132 | extern unsigned char *output_contents; 133 | 134 | void run_second_pass () { 135 | int value; 136 | expr_t *old_expr; 137 | output_t *old_output; 138 | unsigned char *saved_out_ptr = out_ptr; 139 | int saved_listing_offset = listing_offset; 140 | char* old_input_file = curr_input_file; 141 | 142 | pass_one = false; 143 | 144 | //FILE *file = fopen ("passtwoexprs.txt", "w"); 145 | 146 | //printf("running through the list %p\n", expr_list); 147 | while (expr_list) 148 | { 149 | //go through each expression and evaluate it 150 | program_counter = expr_list->program_counter; 151 | line_num = expr_list->line_num; 152 | curr_input_file = expr_list->input_file; 153 | #ifdef USE_REUSABLES 154 | set_curr_reusable(expr_list->curr_reusable); 155 | #endif 156 | 157 | //fprintf(file, "%s:%d:offset(%d): %s\n", curr_input_file, line_num, expr_list->out_ptr - output_contents, expr_list->expr); 158 | 159 | //printf("passtwoexpr: '%s'\n", expr_list->expr); 160 | if (parse_num (expr_list->expr, &value)) 161 | { 162 | //if that was successful, then write it to the file 163 | if (mode & MODE_LIST) 164 | listing_offset = expr_list->listing_offset; 165 | if (expr_list->type == ARG_RST) { 166 | switch (value) { 167 | case 0x00: 168 | case 0x08: 169 | case 0x10: 170 | case 0x18: 171 | case 0x20: 172 | case 0x28: 173 | case 0x30: 174 | case 0x38: 175 | write_arg(value + 0xC7, expr_list->type, expr_list->inst_size, expr_list->or_value); 176 | break; 177 | default: 178 | SetLastSPASMError(SPASM_ERR_INVALID_RST_OPERANDS); 179 | break; 180 | } 181 | } else { 182 | out_ptr = expr_list->out_ptr; 183 | listing_on = expr_list->listing_on; 184 | write_arg (value, expr_list->type, expr_list->inst_size, expr_list->or_value); 185 | } 186 | } 187 | free (expr_list->expr); 188 | old_expr = expr_list; 189 | expr_list = expr_list->next; 190 | free (old_expr); 191 | } 192 | 193 | //fclose(file); 194 | 195 | out_ptr = saved_out_ptr; 196 | if (mode & MODE_LIST) 197 | listing_offset = saved_listing_offset; 198 | 199 | while (output_list) { 200 | //show each saved echo 201 | program_counter = output_list->program_counter; 202 | line_num = output_list->line_num; 203 | curr_input_file = output_list->input_file; 204 | #ifdef USE_REUSABLES 205 | set_curr_reusable(output_list->curr_reusable); 206 | #endif 207 | 208 | switch (output_list->type) { 209 | case OUTPUT_ECHO: 210 | { 211 | WORD orig_attributes = save_console_attributes(); 212 | set_console_attributes (COLOR_GREEN); 213 | int session = StartSPASMErrorSession(); 214 | parse_emit_string (output_list->expr, ES_ECHO, stdout); 215 | ReplaySPASMErrorSession(session); 216 | EndSPASMErrorSession(session); 217 | restore_console_attributes(orig_attributes); 218 | break; 219 | } 220 | case OUTPUT_SHOW: 221 | { 222 | define_t *define; 223 | if (!(define = search_defines (output_list->expr))) 224 | { 225 | SetLastSPASMError(SPASM_ERR_LABEL_NOT_FOUND, output_list->expr); 226 | break; 227 | } 228 | show_define (define); 229 | break; 230 | } 231 | } 232 | #if defined(DUMP_DEFINES) && defined(_DEBUG) 233 | dump_defines(); 234 | #endif 235 | 236 | free (output_list->expr); 237 | old_output = output_list; 238 | output_list = output_list->next; 239 | free (old_output); 240 | } 241 | curr_input_file = old_input_file; 242 | } 243 | 244 | 245 | /* 246 | * Writes an argument 247 | * directly to the file, 248 | * OR'ing it with a value 249 | * for bit numbers 250 | */ 251 | 252 | void write_arg (int value, arg_type type, int inst_size, int or_value) { 253 | 254 | switch (type) { 255 | case ARG_NUM_8: 256 | if (value < -128 || value > 255) 257 | { 258 | SetLastSPASMWarning(SPASM_WARN_TRUNCATING_8); 259 | } 260 | write_out(value & 0xFF); 261 | break; 262 | case ARG_RST: { 263 | switch (value) { 264 | case 0x00: 265 | case 0x08: 266 | case 0x10: 267 | case 0x18: 268 | case 0x20: 269 | case 0x28: 270 | case 0x30: 271 | case 0x38: 272 | write_out(value + 0xC7); 273 | break; 274 | default: 275 | SetLastSPASMError(SPASM_ERR_INVALID_RST_OPERANDS); 276 | break; 277 | } 278 | break; 279 | } 280 | case ARG_NUM_16: 281 | //no range checking for 16 bits, as higher bits of labels are used to store page info 282 | //however, eZ80 code does not carry page information, so 16-bit arguments should be 16-bit 283 | if (mode & MODE_EZ80 && (value < -32768 || value > 65535)) 284 | { 285 | SetLastSPASMWarning(SPASM_WARN_TRUNCATING_16); 286 | } 287 | write_out (value & 0xFF); 288 | write_out ((value >> 8) & 0xFF); 289 | break; 290 | case ARG_NUM_24: 291 | write_out (value & 0xFF); 292 | write_out ((value >> 8) & 0xFF); 293 | write_out ((value >> 16) & 0xFF); 294 | break; 295 | case ARG_ADDR_OFFSET: 296 | if (mode & MODE_EZ80) { 297 | value &= 0xFFFFFF; 298 | value -= ((program_counter & 0xFFFFFF) + inst_size); 299 | } 300 | else 301 | { 302 | value &= 0xFFFF; 303 | value -= ((program_counter & 0xFFFF) + inst_size); 304 | } 305 | 306 | if (value < -128 || value > 127) 307 | { 308 | SetLastSPASMError(SPASM_ERR_JUMP_EXCEEDED, value); 309 | value = 0; 310 | } 311 | write_out (value & 0xFF); 312 | break; 313 | case ARG_IX_IY_OFFSET: 314 | if (value > 127 || value < -128) 315 | { 316 | SetLastSPASMError(SPASM_ERR_INDEX_OFFSET_EXCEEDED, value); 317 | value = 0; 318 | } 319 | write_out (value & 0xFF); 320 | break; 321 | case ARG_BIT_NUM: 322 | if (value < 0 || value > 7) { 323 | show_error ("Bit number can only range from 0 to 7"); 324 | value = 0; 325 | } 326 | write_out (((value & 0x07) << 3) | or_value); 327 | break; 328 | } 329 | } 330 | 331 | -------------------------------------------------------------------------------- /pass_two.h: -------------------------------------------------------------------------------- 1 | #ifndef __PASS_TWO_H 2 | #define __PASS_TWO_H 3 | 4 | typedef enum { 5 | ARG_NUM_8, 6 | ARG_NUM_16, 7 | ARG_NUM_24, 8 | ARG_ADDR_OFFSET, 9 | ARG_IX_IY_OFFSET, 10 | ARG_BIT_NUM, 11 | ARG_RST 12 | } arg_type; 13 | 14 | typedef enum { 15 | OUTPUT_ECHO, 16 | OUTPUT_SHOW 17 | } output_type; 18 | 19 | typedef struct tagexpr{ 20 | int program_counter; 21 | int line_num; 22 | int curr_reusable; 23 | unsigned char *out_ptr; 24 | int listing_offset; 25 | char *expr; 26 | arg_type type; 27 | char *input_file; 28 | bool listing_on; 29 | int inst_size; 30 | int or_value; 31 | struct tagexpr *next; 32 | } expr_t; 33 | 34 | typedef struct output { 35 | int program_counter; 36 | int line_num; 37 | int curr_reusable; 38 | char *expr; 39 | output_type type; 40 | char *input_file; 41 | struct output *next; 42 | } output_t; 43 | 44 | void add_pass_two_expr (char *expr, arg_type type, int inst_size, int or_value); 45 | void add_pass_two_output (char *expr, output_type type); 46 | void run_second_pass (); 47 | void write_arg (int value, arg_type type, int inst_size, int or_value); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /preop.h: -------------------------------------------------------------------------------- 1 | #ifndef __PREOP_H 2 | #define __PREOP_H 3 | 4 | #include "storage.h" 5 | char *handle_preop (char *ptr); 6 | char *parse_arg_defs (const char *ptr, define_t *define); 7 | 8 | #endif 9 | 10 | -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberthdev/spasm-ng/5f0786d38f064835be674d4b7df42969967bb73c/resource.h -------------------------------------------------------------------------------- /spasm.h: -------------------------------------------------------------------------------- 1 | #ifndef __SPASM_H 2 | #define __SPASM_H 3 | 4 | 5 | #include "storage.h" 6 | #include "list.h" 7 | #include "expand_buf.h" 8 | #include "version.h" 9 | 10 | // Debugging prints 11 | #ifdef DEBUG_PRINT 12 | #define DPRINT(fmt, ...) printf("[%s:%d] " fmt, __FILE__,__LINE__,##__VA_ARGS__) 13 | #else 14 | #define DPRINT(fmt, ...) 15 | #endif 16 | 17 | typedef enum { 18 | MODE_NORMAL = 1, 19 | MODE_CODE_COUNTER = 2, 20 | MODE_SYMTABLE = 4, 21 | MODE_STATS = 8, 22 | MODE_LIST = 16, 23 | MODE_COMMANDLINE = 32, 24 | MODE_EZ80 = 64, 25 | } ASM_MODE; 26 | 27 | typedef enum { 28 | EXIT_NORMAL = 0, 29 | EXIT_WARNINGS = 1, 30 | EXIT_ERRORS = 2, 31 | EXIT_FATAL_ERROR = 3 32 | } EXIT_STATUS; 33 | 34 | #ifdef _WINDOWS 35 | #include 36 | #define NEWLINE "\r\n" 37 | #define PATH_SEPARATOR '\\' 38 | #define WRONG_PATH_SEPARATOR '/' 39 | #define strcasecmp _stricmp 40 | #define strncasecmp _strnicmp 41 | #define snprintf sprintf_s 42 | #define strdup _strdup 43 | 44 | #else 45 | #define NEWLINE "\n" 46 | #define PATH_SEPARATOR '/' 47 | #define WRONG_PATH_SEPARATOR '\\' 48 | typedef unsigned short WORD; 49 | typedef int BOOL; 50 | #endif 51 | 52 | #ifdef __MAIN_C 53 | #define GLOBAL 54 | #else 55 | #define GLOBAL extern 56 | #endif 57 | 58 | //#define OUTPUT_BUF_SIZE 8000000 59 | const static unsigned int output_buf_size = 8000000; //size of output buffer for assembled code 60 | 61 | //make sure that MAX_PATH is max path length on *nix and Windows 62 | #if !defined(MAX_PATH) || defined(UNIX_VER) 63 | #include 64 | #define MAX_PATH PATH_MAX 65 | #endif 66 | 67 | GLOBAL unsigned int mode; 68 | GLOBAL list_t *include_dirs, *input_files; 69 | GLOBAL unsigned int program_counter; 70 | GLOBAL label_t *last_label; 71 | GLOBAL unsigned int stats_codesize, stats_datasize, stats_mintime, stats_maxtime; 72 | GLOBAL int line_num; 73 | GLOBAL char temp_path[MAX_PATH]; 74 | GLOBAL char *curr_input_file, *output_filename; 75 | GLOBAL char *input_contents; 76 | GLOBAL unsigned char *out_ptr, *output_contents; 77 | GLOBAL bool error_occurred; 78 | GLOBAL expand_buf *listing_buf; 79 | GLOBAL size_t listing_offset; 80 | GLOBAL bool listing_on; 81 | GLOBAL bool listing_for_line_done; //true if listing for this line has already been done 82 | GLOBAL char *line_start; //saved start of current line 83 | GLOBAL int old_line_num; //saved line number 84 | GLOBAL bool pass_one; //true if pass 1, false if pass 2 85 | GLOBAL int in_macro; //depth in macro - 0 if not in macro, 1 if inside macro, 2 if inside macro called from macro... 86 | GLOBAL bool use_colors; //whether to use colors or not for messages 87 | GLOBAL EXIT_STATUS exit_code; 88 | #ifdef USE_REUSABLES 89 | GLOBAL int total_reusables, curr_reusable; 90 | #endif 91 | #ifdef USE_BUILTIN_FCREATE 92 | GLOBAL int cur_buf; 93 | #endif 94 | #ifdef _WINDLL 95 | GLOBAL char output_text[800000]; 96 | #endif 97 | 98 | //make sure max and min are defined for *nix 99 | #ifndef max 100 | #define max(x,y) (((long) (x) > (long) (y)) ? (x) : (y)) 101 | #endif 102 | #ifndef min 103 | #define min(x,y) (((long) (x) < (long) (y)) ? (x) : (y)) 104 | #endif 105 | 106 | int run_assembly(void); 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // SPASMNew.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | -------------------------------------------------------------------------------- /stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef _STDAFX_H 4 | #define _STDAFX_H 5 | 6 | #if defined(_WINDOWS) 7 | #ifndef STRICT 8 | #define STRICT 9 | #endif 10 | 11 | #include "targetver.h" 12 | 13 | /* Workaround for error in Visual Studio 2015 (and newer): 14 | * error LNK2019: unresolved external symbol ___iob_func referenced in function ___gmp_default_allocate 15 | */ 16 | #if _MSC_VER >= 1900 17 | #include 18 | extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; } 19 | #endif 20 | 21 | #ifdef SPASM_NG_ENABLE_COM 22 | //#define _ATL_APARTMENT_THREADED 23 | #define _ATL_FREE_THREADED 24 | 25 | #define _ATL_NO_AUTOMATIC_NAMESPACE 26 | 27 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit 28 | 29 | #define _ATL_STATIC_REGISTRY 30 | #define ATL_NO_ASSERT_ON_DESTROY_NONEXISTENT_WINDOW 31 | #endif 32 | 33 | #include "resource.h" 34 | 35 | #ifdef SPASM_NG_ENABLE_COM 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | using namespace ATL; 43 | 44 | // We don't have to really worry about this (non-ATL), but 45 | // we'll include it in this #ifdef anyways. 46 | #include 47 | #endif 48 | 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | #include 65 | #include 66 | #include 67 | 68 | #include 69 | #include 70 | #include 71 | 72 | #include 73 | #include 74 | #include 75 | #include 76 | 77 | //#include 78 | 79 | #import rename("FreeSpace", "FreeSpace2") no_namespace, raw_interfaces_only 80 | 81 | #include "SPASM_h.h" 82 | 83 | #include "gmp.h" 84 | 85 | // Silly workarounds for WinSDK conflicts with VS2010 Express 86 | // (a seriously buggy release of VS...) 87 | #ifdef SPASM_NG_ENABLE_COM 88 | // Are we using VS2010? 89 | #if (_MSC_VER == 1600) 90 | 91 | // The Chromium devs did it best, so I'll let them take over here... 92 | 93 | /* Copyright 2013 The Chromium Authors. All rights reserved. 94 | * Use of this source code is governed by a BSD-style license. 95 | * Workaround for: 96 | * http://connect.microsoft.com/VisualStudio/feedback/details/621653/ 97 | * http://crbug.com/225822 98 | * Note that we can't actually include here because there's other 99 | * code in third_party that has partial versions of stdint types that conflict. 100 | */ 101 | #include 102 | #undef INT8_MIN 103 | #undef INT16_MIN 104 | #undef INT32_MIN 105 | #undef INT64_MIN 106 | #undef INT8_MAX 107 | #undef UINT8_MAX 108 | #undef INT16_MAX 109 | #undef UINT16_MAX 110 | #undef INT32_MAX 111 | #undef UINT32_MAX 112 | #undef INT64_MAX 113 | #undef UINT64_MAX 114 | 115 | #endif // VS2010 check 116 | #endif // SPASM_NG_ENABLE_COM 117 | 118 | #else 119 | #include 120 | #include 121 | #include 122 | #include 123 | #include 124 | #include 125 | #include 126 | #include 127 | #ifndef NO_APPSIGN 128 | #include 129 | #include 130 | #endif /* NO_APPSIGN */ 131 | #include 132 | #include 133 | #include 134 | #include 135 | 136 | #define __inout 137 | 138 | #ifdef UNIXVER 139 | typedef unsigned int DWORD; 140 | #else 141 | #include 142 | #endif 143 | typedef const char *LPCTSTR; 144 | typedef char *LPSTR, *LPTSTR; 145 | typedef char TCHAR; 146 | typedef void *LPVOID; 147 | 148 | #define _T(z) z 149 | 150 | #define _stricmp strcasecmp 151 | #define _tcsdup strdup 152 | #define _strdup strdup 153 | #define sprintf_s sprintf 154 | #define _strtoi64 strtoll 155 | 156 | #define StringCchPrintf(dest, size, fmt, ...) snprintf(dest, size, fmt, __VA_ARGS__) 157 | #define StringCchVPrintf(dest, size, fmt, args) vsnprintf(dest, size, fmt, args) 158 | 159 | #ifdef UNIXVER 160 | #define ARRAYSIZE(z) (sizeof(z)/sizeof((z)[0])) 161 | #endif 162 | 163 | #endif 164 | #endif 165 | -------------------------------------------------------------------------------- /storage.h: -------------------------------------------------------------------------------- 1 | #ifndef __STORAGE_H 2 | #define __STORAGE_H 3 | 4 | #include "list.h" 5 | #include "hash.h" 6 | 7 | #if !defined(MAX_PATH) && !defined(_MSC_VER) 8 | #include 9 | #define MAX_PATH PATH_MAX 10 | #endif 11 | 12 | #define MAX_ARGS 16 //max number of args macros can have 13 | #define MAX_REUSABLES 4096 //max number of reusable labels 14 | 15 | //used for defines and macros 16 | typedef struct define { 17 | char *name; 18 | int line_num; 19 | char *input_file; 20 | char *contents; 21 | int num_args; 22 | char *args[MAX_ARGS]; 23 | } define_t; 24 | 25 | typedef struct label { 26 | char *name; 27 | int line_num; 28 | char *input_file; 29 | int value; 30 | } label_t; 31 | 32 | typedef struct common_store { 33 | char *name; 34 | int line_num; 35 | char *input_file; 36 | } common_store_t; 37 | 38 | void write_labels (char *filename); 39 | void init_storage (); 40 | EXPORT void free_storage(); 41 | define_t *add_define (char *name, bool *redefined, bool search_local = true); 42 | define_t *search_defines (const char *name, bool search_local = true); 43 | define_t *search_local_defines (const char *name); 44 | void remove_define (char *name); 45 | void set_define (define_t *define, const char *str, int len, bool redefined); 46 | int get_num_defines (); 47 | 48 | label_t *add_label (char *name, int value); 49 | label_t *search_labels (const char *name); 50 | int get_num_labels (); 51 | 52 | unsigned int search_reusables(int index); 53 | int get_curr_reusable(); 54 | int set_curr_reusable(int index); 55 | void add_reusable(); 56 | int get_num_reusables(); 57 | 58 | list_t *add_arg_set (); 59 | void add_arg (char *name, char *value, list_t *arg_set); 60 | void remove_arg_set (list_t *arg_set); 61 | 62 | void set_case_sensitive(bool sensitive); 63 | bool get_case_sensitive(); 64 | void dump_defines(); 65 | 66 | extern hash_t *label_table; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /test.asm: -------------------------------------------------------------------------------- 1 | #macro errortest() 2 | 10+ 3 | #endmacro 4 | #define err2 24a 5 | #define err3 "test" 6 | 7 | .db errortest + 1, err2, err3 -------------------------------------------------------------------------------- /tests/bugs/empty-macro-name.asm: -------------------------------------------------------------------------------- 1 | ; Test fuzzed input - empty macro name 2 | ; CHECK-ERR: 122 3 | #macro > 4 | -------------------------------------------------------------------------------- /tests/bugs/extra-long-addinstr.asm: -------------------------------------------------------------------------------- 1 | ; Test for correct error handling on long (but invalid) 2 | ; .addinstr arguments 3 | ; CHECK-ERR: 126 4 | .addinstr 0 0 0000000000000000000000000000 5 | -------------------------------------------------------------------------------- /tests/bugs/non-existent-jmp.asm: -------------------------------------------------------------------------------- 1 | ; Test error handling for non-existent label 2 | ; CHECK-NFERR: 106 3 | ; CHECK: C3 0E 00 C3 00 00 00 00 00 00 00 00 00 00 4 | start: 5 | jp end 6 | jp doesntexist 7 | .fill 8 8 | end: 9 | -------------------------------------------------------------------------------- /tests/bugs/pass_one_eol.asm: -------------------------------------------------------------------------------- 1 | ; Test fuzzed input 2 | ; CHECK-ERR: 109 3 | .echo >0 4 | -------------------------------------------------------------------------------- /tests/bugs/too-far-relative-jmp.asm: -------------------------------------------------------------------------------- 1 | ; Test error handling for too far relative jump 2 | ; CHECK-ERR: 120 3 | start: 4 | jr end 5 | .fill 128 6 | end: 7 | -------------------------------------------------------------------------------- /tests/dd.asm: -------------------------------------------------------------------------------- 1 | ; Test large 32-bit unsigned values don't trigger overflow error. 2 | #macro dd(value) 3 | .db value & $ff 4 | .dl value >> 8 5 | #endmacro 6 | #define .dd dd( 7 | .dd 1 << 31 8 | .dd -1 << 31 9 | .dd $ffffffff 10 | ; CHECK: 00000080 00000080 FFFFFFFF 11 | -------------------------------------------------------------------------------- /tests/empty-macro.asm: -------------------------------------------------------------------------------- 1 | ; Test using empty macros. 2 | #macro test 3 | #endmacro 4 | ; CHECK: 5 | .echo test 6 | -------------------------------------------------------------------------------- /tests/eval-neg.asm: -------------------------------------------------------------------------------- 1 | ; Test using an eval'd negative number. 2 | #define test eval(-1) 3 | ; CHECK: FF 4 | .db test 5 | -------------------------------------------------------------------------------- /tests/jr-ez80-offset.asm: -------------------------------------------------------------------------------- 1 | ; Test jr relative offsets for 24-bit eZ80 code as fixed by PR #22. 2 | ; 3 | ; RUN-ARGS: -E 4 | .assume ADL=1 5 | 6 | ; CHECK: 5B 18 FD 7 | jr.lil $ 8 | 9 | -------------------------------------------------------------------------------- /tests/macro-string-argument.asm: -------------------------------------------------------------------------------- 1 | ; Test macro string argument handling 2 | ; CHECK: 74657374 00 3 | #define string(value) .db value,0 4 | string("test") 5 | -------------------------------------------------------------------------------- /tests/macro-string-argument.bin: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /tests/name-starts-with-reg.asm: -------------------------------------------------------------------------------- 1 | ; Test names that start with ix or iy 2 | ; CHECK: 323412 3A7856 3 | ixtest = $1234 4 | iytest = $5678 5 | ld (ixtest), a 6 | ld a, (iytest) 7 | -------------------------------------------------------------------------------- /tests/neg-index-offset.asm: -------------------------------------------------------------------------------- 1 | ; Test negative offsets on index registers 2 | ; CHECK: DD77FF FD7E80 DD7740 FD7E00 3 | ld ( ix - 1 ), a 4 | ld a, ( iy - 128 ) 5 | ld ( ix + 64 ) , a 6 | ld a, ( iy ) 7 | -------------------------------------------------------------------------------- /tests/space-after-unary.asm: -------------------------------------------------------------------------------- 1 | ; Test spaces between unary operators and their values 2 | ; CHECK: FF 3 | .db - 1 4 | -------------------------------------------------------------------------------- /tests/test-runner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # spasm-ng test runner 4 | 5 | import logging 6 | import re 7 | import shlex 8 | import subprocess 9 | import os 10 | import sys 11 | import glob 12 | import tempfile 13 | 14 | class NotImplementedType: 15 | def __getitem__(cls, x): 16 | return NotImplemented 17 | 18 | def min_version_met() -> bool: 19 | (major, minor, _, _, _) = sys.version_info 20 | return (major == 3 and minor >= 5) or major > 3 21 | 22 | def have_typing_module() -> bool: 23 | (major, minor, _, _, _) = sys.version_info 24 | return (major == 3 and minor >= 5) or major > 3 25 | 26 | if not min_version_met(): 27 | print("ERROR: You need Python 3.5+ to run the test suite.") 28 | sys.exit(1) 29 | 30 | if have_typing_module(): 31 | from typing import Tuple, ByteString, Iterable, Sequence 32 | else: 33 | Tuple = ByteString = Iterable = Sequence = NotImplementedType() 34 | 35 | # In case you need more visiblity into what's going on. 36 | #logging.basicConfig(level=logging.DEBUG) 37 | 38 | class Checker(object): 39 | def __init__(self, line_tail): 40 | raise NotImplementedError 41 | 42 | def __call__(self, binary: ByteString, console: Sequence[str]) -> Tuple[ByteString, Sequence[str]]: 43 | raise NotImplementedError 44 | 45 | 46 | class EmitsBytes(Checker): 47 | def __init__(self, s): 48 | s = filter_whitespace(s) 49 | if len(s) % 2 != 0: 50 | raise ValueError("CHECK values must specify exactly two digits per byte") 51 | 52 | expected = bytearray() 53 | for i in range(0, len(s), 2): 54 | expected.append(int(s[i:i+2], 16)) 55 | self.bytes = expected 56 | 57 | def __call__(self, binary, output): 58 | length = len(self.bytes) 59 | actual = binary[:length] 60 | logging.debug('EmitsBytes receives %s, expects %s', 61 | format_bytes(actual), format_bytes(self.bytes)) 62 | assert actual == self.bytes, "Expected {}, found {}".format(self.bytes, actual) 63 | return (binary[:length], output) 64 | 65 | 66 | class SkipBytes(Checker): 67 | def __init__(self, s): 68 | self.length = int(filter_whitespace(s), 16) 69 | 70 | def __call__(self, binary, output): 71 | logging.debug('SkipBytes receives %u, needs %u', len(binary), self.length) 72 | assert len(binary) >= self.length, "Expected at least {} bytes, got {}".format(self.length, len(binary)) 73 | return (binary[:self.length], output) 74 | 75 | 76 | class EmitsWarning(Checker): 77 | def __init__(self, s): 78 | s = filter_whitespace(s) 79 | if len(s) != 3: 80 | raise ValueError("CHECK-WARN values must specify a 3 digit hex value") 81 | 82 | try: 83 | int(s, 16) 84 | except ValueError: 85 | raise ValueError("CHECK-WARN values must specify a 3 digit hex value") 86 | 87 | self.warn_code = s 88 | 89 | def __call__(self, binary, output): 90 | found_warn_code = None 91 | output_idx = 0 92 | 93 | for line in output: 94 | m = re.search(r'error SW(\d{3}):', line) 95 | if m: 96 | found_warn_code = m.group(1) 97 | break 98 | output_idx += 1 99 | 100 | logging.debug('EmitsWarning receives %s, expects %s', 101 | str(found_warn_code), self.warn_code) 102 | assert found_warn_code == self.warn_code, "Expected error code {}, found {}".format(self.warn_code, found_warn_code) 103 | return (binary, output[(output_idx + 1):]) 104 | 105 | class EmitsError(Checker): 106 | def __init__(self, s): 107 | s = filter_whitespace(s) 108 | if len(s) != 3: 109 | raise ValueError("CHECK-ERR values must specify a 3 digit hex value") 110 | 111 | try: 112 | int(s, 16) 113 | except ValueError: 114 | raise ValueError("CHECK-ERR values must specify a 3 digit hex value") 115 | 116 | self.err_code = s 117 | 118 | def __call__(self, binary, output): 119 | assert len(binary) == 0, "Binary is non-empty, despite an error occurring! Got binary {}, length {}!".format(binary, len(binary)) 120 | found_err_code = None 121 | output_idx = 0 122 | 123 | for line in output: 124 | m = re.search(r'error SE(\d{3}):', line) 125 | if m: 126 | found_err_code = m.group(1) 127 | break 128 | output_idx += 1 129 | 130 | logging.debug('EmitsError receives %s, expects %s', 131 | str(found_err_code), self.err_code) 132 | assert found_err_code == self.err_code, "Expected error code {}, found {}".format(self.err_code, found_err_code) 133 | return (binary, output[(output_idx + 1):]) 134 | 135 | class EmitsNonFatalError(EmitsError): 136 | def __init__(self, *args, **kwargs): 137 | super(EmitsNonFatalError, self).__init__(*args, **kwargs) 138 | 139 | def __call__(self, binary, output): 140 | _, output = super(EmitsNonFatalError, self).__call__(bytearray(), output) 141 | return (binary, output) 142 | 143 | CHECK_TYPES = { 144 | '': EmitsBytes, 145 | '-SKIP': SkipBytes, 146 | '-WARN': EmitsWarning, 147 | '-ERR': EmitsError, 148 | '-NFERR': EmitsNonFatalError 149 | } 150 | 151 | 152 | def find_check_directives(sources: Iterable[str]) -> Sequence[Checker]: 153 | """ 154 | Scan a test source file for check directives and return the set of 155 | constraints, among the following: 156 | 157 | * CHECK aa bb cc: assert that the output binary contains the bytes aa, bb 158 | and cc in exactly that order at the location counter, where byte values 159 | are specified in hex. 160 | * CHECK-SKIP nnnn: advance the checker location by nnnn bytes (in hex), 161 | ignoring the bytes in that region. 162 | * CHECK-WARN nnn: assert that warning number nnn is raised. 163 | * CHECK-ERR nnn: assert that error number nnn is raised. 164 | """ 165 | checkers = [] 166 | for line in sources: 167 | m = re.match(r'^\s*;\s*CHECK(?P[^:]*):', line) 168 | if m: 169 | ty = m.group('ty') 170 | if ty not in CHECK_TYPES: 171 | raise ValueError("Check type '{}' is not recognized".format(ty)) 172 | checkers.append(CHECK_TYPES[ty](line[m.end():])) 173 | return checkers 174 | 175 | 176 | def find_assembler_args(sources: Iterable[str]) -> str: 177 | args = [] 178 | for line in sources: 179 | m = re.match(r'^\s*;\s*RUN-ARGS:', line) 180 | if m: 181 | args.append(line[m.end():].strip()) 182 | return ' '.join(args) 183 | 184 | 185 | def run_assembler(assembler: str, infile: str, opts: str) -> Tuple[int, ByteString, Sequence[str]]: 186 | """ 187 | Run the assembler over the given file, with the given command line 188 | arguments. 189 | 190 | Returns the assembler's return code, the binary emitted from the 191 | assembler and the lines emitted to the console. 192 | """ 193 | DEVNULL = open(os.devnull, 'wb') 194 | outfile_fd, outfile_name = tempfile.mkstemp() 195 | 196 | logging.debug("Command: " + " ".join([assembler] + shlex.split(opts) + [infile, outfile_name])) 197 | proc = subprocess.Popen( 198 | [assembler] + shlex.split(opts) + [infile, outfile_name], 199 | stdin=DEVNULL, stdout=subprocess.PIPE, 200 | stderr=subprocess.STDOUT, universal_newlines=True) 201 | pout, perr = proc.communicate() 202 | 203 | outfile = os.fdopen(outfile_fd, "rb") 204 | binary = outfile.read() 205 | outfile.close() 206 | return (proc.returncode, binary, pout.split('\n')) 207 | 208 | 209 | def filter_whitespace(s: str) -> str: 210 | return ''.join(filter(lambda c: not c.isspace(), s)) 211 | 212 | 213 | def format_bytes(b) -> str: 214 | return '[{}]'.format(', '.join('{:02x}'.format(x) for x in b)) 215 | 216 | 217 | def main(assembler, files: Iterable[str]) -> int: 218 | passes = failures = errors = 0 219 | 220 | if len(files) == 0: 221 | print("No tests specified, so sourcing from tests directory.") 222 | script_dir = os.path.dirname(os.path.realpath(__file__)) 223 | files = glob.glob(os.path.join(script_dir, '**', '*.asm'), recursive=True) 224 | 225 | for filename in files: 226 | print('{} '.format(filename), end='') 227 | with open(filename, 'r') as infile: 228 | lines = list(map(lambda s: s.strip(), infile)) 229 | checks = find_check_directives(lines) 230 | logging.debug('Found {} check(s) in source file'.format(len(checks))) 231 | args = find_assembler_args(lines) 232 | logging.debug('File declares assembler args \'{}\''.format(args)) 233 | 234 | (status, binary, console) = run_assembler(assembler, filename, args) 235 | if status != 0: 236 | print("(exited with code {}) ".format(status), end='') 237 | #print('ERROR exited with code {}'.format(status)) 238 | #print('Output:\n{}\n'.format(console)) 239 | #continue 240 | 241 | orig_len = len(binary) 242 | offset = 0 243 | for (i, check) in enumerate(checks): 244 | try: 245 | logging.info('Running checker {} at offset {}'.format(i, offset)) 246 | (binary, console) = check(binary, console) 247 | passes += 1 248 | offset = orig_len - len(binary) 249 | print('PASS') 250 | except AssertionError as e: 251 | failures += 1 252 | print("FAIL at binary offset {}: {}".format(offset, e)) 253 | print("Output:\n{}\n".format(console)) 254 | 255 | print("\nCompleted with {} passes, {} failures and {} errors" 256 | .format(passes, failures, errors)) 257 | return failures + errors 258 | 259 | 260 | if __name__ == '__main__': 261 | if len(sys.argv) > 1: 262 | [assembler_binary, *files] = sys.argv[1:] 263 | if main(assembler_binary, files) > 0: 264 | sys.exit(1) 265 | else: 266 | print("Usage: {} assembler-exe [asm-file...]".format(sys.argv[0]), file=sys.stderr) 267 | -------------------------------------------------------------------------------- /tests/undoc-ez80.asm: -------------------------------------------------------------------------------- 1 | ; Test ez80 undoc insts. 2 | ; RUN-ARGS: -E 3 | ; CHECK: ED3012 ED3034 ED70 ED70 ED70 ED70 4 | 5 | in0 f, (12h) 6 | in0 (34h) 7 | 8 | in f, (bc) 9 | in (bc) 10 | 11 | in f, (c) 12 | in (c) 13 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS_H 2 | #define __UTILS_H 3 | 4 | #include "storage.h" 5 | #include "modp_ascii.h" 6 | 7 | #define MAX_ARG_LEN 256 8 | #define ARG_CONTEXT_INITIALIZER {"", true, false} 9 | typedef struct arg_context 10 | { 11 | char arg[MAX_ARG_LEN]; 12 | bool fExpectingMore; 13 | bool fLiteralArg; 14 | } arg_context_t; 15 | 16 | char *eval (const char *expr); 17 | bool is_end_of_code_line (const char *ptr); 18 | bool is_name_char (char c); 19 | char *skip_to_next_line (const char *ptr); 20 | char *skip_to_name_end (const char *ptr); 21 | char *skip_to_line_end (const char *ptr); 22 | const char *skip_to_code_line_end(const char *ptr); 23 | char *skip_whitespace (const char *ptr); 24 | bool is_arg (char test); 25 | char *next_code_line (char *); 26 | char *next_expr (const char *ptr, const char *delims); 27 | #define read_expr(ptr, word, delims) (read_expr_impl((const char ** const) ptr, word, delims)) 28 | bool read_expr_impl(__inout const char ** const ptr, char word[256], const char *delims); 29 | char *extract_arg_string(const char ** const ptr, arg_context_t *context); 30 | char *parse_args (const char *ptr, define_t *define, list_t **arg_local_labels); 31 | char *replace_literal_args (const char *ptr, define_t *define, list_t **curr_arg_set); 32 | bool line_has_word (char *ptr, const char *word, int word_len); 33 | char *escape_string(const char *input); 34 | char *reduce_string (char* input); 35 | char *fix_filename (char *filename); 36 | bool is_abs_path(const char *filename); 37 | char *strup (const char *input); 38 | char *get_file_contents (const char *filename); 39 | void release_file_contents(char *contents); 40 | char *change_extension (const char *filename, const char *new_ext); 41 | bool define_with_value (const char *name, const int value); 42 | #if defined(MACVER) || defined(WIN32) 43 | 44 | //char *strndup (const char *str, int len); 45 | #define strndup(str, len) strncpy((char *) calloc(1, (len) + 1), (str), (len)) 46 | 47 | 48 | #endif 49 | #ifdef MACVER 50 | int strnlen (const char *str, int maxlen); 51 | #endif 52 | 53 | char *expand_expr (const char *expr, bool search_local = true); 54 | 55 | extern char *curr_input_file; 56 | extern int line_num; 57 | extern bool error_occurred; 58 | 59 | void show_error_prefix (const char *zcif, const int zln); 60 | void show_error (const char *text, ...); 61 | void show_fatal_error (const char *text, ...); 62 | void show_warning_prefix (const char *zcif, const int zln); 63 | void show_warning (const char *text, ...); 64 | 65 | #endif 66 | 67 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | #define SPASM_NG_VERSION "v0.5-beta.3" 2 | -------------------------------------------------------------------------------- /version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | getversion() { 3 | if [ -f version_base.h ];then 4 | VERSION=`cat version_base.h | head -n 1 | cut -f3 -d' ' | tr -d '"'` 5 | else 6 | VERSION="v0.1-alpha.1" 7 | fi 8 | if [ -d ".git" ]; then 9 | test -z "$FORCE_NO_GIT" && which git 2>&1 >/dev/null 10 | if [ "$?" = "0" ];then 11 | git describe --exact-match --tags HEAD 2>/dev/null >/dev/null 12 | if [ "$?" = "0" ];then 13 | echo "$VERSION" 14 | else 15 | echo "$VERSION.git"`date "+%Y%m%d"` 16 | fi 17 | else 18 | echo "$VERSION" 19 | fi 20 | else 21 | echo "$VERSION" 22 | fi 23 | } 24 | 25 | if [ "$1" = "set" ];then 26 | echo '#define SPASM_NG_VERSION "'`getversion`'"' > version.h 27 | which git 2>&1 >/dev/null && git describe --exact-match --tags HEAD 2>/dev/null 28 | if [ ! "$?" = "0" ];then 29 | echo '#define SPASM_NG_GITREV "'`git rev-parse --short HEAD`'"' >> version.h 30 | fi 31 | elif [ "$1" = "dpkg" ];then 32 | getversion | sed -r 's/^.{1}//' | sed 's/[-]/~/' | sed 's/[.]git/+git/' | sed -e ':begin;s/[.]//2;t begin' 33 | else 34 | getversion 35 | which git 2>&1 >/dev/null && git describe --exact-match --tags HEAD 2>/dev/null >/dev/null 36 | if [ ! "$?" = "0" ];then 37 | test -z "$FORCE_NO_GIT" && echo "Git revision: "`git rev-parse --short HEAD` 38 | fi 39 | fi 40 | -------------------------------------------------------------------------------- /version_base.h: -------------------------------------------------------------------------------- 1 | #define SPASM_NG_VERSION "v0.5-beta.3" 2 | --------------------------------------------------------------------------------