Zeus-ish Z80 cross assembler (I suggest you copy this text out and paste it into a text editor/window where you can search it easily. This text and the example files and games on my website form the documentation for Zeus.) It's all the fault of She Who Must Be Obeyed... "I've been converting some of my old games files" "Uh-huh" "Recovered them for some old floppies" "Uh-huh" "Bloody hard work, I might add..." "Yep" "Not helped by the rotten tools I had to use" "Uh huh" "Frankly I could have written a better assembler in the time I wasted" "I should hope so" "From scratch, I'm talking about the whole bloody thing!" "Surprised you didn't. Anything on the TV?" "The useless one I had to use doesn't even know all the instructions!" "Strictly come dancing is on, do you want to watch that?" "Neurgh! Right! I'll do it... I'll show 'em!" "That's nice dear... What's an assembler?" So, as you can see I had no alternative whatsoever to writing this... Gah. Long time (fifteen years) since I've written a Z80 assembler, back then it took two days to do in 80x86 assembler, so I'm assuming it'll be a doddle in Delphi... Session 1 started 22:22 4/Oct/2008 - Start with symbol table and evaluator from the FPGA risc assembler They'll need modifying but that can wait. The symbol table is naive but works - Implemented ds - Implemented align - Implemented db,dw,dl - Implemented fixed opcodes - Implemented INC/DEC - Implemented JP/CALL/RET - Implemented just about everything except the ROT/BIT/LD set Session 1 ends 05:11 5/Oct/2008 Session 2 started 13:19 5/Oct/2008 - Start with ROT, they're easy. - Note! Z80 documentation on web is wrong, "RR" missing. Called "RL" again - Implemented ROT 13:38 - Implemented STRICT and EXTENDED 13:44 - Cleaned up editor exit behaviour with clean files - Implemented BIT 14:00 (Oh gawd, only the LD set left) - Note! Z80 documentation on web is wrong, "LD A,R LD R,A" missing. - Implemented LD A,[full set] 14:51 - Implemented LD [8-bit reg],[full set] 15:09 - Implemented LD [16-bit reg],[full set] 15:41 - Note! Correctly handles indirect addressing issue ld a,(4*5)+1 is not ld a,(n) - Note! Zeus has extra 16-bit loads, eg, ld hl,de... This doesn't yet - All Z80 instructions full implemented. 16:21 - Now we need to bring the evaluator up to scratch. - Added support for $hex, #hex, and either . $ or * for PC 16:34 - Added &,| and ^ as aliases for and, or and xor 16:40 - Added single character string literals to evaluator 16:45 - Assembles Forbidden Planet 16:51 Sluggish though, FP (14,400 lines) takes almost a second I'd guess from previous assemblers that a hashed symbol-table would speed this up by a factor of ten or so... Not really bothered. [Turns out it's much faster except for the long time Delphi's memo takes just to provide the text in the first place...] Session 2 ends 16:58 5/Oct/2008 Session 3 started 21:20 5/Oct/2008 - Ported my binary -> szx file convert routines and added them. FP didn't run. A binary comparison of the output with it assembled by my dev tools showed JR displacements weren't calculated correctly. Forgot you had to store the PC at the start of the instruction so it didn't change as bytes were planted. Doh! Still, it has been 15 years since I last wrote a Z80 assembler... I've said that already, haven't I? Evidence of senility... Now generates same bytes as my development assembler. Project finished? So, it took about 11 hours to write a complete and useable Z80 assembler. Life in the olde dog yet... - Added a QAD find function to editor 22:07 Session 3 ended 22:52 5/Oct/2008 - Added some backward compatibility stuff for Spectrum Zeus. '!' for OR (Note - v1.1 revises this and makes ! and NOT behave as logical nots) 'ENT' is ignored. Well, that's as much as I can be bothered to write for now. All that needs adding to the assembler is conditional assembly and some looping structures. That's about an hour's work. I might add a real editor at some point, but not now. 7 Oct 2008 - Felt frisky after a successful day at work, so I've added some of Zeus's extensions for compatibility. - DEFM aliases DEFB - // comments - Added support for variables as well as labels - Added predefined true/false/version labels - Filtered out Camel Case from internal tokens so they can be used as labels - Added displacement support, both as "DISP" and as "ORG ADDR,DISP" - Added struct/send for declaring structures - Added import_bin for importing raw data - Added more documentation files - Added jump to error line if error message clicked on in report window - Added loop/lend - Added repeat/until - Added while/wend - Added if/elseif/else/endif Released as V1.1 (zeusver = 1) I wanted to call it SPASM, but that's been taken. I've called it Zeus, which is a little unfair to the real Zeus (part of my in-house development tools) but he'll just have to live with it. Dedicated to all those Z80 instructions lost in action. You will not be forgotten... [later] From starting out as an amusing diversion this has developed into an interesting tool... I'm now trying out ideas (mostly implementation details) I'll use in my next generation of development tool assemblers. 7 Oct 2008 - Added a hashed symbol-table (virtually the same as Zeus-2 had in 1984) and revised token handling to use it... Much faster passes. - Added support for Zeus's 16-bit load set. Released as V1.2 (zeusver = 2) 10 Oct 2008 - Added the Diana disassembler. - Added support for multiple files (syntax -> include "c:\fred.asm") - Added support for exporting symbol tables (syntax -> export_sym "c:\fred.txt" [, numeric options] See examples for more details. Released as V1.3 (zeusver = 3) 13 Oct 2008 - Added proc/endp to provide local labels. (see zeus_ex-macros.asm) - Added parameterless macros. (see zeus_ex-macros.asm) Released as V1.4 (zeusver = 4) 13 Oct 2008 (later) - Added flow control checking (noflow - see zeus_ex_macros.asm) - Changed local label format and added global access to local labels - Added segments (see zeus_ex_segments.asm) Released as V1.5 (zeusver = 5) 16 Oct 2008 - New expression evaluator supports strings and floats - Added various string and numeric functions - Changed symbol table reporting options Released as V1.6 (zeusver = 6) 16 Oct 2008 (later) - Fixed a bug (had to have one sooner or later) in multiple source handling. Released as V1.61 (zeusver = 7) 18 Oct 2008 - Added full support for macro parameters - Removed curly-braces comment style. I'm sorry if anyone has used these extensively, but I intend to use these as code block delimiters Released as V1.7 (zeusver = 8) 19 Oct 2008 - Added support for '==' and ':=' - Removed a couple of debugging calls and changes I'd forgotten to disable Released as V1.71 (zeusver = 8) 20 Oct 2008 - Improved macro handling when macros are passed as parameters to macros - Added '\' as a string to token conversion operator. Allows calculated instructions, etc. Released as V1.8 (zeusver = 9) 20 Oct 2008 (later) - Looking at producing a command line version. More of a PITA than it should be... - Added support for multi-line expressions inside brackets. See zeus_ex_macro. Released as V1.81 (zeusver = 9) 23 Oct 2008 - Created a command-line only version for use with other editors. (zcl.exe) - Extended output options to support hex files and multiple file writes Released as V1.9 (zeusver = 10) 24 Oct 2008 - Tidied up command line version. - Added support for roman numerals. Use zero-R as a prefix (i.e. 0rMMVIII) and zeusprintroman At last the wait is over - a Z80 assembler that understands roman notation! "Vos quod regam orbis terrarum!!!!!" - Nero. "Haud incultus hex!" - crem. Released as v1.91 (zeusver = 10) 25 Oct 2008 - Saves screen position Released as v1.92 (zeusver = 10) 26 Oct 2008 - Added syntax highlighting editor. Very experimental. The editor is a sub-set of the one used in the real Zeus. This implementation has always been a pain in the arse, so I expect trouble... Use the config tab to enable it. Released as v2.00 (zeusver = 10) 29 Oct 2008 - Added horizontal scroll bar to Diana definition window Released as v2.01 (zeusver = 10) 6 Nov 2008 - Added various option variables. - Added DG pseudo-op for graphical character definition - Added a couple of options to Diana. See the help text. Released as v2.10 (zeusver = 11) 7 Dec 2008 - Added DF and DEFF which plant IEEE 32-bit floating-point values. Note that these are not compatible with ZX Spectrum floating point values. (I would support ZX format as well but I can't find a definition of it on t'web). - Added TIMESTR plants the time of assembly - eg "db TIMESTR" - Added LOW returns the low 8-bits of a value - eg "db low $1278 ; plants $78" - Added HIGH returns the high 8-bits of a value (shifted right) - eg "db high $1278 ; plants $12" - Added WORD returns the low 16-bits of a value - eg "dw word $12345678 ; plants $5678" (For historical reasons these functions do not require brackets). - Added ZEUSRAND returns a 32-bit random number - Added SUM_MEM calculates the byte sum of an area of memory - eg SUM_MEM(Start,Length) - Added XOR_MEM calculates the byte xor sum of an area of memory - eg XOR_MEM(Start,Length) (Both used to calculate the correct values of simple CRC's for blocks of memory) - Added DBX and DBXO These allow simple encryption (obfuscation, really) of data. DBX takes a byte xor value followed by the same parameters as a DB statement. It then plants the bytes xor'd with that first value. By default DBX xor's each byte with the same value. If you want zeus can change that xor value dynamically by adding a value to it after every byte. Use DBXO to set this increment. - Added :: Scope selector used inside procedures/macros as a prefix to force a symbol to use the global symbol table eg "::Fred = ::Fred+1" - Increased code memory size to 256K To support paged memory designs. The sxz output routines don't support this directly yet. - Changed hex code display to eliminate redundant lines (lines that are all zeros) With 256K of memory it was taking too long to display the code ;) Note that you will need to delete the z80.tok file in order for Zeus to update it if you want Zeus to include these new tokens in the syntax highlighting. Released as v2.20 (zeusver = 12) 8 Dec 2008 - Changed file routines so that they will create paths if they don't already exist. Released as v2.21 (zeusver = 12) 9 Dec 2008 - Changed zeusrand to use Mersenne Twister algorythmn - Delphi's was a joke. Zeus now reseeds the MT algorythmn before each pass. use "zeusrand [value]" to reseed with a constant value. Released as v2.22 (zeusver = 13) 9 Dec 2008 - Revised pass control scheme. Don't know what I was thinking of when I wrote it... - Revised text extraction from syntax highlighting editor. Released as v2.30 (zeusver = 14) 12 Dec 2008 - Bug the third :( When I increased memory size I screwed up "import_bin"... Now fixed Released as v2.31 (zeusver = 15) 6 Feb 2009 - Extended "import_bin" to support loading parts of binary files. (e.g. import_bin "filepath", memory address, length to read, offset into file) It also now supports loading to default PC address (e.g. import_bin "filepath") Released as v2.32 (zeusver = 16) 16 Sept 2009 - Added "TextTidy" option to the edit menu. Just does some simple text reformatting. It usually improves the look of badly formatted source code, but it's easily fooled so I suggest comparing the code generated before and after use to be sure it hasn't altered the source's meaning. Released as v2.33 (zeusver = 16) 30 Sept 2009 - Added backup file options. (See editor options in config tab) - Bug-fix. Unary operators applied to string constants were ignored. Released as v2.34 (zeusver = 17) 26 Aug 2010 - Added SETRADIX pseudo-op to define the radix of numbers with leading zeros This is to aid compatibility with Grahams M80 sources Released as v2.40 (zeusver = 18) 30 Aug 2010 - Added code display option to editor (right-click on an editor) Released as v2.41 (zeusver = 18) 30 Aug 2010 - Added binary comparison option to zeus This is complicated and needs an example source written Released as v2.50 (zeusver = 19) 17 May 2011 - Changed pass end detection logic - Added output_db and output_unit filetypes Released as v2.51 (zeusver = 20) 17 May 2011 - Cured a bug with undefined label reporting - Cured a bug with saving the flow warning option Released as v2.52 20 May 2011 - Added a version of the Delphi Spec spectrum emulator, written by Chris Cowley. This does not handle memory contention but is very useful for debugging code, detecting logical errors, memory access errors and so forth. Released as v2.60 (zeusver = 21) 27 May 2011 - Improved the emulator - Added "emulate_spectrum" pseudo-op Released as v2.61 (zeusver = 22) 29 May 2011 - Added breakpoints to my editor windows Released as v2.62 (zeusver = 22) 30 May 2011 - Improved a few bits and pieces Released as v2.63 (zeusver = 22) 2 June 2011 - Fixed a bug where Zeus didn't fault ld ixl,l and similar illegal loads. - Adding the option to single-step the emulator in the editor F7 = step, F8 = run, F9 = stop Some register inspection works (F7 + hover the cursor over a register) Released as v2.64 (zeusver = 23) 9 June 2011 - Fixed the memory display (was using alt-BC and alt_DE register values, doh!) - Rather more register/memory inspection works (F7 + hover the cursor over a register or memory address, ie, (ix+dWhatever) or the fred bit of ld a,(fred) Released as v2.65 (zeusver = 23) 29 Feb 2012 - Adding some half-assed support for different models of Spectrum Released as v2.70 (zeusver = 24) 26 Mar 2012 - Removed a bug in the Z80 emulator's parity handling (there wasn't any) - my fault. - Changed Spectrum emulation, joystick behaviour mainly. Released as v2.71 (zeusver = 25) 27 Mar 2012 - Added simple compression option for tape-loaders. See next entry for details. Released as v2.72 (zeusver = 26) 27 Mar 2012 - Updated compression option The idea is for Zeus to take a single, contiguous block of memory specified by the user and compress it, then add the code to expand it again to the start so it will be expanded again when run. The compression algorythmn is currently simple and just encodes runs of identical bytes. The pseudo-op is zeuscompress and it requires at least three parameters: START_ADDR - the start memory address of the block to be compressed. END_ADDR - the end address of the block to be compressed. (The byte after the last one you need). EXEC_ADDR - the address (presumably inside this block!) of the code to execute after it is expanded. These can be followed by up to two more optional parameters: RESULT_ADDR - where the compressed block is located in memory (defaults to $5B00) FLAGS - Option flags. At the moment only bit 0 is used. If set, the expander flashes the border during expansion. So, "zeuscompress $8000,$9000,$8080" would compress the block of memory from $8000..$8FFF inclusive and put the resulting compressed data at $5B00. Call it at $5B00 to expand it, and after expansion it would call the start of the code at $8080. So, "zeuscompress $8000,$9000,$8080,$F000,1" would compress the block of memory from $8000..$8FFF inclusive and put the resulting compressed data at $F000. Call it at $5B00 to expand it, and the border would flicker during expansion. After expansion it would call the start of the code at $8080. One issue - a big one - at this point was that Zeus did not know how large the compressed block was until after it had finished passing, generating the code and compressing it. So you had to inspect the code zeus generated to find how large the block is. This behaviour was revised in v3.51 - see below. Released as v2.73 (zeusver = 27) 10 Jul 2014 - Added roman, morse, bitmap and invbitmap functions Released as v2.74 (zeusver = 28) 30 Sept 2014 - Changed import_bin file handling to make it more civilised Filenames with no path will now be assumed to come from the same directory as the main sourcefile Released as v2.75 (zeusver = 29) 1 Oct 2014 - Removed a bug which prevented the use of labels in nested macros 12 Dec 2014 - Added FOR/TO/STEP/NEXT, TOSTR, TOHEX, LABEL (See v3.57 for amendments to LABEL) LABEL has three uses: LABEL(name) defines a label from a string and gives it the current address as a value LABEL(name,val) defines a label from a string and gives it the value Val LABEL(name) in expressions returns the value of a label given as a string these are mainly intended for automatic code generation in macros/loops. - Added HX,LX,HY,LY as aliases for IXH etc, DUP/EDUP for LOOP/LEND these can save some editing when working with sources for other assemblers. - I have changed the string slicing so that the index can optionally start from 0 set this with "zoStringIndexFromZero" - Enabled some of the the profile timing in the free version. You can mark code to be profiled like this: PROFILE = true nop ; This will be counted (NOP takes 4 cycles) PROFILE = false Or you can mark blocks of memory like this: PROFILE Start,Length Usually you'd do that at the end of the source-file or it will be overwritten as code is dumped If you want to see the profile data on a line by line basis you need to use the syntax highlighting editor and right-click to toggle profiling. It will update live if you want (F7/F8) You can elect to profile the number of TStates or the number of times an instruction is executed. Released as v2.80 (zeusver = 31) - Enabled the SWITCH,CASE,ENDCASE,ENDSWITCH,DEFAULT in the free version. - Enabled OPTIONSIZE and OPTIONLIST in the free version. Released as v2.81 (zeusver = 32) 1 Jan 2015 - Various bits of tidying up in the emulator. Added stack display. Removed unnecessary analog joystick panel (just use the screen window) Register values can now be edited (stop the emulation, change value and hit return) PC cursor keys now emulated. - Removed support for non-syntax highlighting editor, too many options need it. Released as v2.82 (zeusver = 32) 3 Jan 2015 - Added the rescale menu option and the notes tab. Released as v2.83 (zeusver = 32) 25 Feb 2015 - Increased the number of include files allowed from 32 to 255. Released as v2.84 (zeusver = 33) 26 Feb 2015 - Local paths on include files are now supported. (Mutters darkly about bugs in other people's code) Released as v2.85 (zeusver = 34) 25 Mar 2015 - Added support for saving 128K szx files. If you set [emulate_spectrum "128K"] and then use output_szx it will save 128K .szx files. If you set [emulate_spectrum "3"] and then use output_szx it will save Model 3 .szx files. How Zeus's memory maps into the blocks isn't straightforward. See the notes below for v3.42 of Zeus. Released as v2.86 (zeusver = 35) 12 Oct 2015 - Relaxed requirement to have "A," on 8-bit add,adc,sbc instructions. (Unless STRICT) So as well as "ADD A,B" Zeus will accept "ADD B", for example. - Some changes to give Zeus compatibility with Pasmo sources, as far as seems reasonable. The biggest issue was their differing operator precedence rules. There's now a flag in Zeus to support low priority logical operators if it really must. [mutter] Released as v3.00 (zeusver = 36) 23 Nov 2015 - Replaced the Spectrum emulator with one that correctly emulates the Z80 flags, understands memory contention and has cycle-accurate timing... Delphi Spec was extremely useful but I've always wanted an emulator that could handle border timing graphics; having invented them with Tony (the space-invader figure that lurks in the border during High Score display in Dark Star) it always nagged at me that the emulator in Zeus couldn't display them... so I finally bit the bullet and wrote one... urgh... I'd say I'm grateful to all the people who've put information out on the web, much of which is entirely accurate, about the Spectrum hardware, except that without them I probably wouldn't have bothered doing this at all and saved myself several hours of farting about. The new emulator should also handle "bi-colour" mode software that dynamically changes the pixel/attribute data in real-time. LIMITATIONS - tested with a 48K Spectrum. Should support 128K accurately. Doesn't attempt to accurately support anything else. - Added output_tzx so that Zeus can directly generate TZX tape files. This was updated, see the documentation for v4.04 and/or v3.52 below. - The profile times are now shown as ccccc , ttttt, where ccccc is the T-State count in the current video frame when this line was last executed, and ttttt is the total time spent in it. The time in the frame is useful when writing code that dynamically alters the border or attributes. In CP/M emulation there's no concept of video frames so this doesn't apply. - Removed support for (* comment *) comments... Too much source code has (* or *) in it. I dithered about this, since it breaks compatibility, but too many old files have stuff like "org (* or blah de bloody blah)" in them, and it gets annoying having Zeus treat it as comments. - Changed the way Zeus supports local labels in nested structures. Shouldn't be a concern to anyone unless they're doing something narsty with macros. Zeus is now more forgiving about some things than it used to be. - Added MULTIPASS support... Do not confuse MULTIPASS with the normal run of the mill multiple passes that Zeus performs anyway... consider a source that has options, so that the user can choose to generate different versions of it. Before MULTIPASS the user would have to edit the source to set the options required, assemble the source, change the options, assemble the source, etc, etc, for each set of options. What MULTIPASS allows is the assembler to automatically run several times with different options. I will provide example source code at some point. - Added support for emulating a simple Z80 machine, with 64K RAM + CF card interface and serial port. This is capable of running the software for a number of simple Z80 machines, notably that of Grant Searle's CP/M project (with the UART code modified)... I will provide example files for running/debugging CP/M on this hardware. See www.desdes.com/products/oldfiles... - Added "import_hex" for importing hex files. import_hex "fred.hex",StartAddr,Length Unlike binary files, hex files contain addresses for the data they contain. StartAddr will only work correctly if the first address mentioned in the file is the lowest address in the file, ie, has the smallest value. This may not always be the case... import_hex will deduce the format of the hex file if it can and copes with most standard 8-bit hex files (Intel, S-Record, etc). Unlike binary files Zeus provides no support for offsetting into hex files, sorry. - Lots of other changes I have forgotten or don't feel like sharing ;) Released as v3.10 (zeusver = 37) 12 Dec 2015 - Added NASCOM 2 emulation. See the usual place for support files... The NASCOM keyboard is mapped onto the PC keys as well as reasonably possible. The "Nascom.zip" file contains several games including The Keys of Kraal, they all run well. The "Upload File" buttons will upload "*.CAS" files to the NASCOM during emulation. - Added MEM_ROM to allow areas of memory to be set as ROM, they cannot then be changed by executing code. - Added an option (See config tab) to allow some of the memory flags to be displayed on the Zeus Code tab. This now updates the code tab whenever it's opened, bit slower but more useful. Released as v3.11 (zeusver = 37) 12 Dec 2015 - Widened the borders in the Spectrum emulator. - Stopped the assembler resetting the editor back to the start of the first source. Released as v3.12 (zeusver = 38) 15 Dec 2015 - Added homebrew emulation. A right bugger, actually, trying to get the LED brightnesses more or less right. Released as v3.20 (zeusver = 39) 17 Dec 2015 - Added ULA+ support and changed ':' handling for increased compatibility with neolithic source syntax Released as v3.30 (zeusver = 40) 23 Dec 2015 - Added support for emulating the Membership Card Z80 project The OS can be found in membership.zip, assemble and emulate membership.asm to see it operating. There are instructions in their PDF file in the zip. Released as v3.31 (zeusver = 40) 23 Dec 2015 - Added support for the membership card serial port. Click on the terminal window in the emulator2 tab while it's running. Released as v3.32 (zeusver = 41) 23 Dec 2015 - Added support for formatted strings. So you can optionally use stuff like "Hello\r\n" The syntax highlighting fails for some obscure cases, an example is "\" " I need to rip that rancid code out and rewrite it. - Changed 48K Spectrum emulation to start code as if loaded from tape, with the interrupt running and so on. - Added "xl", "xh", "yl", "yh" as aliases for the extended register set "ixl" etc... So that's IXL, LX and XL now... how many variants of names for these are there? [historical note - Zeus-2 was the first Z80 assembler to support the extended registers and we defined them as IXL, IXH and so on. That used to be the standard, but lots of things about Z80 history seem to have been forgotten...] 18 May 2016 - Added template file for 48K Spectrum coding. (file menu New) Changed Zeus code tab behaviour to show the rom. I'm going to alter the whole way this displays when I get a moment or two. - Enabled GIF file generation in ZX Spectrum mode. See emulator tab. Released as v3.33 (zeusver = 42) 20 May 2016 - Enabled GIF generation in ULA+ mode (oops) and NASCOM-2 mode. Released as v3.34 (zeusver = 42) 31 May 2016 - Changed profiling instruction counting so ix/iy instructions count as 1 per execution. - Changed TextTidy option (improved tidying for *really* vile sources) - Added "TextUglify" for those who prefer tabs after mnemonics. - Added some notes. - Changed ZeusEmulate_PC to Zeus_PC, Zeus_SP to Zeus_SP,EmulateStartInCycle to Zeus_Cycle - Changed editor focusing code and profile update timing. - Increased Spectrum FAST mode to 30MHz... Released as v3.35 (zeusver = 43) - Oops! One of my dafter ideas in the last update was stopping the terminal window from becoming the active control... this broke the CP/M emulator. Now fixed. Released as v3.36 (zeusver = 43) - Started adding the Battle mode... - Added the "gamble" menu option for people who like garish colours in their syntax highlighting. There is no ungamble option, but rest assured that Zeus will be its normal restrained and relaxing self again after you restart it... - Improved the syntax highlighting option for formatted strings (uses the config option) - More work on the Battle mode... Released as v3.37 (zeusver = 44) - Cured a small bug in the syntax extension that allowed "CP A,0", "OR A,B" and so on. It was allowing registers other than A to be used. - Changed single-step to skip LDIR and the other repeat instructions with one key press. (If you want to stop after every iteration uncheck the "Step skips LDIR(etc)" checkbox which is on the register display panel) - More work on the Battle mode. Not yet functional. - To improve compatibility with the old GENS assembler Zeus now allows: - spaces in hex numbers. So "# FF" now is valid and means "#FF"... "# 1 2 3 4" means "#1234" - The symbol "@" to be used for OR. Note that GENS uses "!" for XOR, whereas Zeus uses "!" for NOT So GENS sources that use "!" will need the "!" to be changed to "xor" or "^" Historical note: I believe Zeus was the first assembler to allow spaces in numbers, back in 1983. I decided to permit them in binary numbers so bit-fields could be shown clearly. But I drew the line at allowing them in hex/decimal numbers as they were less useful there and people found them confusing... Recently I have seen some Russian sources that have spaces in hex numbers, which may just be a printing mistake, but for compatibility I've changed Zeus to support them. It shouldn't break anything. - Enabled support for memory panel views in the free versions of Zeus. (A right bugger too, since it meant re-writing the bloody hex viewer from scratch, as the dynamic updating relied on other things I don't intend to give away in my free assemblers... gah...) The hex panels show as separate windows. These can be dragged and resized as required. Zeus attempts to remember your preference for hex window placement across sessions. This is stored on the computer not in the source file, as placements generally relate to a specific computer's display hardware. The syntax is "zeusmem Address, Name, Width, ShowAddress, ShowText, HideOnEdit" where: Address is the Z80 memory address to start showing Name (optional) is the name to show in the caption bar Width (optional) is the number of bytes to show per line ShowAddress (optional) is set TRUE to show the start address of each line ShowText (optional) is set TRUE to show the data as text characters HideOnEdit (optional) is set TRUE to hide the memory panel when editing (added 16/1/2018) Examples: zeusmem $4000,"Screen",32,true,false ; Show the Spectrum screen memory zeusmem $5800,"Attrs",32,true,false ; Show the Spectrum attributes zeusmem $C000,"Maze",128,true,false ; If playing Halls, show the maze data. zeusmem $C000,"Maze",128,true,false,true ; If playing Halls, show the maze data, but not while editing Note 1 - there is an option on the config tab to force the memory panels to use the same font as the editor. This is optional, as the Dina font I prefer for editing does not scale down to very small sizes. Changes to this option will probably require an assembler restart, or at least the memory panels being undisplayed/restored. Note 2 - should the memory panels not appear as expected, perhaps after a change in the computers display configuration, the edit menu rescale option will restore them to their initial default positions. - Added zeussyntaxhighlight pseudo-op to allow the users to play with the editor syntax highlighting. The syntax is "zeussyntaxhighlight Index, Red, Green, Blue, Bold" where: Index is a number which specifies the type of syntax item 0 = Tokens 1 = Identifiers 2 = Comments 3 = Constants 4 = LineNumbers 5 = Markers 6 = Errors 7 = Margin data bytes .. 16 = Memory Panel Addr Background 17 = Memory Panel Addr Foreground 18 = Memory Panel Data Background 19 = Memory Panel Data Foreground 20 = Memory Panel Changed Data Background 21 = Memory Panel Changed Data Foreground .. 100 = Diana background 101 = Diana foreground 102 = Diana defn background 103 = Diana defn foreground .. 249 = editor "marked line" colour. [not used in this version] 250 = margin separator line colour 251 = margin separator line2 colour 252 = current executing line background colour 253 = current editing line background colour 254 = margin background colour 255 = editor background colour 300 = CP/M terminal Normal background colour 301 = CP/M terminal Normal foreground colour 302 = CP/M terminal Highlight background colour 303 = CP/M terminal Highlight background colour Red,Green,Blue specify an RGB colour (0,0,0 = black, 255,0,0 = bright red, 255,255,255 = white, etc) Bold is optionally true or false to select bold or normal font. It defaults to false. So, Zeus's normal syntax highlighting uses the following: zeussyntaxhighlight 0, $00,$00,$00, true ; Set the token colour zeussyntaxhighlight 1, $00,$00,$00, false ; Set the identifier colour zeussyntaxhighlight 2, $00,$00,$FF, false ; Set the comment colour zeussyntaxhighlight 3, $00,$00,$00, false ; Set the constant colour zeussyntaxhighlight 4, $00,$00,$00, true ; Set the line number colour zeussyntaxhighlight 5, $00,$00,$00, true ; Set the marker colour zeussyntaxhighlight 6, $FF,$00,$00, true ; Set the error colour zeussyntaxhighlight 7, $00,$00,$00, false ; Set the margin data colour zeussyntaxhighlight 16, $00,$00,$30 ; Set the memory panel addr background zeussyntaxhighlight 17, $00,$FF,$FF ; Set the memory panel addr foreground zeussyntaxhighlight 18, $00,$00,$30 ; Set the memory panel data background zeussyntaxhighlight 19, $C0,$C0,$FF ; Set the memory panel data foreground zeussyntaxhighlight 20, $00,$00,$30 ; Set the memory panel new data background zeussyntaxhighlight 21, $FF,$FF,$FF ; Set the memory panel new data foreground zeussyntaxhighlight 250, $D8,$D8,$D8 ; Set the margin separator line colour zeussyntaxhighlight 251, $F8,$F8,$F8 ; Set the margin separator line2 colour zeussyntaxhighlight 252, $00,$FF,$FF ; Set the current executing line background colour zeussyntaxhighlight 253, $F8,$F8,$F8 ; Set the current editing line background colour zeussyntaxhighlight 254, $F0,$F0,$F0 ; Set the margin background colour zeussyntaxhighlight 255, $FF,$FF,$FF ; Set the editor background colour Or you could use something like these for an alternate light on dark editor: zeussyntaxhighlight 0, $FF,$FF,$FF, true ; Set the token colour zeussyntaxhighlight 1, $FF,$FF,$FF, false ; Set the identifier colour zeussyntaxhighlight 2, $00,$C0,$FF, false ; Set the comment colour zeussyntaxhighlight 3, $FF,$FF,$FF, false ; Set the constant colour zeussyntaxhighlight 4, $FF,$FF,$FF, true ; Set the line number colour zeussyntaxhighlight 5, $FF,$FF,$FF, true ; Set the marker colour zeussyntaxhighlight 6, $FF,$FF,$FF ; Set the error colour zeussyntaxhighlight 7, $80,$FF,$FF ; Set the margin data colour zeussyntaxhighlight 100, $FF,$FF,$FF ; Diana background zeussyntaxhighlight 101, $00,$00,$A0 ; Diana foreground zeussyntaxhighlight 102, $FF,$FF,$FF ; Diana defn background zeussyntaxhighlight 103, $00,$00,$A0 ; Diana defn foreground zeussyntaxhighlight 249, $00,$00,$A0 ; Set the "marked line" colour. [not used in this version] zeussyntaxhighlight 250, $00,$00,$A0 ; Set the margin separator line colour zeussyntaxhighlight 251, $00,$00,$C8 ; Set the margin separator line2 colour zeussyntaxhighlight 252, $00,$C0,$C0 ; Set the current executing line background colour zeussyntaxhighlight 253, $10,$40,$80 ; Set the current editing line background colour zeussyntaxhighlight 254, $20,$20,$90 ; Set the margin background colour zeussyntaxhighlight 255, $00,$00,$80 ; Set the editor background colour Memory panels can also be recoloured, note that it may take a couple of assemble cycles for the changes to propagate depending on how you have placed the colour changes/panel definitions zeussyntaxhighlight 16, $FF,$FF,$FF ; Set the memory panel addr background zeussyntaxhighlight 17, $FF,$00,$FF ; Set the memory panel addr foreground zeussyntaxhighlight 18, $FF,$FF,$FF ; Set the memory panel data background zeussyntaxhighlight 19, $00,$00,$00 ; Set the memory panel data foreground zeussyntaxhighlight 20, $FF,$00,$00 ; Set the memory panel new data background zeussyntaxhighlight 21, $00,$00,$FF ; Set the memory panel new data foreground Note that the dynamic change highlighting will automatically adapt to use whatever fore/back colours you provide. The CP/M terminal window can also be customised. zeussyntaxhighlight 300,$00,$20,$00 ; Normal background = light green zeussyntaxhighlight 301,$00,$FF,$00 ; Normal foreground = bright green zeussyntaxhighlight 302,$00,$80,$00 ; Highlight background = Medium green zeussyntaxhighlight 303,$00,$00,$00 ; Highlight foreground = Black - Changed the Zeus Code page behaviour depending on the "Show Memory Flags" option in the config tab. If you have the option to show the memory flags set then the Zeus Code tab behaviour is unchanged. If you don't have it set then the Code Tab shows two independent hex panels. The two hex panels update in real time, etc, etc. Released as v3.38 (zeusver = 45) - Various changes to the editor and emulator interactions to give them a nicer "feel" - Added NOROM as a machine option. This tells the emulator to use RAM instead of ROM for the first 16K in ZX Spectrum emulation. This allows you to write/emulate Spectrum ROMS, and/or if you include the Spectrum ROM as a source file in your projects you can single step through ROM calls if you use them... example: ---------- zeusemulate "48K","ULA+","NOROM" ; Emulate a 48K Spectrum with no ROM ; Your code here as usual ; Follow it with this to add the ROM code SpectrumRom proc ; Enclose this in proc/pend so the labels are local include "sp_rom.asm" ; Include the Spectrum rom source code mem_rom 0,$4000 ; Prevent writes from changing it. pend ; Done ---------- Released as v3.39 (zeusver = 46) - Changes to function keys. F1 - toggle zoom F2 - Zoom out F3 - Zoom in F4 - Toggle "Follow Execution" mode - tells the editor to follow the executing line or not F7 - Single Step F8 - Run F9 - Assemble F10 - Assemble and Run - Added an update rate option for the debugging displays. See the CONFIG tab. On very slow computers you might prefer to reduce the overhead caused by debugging updates. On fast ones you might prefer smoother debugging displays than the old default. Move the scroll-bar in the "Emulator Options" panel to set this to taste. - Added a phosphor decay rate option for the hex debugging displays. See the CONFIG tab. Move the scroll-bar in the "Emulator Options" panel to set the time it takes for changed byte highlights to fade away. This setting does not alter the computing overhead much, so just set this to taste. Released as v3.40 (zeusver = 46) - Added default ROMS/memory contents to all computer emulations so you can just hit reset to use them. - Fixed bug in Nascom emulation introduced in v3.37 (oops) - Added more template files Released as v3.41 (zeusver = 46) - Fixed bug in cpm boot.asm code. Download the cpm_sources.zip file... Released as v3.42 (zeusver = 46) - A few detail changes to improve assembler support for extended memory model machines. - Extended the "output_szx" so that it writes a 128K file for 128K Spectrum models. See below for an explanation of how Zeus handles 128K memory. It also outputs compressed rather than uncomressed files now. - Added "output_z80" to output *.z80 files. These will also be 128K for 128K Spectrum models. (see below) The syntax for "output_z80" is as follows: output_z80 "filename",StackPointer,StartAddress e.g. output_z80 "demo.z80",$0000,$8000 ; Puts the stack at $0000 and starts executing code at $8000 You can also just give Zeus the filename and let Zeus determine the SP and PC values from the emulator settings. Zeus_PC = $8000 ; Start at $8000 output_z80 "filename" ; Save a Z80 file A note on memory addressing in Zeus, this applied to the Spectrum machine models. (See also "zeuspage" in the documentation for v3.52, below). Most Z80 assemblers (every other one as far as I know) do not understand and cannot address more than 64K of memory. This is understandable as the Z80 only has 16-bit addressing. Zeus, however, does understand large memory addressing, up to at least 256K of memory. What this means is that for, say, 128K Spectrum hardware you can use Zeus to build the entire memory contents, and have Zeus save the whole 128K out to file formats that understand 128K of data - this includes "*.szx", "*.z80" and also MODE 3 of "*.tap" and "*.tzx" files at the moment. The mapping might be confusing, this is because the naming of Spectrum memory pages isn't obvious, and Zeus has to follow the existing names and mapping so that it can emulate code correctly. If you are writing code for the 48K Spectrum, things do what you expect and you can ignore the memory paging, because the 48K Spectrum hasn't got the ability to page memory. For the 128K Spectrum models you should consider them as having eight 16K memory pages, which have numbers 0 to 7. For compatibility with the Spectrum hardware, when assembling code they are mapped to Zeus addresses like this: Zeus addresses -> Memory Page number 00000 - 03FFF -> Map into ROM and are not normally writable. 04000 - 07FFF -> Page 5 08000 - 0BFFF -> Page 2 0C000 - 0FFFF -> Page 0 And the pages can also be addressed using these (greater than 64K) addresses. 10000 - 13FFF -> Page 0 14000 - 17FFF -> Page 1 18000 - 1BFFF -> Page 2 1C000 - 1FFFF -> Page 3 20000 - 23FFF -> Page 4 24000 - 27FFF -> Page 5 28000 - 2BFFF -> Page 6 2C000 - 2FFFF -> Page 7 (The code tab can show these higher addresses) Released as v3.43 (zeusver = 46) - Changes to the editor, to clean up searching and code execution swapping. - Improved support for debugging Spectrum ROMS. You can now access the ROMS using these (greater than 64K) addresses. 30000 - 33FFF -> ROM 0 (Normal 48K ROM, lower 128K ROM, Model 3 ROM 0) 34000 - 37FFF -> ROM 1 ( , upper 128K ROM, Model 3 ROM 1) 38000 - 3BFFF -> ROM 2 ( Model 3 ROM 2) 3C000 - 3FFFF -> ROM 3 ( Model 3 ROM 3) So, to write code to the Spectrum 48K ROM you could do this: --------- zeusemulate "48K","norom","raw" org $0000 disp $30000 Start DI ; First byte of the ROM --------- To write code to the Spectrum 128K Lower ROM you could do this: --------- zeusemulate "128K","norom","raw" org $0000 disp $30000 Start DI ; First byte of the ROM --------- To write code to the Spectrum 128K Higher ROM you could do this: --------- zeusemulate "128K","norom","raw" org $0000 disp $34000 Start DI ; First byte of the ROM --------- And so on. Note that by using "org $0000" and "disp $30000" this code is correctly assembled to run at address $0000, but DISPLACED to be stored at $30000, as opposed to simply doing "org $30000" where Zeus would assign $30000 to label Start, etc. Also note that surrounding the ROM code with a proc/endp to make the labels local is probably a good idea, so that routines in the ROM don't accidentally use the addresses of code in a different ROM (the Z80 can only see one at a time). I'd put the first ROM inside a procedure called ROM0, and the second one inside a procedure called ROM1, then to access the ROM routines use Zeus's scoped local names so ROM0.Start or ROM1.Start, etc, etc. For example: --------- zeusemulate "128K","norom","raw" ; Tell Zeus we don't want the default ROMS ; Code for ROM0 ROM0 proc ; Code for the first ROM in a procedure called ROM0 org $0000 ; disp $30000 ; Start DI ; First byte of the ROM JP Start ; pend ; ; Code for ROM1 ROM1 proc ; Code for the second ROM in a procedure called ROM1 org $0000 ; disp $30000 ; nop ; Just so the code in this ROM is different Start DI ; Start is NOT the first byte of ROM 1 JP Start ; pend ; ; Using procedure blocks means the local labels "Start" and "Start" can refer to ; different addresses. ; Then code outside the ROM can refer to the local labels like this "ROMn.Label": org $8000 ; Somewhere in RAM ; Do something to page ROM 0 into memory ; Then use this to call a label inside ROM0 call ROM0.Start ; This calls address 0000 ; Do something to page ROM 1 into memory ; Then use this to call a label inside ROM1 call ROM1.Start ; This calls address 0001 ; And so on. output_bin "rom0",$30000,$4000 ; To write them to disk. output_bin "rom1",$34000,$4000 ; Or use output_srec --------- - Added zeuskeyaddr and zeuskeymask pseudo-ops. These are intended for the ZX Spectrum. Given a string containing key definitions, "A" .. "Z", "0" .. "9" and "[sym]", "[space]" "[shift]" and/or "[enter}" these return the I/O address or the input bit mask. So to scan a key you can do something like this: --------- PlayerLeftKey = "Q" ld bc,zeuskeyaddr(PlayerLeftKey) ; Get the IO address to input in a,(c) ; Read those 5 keys and zeuskeymask(PlayerLeftKey) ; And with the bit for PlayerLeftKey jr z PlayerLeftPressed ; If it's zero the key is pressed --------- The string can contain more than one key and the output will be the combination of all the keys in that string. This is only useful for some combinations. ld bc,zeuskeyaddr("1234567890") ; Get the IO address of the top two rows in a,(c) ; Read those 10 keys ld e,zeuskeymask("1234567890") ; All the bits from any of those keys and e ; See if any are low cp e ; Were any of them cleared? jr nz AnyKeyPressed ; It's different. Something on the top row is pressed The advantage of these pseudo-ops is that changing playing keys becomes a matter of changing a single equate. Released as v3.44 (zeusver = 46) - Added "CPC64" and "CPC128" as targets for the assembler. Not (yet?) for the emulator. Use zeusemulate "CPC64", for example, when writing code for the CPC-64. - Added an option to output Amstrad SNA snapshot files. These are type 1, and either 64 or 128K long to match the CPC64/CPC128 option selected as above. Use output_sna "filename",SP,PC Or you can just use output_sna "filename" and let Zeus pick up the SP and PC values from the usual Zeus_SP and Zeus_PC emulator setting variables. (From version 3.98 onwards Zeus can also output Spectrum 48K/128K SNA files.) Released as v3.50 (zeusver = 47) - Cured a minor timing error in the emulator. - Added an optional address to END statements for backwards compatibility, this sets up the emulator's PC value. eg: END $8000 Is equivalent to having a "Zeus_PC = $8000" statement. - Added zeusinvoke to call external dos commands after successful assembly. This is intended to allow external applications to process the files generated by Zeus. Use "zeusinvoke filename" or "zeusinvoke filename,working_directory" eg: zeusinvoke "test.bat" ; Run "test.bat" every time the assembly process succeeds zeusinvoke "test.bat","D:\" ; Run "test.bat" with "D:\" as the working directory. By default it blocks and waits for the external application to finish before continuing, but you can add a "false" option to stop zeus waiting. eg: zeusinvoke "emulator.exe","",false ; Run "emulator.exe" every time the assembly process succeeds, and don't wait. If the assembly process doesn't succeed nothing happens. - Changed the way Zeus handles data compression. If you use "zeuscompress" Zeus will perform as many passes as required to generate the code, as normal, then run the compression algorythmn to compress the code as specified. That was all it did before this release. Now Zeus changes the assembly pointer (after the zeuscompress pseudo-op)to refer to the first free byte *after* the compressed data block, and then does one more pass - this means that output statements can know the size of the compressed data and the tape file generators can write exactly the right number of bytes to tape. Released as v3.51 (zeusver = 48) - Added an extra mode to "output_tzx", this can now output tzx files with a loader that can automatically load multiple blocks of code/data, as defined by the user, including loading to different pages. So this can be used to load 128K applications/data. (See v4.04 for the simpler alternative "output_tzx_auto") "output_tzx" Usage: There are four separate modes... (see my games source code for examples of these): MODE 0 (Code block for 16K,48K, or first 48K of a 128K models supported) To generate a "CODE" block for storing binary data: output_tzx "pc filename","tape filename","comment",Start,Length From Zeus v3.53 onward you can specify a different load address like this: output_tzx "pc filename","tape filename","comment",Start,Length,0,LoadAddr Note the "0" before the load address, to specify it's mode 0. Where "pc filename" is the name to be used for the file, "tape filename" is the name to be put in the tzx file for the code block, "comment" is any comment required in the file, Start is the address in memory ($4000 .. $FFFF) of the first byte and Length is the number of bytes. On 128K machines this cannot load to the pages that do not map into main memory normally. eg: output_tzx "darkstar.tzx","","",$8000,$4000 output_tzx "darkstar.tzx","beefburger","not the original",$8000,$4000 Use LOAD "" CODE to load this. MODE 1 (Uses a custom loader. 48K, or first 48K of a 128K model) Zeus can also automatically generate a "PROGRAM" block containing a tape loader, and add the code as a data block after it... this uses a loader like the ones I used to use in the 80's that load the screen at the same time as they load the data. This mode cannot be used on the 16K Spectrum, and on the 128K models it cannot load to the pages that do not map into main memory normally. (For 128K loading see mode 3, below). output_tzx "pc filename","tape filename","comment",Start,Length, 1 ,ExecuteAddr or it supports some options, mainly you can change the border colours output_tzx "pc filename","tape filename","comment",Start,Length, 1 ,ExecuteAddr,Options To use this loader sensibly you should have a loading screen loaded in memory at $4000 .. $5AFF for it to use... how Zeus chooses to load the screen depends on how much of it contains visible pixels; if entire lines of pixels are $00 Zeus will not load them. For example, if you have an assembler source for a game that starts at $8000, is $2000 bytes long and you have a loading screen for it in a file called "screen.scr" then Zeus can generate a tzx file with a nice loading screen using the commands: import_bin "screen.scr",$4000 ; This gets the loading screen and puts it in memory in place output_tzx "game.tzx","mygame","(c) Etc",$8000,$2000,1,$8000 ; Generate the tzx file where the 1 indicates this is the required TZX format. To load a program generated using Zeus use LOAD "" on your Spectrum or emulator. The "Options" optional parameter allows you to set the colours used while the loader is operating. Options is a 32-bit value containing six 4-bit fields. These are as follows: Bits | 31 .. 24 | 23 .. 20 | 19 .. 16 | 15 .. 12 | 11 .. 8 | 7 .. 4 | 3 .. 0 | Effect | Not Used | Line INC | Line XOR | Loader A | Loader B | Leader A | Leader B | Where Leader A and B are the colours used while reading the tape leader, Loader A and B are the colours used while reading the data, and the loader colours are modified at the end of each video line by the Line XOR and Line INC values... the XOR and INC values interact with each other... if you don't want the border flashing on each bit-cycle make the A and B values the same. For example these colours are particularly repulsive. output_tzx "tony","tony","Hideous!",$7F00,$4000,1,$7F00,$00123456 The most significant byte is reserved for other loader options to be added in future. Set it to zero for the moment. Use LOAD "" to load this. Or will load automatically on most emulators. MODE 2 (Program. 48K, or first 48K of a 128K models supported) Zeus can also put the code it generates inside a REM statement and save this as a BASIC programme that automatically runs the code. This mode can be used on any model of Spectrum, but on the 128K models it cannot load to the pages that do not map into main memory normally. During tape loading the REM statement code is placed in memory at address $5BC0, so if you assemble and save code starting at that address it is run there. Otherwise Zeus prepends a chunk of code at the start of the REM to move your code into the required position. output_tzx "pc filename","tape filename","comment",Start,Length, 2 ,ExecuteAddr where the 2 indicates this is the required TZX format. Use LOAD "" to load this. Or will load automatically on most emulators. MODE 3 (Uses a custom loader. 48K,128K models fully supported) Zeus can also automatically generate a "PROGRAM" block containing a tape loader, and add the code as a data block after it... this uses a loader like the ones I used to use in the 80's that load the screen at the same time as they load the data. This mode cannot be used on the 16K Spectrum. On the 128K models it can load to any page or pages. This mode can load multiple blocks of memory, which can be on any 16K page, or even overlap pages. The command is the same as for mode 1, including the start and length of the first block as normal: output_tzx "pc filename","tape filename","comment",Start,Length, 3 ,ExecuteAddr or it supports some options, mainly you can change the border colours output_tzx "pc filename","tape filename","comment",Start,Length, 3 ,ExecuteAddr,Options But you can add extra blocks either as Start,Length pairs after the full command like this: output_tzx "pc filename","tape filename","comment",Start,Length, 3 ,ExecuteAddr,Options ,Start2,Length2 and so on, or using "output_tzx_block" commands: output_tzx_block "pc filename",Start,Length output_tzx_block "pc filename",Start,Length,Start2,Length2,Start3,Length3 and so on... Note that the "pc filename" must be the same in the block commands as in the original, that is how zeus knows which file you are adding blocks to. The "start" address can be a normal 16-bit $4000..$FFFF address, or anywhere in Zeus's page memory as described above using greater than 16-bit addressing. So, for example, for a 128K Spectrum page 0 can be addressed at $10000, where that number has four zero's. You could use "zeuspage(0)" as well. The "length" can also be larger than 64K... for example, saving from $10000 for $10000 would save pages 0,1,2, and 3. To use this loader sensibly you should have a loading screen loaded in memory at $4000 .. $5AFF for it to use... how Zeus chooses to load the screen depends on how much of it contains visible pixels; if entire lines of pixels are $00 Zeus will not load them. Also this mode optimises zeros at the beginning and ending of lines... Zeus will load non-zero attribute lines when they are required, just before any pixels using them are loaded. For example, if you have an assembler source for a game that starts at $8000, is $2000 bytes long and you have a loading screen for it in a file called "screen.scr" then Zeus can generate a tzx file with a nice loading screen using the commands: import_bin "screen.scr",$4000 ; This gets the loading screen and puts it in memory in place output_tzx "game.tzx","mygame","(c) Etc",$8000,$2000,3,$8000 ; Generate the tzx file where the 3 indicates this is the required TZX format. To load a program generated using Zeus use LOAD "" on your Spectrum or emulator. The "Options" optional parameter allows you to set the colours used while the loader is operating. (Same as MODE 1) output_tzx "tony","tony","Hideous!",$7F00,$4000,3,$7F00,$00123456 The most significant byte is reserved for other loader options to be added in future. Set it to zero for the moment. LIMITATIONS: 48K Spectrum. You can load blocks of code to any address from $4000 to $FFFF, but you cannot load all 48K. The loader itself needs to be placed in memory on a page boundary between $8000..$FE00. Zeus will handle deciding where to put the loader, but you must leave at least one whole page of memory free somewhere for it to do so. When your code starts running (at ExecuteAddr) interrupts will be disabled and the stack pointer will be left at some address between $8000..$BF00, so I STRONGLY suggest the first thing your code does is to load the SP register with a safe value. 128K Spectrum. You can load blocks of code to any address from $4000 to $FFFF, including all 48K. You can also load as much as you like to any other block, or blocks, but you cannot load all $128K. The loader itself needs to be placed in memory on a page boundary between $8000..$BF00 while running, but if none of this memory is available (because you are loading all over it) Zeus will put the loader somewhere in there and add code so that when the load is finished the loader will automatically be overwritten with your code, so you need not be concerned about it. However, this means that you must leave about 270 bytes free somewhere in the 128K... if you do, Zeus will find it and use it automatically. The execute address should be between $4000..$BFFF... if it is above $C000 the code may not be paged in correctly if Zeus needed to find a different page to put the overlay copy of the user's code in. When your code starts running (at ExecuteAddr) interrupts will be disabled and the stack pointer will be left at some address between $8000..$BF00, so I STRONGLY suggest the first thing your code does is to load the SP register with a safe value... also, the page register (IO address $7FFD) may have been written with a random page, so you should also set this with whatever page you need. NOTE - the loader will also change the ROM page in I/O port $7FFE, so you will need to reset this at the start of your code if you intend to call ROM routines. If you haven't loaded over the ROM variable area then having this code at the start of your code should put it back: ld a,($5B5C) ; Previous value of port is stored here ld bc,$7ffd ; Page register address out (c),a ; Put it back as the BASIC, etc, expects. And as an added bonus for reading this far, bits 26..24 of the "option" word are actually used in mode 3... have fun finding out what they do ;) Use LOAD "" to load this. Or will load automatically on most emulators. - Added ZEUSPAGE function. For use when writing 128K code. Given a page number, 0 .. 7, this returns the 32 bit address in memory which Zeus uses to denote that page. So, to place data in specific pages, for example, one could do this: org zeuspage(0) db "These bytes are placed at the start of page 0" org zeuspage(7) db "These bytes are placed at the start of page 7" How the Spectrum maps the eight 16K blocks is a right bloody mess. Zeus maps them as discussed above in the documentation for v3.43... it isn't nice, but there is no ideal way to do it. - Added a "GotoDefinition" option to the right-click menu. Sometimes. If you right-click while the overlay text is showing the value of a symbol, then Zeus should show "GotoDefinition" in the menu options. Selecting this will jump to the editor line where that symbol was defined. Released as v3.52 (zeusver = 49) - Added support for *.TAP files using "ouput_tap" and "output_tap_block". (See version 4.04 for easier alternatives) These commands take exactly the same parameters as the "ouput_tzx" and "output_tzx_block" commands. Be aware that there is a limitation in the TAP format that blocks cannot be longer than 64K, which imposes a limit on the amount of data that can be encoded by the loader. This will not be a problem with normal 48K files, but it does limit 128K scatterloaded files to loading a total amount of formatted data that will fit in a 64K block... this varies according to the screen data but the formatting overhead is of the order of 2%. Zeus will issue a warning if the data cannot be fitted into a TAP file, but as this is detected after Zeus has finished making passes through the source and generating code, it may not be regarded by Zeus as an error and may not prevent Zeus from executing external applications using zeusinvoke. - Added a load address option to MODE 0 of *.TAP or *.TZX output. See above: - Changed zcl.exe to return an exit code of "0" for success, "1" for failure. - Added "output_hex" to allow the output of text hex files. eg: output_hex "test.hex",Start,Length output_hex "test.hex",Start,Length,AddrWidth output_hex "test.hex",Start,Length,AddrWidth,DataWidth AddrWidth determines if the address is added to the start of the line, and how many digits it uses. DataWidth determines how many bytes of data are shown on each line. By default AddrWidth = 4, DataWidth = 16 Eg: org $8000 db "Hello!" output_hex "test.hex",$8000,6 Would output this in test.hex: 8000 48 65 6C 6C 6F 21 Eg: org $8000 db "Hello!" output_hex "test.hex",$8000,6,8,1 Would output this in test.hex: 00008000 48 00008001 65 00008002 6C 00008003 6C 00008004 6F 00008005 21 Eg: org $8000 db "Hello!" output_hex "test.hex",$8000,6,0,1 Would output this in test.hex: 48 65 6C 6C 6F 21 And so on. - Added support for code timers. These can be used to time how long sections of code take to execute. There are two timers, they are identical. They can be started with "zeustimerstart ID" and stopped with "zeustimerstop ID", where ID = 1 or 2. The results of the timers are shown on the "Timers" Tab page. For example: org $8000 di zeustimerstart 1 TestLoop ld a,12 zeustimerstop 1 zeustimerstart 2 ld hl,1234 zeustimerstop 2 jp TestLoop After executing that if you look on the timer tab you should see timer 1 with a period of 7 and timer 2 with a period of 10. If you move the code down to $6000, which is contended memory you should see the times change as the video accesses stop the processor clock during video accesses. If you remove the "di" you should see the times change as the interrupt routine disturbs the time taken. When on the timer tab, if you double click on any of the "min" or "max" displays, it will clear that min/max value so that the process of establishing the min/max starts again. The time difference between the start of timer 2 and the start of timer 1 is also displayed. If you start a timer again without stopping it, the timer start acts as a timer stop first. So to time the period of a loop you need only have a single "zeustimerstart" in it. Released as v3.53 (zeusver = 50) - Added "zeusgetfilelength". This will look for a file on disc and return its length in bytes (up to 2GB). It returns -1 if the file doesn't exist or can't be opened. Handy for stuff like this: ; Load up a suitable loading screen if we have one if zeusgetfilelength("Test.scr") = $1B00 ; Does this file exist and is it the right length import_bin "Test.scr",$4000 ; Yes, load it endif ; Done - Changed history text slightly - Changed 128K emulation timing slightly, cleared a glitch with NIRVANA+... - Changed the way emulation error traps work to prevent multiple reports. Released as v3.54 (zeusver = 51) - Added options to resize the hex panels on the code tab. - Oops! Looks like the zeuspage(x) function's mapping wasn't right. I've changed it to be as follows: 0 -> $0C000 1 -> $14000 2 -> $08000 3 -> $1C000 4 -> $20000 5 -> $04000 6 -> $28000 7 -> $2C000 (Thanks to Robin Verhagen-Guest for pointing this bug out, the utter bastard) Released as v3.55 (zeusver = 52) - Added stimulation/replay. When you emulate a ZX Spectrum app/game Zeus now stores the keystrokes you type and will replay them the next time you start emulating. This is to allow you to replay games and reproduce bugs. If you start pressing keys during a replay then you resume control of the game from that point and the new keystrokes become the replay in the next run, and so on. If you do not want this check the "Replay Locked" option on the emulator tab. This will still let you take control, but the new keystrokes will not be stored and the replay will revert back to the original on subsequent runs. For the moment the replay keystrokes can be seen and edited on the "Stimulation" tab as text. This file can also be saved and restored in the file menu (use open/save when in the stimulation tab). You can change the CPU speed during replay without altering the gameplay. It is possible to edit the replay text file to embed some commands to dynamically alter options, this will change so I'm not documenting it here yet, but for now S , sets the emulation speed, where has to be a reasonable value and is a bizarrely awful number to do with speed scroll-bar position. It will get better, or rather it will change completely to something nicer. Released (to beta-testers only) as v3.56 (zeusver = 52) - Added "output_c" to generate C header files. E.g. output_c "c:\\temp\\fred.h","SCREEN","unsigned char Screen","#include \"main.h\"",$4000,$1B00 - Added stimulation file control from source code. It is possible to clear any existing stimulation data using "Zeus_StimFileErase": Zeus_StimDataErase = true ; Clear any existing simulation data It is possible to tell Zeus to load an existing stimulation file using "Zeus_StimFileLoad": Zeus_StimDataLoad = "HallsSim.txt" ; Load the file "HallsSim.txt" Note that "Zeus_StimDataLoad" will NOT load a stimulation file if there is already stimulation data present. So if you want to force Zeus to load the same simulation data for each run you would precede "Zeus_StimDataLoad" with "Zeus_StimDataErase" - Amended LABEL behaviour LABEL used to only generate, or look for, global symbols. Now it behaves exactly as other labels as far as scoping rules go. So a LABEL statement used inside a procedure will generate labels local to that procedure. If you want it to generate GLOBAL labels from inside a procedure block, simply prepend the "::" scoping operator to the LABEL operator. LABEL creates fixed numeric address labels. These cannot have their value changed. EG: LABEL("Fred",42) ; Exactly the same as "Fred EQU 42" Bert PROC LABEL("Fred",43) ; Generates a local label, exactly the same as "Fred EQU 42" would here ; This can be seen globally as "Bert.Fred" zeusprint Fred,::Fred zeusprint LABEL("Fred"),::LABEL("Fred") PEND LABEL has three uses: LABEL(name) defines a label from a string and gives it the current address as a value LABEL(name,val) defines a label from a string and gives it the value Val LABEL(name) in expressions returns the value of a label given as a string these are mainly intended for automatic code generation in procs/macros/loops. - Added VARIABLE VARIABLE is an alternative to LABEL, except it creates variables. VARIABLE has three uses: VARIABLE(name) defines a label from a string and gives it the current address as a value VARIABLE(name,val) defines a label from a string and gives it the value Val VARIABLE(name) in expressions returns the value of a label given as a string VARIABLE(name) and LABEL(name) in expressions are identical. these are mainly intended for automatic code generation in procs/macros/loops. EG: VARIABLE("Fred",42) ; Exactly the same as "Fred = 42" Bert PROC VARIABLE("Fred",43) ; Generates a local label, exactly the same as "Fred = 42" would here ; This can be seen globally as "Bert.Fred" zeusprint Fred,::Fred zeusprint VARIABLE("Fred"),::VARIABLE("Fred") ::VARIABLE("Fred",43) ; Alters the global label, exactly the same as "::Fred = 42" would here ; This can be seen globally as "Fred" zeusprint Fred,::Fred zeusprint VARIABLE("Fred"),::VARIABLE("Fred") PEND - Changed emulator screen layout to use a tabbed box to select which controls are visible, The screen was getting too cluttered before, this helps. - Changed Spectrum mouse pointer driven screen address display in emulator. Now shows x/y position, graphic address and pixel number and attribute address, and Nirvana+ line and attribute addresses. You can set the base address of the Nirvana+ data. Double-click on the emulator screen to lock the mouse position, double-click again to unlock it. You can now define a sub-set of the screen for GIF file generation - either enter values directly or hold shift down and press and drag the left mouse button to define the GIF screen area. There's no visible feedback (yet). GIF files can be scaled. Note that choosing any scale other than 1 or 2 makes the emulator slow down dramatically, which is a pain. Your best bet to capture gameplay is probably to play your game before generating the GIF and then reassemble it using the replay option to have Zeus automatically replay it and generate the GIF during the replay. GIF files will replay at normal speed even if generated slowly. Note that keys entered while typing values into the GIF options will be seen as keypresses and cancel the replay, so set the scale etc first, and/or use the "replay locked" option to prevent your presses from destroying the replay data. - Added tape loading support. Very preliminary and incomplete suppport. I wouldn't normally release anything as rough edged as this, but people have asked for a version of Zeus with the replay option and I won't have time to finish the tape support for a while. There's a "Tape" tab. You can give Zeus a *.tzx file and load it using the normal Spectrum ROM tape loading routines. This option is mainly intended for testing tape loaders and so it supports emulating tape speed variations and wow and flutter. See the options on the emulator tab. The tape support is unfinished, actually it's barely started - there's less than an hour's work gone into it... You will need to reset the Spectrum after selecting a new tape file from the menu. You can select the tzx file to load from assembler source code using: Zeus_TapeFileLoad = "tapefilename.tzx" // With the right filename, of course. Remember string escape characters if you enter a path, "\\" for "\" and so on, if they're enabled. To actually load a tape you need to use the emulator - reset and run the Spectrum, then start a tape loading as normal. The keys for 48K Spectrum mode are: J for LOAD, ctrl-P for ", and to get CODE press ctrl and shift together to select E editing mode then release them and press I. For 128K just use the menu and ENTER. - Added "zxpixeladdr" and "zxattraddr" functions. These take an X,Y value and return the memory address of the appropriate byte in screen memory. eg: ld hl,zxpixeladdr(0,0) ; hl = $4000 ld de,zxattraddr(0,0) ; de = $5800 ld hl,zxpixeladdr(255,191) ; hl = $57FF ld de,zxattraddr(255,191) ; de = $5AFF Released as v3.57 (zeusver = 53) - Improved the TZX tape loading. Now ignores "group/group end" blocks. - Added tape export as "*.wav" file. There are two options in the menu. You can export a "clean" file without any speed variation or noise added, or a "dirty" file that has wow and flutter, speed variation and the kind of noise that gets added by nasty playground tape coping with recorders that have AGC added... useful for testing protection methods that rely on noise counting, like the one in my "Dark Star" loader. Both wav files are filtered to emulate a realistic tape player bandwidth, though, so if you just want WAV files for general use there are utilities out there that will generate them in a more compressible though less accurate form... I couldn't actually find one that even got the basic frequencies exactly right, mind you, which I thought was rather unimpressive. I presume they chose to use integer maths and just generate an approximation to the right sample rate; [shrug] Not a terrible decision by any means, the Spectrum is tolerant and they're certainly close enough to load. Speaking of exact timing note that the timings Zeus uses for tape generation change slightly according to the machine model selected at the time of tape generation: The wav file timings are generated assuming a 3.500MHz clock if the model is a 48K machine, otherwise a 3.54690MHz clock is used. The difference is minor, you can ignore it for most purposes, but it is done intentionally... The specification for "*.tzx" files specifies that the timings are for a 3.5MHz clock, and as the wav files are generated from tzx files I initially used this fixed rate. But the purpose of Zeus generating wav files is testing, and for accurate testing purposes I want the timing in the wav file to reflect the timing of the actual hardware that generated it, because I doubt that anyone generating tape files on a 128K machine changed their timing settings from those of 48K machines in order to reflect the different clocks... so Zeus generates wav files for 128K machines with that error baked in to make them realistic. If you want wav files that are accurate to tzx file specifications regarding the clock timing remember to generate them with a 48K model selected, otherwise there's about a 1% difference in frequencies. Released as v3.58 (zeusver = 53) - Added zeussleep function. This allows you to pause while external utilities get on with stuff. Usage: zeussleep Period where period is (1 .. 10000) in mS. If period is outside this range no sleeping occurs. - Added support for an extra tap/tzx tape format, this allows the writing of full custom loaders. MODE 4 (Supports a user supplied custom loader. Any 48K/128K models may be supported) Zeus will use its internal custom loader to load the loading screen and a user supplied loader into memory. Zeus then outputs the required code/data as a stream of bytes with formatting information embedded and calls the user supplied loader to load this stream. This is far too complicated to fully cover here, a zeus source example is provided, and the Halls sources at least will soon be updated to support the new loader. - Added various tools for creating and editing loading screens. See the tabbed control on the emulator tab. Clicking on the Spectrum screen can capture a "character" into data structures suitable for the demo loader (which can be cut and pasted into the demo source), and loading screens can be drawn crudely. Holding shift down while clicking on the Spectrum screen... does things... maybe even useful things. To draw a screen using "Edit" you need to assemble a source that generates a character set. Then you can click on the characters to select them and click on the Spectrum screen to place them. The assembler source needs to include these labels: zeuseditfontbase equ where the data lives in memory zeuseditfontcount equ how many characters So, for example: zeuseditfontbase dg ........ dg ........ dg XXXXXXXX dg XXXXXXXX dg XXXXXXXX dg XXXXXXXX dg ........ dg ........ db $04 zeuseditfontcount equ (* - zeuseditfontbase) / 9; // How many there were From this data Zeus builds the character set used in the Edit tab. Yes, it's an awful hack - you can ask for your money back at any point, I'll understand. Released as v3.59 (zeusver = 54) - Added support for a single data breakpoint. Changed later, see below. Revised and released as v3.60 (zeusver = 55) - Added support for multiple data breakpoints. (See also v3.62 and v3.64 for extensions) These can break only when certain conditions are met, say when the contents of register have specific values. The conditions are set with text strings in the tabs 1 to 9 on the "Breakpoint Code" tab in the emulation window, and the breakpoint is placed in the source window by holding shift down and clicking on the blue dot in the gutter. Data breakpoints are shown in magenta. You can place an unlimited number of data breakpoints, with up to 9 different test code blocks. (If you need more than nine different test cases remember you can include the pc in a test case so one code block could handle an essentially unlimited number of separate condition tests, and what the hell are you writing?) How to place breakpoints using the editor: Hold the shift key down and press and hold the mouse button in the editor gutter column used for breakpoints (there will be blue dots there on executable lines after an assemble). This should insert a data breakpoint number "1" and show this with a "1" in purple. While holding the mouse key down (you can release shift) you can move the mouse to change this number from 1 to 9. I suggest you use 1 if you only have one case. How to enter a test condition: On the first emulator screen there is a control tab called "Breakpoint Code", select it. Under this there are 9 tabs, tab 1 contains the code for breakpoint 1, and so on. Enter the condition code required for breakpoint 1 into the editor under tab 1. Example conditions: A simple example condition might be wanting the code to stop if the value in the accumulator A is higher than some limit. Let's say ten. To have a data breakpoint stop execution when A has a value greater than ten you'd simply pick a data breakpoint number, let's say 1, and find the corresponding tab on the emulator screen under "Breakpoint Code", click on it, then click on the memo panel beneath it and enter the text "a>10"... without the "" quotes, of course. That tells data breakpoint 1 what condition you want it to stop execution on. Now you need to tell Zeus where you want it to do this check, and to do that go to your source code, hold shift down and click in the breakpoint column on the line of source code you want to test. If you want to do the test on more than one line, repeat this as many times as you like, making sure they all say 1... if you get a different number shift-click on it and, while holding the mouse button pressed, move the mouse until you get the number 1, or whatever number tab (1..9) that you are using for this test. When that line or lines is executed, if the value in A is greater than 10 emulation will stop. The test conditions can be as simple as that, or as complicated as you need them to be. Here are some examples: hl=$8000 && a=$42 <- this would break only when hl was $8000 and a was $42 (pc=$8000 && a=$42) || (pc=$8010 && a=$41) <- this would break only when the pc was $8000 and A was $42, or when the pc was $8010 and A was $41 - assuming there were breakpoints placed in the margin at addresses $8000 and $8010, of course. Alternate registers can be selected using a' or bc' and so on. hl<>hl' <- this would break if the value in hl and alternate hl were different. Labels can be used just by using their name. hl < DataStart || hl > DataEnd <- Assuming these labels exist, this would break if hl was not inside a region. The flags can be tested with a bit mask, (F and $01) = 0 <- This is true on NC But as a shorthand Z,NZ,NC,M,P exist. C cannot be used for Carry, as it's a register, but "!" means not, so you can use !NC as shorthand for CF So (hl=$8000) && Z <- Would break when HL=$8000 AND the Zero flag is set. Bytes in memory can be tested using [], so [$8000] = $42 would stop when the byte at $8000 was $42, similarly [hl]=$42 would stop when the byte pointed at by HL was $42. Words in memory can be tested using []W, so [$8000]W = $1278 would stop when the word at $8000 was $1278, and [ix+5]W < $8000 when the 16-bit value at [ix+5] is less than $8000, etc, etc. Longwords in memory (32-bit) can be tested using []L, so [$8000]L = $127800 would stop when the 32-bit value at $8000 was $127800, and [ix+5]L < $8000 when the 32-bit value at [ix+5] is less than $8000, etc, etc. Expressions may use the tertiary ? operator. a=1?b:c for example would return the value of B if A=1 and the value of C if not. Conditions can be as complicated as required. They are evaluated for every line containing a breakpoint marker in the margin. Zeus is fast enough that even complicated breakpoints on every line do not pose an unreasonable emulation speed penalty. [hl*3+42] >= [de]*[bc'] is a valid test condition, for example. Data breakpoint also supports assignment, or writing values to memory. This is done as follows: [$8000] := $42 <- Would write the byte $42 to memory at address $8000 [$8000]W := $1278 <- Would write the word $1278 to memory at addresses $8000..$8001 [$8000]L := $12345678 <- Would write the 32-bit value $12345678 to memory at addresses $8000..$8003 - There is support for data logging/capture using "zeusprint" and "zeusprinthex" in data breakpoint definitions. The usage is considerably different to their use in source code. Briefly you use them like this: zeusprint(condition,data{,Data...}) <- if condition is true, the data is reported in decimal zeusprinthex(condition,data{,Data...}) <- if condition is true, the data is reported in hex Sometimes it is useful to be able print data and also test for a breakpoint, you can separate expressions with a ',' and only the last one will determine if Zeus should break. zeusprint(hl=$8000,a,b,c),hl=$9000 This will report the values in a,b and c only if hl=$8000, and stop only if hl=$9000 The data reports are shown in the emulator report window and also in the main report window if you are stepping through the source code. Be aware that generating lots of reports will cause the computer to become unresponsive. For example, to log the value in A when a routine is entered, put a data breakpoint on the entry and use this as the breakpoint code: zeusprint(true,a) For example, to log the value in A when a routine is entered, but only when HL is $8000, put a data breakpoint on the entry and use this as the breakpoint code: zeusprint(hl=$8000,a) Byte values are treated as unsigned 8-bit data. Word values are treated as unsigned 16-bit data. Label values are treated as 32-bit signed values, floating point and string values are not supported. All calculations are performed using 32-bit signed arithmetic. (The window underneath the data breakpoint shows some debugging information. Ignore it.) The operator precedence for data breakpoint test code is as follows: Highest priority: (),[],[]W !,NOT,-,+ these are unary -/+ &,AND,|,OR,^,XOR these are the binary logical operators *,/,% mul, div, mod -,+ these are add, sub =,<>,!=,>,<,>=,<= comparisons (all signed comparisons) &&,||,^^ these are the logical logical operators ? the ternary operator Lowest priority - There is also a command "zeusdatabreakpoint" that can be used in the source to generate data breakpoints. The usages are: zeusdatabreakpoint breakpoint number, address of breakpoint zeusdatabreakpoint breakpoint number, address of breakpoint, length of breakpoint area zeusdatabreakpoint breakpoint number, "text of breakpoint condition" zeusdatabreakpoint breakpoint number, "text of breakpoint condition",address zeusdatabreakpoint breakpoint number, "text of breakpoint condition",address,length This can easily put multiple breakpoints on large numbers of source lines. "breakpoint number" can be zero, in which case an unconditional code breakpoint is used. If "breakpoint number" is in the range 1 to zeusmaxdatabreakpoint, then that selects a data breakpoint with the corresponding number. Examples: zeusdatabreakpoint 1,"pc=$8000",0,$FFFF ; This would put breakpoint 1 on every line and ; stop when executing an instruction at $8000 Note: Zeus only checks for breakpoints on the first byte of a multi-byte instruction. - While adding support for data breakpoints the user interface machine selection was changed from using radio buttons to a drop-down menu, I made a right mess of this and it introduced some problems when selecting the ula+ versions of the hardware. These have been dealt with. Released as v3.61 (zeusver = 56) - Removed an absurd bug in "find" introduced in v3.61, as in it didn't work at all. (The motto of this version is do NOT write software while ill, it might distract you, but you'll screw things up badly...) - Added some extra functionality to data breakpoints. pagereg1 reports the value last written to I/O location $7FFD (zx128k) pagereg2 reports the value last written to I/O location $1FFD (zx3 only) zeusdisplayaddr(cond,0,address) <- If condition is true, set the left hand code display base zeusdisplayaddr(cond,1,address) <- If condition is true, set the right hand code display base - Added some functionality to the Zeus code hex display page. When looking at memory, to find out which line of source code is responsible for generating any of the bytes shown, double-click on a byte. This will take you to the line of source, if that byte was generated by a line of source. When looking at memory, to find out which line of source code is responsible for last reading that byte, hold shift down and double-click on a byte. This will take you to the line of source containing the Z80 instruction that last read it, if possible. When looking at memory, to find out which line of source code is responsible for last writing to that byte, hold ctrl down and double-click on a byte. This will take you to the line of source containing the Z80 instruction that last wrote to it, if possible. - Changed various aspects of Diana, spacing, made her less "shouty", cured a couple of potential range-error/overflow bugs. Released as v3.62 (zeusver = 57) - Various changes, tidyings up and improvements, mainly of Diana. - Repaired another emulation problem introduced in v3.57... what a disaster writing that change while ill was... urgh... Released as v3.63 (zeusver = 58) - Some improvements to Diana, mainly performance-related. - Extended breakpoint code support to include assignment. Use this with care, it writes to the normal Z80 emulated code space. I've amended the comments above for v3.61 that describe data breakpoints. - Added three extra data-breapoint panels - reads, writes and pages. These can be found with the other data breakpoint editors on the editor tab. "reads" is checked whenever the Z80 reads data from memory. "writes" is checked whenever the Z80 writes data to memory. "pages" is checked whenever the Z80 writes to the memory paging register. Take "writes" as an example. Suppose you have a bug that manifests itself by writing the wrong value to a variable in memory. You can have a data breakpoint that checks every write to see if it is the one doing this using the "writes" tab - for example put the following breakpoint code in the "Writes" tab to check to see if you are writing a value of 0 to a variable called "Index" addr=Index && data=0 All Z80 writes take place 8-bits at a time (the bus is only 8-bits wide) so this code is called once for each 8-bit right of 16-bit operations like "push" or "ld (xxxx),hl", which can be awkward for testing the value of 16-bit variables. However, to help you Zeus sets a flag called "bIs16Bit" for 16-bit write operations which is true for both of the 8-bit writes, and also passes the 16-bit value being written in "dataword", so you can test for 16-bit values like this: bIs16Bit && addr=Index && dataword=$1234 Note that the addr will change depending on which half of the value is being written, so if you compare for the LSB address or the MSB address, it will match only once. "Reads" is similar except that it is checked on read operations. "pages" is checked whenever a memory paging register is written. For this "data" is the value being written, "addr" is the I/O address (either $7FFD or $1FFD), and the current value before the write takes place can be found using "mempage1" or "mempage2". Something to note is that as these traps occur during the execution of an instruction the cyan "run line" will have moved onto the next instruction when Zeus stops and reports a breakpoint. This could be some distance away if the break occurred on a call, jump or ret instruction. To help Zeus marks the line that caused the actual breakpoint by adding a slightly orange background. These breakpoints can be set using the command "zeusdatabreakpoint" in the source as described above (v3.61) If "breakpoint number" is 10, then that selects the "Reads" data breakpoint If "breakpoint number" is 11, then that selects the "Writes" data breakpoint If "breakpoint number" is 12, then that selects the "Pages" data breakpoint After you have finished using these three extra global data-breakpoint panels - reads, writes and pages - remember to delete all the breakpoint text in them, or it might well hang around and cause breakpoints to fire in subsequent code. Be especially careful to clear breakpoint code if it assigns values as these may well overwrite memory in subsequent runs. - Added "output_text" for writing text files from Zeus. usage: output_text "fn",string or integer[,string or integer] eg. output_text "fred.txt","; Creates a text file fred.txt with this comment in it" If you provide more than one string they are concatenated. If you provide an integer parameter it is added as a character. output_text "fred.txt","; Creates", " a text file",32,"fred.txt",$20,"with this comment in it" Multiple output_text commands using the same filename append their text to the file. Zeus inserts a line-separator of CR, LF in the file between the commands. output_text "bert.txt","; Creates a text file bert.txt with this comment in it" output_text "bert.txt","" output_text "bert.txt","; Then a blank line, then this comment" Etc, etc - Added a boolean parameter to the "include" command. If "true" Zeus will reload the text of the source-file from the OS before assembly. eg. include "dynamic.inc",true ; <- The true means reload this file on assembly Normally Zeus loads include files the first time you assemble a file containing any includes and then uses the copy it has loaded for every subsequent assembly operation. There are (rarely) special cases where a user might want to do something unusual with Zeus, for example having Zeus create dynamic assembler source files which are to be loaded up during subsequent assembly processes. This is often called "metaprogramming", and this parameter has been added to include to facilitate that. Note that if you have the "save source files on assemble" option checked then Zeus will save any changes you have made to these files after loading them, *before* it reloads them during assembly. (Which makes the ",true" option fairly safe to use). This option works well with multipass, so metaprogramming code might well also use multipass to generate dynamic source files on the first multipass (multipass=0) then use them on on subsequent multipasses. - Changes to tape file generation to help with incomplete example sources. TAP and TZX generation in modes 1,2 and 3 now check to see if the block of memory you are loading is likely to interfere with the BASIC variables, if it is not then Zeus's loader will preserve the IY register and re-enable interrupts after loading, hopefully allowing simple code that calls the BASIC or OS to run as if just typed in... this may well not work, but it might help beginners who try to save incomplete code examples that were not expecting to have been loaded from tape. Zeus considers that using mode-4, any saving/loading below $5CDE or above $FD00 or using Data Blocks is likely to interfere with BASIC, and in those cases does not preserve the registers or reenable interrupts after loading. The assumption is that by the time people are trying this kind of thing they've moved on and are either writing complete applications that do not rely on that bloody awful ROM or are saving the variables with the loader. - Added "Facebook Copy" to the Edit menu... this is experimental. To use it select a block of source and click on "Facebook Copy" in the edit menu, this copies that source code, reformats it and leaves the reformatted code on the clipboard ready to be pasted (ctrl-V) into Facebook comments and statuses. What it does is change the spacing and replace space characters with a combination of unicode characters that Facebook will not remove, making source code pasted into facebook *slightly* more readable. It can be an improvement, especially if the pasted code is then manually edited. Zeus cannot make this perfect and even if it did Facebook changes often enough to break it, so don't expect miracles or report problems. Released 18/May/2017 as v3.64 (zeusver = 59) - Added command-line "first file" parameter to allow file association with "*.asm" files if required. - Zeus no longer adds include files to the file history list. - Added "Replay Enable" checkbox. If not checked, key(etc) replay is disabled. - Changed Spectrum emulation so that the PC's right shift key can be used as "SYM SHIFT", as well as the Ctrl keys. - Added support for nested procedures (named and anonymous), and a new scoping operator that ascends the stack one level (^^). These are handy when using nested loops. Consider code made of repeated blocks, maybe something like this: call Fred jr nz Next_1 call Fred jr nz Next_1 call Fred jr nz Next_1 call Fred Next_1 call Fred jr nz Next_2 call Fred jr nz Next_2 call Fred jr nz Next_2 call Fred Next_2 call Fred etc,etc Fred: some code Ideally you'd want to be able to write something like this: loop 4 loop 3 call Fred jr nz Next do something lend Next lend But that won't work, because Next is being declared each time through the loop at different places. So, Zeus provided "anonymous procedures" which would allow you to declare a procedure inside the loop to give you local labels, then you can have 4 different Next labels as they're each local. loop 4 proc loop 3 call Fred jr nz Next do something lend Next pend lend And this is fine. But suppose as well as local labels inside the outer loop we wanted local labels inside the loop nested inside it? Zeus did not used to allow nested anonymous procedures. If it did you might try writing something like this, using nested anonymous procedures: loop 4 proc loop 3 proc Lp call Fred djnz Lp jr nz Bert ; This will not work. pend lend Bert pend lend And indeed this will still fail - but only slightly now. Zeus will allow you to declare the nested procedures, but there's an issue in that the label "Bert" referred to in the inner loop is not declared in that inner loop, it's declared in the one above. If Bert was global you could force a reference to it using the scope resolution operator "::", but that's no good here as it's still a local label, just not local to the level of locals we're in when it's referenced. So Zeus now adds another scope resolution operator "^^" which basically means move up the local definition level one layer. So we could write this as: loop 4 proc loop 3 proc Lp call Fred djnz Lp jr nz ^^Bert ; This will work pend lend Bert pend lend You may use multiple ^^ operators to ascend more than one level. "^^^^Fred" is a label Fred in the level above the level above the reference. Note that this is NOT ^^^, you need to repeat the pairs of pointer chars. - Changed the options on the symbol table export. The syntax is still: export_sym filename [, numeric option] if an empty filename is given, then no file is written, but the options are used to generate the symbol table displayed in zeus. The numeric option has changed to make it more convenient to use. The bottom 2 bits of option still select the type of value display (But see $10000, below) $0 = Show as "equ $xxxx" (hex) $1 = Show as "$xxxx" (hex) $2 = Show as "equ xxxx" (decimal) $3 = Show as "xxxx" (decimal) Then the next bits are individual enable bits to enable the display of each symbol type. Combine these: $0004 = Show integers $0008 = Show floating point vars $0010 = Show strings $0020 = Show locals $0040 = Show segments $0080 = Show addresses $0100 = Show variables $0200 = Show macros $0400 = Show anonymous proc labels $1000 = Show Zeus's internal symbols Then there are a couple of unusual options select bits that override all others: $2000 = Output only local variables in some bizarre format $4000 = Generate a Diana symbol table suitable for importing into Diana definitions Then an option to alpha-sort the symbol table (But not if it's in Diana format). $8000 = Sort the table into alphabetical order Then an option to swap the symbol/value order (But not in Diana format). $10000 = Swap the symbol/value order. With this bit set, the bottom two bits work as follows: $0 = Show as "xxxxxxxx fred" (hex) $1 = Show as "$xxxxxxxx fred" (hex) $2 = Show as "0xHHHHHHHH fred" (hex) $3 = Show as "xxxx fred" (decimal) Then an option to force 32-bit fixed-format hex numbers (But not in Diana format). $20000 = Force hex numbers to be 32-bit, with leading zeros With this bit set, all hex numbers are shown as eight digits. To show pretty well all the defined symbols, use this: export_sym "",$1FFC - Added support for the Spectrum Next sprites. These are emulated with behaviour as described on the Next website (as of 3/6/2017) and some deductions made from the behaviour of the TBBlue Spectrum. If you intend to multiplex sprites or change the pixel colours in real time be aware that I don't have a Next and would not rely on the timing being any more accurate than within a video line or so... I will provide a Zeus example source or two for driving these. See "zeus_ex_sprites.asm", etc. Released 3/June/2017 as v3.65 (zeusver = 60) - I've amended the Next sprite support to set the "Max Sprites" flag on 12 sprites rather than 13... I assumed it would be an overflow flag, but apparently it's not. Released 3/June/2017 as v3.65 (zeusver = 60) (Can't be arsed changing it for this) - Amended the tape generation to cure a problem when saving very large mode 1 files, and also made mode 3 support leaving the interrupts enabled for scatter-loaded files that do not seem to destroy BASIC variables. - Added an "optimise" option to help improve loading screens. Click the button on the emulator tab when there's a screen in memory and Zeus will swap the foreground/background attributes where the background is brighter than the foreground, and invert the corresponding pixels so no change is visible. If you save these optimised screens as loading screens they'll probably look better when loading. Released for testing 7/June/2017 still as v3.65 (zeusver = 60) - Altered Next sprite port addressing to be xx53, xx55 and xx57 in accord with the new documentation. (sigh) And either it was wrong or they changed it again. v3.96 changes these ports to Sprite port addressing to be xx53, xx57 and xx5B in accord with the new documentation. And also port 303B. Released 14/June/2017 as v3.66 (zeusver = 61) - Added some support for Polyp as running on the ZX-Uno hardware. Supports single layer display mode only at this point, with pretty accurate display timing (CPU timing is guesswork at the moment) but it should be good enough for me to write a demo ;) - Made various changes to the GIF support, faster and the scaling factor now makes more sense. (aka: "works") - Removed a debugging statement from breakpoint setting, oops. Released for testing 18/June/2017 as v3.67 (zeusver = 62) - Fixed an obscure bug with .Z80 format tape files. (Mutter, mutter, Robin Verhagen-Guest, mutter, found two bugs now, greedy bastard, mutter...) - Extended data formats to allow dynamic resizing... "What the hell does that mean?" I hear you ask. It means you can now do this: db 1,2,3,dw 1000,2000,dl $12345678, df 3.1415 Instead of having to do this: db 1,2,3 dw 1000,2000 dl $12345678 df 3.1415 You can include "spaces" in data definitions using the ds operator *but* the format is subtly different to using a stand-alone "ds" statement, in that you *must* specify the value of the bytes to be placed. The format is ds length:value. eg. db $FF,ds 3:$AA, db $FF ; note the colon in there. This would plant $FF,$AA,$AA,$AA,$FF (Why the colon? Because otherwise it would be ambiguous. What would the ds in "db 1, ds 2,3, db 4" mean? Two bytes of value 3, or a space 2 bytes long followed by a space 3 bytes long?) - Extended data formats to include big endian words and longs. These are specified using the horrible mnemonics "defwbe" and "deflbe", which are "defw" and "defl" with "be" tacked on the end. eg: defwbe $1234 ; This would plant $12,$34 deflbe $12345678 ; This would plant $12,$34,$56,$78 (Normal Z80 16 bit values are little endian. So defw $1234 would plant $34,$12). Released for testing 1/Aug/2017 as v3.68 (zeusver = 63) - Added support for the following "Next opcodes" to the assembler and disassembler and emulator EDIT 26 Jan 2018 - there's now some documentation. I have updated the emulator and timings for these. Most of the timings I'd guessed correctly. EDIT 25 June 2018 - and they've changed again. EDIT 6 July 2018 - and again. EDIT 3 Dec 2018 - and again.. EDIT 7 Feb 2019 - and again... swapnib ED 23 8 T-states mirror a ED 24 8 T-states test XX ED 27 XX 11 T-states bsla de,b ED 28 8 T-states bsra de,b ED 29 8 T-states bsrl de,b ED 2A 8 T-states bsrf de,b ED 2B 8 T-states brlc de,b ED 2C 8 T-states mul d,e ED 30 8 T-states add hl,a ED 31 8 T-states (A is unsigned) add de,a ED 32 8 T-states (A is unsigned) add bc,a ED 33 8 T-states (A is unsigned) add hl,WWWW ED 34 LL HH 16 T-states (I'd assumed 14, tsk...) add de,WWWW ED 35 LL HH 16 T-states (I'd assumed 14, tsk...) add bc,WWWW ED 36 LL HH 16 T-states (I'd assumed 14, tsk...) push WWWW ED 8A LL HH 23 T-states outinb ED 90 Zeus uses 11+Contention T-states, the docs say 14 T-States. nextreg reg,val ED 91 reg,val 16 T-states (I'm not sure I've got this right - needs testing) (I'd guessed 12) nextreg reg,a ED 92 reg 12 T-states (I'm not sure I've got this right - needs testing) (I'd guessed 11) pixeldn ED 93 8 T-states (I'm doing a typical Spectrum move down algorithmn) pixelad ED 94 8 T-states (I'm assuming addresses map into $4000..$57FF else 0) setae ED 95 8 T-states (I'm assuming E[7:3] are ignored jp (c) ED 98 13 T-states apparently. ldix ED A4 Timing as LDI except if not copied -3T, flags set as LDI ldws ED A5 14 T-states lddx ED AC Timing as LDD except if not copied -3T, flags set as LDD ldirx ED B4 Timing as LDIR except if not copied -3T, flags set as LDIR ldpirx ED B7 21/16 lddrx ED BC Timing as LDDR except if not copied -3T, flags set as LDDR ldirscale ED B6 ?? - Added I/O In and Out breakpoint pages, to make trapping I/O activity easier. These can be found with the other data breakpoint editors on the editor tab. (If you are not familiar with Zeus's breakpoint capability see the documentation above for v3.60) "I/O In" is checked whenever the Z80 reads data from an input port. "I/O Out" is checked whenever the Z80 write data to an output port. addr is set to the port address, data is set to the data being read/written. Suppose you want to find out when code reads a Spectrum keyboard - this means checking I/O In instructions but only when bit 0 of the address is 0. The simplest way to do this is to use the following breakpoint code: !(addr&1) Which means "break if the I/O address we're reading has a bottom bit of zero", which is the incompletely decoded keyboard input port on a Spectrum. If you wanted to break only when the keyboard was read and a key was pressed, you could do something like this horror: !(addr&1) & (data&$1F <> $1F) Which breaks when the keyboard port is read and the result has at least one of the bits driven low. NOTE - there is a potential emulation issue here - when Zeus leaves the emulator page the keyboard state is frozen so the state of the keyboard as far as the Spectrum is concerned is fixed as it was when you were emulating it - the reason for Zeus doing this is so that keys that were pressed or released remain pressed or released while you step through the source code... otherwise you'd have to hold them down all the time while debugging. This is helpful in most respects, but it can cause confusion with key-pressed breakpoints like this example as Zeus may continue to break on key pressed unless you manually click on the emulator tab - from v3.69 onwards whenever you return to the emulation page Zeus releases all the pressed keys. These breakpoints can be set using the command "zeusdatabreakpoint" in the source as described above (v3.61) If "breakpoint number" is 13, then that selects the "I/O In" data breakpoint If "breakpoint number" is 14, then that selects the "I/O Out" data breakpoint After you have finished using any global data-breakpoint panel remember to delete all the breakpoint text in them, or it might well hang around and cause breakpoints to fire in subsequent code. Be especially careful to clear breakpoint code if it assigns values as these may well overwrite memory in subsequent runs. Released 6/Sep/2017 as v3.69 (zeusver = 64) - Changed the tape menu to make it more obvious you can load *.TAP files too. - Added the option to preset all the Z80 registers before execution from source code. Zeus used to just allow "Zeus_PC" and "Zeus_SP", but I've extended the support for this. Eg, to start execution with BC containing $1234, do this: Zeus_BC = $1234 ; Set BC a variable that may be redefined later in the source or Zeus_BC equ $1234 ; Set BC as a fixed value that may not be redefined. The 16-bit registers available are: Zeus_AF, Zeus_BC, Zeus_DE, Zeus_HL, Zeus_IX, Zeus_IY, Zeus_PC, Zeus_SP Zeus_AltAF, Zeus_AltBC, Zeus_AltDE, Zeus_AltHL The 8-bit registers available are: Zeus_A, Zeus_B, Zeus_C, Zeus_D, Zeus_E, Zeus_H, Zeus_L, Zeus_F, Zeus_I, Zeus_R Zeus_AltA, Zeus_AltB, Zeus_AltC, Zeus_AltD, Zeus_AltE, Zeus_AltH, Zeus_AltL, Zeus_AltF In addition you can control interrupts with: Zeus_IM, Zeus_IE where Zeus_IM sets the interrupt mode to 0,1,2 and Zeus_IE sets the interrupt enable to true/false. In addition the following registers exist for some versions of the Z80 and some obscure cases: Zeus_A32, Zeus_MEMPTR In the case where the same register is set from both 8 and 16-bit definitions, the 8-bit setting takes precedence. So, in BOTH the following code examples BC starts with the value $FF42 Zeus_B = $FF ; Set B to $FF Zeus_BC = $4242 ; Try to set BC to $4242, but B will still be overridden to $FF or Zeus_BC = $4242 ; Set BC to $4242, but B will be overridden Zeus_B = $FF ; Set B to $FF It should be noted that Zeus is fairly tolerant about these labels. "ZEUSPC", "ZeusPC", "Zeus_PC", "ZeusEmulatePC", and "ZeusEmulate_PC" will all work... basically because I can never remember the format... (5/Nov/2017 - added "zeus_pc" and "zeuspc", etc, etc) Released 12/Sep/2017 as v3.70 (zeusver = 65) - Added options for setting the spacing of the Text-Tidy source formatting. See the config tab. - Typing or clicking in the editor now stops emulation. You can start it again with F8 as usual. - There's now a checkbox near the I register value display. It shows the state of interrupt enable. Clicking on it will toggle the state of interrupt enable. Released 16/Oct/2017 as v3.71 (zeusver = 65) - Added an option to allow the use of non-fixed pitch fonts... don't get excited, the editor will still look horrible if you do use a variable pitch font, how could it do otherwise? But this at least allows the selection of those fonts that are fixed pitch but don't have the right flags set to say that they are... it's a check-box on the config tab near the "Set Editor Font" button. Note that the syntax highlighting uses regular/bold fonts, and assumes that a bold character is no wider than a regular one. This is not true for some otherwise fixed fonts, unfortunately, and they will fail to render properly. Fonts I like which work nicely include "Dina", "LektonCode" "Source Code Pro" and "Fantastique Sans Mono" - they're all free. - Added a padding option to add/subtract space between characters, this can help with some fonts. See the config tab. (Also see v3.38 above for syntax highlighting options) - Added ! as an operator for neatly combining bytes to form words. This takes two values, multiplies the first by 256 and adds the second. This can save you having to type highbyte*256 + lowbyte. Example: HighByte = $12 ; Two arbitrary 8-bit values LowByte = $34 ; Address = HighByte!LowByte ; Combined into a word - address becomes $1234 - Added the option to have multiple load operations in a single LD instruction. This includes optimising some combinations to 16-bit loads. Example: ld a,1,b,2,ix,$1234,a,(hl) ; Equivalent to LD A,1:LD B,2:LD IX,$1234:LD A,(HL) This normally just saves typing the LD characters - not particularly useful and possibly confusing. However, Zeus will optimise some multiple 8-bit loads into 16-bit ones: ld b,5,c,6 ; This would be converted to a single LD BC,$0506 instruction ld c,6,b,6 ; As would this. ld a,1,ixl,2,ixh,3,l,4,h,5 ; Optimised to LD A,1:LD IX,$0302:LD HL,$0504 There are limitations at the moment, for Zeus to optimise two 8-bit loads to one 16-bit one the two 8-bit registers must be consecutive... it doesn't matter what order they're in, but they must not be separated by any other load. (Change suggested by George Phillips) Released 24/Oct/2017 as v3.72 (zeusver = 66) - Added support for floating bus emulation, as some people actually wish to use it. (Urgh) It's accurately modelled on the 48K, and reasonably accurate on the 128K (might be a few t-states out) - Added some support for "snow"... this isn't perfectly accurate but it's representative and useful as a warning. - Added a few functions: lowercase()/uppercase()/titlecase()/ord() - Added [[ ]] as zero-indexed string slicing operators. These operate the same way as [ ] except unlike [ ] which can optionally start with the first character being 0 or 1, the n in [[n]] always starts at zero. e.g. zeusprint "ABCDEF"[1 .. 3] ; Prints either ABC or BCD depending on zoStringIndexFromZero zeusprint "ABCDEF"[[1 .. 3]] ; Always prints BCD - Added "Zeus_IM" (etc) and "Zeus_IE" (etc) to allow presetting of the IM (0..2) and the IFF flags for emulation e.g. Zeus_IM = 2 ; Set interrupt mode 2 Zeus_IE = true ; Interrupts are enabled - Added literal string support. To use this put an @ symbol in front of the string. Literal strings will not process escape characters, so they leave the contents of a string completely alone... with one exception, in strings delimited by double-quotes like this one "blah blah", you can place double-quote characters in the string by using pairs of them. e.g. zeusprint @"Hello\n\r" ; This prints Hello\n\r Released for testing 3/Nov/2017 as v3.80 (zeusver = 66) - Played with CP/M emulation a bit, added some escape codes to the terminal window and remapping of the cursor keys, etc, so that WordStar works reasonably well... You'll probably want to reload the CPM zip file that contains the CPM utilities as I've (slightly) modified the binary of WordStar to work with a larger than normal terminal. Released for testing 20/Dec/2017 as v3.81 (zeusver = 66) - Added support for outputing data directly to CP/M files on an existing virtual disk drive. The command is "output_cpm filename, start address, length" The filename must be in the form "x:name.ext", where x is a single-character drive name from a onwards, "name" is a CP/M format 1 to 8 character filename and ".ext" is an optional three character extension. If the file already exists it will be overwritten. (If a file is output with the length set to zero this will erase the file). Zeus disregards all the CP/M flags, including the read-only flag, and so will overwrite read-only files. Zeus allows various options for the CP/M disk format to be set, but by default uses settings compatible with the version of CP/M 2.2 that the emulator supports. These are set as follows, see example file(s). All these labels ARE case-sensitive, be careful. Zeus_cpm_sec_per_track = 128 ; 128 sectors per track Zeus_cpm_sec_per_block = 32 ; 32 sectors per allocation block (16K) Zeus_cpm_blocks_per_disk = 2048 ; 2048 blocks per virtual disk (8M) Zeus_cpm_dir_cnt = 512 ; 512 directory entries (uses 4 blocks) Zeus_cpm_res_tracks_0 = 1 ; 1 reserved track for CP/M boot files on drive A: Zeus_cpm_res_tracks_x = 0 ; No reserved tracks on the other disks Zeus_cpm_disk_cnt = 16 ; There are 16 virtual disks Zeus_cpm_user = 0 ; Which user we are Note that CP/M 2.2 does not understand file lengths particularly well, and if you write text-files from Zeus you need to add at least one $1A EOF character. - Added support for importing data directly from CP/M files on an existing virtual disk drive. The command is "import_cpm filename" to import bytes to the current assembly position, or "import_cpm filename, start address, length" to import bytes to an address in memory The filename must be in the form "x:name.ext", where x is a single-character drive name from a onwards, "name" is a CP/M format 1 to 8 character filename and ".ext" is an optional three character extension. Note that CP/M does not understand file lengths particularly well, and often reports them rounded up to the nearest 128 bytes. - Added "Zeus_cpm_drives" as a variable which reports the filename of the emulated CP/M disk drives, and which can be used to tell the emulator where to find this file. - Added "zeus_src_dir" as a predefined label which is set by Zeus to the directory (folder) in which the first source file is located. - Added "-" as a new string operator. This subtracts substrings from the tail of a string but only if the substring matches the tail. This will probably be most useful for removing subdirectories from paths, for example, the expression "c:\projects\halls\src" - "src" would yield the string "c:\projects\halls\". If the substring doesn't match then the original string is left unchanged. The substring may contain single character wildcards encoded as "?", which will match any character. Released for testing 9/Jan/2018 as v3.90 (zeusver = 67) - Slight change to the way Zeus handles initialising the SP in the emulator... it now looks at the machine model and for the Spectrums it sets SP according to the model to the right value to have the emulator return to BASIC after emulating a simple programme. - Generating a tap or tzx file used to clear the emulator's SP register, it no longer does. Released for testing 11/Jan/2018 as v3.91 (zeusver = 67) - Added an option to the emulator/"Nascom and CP/M options" tab which allows you to easily select multiple files on the PC and copy them to a CP/M drive... it's important to set the required CP/M drive first as the files copy immediately the diolog box closes. Released for testing 12/Jan/2018 as v3.92 (zeusver = 67) - Added support for changing the CP/M terminal colour scheme to the "zeussyntaxhighlight" option. The syntax is "zeussyntaxhighlight Index, Red, Green, Blue" where: Index is a number which specifies the type of syntax item 300 = CP/M terminal Normal background colour 301 = CP/M terminal Normal foreground colour 302 = CP/M terminal Highlight background colour 303 = CP/M terminal Highlight background colour Red,Green,Blue specify an RGB colour (0,0,0 = black, 255,0,0 = bright red, 255,255,255 = white, etc) So, for example: zeussyntaxhighlight 300,$00,$20,$00 ; Normal background = light green zeussyntaxhighlight 301,$00,$FF,$00 ; Normal foreground = bright green zeussyntaxhighlight 302,$00,$80,$00 ; Highlight background = Medium green zeussyntaxhighlight 303,$00,$00,$00 ; Highlight foreground = Black - Changed CP/M terminal to use the editor font and resize as required. - Added an option to hide the memory panes when in the editor. (See memory panes above) Released for testing 16/Jan/2018 as v3.93 (zeusver = 68) - Fixed a bug in the emulator's +3 memory paging logic. (Spotted by Robin wassisface again... bloody users) Released for testing 22/Jan/2018 as v3.94 (zeusver = 68) - Added support for Zeus controling some more options from the source. Zeus_data_breakpoints = true/false ; Enable/disable data breakpoints Zeus_follow_execution = true/false ; Follow execution, or not - Added "zeusmaxdatabreakpoint" as a constant for use with zeusdatabreakpoint, so you can do things like: for index=1 to zeusmaxdatabreakpoint; Iterate through the data breakpoints zeusdatabreakpoint index,"" ; Clear this one next ; Loop - Changed some of the Spectrum Next extra instruction's timing, and changed the "add rr,a" set to use unsigned addition rather than signed... - Added "ldirscale" and "ldpirx" to both the assembler and the emulator. I think the emulation is correct (Uses HL_A', not HL_E') aside from the timing which may well not be. - Slight change to how the "raw" option works - it now disables Nascom, CPM, Homebrew and Memebership card roms as well as the Spectrum ROMS. Released for testing 26/Jan/2018 as v3.95 (zeusver = 69) - Added "zeusdatafollows" to allow you to give zeus a list of subroutines and RST addresses which have data following the call in memory - if you include the addresses of such RSTs or subroutines in this list then Zeus will not issue a flow warning when they're used. e.g. ; This code that prints data in memory after the call would normally generate a flow warning call PrintFollowingStrZ ; Print "Hello!" db "Hello!",0 ; A string in memory after the call ; But if this statement is present anywhere in the source-code Zeus knows that is allowed. zeusdatafollows PrintFollowingStrZ ; Tell Zeus that "PrintFollowingStrZ" has data after it in mem. - Changed the sprite index port in Zeus's emulation from $xx55 to $xx5B, since the Next hardware has changed at some point. Released for testing 31/Jan/2018 as v3.96 (zeusver = 70) - Following on from a suggestion by Chris Kirby I've added support for the post-fixes 'b','w' and 'l' on binary numbers. These specify that the number should have 8, 16 or 32 bits respectively, and Zeus will emit a warning if one doesn't. So %111 is a perfectly valid three-digit binary number, but %111b would issue a warning because it is not %00000111b, and so on. This is to help avoid simple typing mistakes like missing a digit. Note that this is compatible with all Zeus's existing binary formatting options, so all these are valid: %0000 0000, %0000_0000, %11xxxx00, %1x1x xx_00 (urgh) %0000 0000b, %0000_0000b, %11xxxx00b, %1x1x xx_00b (urgh) Zeus lets you use x or z in binary numbers to indicate bits that don't matter, they both mean 0. (Zeus also supports binary numbers post-fixed with 'b' or 'B', like this one: 1010b) Released for testing 31/Jan/2018 as v3.97 (zeusver = 70) - Improved support for "output_sna" so that it can output Spectrum SNA files as well as Amstrad ones. The format for "output_sna" has been extended so that you can just supply a filename and have Zeus get the SNA file type (Amstrad CPC, Spectrum 48K or 128K) from the machine setting and the register values from the emulator settings: So, for example, you could save an SNA file that should start executing at $8000 like this: Zeus_PC = $8000 output_sna "fred.sna" or set some other registers like this... etc, etc. Zeus_PC = $8000 Zeus_SP = $9000 Zeus_A = $42 output_sna "fred.sna" Note that you can alter the register settings as often as you like in a source, but only the last ones count when generating files because files are generated AFTER Zeus has finished going through the source. If you are saving multiple SNA files and need different register settings for each of them (gawd knows why anyone would do this) then you'll need to use MULTIPASS (as discussed up there ^^^ somewhere). As documented above the possible register setting variables in Zeus are: 32-bit: Zeus_A32 16-bit: Zeus_AF, Zeus_BC, Zeus_DE, Zeus_HL, Zeus_IX, Zeus_IY, Zeus_PC, Zeus_SP, Zeus_AltAF, Zeus_AltBC, Zeus_AltDE, Zeus_AltHL 8-bit: Zeus_A, Zeus_B, Zeus_C, Zeus_D, Zeus_E, Zeus_H, Zeus_L, Zeus_F, Zeus_I, Zeus_R Zeus_AltA, Zeus_AltB, Zeus_AltC, Zeus_AltD, Zeus_AltE, Zeus_AltH, Zeus_AltL, Zeus_AltF Control: Zeus_MEMPTR, Zeus_IM, Zeus_IE - Added a new data definition pseudo-op "dh", which takes strings and extracts hex bytes from them and plants these into memory. e.g. : dh "12" ; Plants a $12, same as "db $12" dh "1234" ; Plants $12,$34 same as "db $12,$34" dh "1234","5678" ; Plants $12,$34,$56,$78 same as "db $12,$34,$56,$78" and so on. Useful for defining large blocks of raw bytes. Released for testing 5/Feb/2018 as v3.98 (zeusver = 71) - The formats for "output_z80" and "output_szx" have been extended so that you can just supply a filename and have Zeus get the register values from the emulator settings, as follows: Zeus_PC = $8000 ; Zeus needs to know the start address at least output_z80 "fred.z80" ; Just a filename will do now output_szx "fred.szx" ; Just a filename will do now - Added support for the Spectrum Next's UART in Zeus's emulator so emulated Next software can talk to ParaSys. The emulation assumes an infinite baud-rate so don't expect timing accuracy. The serial receive buffer is accurately modelled, though. Released for testing 7/Feb/2018 as v3.99 (zeusver = 72) - Added a lot of support for serial ParaSys on the Next to Zeus. See example files, when available. - Changed Zeus to select "Courier New" as a font when first used. Released 2/Mar/2018 as v3.991 (zeusver = 73) - Changed internal memory paging to use 8K blocks instead of 16K blocks to start Next memory emulation. Big changes afoot. - Added "Next" as a machine option. No more support added yet, though... - Functions, SMC labels, defv (void, not readable or writable)) - Slight change to emulator in Spectrum mode to correct the "undocumented" flag behaviour of the CCF and SCF instructions (thanks to investigations of this done by Patrik Rak) - All sorts of goodies related to [censored] which I have no intention of mentioning for the moment. (Okay, this was the introduction of data labels, which I really must document. They're beautiful) - All sorts of goodies related to [censored] which I have no intention of mentioning for the moment. This is a different [censored] to the last [censored] and you'd really like it, but... (And this was the introduction of functions, which I really must document) - Next instructions changed yet again. The current set are documented at "1/Aug/2017 as v3.68" above. - Added malign and maligned for ensuring memory alignment of blocks. malign/maligned is a block pseudo-op used to ensure objects are placed in the same region of memory. Typically the use is to ensure things are placed in the same 256-byte "page", so here is an example: malign 256 fred ds somesize bert ds someothersize maligned malign here moves to an address that is a multiple of 256 and maligned then checks that everything between the malign and maligned fits in that same 256 byte page... if it doesn't Zeus will issue an error. malign can also be used with the parameter ",false" which does not do the initial move unless it is required in order for the items to fit in the same "page". Here is an example: org $8001 ; These two will fit on the same page even though they start at $8001 malign 256,false fred ds 128 ; This would be placed at $8001 bert ds 127 ; This would be placed at $8081 maligned ; The next byte will be at $8100 Here is another example: org $8002 ; These two will NOT fit on the same page starting at $8002 ; So Zeus will move the start to $8100 malign 256,false fred ds 128 ; This would be placed at $8100 bert ds 127 ; This would be placed at $8180 maligned ; The next byte will be at $81FF You can use "malignend" as well as "maligned" for consistency with the other directives. - szx file generation now uses emulation variables (thanks to Graham Mason for pointing out that it didn't) Released for testing only 29/June/2018 as v4.00 (zeusver = 74) - Added "LM" Load Multiple pseudo-op. Val equ $11223344 ; Just a value that's easy to see in the hex ; Showing the LM - Load Multiple pseudo-op. ; Most useful for loading 24 and 32-bit values into registers lm a,b,c,d,$11223344 ; This is the same as "LD A,$11:LD B,$22:LD C,$33:LD D,$44" ; Except Zeus optimises the LD B,$22 and LD C,$33 into LD BC,$2233 lm a,sp,Val ; If SP is included it is always loaded last. (slightly safer IRQ's) lm sp,a,Val ; " " lm a,b,Val ; 16-bit loads to 8-bit registers lm b,c,Val ; Which get optimised to 16-bit loads if possible lm c,b,Val ; lm a,c,e,Val ; 24-bit loads to registers lm d,a,b,Val ; lm a,hl,Val ; Which can include 16-bit registers lm hl,a,Val ; lm b,c,a,Val ; Zeus spots them when split lm c,a,b,Val ; disordered and/or mixed get optimised lm de,hl,Val ; 32-bit loads to 16-bit register pairs lm hl,de,Val ; lm d,l,h,e,Val ; Disordered and mixed pairs get optimised lm c,ix,b,Val ; 32-bit loads to various registers including ixl/h/iyl/h lm a,b,ixl,Val ; lm ixh,iyl,Val ; lm ixh,iyl,ixl,iyh,Val ; Optimised if possible, of course - The outinb instruction is back... - Added support for generating model 3 *.szx files. Use Emulate "3"... be aware the emulation's timing is far from clock-cycle accurate for the Spectrum "2" and "3" models, it's just included to allow generation of files with the right machine type. - Added an option to zeusinvoke to stop Zeus waiting the invoked application to finish. (See doc for v3.50) zeusinvoke "naffemu.exe","",false ; this runs external app naffemu after a successful assemble and does not wait - Added hex address entry and some control keys to "zeusmem" hex display panels. You can now type addresses at hex panels to show specific addresses., and page up/down and cursor moves. - Added "DBL","DBLW" and "DBLL" DefineByteLength These are DB statements that plant the number of bytes defined in the statement as either a byte, word or long at the start. dbl "fred" ; is equivalent to db 4,"fred" dbl 0,0,"a string of bytes" ; is equivalent to db 19,0,0,"a string of bytes" dblw "fred" ; is equivalent to dw 4,db "fred" dblw 0,0,"a string of bytes" ; is equivalent to dw 19,db 0,0,"a string of bytes" dbll "fred" ; is equivalent to dl 4,db "fred" dbll 0,0,"a string of bytes" ; is equivalent to dl 19,db 0,0,"a string of bytes" They're intended to be used for jobs like printing strings of text/control bytes. - Changed how the ParaSys UART is used. See the Parasys Tab. "Emulated ParaSys" - Zeus emulates a UART and ParaSys connects to the emulated UART, no hardware required. "External ParaSys" - ParaSys connects to external hardware using the PC's UART for comms. "(No PS) Drives Hardware" - Zeus emulates a UART and connects the emulator to a real PC UART. Zeus does not emulate baud-rate changes (yet?) so you need to set the appropriate rate in the "ParaSys Options" box on the config tab. Also check "ParaSys uses Next protocol" - I've added a way to limit the length of exported symbol names in case Zeus's unlimited symbol lengths break poorly-written tools. Just put a line like this in the source: ZeusMaxSymbolExportLength = 16 ; Symbol names will be clipped to 16 characters And labels like "ProgrammersLoveLongTediousLables" will become "ProgrammersLoveL" in exported symbol-tables. (I've decided not to append ".." to the clipped symbols) - Added support for "@label" half-arsed local labels. Zeus has procedures to support proper local labels, with nested namespaces, but to help support legacy source-code it will also treat symbols declared with "@" as "local" to the last declared symbol... please use proc/pend instead of this ghastly neolithic bodge in new sources. Here is an example of this, but please don't use it in new code. Print push hl @Lp nop ; Declares the symbol "Print.Lp" call @Lp ; Calls "Print.Lp" pop hl ret PrintStr push hl @Lp nop ; Declares the symbol "PrintStr.Lp" call @Lp ; Calls "PrintStr.Lp" pop hl ret Instead do this: Print proc Lp nop ; Declares "Print.LP" pend ; or use retp to plant the "RET" PrintStr proc Lp nop ; Declares "PrintStr.LP" call PrintStr.Lp ; Calls the Lp in the procedure Print call Print.Lp ; Calls our local "Lp" pend Tidier, but more importantly it nests correctly and saves/restores context. Zeus will sneer at you if you put any of this "@" nonsense in anything other than the root namespace. - Added "dbgexit" and "dbgbreak", which plant DD 00 and DD 01 respectively. For use with other emulators. (DD 00 makes sense, a Z80 will ignore it. But DD 01? Idiotic... If this code gets left in a Z80 will treat that as LC BC,NNNN and fail. There are several single-byte safe opcodes this could and should have used.) To use these you must first set zoDebug=true in the source. You can bracket them as follows: zoDebug = true dbgbreak ; This will plant DD 01 zoDebug = false dbgbreak ; This will not plant anything. (If you want to toggle that inside a macro or proc use ::zoDebug, because it's a global symbol) At the moment Zeus's emulator doesn't know what this amateur-hour crap is, it uses meta-data direct from Zeus for control. - Changed several bits of Diana. Added "Tnn" to run to a terminator and various hex hex/decimal options. See the Diana help text in the help menu. - Added emulation support for Basil If you don't know what Basil was don't worry about it. - Added an extra option to the "output_bin" command, you can now insert bytes generated by Zeus into an existing file output_bin "filename",start,length <- creates a binary file as before output_bin "filename",start,length,offset <- inserts this block of data into an existing file at offset - Added another set of Next instructions to Zeus, Diana and the emulator ("Spectrum Next" mode only) BSLA BSRA BSRL BSRF BRLC JP (C) - Changed the way speculative parsing reports out of range errors. [mutter, grump] - Added an option flag to allow reporting of unreferenced symbols. export_sym "",$100000 These are shown prefixed by the source/line in which they are defined. Double-click on this info to jump to the definition line. - Changed Next support to assume it's a 2M Next. - Added "output_list" to provide debugging support for some external emulators. Usage: output_list "fred.list" This vomits forth a listing file in a format that some emulator or other might like. (user request). - Added zeusassert. Usage: zeusassert conditional,string or zeusassert conditional eg. zeusassert PC>=$8000,"This code can't run in contended memory!" zeusassert ScreenBase=$4000 If the conditional is false, zeus reports an error using the error message as the string. If no string is supplied Zeus will invent a suitable error message. - Added a mechanism for passing commands from assembler source to the emulator. This really needs an example source or two writing... it's provided to support odd requests from users who want the emulator to support some unusual hardware or other. For example, "zeusemucmd $FF" is a command to the emulator to get the current date and time in MSDOS standard 32-bit format and put them into the Z80 registers BC and DE... this was added to support someone developing RTC code. Usage: zeusemucmd cmd,[data] ; Data is passed in the same format as a "db" command. eg zeusemucmd $FF ; The emulator would write the MSDOS time into BC,DE zeusemucmd $FE,dw ClockStringBuffer ; The emulator would write the time and date into memory as raw bytes. zeusemucmd $FD,dw ClockStringBuffer ; The emulator would write the time and date into memory as an ASCII string. These emulation commands may be disabled using "zoDebug = false" - Adding support for outputting *.nex files, an executable file format for the Spectrum_Next See example files. Hopefully there'll be one. This was requested by Robin Verhagen-Guest, who helped by documenting the spec and suggesting ways to suppport it. OUTPUT_NEX directive optionally: OUTPUT_NEX_SCREEN directive OUTPUT_NEX_PALETTE directive OUTPUT_NEX_DATA directive The process of generating a nex file requires an "OUTPUT_NEX" directive appear first, then other NEX directives if required. They often won't be... Zeus will deduce what blocks to include in the file, basically if you have written a byte in any block, Zeus will include that block. You can add one "OUTPUT_NEX_SCREEN" directive to specify a loading screen (if required), and one "OUTPUT_NEX_PALETTE" directive to specify a palette for that loading screen, if it doesn't include one or if you wish to override the one it does include... be aware that some of the NEXT emulators currently fail if the NEX file includes a loading screen. The NEX format is subject to change. Zeus presently supports nex V1.1 and V1.2 (and decides automatically which to use). There are some file options that are set using variables, they are "pu8NEXFileHandle", "pu8NEXCmdLineBase" and "u16NEXCmdLineLength" You would use these as follows, for example: pu8NEXFileHandle = some value, see below: if pu8NEXFileHandle is $0000 the file is closed and nothing is passed if pu8NEXFileHandle is $0001..3FFF file is left open and passed in C register if pu8NEXFileHandle is $4000..FFFF file is left open and the file-handle is poked into memory at (pu8NEXFileHandle) u16NEXCmdLineLength = maximum length of command line passed to your code. pu8NEXCmdLineBase = pointer to the start of a buffer for the command line. The loader will put the text on the end of the command line that loaded your application into this buffer and zero terminate it. The "u16NEXCmdLineLength" parameter limits the maximum length of the line. You should ensure these variables have the values you want them to have before the OUTPUT_NEX directive. If you don't define them, or set them to -1, they'll default to not being supported and Zeus will generate a V1.1 NEX file. You can generate several NEX files during an assembly - these can have different values for these variables, just set them to what you want before each OUTPUT_NEX statement and they'll affect that NEX file and subsequent ones until you set them differently. Or you can make them constants using "equ" instead of "=" when defining them, and they will not be changeable for different files. OUTPUT_NEX: The output_nex directive has this format: output_nex "pc filename", SP, PC[, CoreRequired[, EntryBank[, loadedDelay[, dontResetRegs[, BorderColour]]]]] CoreRequired (string) defaults to "0.0.0", in which case loader never fails core version check EntryBank (byte) defaults to 0, in which case PC would be expected to appear in 16k banks 5/2/0. If EntryBank present, PC would be executed to be $C000..FFFF when EntryBank was paged in at $C000 loadedDelay (bool) defaults to false dontResetRegs (bool) defaults to false BorderColour defaults to 0 (Black) OUTPUT_NEX_SCREEN: (optional) The output_nex_screen directive has this format: output_nex_screen "pc filename","screen filename",loading_bar,loading_colour[,HiResColours] "pc filename" must be the same as the filename in the "output_nex" block. This is how Zeus knows which blocks belong to which file. "screen filename" refers to a file containing the loading screen. This file must exist, and Zeus determines the screen type from the size of the file, so it should be a standard Next screen type. loading_bar is true/false, if true and using a layer-2 loading screen the loader will draw a loading bar loading_colour is an integer, when using a layer-2 loading screen this is the colour used for the loading bar. Optionally you may provide a parameter called HiResColours, which does something for timex hires screens. NOTE: Instead of "screen filename" Zeus will accept a null-string "" followed by a memory address and length, and read the screen data from there instead of from a file. OUTPUT_NEX_PALETTE: (optional) The output_nex_palette directive has this format: output_nex_palette "pc filename","palette filename" "pc filename" must be the same as the filename in the "output_nex" block. This is how Zeus knows which blocks belong to which file. "palette filename" refers to a file containing a standard Next palette. This file must exist, and should probably be 512 bytes long. Zeus will try to cope if it isn't. NOTE: Instead of "palette filename" Zeus will accept a null-string "" followed by a memory address and length, and read the palette data from there instead of from a file. OUTPUT_NEX_DATA: (optional) The output_nex_data directive has this format: output_nex_data "pc filename",[data definition] This directive adds data to the end of the NEX file, after the parts that get loaded as code. "pc filename" must be the same as the filename in the "output_nex" block. This is how Zeus knows which blocks belong to which file. The [data definition] is basically the same as you can put in a "db" or "dbl" statement, with a single exception for adding files - "df" doesn't plant floating point values, but files. Some examples might make this clear: output_nex_data "foo.nex",1,2,3,4 ; Would add the bytes 1,2,3,4 to the end of the file output_nex_data "foo.nex",dw 1,2,3,4 ; Would add the words 1,2,3,4 to the end of the file output_nex_data "foo.nex","Hello!" ; Would add the bytes for the string "Hello!" to the end of the file output_nex_data "foo.nex",dbl "Hello!" ; Would add a length byte (6) then the string "Hello!" to the end of the file You can add raw binary file data like this: output_nex_data "foo.nex",df "data.bin" ; Would add the bytes in the file "data.bin" to the end of the file Note that "df" here doesn't mean "define floating point", but "define file" output_nex_data "foo.nex",df "data.bin",0 ; Would add the bytes in the file "data.bin" to the end of the file ; And put a byte of 0 after them and so on. If you use the "dbl" variants, be aware that the length planted is the length of the total number of bytes after that. So using it for multiple strings, say, may not do what you want... consider this: output_nex_data "foo.nex",dbl "Hello!",dbl "There" This would add these data bytes: $0C,$48,$65,$6C,$6C,$6F,$21,$05,$54,$68,$65,$72,$65 Where the first length is the length of the remaining bytes, not just the length of the first string. To use "dbl" to plant the lengths for separate strings you need to use multiple OUTPUT_NEX_DATA statements, with one dbl directive in each. There's a limit of 2MB on the maximum extra data Zeus will add to a file. If you want more than that you'll have to do it with external tools, which can always be "invoked" by Zeus, of course. (If anyone has a serious use case for a larger limit, contact me.) - Added ZEUSMARKUSED and ZEUSMARKUNUSED Lets you define areas of memory as used or unused by Zeus. This is so Zeus knows which bytes need to be included in files, see NEX files above. Basically Zeus marks any bytes it plants in memory as "USED", and (say) NEX files will then include them. These directives let you override this automatic control. You should not need to do this. If you use these, put them near the end of your source code so that bytes planted later don't override them. - Added ALIAS Much the same as a #define directive to a pre-processor, this lets you give names to lines of code and re-use them. "Then why not support #define, then, you awkward bastard?" "I don't really know, I just think symbol - keyword - parameter is more in keeping with assembler philosophy" "You could support both, you know, if you cared about your users at all" "[mutters]" Example: zp alias zeusprint zp "Hello!" ; This would print "Hello" as zp is replaced by zeusprint aliases can recurse and can be redefined. Example: Mary alias 42 ; Jane alias Mary*Mary ; zp alias zeusprint ; Define "zp" as an alias for "zeusprint" zp Jane ; Prints 1764 Mary alias 10 ; Change Mary zp Jane ; Prints 100 (Jane changes because she uses Mary who has changed) zp alias zeusprinthex ; Redefine "zp" to now be an alias for "zeusprinthex" zp Jane ; Prints 00000064 ALIAS is convenient for options, etc, etc. Okay, okay... you can also use #define. I am supposed to be the nice one, after all. #define Mary 42 ; Etc... - Added zeuskeybit. This is intended for the ZX Spectrum. Given a string containing key definitions, "A" .. "Z", "0" .. "9" and "[sym]", "[space]" "[shift]" and/or "[enter}" this returns the I/O bit number. So to scan a key you can do something like this: --------- PlayerLeftKey = "Q" ld bc,zeuskeyaddr(PlayerLeftKey) ; Get the IO address to input in a,(c) ; Read those 5 keys bit zeuskeybit(PlayerLeftKey) ; Test the bit for PlayerLeftKey jr z PlayerLeftPressed ; If it's zero the key is pressed --------- See v3.44 above for zeuskeyaddr and zeuskeymask - Added ZEUSCOPYFILE, ZEUSMOVEFILE and ZEUSDELETEFILE. These are intended to allow file operations after a successful assembly. zeuscopyfile sourcefilename,targetfilename zeusmovefile sourcefilename,targetfilename zeusdeletefile targetfilename Note: If the assembly process doesn't succeed nothing happens. - Added "DBTB" DefineByteTopBit These are DB statements that set the top bit of the last character of any strings. dbtb "fred" ; Outputs $46,$52,$65,$E4 They're intended to be used where software uses bit 7 as a string terminator. dbtb can be used as a marker inside db/dw/dl and dbl, etc, statements. dbl dbtb "Urgh" ; This is preceded by a length and terminated with the top-bit set. - Changed Spectrum Next memory mapping to match the documented "Real Addresses" When using the Next memory model Zeus now starts the banked memory at $40000, which corresponds more closely with the Next hardware documented addresses. $000000 – $00FFFF (64K) => Memory as seen by the Z80 (depends on bank registers,etc) These addresses are reserved for Zeus to emulate later. $010000 – $013FFF (16K) => ESXDOS ROM $014000 – $017FFF (16K) => Multiface ROM $018000 – $01BFFF (16K) => Multiface extra ROM $01C000 – $01FFFF (16K) => Multiface RAM $020000 – $03FFFF (128K) => divMMC RAM The banked SRAM starts here: $040000 – $05FFFF (128K) => ZX Spectrum RAM $060000 – $07FFFF (128K) => Extra RAM $080000 – $0FFFFF (512K) => 1st Extra IC RAM $100000 – $17FFFF (512K) => 2nd Extra IC RAM $180000 – $1FFFFF (512K) => 3rd Extra IC RAM So, zeusmmu(0) returns $00040000 zeusmmu(1) returns $00042000 and so on... zeusmmu(223) returns $001FE000 In "Next" mode, zeuspage(0) returns $40000, zeuspage(1) returns $44000, etc. - Changed address display in editor margin For machines that have memory pages or banks, the margin display now shows bank numbers when appropriate. For the moment the Spectrum Next always shows bank numbers. - Changed DF so that if a Spectrum model is selected it outputs the Sinclair floating point format. This format can also encode integers in the range -65535 to +65535 and DF supports that, so "DF 1" will output 00 00 01 00 00 - Stripped Next hardware support and the Z80N extended instructions out of the other Spectrum models. - Added "zoStrictSyntax" as a separate option. "zoStrictSyntax=true" enables strict syntax enforcement. "zoStrictSyntax=false" disables strict syntax enforcement. When enabled strict syntax enforcement disallows some of Zeus's relaxed syntax checking and makes Zeus take a much prissier approach to syntax checking... one example is Zeus's indifference to commas after conditional codes in the conditional jump/call instructions. With strict enforcement Zeus will complain if they're not there. Do it too much and it will secretly harbour a silent resentment and feel vaguely contemptuous of you. But hell, it's written in Pascal and probably does anyway. STRICT (and the config option checkbox) sets both "zoStrict" and "zoStrictSyntax" EXTENDED clears them both. - Added "zoSpinnish" to allow some rough emulation of a certain naff assembler's insane attitude to error reporting. "zoSpinnish=true" will add a random offset to the lines reported in error messages, and other jolly japes. Never set it to true. Never. - Added "Zeus_parasys_baud" as a variable that's set to the IDE's currently set parasys baudrate. See the latest ParaSys boot stub source for usage... well, when I release it. If I release it. - Added "zxnextmap" as a way to setup a particular memory map during assembly for the ZX Next memory blocks. Only supported in the "Next" model, at the moment. Can help avoid complicated ORG and DISP or DISPTO in simple apps that don't change the memory model. Dot commands and suchlike. Usage: zxnextmap x0,x1,x2,x3,x4,x5,x6,x7 ; Provide a list of the 8 memory blocks to assume are paged in (see zeusmmu) x0 is the number of the block paged in at $0000 x1 is the number of the block paged in at $2000 and so on. If x is negative, the settings for that block are left unchanged. So: zxnextmap -1,42,-1,-1,-1,-1,-1,-1 ; Sets block 42 at address $2000 You may have as many zxnextmap's as you like in the source, they affect the lines after them. (This had a bug when used with simple filetypes, see the docs below for zeusver=81. Didn't affect *.NEX files, so it escaped notice...) Released for testing only 6/Feb/2020 as v4.01 (zeusver = 75) - added "mexit" and "pexit" as default labels for the exit points of macros and procedures. The idea is that you often want to jump to the end of a macro or procedure, and now don't have to put an explicit label on it... no more of this, basically: mDecAIfNotZero macro() or a jr z exit dec a exit mend Now you can just do: mDecAIfNotZero macro() or a jr z mexit dec a mend Released for testing only 11/Feb/2020 as v4.02 (zeusver = 76) - Changed "Notes" text. - Added a "comment toggle" function to the editor. To use this you must select a block of lines in the editor, then press CTRL-K, and it will comment them or uncomment them (it will uncomment if they were all already commented). It comments by adding a ';' to the start of every line, including those that have one already. It does this so when it removes them again it restores any existing comments. If it doesn't appear to do anything it's because the editor configuration file doesn't contain an entry for CTRL-K, which it doesn't by default - if you haven't changed it to suit yourself then just click on "Delete EditorDef Files" in the "Editor" tab of the "Config" tab and leave then restart Zeus. Otherwise you just need to add this line to your customised "keymap.def" file, in the Zeus execution folder. x 0 1 K CommentToggle Didn't know about "keymap.def"? Well, you do now. Customise away. If you delete it, Zeus will restore the defaults. Released for testing only 6/Apr/2020 as v4.03 (zeusver = 76) - Added some extra range checking to parameters in output commands, etc. - Added automatic options for TAP and TZX file generation. The basic idea is that Zeus knows which bytes your source has generated so it can automatically work out which bytes need to be saved to tape... this is smart enough to use Zeus's custom loader and tape format to load bytes in the high pages of the 128K machines. There are four separate modes... the same as the normal "output_tap" and "output_tzx" commands up there ^^^. But all you will normally need to do is give Zeus the filenames, and leave it to do the rest. If will pick up the execute address from "Zeus_PC" or however you've chosen to set it for the emulator. output_tzx_auto "pc filename","tape filename","comment" Or output_tap_auto "pc filename","tape filename","comment" Those will generate Mode 3 files, with a loader. You can specify a different mode like this output_tzx_auto "pc filename","tape filename","comment",Mode ; Where mode is 0,1,2,3 You can specify the execution address like this, but you'll then need to specify a mode as well. output_tzx_auto "pc filename","tape filename","comment",Mode,ExecAddr ; Where mode is 0,1,2,3 You can specify the colours used by the loader like this, but you'll then need to specify a mode and execution address as well. output_tzx_auto "pc filename","tape filename","comment",3,ExecAddr,Options ; Where mode must be 3 Released for testing only 21/Apr/2020 as v4.04 (zeusver = 77) - Added AY-3-8910 noise generator register display for 128K Spectrum models. Useful for debugging, but still mercifully silent... - Added a configuration option to select the type of displacement calculation performed. See the Notes. Briefly: It transpires there is an ambiguity in the way assemblers handle negative displacements in the indexed addressing mode. The problem arises when the offset is calculated by an expression starting with a negative number. Consider this offset: (ix-1+2), some assemblers will generate the code for (ix+1) and others (ix-3), it's about a 70:30 split between the two cases, in that order... Zeus used to do the first, but having considered which I feel is most correct, it now does the second (by default) with an option to change this. If you want (ix-1+2) to generate (ix+1) set the simple expression configuration option, if you want it to generate (ix-3) clear it... this can be done in the configuration tab or by using "zoSimpleDisplacementCalc=true/false" If you want to write portable code it's best to bracket expressions in these addressing modes. Nearly all assemblers would agree on (ix-(1+2)) being (ix-3), for example. - Mentioned data labels and functions in the history text, which have been supported for several years, but I wasn't really interested in having yet another couple of ideas stolen so information has been restricted to developers I mostly trust... I'm sick and tired of everyone under the sun stealing ideas from Zeus, and thinking they can write a simple assembler and pretend it's as competent... [sigh] Data labels are a way to help support self modifying code. Zeus can plant labels on the data parts of instructions. Consider this traditional self-modifying code: ld (SelfModifyingLoad+1),sp ; Modify the data bytes of the LD SP instruction . . SelfModifyingLoad ld sp,0 ; Nasty, isn't it. You have to know that the data bytes of the the load instruction start one byte into it, and remember to allow for that. Zeus now supports what I call "data labels", which are labels placed on the data parts of instructions. That code could now be: ld (SelfModifyingLoad),sp ; Modify the data bytes of the LD SP instruction . . ld sp,[SelfModifyingLoad] 0 ; The square brackets mean label this data. Which may not be that much clearer, but what about cases like this? ld (ix+[SMC_Disp] 0),[SMC_Val] 0 There the label SMC_Disp is pointing at the displacement byte, and the label SMC_Val is pointing at the data byte. Rather better than having to remember the offset is +2 for the displacement and +3 for the data, hmm? Some data fields are buried in with others in the same opcode byte, for safety these cannot be labeled. (Bit numbers, for example). Note that data labels can also be placed on items in "defb" (etc) pseudo-ops. ld a,'2' ld (pDigit),a db "Hello, you collected ", [pDigit] "0 rings you lucky bugger",0 Used sensibly these can make your source cleaner. Another point worth mentioning is that Zeus is smart enough to tell its emulator about data labels - the bytes you label with them are automatically marked as self-modifying code, which means the emulator will let them be written during emulation without generating a warning even though they form part of an instruction (and so would normally be marked as read/execute only, and cause a warning if written). - Functions are functions. You can now declare functions to be evaluated at assembly time and use them in expressions. Functions are written in a pseudo-code like language that's going to need a lot of documentation... Released for testing only 23/Apr/2020 as v4.05 (zeusver = 78) - Added error detection to the case where someone data-labels the displacement in a 16-bit pseudo-load instruction. e.g. ld (ix+[smcDisp] 42),de ; Error! Which displacement? There's one for D and one for E... There's no problem labeling the displacement byte of an 8-bit load, of course, but for 16-bit loads Zeus generates two 8-bit instructions, and the data label can't apply to both of the displacements. - Changed the editor to hide error message text for the line containing the cursor if it would hide part of the text. Released for testing only 8/May/2020 as v4.06 (zeusver = 79) - Added output_map to provide support for the undocumented "*.MAP" symbol export format. Subject to change. usage: output_map "filename"[,SymbolLength] SymbolLength is the maximum length of symbol names. Must be greater than 4. Names longer than this will be truncated. If SymbolLength is not supplied, it defaults to 16. Released for testing only 14/May/2020 as v4.07 (zeusver = 80) - Fixed a bug(!) introduced in zeusver 75, when using "zxnextmap" to set up a memory map during assembly the default memory mapping was still used instead of the modified one from "zxnextmap" when generating simple binary/hex or Sinclair-specific audio-tape files. (Didn't affect *.NEX files, so it escaped notice...) Released for testing only 21/May/2020 as v4.08 (zeusver = 81) - Changed Text Tidy to leave lines starting with recognised tokens mostly alone. - Internal changes to the way the assembly process updates margin flags and data on editor lines - Removed a spurious ')' from the "output_c" array text generation - Removed unused "Enable ParaSys" checkbox. Released for testing only 30/Aug/2020 as v4.09 (zeusver = 82) - Removed an obscure bug in tapeloader mode 3, which caused screen disruption on tape load failures - Changed tapeloader code to use data labels Released for testing only 6/Apr/2022 as v4.10 (zeusver = 83) - Altered Basil emulation to match the recently repaired Basil (The Winchester failed). Released for testing only 26/Oct/2023 as v4.11 (zeusver = 84) - Added a size option to output_c to allow 8/16/32 byte tables. usage: output_c FileName$,HeaderName$,TypeDefAndName$,Comment$,Base,ByteLength,2 e.g. output_c "sine.h","sinetab","const s16 s16SineTab","// SineTable",Base,ByteLength,2 Released 13/Nov/2023 as v4.12 (zeusver = 85) - Added hints to some "label not found" errors.