diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..2eadb3ce --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,26 @@ +name: CI + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: rlespinasse/github-slug-action@1.1.0 + + - name: Install Dependencies + run: | + sudo apt-get install libncurses-dev + - name: Build + run: | + make + make clean + rm -rf .git + - name: Upload Archive + uses: actions/upload-artifact@v1 + with: + name: RomWBW-${{env.GITHUB_REF_SLUG}}-${{env.GITHUB_SHA_SHORT}} + path: . diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ff0adf6b --- /dev/null +++ b/.gitignore @@ -0,0 +1,95 @@ +# Not sure what patterns to apply +# So ignoring all generated files explicitly + +**/*.[Bb][Ii][Nn] +**/*.[Cc][Oo][Mm] +**/*.[Rr][Oo][Mm] +**/*.com +**/*.eeprom +**/*.hex +**/*.img +**/*.lib +**/*.lst +**/*.o +**/*.prn +**/*.rel +**/*.sym +**/*.sys +**/*.tmp +**/*/font*.asm + +Binary/**/*.mym +Binary/**/*.pt3 + +Source/**/eeprom +Source/Apps/Assign.com +Source/Apps/FDU/FDU.COM +Source/Apps/Format.com +Source/Apps/IntTest.com +Source/Apps/Mode.com +Source/Apps/OSLdr.com +Source/Apps/RTC.com +Source/Apps/SysCopy.com +Source/Apps/SysGen.com +Source/Apps/Talk.com +Source/Apps/Timer.com +Source/Apps/Tune/Tune.com +Source/BPBIOS/bpsys.bak +Source/BPBIOS/bpsys.dat +Source/BPBIOS/def-ww.lib +Source/CPM3/bios3.spr +Source/CPM3/bnkbios3.spr +Source/CPM3/gencpm.dat +Source/CPM3/options.lib +Source/CPM3/zpmbios3.spr +Source/HBIOS/Blank512KB.dat +Source/HBIOS/build.inc +Source/Images/blank144 +Source/Images/blankhd +Source/Prop/Spin/ParPortProp.list +Source/Prop/Spin/PropIO.list +Source/Prop/Spin/PropIO2.list +Source/ZPM3/bnkbios3.spr +Source/ZPM3/gencpm.com +Source/ZPM3/gencpm.com +Source/ZPM3/gencpm.dat + +Tools/Linux +Tools/Darwin + +Tools/unix/bin2asm/bin2asm +Tools/unix/cpmtools/cpmchattr +Tools/unix/cpmtools/cpmchmod +Tools/unix/cpmtools/cpmcp +Tools/unix/cpmtools/cpmls +Tools/unix/cpmtools/cpmrm +Tools/unix/cpmtools/fsck.cpm +Tools/unix/cpmtools/fsed.cpm +Tools/unix/cpmtools/mkfs.cpm +Tools/unix/lzsa/lzsa +Tools/unix/uz80as/uz80as +Tools/unix/zx/config.h +Tools/unix/zx/zx + +!Source/Apps/FAT/FAT.COM +!Source/BPBIOS/bpbuild.com +!Source/BPBIOS/movp112.com +!Source/BPBIOS/Z34RCP11/cledinst.com +!Source/BPBIOS/Z34RCP11/cledsave.com +!Source/Fonts +!Source/Images/**/*.[Cc][Oo][Mm] +!Source/RomDsk/**/*.[Cc][Oo][Mm] +!Source/UBIOS/FSFAT.BIN +!Source/UBIOS/UNA-BIOS.BIN +!Source/ZCCP/*.[Cc][Oo][Mm] +!Source/ZCPR-DJ/*.[Cc][Oo][Mm] +!Source/ZPM3/*.[Cc][Oo][Mm] +!Source/ZSDOS/*.[Cc][Oo][Mm] +!Tools/cpm/bin +!Tools/unix/zx +!Tools/zx + +Source/ZPM3/gencpm.com +Source/ZPM3/startzpm.com +Source/ZPM3/zccp.com +Source/ZPM3/zpmldr.com diff --git a/Binary/Apps/Clean.cmd b/Binary/Apps/Clean.cmd index cf656f2a..1427d697 100644 --- a/Binary/Apps/Clean.cmd +++ b/Binary/Apps/Clean.cmd @@ -2,5 +2,5 @@ setlocal if exist *.com del *.com - -setlocal & cd Tunes && call Clean || exit /b 1 & endlocal +if exist Tunes\*.pt? del Tunes\*.pt? +if exist Tunes\*.mym del Tunes\*.mym diff --git a/Binary/Apps/Makefile b/Binary/Apps/Makefile new file mode 100644 index 00000000..cfa80100 --- /dev/null +++ b/Binary/Apps/Makefile @@ -0,0 +1,10 @@ +TOOLS = ../../Tools +MOREDIFF := $(shell $(TOOLS)/unix/casefn.sh *.com Tunes/*) + +include $(TOOLS)/Makefile.inc + +all:: + mkdir -p Tunes + +clobber:: + rm -f *.bin *.com *.img *.rom *.pdf *.log *.eeprom *.COM *.BIN Tunes/*.mym Tunes/*.pt? diff --git a/Binary/Apps/Tunes/Clean.cmd b/Binary/Apps/Tunes/Clean.cmd deleted file mode 100644 index 3039d346..00000000 --- a/Binary/Apps/Tunes/Clean.cmd +++ /dev/null @@ -1,5 +0,0 @@ -@echo off -setlocal - -if exist *.pt? del *.pt? -if exist *.mym del *.mym diff --git a/Binary/Makefile b/Binary/Makefile new file mode 100644 index 00000000..c856d1b8 --- /dev/null +++ b/Binary/Makefile @@ -0,0 +1,8 @@ +TOOLS = ../Tools +MOREDIFF := $(shell $(TOOLS)/unix/casefn.sh *.img *.rom *.com *.eeprom) +SUBDIRS = Apps + +include $(TOOLS)/Makefile.inc + +clobber:: + rm -f *.bin *.com *.img *.rom *.pdf *.log *.eeprom diff --git a/Binary/ReadMe.txt b/Binary/ReadMe.txt index d6e90a72..9a0be0d4 100644 --- a/Binary/ReadMe.txt +++ b/Binary/ReadMe.txt @@ -77,7 +77,7 @@ The VDU video board requires a dedicated onboard ROM containing the font data. The "vdu.rom" file contains the binary data to program onto that chip. -Disk Images (fd*.img, hd*.img) +Disk Images (fd_*.img, hd_*.img) ------------------------------ RomWBW includes a mechanism for generating floppy disk and hard disk @@ -92,13 +92,13 @@ can quickly create ready-to-use media. Win32DiskImager or RawWriteWin can be used to copy images directly to media. These programs are included in the RomWBW Tools directory. -The fd*.img files are floppy disk images. They are sized for 1.44MB +The fd_*.img files are floppy disk images. They are sized for 1.44MB floppy media and can be copied to actual floppy disks using RawWriteWin (as long as you have access to a floppy drive on your Windows computer). The resulting floppy disks will be usable on any RomWBW-based system with floppy drive(s). -Likewise, the hd*.img files are hard disk images. Each file is +Likewise, the hd_*.img files are hard disk images. Each file is intended to be copied to the start of any type of hard disk media (typically a CF Card or SD Card). The resulting media will be usable on any RomWBW-based system that accepts the corresponding media type. diff --git a/Binary/RomList.txt b/Binary/RomList.txt index ba8ac9ab..16562db5 100644 --- a/Binary/RomList.txt +++ b/Binary/RomList.txt @@ -36,6 +36,7 @@ image to use for each platform: RC2014 w/ Z180 RCZ180_ext.rom (external 512K RAM/ROM module) SC-series SC126, SC130 Easy Z80 EZZ180_std.rom + Dyno DYNO_std.rom You will find there is one additional ROM image called "UNA_std.rom". This ROM image is an UNA-based RomWBW ROM image. As @@ -62,8 +63,7 @@ All of the standard ROM Images are configured for: - 38.4Kbps baud serial console (*) - Auto-discovery of all serial ports -* RC2014 Z80 & Easy Z80 serial port speed is determined by hardware - and is typically 115,200 baud. RC2014 Z180 is normal 38.4Kbps. +* RC2014 and Stephen Cousins' kits run at 115,200Kbps baud All hard disk type devices (IDE, PPIDE, CF Card, SD Card) will be automatically assigned two drive letters per device. The drive @@ -82,6 +82,7 @@ appropriately. SBC (SBC_std.rom): - CPU speed is detected at startup + - Console on onboard UART serial port at 38400 baud - Includes support for PPIDE/CF Card(s) connected to on-board parallel port. - Includes support for CVDU and VGA3 boards. If detected at @@ -105,6 +106,7 @@ SBC (SBC_simh.rom): ZETA (ZETA_std.rom): - CPU speed is detected at startup + - Console on onboard UART serial port at 38400 baud - Includes support for on-board floppy disk controller and two attached floppy disks. - Auto-detects ParPortProp and includes support for it if it @@ -116,6 +118,7 @@ ZETA (ZETA_std.rom): ZETA2 (ZETA2_std.rom): - CPU speed is detected at startup + - Console on onboard UART serial port at 38400 baud - Includes support for on-board floppy disk controller and two attached floppy disks. - Auto-detects ParPortProp and includes support for it if it @@ -128,6 +131,7 @@ ZETA2 (ZETA2_std.rom): N8 (N8_std.rom): - CPU speed is detected at startup + - Console on Z180 onboard primary ASCI serial port at 38400 baud - Includes support for on-board floppy disk controller and two attached floppy disks. - Includes support for on-board TMS9918 video and keyboard @@ -137,6 +141,7 @@ N8 (N8_std.rom): MK4 (MK4_std.rom): - CPU speed is detected at startup + - Console on Z180 onboard primary ASCI serial port at 38400 baud - Includes support for on-board IDE port (CF Card via adapter). - Includes support for on-board SD Card port. - Auto-detects PropIO or PropIO V2 and installs associated @@ -150,6 +155,9 @@ RCZ80 (RCZ80_std.rom): - Requires 512K RAM/ROM module - Auto detects Serial I/O Module (ACIA) and Dual Serial Module (SIO/2). Either one may be used. + - Console on whichever serial module is installed, + but will use the SIO/2 if both are installed. Baud + rate is determined by hardware, but normally 115200. - Includes support for Compact Flash Module - Support for PPIDE Module may be enabled in config - Support for Scott Baker SIO board may be enabled in config @@ -160,6 +168,7 @@ RCZ80 w/ KIO (RCZ80_kio.rom): - Assumes CPU oscillator of 7.3728 MHz - Requires 512K RAM/ROM module - Requires KIO module + - Console on KIO primary serial port at 115200 baud - Includes support for Compact Flash Module - Support for PPIDE Module may be enabled in config - Support for Scott Baker SIO board may be enabled in config @@ -168,7 +177,7 @@ RCZ80 w/ KIO (RCZ80_kio.rom): RCZ180 (RCZ180_nat.rom & RCZ180_ext.rom): - Assumes CPU oscillator of 18.432 MHz - - Console attached to Z180 onboard serial ports at 38400 baud + - Console on Z180 onboard primary ASCI serial port at 115200 baud - Includes support for Compact Flash Module - Support for PPIDE Module may be enabled in config - Support for alternative serial modules may be enabled in config @@ -184,16 +193,24 @@ RCZ180 (RCZ180_nat.rom & RCZ180_ext.rom): SCZ180 (SCZ180_126.rom & SCZ180_130.rom): - Assumes CPU oscillator of 18.432 MHz - - Console attached to Z180 onboard serial ports at 38400 baud + - Console on Z180 onboard primary ASCI serial port at 115200 baud - Includes support for Compact Flash Module - Support for PPIDE Module may be enabled in config - Support for alternative serial modules may be enabled in config - Support for Scott Baker floppy controllers (SMC & WDC) may be enabled in config - - The _126 and _130 varians are functionally identical, they just + - The _126 and _130 variants are functionally identical, they just display a different system label at startup EZZ80 (EZZ80_std.rom): - Assumes CPU oscillator of 10.000 MHz + - Console on primary SIO serial port at 115200 baud - Includes support for on-board SIO - Includes support for IDE via RC bus + +DYNO (DYNO_std.rom): + - Assumes CPU oscillator of 18.432 MHz + - Console on Z180 onboard serial ports at 38400 baud + - Includes support for BQ4842 RTC + - Includes support for onboard PPIDE + - Support for Dyno floppy controllers may be enabled in config diff --git a/Doc/CPM Manual.pdf b/Doc/CPM Manual.pdf index bdcf69ad..b7de4f24 100644 Binary files a/Doc/CPM Manual.pdf and b/Doc/CPM Manual.pdf differ diff --git a/Doc/ChangeLog.txt b/Doc/ChangeLog.txt index bcc3a86e..c6697e08 100644 --- a/Doc/ChangeLog.txt +++ b/Doc/ChangeLog.txt @@ -34,8 +34,22 @@ Version 2.9.2 - PLS: Enhanced Intel Hex Load in dbgmon - WBW: Overhaul disk image creation - WBW: Added support for Dyno platform (based on work by Steve Garcia) -- S?G: Added support for BQ4845 RTC (bqrtc.asm) +- SLG: Added support for BQ4845 RTC (bqrtc.asm) - PMS: Added 80x30 video mode to VGA3 driver +- PMS: Added 80x43 video mode +- PMS: Added font compression option +- PMS: Added a "safe mode" startup w/ minimal device support +- WBW: Switch RC/SC Z180 platforms to 115,200 default baud rate +- PMS: Enhanced PPIDE driver to handle multiple PPI interfaces +- PMS: Added a ROM based game +- WBW: Only assign drive letters to hard disk devices with media +- WBW: Enhanced IDE driver to handle multiple IDE interfaces +- D?R: Contributed SC126 How-To: Preparing a MicroSD Card to Transfer Files to/from a Linux System +- PMS: Updated romldr to handle more than 9 drives +- PMS: Added "user" rom module template +- PMS: Added CP/M 3 manuals +- WBW: Boot from any slice +- C?M: Added Unix build process Version 2.9.1 ------------- diff --git a/Doc/Contrib/Microsoft NASCOM BASIC.docx b/Doc/Contrib/Microsoft NASCOM BASIC.docx new file mode 100644 index 00000000..68af9472 Binary files /dev/null and b/Doc/Contrib/Microsoft NASCOM BASIC.docx differ diff --git a/Doc/Contrib/SC126_How-To_No_1_Serial_Comms_Using_Minicom.pdf b/Doc/Contrib/SC126_How-To_No_1_Serial_Comms_Using_Minicom.pdf new file mode 100644 index 00000000..eb4d5b48 Binary files /dev/null and b/Doc/Contrib/SC126_How-To_No_1_Serial_Comms_Using_Minicom.pdf differ diff --git a/Doc/Contrib/SC126_How-To_No_2_Preparing_an_SD_Card_for_Use_with_SC126_Rev_1-5.pdf b/Doc/Contrib/SC126_How-To_No_2_Preparing_an_SD_Card_for_Use_with_SC126_Rev_1-5.pdf new file mode 100644 index 00000000..9dd3fe3b Binary files /dev/null and b/Doc/Contrib/SC126_How-To_No_2_Preparing_an_SD_Card_for_Use_with_SC126_Rev_1-5.pdf differ diff --git a/Doc/NZCOM Manual.pdf b/Doc/NZCOM Manual.pdf new file mode 100644 index 00000000..59a62a08 Binary files /dev/null and b/Doc/NZCOM Manual.pdf differ diff --git a/Doc/Nascom.txt b/Doc/Nascom.txt deleted file mode 100644 index 5455ab1c..00000000 --- a/Doc/Nascom.txt +++ /dev/null @@ -1,146 +0,0 @@ -NASCOM 2 BASIC (C) 1978 MICROSOFT AS IMPLEMENTED FOR RETROBREWCOMPUTERS - -FUNCTIONS: - -SGN -INT -ABS -USR -FRE -INP -POS -SQR -RND -LOG -EXP -COS -SIN -TAN -ATN -PEEK -DEEK -POINT -LEN -STR -VAL -ASC -CHR$ -HEX$ -BIN$ -LEFT$ -RIGHT$ -MID$ - -RESERVED WORDS: - -END -FOR -NEXT -DATA -INPUT -DIM -READ -LET -GOTO -RUN -IF -RESTORE -GOSUB -RETURN -REM -STOP -OUT -ON -NULL -WAIT -DEF -POKE -DOKE -LINES -CLS -WIDTH -BYE -SET -RESET -PRINT -CONT -LIST -CLEAR -PLAY -REM -NEW -PRINT -TAB -TO -FN -SPC -THEN -NOT -STEP -? - -OPERATORS: - -+ PLUS -- MINUS -* MULTIPLY -/ DIVIDE -AND LOGICAL AND -OR LOGICAL OR -> GREATER THAN -= EQUALS -< LESS THAN -^ POWER - -EXPRESSION PRECEDENCE: - -() EXPRESSIONS IN () -^ POWER -- NEGATION -* / MULTIPLICATION AND DIVISION -+ - ADDITION AND SUBTRACTION -= EQUALS -<> NOT EQUAL -< LESS THAN -> GREATER THAN -<= =< LESS THAN OR EQUAL TO ->= => GREATER THAN OR EQUAL TO -NOT LOGICAL, BITWISE NEGATION -AND LOGICAL, BITWISE AND -OR LOGICAL, BITWISE OR - -VARIABLES: - - NO LENGTH LIMIT BUT ONLY FIRST TWO CHARACTERS ARE SIGNIFICANT. - CANNOT HAVE THE SAME NAME AS RESERVED WORDS. - MAY BE AN ARRAY. - -ERROR CODE: - -NF NEXT without FOR -SN Syntax error -RG RETURN without GOSUB -OD Out of DATA -FC Illegal function call -OV Overflow error -OM Out of memory -UL Undefined line -BS Bad subscript -DD Re-DIMensioned array -/0 Division by zero -ID Illegal direct -TM Type mis-match -OS Out of string space -LS String too long -ST String formula too complex -CN Can't CONTinue -UF Undefined FN function -MO Missing operand -HX HEX error -BN BIN error - -LINE EDITING COMMANDS: - -@ KILL CURRENT LINE -_ NONDESTRUCTIVE DELETE LAST CHARACTER - diff --git a/Doc/cpm3_command_guide.pdf b/Doc/cpm3_command_guide.pdf new file mode 100644 index 00000000..b66d920f Binary files /dev/null and b/Doc/cpm3_command_guide.pdf differ diff --git a/Doc/cpm3_programmers_guide.pdf b/Doc/cpm3_programmers_guide.pdf new file mode 100644 index 00000000..4914042f Binary files /dev/null and b/Doc/cpm3_programmers_guide.pdf differ diff --git a/Doc/cpm3_system_guide.pdf b/Doc/cpm3_system_guide.pdf new file mode 100644 index 00000000..422a0c95 Binary files /dev/null and b/Doc/cpm3_system_guide.pdf differ diff --git a/Doc/cpm3_users_guide.pdf b/Doc/cpm3_users_guide.pdf new file mode 100644 index 00000000..df46c910 Binary files /dev/null and b/Doc/cpm3_users_guide.pdf differ diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..44d7847e --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +all: + cd Tools/unix ; make + cd Source ; make + cd Source/Images ; make + +clean: + cd Tools/unix ; make clean + cd Source ; make clean + cd Binary ; make clean + +clobber: + cd Tools/unix ; make clobber + cd Source ; make clobber + cd Binary ; make clobber + rm -f typescript + +diff: + cd Source ; make diff + diff --git a/ReadMe.txt b/ReadMe.txt index ce7088c4..c920f793 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -7,7 +7,7 @@ *********************************************************************** Wayne Warthen (wwarthen@gmail.com) -Version 2.9.2-pre.25, 2020-01-06 +Version 2.9.2-pre.33, 2020-02-28 https://www.retrobrewcomputers.org/ RomWBW is a ROM-based implementation of CP/M-80 2.2 and Z-System for @@ -54,10 +54,8 @@ Connect a serial terminal or computer with terminal emulation software to the primary RS-232 port of your CPU board. A null-modem connection is generally required. Set the line characteristics to 38400 baud, 8 data bits, 1 stop bit, no parity, and no flow control. -Select VT-100 terminal emulation. In the case of the RC2014 with a -Z80 CPU or Easy Z80, the baud rate is determined by hardware, but is -normally 115200 baud. RC2014 with a Z180 CPU defaults to the -built-in Z180 serial ports and will run at 38400 baud. +Select VT-100 terminal emulation. For RC2014 platforms (including +Stephen Cousins' kits) the baud rate is 115,200. Upon power-up, your terminal should display a sign-on banner within 2 seconds followed by hardware inventory and discovery information. diff --git a/Readme.unix b/Readme.unix new file mode 100644 index 00000000..710487c6 --- /dev/null +++ b/Readme.unix @@ -0,0 +1,49 @@ +this tree now contains makefiles and tools to build on Linux and MacosX +Linux is rather more thoroughly tested compared to os-x. + +to get here, TASM and the propeller generation tools needed to be replaced, +and since the unix filesystem is usually case-sensitive, and CP/M and windows +are not, the cpm tools were made case-insensitive. + +TASM was replaced with uz80as, which implements a subset of TASM and fixes some +bugs. however, I needed to add some functionality to make it build the sources +as they exist in this tree. in particular, one thing to be very careful of is +that TASM is not entirely consistent with respect to the .DS directive. +it's usually a bad idea to mix .DS, .FILL, .DB with .ORG. + .DS n is best thought of as .ORG $ + n + .ORG changes the memory pointer, but does not change the file output point. + it works a lot more like M80, SLR* .phase + +it assumes that you have some standard system tools and libraries installed +specifically: gcc, gnu make, libncurses + +to build: + cd to the top directory and type make. + +heavy use is made of make's include facility and pattern rules. +the master rule set is in Tools/Makefile.inc. changes here will affect +almost every Makefile, and where exceptions are needed, the overrides are +applied in the lower Makefiles. + +these tools can run a windows-linux regression test, where all the binaries are +compared to a baseline windows build. + +Credit: + + uz80as was written by Jorge Giner Cordero, jorge.giner@hotmail.com, + and the original source can be found at https://github.com/jorgicor/uz80as + + the propeller tools use bstc and openspin, parallax tools from + http://www.fnarfbargle.com/bst.html https://github.com/parallaxinc/OpenSpin + note that bst is not open source or even currently maintained, so I could + not generate a version for 64 bit osx. + + cpmtools were the most current I could find, and it has been hacked to do + case-insensitivity. these are not marked, and are not extensive. + + zx is from distributed version, and also has local hacks for case insensitivity. + both zx and cpmtools ship with an overly complicated makefile generation system + and this is ignored. + + this whole linux build framework is the work of Curt Mayer, curt@zen-room.org. + use it for whatever you like; this is not my day job. diff --git a/Source/Apps/FAT/Makefile b/Source/Apps/FAT/Makefile new file mode 100644 index 00000000..51bf7f98 --- /dev/null +++ b/Source/Apps/FAT/Makefile @@ -0,0 +1,6 @@ +OBJECTS = FAT.COM +NODELETE = FAT.COM +DEST = ../../../Binary/Apps + +TOOLS=../../../Tools +include $(TOOLS)/Makefile.inc diff --git a/Source/Apps/FDU/FDU.asm b/Source/Apps/FDU/FDU.asm index 281901ec..da176f06 100644 --- a/Source/Apps/FDU/FDU.asm +++ b/Source/Apps/FDU/FDU.asm @@ -2168,7 +2168,7 @@ FSST_COUNT .EQU (($ - FSST) / FSST_ENTSIZ) ; # ENTRIES IN TABLE ; FCP_CMD .DB 000H ; INPUT: COMMAND CODE FCP_BUFLEN .DB 00H -FCP_BUF .DS 10H +FCP_BUF .FILL 10H FCP_BUFSIZ .EQU $-FCP_BUF FCP_XFRCNT .DW 00H ; BYTES TRANSFERRED DURING COMMAND PHASE ; @@ -2182,7 +2182,7 @@ FXP_BC .DW 00H ; LAST VALUE OF REG BC RECORDED DURING EXECUTION FXP_DE .DW 00H ; LAST VALUE OF REG DE RECORDED DURING EXECUTION FXP_HL .DW 00H ; LAST VALUE OF REG HL RECORDED DURING EXECUTION FXP_BUFLEN .DB 00H -FXP_BUF .DS 50H ; USED FOR CERTAIN EXEC ROUTINES (FORMAT TRACK) +FXP_BUF .FILL 50H ; USED FOR CERTAIN EXEC ROUTINES (FORMAT TRACK) FXP_BUFSIZ .EQU $-FXP_BUF ; ; FDC STATUS @@ -3346,7 +3346,8 @@ FXRR5: INC C ; [04] POINT TO DATA PORT ; ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) - .ORG (($ & 0FF00H) + 40H) + .FILL (($ & 0FF00H) + 40H) - $ + ; .ORG (($ & 0FF00H) + 40H) .ENDIF ; IFXRR: @@ -3374,7 +3375,8 @@ IFXRRX .EQU $ - IFXRR ; ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) - .ORG (($ & 0FF00H) + 40H) + .FILL (($ & 0FF00H) + 40H) - $ + ; .ORG (($ & 0FF00H) + 40H) .ENDIF ; FFXRR: @@ -3452,7 +3454,8 @@ FXRW5: INC C ; [04] POINT TO DATA PORT ; ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) - .ORG (($ & 0FF00H) + 40H) + .FILL (($ & 0FF00H) + 40H) - $ + ; .ORG (($ & 0FF00H) + 40H) .ENDIF ; IFXRW: @@ -3478,7 +3481,8 @@ IFXRW2: EI ; ; AVOID RETURN FROM HALT IN PROBLEMATIC ADDRESS RANGE XX30-XX3F!!! .IF ((($ & 0F0H) == 20H) | (($ & 0F0H) == 30H)) - .ORG (($ & 0FF00H) + 40H) + .FILL (($ & 0FF00H) + 40H) - $ + ; .ORG (($ & 0FF00H) + 40H) .ENDIF ; FFXRW: diff --git a/Source/Apps/FDU/Makefile b/Source/Apps/FDU/Makefile new file mode 100644 index 00000000..a7c26e7c --- /dev/null +++ b/Source/Apps/FDU/Makefile @@ -0,0 +1,10 @@ +OBJECTS = FDU.COM +DOCS = FDU.TXT +DEST = ../../../Binary/Apps +DOCDEST = ../../../Doc +TOOLS = ../../../Tools +include $(TOOLS)/Makefile.inc + +%.COM: %.asm + $(TASM) $< $@ + diff --git a/Source/Apps/Makefile b/Source/Apps/Makefile new file mode 100644 index 00000000..bc549c46 --- /dev/null +++ b/Source/Apps/Makefile @@ -0,0 +1,14 @@ +OBJECTS = SysGen.com Survey.com \ + SysCopy.com Assign.com Format.com Talk.com OSLdr.com Mode.com RTC.com \ + Timer.com IntTest.com +OTHERS = *.hex *.com +SUBDIRS = XM FDU FAT Tune +DEST = ../../Binary/Apps +TOOLS =../../Tools + +include $(TOOLS)/Makefile.inc + +USETASM = 1 + +Survey.com: USETASM=0 + diff --git a/Source/Apps/OSLdr.asm b/Source/Apps/OSLdr.asm index 4ca2973f..15037790 100644 --- a/Source/Apps/OSLdr.asm +++ b/Source/Apps/OSLdr.asm @@ -58,6 +58,9 @@ ; could occur if the BIOS image does not conform to the ; expected structure (size, meta data location, entry point ; location, etc.) +; 3) Hardware platform has been removed from the bootloader, so the +; platform check has been removed for OS loading. This is fine +; unless you attempt to switch between UNA and RomWBW. ;_______________________________________________________________________________ ; ;=============================================================================== @@ -476,11 +479,11 @@ chkos: ; check for signature ; Already verified in chkhdr - ; compare platform id - ld a,(bioplt) ; get current HBIOS platform ID - ld hl,osplt ; point to OS image platform ID - cp (hl) ; compare - jp nz,errplt ; if not equal platform error + ;; compare platform id + ;ld a,(bioplt) ; get current HBIOS platform ID + ;ld hl,osplt ; point to OS image platform ID + ;cp (hl) ; compare + ;jp nz,errplt ; if not equal platform error ; bypass version check if UNA running ld a,(unamod) ; get UNA mode flag @@ -991,7 +994,7 @@ bufptr .dw 0 ; active pointer into buffer ; ; Messages ; -msgban .db "OSLDR v1.1 for RomWBW, 16-Jan-2018",0 +msgban .db "OSLDR v1.2 for RomWBW, 20-Feb-2020",0 msghb .db " (HBIOS Mode)",0 msgub .db " (UBIOS Mode)",0 msguse .db "Usage: OSLDR [/F] []\r\n" diff --git a/Source/Apps/RTC.asm b/Source/Apps/RTC.asm index ae6b78e0..c6f7b645 100644 --- a/Source/Apps/RTC.asm +++ b/Source/Apps/RTC.asm @@ -23,6 +23,7 @@ ; ;[2019/08/11] v1.4 Support SCZ180 platform. ; +;[2020/02/02] v1.5 PMS Basic command line support ; ; Constants ; @@ -40,6 +41,7 @@ PORT_SCZ180 .EQU $0C ; RTC port for SBCZ180 PORT_EZZ80 .EQU $C0 ; RTC port for EZZ80 (actually does not have one!!!) BDOS .EQU 5 ; BDOS invocation vector +FCB .EQU 05CH ; Start of command line BID_BOOT .EQU $00 HB_BNKCALL .EQU $FFF9 @@ -73,7 +75,7 @@ LOOP: ; uses BC ; ; based on following algorithm: -: +; ; const ; hextab : string = ('0','1','2','3','4','5','6','7','8', ; '9','A','B','C','D','E','F'); @@ -1220,18 +1222,22 @@ IDBIO2: ; Note:above code is not fully in sync with current menu code RTC_TOP_LOOP: + CALL RTC_RESET_ON + CALL RTC_BIT_DELAY + CALL RTC_BIT_DELAY + CALL RTC_BIT_DELAY + + LD A,(FCB+1) ; If there a command line tail + CP '/' ; get the command and feed it + LD A,(FCB+2) ; into the input stream + JR Z,RTC_UCL + LD DE,CRLF_MSG LD C,09H ; CP/M write string to console call CALL 0005H CALL RTC_HELP - CALL RTC_RESET_ON - - CALL RTC_BIT_DELAY - CALL RTC_BIT_DELAY - CALL RTC_BIT_DELAY - RTC_TOP_LOOP_1: LD DE,RTC_TOP_LOOP1_PROMPT LD C,09H ; CP/M write string to console call @@ -1239,7 +1245,7 @@ RTC_TOP_LOOP_1: LD C,01H ; CP/M console input call CALL 0005H - +RTC_UCL: AND %01011111 ; handle lower case responses to menu CP 'L' @@ -1312,6 +1318,9 @@ RTC_TOP_LOOP_CHARGE: LD C,09H ; CP/M write string to console call CALL 0005H CALL RTC_CHARGE_ENABLE + LD A,(FCB+1) ; If we came from the + CP '/' ; command line + RET Z ; exit back to CP/M JP RTC_TOP_LOOP_1 RTC_TOP_LOOP_NOCHARGE: @@ -1319,6 +1328,9 @@ RTC_TOP_LOOP_NOCHARGE: LD C,09H ; CP/M write string to console call CALL 0005H CALL RTC_CHARGE_DISABLE + LD A,(FCB+1) ; If we came from the + CP '/' ; command line + RET Z ; exit back to CP/M JP RTC_TOP_LOOP_1 RTC_TOP_LOOP_START: @@ -1350,6 +1362,9 @@ RTC_TOP_LOOP_TIME: LD DE,RTC_PRINT_BUFFER LD C,09H ; CP/M write string to console call CALL 0005H + LD A,(FCB+1) ; If we came from the + CP '/' ; command line + RET Z ; exit back to CP/M JP RTC_TOP_LOOP_1 RTC_TOP_LOOP_RAW: @@ -1552,7 +1567,7 @@ TESTING_BIT_DELAY_OVER: RTC_HELP_MSG: .DB 0Ah, 0Dh ; line feed and carriage return - .TEXT "RTC: Version 1.4" + .TEXT "RTC: Version 1.5" .DB 0Ah, 0Dh ; line feed and carriage return .TEXT "Commands: E)xit T)ime st(A)rt S)et R)aw L)oop C)harge N)ocharge D)elay I)nit G)et P)ut B)oot H)elp" .DB 0Ah, 0Dh ; line feed and carriage return diff --git a/Source/Apps/SysCopy.asm b/Source/Apps/SysCopy.asm index ffb146bf..144f9536 100644 --- a/Source/Apps/SysCopy.asm +++ b/Source/Apps/SysCopy.asm @@ -1,6 +1,6 @@ ;=============================================================================== ; SysCopy - Copy System Image to/from reserved tracks of disk for RomWBW -; adaptation of CP/M 2.2 +; adaptation of CP/M 2.2 & CP/M 3 ;=============================================================================== ; ; Author: Wayne Warthen (wwarthen@gmail.com) @@ -16,6 +16,7 @@ ; ; Change Log: ; 2016-04-24 [WBW] Updated to preserve MBR partition table +; 2020-02-17 [WBW] Updated for CP/M 3 ;_______________________________________________________________________________ ; ; ToDo: @@ -27,6 +28,9 @@ ; Definitions ;=============================================================================== ; +false .equ 0 ; define true +true .equ ~false ; define false +; stksiz .equ $40 ; we are a stack pig ; restart .equ $0000 ; CP/M restart vector @@ -86,6 +90,18 @@ main: init: ; add check for RomWBW? ; + ; get OS version + ld c,12 ; BDOS get os version + call bdos ; do it, L=version + cp $30 ; Test for v3.0 + jr c,init1 ; if <, pre v3.0 + ld a,true ; OS v3.0 or above + ld (v3os),a ; save it + jr init2 +init1: + ld a,false ; OS < v3.0 + ld (v3os),a ; save it +init2: ; locate cbios function table address ld hl,(restart+1) ; load address of CP/M restart vector ld de,-3 ; adjustment for start of table @@ -96,6 +112,20 @@ init: call bdos ; invoke BDOS function inc a ; 1-based index for fcb ld (defdrv),a ; save it + ; print version banner + call crlf ; formatting + ld de,msgban1 ; point to version message part 1 + call prtstr ; print it + ld a,(v3os) ; get OS version flag + or a ; set flags + ld de,msgv2 ; point to V2 mode message + call z,prtstr ; if V2, say so + ld de,msgv3 ; point to V3 mode message + call nz,prtstr ; if V3, say so + call crlf ; formatting + ld de,msgban2 ; point to version message part 2 + call prtstr ; print it + call crlf ; formatting ; return success xor a ret @@ -237,7 +267,7 @@ wrfil1: ; create target file ; write the image ld a,$15 ; setup for bdos write sequential ld (rwfun),a ; save bdos function - ld a,(imgsiz) ; number of records to read + ld a,(imgsiz) ; number of records to write ld (reccnt),a ; init record counter ld hl,imgbuf ; start of buffer ld (bufptr),hl ; init buffer pointer @@ -292,10 +322,8 @@ rddsk: call setdsk ; setup disk ret nz ; abort on error ; set function to read - ld hl,(cbftbl) ; get address of CBIOS function table - ld a,$27 ; $27 is CBIOS READ entry offset - call addhl ; set HL to resultant entry point - ld (actfnc),hl ; save it + ld a,13 ; CBIOS func 13: Read + ld (actfnc),a ; save it ; read the header ld a,12 ; start with 1536 byte header (12 records) ld (reccnt),a ; initialize record counter @@ -339,11 +367,10 @@ wrdsk: ld hl,mbrbuf ; override to read ld (bufptr),hl ; ... into MBR buffer ld a,4 ; 4 records = 1 512 byte sector + ld (reccnt),a ; initialize record counter ; set function to read - ld hl,(cbftbl) ; get address of CBIOS function table - ld a,$27 ; $27 is CBIOS READ entry offset - call addhl ; set HL to resultant entry point - ld (actfnc),hl ; save it + ld a,13 ; CBIOS func 13: Read + ld (actfnc),a ; save it ; read the existing MBR into memory call rwdsk ; read the sector ret nz ; abort on error @@ -366,10 +393,8 @@ wrdsk1: ; setup to write the image from memory to disk call setdsk ; setup disk ret nz ; abort on error ; set function to write - ld hl,(cbftbl) ; get address of CBIOS function table - ld a,$2A ; $2A is CBIOS WRITE entry offset - call addhl ; set HL to resultant entry point - ld (actfnc),hl ; save it + ld a,14 ; CBIOS func 14: Write + ld (actfnc),a ; save it ; setup the record count to write ld a,(imgsiz) ; get previously recorded image size ld (reccnt),a ; save it as pending record count @@ -399,7 +424,8 @@ setdsk: ld c,a ; move to c ld e,0 ; treat as first select call cbios ; invoke cbios with... - .db $1B ; SELDSK entry offset + ;.db $1B ; SELDSK entry offset + .db 9 ; SELDSK entry offset ; check return (sets HL to DPH address) ld a,h or l @@ -436,29 +462,47 @@ setdsk: ; Read or write (reccnt) sectors to/from disk via CBIOS ; rwdsk: + ld hl,128 ; assume rec len for < CP/M 3 + ld (reclen),hl ; and save it + ld a,(v3os) ; CP/M 3 or greater? + or a ; set flags + jr z,rwdsk0 ; if not, continue + ; adjust reccnt, logical (128) to physical (512) + ld a,(reccnt) ; get pending rec cnt + add a,3 ; round up + srl a ; shift to + srl a ; ... divide by 4 + ld (reccnt),a ; and resave it + ld hl,512 ; use physical rec len + ld (reclen),hl ; and save it +rwdsk0: ; setup to read/write a sector ld bc,(acttrk) ; get active track call cbios ; invoke cbios with... - .db $1E ; SETTRK entry offset + ;.db $1E ; SETTRK entry offset + .db 10 ; SETTRK entry offset ld bc,(actsec) ; get active sector call cbios ; invoke cbios with... - .db $21 ; SETSEC entry offset + ;.db $21 ; SETSEC entry offset + .db 11 ; SETSEC entry offset ld bc,(bufptr) ; get active buffer pointer call cbios ; invoke cbios with... - .db $24 ; SETDMA entry offset + ;.db $24 ; SETDMA entry offset + .db 12 ; SETDMA entry offset ; read/write sector ld a,(reccnt) ; get the pending record count dec a ; last record? ld c,2 ; allow cached writes by default jr nz,rwdsk1 ; not last record, continue ld c,1 ; last record, no caching please -rwdsk1: ld hl,(actfnc) ; load the CBIOS function vector - call jphl ; indirect call (read or write) +rwdsk1: + ld a,(actfnc) + call cbiosfn or a ; set flags on return code jp nz,errio ; if not zero, error abort ; adjust buffer pointer ld hl,(bufptr) ; get buffer pointer - ld de,128 ; record length is 128 bytes + ld de,(reclen) ; get rec len add hl,de ; adjust buffer ptr for next record ld (bufptr),hl ; save it ; next sector @@ -479,7 +523,7 @@ rwdsk1: ld hl,(actfnc) ; load the CBIOS function vector rwdsk2: ld hl,reccnt dec (hl) ; decrement pending record count ret z ; if zero, done, return with Z set - jr rwdsk ; otherwise, loop + jr rwdsk0 ; otherwise, loop ; jphl: jp (hl) ; indirect jump ; @@ -656,6 +700,15 @@ chkfcb4: or a ; set flags ret ; +; Print dot +; +prtdot: + push af + ld a,'.' + call prtchr + pop af + ret +; ; Print character in A without destroying any registers ; prtchr: @@ -775,13 +828,37 @@ delim1: ; cbios: ex (sp),hl - ld a,(hl) ; get the function offset + ld a,(hl) ; get the function number inc hl ; point past value following call instruction ex (sp),hl ; put address back at top of stack and recover HL + +cbiosfn: + ; enter here if function already in reg A + ld (bpb_fn),a ; save function +; + ld a,(v3os) ; CP/M 3 or greater? + or a ; set flags + jr nz,cbios2 ; if >= V3, handle it +; + ; CBIOS call for CP/M < v3 + ld a,(bpb_fn) ; get pending function number + ld l,a ; function number to L + add a,l ; ... and multiply by 3 for + add a,l ; ... jump table offset ld hl,(cbftbl) ; address of CBIOS function table to HL call addhl ; determine specific function address jp (hl) ; invoke CBIOS ; +cbios2: + ; CBIOS call for CP/M v3 or greater + ld (bpb_bc),bc + ld (bpb_de),de + ld (bpb_hl),hl + + ld c,50 ; direct bios call function number + ld de,bpb ; BIOS parameter block + jp bdos ; return via BDOS call +; ; Add the value in A to HL (HL := HL + A) ; addhl: @@ -862,10 +939,24 @@ actdsk .db 0 ; active disk no acttrk .dw 0 ; active track actsec .dw 0 ; active sector actspt .dw 0 ; active sectors per track -actfnc .dw 0 ; active function (read or write) +actfnc .db 0 ; active cbios i/o function (read or write) +v3os .db 0 ; true ($FF) if OS v3.0 or greater +reclen .dw 0 ; active record length +; +bpb: ; BIOS parameter block for CP/M 3 BIOS calls +bpb_fn .db 0 ; function +bpb_a .db 0 ; reg A +bpb_bc .dw 0 ; reg BC +bpb_de .dw 0 ; reg DE +bpb_hl .dw 0 ; reg HL ; ; Messages ; +msgban1 .db "SYSCOPY v2.0 for RomWBW CP/M, 17-Feb-2020$" +msgv2 .db " (CP/M 2 Mode)$" +msgv3 .db " (CP/M 3 Mode)$" +msgban2 .db "Copyright 2020, Wayne Warthen, GNU GPL v3$" + msguse .db "Usage: SYSCOPY [=]$" msgamb .db "Ambiguous file specification not allowed$" msgdlm .db "Invalid delimiter$" diff --git a/Source/Apps/Tune/Makefile b/Source/Apps/Tune/Makefile new file mode 100644 index 00000000..3aa5c338 --- /dev/null +++ b/Source/Apps/Tune/Makefile @@ -0,0 +1,12 @@ +OBJECTS = Tune.com +DEST = ../../../Binary/Apps +TOOLS = ../../../Tools + +include $(TOOLS)/Makefile.inc + +Tune.com: Tune.asm + $(TASM) Tune.asm Tune.com + +all:: + mkdir -p $(DEST)/Tunes + cp Tunes/* $(DEST)/Tunes diff --git a/Source/Apps/Tune/Tune.asm b/Source/Apps/Tune/Tune.asm index 54429c23..2526fccf 100644 --- a/Source/Apps/Tune/Tune.asm +++ b/Source/Apps/Tune/Tune.asm @@ -37,6 +37,7 @@ ; 2018-01-26 [WBW] Initial release ; 2018-01-28 [WBW] Added support for MYM sound files ; 2019-11-21 [WBW] Added table-driven configuration +; 2020-02-11 [WBW] Made hardware config & detection more flexible ;_______________________________________________________________________________ ; ; ToDo: @@ -81,30 +82,48 @@ TYPMYM .EQU 3 ; FILTYP value for MYM sound file LD A,RMJ << 4 | RMN ; Expected HBIOS ver CP D ; Compare with result above JP NZ,ERRBIO ; Handle BIOS error + LD A,L ; Platform id to A + LD (CURPLT),A ; Save as current platform id ; - ; Use platform id to setup active configuration - LD A,L ; Platform ID is still in L from above - RLCA ; Adjust for table entry size (4 bytes) - PUSH AF ; Save ID * 2 for later - RLCA LD HL,CFGTBL ; Point to start of config table - CALL ADDHLA ; HL := desired config table entry - LD DE,CFG ; Dest is active config - LD BC,4 ; Copy 4 bytes - LDIR ; Copy to active config -; - LD HL,PLTSTR ; Point to platform string table - POP AF ; Recover platform id * 2 for table offset - CALL ADDHLA ; HL := Platform string index adr - LD E,(HL) ; DE := Platform string adr - INC HL - LD D,(HL) - CALL CRLF ; Formatting - CALL PRTSTR ; Display platform string +CFGSEL: + LD A,$FF ; End of table marker + CP (HL) ; Compare + JP Z,ERRHW ; Bail out if no more configs to try +; + LD BC,CFGSIZ ; Size of one entry + LD DE,CFG ; Active config structure + LDIR ; Update active config structure +; + LD A,(CURPLT) ; Get current running platform id + LD E,A ; Put in E + LD A,(PLT) ; Get platform id of loaded config + CP E ; Equal? + JR NZ,CFGSEL ; If no match keep trying +; + ; Test for hardware (sound chip detection) + CALL SLOWIO + LD DE,(PORTS) ; D := RDAT, E := RSEL + LD C,E ; Port = RSEL + LD A,2 ; Register 2 + OUT (C),A ; Select register 2 + LD C,D ; Port = RDAT + LD A,$AA ; Value = $AA + OUT (C),A ; Write $AA to register 2 + ;LD C,E ; Port = RSEL + LD A,(RIN) ; Port = RIN + LD C,A ; ... to C + IN A,(C) ; Read back value in register 2 + PUSH AF + CALL NORMIO + POP AF + ;CALL PRTHEX ; *debug* + CP $AA ; Value as written? + JR NZ,CFGSEL ; If not, keep trying configs ; - LD A,(CFG) ; RSEL port address to A - INC A ; Test for $FF - JP Z,ERRPLT ; Bail out if unsupported platform + CALL CRLF ; Formatting + LD DE,(DESC) ; Load hardware description pointer + CALL PRTSTR ; Print description ; ; Test for timer running to determine if it can be used for delay LD B,BF_SYSGET ; HBIOS: GET function @@ -123,19 +142,6 @@ SETDLY: LD (WMOD),A ; Save wait mode CALL PRTSTR ; Print it ; -; ; *DEBUG* -; LD A,',' -; CALL PRTCHR -; LD HL,CFG -; LD B,4 -;DBGLP: -; LD A,' ' -; CALL PRTCHR -; LD A,(HL) -; INC HL -; CALL PRTHEX -; DJNZ DBGLP -; ; Get CPU speed & type from RomWBW HBIOS and compute quark delay factor LD B,$F8 ; HBIOS SYSGET function 0xF8 LD C,$F0 ; CPUINFO subfunction 0xF0 @@ -146,31 +152,13 @@ SETDLY: LD (QDLY),HL ; Save result as quark delay factor ; ; Activate SCG card if applicable - LD A,(CFG+3) + LD A,(ACR) CP $FF JR Z,NOSCG LD C,A LD A,$FF OUT (C),A NOSCG: -; - ; Test for hardware (sound chip detection) - CALL SLOWIO - LD DE,(CFG) ; D := RDAT, E := RSEL - LD C,E ; Port = RSEL - LD A,2 ; Register 2 - OUT (C),A ; Select register 2 - LD C,D ; Port = RDAT - LD A,$AA ; Value = $AA - OUT (C),A ; Write $AA to register 2 - LD C,E ; Port = RSEL - IN A,(C) ; Read back value in register 2 - PUSH AF - CALL NORMIO - POP AF - ;CALL PRTHEX ; *debug* - CP $AA ; Value as written? - JP NZ,ERRHW ; If not, handle hardware error ; ; Clear heap storage LD HL,HEAP ; Point to heap start @@ -431,7 +419,7 @@ IDBIO2: ; ; SLOWCPU: - LD A,(CFG+2) ; Z180 base I/O port + LD A,(Z180) ; Z180 base I/O port CP $FF ; Check for no value RET Z ; Bail out if no value ADD A,$1E ; Apply offset of CMR register @@ -451,7 +439,7 @@ SLOWCPU: ; ; NORMCPU: - LD A,(CFG+2) ; Z180 base I/O port + LD A,(Z180) ; Z180 base I/O port CP $FF ; Check for no value RET Z ; Bail out if no value ADD A,$1E ; Apply offset of CMR register @@ -467,7 +455,7 @@ NORMCPU: ; ; SLOWIO: - LD A,(CFG+2) ; Z180 base I/O port + LD A,(Z180) ; Z180 base I/O port CP $FF ; Check for no value RET Z ; Bail out if no value ADD A,$32 ; Apply offset of DCNTL register @@ -482,7 +470,7 @@ SLOWIO: ; ; NORMIO: - LD A,(CFG+2) ; Z180 base I/O port + LD A,(Z180) ; Z180 base I/O port CP $FF ; Check for no value RET Z ; Bail out if no value ADD A,$32 ; Apply offset of DCNTL register @@ -716,25 +704,56 @@ ERR2: ; without the string ; ; CONFIG TABLE, ENTRY ORDER MATCHES HBIOS PLATFORM ID ; -CFGTBL: ; RSEL RDAT Z180 ACR - .DB $FF, $FF, $FF, $FF ; PLATFORM ID 0 IS INVALID - .DB $9A, $9B, $FF, $9C ; SBC W/ SCG - .DB $FF, $FF, $FF, $FF ; ZETA (NOT POSSIBLE) - .DB $FF, $FF, $FF, $FF ; ZETA 2 (NOT POSSIBLE) - .DB $9C, $9D, $40, $FF ; N8 W/ ONBOARD PSG - .DB $9A, $9B, $40, $9C ; MK4 W/ SCG - .DB $9A, $9B, $FF, $FF ; UNA (NOT SUPPORTED) - .DB $D8, $D0, $FF, $FF ; RCZ80 W/ RC SOUND MODULE (EB) - .DB $68, $60, $C0, $FF ; RCZ180 W/ RC SOUND MODULE (EB) - .DB $D8, $D0, $FF, $FF ; EZZ80 W/ RC SOUND MODULE (EB) - .DB $68, $60, $C0, $FF ; SCZ180 W/ RC SOUND MODULE (EB) -; -CFG: ; ACTIVE CONFIG VALUES (FROM SELECTED CFGTBL) +CFGSIZ .EQU 8 +; +CFGTBL: ; PLT RSEL RDAT RIN Z180 ACR + ; DESC + .DB $01, $9A, $9B, $9A, $FF, $9C ; SBC W/ SCG + .DW HWSTR_SCG +; + .DB $04, $9C, $9D, $9C, $40, $FF ; N8 W/ ONBOARD PSG + .DW HWSTR_N8 +; + .DB $05, $9A, $9B, $9A, $40, $9C ; MK4 W/ SCG + .DW HWSTR_SCG +; + .DB $07, $D8, $D0, $D8, $FF, $FF ; RCZ80 W/ RC SOUND MODULE (EB) + .DW HWSTR_RCEB +; + .DB $07, $D1, $D0, $D0, $FF, $FF ; RCZ80 W/ RC SOUND MODULE (MF) + .DW HWSTR_RCMF +; + .DB $08, $68, $60, $68, $C0, $FF ; RCZ180 W/ RC SOUND MODULE (EB) + .DW HWSTR_RCEB +; + .DB $08, $61, $60, $60, $C0, $FF ; RCZ180 W/ RC SOUND MODULE (MF) + .DW HWSTR_RCMF +; + .DB $09, $D8, $D0, $D8, $FF, $FF ; EZZ80 W/ RC SOUND MODULE (EB) + .DW HWSTR_RCEB +; + .DB $09, $D1, $D0, $D0, $FF, $FF ; EZZ80 W/ RC SOUND MODULE (EB) + .DW HWSTR_RCMF +; + .DB $0A, $68, $60, $68, $C0, $FF ; SCZ180 W/ RC SOUND MODULE (EB) + .DW HWSTR_RCEB +; + .DB $0A, $61, $60, $60, $C0, $FF ; SCZ180 W/ RC SOUND MODULE (MF) + .DW HWSTR_RCMF +; + .DB $FF ; END OF TABLE MARKER +; +CFG: ; ACTIVE CONFIG VALUES (FROM SELECTED CFGTBL ENTRY) +PLT .DB 0 ; RomWBW HBIOS platform id +PORTS: RSEL .DB 0 ; Register selection port RDAT .DB 0 ; Register data port +RIN .DB 0 ; Register input port Z180 .DB 0 ; Z180 base I/O port ACR .DB 0 ; Aux Ctrl Reg I/O port on SCG +DESC .DW 0 ; Hardware description string adr ; +CURPLT .DB 0 ; Current platform id reported by HBIOS QDLY .DW 0 ; quark delay factor WMOD .DB 0 ; delay mode, non-zero to use timer DCSAV .DB 0 ; for saving original Z180 DCNTL value @@ -747,8 +766,8 @@ FILTYP .DB 0 ; Sound file type (TYPPT2, TYPPT3, TYPMYM) TMP .DB 0 ; work around use of undocumented Z80 ; -MSGBAN .DB "Tune Player for RomWBW v2.2, 21-Nov-2019",0 -MSGUSE .DB "Copyright (C) 2019, Wayne Warthen, GNU GPL v3",13,10 +MSGBAN .DB "Tune Player for RomWBW v2.3, 11-Feb-2020",0 +MSGUSE .DB "Copyright (C) 2020, Wayne Warthen, GNU GPL v3",13,10 .DB "PTxPlayer Copyright (C) 2004-2007 S.V.Bulba",13,10 .DB "MYMPlay by Marq/Lieves!Tuore",13,10,13,10 .DB "Usage: TUNE .[PT2|PT3|MYM]",0 @@ -764,29 +783,10 @@ MSGDLY .DB ", delay mode",0 MSGPLY .DB "Playing...",0 MSGEND .DB " Done",0 ; -PLTSTR: - .DW 0 - .DW PLTSTR_SBC - .DW PLTSTR_ZETA - .DW PLTSTR_ZETA2 - .DW PLTSTR_N8 - .DW PLTSTR_MK4 - .DW PLTSTR_UNA - .DW PLTSTR_RCZ80 - .DW PLTSTR_RCZ180 - .DW PLTSTR_EZZ80 - .DW PLTSTR_SCZ180 -; -PLTSTR_SBC .DB "SBC w/ SCG ECB Sound Card",0 -PLTSTR_ZETA .DB "Zeta -- Not Supported!!!",0 -PLTSTR_ZETA2 .DB "Zeta 2 -- Not Supported!!!",0 -PLTSTR_N8 .DB "N8 Onboard Sound System",0 -PLTSTR_MK4 .DB "Mark IV w/ SCG ECB Sound Card",0 -PLTSTR_UNA .DB "UNA -- Not Supported!!!",0 -PLTSTR_RCZ80 .DB "RC2014 Z80 w/ Sound Module (EB)",0 -PLTSTR_RCZ180 .DB "RC2014 Z180 w/ Sound Module (EB)",0 -PLTSTR_EZZ80 .DB "Easy Z80 w/ Sound Module (EB)",0 -PLTSTR_SCZ180 .DB "SC Z180 w/ Sound Module (EB)",0 +HWSTR_SCG .DB "SCG ECB Board",0 +HWSTR_N8 .DB "N8 Onboard Sound",0 +HWSTR_RCEB .DB "RC2014 Sound Module (EB)",0 +HWSTR_RCMF .DB "RC2014 Sound Module (MF)",0 ; ;=============================================================================== ; PTx Player Routines @@ -2172,7 +2172,7 @@ LOUT OUT (C),A #IF WBW DI CALL SLOWIO - LD DE,(CFG) ; D := RDAT, E := RSEL + LD DE,(PORTS) ; D := RDAT, E := RSEL XOR A ; start with reg 0 LD C,E ; point to address port LD HL,AYREGS ; start of value list @@ -2336,7 +2336,7 @@ T_PACK .DB $06EC*2/256,$06EC*2 ; ; MYMPLAY - Player for MYM-tunes ; MSX-version by Marq/Lieves!Tuore & Fit 30.1.2000 -: +; ; 1.2.2000 - Added the disk loader. Thanks to Yzi & Plaque for examples. ; 7.2.2000 - Removed one unpack window -> freed 1.7kB memory ; @@ -2557,7 +2557,7 @@ upsg: ld a,(WMOD) ; if WMOD = 1, CPU is z180 call SLOWIO upsg1: ld hl,(psource) - ld de,(CFG) ; E := RSEL, D := RDAT + ld de,(PORTS) ; E := RSEL, D := RDAT xor a psglp: ld c,e ; C := RSEL @@ -2629,47 +2629,47 @@ HEAP .EQU $ VARS -ChanA .DS CHP -ChanB .DS CHP -ChanC .DS CHP +ChanA .DS CHP +ChanB .DS CHP +ChanC .DS CHP ;GlobalVars -DelyCnt .DS 1 -CurESld .DS 2 -CurEDel .DS 1 +DelyCnt .DS 1 +CurESld .DS 2 +CurEDel .DS 1 Ns_Base_AddToNs -Ns_Base .DS 1 -AddToNs .DS 1 +Ns_Base .DS 1 +AddToNs .DS 1 AYREGS -VT_ .DS 256 ;CreatedVolumeTableAddress +VT_ .DS 256 ;CreatedVolumeTableAddress -EnvBase .EQU VT_+14 +EnvBase .EQU VT_+14 -T1_ .EQU VT_+16 ;Tone tables data depacked here +T1_ .EQU VT_+16 ;Tone tables data depacked here -T_OLD_1 .EQU T1_ -T_OLD_2 .EQU T_OLD_1+24 -T_OLD_3 .EQU T_OLD_2+24 -T_OLD_0 .EQU T_OLD_3+2 -T_NEW_0 .EQU T_OLD_0 -T_NEW_1 .EQU T_OLD_1 -T_NEW_2 .EQU T_NEW_0+24 -T_NEW_3 .EQU T_OLD_3 +T_OLD_1 .EQU T1_ +T_OLD_2 .EQU T_OLD_1+24 +T_OLD_3 .EQU T_OLD_2+24 +T_OLD_0 .EQU T_OLD_3+2 +T_NEW_0 .EQU T_OLD_0 +T_NEW_1 .EQU T_OLD_1 +T_NEW_2 .EQU T_NEW_0+24 +T_NEW_3 .EQU T_OLD_3 -PT2EMPTYORN .EQU VT_+31 ;1,0,0 sequence +PT2EMPTYORN .EQU VT_+31 ;1,0,0 sequence -NT_ .FILL 192 ;CreatedNoteTableAddress +NT_ .DS 192 ;CreatedNoteTableAddress ;local var -Ampl .EQU AYREGS+AmplC +Ampl .EQU AYREGS+AmplC -VAR0END .EQU VT_+16 ;INIT zeroes from VARS to VAR0END-1 +VAR0END .EQU VT_+16 ;INIT zeroes from VARS to VAR0END-1 -VARSEND .EQU $ +VARSEND .EQU $ -MDLADDR .EQU $ +MDLADDR .EQU $ ; ;=============================================================================== ; MYM Player Storage @@ -2678,10 +2678,11 @@ MDLADDR .EQU $ .ORG HEAP ; Reserve room for uncompressed data uncomp: -.org $+(3*FRAG*REGS) + .DS (3*FRAG*REGS) ; The tune is stored here -rows: .dw 0 +rows: + .DS 2 ; WORD value data: ; ;=============================================================================== diff --git a/Source/Apps/XM/Makefile b/Source/Apps/XM/Makefile new file mode 100644 index 00000000..72aed531 --- /dev/null +++ b/Source/Apps/XM/Makefile @@ -0,0 +1,12 @@ +OBJECTS = xm.com xmuf.com +DEST = ../../../Binary/Apps +TOOLS = ../../../Tools +OTHERS = *.hex + +include $(TOOLS)/Makefile.inc + +xm.com: xmdm125.hex xmhb.hex + $(ZXCC) $(CPM)/MLOAD25 XM=xmdm125,xmhb + +xmuf.com: xmdm125.hex xmuf.hex + $(ZXCC) $(CPM)/MLOAD25 XMUF=xmdm125,xmuf diff --git a/Source/BPBIOS/@WBW Issues.txt b/Source/BPBIOS/@WBW Issues.txt new file mode 100644 index 00000000..f7d12f4c --- /dev/null +++ b/Source/BPBIOS/@WBW Issues.txt @@ -0,0 +1,15 @@ +Loader uses CBIOS Disk I/O prior to CBOOT/WBOOT being run. As a +result, DIOBUF is not properly initialized. At present, it is +initialized to $7C00 which will work unless the location of the +physical disk buffer in HBIOS ever changes! + +The clock drivers supplied with BPBIOS (LDDS, LDP2D, and LDNZT) +load into ZSYS user memory. If the user segment is not at the +same location as the original BPBIOS ($E900), then the clock +driver will just overlay other code. At present, the N config +is OK because it does not relocate the user segment, but the T +config fails because it uses a user segment at $E700. Note that +this does not affect ZSDOS2 variants because they do not require +a loadable clock driver. + +BPBIOS needs to assign disk units dynamically via discovery of disk type diff --git a/Source/BPBIOS/@WBW Z3ENV.txt b/Source/BPBIOS/@WBW Z3ENV.txt index 0b8bb617..8c17e2ca 100644 --- a/Source/BPBIOS/@WBW Z3ENV.txt +++ b/Source/BPBIOS/@WBW Z3ENV.txt @@ -75,7 +75,7 @@ should be consulted to understand these. The build process used here produces several different configurations which can be loaded at runtime. The original distributed memory segment configuration occupies the top of memory which, unfortunately, conflicts with the RomWBW HBIOS need to -occupt this space. +occupy this space. Although RomWBW HBIOS is implemented in it's own dedicated memory bank, it requires a small proxy at the top of memory which acts as a mechansim to diff --git a/Source/BPBIOS/Build.cmd b/Source/BPBIOS/Build.cmd index db50c0d7..54186541 100644 --- a/Source/BPBIOS/Build.cmd +++ b/Source/BPBIOS/Build.cmd @@ -3,6 +3,8 @@ setlocal setlocal & cd ZCPR33 && call Build || exit /b 1 & endlocal +setlocal & cd Z34RCP11 && call Build || exit /b 1 & endlocal +setlocal & cd NZFCP13 && call Build || exit /b 1 & endlocal set PATH=%PATH%;..\..\Tools\zx;..\..\Tools\cpmtools; @@ -26,22 +28,22 @@ call :makebp 41nbnk rem pause -cpmrm.exe -f wbw_hd0 ../../Binary/hd0.img 0:ws*.* - -cpmrm.exe -f wbw_hd0 ../../Binary/hd0.img 0:*.img -cpmcp.exe -f wbw_hd0 ../../Binary/hd0.img *.img 0: - -cpmrm.exe -f wbw_hd0 ../../Binary/hd0.img 0:*.rel -cpmcp.exe -f wbw_hd0 ../../Binary/hd0.img *.rel 0: - -rem cpmrm.exe -f wbw_hd0 ../../Binary/hd0.img 0:*.dat -rem cpmcp.exe -f wbw_hd0 ../../Binary/hd0.img *.dat 0: - -cpmrm.exe -f wbw_hd0 ../../Binary/hd0.img 0:*.zex -cpmcp.exe -f wbw_hd0 ../../Binary/hd0.img *.zex 0: - -cpmrm.exe -f wbw_hd0 ../../Binary/hd0.img 0:myterm.z3t -cpmcp.exe -f wbw_hd0 ../../Binary/hd0.img myterm.z3t 0:myterm.z3t +rem cpmrm.exe -f wbw_hd0 ../../Binary/hd_bp.img 0:ws*.* +rem +rem cpmrm.exe -f wbw_hd0 ../../Binary/hd_bp.img 0:*.img +rem cpmcp.exe -f wbw_hd0 ../../Binary/hd_bp.img *.img 0: +rem +rem cpmrm.exe -f wbw_hd0 ../../Binary/hd_bp.img 0:*.rel +rem cpmcp.exe -f wbw_hd0 ../../Binary/hd_bp.img *.rel 0: +rem +rem rem cpmrm.exe -f wbw_hd0 ../../Binary/hd_bp.img 0:*.dat +rem rem cpmcp.exe -f wbw_hd0 ../../Binary/hd_bp.img *.dat 0: +rem +rem cpmrm.exe -f wbw_hd0 ../../Binary/hd_bp.img 0:*.zex +rem cpmcp.exe -f wbw_hd0 ../../Binary/hd_bp.img *.zex 0: +rem +rem cpmrm.exe -f wbw_hd0 ../../Binary/hd_bp.img 0:myterm.z3t +rem cpmcp.exe -f wbw_hd0 ../../Binary/hd_bp.img myterm.z3t 0:myterm.z3t goto :eof @@ -57,6 +59,8 @@ rem if exist bpbio-ww.rel del bpbio-ww.rel zx ZMAC -BPBIO-WW -/P if exist bp%VER%.prn del bp%VER%.prn ren bpbio-ww.prn bp%VER%.prn +ren bpbio-ww.err bp%VER%.err +copy bpbio-ww.rel bp%VER%.rel rem pause diff --git a/Source/BPBIOS/Clean.cmd b/Source/BPBIOS/Clean.cmd index 62c3210f..f713819a 100644 --- a/Source/BPBIOS/Clean.cmd +++ b/Source/BPBIOS/Clean.cmd @@ -8,5 +8,8 @@ if exist *.img del *.img if exist bp*.rel del bp*.rel if exist zcpr33*.rel del zcpr33*.rel if exist *.bak del *.bak +if exist def-ww.lib del def-ww.lib setlocal & cd ZCPR33 && call Clean.cmd & endlocal +setlocal & cd Z34RCP11 && call Clean.cmd & endlocal +setlocal & cd NZFCP13 && call Clean.cmd & endlocal diff --git a/Source/BPBIOS/Makefile b/Source/BPBIOS/Makefile new file mode 100644 index 00000000..d1b0c403 --- /dev/null +++ b/Source/BPBIOS/Makefile @@ -0,0 +1,47 @@ +VERSIONS = \ + 33t 33tbnk \ + 33n 33nbnk \ + 34t 34tbnk \ + 34n 34nbnk \ + 41tbnk 41nbnk + +HD0IMG = ../../Binary/hd_bp.img +IMGFILES = $(foreach ver,$(VERSIONS),bp$(ver).img) +DISTFILES = *.zex *.rel myterm.z3t + +OTHERS = zcpr33n.rel zcpr33t.rel \ + bpbio-ww.rel bpsys.dat bpsys.bak bpbio-ww.err def-ww.lib *.img + +TOOLS = ../../Tools + +SUBDIRS = ZCPR33 NZFCP13 Z34RCP11 +include $(TOOLS)/Makefile.inc + +$(HD0IMG): $(IMGFILES) + if [ -f $(HD0IMG) ] ; then \ + for f in $(IMGFILES) $(DISTFILES) ; do \ + $(BINDIR)/cpmrm -f wbw_hd0 $(HD0IMG) 0:$$f ; \ + done ; \ + $(CPMCP) -f wbw_hd0 $(HD0IMG) $(IMGFILES) $(DISTFILES) 0: ; \ + fi + +zcpr33n.rel zcpr33t.rel: + (cd ZCPR33 ; make) + +all:: $(HD0IMG) + +clobber:: + @rm -f $(HD0IMG) + +%.img: zcpr33n.rel zcpr33t.rel + $(eval VER := $(subst .img,,$(subst bp,,$@))) + cp def-ww-z$(VER).lib def-ww.lib + rm -f bpbio-ww.rel + $(ZXCC) $(CPM)/ZMAC -BPBIO-WW -/P + mv bpbio-ww.prn bp$(VER).prn + cp bp$(VER).dat bpsys.dat + $(ZXCC) ./bpbuild.com -bpsys.dat 0 < bpbld1.rsp + cp bpsys.img bpsys.dat + $(ZXCC) ./bpbuild.com -bpsys.dat 0 < bpbld2.rsp + mv bpsys.img bp$(VER).img + diff --git a/Source/BPBIOS/NZFCP13/Build.cmd b/Source/BPBIOS/NZFCP13/Build.cmd new file mode 100644 index 00000000..2c53cfd9 --- /dev/null +++ b/Source/BPBIOS/NZFCP13/Build.cmd @@ -0,0 +1,11 @@ +@echo off +setlocal + +set PATH=%PATH%;..\..\..\Tools\zx;..\..\..\Tools\cpmtools; + +set ZXBINDIR=../../../tools/cpm/bin/ +set ZXLIBDIR=../../../tools/cpm/lib/ +set ZXINCDIR=../../../tools/cpm/include/ + +zx Z80ASM -nzfcp13/MF +rem zx ZMAC -nzfcp13.z80 -/P diff --git a/Source/BPBIOS/NZFCP13/Clean.cmd b/Source/BPBIOS/NZFCP13/Clean.cmd new file mode 100644 index 00000000..a088f4e8 --- /dev/null +++ b/Source/BPBIOS/NZFCP13/Clean.cmd @@ -0,0 +1,7 @@ +@echo off +setlocal + +if exist *.prn del *.prn +if exist *.lst del *.lst +if exist *.err del *.err +if exist *.rel del *.rel diff --git a/Source/BPBIOS/NZFCP13/Makefile b/Source/BPBIOS/NZFCP13/Makefile new file mode 100644 index 00000000..614a877d --- /dev/null +++ b/Source/BPBIOS/NZFCP13/Makefile @@ -0,0 +1,5 @@ +OBJECTS = nzfcp13.rel +OTHERS = +TOOLS = ../../../Tools + +include $(TOOLS)/Makefile.inc diff --git a/Source/BPBIOS/NZFCP13/fcp-4.zrl b/Source/BPBIOS/NZFCP13/fcp-4.zrl new file mode 100644 index 00000000..a6f2c9a2 Binary files /dev/null and b/Source/BPBIOS/NZFCP13/fcp-4.zrl differ diff --git a/Source/BPBIOS/NZFCP13/fcp-4t.zrl b/Source/BPBIOS/NZFCP13/fcp-4t.zrl new file mode 100644 index 00000000..8d3cb48c Binary files /dev/null and b/Source/BPBIOS/NZFCP13/fcp-4t.zrl differ diff --git a/Source/BPBIOS/NZFCP13/fcp-5.zrl b/Source/BPBIOS/NZFCP13/fcp-5.zrl new file mode 100644 index 00000000..a2456212 Binary files /dev/null and b/Source/BPBIOS/NZFCP13/fcp-5.zrl differ diff --git a/Source/BPBIOS/NZFCP13/fcp-5t.zrl b/Source/BPBIOS/NZFCP13/fcp-5t.zrl new file mode 100644 index 00000000..eb94964c Binary files /dev/null and b/Source/BPBIOS/NZFCP13/fcp-5t.zrl differ diff --git a/Source/BPBIOS/NZFCP13/fcp-6.zrl b/Source/BPBIOS/NZFCP13/fcp-6.zrl new file mode 100644 index 00000000..6aa1aea9 Binary files /dev/null and b/Source/BPBIOS/NZFCP13/fcp-6.zrl differ diff --git a/Source/BPBIOS/NZFCP13/fcp-6t.zrl b/Source/BPBIOS/NZFCP13/fcp-6t.zrl new file mode 100644 index 00000000..b75fbfb7 Binary files /dev/null and b/Source/BPBIOS/NZFCP13/fcp-6t.zrl differ diff --git a/Source/BPBIOS/NZFCP13/fcp-7t.zrl b/Source/BPBIOS/NZFCP13/fcp-7t.zrl new file mode 100644 index 00000000..8a12be09 Binary files /dev/null and b/Source/BPBIOS/NZFCP13/fcp-7t.zrl differ diff --git a/Source/BPBIOS/NZFCP13/nzfcp.doc b/Source/BPBIOS/NZFCP13/nzfcp.doc new file mode 100644 index 00000000..1eb1b0ab --- /dev/null +++ b/Source/BPBIOS/NZFCP13/nzfcp.doc @@ -0,0 +1,89 @@ + Z-Relocatable Flow Control Packages + 11 October 89 by Carson Wilson + +The seven pre-compiled Flow Control Packages (FCP's) in this library may be +loaded directly to Z System with NZ-COM, Z3PLUS, or JetLDR, provided +sufficient space has been allocated to the FCP segment. The FCP's come in two +flavors. Files named FCP-nT.ZRL implement extended flow control processing +when needed using a program named IF.COM at the root of your path, or at +directory A0 if there is no path. Files named simply FCP-n.ZRL rely +exclusively on memory-based processing. The "n" in the filename indicates the +total number of records required by each package. The standard number of +records for both NZ-COM and Z3PLUS is currently 5, so if you wish to load a +larger FCP you must reconfigure your system with MKZCM or by editing your .Z3P +file. For general help with flow control commands, see the file IF.HLP, or +section 3.2.2 of your NZ-COM or Z3PLUS manual. + +While IF.COM allows options not available in memory-resident flow-control +processing, fully resident versions free the user from the requirement that +IF.COM be present during flow control processing. Resident processing is also +somewhat faster due to the need to load IF.COM from disk for each flow control +command. Finally, the ability to locate and load the transient IF.COM +requires considerable code space in the resident code itself. Generally, +IF.COM is best used on fixed-disk systems, while resident processing is more +suited to floppy-based systems. + +The options included in fully resident versions were selected somewhat +differently than those of those using IF.COM. Criteria, in order of +precedence, were as follows: + +Transient Versions Resident Versions +------------------ ----------------- +1. Is the feature available in 1. How useful is the feature? + IF.COM? 2. How much memory does the feature +2. Does the feature require disk require? + access? +3. How useful is the feature? +4. How much memory does the feature + require? + +The tables below summarize the size and features of each flavor of FCP. Each +package includes only the features appearing above it. For example, +FCP-5T.ZRL (the default FCP for both NZ-COM and Z3PLUS) contains only ZIF, +IFQ, OR, AND, negation, ERROR, and NULL. Size is expressed as records plus +remaining bytes required by the FCP. As FCP space is allocated in record +units only, I have attempted to include only combinations which leave as +little remaining space possible. Sizes without corresponding filenames are +included for reference purposes only. + +FEATURES OF TRANSIENT (IF.COM) FCPS SIZE(RECS+BYTES) FILE NAME +----------------------------------------------------------------------------- +ZIF - unconditionally clear IF states? +IFQ - show current if status? 3+124 FCP-4T.ZRL +OR - set state at current level? 4+25 +AND - reset state at current level? +Allow negation of conditions? +ERROR - test program error flag? +NULL - test for no file name? 4+84 FCP-5T.ZRL +REG - test register values? 5+16 +AMBIG - test for "?" in file spec? +COMPR - test for compressed filespec? += - test tokens for equality? 5+92 FCP-6T.ZRL +INPUT - test user input? 6+25 +Allow "IF T" and "IF F" forms? +WHEEL - test wheel byte? +TCAP - test whether TCAP loaded? +EXIST - test for file existence? 6+106 FCP-7T.ZRL +EMPTY - test files for contents? 7+10 + + +FEATURES OF RESIDENT FCPS SIZE(RECS+BYTES) FILE NAME +----------------------------------------------------------------------------- +ZIF - unconditionally clear IF states? +IFQ - show current if status? +OR - set state at current level? +AND - reset state at current level? +Allow negation of conditions? +NULL - test for no file name? +INPUT - test user input? += - test tokens for equality? +ERROR - test program error flag? +Allow "IF T" and "IF F" forms? +EXIST - test for file existence? 3+123 FCP-4.ZRL +AMBIG - test for "?" in file spec? +COMPR - test for compressed filespec? +REG - test register values? +WHEEL - test wheel byte? +TCAP - test whether TCAP loaded? 4+113 FCP-5.ZRL +EMPTY - test files for contents? 5+17 FCP-6.ZRL + \ No newline at end of file diff --git a/Source/BPBIOS/NZFCP13/nzfcp.lib b/Source/BPBIOS/NZFCP13/nzfcp.lib new file mode 100644 index 00000000..db55f33f --- /dev/null +++ b/Source/BPBIOS/NZFCP13/nzfcp.lib @@ -0,0 +1,161 @@ +; Module: NZFCP.LIB +; Author: Carson Wilson +; Version: 1.3 +; Date: 9 Oct 89 +; Changes: Prompts for options if SLR true. + +; This module is a set of configuration options for the flow control package +; code in NZFCP.Z80. The options are not fully commented here; see +; "ZCPR3, The Manual" for a detailed discussion of most of them. + + +; * * * * * * N O T I C E * * * * * * +; +; The main code in Z33FCP.Z80 has been written with relative jump instructions +; for minimum code size. If the options below are changed, then some of the +; jumps may become out-of-range. You will then have to change the code based +; on the error messages you get from your assembler. I cannot think of any +; way to handle the jumps automatically (macros fail because the jump distances +; cannot be resolved fully even in two passes of the assembler). If you want +; to play things really safe, you can do a global search and replace to change +; all occurrences of the JR instruction to JP (but note that this change is +; irreversible). I prefer to live with this nuisance so that I can get the +; most out of the code. +; +; Another possible way to clear up a problem with a relative jump that is out +; of range is to take the block of code with the labels IFCTRUE and IFCFALSE +; and move it up or down in the code. It is placed somewhere in the middle of +; the resident options, so that the options can reach those entry points with +; a relative jump. You should try to place that code near the middle of the +; options which you have enabled. +; +; Jay Sage (May 17, 1987) + +;============================================================================= +; +; M A C R O S +; +;============================================================================= + +; SLR assemblers allow selection of options during assembly. + +SLR equ no + + if SLR + +y equ yes +n equ no + +select macro option default comment + .accept comment,option ; Prompt user for selection + endm + + .printx + .printx Answer "Y" to include, "N" to exclude commands: + .printx + + else + +select macro option default comment +option equ default ; Use selections from file + endm + endif ; SLR + +; ------------------------------------------------------------------------- + +; Command names + +; The CTABLE macro, which constructs the command dispatch table, includes a +; line for each command, the first parameter of which is the name of the +; command. These names may be changed if you wish. But make sure that you +; do not use a name that is longer than the maximum allowed length. If you +; do, the name will be truncated and a nonfatal error will occur during +; assembly. DO NOT CHANGE ANY PARAMETER OTHER THAN THE COMMAND NAME. Lower +; case letters will be converted to upper case. The macro COMMAND is defined +; in Z34MAC.LIB. + +cmdsize equ 4 ; Maximum length of command names + +; Command table name, enable, wheel, jump_addr + ; [ DO NOT CHANGE THESE PARAMETERS ] +ctable macro +ifcmd: command if, yes, no, ifstart + command and, andopt, no, andstart + command or, oropt, no, orstart + command else, yes, no, ifelse + command fi, yes, no, ifend + command ifq, ifqopt, no, ifstat0 + command xif, yes, no, ifexit + command zif, zifopt, no, ifzero + endm + +;----------------------------------------------------------------------------- + +; General configuration options + +NOISE equ no ; Don't display if-state messages + +select COMIF no 'Enable transient IF processing? ' + +PATHROOT equ yes ; Find transient IF in root of path + +ifdrv equ 'A' ; Drive to use if PATHROOT is off or + ; ..if the path is empty +ifusr equ 0 ; User to use if PATHROOT is off or + ; ..if the path is empty + +; --------------------------------------------------------------------- + +; Command inclusion options + +select ZIFOPT yes 'ZIF - unconditionally clear IF states? ' +select IFQOPT yes 'IFQ - show current if status? ' +select OROPT yes 'OR - set state at current level? ' +select ANDOPT yes 'AND - reset state at current level? ' + +;----------------------------------------------------------------------------- + +; If Condition Options + + if COMIF ; Different precedence if transient IF available. +select IFONEG yes 'Allow negation of conditions? ' +negchar equ '~' ; Character to use if negation allowed +select IFOERROR yes 'ERROR - test program error flag? ' +select IFONULL yes 'NULL - test for no file name? ' +select IFOREG yes 'REG - test register values? ' +select IFAMBIG yes 'AMBIG - test for "?" in file spec? ' +select IFCOMPR yes 'COMPR - test for compressed filespec? ' +select IFOEQ yes '= - test tokens for equality? ' +select IFOINPUT yes 'INPUT - test user input? ' +XEQOPT equ yes ; Test only first token for equal sign +select IFOTRUE yes 'Allow "IF T" and "IF F" forms? ' +select IFOWHEEL no 'WHEEL - test wheel byte? ' +select IFOTCAP no 'TCAP - test whether TCAP loaded? ' +select IFOEXIST yes 'EXIST - test for file existence? ' +select IFOEMPTY yes 'EMPTY - test files for contents? ' + else +select IFONEG yes 'Allow negation of conditions? ' +negchar equ '~' ; Character to use if negation allowed +select IFONULL yes 'NULL - test for no file name? ' +select IFOINPUT yes 'INPUT - test user input? ' +select IFOEQ yes '= - test tokens for equality? ' +XEQOPT equ yes ; Test only first token for equal sign +select IFOERROR yes 'ERROR - test program error flag? ' +select IFOTRUE yes 'Allow "IF T" and "IF F" forms? ' +select IFOEXIST yes 'EXIST - test for file existence? ' +select IFAMBIG no 'AMBIG - test for "?" in file spec? ' +select IFCOMPR no 'COMPR - test for compressed filespec? ' +select IFOREG no 'REG - test register values? ' +select IFOWHEEL no 'WHEEL - test wheel byte? ' +select IFOTCAP no 'TCAP - test whether TCAP loaded? ' +select IFOEMPTY no 'EMPTY - test files for contents? ' + endif ; COMIF + +; Miscellaneous configuration information + +curusr equ z3msg+2eh ; Current logged user address +curdr equ z3msg+2fh ; Current logged drive address +curint equ '$' ; Path symbol for current drive/user + +; END of NZFCP.LIB + \ No newline at end of file diff --git a/Source/BPBIOS/NZFCP13/nzfcp.lib.sav b/Source/BPBIOS/NZFCP13/nzfcp.lib.sav new file mode 100644 index 00000000..18f557f8 --- /dev/null +++ b/Source/BPBIOS/NZFCP13/nzfcp.lib.sav @@ -0,0 +1,161 @@ +; Module: NZFCP.LIB +; Author: Carson Wilson +; Version: 1.3 +; Date: 9 Oct 89 +; Changes: Prompts for options if SLR true. + +; This module is a set of configuration options for the flow control package +; code in NZFCP.Z80. The options are not fully commented here; see +; "ZCPR3, The Manual" for a detailed discussion of most of them. + + +; * * * * * * N O T I C E * * * * * * +; +; The main code in Z33FCP.Z80 has been written with relative jump instructions +; for minimum code size. If the options below are changed, then some of the +; jumps may become out-of-range. You will then have to change the code based +; on the error messages you get from your assembler. I cannot think of any +; way to handle the jumps automatically (macros fail because the jump distances +; cannot be resolved fully even in two passes of the assembler). If you want +; to play things really safe, you can do a global search and replace to change +; all occurrences of the JR instruction to JP (but note that this change is +; irreversible). I prefer to live with this nuisance so that I can get the +; most out of the code. +; +; Another possible way to clear up a problem with a relative jump that is out +; of range is to take the block of code with the labels IFCTRUE and IFCFALSE +; and move it up or down in the code. It is placed somewhere in the middle of +; the resident options, so that the options can reach those entry points with +; a relative jump. You should try to place that code near the middle of the +; options which you have enabled. +; +; Jay Sage (May 17, 1987) + +;============================================================================= +; +; M A C R O S +; +;============================================================================= + +; SLR assemblers allow selection of options during assembly. + +SLR equ yes + + if SLR + +y equ yes +n equ no + +select macro option default comment + .accept comment,option ; Prompt user for selection + endm + + .printx + .printx Answer "Y" to include, "N" to exclude commands: + .printx + + else + +select macro option default comment +option equ default ; Use selections from file + endm + endif ; SLR + +; ------------------------------------------------------------------------- + +; Command names + +; The CTABLE macro, which constructs the command dispatch table, includes a +; line for each command, the first parameter of which is the name of the +; command. These names may be changed if you wish. But make sure that you +; do not use a name that is longer than the maximum allowed length. If you +; do, the name will be truncated and a nonfatal error will occur during +; assembly. DO NOT CHANGE ANY PARAMETER OTHER THAN THE COMMAND NAME. Lower +; case letters will be converted to upper case. The macro COMMAND is defined +; in Z34MAC.LIB. + +cmdsize equ 4 ; Maximum length of command names + +; Command table name, enable, wheel, jump_addr + ; [ DO NOT CHANGE THESE PARAMETERS ] +ctable macro +ifcmd: command if, yes, no, ifstart + command and, andopt, no, andstart + command or, oropt, no, orstart + command else, yes, no, ifelse + command fi, yes, no, ifend + command ifq, ifqopt, no, ifstat0 + command xif, yes, no, ifexit + command zif, zifopt, no, ifzero + endm + +;----------------------------------------------------------------------------- + +; General configuration options + +NOISE equ no ; Don't display if-state messages + +select COMIF no 'Enable transient IF processing? ' + +PATHROOT equ yes ; Find transient IF in root of path + +ifdrv equ 'A' ; Drive to use if PATHROOT is off or + ; ..if the path is empty +ifusr equ 0 ; User to use if PATHROOT is off or + ; ..if the path is empty + +; --------------------------------------------------------------------- + +; Command inclusion options + +select ZIFOPT yes 'ZIF - unconditionally clear IF states? ' +select IFQOPT yes 'IFQ - show current if status? ' +select OROPT yes 'OR - set state at current level? ' +select ANDOPT yes 'AND - reset state at current level? ' + +;----------------------------------------------------------------------------- + +; If Condition Options + + if COMIF ; Different precedence if transient IF available. +select IFONEG yes 'Allow negation of conditions? ' +negchar equ '~' ; Character to use if negation allowed +select IFOERROR yes 'ERROR - test program error flag? ' +select IFONULL yes 'NULL - test for no file name? ' +select IFOREG yes 'REG - test register values? ' +select IFAMBIG yes 'AMBIG - test for "?" in file spec? ' +select IFCOMPR yes 'COMPR - test for compressed filespec? ' +select IFOEQ yes '= - test tokens for equality? ' +select IFOINPUT yes 'INPUT - test user input? ' +XEQOPT equ yes ; Test only first token for equal sign +select IFOTRUE yes 'Allow "IF T" and "IF F" forms? ' +select IFOWHEEL no 'WHEEL - test wheel byte? ' +select IFOTCAP no 'TCAP - test whether TCAP loaded? ' +select IFOEXIST yes 'EXIST - test for file existence? ' +select IFOEMPTY yes 'EMPTY - test files for contents? ' + else +select IFONEG yes 'Allow negation of conditions? ' +negchar equ '~' ; Character to use if negation allowed +select IFONULL yes 'NULL - test for no file name? ' +select IFOINPUT yes 'INPUT - test user input? ' +select IFOEQ yes '= - test tokens for equality? ' +XEQOPT equ yes ; Test only first token for equal sign +select IFOERROR yes 'ERROR - test program error flag? ' +select IFOTRUE yes 'Allow "IF T" and "IF F" forms? ' +select IFOEXIST yes 'EXIST - test for file existence? ' +select IFAMBIG yes 'AMBIG - test for "?" in file spec? ' +select IFCOMPR yes 'COMPR - test for compressed filespec? ' +select IFOREG yes 'REG - test register values? ' +select IFOWHEEL no 'WHEEL - test wheel byte? ' +select IFOTCAP no 'TCAP - test whether TCAP loaded? ' +select IFOEMPTY yes 'EMPTY - test files for contents? ' + endif ; COMIF + +; Miscellaneous configuration information + +curusr equ z3msg+2eh ; Current logged user address +curdr equ z3msg+2fh ; Current logged drive address +curint equ '$' ; Path symbol for current drive/user + +; END of NZFCP.LIB + \ No newline at end of file diff --git a/Source/BPBIOS/NZFCP13/nzfcp13.for b/Source/BPBIOS/NZFCP13/nzfcp13.for new file mode 100644 index 00000000..40fb0c2c --- /dev/null +++ b/Source/BPBIOS/NZFCP13/nzfcp13.for @@ -0,0 +1,9 @@ +Source code plus the compiled Flow Control Packages (FCP's) +distributed in FCP.LBR as part of the Z System. The precompiled +modules are in Z-Relocatable form, and must be loaded with JetLDR, +NZCOM, or Z3PLUS. Now features interactive assembly of code +a-la-Z34RCP11 under the SLR or ZMAC assemblers. Macro now reports +length of resulting FCP in records and bytes following assembly. +JetLDR signons also automatically generated. 9/15/90 Carson Wilson + + \ No newline at end of file diff --git a/Source/BPBIOS/NZFCP13/nzfcp13.z80 b/Source/BPBIOS/NZFCP13/nzfcp13.z80 new file mode 100644 index 00000000..fe700554 --- /dev/null +++ b/Source/BPBIOS/NZFCP13/nzfcp13.z80 @@ -0,0 +1,1424 @@ + +; Program: NZFCP +; Date: October 9, 1989 +; Author: Carson Wilson +; Version: 1.3 +; Changes: Updated & improved JetLDR signon. +; Changed four JR's to JP's. + +; Date: August 21, 1988 +; Author: Carson Wilson +; Version: 1.2 + +; Derived from: + +; Date: April 1988 +; Name changed and code modified for NZ-COM. + +; Derived from: + +; PROGRAM: Z34FCP +; AUTHOR: Jay Sage +; VERSION: 1.0 +; DATE: May 25, 1987 +; DERIVATION: FCP10 by Jay Sage (ZSIG) + +; ZCPR34 is copyright 1987 by Jay P. Sage. All rights reserved. End-user +; distribution and duplication permitted for non-commercial purposes only. +; Any commercial use of ZCPR34, defined as any situation where the duplicator +; recieves revenue by duplicating or distributing ZCPR34 by itself or in +; conjunction with any hardware or software product, is expressly prohibited +; unless authorized in writing by Jay P. Sage. + +;============================================================================= +; +; R E V I S I O N H I S T O R Y +; +;============================================================================= +; +; 21 Aug 88 Added JetLDR signon description. +; IF IN now prints ' (Y/N)? ', and accepts only Y or y or +; N or n. +; Added macro code to show FCP length following assembly. +; +; Carson Wilson. +; +; 6 April 88 Handles latest Type 4 IF.COM +; 1.2 Changed command tail loader to accept :IF. Joe Wright +; +; 12/31/87 Modified for use with Z34CMN.LIB for NZ-COM. Joe Wright. +; 1.1 +; +; 05/25/87 Created ZCPR33 version from the code I released through ZSIG. +; 1.0 This code differs only in the more efficient way in which it +; determines if it was invoked with a directory prefix that +; signals that the transient IF.COM should be used to process +; the IF command. This permits the user to force the use of a +; more powerful option processor in the transient IF.COM than in +; the resident code. Option bytes were added after the end of +; the resident option dispatch table so that SHOW can report +; configuration options to the user. +; +; FCP10 notes +; +; The transient processor can now be loaded at an address other +; than 100h so as not to interfere with code loaded in the TPA. +; Then the GO command can normally be used even after IF.COM is +; used to process the flow test. If the LOADCHK equate +; is true then the FCP will verify that the transient +; processor has been loaded to the page in memory for which +; it was assembled. If loaded to the wrong page, it will +; be reloaded to the correct one. +; +; The test for the form ARG1=ARG2 was tightened up so as not to +; be confused by an equal sign in some later part of the command +; tail (e.g., "IF REG 1 = 2"). Now only the first token +; (contiguous string of characters) is checked. This extra code +; is under the control of the XEQOPT equate. The only option +; that is still a problem is the COMIF form '~='. Since the '=' +; is in the first token, this 'not equal' condition cannot be +; distinguished from an equality test against the character '~'. +; The solution is to turn off equality testing in the resident FCP +; or to use the alternative COMIF options 'NE' or '~EQ' for this +; test. +; +; Added optional commands AND and OR. These work like IF except +; that they affect the current IF level rather than going one +; level deeper. +; +; Added optional command ZIF to zero out all IF states no matter +; whether current state is true or false (XIF only works if state +; is true. +; +; Added new optional command IFQ (if-query) and enhanced the +; IFSTAT code that is invoked when the NOISE equate is true. +; In both cases, the entire tree of IF states is now shown, +; starting with the current level. For example, IFQ might result +; in the display "IF FTT" (we are at third IF level and it is +; false; the second and first IF levels are true). If the +; current IF level is 0, then the display is "IF None". +; +; Added two new resident options: AMBIGUOUS (AM) returns true if +; the file specification in the second token has a '?' (or '*') +; in it; COMPRESSED (CO) returns true if the file specificaton in +; the second token has a 'Z' or a 'Q' in the second character of +; the file type. +; +; Howard Goldstein contributed significantly to the development +; of this code. Bridger Mitchell also offered helpful +; suggestions. +; +; Jay Sage +; +; Notes from earlier SYSFCP revisions +; +; 09/12/85 Fixed bug in my code used when IF.COM is found in a specified +; drive/user area. The values of CDISK and CUSER were not being +; set, and as a result the user was not returned to the correct +; directory. The EXIST and EMPTY tests did not work correctly +; unless a DIR: or DU: was given explicitly with each file name. +; Jay Sage + +; 08/29/85 Reorganized code so that COMIF code handles only those +; options not in the table of local IF functions. Also changed +; code to allow searching for IF.COM in a specified directory +; instead of using the ROOT of the path. Also renamed macros +; to make code ZAS compatible. +; Jay Sage + +; 07/21/85 Corrected reversed sensing of program error flag in the +; IF ERROR test. +; Jay Sage + +; 01/02/85 Revised to correct a bug in the IF EMPTY test. First, the +; current record byte was not being set to zero before trying +; to read from the file. Secondly, the test for error was not +; testing for FF but for 00. My BDOS does not return 0 for +; success. It seems to return 00, 01, 02, or 03. This made the +; file appear to be empty. +; Jay Sage + + +;============================================================================= +; +; M A C R O S A N D E Q U A T E S +; +;============================================================================= + + name ('FCP') + +; External macro references + + maclib Z34CMN.LIB ; Source of system addresses + maclib NZFCP.LIB ; Source of configuration options + maclib Z34MAC.LIB ; Z34 macros + +; Equates section + +version equ 13 + +lf equ 0ah +cr equ 0dh +bell equ 07h + +base equ 0 +wboot equ base+0000h ; CP/M warm boot address +udflag equ base+0004h ; User num in high nybble, disk in low +bdos equ base+0005h ; BDOS function call entry point +tfcb equ base+005ch ; Default FCB buffer +fcb1 equ tfcb ; 1st and 2nd FCBs +fcb2 equ tfcb+16 +tbuff equ base+0080h ; Default disk I/O buffer +tpa equ base+0100h ; Base of TPA + + +;============================================================================= +; +; J e t L D R S I G N - O N +; +;============================================================================= + +; This prints an extended ID message upon loading with JetLDR. +; These are NOT the command names. + + COM /_ID_/ + + db 'Copyright 1989 ZSA',cr,lf + db 'Commands:',cr,lf + db ' IF ELSE FI XIF ' + + if andopt + db 'AND ' + endif + if oropt + db 'OR ' + endif + if ifqopt + db 'IFQ ' + endif + if zifopt + db 'ZIF ' + endif + + db cr,lf,'Options' + if ifoneg + db ' (use "',negchar,'" to negate)' + endif + if noise + db '; (noise)' + endif + db ':',cr,lf + + if ifotrue + db ' T F ' + endif + if ifambig + db 'AMbig ' + endif + if ifcompr + db 'COmpr ' + endif + if ifoempty + db 'EMpty ' + endif + if ifoeq + db 'x=y ' + endif + if ifoerror + db 'ERror ' + endif + if ifoexist + db 'EXist ' + endif + if ifoinput + db 'INput ' + endif + if ifonull + db 'NUll ' + endif + if iforeg + db 'REgs ' + endif + if ifotcap + db 'TCap ' + endif + if ifowheel + db 'WHeel ' + endif + + if comif + db cr,lf,' Use ' + if pathroot + db 'root:' + endif + db 'IF.COM' + endif + + db 0 ; End of JetLDR sign-on message + + CSEG + +;============================================================================= + +; Start of code + +start: + db 'Z3FCP' ; Flag for Package Loader + +;============================================================================= +; +; C O M M A N D T A B L E +; +;============================================================================= + +; The command name table is structured as follows: +; +; The first byte is the number of characters in each command name. +; Next come records consisting of command names followed by entry +; point addresses for the code to process the command. Finally, +; there is a null to indicate the end of the dispatch table. + + db cmdsize ; Size of text entries +ctab: ctable ; Macro defined in NZFCP.LIB + db 0 + +;============================================================================= +; +; I F C O N D I T I O N O P T I O N S +; +;============================================================================= + +condtab: + + if ifotrue + db 'T ' ; TRUE + dw ifctrue + db 'F ' ; FALSE + dw ifcfalse + endif ; ifotrue + + if ifambig ; Ambiguous file spec + db 'AM' + dw ifcambig + endif ; ifambig + + if ifcompr ; Squeezed or crunched + db 'CO' + dw ifccompr + endif ; ifcompr + + if ifoempty + db 'EM' ; File empty + dw ifcempty + endif ; ifoempty + + if ifoerror + db 'ER' ; Error message + dw ifcerror + endif ; ifoerror + + if ifoexist + db 'EX' ; File exists + dw ifcex + endif ; ifoexist + + if ifoinput + db 'IN' ; User input + dw ifcinput + endif ; ifoinput + + if ifonull + db 'NU' + dw ifcnull + endif ; ifonull + + if ifotcap ; Z3 TCAP available + db 'TC' + dw ifctcap + endif ; ifotcap + + if ifowheel ; Wheel Byte + db 'WH' + dw ifcwheel + endif ; ifowheel + + db 0 + +; Option bytes: these option bytes can be used to convey information to +; programs such as SHOW. The first one is used to reduce the chance of +; misinterpreting data from an earlier version of the FCP that does not +; have the option bytes. The next byte tells if COMIF has been activated +; and if the root of the path will be used as the directory in which to look +; for IF.COM. If PATHROOT is not selected (or if the path is empty), then +; the specified drive/user will be used. The overflow bit in case the user +; number is greater than 15 is kept in bit 2 of the second option byte. The +; combined user/drive value is kept in the third option byte. + +highuser defl ifusr gt 15 + +opt0: db 34h ; ZCPR34 version ID +opt1: optflag highuser,pathroot,comif +opt2: db ( ifusr and 0fh ) shl 4 + ( ifdrv - 'A' ) ; user/drive flag + +;============================================================================= +; +; C O M M A N D P R O C E S S I N G C O D E +; +;============================================================================= + +; Command: ZIF +; +; This command zeros out the IF system no matter what the current +; level IF state is. + + if zifopt + +ifzero: + if noise + call nl ; Print new line + endif ; noise + + jr ifexit1 + + endif ; zifopt + +;----------------------------------------------------------------------------- + +; Command: XIF +; +; If current IF state is true, XIF terminates all IFs, restoring a basic +; TRUE state. + +ifexit: + if noise + call nl ; Print new line + endif ; noise + + call iftest ; See if current IF is running and FALSE + + if noise + jr z,ifstat ; Abort with status message if so + else ; not noise + ret z ; Or just return if false + endif ; noise + +ifexit1: + ld hl,z3msg+1 ; Pt to IF flag + ld (hl),0 ; Zero IF flag + jr ifendmsg ; Print message + +;----------------------------------------------------------------------------- + +; Command: FI +; +; FI decrements to the previous IF level. It does this by shifting the +; current-if-bit in the first 'if' message in the Z3MSG buffer right one +; position. + +ifend: + if noise + call nl ; Print new line + endif ; noise + +; ld hl,z3msg+1 ; Point to IF flag +; ld a,(hl) ; Get it +; or a ; No IF active? + + call msgbf1 + dec hl ; Save a byte over the three lines above + + jr z,ifnderr + +ifendmsg: + if noise + call print + dc 'To ' ; Prefix to status display + endif ; noise + + srl (hl) ; Adjust active bit + + if noise + jr nz,ifstat ; Print status if IF still active + endif ; noise + +ifnderr: + if noise + + call print ; Print message + dc 'No ' + jp prif + + else ; not noise + + ret + + endif ; noise + +;----------------------------------------------------------------------------- + +; Command: ELSE +; +; ELSE complements the Active Bit for the Current IF provided the +; previous IF state was true. If the previous state was false, the +; command is flushed. +; +; This is accomplished according to the following algorithm. If the +; current IF is 0 (no IF) or 1 (one IF), then take the previous state +; to be true and perform the toggle. Otherwise, test the previous +; IF level condition and toggle only if it is true. + +ifelse: + if noise and (not ifqopt) + call nl ; Print new line + endif ; noise and (not ifqopt) + + call msgbf1 ; Get current if + ld b,a ; Save in B + srl a ; Back up if pointer bit to previous IF level + jr z,iftog ; If no previous IF level, go to toggle code + and (hl) ; Determine state of previous IF level + + if noise + if ifqopt + jr z,ifstat0 ; Print status on new line + else + jr z,ifstat ; If false, just print status + endif ; Ifqopt + else ; not noise + ret z ; Or simply return + endif ; noise + +iftog: + ld a,(hl) ; Get if-status message byte + xor b ; Flip current state + ld (hl),a ; Put result back in message byte + ; ..and fall thru to print status + + if not noise + ret + endif + +;----------------------------------------------------------------------------- + +; Indicate if current IF is True or False + +ifstat0: + call nl +ifstat: + call prif ; Print 'IF ' + call msgbf1 ; Get current if byte and set flags + ld b,a ; Get it into B + jr nz,ifstat1 ; Nz means if active + + call print + dc 'None' + ret + +ifstat1: + ld a,(hl) ; Get if-status message byte + and b ; Mask in currently active IF level status + ld c,'F' ; Load with false indicator + jr z,ifstat2 ; If current IF is false, jump + ld c,'T' ; Else, load with true indicator +ifstat2: + ld a,c + call conout + + srl b ; Drop one IF level + jr nz,ifstat1 ; Loop through all IF states + ret + +;------------------------- + +; Output CRLF + +nl: call print + dc cr,lf + ret + +;----------------------------------------------------------------------------- + +; Command: OR + +; This command performs a logical or operation by updating the +; if state without going to a new level. If there are active +; IFs and the current state is true, we do nothing. Else we back +; up one level and fall through to normal IF processing. + + if oropt + +orstart: + call msgbf1 ; Get if active byte + jr z,backup ; Treat like if if no IFs active + and (hl) ; Check current state + jr z,backup ; Current STATE false so go proecess + + if noise + jr ifstat0 ; Else return and show status + else + ret ; Or just return + + endif ; Noise + endif ; Oropt + +;----------------------------------------------------------------------------- + +; Command: AND + +; This command performs a logical and operation by updating the +; if state without going to a new level. If there are active +; IFs and the current state is false, we do nothing. Else we back +; up one level and fall through to normal IF processing. + + if andopt + +andstart: + call iftest ; Test for IF running and false + if noise + jr z,ifstat0 ; Condition met, show status & return + else + ret z ; Condition met, return + endif ; Noise + endif ; Andopt + +; Common stuff for and and or + + if andopt or oropt + +backup: + dec hl ; Pt to flag byte + srl (hl) ; Drop back one level +; +; Poke "IF" into external fcb for transient +; + if comif +pokefcb: + ld de,extfcb+1 ; Pt to external fcb + ld hl,ifcmd ; Pointer to IF command in table + ld bc,cmdsize ; Length + ldir ; Move it in + + endif ; comif + +; Fall through to IF PROCESSING + endif ;Andopt or oropt + +;----------------------------------------------------------------------------- + +; FCP Command: IF +; +; If current IF state is false, then advance to next level and set it +; to false also. If current IF state is true, then test condition and +; set the next level accordingly. + +ifstart: + if not ifqopt + + ld a,(extfcb) ; NZ if explicit + ld hl,tbuff + or (hl) + jp z,ifstat0 ; Report IF status + + endif ; not ifqopt + +ifstrt: + if noise + call nl ; Print new line + endif ; noise + + call iftest ; See if current IF is running and FALSE + jP z,ifcf ; Yes, do the right thing + +; Test for presence of colon in command. If colon present, then go directly +; to COMIF processing. + + if comif + ld a,(extfcb) ; Check drive byte of external FCB + or a ; If it is zero, no colon was present + jp nz,runcomif ; If colon, go to comif processing + ; Else fall through to resident processing + endif ; comif + +;----------------------------------------------------------------------------- +; +; R E S I D E N T C O M M A N D P R O C E S S I N G +; +;----------------------------------------------------------------------------- + +resident: + +; Test for Equality if Equal Sign in Token + + if ifoeq + + ld hl,tbuff+1 + + if xeqopt ; Extended equal testing + +skipsp: ; Skip over any space to first token + ld a,(hl) + or a ; Check for end of tail + jr z,ifck0 ; If so , go on + cp ' '+1 ; Test for space or control character + jr nc,tsteq ; If not, we are at first token + inc hl ; Otherwise advance to next character + jr skipsp ; ..and continue testing + + endif ; xeqopt + +tsteq: + ld a,(hl) ; Get character from command tail + inc hl ; Point to next one + or a ; EOL? + jr z,ifck0 ; Continue if so + + if xeqopt + cp ' '+1 ; End of token? + jr c,ifck0 ; If so, go on + endif ; xeqopt + + cp '=' ; Found '=' ? + jr nz,tsteq ; If not, continue scan + + ld hl,fcb1+1 ; Else, get ready to compare FCBs + ld de,fcb2+1 + ld b,11 ; 11 bytes +eqtest: + ld a,(de) ; Compare + cp (hl) + jr nz,ifcf + inc hl ; Pt to next + inc de + djnz eqtest + jr ifct + + endif ; ifoeq + + +ifck0: + ld de,fcb1+1 ; Point to first character in FCB1 + + if ifoneg + ld a,(de) ; Get it + ld (negflag),a ; Set negate flag + cp negchar ; Is it a negate? + jr nz,ifck1 ; If not, go on + inc de ; Else point to character after negchar +ifck1: + endif ; ifoneg + + if iforeg ; REGISTERS + call regtest ; Test for register value + jr nz,runreg + endif ; iforeg + + call condtest ; Test of condition match + jr nz,runcond ; If found, process condition + + if comif + jp runcomif ; If function not found in table, use transient + else + + call print ; Beep to indicate error + dc bell + + if noise + jp ifstat ; No condition, display current condition + else ; no noise + ret + endif ; noise + endif ; comif + +;----------------------------------------------------------------------------- +; +; Process register - register value is in A +; +;----------------------------------------------------------------------------- + + if iforeg +runreg: + push af ; Save value + call getnum ; Extract value in FCB2 as a number + pop af ; Get value + cp b ; Compare against extracted value + jr jrtrue ; True if match; false if not + endif ; iforeg + +;----------------------------------------------------------------------------- +; +; Process conditional test - address of conditional routine is in HL +; +;----------------------------------------------------------------------------- + +runcond: + jp (hl) ; "call" routine pted to by HL + +;============================================================================= +; +; R E S I D E N T C O N D I T I O N O P T I O N S +; +;============================================================================= + +; Condition: AMBIGUOUS + + if ifambig + +ifcambig: + ld hl,fcb2+1 ; Scan FCB2 for a '?' character + ld bc,11 ; Characters to scan + ld a,'?' ; Reference character + cpir + jr jrtrue ; True if '?' found; false if not + + endif ; ifambig + +;----------------------------------------------------------------------------- + +; Condition: COMPRESSED + + if ifcompr + +ifccompr: + ld a,(fcb2+10) ; Get middle character of file type + cp 'Z' ; Crunched + jr z,ifctrue + cp 'Q' ; Squeezed + jr jrtrue + + endif ; ifcompr + +;----------------------------------------------------------------------------- + +; Condition: TRUE +; IFCTRUE enables an active IF +; Condition: FALSE +; IFCFALSE enables an inactive IF + + if ifoempty or ifoerror or ifoexist or ifowheel +jrfalse: + jr z,ifcfalse + endif ; Ifoempty or ifoerror or ifoexist or ifowheel + +ifctrue: + + if ifoneg + call negtest ; Test for negate + jr z,ifcf + endif ; ifoneg + +ifct: + ld b,0ffh ; Active + jp ifset + + if iforeg or ifambig or ifcompr or ifoinput or ifonull +jrtrue: + jr z,ifctrue + endif ; Iforeg or ifambig or ifcompr or ifoinput or ifonull + +ifcfalse: + + if ifoneg + call negtest ; Test for negate + jr z,ifct + endif ; ifoneg + +ifcf: + ld b,0 ; Inactive + jp ifset + +;----------------------------------------------------------------------------- + +; Condition: EMPTY filename.typ + + if ifoempty +ifcempty: + call tlog ; Log into FCB2's DU + ld de,fcb2 ; Pt to fcb2 + ld c,15 ; Open file + push de ; Save fcb ptr + call bdos + pop de + inc a ; Not found? + jr z,ifctrue + ld c,20 ; Try to read a record + xor a ; set cr value to zero + ld (fcb2+32),a ; to attempt to read first record + call bdos + or a ; 0=OK + jr jrfalse ; true if no read + endif ; ifoempty + +;----------------------------------------------------------------------------- + +; Condition: ERROR + + if ifoerror +ifcerror: + ld a,(z3msg+6) ; Get error byte + or a ; 0=FALSE (no error registered) + jr jrfalse + endif ; ifoerror + +;----------------------------------------------------------------------------- + +; Condition: EXIST filename.typ + + if ifoexist +ifcex: + call tlog ; Log into DU + ld de,fcb2 ; Pt to fcb + ld c,17 ; Search for first + call bdos + inc a ; Set zero if error + jr jrfalse + endif ; Ifoexist + +;----------------------------------------------------------------------------- + +; Condition: INPUT (from user) + +; Modified to say " (Y/N)? ", and accept ONLY Y or y or N or n +; Carson Wilson 3/1/88 + + if ifoinput +ifcinput: + call print + dc ' (Y/N)? ' +ifcinp1: + ld hl,z3msg+7 ; Pt to ZEX message byte + ld (hl),10b ; Suspend ZEX input + push hl ; Save ptr to ZEX message byte +ifcinp2: + ld e,0ffh + ld c,6 ; Direct input from console + call bdos + or a ; Any input yet? + jr z,ifcinp2 ; Nope, try again + + pop hl ; Get ptr to ZEX message byte + ld (hl),0 ; Return ZEX to normal processing + and 5fh ; Mask and capitalize user input + cp 'Y' + jr nz,testN ; No, check if 'N' + call conout ; Display 'Y' + jr ifctrue ; Process as true +testN: + cp 'N' + jr nz,notN ; Not 'N' or 'n' + call conout ; Display 'N' + jr ifcfalse ; Process as false +notN: + ld a,bell ; Protest! + call conout + jr ifcinp1 ; Force either Y or y or N or n + + endif ; ifoinput + +;----------------------------------------------------------------------------- + +; Condition: NULL (2nd file name) + + if ifonull +ifcnull: + ld a,(fcb2+1) ; Get first char of 2nd file name + cp ' ' ; Space = null + jr jrtrue + endif ; ifonull + +;----------------------------------------------------------------------------- + +; Condition: TCAP + + if ifotcap +ifctcap: + ld a,(z3env+80h) ; Get first char of Z3 TCAP Entry + cp ' '+1 ; Space or less = none + jP c,ifcfalse + jP ifctrue + endif ; ifotcap + +;----------------------------------------------------------------------------- + +; Condition: WHEEL + + if ifowheel +ifcwheel: + ld hl,(z3env+29h) ; Get address of wheel byte + ld a,(hl) ; Get byte + or a ; Test for true + jP jrfalse ; False if 0 + endif ; ifowheel + +;============================================================================= +; +; S U P P O R T R O U T I N E S +; +;============================================================================= + +; Convert chars in FCB2 into a number in B + + if iforeg +getnum: + ld b,0 ; Set number + ld hl,fcb2+1 ; Pt to first char +getn1: + ld a,(hl) ; Get char + inc hl ; Pt to next + sub '0' ; Convert to binary + ret c ; Done if error + cp 10 ; Range? + ret nc ; Done if out of range + ld c,a ; Value in C + ld a,b ; A=old value + add a,a ; *2 + add a,a ; *4 + add a,b ; *5 + add a,a ; *10 + add a,c ; Add in new digit value + ld b,a ; Result in B + jr getn1 ; Continue processing + endif ; iforeg + +;----------------------------------------------------------------------------- + +; Log into DU in FCB2 + + if ifoexist or ifoempty + +tlog: + ld a,(fcb2) ; Get disk + or a ; Current? + jr nz,tlog1 + ld c,25 ; Get disk + call bdos + inc a ; Increment for following decrement +tlog1: + dec a ; A=0 + ld e,a ; Disk in E + ld c,14 + call bdos + ld a,(fcb2+13) ; Pt to user + ld e,a + ld c,32 ; Set user + jp bdos + + endif ; ifoexist or ifoempty + +;----------------------------------------------------------------------------- + +; Test of Negate Flag = negchar + + if ifoneg +negtest: +negflag equ $+1 ; Pointer for in-the-code modification + ld a,0 ; 2nd byte is filled in + cp negchar ; Test for No + ret + endif ; ifoneg + +;----------------------------------------------------------------------------- + +; Test FCB1 against a single digit (0-9) +; Return with register value in A and NZ if so + + if iforeg +regtest: + ld a,(de) ; Get digit + sub '0' + jr c,zret ; Z flag for no digit + cp 10 ; Range? + jr nc,zret ; Z flag for no digit + ld hl,z3msg+30h ; Pt to registers + add a,l ; Pt to register + ld l,a + ld a,h ; Add in H + adc 0 + ld h,a + xor a ; Set NZ + dec a + ld a,(hl) ; Get register value + ret +zret: + xor a ; Set Z + ret + endif ; iforeg + +;----------------------------------------------------------------------------- + +; Test to see if a current IF is running and if it is FALSE +; If so, return with Zero Flag Set (Z) +; If not, return with Zero Flag Clear (NZ) +; Affect only HL and PSW + +iftest: + call msgbf1 ; Test for active IF + jr z,ifok ; No active IF + and (hl) ; Check active flag + ret z ; Return Z since IF running and FALSE +ifok: + or 255 ; Return NZ for OK + ret + +msgbf1: + ld hl,z3msg+1 ; Get IF active flag + ld a,(hl) + inc hl ; Pt to If status byte + or a ; Set z if no IF active + ret + +;----------------------------------------------------------------------------- + +; Test FCB1 against condition table (must have 2-char entries) +; Return with routine address in HL if match and NZ flag + +condtest: + ld hl,condtab ; Pt to table +condt1: + ld a,(hl) ; End of table? + or a + ret z + ld a,(de) ; Get char + cp (hl) ; Comppare entries + inc hl ; Pt to next + inc de + jr nz,condt2 + ld a,(de) ; Get 2nd char + cp (hl) ; Compare + jr nz,condt2 + inc hl ; Pt to address + ld a,(hl) ; Get address in HL + inc hl + ld h,(hl) + ld l,a ; HL = address + jr ifok ; Set NZ for OK +condt2: + inc hl ; Pt to next entry + inc hl ; Skip over addr + inc hl + dec de ; Pt to 1st char of condition + jr condt1 + +;----------------------------------------------------------------------------- + +; Turn on next IF level +; B register is 0 if level is inactive, 0FFH if level is active + +ifset: +; ld hl,z3msg+1 ; Get IF flag +; ld a,(hl) +; or a ; If no if at all, start 1st one + + call msgbf1 + dec hl + + jr z,ifset1 +ifset0: + add a,a ; Advance to next level + jr c,iferr ; Check for overflow (8 IFs max) + ld (hl),a ; Set IF byte + jr ifset2 +ifset1: + inc a ; A=1 + ld (hl),a ; Set 1st IF +ifset2: + ld d,a ; Get IF byte + and b ; Set interested bit + ld b,a + inc hl ; Pt to active flag + ld a,d ; Complement IF byte + cpl + and (hl) ; Mask in only uninterested bits + or b ; Mask in interested bit + ld (hl),a ; Save result + + if noise + jp ifstat ; Print status and exit + else + ret ; Or just exit + endif ; noise + +iferr: + call print ; Beep to indicate overflow + dc bell + ret + +;============================================================================= +; +; T R A N S I E N T I F P R O C E S S I N G +; +;============================================================================= + + + if comif + +runcomif: + +; First we have to find IF.COM + + ld bc,100h*(ifdrv-'A')+ifusr ; Values to use if null path + + if pathroot + + ld hl,(expath) ; Point to symbolic path (indirect) +fndroot: + ld a,(hl) ; Check for end of path + or a + jr z,froot2 ; If end, branch + +; Process Next Path Element + + cp curint ; Current disk/user symbol? + jr nz,froot0 ; If not, branch + ld a,(curdr) ; Get current disk + inc a ; Compensate for following decrement +froot0: + dec a ; Shift to range 0..15 + ld b,a ; Set disk + inc hl ; Point to user in path + ld a,(hl) ; Get user + cp curint ; Current drive/user symbol? + jr nz,froot1 ; If not, branch + ld a,(curusr) ; Get current user +froot1: + ld c,a ; Set user + inc hl ; Point to next element in symbolic path + jr fndroot + +; Done with Search - BC Contains ROOT DU (or specified DU if path is empty) + + endif ; pathroot + +froot2: + call logbc ; Log into IF.COM's directory + +; Try to Open File IF.COM + + ld de,extfcb ; Point to command FCB + xor a + ld (de),a ; Force current drive + ld c,15 ; Open file + call bdos + inc a + jr nz,ifload ; Branch if file found + +; IF.COM not found - process as IF F + +ifnotfnd: + call iferr ; Ring bell + call reset ; Return home + jp ifcf + +; Load File IF.COM + +ifload: + call defdma ; First record to tbuff + call readcmd ; Read 1st record from IF.COM + jr nz,ifnotfnd ; If eof, treat as if file not found + + ld (extfcb+32),a ; Start from scratch (record 0) + ld a,(tbuff+8) + cp 3 + jr c,ifnotfnd ; Only Types 3 and 4 are acceptable + + call loadif ; Load IF.COM and set IFADR appropriately +; +; Build the command tail at tbuff +; + ld de,tbuff ; Point DE to tbuff + push de ; Save it for later + ld hl,(z3msg+4) ; Points into MCL buffer +; +; Advance HL to first 'space' after IF or .IF or :IF +; +advsp: inc hl + ld a,(hl) + cp ' '+1 ; Carry if space or null + jr nc,advsp + + ld c,0 ; Clear a counter + +putt: inc de ; Advance tbuff pointer + ld a,(hl) ; From MCL + ld (de),a ; To tbuff + inc hl ; Advance MCL pointer + or a ; Check for null + jr z,putx ; End of command line + cp ';' ; Command separator + jr z,putx ; End of command + inc c ; Count it up + jr putt ; Next.. + +putx: xor a ; Get a null + ld (de),a ; Terminate the line in tbuff + pop hl ; Beginning of tbuff + ld (hl),c ; Character count +; +; Pick up the execution address for Type 3 or 4 +; + ld hl,(ifadr) ; Load address + ld a,(hl) ; First byte at load address + cp 0c7h ; Test for RST 0 + jr nz,runif ; Nope, execute it + ld (hl),0c3h ; Plug in a JP +; +; Arrive here to execute IF.COM +; +runif: ld hl,z3env ; Pass environment in HL + db 0c3h ; JP instruction +ifadr: dw 0 ; Load/Execution address of IF.COM + +; +; Load IF.COM +; +loadif: + ld hl,(tbuff+11) ; Type 3 load address + jr z,loada ; Load as Type 3 +; +; Assume Type 4 (or higher) +; + ld hl,extfcb+32 ; Point to CR of extfcb + ld (hl),2 ; Set up for record 2 + push hl ; Save the pointer + call readcmd ; Get it into tbuff + pop hl + jp nz,ifnotfnd ; Too short + ld (hl),a ; Record 0 again + ld hl,(tbuff+11) ; Size word + push hl ; Save it + call readcmd ; Read record 0 again + pop bc ; Size + ld de,(ccp) ; CCP start + ld hl,z3env + dec a ; Phony fullget flag + call tbuff+9 ; Call Type 4 loader + push hl ; Save load address + call readcmd ; Read record 1 to tbuff (point to record 2) + pop hl ; Load address +; +loada: ld (ifadr),hl ; Save it +; +; Load IF.COM to (HL) until end of file, reset DMA and DU and return +; +load: push hl ; Save loading address + call setdma ; According to HL + call readcmd ; Read a record from file + pop hl ; Get current loading address back + jr nz,reset ; End of file + ld de,128 ; Advance it by one record + add hl,de + jr load ; Back to read some more + +; Reset DMA and Current DU + +reset: call defdma + ld bc,(curusr) ; Return home + +; Log Into DU in BC + +logbc: ld e,b ; Set disk + push bc + ld c,14 ; Select disk + call bdos + pop bc + ld e,c ; Set user + ld c,32 ; Select user + jp bdos + + +; Set default DMA address + +defdma: ld hl,tbuff + +; Set DMA to address according to HL + +setdma: push hl ; Save it + ex de,hl ; To DE + ld c,26 ; Set DMA command + call bdos ; Do it + pop hl ; DMA address + ret + +; Read a record from file in EXTFCB + +readcmd: + ld de,extfcb + ld c,20 + call bdos + or a ; Set NZ if error (end of file) + ret + + endif ; comif + +;============================================================================= +; +; U T I L I T Y S U B R O U T I N E S +; +;============================================================================= + +; Print "IF " + +prif: + call print + dc 'IF ' + ret + +;----------------------------------------------------------------------------- + +; Print String (terminated in 0 or MSB Set) at Return Address + +print: + ex (sp),hl ; Get address + call print1 + ex (sp),hl ; Put address + ret + +; Print String (terminated by MSB Set) pted to by HL + +print1: + ld a,(hl) ; Done? + inc hl ; Pt to next + call conout ; Print char + or a ; Set msb flag (m) + ret m ; Msb terminator + jr print1 + +;----------------------------------------------------------------------------- + +; Console Output Routine + +conout: + push hl ; Save regs + push de + push bc + push af + and 7fh ; Clear msb + ld e,a ; Char in E + ld c,2 ; Output + call bdos + pop af ; Get regs + pop bc + pop de + pop hl + ret + +;============================================================================= +; +; Display current length in records +; +prtval macro m1,v1,m2,v2,m3 + .radix 10 + .printx m1 v1 m2 v2 m3 + endm + +length equ $ - start +recs equ length / 128 +bytes equ length mod 128 + + .printx + prtval ,%recs,,%bytes, + .printx + + end + +; End of NZFCP.Z80 + \ No newline at end of file diff --git a/Source/BPBIOS/NZFCP13/z34cmn.lib b/Source/BPBIOS/NZFCP13/z34cmn.lib new file mode 100644 index 00000000..ad8c8cec --- /dev/null +++ b/Source/BPBIOS/NZFCP13/z34cmn.lib @@ -0,0 +1,105 @@ +; +; Library: Z34CMN.LIB +; Author: Carson Wilson +; Version: 1.2 +; Date: 16 June 1988 +; Changes: Renamed CRT0 to LINS for NZCOM compatibility. +; Added CUSR " " " + +; Author: Carson Wilson +; Version: 1.1 +; Date: 12 June 1988 +; Changes: Added CDRV for various Z34RCP commands. +; Added CRT0 for Z34RCP TYPE command. +; Added Z3TCAP and CLRSCR for Z34RCP CLS command. +; Added QUIET for ZCPR34 time in prompt. +; Added RSDMSG for ZCPR34 time in prompt. + +; Library: Z34CMN.LIB +; Author: Joe Wright +; Date: 23 March 1988 + +; As a replacement for Z3BASE.LIB, some usual equates. + +base equ 0 ; Base Page + +false equ 0 +true equ not false + +no equ false +yes equ true + +off equ false +on equ true + +; Named COMMON declarations start here. For compatibility, these +; are the same names used by Bridger Mitchell's JetLDR. + + common /_ENV_/ +z3env: ; Z3 Environment descriptor +z3envs equ yes ; There is one + +expath equ z3env+9 ; Address of External Path +expaths equ 10 ; Maximum 10 elements for MPATH + +rcp equ z3env+0ch ; Address of RCP +rcps equ yes ; Used as existence test, not size + +fcp equ z3env+12h ; Address of FCB +fcps equ yes ; Used as existence test, not size + +z3ndir equ z3env+15h ; Address of NDR +z3ndirs equ yes ; Used as existence test, not size + +quiet equ z3env+28h ; Quiet flag address + +z3whl equ z3env+29h ; Wheel byte address +z3whls equ yes ; There is a wheel + +lins equ z3env+33h ; CRT text lines address + +ccp equ z3env+3fh ; CCP entry +ccps equ z3env+41h ; Size + +dos equ z3env+42h ; DOS entry (+6) +doss equ z3env+44h ; Size + +bio equ z3env+45h ; BIO entry + +z3tcap equ z3env+80h ; TCAP address + +clrscr equ z3env+97h ; Clear screen string address + + common /_SSTK_/ +shstk: ; Top of Shell stack +shstks equ yes ; There is a shell stack + + common /_MSG_/ +z3msg: ; Message buffer +z3msgs equ yes ; There is one + +cusr equ z3msg+2eh ; Current user +cdrv equ z3msg+2fh ; Current drive + +rsdmsg equ z3msg+3ah ; Reserved bytes + + common /_FCB_/ +extfcb: ; External file control block +extfcbs equ yes ; There is one + + common /_MCL_/ +z3cl: ; Multiple command line +z3cls equ yes ; There is one + + common /_XSTK_/ +extstk: ; External stack +extstks equ yes ; There is one + + common /_BIOS_/ +bios: + + cseg ; Select Code Segment + +; End of Z34CMN.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/NZFCP13/z34mac.lib b/Source/BPBIOS/NZFCP13/z34mac.lib new file mode 100644 index 00000000..8ab3ffbe --- /dev/null +++ b/Source/BPBIOS/NZFCP13/z34mac.lib @@ -0,0 +1,122 @@ + +; Z33MAC.LIB : Macros for use with ZCPR33 + +; General purpose macros + +putreg macro + push hl ; Save registers in order + push de + push bc + endm + +getreg macro + pop bc ; Restore registers in order + pop de + pop hl + endm + +swap macro + rrca ; Exchange nibbles + rrca + rrca + rrca + endm + +;---------------------------------------- + +; Macro for forming option bytes + +; This macro generates a byte with bits corresponding to up to 8 option +; flags. The bits are filled in the order of the parameters and are right +; justified in the byte. + +optflag macro f1,f2,f3,f4,f5,f6,f7,f8 + +flag defl 0 ;; initial value + + irp temp, + + if not nul temp +flag defl flag shl 1 + if temp +flag defl flag or 1 + endif ;;temp + endif ;;not nul temp + + endm ;; irp + + defb low flag + + endm ;; optflag + +;----------------------------------------------------------------------------- + +; Command table entry definition macro + +; Macro to form an entry for one command in the table. The first parameter is +; the name to be used for the command (no quotes); the second parameter is the +; flag that indicates whether or not the command is to be enabled; the third +; parameter is the wheel control flag; and the last parameter is the jump +; address to the code that carries out the command. The command names are +; automatically padded out to the correct length (they will be truncated and +; an error message will result if a command name is too long). The characters +; in the command name are automatically converted to upper case. + +command macro cmdname,enableflag,wheelflag,address + + if enableflag ;; Generate command only if enabled + +whlmask defl wheelflag ;; Initialize variables +count defl cmdsize ;; Initialize to size of each command name + + irpc char,cmdname ;; Repeat over letters in command name + +count defl count - 1 ;; Count down characters in name + + if [ count lt cmdsize ] + + ;; If character is lower case, convert to upper case + + if [ '&char' ge 'a' ] and [ '&char' le 'z' ] + + if whlmask + defb [ '&char' and 5fh ] + 80h + else ;;not whlmask + defb [ '&char' and 5fh ] + endif ;;whlmask + + else ;;not lower case + + if whlmask + defb '&char' + 80h ;; If controlled by wheel, set high bit + else ;;not whlmask + defb '&char' ;; If not restricted, leave high bit clear + endif ;;whlmask + + endif ;;lower case + + endif ;;[ count lt cmdsize ] + +whlmask defl false ;; Turn off high-bit setting after first char + + endm ;irpc + + ;; Pad command name with blanks + + if [ count gt cmdsize ] ;; If we underflowed + *** Command name "&cmdname" is too long / truncated *** + else + rept count + defb ' ' + endm + endif ;[ count gt cmdsize ] + + dw address ;; Dispatch address for command + + endif ;enable + + endm ;command + +; End Z33MAC.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/WW.Z3T b/Source/BPBIOS/WW.Z3T new file mode 100644 index 00000000..004cc957 Binary files /dev/null and b/Source/BPBIOS/WW.Z3T differ diff --git a/Source/BPBIOS/Z34RCP11/Build.cmd b/Source/BPBIOS/Z34RCP11/Build.cmd new file mode 100644 index 00000000..1323b95f --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/Build.cmd @@ -0,0 +1,11 @@ +@echo off +setlocal + +set PATH=%PATH%;..\..\..\Tools\zx;..\..\..\Tools\cpmtools; + +set ZXBINDIR=../../../tools/cpm/bin/ +set ZXLIBDIR=../../../tools/cpm/lib/ +set ZXINCDIR=../../../tools/cpm/include/ + +rem zx Z80ASM -z34rcp11/MF +zx ZMAC -z34rcp11.z80 -/P diff --git a/Source/BPBIOS/Z34RCP11/Clean.cmd b/Source/BPBIOS/Z34RCP11/Clean.cmd new file mode 100644 index 00000000..a088f4e8 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/Clean.cmd @@ -0,0 +1,7 @@ +@echo off +setlocal + +if exist *.prn del *.prn +if exist *.lst del *.lst +if exist *.err del *.err +if exist *.rel del *.rel diff --git a/Source/BPBIOS/Z34RCP11/Makefile b/Source/BPBIOS/Z34RCP11/Makefile new file mode 100644 index 00000000..7d8b55f8 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/Makefile @@ -0,0 +1,5 @@ +OBJECTS = z34rcp11.rel +TOOLS = ../../../Tools +DEST = + +include $(TOOLS)/Makefile.inc diff --git a/Source/BPBIOS/Z34RCP11/cledinst.com b/Source/BPBIOS/Z34RCP11/cledinst.com new file mode 100644 index 00000000..c26a3cf0 Binary files /dev/null and b/Source/BPBIOS/Z34RCP11/cledinst.com differ diff --git a/Source/BPBIOS/Z34RCP11/cledsave.com b/Source/BPBIOS/Z34RCP11/cledsave.com new file mode 100644 index 00000000..dde04bc1 Binary files /dev/null and b/Source/BPBIOS/Z34RCP11/cledsave.com differ diff --git a/Source/BPBIOS/Z34RCP11/nzrcp.z80 b/Source/BPBIOS/Z34RCP11/nzrcp.z80 new file mode 100644 index 00000000..52b844e3 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/nzrcp.z80 @@ -0,0 +1,3010 @@ + +; New Name: NZRCP.Z80 Joe Wright July 1987 + +; Program: Z34RCP +; Version: 1.0 +; Description: Resident Command Package (RCP) for ZCPR34 +; Author: Jay Sage +; Date: March 1, 1987 +; Derivation: SYSRCP (Richard Conn) and many other contributions + +; ZCPR34 is copyright 1987 by Jay P. Sage. All rights reserved. End-user +; distribution and duplication permitted for non-commercial purposes only. +; Any commercial use of ZCPR34, defined as any situation where the duplicator +; recieves revenue by duplicating or distributing ZCPR34 by itself or in +; conjunction with any hardware or software product, is expressly prohibited +; unless authorized in writing by Jay P. Sage. + +version equ 2 +subver equ 4 + +; Vers 2.4 - Add whlchk subroutine for indirect addressing of wheel byte. +; 4 Apr 88 - Fixed command list routines. +; Joe Wright - Inhibit SPOP if there is no shell. Allow shstks = 1. + +; Vers 2.3 - Reset command, after calling F37, jumps to 0. +; 22 Feb 88 + +; Vers 2.2 - Modified for use of Z34CMN.LIB and NZ-COM. +; 31 Dec 87 +; Joe Wright + + +; Vers 2.1 - R command uses ZRDOS function 37 to reset drives. +; 6 Sep 87 - WHL command changed, WHLQ disappears. +; Joe Wright - REG command expanded. Add REG E (program error byte) +; - SPOP command added. Pops the shell stack + +;============================================================================= +; +; D E F I N I T I O N S S E C T I O N +; +;============================================================================= + + name ('RCP') + + maclib z34cmn.lib ; Defines ZCPR system addresses + maclib z34defn.lib ; Defines offsets in Z34 command processor + maclib z34mac.lib ; Macros + maclib sysdef.lib ; Common logic, sys, ascii defines + maclib nzrcp.lib ; Defines command options + +;============================================================================= +; +; E N T R Y C O D E S E C T I O N +; +;============================================================================= + +start: + db 'Z3RCP' ; Package ID + + +;---------------------------------------- + +; Command table + + db cmdsize ; Length of each command name + cmdtbl ; Dispatch table from Z33RCP.LIB + db 0 ; Marks end of command jump table + + +;---------------------------------------- + +; Name of RCP + +; This block allows the 'H' command and/or the SHOW utility to display a name +; and version number for this RCP as well as the commands that are supported. + +rcpname: + idstring ; From macro in Z33RCP.LIB + +; Include only those code sections that are required. + +; include rcph ; 'H' help (command list) command + page + +; RCP-H.Z80 'H' Command + +;============================================================================= +; +; H E L P C O M M A N D +; +;============================================================================= + +; This command displays a list of all resident commands that are supported, +; including those in the CPR (command processor), RCP, and FCP. + +clist: + +; Print the CPR-resident command names + + if listcpr + + call print ; Print "CPR" + db lf + db 'CP','R'+80h +; + ld hl,(ccp) ; CCP location from Z3ENV + ld de,offcmd ; Offset to CPR command table + add hl,de + call cmdlist ; Display the list of commands + + endif ;listcpr + +; Print the FCP-resident command names + + if listfcp + + ld hl,(fcp) + ld a,h + or l + jr z,rcplist ; No FCP + + ld a,(hl) + or a + jr z,rcplist ; FCP removed + + call print ; Print header for FCP + db lf + db 'FC','P'+80h + ld de,5 + add hl,de ; Point to FCP command table + call cmdlist + + endif ;listfcp + +; Print the RCP-resident command names + +rcplist: + if listrcp + + call crlf ; Skip a line + ld hl,rcpname ; Print RCP name + call printhl + ld hl,start+5 ; Point to RCP command table + + else + + ret + + endif ;listrcp + + ; Fall through to CMDLIST + +;---------------------------------------- + +; Subroutine to display list of commands in a command table (code above +; falls through to this routine -- do not move it). The commands are +; displayed 5 per line with 8 character spaces allowed for each command +; (subject to equates below). + +cmdlist: + call crlf ; Start with new line + ld e,(hl) ; Get size of each command name into DE + ld d,0 + inc hl ; Point to name of first command + ld c,cmdsline ; Set names-per-line value + +cmdlist1: + ld a,(hl) ; Get first character of the command name + or a ; See if it is null + jr nz,cmdlist1a ; If not, continue + ld a,cmdsline ; See if we are already on a new line + cp c + call nz,crlf ; If not, skip a line + ret + +cmdlist1a: + if noshow ; Option to suppress wheel-limited cmds + rla ; Shift high bit of name into carry bit + jr nc,cmdlist2 ; If not restricted, go on + call whlchk ; Check wheel byte + jr nz,cmdlist2 ; If wheel set, continue as usual + add hl,de ; Otherwise skip this command + jr cmdlist5 + endif + +; Print leading spaces between names + +cmdlist2: + ld a,cmdspace ; Spacing between command names + sub e ; Less length of each command name + ld b,a + ld a,' ' +cmdlist3: + call conout + djnz cmdlist3 + +; Print name of command + + ld b,e ; Length of each name into B +cmdlist4: + ld a,(hl) ; Get command name character + call conout + inc hl ; Point to next + djnz cmdlist4 + + dec c ; Decrement count of names on this line + jr nz,cmdlist5 ; Branch if room for more names + call crlf ; Otherwise, end this line and + ld c,cmdsline ; ..reset count for another line of commands + +; Skip to next command name + +cmdlist5: + inc hl ; Skip jump vector + inc hl + jr cmdlist1 ; Back to process next name + +; End RCP-H.Z80 + +;============================================================================= +; +; P O P S H E L L S T A C K C O M M A N D +; +;============================================================================= + +; +; POP the Shell Stack +; + if spopon + +; Pop the shell stack + +spop: ld hl,(z3env+1eh) ; SHSTK (indirect) + ld a,h + or l + ret z ; No shell stack + + ex de,hl ; SHSTK to DE + ld hl,(z3env+20h) ; SHSTKS to L, SHSIZE to H + push hl ; Save SHSIZE + xor a ; Your basic null in A + ld b,l + dec b ; SHSTKS-1 in B + jr z,sp0a ; Clear one entry + + push de ; Save SHSTK + ld e,h ; SHSIZE to E + ld d,a ; Clear D + ld h,a ; Clear H.. + ld l,a ; ..and L +sp0: add hl,de ; Multiply SHSIZE*(SHSTKS-1) + djnz sp0 + ld b,h + ld c,l ; Length to BC + ex de,hl ; SHSIZE to HL + pop de ; Get SHSTK (destination) + add hl,de ; SHSTK+SHSIZE to HL (Source) + ldir + +sp0a: pop bc ; Get SHSIZE in B +sp1: ld (de),a ; Clear last entry + inc de + djnz sp1 + ret + endif ; SPOPON + + + if clson +; include rcpcls ; 'CLS' clear screen command + page + +; RCP-CLS.Z80 'CLS' Command + +;============================================================================= +; +; C L E A R S C R E E N C O M M A N D +; +;============================================================================= + +; Command: CLS +; Function: To clear the CRT screen +; Comments: The setting of the CLSTCAP equate determines whether this +; command uses the TCAP information or not. If not, it uses the +; clear-screen string passed in macro CLSSTR. That string should +; end with the high bit set. + +cls: + if clstcap ; If using TCAP for clear screen string + + ld a,(z3env+80h) ; Get beginning of tcap + cp ' '+1 ; See if blank or perhaps null + jr nc,cls1 ; If not, go to clear screen code + call print ; If blank, then give error message + db ' No TCA','P'+80h + ret + +cls1: ld hl,z3env+97h ; Point to beginning of clear screen string + jp printhl ; Display it + + else ; Not using tcap + + call print + clsstr ; String from Z33RCP.LIB + ret + + endif ;clstcap + +; End RCP-CLS.Z80 + + endif ;clson + + if reson +; include rcpr ; 'R' disk reset command + page + +; RCP-R.Z80 'R' command + +;============================================================================= +; +; D I S K R E S E T C O M M A N D +; +;============================================================================= + +; Command: RESET +; Function: Reset the disk system +; Comments: ZRDOS does not require a disk system reset when disks are +; changed, but directory programs will not show the correct +; size if this is not done. It is also good practice. Since +; no warm boot performed, the disk in drive A need not have the +; operating system on it. +; Ver 2.1 Now logs all drives off and forces fixed and ram disks to +; re-log. +reset: + if resmsg ; If displaying a reset message + call print ; Report action + dc ' Reset' + endif ;resmsg + + ld de,-1 ; All 16 drives + ld c,37 ; Disks reset ZRDOS function + call bdos + ld c,13 ; Reset disk system + jp bdos + +; End RCP-R.Z80 + + endif ;reson + + if tston +; include rcptst ; 'TST' error test command + page + +; RCP-TST.Z80 'TST' Command + +;============================================================================= +; +; E R R O R T E S T C O M M A N D +; +;============================================================================= + +; Command: TST +; Function: To set the message buffer program error flag based on +; error count reported by M80 or L80 +; Syntax: TST PN where PN is (at least) the first letter of M80 or L80 + +testerr: + +; Check for name of program to test + + ld a,(fcb1+1) ; Get first character in program name + + if testm80 + ld hl,m80f ; Preset for m80 test counts + ld de,m80w + cp 'M' + jr z,testcount + endif ; Testm80 + + if testf80 + ld hl,f80f + ld de,f80w + cp 'F' + jr z,testcount + endif ; Testf80 + +; If no match, give error message + + call print + db 'bad nam','e'+80h + +testcount: + ld a,(hl) ; Test first error count word + inc hl + or (hl) + ex de,hl ; Test second word + or (hl) + inc hl + or (hl) + ld hl,z3msg+6 ; Point to program error flag + ld (hl),0 ; Clear it + ret z ; If counts were zero, we are done + ld (hl),0ffh ; Else set the error flag + ret + +; End RCP-TST.Z80 + + endif ;tston + + if spaceon +; include rcpsp ; 'SP' space on disk command + page + +; RCP-SP.Z80 'SP' Command + +;============================================================================= +; +; D I S K S P A C E C O M M A N D +; +;============================================================================= + +; Command: SP +; Function: Shows space remaining on designated drive +; Syntax: SP [DIR:|DU:] +; Comments: This code can be called by several other RCP commands so that +; they can show the space remaining on the disk after their +; operation. + + if [erasp or cpsp or dirsp] +crspace: ; Used to call space after other subroutines + call crlf ; Start new line + endif ;[erasp or cpsp or dirsp] + +space: + ld a,(fcb1) ; Determine requested drive + or a ; If drive explicitly selected + jr nz,space1 ; ..then skip + + ld c,25 ; BDOS get current drive function + call bdos + inc a ; Shift to range 1..16 + +space1: + dec a ; Shift to range 0..15 + ld e,a ; Save in E for selecting disk below + add 'A' ; Convert to letter and + ld (seldrv),a ; save in message string below + ld c,14 ; BDOS select disk function + call bdos ; Not needed if no drive selected, but smallest + ; ..possible code size this way. + +; Here we extract the following disk parameter information from the disk +; parameter block (DPB): +; BLKSHF: block shift factor (1 byte) +; BLKMAX: max number of blocks on disk (2 bytes) + +dparams: + ld c,31 ; BDOS get disk parameters function + call bdos + inc hl ; Advance to block shift factor byte + inc hl + ld a,(hl) ; Get value and + ld (blkshf),a ; ..save it in code below + inc hl ; Advance to max block number word + inc hl + inc hl + ld e,(hl) ; Get value into HL + inc hl + ld d,(hl) + inc de ; Add 1 for max number of blocks + +; Compute amount of free space left on disk + +dfree: + ld c,27 ; BDOS get allocation vector function + push de ; Save BLKMAX value + call bdos ; Get allocation vector into HL + ld b,h ; Copy allocation vector to BC + ld c,l + pop hl ; Restore MAXBLK value to HL + ld de,0 ; Inititialize count of free blocks + +; At this point we have +; BC = allocation vector address +; DE = free block count +; HL = number of blocks on disk + +free1: + push bc ; Save allocation address + ld a,(bc) ; Get bit pattern of allocation byte + ld b,8 ; Set to process 8 blocks +free2: + rla ; Rotate allocated block bit into carry flag + jr c,free3 ; If set (bit=1), block is allocated + inc de ; If not set, block is not allocated, so + ; ..increment free block count +free3: + ld c,a ; Save remaining allocation bits in C + dec hl ; Count down number of blocks on disk + ld a,l ; See if we are down to zero + or h + jr z,free4 ; Branch if no more blocks to check + ld a,c ; Get back current allocation bit pattern + djnz free2 ; Loop through 8 bits + pop bc ; Get pointer to allocation vector + inc bc ; Point to next allocation byte + jr free1 ; Continue by processing next allocation byte + +free4: + pop bc ; Clean up stack + ex de,hl ; Free block count to HL +blkshf equ $+1 ; Pointer for in-the-code modification + ld a,0 ; Get block shift factor + sub 3 ; Convert to log base 2 of K per block + jr z,free6 ; Done if single density (1k per block) + +; Convert for blocks of more than 1K each + +free5: + add hl,hl + dec a + jr nz,free5 + +; At this point HL = amount of free space on disk in K + +free6: + call print + db ' Space on ' +seldrv: db 0 ; Modified above to contain drive letter + db ':',[' '+80h] + +; Display decimal value of HL + + ld b,0 ; Initialize count of digits already printed + ld de,10000 ; Divisor in DE + call decdsp ; Print digit (or space if leading '0') + ld de,1000 + call decdsp + call decdsp3 ; Display hundreds, tens, and units + ld a,'K' + jp conout ; Final return from space routine + +; End RCP-SP.Z80 + + endif ;spaceon + + if diron +; include rcpdir ; 'DIR' directory command + page + +; RCP-DIR.Z80 'DIR' Command + +;============================================================================= +; +; D I R E C T O R Y D I S P L A Y C O M M A N D +; +;============================================================================= + +; Command: DIR +; Function: Display a directory of the files on disk +; Syntax: DIR Displays the DIR files +; DIR S Displays the SYS files +; DIR A Display both DIR and SYS files +; DIR /S Equivalent to DIR *.* S +; DIR /A Equivalent to DIR *.* A + +dir: + call retsave ; Save return address and set stack + +; See if FCB should be made wild (all '?') + + ld hl,fcb1+1 ; Point to file name in FCP + ld a,(hl) ; Get first character of filename + + if slashchk ; Allow "DIR /S" and "DIR /A" formats + cp '/' ; If name does not start with '/' + jr nz,dir01 ; ..branch and process normally + inc hl ; Point to second character + ld a,(hl) ; Get option character after slash + ld (fcb2+1),a ; ..and put it into second FCB + dec hl ; Back to first character + ld a,' ' ; Simulate empty FCB + endif ;slashchk + +dir01: + ld b,11 ; Prepare to fill FCB name and type with '?' + cp ' ' ; See if no file spec given + ld a,'?' ; Get ready to fill with '?' + call z,fillp ; ..carry out fill + + if nosys ; Suppress-SYS-file-if-no-wheel option + call whlchk ; Check wheel byte + jr z,dirnly ; If wheel off, ignore options + endif + + ld a,(fcb2+1) ; Get first char of 2nd file name + ld b,1 ; Set for both dir and sys files + cp allflag ; SYS and DIR flag specifier? + jr z,dirpr ; Got system specifier + dec b ; B=0 for sys files only + cp sysflag ; SYS only? + jr z,dirpr + +dirnly: ld b,80h ; Must be dir-only selection + +; DIRECTORY PRINT ROUTINE; ON ENTRY, B REG IS SET AS FOLLOWS: +; 0 FOR ONLY SYSTEM FILES, 80H FOR ONLY DIR FILES, 1 FOR BOTH +; +dirpr: + ld a,b ; Get systst flag + call getdir ; Load and sort directory + jp z,prfnf ; Print no file message + ld e,width ; Count down to 0 +; +; ENTRY PRINT LOOP; ON ENTRY, HL PTS TO FILES SELECTED (TERMINATED BY 0) +; AND E IS ENTRY COUNTER +; +dir3: + ld a,(hl) ; Check for done + or a + if dirsp and spaceon + jp z,spaexit ; Show space when done + else + jp z,exit ; Exit if done + endif ; Dirsp and spaceon + ld a,e ; Get entry counter + or a ; Output if 4 entries printed in line + jr nz,dir3a ; Continue + call crlf ; New line + ld e,width ; Reset entry count + ld a,e ; Get entry count +dir3a cp width ; First entry? + jr z,dir4 + call print +; + if wide +; + db ' ' ; 2 spaces + db fence ; Then fence char + db ' '+80h ; Then 1 more space +; + else +; + db ' ' ; Space + db fence+80h ; Then fence char +; + endif ; Wide +; +dir4: + call prfn ; Print file name + call break ; Check for abort + dec e ; Decrement entry counter + jr dir3 + +; End RCP-DIR.Z80 + + endif ;diron + + if eraon +; include rcpera ; 'ERA' erase command + page + +; RCP-ERA.Z80 'ERA' Command + +;============================================================================= +; +; E R A S E C O M M A N D +; +;============================================================================= + +;Command: ERA +;Function: Erase files +;Forms: +; ERA Erase Specified files and print their names +; ERA I Erase Specified files and print their names, but ask +; for verification before Erase is done + +era: + call retsave + ld a,(fcb2+1) ; Get eraflg if it's there + ld (eraflg),a ; Save it as a flag + ld a,1 ; Dir files only + call getdir ; Load directory of files + jp z,prfnf ; Abort if no files +; +; MAIN ERASE LOOP +; +era1: + call break ; See if user wants to stop + push hl ; Save ptr to file + call prfn ; Print its name + ld (nxtfile),hl ; Save ptr to next file + pop hl ; Get ptr to this file + call rotest ; Test file pted to by hl for r/o + jr nz,era3 +eraflg equ $+1 ; Address of flag + ld a,0 ; 2nd byte is flag + cp 'I' ; Is it an inspect option? + jr nz,era2 ; Skip prompt if it is not + call eraq ; Erase? + jr nz,era3 ; Skip if not +era2: + ld de,fcb1+1 ; Copy into fcb1 + ld b,11 ; 11 bytes + call blkmov + call initfcb1 ; Init fcb + ld c,19 ; Delete file + call bdos +era3: + ld hl,(nxtfile) ; Hl pts to next file + ld a,(hl) ; Get char + or a ; Done? + if erasp and spaceon + jp z,spaexit + else + jp z,exit + endif ; Erasp and spaceon + call crlf ; New line + jr era1 + +; End RCP-ERA.Z80 + + endif ;eraon + + if lton +; include rcplt ; 'LIST' and 'TYPE' commands + page + +; RCP-LT.Z80 + +;============================================================================= +; +; L I S T A N D T Y P E C O M M A N D S +; +;============================================================================= + +;Command: LIST +;Function: Print out specified file on the LST: Device +;Forms: +; LIST Print file (NO Paging) +;Notes: +; The flags which apply to TYPE do not take effect with LIST + + if liston +list: +; +; CHECK FOR WHEEL APPROVAL IF OPTION ENABLED + + + call retsave + ld a,0ffh ; Turn on printer flag + jr type0 + endif ;liston + +;Command: TYPE +;Function: Print out specified file on the CON: Device +;Forms: +; TYPE Print file +; TYPE P Print file with paging flag +;Notes: +; The flag PGDFLG defines the letter which toggles the paging +; facility (P in the forms section above) +; The flag PGDFLT determines if TYPE is to page by default +; (PGDFLT=TRUE if TYPE pages by default); combined with +; PGDFLG, the following events occur -- +; If PGDFLT = TRUE, PGDFLG turns OFF paging +; If PGDFLT = FALSE, PGDFLG turns ON paging +; +type: +; +; CHECK FOR WHEEL APPROVAL IF OPTION ENABLED +; +; + call retsave + xor a ; Turn off printer flag +; +; ENTRY POINT FOR CPR LIST FUNCTION (LIST) +; +type0: + if liston + ld (prflg),a ; Set flag + endif ; Liston + + ld a,(fcb2+1) ; Get page flag + ld (pgflg),a ; Save it as a flag + ld a,1 ; Select dir files + call getdir ; Allow ambiguous files (HL points to buffer) + jp z,prfnf ; No files + jr typex2 + + ; Entry point for successive files +typex: + ld hl,(nxtfile) ; Get ptr to next file + ld a,(hl) ; Any files? + or a + jp z,exit + + if liston + ld a,(prflg) ; Check for list output + or a ; 0=type + jr z,typex1 + ld a,cr ; Bol on printer + call lcout + ld a,ff ; Form feed the printer + call lcout + jr typex2 + endif ; Liston + +typex1: +; LDA PAGCNT ; If we've just done so, + push hl + ld hl,(pagcnt) + ld a,(hl) + pop hl + cp nlines-2 ; Don't type another + call nz,pagebreak ; Page break message +typex2: + ld de,fcb1+1 ; Copy into fcb1 + ld b,11 ; 11 bytes + call blkmov + ld (nxtfile),hl ; Set ptr to next file + call initfcb1 ; Init fcb1 + ld c,15 ; Open file + call bdos + inc a ; Set error flag + jp z,prfnf ; Abort if error +; MVI A,NLINES-2 ; Set line count +; STA PAGCNT + ld hl,(pagcnt) + ld (hl),nlines-2 + ld a,cr ; New line + call lcout + ld a,lf + call lcout + ld bc,080h ; Set char position and tab count + ; (b=0=tab, c=080h=char position) +; +; MAIN LOOP FOR LOADING NEXT BLOCK +; +type2: + ld a,c ; Get char count + cp 80h + jr c,type3 +; PUSH H ; Read next block + push bc + ld de,fcb1 ; Pt to fcb + ld c,20 ; Read record + call bdos + or a ; Set flags + pop bc +; POP H + jr nz,typex ; End of file? + ld c,0 ; Set char count + ld hl,tbuff ; Pt to first char +; +; MAIN LOOP FOR PRINTING CHARS IN TBUFF +; +type3: + ld a,(hl) ; Get next char + and 7fh ; Mask out msb + cp 1ah ; End of file (^z)? + jr z,typex ; Next file if so +; +; OUTPUT CHAR TO CON: OR LST: DEVICE WITH TABULATION +; + cp cr ; Reset tab count? + jr z,type4 + cp lf ; Reset tab count? + jr z,type4 + cp tab ; Tab? + jr z,type5 +; +; OUTPUT CHAR AND INCREMENT CHAR COUNT +; + call lcout ; Output char + inc b ; Increment tab count + jr type6 +; +; OUTPUT OR AND RESET TAB COUNT +; +type4: + call lcout ; Output or + ld b,0 ; Reset tab counter + jr type6 +; +; TABULATE +; +type5: + ld a,' ' ; + call lcout + inc b ; Incr pos count + ld a,b + and 7 + jr nz,type5 +; +; CONTINUE PROCESSING +; +type6: + inc c ; Increment char count + inc hl ; Pt to next char + call break ; Check for abort + jp z,typex ; Skip + jr type2 +; +; SEND OUTPUT TO LST: OR CON:, AS PER THE FLAG +; RETURN WITH Z IF ABORT +; +lcout: + push hl ; Save regs + push bc + ld e,a ; Char in e + ld c,2 ; Output to con: + if liston +prflg equ $+1 ; Pointer for in-the-code modification + ld a,0 ; 2nd byte is the print flag + or a ; 0=type + jr z,lc1 + ld c,5 ; Output to lst: + endif ; Liston + +lc1: + push de ; Save char + call bdos ; Output char in e + pop de ; Get char + ld a,e + cp lf + jr nz,lc2 + if liston + ld a,(prflg) ; Output to lst:? + or a ; Nz = yes + jr nz,lc2 + endif ; Liston +; +; CHECK FOR PAGING +; +; LXI H,PAGCNT ; Count down + ld hl,(pagcnt) + dec (hl) + jr nz,lc2 ; Jump if not end of page + ld (hl),nlines-2 ; Refill counter +pgflg equ $+1 ; Pointer to in-the-code buffer + ld a,0 ; 2nd byte is the paging flag + cp pgdflg ; Page default override option wanted? +; + if pgdflt ; If paging is default +; + jr z,lc2 ; Pgdflg means no paging +; + else +; + jr nz,lc2 ; Pgdflg means page +; + endif ; Pgdflt +; + call pagebreak ; Print page break message + jp z,typex ; Z to skip +lc2: + pop bc ; Restore regs + pop hl + ret +; +; PRINT PAGE BREAK MESSAGE AND GET USER INPUT +; ABORT IF ^C, RZ IF ^X +; +pagebreak: + push hl ; Save hl + call print + db cr,lf,' Typing',' '+80h + ld hl,fcb1+1 ; Print file name + call prfn + call dash ; Print dash + call conin ; Get input + pop hl ; Restore hl + push af + call crlf ; New line + pop af + jp break1 +; +; End RCP-LT.Z80 + + endif ;lton + + if renon +; include rcpren ; 'REN' rename command + page + +; RCP-REN.Z80 + +;Section 5E +;Command: REN +;Function: To change the name of an existing file +;Forms: +; REN = Perform function +; +ren: +; +; CHECK FOR WHEEL APPROVAL IF OPTION ENABLED +; + + call retsave +; +; +; STEP 1: CHECK FOR FILE 2 BEING AMBIGUOUS +; + ld hl,fcb2+1 ; Can't be ambiguous + call ambchk1 +; +; STEP 2: LOG INTO USER AREA +; + call logusr ; Log into user area of fcb1 +; +; STEP 3: SEE IF OLD FILE IS R/O +; + ld hl,fcb1 ; Pt to 1st fcb + push hl + ld de,fcb2 ; Pt to 2nd file + push de ; Save ptr + ld a,(hl) ; Get 1st's drive + ld (de),a ; Stuff into second fcb + ld c,17 ; Look for file + call bdos + inc a + jp z,prfnf + call getsbit ; Get ptr to entry in tbuff + ex de,hl ; Hl pts to entry + inc hl ; Pt to fn + call rotest ; See if file is r/o + jp nz,exit +; +; STEP 4: SEE IF NEW FILE ALREADY EXISTS +; EXTEST PERFORMS A NUMBER OF CHECKS: +; 1) AMBIGUITY +; 2) R/O +; 3) IF FILE EXISTS AND NOT R/O, PERMISSION TO DELETE +; + call extest + jp z,exit ; R/o or no permission +; +; STEP 5: EXCHANGE FILE NAME FIELDS FOR RENAME +; + pop de ; Pt to old + pop hl ; Pt to new + push hl ; Save ptr + ld b,12 ; 12 bytes + call iswap1 +; +; STEP 6: RENAME THE FILE +; + pop de ; Get ptr to fcb + ld c,23 ; Rename + call bdos + inc a ; Set zero flag if error + jp z,prfnf ; Print no source file message + jp exit +; +; +; End RCP-REN.Z80 + + endif ;renon + + if proton +; include rcpprot ; 'PROT' file attribute setting command + page + +; RCP-PROT.Z80 + +;Section 5F +;Command: PROT +;Function: To set the attributes of a file (R/O and SYS) +; +;Form: +; PROT afn RSI +;If either R or S are omitted, the file is made R/W or DIR, resp; +;R and S may be in any order. If I is present, Inspection is enabled. + +att: + call retsave + xor a ; Set no inspect + ld (inspect),a + ld hl,0 ; Set r/o and sys attributes off + ld de,fcb2+1 ; Pt to attributes + ld b,3 ; 3 chars max +att1: + ld a,(de) ; Get char + inc de ; Pt to next + cp 'I' ; Inspect? + jr z,atti + cp 'R' ; Set r/o? + jr z,attr + cp 'S' ; Set sys? + jr z,atts +att2: + djnz att1 + jr att3 +atti: + ld (inspect),a ; Set flag + jr att2 +attr: + ld h,80h ; Set r/o bit + jr att2 +atts: + ld l,80h ; Set sys bit + jr att2 +att3: + ld (fatt),hl ; Save file attributes + ld a,1 ; Select dir and sys files + call getdir ; Load directory + jp z,prfnf ; No file error + jr att5 +att4: + ld hl,(nxtfile) ; Pt to next file + ld a,(hl) ; End of list? + or a + jp z,exit + call crlf ; New line +att5: + call break ; Check for possible abort + push hl ; Save ptr to current file + call prfn ; Print its name + ld (nxtfile),hl ; Save ptr to next file + call print + db ' Set to R','/'+80h + ld hl,(fatt) ; Get attributes + ld c,'W' ; Assume r/w + ld a,h ; Get r/o bit + or a + jr z,att6 + ld c,'O' ; Set r/o +att6: + ld a,c ; Get char + call conout + ld a,l ; Get sys flag + or a ; Set flag + jr z,att7 + call print + db ' and SY','S'+80h +att7: +inspect equ $+1 ; Ptr for in-the-code modification + ld a,0 ; Get inspect flag + or a ; Z=no + pop hl ; Get ptr to current file + jr z,att8 + call eraq1 ; Ask for y/n + jr nz,att4 ; Advance to next file if not y +att8: + ld de,fcb1+1 ; Copy into fcb1 + ld b,11 ; 11 bytes + call blkmov +fatt equ $+1 ; Ptr for in-the-code modification + ld hl,0 ; Get attributes + dec de ; Pt to sys byte + dec de + ld a,l ; Get sys flag + call attset ; Set attribute correctly + dec de ; Pt to r/o byte + ld a,h ; Get r/o flag + call attset + ld de,fcb1 ; Pt to fcb + ld c,30 ; Set attributes + call bdos + jr att4 +attset: + or a ; 0=clear attribute + jr z,attst1 + ld a,(de) ; Get byte + or 80h ; Set attribute + ld (de),a + ret +attst1: + ld a,(de) ; Get byte + and 7fh ; Clear attribute + ld (de),a + ret +; +; End RCP-PROT.Z80 + + endif ;proton + + if cpon +; include rcpcp ; 'CP' file copying command + +; RCP-CP.Z80 + +;============================================================================= +; +; F I L E C O P Y C O M M A N D +; +;============================================================================= + +; Command: CP +; Function: Copy a file from one place to another +; Syntax: CP destfile=srcfile +; CP srcfile +; Comments: Both file specifications can include a directory specification. +; If only one file name is given, then the current directory and +; the source file name are assumed for the destination. + +copy: + call retsave + +; If new is blank, make it the same name and type as old + + ld de,fcb1+1 ; Point to destination file name + ld a,(de) ; Get first character + cp ' ' ; If not blank (no name) + jr nz,copy0 ; ..then branch to copy + + ld hl,fcb2+1 ; Copy source name into destination FCB + ld b,11 ; Name and type are 11 bytes + call blkmov + +; See if destination is same as source, and abort if so + +copy0: + ld hl,fcb1 ; Set up pointers to two files + ld de,fcb2 + push hl + push de + inc hl ; Point to names of files + inc de + ld b,13 ; Compare 13 bytes (name, type, and user #) +copy1: + call comp + jr nz,copy2 ; If they differ, go on with copy + + ld c,25 ; Get-current-disk BDOS function + call bdos ; Get it in case no drive given explicitly + inc a ; Shift to range 1..16 + ld b,a ; ..and keep value in B + pop de ; Restore pointers to FCBs + pop hl + ld a,(de) ; Get drive of source file + ld c,a ; ..and save it in C + or a ; Is it default drive? + jr nz,copy1a ; Branch if drive made explicit + ld c,b ; Otherwise, copy default drive into C +copy1a: + ld a,(hl) ; Get drive of destination file + or a ; Is it default drive? + jr nz,copy1b ; Branch if drive made explicit + ld a,b ; Otherwise, get current drive +copy1b: + cp c ; Compare the two drives specified + jr nz,copy3 ; Branch if they are different + jr cperr ; Branch to error code if they are the same + +copy2: + pop de ; Clean up the stack + pop hl + +; Make note of the user numbers of the two files + +copy3: + ld a,(fcb1+13) ; Get destination user number + ld (usrdest),a + ld a,(fcb2+13) ; Get source user number + ld (usrsrc),a + +; Set up new FCB for source file and open the source + + call define ; Define buffer addresses dynamically + ld hl,(srcfcb) ; Get address to use for new source FCB + push hl + ex de,hl ; Copy file data to new FCB + ld b,12 + call blkmov + call logsrc ; Log in user number of source file + pop hl ; Initialize the source file FCB + call initfcb2 + ld c,15 ; Open file + call bdos + inc a ; Check for error + jp z,prfnf ; Branch if file not found + +; Make sure destination file does not already exist + + call logdest ; Log into destination s user area + call extest ; Test for existence of file + jp z,exit ; Branch if it exists + +; Create destination file + + ld de,fcb1 ; Point to destination FCB + ld c,22 ; BDOS make-file function + call bdos + inc a ; Test for error (no directory space) + jr nz,copy5 ; Branch if OK + +; Report file error + +cperr: + call print + db ' Copy','?'+80h + jp exit + +; Copy source to destination with buffering + +;++++++++++ this should be done by changing DMA address to save all the +; buffer swapping + +copy5: + call logsrc ; Log in source user area + ld b,0 ; Initialize counter + ld hl,(cbuff) ; Initialize buffer pointer + +copy5a: + push hl ; Save address and counter + push bc + ld hl,(srcfcb) ; Point to source file FCB + ex de,hl ; Put it in DE for BDOS call + ld c,20 ; BDOS read-sequential function + call bdos + pop bc ; Get counter and address + pop de + or a ; Read Ok? + jr nz,copy5b ; Branch if end of file + + push bc ; Save counter + ld hl,tbuff ; Copy from 80h to buffer + ld b,128 ; 128 bytes + call blkmov + ex de,hl ; HL points to next buffer address + pop bc ; Get counter back + inc b ; Increment it + ld a,b ; See if buffer full + cp cpblocks + jr nz,copy5a ; If not, go back for more + +copy5b: + ld a,b ; Get count of blocks loaded into buffer + or a ; Are there any? + jr z,copy6 ; Branch if not (we are done) + + push bc ; Save count + call logdest ; Log into destination user number +cbuff equ $+1 ; Pointer for in-the-code modification + ld hl,0 ; Point to beginning of copy buffer +copy5c: + ld de,tbuff ; Copy into tbuff + ld b,128 ; 128 bytes + call blkmov + push hl ; Save pointer to next block + ld de,fcb1 ; Point to destination file FCB + ld c,21 ; Write the block + call bdos + or a + jr nz,cperr ; Branch on error (disk full of write error) + pop hl ; Get back pointer to next block + pop bc ; Get count +; djnz copy5 ; Work through the blocks + dec b ; + jr z,copy5 ; + push bc ; Save count + jr copy5c ; Back for another bufferful + +; Close the destination file + +copy6: + call logdest ; Log into destination user number + ld de,fcb1 ; Point to destination FCB + ld c,16 ; Close file + call bdos + call print + db ' Don','e'+80h + + if cpsp and spaceon + jp spaexit ; Report space remaining on destination drive + else + jp exit + endif ;cpsp and spaceon + +; Log into user number of source file + +logsrc: +usrsrc equ $+1 ; Pointer for in-the-code modification + ld a,0 ; Get user number + jr setusrrel ; Local jump to save code + +; Log into user number of destination file + +logdest: +usrdest equ $+1 ; Pointer for in-the-code modification + ld a,0 ; Get user number +setusrrel: + jp setusr + +; End RCP-CP.Z80 + + endif ;cpon + + if peekon or pokeon or porton +; include rcpiom ; 'PEEK', 'POKE', 'PORT' commands + page + +; RCP-IOM.Z80 + +; Command: PEEK +; Function: Display memory contents +; +; Form: +; PEEK startadr 256 bytes displayed +; PEEK startadr endadr range of bytes displayed + + if peekon + +peek: + call retsave + + ld hl,tbuff+1 ; Find first number +nxtpeek equ $+1 ; Pointer for in-the-code modification + ld de,100h ; Default peek address if none + call sksp ; Skip to first token (if any) + call nz,hexnum ; Get start address if any + + push de ; Save starting address + ld bc,255 ; Compute default ending address + ex de,hl + add hl,bc + + if peekchk ; Check for overflow + jr nc,peek0 ; If no overflow past FFFF, go on + ld hl,0ffffh ; Else use FFFF as ending address +peek0: + endif ;peekchk + + ex de,hl ; End address in DE + call sksp ; Skip to next token (if any) + call nz,hexnum ; Get 2nd number in DE (else default) + +peek1: + pop hl ; HL is start address, DE is end address + + if peekhdr + + push hl ; Save starting address again + ld b,8 ; Output leading spaces +peek0a: + call print + db ' '+80h + djnz peek0a + + ld b,16 ; Display 16 column headers +peek0b: + ld a,l + and 03h + call z,spac + + call spac + call spac + ld a,l ; Get low byte of address + and 0fh ; Display low hex digit + call pah + inc hl + djnz peek0b + + if peekbdr + call crlf + ld b,8 +peek0c: + call print + db ' '+80h + djnz peek0c + ld b,16 +peek0d: + ld a,l + and 3 + call z,spac + inc l + call print + db ' -', '-'+80h + djnz peek0d + endif ;peekbdr + + pop hl ; Restore starting address + + endif ;peekhdr + + ld c,0ffh ; Use C as continue flag + call peek2 ; Do peek + ld (nxtpeek),hl ; Set continued peek address + jp exit + +peek2: + ld a,c ; Check continuation flag + or a ; + ret z ; + +; Print line header + +peek2a: + call crlf ; New line + ld a,h ; Print address + call pashc + ld a,l + call pahc + call dash ; Print leader + ld b,16 ; 16 bytes to display + push hl ; Save start address + + ; Print hex values for 16 bytes + +peek3: + ld a,l + and 03h + call z,spac + + ld a,(hl) ; Get next byte + call pashc ; Print with leading space + + ; Check for last address + ; If c is already 0, leave it that way. + ; Otherwise check for end address and if so + ; Set c to zero. + + ld a,c ; See if continue flag already cleared + or a + jr z,peek3a ; If so, skip test + ld a,h + sub a,d ; See if h = d + ld c,a + ld a,l + sub a,e ; See if l = e + or c ; Combine two tests + ld c,a + +peek3a: inc hl ; Pt to next + djnz peek3 + + ; Print ascii equivalents for 16 bytes + + pop hl ; Pt to first address again + ld b,16 ; 16 bytes + call print ; Space and fence + db ' ' + db fence+80h + push bc ; Save flag in c +peek4: + ld a,(hl) ; Get next byte + ld c,'.' ; Assume dot + and 7fh ; Mask it + cp ' ' ; Dot if less than space + jr c,peek5 + cp 7fh ; Don't print del + jr z,peek5 + ld c,a ; Char in c +peek5: + ld a,c ; Get char + call conout ; Send it + inc hl ; Pt to next + djnz peek4 + + call print ; Closing fence + db fence+80h + pop bc ; Get flag in c back + call break ; Allow abort + jr peek2 + + endif ; Peekon +; +; PRINT A AS 2 HEX CHARS +; PASHC - LEADING SPACE +; + if peekon or [pokeon and not pokeq] or porton +pashc: + push af ; Save a + call spac + pop af +pahc: + push af + rrca ; Exchange nybbles + rrca + rrca + rrca + call pah ; Print hex char + pop af +pah: + and 0fh ; Mask + add a,'0' ; Convert to ascii + cp '9'+1 ; Letter? + jr c,pah1 + add a,7 ; Adjust to letter +pah1: + jp conout +; + endif ; Peekon or [pokeon and not pokeq] or porton +; +;Section 5I +;Command: POKE +;Function: Place Values into Memory +; +;Form: +; POKE startadr val1 val2 ... +; + if pokeon +poke: + call retsave + ld hl,tbuff+1 ; Pt to first char + call sksp ; Skip to non-blank + jr z,noargs ; Arg error + call hexnum ; Convert to number + + if not pokeq + call print + db ' Pok','e'+80h + call adrat ; Print at message + endif + +; LOOP FOR STORING HEX VALUES SEQUENTIALLY VIA POKE + +poke1: + push de ; Save address + call sksp ; Skip to non-blank + jp z,exit ; Done + cp '"' ; Quoted text? + jr z,poke2 + call hexnum ; Get number + ld a,e ; Get low + pop de ; Get address + ld (de),a ; Store number + inc de ; Pt to next + jr poke1 +; +; STORE ASCII CHARS +; +poke2: + pop de ; Get next address + inc hl ; Pt to next char +poke3: + ld a,(hl) ; Get next char + or a ; Done? + jp z,exit + ld (de),a ; Put char + inc hl ; Pt to next + inc de + jr poke3 + + endif ; Pokeon +; +; No Argument Error +; + + if pokeon or porton + +noargs: + call print + db ' Arg','?'+80h + jp exit +; + endif ; Pokeon or porton + +; +;Section 5I+ +;Command: PORT +;Function: Display or Set I/O Port Data +; +;Form: +; PORT addr - read port and display value +; PORT addr value - output value to port +; + if porton +port: + call retsave + ld hl,tbuff+1 ; Find first number + call sksp ; Skip to first command-line token + jr z,noargs ; Abort if no port address given + call hexnum ; Get start address into de + push hl ; Save pointer to command tail + ld hl,portaddr ; Modify code + ld (hl),e ; Move specified port addr into place + dec hl ; Point to opcode position + ld (hl),0dbh ; Poke 'in' opcode + ex (sp),hl ; Get tail pointer back while saving this one + call print ; Print header + db ' Por','t'+80h + ld a,e + call pashc ; Print port address + call sksp ; Skip to possible second value + jr z,portin ; Proceed with port input + + call hexnum ; Get 2nd number in de + ex (sp),hl ; Get pointer to opcode back + ld (hl),0d3h ; Poke 'out' opcode + call print + db ': OU','T'+80h + ld a,e ; Get value to output + jr paddr + +portin: call print + db ': I','N'+80h + xor a ; Make sure high port address = 0 (for HD64180) +paddr: ld b,0 ; ..for both IN and OUT instructions +opcode: + db 0 ; Opcode for IN or OUT inserted by code above +portaddr: + db 0 ; Port address inserted by code above + call pashc + pop hl ; Clean up stack + jp exit + + endif ; Porton + +; End RCP-IOM.Z80 + + endif ;peekon or pokeon or porton + + if regon +; include rcpreg ; 'REG' register operation commands + page + +; RCP-REG.Z80 + +; +;Section 5J +;Command: REG +;Function: Manipulate Memory Registers +; +;Forms: +; REG D or REG <-- Display 10 Register Values +; REG Mreg <-- Decrement Register Value +; REG Preg <-- Increment Register Value +; REG Sreg value <-- Set Register Value +; +; Vers 2.1 Joe Wright +; +; REG reg <-- Display a single register value +; +; REG numbers now range from 0 to 31, although only the first ten are +; displayed with REG D. +; +; REG now treats the program error byte as register E. +; +register: + ld de,fcb1+2 ; Pt to first arg + ld a,(de) ; Get possible digit + call regptr ; Pt HL to potential register + dec de ; Point to command + ld a,(de) + cp 'S' ; Set? + jr z,rset + cp 'P' ; Plus? + jr z,rinc + cp 'M' ; Minus? + jr z,rdec + cp ' ' + jr z,rshow + cp 'D' + jr z,rshow + call regptr + jp regout + +; INCREMENT REGISTER VALUE +; HL PTS TO MEMORY REGISTER ON INPUT + +rinc: + inc (hl) ; Increment it + jr regout ; Print result + +; DECREMENT REGISTER VALUE +; HL PTS TO MEMORY REGISTER ON INPUT + +rdec: + dec (hl) ; Decrement value + jr regout ; Print result + +; Show first ten registers and Program Error byte +; +rshow: + call rshow10 + ld hl,z3msg+6 + jp regout + +rshow10: + xor a ; Select register 0 + ld b,a ; Counter set to 0 in B + call regp1 ; HL pts to register 0 +rshow1: + ld a,b ; Get counter value + cp 10 ; First ten registers + ret z ; Exit if done + push bc ; Save counter + push hl ; Save pointer + call regout ; Print register value + pop hl ; Get pointer + pop bc ; Get counter + inc b ; Increment counter + ld a,b ; Check for new line + and 3 + call z,crlf ; Newline after fourth display + inc hl ; Pt to next register + jr rshow1 + +; SET REGISTER VALUE +; HL PTS TO REGISTER ON INPUT + +rset: + ld de,fcb2+1 ; Pt to value + call de2bin ; Eval string at DE to binary in B + ld (hl),b ; Set value + +; Enter with HL pointing to the register. HL is maintained. +; +regout: + call print + db ' Reg',' '+80h + ld de,z3msg+30h ; Register 0 + sbc hl,de ; Register number in HL + ld a,l + cp 32 ; A numbered Register? + jr c,rego0 ; Yep + call print + db ' ','E'+80h + jr rego1 ; Report + +rego0: push hl + push de + ld b,0 ; Suppress zeros + call decdsp2 ; Report register number + pop de + pop hl + +rego1: add hl,de ; HL points to register again + call print + db ' =',' '+80h + ld l,(hl) + xor a + ld h,a + ld b,a ; Suppress leading zeros + jp decdsp3 ; Display value + +; Evaluate decimal string at DE to binary in B +; +de2bin: + ld b,0 ; Init value to zero +de2b: + ld a,(de) ; Get this digit + inc de ; Pt to next + sub '0' ; Convert to binary + ret c ; A space, finished + cp 10 ; Range? + ret nc ; Not decimal, finished + ld c,a ; Digit in c + ld a,b ; Multiply old by 10 + add a,a ; *2 + add a,a ; *4 + add a,b ; *5 + add a,a ; *10 + add a,c ; Add in new digit + ld b,a ; Result in b + jr de2b ; Again + +; SET HL TO POINT TO MEMORY REGISTER WHOSE INDEX IS PTED TO BY HL +; ON INPUT, A CONTAINS REGISTER CHAR +; ON OUTPUT, HL = ADDRESS OF MEMORY REGISTER (REG 0 ASSUMED IF ERROR) + +regptr: + ld hl,z3msg+6 ; The E register + cp 'E' + ret z + push de + call de2bin ; Get register number in B + pop de + ld a,b + cp 32 ; Range 0-31 + ld a,0 + jr nc,regp1 ; Out of range, use 0 + ld a,b ; Value in A +regp1: + ld hl,z3msg+30h ; Pt to memory registers + add a,l ; Pt to proper register + ld l,a + ret ; No chance of crossing page boundary +; +; End RCP-REG.Z80 + + endif ;regon + + if whlon or whlqon +; include rcpwhl ; 'WHL' and 'WHLQ' commands + page +; +;Section 5K +;Command: WHL/WHLQ +;Function: Set the Wheel Byte on or off +; +;If WHLQUIET equate is true, then RCP does not report wheel status with WHL +;command. +; +;Form: +; WHL -- turn Wheel Byte OFF +; WHL password -- turn Wheel Byte ON if password is correct +; WHLQ -- find out status of Wheel Byte +; +; Vers 2.1 Changes the function a little as follows: +; +; WHL -- Report Wheel Status (no WHLQ) +; WHL password -- Set Wheel ON if password is correct +; -- Set Wheel OFF if password incorrect +whl: + ld hl,fcb1+1 ; Pt to first char + ld a,(hl) ; Get it + + if not whlqon + cp ' ' + jr z,whlmsg ; Report wheel status if no password + endif + + ld de,whlpass + ld b,8 ; Check 8 chars + call comp ; Compare + jr nz,whloff ; Set wheel OFF if incorrect password + +; TURN ON WHEEL BYTE + + ld a,0ffh ; Turn on wheel byte + jr whlset + +; TURN OFF WHEEL BYTE + +whloff: + xor a ; Turn off wheel byte +whlset: + ld hl,(z3whl) ; Indirect from z3env + ld (hl),a +whlq: + if whlquiet + ret + endif + +; PRINT WHEEL BYTE MESSAGE + + if not whlquiet + +whlmsg: + call print + dc ' Wheel ' + call whlchk ; Check wheel byte + jr z,offm + call print + dc 'On' + ret +offm: + call print + dc 'Off' + ret + + endif ;[not whlquiet] or whlqon + + +; WHEEL PASSWORD DEFINED FROM SYSRCP.LIB FILE + + db 'Z'-'@' ; Leading ^z to block attempt to type rcp file +whlpass: + wpass ; Use macro +; +; End RCP-WHL.Z80 + + endif ;whlon + + if echoon +; include rcpecho ; 'ECHO' command + page + +; RCP-ECHO.Z80 + +;============================================================================= +; +; E C H O T E X T T O S C R E E N A N D P R I N T E R +; +;============================================================================= + +; Command: ECHO +; Function: Echo text to console or printer + +echo: + xor a ; Lower case flag setting + + if upcase ; If upper case default + dec a + endif ;upcase + + ld (casefl),a ; Store flag in code below + + ld hl,tbuff+1 ; Point to first character + call getchar ; Get first character (should be blank) + ; If none, exit from routine + + if echolst + call getchar ; Get first char after leading blank + ld b,a ; Save first char as list output flag + cp '$' ; Print flag? + jr z,echo2 ; If so, go on + dec hl ; Else backup one character + endif ; Echolst + +; LOOP TO ECHO CHARS + +echo2: call getchar + + if echolst + cp ff ; Form feed? + jr z,echo3 + endif ;echolst + + cp '^' + jr nz,echo2a ; Not control character prefix + call getchar ; Get next character + and 1fh ; Convert to control character + jr echo2d ; Echo it + +echo2a: cp cmdchar ; Case shift prefix? + jr nz,echo2d ; No, normal echo + call getchar ; Get next character + cp ucasechar ; Up-shift character? + jr z,echo2c ; Store non-zero value in case flag + +echo2b: cp lcasechar ; Lower-case character? + jr nz,echo2d ; No, echo the character as is + xor a ; Else, clear case flag +echo2c: ld (casefl),a + jr echo2 ; On to next character + +echo2d: + call echout ; Send char + jr echo2 + +; FORM FEED - SEND NEW LINE FOLLOWED BY FORM FEED IF PRINTER OUTPUT + + if echolst +echo3: + ld a,b ; Check for printer output + cp '$' + jr nz,echoff ; Send form feed normally if not printer + call echonl ; Send new line + ld a,ff ; Send form feed + jr echout + +; SEND FORM FEED CHAR TO CONSOLE + +echoff: + ld a,ff ; Get char + jr echo2d + endif ;echolst + +; END OF PRINT LOOP - CHECK FOR PRINTER TERMINATION + +echo4: + if not echolst + + ret + + else + + ld a,b ; Get list mode flag + cp '$' + ret nz ; Done if no printer output + +; OUTPUT A NEW LINE + +echonl: + ld a,cr ; Output new line on printer + call echout + ld a,lf ; Fall thru to echout + + endif ;not echolst + +; OUTPUT CHAR TO PRINTER OR CONSOLE + +echout: + ld c,a ; Char in c + + cp 'A' ; If less than 'a' + jr c,echouta ; Leave as is + cp 'Z'+1 ; If greater than 'z' + jr nc,echouta ; Leave as is + add 20h ; Else convert to lower case +echouta: + ld d,a ; Save lower case version in d +casefl equ $+1 ; Pointer for in-the-code modification + ld a,0 + or a + jr nz,echoutb ; If upper case selected, go on as is + ld c,d ; Else substitute lower case version +echoutb: + + push hl ; Save hl + push bc ; Save bc + ld de,0ch-3 ; Offset for console output + + if echolst + ld a,b ; Check for printer output + cp '$' + jr nz,echout1 + inc de ; Add 3 for printer offset + inc de + inc de + endif ;echolst + +; OUTPUT CHAR IN C WITH BIOS OFFSET IN DE + +echout1: + call biout ; Bios output + pop bc ; Restore bc,hl + pop hl + ret + +; Get a character from the command tail buffer + +getchar: + ld a,(hl) ; Get character + inc hl ; Point to next one + or a ; Check for end of string + ret nz ; If not end, return + pop hl ; Else, clean up stack + jr echo4 ; And exit from routine + +; OUTPUT CHAR IN C TO BIOS WITH OFFSET IN DE + +biout: + ld hl,(wboot+1) ; Get address of warm boot + add hl,de ; Pt to routine + jp (hl) ; Jump to it + +; End RCP-ECHO.Z80 + + endif ;echoon + +; include rcpsubs ; File of subroutines + page + +; RCPSUBS.Z80 Subroutines for Z33RCP.Z80 + + +;----------------------------------------------------------------------------- + +; Display decimal digit routines + +;-------------------- + +; Display hundreds, tens, and units digits (assumes flag in B has been set) + + if regon or spaceon + +decdsp3: + ld de,100 ; Display hundreds + call decdsp +decdsp2: + ld de,10 ; Display tens + call decdsp + ld a,l ; Get remaining units value + add '0' ; Convert to character + jr conout ; Print it and return + +;-------------------- + +; Routine to print any single digit + +; Actually, this routine displays the value of HL divided by DE and leaves the +; remainder in HL. In computing the character to display, it assumes that the +; result of the division will be a decimal digit. If the result is zero, the +; value in the B register, which is the number of digits already printed, is +; checked. If it is zero, a space is printed instead of a leading '0'. If it +; is not zero, the '0' is printed. Whenever any digit (not a space) is +; printed, the value in B is incremented. + +decdsp: + ld c,'0'-1 ; Initialize digit count + xor a ; Clear carry flag + +decdsp1: + inc c ; Pre-increment the digit + sbc hl,de ; Subtract DE from HL + jr nc,decdsp1 + + add hl,de ; Add back in to produce remainder + ld a,c ; Get decimal digit + cp '0' ; Check for leading 0 + jr nz,decdout ; If not 0, proceed to display it + ld a,b ; Digit printed already? + or a + ld a,' ' ; Possible space for calling routine to print +; ret z ; If no digit printed, return zero flag set + jr z,conout ; Print leading space +decdout: + inc b ; Indicate digit printed + ld a,c ; Else print real digit + ; Fall through to CONOUT + + endif ;regon or spaceon + +;----------------------------------------------------------------------------- + +; Console Output Routine + +conout: + putreg ; Save all register except AF + push af ; Save AF, too + and 7fh ; Mask out MSB + ld e,a ; Transfer character to E + ld c,2 ; BDOS conout function number + call bdos + pop af + getreg ; Restore registers +note: ; Use this RET for NOTE command + ret + + if peekon or [pokeon and not pokeq] or porton +spac: ld a,' ' + jr conout + endif ; peekon or [pokeon and not pokeq] or porton + + +;----------------------------------------------------------------------------- + +; String printing routines + +;-------------------- + +; Print string following call (terminated with null or character with the +; high bit set) + +print: + ex (sp),hl ; Get address + call printhl + ex (sp),hl ; Put address + ret + +;-------------------- + +; Print string pointed to by HL (terminated with null or character with the +; high bit set) + +printhl: + ld a,(hl) ; Get next character + inc hl ; Point to following one + or a ; See if null terminator + ret z ; If so, we are done + call conout ; Display the character + ret m ; We are done if MSB is set (negative number) + jr printhl ; Back for more + +;----------------------------------------------------------------------------- + +; OUTPUT NEW LINE TO CON: + +crlf: + call print + db cr,lf+80h + ret + +; CONSOLE INPUT + + if eraon or lton or proton or renon or cpon + +conin: + push hl ; Save regs + push de + push bc + ld c,1 ; Input + call bdos + pop bc ; Get regs + pop de + pop hl + and 7fh ; Mask msb + cp 61h + ret c + and 5fh ; To upper case + ret + + endif ; Eraon or lton or proton or renon or cpon +; +; SAVE RETURN ADDRESS +; +retsave: + pop de ; Get return address + pop hl ; Get return address to zcpr3 + ld (z3ret),hl ; Save it + push hl ; Put return address to zcpr3 back + push de ; Put return address back + ret + +; + if spaceon and [dirsp or cpsp or erasp] +spaexit: + call crspace ; Show space remaining + endif ; Spaceon and [dirsp or cpsp or erasp] +; +; EXIT TO ZCPR3 +; +exit: +z3ret equ $+1 ; Pointer to in-the-code modification + jp 0 ; Return address + +; +; PRINT A DASH +; + if lton or peekon +dash: + call print + db ' -',' '+80h + ret +; + endif ; Lton or peekon +; +; PRINT ADDRESS MESSAGE +; PRINT ADDRESS IN DE +; + if peekon or pokeon + if not pokeq +adrat: + call print + db ' at',' '+80h + ld a,d ; Print high + call pahc + ld a,e ; Print low + jp pahc + + endif ; Not pokeq + endif ; Peekon or pokeon + + if peekon or pokeon or porton +; +; EXTRACT HEXADECIMAL NUMBER FROM LINE PTED TO BY HL +; RETURN WITH VALUE IN DE AND HL PTING TO OFFENDING CHAR +; +hexnum: + ld de,0 ; De=accumulated value +hnum1: + ld a,(hl) ; Get char + cp ' '+1 ; Done? + ret c ; Return if space or less + inc hl ; Pt to next + sub '0' ; Convert to binary + jr c,numerr ; Return and done if error + cp 10 ; 0-9? + jr c,hnum2 + sub 7 ; A-f? + cp 10h ; Error? + jr nc,numerr +hnum2: + push hl ; Save pointer + ex de,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl ; DE x16 to HL + ld e,a + ld d,0 + add hl,de + ex de,hl ; DE = DE * 16 + A + pop hl ; Get the pointer + jr hnum1 ; Try again +; +; NUMBER ERROR +; +numerr: + call print + db ' Num','?'+80h + jp exit +; +; SKIP TO NEXT NON-BLANK +; +sksp: + ld a,(hl) ; Get char + inc hl ; Pt to next + cp ' ' ; Skip spaces + jr z,sksp + dec hl ; Pt to good char + or a ; Set eol flag + ret +; + endif ; Peekon or pokeon or porton + +;----------------------------------------------------------------------------- + +; Test File in FCB for unambiguity and existence, ask user to delete if so +; Return with Z flag set if R/O or no permission to delete +; + if renon or cpon +extest: + call ambchk ; Ambiguous file names not allowed + call searf ; Look for specified file + jr z,exok ; Ok if not found + call getsbit ; Position into dir + inc de ; Pt to file name + ex de,hl ; Hl pts to file name + push hl ; Save ptr to file name + call prfn ; Print file name + pop hl + call rotest ; Check for r/o + jr nz,exer + call eraq ; Erase? + jr nz,exer ; Restart as error if no + ld de,fcb1 ; Pt to fcb1 + ld c,19 ; Delete file + call bdos +exok: + xor a + dec a ; Nz = ok + ret +exer: + xor a ; Error flag - file is r/o or no permission + ret + +; +; CHECK FOR AMBIGUOUS FILE NAME IN FCB1 +; RETURN Z IF SO +; +ambchk: + ld hl,fcb1+1 ; Pt to fcb +; +; CHECK FOR AMBIGUOUS FILE NAME PTED TO BY HL +; +ambchk1: + push hl + ld b,11 ; 11 bytes +amb1: + ld a,(hl) ; Get char + and 7fh ; Mask + cp '?' + jr z,amb2 + inc hl ; Pt to next + djnz amb1 + dec b ; Set nz flag + pop de + ret +amb2: + pop hl ; Pt to file name + call prfn + call print + db ' is AF','N'+80h + jp exit +; + endif ; Renon or cpon +; +; TEST FILE PTED TO BY HL FOR R/O +; NZ IF R/O +; + if renon or cpon or eraon +; +rotest: + push hl ; Advance to r/o byte + ld bc,8 ; Pt to 9th byte + add hl,bc + ld a,(hl) ; Get it + and 80h ; Mask bit + push af + ld hl,romsg + call nz,printhl ; Print if nz + pop af ; Get flag + pop hl ; Get ptr + ret +romsg: + db ' is R/','O'+80h +; +; CHECK USER TO SEE IF HE APPROVES ERASE OF FILE +; RETURN WITH Z IF YES +; +eraq: + call print + db ' - Eras','e'+80h + endif ; Renon or cpon or eraon + + if renon or cpon or eraon or proton +eraq1: + call print + db ' (Y/N/Q)?',' '+80h + call conin ; Get response + cp 'Q' ; Quit command? + jp z,exit + cp 'Y' ; Key on yes + ret +; + endif ; Renon or cpon or eraon or proton +; +; INIT FCB1, RETURN WITH DE PTING TO FCB1 +; + if eraon or lton or cpon +initfcb1: + ld hl,fcb1 ; Pt to fcb +initfcb2: + push hl ; Save ptr + ld bc,12 ; Pt to first byte + add hl,bc + ld b,24 ; Zero 24 bytes + xor a ; Zero fill + call fillp ; Fill memory + pop de ; Pt to fcb + ret +; + endif ; Eraon or lton or cpon +; + if eraon or lton or cpon or diron + +fillp: + ld (hl),a ; Store byte + inc hl ; Pt to next + djnz fillp ; Count down + ret +; + endif ; Eraon or lton or cpon or diron + +; +; CHECK FOR USER INPUT; IF ^C, RETURN WITH Z +; + if diron or lton or eraon or proton or peekon + +break: + push hl ; Save regs + push de + push bc + ld c,11 ; Console status check + call bdos + or a + ld c,1 ; Get char if any + call nz,bdos + pop bc ; Restore regs + pop de + pop hl +break1: cp ctrlc ; Check for abort + jp z,exit ; Exit + cp ctrlx ; Skip? + ret + endif ; Diron or lton or eraon or proton or peekon + +; AFTER A SEARCH, RETURN NZ SET IF DESIRED TYPE OF FILE FOUND, Z IF NOT +; THIS ALGORITHM LOOKS AT THE SYSTEM BIT OF THE LOCATED FILE; THIS +; BIT IS SET TO 1 IF THE FILE IS A SYSTEM FILE AND 0 IF NOT A SYSTEM +; FILE. THE FOLLOWING EXCLUSIVE OR MASKS ARE APPLIED TO RETURN Z OR NZ +; AS REQUIRED BY THE CALLING PROGRAM: +; +; SYSTEM BYTE: X 0 0 0 0 0 0 0 (AFTER 80H MASK, X=1 IF SYS, 0 IF DIR) +; +; SYS-ONLY : 0 0 0 0 0 0 0 0 (XOR 0 = 0 if X=0, = 80H if X=1) +; DIR-ONLY : 1 0 0 0 0 0 0 0 (XOR 80H = 80h if X=0, = 0 if X=1) +; BOTH : 0 0 0 0 0 0 0 1 (XOR 1 = 81H or 1H, NZ in both cases) + + if diron or eraon or lton or proton or cpon or renon + +getsbit: + dec a ; Adjust to returned value + rrca ; Convert number to offset into tbuff + rrca + rrca + and 60h + ld de,tbuff ; Pt to buffer + add a,e ; Add entry offset to base addr + ld e,a ; Result in e + push de ; Save ptr in de + add 10 ; Add offset of 10 to pt to system byte + ld e,a ; Set address + ld a,(de) ; Get byte + pop de ; Get ptr in de + and 80h ; Look at only system bit +systst equ $+1 ; In-the-code variable + xor 0 ; If systst=0, sys only; if systst=80h, dir + ; Only; if systst=1, both sys and dir + ret ; Nz if ok, z if not ok +; +; +; COPY HL TO DE FOR B BYTES +; +blkmov: + ld a,(hl) ; Get + ld (de),a ; Put + inc hl ; Pt to next + inc de + djnz blkmov ; Loop + ret + +; +; PRINT FILE NOT FOUND MESSAGE +; +prfnf: + call print + db ' No File','s'+80h + jp exit + +; LOG INTO USER AREA CONTAINED IN FCB1 +; +logusr: + ld a,(fcb1+13) ; Get user number +setusr: + ld e,a + ld c,32 ; Use bdos fct + jp bdos + +; +; PRINT FILE NAME PTED TO BY HL +; +prfn: + call print ; Leading space + db ' '+80h + ld b,8 ; 8 chars + call prfn1 + call print + db '.'+80h ; Dot + ld b,3 ; 3 chars +prfn1: + ld a,(hl) ; Get char + inc hl ; Pt to next + call conout ; Print char + djnz prfn1 ; Count down + ret + +; +; SEARCH FOR FIRST +; +searf: + push bc ; Save counter + push hl ; Save hl + ld c,17 ; Search for first function +searf1: + ld de,fcb1 ; Pt to fcb + call bdos + inc a ; Set zero flag for error return + pop hl ; Get hl + pop bc ; Get counter + ret + + endif ; Diron or eraon or lton or proton or cpon or renon + +;----------------------------------------------------------------------------- + +; Define buffers as high as possible in TPA for the following groups +; of commands: +; COPY needs SRCFCB and CBUFF +; LIST/TYPE needs PAGCNT and DIRBUF +; ERA, PROT, and DIR commands. needs DIRBUF +; If DIRBUF is defined, its value is in HL on return from this code. The DE +; register pair is not changed by the code, but the BC pair is affected. + +dirbufon equ lton or diron or eraon or proton + + if dirbufon +dirbuf: ds 2 ; Address for directory buffer + endif ;dirbufon + + if cpon +srcfcb: ds 2 ; Address of source file FCB (CBUFF address + ; ..is in the code) + endif ;cpon + + if lton +pagcnt: ds 2 ; Address for page counter + endif ;lton + + + if cpon or lton or eraon or proton or diron + +define: + push de + ld hl,(bdos+1) ; Get bottom of BDOS + ex de,hl ; ..into DE + ld hl,(1) ; Get BIOS warmboot address into HL + ld bc,-[0e00h+800h+3] ; Offset to command processor address + add hl,bc + +; Now we have to compare and pick the lower address as the top of TPA + + push hl ; Save CPR address while comparing + xor a ; Clear the carry flag + sbc hl,de ; Compute (CPR-BDOS) + pop hl ; Restore CPR address + jr c,define1 ; Branch if BDOS address is higher (use CPR) + ex de,hl ; Otherwise use BDOS address +define1: + + if lton + dec hl ; Put PAGCNT in first free byte at top of TPA + ld (pagcnt),hl + endif ;lton + + if cpon + ld de,-36 ; Calculate place for SRCFCB for copy command + add hl,de + ld (srcfcb),hl + if dirbufon + push hl ; Save if needed below + endif ;dirbufon + ld de,-[cpblocks*128] ; CBUFF can use same space as DIRBUF + add hl,de + ld (cbuff),hl + if dirbufon + pop hl + endif ;dirbufon + endif ;cpon + + if dirbufon + ld de,-[maxdirs*11] ; Space for directory buffer + add hl,de + ld (dirbuf),hl + endif + + pop de + ret + + endif ;cpon or dirbufon + +;----------------------------------------------------------------------------- + +; +; SEARCH FOR NEXT +; + if diron or eraon or lton or proton + +searn: + push bc ; Save counter + push hl ; Save hl + ld c,18 ; Search for next function + jr searf1 + +; LOAD DIRECTORY AND SORT IT +; ON INPUT, A=SYSTST FLAG (0=SYS, 1=DIR, 80H=BOTH) +; DIRECTORY IS LOADED INTO BUFFER AT TOP OF TPA +; RETURN WITH ZERO SET IF NO MATCH AND HL PTS TO 1ST ENTRY IF MATCH + +direrr: + call print + db 'DIR Ovf','l'+80h + jp exit + +getdir: + ld (systst),a ; Set system test flag + call logusr ; Log into user area of fcb1 + +; LXI H,DIRBUF ; Pt to dir buffer + call define ; Define buffer addresses + ld (hl),0 ; Set empty + ld bc,0 ; Set counter + call searf ; Look for match + ret z ; Return if not found +; +; STEP 1: LOAD DIRECTORY +; +gd1: + push bc ; Save counter + call getsbit ; Check for system ok + pop bc + jr z,gd2 ; Not ok, so skip + push bc ; Save counter + inc de ; Pt to file name + ex de,hl ; Hl pts to file name, de pts to buffer + ld b,11 ; Copy 11 bytes + call blkmov ; Do copy + pop bc ; Get counter + inc bc ; Increment counter + ld hl,maxdirs-1 ; See if count equals or exceeds MAXDIRS + ld a,b ; Check high bytes + sub a,h + jr c,gd1a ; If carry set, we are OK + ld a,c ; Check low bytes + sub a,l + jr nc,direrr ; If no carry, jump to error message +gd1a: + ex de,hl ; Hl pts to next buffer location +gd2: + call searn ; Look for next + jr nz,gd1 + ld (hl),0 ; Store ending 0 +; LXI H,DIRBUF ; Pt to dir buffer + ld hl,(dirbuf) ; Pt to dir buffer + ld a,(hl) ; Check for empty + or a + ret z +; +; STEP 2: SORT DIRECTORY +; + if sorton + push hl ; Save ptr to dirbuf for return + call diralpha ; Sort + pop hl + endif + xor a ; Set nz flag for ok + dec a + ret + +; +; DIRALPHA -- ALPHABETIZES DIRECTORY IN DIRBUF; BC CONTAINS +; THE NUMBER OF FILES IN THE DIRECTORY +; +diralpha: +; +; SHELL SORT -- +; THIS SORT ROUTINE IS ADAPTED FROM "SOFTWARE TOOLS" +; BY KERNIGAN AND PLAUGHER, PAGE 106. COPYRIGHT, 1976, ADDISON-WESLEY. +; + ld h,b ; Hl=bc=file count + ld l,c + ld (n),hl ; Set "N" + ld (gap),hl ; Set initial gap to n for first division by 2 + +; FOR (GAP = N/2; GAP > 0; GAP = GAP/2) +srtl0: + or a ; Clear carry +gap equ $+1 ; Pointer for in-the-code modification + ld hl,0 ; Get previous gap + ld a,h ; Rotate right to divide by 2 + rra + ld h,a + ld a,l + rra + ld l,a + +; TEST FOR ZERO + or h + ret z ; Done with sort if gap = 0 + + ld (gap),hl ; Set value of gap + ld (ii),hl ; Set ii=gap for following loop + +; FOR (II = GAP + 1; II <= N; II = II + 1) +srtl1: +ii equ $+1 ; Pointer for in-the-code modification + ld hl,0 ; Add 1 to ii + inc hl + ld (ii),hl + +; TEST FOR II <= N + ex de,hl ; Ii is in de +n equ $+1 ; Pointer for in-the-code modification + ld hl,0 ; Number of items to sort + ld a,l ; Compare by subtraction + sub a,e + ld a,h + sbc a,d ; Carry set means ii > n + jr c,srtl0 ; Don't do for loop if ii > n + + ex de,hl ; Set jj = ii initially for first subtraction of gap + ld (jj),hl + +; FOR (JJ = II - GAP; JJ > 0; JJ = JJ - GAP) +srtl2: + ld hl,(gap) ; Get gap + ex de,hl ; In de +jj equ $+1 ; Pointer for in-the-code modification + ld hl,0 ; Get jj + ld a,l ; Compute jj - gap + sub a,e + ld l,a + ld a,h + sbc a,d + ld h,a + ld (jj),hl ; Jj = jj - gap + jr c,srtl1 ; If carry from subtractions, jj < 0 and abort + or l ; Jj=0? + + jr z,srtl1 ; If zero, jj=0 and abort + +; SET JG = JJ + GAP + ex de,hl ; Jj in de + ld hl,(gap) ; Get gap + add hl,de ; Jj + gap + ld (jg),hl ; Jg = jj + gap + +; IF (V(JJ) <= V(JG)) + call icompare ; J in de, jg in hl + +; ... THEN BREAK + jr c,srtl1 + +; ... ELSE EXCHANGE + ld hl,(jj) ; Swap jj, jg + ex de,hl +jg equ $+1 ; Pointer for in-the-code modification + ld hl,0 + call iswap ; Jj in de, jg in hl + +; END OF INNER-MOST FOR LOOP + jr srtl2 + +; +; SWAP (Exchange) the elements whose indexes are in HL and DE +; +iswap: + call ipos ; Compute position from index + ex de,hl + call ipos ; Compute 2nd element position from index + ld b,11 ; 11 bytes to flip + endif ; Diron or eraon or lton or proton + + if diron or eraon or lton or proton or renon +iswap1: + ld a,(de) ; Get bytes + ld c,(hl) + ld (hl),a ; Put bytes + ld a,c + ld (de),a + inc hl ; Pt to next + inc de + djnz iswap1 + ret + endif ; Diron or eraon or lton or proton or renon + + if diron or eraon or lton or proton +; +; ICOMPARE compares the entry pointed to by the pointer pointed to by HL +; with that pointed to by DE (1st level indirect addressing); on entry, +; HL and DE contain the numbers of the elements to compare (1, 2, ...); +; on exit, Carry Set means ((DE)) < ((HL)), Zero Set means ((HL)) = ((DE)), +; and Non-Zero and No-Carry means ((DE)) > ((HL)) +; +icompare: + call ipos ; Get position of first element + ex de,hl + call ipos ; Get position of 2nd element + ex de,hl +; +; COMPARE DIR ENTRY PTED TO BY HL WITH THAT PTED TO BY DE; +; NO NET EFFECT ON HL, DE; RET W/CARRY SET MEANS DE/>> to indicate state) + ^W - recall command lines from history stack + ^E - recall history in reverse direction + + +Installation: + + The installation program, CLEDINST, allows you to set up the editor +to your preference. The RCP can be installed directly in memory or in a disk +file -- RCPxxx.ZRL or a system configuration saved with SNAP or NZBLITZ. +CLEDINST also serves as a "help" utility by displaying the current command +bindings. Type "CLEDINST //" for help with CLEDINST.COM. + + +History Tool: + + CLEDSAVE writes the contents of the history stack to a text file on disk. +The file can be reloaded later (CLEDSAVE L), or composed in advance +with a text editor, then loaded. If the file is too large for the history +stack, as many commmand lines as fit are loaded. + + CLEDSAVE is useful as in a startup alias to load frequently used command +lines from an easily altered file. For example: + +Alias: START + +A15: +CLEDSYS <- load SNAP image of ENV,TCAP,RCP,FCP,NDR,QUIET,&PATH +CLEDSAVE CLED.VAR L <- load precomposed command lines into CLED +CLED <- turn the shell on +... <- rest of startup line runs before CLED gets control + +For help with CLEDSAVE.COM, type "CLEDSAVE //". + + You may also use SNAP or NZBLITZ to save the system segment image with +command lines already loaded (turn SAVE OFF first.) + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpcled.lib b/Source/BPBIOS/Z34RCP11/rcpcled.lib new file mode 100644 index 00000000..6c2879a7 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpcled.lib @@ -0,0 +1,1327 @@ + page + +; Library: RCPCLED for Z34RCP +; Author: Carson Wilson (modifications only) +; Version: 1.3b +; Date: October 8, 1989 +; Changes: Civilian time now prints "12" instead of "0" for midnight hour. +; Put Z3PLUS time display capability back in. +; Z3PLUS time display disabled if date = 01/01/78 (no clock). +; Added Rob Friefeld's fix to SAVE_LINE. +; +; Author: rdf +; Version: 1.3a +; Date: October 2, 1989 +; Changes: Changed order of installable highlight codes and history buffer +; addresses to simplify installing a ZRL file. Changed version +; number to guarantee compatibility with accessory programs. +; Option to print user number zero at prompt. +; Time string separator installable. + +; Author: Carson Wilson (modifications only) +; Version: 1.2 B +; Date: September 29, 1989 +; Changes: Made ">>" the prompt when previous commands are NOT being +; overwritten. +; Changed line edit commands to be closer to CP/M Plus. +; Shortened the clock read code (ZSDOS allows straight +; DateStamper calls). +; Uses DEFINE from RCPSUBS.LIB to save space. +; Does not print prompt user number if at user zero. +; Optional installable highlight codes for time in prompt. +; Time prompt shortened to "hh.mm" for easier reading. + +; Author: Rob Friefeld +; Version: 1.2 a +; Date: September 20, 1989 + +; Syntax: CLED [/] if "/", then run for one command line only +; e.g. from a shell like ZFILER + +; +; ===== D E F I N I T I O N S S E C T I O N ===================== +; + +clver equ 13 ; Version number (Install program compat.) +clrev equ 'b' ; Revision (Does not affect config.) + +del equ 7fh ; not in sysdef.lib + +; +;===== C O N F I G U R A T I O N A R E A ======================== +; + +; The installation program and buffer loader depend on the configuration +; of this data structure. It should not be changed. + +shname: db 'CLED',0 ; Name put on shell stack +versid: db clver ; CLED version +ddsep: db dudir_sep ; DU:DIR separator char +ins_flag: db clins ; Yes = insert mode +minsave: db clmin ; Discard line =< this +era_flag: db clera ; Erase stored line on exit +save_flag: db clsav ; Save command lines +tim_sep: db timesep ; Time string separator + +; --------------------------- + +; Command list for RCPCLED, Version 1.3 +; Set bit 7 to use a command with meta key + +cmd_list: + db 'Q' ; Meta key 1 + db 'H' ; Backspace + db 'S' ; Cursor left + db 'D' ; Cursor right + db 'A' ; Word left + db 'F' ; Word right + db 'B' ; Line end/ start + dc 'S' ; Line start + dc 'D' ; Line end + db 'G' ; Delete char + db DEL ; Delete left + db 'T' ; Delete word + db 'L' ; Delete word left + db 'X' ; Delete to SOL + db 'Y' ; Delete line + db 'K' ; Delete to EOL + db 'V' ; Toggle insert + db 'P' ; Enter control + db 'W' ; Recall line + db 'E' ; Recall reverse + db 'M' ; Execute line + db '[' ; ESC menu + +cmdlen equ $ - cmd_list + +; --------------------------- +; Highlight on/off codes for time display (installable) +; +stndout: db 0,0,0,0 ; Must terminate with hibit or binary 0 +stndend: db 0,0,0,0 ; Ditto + +; --------------------------- +; 4 bytes are used here for the information of the history save/load tool, +; CLEDSAVE + +histaddr: dw history ; Pointer to history buffer +histsz: dw histsize ; Buffer size + +; +;===== M A I N C O D E S E C T I O N ======================== +; +cled: + call define ; Set pointer to free mem + ld de,-lbufwid-1 + add hl,de + ld (line),hl ; Set line buffer location + xor a + ld (hl),a ; Zero line + ld hl,history ; History stack + ld (recall_ptr),hl ; Init position pointer to start + + ld a,(fcb+1) ; Check command line option + cp '/' + jr z,cledit ; Go right to editing + + ld a,(z3msg+3) ; QSHELL + dec a ; <> 1 on manual invocation + jr z,cledit ; Skip installation + +; +;===== S H E L L I N S T A L L A T I O N ========================= +; +sh_inst: + ld hl,shname + call shpush ; Z = OK + ret z + call print ; Complain about stack and cancel + dc cr,lf,'SH STK' ; Full or non-existent + ret + +; +;===== L I N E E D I T =========================================== +; + +; This is the main entry point for the shell +; 1 - Display prompt +; 2 - Get user input +; 3 - Reset shell bit +; 4 - Run command line + + +; Subtask 1 -- +cledit: + call prompt ; Display system prompt + +;---------------------------------------- +; Subtask 2 -- + +; The editor returns NZ if the shell pop command has been given. If not, it +; returns the character count of the command line in B. + + call EDIT + +;---------------------------------------- +; Subtask 3 -- + push af ; Save return code + xor a + ld (z3msg+3),a ; PUTCST + pop af + +;---------------------------------------- +; Subtask 4 -- + jp nz,shpop ; Quit shell + +; Here we load the MCL directly from the line buffer. On OVFL, loop to edit. + +loadcl: + ld a,(z3cl+2) ; MCL size + inc b ; B contains line count, include terminating 0 + cp b ; Compare to line size + jr nc,loadcl1 ; OK + +mclerr: + call print + dc cr,lf,'OVFL',cr,lf + jp cledit + +loadcl1: + ld de,z3cl+4 ; Set MCL pointer to start + ld (z3cl),de + + ld hl,(line) + ld c,b + ld b,0 + ldir ; Move line buff to MCL + ret ; Run it + + +; +;===== S U B R O U T I N E S ======================================= +; + +; Prompt -- PRINT a DU:DIR prompt. +; +prompt: + if systime + call print_time + endif + + ld bc,(cusr) ; GDEFDU + ld a,b ; Drive + add a,'A' ; Make it a letter + call conout ; Write it + ld a,c ; Get user + + if puser0 + call pusr ; Write it + else + or a + call nz,pusr ; Write it IF NONZERO + endif ;puser0 + + call dutdir ; Get the ndr + jr z,prompt1 + ld a,(ddsep) ; DU:DIR separator + call conout + ld b,8 ; Eight chars max +nameloop: + ld a,(hl) ; Get the first char + cp ' ' + call nz,conout ; Write it if not blank + inc hl + djnz nameloop + +prompt1: + call prompt2 + ld a,(save_flag) ; If save is OFF, prompt is >> + or a + ret nz +prompt2: + call print + dc '>' + ret + +; PUSR -- Convert user # in A to decimal and print +; +pusr: + ld hl,10 shl 8 + '0'-1 ; H=10, L='0'-1 + cp h ; User < 10 ? + jr c,pusr1 +pusr0: + inc l ; Advance character for user number tens digit + sub h + jr nc,pusr0 + add a,h + ld h,a ; Keep low digit of user number in H + ld a,l ; Display tens digit + call conout + ld a,h ; Ready to process units digit +pusr1: + jp decout ; Routine in RCPSUBS.LIB +; add '0' +;pusr2: +; jp conout + +; Console input without echo + +cin: + push hl + push de + push bc +cin1: ld c,dirconf ; DCIO + ld e,-1 + call bdos + or a + jr z,cin1 + pop bc + pop de + pop hl + ret + +; +;===== E D I T O R S E C T I O N ================================ +; + +; Date: October 2, 1989 + +; Entry is EDIT +; Return Z = Execute command line, NZ = Quit shell, B = char count of line + +; Initialize to on-line environment. +; While editing, HL -> current position in LINE, B = char count, +; C = cursor position (0 .. count), DE = scratch + +edit: + ld hl,(line) ; Init to start of line + xor a + ld b,a ; Line count = 0 + ld c,a ; Cursor pos = 0 + + push hl ; There may already be a line here + dec b ; Accumulate possible char count in B +edit1: + inc b + cp (hl) ; A = 0 + inc hl + jr nz,edit1 ; Loop until 0 terminator +edit2: + pop hl ; Point to line again + call zline ; Zero the remainder of LINE buffer + call ptail ; Print the line from cursor position + +;-------------------------------------------------------------------- + +; EDIT COMMAND LOOP + +; Get a char. If it is text, enter it. If it is a control, scan the +; CMD_LIST for a match. If found, compute offset into jump table and go. +; A "shifted" key (high bit set in table) is matched after the "meta-key" +; has been entered. + +ecmd: + exx ; Main regs must be preserved + ld hl,ecmd ; Save address so a return comes back here + push hl + +no_match: + call cin ; Next key... + cp 'C'-'@' ; Warm boot? + jp z,0000h + + ld hl,meta_flag ; Shift flag + or (hl) ; Mask in possible high bit + ld (hl),0 ; Reset flag + exx ; Recover main regs + + cp 20h ; Test key + jr c,control_key ; Not text + cp del ; This control char > text chars + jp c,enter ; Input text + +control_key: + call menuinp ; Convert control char to cap + + exx ; Must preserve main regs + ld hl,cmd_list ; Scan command list + ld bc,cmdlen + cpir + jr nz,no_match + ld hl,cmd_vector + ld a,cmdlen-1 ; Point to address in vector table + sub c + add a,a + ld c,a + add hl,bc + ld c,(hl) + inc hl + ld b,(hl) + ld (cjump),bc ; Address to jump to + exx ; Restore regs! + +cjump equ $+1 + jp 0 + + +; Convert a control key entry to cap char + +menuinp: + push af + and 80h ; Keep high bit + ld e,a + pop af + and 7fh + call ucase + or e ; Restore high bit + ret + +; Mark meta-key flag + +meta_key1: + ld a,10000000b + ld (meta_flag),a + ret + +meta_flag: db 0 ; Initial value 0 = no shift + + +; Jump table for commands + +cmd_vector: + dw meta_key1 ; Shift key + dw bsp ; Backspace + dw bsp ; Cursor left + dw fsp ; Cursor right + dw bwrd ; Left word + dw fwrd ; Right word + dw linend ; To EOL + dw linbeg ; To SOL + dw linend1 ; To EOL/SOL + dw delete ; Delete char + dw delft ; Delete char left + dw delwrd ; Delete word right + dw delwlft ; Delete word left + dw delsol ; Delete to start of line + dw dline ; Delete line + dw deleol ; Delete to end of line + dw instog ; Toggle insert + dw ctl_entry ; Enter control char + dw recall_back ; Scroll back in history + dw recall_fwrd ; Scroll ahead in history + dw eds$ex ; Execute line + dw esc_menu ; Submenu + +;-------------------------------------------------------------------- + +; ON-LINE ROUTINES, EDITING CURRENT LINE IN LINE BUFFER + +; WHILE ON LINE: +; B = CHAR COUNT (0..lbufwid) C = CURSOR POSITION (0..lbufwid) +; HL = MEM POSITION + + +; Backspace +; Return Z = backspace not done, NZ = all OK + +bsp: + xor a + cp c ; Cursor pos + ret z ; At start + dec hl ; Back up in mem + dec c ; Cursor pos back +bspace: + ld a,bs ; Back up on screen + or a ; Must ret nz + jp conout + + +; Forward space +; Return Z = not done + +fsp: + ld a,(hl) + or a + ret z ; At EOL + inc hl + inc c + jp pctl ; Screen advance by reprinting char + + +; Back word + +bwrd: + call bsp ; Backspace + ret z ; Nowhere to go + ld a,(hl) + cp ' ' + jr z,bwrd ; Backspace over blanks + dec hl ; Now backspace until next wordsep + call wrdsep ; Look at char before this position + inc hl + jr nz,bwrd + ret + +; Forward word + +fwrd: + call wrdsep ; Are we on a word separator? + jr z,fwrd1 ; Yes + call fsp ; No, advance until we find one + jr fwrd + +fwrd1: call fsp ; Word sep found, advance 1 more space + ld a,(hl) ; Are we on a blank? + cp ' ' + jr z,fwrd1 ; Don't quit on a blank + ret + + +; Delete char left + +delft: + call bsp ; Backspace and fall through to delete + + +; Delete char + +delete: + call delmem ; In memory + jp ptail ; Refresh screen from cursor position + +; Delete to start of line + +delsol: + ld a,c ; Get cursor pos + or a + ret z ; Already at start + cp b + jr z,dline ; At end, so delete entire line (quicker) + ld e,a ; Cursor pos = # chars to delete + call linbeg ; Go to start +delcmd1: + call delmem ; Delete first char in memory + dec e ; Loop counter + jr nz,delcmd1 + jp ptail ; Now update screen + + +; Delete word left + +delwlft: + call bwrd ; Back a word and fall thru ... + + +; Delete word right + +delwrd: + call wrdsep ; On a word sep? + jr z,delete ; Yes, kill it + ld a,b ; Compare line count to cursor pos + cp c + jr z,delete ; On last char of line +delwrd1: + call delmem ; Delete in mem, let screen catch up later + jr delwrd ; Go until word sep found + + +; Delete line + +dline: + call linbeg ; Position at line start and fall thru ... + +; Delete to eoln + +deleol: + call ereol ; Clear on screen + ld b,c ; Char count = current position + jp zline ; Zero line tail in mem + +; Insert/overwrite toggle + +instog: + ld a,(ins_flag) ; Flag 0 -> owrt + cpl + ld (ins_flag),a + ret + +; Enter a control + +ctl_entry: + call cin + and 1fh ; Fall thru to normal char entry + +; Enter a char + +enter: + ex af,af' ; Save char + ld a,b ; At eoln? + cp c + jr z,ovrwrt ; Yes, no need for insert mode + ld a,(ins_flag) ; Which mode are we in? + or a ; 0 = overwrite, nz = insert + jr nz,insert + + +; Enter char in overwrite mode + +ovrwrt: + ld a,b ; Char count + cp lbufwid-2 ; Line full? + jr c,ovr1 ; No + cp c ; At EOLN? + ret z ; Accept no more chars + +ovr1: ex af,af' ; Recover char + ld (hl),a ; Put char in place + call fsp ; Advance by printing it + ld a,b ; Char count -> a + cp c + ret nc ; No need to incr char count inside line + inc b ; Else add to count + ret + +; Enter char in insert mode + +insert: + ld a,b ; Line full? + cp lbufwid-2 + ret nc + +insrt: + ld a,b ; At eoln? + sub c ; A = # chars to eoln + jr z,ovr1 ; Yes, really want overwrite + call insmem ; Push chars down to make room + ex af,af' ; Recover new char + ld (hl),a ; Place char in line + call ptail ; Reprint entire line from here + inc b ; Inc char count + jp fsp ; Advance cursor + + +; Line end/start toggle + +linend: ; Go to eoln or, if there, to start of line + ld a,b + cp c + jr z,linbeg + +linend1: + call fsp ; Print ahead until EOL + jr nz,linend1 + ret + +linbeg: + call bsp ; Backspace until start + jr nz,linbeg + ret + + +; Compare current char to list of word separators + +wrdsep: + push hl + push bc + ld bc,wrdseplen + ld a,(hl) + ld hl,wrdseps + cpir + pop bc + pop hl + ret + +wrdseps: + db 0,' ,;:.' ; Punctuation word separators +wrdseplen equ $ - wrdseps + + +; Delete current char from line + +delmem: + ld (hl),0 ; Terminal 0 or char to be deleted + ld a,b + sub c ; A = (count-position) = chars from end + ret z ; At eoln, no char + dec b + ret z ; Single char line + dec a + ret z ; On last char, just deleted it +delmem1: + inc a ; To move terminal 0 in + push hl + push de + push bc + ld d,h ; Dest is current pos + ld e,l + inc hl ; Source, terminal 0 + ld c,a ; Count, line tail + ld b,0 + ldir ; Block move + pop bc + pop de + pop hl + ret + +; Insert a char in line + +insmem: + push bc ; Make room for char in line + push de + ld c,a ; Bc = # chars to move + ld b,0 + add hl,bc ; Dest is new eoln + ld d,h ; Now in DE + ld e,l + dec hl ; Source is current eoln + lddr ; Tail move + pop de + pop bc ; Recover char count, crs pos info + inc hl ; Hl to next char + ret + + +; Print line tail from cursor position, return to position + +ptail: + push hl ; Save mem pos + push bc ; Save screen pos + call linend1 ; Print ahead to end of line + call ereol ; Clean off danglers +ptail1: + ld a,c ; End of line cursor pos + pop bc + pop hl + sub c ; Current cursor pos + ret z ; At end of line already + ld e,a ; Loop counter +ptail2: + call bspace ; Else back up to where we were + dec e + jr nz,ptail2 + ret + + +; Print a char, turn a control char into a cap char + +pctl: + push af + cp 20h + jr nc,pctl1 + add '@' +pctl1: call conout + pop af + ret + + +; Convert char or control key to upper case + +ucase: + cp ' ' + jr nc,notctl + add '@' +notctl: cp 'a' + ret c ; Not a lowercase + cp 'z'+1 + ret nc ; Not a lowercase + sub ' ' ; Yes, a lowercase + ret + + +; Zero line tail + +zline: + push hl + push bc + ld hl,(line) ; HL -> start of line + ld c,b ; BC = char count + ld b,0 + add hl,bc ; HL -> EOLN + ld a,lbufwid + sub c + dec a + jr z,zline0 + ld b,a ; # of 0's + xor a +zline1: + ld (hl),a + inc hl + djnz zline1 +zline0: + pop bc + pop hl + ret + + +; ESC key pressed - get submenu command + +esc_menu: + call cin + call ucase + cp 'Q' ; Quit + jr z,edquit + cp 'S' ; Toggle Save + ret nz ; Loop if none of these + +; Toggle recording state +; - Alter line prompt to > if save ON, >> if save OFF + +save_tog: + ld a,(save_flag) ; Flip flag byte + cpl + ld (save_flag),a + call crlf + call prompt ; Print new prompt string + pop af ; Lift ecmd from stack + jp edit ; Restart + + +; Exit editor + +eds$ex: + pop af ; Lift ECMD from stack + ld a,(save_flag) ; Are we recording? + or a + ret z ; Nope + + ld a,(minsave) ; Is line worth keeping? + cp b + push bc + call c,save_line + pop bc + +edn$ex: + xor a ; Return Z + ret + + +; Exit and pop shell + +edquit: + pop af ; Lift ECMD from stack + xor a ; Return NZ + dec a + ret + + +; --------------------------- + +; HISTORY STACK ROUTINES for RCPCLED, Version 1.2 + +;Each command line is pushed onto the history stack before execution. As the +;older ones overflow, they are eliminated. The last character of each line +;has the high bit set. The buffer terminates with a 0. +;The history stack is internal to the RCP, but could be implemented in an RSX + +; Save new line to stack +; - This routine called only on exit, so on-line regs not preserved. +; - Push contents down by size of current line +; - Move line buffer to start of stack +; - Terminate line with high bit set +; - Terminate history with 0 after last complete line +; - If current line is too big for buffer size chosen, do nothing + +save_line: + ld c,b ; Line size from b to bc + xor a + ld b,a + push bc + ld hl,HISTSIZE + sbc hl,bc ; Buffer size - line length + jr z,savel_err ; Not enough room + jr c,savel_err ; Definitely not enough room! + push hl + ld hl,hbuf_top + push hl + sbc hl,bc ; hl -> bufftop - line size + pop de ; de -> bufftop + pop bc ; bc = buffsize - line size + lddr ; tail move + + pop bc ; Recover line size in bc + ex de,hl + inc de ; de -> buffstart + ld hl,(line) ; Move in line + ldir + dec de + ex de,hl + set 7,(hl) ; Tag line terminus + + ld hl,hbuf_top ; Terminate history after last complete line +savel1: + dec hl ; Back up to EOLN + bit 7,(hl) + jr z,savel1 ; Loop until hi-bit encountered + inc hl + ld (hl),0 + ret + +savel_err: + pop af ; Lift BC push + ret + +; Recall command history, newest -> oldest +; - recall_ptr is init to start of buffer on each CLED invocation +; - return with pointer updated to next line + +recall_back: + call check_recall ; Is there anything in buffer? + ret nc ; No + +; Transfer from recall pointer to line buffer +; - enter hl @ recall_ptr +; - return ptr -> start of next command if no OVFL + +rc_back1: + ld de,(line) ; Destination for move +rc_back1a: + ld a,(hl) + or a + jr z,recall_quit ; Buff end + ldi + bit 7,a + jr z,rc_back1a + + ld (recall_ptr),hl ; Update ptr now + ex de,hl ; Point to end of line in line buffer + ld (hl),0 ; Terminate it + dec hl + res 7,(hl) ; Fix high bit from storage + + pop af ; Lift ecmd from stack + jp edit ; Restart on this line + +recall_quit0: + pop af ; Lift subroutine call from stack +recall_quit: + exx ; Recover main regs + ret ; Back to editing + + +; Recall command history, oldest -> newest + +recall_fwrd: + call check_recall ; Anything in buffer? + ret nc ; No + call rc_fwrd1 ; Move to previous line + call rc_fwrd1 ; Don't repeat line on direction rev + jr rc_back1 ; Now same code as recall_back + +rc_fwrd1: + dec hl ; Initially, HL -> next line to recall + ld de,history ; Underflow address +rc_fwrd1a: + push hl ; Compute position relative to top + xor a + sbc hl,de + pop hl + ret z ; Quit when start of buff reached + jr c,recall_quit0 ; Underflow + dec hl ; Going backwards in buffer + bit 7,(hl) + jr z,rc_fwrd1a + inc hl ; Point to char past command terminator + ret + + +; Check to see if anything in recall buffer yet +; - Ret NC = no, main regs preserved +; - Else switch main regs to alt, ret HL @ recall buffer line + +check_recall: + ld a,(history) ; Is anything in buffer yet? + or a + ret z ; Nope + call linbeg + exx + ld hl,(recall_ptr) + scf + ret + +; --------------------------- + +; Routine: EREOL function for Z34RCP +; Author: Rob Friefeld +; Version: 1.0 +; Date: September 19, 1989 +; +; Entry: EREOL +; Function: To clear to end of line +; Comments: The setting of the ERLTCAP equate determines whether this +; command uses the TCAP information or not. If not, it uses the +; ereol string passed in macro CLR_EOL. That string should +; end with the high bit set. The setting of the ERLQUICK equate +; determines whether to simply output the TCAP string for this +; function or to interpret it as does Rick Conn's VLIB version. +; Uses RCPSUBS.LIB routines CONOUT and PRINTHL. + + +; ------------------------------------------------------------------- + + if [not erltcap] +; Erase to end of line. Return NZ. + +ereol: call print + clr_eol +; or -1 ; For VLIB compatibility + ret + + + else + if erlquick + +; --------------------------- + +; This version just prints the EREOL string: no delay, no interpretation. + +ereol: + push hl + ld hl,z3tcap+17h ; CLS string + xor a ; Skip to EREOL string +ereol1: cp (hl) ; Skip once + inc hl + jr nz,ereol1 +ereol2: cp (hl) ; Skip twice + inc hl + jr nz,ereol2 + call printhl ; Print it + pop hl + ret + +; --------------------------- + +; This is a disassembly of EREOL from VLIB + else + +ereol: + push bc + push de + push hl + ld hl,z3tcap+16h ; Point to ereol delay + ld d,(hl) + inc hl + call vidskp + call vidskp + call vidout + pop hl + pop de + pop bc + xor a + dec a + ret + +vidskp: + ld a,(hl) + inc hl + or a + ret z + cp '\' + jr nz,vidskp + inc hl + jr vidskp + +vidout: + ld a,(hl) + or a + jr z,vid2 + inc hl + cp '\' + jr nz,vid1 + ld a,(hl) +vid1: + call conout + jr vidout + +vid2: + ld a,d + or a + ret z + ld c,a + ld hl,z3env+2bh ; Processor speed + ld a,(hl) + or a + jr nz,vidl1 + ld a,4 +vidl1: + ld b,a + push bc + call vdelay + pop bc + dec c + jr nz,vidl2 + ret +vdelay: + call vdel1 + djnz vdelay + ret +vdel1: + ld c,20 +vdel1a: + ex (sp),hl + ex (sp),hl + dec c + jr nz,vdel1a + ret + + endif ;erlquick + endif ;not erltcap + +; +;===== Z 3 L I B R O U T I N E S ================================ +; + +; Disassembly of Z3LIB routines DUTDIR, SHPUSH, SHPOP +; For use with CLED RCP segment ONLY +; Does not save regs as does Z3LIB, and has less env error checking +; rdf 10/2/89 + +DUTDIR: + ld a,z3ndirs ; No NDR + or a + ret z + + ld hl,(z3ndir) + inc b +dutdir1: + ld a,(hl) + or a + jr nz,dutdir2 + dec b + xor a + ret +dutdir2: + cp b + inc hl + jr nz,dutdir3 + ld a,(hl) + cp c + jr nz,dutdir3 + inc hl + dec b + xor a + dec a + ret +dutdir3: + push bc + ld bc,11h + add hl,bc + pop bc + jr dutdir1 + + +shpop: + +; *** +;Special function for RCPCLED -- null saved command line + ld a,(era_flag) ; Erase? + or a + jr z,eflag1 ; Z = NO + xor a + ld (history),a +eflag1: +; *** + +;shpop: + call getsh ; HL -> stack, DE = size, B = entries + ret z ; No stack + ld c,e ; Entry size + ld a,(hl) + or a + ret z ; Empty + ex de,hl + add hl,de ; HL -> next entry, DE -> first entry + xor a +shpop1: + ld (de),a ; Zero entry + dec b + ret z ; Successful exit, no more entries + push bc ; Pop next entry + ld b,0 + ldir + pop bc + jr shpop1 + + +shpush: + push hl ; Save string pointer + call getsh + jr z,shpush_err1 ; No stack + +shpush3: + ld a,(hl) ; Look for free entry + or a + jr z,shpush4 + add hl,de + djnz shpush3 + jr shpush_err2 ; Stack full +shpush4: + call getsh ; Point to top of stack + push bc +shpush5: + dec b + jr z,shpush6 + add hl,de + jr shpush5 +shpush6: + pop bc + ld c,e + dec hl + ex de,hl + add hl,de ; HL -> (entry-1) + size, DE -> (entry-1) + ex de,hl +shpush7: + ld a,b + cp 1 + jr z,shpush8 + dec b + push bc + ld b,0 + lddr + pop bc + jr shpush7 +shpush8: + call getsh + pop de + ex de,hl +shpush9: + ld a,(hl) + ldi + or a + jr nz,shpush9 + +shpushx: + ret + +shpush_err1: +; ld a,1 ; No stack +; jr shpush_err + +shpush_err2: + ld a,2 ; Stack full +shpush_err: + pop hl + or a + ret + + +; Get shell stack entry +; Return HL -> top of stack +; DE = entry size +; C = unchanged +; B = # entries +; A = # entries +; Z = no entries +getsh: +getsh2: + ld hl,(z3env+1eh) ; Stack + ld a,(z3env+20h) ; # entries + ld b,a + ld de,(z3env+21h) ; Entry size in E + ld d,0 + or a + ret + +; +;===== C L O C K R E A D I N G ================================== +; + + if systime + +; Print system time from DateStamper, ZS/ZDDOS/Z3PLUS clock + +; Entry point +; Print the string with leading '0' suppression +; Format: "h.mm " or "hh.mm " + +print_time: + +; 1. Test for DateStamper/ZSDOS/Z3PLUS and read clock if present + + ld c,12 ; Return version + ld e,'D' ; DateStamper test + call bdos + ld a,l ; Version # + cp 30h ; Z3PLUS? + jr nc,time1 ; Yes + ld a,h + cp 'D' + ret nz ; No clock + +; 2. Get time + + ld hl,time2 + push hl ; Return address on stack + push de ; Clock address on stack + ld hl,dtbuf ; Point to buffer + ret ; Call clock, return to time2 +time1: ; Z3PLUS entry point + ld c,105 ; CP/M Plus get time + ld de,dtbuf+1 + push de + call bdos + pop hl + ld a,(hl) + inc hl + ld b,(hl) + dec a + or b + ret z ; No clock if date = 0001 +time2: + +; 3. Turn highlight on, if present + + ld hl,stndout + call printhl + + ld hl,dtbuf+3 ; Point to hours + +; 4. Convert military time to civilian, if selected + + if civtim + ld a,(hl) ; Hours + or a ; Midnight? + jr nz,time3 ; No + ld a,24h ; Yes, say "12" +time3: sub 13h ; Time past 12:59 pm? + jr c,time4 ; No, don't change + daa ; Decimal adjust + inc a ; Yes, xlate to 12-hour + daa + ld (hl),a ; ..and patch in. + endif ; civtim + +; 5. Display time + +time4: + xor a + call pmbcd ; Print hours as 1 or 2 digits + ld a,(tim_sep) ; Print separator between hours, minutes + call conout + inc hl ; Point to minutes + ld a,80h ; Say print leading 0 + call pmbcd ; Print minutes as 2 digits + +; 2. Turn highlight off, if present + + ld hl,stndend + call printhl + jp spac ; Space before rest of prompt + +; +;===== D A T A ==================================================== +; + +; Buffer for date/time for read/write system clock + +dtbuf: ds 6 + + endif ;systime + + +line ds 2 ; Pointer to line buffer +recall_ptr ds 2 ; History position pointer +history ds HISTSIZE,0 ; History buffer +hbuf_top: equ $-1 + +; End RCPCLED.LIB + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpcls.lib b/Source/BPBIOS/Z34RCP11/rcpcls.lib new file mode 100644 index 00000000..e6264137 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpcls.lib @@ -0,0 +1,35 @@ + page + +; Library: RCPCLS for Z34RCP +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: CLS +; Function: To clear the CRT screen +; Comments: The setting of the CLSTCAP equate determines whether this +; command uses the TCAP information or not. If not, it uses the +; clear-screen string passed in macro CLSSTR. That string should +; end with the high bit set. + +cls: + if clstcap ; If using TCAP for clear screen string + ld a,(z3tcap) ; TCAP address from Z34CMN.LIB + cp ' '+1 ; See if blank + jr nc,cls1 ; If not, go to clear screen code + jp crlf ; If blank, just do CRLF +cls1: + ld hl,clrscr ; Address from Z34CMN.LIB + jp printhl ; Display it + + else ; Not using tcap + + call print + clsstr ; String from Z34RCP.LIB + ret + + endif ;clstcap + +; End RCPCLS.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpcmd.lib b/Source/BPBIOS/Z34RCP11/rcpcmd.lib new file mode 100644 index 00000000..58a54e78 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpcmd.lib @@ -0,0 +1,56 @@ + page + +; Library: RCPCMD for Z34RCP +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; Description: Command Table and Custom Commands for Z34-RCP + +; This is the customization file for the ZCPR Version 3.4 resident command +; package. Use it to add your own custom commands to the RCP. + +; Give each of your custom commands a unique label and command name. Then +; add the command name(s) to the macro CUSTTBL, below, and append the code +; for the commands to the end of this file. + +; CUSTTBL - Custom commands table. +; +; Add one line to CUSTTBL for each of your custom commands. Each line +; must be of the form: +; +; COMMAND name, TRUE, wheel, label +; +; Where COMMAND and TRUE appear as-is, and "name", "wheel", and "label" +; are created by the user. "Name" is a name of four characters or less used +; to invoke the command, in upper case. "Wheel" is "true" to protect the +; command from non-wheel users, "false" otherwise. "Label" marks the +; beginning of the custom code. For example, to create a wheel- +; protected command named "DIR" which calls the code at label "directory", +; CUSTTBL appears as follows: +; +;custtbl macro +; command DIR, true, true, directory +; endm + +custtbl macro +;; command ____, true, ____, _______ ; Template for custom commands + endm + +; ------------------------------ + +; RCP command dispatch table + + db cmdsize ; Length of each command name + cmdtbl ; Dispatch table from Z34RCP.LIB + custtbl ; Optional custom commands + db 0 ; Marks end of command jump table + +; -------------------------------------------------- + +; Insert label(s) and code for custom RCP commands here: + +; + +; END RCPCMD.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpcp.lib b/Source/BPBIOS/Z34RCP11/rcpcp.lib new file mode 100644 index 00000000..6ec69f48 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpcp.lib @@ -0,0 +1,245 @@ + page + +; Library: RCPCP for Z34RCP +; Author: Carson Wilson +; Version: 1.3 +; Date: August 11, 1989 +; Changes: Responds dynamically to QUIET flag, eliminating "noise." + +; Version: 1.2 +; Date: December 30, 1988 +; Changes: Now works properly with CP/M Plus. +; Moved SETDMA to common routines. + +; Author: Carson Wilson +; Version: 1.1 +; Date: August 4, 1988 +; Changes: Now initializes FCB1 before calling SetFStp, allowing +; stamp setting of multiple-extent files. +; +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: CP +; Function: Copy a file from one place to another +; Syntax: If FCBSWAP false: +; CP destfile=srcfile, CP =srcfile +; If FCBSWAP true: +; CP srcfile destfile, CP srcfile +; If TESTEQ and FCBSWAP true: +; Both of the above forms work +; +; Comments: Both file specifications can include a directory specification. +; If only one file name is given, then the current directory and +; the source file name are assumed for the destination. + +; +; New ZSDOS/DosDisk BDOS standard functions: +; +GetFStp equ 102 ; Get file stamp function +SetFStp equ 103 ; Set file stamp function + +copy: + call retsave + call dirchek ; Test bad directory + + if leftright + call fcbswap ; Exchange fcb1 with fcb2 + endif + +; If new is blank, make it the same name and type as old + + ld de,fcb1+1 ; Point to destination file name + ld a,(de) ; Get first character + cp ' ' ; If not blank (no name) + jr nz,copy0 ; ..then branch to copy + ld hl,fcb2+1 ; Copy source name into destination FCB + ld b,11 ; Name and type are 11 bytes + call blkmov + +; See if destination is same as source, and abort if so + +copy0: + ld hl,fcb1 ; Set up pointers to two files + ld de,fcb2 + push hl + push de + inc hl ; Point to names of files + inc de + ld b,13 ; Compare 13 bytes (name, type, and user #) +copy1: call comp + jr nz,copy2 ; If they differ, go on with copy + ld a,(cdrv) ; ZCPR current drive + inc a ; Shift to range 1..16 + ld b,a ; ..and keep value in B + pop de ; Restore pointers to FCBs + pop hl + ld a,(de) ; Get drive of source file + ld c,a ; ..and save it in C + or a ; Is it default drive? + jr nz,copy1a ; Branch if drive made explicit + ld c,b ; Otherwise, copy default drive into C +copy1a: ld a,(hl) ; Get drive of destination file + or a ; Is it default drive? + jr nz,copy1b ; Branch if drive made explicit + ld a,b ; Otherwise, get current drive +copy1b: cp c ; Compare the two drives specified + jr nz,copy3 ; Branch if they are different + jp duperr ; Tell EH duplicate filespecs +copy2: + pop de ; Clean up the stack + pop hl + +; Make note of the user numbers of the two files + +copy3: + ld a,(fcb1+13) ; Get destination user number + ld (usrdest),a + ld a,(fcb2+13) ; Get source user number + ld (usrsrc),a + +; Set up new FCB for source file and open the source + + call define ; Define buffer addresses dynamically +srcfcb equ $+1 + ld hl,0 ; Get address to use for new source FCB + push hl + ex de,hl ; Copy file data to new FCB + ld b,12 + call blkmov + call logsrc ; Log in user number of source file + pop hl ; Initialize the source file FCB + call initfcb2 + ld c,15 ; Open source file + call bdos + inc a ; Check for error + jp z,noflerr ; File not found error handler + + if StpCall + call cpmver + jr nc,copy4 ; Don't do this if CP/M Plus +stpbuf equ $+1 + ld de,0 + call setdma ; Set DMA to date buffer + ld de,(srcfcb) + ld c,GetFStp ; Get stamp (if any) to DMA + call bdos + ld (gotstp),a ; Store result + ld de,tbuff ; Restore DMA + call setdma ; ..for search +copy4: + endif ; StpCall + +; Make sure destination file does not already exist + + call logdest ; Log into destination user area + call extest ; Test for existence of file in fcb1 + jp z,exit ; Branch if it exists and user says no + +; Create destination file + + ld de,fcb1 ; Point to destination FCB + ld c,22 ; BDOS make-file function + call bdos + inc a ; Test for error (no directory space) + jp z,fulerr ; Invoke EH if not OK + +; Copy source to destination + +copy5: call logsrc ; Log in source user area + ld b,0 ; Initialize counter + ld de,(cbuff) ; Initialize buffer pointer +copy5a: push de ; Save address and counter + push bc + call setdma ; Set DMA to cbuff+(b*128) + ld de,(srcfcb) ; Point to source file FCB + ld c,20 ; BDOS read-sequential function + call bdos + pop bc ; Get counter and address + pop de + or a ; Read Ok? + jr nz,copy5b ; Branch if end of file + ld hl,128 ; Point DE to next buffer address + add hl,de + ex de,hl + inc b ; Increment counter + ld a,b ; See if buffer full + cp cpblocks + jr nz,copy5a ; If not, go back for more +copy5b: ld a,b ; Get count of blocks loaded into buffer + or a ; Are there any? + jr z,copy6 ; Branch if not (we are done) + push bc ; Save count + call logdest ; Log into destination user number +cbuff equ $+1 ; Pointer for in-the-code modification + ld de,0 ; Point to beginning of copy buffer +copy5c: push de ; Save buffer address + call setdma ; Set dma to buffer + ld de,fcb1 ; Point to destination file FCB + ld c,21 ; Sequential write the block + call bdos + or a ; Get result + jp nz,fulerr ; Invoke EH (disk full or write error) + pop de ; Get buffer address & balance stack + pop bc ; Get count + dec b ; Buffer empty? + jr z,copy5 ; Yes. Back for refill + push bc ; No. Save count + ld hl,128 + add hl,de + ex de,hl ; DE points to next buffer address + jr copy5c ; Back for another sector to write + +; Close the destination file + +copy6: call logdest ; Log into destination user number + ld de,fcb1 ; Point to destination FCB + ld c,16 ; Close file + call bdos + inc a ; 0ffh --> 0 if error + jp z,fulerr ; Invoke EH + + if StpCall +gotstp equ $+1 + ld a,0 ; File had stamp? + dec a ; 1 --> 0 = yes + jr nz,noset ; No + ld de,(stpbuf) ; Point to buffer + call setdma ; Set DMA + call initfcb1 ; Init. for SetFStp, point to dest. + ld c,SetFStp ; Set file's stamp + call bdos ; CCP restores DMA +noset: + endif ; StpCall + + ld a,(quiet) + or a + jr nz,qcpdone + call print + db ' Don','e'+80h +qcpdone: + if cpsp and spaceon + jp spaexit ; Report space remaining on destination drive + else + jp exit + endif ;cpsp and spaceon + +; Log into user number of source file + +logsrc: +usrsrc equ $+1 ; Pointer for in-the-code modification + ld a,0 ; Get user number + jr setusrrel ; Local jump to save code + +; Log into user number of destination file + +logdest: +usrdest equ $+1 ; Pointer for in-the-code modification + ld a,0 ; Get user number +setusrrel: + jp setusr + +; End RCPCP.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpdir.lib b/Source/BPBIOS/Z34RCP11/rcpdir.lib new file mode 100644 index 00000000..76c08bc3 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpdir.lib @@ -0,0 +1,137 @@ + page + +; Library: RCPDIR for Z34RCP +; Author: Carson Wilson +; Version: 1.0 +; Date: August 6, 1989 +; Changes: Now allows "DIR [dir:].aft" as well as "DIR [dir:]*.aft" +; to show all files of a given file extentsion, +; e.g., "d .?80" gives all .Z80 and .180 files. +; +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: DIR +; Function: Display a directory of the files on disk +; Syntax: DIR [dir:afn] Displays the DIR files +; DIR [dir:afn] S Displays the SYS files +; DIR [dir:afn] A Display both DIR and SYS files +; +; If slashchk is true: +; +; DIR [dir:]/S Equivalent to DIR [dir:]*.* S +; DIR [dir:]/A Equivalent to DIR [dir:]*.* A + +dir: + call retsave ; Save return address and set stack + +; See if FCB should be made wild (all '?') + + ld hl,fcb1+1 ; Point to file name in FCP + ld a,(hl) ; Get first character of filename + + if slashchk ; Allow "DIR /S" and "DIR /A" formats + cp '/' ; If name does not start with '/' + jr nz,dir01 ; ..branch and process normally + inc hl ; Point to second character + ld a,(hl) ; Get option character after slash + ld (fcb2+1),a ; ..and put it into second FCB + dec hl ; Back to first character + ld a,' ' ; Simulate empty FCB + endif ;slashchk + +dir01: + cp ' ' ; See if no file spec given + jr nz,dir02 ; Spec given + + ld b,8 ; Wildcard name + ld a,(fcb1+9) + cp ' ' ; Wildcard type? + jr nz,dir01a ; No + ld b,11 ; Yes. Fill name and type. +dir01a: ld a,'?' ; Get ready to fill with '?' + call fillp ; ..carry out fill +dir02: + if nosys ; Suppress-SYS-file-if-no-wheel option + call getwhl ; Get wheel status + jr z,dirnly ; If wheel off, ignore options + endif + + ld a,(fcb2+1) ; Get first char of 2nd file name + ld b,1 ; Set for both dir and sys files + cp allflag ; SYS and DIR flag specifier? + jr z,dirpr ; Got system specifier + dec b ; B=0 for sys files only + cp sysflag ; SYS only? + jr z,dirpr + +dirnly: ld b,80h ; Must be dir-only selection + +; DIRECTORY PRINT ROUTINE +; On entry, B reg is set as follows: +; 0 for only system files, 80h for only dir files, 1 for both +; +dirpr: + ld a,b ; Get systst flag + call getdir ; Load and sort directory + jp z,prfnf ; Print no file message + if wide + ld e,5 + else + ld e,4 ; Count down to 0 + endif ; wide +; +; ENTRY PRINT LOOP +; On entry, HL pts to files selected (terminated by 0) +; and E is entry counter +; +dir3: + ld a,(hl) ; Check for done + or a + if dirsp and spaceon + jp z,spaexit ; Show space when done + else + jp z,exit ; Exit if done + endif ; Dirsp and spaceon + ld a,e ; Get entry counter + or a ; Output CRLF if 4 or 5 entries printed in line + jr nz,dir3a ; Continue + call crlf ; New line + if wide + ld e,5 + else + ld e,4 ; Reset entry count + endif ; wide + + ld a,e ; Get entry count +dir3a: + if wide + cp 5 + else + cp 4 ; First entry? + endif ; wide + + jr z,dir4 + call print +; + if wide +; + db ' ' ; 2 spaces + db ' '+80h ; Then 1 more space +; + else +; + db ' ' ; Space + db fence+80h ; Then fence char +; + endif ; Wide +; +dir4: + call prfn ; Print file name + call break ; Check for abort + dec e ; Decrement entry counter + jr dir3 + +; End RCPDIR.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpecho.lib b/Source/BPBIOS/Z34RCP11/rcpecho.lib new file mode 100644 index 00000000..ffe56183 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpecho.lib @@ -0,0 +1,162 @@ + page + +; Library: RCPECHO for Z34RCP +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: ECHO +; Function: Echo text to console or printer + +echo: + xor a ; Lower case/printer off flag setting + ld (crtfl),a ; Printer off by default + + if upcase ; If upper case default + dec a + endif ;upcase + + ld (casefl),a ; Store flag in code below + ld hl,tbuff+1 ; Point to first character + call getchar ; Get first character (should be blank) + ; If none, exit from routine + +; Loop to echo chars + +echo2: call getchar + + if echolst + cp ff ; Form feed? + jr z,echo3 + endif ;echolst + + cp '^' + jr nz,echo2a ; Not control character prefix + call getchar ; Get next character + and 1fh ; Convert to control character + jr echo2e ; Echo it +echo2a: + cp cmdchar ; Case shift prefix? + jr nz,echo2e ; No, normal echo + call getchar ; Get next character + + if echolst + cp prtchar ; Turn printer on? + jr z,echo2b ; Store non-zero in crt flag + cp crtchar ; Turn printer off? + jr nz,echo2c ; No, test for shift characters + xor a ; Yes, clear crt flag +echo2b: ld (crtfl),a + jr echo2 ; On to next character +echo2c: + endif ; echolst + + cp ucasechar ; Up-shift character? + jr z,echo2d ; Store non-zero value in case flag + cp lcasechar ; Lower-case character? + jr nz,echo2e ; No, echo the character as is + xor a ; Else, clear case flag +echo2d: ld (casefl),a + jr echo2 ; On to next character +echo2e: + call echout ; Send char + jr echo2 + +; Form feed - send new line followed by form feed if printer output + + if echolst +echo3: + ld a,(crtfl) ; Check for printer output + or a ; Non-zero? + jr z,echoff ; No, send form feed normally + call echonl ; Send new line + ld a,ff ; Send form feed + jr echout + +; Send form feed char to console + +echoff: + ld a,ff ; Get char + jr echo2e + endif ;echolst + +; End of print loop - check for printer termination + +echo4: + if not echolst + ret + + else + ld a,(crtfl) ; Get list mode flag + or a + ret z ; Done if no printer output + +; Output a new line + +echonl: + ld a,cr ; Output new line on printer + call echout + ld a,lf ; Fall thru to echout + endif ; not echolst + +; Output char to printer or console + +echout: + ld c,a ; Char in c + cp 'A' ; If less than 'A' + jr c,echouta ; Leave as is + cp 'Z'+1 ; If greater than 'Z' + jr nc,echouta ; Leave as is + add 20h ; Else convert to lower case +echouta: + ld d,a ; Save lower case version in d +casefl equ $+1 ; Pointer for in-the-code modification + ld a,0 + or a ; Upper case? + jr nz,echoutb ; If upper case selected, go on as is + ld c,d ; Else substitute lower case version +echoutb: + + push hl ; Save hl + push bc ; Save bc + ld de,0ch-3 ; Offset for BIOS console output + + if echolst + +crtfl equ $+1 + ld a,0 + or a ; Printer? + jr z,echout1 ; No + inc de ; Offset for BIOS printer output + inc de + inc de + endif ;echolst + +; Output char in C with BIOS offset in DE + +echout1: + call biout ; Bios output + pop bc ; Restore bc,hl + pop hl + ret + +; Get a character from the command tail buffer + +getchar: + ld a,(hl) ; Get character + inc hl ; Point to next one + or a ; Check for end of string + ret nz ; If not end, return + pop hl ; Else, clean up stack + jr echo4 ; And exit from routine + +; Output char in C to BIOS with offset in DE + +biout: + ld hl,(wboot+1) ; Get address of warm boot + add hl,de ; Pt to routine + jp (hl) ; Jump to it + +; End RCPECHO.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpera.lib b/Source/BPBIOS/Z34RCP11/rcpera.lib new file mode 100644 index 00000000..776becb8 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpera.lib @@ -0,0 +1,76 @@ + page + +; Library: RCPERA for Z34RCP +; Author: Carson Wilson +; Version: 1.2 +; Date: Sept. 15, 1989 +; Changes: Chains to error handler with flag set to invoke transient ERA +; if a read only file is encountered. Propose error code +; 17 decimal for "file read only" error. +; +; Version: 1.1 +; Date: August 12, 1989 +; Changes: Now responds dynamically to QUIET flag, eliminating "noise". +; NOTE: if QUIET is active, using the inspect option or trying +; to erase R/O files will give meaningless messages. Use +; the PROT command to set R/O files to R/W first in order to +; erase them quietly. +; +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: ERA +; Function: Erase files +; Forms: +; ERA Erase Specified files and print their names +; ERA o Erase Specified files and print their names, but ask +; for verification before Erase is done + +era: + call retsave + ld a,(fcb2+1) ; Get eraflg if it's there + ld (eraflg),a ; Save it as a flag + ld a,1 ; Dir files only + call getdir ; Load directory of files + jp z,prfnf ; Abort if no files +; +; Main erase loop +; +era1: call break ; See if user wants to stop + call qplug ; Turn of output if quiet + push hl ; Save ptr to file + call prfn ; Print its name + ld (nxtfile),hl ; Save ptr to next file + pop hl ; Get ptr to this file + call unplug ; Turn output on + call rotest ; Test file pted to by hl for r/o + ld a,17 ; Proposed file R/O error code + ld b,00010000b ; EH flag to invoke transient + jp nz,errex1 ; Chain to transient if R/O +eraflg equ $+1 ; Address of flag + ld a,0 ; 2nd byte is flag + cp ' ' ; Is it an inspect option? + jr z,era2 ; Skip prompt if it is not + call eraq ; Erase? + jr nz,era3 ; Skip if not +era2: ld de,fcb1+1 ; Copy into fcb1 + ld b,11 ; 11 bytes + call blkmov + call initfcb1 ; Init fcb + ld c,19 ; Delete file + call bdos +era3: ld hl,(nxtfile) ; Hl pts to next file + ld a,(hl) ; Get char + or a ; Done? + if erasp and spaceon + jp z,spaexit + else + jp z,exit + endif ; Erasp and spaceon + call crlf ; New line + jr era1 + +; End RCPERA.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcph.lib b/Source/BPBIOS/Z34RCP11/rcph.lib new file mode 100644 index 00000000..34cae928 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcph.lib @@ -0,0 +1,119 @@ + page + +; Library: RCPH for Z34RCP +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: HELP +; Function: This command displays a list of all resident commands that +; are supported, including those in the CPR (command processor), +; RCP, and FCP. +; +; Syntax: H + +clist: + +; Print the FCP-resident command names + + if listfcp + call print ; Print header for FCP + db lf + db 'FC','P'+80h + ld hl,(fcp) ; Get FCP address dynamically from ENV + ld a,h ; See if still there + or l + jr z,nofcp ; FCP has been removed + ld bc,5 ; Calculate address of FCP command table + add hl,bc + call cmdlist ; Display list of commands +nofcp: + endif ;listfcp + +; Print the CPR-resident command names + + if listcpr + call print ; Print "CPR" + db cr,lf ; Need CR if no FCP + db 'CP','R'+80h + ld hl,(ccp) ; Get CCP address from ENV + ld bc,offcmd ; Point to command table in CPR + add hl,bc + call cmdlist ; Display the list of commands + endif ;listcpr + +; Print the RCP-resident command names + + call crlf ; Skip a line + ld hl,rcpname ; Print RCP name + call printhl + ld hl,RCPbegin+5 ; Point to RCP command table + ; Fall through to CMDLIST + +;---------------------------------------- + +; Subroutine to display list of commands in a command table (code above +; falls through to this routine -- do not move it). The commands are +; displayed 5 per line with 8 character spaces allowed for each command +; (subject to equates below). + +cmdlist: + call crlf ; Start with new line + ld e,(hl) ; Get size of each command name into DE + ld d,0 + inc hl ; Point to name of first command + ld c,cmdsline ; Set names-per-line value +cmdlist1: + ld a,(hl) ; Get first character of the command name + or a ; See if it is null + jr nz,cmdlist1a ; If not, continue + ld a,cmdsline ; See if we are already on a new line + cp c + call nz,crlf ; If not, skip a line + ret + +cmdlist1a: + if noshow ; Option to suppress wheel-limited cmds + rla ; Shift high bit of name into carry bit + jr nc,cmdlist2 ; If not restricted, go on + call getwhl ; Otherwise, check wheel byte + or a + jr nz,cmdlist2 ; If wheel set, continue as usual + add hl,de ; Otherwise skip this command + jr cmdlist5 + endif + +; Print leading spaces between names + +cmdlist2: + ld a,cmdspace ; Spacing between command names + sub e ; Less length of each command name + ld b,a +cmdlist3: + call spac + djnz cmdlist3 + +; Print name of command + + ld b,e ; Length of each name into B +cmdlist4: + ld a,(hl) ; Get command name character + call conout + inc hl ; Point to next + djnz cmdlist4 + + dec c ; Decrement count of names on this line + jr nz,cmdlist5 ; Branch if room for more names + call crlf ; Otherwise, end this line and + ld c,cmdsline ; ..reset count for another line of commands + +; Skip to next command name + +cmdlist5: + inc hl ; Skip jump vector + inc hl + jr cmdlist1 ; Back to process next name + +; End RCPH.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpid.lib b/Source/BPBIOS/Z34RCP11/rcpid.lib new file mode 100644 index 00000000..dfbacaf2 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpid.lib @@ -0,0 +1,64 @@ + page + +; Library: RCPID for Z34RCP +; Author: Carson Wilson +; Version: 1.0 +; Date: September 29, 1989 +; Changes: ID appended with 'C' if CLED is present +; +; Author: Carson Wilson +; Version: 1.0 +; Date: September 14, 1988 +; +; Function: Build the name of the RCP and append it at the end of the +; file at label RCPID. This serves two purposes: first it +; allows the 'H' command to describe which RCP is active, +; second, the string identifies the actual end of the RCP +; in memory. +; +; ------------------------------------------------------------------------ + +; Macros to build RCP ID for Help command + +; Under SLR and compatible assemblers, the RCP name reflects the +; RCP size, e.g., "RCP-21F" means the RCP occupies 21 records. + + if SLR +rcpid macro +rcplen defl RCPend - RCPbegin +fulrecs equ rcplen / 128 ; Full records +lastr equ [rcplen mod 128 + 127]/128 ; Last record, if any +ttlrecs equ fulrecs + lastr + + db 'RCP-' + db ttlrecs / 10 + '0' ; Tens of records + db ttlrecs mod 10 + '0' ; Ones + db rcptype ; 'F', 'H', etc. from RCP.LIB + if cledon + db 'C' ; Distinguish CLED versions + endif + db 0 ; ID string terminator + endm + + else ; Non-SLR +rcpid macro + db 'RCP-' + db rcptype + db 0 + endm + endif ; SLR + +; ----------------------------------------------------------------------- + +; Name of RCP + +; This block allows the 'H' command and/or the SHOW utility to display a name +; and version number for this RCP as well as the commands that are supported. +; It also generates a unique string marking the end of the RCP module. + +rcpname: + rcpid + +; End of RCPID.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpiom.lib b/Source/BPBIOS/Z34RCP11/rcpiom.lib new file mode 100644 index 00000000..d85e553d --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpiom.lib @@ -0,0 +1,296 @@ + page + +; Library: RCPIOM for Z34RCP +; Author: Carson Wilson +; Version: 1.1 +; Date: August 12, 1989 +; Changes: POKE and PORT now respond dynamically to QUIET flag, +; eliminating "noise." + +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Commands: PEEK, POKE, and PORT + +; ------------------------------------------------------------------ +; +; Command: PEEK +; Function: Display memory contents +; +; Form: +; PEEK startadr 128 bytes displayed +; PEEK startadr endadr Range of bytes displayed + + if peekon +peek: + call retsave + ld hl,tbuff+1 ; Find first number +nxtpeek equ $+1 ; Pointer for in-the-code modification + ld de,100h ; Default peek address if none + call sksp ; Skip to first token (if any) + call nz,hexnum ; Get start address if any + push de ; Save starting address + ld bc,peeklen ; Compute default ending address + ex de,hl + add hl,bc + + if peekchk ; Check for overflow + jr nc,peek0 ; If no overflow past FFFF, go on + ld hl,0ffffh ; Else use FFFF as ending address +peek0: + endif ;peekchk + + ex de,hl ; End address in DE + call sksp ; Skip to next token (if any) + call nz,hexnum ; Get 2nd number in DE (else default) +peek1: pop hl ; HL is start address, DE is end address + + if peekhdr + push hl ; Save starting address again + ld b,8 ; Output leading spaces +peek0a: call spac + djnz peek0a + ld b,16 ; Display 16 column headers +peek0b: ld a,l ; Get low byte of address + and 0fh ; Display low hex digit + call pashc + inc hl + djnz peek0b +; +; Display header for ASCII area +; + call print + db ' ',' '+80H ; Space over to ASCII area + pop hl ; Get address + push hl + ld b,16 ; Display 16 chars. ASCII header +peek0b1:ld a,l ; Get byte + and 0fh ; Mask + call pah ; Print ASCII char. + inc hl ; Next byte + djnz peek0b1 + + if peekbdr + call crlf + ld b,8 +peek0c: call spac ; Print leading spaces + djnz peek0c + ld b,16 +peek0d: call print + db ' -','-'+80h + djnz peek0d + +; Print border at ASCII area + + call print + db ' ',' '+80h ; Space to ASCII border + ld b,16 ; 16 dashes +peek0e: call print + db '-'+80h + djnz peek0e + endif ;peekbdr + + pop hl ; Restore starting address + endif ;peekhdr + + ld c,0ffh ; Use C as continue flag + call peek2 ; Do peek + ld (nxtpeek),hl ; Set continued peek address + jp exit +peek2: + ld a,c ; Check continuation flag + or a + ret z + +; Print line header + +peek2a: call crlf ; New line + ld a,h ; Print address + call pashc + ld a,l + call pahc + call dash ; Print leader + ld b,16 ; 16 bytes to display + push hl ; Save start address + +; Print hex values for 16 bytes + +peek3: ld a,(hl) ; Get next byte + call pashc ; Print with leading space + +; Check for last address. If C is already 0, leave it that way. +; Otherwise check for end address and if so set C to zero. + + ld a,c ; See if continue flag already cleared + or a + jr z,peek3a ; If so, skip test + ld a,h + sub a,d ; See if h = d + ld c,a + ld a,l + sub a,e ; See if l = e + or c ; Combine two tests + ld c,a +peek3a: inc hl ; Pt to next + djnz peek3 + +; Print ASCII equivalents for 16 bytes + + pop hl ; Pt to first address again + ld b,16 ; 16 bytes + call print ; Space and fence + db ' ' + db fence+80h + push bc ; Save flag in c +peek4: ld a,(hl) ; Get next byte + ld c,'.' ; Assume dot + and 7fh ; Mask it + cp ' ' ; Dot if less than space + jr c,peek5 + cp 7fh ; Don't print del + jr z,peek5 + ld c,a ; Char in c +peek5: ld a,c ; Get char + call conout ; Send it + inc hl ; Pt to next + djnz peek4 + call print ; Closing fence + db fence+80h + pop bc ; Get flag in c back + call break ; Allow abort + jr peek2 + endif ; Peekon + +; PAHC - Print A as 2 hex chars +; PASHC - With leading space + + if peekon or pokeon or porton +pashc: + push af ; Save A + call spac + pop af +pahc: push bc ; Save bc + ld c,a ; Byte in c + rrca ; Exchange nybbles + rrca + rrca + rrca + call pah ; Print hex char + ld a,c ; Get low + pop bc ; Restore bc and fall thru to pah +pah: and 0fh ; Mask + add '0' ; Convert to ascii + cp '9'+1 ; Letter? + jr c,pah1 + add 7 ; Adjust to letter +pah1: jp conout + endif ; Peekon or pokeon or porton + +; -------------------------------------------------------------------- +; +; Command: POKE +; Function: Place Values into Memory +; +; Form: +; POKE startadr val1 val2 ... +; + if pokeon +poke: + call retsave + ld hl,tbuff+1 ; Pt to first char + call sksp ; Skip to non-blank + jp z,numerr ; Numerical error + call hexnum ; Convert to number + call qplug ; Shut off output if quiet + call print + db ' Pok','e'+80h + call adrat ; Print at message (quiet sensitive) + call unplug ; Turn on output + +; Loop for storing hex values sequentially via POKE + +poke1: push de ; Save address + call sksp ; Skip to non-blank + jp z,exit ; Done + cp '"' ; Quoted text? + jr z,poke2 + call hexnum ; Get number + ld a,e ; Get low + pop de ; Get address + ld (de),a ; Store number + inc de ; Pt to next + jr poke1 + +; Store ASCII chars. + +poke2: pop de ; Get next address + inc hl ; Pt to next char +poke3: ld a,(hl) ; Get next char + or a ; Done? + jp z,exit + ld (de),a ; Put char + inc hl ; Pt to next + inc de + jr poke3 + + endif ; Pokeon + +; ------------------------------------------------------------------- +; +; Command: PORT +; Function: Display or Set I/O Port Data +; +; Forms: +; PORT addr - Read port and display value +; PORT addr value - Output value to port +; + if porton +port: + call retsave + ld hl,tbuff+1 ; Find first number + call sksp ; Skip to first command-line token + jp z,numerr ; Numerical error + call hexnum ; Get start address into de + push hl ; Save pointer to command tail + ld hl,portadl ; Modify code + ld (hl),e ; Move specified port addr into place + dec hl ; Point to opcode position + ld (hl),0dbh ; Poke 'in' opcode + dec hl + ld (hl),d ; Save MSB for port address + inc hl + ex (sp),hl ; Get tail pointer back while saving this one + call qplug ; Shut off output if quiet + call print ; Print header + db ' Por','t'+80h + ld a,e + call pashc ; Print port address + call sksp ; Skip to possible second value + jr z,portin ; Proceed with port input + call hexnum ; Get 2nd number in de + ex (sp),hl ; Get pointer to opcode back + ld (hl),0d3h ; Poke 'out' opcode + call print + db ': OU','T'+80h + ld a,e ; Get value to output + jr paddr +portin: + call print + db ': I','N'+80h + xor a ; Make sure high port address = 0 (for hd64180) +portadh equ $+1 +paddr: ld b,0 ; ..for both in and out instructions +opcode: + db 0 ; Opcode for in or out inserted by code above +portadl: + db 0 ; Port address inserted by code above + call pashc + call unplug ; Turn on output + pop hl ; Clean up stack + jp exit + + endif ; Porton + +; End RCPIOM.Z80 + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcplt.lib b/Source/BPBIOS/Z34RCP11/rcplt.lib new file mode 100644 index 00000000..98d6b0ba --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcplt.lib @@ -0,0 +1,256 @@ + page + +; Library: RCPLT for Z34RCP +; Author: Carson Wilson +; Version: 1.1 +; Date: August 26, 1989 +; Changes: Some WordStar characters caused garbage to appear on the +; screen. Now filters control characters other than CR, +; TAB, and LF. Thanks to Gene Pizzetta for this suggestion. +; +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Commands: LIST and TYPE + +; ---------------------------------------------------------------- +; +; Command: LIST +; Function: Print out specified file on the LST: Device +; Forms: +; LIST Do form feed +; LIST Print file(s) (NO Paging) +; Notes: +; The flags which apply to TYPE do not take effect with LIST +; The tab expansion code is required for LST: output. + + if liston +list: + ld a,(fcb1+1) ; Get filename or ' ' from command + ld (prflg),a ; List flag (A can't be 0) + cp ' ' ; Null command? + jp z,lstff ; Yes, do form feed and return + jr type0 ; No, send file to LST: + endif ;liston + +; -------------------------------------------------------------------- +; +; Command: TYPE +; Function: Print out specified file on the CON: Device +; Forms: +; TYPE Print file +; TYPE P Print file with paging flag +; Notes: +; The flag PGDFLG defines the letter which toggles the paging +; facility (P in the forms section above) +; The flag PGDFLT determines if TYPE is to page by default +; (PGDFLT=TRUE if TYPE pages by default); combined with +; PGDFLG, the following events occur -- +; If PGDFLT = TRUE, PGDFLG turns OFF paging +; If PGDFLT = FALSE, PGDFLG turns ON paging + +type: + if liston + xor a ; Turn off printer flag + ld (prflg),a ; Set flag + endif ; Liston + +; Entry point for list function (LIST) + +type0: call retsave ; Save return address + ld a,(fcb2+1) ; Get page flag from command + ld (pgflg),a ; Store it + ld a,1 ; Select dir files + call getdir ; Allow ambiguous files (HL points to buffer) + jp z,noflerr ; EH no files error + ld a,(lins) ; Set line count + ld (pagcnt),a + jr typex1 + +; Entry point for successive files + +typex0: pop hl ; Balance stack for skip + pop hl ; ..to next file command (^X) +typex: ld hl,(nxtfile) ; Get ptr to next file + ld a,(hl) ; Any files? + or a + jp z,exit + + if liston + ld a,(prflg) ; Check for lst: output + or a ; 0=type + jr z,typex1 + ld a,cr ; Bol on printer + call lcout + call lstff ; Form feed the printer +; fall thru + endif ; Liston + + ; Entry point for 1st file +typex1: ld de,fcb1+1 ; Copy into fcb1 + ld b,11 ; 11 bytes + call blkmov + ld (nxtfile),hl ; Set ptr to next file + call initfcb1 ; Init fcb1 + ld c,15 ; Open file + call bdos + inc a ; Set error flag + jp z,noflerr ; EH no files error + ld a,cr ; New line + call lcout + ld a,lf + call lcout + ld bc,080h ; Set char position and tab count + ; (b=0=tab, c=080h=char position) + +; Main loop for loading next block + +type2: ld a,c ; Get char count + cp 80h + jr c,type3 + call break + push bc ; Read next block + ld de,fcb1 ; Pt to fcb + ld c,20 ; Read record + call bdos + or a ; Set flags + pop bc + jr nz,typex ; End of file? + ld c,0 ; Set char count + ld hl,tbuff ; Pt to first char + +; Main loop for printing chars in tbuff + +type3: ld a,(hl) ; Get next char + and 7fh ; Mask out msb + cp 1ah ; End of file (^z)? + jr z,typex ; Next file if so + +; Output char to CON: or LST: device with tabulation + + cp cr ; Reset tab count? + jr z,type4 + cp lf ; Reset tab count? + jr z,type4 + cp tab ; Tab? + jr z,type5 + cp ' ' ; Skip other ctls. + jr c,type6 + +; Output char and increment char count + + call lcout ; Output char + inc b ; Increment tab count + jr type6 + +; Output or and reset tab count + +type4: call lcout ; Output or + ld b,0 ; Reset tab counter + jr type6 + +; Tabulate + +type5: ld a,' ' ; + call lcout + inc b ; Incr pos count + ld a,b + and 7 + jr nz,type5 + +; Continue processing + +type6: + inc c ; Increment char count + inc hl ; Pt to next char + jr type2 + +; Send a formfeed to LST:. Assumes PRFLG <> 0. + +lstff: + ld a,ff ; formfeed +; fall thru + +; Send output to LST: or CON:, as per the flag +; Return with Z if abort + +lcout: push hl ; Save regs + push bc + ld e,a ; Char in e + ld c,2 ; Output to con: + + if liston +prflg equ $+1 ; Pointer for in-the-code modification + ld a,0 ; 2nd byte is the print flag + or a ; 0=type + jr z,lc1 + ld c,5 ; Output to lst: + endif ; Liston + +lc1: push de ; Save char + call bdos ; Output char in e + pop de ; Get char + ld a,e + cp lf ; New line? + jr nz,lc2 ; No, return + call break ; Check for abort + jp z,typex0 ; Skip if ^X + + if liston + ld a,(prflg) ; Output to lst:? + or a ; Nz = yes + jr nz,lc2 + endif ; Liston + +; New line, so check for paging + + ld hl,pagcnt + dec (hl) + jr nz,lc2 ; Jump if not end of page + ld a,(lins) + ld (hl),a ; Reset counter +pgflg equ $+1 ; Pointer to in-the-code buffer + ld a,0 ; 2nd byte is the paging flag + cp pgdflg ; Page default override option wanted? + + if pgdflt ; If paging is default + jr z,lc2 ; Pgdflg means no paging + else + jr nz,lc2 ; Pgdflg means page + endif ; Pgdflt + + push hl ; Save hl + call print + db cr,lf,' Typing',' '+80h + ld hl,fcb1+1 ; Print file name + call prfn + call dash ; Print dash + call conin ; Get input + pop hl ; Restore hl + call break1 ; Set Z flag or abort + push af ; Save results + + if typecls and clson + call cls ; Clear between screens + else + call crlf + endif + + pop af ; Get results + jp z,typex0 ; Control-X, so skip to next file + cp ctrlz ; If Control-Z, + jr nz,lc2 + ld a,pgdflg ; Switch to non-default + ld (pgflg),a ; ..paging mode +lc2: pop bc ; Restore regs + pop hl + ret + +; Storage for line counter + +pagcnt: + ds 1 + +; End RCPLT.LIB + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpprot.lib b/Source/BPBIOS/Z34RCP11/rcpprot.lib new file mode 100644 index 00000000..4e8a5479 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpprot.lib @@ -0,0 +1,130 @@ + page + +; Library: RCPPROT for Z34RCP +; Author: Carson Wilson +; Version: 1.1 +; Date: August 12, 1989 +; Changes: Now responds dynamically to QUIET flag, eliminating "noise." + +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: PROT +; Function: To set the attributes of a file (R/O, SYS, and ARC) +; Form: PROT afn RSAI +; Comments: If R, S, or A are omitted, the file is made R/W, DIR, or +; modified, respectively. R, S, A, and I may be in any order. +; If I is present, Inspection is enabled. + +att: + call retsave + call dirchek ; Test bad directory + xor a ; Set no inspect + ld (inspect),a + ld hl,0 ; Set r/o and sys attributes off + ld c,0 ; Set Arc attribute off + ld de,fcb2+1 ; Pt to attributes + ld b,4 ; 4 chars max +att1: + ld a,(de) ; Get char + inc de ; Pt to next + cp 'I' ; Inspect? + jr z,atti + cp 'R' ; Set r/o? + jr z,attr + cp 'S' ; Set sys? + jr z,atts + cp 'A' + jr z,atta +att2: + djnz att1 + jr att3 +atti: + ld (inspect),a ; Set flag + jr att2 +attr: + ld h,a ; Save R/O flag + jr att2 +atts: + ld l,a ; Save SYS flag + jr att2 +atta: + ld c,a ; Save ARC + jr att2 +att3: + ld (fatt2),hl ; Save file attributes + ld a,c + ld (fatt1),a ; Save Arc attribute + ld a,1 ; Select dir and sys files + call getdir ; Load directory + jp z,noflerr ; Tell error handler no file + jr att5 +att4: + ld hl,(nxtfile) ; Pt to next file + ld a,(hl) ; End of list? + or a + jp z,exit + call crlf ; New line +att5: call break ; Check for possible abort + call qplug ; Turn off output if quiet + push hl ; Save ptr to current file + call prfn ; Print its name + ld (nxtfile),hl ; Save ptr to next file + call print + db ' Set to R','/'+80h + ld hl,(fatt2) ; Get attributes + ld c,'W' ; Assume r/w + ld a,h ; Get r/o bit + or a + jr z,att6 + ld c,'O' ; Set r/o +att6: ld a,c ; Get char + call conout + ld a,l ; Get sys flag + or a ; Set flag + jr z,att7 + call print + db ', SY','S'+80h +att7: ld a,(fatt1) + or a + jr z,att7a + call print + db ', AR','C'+80h +att7a: call unplug ; Turn output on +inspect equ $+1 ; Ptr for in-the-code modification + ld a,0 ; Get inspect flag + or a ; Z=no + pop hl ; Get ptr to current file + jr z,att8 + call eraq1 ; Ask for y/n + jr nz,att4 ; Advance to next file if not y +att8: ld de,fcb1+1 ; Copy into fcb1 + ld b,11 ; 11 bytes + call blkmov + ex de,hl + dec hl ; Pt to archive byte +fatt1 equ $+1 + ld a,0 + call attset + dec hl ; Pt to sys byte +fatt2 equ $+1 ; Ptr for in-the-code modification + ld de,0 ; Get attributes + ld a,e ; Get sys flag + call attset ; Set attribute correctly + dec hl ; Pt to r/o byte + ld a,d ; Get r/o flag + call attset + ld de,fcb1 ; Pt to fcb + ld c,30 ; Set attributes + call bdos + jp att4 +attset: + res 7,(hl) ; Clear attribute + or a + ret z ; 0=clear attribute + set 7,(hl) ; Set attribute + ret + +; End RCPPROT.LIB + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpquiet.lib b/Source/BPBIOS/Z34RCP11/rcpquiet.lib new file mode 100644 index 00000000..9269d241 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpquiet.lib @@ -0,0 +1,49 @@ + page + +; Library: RCPQUIET for Z34RCP +; Author: Carson Wilson +; Version: 1.1 +; Date: August 6, 1989 +; Changes: Now "Q R[eset]" and "Q S[et]" just reset and set quiet status +; without showing it, and Q alone (or with any other character, +; e.g., "Q ?") always just shows status. +; Forms: +; Q - Display quiet flag (always) +; Q s - Set quiet flag ON +; Q r - Set quiet flag OFF +; +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: Q +; Function: Set the Quiet flag on or off +; Comments: If QQUIET equate is true, then RCP does not report +; quiet status with the Q command. +; Forms: +; Q - Display quiet flag (if QQUIET false) +; Q s - Set quiet flag ON +; Q r - Set quiet flag OFF + +quset: + ld a,(fcb1+1) ; Get first char + ld b,1 ; Prepare to turn on + cp 'S' ; S-et quiet (ON) + jr z,quset1 + ld b,0 ; Prepare to turn off + cp 'R' ; R-eset quiet (OFF) + jr nz,qmsg ; Neither S nor R, so display +quset1: + ld a,b + ld (quiet),a + ret + +qmsg: +; Print Quiet Flag Message + + ld a,(quiet) + jp tella ; Say " On" or " Off" and return + +; End RCPQUIET.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpr.lib b/Source/BPBIOS/Z34RCP11/rcpr.lib new file mode 100644 index 00000000..e29a4f37 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpr.lib @@ -0,0 +1,108 @@ + page + +; Library: RCPR for Z34RCP +; Author: Carson Wilson +; Version: 1.5 +; Date: August 30, 1989 +; Changes: Minor bug fix as suggested by Howard Goldstein. +; +; Version: 1.4 +; Date: August 26, 1989 +; Changes: Now performs reset 37 for ALL drives under CP/M 2.2 or ZRDOS +; to ensure that fast fixed disks are reset under ZRDOS, then +; exits with reset 13 to compensate for bugs in ZRDOS/CP/M 2.2 +; reset 37. Still resets individual drives selectively under +; CP/M Plus and ZS/ZDDOS. + +; Version: 1.3 +; Date: August 11, 1989 +; Changes: Now responds dynamically to QUIET flag, eliminating "noise." +; Now performs reset 37 in ALL cases, reset 13 as well if not +; CP/M Plus or ZSDOS (per suggestion by Howard Goldstein). + +; Version: 1.2 +; Date: December 30, 1988 +; Changes: Now resets single drives under Z3PLUS. + +; Version: 1.1 +; Date: September 11, 1988 +; Changes: Fixed bug which failed to detect ZRDOS. ZRDOS' function +; 37, like CP/M's, is not reliable. + +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: RESET +; Function: Reset the disk system +; Forms: R [d: or dir:] - ZSDOS or CP/M Plus +; R - CP/M 2.2 or ZRDOS +; Comments: ZRDOS does not require a disk system reset when disks are +; changed, but directory programs will not show the correct +; size if this is not done. It is also good practice. Since +; no warm boot is performed, the disk in drive A need not have +; the operating system on it. +; +; Under ZSDOS or CP/M Plus, individual drives may be reset, and +; if RESETSP is true, space remaining is also given. If +; the fast hard disk reset capability is enabled under ZSDOS, +; any "fast" fixed disks are also reset at this time. +; +; NOTE: It is necessary to reset a legal directory or DU when there are +; protected directories. + +reset: ld a,(quiet) + or a ; Skip message if quiet + jr nz,QReset + call print ; Report action + db ' Rese','t'+80h +QReset: call cpmver ; CP/M Plus? + jr nc,reset0 ; Yes + ld c,48 + call bdos ; ZRDOS or CP/M? + ld a,h + or a + jr nz,reset0 ; No, assume bug-free F37 + ld de,0ffffh ; Yes, reset ALL drives, both ways + ld c,37 + call bdos + ld c,13 + jp bdos ; Yes, do regular reset + +; Reset individual drive(s) + +reset0: call dirchek ; Abort with error if illegal drivespec + ld a,(fcb1) ; Use default drive? + or a + jr nz,reset1 ; No, use drive from FCB1 + ld a,(cdrv) ; Yes, get ZCPR 3.3 current drive byte + inc a ; Shift range to 1..16 +reset1: ld hl,1 ; Map drive "A:" +reset2: dec a ; Done yet? + jr z,reset3 ; Yes + add hl,hl ; No, shift vector to next drive + jr reset2 + +; Check for fixed disks + +reset3: push hl ; Save current or specified vector + ld c,39 + call bdos ; ZSDOS return fixed disks in HL + pop de ; Restore vector + ld a,d + or h ; Add any fixed disks + ld d,a + ld a,e + or l + ld e,a + ld c,37 ; Reset individual drive(s) + + if spaceon and resetsp + call bdos + jp crspace ; Show space remaining (QUIET sensitive) + else + jp bdos ; Do reset and return + endif + +; End RCPR.LIB + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpreg.lib b/Source/BPBIOS/Z34RCP11/rcpreg.lib new file mode 100644 index 00000000..d73eee05 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpreg.lib @@ -0,0 +1,165 @@ + page + +; Library: RCPREG for Z34RCP +; Version: 1.1 +; Date: August 11, 1989 +; Changes: Register Set, Decrement, and Increment commands now respond +; dynamically to QUIET flag, eliminating "noise." + +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: REG +; Function: Manipulate Memory Registers +; +; Forms: +; REG D or REG <-- Display 10 Register Values +; REG Mreg <-- Decrement Register Value +; REG Preg <-- Increment Register Value +; REG Sreg value <-- Set Register Value +; +; Vers 2.1 Joe Wright +; +; REG reg <-- Display a single register value +; +; REG numbers now range from 0 to 31, although only the first ten are +; displayed with REG D. +; +; REG now treats the program error byte as register E. +; +register: + ld de,fcb1+2 ; Pt to first arg + ld a,(de) ; Get possible digit + call regptr ; Pt hl to potential register + dec de ; Point to command + ld a,(de) + cp 'S' ; Set? + jr z,rset + cp 'P' ; Plus? + jr z,rinc + cp 'M' ; Minus? + jr z,rdec + cp ' ' + jr z,rshow + cp 'D' + jr z,rshow + call regptr + jp regout + +; Increment register value +; HL pts to memory register on input + +rinc: inc (hl) ; Increment it + jr Qregout ; Print result + +; Decrement register value +; HL pts to memory register on input + +rdec: dec (hl) ; Decrement value + jr Qregout ; Print result + +; Show first ten registers and Program Error byte + +rshow: call rshow10 + ld hl,z3msg+6 + jp regout + +rshow10:xor a ; Select register 0 + ld b,a ; Counter set to 0 in b + call regp1 ; Hl pts to register 0 +rshow1: ld a,b ; Get counter value + cp 10 ; First ten registers + ret z ; Exit if done + push bc ; Save counter + push hl ; Save pointer + call regout ; Print register value + pop hl ; Get pointer + pop bc ; Get counter + inc b ; Increment counter + ld a,b ; Check for new line + and 3 + call z,crlf ; Newline after fourth display + inc hl ; Pt to next register + jr rshow1 + +; Set register value +; HL pts to register on input + +rset: + ld de,fcb2+1 ; Pt to value + call de2bin ; Eval string at de to binary in b + ld (hl),b ; Set value + +; Enter with HL pointing to the register. HL is maintained. +; +qregout:ld a,(quiet) + or a + ret nz +regout: call print + db ' Reg',' '+80h + ld de,z3msg+30h ; Register 0 + sbc hl,de ; Register number in hl + ld a,l + cp 32 ; A numbered register? + jr c,rego0 ; Yep + call print + db ' ','E'+80h + jr rego1 ; Report + +rego0: push hl + push de + ld b,0 ; Suppress zeros + call decdsp2 ; Report register number + pop de + pop hl +rego1: add hl,de ; Hl points to register again + call print + db ' =',' '+80h + ld l,(hl) + xor a + ld h,a + ld b,a ; Suppress leading zeros + jp decdsp3 ; Display value + +; Evaluate decimal string at DE to binary in B + +de2bin: ld b,0 ; Init value to zero +de2b: ld a,(de) ; Get this digit + inc de ; Pt to next + sub '0' ; Convert to binary + ret c ; A space, finished + cp 10 ; Range? + ret nc ; Not decimal, finished + ld c,a ; Digit in c + ld a,b ; Multiply old by 10 + add a,a ; *2 + add a,a ; *4 + add a,b ; *5 + add a,a ; *10 + add a,c ; Add in new digit + ld b,a ; Result in b + jr de2b ; Again + +; Set HL to point to memory register whose index is pted to by HL +; On input, A contains register char +; On output, HL = address of memory register (reg 0 assumed if error) + +regptr: ld hl,z3msg+6 ; The e register + cp 'E' + ret z + push de + call de2bin ; Get register number in b + pop de + ld a,b + cp 32 ; Range 0-31 + ld a,0 + jr nc,regp1 ; Out of range, use 0 + ld a,b ; Value in a +regp1: ld hl,z3msg+30h ; Pt to memory registers + add a,l ; Pt to proper register + ld l,a + ret ; No chance of crossing page boundary + +; End RCPREG.LIB + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpren.lib b/Source/BPBIOS/Z34RCP11/rcpren.lib new file mode 100644 index 00000000..6947bad2 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpren.lib @@ -0,0 +1,100 @@ + page + +; Library: RCPREN for Z34RCP +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: REN +; Function: To change the name of an existing file +; Forms: +; LEFTRIGHT false: +; REN = +; LEFTRIGHT true: +; REN +; LEFTRIGHT and TESTEQ both true: +; Either of the above forms may be used. + +ren: + call retsave + call dirchek ; Test bad dirspec + + if leftright + call fcbswap ; Exchange command line fcb's + endif +; +; STEP 1: See if old name is ambiguous +; + ld hl,fcb2+1 ; Can't be ambiguous + call ambchk1 +; +; STEP 2: Log into user area +; +; If dirspec given at old name, use it +; else use dirspec (or default) given at new name. + + ld hl,fcb1 ; Pt to new name + push hl + ld de,fcb2 ; Pt to old name + push de ; Save ptr + ld a,(de) ; Test if dirspec issued + or a ; ..at old name + jr z,ren1 ; No, use user at new name + ld (hl),a ; Stuff drive into new file + ld a,(fcb2+13) ; Yes, log to user area + call setusr ; ..of old name + jr ren2 +; +; Use dirspec at new name (none given at old name) +; +ren1: + ld a,(hl) ; Stuff drive of new name + ld (de),a ; ..into old name + call logusr ; Log to user at new name +ren2: +; +; STEP 3: See if old file is R/O +; + pop de ; Restore ptr to old FCB + push de ; Save it again + ld c,17 ; Look for old file + call bdos + inc a + jr z,rnxit + call getsbit ; Match found, get ptr to entry in tbuff + ex de,hl ; Hl pts to entry + inc hl ; Pt to fn + call rotest ; See if file is r/o + jr nz,rnxit1 ; Abort if so +; +; STEP 4: See if new file already exists +; EXTEST performs a number of checks: +; 1) Ambiguity +; 2) R/O +; 3) If file exists and not R/O, permission to delete +; + call extest + jr z,rnxit1 ; R/o or no permission +; +; STEP 5: Exchange file name fields for rename +; + pop de ; Pt to old + pop hl ; Pt to new + push hl ; Save ptr + ld b,12 ; 12 bytes + call iswap1 +; +; STEP 6: Rename the file +; + pop de ; Get ptr to FCB + ld c,23 ; Rename + call bdos + inc a ; Set zero flag if error +rnxit: + jp z,noflerr ; EH print no source file message +rnxit1: + jp exit + +; End RCPREN.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpsp.lib b/Source/BPBIOS/Z34RCP11/rcpsp.lib new file mode 100644 index 00000000..0fc5ecf6 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpsp.lib @@ -0,0 +1,163 @@ + page + +; Library: RCPSP for Z34RCP +; Author: Carson Wilson +; Version: 1.2 +; Date: August 11, 1989 +; Changes: CRSPACE does nothing if QUIET is true. +; +; Author: Carson Wilson +; Version: 1.1 +; Date: December 30, 1988 +; Changes: Calls CPMVER common routine. +; Sets DMA to TBUFF for CP/M Plus in case we chained to SP. + +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Command: SP +; Function: Shows space remaining on designated drive +; Syntax: SP [DIR:|DU:] +; Comments: This code can be called by several other RCP commands so that +; they can show the space remaining on the disk after their +; operation. + +; Now works with CP/M Plus. + + if [erasp or cpsp or dirsp] +crspace: ; Used to call space after other subroutines + ld a,(quiet) + or a ; Skip if we're being quiet + ret nz + call crlf ; Start new line + jr space0 ; Skip directory check + endif ;[erasp or cpsp or dirsp] +space: + call dirchek ; Abort to EH on bad dirspec +space0: ld a,(fcb1) ; Determine requested drive + or a ; If drive explicitly selected + jr nz,space1 ; ..then skip + ld a,(cdrv) ; Get current drive from ZCPR 3.4 + inc a ; Shift to range 1..16 + +space1: + dec a ; Shift to range 0..15 + ld e,a ; Save in E for selecting disk below + add a,'A' ; Convert to letter and + ld (seldrv),a ; save in message string below + ld c,14 ; BDOS select disk function + call bdos ; Not needed if no drive selected, but smallest + ; ..possible code size this way. + +; Here we extract the following disk parameter information from the disk +; parameter block (DPB): +; BLKSHF: block shift factor (1 byte) +; BLKMAX: max number of blocks on disk (2 bytes) + + call cpmver ; What BDOS is running? + jr nc,isplus + ld c,31 ; BDOS get disk parameters function + call bdos + inc hl ; Advance to block shift factor byte + inc hl + ld a,(hl) ; Get value and + ld (blkshf),a ; ..save it in code below + inc hl ; Advance to max block number word + inc hl + inc hl + ld e,(hl) ; Get value into HL + inc hl + ld d,(hl) + inc de ; Add 1 for max number of blocks + +; Compute amount of free space left on disk + +dfree: + ld c,27 ; BDOS get allocation vector function + push de ; Save BLKMAX value + call bdos ; Get allocation vector into HL + ld b,h ; Copy allocation vector to BC + ld c,l + pop hl ; Restore BLKMAX value to HL + ld de,0 ; Inititialize count of free blocks + +; At this point we have +; BC = allocation vector address +; DE = free block count +; HL = number of blocks on disk + +free1: push bc ; Save allocation address + ld a,(bc) ; Get bit pattern of allocation byte + ld b,8 ; Set to process 8 blocks +free2: rla ; Rotate allocated block bit into carry flag + jr c,free3 ; If set (bit=1), block is allocated + inc de ; If not set, block is not allocated, so + ; ..increment free block count +free3: ld c,a ; Save remaining allocation bits in C + dec hl ; Count down number of blocks on disk + ld a,l ; See if we are down to zero + or h + jr z,free4 ; Branch if no more blocks to check + ld a,c ; Get back current allocation bit pattern + djnz free2 ; Loop through 8 bits + pop bc ; Get pointer to allocation vector + inc bc ; Point to next allocation byte + jr free1 ; Continue by processing next allocation byte + +free4: pop bc ; Clean up stack + ex de,hl ; Free block count to HL +blkshf equ $+1 ; Pointer for in-the-code modification + ld a,0 ; Get block shift factor + sub 3 ; Convert to log base 2 of K per block + jr z,free6 ; Done if single density (1k per block) + +; Convert for blocks of more than 1K each + +free5: add hl,hl + dec a + jr nz,free5 + +; At this point HL = amount of free space on disk in K + +; Display decimal value of HL + +free6: ld b,0 ; Initialize count of digits already printed + ld de,10000 ; Divisor in DE + call decdsp ; Print digit (or space if leading '0') + ld de,1000 + call decdsp + call decdsp3 ; Display hundreds, tens, and units + + call print + db 'k free on ' +seldrv: db 0 ; Modified above to contain drive letter + db ':'+80h + ret + +; CP/M Plus free space calculation + +isplus: ld de,tbuff ; We may have chained to SP + call setdma + ld a,(seldrv) ; Get drive letter + sub 'A' ; Convert to BDOS call value + ld e,a + ld c,2eh ; Get free space call + call bdos + +; Convert 3 byte count of records to K + + ld b,3 ; Total amount to shift +c3fre1: ld hl,tbuff+2 ; Point to buffer start + rr (hl) + dec hl + rr (hl) + dec hl + rr (hl) + djnz c3fre1 + ld hl,(tbuff) ; Get free mod 65536k + jr free6 + +; End RCPSP.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/rcpsubs.lib b/Source/BPBIOS/Z34RCP11/rcpsubs.lib new file mode 100644 index 00000000..3879e9a2 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/rcpsubs.lib @@ -0,0 +1,1057 @@ + page + +; Library: RCPSUBS for Z34RCP +; Author: Carson Wilson +; Version: 1.4 +; Date: October 4, 1989 +; Changes: Added modifications by Rob Friefeld for CLED. +; +; Version: 1.3 +; Date: Sept. 8, 1989 +; Changes: Added ERREX1 routine. RCP now sets bit 4 of the command +; status flag on ambiguous file name errors. Intelligent +; error handlers can interpret this bit and chain to +; more sophisticated transient programs of the same name, +; much the same as CP/M Plus. +; +; Version: 1.2 +; Date: August 12, 1989 +; Changes: Added QPLUG and UNPLUG routines for QUIET operation. + +; Author: Carson Wilson +; Version: 1.1 +; Date: December 30, 1988 +; Changes: Added CPMVER routine to detect CP/M Plus. +; Expanded ZSDOS datestamp buffer to full 128 bytes. +; Now gets CCP address from Z34CMN.LIB instead of calculating +; it from BIOS address, since NZCOM allows nonstandard length +; CCP segments (suggested by Howard Goldstein). + +; Author: Carson Wilson +; Version: 1.0 +; Date: June 15, 1988 +; +; Subroutines for Z34RCP.Z80 + +; ---------------------------------------- + +; Routines to chain to Error Handler (EH) + +; 1. Error codes (from ZCPR34.LBR) + +; ZCPR34 uses the error byte at the beginning of the message buffer as a flag +; to show what kind of error occurred. Advanced error handlers will be able +; to help the user further by suggesting the possible cause of the error. + +ecbaddir equ 2 ; Bad directory specification -- logging of + ; ..user number beyond legal range, + ; ..nonexistent named directory +ecambig equ 8 ; Ambiguous file specification where not + ; ..allowed (SAVE, GET, REN) +ecbadnum equ 9 ; Bad numerical value -- not a number where + ; ..number expected, number out of range +ecnofile equ 10 ; File not found -- REN, TYPE, LIST could not + ; ..find a specified file +ecdiskfull equ 11 ; Disk directory or data area full + ; ..(DOS write error) +ectpafull equ 12 ; TPA overflow error +ecdupspec equ 16 ; Duplicate filespecs (COPY, RENAME) + +; 2. Error Routines + + if cpon or renon or lton or proton +NoFlErr: ; File missing + ld a,ecnofile ; File not found error + jr errexit ; Chain to error handler + endif ; cpon or renon or lton or proton + + if cpon +FulErr: ; Disk or directory full (BDOS write error) + ld a,ecdiskfull ; Disk or data area full + jr errexit ; Chain to error handler +DupErr: ; Duplicate file specs + ld a,ecdupspec ; Duplicate filespec error + jr errexit ; Chain to error handler + endif ; cpon +; +; Check for illegal directory specification under ZCPR 3.4. +; DirChek assumes that FCB's have not been altered since they were +; set by the CCP. Therefore DirChek is called before other BDOS calls. + + if cpon or lton or renon or diron or eraon or proton or reson or spaceon +DirChek: + ld a,(fcb1+15) ; Z34 sets these to non zero + ld hl,fcb2+15 ; ..if illegal dirspecs. found + or (hl) + ret z ; Return if OK + ld a,ecbaddir ; Bad dir. error code +; fall thru + endif +; +; Set error type, then set error, ECP, and external program bits of command +; status flag to tell CCP to go straight to EH. + +ErrExit: + ld b,0 +ErrEx1: ld ix,z3msg ; Point to message buffer + ld (ix+0),a ; First set error type in error byte + ld a,00001110b ; Tell CCP External, No ECP, Error, No shell + or b ; Add any specified bits + ld (ix+3),A ; Set bits in command status flag + jp exit ; Return to CCP + +; ------------------------------------------------------------- + +; Routine to get wheel byte - Returns wheel in A with flags set + +getwhl: + push hl + ld hl,(z3whl) ; Get wheel address from ENV + ld a,(hl) ; Read wheel byte + and a ; Set flags + pop hl + ret + +; ------------------------------ + + if systime ; Time in CLED prompt +; +; PMBCD - Print byte at (HL) to console as 1 or 2 BCD digits. +; +; Entry: A = 80h for leading zero's +; A = 0h for no leading zero's +; +pmbcd: + rld ; Get hi nibble (HL) to low nibble A + or a ; For HD64180 + call nz,decout ; Display if low or high of A not zero + rld ; Get low nibble (HL), from above +decout: + and 00001111b ; Mask high nibble + add a,'0' ; Convert to character + jr conout + endif ; systime + +; Display decimal digit routines + +; Display hundreds, tens, and units digits (assumes flag in B has been set) + + if regon or spaceon +decdsp3: + ld de,100 ; Display hundreds + call decdsp +decdsp2: + ld de,10 ; Display tens + call decdsp + ld a,l ; Get remaining units value +decdsp4: + add a,'0' ; Convert to character + jr conout ; Print it and return + +; --------------------------------- + +; Routine to print any single digit + +; Actually, this routine displays the value of HL divided by DE and leaves the +; remainder in HL. In computing the character to display, it assumes that the +; result of the division will be a decimal digit. If the result is zero, the +; value in the B register, which is the number of digits already printed, is +; checked. If it is zero, a space is printed instead of a leading '0'. If it +; is not zero, the '0' is printed. Whenever any digit (not a space) is +; printed, the value in B is incremented. + + +decdsp: + ld c,'0'-1 ; Initialize digit count + xor a ; Clear carry flag + +decdsp1: + inc c ; Pre-increment the digit + sbc hl,de ; Subtract DE from HL + jr nc,decdsp1 + + add hl,de ; Add back in to produce remainder + ld a,c ; Get decimal digit + cp '0' ; Check for leading 0 + jr nz,decdout ; If not 0, proceed to display it + ld a,b ; Digit printed already? + or a + jr z,spac ; Print leading space if not +decdout: + ld a,c ; Else print real digit + inc b ; Indicate digit printed + jr conout + endif ; regon or spaceon + +; ------------ + +; Print a dash + + if lton or peekon +dash: call print + db ' -',' '+80h + ret + endif ; Lton or peekon + +; ------------------------- + +; Shut off output in QUIET is set. Return Z if QUIET, NZ otherwise. +; Uses: AF + +qplug: + ld a,(quiet) + or a + ret z + ld a,0C9h ; "RET" +qplug1: ld (plug),a + ret + +; -------------------------- + +; Turn on output + +unplug: xor a + jr qplug1 + + +; ------------- + +; Print a space + +spac: ld a,' ' + +; fall thru + +; Console Output Routine + +conout: +plug: db 00h ; Goes to 0C9h to turn off output + putreg ; Save all registers except AF + push af ; Save AF, too + and 7fh ; Mask out MSB + ld e,a ; Transfer character to E + ld c,2 ; BDOS conout function number + call bdos + pop af + getreg ; Restore registers +note: ; Use this RET for NOTE command + ret + +; ------------------------ + +; String printing routines + +; Print string following call (terminated with null or character with the +; high bit set) + +print: + ex (sp),hl ; Get address + call printhl + ex (sp),hl ; Put address + ret + + if whlon or quieton +; +; Routine to say "On" if A is non-zero or "Off" if A is zero. +; Called by WHL and Q commands. + +onmsg: + dc ' On' +offmsg: + dc ' Off' +tella: + ld hl,offmsg ; Prepare to say Off + or a + jr z,printhl ; Say it if A=0 + ld hl,onmsg ; Say On + +; fall thru ; Display message and return + + endif ; whlon or quieton + +; Print string pointed to by HL (terminated with null or character with the +; high bit set) + +printhl: + ld a,(hl) ; Get next character + inc hl ; Point to following one + or a ; See if null terminator + ret z ; If so, we are done + call conout ; Display the character + ret m ; We are done if MSB is set (negative number) + jr printhl ; Back for more + +; ------------------------ + +; Output new line to CON: + +crlf: + call print + db cr,lf+80h + ret + +; Console input + + if eraon or lton or proton or renon or cpon +conin: + push hl ; Save regs + push de + push bc + ld c,1 ; Input + call bdos + pop bc ; Get regs + pop de + pop hl + and 7fh ; Mask msb + cp 61h + ret c + and 5fh ; To upper case + ret + endif ; Eraon or lton or proton or renon or cpon + +; ------------------- + +; Save return address + +retsave: + pop de ; Get return address + pop hl ; Get return address to zcpr3 + ld (z3ret),hl ; Save it + push hl ; Put return address to zcpr3 back + push de ; Put return address back + ret + +; ------------------------------- + +; Test file pted to by HL for R/O +; NZ if R/O + + if renon or cpon or eraon +rotest: + push hl ; Advance to r/o byte + ld bc,8 ; Pt to 9th byte + add hl,bc + ld a,(hl) ; Get it + and 80h ; Mask bit + push af + ld hl,romsg + call nz,printhl ; Print if nz + pop af ; Get flag + pop hl ; Get ptr + ret +romsg: + db ' is R/','O'+80h + +; ----------------------------------------------- + +; Check user to see if he approves erase of file +; Return with Z if yes + +eraq: + call print + db ' - Eras','e'+80h + endif ; Renon or cpon or eraon + + if renon or cpon or eraon or proton +eraq1: + call print + db ' (Y/N/Q)? N',88h ; 88h = backspace + call conin ; Get response + cp 'Q' ; Quit command? + jr z,exit + cp 'Y' ; Key on yes + ret + endif ; Renon or cpon or eraon or proton + +; ------------------------------------- + +; Give space on current disk and return + + if spaceon and [dirsp or cpsp or erasp or resetsp] +spaexit: + call crspace ; Show space remaining +; fall thru + endif ; spaceon and [dirsp or cpsp or erasp or resetsp] + +; +; Exit to ZCPR3 +; +exit: +z3ret equ $+1 ; Pointer to in-the-code modification + ld hl,0 ; Return address + jp (hl) ; Goto ZCPR3 + +; -------------------------------------------------------- + +; Check for user input; if ^X, return with Z, abort if ^C + + if diron or lton or eraon or proton or peekon +break: + push hl ; Save regs + push de + push bc + ld c,11 ; Console status check + call bdos + or a + ld c,1 ; Get char if any + call nz,bdos + pop bc ; Restore regs + pop de + pop hl +break1: cp ctrlc ; Check for abort + jr z,exit ; Exit + cp ctrlx ; Skip? + ret + endif ; Diron or lton or eraon or proton or peekon + +; --------------------- + +; Print address in DE + + if peekon or pokeon +adrat: + call print + db ' at',' '+80h + ld a,d ; Print high + call pahc + ld a,e ; Print low + jp pahc + endif ; Peekon or pokeon + +; -------------------------------------------------- + +; Extract hexadecimal number from line pted to by HL +; Return with value in DE and HL pting to offending char + + if peekon or pokeon or porton +hexnum: + ld de,0 ; De=accumulated value +hnum1: + ld a,(hl) ; Get char + cp ' '+1 ; Done? + ret c ; Return if space or less + inc hl ; Pt to next + sub '0' ; Convert to binary + jr c,numerr ; Return and done if error + cp 10 ; 0-9? + jr c,hnum2 + sub 7 ; A-f? + cp 10h ; Error? + jr nc,numerr +hnum2: + push hl ; Save pointer + ex de,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl ; De x16 to hl + ld e,a + ld d,0 + add hl,de + ex de,hl ; De = de * 16 + a + pop hl ; Get the pointer + jr hnum1 ; Try again +; +; Number error +; +numerr: + ld a,ecbadnum ; Numeric error + jp errexit ; Chain to error handler + +; Skip to next non-blank + +sksp: + ld a,(hl) ; Get char + inc hl ; Pt to next + cp ' ' ; Skip spaces + jr z,sksp + dec hl ; Pt to good char + or a ; Set eol flag + ret + endif ; Peekon or pokeon or porton + +; ------------------------------------------------------------------------ + +; Test File in FCB for unambiguity and existence, ask user to delete if so +; Return with Z flag set if R/O or no permission to delete + + if renon or cpon +extest: + call ambchk ; Ambiguous file names not allowed + call searf ; Look for specified file + jr z,exok ; Ok if not found + call getsbit ; Position into dir + inc de ; Pt to file name + ex de,hl ; Hl pts to file name + push hl ; Save ptr to file name + call prfn ; Print file name + pop hl + call rotest ; Check for r/o + jr nz,exer + call eraq ; Erase? + jr nz,exer ; Restart as error if no + ld de,fcb1 ; Pt to fcb1 + ld c,19 ; Delete file + call bdos +exok: xor a + dec a ; Nz = ok + ret +exer: + xor a ; Error flag - file is r/o or no permission + ret + +; Check for ambiguous file name in FCB1 +; Return z if so + +ambchk: + ld hl,fcb1+1 ; Pt to fcb + +; Check for ambiguous file name pted to by HL + +ambchk1: + push hl + ld b,11 ; 11 bytes +amb1: ld a,(hl) ; Get char + and 7fh ; Mask + cp '?' + jr z,amb2 + inc hl ; Pt to next + djnz amb1 + dec b ; Set nz flag + pop de + ret +amb2: + pop hl ; Pt to file name + ld a,ecambig ; Ambiguous name error + ld b,00010000b ; Bit 4 tells EH to load transient + jp ErrEx1 ; Chain to error handler + endif ; Renon or cpon + +; --------------------------------------- + +; Init FCB1, return with DE pting to FCB1 + + if eraon or lton or cpon +initfcb1: + ld hl,fcb1 ; Pt to fcb +initfcb2: + push hl ; Save ptr + ld bc,12 ; Pt to first byte + add hl,bc + ld b,24 ; Zero 24 bytes + xor a ; Zero fill + call fillp ; Fill memory + pop de ; Pt to fcb + ret + endif ; Eraon or lton or cpon + +; Fill a region with byte in A + + if eraon or lton or cpon or diron +fillp: + ld (hl),a ; Store byte + inc hl ; Pt to next + djnz fillp ; Count down + ret + endif ; Eraon or lton or cpon or diron + +; --------------------------------------------------------------------- + +; After a search, return NZ set if desired type of file found, Z if not. +; This algorithm looks at the system bit of the located file; this +; bit is set to 1 if the file is a system file and 0 if not a system +; file. The following exclusive or masks are applied to return Z or NZ +; as required by the calling routine: +; +; SYSTEM BYTE: X 0 0 0 0 0 0 0 (AFTER 80H MASK, X=1 IF SYS, 0 IF DIR) +; +; SYS-ONLY : 0 0 0 0 0 0 0 0 (XOR 0 = 0 if X=0, = 80H if X=1) +; DIR-ONLY : 1 0 0 0 0 0 0 0 (XOR 80H = 80h if X=0, = 0 if X=1) +; BOTH : 0 0 0 0 0 0 0 1 (XOR 1 = 81H or 1H, NZ in both cases) + + if diron or eraon or lton or proton or cpon or renon +getsbit: + dec a ; Adjust to returned value + rrca ; Convert number to offset into tbuff + rrca + rrca + and 60h + ld de,tbuff ; Pt to buffer + add a,e ; Add entry offset to base addr + ld e,a ; Result in e + push de ; Save ptr in de + add a,10 ; Add offset of 10 to pt to system byte + ld e,a ; Set address + ld a,(de) ; Get byte + pop de ; Get ptr in de + and 80h ; Look at only system bit +systst equ $+1 ; In-the-code variable + xor 0 ; If systst=0, sys only; if systst=80h, dir + ; Only; if systst=1, both sys and dir + ret ; Nz if ok, z if not ok + +; Log into user area contained in FCB1 + +logusr: + ld a,(fcb1+13) ; Get user number +setusr: + ld e,a + ld c,32 ; Use bdos fct + jp bdos + +; Print file name pted to by HL + +prfn: + call spac ; Leading space + ld b,8 ; 8 chars + call prfn1 + call print + db '.'+80h ; Dot + ld b,3 ; 3 chars +prfn1: ld a,(hl) ; Get char + inc hl ; Pt to next + call conout ; Print char + djnz prfn1 ; Count down + ret + +; Search for first + +searf: + push bc ; Save counter + push hl ; Save hl + call dirchek ; Check bad dirspec + ld c,17 ; Search for first function +searf1: ld de,fcb1 ; Pt to fcb + call bdos + inc a ; Set zero flag for error return + pop hl ; Get hl + pop bc ; Get counter + ret + endif ; Diron or eraon or lton or proton or cpon or renon + +; ------------------------- + +; Copy HL to DE for B bytes + + if diron or eraon or lton or proton or cpon +blkmov: + ld a,(hl) ; Get + ld (de),a ; Put + inc hl ; Pt to next + inc de + djnz blkmov ; Loop + ret + endif ; Diron or eraon or lton or proton or cpon + +; ----------------------------- + +; Print file not found message + + if diron or eraon +prfnf: + call print + db ' No File','s'+80h + jp exit + endif ; Diron or eraon + +; ------------------------------------------------------------------ + +; Define buffers as high as possible in TPA for the following groups +; of commands: +; +; COPY needs SRCFCB, CBUFF, STPBUF (if STPCALL) +; ERA, PROT, LIST/TYPE need DIRBUF and NXTFILE +; DIR needs DIRBUF +; +; If DIRBUF is defined, its value is in HL on return from this code. The DE +; register pair is not changed by the code, but the BC pair is affected. + +dirbufon equ lton or eraon or proton or diron + + if dirbufon +dirbuf: ds 2 ; Address for directory buffer + endif ; dirbufon + + if lton or eraon or proton +nxtfile: ds 2 ; Ptr to next file in list + endif ; eraon or lton or proton + + if cpon or dirbufon or cledon +define: + push de + ld hl,(bdos+1) ; Get bottom of BDOS + ex de,hl ; ..into DE + ld hl,(ccp) ; From Z34CMN.LIB + +; Now we have to compare and pick the lower address as the top of TPA + + push hl ; Save CPR address while comparing + xor a ; Clear the carry flag + sbc hl,de ; Compute (CPR-BDOS) + pop hl ; Restore CPR address + jr c,define1 ; Branch if BDOS address is higher (use CPR) + ex de,hl ; Otherwise use BDOS address +define1: + if cpon + ld de,-36 ; Calculate place for SRCFCB for copy command + add hl,de + ld (srcfcb),hl + + if stpcall + ld de,-128 + add hl,de ; Buffer for datestamps + ld (stpbuf),hl + endif ; stpcall + + if dirbufon + push hl ; Save if needed below + endif ; dirbufon + + ld de,-[cpblocks*128] ; CBUFF can use same space as DIRBUF + add hl,de + ld (cbuff),hl + + if dirbufon + pop hl + endif ; dirbufon + + endif ; cpon + + if dirbufon + ld de,-[maxdirs*11] ; Space for directory buffer + add hl,de + ld (dirbuf),hl + endif ; dirbufon + + pop de + ret + + endif ; cpon or dirbufon or cledon + +; --------------- + +; Search for next + + if diron or eraon or lton or proton +searn: + push bc ; Save counter + push hl ; Save hl + ld c,18 ; Search for next function + jr searf1 + +; Load directory and sort it +; On input, A=SYSTST flag (0=SYS, 1=DIR, 80H=BOTH) +; Directory is loaded into buffer at top of TPA +; Return with ZERO set if no match and HL pts to 1st entry if match + +direrr: + ld a,ectpafull ; Chain to error handler + jp errexit + +getdir: + ld (systst),a ; Set system test flag + call logusr ; Log into user area of fcb1 + call define ; Define buffer addresses + ld (hl),0 ; Set empty + ld bc,0 ; Set counter + call searf ; Look for match + ret z ; Return if not found + +; Step 1: Load directory + +gd1: + push bc ; Save counter + call getsbit ; Check for system ok + pop bc + jr z,gd2 ; Not ok, so skip + push bc ; Save counter + inc de ; Pt to file name + ex de,hl ; Hl pts to file name, de pts to buffer + ld b,11 ; Copy 11 bytes + call blkmov ; Do copy + pop bc ; Get counter + inc bc ; Increment counter + ld hl,maxdirs-1 ; See if count equals or exceeds MAXDIRS + ld a,b ; Check high bytes + sub h + jr c,gd1a ; If carry set, we are OK + ld a,c ; Check low bytes + sub l + jr nc,direrr ; If no carry, jump to error message +gd1a: ex de,hl ; Hl pts to next buffer location +gd2: call searn ; Look for next + jr nz,gd1 + ld (hl),0 ; Store ending 0 + ld hl,(dirbuf) ; Pt to dir buffer + ld a,(hl) ; Check for empty + or a + ret z + +; Step 2: Sort directory + + if dsort + push hl ; Save ptr to dirbuf for return + call diralpha ; Sort + pop hl + endif + + xor a ; Set nz flag for ok + dec a + ret + + +; DIRALPHA -- Alphabetizes directory in DIRBUF; BC contains +; the number of files in the directory +; + if dsort +diralpha: +; +; SHELL SORT -- +; This sort routine is adapted from "Software Tools" +; by Kernigan and Plaugher, page 106. Copyright, 1976, Addison-Wesley. +; + ld h,b ; Hl=bc=file count + ld l,c + ld (num),hl ; Set "NUM" + ld (gap),hl ; Set initial gap to n for first division by 2 + +; For (gap = num/2; gap > 0; gap = gap/2) + +srtl0: +gap equ $+1 ; Pointer for in-the-code modification + ld hl,0 ; Get previous gap + or a ; Clear carry + rr h ; Rotate right to divide by 2 + rr l + + ld a,l ; Test for zero + or h + ret z ; Done with sort if gap = 0 + + ld (gap),hl ; Set value of gap + push hl + pop ix ; Set ix=gap for following loop + +; For (ix = gap + 1; ix <= num; ix = ix + 1) + +srtl1: + inc ix ; Add 1 to ix + push ix + pop de ; IX is in DE + +; Test for ix <= num + +num equ $+1 ; Pointer for in-the-code modification + ld hl,0 ; Number of items to sort + ld a,l ; Compare by subtraction + sub e + ld a,h + sbc a,d ; Carry set means ix > n + jr c,srtl0 ; Don't do for loop if ix > n + ex de,hl ; Set jj = ix initially for first + ; ..subtraction of gap + ld (jj),hl + +; For (jj = ix - gap; jj > 0; jj = jj - gap) + +srtl2: + ld de,(gap) +jj equ $+1 ; Pointer for in-the-code modification + ld hl,0 ; Get jj + + sbc hl,de ; Compute JJ - GAP + ld a,h + ld (jj),hl ; Jj = jj - gap + jr c,srtl1 ; If carry from subtractions, jj < 0 and abort + or l ; Jj=0? + jr z,srtl1 ; If zero, jj=0 and abort + +; Set iy = jj + gap + + ex de,hl ; Jj in de + ld hl,(gap) ; Get gap + add hl,de ; Jj + gap + push hl + pop iy ; IY = iy + gap + +; If (v(jj) <= v(iy)) + + call icompare ; JJ in de, iy in hl + +; ...then break + + jr c,srtl1 + +; ...else exchange + + ld de,(jj) ; Swap jj, iy + push iy + pop hl + call iswap ; Jj in de, iy in hl + +; End of inner-most for loop + + jr srtl2 + +; +; SWAP (Exchange) the elements whose indexes are in HL and DE +; +iswap: + call ipos ; Compute position from index + ex de,hl + call ipos ; Compute 2nd element position from index + ld b,11 ; 11 bytes to flip + endif ; Dsort + endif ; Diron or eraon or lton or proton + + if diron or eraon or lton or proton or renon or (cpon and leftright) +iswap1: + ld a,(de) ; Get bytes + ld c,(hl) + ex de,hl + ld (de),a ; Put bytes + ld (hl),c + inc hl ; Pt to next + inc de + djnz iswap1 + ret + endif ; Diron or eraon or lton or proton or renon + ; ..or (cpon and leftright) + + if leftright and (cpon or renon) +; +; FCBSWAP exchanges 16 byte FCB1 with FCB2 from the command line. +; This allows COPY and REN commands to execute left to right, +; e.g., "cp source dest". Costs 10 bytes. +; +; If TESTEQ is true, then commands containing '=' still execute right +; to left, e.g., "cp dest=source". Costs 11 bytes additional. +; +fcbswap: + if testeq + xor a ; Zero B + ld b,a + ld hl,80h ; Point to command line, length + ld c,(hl) ; Set up for CPIR + ld a,'=' ; Search for '=' + cpir ; '=' found in command? + ret z ; Yes, don't swap + endif ; Testeq + + ld de,fcb1 ; Point to command fcb's + ld hl,fcb2 + ld b,16 ; Exchange them + jr iswap1 + endif ; leftright and (cpon or renon) + + if diron or eraon or lton or proton + if dsort +; +; ICOMPARE compares the entry pointed to by the pointer pointed to by HL +; with that pointed to by DE (1st level indirect addressing); on entry, +; HL and DE contain the numbers of the elements to compare (1, 2, ...); +; on exit, Carry Set means ((DE)) < ((HL)), Zero Set means ((HL)) = ((DE)), +; and Non-Zero and No-Carry means ((DE)) > ((HL)) +; +icompare: + call ipos ; Get position of first element + ex de,hl + call ipos ; Get position of 2nd element + ex de,hl +; +; Compare dir entry pted to by HL with that pted to by DE; +; No net effect on HL, DE; ret w/CARRY set means DE +; Version: 1.2 +; Date: 16 June 1988 +; Changes: Renamed CRT0 to LINS for NZCOM compatibility. +; Added CUSR " " " + +; Author: Carson Wilson +; Version: 1.1 +; Date: 12 June 1988 +; Changes: Added CDRV for various Z34RCP commands. +; Added CRT0 for Z34RCP TYPE command. +; Added Z3TCAP and CLRSCR for Z34RCP CLS command. +; Added QUIET for ZCPR34 time in prompt. +; Added RSDMSG for ZCPR34 time in prompt. + +; Library: Z34CMN.LIB +; Author: Joe Wright +; Date: 23 March 1988 + +; As a replacement for Z3BASE.LIB, some usual equates. + +base equ 0 ; Base Page + +false equ 0 +true equ not false + +no equ false +yes equ true + +off equ false +on equ true + +; Named COMMON declarations start here. For compatibility, these +; are the same names used by Bridger Mitchell's JetLDR. + + common /_ENV_/ +z3env: ; Z3 Environment descriptor +z3envs equ yes ; There is one + +expath equ z3env+9 ; Address of External Path +expaths equ 10 ; Maximum 10 elements for MPATH + +rcp equ z3env+0ch ; Address of RCP +rcps equ yes ; Used as existence test, not size + +fcp equ z3env+12h ; Address of FCB +fcps equ yes ; Used as existence test, not size + +z3ndir equ z3env+15h ; Address of NDR +z3ndirs equ yes ; Used as existence test, not size + +quiet equ z3env+28h ; Quiet flag address + +z3whl equ z3env+29h ; Wheel byte address +z3whls equ yes ; There is a wheel + +lins equ z3env+33h ; CRT text lines address + +ccp equ z3env+3fh ; CCP entry +ccps equ z3env+41h ; Size + +dos equ z3env+42h ; DOS entry (+6) +doss equ z3env+44h ; Size + +bio equ z3env+45h ; BIO entry + +z3tcap equ z3env+80h ; TCAP address + +clrscr equ z3env+97h ; Clear screen string address + + common /_SSTK_/ +shstk: ; Top of Shell stack +shstks equ yes ; There is a shell stack + + common /_MSG_/ +z3msg: ; Message buffer +z3msgs equ yes ; There is one + +cusr equ z3msg+2eh ; Current user +cdrv equ z3msg+2fh ; Current drive + +rsdmsg equ z3msg+3ah ; Reserved bytes + + common /_FCB_/ +extfcb: ; External file control block +extfcbs equ yes ; There is one + + common /_MCL_/ +z3cl: ; Multiple command line +z3cls equ yes ; There is one + + common /_XSTK_/ +extstk: ; External stack +extstks equ yes ; There is one + + common /_BIOS_/ +bios: + + cseg ; Select Code Segment + +; End of Z34CMN.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/z34defn.lib b/Source/BPBIOS/Z34RCP11/z34defn.lib new file mode 100644 index 00000000..683fc322 Binary files /dev/null and b/Source/BPBIOS/Z34RCP11/z34defn.lib differ diff --git a/Source/BPBIOS/Z34RCP11/z34mac.lib b/Source/BPBIOS/Z34RCP11/z34mac.lib new file mode 100644 index 00000000..8ab3ffbe --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/z34mac.lib @@ -0,0 +1,122 @@ + +; Z33MAC.LIB : Macros for use with ZCPR33 + +; General purpose macros + +putreg macro + push hl ; Save registers in order + push de + push bc + endm + +getreg macro + pop bc ; Restore registers in order + pop de + pop hl + endm + +swap macro + rrca ; Exchange nibbles + rrca + rrca + rrca + endm + +;---------------------------------------- + +; Macro for forming option bytes + +; This macro generates a byte with bits corresponding to up to 8 option +; flags. The bits are filled in the order of the parameters and are right +; justified in the byte. + +optflag macro f1,f2,f3,f4,f5,f6,f7,f8 + +flag defl 0 ;; initial value + + irp temp, + + if not nul temp +flag defl flag shl 1 + if temp +flag defl flag or 1 + endif ;;temp + endif ;;not nul temp + + endm ;; irp + + defb low flag + + endm ;; optflag + +;----------------------------------------------------------------------------- + +; Command table entry definition macro + +; Macro to form an entry for one command in the table. The first parameter is +; the name to be used for the command (no quotes); the second parameter is the +; flag that indicates whether or not the command is to be enabled; the third +; parameter is the wheel control flag; and the last parameter is the jump +; address to the code that carries out the command. The command names are +; automatically padded out to the correct length (they will be truncated and +; an error message will result if a command name is too long). The characters +; in the command name are automatically converted to upper case. + +command macro cmdname,enableflag,wheelflag,address + + if enableflag ;; Generate command only if enabled + +whlmask defl wheelflag ;; Initialize variables +count defl cmdsize ;; Initialize to size of each command name + + irpc char,cmdname ;; Repeat over letters in command name + +count defl count - 1 ;; Count down characters in name + + if [ count lt cmdsize ] + + ;; If character is lower case, convert to upper case + + if [ '&char' ge 'a' ] and [ '&char' le 'z' ] + + if whlmask + defb [ '&char' and 5fh ] + 80h + else ;;not whlmask + defb [ '&char' and 5fh ] + endif ;;whlmask + + else ;;not lower case + + if whlmask + defb '&char' + 80h ;; If controlled by wheel, set high bit + else ;;not whlmask + defb '&char' ;; If not restricted, leave high bit clear + endif ;;whlmask + + endif ;;lower case + + endif ;;[ count lt cmdsize ] + +whlmask defl false ;; Turn off high-bit setting after first char + + endm ;irpc + + ;; Pad command name with blanks + + if [ count gt cmdsize ] ;; If we underflowed + *** Command name "&cmdname" is too long / truncated *** + else + rept count + defb ' ' + endm + endif ;[ count gt cmdsize ] + + dw address ;; Dispatch address for command + + endif ;enable + + endm ;command + +; End Z33MAC.LIB + + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/z34rcp.lib b/Source/BPBIOS/Z34RCP11/z34rcp.lib new file mode 100644 index 00000000..74908d54 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/z34rcp.lib @@ -0,0 +1,515 @@ + +; File: Z34RCP.LIB +; Description: Resident Command Package (RCP) for ZCPR34 +; Version: 1.4 +; Author: Carson Wilson +; Date: October 4, 1989 +; Changes: Added PUSER0 equate suggested by Rob Friefeld. +; +; Version: 1.3 +; Author: Carson Wilson +; Date: September 29, 1989 +; Changes: Added CLED (Command Line Editor Shell) command options +; and equates. +; +; Version: 1.2 +; Author: Carson Wilson +; Date: August 11, 1989 +; Changes: Removed several "quiet" conditional assembly equates as +; we are now sensing the QUIET flag instead. +; Changed default of WHLQUIET to false. +; +; Version: 1.1 +; Author: Carson Wilson +; Date: July 10, 1989 +; Changes: Changed 'S' command name to 'SP'. +; Removed wheel protection from 'Q' command and set QQUIET +; on by default. +; Changed command order. + +; Version: 1.0 +; Author: Carson Wilson +; Date: June 15, 1988 + +; This is the configuration file for the ZCPR Version 3.4 resident command +; package. You should read through this file and set the equates according +; to the features you want to implement. Since most systems have a limited +; amount of space for the resident command package, it will probably be +; impossible to include all features. + +; If you are using an SLR or equivalent assembler, you will be prompted +; at assembly time for which commands to include in the RCP. + +;============================================================================= +; +; A S S E M B L Y O P T I O N S +; +;============================================================================= + +; SLR controls whether special pseudo-ops such as ".printx" and ".accept", +; and complex macros are used at assembly time. For SLR Systems or compatible +; macro assemblers, set this equate to true. + +SLR equ false + +; RCPTYPE is the final character of the RCP name in the H(elp) command, +; and determines the order in which commands are added. + + if SLR +Y equ true +N equ false +H equ 'H' +F equ 'F' + +.accept 'Enter "F" for floppy, "H" for fixed disk version: ',rcptype + else +rcptype equ 'H' ; Floppy disk version + endif ; SLR + +; ZRL controls whether named common labels are used in the code. To create +; absolute code, modify RCPBASE.LIB to reflect your system's addresses, set +; ZRL to false and instruct your assembler to generate absolute code (either +; a .COM or .HEX file). +; +; To create a relocatable file for use with Bridger Mitchell's JetLDR or +; Joe Wright's NZCOM, you must use an assembler which recognizes named +; common blocks. If your assembler has this capability, set ZRL to true +; and instruct the assembler to generate relocatable (.REL) code. + +ZRL equ true + +;============================================================================= +; +; M A C R O S +; +;============================================================================= + +; SLR assemblers allow selection of options during assembly. + + if SLR + +select macro option default comment + .accept comment,option ; Prompt user for selection + endm + + .printx + .printx Answer "Y" to include, "N" to exclude commands: + .printx + + else + +select macro option default comment +option equ default ; Use selections from file + endm + endif ; SLR + +;============================================================================= +; +; R E S I D E N T C O M M A N D S S E L E C T I O N +; +;============================================================================= + +; Commands to include in the resident command package + +; There are some interrelations between a number of the possible resident +; commands. Some are so close (LIST and TYPE) that a single equate controls +; both functions. Others like DIR and ERA share code (both display file +; listings). It is efficient to select or deselect them together. + +; If you are using an SLR or compatible assembler, you will be prompted +; at assembly time for command selections. Otherwise, select commands to +; be included by setting each SELECT macro below to yes or no. + +select CLEDON no 'CLED - command line editor shell? ' +select CLSON yes 'CLS - clear screen? ' +select RESON yes 'R - reset disk system? ' +select SPACEON yes 'SP - show space remaining on disk? ' + + if RCPTYPE='F' ; Assemble in floppy order +select DIRON yes 'D - disk directory? ' +select ERAON yes 'ERA - erase files? ' +select ECHOON yes 'ECHO - send text to screen or printer? ' +select RENON yes 'REN - rename files? ' +select CPON yes 'CP - copy file? ' +select LTON yes 'TYPE - display file on console? ' +select POKEON yes 'POKE - set memory? ' +select PEEKON yes 'PEEK - view memory? ' + else ; Assemble in fixed-disk order +select ECHOON yes 'ECHO - send text to screen or printer? ' +select POKEON yes 'POKE - set memory? ' +select PEEKON yes 'PEEK - view memory? ' +select QUIETON yes 'Q - set or clear quiet status? ' +select DIRON yes 'D - disk directory? ' +select ERAON no 'ERA - erase files? ' +select RENON no 'REN - rename files? ' +select CPON no 'CP - copy file? ' +select LTON yes 'TYPE - display file on console? ' + endif ; RCPTYPE='F' + + if LTON +select LISTON no 'LIST - list to printer? ' + else ; allowed only if TYPE is enabled +LISTON equ no + endif + + if RCPTYPE='F' ; Assemble in floppy order +select QUIETON yes 'Q - set or clear quiet status? ' + endif + +select PROTON no 'PROT - set file attributes? ' +select NOTEON no 'NOTE - command-line comment? ' +select REGON no 'REG - set and display user registers? ' +select WHLON no 'WHL - set or clear wheel status? ' +select PORTON no 'PORT - view and set I/O ports? ' + +;============================================================================= +; +; W H E E L P R O T E C T I O N +; +;============================================================================= + +; To prevent unauthorized users from performing certain dangerous or sensitive +; operations on the computer, ZCPR34 provides the capability of disabling the +; operation of certain commands when the wheel byte is off. In ZCPR30, an +; attempt to use one of these wheel-protected commands when the wheel byte was +; off resulted in an error message. In ZCPR34 things work differently. In the +; same situation, the command simply disappears. In this way a transient +; program or extended command processor function can take over and deal with +; the attempt to use the command in a much more flexible way. +; +; Wheel-protected commands in ZCPR30 had extra code to intercept the function +; and disable it. In ZCPR34, wheel protection is enabled and disabled in a +; different way. To wheel-protect a command the high bit of the first +; character in the command name is set. The command table scanner in ZCPR34 +; will not recognize these commands when the wheel byte is off. Since the same +; command scanner is used to scan the commands in the RCP and FCP (flow control +; package), commands there can be wheel protected in the same way. For skilled +; computer operators it is very easy to use a debugger, file patcher, or disk +; utility to enable and disable wheel protection without having to reassemble +; the CPR, RCP, or FCP. +; +; Because of the way the command scanner works, once wheel protection is +; implemented in the CCP, there is no further code penalty in protecting RCP +; commands. Therefore, we recommend protecting all possibly sensitive +; commands or none. + +wcled equ no ; CLED +wcp equ yes ; CP +wdir equ no ; DIR +wera equ yes ; ERA +wlist equ yes ; LIST +wpeek equ yes ; PEEK +wpoke equ yes ; POKE +wport equ yes ; PORT +wprot equ yes ; PROT +wquiet equ no ; QUIET +wreg equ yes ; REG +wren equ yes ; REN +wspop equ yes ; SPOP +wtype equ yes ; TYPE +whrc equ no ; H + +wheel defl wcp or wdir or wera or wlist or wpeek or wpoke or wport +wheel defl wheel or wprot or wreg or wren or wtype or whrc + +;============================================================================= +; +; C O M M A N D O P T I O N S +; +;============================================================================= + +; Options affecting several commands + +; DSORT includes code to sort file entries for the D, TYPE, LIST, ERA, +; and PROT commands. + +dsort equ yes + +; LEFTRIGHT affects the COPY and RENAME commands. If yes, add code to allow +; CP and REN operations work from left to right, and CP to accept a +; single filespec as its source. +; Examples: +; B0>ren b1:old.nam new.nam Renames B1:old.nam to B1:new.nam +; B0>cp a15:that.fil Copies A15:that.fil to B0: +; B0>cp source.fil dest.fil Copies source.fil to dest.fil + +leftright equ yes + +; If LEFTRIGHT is yes, setting TESTEQ to yes also allows CP and REN commands +; to be processed from right to left if they contain an equal sign ("="). +; Costs 11 bytes. +; Example: +; B0>cp dest.fil=source.fil Copies source.fil to dest.fil + +testeq equ yes + +;----------------------------------------------------------------------------- + +; 'CLS' command + +; This command clears the console screen. It can either use a fixed string +; (for shorter code if the same terminal is always used) or use the TCAP +; entry for automatic adaptation to any terminal. + +clstcap equ true ; Use TCAP for clear-screen string + +; If CLSTCAP is not enabled, then the string below must be provided and +; it must end with a character with the high bit set + + if not clstcap +clsstr macro + db 'Z'-'@'+80h ; Control-Z to clear screen + endm + endif ;not clstcap + +typecls equ true ; Clear between screens when typing files + +;----------------------------------------------------------------------------- + +; 'CP' command + +; This command copies a single file. The destination file can have a different +; name from the source file. Set the size of the memory buffer in K below (do +; not change the second definition). The copy buffer is placed at the top of +; the TPA, where it will generally not interfere with subsequent use of the GO +; command to rerun a program loaded in the TPA. + +cpblocks defl 16 ; Size of copy buffer in K + +cpblocks defl cpblocks * 8 ; Convert to number of records + +; If you will be using a BDOS or RSX which supports new functions 102 (Get +; Stamp) and 103 (Set Stamp), set STPCALL to true, and datestamps of files +; will be preserved across copies. The "last access" stamps of both copies +; are set to the time the copy took place if access stamping is enabled. + +stpcall equ true ; Copy file stamps with functions 102/3 + +;----------------------------------------------------------------------------- + +; 'DIR' command + +; This command displays a sorted listing of the files in a directory. The +; file name buffer is allocated at the top of the TPA so as not to modify +; any program loaded in low memory. Consequently, the size of the buffer +; must be set in advance. + + +maxdirs equ 256 ; Maximum number of names in directory buffer + +nosys equ yes ; suppress 'A' and 'S' options if wheel off + +slashchk equ yes ; allow "DIR /S" or "DIR /A" formats (costs + ; about 12 bytes) + +allflag equ 'A' ; Option character for showing + ; all file (both SYS and DIR) +sysflag equ 'S' ; Option character for showing + ; SYS files only +sortnt equ yes ; Sort file by name then type +wide equ yes ; Make display for 80-column screen +fence equ '|' ; Character to use as fence between columns + ; ..if WIDE is false. + +;----------------------------------------------------------------------------- + +; 'ECHO' command + +; This command sends characters to the console or list device. If ECHOLST is +; enabled, then code is included for sending characters to the LST device. +; Characters normally go the the console device and are normally upper or lower +; case depending on the setting of UPCASE. The command line can have special +; command sequences to toggle the case of the output and to change the +; destination between the console and printer. Any other character following +; the command character will be sent as is. For the normal setting of the +; equates below (upcase/yes, cmdchar/%, prtchar/P, crtchar/C, lcasech/>, +; and ucasech/<) an example command line would be: +; A0:ROOT>ECHO T%>HIS IS A TEST^M^J^IDONE%PONE, TWO, %' ; Character after CASECHAR that toggles + ; subsequent output to lower case + +; ---------------------------------------------------------------------------- + +; 'H' command + +; This command displays a list of the resident commands implemented in the +; system. FCP, CPR, and RCP commands can be displayed. The basic command is +; not optional; it is always included. + +listcpr equ yes ; Include list of CPR-resident commands in + ; display (highly recommended) + +listfcp equ yes ; Include list of FCP-resident commands in + ; display (recommended if there is room) + +noshow equ yes ; Suppress listing commands that are wheel- + ; restricted unless wheel byte is set (i.e., + ; don't show commands that won't run) + +cmdsline equ 5 ; Number of commands on each line of display + +cmdspace equ 8 ; Space in display for each command name + +;----------------------------------------------------------------------------- + +; 'LIST' and 'TYPE' commands + +pgdflt equ yes ; Default to paging of console output + +pgdflg equ 'P' ; Character to toggle paging status + +;---------------------------------------- + +; 'PEEK' command + +peekhdr equ yes ; Label columns in PEEK display + +peekbdr equ yes ; Include line of hyphens under labels + +peekchk equ yes ; If yes, the ending address will be tested to + ; prevent overflow past FFFF (costs 5 bytes) + +peeklen equ 127 ; Display 1 record (128 bytes) as default. + ; PEEK displays in increments of 16 bytes. + ; You may set PEEKLEN for 0 to 65525. + +;---------------------------------------- + +; 'S' command + +; Print disk space with other commands (recommended) + +dirsp equ true ; Show space after DIR +erasp equ true ; Show space after ERA +cpsp equ true ; Show space after CP +resetsp equ true ; Show space after R + +;---------------------------------------- + +; 'WHL' command + +whlquiet equ false ; If true, don't report wheel state with + ; .."WHL password" command (costs 1 byte) + +wpass macro + db 'SYSTEM ' + endm + +;---------------------------------------- + +; 'CLED' command +; +; Command line editing takes place in the line buffer. It does not take up +; room in the RCP. The editor will handle a 255 byte line. Although that +; is larger than the maximum command line size, it allows you to edit a long +; line without deleting something first. The history is kept inside the RCP, +; so its size will depend upon how many other RCP commands are enabled. +; HISTSIZE should be at least as large as the multiple command line buffer +; (204 bytes). + +lbufwid equ 255 ; Line buffer size, byte +histsize equ 204 ; History buffer size, word + +; CLERA controls whether to null the internal history when CLED is exited, or +; to leave it for the next run. Secure systems will want to set it to YES. + +clera equ no ; Erase history on terminating shell + +; CLMIN sets the cutoff for discarding short lines from history recording. +; A setting of 3 would discard a command such as DIR. + +clmin equ 5 ; Discard lines which do not exceed this + +clins equ yes ; Initial insert state ON +clsav equ yes ; Initial history recording ON + +; The appearance of the command line prompt may include the system time if a +; DateStamper or ZS/ZDDOS clock is implemented. The DU:DIR separator character +; can be changed to indicate that CLED is running. + +dudir_sep equ '/' ; DU:DIR separator char +puser0 equ no ; Print user #0 in prompt +systime equ yes ; Include system time in prompt + + if systime +civtim equ yes ; Yes for 12-hr civil time vs. 24-hr military +timesep equ '.' ; Separator between hours and minutes + endif + +; The editor must use EREOL. The VLIB routine to send the EREOL sequence +; to the terminal needs to test for a video delay and the presence of the "/" +; quote character. Most terminals can just print the TCAP sequence as is. If +; that doesn't work, set ERLQUICK to NO. If you are using only one terminal, +; it will be most efficient to set ERLTCAP to NO and use the CLR_EOL macro. + +erltcap equ yes ; Use TCAP routine vs macro + + if erltcap +erlquick equ yes ; Use simplified TCAP routine + else + +clr_eol macro ; Define terminal specific string + dc 1bh,'T' ; ...set high bit on last character + endm + + endif ;erltcap + +;----------------------------------------------------------------------------- + +; Command names + +; The CTABLE macro, which constructs the command dispatch table, includes a +; line for each command, the first parameter of which is the name of the +; command. These names may be changed if you wish. But make sure that you +; do not use a name that is longer than the maximum allowed length. If you +; do, the name will be truncated and a nonfatal error will occur during +; assembly. DO NOT CHANGE ANY PARAMETER OTHER THAN THE COMMAND NAME. Lower +; case letters will be converted to upper case. The macro COMMAND is defined +; in Z34MAC.LIB. + +cmdsize equ 4 ; Length of each command name + +cmdtbl macro + command CLED, cledon, wcled, cled + command CLS, clson, false, cls + command CP, cpon, wcp, copy + command D, diron, wdir, dir + command ECHO, echoon, false, echo + command ERA, eraon, wera, era + command H, true, whrc, clist + command LIST, liston, wlist, list + command NOTE, noteon, false, note + command P, peekon, wpeek, peek + command POKE, pokeon, wpoke, poke + command PORT, porton, wport, port + command PROT, proton, wprot, att + command Q, quieton,wquiet, quset + command R, reson, false, reset + command REG, regon, wreg, register + command REN, renon, wren, ren + command SP, spaceon,false, space + command SPOP, cledon, wspop, shpop + command TYPE, lton, wtype, type + command WHL, whlon, false, whl + endm + +; END Z34RCP.LIB + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/z34rcp.lib.sav b/Source/BPBIOS/Z34RCP11/z34rcp.lib.sav new file mode 100644 index 00000000..a0272445 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/z34rcp.lib.sav @@ -0,0 +1,515 @@ + +; File: Z34RCP.LIB +; Description: Resident Command Package (RCP) for ZCPR34 +; Version: 1.4 +; Author: Carson Wilson +; Date: October 4, 1989 +; Changes: Added PUSER0 equate suggested by Rob Friefeld. +; +; Version: 1.3 +; Author: Carson Wilson +; Date: September 29, 1989 +; Changes: Added CLED (Command Line Editor Shell) command options +; and equates. +; +; Version: 1.2 +; Author: Carson Wilson +; Date: August 11, 1989 +; Changes: Removed several "quiet" conditional assembly equates as +; we are now sensing the QUIET flag instead. +; Changed default of WHLQUIET to false. +; +; Version: 1.1 +; Author: Carson Wilson +; Date: July 10, 1989 +; Changes: Changed 'S' command name to 'SP'. +; Removed wheel protection from 'Q' command and set QQUIET +; on by default. +; Changed command order. + +; Version: 1.0 +; Author: Carson Wilson +; Date: June 15, 1988 + +; This is the configuration file for the ZCPR Version 3.4 resident command +; package. You should read through this file and set the equates according +; to the features you want to implement. Since most systems have a limited +; amount of space for the resident command package, it will probably be +; impossible to include all features. + +; If you are using an SLR or equivalent assembler, you will be prompted +; at assembly time for which commands to include in the RCP. + +;============================================================================= +; +; A S S E M B L Y O P T I O N S +; +;============================================================================= + +; SLR controls whether special pseudo-ops such as ".printx" and ".accept", +; and complex macros are used at assembly time. For SLR Systems or compatible +; macro assemblers, set this equate to true. + +SLR equ true + +; RCPTYPE is the final character of the RCP name in the H(elp) command, +; and determines the order in which commands are added. + + if SLR +Y equ true +N equ false +H equ 'H' +F equ 'F' + +.accept 'Enter "F" for floppy, "H" for fixed disk version: ',rcptype + else +rcptype equ 'F' ; Floppy disk version + endif ; SLR + +; ZRL controls whether named common labels are used in the code. To create +; absolute code, modify RCPBASE.LIB to reflect your system's addresses, set +; ZRL to false and instruct your assembler to generate absolute code (either +; a .COM or .HEX file). +; +; To create a relocatable file for use with Bridger Mitchell's JetLDR or +; Joe Wright's NZCOM, you must use an assembler which recognizes named +; common blocks. If your assembler has this capability, set ZRL to true +; and instruct the assembler to generate relocatable (.REL) code. + +ZRL equ true + +;============================================================================= +; +; M A C R O S +; +;============================================================================= + +; SLR assemblers allow selection of options during assembly. + + if SLR + +select macro option default comment + .accept comment,option ; Prompt user for selection + endm + + .printx + .printx Answer "Y" to include, "N" to exclude commands: + .printx + + else + +select macro option default comment +option equ default ; Use selections from file + endm + endif ; SLR + +;============================================================================= +; +; R E S I D E N T C O M M A N D S S E L E C T I O N +; +;============================================================================= + +; Commands to include in the resident command package + +; There are some interrelations between a number of the possible resident +; commands. Some are so close (LIST and TYPE) that a single equate controls +; both functions. Others like DIR and ERA share code (both display file +; listings). It is efficient to select or deselect them together. + +; If you are using an SLR or compatible assembler, you will be prompted +; at assembly time for command selections. Otherwise, select commands to +; be included by setting each SELECT macro below to yes or no. + +select CLEDON no 'CLED - command line editor shell? ' +select CLSON yes 'CLS - clear screen? ' +select RESON yes 'R - reset disk system? ' +select SPACEON yes 'SP - show space remaining on disk? ' + + if RCPTYPE='F' ; Assemble in floppy order +select DIRON yes 'D - disk directory? ' +select ERAON yes 'ERA - erase files? ' +select ECHOON yes 'ECHO - send text to screen or printer? ' +select RENON yes 'REN - rename files? ' +select CPON yes 'CP - copy file? ' +select LTON yes 'TYPE - display file on console? ' +select POKEON yes 'POKE - set memory? ' +select PEEKON yes 'PEEK - view memory? ' + else ; Assemble in fixed-disk order +select ECHOON yes 'ECHO - send text to screen or printer? ' +select POKEON yes 'POKE - set memory? ' +select PEEKON yes 'PEEK - view memory? ' +select QUIETON yes 'Q - set or clear quiet status? ' +select DIRON yes 'D - disk directory? ' +select ERAON yes 'ERA - erase files? ' +select RENON yes 'REN - rename files? ' +select CPON yes 'CP - copy file? ' +select LTON yes 'TYPE - display file on console? ' + endif ; RCPTYPE='F' + + if LTON +select LISTON no 'LIST - list to printer? ' + else ; allowed only if TYPE is enabled +LISTON equ no + endif + + if RCPTYPE='F' ; Assemble in floppy order +select QUIETON yes 'Q - set or clear quiet status? ' + endif + +select PROTON no 'PROT - set file attributes? ' +select NOTEON no 'NOTE - command-line comment? ' +select REGON no 'REG - set and display user registers? ' +select WHLON no 'WHL - set or clear wheel status? ' +select PORTON no 'PORT - view and set I/O ports? ' + +;============================================================================= +; +; W H E E L P R O T E C T I O N +; +;============================================================================= + +; To prevent unauthorized users from performing certain dangerous or sensitive +; operations on the computer, ZCPR34 provides the capability of disabling the +; operation of certain commands when the wheel byte is off. In ZCPR30, an +; attempt to use one of these wheel-protected commands when the wheel byte was +; off resulted in an error message. In ZCPR34 things work differently. In the +; same situation, the command simply disappears. In this way a transient +; program or extended command processor function can take over and deal with +; the attempt to use the command in a much more flexible way. +; +; Wheel-protected commands in ZCPR30 had extra code to intercept the function +; and disable it. In ZCPR34, wheel protection is enabled and disabled in a +; different way. To wheel-protect a command the high bit of the first +; character in the command name is set. The command table scanner in ZCPR34 +; will not recognize these commands when the wheel byte is off. Since the same +; command scanner is used to scan the commands in the RCP and FCP (flow control +; package), commands there can be wheel protected in the same way. For skilled +; computer operators it is very easy to use a debugger, file patcher, or disk +; utility to enable and disable wheel protection without having to reassemble +; the CPR, RCP, or FCP. +; +; Because of the way the command scanner works, once wheel protection is +; implemented in the CCP, there is no further code penalty in protecting RCP +; commands. Therefore, we recommend protecting all possibly sensitive +; commands or none. + +wcled equ no ; CLED +wcp equ yes ; CP +wdir equ no ; DIR +wera equ yes ; ERA +wlist equ yes ; LIST +wpeek equ yes ; PEEK +wpoke equ yes ; POKE +wport equ yes ; PORT +wprot equ yes ; PROT +wquiet equ no ; QUIET +wreg equ yes ; REG +wren equ yes ; REN +wspop equ yes ; SPOP +wtype equ yes ; TYPE +whrc equ no ; H + +wheel defl wcp or wdir or wera or wlist or wpeek or wpoke or wport +wheel defl wheel or wprot or wreg or wren or wtype or whrc + +;============================================================================= +; +; C O M M A N D O P T I O N S +; +;============================================================================= + +; Options affecting several commands + +; DSORT includes code to sort file entries for the D, TYPE, LIST, ERA, +; and PROT commands. + +dsort equ yes + +; LEFTRIGHT affects the COPY and RENAME commands. If yes, add code to allow +; CP and REN operations work from left to right, and CP to accept a +; single filespec as its source. +; Examples: +; B0>ren b1:old.nam new.nam Renames B1:old.nam to B1:new.nam +; B0>cp a15:that.fil Copies A15:that.fil to B0: +; B0>cp source.fil dest.fil Copies source.fil to dest.fil + +leftright equ yes + +; If LEFTRIGHT is yes, setting TESTEQ to yes also allows CP and REN commands +; to be processed from right to left if they contain an equal sign ("="). +; Costs 11 bytes. +; Example: +; B0>cp dest.fil=source.fil Copies source.fil to dest.fil + +testeq equ yes + +;----------------------------------------------------------------------------- + +; 'CLS' command + +; This command clears the console screen. It can either use a fixed string +; (for shorter code if the same terminal is always used) or use the TCAP +; entry for automatic adaptation to any terminal. + +clstcap equ true ; Use TCAP for clear-screen string + +; If CLSTCAP is not enabled, then the string below must be provided and +; it must end with a character with the high bit set + + if not clstcap +clsstr macro + db 'Z'-'@'+80h ; Control-Z to clear screen + endm + endif ;not clstcap + +typecls equ true ; Clear between screens when typing files + +;----------------------------------------------------------------------------- + +; 'CP' command + +; This command copies a single file. The destination file can have a different +; name from the source file. Set the size of the memory buffer in K below (do +; not change the second definition). The copy buffer is placed at the top of +; the TPA, where it will generally not interfere with subsequent use of the GO +; command to rerun a program loaded in the TPA. + +cpblocks defl 16 ; Size of copy buffer in K + +cpblocks defl cpblocks * 8 ; Convert to number of records + +; If you will be using a BDOS or RSX which supports new functions 102 (Get +; Stamp) and 103 (Set Stamp), set STPCALL to true, and datestamps of files +; will be preserved across copies. The "last access" stamps of both copies +; are set to the time the copy took place if access stamping is enabled. + +stpcall equ true ; Copy file stamps with functions 102/3 + +;----------------------------------------------------------------------------- + +; 'DIR' command + +; This command displays a sorted listing of the files in a directory. The +; file name buffer is allocated at the top of the TPA so as not to modify +; any program loaded in low memory. Consequently, the size of the buffer +; must be set in advance. + + +maxdirs equ 256 ; Maximum number of names in directory buffer + +nosys equ yes ; suppress 'A' and 'S' options if wheel off + +slashchk equ yes ; allow "DIR /S" or "DIR /A" formats (costs + ; about 12 bytes) + +allflag equ 'A' ; Option character for showing + ; all file (both SYS and DIR) +sysflag equ 'S' ; Option character for showing + ; SYS files only +sortnt equ yes ; Sort file by name then type +wide equ yes ; Make display for 80-column screen +fence equ '|' ; Character to use as fence between columns + ; ..if WIDE is false. + +;----------------------------------------------------------------------------- + +; 'ECHO' command + +; This command sends characters to the console or list device. If ECHOLST is +; enabled, then code is included for sending characters to the LST device. +; Characters normally go the the console device and are normally upper or lower +; case depending on the setting of UPCASE. The command line can have special +; command sequences to toggle the case of the output and to change the +; destination between the console and printer. Any other character following +; the command character will be sent as is. For the normal setting of the +; equates below (upcase/yes, cmdchar/%, prtchar/P, crtchar/C, lcasech/>, +; and ucasech/<) an example command line would be: +; A0:ROOT>ECHO T%>HIS IS A TEST^M^J^IDONE%PONE, TWO, %' ; Character after CASECHAR that toggles + ; subsequent output to lower case + +; ---------------------------------------------------------------------------- + +; 'H' command + +; This command displays a list of the resident commands implemented in the +; system. FCP, CPR, and RCP commands can be displayed. The basic command is +; not optional; it is always included. + +listcpr equ yes ; Include list of CPR-resident commands in + ; display (highly recommended) + +listfcp equ yes ; Include list of FCP-resident commands in + ; display (recommended if there is room) + +noshow equ yes ; Suppress listing commands that are wheel- + ; restricted unless wheel byte is set (i.e., + ; don't show commands that won't run) + +cmdsline equ 5 ; Number of commands on each line of display + +cmdspace equ 8 ; Space in display for each command name + +;----------------------------------------------------------------------------- + +; 'LIST' and 'TYPE' commands + +pgdflt equ yes ; Default to paging of console output + +pgdflg equ 'P' ; Character to toggle paging status + +;---------------------------------------- + +; 'PEEK' command + +peekhdr equ yes ; Label columns in PEEK display + +peekbdr equ yes ; Include line of hyphens under labels + +peekchk equ yes ; If yes, the ending address will be tested to + ; prevent overflow past FFFF (costs 5 bytes) + +peeklen equ 127 ; Display 1 record (128 bytes) as default. + ; PEEK displays in increments of 16 bytes. + ; You may set PEEKLEN for 0 to 65525. + +;---------------------------------------- + +; 'S' command + +; Print disk space with other commands (recommended) + +dirsp equ true ; Show space after DIR +erasp equ true ; Show space after ERA +cpsp equ true ; Show space after CP +resetsp equ true ; Show space after R + +;---------------------------------------- + +; 'WHL' command + +whlquiet equ false ; If true, don't report wheel state with + ; .."WHL password" command (costs 1 byte) + +wpass macro + db 'SYSTEM ' + endm + +;---------------------------------------- + +; 'CLED' command +; +; Command line editing takes place in the line buffer. It does not take up +; room in the RCP. The editor will handle a 255 byte line. Although that +; is larger than the maximum command line size, it allows you to edit a long +; line without deleting something first. The history is kept inside the RCP, +; so its size will depend upon how many other RCP commands are enabled. +; HISTSIZE should be at least as large as the multiple command line buffer +; (204 bytes). + +lbufwid equ 255 ; Line buffer size, byte +histsize equ 204 ; History buffer size, word + +; CLERA controls whether to null the internal history when CLED is exited, or +; to leave it for the next run. Secure systems will want to set it to YES. + +clera equ no ; Erase history on terminating shell + +; CLMIN sets the cutoff for discarding short lines from history recording. +; A setting of 3 would discard a command such as DIR. + +clmin equ 5 ; Discard lines which do not exceed this + +clins equ yes ; Initial insert state ON +clsav equ yes ; Initial history recording ON + +; The appearance of the command line prompt may include the system time if a +; DateStamper or ZS/ZDDOS clock is implemented. The DU:DIR separator character +; can be changed to indicate that CLED is running. + +dudir_sep equ '/' ; DU:DIR separator char +puser0 equ no ; Print user #0 in prompt +systime equ yes ; Include system time in prompt + + if systime +civtim equ yes ; Yes for 12-hr civil time vs. 24-hr military +timesep equ '.' ; Separator between hours and minutes + endif + +; The editor must use EREOL. The VLIB routine to send the EREOL sequence +; to the terminal needs to test for a video delay and the presence of the "/" +; quote character. Most terminals can just print the TCAP sequence as is. If +; that doesn't work, set ERLQUICK to NO. If you are using only one terminal, +; it will be most efficient to set ERLTCAP to NO and use the CLR_EOL macro. + +erltcap equ yes ; Use TCAP routine vs macro + + if erltcap +erlquick equ yes ; Use simplified TCAP routine + else + +clr_eol macro ; Define terminal specific string + dc 1bh,'T' ; ...set high bit on last character + endm + + endif ;erltcap + +;----------------------------------------------------------------------------- + +; Command names + +; The CTABLE macro, which constructs the command dispatch table, includes a +; line for each command, the first parameter of which is the name of the +; command. These names may be changed if you wish. But make sure that you +; do not use a name that is longer than the maximum allowed length. If you +; do, the name will be truncated and a nonfatal error will occur during +; assembly. DO NOT CHANGE ANY PARAMETER OTHER THAN THE COMMAND NAME. Lower +; case letters will be converted to upper case. The macro COMMAND is defined +; in Z34MAC.LIB. + +cmdsize equ 4 ; Length of each command name + +cmdtbl macro + command CLED, cledon, wcled, cled + command CLS, clson, false, cls + command CP, cpon, wcp, copy + command D, diron, wdir, dir + command ECHO, echoon, false, echo + command ERA, eraon, wera, era + command H, true, whrc, clist + command LIST, liston, wlist, list + command NOTE, noteon, false, note + command P, peekon, wpeek, peek + command POKE, pokeon, wpoke, poke + command PORT, porton, wport, port + command PROT, proton, wprot, att + command Q, quieton,wquiet, quset + command R, reson, false, reset + command REG, regon, wreg, register + command REN, renon, wren, ren + command SP, spaceon,false, space + command SPOP, cledon, wspop, shpop + command TYPE, lton, wtype, type + command WHL, whlon, false, whl + endm + +; END Z34RCP.LIB + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/z34rcp11.doc b/Source/BPBIOS/Z34RCP11/z34rcp11.doc new file mode 100644 index 00000000..92195c5d --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/z34rcp11.doc @@ -0,0 +1,429 @@ + + ZCPR Version 3.4 Resident Command Package + Source Code + + Revised Documentation for Version 1.1 + October 7, 1989 by Carson Wilson + + Version 1.0 Documentation + June 17, 1988 by Carson Wilson + + + Contents + -------- + + 1. Files in this Library. + + 2. Purpose of the Resident Command Package. + + 2.1. "Resident" vs. "Transient" Commands. + + 2.2. The Z-System Resident Command Package. + + 2.2.1. Design Philosophy of the Z34 RCP. + + 3. Structure and Operation of the RCP Segment. + + 4. Generating an RCP for your System. + + 4.1. The Z-Relocatable Approach. + + 4.1.1. NZCOM and JetLDR. + 4.1.2. Z-Relocatable (ZRL) Code + + 4.2. The Assembly Approach. + + 4.2.1. Note on Assemblers. + 4.2.2. Assembly to Absolute Code. + 4.2.3. Assembly to Z-Relocatable Code. + 4.2.4. Adding Custom Commands to Z34RCP. + + 5. Legal Use of These Files. + + + +1. Files in this Library. + +This library of files contains the assembler source code and +accompanying documentation for the Z-System Resident Command +Package (RCP). To use Z34RCP you must be running ZCPR versions +3.3 or above. For pre-assembled Z-Relocatable (ZRL) RCPs, see +RCPZRL11.LBR. Z34RCP11.LBR consists of the following files +(uncompress with UNCR.COM, QL.COM or LBRE.COM): + +CLEDINST.CZM - Installer for CLED command. +CLEDSAVE.CZM - Command file utility for CLED. +RCPCLED .DZC - Documentation on CLED. +RCPBASE .LZB - Used to assemble an absolute address RCP. +RCPCMD .LZB - Used to include custom commands in the RCP. +RCP?????.LZB - Code modules for the standard RCP commands. +SYSDEF .LZB - Commonly used equates. +Z34CMN .LZB - Used to assemble Z-Relocatable RCP's. +Z34DEFN .LZB - Offsets in the ZCPR 3.4 code. +Z34MAC .LZB - Assembler macros. +Z34RCP11.DZC - This file. +Z34RCP11.FOR - Short description of the library. +Z34RCP11.HZP - Help file for the RCP, gives command syntax and + examples. +Z34RCP .LZB - Used to select features when assembling the RCP. +Z34RCP11.NZW - Changes since Z34RCP10.LBR. Intended for former + RCP users, this will be only of passing interest + to new users. +Z34RCP11.ZZ0 - Main file for assembling the RCP. +ZSYSTEM .AZ - General information about Z System. + + +2. Purpose of the Resident Command Package. + + 2.1. "Resident" vs. "Transient" Commands. + +Most of the commands used under CP/M and Z-System are "transient" +commands. The machine code for these commands (or "programs") is +stored in "command files," whose file type is normally .COM. +Thus, the machine code for WordStar is stored in a file called +"WS.COM". + +To run WordStar, the user issues the command "WS" at the command +line, the operating system loads WS.COM from the disk drive to +memory, and WS.COM takes over control of the computer. If the +user then exits WordStar and issues another command, the +operating system can load another command file from the disk, +reusing the area of memory previously occupied by WordStar. + +Recycling memory in this way makes personal computers quite +versatile, as the number of commands available to the user is +only limited to the number of command files stored on disk. +There are some drawbacks to this approach, however. Although +disk-based commands conserve memory by recycling it, they must be +loaded from disk each time they operate. This creates its own +costs in terms of speed of execution, disk storage space, and +memory reallocation. + +The most obvious disadvantage of loading a file from disk each +time a command is issued is that it takes time to find and load +disk files. Especially with floppy diskettes, the amount of time +which elapses after the command is issued and before the command +executes is significant. A second drawback to disk-based +commands is that commands will not execute unless their machine +code is available on the disk system. This means that the user +must know what command files are available on disk before issuing +commands. The final disadvantage of disk file based commands is +that some commands (e.g., SAVE) require that the area of memory +normally overwritten by command files (known as the "Transient +Program Area" or TPA) be preserved. + +In answer to these difficulties, the creators of CP/M designed +some of its commands as "built-in" commands. The DIR, REN, SAVE, +ERA, USER, and TYPE commands of CP/M 2.2 remain in memory and can +be issued without loading machine code files from disk. Thus, +the DIR command provides a disk directory regardless of whether +the command file "DIR.COM" is present on disk, and the SAVE +command saves the contents of memory at 100 hex to a disk file. +Residing permanently in memory, these commands are simple in +order to leave memory for disk-based commands to load in. + + 2.2. The Z-System Resident Command Package. + +The creators of Z-System have followed the resident command +philosophy of CP/M, extending and enhancing the capabilities of +resident commands with the Z-System Resident Command Package. +Under CP/M 2.2, resident commands are tightly integrated with the +operating system. The user is therefore limited to a fixed set +of resident commands. The code in the Z-System Resident Command +Package is much more loosely linked with the operating system. + +The RCP occupies a section of protected memory, but how that +memory is used is only very loosely constrained. The Z-System +resident command package may be tailored for a wide variety of +installations, and easily altered within a single installation. + + 2.2.1. Design Philosophy of the Z34 RCP. + +Though any set of commands which will fit in the RCP memory area +can be loaded as an RCP, a standard set of Z-System RCP commands +have evolved over the years. Distributed as the standard +Z-System RCP, this set of commands consists of universal, +single-purpose commands modeled after the resident command set of +CP/M 2.2, such as DIR, REN, and ERA. + +Keeping these commands simple both conserves memory and allows +their use in custom command scripts. None of the standard RCP +commands require interaction on the part of the user--they +perform a single function and quit. Aliases or other command +scripts which build complex commands from simple RCP commands +therefore retain complete control over the user interface. + +However, the standard RCP forms only one of many alternative uses +to which the Z-System RCP memory segment may be put. Since the +RCP memory is protected, it can be used by background programs +which continue to function even while other applications have +been loaded into the transient program area. Several such +programs, including key-redefiners, screen-trapping programs, +background numeric calculators, program assembly environments, +and memory utilities have been developed. + +Until Z34RCP, all but the most advanced users had to surrender +their entire RCP to use the custom commands, even if the commands +needed far less than the available RCP space. Thus, to use a +key-redefinition RCP, one was forced to give up all of his other +RCP commands. Z34RCP solves this problem by acting as a base to +which programmers may easily add code. By modifying the standard +file RCPCMD.LIB, programmers can implement and distribute custom +RCP commands which may be installed by those with only basic +knowledge of assembly language. See "Adding Custom Commands," +below, for details. + + +3. Structure and Operation of the RCP Segment. + +Typically, the Resident Command Package occupies 2 kilobytes of +memory, protected above the system's Basic Input Output System +(BIOS). Its internal structure is very simple, consisting of the +five byte string "Z3RCP", a table of command names and their +addresses within the RCP, and the machine code of the commands +themselves. This means that an RCP of a given length can contain +any number of resident commands which will fit in 2k, from one +large program to many shorter programs. + +Further, since the RCP is independent of the rest of the +operating system, the resident commands may be changed at will. +On system startup, a default RCP is loaded into memory. +Thereafter the RCP's commands remain active until the system is +turned off or another RCP is loaded. + +Using loader programs to load the RCP to memory allows another +important ability--it can exchange RCPs during a single session. +By allowing users to change their RCPs "on-the-fly," loader +programs provide some of the benefits of transient commands even +while preserving the benefits of resident commands. In fact, +even what portion of memory is allocated to the RCP can be +altered in this way by advanced users! (see the Z-System User's +Manual for more on this). + + +4. Generating an RCP for your System. + +Since Z80 computers come with a wide variety of operating system +environments, it is impossible for a single segment of machine +code function as a universal RCP. Due to differences in +operating system and hardware design, the size and location of +memory available to the Z-System RCP vary widely. Until +Z34RCP, this meant that each type of Z80 computer required an RCP +segment assembled specifically for that computer. + +Now, however, there are two basic means by which you may generate +an RCP for your system. The simplest route is to load already- +assembled Z-Relocatable code modules. This technique is +recommended for beginners and those with little experience in +assembly language. See RCPZRL11.LBR for pre-assembled RCP +modules. For more experienced users with an interest in +customizing or their RCPs or adding their own commands, custom +assembly of the files in this library is also an option. + + 4.1. The Z-Relocatable Approach. + + 4.1.1. NZCOM and JetLDR. + +LDR.COM, the familiar Z-System segment loader first introduced by +Rick Conn as a means of loading the various segments of the ZCPR +environment from disk to memory, required that system segments be +assembled to run only at system-specific addresses. For example, +if the system's RCP started at FA00 hex, only an RCP assembled to +run at that starting address would function on the system. +Therefore, separate SYS.RCP files were needed for each computer +installation. + +Thanks to several advanced loader utilities, namely NZCOM by Joe +Wright and JetLDR and Z3PLUS by Bridger Mitchell, custom- +assembling an RCP for a given target machine is no longer +necessary. These three programs can load specially assembled RCP +segments (known as Z-ReLocatable or "ZRL" segments) to any area +of memory. Z-System users now need only obtain previously +assembled RCP segments, much as they need only obtain assembled +.COM or .OBJ files to run transient programs. + +Using information in the system's environmental descriptor, +advanced loaders load pre-assembled segments to the proper +addresses in any system. The only remaining constraint is the +amount of memory allocated to the RCP. While the standard RCP +size is 2 kilobytes, many Z-System installations vary +considerably from this standard, so it is still necessary to +ensure that a ZRL segment does not exceed available RCP memory. +Fortunately, this is taken care of automatically by the advanced +loader programs. + +Using NZCOM or JetLDR, it is possible to load an RCP directly +from RCPZRL11.LBR. See NZCOM or JetLDR documentation for full +instructions on how to load Z-Relocatable RCPs with these +programs. + + 4.1.2. Z-Relocatable (ZRL) Code + +The technique used by NZCOM and JetLDR to load a single RCP to +any address in memory involves relocatable code. Relocatable +code has been used for years by the "linker" programs available +to assembly language programmers. Since only portions of a given +Z80 machine code routine are specific to one memory address, it +is possible to create "relocatable" files of Z80 routines (known +by their file type .REL) containing all but the address-specific +codes. These files can then be very quickly relocated (or +"linked") by a linker program to operate at any memory address. + +Z-Relocatable code is simply a more sophisticated form of +relocatable code. ZRL files contain separately labeled segments, +known as "named common blocks." Where normal relocatable code is +all linked to operate at one address at a time by the linker +program, named common blocks allow the linker to distinguish +between different sections of the file and to link each "block" +to operate at a different address. + +This represents a major step forwards in user convenience, and +will certainly lead to more innovative work using the ZCPR system +segments. There is a minor drawback, however, inherent in the +form of assembly required by NZCOM and JetLDR. Before the advent +of these two programs, system segments were equivalent to .COM +files which were assembled to operate at addresses other than the +standard 100 hex. The ability to load a single system segment to +any address depends partially on the form of the assembled system +segment itself. + +While public domain assemblers will produce absolute object +files, more sophisticated assemblers, such as SLR's Z80ASM (a +bargain at $50) are required to assemble code with named common +addresses. NZCOM and JetLDR thus greatly reduce the time needed +by most users to install Z-System segments, but those wishing to +assemble Z-Relocatable segments will probably need a commercial +assembler. + + 4.2. The Assembly Approach. + +Those with knowledge of assembly language may opt to assemble +their own custom RCPs, including only the commands and options +which match their exact requirements. Z34RCP.LBR contains the +complete source to the ZCPR 3.4 RCP. The file Z34RCP.Z80 +contains include statements to combine the various library files +during assembly. The file Z34RCP.LIB contains all of the equates +controlling assembly time options, and should be edited before +assembling Z34RCP.Z80. + + 4.2.1. Note on Assemblers. + +The development work on Z34RCP was done exclusively with the SLR +Systems line of Z80 assemblers. Hence, some of the source code +is unavoidably specific to SLR and compatible assemblers. Those +using incompatible assemblers such as public-domain Z80 +assemblers should set the SLR equate in Z34RCP.LIB to FALSE. +This removes most SLR-specific pseudo-ops, but some additional +editing of the source files may be required for error-free +assembly. Unless your assembler can handle named common blocks, +you will also want to set the ZRL equate in Z34RCP.LIB to FALSE. + + 4.2.2. Assembly to Absolute Code. + +If you are assembling a custom RCP for use at one memory address +only, or if your assembler can't handle named common blocks, you +can set the ZRL equate in Z34RCP.LIB to FALSE, and instruct your +assembler to produce absolute code (either a .HEX or a .COM file). + +Before generating absolute code you must set addresses in the +file RCPBASE.LIB for the particular installation. Once you have +set these addresses, you may proceed to assemble Z34RCP.Z80 to +either Z34RCP.COM or Z34RCP.HEX. If your assembler generated a +.HEX file, use LOAD.COM to convert Z34RCP.HEX to Z34RCP.COM. + +If you are running a version of ZCPR prior to 3.4, you must now +alter two bytes in your Z-System environment segment to point to +your system's CCP address. The bytes at offset 39 and 40 hex +(formerly containing "width of printer 2" and "lines on printer +2") should contain a 2-byte address of your system's CCP, most +significant byte last. + +Z34RCP.COM may now be renamed to SYS.RCP and loaded with the Z- +System loader program LDR.COM or Bridger Mitchell's JetLDR. + + 4.2.3. Assembly to Z-Relocatable Code. + +Unlike absolute RCP segments, Z-Relocatable segments may be +loaded to ANY address, provided enough RCP space to contain them +is available. To assemble a Z-Relocatable RCP, you will need an +assembler which can handle named common blocks (see 4.2.1 above). +Set the ZRL equate in Z34RCP.LIB to TRUE, and instruct your +assembler to produce a relocatable (.REL) file. The resulting +Z34RCP.REL may then be renamed to Z34RCP.ZRL and loaded with +NZCOM, Z3PLUS, JetLDR. + + 4.2.4. Adding Custom Commands to Z34RCP. + +As mentioned above, I have designed Z34RCP as a base to which +custom RCP commands may readily be added. All code and command +names for custom commands may be included in RCPCMD.LIB. +Thereafter, whenever the RCP is assembled the commands added to +RCPCMD.LIB will be included automatically. To remove the custom +commands, just replace the modified RCPCMD.LIB with an unmodified +copy. See RCPCMD.LIB for detailed instructions on how to add +code and command names. + +RCPCMD.LIB also facilitates distribution of custom commands to be +included by others in their RCPs. Simply distribute a copy of +your modified RCPCMD.LIB along with instructions on how to use +your custom commands. Users may then easily include the new +commands by substituting your modified RCPCMD.LIB for their +original copy and reassembling their RCPs. + +Of course, users of NZCOM, Z3PLUS, or JetLDR may simply trade +custom RCPs in Z-Relocatable form and dispense with the need for +any assembly work on the part of the user. All three of these +advanced loaders are available from Z Systems Associates (see +below). + + +5. Legal Use of These Files. + +Z34RCP is copyright 1989 by Z Systems Associates (ZSA), all +rights reserved. Any commercial use of Z34RCP, defined as any +situation where the duplicator recieves revenue by duplicating or +distributing Z34RCP by itself or in conjunction with any hardware +or software product, is expressly prohibited unless authorized in +writing by ZSA. + +Except for the file RCPCMD.LIB (see 4.2.4 above), you may +redistribute Z34RCP.LBR in its present form only. I encourage +you to explore the source code, suggest improvements, and +document errors. However, please obtain permission from Z +Systems Associates before redistributing any of these files in +altered form. This will prevent confusion by allowing your work +to be coordinated with the efforts of others. + +The Z Systems Associates are: + + Sage Microsystems East + Selling & Supporting the Best in 8-Bit Software + 1435 Centre St., Newton Centre, MA 02159-2469 + Voice: 617/965-3552 (9:00am - 11:30pm) + Modem: 617/965-7259 (password = DDT)(MABOS on PC-Pursuit) + +and: + + Plu*Perfect Systems + "==World-Class Software" + 410 23rd Street, Santa Monica, CA 90402 + Voice: 213/393-6105 (evenings) + Modem: 213/670-9465 (leave message for "Bridger Mitchell") + +For more information on Z System, check at the above bulletin +board systems, or consult The Computer Journal for excellent +articles on ZCPR and CP/M. The Computer Journal is published six +times a year by Publishing Consultants, 190 Sullivan Crossroad, +Columbia Falls, MT 59912, phone 406/257-9119. An issue averages +forty pages with few ads. Subscription rates are $16 for one +year (6 issues), or $28 for two years (12 issues) in the U.S., +$22 for one year Canada and Mexico, and $24 (surface) for one +year in other countries. + + +Carson Wilson is the operator of: + + Antelope Freeway Remote Access System + Chicago, 312-764-5162 + 300-1200-2400 Baud, 24 Hours + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/z34rcp11.for b/Source/BPBIOS/Z34RCP11/z34rcp11.for new file mode 100644 index 00000000..c60e1910 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/z34rcp11.for @@ -0,0 +1,7 @@ +Z-System Resident Command Package (RCP) in source code form, version +1.1. Complete Z80 source code to the RCP plus documentation, for +ZCPR versions 3.3 and above. Adds several exciting new commands and +features and fixes various bugs in the previous release. See +companion file RCPZRL11.LBR for pre-compiled RCP modules which may +be loaded with NZ-COM, Z3PLUS, or JetLDR. + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/z34rcp11.hlp b/Source/BPBIOS/Z34RCP11/z34rcp11.hlp new file mode 100644 index 00000000..991d5d71 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/z34rcp11.hlp @@ -0,0 +1,390 @@ + The RCP Commands + CLED/SPOP Command Line Editor  + CLS Clear the CRT screen  + CP Copy a File  + D Display Directory  + ECHO Echo Command Tail  + ERA Erase Files  + H Display Available Commands  + LIST/TYPE Display a File on Printer/CRT + NOTE Comment  + P/POKE Display/Alter Memory  + PORT Input/Output to system ports  + PROT Protect Files  + Q Alter or Query the Quiet Flag + R Reset Disk  + REG ZCPR3 Register Display/Alter  + REN Rename a File  + SP Display Disk Space Remaining  + WHL Alter or Query Wheel Byte  +:Thå RCÐ Commands + + + Thå   standarä ZCPR³ Systeí Residenô Commanä  Packagå  (RCP© +distributeä  witè thå ZCPR³ systeí containó á numbeò oæ  residenô +commandó  whicè  maù bå enableä (dependinç oî systeí  memorù  anä +securitù  characteristics©  aô thå discretioî oæ  thå  installer® +Usagå oæ thå RCÐ commandó ió describeä iî thió HELÐ file. + + Thå  RCÐ commandó residå iî memorù ratheò thaî beinç  loadeä +froí  disk¬ anä thereforå operatå verù efficientlù anä arå  disë­ +independent®   Oî thå otheò hand¬ duå tï memorù constraints¬  thå +RCÐ commandó arå ofteî lesó powerfuì thaî theiò transienô prograí +counterparts® + + + + Iæ aî erroò occurs¬ thå RCÐ commandó generallù chaiî tï  thå +installeä erroò handler¬ providinç á detaileä reporô oæ thå erroò +anä  allowinç thå commanä tï bå editeä oò aborted® Iæ  nï  erroò +handleò  ió  installed¬  thå commanä whicè causeä  thå  erroò  ió +echoeä tï thå screen¬ followeä bù á questioî mark. + + Alì RCÐ commandó arå installatioî-dependent¬ anä manù maù bå +configureä  aô  assemblù timå tï perforí  iî  slightlù  differenô +ways®   Thå  exampleó giveî iî thió HELÐ filå arå  baseä  oî  thå +distributeä seô oæ options® Seå sourcå codå filå Z34RCP.LIÂ  foò +morå detailó oî commanä anä optioî selection. + +:CLED anä SPOÐ Commands + Transient Counterparts: LSÈ anä SPOP + + Thå  CLEÄ commanä invokeó á speciaì prograí  whicè  provideó +WordStaò-likå editinç oæ commandó aô thå systeí prompt® Iô  alsï +allowó yoõ tï recalì thå lasô severaì commandó invokeä witè  onlù +onå keystroke® Finally¬ iæ available¬ CLEÄ displayó thå  currenô +systeí timå tï thå lefô oæ youò prompt® Thå syntaø is: + + CLED - invoke as shell + CLED / - invoke for next command only + + + + Thå SPOÐ commanä "pops¢ thå toð shelì froí thå shelì  stack¬ +sï thaô iô nï longeò loadó afteò everù command® Thió commanä  ió +rarelù  needed¬  anä ió includeä onlù witè versionó  oæ  thå  RCÐ +includinç CLEÄ (seå above)® Thå syntaø ió simply: + + SPOP +:CLS Command + Transient Counterpart: None + + Thå  CLÓ  commanä  clearó  thå  terminaì  screen¬   removinç +displayó  whicè  mighô  havå beeî lefô bù  previouó  commandó  oò +programs® Thå syntaø is: + + CLS + +:CP Command + Transient Counterpart: MCOPY + + Thå  CÐ  commanä copieó onå filå froí onå DÕ tï  anotheò  oò +intï  thå samå DÕ undeò á differenô name® Iæ functionó 10²  (geô +stamp©  anä 10³ (seô stamp© arå supporteä bù aî RSØ oò DOS¬  filå +datestampó arå preserveä acrosó copies® Thå syntaø is: + + CP dir:ufn1 dir:ufn2 -- ufn1 to ufn2 + CP dir:ufn1 -- ufn1 to current dir: + CP dir:ufn2=dir:ufn1 -- ufn1 to ufn2 + + Examples: + + CP f1.txt f2.txt + CP b0:f1.txt a15: + CP root:f1.txt + +:D Command + Transient Counterpart: DIR, XD, XDIR + + Thå   Ä   commanä  displayó  thå  directorù  oæ   fileó   iî +alphabeticaì ordeò acrosó thå lineó tï thå user® Thå syntaø is: + + D dir:afn.aft o + D .aft o + D /o + + Options (wheel only) are: + + S - Display System Files Only + A - Display Both Non-System and System Files + + Examples: + + D /a + D root:myfile.txt Š D .com + +:ECHO Command + Transient Counterpart: ECHO + + ECHÏ   ió usefuì iî issuinç botè messageó (tï thå  user¬ +saù withiî á commanä filå durinç execution© anä escapå sequences® +Bù  uså oæ thå %¾ anä %¼ parameters¬ ECHÏ caî senä itó outpuô  iî +combinationó  oæ uppeò anä loweò case® Bù uså oæ thå %Ð  anä  %Ã +parameters¬  ECHÏ  outpuô  caî  bå  toggleä  betweeî  screeî  anä +printer®   ECHÏ  useó  BIOÓ  calls¬  sï  alì  controì  characteró +(entereä aó ^character© arå passeä exactly® Hence¬ consolå-leveì +programminç oæ sucè deviceó (CRTó anä printers© ió possible. + + Examples: + ECHO %Pthis is a test%Cof echo%P + -- "THIS IS A TEST" goes to the printer + "OF ECHO" goes to the console + (trailing %P flushes printer buffer) + + ECHO t%>his is a % + prtdec ,%recs,,%bytes, + prtdec ; Trailing CRLF + endm + + else ; Non-SLR assemblers +include macro filename + $include filename&.lib + endm + endif ; SLR + + + if ZRL + +; ============================================================================= + +; J e t L D R I D S E C T I O N + +; ============================================================================= + +; Macro to build ID block message for JetLDR. Max. length is ~256 chars. + +optcnt defl 0 ; Options counter + +option macro string1,enable1,string2,enable2 + + if enable1 ;; Skip if command not present + if [optcnt mod 4] eq 0 + db cr,lf ;; New line every four options + endif + optcnt defl optcnt + 1 ;; Increment options counter + count defl 0 ;; Initialize character count + + irpc char,string1 ;; Count and define characters + count defl count + 1 + db '&char' + endm ; irpc + + if not nul enable2 ;; Sub-option label present + if enable2 ;; Sub-option enabled + db ' (' ;; Begin sub-opt description + irpc char,string2 ;; Count and define characters + count defl count + 1 + db '&char' + endm ; irpc + db ')' ;; End sub-opt description + count defl count + 3 ;; For ' ()' + endif ; enable2 + endif ; not nul enable2 + + if [optcnt mod 4] ne 0 + count defl 11 - count + rept count + db ' ' ;; Pad to 11 spaces + endm ; rept + endif ; [optcnt mod 4] ne 0 + + endif ; enable1 + endm ; option macro + +; --------------------------------------------------------- + + COM /_ID_/ ; JetLDR ID block + db 'Copr. 1989 ZSA. Enabled Commands:' + option Cled,cledon + option Cls,clson,TC,clstcap + option Cp,cpon,stp,stpcall + option Dir,diron,sp, + option Echo,echoon,lst,echolst + option Era,eraon,sp, + option Help,true + option List,liston + option Note,noteon + option Peek,peekon,hdr,peekhdr + ; option Poke,pokeon,q,pokeq + option Poke,pokeon + option Port,porton + option Protect,proton + option Quiet,quieton + option Register,regon + option Rename,renon + option Reset,reson,sp, + option Space,spaceon + option Spop,cledon + option Type,lton,cls, + option Wheel,whlon,q,whlquiet + db 0 ; ID string terminator + +;============================================================================= +; +; E N T R Y C O D E S E C T I O N +; +;============================================================================= + + cseg + else + org z3rcp + endif ; ZRL + +RCPbegin: + db 'Z3RCP' ; Package ID + + include RCPcmd ; Command table and custom commands + + include RCPsubs ; File of subroutines + + include RCPh ; 'H' help (command list) command + +; Include only selected code sections. + + if cledon ; 'CLED' and 'SPOP' commands + include RCPcled + endif ;cledon + + if clson + include RCPcls ; 'CLS' clear screen command + endif ;clson + + if reson + include RCPr ; 'R' disk reset command + endif ;reson + + if spaceon + include RCPsp ; 'SP' space on disk command + endif ;spaceon + + if diron + include RCPdir ; 'DIR' directory command + endif ;diron + + if eraon + include RCPera ; 'ERA' erase command + endif ;eraon + + if renon + include RCPren ; 'REN' rename command + endif ;renon + + if cpon + include RCPcp ; 'CP' file copying command + endif ;cpon + + if echoon + include RCPecho ; 'ECHO' command + endif ;echoon + + if quieton + include RCPquiet ; 'Q' quiet flag + endif ;quieton + + if lton + include RCPlt ; 'LIST' and 'TYPE' commands + endif ;lton + + if proton + include RCPprot ; 'PROT' file attribute setting command + endif ;proton + + if peekon or pokeon or porton + include RCPiom ; 'PEEK', 'POKE', 'PORT' commands + endif ;peekon or pokeon or porton + + if regon + include RCPreg ; 'REG' register operation commands + endif ;regon + + if whlon + include RCPwhl ; 'WHL' command + endif ;whlon + + include RCPid ; Add ID string (must come last) + +RCPend: ; Used to calculate length + end + +; End of Z34RCP.Z80 + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/z34rcp11.z80.sav b/Source/BPBIOS/Z34RCP11/z34rcp11.z80.sav new file mode 100644 index 00000000..7af6b875 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/z34rcp11.z80.sav @@ -0,0 +1,267 @@ + +; Program: Z34RCP +; Description: Resident Command Package (RCP) for ZCPR34 +; Version: 1.3 +; Author: Carson Wilson +; Date: September 13, 1989 +; Changes: Added CLED (command line editor shell) command. +; To save space, JetLDR ID section no longer pads end of each +; line. (JetLDR allows only 256 bytes maximum in its +; description field). + +; Version: 1.2 +; Author: Carson Wilson +; Date: July 9, 1989 +; Changes: Uses spaces instead of tabs in JetLDR displays. +; Copyright now ZSA. + +; Version: 1.1 +; Author: Carson Wilson +; Date: September 14, 1988 +; Changes: RCPID macro called from RCPID.LIB so that the final INCLUDE +; macro call gives total size of the RCP, including the ID +; string, with SLR assemblers. +; Label WHLQ changed to WHLQUIET in Option macro to match +; Z34HDR.LIB equate. + +; Version: 1.0 +; Author: Carson Wilson +; Date: June 15, 1988 + +; Derivation: SYSRCP (Richard Conn) + +; Z34RCP is copyright 1989 by Z Systems Associates. All rights reserved. +; End-user distribution and duplication permitted for non-commercial purposes +; only. Any commercial use of Z34RCP, defined as any situation where the +; duplicator recieves revenue by duplicating or distributing Z34RCP by itself +; or in conjunction with any hardware or software product, is expressly +; prohibited unless authorized in writing by Z Systems Associates. + +;============================================================================= +; +; D E F I N I T I O N S S E C T I O N +; +;============================================================================= + + maclib sysdef.lib ; Common logic, sys, ascii defines + maclib z34defn.lib ; Defines offsets in Z34 command processor + maclib z34mac.lib ; Macros + maclib z34rcp.lib ; Defines command options + + if ZRL + maclib z34cmn.lib ; Defines universal ZCPR named common blocks + else + maclib rcpbase.lib ; Defines addresses for one system + endif + + name RCP11 ; Declare module name + +; ============================================================================ + +; M A C R O S S E C T I O N + +; ============================================================================ + +; Macros to include module file and print module length. +; Under SLR and compatible assemblers, a running account of current RCP +; size and bytes added will be given for each module. +; +; For other assemblers you may have to either modify the non-SLR include +; macro or use a text editor to read in the actual contents of each file +; in place of the include statement. + + if SLR +; +; General purpose macro to display decimal values and messages +; +prtdec macro m1,m2,m3,m4,m5 + .radix 10 ; Decimal output + .printx m1 m2 m3 m4 m5 ; Print to screen + endm +; +; Macro to include modules and display lengths. +; +include macro filename +before defl $ + $include filename&.lib +after defl $ +modlen defl after - before ; Module length +rcplen defl after - RCPbegin ; RCP length +recs defl rcplen / 128 ; ..in records +bytes defl rcplen mod 128 ; ..and additional bytes + prtdec %modlen, + prtdec ,%recs,,%bytes, + prtdec ; Trailing CRLF + endm + + else ; Non-SLR assemblers +include macro filename + $include filename&.lib + endm + endif ; SLR + + + if ZRL + +; ============================================================================= + +; J e t L D R I D S E C T I O N + +; ============================================================================= + +; Macro to build ID block message for JetLDR. Max. length is ~256 chars. + +optcnt defl 0 ; Options counter + +option macro string1,enable1,string2,enable2 + + if enable1 ;; Skip if command not present + if [optcnt mod 4] eq 0 + db cr,lf ;; New line every four options + endif + optcnt defl optcnt + 1 ;; Increment options counter + count defl 0 ;; Initialize character count + + irpc char,string1 ;; Count and define characters + count defl count + 1 + db '&char' + endm ; irpc + + if not nul enable2 ;; Sub-option label present + if enable2 ;; Sub-option enabled + db ' (' ;; Begin sub-opt description + irpc char,string2 ;; Count and define characters + count defl count + 1 + db '&char' + endm ; irpc + db ')' ;; End sub-opt description + count defl count + 3 ;; For ' ()' + endif ; enable2 + endif ; not nul enable2 + + if [optcnt mod 4] ne 0 + count defl 11 - count + rept count + db ' ' ;; Pad to 11 spaces + endm ; rept + endif ; [optcnt mod 4] ne 0 + + endif ; enable1 + endm ; option macro + +; --------------------------------------------------------- + + COM /_ID_/ ; JetLDR ID block + db 'Copr. 1989 ZSA. Enabled Commands:' + option Cled,cledon + option Cls,clson,TC,clstcap + option Cp,cpon,stp,stpcall + option Dir,diron,sp, + option Echo,echoon,lst,echolst + option Era,eraon,sp, + option Help,true + option List,liston + option Note,noteon + option Peek,peekon,hdr,peekhdr + option Poke,pokeon,q,pokeq + option Port,porton + option Protect,proton + option Quiet,quieton + option Register,regon + option Rename,renon + option Reset,reson,sp, + option Space,spaceon + option Spop,cledon + option Type,lton,cls, + option Wheel,whlon,q,whlquiet + db 0 ; ID string terminator + +;============================================================================= +; +; E N T R Y C O D E S E C T I O N +; +;============================================================================= + + cseg + else + org z3rcp + endif ; ZRL + +RCPbegin: + db 'Z3RCP' ; Package ID + + include RCPcmd ; Command table and custom commands + + include RCPsubs ; File of subroutines + + include RCPh ; 'H' help (command list) command + +; Include only selected code sections. + + if cledon ; 'CLED' and 'SPOP' commands + include RCPcled + endif ;cledon + + if clson + include RCPcls ; 'CLS' clear screen command + endif ;clson + + if reson + include RCPr ; 'R' disk reset command + endif ;reson + + if spaceon + include RCPsp ; 'SP' space on disk command + endif ;spaceon + + if diron + include RCPdir ; 'DIR' directory command + endif ;diron + + if eraon + include RCPera ; 'ERA' erase command + endif ;eraon + + if renon + include RCPren ; 'REN' rename command + endif ;renon + + if cpon + include RCPcp ; 'CP' file copying command + endif ;cpon + + if echoon + include RCPecho ; 'ECHO' command + endif ;echoon + + if quieton + include RCPquiet ; 'Q' quiet flag + endif ;quieton + + if lton + include RCPlt ; 'LIST' and 'TYPE' commands + endif ;lton + + if proton + include RCPprot ; 'PROT' file attribute setting command + endif ;proton + + if peekon or pokeon or porton + include RCPiom ; 'PEEK', 'POKE', 'PORT' commands + endif ;peekon or pokeon or porton + + if regon + include RCPreg ; 'REG' register operation commands + endif ;regon + + if whlon + include RCPwhl ; 'WHL' command + endif ;whlon + + include RCPid ; Add ID string (must come last) + +RCPend: ; Used to calculate length + end + +; End of Z34RCP.Z80 + \ No newline at end of file diff --git a/Source/BPBIOS/Z34RCP11/zsystem.ad b/Source/BPBIOS/Z34RCP11/zsystem.ad new file mode 100644 index 00000000..5231a575 --- /dev/null +++ b/Source/BPBIOS/Z34RCP11/zsystem.ad @@ -0,0 +1,97 @@ + + Z System Upgrades CP/M + +Z System is first-rate, state-of-the-art software for your CP/M machine. +ZCPR has been with us for over five years now, and has enhanced computer +productivity for thousands of CP/M users. Z System is now a mature +replacement for CP/M 2.2 or CP/M Plus, yet is "backward compatible" with +almost all programs written for CP/M. This means that you can still run +the CP/M programs you now use, yet take advantage of greatly increased +power and performance at the same time. + +NZ-COM and Z3PLUS form the heart of Z System. They replace the most +visible parts of CP/M 2.2 and CP/M Plus respectively, adding enhanced +command processing, named directories, vastly improved resident +commands, flow control processing, error handling, and much more. But +it isn't necessary to master all of these (at least at first!) to take +advantage of NZ-COM and Z3PLUS. In fact, though both packages come with +several disks of software and excellent manuals, almost anyone can +install either of these packages on their CP/M computer in just a few +minutes' time. The price for either NZ-COM or Z3PLUS is $69.95 plus $3 +shipping. + +The next step up in sophistication for CP/M 2.2 users is ZSDOS. ZSDOS +replaces the less visible portion of CP/M 2.2 which controls program +input and output. Through clever coding and exhaustive testing, the +authors of ZSDOS offer significant improvements in performance, safety, +and versatility for CP/M 2.2, including file time and date stamping, +file archiving for faster backups, public files (accessable from all +user areas), path access to files, and improved error messages and +handling. As with NZ-COM and Z3PLUS, ZSDOS installation is completely +menu-driven. ZSDOS comes with a collection of state-of-the-art utility +programs and a 140 page manual to help you make the most of the extended +features. ZSDOS costs $75 ($60 when purchased with NZ-COM) plus $3 +shipping. + +Another path to system enhancement for CP/M 2.2 users with hard or RAM +disks is available in the form of BackGrounder ii (BGii). BGii allows +you to "suspend" any program at the touch of a button and use a wide +range of resident commands such as DIR, REN, ERA, CALC, or TYPE. Then +hit the button again and BGii quickly returns you to the program you +suspended, right where you left off. Or use BGii's SWAP command and +you're back at the CP/M prompt, ready to run any other CP/M program. +When you're through, SWAP again and BGii returns you to your original +program, exactly as you left it! Advanced "Cut" and "Paste" commands +are also available for many terminals, allowing you to transfer sections +of screen directly from one program to another. Print spooling, +advanced key redefinition and recording capabilities, and a beautifully +designed 140-page user's manual are included. Installation is +menu-driven and easy. BGii is compatible with either ZCPR or CP/M 2.2, +and costs $75.00 plus $3 shipping. + +Last but not least is DosDisk, a small but powerful program which allows +you to read or write DIRECTLY to MS-DOS disks with your CP/M computer. +No more file transfers--simply insert a standard MS-DOS DSDD diskette in +your CP/M machine, type "DosDisk :", and away you go--all files +on the diskette are now accessable by all of your CP/M programs--even +files in MS-DOS subdirectories! DosDisk comes with a handsome 38-page +user's manual, and is available preconfigured for most machines at just +$30.00 plus $3 shipping. DosDisk is compatible with either ZCPR or +CP/M 2.2. + +These and other fine products for CP/M and CP/M Plus are available from +users groups around the country, or directly from Z Systems Associates. +Sizeable discounts for users' groups are also available from Z Systems +Associates through the Z Plan. The Z Systems Associates are: + + Sage Microsystems East + Selling & Supporting the Best in 8-Bit Software + 1435 Centre St., Newton Centre, MA 02159-2469 + Voice: 617/965-3552 (9:00am - 11:30pm) + Modem: 617/965-7259 (password = DDT)(MABOS on PC-Pursuit) + + Same-day shipping of most products with modem download and support + available. Order by phone, mail, or modem. Shipping and handling $4 + per order (USA). Check, VISA, or MasterCard. Specify exact disk format. + +and: + + Plu*Perfect Systems + "==World-Class Software" + 410 23rd Street, Santa Monica, CA 90402 + Voice: 213/393-6105 (evenings) + Modem: 213/670-9465 (leave message for "Bridger Mitchell") + + To order: Specify product, operating system, computer, 5 1/4" disk + format. Enclose check, adding $3 shipping ($5 foreign) + 6.5% tax in + California. Enclose invoice if upgrading BGii or ZRDOS. + +For more information on Z System, check at the above bulletin board +systems, or consult The Computer Journal for excellent articles on ZCPR +and CP/M. The Computer Journal is published six times a year by +Publishing Consultants, 190 Sullivan Crossroad, Columbia Falls, MT +59912, phone 406/257-9119. An issue averages forty pages with few ads. +Subscription rates are $16 for one year (6 issues), or $28 for two years +(12 issues) in the U.S., $22 for one year Canada and Mexico, and $24 +(surface) for one year in other countries. + \ No newline at end of file diff --git a/Source/BPBIOS/ZCPR33/Makefile b/Source/BPBIOS/ZCPR33/Makefile new file mode 100644 index 00000000..62450918 --- /dev/null +++ b/Source/BPBIOS/ZCPR33/Makefile @@ -0,0 +1,18 @@ +OBJECTS = zcpr33n.rel zcpr33t.rel +OTHERS = z3basen.lib z3baset.lib +TOOLS = ../../../Tools +DEST = .. + +include $(TOOLS)/Makefile.inc + +DIFFPATH = $(DIFFTO)/Source/BPBIOS + +zcpr33t.rel: ../z3baset.lib + cp ../z3baset.lib z3baset.lib + $(ZXCC) $(CPM)/ZMAC -zcpr33t.z80 -/P + rm z3baset.lib + +zcpr33n.rel: ../z3basen.lib + cp ../z3basen.lib z3basen.lib + $(ZXCC) $(CPM)/ZMAC -zcpr33n.z80 -/P + rm z3basen.lib diff --git a/Source/BPBIOS/cboot-ww.z80 b/Source/BPBIOS/cboot-ww.z80 index ce08a842..3febd543 100644 --- a/Source/BPBIOS/cboot-ww.z80 +++ b/Source/BPBIOS/cboot-ww.z80 @@ -304,7 +304,7 @@ MEMOK: IF MOVCPM ; Space is critical for boot tracks DEFB CR,LF,'P112 - ' ; Save all bytes possible ELSE ; Otherwise sign on with complete name - DEFB CR,LF,'D-X Designs P112 - ' + DEFB CR,LF,'RomWBW - ' ENDIF DEFB 'B/P 50.00k Bios' ;**** Do NOT alter this string **** @@ -322,7 +322,7 @@ MEMOK: DEFB ' ZCPR3+ Env' IF CLOCK IF DS1202 - DEFB CR,LF,' Dallas DS-1202 Clock, ' + DEFB CR,LF,' RomWBW HBIOS Clock, ' IF CLKSET DEFB 'with ' ELSE diff --git a/Source/BPBIOS/def-ww.lib b/Source/BPBIOS/def-ww.lib deleted file mode 100644 index 0eef1948..00000000 --- a/Source/BPBIOS/def-ww.lib +++ /dev/null @@ -1,373 +0,0 @@ -;:::::::::::::::::::::::::::::::::::::::::::::::********************** -; B/P BIOS Configuration and Equate File. ** System Dependant ** -; - D-X Designs Pty Ltd P112 CPU Board - ********************** -; Tailor your system here. -; -; 30 Aug 01 - Cleaned up for GPL release. HFB -; 11 May 97 - Added GIDE and adjusted HD equates. HFB -; 5 Jan 97 - Reformatted to Standard. HFB -; 10 Jun 96 - Initial Test Release. HFB -;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -; BIOS Configuration Equates and Macros - -DATE MACRO - DEFB '17 Jan 14' ; Date of this version - ENDM - -AUTOCL MACRO - DEFB 8,'ZEX Z41 ',0 ; Autostart command line - ENDM - -;--- Basic System and Z-System Section --- - -MOVCPM EQU no ; Integrate into MOVCPM "type" loader? - IF MOVCPM -VERS EQU 13H ; Version number in BCD (Hex) (Major/Minor) - ELSE -VERS EQU 21H ; Version number w/Device Swapping permitted - ENDIF -BANKED EQU YES ; Is this a banked BIOS? -ZSDOS2 EQU YES ; Yes = Banked Dos, No = CP/M 2.2 Compatible -INROM EQU NO ; Alternate bank in ROM? -MHZ EQU 18 ; Set to Speed in MHZ (6/9/12/16/18/24) -FASTWB EQU YES ; Yes if restoring CPR from banked RAM - ; ..No if restoring from Drive A -Z3 EQU YES ; Include ZCPR init code? -HAVIOP EQU NO ; Include IOP code into Jump table? -INTPXY EQU YES ; Internal HBIOS Mini Proxy -CONF_T EQU NO ; Set for Segment Configuration T -CONF_N EQU YES ; Set for Segment Configuration N - -;--- Memory configuration Section --- (Expansion Memory configured here) - -IBMOVS EQU NO ; Yes = Inter-bank Moves allowed (Z180/64180) - ; No = Include Common RAM transfer buffer -;--- Character Device Section --- - -MORDEV EQU NO ; YES = Include any extra Char Device Drivers - ; NO = Only use the 4 defined Char Devices -ESCC_B EQU no ; Include ESCC Channel B Driver? - ; The following two devices result in non-standard data rates - ; with the standard 16.00 MHz crystal in the P112. If a more - ; "standard" crystal is used (12.288, 18.432, 24.576 MHz etc) - ; is used, the ports become usable. - ; Driver code for ASCI0 and ASCI1 includes an option for - ; assembling Polled or Interrupt-driven buffered input. - ; Select the desired option for ASCI0 with the BUFFA0 flag, - ; and BUFFA1 for ASCI1. -ASCI_0 EQU false ; Include ASCI0 Driver? -BUFFA0 EQU false ; Use buffered ASCI0 Input Driver? -ASCI_1 EQU false ; Include ASCI1 Driver? -BUFFA1 EQU false ; Use buffered ASCI1 Input Driver? - -QSIZE EQU 32 ; size of interrupt typeahead buffers (if used) - ; ..must be 2^n with n<8 -RTSCTS EQU no ; Include RTS/CTS code on Serial Outputs? -XONOFF EQU no ; Include Xon/Xoff handshaking in Serial lines? - -;--- Clock and Time Section --- - -CLOCK EQU YES ; Include ZSDOS Clock Driver Code? -DS1202 EQU YES ; Use Dallas DS-1202 instead of Interrupt RTC? -CLKSET EQU YES ; Allow DS-1202 Clock Sets? (Error if No) -TICTOC EQU NO ;== NOT USED IN P112 ("heartbeat" count) - -;--- Floppy Diskette Section --- - -BIOERM EQU yes ; Print BIOS error messages? -CALCSK EQU YES ; Calculate skew table? -AUTOSL EQU YES ; Auto select floppy formats? - ; If AUTOSL=True, the next two are active... -FDDMA EQU no ; Use DMA Control for Floppy Drive Transfers? -FLOPYH EQU no ; Include "Hi-Density" Floppy Formats? -FLOPY8 EQU no ; Include 8" Floppy Formats? -MORDPB EQU NO ; Include additional Floppy DPB Formats? - -;--- RAM Disk Section --- - -RAMDSK EQU YES ; YES = Make RAM-Disk Code, NO = No code made - -;--- Hard Disk Section --- - -HARDDSK EQU YES ; YES = Add Hard-disk Code, NO = Floppy Only - ; (Pick 1 of 3 options below) -SCSI EQU NO ; YES = Use SCSI Driver -IDE EQU NO ; YES = Use IDE Driver -SIMHDSK EQU NO ; YES = Use SIMH HDSK Driver -HBDSK EQU YES ; YES = Use HBIOS Disk Driver -HDDMA EQU NO ; Use DMA-Controlled Hard Disk Data Transfers? - ; (DMA not implemented for GIDE) -UNIT_0 EQU YES ; Hard Disk Physical Unit 1 -UNIT_1 EQU YES ; Hard Disk Physical Unit 2 -UNIT_2 EQU YES ; Hard Disk Physical Unit 3 - -;--- Logical Drive Section --- - -DRV_A EQU no ; Set each of these equates for the drive and -DRV_B EQU no ; partition complement of your system. Assume -DRV_C EQU no ; that A-D are Floppies. -DRV_D EQU no -DRV_E EQU yes ; Assume that E-L and N-P are Hard Disk -DRV_F EQU yes ; Partitions -DRV_G EQU yes -DRV_H EQU yes -DRV_I EQU yes -DRV_J EQU yes -DRV_K EQU yes -DRV_L EQU yes -DRV_M EQU RAMDSK ; This is Yes for RAM drive -DRV_N EQU yes -DRV_O EQU ~RAMDSK ; Use HBIOS RAM disk if BPBIOS RAM disk is not enabled -DRV_P EQU no - -;========== Configuration Unique Equates (P112) =========== -;>>>>>>>>>>>>>>>>>>>>>>>>>>> W A R N I N G <<<<<<<<<<<<<<<<<<<<<<<<<<<<< -;>>> Do NOT Alter these unless you KNOW what you're doing <<< -;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - -REFRSH EQU NO ; Set to NO for only Static RAM, needed for - ; systems with dynamic RAMs. -NOWAIT EQU NO ; Set to NO to use configured Wait States in - ; Hard Disk Driver. Yes to eliminate Waits. - -;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -; For Z-180/HD64180 systems, The Bank numbers should reflect Physical -; memory in 32k increments. In P112, the ROM occupies the first 32k -; increment and is ambiguously addressed occupying 0-1FFFFH. The upper -; memory bounds (BNKU, BNK3 and BNKM) should be set for your configuration. - -BNK0 EQU BID_USR ; First TPA Bank (switched in/out) 40000H -BNK1 EQU BID_HB ; Second TPA Bank (Common Bank) 48000H -BNK2 EQU BID_SYS ; System Bank (BIOS, DOS, CPR) 50000H -BNKU EQU 00H ; User Area Bank 58000H - ; (set to 0 to disable) -BNK3 EQU BID_RAMD ; First Bank for RAM disk 60000H -BNKM EQU BID_RAMM ; Maximum Bank # F8000H - ; With both on-board RAMs only (MEM1 or MEM2), - ; the maximum Bank number is 11 (0BH). - -;=========== CPU-dependent Equates, Zilog Z-180/Hitachi HD64180 ========== - -CNTLA0 EQU 00H ; Control Port ASCI 0 -CNTLA1 EQU 01H ; Control Port ASCI 1 -STAT0 EQU 04H ; Serial port 0 Status -STAT1 EQU 05H ; Serial port 1 Status -TDR0 EQU 06H ; Serial port 0 Output Data -TDR1 EQU 07H ; Serial port 1 Output Data -RDR0 EQU 08H ; Serial port 0 Input Data -RDR1 EQU 09H ; Serial Port 1 Input Data -CNTR EQU 0AH ; HD64180 Counter port -TMDR0L EQU 0CH ; HD64180 DMA channel reg (low) -TMDR0H EQU 0DH ; HD64180 DMA channel reg (hi) -RLDR0L EQU 0EH ; CTC0 Reload Count, Low -RLDR0H EQU 0FH ; CTC0 Reload Count, High -TCR EQU 10H ; Interrupt Control Register -TMDR1L EQU 14H ; Timer Data Reg Ch1 (Low) -TMDR1H EQU 15H ; Timer Data Reg Ch1 (High) -RLDR1L EQU 16H ; Timer Reload Reg Ch1 (Low) -RLDR1H EQU 17H ; Timer Reload Reg Ch1 (High) -FRC EQU 18H ; Free-Running Counter -CCR EQU 1FH ; CPU Control Register (ZS8180/Z80182) -SAR0L EQU 20H ; DMA Channel 0 Register start (8 ports) -MAR1L EQU 28H ; DMA Channel 1 Register start (8 ports) -DSTAT EQU 30H ; DMA Status/Control port -DMODE EQU 31H ; DMA Mode Control port -DCNTL EQU 32H ; DMA/WAIT Control Register -IL EQU 33H ; Interrupt Segment Register -ITC EQU 34H ; Interrupt/Trap Control Register -RCR EQU 36H ; HD64180 Refresh Control register -CBR EQU 38H ; MMU Common Base Register -BBR EQU 39H ; MMU Bank Base Register -CBAR EQU 3AH ; MMU Common/Bank Area Register -OMCR EQU 3EH ; Operation Mode Control Reg -ICR EQU 3FH ; I/O Control Register - -; Some bit definitions used with the Z-180 on-chip peripherals: - -TDRE EQU 02H ; ACSI Transmitter Buffer Empty -RDRF EQU 80H ; ACSI Received Character available - -;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -; Extended Features of Z80182 for P112 - -WSGCS EQU 0D8H ; Wait-State Generator CS -ENH182 EQU 0D9H ; Z80182 Enhancements Register -PINMUX EQU 0DFH ; Interrupt Edge/Pin Mux Register -RAMUBR EQU 0E6H ; RAM End Boundary -RAMLBR EQU 0E7H ; RAM Start Boundary -ROMBR EQU 0E8H ; ROM Boundary -FIFOCTL EQU 0E9H ; FIFO Control Register -RTOTC EQU 0EAH ; RX Time-Out Time Constant -TTOTC EQU 0EBH ; TX Time-Out Time Constant -FCR EQU 0ECH ; FIFO Register -SCR EQU 0EFH ; System Pin Control -RBR EQU 0F0H ; MIMIC RX Buffer Register (R) -THR EQU 0F0H ; MIMIN TX Holding Register (W) -IER EQU 0F1H ; Interrupt Enable Register -LCR EQU 0F3H ; Line Control Register -MCR EQU 0F4H ; Modem Control Register -LSR EQU 0F5H ; Line Status Register -MDMSR EQU 0F6H ; Modem Status Register -MSCR EQU 0F7H ; MIMIC Scratch Register -DLATL EQU 0F8H ; Divisor Latch (Low) -DLATM EQU 0F9H ; Divisor Latch (High) -TTCR EQU 0FAH ; TX Time Constant -RTCR EQU 0FBH ; RX Time Constant -IVEC EQU 0FCH ; MIMIC Interrupt Vector -MIMIE EQU 0FDH ; MIMIC Interrupt Enable Register -IUSIP EQU 0FEH ; MIMIC Interrupt Under-Service Register -MMCR EQU 0FFH ; MIMIC Master Control Register - -; Z80182 PIO Registers - -DDRA EQU 0EDH ; Data Direction Register A -DRA EQU 0EEH ; Port A Data -DDRB EQU 0E4H ; Data Direction Register B -DRB EQU 0E5H ; Data B Data -DDRC EQU 0DDH ; Data Direction Register C -DRC EQU 0DEH ; Data C Data - -;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -; ESCC Registers on Z80182 - -SCCACNT EQU 0E0H ; ESCC Control Channel A -SCCAD EQU 0E1H ; ESCC Data Channel A -SCCBCNT EQU 0E2H ; ESCC Control Channel B -SCCBD EQU 0E3H ; ESCC Data Channel B - -; [E]SCC Internal Register Definitions - -RR0 EQU 00H -RR1 EQU 01H -RR2 EQU 02H -RR3 EQU 03H -RR6 EQU 06H -RR7 EQU 07H -RR10 EQU 0AH -RR12 EQU 0CH -RR13 EQU 0DH -RR15 EQU 0FH - -WR0 EQU 00H -WR1 EQU 01H -WR2 EQU 02H -WR3 EQU 03H -WR4 EQU 04H -WR5 EQU 05H -WR6 EQU 06H -WR7 EQU 07H -WR9 EQU 09H -WR10 EQU 0AH -WR11 EQU 0BH -WR12 EQU 0CH -WR13 EQU 0DH -WR14 EQU 0EH -WR15 EQU 0FH - -; FDC37C665/6 Parallel Port in Standard AT Mode - -DPORT EQU 8CH ; Data Port -SPORT EQU 8DH ; Status Port -CPORT EQU 8EH ; Control Port - -; FDC37C665/6 Configuration Control (access internal registers) - -CFCNTL EQU 90H ; Configuration control port -CFDATA EQU 91H ; Configuration data port - -; FDC37C665/6 Floppy Controller on P112 (Intel 80277 compatible) - -DCR EQU 92H ; Drive Control Register (Digital Output) -MSR EQU 94H ; Main Status Register -DR EQU 95H ; Data/Command Register -DRR EQU 97H ; Data Rate Register/Disk Changed Bit in B7 - -_DMA EQU 0A0H ; Diskette DMA Address - -; FDC37C665/6 Serial Port (National 16550 compatible) - -_RBR EQU 68H ;R Receiver Buffer -_THR EQU 68H ;W Transmit Holding Reg -_IER EQU 69H ;RW Interrupt-Enable Reg -_IIR EQU 6AH ;R Interrupt Ident. Reg -_FCR EQU 6AH ;W FIFO Control Reg -_LCR EQU 6BH ;RW Line Control Reg -_MCR EQU 6CH ;RW Modem Control Reg -_LSR EQU 6DH ;RW Line Status Reg -_MMSR EQU 6EH ;RW Modem Status Reg -_SCR EQU 6FH ;N/A Scratch Reg. (not avail in XT) -_DDL EQU 68H ;RW Divisor LSB | wih DLAB -_DLM EQU 69H ;RW Divisor MSB | set High - -;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -; Equates for the National DP8490/NCR 5380 Prototype SCSI controller - - IF HARDDSK -NCR EQU 40H ; Base of NCR 5380 - -; 5380 Chip Registers - -NCRDAT EQU NCR ; Current SCSI Data (Read) - ; Output Data Register (Write) -NCRCMD EQU NCR+1 ; Initiator Command Register (Read/Write) -NCRMOD EQU NCR+2 ; Mode Register (Read/Write) -NCRTGT EQU NCR+3 ; Target Command Register (Read/Write) -NCRBUS EQU NCR+4 ; Current SCSI Bus Status (Read) -NCRST EQU NCR+5 ; Bus & Status Register (Read) - ; Start DMA Send (Write) -NCRINT EQU NCR+7 ; Reset Parity/Interrupt (Read) - ; Start DMA Initiator Receive (Write) -DMAACK EQU NCR+8 ; SCSI Dack IO Port (Read/Write) - -; Bit Assignments for NCR 5380 Ports as indicated - -B_ARST EQU 10000000B ; Assert *RST (NCRCMD) -B_AACK EQU 00010000B ; Assert *ACK (NCRCMD) -B_ASEL EQU 00000100B ; Assert *SEL (NCRCMD) -B_ABUS EQU 00000001B ; Assert *Data Bus (NCRCMD) - -B_BSY EQU 01000000B ; *Busy (NCRBUS) -B_REQ EQU 00100000B ; *Request (NCRBUS) -B_MSG EQU 00010000B ; *Message (NCRBUS) -B_CD EQU 00001000B ; *Command/Data (NCRBUS) -B_IO EQU 00000100B ; *I/O (NCRBUS) -B_SEL EQU 00000010B ; *Select (NCRBUS) - -B_PHAS EQU 00001000B ; Phase Match (NCRST) -B_BBSY EQU 00000100B ; Bus Busy (NCRST) - -B_MBSY EQU 00000100B ; Monitor Busy Flag (NCRMOD) -B_DMA EQU 00000010B ; DMA Mode of transfer (NCRMOD) - ENDIF ;harddsk - -;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -; Equates reflecting GIDE Base address from Address Jumpers (if GIDE added) -; Set the base GIDE equate to the jumper setting on the GIDE board. - - IF IDE -GIDE EQU 50H ; Set base of 16 byte address range - -IDEDOR EQU GIDE+6 ; Digital Output Register -IDEDat EQU GIDE+8 ; IDE Data Register (16-bit wide) -IDEErr EQU GIDE+9 ; IDE Error Register -IDESCnt EQU GIDE+0AH ; IDE Sector Count Register -IDESNum EQU GIDE+0BH ; IDE Sector Number Register -IDECLo EQU GIDE+0CH ; IDE Cylinder Number (Low) -IDECHi EQU GIDE+0DH ; IDE Cylinter Number (High) -IDESDH EQU GIDE+0EH ; IDE S-Drive-Head Register -IDECmd EQU GIDE+0FH ; IDE Command/Status Register - -CMDHOM EQU 10H ; Home Drive Heads -CMDRD EQU 20H ; Read Sector Command (w/retry) -CMDWR EQU 30H ; Write Sector Command (w/retry) -CMDVER EQU 40H ; Verify Sector(s) Command (w/retry) -CMDFMT EQU 50H ; Format Track Command -CMDDIAG EQU 90H ; Execute Diagnostics Command -CMDINIT EQU 91H ; Initialize Drive Params Command -CMDPW0 EQU 0E0H ; Low Range of Power Control Commands -CMDPW3 EQU 0E3H ; High Range of Power Control Commands -CMDPWQ EQU 0E5H ; Power Status Query Command -CMDID EQU 0ECH ; Read Drive Ident Data Command - ENDIF ;ide -;=================== End Unique Equates ======================= - \ No newline at end of file diff --git a/Source/BPBIOS/dph.lib b/Source/BPBIOS/dph.lib index 72f39fef..1f6021e6 100644 --- a/Source/BPBIOS/dph.lib +++ b/Source/BPBIOS/dph.lib @@ -11,6 +11,11 @@ ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: CSEG + +; Table has been reordered so that hard disk entries come before +; floppy entries. This means that "DRV_A" appears as I: and "DRV_E" +; appears as A:. The point is to allow booting from the first +; hard drive which pretty much needs to be A: DPHTBL: IF DRV_E AND HARDDSK @@ -34,46 +39,46 @@ DPHTBL: DEFW 00 ENDIF - IF DRV_A - DEFW DPH$00 ; Dph for drive A (Floppy) + IF DRV_I AND HARDDSK + DEFW DPH$54 ; Dph for Hard drive Partition I ELSE DEFW 00 ENDIF - IF DRV_B - DEFW DPH$01 ; Dph for drive B (Floppy) + IF DRV_J AND HARDDSK + DEFW DPH$55 ; Dph for Hard drive Partition J ELSE DEFW 00 ENDIF - IF DRV_C - DEFW DPH$02 ; Dph for drive C (Floppy) + IF DRV_K AND HARDDSK + DEFW DPH$56 ; Dph for Hard drive Partition K ELSE DEFW 00 ENDIF - IF DRV_D - DEFW DPH$03 ; Dph for drive D (Floppy) + IF DRV_L AND HARDDSK + DEFW DPH$57 ; Dph for Hard drive Partition L ELSE - DEFW 0 ; Dph for drive D + DEFW 00 ENDIF - IF DRV_I AND HARDDSK - DEFW DPH$54 ; Dph for Hard drive Partition I + IF DRV_A + DEFW DPH$00 ; Dph for drive A (Floppy) ELSE DEFW 00 ENDIF - IF DRV_J AND HARDDSK - DEFW DPH$55 ; Dph for Hard drive Partition J + IF DRV_B + DEFW DPH$01 ; Dph for drive B (Floppy) ELSE DEFW 00 ENDIF - IF DRV_K AND HARDDSK - DEFW DPH$56 ; Dph for Hard drive Partition K + IF DRV_C + DEFW DPH$02 ; Dph for drive C (Floppy) ELSE DEFW 00 ENDIF - IF DRV_L AND HARDDSK - DEFW DPH$57 ; Dph for Hard drive Partition L + IF DRV_D + DEFW DPH$03 ; Dph for drive D (Floppy) ELSE - DEFW 00 + DEFW 0 ; Dph for drive D ENDIF IF DRV_M AND RAMDSK diff --git a/Source/BPBIOS/hardhb.z80 b/Source/BPBIOS/hardhb.z80 index 39d2afab..d19ccb40 100644 --- a/Source/BPBIOS/hardhb.z80 +++ b/Source/BPBIOS/hardhb.z80 @@ -189,7 +189,8 @@ HDSK_RW1: POP BC ; RESTORE INCOMING FUNCTION, DEVICE/UNIT RET NZ ; ABORT IF SEEK RETURNED AN ERROR W/ ERROR IN A LD HL,(HB_DSKBUF) ; GET BUFFER ADDRESS - LD DE,1 ; TRANSFER ONE SECTOR + LD D,BID_HB ; BUFFER IN HBIOS BANK + LD E,1 ; ONE SECTOR CALL HBX_INVOKE ; DO IT OR A ; SET FLAGS RET Z ; DONE IF NO ERROR diff --git a/Source/BPBIOS/hbios.z80 b/Source/BPBIOS/hbios.z80 index be63e92d..c8671c87 100644 --- a/Source/BPBIOS/hbios.z80 +++ b/Source/BPBIOS/hbios.z80 @@ -115,7 +115,7 @@ HBX_INIT: LD (HB_SRCBNK),A LD (HB_DSTBNK),A - IF BANKED + IF BANKED ; Copy vectors from TPA page zero to SYS page zero LD BC,(TPABNK) ; C := TPABNK, B := SYSBNK @@ -127,8 +127,68 @@ HBX_INIT: LD A,(TPABNK) ; Set all Bank regs to TPA ENDIF + +; CSEG +;ORG_CSEG EQU $ +; DSEG +;ORG_DSEG EQU $ + IF BANKED + COMMON /BANK2/ +ORG_BANK2 EQU $ + COMMON /B2RAM/ +ORG_B2RAM EQU $ + ENDIF + + CSEG + + CALL NEWLINE2 + LD DE,HB_STR_TAG + CALL WRITESTR + + IF INTPXY + LD DE,HB_STR_INTPXY + ELSE + LD DE,HB_STR_EXTPXY + ENDIF + CALL WRITESTR + + CALL NEWLINE + LD DE,HB_STR_CSEG + CALL WRITESTR + LD BC,BIOSJT + CALL PRTHEXWORD + LD DE,HB_STR_DSEG + CALL WRITESTR + LD BC,CBOOT + CALL PRTHEXWORD + + IF BANKED + LD DE,HB_STR_BANK2 + CALL WRITESTR + LD BC,ROMJT + CALL PRTHEXWORD + LD DE,HB_STR_B2RAM + CALL WRITESTR + LD BC,CBOOT0 + CALL PRTHEXWORD + LD DE,HB_STR_RESVD + CALL WRITESTR + LD BC,ALV$50 + CALL PRTHEXWORD + ENDIF + + CALL NEWLINE RET + +HB_STR_TAG DB "HBIOS: $" +HB_STR_INTPXY DB "Internal Proxy$" +HB_STR_EXTPXY DB "External Proxy$" +HB_STR_CSEG DB "CSEG=$" +HB_STR_DSEG DB ", DSEG=$" +HB_STR_BANK2 DB ", BANK2=$" +HB_STR_B2RAM DB ", B2RAM=$" +HB_STR_RESVD DB ", RESVD=$" HBX_XCOPY: LD A,C @@ -194,6 +254,9 @@ HBX_ROM: IF MK4 RLCA + JR NC,HBX_BNKSEL1 + XOR 00100001B +HBX_BNKSEL1: RLCA RLCA OUT0 (CPU_BBR),A diff --git a/Source/BPBIOS/romwbw-mk4.lib b/Source/BPBIOS/romwbw-mk4.lib index 09968231..e4dcf70f 100644 --- a/Source/BPBIOS/romwbw-mk4.lib +++ b/Source/BPBIOS/romwbw-mk4.lib @@ -26,15 +26,15 @@ HBCLK EQU YES ; HBIOS clock driver ; ; Set HB_IODEV to appropriate console device ; -HB_IODEV EQU 0 +HB_IODEV EQU 0 ; Assume we want to use first HBIOS serial device ; ; Set HB_HDDEV to appropriate hard disk driver ; -HB_HDDEV EQU 2 +HB_HDDEV EQU 2 ; Assumes disk device #2 is first hard disk device ; ; Set HB_MDDEV to appropriate memory disk driver ; -HB_MDDEV EQU 0 +HB_MDDEV EQU 0 ; Assumes disk device #0 is ROM disk device ; ; RAM/ROM disk sizes expressed as count of 2K blocks ; @@ -66,3 +66,17 @@ MEMTOP EQU 0FFE0H - 1 ; Start of HBIOS 32 byte control block ELSE MEMTOP EQU HBLOC - 1 ; Start of HBIOS 512 byte proxy ENDIF + + IF INTPXY +HB_EI MACRO + ENDM +HB_DI MACRO + ENDM + ELSE +HB_EI MACRO + EI + ENDM +HB_DI MACRO + DI + ENDM + ENDIF diff --git a/Source/BPBIOS/romwbw-sim.lib b/Source/BPBIOS/romwbw-sim.lib index 61087ae5..d666295f 100644 --- a/Source/BPBIOS/romwbw-sim.lib +++ b/Source/BPBIOS/romwbw-sim.lib @@ -26,15 +26,15 @@ HBCLK EQU YES ; HBIOS clock driver ; ; Set HB_IODEV to appropriate console device ; -HB_IODEV EQU 0 +HB_IODEV EQU 0 ; Assume we want to use first HBIOS serial device ; ; Set HB_HDDEV to appropriate hard disk driver ; -HB_HDDEV EQU 2 +HB_HDDEV EQU 2 ; Assumes disk device #2 is first hard disk device ; ; Set HB_MDDEV to appropriate memory disk driver ; -HB_MDDEV EQU 0 +HB_MDDEV EQU 0 ; Assumes disk device #0 is ROM disk device ; ; RAM/ROM disk sizes expressed as count of 2K blocks ; @@ -66,3 +66,17 @@ MEMTOP EQU 0FFE0H - 1 ; Start of HBIOS 32 byte control block ELSE MEMTOP EQU HBLOC - 1 ; Start of HBIOS 512 byte proxy ENDIF + + IF INTPXY +HB_EI MACRO + ENDM +HB_DI MACRO + ENDM + ELSE +HB_EI MACRO + EI + ENDM +HB_DI MACRO + DI + ENDM + ENDIF diff --git a/Source/BPBIOS/util.z80 b/Source/BPBIOS/util.z80 index cf71b8ee..4602fcfa 100644 --- a/Source/BPBIOS/util.z80 +++ b/Source/BPBIOS/util.z80 @@ -90,6 +90,8 @@ PC_PRTCHR: POP AF RET +NEWLINE2: + CALL NEWLINE NEWLINE: CALL PC_CR CALL PC_LF diff --git a/Source/BPBIOS/z33.zex b/Source/BPBIOS/z33.zex index bd8b92d3..ea1e714e 100644 --- a/Source/BPBIOS/z33.zex +++ b/Source/BPBIOS/z33.zex @@ -1,3 +1,3 @@ IOPINIT -LDR SYS.RCP,SYS.NDR,SYS.FCP,WW.Z3T +JETLDR RCP.ZRL,SYS.NDR,FCP.ZRL,MYTERM.Z3T  \ No newline at end of file diff --git a/Source/BPBIOS/z34.zex b/Source/BPBIOS/z34.zex index bd8b92d3..ea1e714e 100644 --- a/Source/BPBIOS/z34.zex +++ b/Source/BPBIOS/z34.zex @@ -1,3 +1,3 @@ IOPINIT -LDR SYS.RCP,SYS.NDR,SYS.FCP,WW.Z3T +JETLDR RCP.ZRL,SYS.NDR,FCP.ZRL,MYTERM.Z3T  \ No newline at end of file diff --git a/Source/BPBIOS/z41.zex b/Source/BPBIOS/z41.zex index 010e3334..aa944b80 100644 --- a/Source/BPBIOS/z41.zex +++ b/Source/BPBIOS/z41.zex @@ -1,4 +1,4 @@ IOPINIT -LDR SYS.NDR,SYS.FCP,WW.Z3T +JETLDR SYS.NDR,FCP.ZRL,MYTERM.Z3T ZSCFG2 CB  \ No newline at end of file diff --git a/Source/Build.cmd b/Source/Build.cmd index 77751057..9419b3ce 100644 --- a/Source/Build.cmd +++ b/Source/Build.cmd @@ -3,7 +3,7 @@ setlocal REM setlocal & call BuildDoc || exit /b 1 & endlocal setlocal & call BuildProp || exit /b 1 & endlocal -setlocal & call BuildImages || exit /b 1 & endlocal setlocal & call BuildShared || exit /b 1 & endlocal REM setlocal & call BuildBP || exit /b 1 & endlocal +setlocal & call BuildImages || exit /b 1 & endlocal setlocal & call BuildROM %* || exit /b 1 & endlocal \ No newline at end of file diff --git a/Source/BuildShared.cmd b/Source/BuildShared.cmd index 13c23f9d..578af84e 100644 --- a/Source/BuildShared.cmd +++ b/Source/BuildShared.cmd @@ -1,13 +1,13 @@ @echo off setlocal -setlocal & cd Apps && call Build || exit /b 1 & endlocal +setlocal & cd CBIOS && call Build || exit /b 1 & endlocal setlocal & cd CPM22 && call Build || exit /b 1 & endlocal setlocal & cd ZCPR && call Build || exit /b 1 & endlocal setlocal & cd ZCPR-DJ && call Build || exit /b 1 & endlocal setlocal & cd ZSDOS && call Build || exit /b 1 & endlocal -setlocal & cd CBIOS && call Build || exit /b 1 & endlocal setlocal & cd CPM3 && call Build || exit /b 1 & endlocal setlocal & cd ZPM3 && call Build || exit /b 1 & endlocal +setlocal & cd Apps && call Build || exit /b 1 & endlocal setlocal & cd Forth && call Build || exit /b 1 & endlocal - +setlocal & cd Fonts && call Build || exit /b 1 & endlocal diff --git a/Source/CBIOS/Makefile b/Source/CBIOS/Makefile new file mode 100644 index 00000000..46e4b507 --- /dev/null +++ b/Source/CBIOS/Makefile @@ -0,0 +1,10 @@ +OBJECTS = cbios_wbw.bin cbios_una.bin +TOOLS = ../../Tools +include $(TOOLS)/Makefile.inc + +cbios_wbw.bin: cbios.asm + $(TASM) -dPLTWBW $< $@ cbios_wbw.lst + +cbios_una.bin: cbios.asm + $(TASM) -dPLTUNA $< $@ cbios_una.lst + diff --git a/Source/CBIOS/cbios.asm b/Source/CBIOS/cbios.asm index 971d3690..aa245aae 100644 --- a/Source/CBIOS/cbios.asm +++ b/Source/CBIOS/cbios.asm @@ -300,9 +300,8 @@ DPBCNT .EQU ($ - DPBMAP) / 2 BOOT: ; STANDARD BOOT INVOCATION LD SP,STACK ; STACK FOR INITIALIZATION - ; - ; COPY INITIALIZATION CODE TO RUNNINT LOCATION $8000 + ; COPY INITIALIZATION CODE TO RUNNING LOCATION $8000 LD HL,BUFPOOL LD DE,$8000 LD BC,CBIOS_END - BUFPOOL @@ -351,7 +350,7 @@ WBOOT: #IFDEF PLTUNA ; RESTORE COMMAND PROCESSOR FROM UNA BIOS CACHE LD BC,$01FB ; UNA FUNC = SET BANK - LD DE,BID_BIOS ; UBIOS_PAGE (SEE PAGES.INC) + LD DE,(BNKBIOS) ; UBIOS_PAGE (SEE PAGES.INC) RST 08 ; DO IT PUSH DE ; SAVE PREVIOUS BANK @@ -1514,6 +1513,11 @@ BNKBIOS .DB 0 ; BIOS BANK ID BNKUSER .DB 0 ; USER BANK ID #ENDIF ; +#IFDEF PLTUNA +BNKBIOS .DW 0 ; BIOS BANK ID +BNKUSER .DW 0 ; USER BANK ID +#ENDIF +; ; DOS DISK VARIABLES ; DSKOP .DB 0 ; DISK OPERATION (DOP_READ/DOP_WRITE) @@ -1670,8 +1674,8 @@ DPB_RF: .DB 15 ; BLM: BLOCK MASK .DB 0 ; EXM: EXTENT MASK .DW 2047 ; DSM: TOTAL STORAGE IN BLOCKS - 1 BLK = (4MB / 2K BLS) - 1 = 2047 - .DW 255 ; DRM: DIR ENTRIES - 1 = 256 - 1 = 255 - .DB 11110000B ; AL0: DIR BLK BIT MAP, FIRST BYTE + .DW 511 ; DRM: DIR ENTRIES - 1 = 256 - 1 = 255 + .DB 11111111B ; AL0: DIR BLK BIT MAP, FIRST BYTE .DB 00000000B ; AL1: DIR BLK BIT MAP, SECOND BYTE .DW 0 ; CKS: ZERO FOR NON-REMOVABLE MEDIA .DW 0 ; OFF: RESERVED TRACKS = 0 TRK @@ -1822,14 +1826,24 @@ INIT: LD (CBIOS_LOC + 1),HL ; STORE IT IN FIRST ENTRY OF CBIOS JUMP TABLE #IFDEF PLTUNA + ; GET CRITICAL BANK ID'S + LD BC,$03FA ; UNA FUNC = GET CUR EXEC ENV + CALL $FFFD ; DE = CUR EXEC PAGE, HL = UBIOS PAGE + LD (BNKBIOS),HL ; SAVE UBIOS PAGE + LD BC,$05FA ; UNA FUNC = GET USER EXEC ENV + CALL $FFFD ; DE = USER LOW PAGE, HL = USER HIGH PAGE (COMMMON) + LD (BNKUSER),DE ; SAVE USER PAGE + LD DE,$8000 ; RAM DRIVE STARTS AT FIRST RAM BANK + LD (BNKRAMD),DE ; SAVE STARTING RAM DRIVE BANK + ; MAKE SURE UNA EXEC PAGE IS ACTIVE LD BC,$01FB ; UNA FUNC = SET BANK - LD DE,BID_USR ; SWITCH BACK TO EXEC BANK + LD DE,(BNKUSER) ; SWITCH BACK TO EXEC BANK CALL $FFFD ; DO IT (RST 08 NOT YET INSTALLED) ; COPY BIOS PAGE ZERO TO USER BANK LD BC,$01FB ; UNA FUNC = SET BANK - LD DE,BID_BIOS ; UBIOS_PAGE (SEE PAGES.INC) + LD DE,(BNKBIOS) ; UBIOS_PAGE (SEE PAGES.INC) CALL $FFFD ; DO IT (RST 08 NOT YET INSTALLED) PUSH DE ; SAVE PREVIOUS BANK @@ -1852,6 +1866,7 @@ INIT: LD (8),A ; STORE AT 0x0008 LD HL,($FFFE) ; UNA ENTRY VECTOR LD (9),HL ; STORE AT 0x0009 + #ELSE ; GET CRITICAL BANK ID'S LD B,BF_SYSGET ; HBIOS FUNC=GET SYS INFO @@ -1886,10 +1901,6 @@ INIT: LD A,DEF_IOBYTE ; LOAD DEFAULT IOBYTE LD (IOBYTE),A ; STORE IT - ; INIT DEFAULT DRIVE TO A: FOR NOW - XOR A ; ZERO - LD (DEFDRIVE),A ; STORE IT - ; CBIOS BANNER CALL NEWLINE2 ; FORMATTING LD DE,STR_BANNER ; POINT TO BANNER @@ -1904,7 +1915,7 @@ INIT: LD (CCPBUF),HL ; SAVE THE ADDRESS (IN BIOS MEM) LD BC,$01FB ; UNA FUNC = SET BANK - LD DE,BID_BIOS ; UBIOS_PAGE (SEE PAGES.INC) + LD DE,(BNKBIOS) ; UBIOS_PAGE (SEE PAGES.INC) RST 08 ; DO IT PUSH DE ; SAVE PREVIOUS BANK @@ -1998,23 +2009,49 @@ AUTOSUB: ; ; SETUP AUTO SUBMIT COMMAND (IF REQUIRED FILES EXIST) LD A,(DEFDRIVE) ; GET DEFAULT DRIVE + + ;CALL PRTHEXBYTE + + PUSH AF + INC A ; CONVERT FROM DRIVE NUM TO FCB DRIVE CODE LD (FCB_SUB),A ; SET DRIVE OF SUBMIT.COM FCB LD (FCB_PRO),A ; SET DRIVE OF PROFILE.SUB FCB +; + LD C,13 ; RESET DISK SYSTEM + CALL BDOS + + ;CALL PC_PERIOD + + POP AF +; + ;DEC A ; BACK TO ZERO INDEX + + ;LD E,A ; PUT IN E + ;LD C,14 ; SELECT DISK FUNCTION + ;CALL BDOS ; DO IT + + ;CALL PC_PERIOD ; LD C,17 ; BDOS FUNCTION: FIND FIRST LD DE,FCB_SUB ; CHECK FOR SUBMIT.COM CALL BDOS ; INVOKE BDOS TO LOOK FOR FILE + + ;CALL PRTHEXBYTE + INC A ; CHECK FOR ERR, $FF --> $00 RET Z ; ERR, DO NOT ATTEMPT AUTO SUBMIT ; LD C,17 ; BDOS FUNCTION: FIND FIRST LD DE,FCB_PRO ; CHECK FOR PROFILE.SUB CALL BDOS ; INVOKE BDOS TO LOOK FOR FILE + + ;CALL PRTHEXBYTE + INC A ; CHECK FOR ERR, $FF --> $00 RET Z ; ERR, DO NOT ATTEMPT AUTO SUBMIT ; - LD HL,CMD ; ADDRESS OF STARTUP COMMAND + LD HL,CMD ; ADDRESS OF STARTUP COMMANDS LD DE,CCP_LOC + 7 ; START OF COMMAND BUFFER IN CCP LD BC,CMDLEN ; LENGTH OF AUTOSTART COMMAND LDIR ; PATCH COMMAND LINE INTO CCP @@ -2168,7 +2205,7 @@ MD_INIT4: #IF (CLRRAMDISK != CLR_NEVER) DI ; NO INTERRUPTS LD BC,$01FB ; UNA FUNC = SET BANK - LD DE,BID_RAMD0 ; FIRST BANK OF RAM DISK + LD DE,(BNKRAMD) ; FIRST BANK OF RAM DISK CALL $FFFD ; DO IT (RST 08 NOT SAFE HERE) #IF (CLRRAMDISK == CLR_AUTO) @@ -2193,7 +2230,7 @@ CLRRAM1: CLRRAM2: #ENDIF LD BC,$01FB ; UNA FUNC = SET BANK - LD DE,BID_USR ; SWITCH BACK TO EXEC BANK FOR WRITESTR + LD DE,(BNKUSER) ; SWITCH BACK TO EXEC BANK FOR WRITESTR CALL $FFFD ; DO IT (RST 08 NOT SAFE HERE) CALL NEWLINE2 ; FORMATTING @@ -2201,7 +2238,7 @@ CLRRAM2: CALL WRITESTR ; DISPLAY IT LD BC,$01FB ; UNA FUNC = SET BANK - LD DE,BID_RAMD0 ; FIRST BANK OF RAM DISK + LD DE,(BNKRAMD) ; FIRST BANK OF RAM DISK CALL $FFFD ; DO IT (RST 08 NOT SAFE HERE) LD HL,0 ; SOURCE ADR FOR FILL @@ -2210,7 +2247,7 @@ CLRRAM2: CALL FILL ; DO IT CLRRAM3: LD BC,$01FB ; UNA FUNC = SET BANK - LD DE,BID_USR ; SWITCH BACK TO EXEC BANK + LD DE,(BNKUSER) ; SWITCH BACK TO EXEC BANK CALL $FFFD ; DO IT (RST 08 NOT SAFE HERE) EI ; RESUME INTERRUPTS @@ -2283,6 +2320,14 @@ DRV_INIT: LD D,L ; SAVE L AS UNIT LD E,0 ; SLICE IS ZERO LD (BOOTVOL),DE ; D -> UNIT, E -> SLICE +; + ; INIT DEFAULT + LD A,D ; BOOT UNIT? + CP 1 ; IF ROM BOOT, DEF DRIVE SHOULD BE B: + JR Z,DRV_INIT1 ; ... SO LEAVE AS IS AND SKIP AHEAD + XOR A ; ELSE FORCE TO DRIVE A: +DRV_INIT1: + LD (DEFDRIVE),A ; STORE IT ; ; SETUP THE DRVMAP STRUCTURE LD HL,(HEAPTOP) ; GET CURRENT HEAP TOP @@ -2330,6 +2375,14 @@ DRV_INIT5: LD A,E ; SLICES PER VOLUME VALUE TO ACCUM LD (HDSPV),A ; SAVE IT ; + LD DE,(BOOTVOL) ; BOOT VOLUME (UNIT, SLICE) + LD A,1 ; ROM DISK UNIT? + CP D ; CHECK IT + JR Z,DRV_INIT5A ; IF SO, SKIP BOOT DRIVE + LD B,1 ; JUST ONE SLICE PLEASE + CALL DRV_INIT8A ; DO THE BOOT DEVICE +; +DRV_INIT5A: ; SETUP TO ENUMERATE DEVICES TO BUILD DRVMAP LD B,0 ; START WITH UNIT 0 ; @@ -2358,7 +2411,21 @@ DRV_INIT7: ; PROCESS CURRENT UNIT (SEE UNA PROTOIDS.INC) LD A,(HDSPV) ; GET SLICES PER VOLUME TO ACCUM LD B,A ; MOVE TO B FOR LOOP COUNTER ; -DRV_INIT8: ; SLICE CREATION LOOP +DRV_INIT8: + ; SLICE CREATION LOOP + ; DE=UNIT/SLICE, B=SLICE CNT + LD A,(BOOTVOL + 1) ; GET BOOT UNIT + CP 1 ; ROM BOOT? + JR Z,DRV_INIT8A ; IF SO, OK TO CONTINUE + CP D ; COMPARE TO CUR UNIT + JR NZ,DRV_INIT8A ; IF NE, OK TO CONTINUE + LD A,(BOOTVOL) ; GET BOOT SLICE + CP E ; COMPARE TO CUR SLICE + JR NZ,DRV_INIT8A ; IF NE, OK TO CONTINUE + INC E ; IS BOOT DU/SLICE, SKIP IT + JR DRV_INIT8 ; AND RESTART LOOP +; +DRV_INIT8A: ; ENTRY POINT TO SKIP BOOT DISK/LU CHECK ; ; INC DRVMAP ENTRY COUNT AND CHECK FOR 16 ENTRY MAXIMUM LD HL,(DRVMAPADR) ; POINT TO DRIVE MAP @@ -2391,6 +2458,14 @@ DRV_INIT: ; GET BOOT UNIT/SLICE INFO LD DE,(HCB + HCB_BOOTVOL) ; BOOT VOLUME (UNIT, SLICE) LD (BOOTVOL),DE ; D -> UNIT, E -> SLICE +; + ; INIT DEFAULT + LD A,D ; BOOT UNIT? + CP 1 ; IF ROM BOOT, DEF DRIVE SHOULD BE B: + JR Z,DRV_INIT1 ; ... SO LEAVE AS IS AND SKIP AHEAD + XOR A ; ELSE FORCE TO DRIVE A: +DRV_INIT1: + LD (DEFDRIVE),A ; STORE IT ; ; SETUP THE DRVMAP STRUCTURE LD HL,(HEAPTOP) ; GET CURRENT HEAP TOP @@ -2398,7 +2473,11 @@ DRV_INIT: LD (DRVMAPADR),HL ; SAVE AS DRVMAP ADDRESS LD (HEAPTOP),HL ; AND AS NEW HEAP TOP ; - ; SETUP TO LOOP THROUGH AVAILABLE DEVICES + ; SETUP TO LOOP THROUGH AVAILABLE DEVICES BUILDING LIST OF + ; ACTIVE UNITS AND COUNTING NUMBER OF ACTIVE HARD DISK + ; DEVICES. NON-HARD DISK UNITS ARE ALWAYS CONSIDERED + ; ACTIVE, BUT HARD DISK UNITS ARE ONLY CONSIDERED ACTIVE + ; IF THERE IS MEDIA IN THE DRIVE. LD B,BF_SYSGET LD C,BF_SYSGET_DIOCNT RST 08 ; E := DISK UNIT COUNT @@ -2408,31 +2487,76 @@ DRV_INIT: RET Z ; HANDLE ZERO DEVICES (ALBEIT POORLY) ; ; LOOP THRU DEVICES TO COUNT TOTAL HARD DISK VOLUMES - PUSH BC ; SAVE THE DEVICE COUNT - LD C,0 ; USE C AS DEVICE LIST INDEX - LD E,0 ; INIT E FOR HARD DISK VOLUME COUNT + LD C,0 ; INIT C AS DEVICE LIST INDEX + LD D,0 ; INIT D AS TOTAL DEVICE COUNT + LD E,0 ; INIT E FOR HARD DISK DEVICE COUNT + LD HL,DRVLST ; INIT HL PTR TO DRIVE LIST ; DRV_INIT2: - PUSH BC ; SAVE LOOP CONTROL CALL DRV_INIT3 ; CHECK DRIVE - POP BC ; RESTORE LOOP CONTROL INC C ; NEXT UNIT DJNZ DRV_INIT2 ; LOOP - POP BC ; RESTORE UNIT COUNT IN B + LD A,D ; TOTAL DEVICE COUNT TO D + LD (DRVLSTC),A ; SAVE THE COUNT JR DRV_INIT4 ; CONTINUE ; DRV_INIT3: PUSH DE ; SAVE DE (HARD DISK VOLUME COUNTER) + PUSH HL ; SAVE DRIVE LIST PTR + PUSH BC ; SAVE LOOP CONTROL LD B,BF_DIODEVICE ; HBIOS FUNC: REPORT DEVICE INFO RST 08 ; CALL HBIOS, UNIT TO C LD A,D ; DEVICE TYPE TO A + POP BC ; RESTORE LOOP CONTROL + POP HL ; RESTORE DRIVE LIST PTR POP DE ; RESTORE DE CP DIODEV_IDE ; HARD DISK DEVICE? - RET C ; NOPE, RETURN + JR NC,DRV_INIT3A ; IF SO, HANDLE SPECIAL + LD (HL),C ; SAVE UNIT NUM IN LIST + INC HL ; BUMP PTR + INC D ; INC TOTAL DEVICE COUNT + RET +; +DRV_INIT3A: + ; CHECK FOR ACTIVE AND RETURN IF NOT + PUSH DE ; SAVE DE (HARD DISK VOLUME COUNTER) + PUSH HL ; SAVE DRIVE LIST PTR + PUSH BC ; SAVE LOOP CONTROL + + LD B,BF_DIOMEDIA ; HBIOS FUNC: SENSE MEDIA + LD E,1 ; PERFORM MEDIA DISCOVERY + RST 08 + + POP BC ; RESTORE LOOP CONTROL + POP HL ; RESTORE DRIVE LIST PTR + POP DE ; RESTORE DE + + RET NZ ; IF NO MEDIA, JUST RETURN + + ; IF ACTIVE... + LD (HL),C ; SAVE UNIT NUM IN LIST + INC HL ; BUMP PTR + INC D ; INC TOTAL DEVICE COUNT INC E ; INCREMENT HARD DISK COUNT RET ; AND RETURN ; DRV_INIT4: ; SET SLICES PER VOLUME (HDSPV) BASED ON HARD DISK VOLUME COUNT + +; ; *** DEBUG *** +; CALL NEWLINE2 +; LD A,(DRVLSTC) +; LD B,A +; CALL PRTHEXBYTE +; LD A,' ' +; CALL COUT +; LD HL,DRVLST +;TEMP1: +; LD A,(HL) +; INC HL +; CALL PRTHEXBYTE +; DJNZ TEMP1 +; ; *** DEBUG *** + LD A,E ; HARD DISK VOLUME COUNT TO A LD E,8 ; ASSUME 8 SLICES PER VOLUME DEC A ; DEC ACCUM TO CHECK FOR COUNT = 1 @@ -2446,14 +2570,28 @@ DRV_INIT5: LD A,E ; SLICES PER VOLUME VALUE TO ACCUM LD (HDSPV),A ; SAVE IT ; - ; SETUP TO ENUMERATE DEVICES TO BUILD DRVMAP - LD B,BF_SYSGET - LD C,BF_SYSGET_DIOCNT - RST 08 ; E := DISK UNIT COUNT - LD B,E ; COUNT TO B - LD C,0 ; USE C AS DEVICE LIST INDEX +; ; SETUP TO ENUMERATE DEVICES TO BUILD DRVMAP +; LD B,BF_SYSGET +; LD C,BF_SYSGET_DIOCNT +; RST 08 ; E := DISK UNIT COUNT +; LD B,E ; COUNT TO B +; LD C,0 ; USE C AS DEVICE LIST INDEX +; + LD DE,(BOOTVOL) ; BOOT VOLUME (UNIT, SLICE) + LD A,1 ; ROM DISK UNIT? + CP D ; CHECK IT + JR Z,DRV_INIT5A ; IF SO, SKIP BOOT DRIVE + LD B,1 ; JUST ONE SLICE PLEASE + CALL DRV_INIT8A ; DO THE BOOT DEVICE +; +DRV_INIT5A: + LD A,(DRVLSTC) ; ACTIVE DRIVE LIST COUNT TO ACCUM + LD B,A ; ... AND MOVE TO B FOR LOOP COUNTER + LD HL,DRVLST ; HL IS PTR TO ACTIVE DRIVE LIST ; DRV_INIT6: ; LOOP THRU ALL UNITS AVAILABLE + PUSH HL ; PRESERVE DRIVE LIST PTR + LD C,(HL) ; GET UNIT NUM FROM LIST PUSH BC ; PRESERVE LOOP CONTROL LD B,BF_DIODEVICE ; HBIOS FUNC: REPORT DEVICE INFO RST 08 ; CALL HBIOS, D := DEVICE TYPE @@ -2462,6 +2600,8 @@ DRV_INIT6: ; LOOP THRU ALL UNITS AVAILABLE CALL DRV_INIT7 ; MAKE DRIVE MAP ENTRY(S) POP BC ; RESTORE LOOP CONTROL INC C ; INCREMENT LIST INDEX + POP HL ; RESTORE DRIVE LIST PTR + INC HL ; INCREMENT ACTIVE DRIVE LIST PTR DJNZ DRV_INIT6 ; LOOP AS NEEDED RET ; FINISHED ; @@ -2475,7 +2615,21 @@ DRV_INIT7: ; PROCESS UNIT LD A,(HDSPV) ; GET SLICES PER VOLUME TO ACCUM LD B,A ; MOVE TO B FOR LOOP COUNTER ; -DRV_INIT8: ; SLICE CREATION LOOP +DRV_INIT8: + ; SLICE CREATION LOOP + ; DE=UNIT/SLICE, B=SLICE CNT + LD A,(BOOTVOL + 1) ; GET BOOT UNIT + CP 1 ; ROM BOOT? + JR Z,DRV_INIT8A ; IF SO, OK TO CONTINUE + CP D ; COMPARE TO CUR UNIT + JR NZ,DRV_INIT8A ; IF NE, OK TO CONTINUE + LD A,(BOOTVOL) ; GET BOOT SLICE + CP E ; COMPARE TO CUR SLICE + JR NZ,DRV_INIT8A ; IF NE, OK TO CONTINUE + INC E ; IS BOOT DU/SLICE, SKIP IT + JR DRV_INIT8 ; AND RESTART LOOP +; +DRV_INIT8A: ; ENTRY POINT TO SKIP BOOT DISK/LU CHECK ; ; INC DRVMAP ENTRY COUNT AND ENFORCE FOR 16 ENTRY MAXIMUM LD HL,(DRVMAPADR) ; POINT TO DRIVE MAP @@ -2558,20 +2712,20 @@ DPH_INIT1: CALL PRTDRV ; PRINT DRIVE INFO LD A,D ; A := UNIT PUSH HL ; SAVE DRIVE MAP POINTER - PUSH AF ; SAVE UNIT - ; MATCH AND SAVE DEFAULT DRIVE BASED ON BOOT UNIT/SLICE - LD HL,BOOTVOL + 1 ; POINT TO BOOT UNIT - LD A,D ; LOAD CURRENT UNIT - CP (HL) ; MATCH? - JR NZ,DPH_INIT1A ; BYPASS IF NOT BOOT UNIT - DEC HL ; POINT TO BOOT SLICE - LD A,E ; LOAD CURRENT SLICE - CP (HL) ; MATCH? - JR NZ,DPH_INIT1A ; BYPASS IF NOT BOOT SLICE - LD A,C ; LOAD THE CURRENT DRIVE NUM - LD (DEFDRIVE),A ; SAVE AS DEFAULT + ;PUSH AF ; SAVE UNIT + ;; MATCH AND SAVE DEFAULT DRIVE BASED ON BOOT UNIT/SLICE + ;LD HL,BOOTVOL + 1 ; POINT TO BOOT UNIT + ;LD A,D ; LOAD CURRENT UNIT + ;CP (HL) ; MATCH? + ;JR NZ,DPH_INIT1A ; BYPASS IF NOT BOOT UNIT + ;DEC HL ; POINT TO BOOT SLICE + ;LD A,E ; LOAD CURRENT SLICE + ;CP (HL) ; MATCH? + ;JR NZ,DPH_INIT1A ; BYPASS IF NOT BOOT SLICE + ;LD A,C ; LOAD THE CURRENT DRIVE NUM + ;LD (DEFDRIVE),A ; SAVE AS DEFAULT DPH_INIT1A: - POP AF ; RESTORE UNIT + ;POP AF ; RESTORE UNIT LD DE,(DPHTOP) ; GET ADDRESS OF NEXT DPH PUSH DE ; ... AND SAVE IT ; INVOKE THE DPH BUILD ROUTINE @@ -2878,8 +3032,16 @@ DPHTOP .DW 0 ; CURRENT TOP OF DPH POOL DIRBUF .DW 0 ; DIR BUF POINTER HEAPTOP .DW BUFPOOL ; CURRENT TOP OF HEAP BOOTVOL .DW 0 ; BOOT VOLUME, MSB=BOOT UNIT, LSB=BOOT SLICE -BNKRAMD .DB 0 ; STARTING BANK ID FOR RAM DRIVE HDSPV .DB 2 ; SLICES PER VOLUME FOR HARD DISKS (MUST BE >= 1) +DRVLST .FILL 32 ; ACTIVE DRIVE LIST USED DURINT DRV_INIT +DRVLSTC .DB 0 ; ENTRY COUNT FOR ACTIVE DRIVE LIST +; +#IFDEF PLTWBW +BNKRAMD .DB 0 ; STARTING BANK ID FOR RAM DRIVE (WBW) +#ENDIF +#IFDEF PLTUNA +BNKRAMD .DW 0 ; STARTING BANK ID FOR RAM DRIVE (UNA) +#ENDIF ; CMD .DB CMDLEN - 2 .TEXT "SUBMIT PROFILE" diff --git a/Source/CBIOS/ver.inc b/Source/CBIOS/ver.inc index ed96d795..8320a3c2 100644 --- a/Source/CBIOS/ver.inc +++ b/Source/CBIOS/ver.inc @@ -2,4 +2,4 @@ #DEFINE RMN 9 #DEFINE RUP 2 #DEFINE RTP 0 -#DEFINE BIOSVER "2.9.2-pre.25" +#DEFINE BIOSVER "2.9.2-pre.33" diff --git a/Source/CPM22/Build.cmd b/Source/CPM22/Build.cmd index 93a836cb..5c1effaa 100644 --- a/Source/CPM22/Build.cmd +++ b/Source/CPM22/Build.cmd @@ -32,6 +32,14 @@ zx MLOAD25 -OS2CCP.BIN=OS2CCP.HEX zx MAC -OS3BDOS.ASM -$PO zx MLOAD25 -OS3BDOS.BIN=OS3BDOS.HEX +tasm -t80 -g3 -fFF loader.asm loader.bin loader.lst + +copy /b os2ccp.bin + os3bdos.bin + ..\cbios\cbios_wbw.bin cpm_wbw.bin +copy /b os2ccp.bin + os3bdos.bin + ..\cbios\cbios_una.bin cpm_una.bin + +copy /b loader.bin + cpm_wbw.bin cpm_wbw.sys +copy /b loader.bin + cpm_una.bin cpm_una.sys + goto :eof :asm diff --git a/Source/CPM22/Clean.cmd b/Source/CPM22/Clean.cmd index e2e6145a..243e3c08 100644 --- a/Source/CPM22/Clean.cmd +++ b/Source/CPM22/Clean.cmd @@ -5,3 +5,4 @@ if exist *.bin del *.bin if exist *.lst del *.lst if exist *.prn del *.prn if exist *.hex del *.hex +if exist *.sys del *.sys diff --git a/Source/CPM22/Makefile b/Source/CPM22/Makefile new file mode 100644 index 00000000..b1f37b0d --- /dev/null +++ b/Source/CPM22/Makefile @@ -0,0 +1,16 @@ +SYSFILES = cpm_wbw.sys cpm_una.sys +BINFILES = cpm_wbw.bin cpm_una.bin +OBJECTS = CCP.bin BDOS.bin CCP22.bin BDOS22.bin OS2CCP.bin OS3BDOS.bin \ + ccpb03.bin bdosb01.bin loader.bin $(SYSFILES) $(BINFILES) +OTHERS = *.hex +TOOLS = ../../Tools +include $(TOOLS)/Makefile.inc + +%.sys: %.bin loader.bin + cat loader.bin $*.bin > $@ + +cpm_una.bin: OS2CCP.bin OS3BDOS.bin ../CBIOS/cbios_una.bin + cat OS2CCP.bin OS3BDOS.bin ../CBIOS/cbios_una.bin > $@ + +cpm_wbw.bin: OS2CCP.bin OS3BDOS.bin ../CBIOS/cbios_wbw.bin + cat OS2CCP.bin OS3BDOS.bin ../CBIOS/cbios_wbw.bin > $@ diff --git a/Source/CPM22/ccpb03.asm b/Source/CPM22/ccpb03.asm index dd3c8462..5fef8040 100644 --- a/Source/CPM22/ccpb03.asm +++ b/Source/CPM22/ccpb03.asm @@ -803,7 +803,7 @@ RESETDR:LD A,(CHGDRV) ; DRIVE CHANGE INDICATED? ;************************************************************** ; .IF MON -MONITOR:RST 38 +MONITOR:RST 38H .ENDIF ; diff --git a/Source/CPM22/loader.asm b/Source/CPM22/loader.asm new file mode 100644 index 00000000..93cac556 --- /dev/null +++ b/Source/CPM22/loader.asm @@ -0,0 +1,228 @@ +;=============================================================================== +; LOADER.ASM +; +; BOOTLOADER FOR ROMWBW DISK OPERATING SYSTEMS. +; +; CP/M DISK FORMATS ALLOW FOR RESERVED TRACKS THAT CONTAIN AN IMAGE OF THE +; OPERATING SYSTEM TO BE LOADED WHEN THE DISK IS BOOTED. THE OPERATING SYSTEM +; IMAGE ITSELF IS NORMALLY PREFIXED BY A 1-N SECTORS CONTAINING OS BOOTSTRAP +; CODE AND DISK METADATA. +; +; THE RETROBREW COMPUTING GROUP HAS BEEN USING A CONVENTION OF PREFIXING THE +; OS IMAGE WITH 3 SECTORS (512 BYTES X 3 FOR A TOTAL OF 1536 BYTES): +; +; SECTOR 1: IBM-PC STYLE BOOT BLOCK CONTAINING BOOTSTRAP, +; PARTITION TABLE, AND BOOT SIGNATURE +; SECTOR 2: RESERVED +; SECTOR 3: METADATA +; +; THE HARDWARE BIOS IS EXPECTED TO READ AND LOAD THE FIRST TWO SECTORS FROM THE +; DISK TO MEMORY ADDRESS $8000 AND JUMP TO THAT LOCATION TO BEGIN THE BOOT +; PROCESS. THE BIOS IS EXPECTED TO VERIFY THAT A STANDARD BOOT SIGNATURE +; OF $55, $AA IS PRESENT AT OFFSET $1FE-$1FF. IF THE SIGNATURE IS NOT FOUND, +; THE BIOS SHOULD ASSUME THE DISK HAS NOT BEEN PROPERLY INITIALIZED AND SHOULD +; NOT JUMP TO THE LOAD ADDRESS. +; +;=============================================================================== +; +#INCLUDE "ver.inc" +; +SYS_ENT .EQU $E600 ; SYSTEM (OS) ENTRY POINT ADDRESS +SYS_LOC .EQU $D000 ; STARTING ADDRESS TO LOAD SYSTEM IMAGE +SYS_END .EQU $FE00 ; ENDING ADDRESS OF SYSTEM IMAGE +; +BYT .EQU 1 ; used to describe METADATA_SIZE below +WRD .EQU 2 +; +SECTOR_SIZE .EQU 512 +BLOCK_SIZE .EQU 128 +PREFIX_SIZE .EQU (3 * SECTOR_SIZE) ; 3 SECTORS +METADATA_SIZE .EQU BYT+WRD+(4*BYT)+16+BYT+WRD+WRD+WRD+WRD ; (as defined below) +; +PARTTBL_LOC .EQU $1BE +PARTTBL_SIZ .EQU $40 +BOOTSIG_LOC .EQU $1FE +; +;------------------------------------------------------------------------------- +; SECTOR 1 +; +; THIS SECTOR FOLLOWS THE CONVENTIONS OF AN IBM-PC MBR CONTAINING THE OS +; BOOTSTRAP CODE, PARTITION TABLE, AND BOOT SIGNATURE +; +;---------------------------------------------------------------------------- +; +; THE FOLLOWING BOOTSTRAP CODE IS BUILT TO ASSUME IT WILL BE EXECUTED AT A STARTING +; ADDRESS OF $8000. +; + .ORG $8000 + JR BOOT +; +BOOT: + LD DE,STR_LOAD ; LOADING STRING + CALL PRTSTR ; PRINT + CALL PRTDOT ; PROGRESS +; + LD BC,$00FC ; UNA FUNC: GET BOOTSTRAP HISTORY + CALL $FFFD ; CALL UNA + JR NZ,ERROR ; HANDLE ERROR + CALL PRTDOT ; PROGRESS + LD B,L ; MOVE BOOT UNIT ID TO B +; + LD C,$41 ; UNA FUNC: SET LBA + LD DE,0 ; HI WORD ALWAYS ZERO + LD HL,3 ; IMAGE STARTS AT FOURTH SECTOR + CALL $FFFD ; SET LBA + JR NZ,ERROR ; HANDLE ERROR + CALL PRTDOT ; PROGRESS +; + LD C,$42 ; UNA FUNC: READ SECTORS + LD DE,$D000 ; STARTING ADDRESS FOR IMAGE + LD L,22 ; READ 22 SECTORS + CALL $FFFD ; DO READ + JR NZ,ERROR ; HANDLE ERROR + CALL PRTDOT ; PROGRESS +; + LD DE,STR_DONE ; DONE MESSAGE + CALL PRTSTR ; PRINT IT +; + LD D,B ; PASS BOOT UNIT TO OS + LD E,0 ; ASSUME LU IS ZERO + JP SYS_ENT ; GO TO SYSTEM +; +PRTCHR: + PUSH BC + PUSH DE + LD BC,$0012 ; UNIT 0, WRITE CHAR + LD E,A ; CHAR TO PRINT + CALL $FFFD ; PRINT + POP DE + POP BC + RET +; +PRTSTR: + PUSH BC + PUSH HL + LD BC,$0015 ; UNIT 0, WRITE CHARS UNTIL TERMINATOR + LD L,0 ; TERMINATOR IS NULL + CALL $FFFD ; PRINT + POP HL + POP BC + RET +; +PRTDOT: + LD A,'.' ; DOT CHARACTER + JR PRTCHR ; PRINT AND RETURN +; +; PRINT THE HEX BYTE VALUE IN A +; +PRTHEXBYTE: + PUSH AF + PUSH DE + CALL HEXASCII + LD A,D + CALL PRTCHR + LD A,E + CALL PRTCHR + POP DE + POP AF + RET +; +; CONVERT BINARY VALUE IN A TO ASCII HEX CHARACTERS IN DE +; +HEXASCII: + LD D,A + CALL HEXCONV + LD E,A + LD A,D + RLCA + RLCA + RLCA + RLCA + CALL HEXCONV + LD D,A + RET +; +; CONVERT LOW NIBBLE OF A TO ASCII HEX +; +HEXCONV: + AND 0FH ;LOW NIBBLE ONLY + ADD A,90H + DAA + ADC A,40H + DAA + RET +; +ERROR: + LD DE,STR_ERR ; POINT TO ERROR STRING + CALL PRTSTR ; PRINT IT + HALT ; HALT +; +; DATA +; +STR_LOAD .DB "\r\nLoading",0 +STR_DONE .DB "\r\n",0 +STR_ERR .DB " Read Error!",0 +; + .ORG $ - $8000 ; RESTORE ORG + .FILL PARTTBL_LOC - $ ; FILL TO START OF PARTITION TABLE +; +; RESERVE SPACE FOR STANDARD IBM-PC PARTITION TABLE. ALTHOUGH A +; PARTITION TABLE IS NOT RELEVANT FOR A FLOPPY DISK, IT DOES NO HARM. +; THE CONTENTS OF THE PARTITION TABLE MUST BE MANAGED BY FDISK80. +; +PARTTBL .FILL PARTTBL_SIZ,$00 ; PARTITION TABLE, FILL WITH ZEROES +; +; THE END OF THE FIRST SECTOR MUST CONTAIN THE TWO BYTE BOOT +; SIGNATURE. +; +BOOTSIG .DB $55,$AA ; STANDARD BOOT SIGNATURE +; +;------------------------------------------------------------------------------- +; SECTOR 2 +; +; THIS SECTOR HAS NOT BEEN DEFINED AND IS RESERVED. +; +;---------------------------------------------------------------------------- +; + .FILL 512,0 ; JUST FILL SECTOR WITH ZEROES +; +;------------------------------------------------------------------------------- +; SECTOR 3 +; +; OS AND DISK METADATA +; +;---------------------------------------------------------------------------- +; + .FILL 128 * 3,0 ; FIRST 384 BYTES ARE NOT YET DEFINED +; +; THE FOLLOWING TWO BYTES ARE AN ADDITIONAL SIGNATURE THAT IS VERIFIED BY +; SOME HARDWARE BIOSES. +; +PR_SIG .DB $5A,$A5 ; SIGNATURE GOES HERE +; +; FIRST CHUNK OF METADATA IMMEDIATELY FOLLOWS THE SIGNATURE BYTES +; +PR_PLATFORM .DB 0 ; PLATFORM ID (SEE STD.ASM) +PR_DEVICE .DB 0 ; ? (PROBABLY UNUSED) +PR_FORMATTER .DB 0,0,0,0,0,0,0,0 ; ? (PROBABLY UNUSED) +PR_DRIVE .DB 0 ; ? (PROBABLY UNUSED) +PR_LOG_UNIT .DW 0 ; ? (PROBABLY UNUSED) +; +; FILLER TO PLACE SECOND CHUNK OF METADATA AT THE END OF THE SECTOR +; + .FILL ((PREFIX_SIZE - METADATA_SIZE) - $),00H +; +; SECOND CHUNK OF METADATA +; +PR_WP .DB 0 ; WRITE PROTECT BOOLEADN +PR_UPDSEQ .DW 0 ; PREFIX UPDATE SEQUENCE NUMBER (DEPRECATED?) +PR_VER .DB RMJ,RMN,RUP,RTP ; OS BUILD VERSION +PR_LABEL .DB "Unlabeled Drive ","$" ; DISK LABEL (EXACTLY 16 BYTES!!!) + .DW 0 ; DEPRECATED +PR_LDLOC .DW SYS_LOC ; ADDRESS TO START LOADING SYSTEM +PR_LDEND .DW SYS_END ; ADDRESS TO STOP LOADING SYSTEM +PR_ENTRY .DW SYS_ENT ; ADDRESS TO ENTER SYSTEM (OS) +; +; +; + .END diff --git a/Source/CPM22/ver.inc b/Source/CPM22/ver.inc new file mode 100644 index 00000000..8320a3c2 --- /dev/null +++ b/Source/CPM22/ver.inc @@ -0,0 +1,5 @@ +#DEFINE RMJ 2 +#DEFINE RMN 9 +#DEFINE RUP 2 +#DEFINE RTP 0 +#DEFINE BIOSVER "2.9.2-pre.33" diff --git a/Source/CPM3/Build.cmd b/Source/CPM3/Build.cmd index 5008a0ca..003bd7eb 100644 --- a/Source/CPM3/Build.cmd +++ b/Source/CPM3/Build.cmd @@ -3,25 +3,31 @@ setlocal set TOOLS=../../Tools -set PATH=%TOOLS%\zx;%TOOLS%\cpmtools;%PATH% +set PATH=%TOOLS%\tasm32;%TOOLS%\zx;%TOOLS%\cpmtools;%PATH% + +set TASMTABS=%TOOLS%\tasm32 set ZXBINDIR=%TOOLS%/cpm/bin/ set ZXLIBDIR=%TOOLS%/cpm/lib/ set ZXINCDIR=%TOOLS%/cpm/include/ -rem cmd - -rem CPM Loader echo. echo. echo *** CPM Loader *** echo. zx RMAC -CPMLDR +copy optdsk.lib ldropts.lib +zx Z80ASM -BIOSLDR/MF +move /Y biosldr.rel biosldrd.rel +zx LINK -CPMLDRD[L100]=CPMLDR,BIOSLDRD +move /Y cpmldrd.com cpmldr.bin +copy optcmd.lib ldropts.lib zx Z80ASM -BIOSLDR/MF -zx LINK -CPMLDR[L100]=CPMLDR,BIOSLDR +move /Y biosldr.rel biosldrc.rel +zx LINK -CPMLDRC[L100]=CPMLDR,BIOSLDRC +move /Y cpmldrc.com cpmldr.com rem pause -rem Resident CPM3 echo. echo. echo *** Resident CPM3 BIOS *** @@ -35,12 +41,12 @@ zx Z80ASM -CHARIO/MF zx Z80ASM -MOVE/MF zx Z80ASM -DRVTBL/MF zx Z80ASM -DISKIO/MF -zx LINK -BIOS3[OS]=BIOSKRNL,SCB,BOOT,CHARIO,MOVE,DRVTBL,DISKIO +zx Z80ASM -UTIL/MF +zx LINK -BIOS3[OS]=BIOSKRNL,SCB,BOOT,CHARIO,MOVE,DRVTBL,DISKIO,UTIL zx GENCPM -AUTO -DISPLAY copy cpm3.sys cpm3res.sys rem pause -rem Banked CPM3 echo. echo. echo *** Banked CPM3 BIOS *** @@ -54,12 +60,12 @@ zx Z80ASM -CHARIO/MF zx Z80ASM -MOVE/MF zx Z80ASM -DRVTBL/MF zx Z80ASM -DISKIO/MF -zx LINK -BNKBIOS3[B]=BIOSKRNL,SCB,BOOT,CHARIO,MOVE,DRVTBL,DISKIO +zx Z80ASM -UTIL/MF +zx LINK -BNKBIOS3[B]=BIOSKRNL,SCB,BOOT,CHARIO,MOVE,DRVTBL,DISKIO,UTIL zx GENCPM -AUTO -DISPLAY copy cpm3.sys cpm3bnk.sys rem pause -rem Banked ZPM3 echo. echo. echo *** Banked ZPM3 BIOS *** @@ -73,49 +79,24 @@ zx Z80ASM -CHARIO/MF zx Z80ASM -MOVE/MF zx Z80ASM -DRVTBL/MF zx Z80ASM -DISKIO/MF -zx LINK -ZPMBIOS3[B]=BIOSKRNL,SCB,BOOT,CHARIO,MOVE,DRVTBL,DISKIO +zx Z80ASM -UTIL/MF +zx LINK -ZPMBIOS3[B]=BIOSKRNL,SCB,BOOT,CHARIO,MOVE,DRVTBL,DISKIO,UTIL rem zx GENCPM -AUTO -DISPLAY rem copy cpm3.sys zpm3.sys rem pause rem *** Resident *** + rem copy cpm3res.sys cpm3.sys rem copy genres.dat getcpm.dat rem *** Banked *** + copy cpm3bnk.sys cpm3.sys copy genbnk.dat gencpm.dat -if not exist ../../Binary/hd_cpm3.img goto :eof - -rem Update cpm_hd.img -echo. -echo. -echo *** Update Disk Image *** -echo. -for %%f in ( - cpmldr.com - ccp.com - gencpm.com - genres.dat - genbnk.dat - bios3.spr - bnkbios3.spr - bdos3.spr - bnkbdos3.spr - resbdos3.spr - cpm3res.sys - cpm3bnk.sys - gencpm.dat - cpm3.sys - readme.1st - cpm3fix.pat -) do call :upd_img %%f +rem Loader -goto :eof +tasm -t80 -g3 -fFF loader.asm loader.bin loader.lst -:upd_img -echo %1... -cpmrm.exe -f wbw_hd0 ../../Binary/hd_cpm3.img 0:%1 -cpmcp.exe -f wbw_hd0 ../../Binary/hd_cpm3.img %1 0:%1 -goto :eof \ No newline at end of file +copy /b loader.bin + cpmldr.bin cpmldr.sys diff --git a/Source/CPM3/Clean.cmd b/Source/CPM3/Clean.cmd index 02fa2692..950d6b8b 100644 --- a/Source/CPM3/Clean.cmd +++ b/Source/CPM3/Clean.cmd @@ -10,5 +10,7 @@ if exist *.err del *.err if exist *.lst del *.lst if exist *.sym del *.sym if exist *.sys del *.sys +if exist *.bin del *.bin if exist gencpm.dat del gencpm.dat if exist options.lib del options.lib +if exist ldropts.lib del ldropts.lib diff --git a/Source/CPM3/Makefile b/Source/CPM3/Makefile new file mode 100644 index 00000000..8d8b4b73 --- /dev/null +++ b/Source/CPM3/Makefile @@ -0,0 +1,89 @@ +# +# this makefile does double duty. it serves as the top level make +# and as the invoked make for the different ways that the cpm3 is built +# +# it does this by overriding OBJECTS in an invoked sub-make +# +OBJECTS = cpmldr.com cpmldr.sys cpm3res cpm3bnk zpmbios3 cpm3.sys gencpm.dat +OTHERS = cpmldr.rel biosldr.rel cpm3res.sys cpm3bnk.sys zpmbios3.spr loader.bin cpmldr.bin +OTHERS += biosldrc.rel biosldrd.rel + +TOOLS = ../../Tools +include $(TOOLS)/Makefile.inc + +BIOSOBJS = bioskrnl.rel scb.rel boot.rel chario.rel +BIOSOBJS += move.rel drvtbl.rel diskio.rel util.rel +COMMA := , +NULL := +SPACE := $(NULL) $(NULL) +BIOSNAMES := $(subst $(SPACE),$(COMMA),$(basename $(BIOSOBJS))) + +DEFCPM3 = bnk +#DEFCPM3 = res + +clean:: biosclean + @rm -f bios3.spr bnkbios3.spr zpmbios3.spr cpmldr.com gencpm.dat options.lib ldropts.lib + +biosclean: + @rm -f $(BIOSOBJS) + +cpm3res: + make biosclean + cp optres.lib options.lib + cp genres.dat gencpm.dat + make OBJECTS=bios3.spr + $(ZXCC) gencpm -auto -display + mv cpm3.sys cpm3res.sys + rm gencpm.dat + +cpm3bnk: + make biosclean + cp optbnk.lib options.lib + cp genbnk.dat gencpm.dat + make OBJECTS=bnkbios3.spr + $(ZXCC) gencpm -auto -display + mv cpm3.sys cpm3bnk.sys + rm gencpm.dat + +zpmbios3: + make biosclean + cp optzpm.lib options.lib + cp genbnk.dat gencpm.dat + make OBJECTS=zpmbios3.spr + rm gencpm.dat + +cpmldr.bin: biosldrd.rel cpmldr.rel + $(ZXCC) $(TOOLS)/cpm/bin/LINK -CPMLDRD[L100]=CPMLDR,BIOSLDRD + mv cpmldrd.com cpmldr.bin + +cpmldr.com: biosldrc.rel cpmldr.rel + $(ZXCC) $(TOOLS)/cpm/bin/LINK -CPMLDRC[L100]=CPMLDR,BIOSLDRC + mv cpmldrc.com cpmldr.com + +biosldrc.rel: biosldr.z80 optcmd.lib + cp optcmd.lib ldropts.lib + $(ZXCC) $(TOOLS)/cpm/bin/Z80ASM -BIOSLDR/MF + mv biosldr.rel biosldrc.rel + +biosldrd.rel: biosldr.z80 optdsk.lib + cp optdsk.lib ldropts.lib + $(ZXCC) $(TOOLS)/cpm/bin/Z80ASM -BIOSLDR/MF + mv biosldr.rel biosldrd.rel + +bios3.spr: $(BIOSOBJS) + $(ZXCC) $(CPM)/LINK -bios3[OS]=$(BIOSNAMES) + +bnkbios3.spr: $(BIOSOBJS) + $(ZXCC) $(CPM)/LINK -bnkbios3[B]=$(BIOSNAMES) + +zpmbios3.spr: $(BIOSOBJS) + $(ZXCC) $(TOOLS)/cpm/bin/LINK -zpmbios3[B]=$(BIOSNAMES) + +cpm3.sys: cpm3$(DEFCPM3).sys + cp cpm3$(DEFCPM3).sys cpm3.sys + +gencpm.dat: gen$(DEFCPM3).dat + cp gen$(DEFCPM3).dat gencpm.dat + +cpmldr.sys: loader.bin cpmldr.bin + cat loader.bin cpmldr.bin > $@ diff --git a/Source/CPM3/bioskrnl.asm b/Source/CPM3/bioskrnl.asm index 32e918b9..ab3a9ccd 100644 --- a/Source/CPM3/bioskrnl.asm +++ b/Source/CPM3/bioskrnl.asm @@ -244,7 +244,7 @@ co$next: push h ; save the vector push b ; save the count and character not$out$ready: - call coster ! ora a ! jz not$out$ready + push b ! call coster ! pop b ! ora a ! jz not$out$ready pop b ! push b ; restore and resave the character and device call ?co ; if device selected, print it pop b ; recover count and character diff --git a/Source/CPM3/biosldr.z80 b/Source/CPM3/biosldr.z80 index 754dc299..de97bb36 100644 --- a/Source/CPM3/biosldr.z80 +++ b/Source/CPM3/biosldr.z80 @@ -1,4 +1,6 @@ + maclib ldropts.lib + maclib cpm3.lib cseg @@ -50,48 +52,79 @@ jp 0 ; reserved for future expansion boot: - ;ld bc,0F8E0h ; HBIOS func: get boot info - ;call 0FFF0h ; do it, D := boot unit - ;ld a,d ; move to A - ;ld (unit),a ; save it - ;ret + + if cmdline ld (stksav),sp ld sp,stack boot1: - ld de,prompt + ld de,msgunit call writestr - call cin - push af call cout - ld de,crlf - call writestr pop af - ld sp,(stksav) - sub '0' - jr c,boot1 - cp 10 ; !!! Need to test against max disk unit num !!! - jr nc,boot1 - ld (unit),a + jr c,selerr + ld bc,0F810h ; HBIOS, get disk unit count + call 0FFF0h ; do it, E := disk unit count + ld a,(unit) ; get unit num back + cp e ; compare to entry + jr nc,selerr ; loop if too high + + ld de,msgslc + call writestr + call cin + push af + call cout + pop af + + sub '0' + ld (slice),a + jr c,selerr + cp 10 + jr nc,selerr + + jr boot2 + +selerr: + ld de,msginv + call writestr + jr boot1 + +boot2: + ld de,crlf + call writestr + + ld sp,(stksav) + ld bc,0F9E0h ; HBIOS func: set boot info - ld d,a ; Unit - ld e,0 ; Slice - ld l,0 ; Bank + ld a,(unit) ; get unit + ld d,a ; put in D + ld a,(slice) ; get slice + ld e,a ; put in E + ld l,0 ; Bank is always zero call 0FFF0h ; do it + else + ld bc,0F8E0h ; HBIOS func: get boot info + call 0FFF0h ; do it, D := boot unit, E: := slice + ld a,d ; move unit to A + ld (unit),a ; save it + ld a,e ; move slice to A + ld (slice),a ; save it + + endif ld a,(unit) ; Get boot unit ld c,a ; put in C ld b,18h ; HBIOS Media function - ld e,1 ; Enabled media check/discovery + ld e,1 ; Enable media check/discovery call 0FFF0H ; HBIOS call ld a,e ; Resultant media id to accum or a ; Set flags @@ -110,7 +143,6 @@ dsk$login1: ; hl is ptr to desired dpb ld de,dph0 ; load DPH pointer - ;halt ex de,hl ; de = DPB adr, hl = DPH adr push de ; save DPB adr ld de,12 ; offset of DPB in DPH @@ -166,23 +198,48 @@ setdma: ld (dma),bc ret read: - ; Seek + ld a,(unit) ; get unit + ld c,a ; BIOS Disk Unit in C + ld b,12H ; HBIOS SEEK function + push bc ; save it + ;push de ; save XDPH pointer + ld b,17h ; HBIOS DEVICE function + rst 08 ; Do it, D=device type + ld a,d ; put in accum + and 0F0h ; isolate high bits + ld b,1 ; assume it is floppy, 1 head bit + ld c,01h ; 1 bit head mask + cp 10h ; floppy? + jr z,seek0 ; yup, skip ahead + ld b,4 ; must be hard disk, 4 head bits + ld c,0Fh ; 4 bit head mask +seek0: + ;pop de ; recover XDPH pointer + push bc ; save bc + ld a,(slice) ; get slice + ld e,a ; slice to E + ld h,65 ; number of tracks per slice + call mult8 ; HL now has track offset for slice + pop bc ; recover bc + push hl ; save track offset for now ld hl,(trk) ; get track value ld a,l ; lsb of track to a - and 0FH ; isolate head in low 4 bits - ld d,a ; stuff it in d - ld a,(sect) ; get sector - ld e,a ; stuff it in e - ld b,4 ; prepare to shift out 4 bit head value -read1: + and c ; apply mask + ld d,a ; save in d +seek1: srl h ; shift one bit out rr l ; ... of hl - djnz read1 ; do all 4 bits - ld b,12h ; HBIOS seek - ld a,(unit) ; get boot unit - ld c,a ; put in C + djnz seek1 ; do all bits + ld a,(sect) ; get sector + ld e,a ; stuff it in e + ex de,hl ; de=track, hl=head/sect + ex (sp),hl ; save head/sect, hl = offset + add hl,de ; hl has final track value + pop de ; recover head/sect to de + pop bc ; recover function & unit ;rst 08 ; perform seek call 0FFF0H + ; Read Sector ld b,13h ; HBIOS read ld a,(unit) ; get boot unit @@ -282,7 +339,26 @@ writestr2: pop af ret -prompt db 13,10,'Boot CP/M 3 from Disk Unit: $' +; +; multiply 8-bit values +; in: multiply h by e +; out: hl = result, e = 0, b = 0 +; +mult8: + ld d,0 + ld l,d + ld b,8 +mult8_loop: + add hl,hl + jr nc,mult8_noadd + add hl,de +mult8_noadd: + djnz mult8_loop + ret + +msgunit db 13,10,13,10,'Boot CP/M 3 from Disk Unit: $' +msgslc db ' Slice: $' +msginv db 13,10,13,10,'*** Invalid Selection ***$' crlf db 13,10,'$' dpb$start: @@ -443,6 +519,7 @@ dtabcb: db 0ffh ; drv dw dtabuf ; buffad unit ds 1 ; HBIOS unit number +slice ds 1 ; HBIOS slice number trk ds 2 ; current track sect ds 2 ; current sector dma ds 2 ; current DMA address @@ -456,4 +533,4 @@ dtabuf ds 512 ; sector buffer stack equ $ stksav dw 0 - end \ No newline at end of file + end diff --git a/Source/CPM3/boot.z80 b/Source/CPM3/boot.z80 index d0b5cb0f..99c7bbda 100644 --- a/Source/CPM3/boot.z80 +++ b/Source/CPM3/boot.z80 @@ -3,7 +3,7 @@ maclib options.lib public ?init,?ldccp,?rlccp,?time - public @bootdu + public @bootdu,@bootsl extrn ?pmsg,?conin extrn ?mvinit,?bnkxlt,?xmove,?move extrn @civec,@covec,@aivec,@aovec,@lovec @@ -13,6 +13,8 @@ extrn @dtbl,@ctbl extrn @date,@hour,@min,@sec extrn @srch1 + extrn addhla, bcd2bin, bin2bcd + ;extrn cout, phex8 include ver.inc @@ -58,9 +60,11 @@ init$2: ; Get boot disk unit and save it ld bc,0F8E0h ; HBIOS func: get boot info - rst 08 ; do it, D := boot unit - ld a,d ; move to A + rst 08 ; do it, D := boot unit, E: := boot slice + ld a,d ; move boot unit to A ld (@bootdu),a ; save it + ld a,e ; move boot slice to A + ld (@bootsl),a ; save it call dinit ret @@ -121,31 +125,77 @@ dinit: ret z ; !!! handle zero devices (albeit poorly) !!! ; loop thru devices to count total hard disk volumes - push bc ; save the device count - ld c,0 ; use c as device list index - ld e,0 ; init e for hard disk volume count - + ld c,0 ; init c as device list index + ld d,0 ; init d as total device count + ld e,0 ; init e for hard disk device count + ld hl,drvlst ; init hl ptr to drive list +; dinit2: - push bc ; save loop control call dinit3 ; check drive - pop bc ; restore loop control inc c ; next unit djnz dinit2 ; loop - pop bc ; restore unit count in b + ld a,d ; total device count to d + ld (drvlstc),a ; save the count jr dinit4 ; continue dinit3: push de ; save de (hard disk volume counter) - ld b,017h ; hbios func: report device info + push hl ; save drive list ptr + push bc ; save loop control + ld b,17h ; hbios func: report device info rst 08 ; call hbios, unit to c ld a,d ; device type to a + pop bc ; restore loop control + pop hl ; restore drive list ptr pop de ; restore de - cp 050h ; hard disk device? - ret c ; nope, return + cp 30h ; hard disk device? + jr nc,dinit3a ; if so, handle special + ld (hl),c ; save unit num in list + inc hl ; bump ptr + inc d ; inc total device count + ret +; +dinit3a: + ; check for active and return if not + push de ; save de (hard disk volume counter) + push hl ; save drive list ptr + push bc ; save loop control + + ld b,18h ; hbios func: sense media + ld e,1 ; perform media discovery + rst 08 + + pop bc ; restore loop control + pop hl ; restore drive list ptr + pop de ; restore de + + ret nz ; if no media, just return + + ; if active... + ld (hl),c ; save unit num in list + inc hl ; bump ptr + inc d ; inc total device count inc e ; increment hard disk count ret ; and return + dinit4: ; set slices per volume (hdspv) based on hard disk volume count + +; ; *** debug *** +; ;call newline2 +; ld a,(drvlstc) +; ld b,a +; call phex8 +; ld a,' ' +; call cout +; ld hl,drvlst +;temp1: +; ld a,(hl) +; inc hl +; call phex8 +; djnz temp1 +; ; *** debug *** + ld a,e ; hard disk volume count to a ld e,8 ; assume 8 slices per volume dec a ; dec accum to check for count = 1 @@ -158,26 +208,37 @@ dinit4: ; set slices per volume (hdspv) based on hard disk volume count dinit5: ld a,e ; slices per volume value to accum ld (hdspv),a ; save it - - ; setup to enumerate devices to build drvmap - ld b,0F8h ; SYS GET - ld c,010h ; Disk Drive Unit Count - rst 08 ; e := disk unit count - ld b,e ; count to b - ld c,0 ; use c as device list index ld hl,0 ; dph index -dinit6: ; loop thru all units available + ld a,(@bootdu) ; boot disk unit + ld d,a ; ... to d + ld a,(@bootsl) ; boot slice + ld e,a ; ... to e + ld b,1 ; one slice please + call dinit8a ; make DPH for A: + + ld a,(drvlstc) ; active drive list count to accum + ld b,a ; ... and move to b for loop counter + ld de,drvlst ; de is ptr to active drive list + +dinit6: + ; loop thru all units available + push de ; preserve drive list ptr + ex de,hl ; list ptr to hl + ld c,(hl) ; get unit num from list + ex de,hl ; list ptr back to de push bc ; preserve loop control push hl ; preserve dph pointer - ld b,017h ; hbios func: report device info + ld b,17h ; hbios func: report device info rst 08 ; call hbios, d := device type pop hl ; restore dph pointer pop bc ; get unit index back in c push bc ; resave loop control - call dinit7 ; update dph entries + call dinit7 ; make drive map entry(s) pop bc ; restore loop control inc c ; increment list index + pop de ; restore drive list ptr + inc de ; increment active drive list ptr djnz dinit6 ; loop as needed ; zero out remaining dph table entries @@ -198,18 +259,28 @@ dinit6a: djnz dinit6a ret ; finished -dinit7: ; process unit +dinit7: ; process a unit (all slices) ld e,0 ; initialize slice index ld b,1 ; default loop counter ld a,d ; device type to accum ld d,c ; unit number to d - cp 050h ; hard disk device? + cp 030h ; hard disk device? jr c,dinit8 ; nope, leave loop count at 1 ld a,(hdspv) ; get slices per volume to accum ld b,a ; move to b for loop counter -dinit8: - ; d=unit, e=slice, l=dph# +dinit8: ; test to avoid reallocating boot disk unit/slice + ld a,(@bootdu) ; boot disk unit to accum + cp d ; compare to cur unit + jr nz,dinit8a ; if ne, ok to continue + ld a,(@bootsl) ; boot slice to accum + cp e ; compare to cur slice + jr nz,dinit8a ; if ne, ok to continue + inc e ; is boot du/slice, skip it + jr dinit8 ; and restart loop + +dinit8a: + ; d=unit, e=slice, l=dph#, b=slice cnt ld a,l ; dph # to accum cp 16 ; dph table size ret z ; bail out if overflow @@ -225,9 +296,9 @@ dinit8: ld (hl),e ; update slice number dec hl ; backup to unit number ld (hl),d ; update unit number - inc e ; next slice pop hl ; restore dph # inc hl ; next dph # + inc e ; next slice djnz dinit8 ; loop till done with unit ret @@ -250,25 +321,25 @@ stpsiz equ $ - stpimg ?ldccp: - if zpm - - ; Swap A: and system drive (make A: the system drive) - ld bc,(@dtbl) ; get drive A DPH - ld hl,@dtbl ; point to boot drive DPH - ld a,(@sysdr) - rlca - call addhla - ld e,(hl) ; set boot drive to drive A DPH - ld (hl),c ; ... and save boot drive DPH - inc hl - ld d,(hl) - ld (hl),b - ld (@dtbl),de ; set drive a DPH to boot drive - - xor a ; update @sysdr - ld (@sysdr),a - - endif + ;if zpm + ; + ;; Swap A: and system drive (make A: the system drive) + ;ld bc,(@dtbl) ; get drive A DPH + ;ld hl,@dtbl ; point to boot drive DPH + ;ld a,(@sysdr) + ;rlca + ;call addhla + ;ld e,(hl) ; set boot drive to drive A DPH + ;ld (hl),c ; ... and save boot drive DPH + ;inc hl + ;ld d,(hl) + ;ld (hl),b + ;ld (@dtbl),de ; set drive a DPH to boot drive + ; + ;xor a ; update @sysdr + ;ld (@sysdr),a + ; + ;endif ; Force CCP to use system boot drive as initial default ld a,(@sysdr) ; get system boot drive @@ -570,50 +641,6 @@ read: ld c,20 jp bdos -addhla: - add a,l - ld l,a - ret nc - inc h - ret - -bcd2bin: - ; convert A from packed bcd to binary - push bc - ld c,a - and 0F0h - srl a - ld b,a - srl a - srl a - add a,b - ld b,a - ld a,c - and 0Fh - add a,b - pop bc - ret - -bin2bcd: - ; convert A from binary to packed bcd - push bc - ld b,10 - ld c,-1 -bin2bcd1: - inc c - sub b - jr nc,bin2bcd1 - add a,b - ld b,a - ld a,c - add a,a - add a,a - add a,a - add a,a - or b - pop bc - ret - if zpm signon$msg db 13,10,'ZPM3' @@ -654,8 +681,11 @@ fcb$nr db 0,0,0 endif -@bootdu db 0 +@bootdu db 0 ; boot disk unit +@bootsl db 0 ; boot slice hdspv db 2 ; slices per volume for hard disks (must be >= 1) +drvlst ds 32 ; active drive list used durint drv_init +drvlstc db 0 ; entry count for active drive list ; The following section contains key information and addresses for the ; RomWBW CBIOS. A pointer to the start of this section is stored with diff --git a/Source/CPM3/diskdefs b/Source/CPM3/diskdefs deleted file mode 100644 index 937bfcf2..00000000 --- a/Source/CPM3/diskdefs +++ /dev/null @@ -1,417 +0,0 @@ -diskdef ibm-3740 - seclen 128 - tracks 77 - sectrk 26 - blocksize 1024 - maxdir 64 - skew 6 - boottrk 2 - os p2dos -end - -diskdef 4mb-hd - seclen 128 - tracks 1024 - sectrk 32 - blocksize 2048 - maxdir 256 - skew 1 - boottrk 0 - os p2dos -end - -diskdef pcw - seclen 512 - tracks 40 - sectrk 9 - blocksize 1024 - maxdir 64 - skew 1 - boottrk 1 - os 3 -end - -diskdef pc1.2m - seclen 512 - tracks 80 - # this format uses 15 sectors per track, but 30 per cylinder - sectrk 30 - blocksize 4096 - maxdir 256 - skew 1 - boottrk 0 - os 3 -end - -# CP/M 86 on 1.44MB floppies -diskdef cpm86-144feat - seclen 512 - tracks 160 - sectrk 18 - blocksize 4096 - maxdir 256 - skew 1 - boottrk 2 - os 3 -end - -diskdef cf2dd - seclen 512 - tracks 160 - sectrk 9 - blocksize 2048 - maxdir 256 - skew 1 - boottrk 1 - os 3 -end - -#amstrad: values are read from super block (special name hardcoded) - -# Royal alphatronic -# setfdprm /dev/fd1 dd ssize=256 cyl=40 sect=16 head=2 -diskdef alpha - seclen 256 - tracks 40 - sectrk 32 - blocksize 2048 - maxdir 128 - skew 1 - boottrk 2 - os 2.2 -end - -# Apple II CP/M skew o Apple II DOS 3.3 skew -diskdef apple-do - seclen 256 - tracks 35 - sectrk 16 - blocksize 1024 - maxdir 64 - skew 0 - boottrk 3 - os 2.2 -end - -# Apple II CP/M skew o Apple II PRODOS skew -diskdef apple-po - seclen 256 - tracks 35 - sectrk 16 - blocksize 1024 - maxdir 64 - skew 0 - boottrk 3 - os 2.2 -end - -# MYZ80 hard drive (only works with libdsk, because it has a 256-byte header) -diskdef myz80 - seclen 1024 - tracks 64 - sectrk 128 - blocksize 4096 - maxdir 1024 - skew 1 - boottrk 0 - os 3 -end - -# Despite being Amstrad formats, CPC System and CPC Data don't have an Amstrad -# superblock. You'll need to use libdsk to access them because the Linux -# and Windows kernel drivers won't touch them. -diskdef cpcsys - seclen 512 - tracks 40 - sectrk 9 - blocksize 1024 - maxdir 64 - skew 1 - boottrk 2 - os 3 -end -diskdef cpcdata - seclen 512 - tracks 40 - sectrk 9 - blocksize 1024 - maxdir 64 - skew 1 - boottrk 0 - os 3 -end - -# after being read in with no sector skew. -diskdef nigdos - seclen 512 - # NigDos double sided disk format, 42 tracks * 2 sides - tracks 84 - sectrk 10 - blocksize 2048 - maxdir 128 - skew 1 - boottrk 0 - # this format wastes half of the directory entry - logicalextents 1 - os 3 -end - -diskdef epsqx10 - seclen 512 - tracks 40 - sectrk 20 - blocksize 2048 - maxdir 128 - skew 0 - boottrk 2 - os 2.2 -end - -diskdef ibm-8ss - seclen 512 - tracks 40 - sectrk 8 - blocksize 1024 - maxdir 64 - skew 0 - boottrk 1 - os 2.2 -end - -diskdef ibm-8ds - seclen 512 - tracks 40 - sectrk 8 - blocksize 1024 - maxdir 64 - skew 0 - boottrk 1 - os 2.2 -end - -diskdef electroglas - seclen 512 - tracks 80 - sectrk 10 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 1 - os 3 -end - -# IBM CP/M-86 -# setfdprm /dev/fd1 sect=8 dtr=1 hd ssize=512 tpi=48 head=1 -diskdef ibmpc-514ss - seclen 512 - tracks 40 - sectrk 8 - blocksize 1024 - maxdir 64 - skew 1 - boottrk 1 - os 2.2 -end - -# IBM CP/M-86 -# setfdprm /dev/fd1 sect=8 dtr=1 hd ssize=512 tpi=48 -diskdef ibmpc-514ds - seclen 512 - tracks 80 - sectrk 8 - blocksize 2048 - maxdir 64 - skew 0 - boottrk 2 - os 2.2 -end - -diskdef p112 - seclen 512 - tracks 160 - sectrk 18 - blocksize 2048 - maxdir 256 - skew 1 - boottrk 2 - os 3 -end - -diskdef p112-old - seclen 512 - tracks 160 - sectrk 18 - blocksize 2048 - maxdir 128 - skew 1 - boottrk 1 - os 3 -end - -diskdef kpii - seclen 512 - tracks 40 - sectrk 10 - blocksize 1024 - maxdir 32 - skew 0 - boottrk 1 - os 2.2 -end - -# setfdprm /dev/fd0 dd sect=10 -diskdef interak - seclen 512 - tracks 80 - sectrk 20 - blocksize 4096 - maxdir 256 - skew 0 - boottrk 2 - os 2.2 -end - -# RomWBW 512KB ROM (128KB reserved, 384KB ROM Disk) - -diskdef wbw_rom512 - seclen 512 - tracks 12 - sectrk 64 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 0 - os 2.2 -end - -# RomWBW 1024KB ROM (128KB reserved, 896KB ROM Disk) - -diskdef wbw_rom1024 - seclen 512 - tracks 28 - sectrk 64 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 0 - os 2.2 -end - -# UNA 512KB ROM (128KB reserved, 384KB ROM Disk) - -diskdef una_rom512 - seclen 512 - tracks 12 - sectrk 64 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 0 - os 2.2 -end - -# UNA 512KB ROM (128KB reserved, 896KB ROM Disk) - -diskdef una_rom1024 - seclen 512 - tracks 28 - sectrk 64 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 0 - os 2.2 -end - -# RomWBW 8MB Hard Disk, LU 0-3 -diskdef wbw_hd0 - seclen 512 - tracks 65 - sectrk 256 - blocksize 4096 - maxdir 512 - skew 0 - boottrk 1 - os 2.2 -end - -diskdef wbw_hd1 - seclen 512 - tracks 130 - sectrk 256 - blocksize 4096 - maxdir 512 - skew 0 - boottrk 66 - os 2.2 -end - -diskdef wbw_hd2 - seclen 512 - tracks 195 - sectrk 256 - blocksize 4096 - maxdir 512 - skew 0 - boottrk 131 - os 2.2 -end - -diskdef wbw_hd3 - seclen 512 - tracks 260 - sectrk 256 - blocksize 4096 - maxdir 512 - skew 0 - boottrk 196 - os 2.2 -end - -# RomWBW 720K floppy media -diskdef wbw_fd720 - seclen 512 - tracks 160 - sectrk 9 - blocksize 2048 - maxdir 128 - skew 0 - boottrk 4 - os 2.2 -end - -# RomWBW 1.44M floppy media -diskdef wbw_fd144 - seclen 512 - tracks 160 - sectrk 18 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 2 - os 2.2 -end - -# RomWBW 360K floppy media -diskdef wbw_fd360 - seclen 512 - tracks 80 - sectrk 9 - blocksize 2048 - maxdir 128 - skew 0 - boottrk 4 - os 2.2 -end - -# RomWBW 1.20M floppy media -diskdef wbw_fd120 - seclen 512 - tracks 160 - sectrk 15 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 2 - os 2.2 -end diff --git a/Source/CPM3/diskio.z80 b/Source/CPM3/diskio.z80 index 91613e73..154d601d 100644 --- a/Source/CPM3/diskio.z80 +++ b/Source/CPM3/diskio.z80 @@ -14,7 +14,7 @@ ; Linked BIOS variables public @sysdr - extrn @bootdu + extrn @bootdu,@bootsl ; Variables containing parameters passed by BDOS @@ -36,6 +36,8 @@ extrn ?const ; get console status extrn ?bnkxlt + + ;extrn phex8, cout ; CP/M 3 Disk definition macros @@ -321,20 +323,21 @@ dpb_fd111: ; 8" DS/DD Floppy Drive (1.11M) ; called for first time initialization. dsk$init: - inc de ; point to slice in XDPH - inc de - inc de - ld a,(de) ; get slice - or a ; set flags - ret nz ; done if not zero - - ld a,(@rdrv) ; unit being initialized - ld hl,@bootdu - cp (hl) - ret nz ; done if no match - - ld a,(@adrv) ; get cp/m drive - ld (@sysdr),a ; and save it + ;ld a,(@rdrv) ; unit being initialized + ;ld hl,@bootdu + ;cp (hl) ; compare to boot unit + ;ret nz ; done if no match + ; + ;inc de ; point to slice in XDPH + ;inc de + ;inc de + ;ld a,(de) ; get slice + ;ld hl,@bootsl + ;cp (hl) ; compare to boot slice + ;ret nz ; done if not zero + ; + ;ld a,(@adrv) ; get cp/m drive + ;ld (@sysdr),a ; and save it ret ; lxi h,init$table @@ -365,6 +368,9 @@ dsk$login: ; simple single density only environment. + ;ld a,'L' + ;call cout + push de ; save DPH ptr ; check media @@ -372,7 +378,8 @@ dsk$login: ;halt ld c,a ; put in C ld b,18h ; HBIOS Media function - ld e,1 ; Enabled media check/discovery + ld e,1 ; Enable media check/discovery + ;rst 08 call 0FFF0H ; HBIOS call ld a,e ; Resultant media id to accum or a ; Set flags @@ -380,8 +387,11 @@ dsk$login: ; ; !!! Need to do something on error !!! ; - ret z ; Bail out on error + jr nz,dsk$login0 ; continue if OK + pop de ; else error + ret ; return +dsk$login0: ld hl,dpb$start - dpb$sz ld de,dpb$sz ld b,a ; loop count @@ -422,6 +432,9 @@ dsk$read: ; ld ix,30H ; halt + ;ld a,'R' + ;call cout + push de ; save XDPH pointer call dsk$seek ; disk seek pop hl ; restore pointer to HL @@ -440,8 +453,10 @@ dsk$read: endif ld d,a ; set desk bank ld e,1 ; 1 sector - ;rst 08 ; do it - call 0FFF0H + rst 08 ; do it + ;call 0FFF0H + + ;call phex8 ret ; return ; lxi h,read$msg ; point at " Read " @@ -470,8 +485,8 @@ dsk$write: endif ld d,a ; set desk bank ld e,1 ; 1 sector - ;rst 08 ; do it - call 0FFF0H + rst 08 ; do it + ;call 0FFF0H ret ; return ; lxi h,write$msg ; point at " Write " @@ -485,34 +500,44 @@ dsk$seek: ld c,a ; BIOS Disk Unit in C ld b,12H ; HBIOS SEEK function push bc ; save it - + push de ; save XDPH pointer + ld b,17h ; HBIOS DEVICE function + rst 08 ; Do it, D=device type + ld a,d ; put in accum + and 0F0h ; isolate high bits + ld b,1 ; assume it is floppy, 1 head bit + ld c,01h ; 1 bit head mask + cp 10h ; floppy? + jr z,seek0 ; yup, skip ahead + ld b,4 ; must be hard disk, 4 head bits + ld c,0Fh ; 4 bit head mask +seek0: + pop de ; recover XDPH pointer + push bc ; save bc inc de ; point to slice field of XDPH ld a,(de) ; get it ld e,a ; slice to E ld h,65 ; number of tracks per slice call mult8 ; HL now has track offset for slice - push hl ; save it for now - + pop bc ; recover bc + push hl ; save track offset for now ld hl,(@trk) ; get track value ld a,l ; lsb of track to a - and 0FH ; isolate head in low 4 bits - ld d,a ; stuff it in d - ld a,(@sect) ; get sector - ld e,a ; stuff it in e - ld b,4 ; prepare to shift out 4 bit head value + and c ; apply mask + ld d,a ; save in d seek1: srl h ; shift one bit out rr l ; ... of hl - djnz seek1 ; do all 4 bits - + djnz seek1 ; do all bits + ld a,(@sect) ; get sector + ld e,a ; stuff it in e ex de,hl ; de=track, hl=head/sect ex (sp),hl ; save head/sect, hl = offset add hl,de ; hl has final track value pop de ; recover head/sect to de - pop bc ; recover function & unit - ;rst 08 ; perform seek - call 0FFF0H + rst 08 ; perform seek + ;call 0FFF0H ret ; diff --git a/Source/CPM3/loader.asm b/Source/CPM3/loader.asm new file mode 100644 index 00000000..982f7205 --- /dev/null +++ b/Source/CPM3/loader.asm @@ -0,0 +1,228 @@ +;=============================================================================== +; LOADER.ASM +; +; BOOTLOADER FOR ROMWBW DISK OPERATING SYSTEMS. +; +; CP/M DISK FORMATS ALLOW FOR RESERVED TRACKS THAT CONTAIN AN IMAGE OF THE +; OPERATING SYSTEM TO BE LOADED WHEN THE DISK IS BOOTED. THE OPERATING SYSTEM +; IMAGE ITSELF IS NORMALLY PREFIXED BY A 1-N SECTORS CONTAINING OS BOOTSTRAP +; CODE AND DISK METADATA. +; +; THE RETROBREW COMPUTING GROUP HAS BEEN USING A CONVENTION OF PREFIXING THE +; OS IMAGE WITH 3 SECTORS (512 BYTES X 3 FOR A TOTAL OF 1536 BYTES): +; +; SECTOR 1: IBM-PC STYLE BOOT BLOCK CONTAINING BOOTSTRAP, +; PARTITION TABLE, AND BOOT SIGNATURE +; SECTOR 2: RESERVED +; SECTOR 3: METADATA +; +; THE HARDWARE BIOS IS EXPECTED TO READ AND LOAD THE FIRST TWO SECTORS FROM THE +; DISK TO MEMORY ADDRESS $8000 AND JUMP TO THAT LOCATION TO BEGIN THE BOOT +; PROCESS. THE BIOS IS EXPECTED TO VERIFY THAT A STANDARD BOOT SIGNATURE +; OF $55, $AA IS PRESENT AT OFFSET $1FE-$1FF. IF THE SIGNATURE IS NOT FOUND, +; THE BIOS SHOULD ASSUME THE DISK HAS NOT BEEN PROPERLY INITIALIZED AND SHOULD +; NOT JUMP TO THE LOAD ADDRESS. +; +;=============================================================================== +; +#INCLUDE "../HBIOS/ver.inc" +; +SYS_ENT .EQU $0100 ; SYSTEM (OS) ENTRY POINT ADDRESS +SYS_LOC .EQU $0100 ; STARTING ADDRESS TO LOAD SYSTEM IMAGE +SYS_END .EQU $1480 ; ENDING ADDRESS OF SYSTEM IMAGE +; +BYT .EQU 1 ; used to describe METADATA_SIZE below +WRD .EQU 2 +; +SECTOR_SIZE .EQU 512 +BLOCK_SIZE .EQU 128 +PREFIX_SIZE .EQU (3 * SECTOR_SIZE) ; 3 SECTORS +METADATA_SIZE .EQU BYT+WRD+(4*BYT)+16+BYT+WRD+WRD+WRD+WRD ; (as defined below) +; +PARTTBL_LOC .EQU $1BE +PARTTBL_SIZ .EQU $40 +BOOTSIG_LOC .EQU $1FE +; +;------------------------------------------------------------------------------- +; SECTOR 1 +; +; THIS SECTOR FOLLOWS THE CONVENTIONS OF AN IBM-PC MBR CONTAINING THE OS +; BOOTSTRAP CODE, PARTITION TABLE, AND BOOT SIGNATURE +; +;---------------------------------------------------------------------------- +; +; THE FOLLOWING BOOTSTRAP CODE IS BUILT TO ASSUME IT WILL BE EXECUTED AT A STARTING +; ADDRESS OF $8000. +; + .ORG $8000 + JR BOOT +; +BOOT: + LD DE,STR_LOAD ; LOADING STRING + CALL PRTSTR ; PRINT + CALL PRTDOT ; PROGRESS +; + LD BC,$00FC ; UNA FUNC: GET BOOTSTRAP HISTORY + CALL $FFFD ; CALL UNA + JR NZ,ERROR ; HANDLE ERROR + CALL PRTDOT ; PROGRESS + LD B,L ; MOVE BOOT UNIT ID TO B +; + LD C,$41 ; UNA FUNC: SET LBA + LD DE,0 ; HI WORD ALWAYS ZERO + LD HL,3 ; IMAGE STARTS AT FOURTH SECTOR + CALL $FFFD ; SET LBA + JR NZ,ERROR ; HANDLE ERROR + CALL PRTDOT ; PROGRESS +; + LD C,$42 ; UNA FUNC: READ SECTORS + LD DE,$D000 ; STARTING ADDRESS FOR IMAGE + LD L,22 ; READ 22 SECTORS + CALL $FFFD ; DO READ + JR NZ,ERROR ; HANDLE ERROR + CALL PRTDOT ; PROGRESS +; + LD DE,STR_DONE ; DONE MESSAGE + CALL PRTSTR ; PRINT IT +; + LD D,B ; PASS BOOT UNIT TO OS + LD E,0 ; ASSUME LU IS ZERO + JP SYS_ENT ; GO TO SYSTEM +; +PRTCHR: + PUSH BC + PUSH DE + LD BC,$0012 ; UNIT 0, WRITE CHAR + LD E,A ; CHAR TO PRINT + CALL $FFFD ; PRINT + POP DE + POP BC + RET +; +PRTSTR: + PUSH BC + PUSH HL + LD BC,$0015 ; UNIT 0, WRITE CHARS UNTIL TERMINATOR + LD L,0 ; TERMINATOR IS NULL + CALL $FFFD ; PRINT + POP HL + POP BC + RET +; +PRTDOT: + LD A,'.' ; DOT CHARACTER + JR PRTCHR ; PRINT AND RETURN +; +; PRINT THE HEX BYTE VALUE IN A +; +PRTHEXBYTE: + PUSH AF + PUSH DE + CALL HEXASCII + LD A,D + CALL PRTCHR + LD A,E + CALL PRTCHR + POP DE + POP AF + RET +; +; CONVERT BINARY VALUE IN A TO ASCII HEX CHARACTERS IN DE +; +HEXASCII: + LD D,A + CALL HEXCONV + LD E,A + LD A,D + RLCA + RLCA + RLCA + RLCA + CALL HEXCONV + LD D,A + RET +; +; CONVERT LOW NIBBLE OF A TO ASCII HEX +; +HEXCONV: + AND 0FH ;LOW NIBBLE ONLY + ADD A,90H + DAA + ADC A,40H + DAA + RET +; +ERROR: + LD DE,STR_ERR ; POINT TO ERROR STRING + CALL PRTSTR ; PRINT IT + HALT ; HALT +; +; DATA +; +STR_LOAD .DB "\r\nLoading",0 +STR_DONE .DB "\r\n",0 +STR_ERR .DB " Read Error!",0 +; + .ORG $ - $8000 ; RESTORE ORG + .FILL PARTTBL_LOC - $ ; FILL TO START OF PARTITION TABLE +; +; RESERVE SPACE FOR STANDARD IBM-PC PARTITION TABLE. ALTHOUGH A +; PARTITION TABLE IS NOT RELEVANT FOR A FLOPPY DISK, IT DOES NO HARM. +; THE CONTENTS OF THE PARTITION TABLE MUST BE MANAGED BY FDISK80. +; +PARTTBL .FILL PARTTBL_SIZ,$00 ; PARTITION TABLE, FILL WITH ZEROES +; +; THE END OF THE FIRST SECTOR MUST CONTAIN THE TWO BYTE BOOT +; SIGNATURE. +; +BOOTSIG .DB $55,$AA ; STANDARD BOOT SIGNATURE +; +;------------------------------------------------------------------------------- +; SECTOR 2 +; +; THIS SECTOR HAS NOT BEEN DEFINED AND IS RESERVED. +; +;---------------------------------------------------------------------------- +; + .FILL 512,0 ; JUST FILL SECTOR WITH ZEROES +; +;------------------------------------------------------------------------------- +; SECTOR 3 +; +; OS AND DISK METADATA +; +;---------------------------------------------------------------------------- +; + .FILL 128 * 3,0 ; FIRST 384 BYTES ARE NOT YET DEFINED +; +; THE FOLLOWING TWO BYTES ARE AN ADDITIONAL SIGNATURE THAT IS VERIFIED BY +; SOME HARDWARE BIOSES. +; +PR_SIG .DB $5A,$A5 ; SIGNATURE GOES HERE +; +; FIRST CHUNK OF METADATA IMMEDIATELY FOLLOWS THE SIGNATURE BYTES +; +PR_PLATFORM .DB 0 ; PLATFORM ID (SEE STD.ASM) +PR_DEVICE .DB 0 ; ? (PROBABLY UNUSED) +PR_FORMATTER .DB 0,0,0,0,0,0,0,0 ; ? (PROBABLY UNUSED) +PR_DRIVE .DB 0 ; ? (PROBABLY UNUSED) +PR_LOG_UNIT .DW 0 ; ? (PROBABLY UNUSED) +; +; FILLER TO PLACE SECOND CHUNK OF METADATA AT THE END OF THE SECTOR +; + .FILL ((PREFIX_SIZE - METADATA_SIZE) - $),00H +; +; SECOND CHUNK OF METADATA +; +PR_WP .DB 0 ; WRITE PROTECT BOOLEADN +PR_UPDSEQ .DW 0 ; PREFIX UPDATE SEQUENCE NUMBER (DEPRECATED?) +PR_VER .DB RMJ,RMN,RUP,RTP ; OS BUILD VERSION +PR_LABEL .DB "Unlabeled Drive ","$" ; DISK LABEL (EXACTLY 16 BYTES!!!) + .DW 0 ; DEPRECATED +PR_LDLOC .DW SYS_LOC ; ADDRESS TO START LOADING SYSTEM +PR_LDEND .DW SYS_END ; ADDRESS TO STOP LOADING SYSTEM +PR_ENTRY .DW SYS_ENT ; ADDRESS TO ENTER SYSTEM (OS) +; +; +; + .END diff --git a/Source/CPM3/optcmd.lib b/Source/CPM3/optcmd.lib new file mode 100644 index 00000000..3f74e713 --- /dev/null +++ b/Source/CPM3/optcmd.lib @@ -0,0 +1,7 @@ + ; global assembler options for CP/M loader + ; when loading from a command line + +true equ -1 +false equ not true + +cmdline equ true diff --git a/Source/CPM3/optdsk.lib b/Source/CPM3/optdsk.lib new file mode 100644 index 00000000..c2caa8b3 --- /dev/null +++ b/Source/CPM3/optdsk.lib @@ -0,0 +1,7 @@ + ; global assembler options for CP/M loader + ; when loading from a boot disk + +true equ -1 +false equ not true + +cmdline equ false diff --git a/Source/CPM3/util.z80 b/Source/CPM3/util.z80 new file mode 100644 index 00000000..affa7ade --- /dev/null +++ b/Source/CPM3/util.z80 @@ -0,0 +1,133 @@ + title 'Utility module for RomWBW' + + maclib options.lib + + public addhla, bcd2bin, bin2bcd + public phex16, phex8, cout + + cseg + +addhla: + add a,l + ld l,a + ret nc + inc h + ret + +bcd2bin: + ; convert A from packed bcd to binary + push bc + ld c,a + and 0F0h + srl a + ld b,a + srl a + srl a + add a,b + ld b,a + ld a,c + and 0Fh + add a,b + pop bc + ret + +bin2bcd: + ; convert A from binary to packed bcd + push bc + ld b,10 + ld c,-1 +bin2bcd1: + inc c + sub b + jr nc,bin2bcd1 + add a,b + ld b,a + ld a,c + add a,a + add a,a + add a,a + add a,a + or b + pop bc + ret + + if 0 +; +; Print the hex word value in HL +; +phex16: + push af + ld a,h + call phex8 + ld a,l + call phex8 + pop af + ret +; +; Print the hex byte value in A +; +phex8: + push af + push de + call hexascii + ld a,d + call cout + ld a,e + call cout + pop de + pop af + ret + +; +; Convert binary value in A to ascii hex characters in DE +; +hexascii: + ld d,a + call hexconv + ld e,a + ld a,d + rlca + rlca + rlca + rlca + call hexconv + ld d,a + ret +; +; convert low nibble of A to ascii hex +; +hexconv: + and 0Fh ;low nibble only + add a,90h + daa + adc a,40h + daa + ret +; +; output character from A +; +cout: + ; save all incoming registers + push af + push bc + push de + push hl + ld e,a + ld bc,0100h + rst 08 + pop hl + pop de + pop bc + pop af + ret + + else + +phex16: +phex8: +cout: + halt + + endif + + end diff --git a/Source/CPM3/ver.inc b/Source/CPM3/ver.inc index 6fea63ae..90c458fb 100644 --- a/Source/CPM3/ver.inc +++ b/Source/CPM3/ver.inc @@ -3,5 +3,5 @@ rmn equ 9 rup equ 2 rtp equ 0 biosver macro - db "2.9.2-pre.25" + db "2.9.2-pre.33" endm diff --git a/Source/Clean.cmd b/Source/Clean.cmd index 8f681c22..a4420787 100644 --- a/Source/Clean.cmd +++ b/Source/Clean.cmd @@ -10,6 +10,7 @@ setlocal & cd CBIOS && call Clean.cmd & endlocal setlocal & cd CPM3 && call Clean.cmd & endlocal setlocal & cd ZPM3 && call Clean.cmd & endlocal setlocal & cd Forth && call Clean.cmd & endlocal +setlocal & cd Fonts && call Clean.cmd & endlocal setlocal & cd BPBIOS && call Clean.cmd & endlocal setlocal & cd HBIOS && call Clean.cmd & endlocal setlocal & cd Doc && call Clean.cmd & endlocal diff --git a/Source/Fonts/Build.cmd b/Source/Fonts/Build.cmd new file mode 100644 index 00000000..cec086aa --- /dev/null +++ b/Source/Fonts/Build.cmd @@ -0,0 +1,20 @@ +@echo off +setlocal + +set TOOLS=../../Tools + +set PATH=%TOOLS%\lzsa;%TOOLS%\fonttool;%PATH% + +echo. +echo Preparing compressed font files... + +lzsa -f2 -r font8x8u.bin font8x8c.bin +lzsa -f2 -r font8x11u.bin font8x11c.bin +lzsa -f2 -r font8x16u.bin font8x16c.bin + +fonttool font8x8u.bin > font8x8u.asm +fonttool font8x11u.bin > font8x11u.asm +fonttool font8x16u.bin > font8x16u.asm +fonttool font8x8c.bin > font8x8c.asm +fonttool font8x11c.bin > font8x11c.asm +fonttool font8x16c.bin > font8x16c.asm diff --git a/Source/Fonts/Clean.cmd b/Source/Fonts/Clean.cmd new file mode 100644 index 00000000..8712ec61 --- /dev/null +++ b/Source/Fonts/Clean.cmd @@ -0,0 +1,6 @@ +@echo off +setlocal + + +if exist *.asm del *.asm +if exist *c.bin del *c.bin diff --git a/Source/Fonts/Makefile b/Source/Fonts/Makefile new file mode 100644 index 00000000..c9a06b35 --- /dev/null +++ b/Source/Fonts/Makefile @@ -0,0 +1,28 @@ +OBJECTS = \ + font8x8u.asm font8x11u.asm font8x16u.asm \ + font8x8c.asm font8x11c.asm font8x16c.asm + +OTHERS = font8x8c.bin font8x11c.bin font8x16c.bin + +DEST = ../HBIOS +TOOLS = ../../Tools + +include $(TOOLS)/Makefile.inc + +# +# these 2 rules cancel out the default rules from the Makefile.inc +# +%.rel: %.asm +%.bin: %.asm + +font8x8c.bin: font8x8u.bin + $(BINDIR)/lzsa -f2 -r $< $@ + +font8x11c.bin: font8x11u.bin + $(BINDIR)/lzsa -f2 -r $< $@ + +font8x16c.bin: font8x16u.bin + $(BINDIR)/lzsa -f2 -r $< $@ + +%.asm: %.bin + $(BINDIR)/bin2asm $< > $@ diff --git a/Source/Fonts/font8x11.png b/Source/Fonts/font8x11.png new file mode 100644 index 00000000..1e7ecbab Binary files /dev/null and b/Source/Fonts/font8x11.png differ diff --git a/Source/Fonts/font8x11u.bin b/Source/Fonts/font8x11u.bin new file mode 100644 index 00000000..330a44e0 Binary files /dev/null and b/Source/Fonts/font8x11u.bin differ diff --git a/Source/Fonts/font8x16.png b/Source/Fonts/font8x16.png new file mode 100644 index 00000000..b19ca57f Binary files /dev/null and b/Source/Fonts/font8x16.png differ diff --git a/Source/Fonts/font8x16u.bin b/Source/Fonts/font8x16u.bin new file mode 100644 index 00000000..c7ed1f51 Binary files /dev/null and b/Source/Fonts/font8x16u.bin differ diff --git a/Source/Fonts/font8x8.png b/Source/Fonts/font8x8.png new file mode 100644 index 00000000..b943becf Binary files /dev/null and b/Source/Fonts/font8x8.png differ diff --git a/Source/Fonts/font8x8u.bin b/Source/Fonts/font8x8u.bin new file mode 100644 index 00000000..17f16d25 Binary files /dev/null and b/Source/Fonts/font8x8u.bin differ diff --git a/Source/Fonts/fonts.txt b/Source/Fonts/fonts.txt new file mode 100644 index 00000000..2f720b44 --- /dev/null +++ b/Source/Fonts/fonts.txt @@ -0,0 +1,45 @@ +Font files for ROMWBW. + +There are three fonts associated with ROMWBW supported hardware - ECB-SCG, ECB-CVDU and the ECB-VGA3. + +Name Format Size Board & Display Mode +------------------------------------------------------------------------------------ +font8x8u.bin 8x8 2048 ECB-SCG, ECB-VGA3 (80x60) +font8x11u.bin 8x11 2816 ECB-VGA3 (80x43) +font8x16u.bin 8x16 4096 ECB-CVDU (80x25), ECB-VGA3 (80x24, 80x25, 80x30) + +For inclusion in HBIOS the .bin format files must be convert to assembler .asm format. +This is acheived using the fonttool utility and is completed automatically as part of the build process. +i.e. fonts files are converted to .asm format and then copied to the HBIOS directory. + +To replace a font, simply copy it to the Fonts directory using the same naming convention above, +ensuring format and size match. Then complete a new build process. + +To reduce the size of the HBIOS image, fonts can be compressed by using the USEZLSA2 equate. + +Use the following in your custom configuration to turn on font compression: + +USELZSA2 .SET TRUE + +Compressed fonts are created using lzsa utility. + +During the build process, fonts are compressed using the command line compressor by Emmanuel Marty +The compression is done as follows: + +lzsa.exe -f2 -r + +where option -f2 selects lzsa version 2 compression. +where option -r asks for the generation of raw (frame-less) data. + +Original compression source code and files available here: + +Latest github code: + https://github.com/emmanuel-marty/lzsa +Implementation example (older version): + https://cpcrulez.fr/applications_tools_cruncher_LZSA2_Z80.htm +x86 lzsa compressor application: + http://www.pouet.net/prod.php?which=81573 + + + + diff --git a/Source/Forth/Makefile b/Source/Forth/Makefile new file mode 100644 index 00000000..51fbdee2 --- /dev/null +++ b/Source/Forth/Makefile @@ -0,0 +1,6 @@ +OBJECTS = camel80.bin +TOOLS = ../../Tools +OTHERS = *.rel +DEST = ../HBIOS + +include $(TOOLS)/Makefile.inc diff --git a/Source/HBIOS/Build.ps1 b/Source/HBIOS/Build.ps1 index 6f379321..cc61b0b9 100644 --- a/Source/HBIOS/Build.ps1 +++ b/Source/HBIOS/Build.ps1 @@ -107,7 +107,7 @@ $ComFile = "${OutDir}/${RomName}.com" # Final name of COM image (command line lo $ImgFile = "${OutDir}/${RomName}.img" # Final name of IMG image (memory loadable HBIOS/CBIOS image) # Select the proper CBIOS to include in the ROM. UNA is special. -if ($Platform -eq "UNA") {$CBiosFile = '../CBIOS/cbios_una.bin'} else {$CBiosFile = '../CBIOS/cbios_wbw.bin'} +if ($Platform -eq "UNA") {$Bios = 'una'} else {$Bios = 'wbw'} # List of RomWBW proprietary apps to imbed in ROM disk. $RomApps = "assign","fdu","format","mode","osldr","rtc","survey","syscopy","sysgen","talk","timer","xm","inttest" @@ -140,7 +140,7 @@ Function Concat($InputFileList, $OutputFile) # # Since TASM has no mechanism to include files dynamically based on variables, a file -# if built on-the-fly here for imbedding in the build process. This file is basically +# is built on-the-fly here for imbedding in the build process. This file is basically # just used to include the platform and config files. It also passes in some values # from the build to include in the build. @@ -155,31 +155,27 @@ ROMSIZE .EQU ${ROMSize} ; "@ | Out-File "build.inc" -Encoding ASCII -# Bring over previously assembled binary copy of the CP/M CCP and BDOS images for later use. -Copy-Item '..\cpm22\os2ccp.bin' 'ccp.bin' -Copy-Item '..\cpm22\os3bdos.bin' 'bdos.bin' +# # Bring over previously assembled binary copy of Forth for later use. +# Copy-Item '..\Forth\camel80.bin' 'camel80.bin' -# Bring over previously assembled binary copy of the ZSystem CCP and BDOS images for later use. -Copy-Item '..\zcpr-dj\zcpr.bin' 'zcpr.bin' -Copy-Item '..\zsdos\zsdos.bin' 'zsdos.bin' - -# Bring over previously assembled binary copy of Forth for later use. -Copy-Item '..\Forth\camel80.bin' 'camel80.bin' +# Bring over previously generated font files. +Copy-Item '..\Fonts\font*.asm' '.' # Assemble individual components. Note in the case of UNA, there is less to build. -Asm 'dbgmon' -Asm 'prefix' -Asm 'romldr' -Asm 'eastaegg' -Asm 'nascom' -Asm 'tastybasic' -Asm 'imgpad' -Asm 'imgpad0' +$RomComponentList = "dbgmon", "romldr", "eastaegg", "imgpad" +ForEach ($RomComponentName in $RomComponentList) {Asm $RomComponentName} + if ($Platform -ne "UNA") { Asm 'hbios' '-dROMBOOT' -Output 'hbios_rom.bin' -List 'hbios_rom.lst' Asm 'hbios' '-dAPPBOOT' -Output 'hbios_app.bin' -List 'hbios_app.lst' Asm 'hbios' '-dIMGBOOT' -Output 'hbios_img.bin' -List 'hbios_img.lst' + + Asm 'nascom' + Asm 'tastybasic' + Asm 'game' + Asm 'usrrom' + Asm 'imgpad0' } # @@ -188,17 +184,15 @@ if ($Platform -ne "UNA") # "Building ${RomName} output files..." -# Combine the CCP and BDOS portions of CP/M and ZSystem to create OS images -Concat 'ccp.bin','bdos.bin',$CBiosFile 'cpm.bin' -Concat 'zcpr.bin','zsdos.bin',$CBiosFile 'zsys.bin' +# Build 32K OS chunk containing the loader, debug monitor, and OS images +Concat 'romldr.bin', 'eastaegg.bin','dbgmon.bin', "..\cpm22\cpm_${Bios}.bin", "..\zsdos\zsys_${Bios}.bin" osimg.bin -# Prepend a bit of boot code required to bootstrap the OS images -Concat 'prefix.bin','cpm.bin' 'cpm.sys' -Concat 'prefix.bin','zsys.bin' 'zsys.sys' +# Build second 32K chunk containing supplemental ROM apps (not for UNA) +if ($Platform -ne "UNA") +{ + Concat '..\Forth\camel80.bin', 'nascom.bin', 'tastybasic.bin', 'game.bin', 'imgpad0.bin', 'usrrom.bin' osimg1.bin +} -# Build 32K OS chunk containing the loader, debug monitor, and OS images -Concat 'romldr.bin', 'eastaegg.bin','dbgmon.bin', 'cpm.bin', 'zsys.bin' osimg.bin -Concat 'camel80.bin', 'nascom.bin', 'tastybasic.bin', 'imgpad0.bin' osimg1.bin # # Now the ROM disk image is created. This is done by starting with a # blank ROM disk image of the correct size, then cpmtools is used to @@ -226,7 +220,8 @@ foreach ($App in $RomApps) } # Add the CP/M and ZSystem system images to the ROM disk (used by SYSCOPY) -cpmcp -f $RomFmt $RomDiskFile *.sys 0: +cpmcp -f $RomFmt $RomDiskFile ..\cpm22\cpm_${Bios}.sys 0:cpm.sys +cpmcp -f $RomFmt $RomDiskFile ..\zsdos\zsys_${Bios}.sys 0:zsys.sys # # Finally, the individual binary components are concatenated together to produce @@ -246,5 +241,5 @@ else Concat 'hbios_img.bin','osimg.bin' $ImgFile } -# Remove the temprary working ROM disk file +# Remove the temporary working ROM disk file Remove-Item $RomDiskFile diff --git a/Source/HBIOS/Build.sh b/Source/HBIOS/Build.sh new file mode 100755 index 00000000..62c85db4 --- /dev/null +++ b/Source/HBIOS/Build.sh @@ -0,0 +1,153 @@ +#!/bin/bash + +# fail on any error +set -e + +CPMCP=../../Tools/`uname`/cpmcp + +timestamp=$(date +%Y-%m-%d) +#timestamp="2020-02-24" + +if [ $1 == '-d' ] ; then + shift + diffdir=$1 + shift + if [ -f $diffdir/build.inc ] ; then + timestamp=$(grep TIMESTAMP $diffdir/build.inc | awk '{print $3}' | tr -d '\015"') + echo diff build using $timestamp + fi +fi + +# positional arguments +platform=$1 +config=$2 +romsize=$3 +romname=$4 + +# prompt if no match +platforms=($(find Config -name \*.asm -print | \ + sed -e 's,Config/,,' -e 's/_.*$//' | sort -u)) + +while ! echo ${platforms[@]} | grep -q -w -s "$platform" ; do + echo -n "Enter platform [" ${platforms[@]} "] :" + read platform +done + +configs=$(find Config -name ${platform}_\* -print | \ + sed -e 's,Config/,,' -e "s/${platform}_//" -e "s/.asm//") +while ! echo ${configs[@]} | grep -s -w -q "$config" ; do + echo -n "Enter config for $platform [" ${configs[@]} "] :" + read config +done +configfile=Config/${platform}_${config}.asm + +while [ ! '(' "$romsize" = 1024 -o "$romsize" = 512 ')' ] ; do + echo -n "Romsize :" + read romsize +done + +if [ -z "$romname" ] ; then + romname=${platform}_${config} +fi +echo Building for $romname for $platform $config $romsize + +if [ $platform == UNA ] ; then + BIOS=una +else + BIOS=wbw +fi + +Apps=(assign fdu format mode osldr rtc survey syscopy sysgen talk timer xm inttest) + +blankfile=Blank${romsize}KB.dat +romdiskfile=RomDisk.tmp +romfmt=wbw_rom${romsize} +outdir=../../Binary + +echo "creating empty rom disk of size $romsize in $blankfile" +LANG=en_US.US-ASCII tr '\000' '\345' build.inc +; RomWBW Configured for $platform $config $timestamp +; +#DEFINE TIMESTAMP "$timestamp" +; +ROMSIZE .EQU $romsize +; +#INCLUDE "$configfile" +; +EOF + +echo "checking prerequisites" +for need in ../CPM22/cpm_$BIOS.bin ../ZSDOS/zsys_$BIOS.bin \ + ../Forth/camel80.bin font8x11c.asm font8x11u.asm font8x16c.asm \ + font8x16u.asm font8x8c.asm font8x8u.asm ; do + if [ ! -f $need ] ; then + echo $need missing + exit 2 + fi +done + +cp ../Forth/camel80.bin . + +make dbgmon.bin romldr.bin eastaegg.bin imgpad.bin + +if [ $platform != UNA ] ; then + make nascom.bin tastybasic.bin game.bin usrrom.bin imgpad0.bin + make hbios_rom.bin hbios_app.bin hbios_img.bin +fi + +echo "Building $romname output files..." + +cat romldr.bin eastaegg.bin dbgmon.bin ../CPM22/cpm_$BIOS.bin ../ZSDOS/zsys_$BIOS.bin >osimg.bin + +if [ $platform != UNA ] ; then + cat camel80.bin nascom.bin tastybasic.bin game.bin imgpad0.bin usrrom.bin >osimg1.bin +fi + +echo "Building ${romsize}KB $romname ROM disk data file..." + +cp $blankfile $romdiskfile + +echo placing files into $romdiskfile + +for file in $(ls -1 ../RomDsk/ROM_${romsize}KB/* | sort -V) ; do + echo " " $file + $CPMCP -f $romfmt $romdiskfile $file 0: +done + +if [ -d ../RomDsk/$platform ] ; then + for file in ../RomDsk/$platform/* ; do + echo " " $file + $CPMCP -f $romfmt $romdiskfile $file 0: + done +fi + +echo "adding apps to $romdiskfile" +for i in ${Apps[@]} ; do + set +e + f=$(../../Tools/unix/casefn.sh ../../Binary/Apps/$i.com) + set -e + if [ -z "$f" ] ; then + echo " " $i "not found" + else + echo " " $f + $CPMCP -f $romfmt $romdiskfile $f 0: + fi +done + +echo "copying systems to $romdiskfile" +$CPMCP -f $romfmt $romdiskfile ../CPM22/cpm_$BIOS.sys 0:cpm.sys +$CPMCP -f $romfmt $romdiskfile ../ZSDOS/zsys_$BIOS.sys 0:zsys.sys + +if [ $platform = UNA ] ; then + cp osimg.bin $outdir/UNA_WBW_SYS.bin + cp $romdiskfile $outdir/UNA_WBW_ROM$romsize.bin + cat ../UBIOS/UNA-BIOS.BIN osimg.bin ../UBIOS/FSFAT.BIN $romdiskfile >$romname.rom +else + cat hbios_rom.bin osimg.bin osimg1.bin osimg.bin $romdiskfile >$romname.rom + cat hbios_app.bin osimg.bin > $romname.com + cat hbios_img.bin osimg.bin > $romname.img +fi + +#rm $romdiskfile diff --git a/Source/HBIOS/Clean.cmd b/Source/HBIOS/Clean.cmd index ef213acd..c2194335 100644 --- a/Source/HBIOS/Clean.cmd +++ b/Source/HBIOS/Clean.cmd @@ -10,4 +10,5 @@ if exist *.exp del *.exp if exist *.tmp del *.tmp if exist *.mrk del *.mrk if exist *.sys del *.sys -if exist build.inc del build.inc \ No newline at end of file +if exist build.inc del build.inc +if exist font*.asm del font*.asm \ No newline at end of file diff --git a/Source/HBIOS/Config/EZZ80_std.asm b/Source/HBIOS/Config/EZZ80_std.asm index b69ab59d..c0d150bf 100644 --- a/Source/HBIOS/Config/EZZ80_std.asm +++ b/Source/HBIOS/Config/EZZ80_std.asm @@ -25,6 +25,3 @@ #include "cfg_ezz80.asm" ; CPUOSC .SET 10000000 ; CPU OSC FREQ IN MHZ -; -;SIO0ACFG .SET SER_115200_8N1 ; SIO 0A: SERIAL LINE CONFIG -;SIO0BCFG .SET SER_115200_8N1 ; SIO 0B: SERIAL LINE CONFIG diff --git a/Source/HBIOS/Config/MK4_std.asm b/Source/HBIOS/Config/MK4_std.asm index f0e45a2d..4909dc00 100644 --- a/Source/HBIOS/Config/MK4_std.asm +++ b/Source/HBIOS/Config/MK4_std.asm @@ -38,10 +38,8 @@ FDENABLE .SET FALSE ; FD: ENABLE FLOPPY DISK DRIVER (FD.ASM) FDMODE .SET FDMODE_DIDE ; FD: DRIVER MODE: FDMODE_[DIO|ZETA|DIDE|N8|DIO3] ; IDEENABLE .SET TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .SET IDEMODE_MK4 ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] ; PPIDEENABLE .SET FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .SET PPIDEMODE_MFP ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC] ; SDENABLE .SET TRUE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .SET SDMODE_MK4 ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC] diff --git a/Source/HBIOS/Config/RCZ180_ext.asm b/Source/HBIOS/Config/RCZ180_ext.asm index a7a449f7..7c9cca5b 100644 --- a/Source/HBIOS/Config/RCZ180_ext.asm +++ b/Source/HBIOS/Config/RCZ180_ext.asm @@ -33,8 +33,6 @@ Z180_MEMWAIT .SET 0 ; Z180: MEMORY WAIT STATES (0-3) Z180_IOWAIT .SET 1 ; Z180: I/O WAIT STATES TO ADD ABOVE 1 W/S BUILT-IN (0-3) ; ASCIENABLE .SET TRUE ; ASCI: ENABLE Z180 ASCI SERIAL DRIVER (ASCI.ASM) -;ASCI0CFG .SET SER_115200_8N1 ; ASCI 0: SERIAL LINE CONFIG -;ASCI1CFG .SET SER_115200_8N1 ; ASCI 1: SERIAL LINE CONFIG ; ACIAENABLE .SET FALSE ; ACIA: ENABLE MOTOROLA 6850 ACIA DRIVER (ACIA.ASM) SIOENABLE .SET FALSE ; SIO: ENABLE ZILOG SIO SERIAL DRIVER (SIO.ASM) @@ -43,6 +41,5 @@ FDENABLE .SET FALSE ; FD: ENABLE FLOPPY DISK DRIVER (FD.ASM) FDMODE .SET FDMODE_RCWDC ; FD: DRIVER MODE: FDMODE_[DIO|ZETA|DIDE|N8|DIO3] ; IDEENABLE .SET TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .SET IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] ; PPIDEENABLE .SET FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) diff --git a/Source/HBIOS/Config/RCZ180_nat.asm b/Source/HBIOS/Config/RCZ180_nat.asm index 74dc43f5..5c83fe9a 100644 --- a/Source/HBIOS/Config/RCZ180_nat.asm +++ b/Source/HBIOS/Config/RCZ180_nat.asm @@ -33,8 +33,6 @@ Z180_MEMWAIT .SET 0 ; Z180: MEMORY WAIT STATES (0-3) Z180_IOWAIT .SET 1 ; Z180: I/O WAIT STATES TO ADD ABOVE 1 W/S BUILT-IN (0-3) ; ASCIENABLE .SET TRUE ; ASCI: ENABLE Z180 ASCI SERIAL DRIVER (ASCI.ASM) -;ASCI0CFG .SET SER_115200_8N1 ; ASCI 0: SERIAL LINE CONFIG -;ASCI1CFG .SET SER_115200_8N1 ; ASCI 1: SERIAL LINE CONFIG ; ACIAENABLE .SET FALSE ; ACIA: ENABLE MOTOROLA 6850 ACIA DRIVER (ACIA.ASM) SIOENABLE .SET FALSE ; SIO: ENABLE ZILOG SIO SERIAL DRIVER (SIO.ASM) @@ -43,6 +41,5 @@ FDENABLE .SET FALSE ; FD: ENABLE FLOPPY DISK DRIVER (FD.ASM) FDMODE .SET FDMODE_RCWDC ; FD: DRIVER MODE: FDMODE_[DIO|ZETA|DIDE|N8|DIO3] ; IDEENABLE .SET TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .SET IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] ; PPIDEENABLE .SET FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) diff --git a/Source/HBIOS/Config/RCZ80_kio.asm b/Source/HBIOS/Config/RCZ80_kio.asm index d8229e6b..eeede184 100644 --- a/Source/HBIOS/Config/RCZ80_kio.asm +++ b/Source/HBIOS/Config/RCZ80_kio.asm @@ -42,13 +42,10 @@ SIO0BASE .SET KIOBASE+$08 ; SIO 0: REGISTERS BASE ADR SIO0CTCC .SET 0 ; SIO 0: CTC CHANNEL CLOCK SCALER, (0-3), -1 FOR NONE SIO0ACLK .SET 1843200 ; SIO 0A: OSC FREQ IN HZ, ZP=2457600/4915200, RC/SMB=7372800 SIO0BCLK .SET 1843200 ; SIO 0B: OSC FREQ IN HZ, ZP=2457600/4915200, RC/SMB=7372800 -;SIO0ACFG .SET SER_115200_8N1 ; SIO 0A: SERIAL LINE CONFIG -;SIO0BCFG .SET SER_115200_8N1 ; SIO 0B: SERIAL LINE CONFIG ; FDENABLE .SET FALSE ; FD: ENABLE FLOPPY DISK DRIVER (FD.ASM) FDMODE .SET FDMODE_RCWDC ; FD: DRIVER MODE: FDMODE_[DIO|ZETA|DIDE|N8|DIO3] ; IDEENABLE .SET TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .SET IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] ; PPIDEENABLE .SET FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) diff --git a/Source/HBIOS/Config/RCZ80_mt.asm b/Source/HBIOS/Config/RCZ80_mt.asm index d0e4f90a..7006d750 100644 --- a/Source/HBIOS/Config/RCZ80_mt.asm +++ b/Source/HBIOS/Config/RCZ80_mt.asm @@ -24,7 +24,7 @@ ; #DEFINE PLATFORM_NAME "RC2014 (mt)" ; -#include "config/RCZ80_std.asm" +#include "Config/RCZ80_std.asm" ; SDENABLE .SET TRUE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .SET SDMODE_MT ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/Config/RCZ80_std.asm b/Source/HBIOS/Config/RCZ80_std.asm index b990c968..d266d9f2 100644 --- a/Source/HBIOS/Config/RCZ80_std.asm +++ b/Source/HBIOS/Config/RCZ80_std.asm @@ -27,17 +27,12 @@ CPUOSC .SET 7372800 ; CPU OSC FREQ IN MHZ ; ACIAENABLE .SET TRUE ; ACIA: ENABLE MOTOROLA 6850 ACIA DRIVER (ACIA.ASM) -;ACIA0CFG .SET SER_115200_8N1 ; ACIA 0: SERIAL LINE CONFIG (SEE STD.ASM) -;ACIA1CFG .SET SER_115200_8N1 ; ACIA 1: SERIAL LINE CONFIG (SEE STD.ASM) ; SIOENABLE .SET TRUE ; SIO: ENABLE ZILOG SIO SERIAL DRIVER (SIO.ASM) -;SIO0ACFG .SET SER_115200_8N1 ; SIO 0A: SERIAL LINE CONFIG -;SIO0BCFG .SET SER_115200_8N1 ; SIO 0B: SERIAL LINE CONFIG ; FDENABLE .SET FALSE ; FD: ENABLE FLOPPY DISK DRIVER (FD.ASM) FDMODE .SET FDMODE_RCWDC ; FD: DRIVER MODE: FDMODE_[DIO|ZETA|DIDE|N8|DIO3] ; IDEENABLE .SET TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .SET IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] ; PPIDEENABLE .SET FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) diff --git a/Source/HBIOS/Config/RCZ80_wiz.asm b/Source/HBIOS/Config/RCZ80_wiz.asm index bee82841..b61b2d0f 100644 --- a/Source/HBIOS/Config/RCZ80_wiz.asm +++ b/Source/HBIOS/Config/RCZ80_wiz.asm @@ -24,7 +24,7 @@ ; #DEFINE PLATFORM_NAME "RC2014 (wiz)" ; -#include "config/RCZ80_std.asm" +#include "Config/RCZ80_std.asm" ; SDENABLE .SET TRUE SDMODE .SET SDMODE_MT diff --git a/Source/HBIOS/Config/SBC_std.asm b/Source/HBIOS/Config/SBC_std.asm index 8097a3d6..d5cb313a 100644 --- a/Source/HBIOS/Config/SBC_std.asm +++ b/Source/HBIOS/Config/SBC_std.asm @@ -34,10 +34,8 @@ FDENABLE .SET FALSE ; FD: ENABLE FLOPPY DISK DRIVER (FD.ASM) FDMODE .SET FDMODE_DIO3 ; FD: DRIVER MODE: FDMODE_[DIO|ZETA|DIDE|N8|DIO3] ; IDEENABLE .SET FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .SET IDEMODE_DIO ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] ; PPIDEENABLE .SET TRUE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .SET PPIDEMODE_SBC ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC] ; SDENABLE .SET FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .SET SDMODE_PPI ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC] diff --git a/Source/HBIOS/Config/SCZ180_126.asm b/Source/HBIOS/Config/SCZ180_126.asm index 8fdacde6..09d2100a 100644 --- a/Source/HBIOS/Config/SCZ180_126.asm +++ b/Source/HBIOS/Config/SCZ180_126.asm @@ -33,8 +33,6 @@ Z180_MEMWAIT .SET 0 ; Z180: MEMORY WAIT STATES (0-3) Z180_IOWAIT .SET 1 ; Z180: I/O WAIT STATES TO ADD ABOVE 1 W/S BUILT-IN (0-3) ; ASCIENABLE .SET TRUE ; ASCI: ENABLE Z180 ASCI SERIAL DRIVER (ASCI.ASM) -;ASCI0CFG .SET SER_115200_8N1 ; ASCI 0: SERIAL LINE CONFIG -;ASCI1CFG .SET SER_115200_8N1 ; ASCI 1: SERIAL LINE CONFIG ; ACIAENABLE .SET FALSE ; ACIA: ENABLE MOTOROLA 6850 ACIA DRIVER (ACIA.ASM) SIOENABLE .SET FALSE ; SIO: ENABLE ZILOG SIO SERIAL DRIVER (SIO.ASM) @@ -43,7 +41,6 @@ FDENABLE .SET FALSE ; FD: ENABLE FLOPPY DISK DRIVER (FD.ASM) FDMODE .SET FDMODE_RCWDC ; FD: DRIVER MODE: FDMODE_[DIO|ZETA|DIDE|N8|DIO3] ; IDEENABLE .SET TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .SET IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] ; PPIDEENABLE .SET FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) ; diff --git a/Source/HBIOS/Config/SCZ180_130.asm b/Source/HBIOS/Config/SCZ180_130.asm index 5ef52318..ae332544 100644 --- a/Source/HBIOS/Config/SCZ180_130.asm +++ b/Source/HBIOS/Config/SCZ180_130.asm @@ -35,8 +35,6 @@ Z180_IOWAIT .SET 1 ; Z180: I/O WAIT STATES TO ADD ABOVE 1 W/S BUILT-IN (0-3) LEDENABLE .SET TRUE ; ENABLES STATUS LED (SINGLE LED) ; ASCIENABLE .SET TRUE ; ASCI: ENABLE Z180 ASCI SERIAL DRIVER (ASCI.ASM) -;ASCI0CFG .SET SER_115200_8N1 ; ASCI 0: SERIAL LINE CONFIG -;ASCI1CFG .SET SER_115200_8N1 ; ASCI 1: SERIAL LINE CONFIG ; ACIAENABLE .SET FALSE ; ACIA: ENABLE MOTOROLA 6850 ACIA DRIVER (ACIA.ASM) SIOENABLE .SET FALSE ; SIO: ENABLE ZILOG SIO SERIAL DRIVER (SIO.ASM) @@ -45,7 +43,6 @@ FDENABLE .SET FALSE ; FD: ENABLE FLOPPY DISK DRIVER (FD.ASM) FDMODE .SET FDMODE_RCWDC ; FD: DRIVER MODE: FDMODE_[DIO|ZETA|DIDE|N8|DIO3] ; IDEENABLE .SET TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .SET IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] ; PPIDEENABLE .SET FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) ; diff --git a/Source/HBIOS/Makefile b/Source/HBIOS/Makefile new file mode 100644 index 00000000..3ca139f5 --- /dev/null +++ b/Source/HBIOS/Makefile @@ -0,0 +1,70 @@ +OBJECTS = + +ifeq (1,1) +OBJECTS += DYNO_std.rom +OBJECTS += EZZ80_std.rom +OBJECTS += MK4_std.rom +OBJECTS += N8_std.rom +OBJECTS += RCZ180_ext.rom +OBJECTS += RCZ180_nat.rom +OBJECTS += RCZ80_kio.rom +OBJECTS += RCZ80_mt.rom +OBJECTS += RCZ80_std.rom +OBJECTS += RCZ80_wiz.rom +OBJECTS += SBC_simh.rom +OBJECTS += SBC_std.rom +OBJECTS += SCZ180_126.rom +OBJECTS += SCZ180_130.rom +OBJECTS += UNA_std.rom +OBJECTS += ZETA_std.rom +OBJECTS += ZETA2_std.rom +else +OBJECTS += ZETA2_std.rom +endif + +MOREDIFF = camel80.bin game.bin hbios_rom.bin nascom.bin prefix.bin usrrom.bin \ + dbgmon.bin hbios_app.bin imgpad0.bin osimg1.bin romldr.bin \ + eastaegg.bin hbios_img.bin imgpad.bin osimg.bin tastybasic.bin \ + game.bin usrrom.bin + +SUBDIRS = +DEST = ../../Binary +TOOLS =../../Tools +OTHERS = *.img *.rom *.com *.bin *.z80 cpm.sys zsys.sys Build.inc RomDisk.tmp font*.asm +include $(TOOLS)/Makefile.inc + +ifeq ($(DIFFMAKE),1) + DIFFBUILD := -d $(DIFFTO)/Source/HBIOS +endif + +DIFFPATH = $(DIFFTO)/Binary + + +ROMSIZE=512 + +N8_std.rom: ROMSIZE=512 + +%.rom: + bash Build.sh $(DIFFBUILD) $(shell echo $* | tr '_' ' ') $(ROMSIZE) + +#ZETA2_std.rom: +# bash Build.sh ZETA2 std 512 + +hbios_rom.bin: hbios.asm build.inc + $(TASM) -dROMBOOT hbios.asm hbios_rom.bin hbios_rom.lst + +hbios_app.bin: hbios.asm build.inc + $(TASM) -dAPPBOOT hbios.asm hbios_app.bin hbios_app.lst + +hbios_img.bin: hbios.asm build.inc + $(TASM) -dIMGBOOT hbios.asm hbios_img.bin hbios_img.lst + +romldr.bin: build.inc +dbgmon.bin: build.inc +nascom.bin: build.inc +eastaegg.bin: build.inc + +dumps: + for i in $(MOREDIFF) ; do \ + hexdump -C $$i >$$i.dump ; \ + done diff --git a/Source/HBIOS/asci.asm b/Source/HBIOS/asci.asm index 788a705a..26a71d87 100644 --- a/Source/HBIOS/asci.asm +++ b/Source/HBIOS/asci.asm @@ -520,8 +520,10 @@ ASCI_INITGO: INC C ; ... STAT REG, B IS STILL 0 #IF (INTMODE > 0) LD A,$08 ; SET RIE BIT ON - OUT (C),A ; WRITE STAT REG +#ELSE + XOR A ; CLEAR RIE/TIE #ENDIF + OUT (C),A ; WRITE STAT REG LD A,$0E ; BUMP TO ADD A,C ; ... ASEXT REG LD C,A ; PUT IN C FOR I/O, B IS STILL 0 diff --git a/Source/HBIOS/cfg_dyno.asm b/Source/HBIOS/cfg_dyno.asm index 6e034243..0babcf5d 100644 --- a/Source/HBIOS/cfg_dyno.asm +++ b/Source/HBIOS/cfg_dyno.asm @@ -105,14 +105,33 @@ FDMAUTO .EQU TRUE ; FD: AUTO SELECT DEFAULT/ALTERNATE MEDIA FORMATS RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER ; IDEENABLE .EQU FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .EQU IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] IDETRACE .EQU 1 ; IDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -IDE8BIT .EQU TRUE ; IDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) -; -PPIDEENABLE .EQU TRUE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_DYNO ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] +IDECNT .EQU 1 ; IDE: NUMBER OF IDE INTERFACES TO DETECT (1-3), 2 DRIVES EACH +IDE0MODE .EQU IDEMODE_RC ; IDE 0: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE0BASE .EQU $10 ; IDE 0: IO BASE ADDRESS +IDE0DATLO .EQU $00 ; IDE 0: DATA LO PORT FOR 16-BIT I/O +IDE0DATHI .EQU $00 ; IDE 0: DATA HI PORT FOR 16-BIT I/O +IDE0A8BIT .EQU TRUE ; IDE 0A (MASTER): 8 BIT XFER +IDE0B8BIT .EQU TRUE ; IDE 0B (MASTER): 8 BIT XFER +IDE1MODE .EQU IDEMODE_NONE ; IDE 1: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE1BASE .EQU $00 ; IDE 1: IO BASE ADDRESS +IDE1DATLO .EQU $00 ; IDE 1: DATA LO PORT FOR 16-BIT I/O +IDE1DATHI .EQU $00 ; IDE 1: DATA HI PORT FOR 16-BIT I/O +IDE1A8BIT .EQU TRUE ; IDE 1A (MASTER): 8 BIT XFER +IDE1B8BIT .EQU TRUE ; IDE 1B (MASTER): 8 BIT XFER +IDE2MODE .EQU IDEMODE_NONE ; IDE 2: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE2BASE .EQU $00 ; IDE 2: IO BASE ADDRESS +IDE2DATLO .EQU $00 ; IDE 2: DATA LO PORT FOR 16-BIT I/O +IDE2DATHI .EQU $00 ; IDE 2: DATA HI PORT FOR 16-BIT I/O +IDE2A8BIT .EQU TRUE ; IDE 2A (MASTER): 8 BIT XFER +IDE2B8BIT .EQU TRUE ; IDE 2B (MASTER): 8 BIT XFER +; +PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $4C ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_PPI ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cfg_ezz80.asm b/Source/HBIOS/cfg_ezz80.asm index 35fedb50..d691eb2d 100644 --- a/Source/HBIOS/cfg_ezz80.asm +++ b/Source/HBIOS/cfg_ezz80.asm @@ -120,14 +120,33 @@ FDMAUTO .EQU TRUE ; FD: AUTO SELECT DEFAULT/ALTERNATE MEDIA FORMATS RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER ; IDEENABLE .EQU TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .EQU IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] IDETRACE .EQU 1 ; IDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -IDE8BIT .EQU TRUE ; IDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +IDECNT .EQU 1 ; IDE: NUMBER OF IDE INTERFACES TO DETECT (1-3), 2 DRIVES EACH +IDE0MODE .EQU IDEMODE_RC ; IDE 0: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE0BASE .EQU $10 ; IDE 0: IO BASE ADDRESS +IDE0DATLO .EQU $00 ; IDE 0: DATA LO PORT FOR 16-BIT I/O +IDE0DATHI .EQU $00 ; IDE 0: DATA HI PORT FOR 16-BIT I/O +IDE0A8BIT .EQU TRUE ; IDE 0A (MASTER): 8 BIT XFER +IDE0B8BIT .EQU TRUE ; IDE 0B (MASTER): 8 BIT XFER +IDE1MODE .EQU IDEMODE_NONE ; IDE 1: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE1BASE .EQU $00 ; IDE 1: IO BASE ADDRESS +IDE1DATLO .EQU $00 ; IDE 1: DATA LO PORT FOR 16-BIT I/O +IDE1DATHI .EQU $00 ; IDE 1: DATA HI PORT FOR 16-BIT I/O +IDE1A8BIT .EQU TRUE ; IDE 1A (MASTER): 8 BIT XFER +IDE1B8BIT .EQU TRUE ; IDE 1B (MASTER): 8 BIT XFER +IDE2MODE .EQU IDEMODE_NONE ; IDE 2: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE2BASE .EQU $00 ; IDE 2: IO BASE ADDRESS +IDE2DATLO .EQU $00 ; IDE 2: DATA LO PORT FOR 16-BIT I/O +IDE2DATHI .EQU $00 ; IDE 2: DATA HI PORT FOR 16-BIT I/O +IDE2A8BIT .EQU TRUE ; IDE 2A (MASTER): 8 BIT XFER +IDE2B8BIT .EQU TRUE ; IDE 2B (MASTER): 8 BIT XFER ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_RC ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $20 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_PPI ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cfg_master.asm b/Source/HBIOS/cfg_master.asm index 41395a09..f02968d7 100644 --- a/Source/HBIOS/cfg_master.asm +++ b/Source/HBIOS/cfg_master.asm @@ -96,6 +96,7 @@ UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA ONBOARD UART UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART +UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED ; ASCIENABLE .EQU FALSE ; ASCI: ENABLE Z180 ASCI SERIAL DRIVER (ASCI.ASM) ASCI0CFG .EQU DEFSERCFG ; ASCI 0: SERIAL LINE CONFIG @@ -160,17 +161,42 @@ FDMEDIAALT .EQU FDM720 ; FD: ALTERNATE MEDIA FORMAT FDM[720|144|360|120|111] FDMAUTO .EQU TRUE ; FD: AUTO SELECT DEFAULT/ALTERNATE MEDIA FORMATS ; RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER -RFCNT .EQU 1 ; RF: NUMBER OF RAM FLOPPY UNITS (1-2) +RFCNT .EQU 1 ; RF: NUMBER OF RAM FLOPPY UNITS (1-4) ; IDEENABLE .EQU FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .EQU IDEMODE_NONE ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] IDETRACE .EQU 1 ; IDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -IDE8BIT .EQU FALSE ; IDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +IDECNT .EQU 1 ; IDE: NUMBER OF IDE INTERFACES TO DETECT (1-3), 2 DRIVES EACH +IDE0MODE .EQU IDEMODE_NONE ; IDE 0: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE0BASE .EQU $20 ; IDE 0: IO BASE ADDRESS +IDE0DATLO .EQU $00 ; IDE 0: DATA LO PORT FOR 16-BIT I/O +IDE0DATHI .EQU $00 ; IDE 0: DATA HI PORT FOR 16-BIT I/O +IDE0A8BIT .EQU TRUE ; IDE 0A (MASTER): 8 BIT XFER +IDE0B8BIT .EQU TRUE ; IDE 0B (MASTER): 8 BIT XFER +IDE1MODE .EQU IDEMODE_NONE ; IDE 1: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE1BASE .EQU $00 ; IDE 1: IO BASE ADDRESS +IDE1DATLO .EQU $00 ; IDE 1: DATA LO PORT FOR 16-BIT I/O +IDE1DATHI .EQU $00 ; IDE 1: DATA HI PORT FOR 16-BIT I/O +IDE1A8BIT .EQU TRUE ; IDE 1A (MASTER): 8 BIT XFER +IDE1B8BIT .EQU TRUE ; IDE 1B (MASTER): 8 BIT XFER +IDE2MODE .EQU IDEMODE_NONE ; IDE 2: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE2BASE .EQU $00 ; IDE 2: IO BASE ADDRESS +IDE2DATLO .EQU $00 ; IDE 2: DATA LO PORT FOR 16-BIT I/O +IDE2DATHI .EQU $00 ; IDE 2: DATA HI PORT FOR 16-BIT I/O +IDE2A8BIT .EQU TRUE ; IDE 2A (MASTER): 8 BIT XFER +IDE2B8BIT .EQU TRUE ; IDE 2B (MASTER): 8 BIT XFER ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_NONE ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $60 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE1BASE .EQU $20 ; PPIDE 1: PPI REGISTERS BASE ADR +PPIDE1A8BIT .EQU FALSE ; PPIDE 1A (MASTER): 8 BIT XFER +PPIDE1B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE2BASE .EQU $44 ; PPIDE 2: PPI REGISTERS BASE ADR +PPIDE2A8BIT .EQU FALSE ; PPIDE 2A (MASTER): 8 BIT XFER +PPIDE2B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_NONE ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cfg_mk4.asm b/Source/HBIOS/cfg_mk4.asm index a614407f..37025c1a 100644 --- a/Source/HBIOS/cfg_mk4.asm +++ b/Source/HBIOS/cfg_mk4.asm @@ -81,6 +81,7 @@ UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA ONBOARD UART UARTCAS .EQU TRUE ; UART: AUTO-DETECT ECB CASSETTE UART UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART UART4 .EQU TRUE ; UART: AUTO-DETECT 4UART UART +UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED ; ASCIENABLE .EQU TRUE ; ASCI: ENABLE Z180 ASCI SERIAL DRIVER (ASCI.ASM) ASCI0CFG .EQU DEFSERCFG ; ASCI 0: SERIAL LINE CONFIG @@ -115,17 +116,42 @@ FDMEDIAALT .EQU FDM720 ; FD: ALTERNATE MEDIA FORMAT FDM[720|144|360|120|111] FDMAUTO .EQU TRUE ; FD: AUTO SELECT DEFAULT/ALTERNATE MEDIA FORMATS ; RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER -RFCNT .EQU 1 ; RF: NUMBER OF RAM FLOPPY UNITS (1-2) +RFCNT .EQU 1 ; RF: NUMBER OF RAM FLOPPY UNITS (1-4) ; IDEENABLE .EQU TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .EQU IDEMODE_MK4 ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] IDETRACE .EQU 1 ; IDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -IDE8BIT .EQU TRUE ; IDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +IDECNT .EQU 1 ; IDE: NUMBER OF IDE INTERFACES TO DETECT (1-3), 2 DRIVES EACH +IDE0MODE .EQU IDEMODE_MK4 ; IDE 0: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE0BASE .EQU $80 ; IDE 0: IO BASE ADDRESS +IDE0DATLO .EQU $00 ; IDE 0: DATA LO PORT FOR 16-BIT I/O +IDE0DATHI .EQU $00 ; IDE 0: DATA HI PORT FOR 16-BIT I/O +IDE0A8BIT .EQU TRUE ; IDE 0A (MASTER): 8 BIT XFER +IDE0B8BIT .EQU TRUE ; IDE 0B (MASTER): 8 BIT XFER +IDE1MODE .EQU IDEMODE_DIDE ; IDE 1: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE1BASE .EQU $20 ; IDE 1: IO BASE ADDRESS +IDE1DATLO .EQU $28 ; IDE 1: DATA LO PORT FOR 16-BIT I/O +IDE1DATHI .EQU $28 ; IDE 1: DATA HI PORT FOR 16-BIT I/O +IDE1A8BIT .EQU FALSE ; IDE 1A (MASTER): 8 BIT XFER +IDE1B8BIT .EQU FALSE ; IDE 1B (MASTER): 8 BIT XFER +IDE2MODE .EQU IDEMODE_DIDE ; IDE 2: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE2BASE .EQU $30 ; IDE 2: IO BASE ADDRESS +IDE2DATLO .EQU $38 ; IDE 2: DATA LO PORT FOR 16-BIT I/O +IDE2DATHI .EQU $38 ; IDE 2: DATA HI PORT FOR 16-BIT I/O +IDE2A8BIT .EQU FALSE ; IDE 2A (MASTER): 8 BIT XFER +IDE2B8BIT .EQU FALSE ; IDE 2B (MASTER): 8 BIT XFER ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_DIO3 ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 2 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $44 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE1BASE .EQU $20 ; PPIDE 1: PPI REGISTERS BASE ADR +PPIDE1A8BIT .EQU FALSE ; PPIDE 1A (MASTER): 8 BIT XFER +PPIDE1B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE2BASE .EQU $00 ; PPIDE 2: PPI REGISTERS BASE ADR +PPIDE2A8BIT .EQU FALSE ; PPIDE 2A (MASTER): 8 BIT XFER +PPIDE2B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU TRUE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_MK4 ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cfg_n8.asm b/Source/HBIOS/cfg_n8.asm index 22498548..3592584b 100644 --- a/Source/HBIOS/cfg_n8.asm +++ b/Source/HBIOS/cfg_n8.asm @@ -84,6 +84,7 @@ UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA ONBOARD UART UARTCAS .EQU TRUE ; UART: AUTO-DETECT ECB CASSETTE UART UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART UART4 .EQU TRUE ; UART: AUTO-DETECT 4UART UART +UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED ; ASCIENABLE .EQU TRUE ; ASCI: ENABLE Z180 ASCI SERIAL DRIVER (ASCI.ASM) ASCI0CFG .EQU DEFSERCFG ; ASCI 0: SERIAL LINE CONFIG @@ -118,17 +119,42 @@ FDMEDIAALT .EQU FDM720 ; FD: ALTERNATE MEDIA FORMAT FDM[720|144|360|120|111] FDMAUTO .EQU TRUE ; FD: AUTO SELECT DEFAULT/ALTERNATE MEDIA FORMATS ; RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER -RFCNT .EQU 1 ; RF: NUMBER OF RAM FLOPPY UNITS (1-2) +RFCNT .EQU 1 ; RF: NUMBER OF RAM FLOPPY UNITS (1-4) ; IDEENABLE .EQU FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .EQU IDEMODE_DIO ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] IDETRACE .EQU 1 ; IDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -IDE8BIT .EQU FALSE ; IDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +IDECNT .EQU 1 ; IDE: NUMBER OF IDE INTERFACES TO DETECT (1-3), 2 DRIVES EACH +IDE0MODE .EQU IDEMODE_DIO ; IDE 0: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE0BASE .EQU $20 ; IDE 0: IO BASE ADDRESS +IDE0DATLO .EQU $20 ; IDE 0: DATA LO PORT FOR 16-BIT I/O +IDE0DATHI .EQU $28 ; IDE 0: DATA HI PORT FOR 16-BIT I/O +IDE0A8BIT .EQU FALSE ; IDE 0A (MASTER): 8 BIT XFER +IDE0B8BIT .EQU FALSE ; IDE 0B (MASTER): 8 BIT XFER +IDE1MODE .EQU IDEMODE_NONE ; IDE 1: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE1BASE .EQU $00 ; IDE 1: IO BASE ADDRESS +IDE1DATLO .EQU $00 ; IDE 1: DATA LO PORT FOR 16-BIT I/O +IDE1DATHI .EQU $00 ; IDE 1: DATA HI PORT FOR 16-BIT I/O +IDE1A8BIT .EQU TRUE ; IDE 1A (MASTER): 8 BIT XFER +IDE1B8BIT .EQU TRUE ; IDE 1B (MASTER): 8 BIT XFER +IDE2MODE .EQU IDEMODE_NONE ; IDE 2: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE2BASE .EQU $00 ; IDE 2: IO BASE ADDRESS +IDE2DATLO .EQU $00 ; IDE 2: DATA LO PORT FOR 16-BIT I/O +IDE2DATHI .EQU $00 ; IDE 2: DATA HI PORT FOR 16-BIT I/O +IDE2A8BIT .EQU TRUE ; IDE 2A (MASTER): 8 BIT XFER +IDE2B8BIT .EQU TRUE ; IDE 2B (MASTER): 8 BIT XFER ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_N8 ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $80 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE1BASE .EQU $00 ; PPIDE 1: PPI REGISTERS BASE ADR +PPIDE1A8BIT .EQU FALSE ; PPIDE 1A (MASTER): 8 BIT XFER +PPIDE1B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE2BASE .EQU $00 ; PPIDE 2: PPI REGISTERS BASE ADR +PPIDE2A8BIT .EQU FALSE ; PPIDE 2A (MASTER): 8 BIT XFER +PPIDE2B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU TRUE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_CSIO ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cfg_rcz180.asm b/Source/HBIOS/cfg_rcz180.asm index f8ac4d1e..aeb1f0c4 100644 --- a/Source/HBIOS/cfg_rcz180.asm +++ b/Source/HBIOS/cfg_rcz180.asm @@ -24,7 +24,7 @@ BOOT_DEFAULT .EQU 'Z' ; AUTO BOOT SELECTION TO INVOKE AT TIMEOUT ; CPUOSC .EQU 18432000 ; CPU OSC FREQ IN MHZ INTMODE .EQU 2 ; INTERRUPTS: 0=NONE, 1=MODE 1, 2=MODE 2 -DEFSERCFG .EQU SER_38400_8N1 ; DEFAULT SERIAL LINE CONFIG (SEE STD.ASM) +DEFSERCFG .EQU SER_115200_8N1 ; DEFAULT SERIAL LINE CONFIG (SEE STD.ASM) ; RAMSIZE .EQU 512 ; SIZE OF RAM IN KB (MUST MATCH YOUR HARDWARE!!!) MEMMGR .EQU MM_Z180 ; MEMORY MANAGER: MM_[SBC|Z2|N8|Z180] @@ -125,15 +125,39 @@ FDMAUTO .EQU TRUE ; FD: AUTO SELECT DEFAULT/ALTERNATE MEDIA FORMATS RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER ; IDEENABLE .EQU FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .EQU IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] IDETRACE .EQU 1 ; IDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -IDE8BIT .EQU TRUE ; IDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +IDECNT .EQU 1 ; IDE: NUMBER OF IDE INTERFACES TO DETECT (1-3), 2 DRIVES EACH +IDE0MODE .EQU IDEMODE_RC ; IDE 0: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE0BASE .EQU $10 ; IDE 0: IO BASE ADDRESS +IDE0DATLO .EQU $00 ; IDE 0: DATA LO PORT FOR 16-BIT I/O +IDE0DATHI .EQU $00 ; IDE 0: DATA HI PORT FOR 16-BIT I/O +IDE0A8BIT .EQU TRUE ; IDE 0A (MASTER): 8 BIT XFER +IDE0B8BIT .EQU TRUE ; IDE 0B (MASTER): 8 BIT XFER +IDE1MODE .EQU IDEMODE_NONE ; IDE 1: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE1BASE .EQU $00 ; IDE 1: IO BASE ADDRESS +IDE1DATLO .EQU $00 ; IDE 1: DATA LO PORT FOR 16-BIT I/O +IDE1DATHI .EQU $00 ; IDE 1: DATA HI PORT FOR 16-BIT I/O +IDE1A8BIT .EQU TRUE ; IDE 1A (MASTER): 8 BIT XFER +IDE1B8BIT .EQU TRUE ; IDE 1B (MASTER): 8 BIT XFER +IDE2MODE .EQU IDEMODE_NONE ; IDE 2: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE2BASE .EQU $00 ; IDE 2: IO BASE ADDRESS +IDE2DATLO .EQU $00 ; IDE 2: DATA LO PORT FOR 16-BIT I/O +IDE2DATHI .EQU $00 ; IDE 2: DATA HI PORT FOR 16-BIT I/O +IDE2A8BIT .EQU TRUE ; IDE 2A (MASTER): 8 BIT XFER +IDE2B8BIT .EQU TRUE ; IDE 2B (MASTER): 8 BIT XFER ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_RC ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) -; +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $20 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE1BASE .EQU $00 ; PPIDE 1: PPI REGISTERS BASE ADR +PPIDE1A8BIT .EQU FALSE ; PPIDE 1A (MASTER): 8 BIT XFER +PPIDE1B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE2BASE .EQU $00 ; PPIDE 2: PPI REGISTERS BASE ADR +PPIDE2A8BIT .EQU FALSE ; PPIDE 2A (MASTER): 8 BIT XFER +PPIDE2B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER; SDENABLE .EQU FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_PPI ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] SDCNT .EQU 1 ; SD: NUMBER OF SD CARD DEVICES (1-2), FOR DSD & SC ONLY diff --git a/Source/HBIOS/cfg_rcz80.asm b/Source/HBIOS/cfg_rcz80.asm index 89b70b4f..3a49b4d3 100644 --- a/Source/HBIOS/cfg_rcz80.asm +++ b/Source/HBIOS/cfg_rcz80.asm @@ -129,14 +129,39 @@ FDMAUTO .EQU TRUE ; FD: AUTO SELECT DEFAULT/ALTERNATE MEDIA FORMATS RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER ; IDEENABLE .EQU FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .EQU IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] IDETRACE .EQU 1 ; IDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -IDE8BIT .EQU TRUE ; IDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +IDECNT .EQU 1 ; IDE: NUMBER OF IDE INTERFACES TO DETECT (1-3), 2 DRIVES EACH +IDE0MODE .EQU IDEMODE_RC ; IDE 0: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE0BASE .EQU $10 ; IDE 0: IO BASE ADDRESS +IDE0DATLO .EQU $00 ; IDE 0: DATA LO PORT FOR 16-BIT I/O +IDE0DATHI .EQU $00 ; IDE 0: DATA HI PORT FOR 16-BIT I/O +IDE0A8BIT .EQU TRUE ; IDE 0A (MASTER): 8 BIT XFER +IDE0B8BIT .EQU TRUE ; IDE 0B (MASTER): 8 BIT XFER +IDE1MODE .EQU IDEMODE_NONE ; IDE 1: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE1BASE .EQU $00 ; IDE 1: IO BASE ADDRESS +IDE1DATLO .EQU $00 ; IDE 1: DATA LO PORT FOR 16-BIT I/O +IDE1DATHI .EQU $00 ; IDE 1: DATA HI PORT FOR 16-BIT I/O +IDE1A8BIT .EQU TRUE ; IDE 1A (MASTER): 8 BIT XFER +IDE1B8BIT .EQU TRUE ; IDE 1B (MASTER): 8 BIT XFER +IDE2MODE .EQU IDEMODE_NONE ; IDE 2: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE2BASE .EQU $00 ; IDE 2: IO BASE ADDRESS +IDE2DATLO .EQU $00 ; IDE 2: DATA LO PORT FOR 16-BIT I/O +IDE2DATHI .EQU $00 ; IDE 2: DATA HI PORT FOR 16-BIT I/O +IDE2A8BIT .EQU TRUE ; IDE 2A (MASTER): 8 BIT XFER +IDE2B8BIT .EQU TRUE ; IDE 2B (MASTER): 8 BIT XFER ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_RC ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $20 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE1BASE .EQU $00 ; PPIDE 1: PPI REGISTERS BASE ADR +PPIDE1A8BIT .EQU FALSE ; PPIDE 1A (MASTER): 8 BIT XFER +PPIDE1B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE2BASE .EQU $00 ; PPIDE 2: PPI REGISTERS BASE ADR +PPIDE2A8BIT .EQU FALSE ; PPIDE 2A (MASTER): 8 BIT XFER +PPIDE2B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_PPI ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cfg_sbc.asm b/Source/HBIOS/cfg_sbc.asm index 54dcea5e..78193630 100644 --- a/Source/HBIOS/cfg_sbc.asm +++ b/Source/HBIOS/cfg_sbc.asm @@ -75,6 +75,7 @@ UARTSBC .EQU TRUE ; UART: AUTO-DETECT SBC/ZETA ONBOARD UART UARTCAS .EQU TRUE ; UART: AUTO-DETECT ECB CASSETTE UART UARTMFP .EQU TRUE ; UART: AUTO-DETECT MF/PIC UART UART4 .EQU TRUE ; UART: AUTO-DETECT 4UART UART +UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED ; ASCIENABLE .EQU FALSE ; ASCI: ENABLE Z180 ASCI SERIAL DRIVER (ASCI.ASM) ; @@ -118,17 +119,42 @@ FDMEDIAALT .EQU FDM720 ; FD: ALTERNATE MEDIA FORMAT FDM[720|144|360|120|111] FDMAUTO .EQU TRUE ; FD: AUTO SELECT DEFAULT/ALTERNATE MEDIA FORMATS ; RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER -RFCNT .EQU 1 ; RF: NUMBER OF RAM FLOPPY UNITS (1-2) +RFCNT .EQU 1 ; RF: NUMBER OF RAM FLOPPY UNITS (1-4) ; IDEENABLE .EQU FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .EQU IDEMODE_DIO ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] IDETRACE .EQU 1 ; IDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -IDE8BIT .EQU FALSE ; IDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +IDECNT .EQU 1 ; IDE: NUMBER OF IDE INTERFACES TO DETECT (1-3), 2 DRIVES EACH +IDE0MODE .EQU IDEMODE_DIO ; IDE 0: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE0BASE .EQU $20 ; IDE 0: IO BASE ADDRESS +IDE0DATLO .EQU $20 ; IDE 0: DATA LO PORT FOR 16-BIT I/O +IDE0DATHI .EQU $28 ; IDE 0: DATA HI PORT FOR 16-BIT I/O +IDE0A8BIT .EQU FALSE ; IDE 0A (MASTER): 8 BIT XFER +IDE0B8BIT .EQU FALSE ; IDE 0B (MASTER): 8 BIT XFER +IDE1MODE .EQU IDEMODE_NONE ; IDE 1: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE1BASE .EQU $00 ; IDE 1: IO BASE ADDRESS +IDE1DATLO .EQU $00 ; IDE 1: DATA LO PORT FOR 16-BIT I/O +IDE1DATHI .EQU $00 ; IDE 1: DATA HI PORT FOR 16-BIT I/O +IDE1A8BIT .EQU TRUE ; IDE 1A (MASTER): 8 BIT XFER +IDE1B8BIT .EQU TRUE ; IDE 1B (MASTER): 8 BIT XFER +IDE2MODE .EQU IDEMODE_NONE ; IDE 2: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE2BASE .EQU $00 ; IDE 2: IO BASE ADDRESS +IDE2DATLO .EQU $00 ; IDE 2: DATA LO PORT FOR 16-BIT I/O +IDE2DATHI .EQU $00 ; IDE 2: DATA HI PORT FOR 16-BIT I/O +IDE2A8BIT .EQU TRUE ; IDE 2A (MASTER): 8 BIT XFER +IDE2B8BIT .EQU TRUE ; IDE 2B (MASTER): 8 BIT XFER ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_SBC ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $60 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE1BASE .EQU $20 ; PPIDE 1: PPI REGISTERS BASE ADR +PPIDE1A8BIT .EQU FALSE ; PPIDE 1A (MASTER): 8 BIT XFER +PPIDE1B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE2BASE .EQU $44 ; PPIDE 2: PPI REGISTERS BASE ADR +PPIDE2A8BIT .EQU FALSE ; PPIDE 2A (MASTER): 8 BIT XFER +PPIDE2B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_JUHA ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cfg_scz180.asm b/Source/HBIOS/cfg_scz180.asm index fd02bc7c..532544d7 100644 --- a/Source/HBIOS/cfg_scz180.asm +++ b/Source/HBIOS/cfg_scz180.asm @@ -24,7 +24,7 @@ BOOT_DEFAULT .EQU 'Z' ; AUTO BOOT SELECTION TO INVOKE AT TIMEOUT ; CPUOSC .EQU 18432000 ; CPU OSC FREQ IN MHZ INTMODE .EQU 2 ; INTERRUPTS: 0=NONE, 1=MODE 1, 2=MODE 2 -DEFSERCFG .EQU SER_38400_8N1 ; DEFAULT SERIAL LINE CONFIG (SEE STD.ASM) +DEFSERCFG .EQU SER_115200_8N1 ; DEFAULT SERIAL LINE CONFIG (SEE STD.ASM) ; RAMSIZE .EQU 512 ; SIZE OF RAM IN KB (MUST MATCH YOUR HARDWARE!!!) MEMMGR .EQU MM_Z180 ; MEMORY MANAGER: MM_[SBC|Z2|N8|Z180] @@ -120,14 +120,39 @@ FDMAUTO .EQU TRUE ; FD: AUTO SELECT DEFAULT/ALTERNATE MEDIA FORMATS RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER ; IDEENABLE .EQU FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) -IDEMODE .EQU IDEMODE_RC ; IDE: DRIVER MODE: IDEMODE_[DIO|DIDE] IDETRACE .EQU 1 ; IDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -IDE8BIT .EQU TRUE ; IDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +IDECNT .EQU 1 ; IDE: NUMBER OF IDE INTERFACES TO DETECT (1-3), 2 DRIVES EACH +IDE0MODE .EQU IDEMODE_RC ; IDE 0: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE0BASE .EQU $10 ; IDE 0: IO BASE ADDRESS +IDE0DATLO .EQU $00 ; IDE 0: DATA LO PORT FOR 16-BIT I/O +IDE0DATHI .EQU $00 ; IDE 0: DATA HI PORT FOR 16-BIT I/O +IDE0A8BIT .EQU TRUE ; IDE 0A (MASTER): 8 BIT XFER +IDE0B8BIT .EQU TRUE ; IDE 0B (MASTER): 8 BIT XFER +IDE1MODE .EQU IDEMODE_NONE ; IDE 1: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE1BASE .EQU $00 ; IDE 1: IO BASE ADDRESS +IDE1DATLO .EQU $00 ; IDE 1: DATA LO PORT FOR 16-BIT I/O +IDE1DATHI .EQU $00 ; IDE 1: DATA HI PORT FOR 16-BIT I/O +IDE1A8BIT .EQU TRUE ; IDE 1A (MASTER): 8 BIT XFER +IDE1B8BIT .EQU TRUE ; IDE 1B (MASTER): 8 BIT XFER +IDE2MODE .EQU IDEMODE_NONE ; IDE 2: DRIVER MODE: IDEMODE_[DIO|DIDE|MK4|RC] +IDE2BASE .EQU $00 ; IDE 2: IO BASE ADDRESS +IDE2DATLO .EQU $00 ; IDE 2: DATA LO PORT FOR 16-BIT I/O +IDE2DATHI .EQU $00 ; IDE 2: DATA HI PORT FOR 16-BIT I/O +IDE2A8BIT .EQU TRUE ; IDE 2A (MASTER): 8 BIT XFER +IDE2B8BIT .EQU TRUE ; IDE 2B (MASTER): 8 BIT XFER ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_RC ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $20 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE1BASE .EQU $00 ; PPIDE 1: PPI REGISTERS BASE ADR +PPIDE1A8BIT .EQU FALSE ; PPIDE 1A (MASTER): 8 BIT XFER +PPIDE1B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER +PPIDE2BASE .EQU $00 ; PPIDE 2: PPI REGISTERS BASE ADR +PPIDE2A8BIT .EQU FALSE ; PPIDE 2A (MASTER): 8 BIT XFER +PPIDE2B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU TRUE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_SC ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cfg_zeta.asm b/Source/HBIOS/cfg_zeta.asm index 4997be12..d34cfdbb 100644 --- a/Source/HBIOS/cfg_zeta.asm +++ b/Source/HBIOS/cfg_zeta.asm @@ -101,9 +101,11 @@ RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER IDEENABLE .EQU FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_SBC ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $60 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_PPI ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cfg_zeta2.asm b/Source/HBIOS/cfg_zeta2.asm index 65c3e8d5..44c6bdcd 100644 --- a/Source/HBIOS/cfg_zeta2.asm +++ b/Source/HBIOS/cfg_zeta2.asm @@ -106,9 +106,11 @@ RFENABLE .EQU FALSE ; RF: ENABLE RAM FLOPPY DRIVER IDEENABLE .EQU FALSE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM) ; PPIDEENABLE .EQU FALSE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM) -PPIDEMODE .EQU PPIDEMODE_SBC ; PPIDE: DRIVER MODE: PPIDEMODE_[SBC|DIO3|MFP|N8|RC|DYNO] PPIDETRACE .EQU 1 ; PPIDE: TRACE LEVEL (0=NO,1=ERRORS,2=ALL) -PPIDE8BIT .EQU FALSE ; PPIDE: USE 8-BIT TRANSFERS (CF CARDS MOSTLY) +PPIDECNT .EQU 1 ; PPIDE: NUMBER OF PPI CHIPS TO DETECT (1-3), 2 DRIVES PER CHIP +PPIDE0BASE .EQU $60 ; PPIDE 0: PPI REGISTERS BASE ADR +PPIDE0A8BIT .EQU FALSE ; PPIDE 0A (MASTER): 8 BIT XFER +PPIDE0B8BIT .EQU FALSE ; PPIDE 0B (SLAVE): 8 BIT XFER ; SDENABLE .EQU FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM) SDMODE .EQU SDMODE_PPI ; SD: DRIVER MODE: SDMODE_[JUHA|N8|CSIO|PPI|UART|DSD|MK4|SC|MT] diff --git a/Source/HBIOS/cvdu.asm b/Source/HBIOS/cvdu.asm index d117c412..9ee98c40 100644 --- a/Source/HBIOS/cvdu.asm +++ b/Source/HBIOS/cvdu.asm @@ -10,6 +10,7 @@ ; - IMPLEMENT SET CURSOR STYLE (VDASCS) FUNCTION ; - IMPLEMENT ALTERNATE DISPLAY MODES? ; - IMPLEMENT DYNAMIC READ/WRITE OF CHARACTER BITMAP DATA? +; - IMPLEMENT TIMEOUT ON PROBE ; ;====================================================================== ; CVDU DRIVER - CONSTANTS @@ -26,6 +27,9 @@ CVDU_DATA .EQU CVDU_BASE + $0C ; READ/WRITE M8563 DATA CVDU_ROWS .EQU 25 CVDU_COLS .EQU 80 ; +#DEFINE USEFONT8X16 +#DEFINE CVDU_FONT FONT8X16 +; TERMENABLE .SET TRUE ; INCLUDE TERMINAL PSEUDODEVICE DRIVER ; ;====================================================================== @@ -58,12 +62,12 @@ CVDU_INIT1: ; ADD OURSELVES TO VDA DISPATCH TABLE LD BC,CVDU_FNTBL ; BC := FUNCTION TABLE ADDRESS - LD DE,CVDU_IDAT ; DE := VGA INSTANCE DATA PTR + LD DE,CVDU_IDAT ; DE := CVDU INSTANCE DATA PTR CALL VDA_ADDENT ; ADD ENTRY, A := UNIT ASSIGNED ; INITIALIZE EMULATION LD C,A ; C := ASSIGNED VIDEO DEVICE NUM - LD DE,VGA_FNTBL ; DE := FUNCTION TABLE ADDRESS + LD DE,CVDU_FNTBL ; DE := FUNCTION TABLE ADDRESS LD HL,CVDU_IDAT ; HL := CVDU INSTANCE DATA PTR CALL TERM_ATTACH ; DO IT @@ -281,7 +285,6 @@ CVDU_PROBE: CALL CVDU_WR ; WRITE VALUE TO LOC 0, ADR PTR INCREMENTS CPL ; INVERT TEST VALUE CALL CVDU_WR ; WRITE INVERTED VALUE TO LOC 1 - ; READ TEST PATTERN BACK TO CONFIRM HARDWARE EXISTS LD HL,0 ; POINT TO FIRST BYTE OF VRAM LD C,18 ; ADDRESS REGISTER PAIR @@ -360,7 +363,22 @@ CVDU_LOADFONT: LD C,18 ; UPDATE ADDRESS REGISTER PAIR CALL CVDU_WRX ; DO IT - LD HL,CVDU_FONT ; POINTER TO FONT DATA +#IF USELZSA2 + LD (CVDU_STACK),SP ; SAVE STACK + LD HL,(CVDU_STACK) ; AND SHIFT IT + LD DE,$2000 ; DOWN 4KB TO + CCF ; CREATE A + SBC HL,DE ; DECOMPRESSION BUFFER + LD SP,HL ; HL POINTS TO BUFFER + EX DE,HL ; START OF STACK BUFFER + PUSH DE ; SAVE IT + LD HL,CVDU_FONT ; START OF FONT DATA + CALL DLZSA2 ; DECOMPRESS TO DE + POP HL ; RECALL STACK BUFFER POSITION +#ELSE + LD HL,CVDU_FONT ; START OF FONT DATA +#ENDIF + LD DE,$2000 ; LENGTH OF FONT DATA LD C,31 ; DATA REGISTER CVDU_LOADFONT1: @@ -371,7 +389,15 @@ CVDU_LOADFONT1: LD A,D ; CHECK DE... OR E ; FOR COUNTER EXHAUSTED JR NZ,CVDU_LOADFONT1 ; LOOP TILL DONE + +#IF USELZSA2 + LD HL,(CVDU_STACK) ; ERASE DECOMPRESS BUFFER + LD SP,HL ; BY RESTORING THE STACK + RET ; DONE +CVDU_STACK .DW 0 +#ELSE RET +#ENDIF ; ;---------------------------------------------------------------------- ; SET CURSOR POSITION TO ROW IN D AND COLUMN IN E diff --git a/Source/HBIOS/eastaegg.asm b/Source/HBIOS/eastaegg.asm index 24744f11..66982bee 100644 --- a/Source/HBIOS/eastaegg.asm +++ b/Source/HBIOS/eastaegg.asm @@ -28,7 +28,7 @@ scale .equ 256 ; Do NOT change this - the ; this scaling factor! :-) divergent .equ scale * 4 - ld sp,0fdffh + ld sp,HBX_LOC ld hl, welcome ; Print a welcome message call _puts @@ -173,16 +173,29 @@ inner_loop_end call _putcrlf ; Print a CR/LF pair mandel_end ld hl, finished ; Print finished-message call _puts ; GET CONSOLE INPUT STATUS VIA HBIOS -waitch LD C,CIODEV_CONSOLE ; CONSOLE UNIT TO C - LD B,BF_CIOIST ; HBIOS FUNC: INPUT STATUS - RST 08 ; HBIOS RETURNS STATUS IN A - ; RESTORE REGISTERS (AF IS OUTPUT) - JR Z,waitch - ; Return to the loader - LD A,BID_BOOT ; BOOT BANK - LD HL,0 ; ADDRESS ZERO - CALL HB_BNKCALL ; DOES NOT RETURN - HALT +waitch +#IF (BIOS == BIOS_WBW) + LD C,CIODEV_CONSOLE ; CONSOLE UNIT TO C + LD B,BF_CIOIN ; HBIOS FUNC: INPUT CHAR + RST 08 ; DO IT + + ; RETURN TO THE LOADER + LD A,BID_BOOT ; BOOT BANK + LD HL,0 ; ADDRESS ZERO + CALL HB_BNKCALL ; DOES NOT RETURN + HALT +#ENDIF +#IF (BIOS == BIOS_UNA) + LD B,0 ; CONSOLE UNIT TO B + LD C,BF_CIOIN ; UBIOS FUNC: INPUT CHAR + CALL $FFFD ; DO IT + + ; RETURN TO THE LOADER + LD BC,$01FB ; UNA FUNC = SET BANK + LD DE,0 ; ROM BANK 0 + CALL $FFFD ; DO IT + JP 0 ; JUMP TO RESTART ADDRESS +#ENDIF _putcrlf ld hl, crlf _puts push af @@ -195,22 +208,41 @@ puts0 ld a,(hl) puts1 pop af ret -_putc PUSH AF - PUSH BC - PUSH DE - PUSH HL - LD E,A ; OUTPUT CHAR TO E - LD C,CIODEV_CONSOLE; CONSOLE UNIT TO C - LD B,BF_CIOOUT ; HBIOS FUNC: OUTPUT CHAR - RST 08 ; HBIOS OUTPUTS CHARACTDR - POP HL - POP DE - POP BC - POP AF - RET +_putc +#IF (BIOS == BIOS_WBW) + PUSH AF + PUSH BC + PUSH DE + PUSH HL + LD E,A ; OUTPUT CHAR TO E + LD C,CIODEV_CONSOLE ; CONSOLE UNIT TO C + LD B,BF_CIOOUT ; HBIOS FUNC: OUTPUT CHAR + RST 08 ; HBIOS OUTPUTS CHARACTDR + POP HL + POP DE + POP BC + POP AF + RET +#ENDIF +#IF (BIOS == BIOS_UNA) + PUSH AF + PUSH BC + PUSH DE + PUSH HL + LD E,A ; OUTPUT CHAR TO E + LD B,0 ; CONSOLE UNIT TO B + LD C,BF_CIOOUT ; UBIOS FUNC: OUTPUT CHAR + CALL $FFFD ; UBIOS OUTPUTS CHARACTDR + POP HL + POP DE + POP BC + POP AF + RET +#ENDIF -welcome .db "Generating a Mandelbrot set" - .db cr, lf, eos + +welcome .db "Generating a Mandelbrot set..." + .db cr, lf, cr, lf, eos finished .db "Computation finished." crlf .db cr, lf, eos diff --git a/Source/HBIOS/font8043.asm b/Source/HBIOS/font8043.asm deleted file mode 100644 index 406db88c..00000000 --- a/Source/HBIOS/font8043.asm +++ /dev/null @@ -1,260 +0,0 @@ -; Zap font zap-vga-09 (c) John Zaitseff released under GNU General Public Licence. Refer www.gnu.org/licenses/ -; - .DB $7E, $C3, $99, $F3, $E7, $FF, $E7, $7E, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $7F, $E6, $66, $66, $C3, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $0C, $18, $FC, $30, $FC, $60, $C0, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $60, $30, $18, $00, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $18, $30, $60, $00, $F8, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $3C, $3C, $3C, $3C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $38, $7C, $FE, $7C, $38, $10, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C3, $C6, $CC, $D8, $36, $6E, $D6, $BF, $06, $00, $00, $00, $00, $00, $00, $00 - .DB $C3, $C6, $CC, $D8, $36, $6B, $C6, $8C, $0F, $00, $00, $00, $00, $00, $00, $00 - .DB $E1, $33, $66, $34, $EA, $36, $6A, $DF, $82, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $00, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00 - .DB $6C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $00, $0C, $78, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $0E, $1B, $18, $3C, $18, $18, $D8, $70, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $7E, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $7E, $18, $7E, $18, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $CC, $D8, $30, $60, $DB, $9B, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $F1, $5B, $55, $51, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $DB, $DB, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $0C, $18, $30, $18, $0C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $30, $18, $0C, $18, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $CC, $CC, $66, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $66, $66, $CC, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $66, $66, $CC, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $CC, $CC, $66, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $18, $18, $0C, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C6, $7C, $7E, $C0, $CE, $C6, $7E, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C6, $7C, $00, $76, $CC, $7C, $0C, $F8, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $30, $00, $78, $30, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $70, $30, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $C4, $70, $38, $8C, $78, $0C, $78, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $7C, $C0, $78, $0C, $F8, $0C, $78, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $30, $78, $78, $30, $30, $00, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $6C, $6C, $28, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $6C, $6C, $FE, $6C, $FE, $6C, $6C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $7C, $D0, $7C, $16, $7C, $10, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $C6, $CC, $18, $30, $66, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $6C, $38, $76, $DC, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $60, $60, $60, $30, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $18, $18, $18, $30, $60, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $6C, $38, $FE, $38, $6C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $30, $30, $FC, $30, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $18, $18, $30, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $30, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $06, $0C, $18, $30, $60, $C0, $80, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $7C, $C6, $C6, $D6, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $30, $70, $30, $30, $30, $30, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $CC, $0C, $38, $60, $CC, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $CC, $0C, $38, $0C, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $1C, $3C, $6C, $CC, $FE, $0C, $1E, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $FC, $C0, $F8, $0C, $0C, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $60, $C0, $F8, $CC, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $FC, $CC, $0C, $18, $30, $30, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $CC, $CC, $78, $CC, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $CC, $CC, $7C, $0C, $18, $70, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $30, $30, $00, $00, $30, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $30, $30, $00, $00, $30, $30, $60, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $60, $C0, $60, $30, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $FC, $00, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $18, $0C, $18, $30, $60, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $CC, $0C, $18, $30, $00, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $7C, $C6, $DE, $DE, $DC, $C0, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $6C, $C6, $C6, $FE, $C6, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $FC, $66, $66, $7C, $66, $66, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $3C, $66, $C0, $C0, $C0, $66, $3C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $F8, $6C, $66, $66, $66, $6C, $F8, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $FE, $62, $68, $78, $68, $62, $FE, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $FE, $62, $68, $78, $68, $60, $F0, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $3C, $66, $C0, $C0, $CE, $66, $3E, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C6, $C6, $C6, $FE, $C6, $C6, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $30, $30, $30, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $1E, $0C, $0C, $0C, $CC, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $E6, $66, $6C, $78, $6C, $66, $E6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $F0, $60, $60, $60, $62, $66, $FE, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C6, $EE, $FE, $FE, $D6, $C6, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C6, $E6, $F6, $DE, $CE, $C6, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $7C, $C6, $C6, $C6, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $FC, $66, $66, $7C, $60, $60, $F0, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $7C, $C6, $C6, $C6, $C6, $CE, $7C, $0E, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $FC, $66, $66, $7C, $6C, $66, $E6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $CC, $E0, $78, $1C, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $FC, $B4, $30, $30, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C6, $C6, $C6, $C6, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C6, $C6, $C6, $C6, $C6, $6C, $38, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C6, $C6, $C6, $D6, $D6, $FE, $6C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C6, $C6, $6C, $38, $6C, $C6, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $CC, $CC, $CC, $78, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $FE, $CC, $98, $30, $62, $C6, $FE, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $60, $60, $60, $60, $60, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C0, $60, $30, $18, $0C, $06, $02, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $18, $18, $18, $18, $18, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $38, $6C, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $00, $00, $FE, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $30, $30, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $78, $0C, $7C, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $E0, $60, $60, $7C, $66, $66, $DC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $78, $CC, $C0, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $1C, $0C, $0C, $7C, $CC, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $78, $CC, $FC, $C0, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $6C, $60, $F0, $60, $60, $F0, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $76, $CC, $CC, $7C, $0C, $F8, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $E0, $60, $6C, $76, $66, $66, $E6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $30, $00, $70, $30, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $0C, $00, $1C, $0C, $0C, $CC, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $E0, $60, $66, $6C, $78, $6C, $E6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $70, $30, $30, $30, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $EC, $FE, $D6, $D6, $D6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $DC, $66, $66, $66, $66, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $78, $CC, $CC, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $DC, $66, $66, $7C, $60, $F0, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $76, $CC, $CC, $7C, $0C, $1E, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $DC, $76, $60, $60, $F0, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $7C, $C0, $78, $0C, $F8, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $30, $FC, $30, $30, $36, $1C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $CC, $CC, $CC, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $C6, $C6, $C6, $6C, $38, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $C6, $D6, $D6, $FE, $6C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $C6, $6C, $38, $6C, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $CC, $CC, $CC, $7C, $0C, $F8, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $FC, $98, $30, $64, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $1C, $30, $30, $E0, $30, $30, $1C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $E0, $30, $30, $1C, $30, $30, $E0, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $76, $DC, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $18, $3C, $3C, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $C0, $60, $1C, $36, $63, $7F, $63, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $03, $06, $38, $6C, $C6, $FE, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $28, $00, $7C, $C6, $FE, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $76, $DC, $00, $7C, $C6, $FE, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $6C, $00, $38, $6C, $C6, $FE, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $6C, $38, $6C, $C6, $FE, $C6, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $3F, $6D, $CC, $FF, $CC, $CD, $CF, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $3C, $66, $C0, $C0, $66, $3C, $06, $3C, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $FE, $62, $78, $62, $FE, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $0C, $18, $FE, $62, $78, $62, $FE, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $28, $FE, $62, $78, $62, $FE, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $6C, $00, $FE, $62, $78, $62, $FE, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $00, $78, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $00, $78, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $20, $50, $00, $78, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $CC, $00, $78, $30, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $F8, $6C, $66, $F6, $66, $6C, $F8, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $76, $DC, $00, $E6, $F6, $DE, $CE, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $00, $7C, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $0C, $18, $00, $7C, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $28, $00, $7C, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $76, $DC, $00, $7C, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $6C, $00, $7C, $C6, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $6C, $38, $6C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $3D, $67, $6E, $7E, $76, $E6, $BC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $00, $C6, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $0C, $18, $00, $C6, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $28, $00, $C6, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $6C, $00, $C6, $C6, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $CC, $CC, $78, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $F0, $60, $7C, $66, $7C, $60, $F0, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $CC, $CC, $D8, $CC, $C6, $CC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $AA, $55, $AA, $55, $AA, $55, $AA, $55, $AA, $00, $00, $00, $00, $00, $00, $00 - .DB $30, $00, $30, $30, $78, $78, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $10, $7C, $D6, $D0, $D6, $7C, $10, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $6C, $64, $F0, $60, $66, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $1E, $31, $FC, $60, $F8, $33, $1E, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $CC, $CC, $78, $FC, $30, $FC, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $28, $10, $7C, $C0, $78, $0C, $F8, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $3E, $61, $3C, $66, $66, $3C, $86, $7C, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $28, $10, $7C, $C0, $78, $0C, $F8, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $3C, $42, $99, $A1, $A1, $99, $42, $3C, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $3C, $6C, $6C, $3E, $00, $7E, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $33, $66, $CC, $66, $33, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $FC, $0C, $0C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $66, $3C, $66, $66, $3C, $66, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $3C, $42, $B9, $A5, $B9, $A5, $42, $3C, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $6C, $6C, $38, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $30, $30, $FC, $30, $30, $00, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $6C, $18, $30, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $78, $0C, $38, $0C, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $50, $20, $FC, $98, $30, $64, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $66, $66, $66, $66, $7B, $C0, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $7F, $DB, $DB, $7B, $1B, $1B, $1B, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $30, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $50, $20, $FC, $98, $30, $64, $FC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $38, $18, $18, $3C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $6C, $6C, $38, $00, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $CC, $66, $33, $66, $CC, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $7F, $CD, $CC, $CF, $CC, $CD, $7F, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $7E, $DB, $DE, $D8, $7E, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $CC, $00, $CC, $CC, $78, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $30, $00, $30, $60, $C0, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $FF, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $18, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $1F, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $F8, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $1F, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $F8, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $1F, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $F8, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $FF, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $FF, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $FF, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00 - .DB $88, $22, $88, $22, $88, $22, $88, $22, $88, $00, $00, $00, $00, $00, $00, $00 - .DB $FF, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $FF, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $00, $FF, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $00, $00, $00, $00, $00, $FF, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $FF, $00, $FF, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $36, $36, $36, $36, $36, $36, $36, $36, $36, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $3F, $30, $37, $36, $36, $36, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $FE, $06, $F6, $36, $36, $36, $00, $00, $00, $00, $00, $00, $00 - .DB $36, $36, $36, $37, $30, $3F, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $36, $36, $36, $F6, $06, $FE, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $36, $36, $36, $37, $30, $37, $36, $36, $36, $00, $00, $00, $00, $00, $00, $00 - .DB $36, $36, $36, $F6, $06, $F6, $36, $36, $36, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $00, $FF, $00, $F7, $36, $36, $36, $00, $00, $00, $00, $00, $00, $00 - .DB $36, $36, $36, $F7, $00, $FF, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $36, $36, $36, $F7, $00, $F7, $36, $36, $36, $00, $00, $00, $00, $00, $00, $00 - .DB $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $3C, $7E, $18, $18, $18, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $18, $18, $18, $7E, $3C, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $18, $30, $7F, $30, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $18, $0C, $FE, $0C, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $78, $0C, $7C, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $78, $0C, $7C, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $28, $78, $0C, $7C, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $76, $DC, $78, $0C, $7C, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $6C, $00, $78, $0C, $7C, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $38, $6C, $38, $0C, $7C, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $00, $7E, $1B, $7E, $D8, $6E, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $78, $CC, $C0, $CC, $78, $0C, $78, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $78, $CC, $FC, $C0, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $78, $CC, $FC, $C0, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $28, $78, $CC, $FC, $C0, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $6C, $00, $78, $CC, $FC, $C0, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $00, $70, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $00, $70, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $20, $50, $00, $70, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $D8, $00, $70, $30, $30, $30, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $34, $18, $2C, $7C, $CC, $CC, $78, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $76, $DC, $00, $DC, $66, $66, $66, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $00, $7C, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $0C, $18, $00, $7C, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $10, $28, $00, $7C, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $76, $DC, $00, $7C, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $6C, $00, $7C, $C6, $C6, $7C, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $30, $30, $00, $FC, $00, $30, $30, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $00, $3D, $66, $6E, $76, $66, $BC, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $60, $30, $00, $CC, $CC, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $00, $CC, $CC, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $20, $50, $00, $CC, $CC, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $CC, $00, $CC, $CC, $CC, $CC, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $18, $30, $CC, $CC, $CC, $7C, $0C, $F8, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $E0, $60, $7C, $66, $66, $7C, $60, $F0, $00, $00, $00, $00, $00, $00, $00, $00 - .DB $CC, $00, $CC, $CC, $CC, $7C, $0C, $F8, $00, $00, $00, $00, $00, $00, $00, $00 - - diff --git a/Source/HBIOS/font_hi.asm b/Source/HBIOS/font_hi.asm deleted file mode 100644 index 4fbc2e7a..00000000 --- a/Source/HBIOS/font_hi.asm +++ /dev/null @@ -1,256 +0,0 @@ - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$7E,$81,$A5,$81,$81,$BD,$99,$81,$81,$7E,$00,$00,$00,$00 - .DB $00,$00,$7C,$FE,$FE,$D6,$FE,$FE,$BA,$C6,$FE,$7C,$00,$00,$00,$00 - .DB $00,$00,$00,$6C,$EE,$FE,$FE,$FE,$FE,$7C,$38,$10,$00,$00,$00,$00 - .DB $00,$00,$00,$10,$38,$7C,$FE,$7C,$38,$10,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$10,$38,$38,$10,$6C,$EE,$6C,$10,$38,$00,$00,$00,$00 - .DB $00,$00,$10,$38,$7C,$7C,$FE,$FE,$FE,$6C,$10,$38,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$18,$3C,$3C,$3C,$18,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$E7,$C3,$C3,$C3,$E7,$FF,$FF,$FF,$FF,$FF,$FF - .DB $00,$00,$00,$00,$18,$3C,$66,$66,$66,$3C,$18,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$E7,$C3,$99,$99,$99,$C3,$E7,$FF,$FF,$FF,$FF,$FF - .DB $00,$00,$1E,$0E,$1E,$36,$78,$CC,$CC,$CC,$CC,$78,$00,$00,$00,$00 - .DB $00,$00,$3C,$66,$66,$66,$3C,$18,$7E,$18,$18,$18,$00,$00,$00,$00 - .DB $00,$00,$1E,$1A,$1E,$18,$18,$18,$18,$78,$F8,$70,$00,$00,$00,$00 - .DB $00,$00,$3E,$36,$3E,$36,$36,$76,$F6,$66,$0E,$1E,$0C,$00,$00,$00 - .DB $00,$00,$18,$DB,$7E,$3C,$66,$66,$3C,$7E,$DB,$18,$00,$00,$00,$00 - .DB $00,$00,$00,$80,$E0,$F0,$FC,$FE,$FC,$F0,$E0,$80,$00,$00,$00,$00 - .DB $00,$00,$00,$02,$0E,$3E,$7E,$FE,$7E,$3E,$0E,$02,$00,$00,$00,$00 - .DB $00,$00,$18,$3C,$7E,$18,$18,$18,$18,$7E,$3C,$18,$00,$00,$00,$00 - .DB $00,$00,$66,$66,$66,$66,$66,$66,$66,$00,$66,$66,$00,$00,$00,$00 - .DB $00,$00,$7F,$DB,$DB,$DB,$DB,$7B,$1B,$1B,$1B,$1B,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$60,$7C,$F6,$DE,$7C,$0C,$C6,$C6,$7C,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$FE,$FE,$FE,$FE,$00,$00,$00,$00 - .DB $00,$00,$18,$3C,$7E,$18,$18,$18,$7E,$3C,$18,$7E,$00,$00,$00,$00 - .DB $00,$00,$18,$3C,$7E,$18,$18,$18,$18,$18,$18,$18,$00,$00,$00,$00 - .DB $00,$00,$18,$18,$18,$18,$18,$18,$18,$7E,$3C,$18,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$0C,$0E,$FF,$0E,$0C,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$30,$70,$FE,$70,$30,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$C0,$C0,$C0,$FE,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$24,$66,$FF,$66,$24,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$10,$38,$38,$38,$7C,$7C,$FE,$FE,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$FE,$FE,$7C,$7C,$7C,$38,$38,$10,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$18,$3C,$3C,$3C,$3C,$18,$18,$00,$18,$18,$00,$00,$00,$00 - .DB $00,$36,$36,$36,$36,$14,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$6C,$6C,$6C,$FE,$6C,$6C,$FE,$6C,$6C,$6C,$00,$00,$00,$00 - .DB $00,$00,$18,$18,$7C,$C6,$C0,$78,$3C,$06,$C6,$7C,$18,$18,$00,$00 - .DB $00,$00,$00,$00,$00,$62,$66,$0C,$18,$30,$66,$C6,$00,$00,$00,$00 - .DB $00,$00,$38,$6C,$38,$30,$76,$7E,$CC,$CC,$CC,$76,$00,$00,$00,$00 - .DB $00,$0C,$0C,$0C,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$0C,$18,$30,$30,$30,$30,$30,$30,$18,$0C,$00,$00,$00,$00 - .DB $00,$00,$30,$18,$0C,$0C,$0C,$0C,$0C,$0C,$18,$30,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$6C,$38,$FE,$38,$6C,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$18,$18,$7E,$18,$18,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$0C,$0C,$0C,$18,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$FE,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$18,$18,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$02,$06,$0C,$18,$30,$60,$C0,$80,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$CE,$DE,$F6,$E6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$18,$78,$18,$18,$18,$18,$18,$18,$18,$7E,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$06,$0C,$18,$30,$60,$C6,$FE,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$06,$06,$3C,$06,$06,$06,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$0C,$1C,$3C,$6C,$CC,$CC,$FE,$0C,$0C,$1E,$00,$00,$00,$00 - .DB $00,$00,$FE,$C0,$C0,$C0,$FC,$06,$06,$06,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C0,$C0,$FC,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$FE,$C6,$06,$0C,$18,$30,$30,$30,$30,$30,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$C6,$7C,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$C6,$C6,$7E,$06,$06,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$0C,$0C,$00,$00,$0C,$0C,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$0C,$0C,$00,$00,$0C,$0C,$0C,$18,$00,$00,$00 - .DB $00,$00,$00,$0C,$18,$30,$60,$C0,$60,$30,$18,$0C,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$FE,$00,$FE,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$60,$30,$18,$0C,$06,$0C,$18,$30,$60,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$0C,$18,$18,$18,$00,$18,$18,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$C6,$DE,$DE,$DE,$DC,$C0,$7E,$00,$00,$00,$00 - .DB $00,$00,$38,$6C,$C6,$C6,$C6,$FE,$C6,$C6,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$FC,$66,$66,$66,$7C,$66,$66,$66,$66,$FC,$00,$00,$00,$00 - .DB $00,$00,$3C,$66,$C2,$C0,$C0,$C0,$C0,$C2,$66,$3C,$00,$00,$00,$00 - .DB $00,$00,$F8,$6C,$66,$66,$66,$66,$66,$66,$6C,$F8,$00,$00,$00,$00 - .DB $00,$00,$FE,$66,$60,$64,$7C,$64,$60,$60,$66,$FE,$00,$00,$00,$00 - .DB $00,$00,$FE,$66,$60,$64,$7C,$64,$60,$60,$60,$F0,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$C0,$C0,$C0,$CE,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$C6,$C6,$C6,$C6,$FE,$C6,$C6,$C6,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$3C,$18,$18,$18,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00 - .DB $00,$00,$3C,$18,$18,$18,$18,$18,$18,$D8,$D8,$70,$00,$00,$00,$00 - .DB $00,$00,$C6,$C6,$CC,$D8,$F0,$F0,$D8,$CC,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$F0,$60,$60,$60,$60,$60,$60,$62,$66,$FE,$00,$00,$00,$00 - .DB $00,$00,$C6,$C6,$EE,$EE,$FE,$D6,$D6,$D6,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$C6,$C6,$E6,$E6,$F6,$DE,$CE,$CE,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$FC,$66,$66,$66,$66,$7C,$60,$60,$60,$F0,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$C6,$C6,$C6,$C6,$D6,$D6,$7C,$06,$00,$00,$00 - .DB $00,$00,$FC,$66,$66,$66,$7C,$78,$6C,$66,$66,$E6,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C0,$C0,$70,$1C,$06,$06,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$7E,$5A,$18,$18,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00 - .DB $00,$00,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$6C,$38,$10,$00,$00,$00,$00 - .DB $00,$00,$C6,$C6,$C6,$D6,$D6,$D6,$FE,$EE,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$C6,$C6,$C6,$6C,$38,$38,$6C,$C6,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$66,$66,$66,$66,$66,$3C,$18,$18,$18,$3C,$00,$00,$00,$00 - .DB $00,$00,$FE,$C6,$86,$0C,$18,$30,$60,$C2,$C6,$FE,$00,$00,$00,$00 - .DB $00,$00,$7C,$60,$60,$60,$60,$60,$60,$60,$60,$7C,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$80,$C0,$60,$30,$18,$0C,$06,$02,$00,$00,$00,$00 - .DB $00,$00,$7C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$7C,$00,$00,$00,$00 - .DB $00,$10,$38,$6C,$C6,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$FF,$00,$00 - .DB $00,$18,$18,$18,$0C,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$78,$0C,$7C,$CC,$CC,$DC,$76,$00,$00,$00,$00 - .DB $00,$00,$E0,$60,$60,$7C,$66,$66,$66,$66,$66,$FC,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$7C,$C6,$C0,$C0,$C0,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$1C,$0C,$0C,$7C,$CC,$CC,$CC,$CC,$CC,$7E,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$7C,$C6,$C6,$FE,$C0,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$1C,$36,$30,$30,$FC,$30,$30,$30,$30,$78,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$76,$CE,$C6,$C6,$CE,$76,$06,$C6,$7C,$00,$00 - .DB $00,$00,$E0,$60,$60,$7C,$66,$66,$66,$66,$66,$E6,$00,$00,$00,$00 - .DB $00,$00,$18,$18,$00,$38,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00 - .DB $00,$00,$0C,$0C,$00,$1C,$0C,$0C,$0C,$0C,$0C,$CC,$CC,$78,$00,$00 - .DB $00,$00,$E0,$60,$60,$66,$66,$6C,$78,$6C,$66,$E6,$00,$00,$00,$00 - .DB $00,$00,$18,$18,$18,$18,$18,$18,$18,$18,$18,$1C,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$6C,$FE,$D6,$D6,$C6,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$DC,$66,$66,$66,$66,$66,$66,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$7C,$C6,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$DC,$66,$66,$66,$66,$7C,$60,$60,$F0,$00,$00 - .DB $00,$00,$00,$00,$00,$76,$CC,$CC,$CC,$CC,$7C,$0C,$0C,$1E,$00,$00 - .DB $00,$00,$00,$00,$00,$DC,$66,$60,$60,$60,$60,$F0,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$7C,$C6,$C0,$7C,$06,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$30,$30,$30,$FC,$30,$30,$30,$30,$36,$1C,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$CC,$CC,$CC,$CC,$CC,$CC,$76,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$C6,$C6,$C6,$C6,$6C,$38,$10,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$C6,$C6,$D6,$D6,$D6,$FE,$6C,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$C6,$C6,$6C,$38,$6C,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$C6,$C6,$C6,$C6,$CE,$76,$06,$C6,$7C,$00,$00 - .DB $00,$00,$00,$00,$00,$FE,$86,$0C,$18,$30,$62,$FE,$00,$00,$00,$00 - .DB $00,$00,$0E,$18,$18,$18,$70,$18,$18,$18,$18,$0E,$00,$00,$00,$00 - .DB $00,$00,$18,$18,$18,$18,$00,$18,$18,$18,$18,$18,$00,$00,$00,$00 - .DB $00,$00,$70,$18,$18,$18,$0E,$18,$18,$18,$18,$70,$00,$00,$00,$00 - .DB $00,$00,$76,$DC,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$10,$38,$38,$6C,$6C,$FE,$00,$00,$00,$00,$00 - .DB $00,$00,$3C,$66,$C0,$C0,$C0,$C6,$66,$3C,$18,$0C,$CC,$38,$00,$00 - .DB $00,$00,$C6,$00,$00,$C6,$C6,$C6,$C6,$C6,$CE,$76,$00,$00,$00,$00 - .DB $00,$0C,$18,$30,$00,$7C,$C6,$C6,$FE,$C0,$C6,$7C,$00,$00,$00,$00 - .DB $00,$30,$78,$CC,$00,$78,$0C,$7C,$CC,$CC,$DC,$76,$00,$00,$00,$00 - .DB $00,$00,$CC,$00,$00,$78,$0C,$7C,$CC,$CC,$DC,$76,$00,$00,$00,$00 - .DB $00,$60,$30,$18,$00,$78,$0C,$7C,$CC,$CC,$DC,$76,$00,$00,$00,$00 - .DB $00,$38,$6C,$38,$00,$78,$0C,$7C,$CC,$CC,$DC,$76,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$7C,$C6,$C0,$C0,$C6,$7C,$18,$0C,$6C,$38,$00,$00 - .DB $00,$30,$78,$CC,$00,$7C,$C6,$C6,$FE,$C0,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$CC,$00,$00,$7C,$C6,$C6,$FE,$C0,$C6,$7C,$00,$00,$00,$00 - .DB $00,$30,$18,$0C,$00,$7C,$C6,$C6,$FE,$C0,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$66,$00,$00,$38,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00 - .DB $00,$18,$3C,$66,$00,$38,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00 - .DB $00,$30,$18,$0C,$00,$38,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00 - .DB $00,$C6,$00,$38,$6C,$C6,$C6,$C6,$FE,$C6,$C6,$C6,$00,$00,$00,$00 - .DB $38,$6C,$38,$00,$38,$6C,$C6,$C6,$FE,$C6,$C6,$C6,$00,$00,$00,$00 - .DB $0C,$18,$30,$00,$FE,$60,$60,$7C,$60,$60,$60,$FE,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$66,$DB,$1B,$7F,$D8,$D8,$DF,$76,$00,$00,$00,$00 - .DB $00,$00,$7E,$D8,$D8,$D8,$D8,$FE,$D8,$D8,$D8,$DE,$00,$00,$00,$00 - .DB $00,$30,$78,$CC,$00,$7C,$C6,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$C6,$00,$00,$7C,$C6,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$30,$18,$0C,$00,$7C,$C6,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$30,$78,$CC,$00,$C6,$C6,$C6,$C6,$C6,$CE,$76,$00,$00,$00,$00 - .DB $00,$60,$30,$18,$00,$C6,$C6,$C6,$C6,$C6,$CE,$76,$00,$00,$00,$00 - .DB $00,$18,$00,$3C,$18,$18,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00 - .DB $00,$C6,$00,$7C,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$C6,$00,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$18,$18,$7C,$C6,$C0,$C0,$C6,$7C,$18,$18,$00,$00,$00,$00 - .DB $00,$38,$6C,$60,$60,$F0,$60,$60,$60,$66,$F6,$6C,$00,$00,$00,$00 - .DB $00,$66,$66,$66,$66,$3C,$18,$7E,$18,$3C,$18,$18,$00,$00,$00,$00 - .DB $00,$00,$3E,$63,$63,$30,$1C,$06,$63,$63,$3E,$00,$1C,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$3E,$63,$38,$0E,$63,$3E,$00,$1C,$00,$00,$00 - .DB $00,$0C,$18,$30,$00,$78,$0C,$7C,$CC,$CC,$DC,$76,$00,$00,$00,$00 - .DB $00,$0C,$18,$30,$00,$38,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00 - .DB $00,$0C,$18,$30,$00,$7C,$C6,$C6,$C6,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$18,$30,$60,$00,$CC,$CC,$CC,$CC,$CC,$DC,$76,$00,$00,$00,$00 - .DB $00,$00,$76,$DC,$00,$DC,$66,$66,$66,$66,$66,$66,$00,$00,$00,$00 - .DB $00,$76,$DC,$00,$C6,$C6,$E6,$F6,$DE,$CE,$C6,$C6,$00,$00,$00,$00 - .DB $00,$21,$1E,$00,$1E,$33,$60,$60,$67,$63,$33,$1D,$00,$00,$00,$00 - .DB $00,$42,$3C,$00,$3B,$66,$66,$66,$3E,$06,$66,$3C,$00,$00,$00,$00 - .DB $00,$00,$30,$30,$00,$30,$30,$30,$60,$C6,$C6,$7C,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$7E,$60,$60,$60,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$7E,$06,$06,$06,$00,$00,$00,$00,$00 - .DB $00,$60,$60,$62,$66,$6C,$18,$30,$60,$DC,$36,$0C,$18,$3E,$00,$00 - .DB $00,$60,$60,$62,$66,$6C,$18,$36,$6E,$DE,$36,$7E,$06,$06,$00,$00 - .DB $00,$00,$18,$18,$00,$18,$18,$3C,$3C,$3C,$3C,$18,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$36,$6C,$D8,$6C,$36,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$D8,$6C,$36,$6C,$D8,$00,$00,$00,$00,$00,$00 - .DB $11,$44,$11,$44,$11,$44,$11,$44,$11,$44,$11,$44,$11,$44,$11,$44 - .DB $AA,$55,$AA,$55,$AA,$55,$AA,$55,$AA,$55,$AA,$55,$AA,$55,$AA,$55 - .DB $DD,$77,$DD,$77,$DD,$77,$DD,$77,$DD,$77,$DD,$77,$DD,$77,$DD,$77 - .DB $18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $18,$18,$18,$18,$18,$18,$18,$F8,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $18,$18,$18,$18,$18,$F8,$18,$F8,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $36,$36,$36,$36,$36,$36,$36,$F6,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $00,$00,$00,$00,$00,$00,$00,$FE,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $00,$00,$00,$00,$00,$F8,$18,$F8,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $36,$36,$36,$36,$36,$F6,$06,$F6,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $00,$00,$00,$00,$00,$FE,$06,$F6,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $36,$36,$36,$36,$36,$F6,$06,$FE,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $36,$36,$36,$36,$36,$36,$36,$FE,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$18,$18,$F8,$18,$F8,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$F8,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $18,$18,$18,$18,$18,$18,$18,$1F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$18,$18,$18,$18,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$FF,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $18,$18,$18,$18,$18,$18,$18,$1F,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $00,$00,$00,$00,$00,$00,$00,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$18,$18,$18,$18,$FF,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $18,$18,$18,$18,$18,$1F,$18,$1F,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $36,$36,$36,$36,$36,$36,$36,$37,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $36,$36,$36,$36,$36,$37,$30,$3F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$3F,$30,$37,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $36,$36,$36,$36,$36,$F7,$00,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$FF,$00,$F7,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $36,$36,$36,$36,$36,$37,$30,$37,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $00,$00,$00,$00,$00,$FF,$00,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $36,$36,$36,$36,$36,$F7,$00,$F7,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $18,$18,$18,$18,$18,$FF,$00,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $36,$36,$36,$36,$36,$36,$36,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$FF,$00,$FF,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $00,$00,$00,$00,$00,$00,$00,$FF,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $36,$36,$36,$36,$36,$36,$36,$3F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$18,$18,$1F,$18,$1F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$1F,$18,$1F,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $00,$00,$00,$00,$00,$00,$00,$3F,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $36,$36,$36,$36,$36,$36,$36,$FF,$36,$36,$36,$36,$36,$36,$36,$36 - .DB $18,$18,$18,$18,$18,$FF,$18,$FF,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $18,$18,$18,$18,$18,$18,$18,$F8,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$1F,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF - .DB $00,$00,$00,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF - .DB $F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0 - .DB $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F - .DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$76,$DC,$D8,$D8,$D8,$D8,$DC,$76,$00,$00,$00,$00 - .DB $00,$00,$78,$CC,$CC,$D8,$FC,$C6,$C6,$C6,$C6,$CC,$00,$00,$00,$00 - .DB $00,$00,$FE,$66,$62,$60,$60,$60,$60,$60,$60,$60,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$FE,$6C,$6C,$6C,$6C,$6C,$6C,$00,$00,$00,$00 - .DB $00,$00,$FE,$C6,$62,$30,$18,$18,$30,$62,$C6,$FE,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$7E,$D8,$CC,$CC,$CC,$D8,$70,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$66,$66,$66,$66,$66,$7C,$60,$C0,$80,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$76,$DC,$18,$18,$18,$18,$18,$00,$00,$00,$00 - .DB $00,$00,$FE,$38,$38,$6C,$C6,$C6,$6C,$38,$38,$FE,$00,$00,$00,$00 - .DB $00,$00,$00,$38,$6C,$C6,$C6,$FE,$C6,$C6,$6C,$38,$00,$00,$00,$00 - .DB $00,$00,$38,$6C,$C6,$C6,$C6,$C6,$6C,$6C,$6C,$EE,$00,$00,$00,$00 - .DB $00,$00,$3E,$60,$60,$3C,$66,$C6,$C6,$C6,$CC,$78,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$7E,$DB,$DB,$DB,$7E,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$02,$06,$7C,$CE,$DE,$F6,$F6,$7C,$60,$C0,$00,$00,$00,$00 - .DB $00,$00,$00,$1C,$30,$60,$60,$7C,$60,$60,$30,$1C,$00,$00,$00,$00 - .DB $00,$00,$7C,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$C6,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$FE,$00,$00,$FE,$00,$00,$FE,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$18,$18,$7E,$18,$18,$00,$00,$7E,$00,$00,$00,$00 - .DB $00,$00,$30,$18,$0C,$06,$0C,$18,$30,$00,$00,$7E,$00,$00,$00,$00 - .DB $00,$00,$0C,$18,$30,$60,$30,$18,$0C,$00,$00,$7E,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$0C,$1E,$1A,$18,$18,$18,$18,$18,$18,$18,$18,$18 - .DB $18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$58,$78,$30,$00,$00,$00 - .DB $00,$00,$00,$00,$18,$18,$00,$7E,$00,$18,$18,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$76,$DC,$00,$76,$DC,$00,$00,$00,$00,$00 - .DB $00,$00,$78,$CC,$CC,$78,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$18,$18,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$18,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$1F,$18,$18,$18,$18,$18,$D8,$D8,$78,$38,$18,$00,$00,$00 - .DB $00,$00,$D8,$6C,$6C,$6C,$6C,$6C,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$70,$D8,$18,$30,$60,$F8,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$7E,$7E,$7E,$7E,$7E,$7E,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 diff --git a/Source/HBIOS/font_lo.asm b/Source/HBIOS/font_lo.asm deleted file mode 100644 index cf865267..00000000 --- a/Source/HBIOS/font_lo.asm +++ /dev/null @@ -1,515 +0,0 @@ -FONT_LO: - .DB $18,$18,$18,$FF,$FF,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C0,$C0,$30,$30,$C0,$C0,$30,$30,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$18,$18,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $33,$33,$CC,$CC,$33,$33,$CC,$CC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $33,$99,$CC,$66,$33,$99,$CC,$66,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $CC,$CC,$33,$33,$CC,$CC,$33,$33,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $03,$03,$03,$03,$03,$03,$03,$03,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$CC,$CC,$33,$33,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $CC,$99,$33,$66,$CC,$99,$33,$66,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $03,$03,$03,$03,$03,$03,$03,$03,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$1F,$1F,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$0F,$0F,$0F,$0F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$1F,$1F,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$F8,$F8,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$1F,$1F,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$FF,$FF,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$F8,$F8,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E0,$E0,$E0,$E0,$E0,$E0,$E0,$E0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $07,$07,$07,$07,$07,$07,$07,$07,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $01,$03,$06,$6C,$78,$70,$60,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$18,$00,$00,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$FF,$66,$FF,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$3E,$60,$3C,$06,$7C,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $62,$66,$0C,$18,$30,$66,$46,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$3C,$38,$67,$66,$3F,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $06,$0C,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0C,$18,$30,$30,$30,$18,$0C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $30,$18,$0C,$0C,$0C,$18,$30,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$66,$3C,$FF,$3C,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$18,$18,$7E,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$18,$18,$30,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$03,$06,$0C,$18,$30,$60,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$6E,$76,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$38,$18,$18,$18,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$06,$0C,$30,$60,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$06,$1C,$06,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $06,$0E,$1E,$66,$7F,$06,$06,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$60,$7C,$06,$06,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$60,$7C,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$66,$0C,$18,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$66,$3C,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$66,$3E,$06,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$18,$00,$00,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$18,$00,$00,$18,$18,$30,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0E,$18,$30,$60,$30,$18,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$7E,$00,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $70,$18,$0C,$06,$0C,$18,$70,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$06,$0C,$18,$00,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$6E,$6E,$60,$62,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$3C,$66,$7E,$66,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7C,$66,$66,$7C,$66,$66,$7C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$60,$60,$60,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $78,$6C,$66,$66,$66,$6C,$78,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$60,$60,$78,$60,$60,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$60,$60,$78,$60,$60,$60,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$60,$6E,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$7E,$66,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $1E,$0C,$0C,$0C,$0C,$6C,$38,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$6C,$78,$70,$78,$6C,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $60,$60,$60,$60,$60,$60,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $63,$77,$7F,$6B,$63,$63,$63,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$76,$7E,$7E,$6E,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$66,$66,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7C,$66,$66,$7C,$60,$60,$60,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$66,$66,$66,$3C,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7C,$66,$66,$7C,$78,$6C,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$60,$3C,$06,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$18,$18,$18,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$66,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$66,$66,$3C,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $63,$63,$63,$6B,$7F,$77,$63,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$3C,$18,$3C,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$3C,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$06,$0C,$18,$30,$60,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$30,$30,$30,$30,$30,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $40,$60,$30,$18,$0C,$06,$02,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$0C,$0C,$0C,$0C,$0C,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$18,$24,$42,$42,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$30,$18,$0C,$04,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$3C,$06,$3E,$66,$3E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$60,$60,$7C,$66,$66,$7C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$3C,$60,$60,$60,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$06,$06,$3E,$66,$66,$3E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$3C,$66,$7E,$60,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$0E,$18,$3E,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$3E,$66,$66,$3E,$06,$7C,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$60,$60,$7C,$66,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$18,$00,$38,$18,$18,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$06,$00,$06,$06,$06,$06,$3C,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$60,$60,$6C,$78,$6C,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$38,$18,$18,$18,$18,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$66,$7F,$7F,$6B,$63,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$7C,$66,$66,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$3C,$66,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$7C,$66,$66,$7C,$60,$60,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$3E,$66,$66,$3E,$06,$06,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$7C,$66,$60,$60,$60,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$3E,$60,$3C,$06,$7C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$18,$7E,$18,$18,$18,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$66,$66,$66,$66,$3E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$66,$66,$66,$3C,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$63,$6B,$7F,$3E,$36,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$66,$3C,$18,$3C,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$66,$66,$66,$3E,$0C,$78,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$7E,$0C,$18,$30,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$0C,$10,$10,$20,$10,$10,$0C,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$18,$18,$18,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$18,$04,$04,$02,$04,$04,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$32,$4C,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$81,$81,$81,$81,$81,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F0,$F0,$F0,$F0,$0F,$0F,$0F,$0F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$91,$91,$9F,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$C3,$F9,$C1,$99,$C1,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$9F,$9F,$83,$99,$99,$83,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$C3,$9F,$9F,$9F,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$F9,$F9,$C1,$99,$99,$C1,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$C3,$99,$81,$9F,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$F1,$E7,$C1,$E7,$E7,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$C1,$99,$99,$C1,$F9,$83,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$9F,$9F,$83,$99,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$E7,$FF,$C7,$E7,$E7,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$F9,$FF,$F9,$F9,$F9,$F9,$C3,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$9F,$9F,$93,$87,$93,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$C7,$E7,$E7,$E7,$E7,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$99,$80,$80,$94,$9C,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$83,$99,$99,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$C3,$99,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$83,$99,$99,$83,$9F,$9F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$C1,$99,$99,$C1,$F9,$F9,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$83,$99,$9F,$9F,$9F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$C1,$9F,$C3,$F9,$83,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$E7,$81,$E7,$E7,$E7,$F1,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$99,$99,$99,$99,$C1,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$99,$99,$99,$C3,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$9C,$94,$80,$C1,$C9,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$99,$C3,$E7,$C3,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$99,$99,$99,$C1,$F3,$87,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$81,$F3,$E7,$CF,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$CF,$CF,$CF,$CF,$CF,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F3,$ED,$CF,$83,$CF,$9D,$03,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$F3,$F3,$F3,$F3,$F3,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$E7,$C3,$81,$E7,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$EF,$CF,$80,$80,$CF,$EF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$E7,$FF,$FF,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$00,$99,$00,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$C1,$9F,$C3,$F9,$83,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $9D,$99,$F3,$E7,$CF,$99,$B9,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$C3,$C7,$98,$99,$C0,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F9,$F3,$E7,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F3,$E7,$CF,$CF,$CF,$E7,$F3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $CF,$E7,$F3,$F3,$F3,$E7,$CF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$99,$C3,$00,$C3,$99,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$E7,$E7,$81,$E7,$E7,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$E7,$E7,$CF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$81,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$E7,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FC,$F9,$F3,$E7,$CF,$9F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$91,$89,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$C7,$E7,$E7,$E7,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$F9,$F3,$CF,$9F,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$F9,$E3,$F9,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F9,$F1,$E1,$99,$80,$F9,$F9,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$9F,$83,$F9,$F9,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$9F,$83,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$99,$F3,$E7,$E7,$E7,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$99,$C3,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$99,$C1,$F9,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$E7,$FF,$FF,$E7,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$E7,$FF,$FF,$E7,$E7,$CF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F1,$E7,$CF,$9F,$CF,$E7,$F1,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$81,$FF,$81,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $8F,$E7,$F3,$F9,$F3,$E7,$8F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$F9,$F3,$E7,$FF,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$00,$00,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$C3,$99,$81,$99,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $83,$99,$99,$83,$99,$99,$83,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$9F,$9F,$9F,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $87,$93,$99,$99,$99,$93,$87,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$9F,$9F,$87,$9F,$9F,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$9F,$9F,$87,$9F,$9F,$9F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$9F,$91,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$81,$99,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$E7,$E7,$E7,$E7,$E7,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E1,$F3,$F3,$F3,$F3,$93,$C7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$93,$87,$8F,$87,$93,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $9F,$9F,$9F,$9F,$9F,$9F,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $9C,$88,$80,$94,$9C,$9C,$9C,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$89,$81,$81,$91,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$99,$99,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $83,$99,$99,$83,$9F,$9F,$9F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$99,$99,$99,$C3,$F1,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $83,$99,$99,$83,$87,$93,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$9F,$C3,$F9,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$E7,$E7,$E7,$E7,$E7,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$99,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$99,$99,$C3,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $9C,$9C,$9C,$94,$80,$88,$9C,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$C3,$E7,$C3,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$C3,$E7,$E7,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$F9,$F3,$E7,$CF,$9F,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$00,$00,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3F,$3F,$CF,$CF,$3F,$3F,$CF,$CF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$E7,$E7,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $CC,$CC,$33,$33,$CC,$CC,$33,$33,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $CC,$66,$33,$99,$CC,$66,$33,$99,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $33,$33,$CC,$CC,$33,$33,$CC,$CC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FC,$FC,$FC,$FC,$FC,$FC,$FC,$FC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$33,$33,$CC,$CC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $33,$66,$CC,$99,$33,$66,$CC,$99,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FC,$FC,$FC,$FC,$FC,$FC,$FC,$FC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$E0,$E0,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$F0,$F0,$F0,$F0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$E0,$E0,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$07,$07,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$E0,$E0,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$00,$00,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$00,$00,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$07,$07,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $1F,$1F,$1F,$1F,$1F,$1F,$1F,$1F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F8,$F8,$F8,$F8,$F8,$F8,$F8,$F8,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FE,$FC,$F9,$93,$87,$8F,$9F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$0F,$0F,$0F,$0F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F0,$F0,$F0,$F0,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$07,$07,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0F,$0F,$0F,$0F,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 -#IF 0 - .DB $0F,$0F,$0F,$0F,$F0,$F0,$F0,$F0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$6E,$6E,$60,$62,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$3C,$66,$7E,$66,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7C,$66,$66,$7C,$66,$66,$7C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$60,$60,$60,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $78,$6C,$66,$66,$66,$6C,$78,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$60,$60,$78,$60,$60,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$60,$60,$78,$60,$60,$60,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$60,$6E,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$7E,$66,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$18,$18,$18,$18,$18,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $1E,$0C,$0C,$0C,$0C,$6C,$38,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$6C,$78,$70,$78,$6C,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $60,$60,$60,$60,$60,$60,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $63,$77,$7F,$6B,$63,$63,$63,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$76,$7E,$7E,$6E,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$66,$66,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7C,$66,$66,$7C,$60,$60,$60,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$66,$66,$66,$3C,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7C,$66,$66,$7C,$78,$6C,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$60,$3C,$06,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$18,$18,$18,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$66,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$66,$66,$3C,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $63,$63,$63,$6B,$7F,$77,$63,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$3C,$18,$3C,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$3C,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$06,$0C,$18,$30,$60,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$30,$30,$30,$30,$30,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0C,$12,$30,$7C,$30,$62,$FC,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$0C,$0C,$0C,$0C,$0C,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$18,$3C,$7E,$18,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$10,$30,$7F,$7F,$30,$10,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$18,$00,$00,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $66,$66,$FF,$66,$FF,$66,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$3E,$60,$3C,$06,$7C,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $62,$66,$0C,$18,$30,$66,$46,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$3C,$38,$67,$66,$3F,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $06,$0C,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0C,$18,$30,$30,$30,$18,$0C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $30,$18,$0C,$0C,$0C,$18,$30,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$66,$3C,$FF,$3C,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$18,$18,$7E,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$18,$18,$30,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$03,$06,$0C,$18,$30,$60,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$6E,$76,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$38,$18,$18,$18,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$06,$0C,$30,$60,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$06,$1C,$06,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $06,$0E,$1E,$66,$7F,$06,$06,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$60,$7C,$06,$06,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$60,$7C,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $7E,$66,$0C,$18,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$66,$3C,$66,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$66,$3E,$06,$66,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$18,$00,$00,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$18,$00,$00,$18,$18,$30,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0E,$18,$30,$60,$30,$18,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$7E,$00,$7E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $70,$18,$0C,$06,$0C,$18,$70,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$66,$06,$0C,$18,$00,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $08,$1C,$3E,$7F,$7F,$1C,$3E,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$18,$18,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $30,$30,$30,$30,$30,$30,$30,$30,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$E0,$F0,$38,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$1C,$0F,$07,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$38,$F0,$E0,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C0,$C0,$C0,$C0,$C0,$C0,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C0,$E0,$70,$38,$1C,$0E,$07,$03,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $03,$07,$0E,$1C,$38,$70,$E0,$C0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$C0,$C0,$C0,$C0,$C0,$C0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$03,$03,$03,$03,$03,$03,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$3C,$7E,$7E,$7E,$7E,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $36,$7F,$7F,$7F,$3E,$1C,$08,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $60,$60,$60,$60,$60,$60,$60,$60,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$07,$0F,$1C,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$E7,$7E,$3C,$3C,$7E,$E7,$C3,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$3C,$7E,$66,$66,$7E,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$66,$66,$18,$18,$3C,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $06,$06,$06,$06,$06,$06,$06,$06,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $08,$1C,$3E,$7F,$3E,$1C,$08,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$FF,$FF,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C0,$C0,$30,$30,$C0,$C0,$30,$30,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$18,$18,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$03,$3E,$76,$36,$36,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$7F,$3F,$1F,$0F,$07,$03,$01,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$00,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $CC,$CC,$33,$33,$CC,$CC,$33,$33,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $03,$03,$03,$03,$03,$03,$03,$03,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$CC,$CC,$33,$33,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FE,$FC,$F8,$F0,$E0,$C0,$80,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $03,$03,$03,$03,$03,$03,$03,$03,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$1F,$1F,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$0F,$0F,$0F,$0F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$1F,$1F,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$F8,$F8,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$00,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$1F,$1F,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$FF,$FF,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$F8,$F8,$18,$18,$18,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E0,$E0,$E0,$E0,$E0,$E0,$E0,$E0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $07,$07,$07,$07,$07,$07,$07,$07,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$00,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $03,$03,$03,$03,$03,$03,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$00,$F0,$F0,$F0,$F0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0F,$0F,$0F,$0F,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $18,$18,$18,$F8,$F8,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F0,$F0,$F0,$F0,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F0,$F0,$F0,$F0,$0F,$0F,$0F,$0F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$91,$91,$9F,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$C3,$99,$81,$99,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $83,$99,$99,$83,$99,$99,$83,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$9F,$9F,$9F,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $87,$93,$99,$99,$99,$93,$87,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$9F,$9F,$87,$9F,$9F,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$9F,$9F,$87,$9F,$9F,$9F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$9F,$91,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$81,$99,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$E7,$E7,$E7,$E7,$E7,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E1,$F3,$F3,$F3,$F3,$93,$C7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$93,$87,$8F,$87,$93,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $9F,$9F,$9F,$9F,$9F,$9F,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $9C,$88,$80,$94,$9C,$9C,$9C,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$89,$81,$81,$91,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$99,$99,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $83,$99,$99,$83,$9F,$9F,$9F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$99,$99,$99,$C3,$F1,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $83,$99,$99,$83,$87,$93,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$9F,$C3,$F9,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$E7,$E7,$E7,$E7,$E7,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$99,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$99,$99,$C3,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $9C,$9C,$9C,$94,$80,$88,$9C,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$C3,$E7,$C3,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$C3,$E7,$E7,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$F9,$F3,$E7,$CF,$9F,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$CF,$CF,$CF,$CF,$CF,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F3,$ED,$CF,$83,$CF,$9D,$03,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$F3,$F3,$F3,$F3,$F3,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$E7,$C3,$81,$E7,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$EF,$CF,$80,$80,$CF,$EF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$E7,$FF,$FF,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$99,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $99,$99,$00,$99,$00,$99,$99,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$C1,$9F,$C3,$F9,$83,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $9D,$99,$F3,$E7,$CF,$99,$B9,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$C3,$C7,$98,$99,$C0,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F9,$F3,$E7,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F3,$E7,$CF,$CF,$CF,$E7,$F3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $CF,$E7,$F3,$F3,$F3,$E7,$CF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$99,$C3,$00,$C3,$99,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$E7,$E7,$81,$E7,$E7,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$E7,$E7,$CF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$81,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$E7,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FC,$F9,$F3,$E7,$CF,$9F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$91,$89,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$C7,$E7,$E7,$E7,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$F9,$F3,$CF,$9F,$81,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$F9,$E3,$F9,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F9,$F1,$E1,$99,$80,$F9,$F9,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$9F,$83,$F9,$F9,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$9F,$83,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $81,$99,$F3,$E7,$E7,$E7,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$99,$C3,$99,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$99,$C1,$F9,$99,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$E7,$FF,$FF,$E7,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$E7,$FF,$FF,$E7,$E7,$CF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F1,$E7,$CF,$9F,$CF,$E7,$F1,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$81,$FF,$81,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $8F,$E7,$F3,$F9,$F3,$E7,$8F,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C3,$99,$F9,$F3,$E7,$FF,$E7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$00,$00,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F7,$E3,$C1,$80,$80,$E3,$C1,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$E7,$E7,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$00,$00,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$00,$00,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$00,$00,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$00,$00,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $CF,$CF,$CF,$CF,$CF,$CF,$CF,$CF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F3,$F3,$F3,$F3,$F3,$F3,$F3,$F3,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$1F,$0F,$C7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E3,$F0,$F8,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$C7,$0F,$1F,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3F,$3F,$3F,$3F,$3F,$3F,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3F,$1F,$8F,$C7,$E3,$F1,$F8,$FC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FC,$F8,$F1,$E3,$C7,$8F,$1F,$3F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$3F,$3F,$3F,$3F,$3F,$3F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$FC,$FC,$FC,$FC,$FC,$FC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$C3,$81,$81,$81,$81,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$00,$00,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $C9,$80,$80,$80,$C1,$E3,$F7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $9F,$9F,$9F,$9F,$9F,$9F,$9F,$9F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$F8,$F0,$E3,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3C,$18,$81,$C3,$C3,$81,$18,$3C,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$C3,$81,$99,$99,$81,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$99,$99,$E7,$E7,$C3,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F9,$F9,$F9,$F9,$F9,$F9,$F9,$F9,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F7,$E3,$C1,$80,$C1,$E3,$F7,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$00,$00,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3F,$3F,$CF,$CF,$3F,$3F,$CF,$CF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$E7,$E7,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FC,$C1,$89,$C9,$C9,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$80,$C0,$E0,$F0,$F8,$FC,$FE,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $33,$33,$CC,$CC,$33,$33,$CC,$CC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FC,$FC,$FC,$FC,$FC,$FC,$FC,$FC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$33,$33,$CC,$CC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$01,$03,$07,$0F,$1F,$3F,$7F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FC,$FC,$FC,$FC,$FC,$FC,$FC,$FC,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$E0,$E0,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$F0,$F0,$F0,$F0,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$E0,$E0,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$07,$07,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$E0,$E0,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$00,$00,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$00,$00,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$07,$07,$E7,$E7,$E7,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $1F,$1F,$1F,$1F,$1F,$1F,$1F,$1F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F8,$F8,$F8,$F8,$F8,$F8,$F8,$F8,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $00,$00,$00,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FC,$FC,$FC,$FC,$FC,$FC,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $FF,$FF,$FF,$FF,$0F,$0F,$0F,$0F,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $F0,$F0,$F0,$F0,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $E7,$E7,$E7,$07,$07,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 - .DB $0F,$0F,$0F,$0F,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 -#ENDIF \ No newline at end of file diff --git a/Source/HBIOS/font_tms.asm b/Source/HBIOS/font_tms.asm deleted file mode 100644 index c5f3da74..00000000 --- a/Source/HBIOS/font_tms.asm +++ /dev/null @@ -1,260 +0,0 @@ -; tms_font.inc - automatically generated by n8vidtst -FONT_TMS: - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x00(0) - .DB 030h,030h,030h,03ch,03ch,000h,000h,000h ; 0x01(1) - .DB 030h,030h,030h,0f0h,0f0h,000h,000h,000h ; 0x02(2) - .DB 000h,000h,000h,03ch,03ch,030h,030h,030h ; 0x03(3) - .DB 000h,000h,000h,0f0h,0f0h,030h,030h,030h ; 0x04(4) - .DB 030h,030h,030h,030h,030h,030h,030h,030h ; 0x05(5) - .DB 000h,000h,000h,0ffh,0ffh,000h,000h,000h ; 0x06(6) - .DB 000h,000h,020h,070h,0f8h,070h,020h,000h ; 0x07(7) - .DB 000h,0f8h,0d8h,088h,000h,088h,0d8h,0f8h ; 0x08(8) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x09(9) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x0a(10) - .DB 000h,000h,060h,090h,060h,020h,018h,018h ; 0x0b(11) - .DB 000h,000h,020h,070h,020h,020h,050h,020h ; 0x0c(12) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x0d(13) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x0e(14) - .DB 000h,0a8h,050h,050h,0d8h,050h,050h,0a8h ; 0x0f(15) - .DB 030h,030h,030h,0fch,0fch,030h,030h,030h ; 0x10(16) - .DB 000h,010h,030h,070h,0f0h,070h,030h,010h ; 0x11(17) - .DB 000h,020h,070h,020h,020h,020h,070h,020h ; 0x12(18) - .DB 000h,000h,050h,000h,050h,050h,050h,050h ; 0x13(19) - .DB 000h,000h,028h,028h,068h,0a8h,0a8h,078h ; 0x14(20) - .DB 000h,000h,000h,0fch,0fch,030h,030h,030h ; 0x15(21) - .DB 030h,030h,030h,0fch,0fch,000h,000h,000h ; 0x16(22) - .DB 030h,030h,030h,0f0h,0f0h,030h,030h,030h ; 0x17(23) - .DB 020h,020h,020h,020h,020h,020h,070h,020h ; 0x18(24) - .DB 030h,030h,030h,03ch,03ch,030h,030h,030h ; 0x19(25) - .DB 000h,000h,010h,018h,0fch,018h,010h,000h ; 0x1a(26) - .DB 000h,000h,020h,060h,0fch,060h,020h,000h ; 0x1b(27) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x1c(28) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x1d(29) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x1e(30) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x1f(31) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x20(32) - .DB 000h,000h,030h,000h,030h,078h,078h,030h ; 0x21(33) - .DB 000h,000h,000h,000h,000h,050h,0d8h,0d8h ; 0x22(34) - .DB 048h,048h,0fch,048h,048h,0fch,048h,048h ; 0x23(35) - .DB 000h,020h,0f8h,024h,078h,0a0h,078h,020h ; 0x24(36) - .DB 000h,000h,098h,058h,020h,0d0h,0c8h,000h ; 0x25(37) - .DB 000h,078h,090h,060h,050h,048h,048h,030h ; 0x26(38) - .DB 000h,000h,000h,000h,000h,040h,030h,030h ; 0x27(39) - .DB 000h,010h,020h,020h,020h,020h,020h,010h ; 0x28(40) - .DB 000h,020h,010h,010h,010h,010h,010h,020h ; 0x29(41) - .DB 000h,020h,0a8h,070h,0f8h,070h,0a8h,020h ; 0x2a(42) - .DB 000h,000h,020h,020h,0f8h,020h,020h,000h ; 0x2b(43) - .DB 040h,030h,030h,000h,000h,000h,000h,000h ; 0x2c(44) - .DB 000h,000h,000h,000h,070h,000h,000h,000h ; 0x2d(45) - .DB 000h,030h,030h,000h,000h,000h,000h,000h ; 0x2e(46) - .DB 000h,000h,080h,040h,020h,010h,008h,004h ; 0x2f(47) - .DB 000h,070h,0c8h,0a8h,0a8h,0a8h,098h,070h ; 0x30(48) - .DB 000h,0f8h,020h,020h,020h,020h,060h,020h ; 0x31(49) - .DB 000h,0f8h,080h,060h,010h,008h,088h,070h ; 0x32(50) - .DB 000h,070h,088h,008h,070h,008h,088h,070h ; 0x33(51) - .DB 000h,010h,010h,010h,0f8h,090h,050h,030h ; 0x34(52) - .DB 000h,070h,088h,008h,0f0h,080h,080h,0f8h ; 0x35(53) - .DB 000h,070h,088h,088h,0f0h,080h,040h,038h ; 0x36(54) - .DB 000h,020h,020h,020h,020h,010h,008h,0f8h ; 0x37(55) - .DB 000h,070h,088h,088h,070h,088h,088h,070h ; 0x38(56) - .DB 000h,070h,088h,008h,078h,088h,088h,070h ; 0x39(57) - .DB 000h,000h,030h,030h,000h,030h,030h,000h ; 0x3a(58) - .DB 000h,060h,030h,030h,000h,030h,030h,000h ; 0x3b(59) - .DB 000h,010h,020h,040h,080h,040h,020h,010h ; 0x3c(60) - .DB 000h,000h,000h,0fch,000h,0fch,000h,000h ; 0x3d(61) - .DB 000h,040h,020h,010h,008h,010h,020h,040h ; 0x3e(62) - .DB 000h,020h,000h,020h,030h,008h,088h,070h ; 0x3f(63) - .DB 000h,070h,080h,080h,0b0h,0b8h,088h,070h ; 0x40(64) - .DB 000h,088h,088h,088h,0f8h,088h,050h,020h ; 0x41(65) - .DB 000h,0f0h,048h,048h,070h,048h,048h,0f0h ; 0x42(66) - .DB 000h,070h,088h,080h,080h,080h,088h,070h ; 0x43(67) - .DB 000h,0f0h,048h,048h,048h,048h,048h,0f0h ; 0x44(68) - .DB 000h,0f8h,088h,080h,0e0h,080h,088h,0f8h ; 0x45(69) - .DB 000h,080h,080h,080h,0f0h,080h,088h,0f8h ; 0x46(70) - .DB 000h,070h,088h,088h,0b8h,080h,088h,070h ; 0x47(71) - .DB 000h,088h,088h,088h,0f8h,088h,088h,088h ; 0x48(72) - .DB 000h,0f8h,020h,020h,020h,020h,020h,0f8h ; 0x49(73) - .DB 000h,070h,088h,008h,008h,008h,008h,01ch ; 0x4a(74) - .DB 000h,088h,090h,0a0h,0c0h,0a0h,090h,088h ; 0x4b(75) - .DB 000h,0f8h,088h,080h,080h,080h,080h,080h ; 0x4c(76) - .DB 000h,088h,088h,088h,088h,0a8h,0d8h,088h ; 0x4d(77) - .DB 000h,088h,098h,0a8h,0a8h,0a8h,0c8h,088h ; 0x4e(78) - .DB 000h,070h,088h,088h,088h,088h,088h,070h ; 0x4f(79) - .DB 000h,080h,080h,080h,0f0h,088h,088h,0f0h ; 0x50(80) - .DB 004h,078h,098h,0a8h,088h,088h,088h,070h ; 0x51(81) - .DB 000h,088h,090h,0a0h,0f0h,088h,088h,0f0h ; 0x52(82) - .DB 000h,070h,088h,010h,020h,040h,088h,070h ; 0x53(83) - .DB 000h,020h,020h,020h,020h,020h,020h,0f8h ; 0x54(84) - .DB 000h,070h,088h,088h,088h,088h,088h,088h ; 0x55(85) - .DB 000h,020h,050h,050h,050h,088h,088h,088h ; 0x56(86) - .DB 000h,050h,0a8h,0a8h,0a8h,088h,088h,088h ; 0x57(87) - .DB 000h,088h,088h,050h,020h,050h,088h,088h ; 0x58(88) - .DB 000h,020h,020h,020h,020h,050h,088h,088h ; 0x59(89) - .DB 000h,0f8h,088h,040h,020h,010h,088h,0f8h ; 0x5a(90) - .DB 000h,078h,040h,040h,040h,040h,040h,078h ; 0x5b(91) - .DB 000h,000h,008h,010h,020h,040h,080h,000h ; 0x5c(92) - .DB 000h,078h,008h,008h,008h,008h,008h,078h ; 0x5d(93) - .DB 000h,000h,000h,000h,000h,088h,050h,020h ; 0x5e(94) - .DB 000h,0fch,000h,000h,000h,000h,000h,000h ; 0x5f(95) - .DB 000h,000h,000h,000h,000h,010h,060h,060h ; 0x60(96) - .DB 000h,070h,088h,078h,008h,0f0h,000h,000h ; 0x61(97) - .DB 000h,0f0h,088h,088h,0f0h,080h,080h,080h ; 0x62(98) - .DB 000h,070h,080h,080h,070h,000h,000h,000h ; 0x63(99) - .DB 000h,078h,088h,088h,078h,008h,008h,008h ; 0x64(100) - .DB 000h,070h,080h,0f8h,088h,070h,000h,000h ; 0x65(101) - .DB 000h,040h,040h,040h,0e0h,048h,030h,000h ; 0x66(102) - .DB 070h,008h,038h,048h,048h,038h,000h,000h ; 0x67(103) - .DB 000h,088h,088h,0c8h,0b0h,080h,080h,000h ; 0x68(104) - .DB 000h,070h,020h,060h,000h,020h,000h,000h ; 0x69(105) - .DB 030h,048h,008h,008h,018h,000h,008h,000h ; 0x6a(106) - .DB 000h,090h,0a0h,0c0h,0a0h,090h,080h,080h ; 0x6b(107) - .DB 000h,020h,020h,020h,020h,020h,020h,020h ; 0x6c(108) - .DB 000h,088h,0a8h,0a8h,050h,000h,000h,000h ; 0x6d(109) - .DB 000h,088h,088h,0c8h,0b0h,000h,000h,000h ; 0x6e(110) - .DB 000h,070h,088h,088h,070h,000h,000h,000h ; 0x6f(111) - .DB 040h,040h,070h,048h,048h,070h,000h,000h ; 0x70(112) - .DB 008h,008h,038h,048h,048h,038h,060h,000h ; 0x71(113) - .DB 000h,080h,080h,0c8h,0b0h,000h,000h,000h ; 0x72(114) - .DB 000h,060h,010h,060h,080h,060h,000h,000h ; 0x73(115) - .DB 000h,020h,040h,040h,0e0h,040h,000h,000h ; 0x74(116) - .DB 000h,068h,090h,090h,090h,000h,000h,000h ; 0x75(117) - .DB 000h,020h,050h,088h,088h,000h,000h,000h ; 0x76(118) - .DB 000h,050h,0a8h,0a8h,088h,000h,000h,000h ; 0x77(119) - .DB 000h,048h,030h,030h,048h,000h,000h,000h ; 0x78(120) - .DB 000h,080h,040h,020h,050h,088h,000h,000h ; 0x79(121) - .DB 000h,0f8h,040h,020h,010h,0f8h,000h,000h ; 0x7a(122) - .DB 000h,010h,020h,020h,040h,020h,020h,010h ; 0x7b(123) - .DB 000h,020h,020h,020h,000h,020h,020h,020h ; 0x7c(124) - .DB 000h,040h,020h,020h,010h,020h,020h,040h ; 0x7d(125) - .DB 000h,000h,000h,000h,000h,000h,090h,06ch ; 0x7e(126) - .DB 000h,070h,050h,050h,050h,050h,050h,070h ; 0x7f(127) - .DB 0a8h,0a8h,0a8h,0ach,0a0h,0bch,080h,0fch ; 0x80(128) - .DB 000h,000h,000h,0fch,000h,0fch,000h,0fch ; 0x81(129) - .DB 054h,054h,054h,0d4h,014h,0f4h,004h,0fch ; 0x82(130) - .DB 054h,054h,054h,054h,054h,054h,054h,054h ; 0x83(131) - .DB 0fch,004h,0f4h,014h,0d4h,054h,054h,054h ; 0x84(132) - .DB 0ffh,000h,0ffh,000h,0ffh,000h,000h,000h ; 0x85(133) - .DB 0fch,080h,0bch,0a0h,0ach,0a8h,0a8h,0a8h ; 0x86(134) - .DB 0a8h,0a8h,0a8h,0a8h,0a8h,0a8h,0a8h,0a8h ; 0x87(135) - .DB 0a8h,0a8h,0a8h,0ach,0a0h,0ach,0a8h,0a8h ; 0x88(136) - .DB 054h,054h,054h,0d4h,014h,0d4h,054h,054h ; 0x89(137) - .DB 000h,000h,000h,0fch,000h,0fch,000h,000h ; 0x8a(138) - .DB 080h,080h,080h,080h,080h,080h,080h,0fch ; 0x8b(139) - .DB 000h,000h,000h,000h,000h,000h,000h,0fch ; 0x8c(140) - .DB 004h,004h,004h,004h,004h,004h,004h,0fch ; 0x8d(141) - .DB 004h,004h,004h,004h,004h,004h,004h,004h ; 0x8e(142) - .DB 0fch,004h,004h,004h,004h,004h,004h,004h ; 0x8f(143) - .DB 0fch,000h,000h,000h,000h,000h,000h,000h ; 0x90(144) - .DB 0fch,080h,080h,080h,080h,080h,080h,080h ; 0x91(145) - .DB 080h,080h,080h,080h,080h,080h,080h,080h ; 0x92(146) - .DB 080h,080h,080h,080h,0fch,080h,080h,080h ; 0x93(147) - .DB 004h,004h,004h,004h,0fch,004h,004h,004h ; 0x94(148) - .DB 000h,000h,000h,000h,0fch,000h,000h,000h ; 0x95(149) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x96(150) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x97(151) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x98(152) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x99(153) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x9a(154) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x9b(155) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x9c(156) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x9d(157) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x9e(158) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0x9f(159) - .DB 000h,070h,088h,078h,008h,0f0h,040h,020h ; 0xa0(160) - .DB 000h,0f8h,020h,020h,060h,000h,020h,010h ; 0xa1(161) - .DB 000h,070h,088h,088h,070h,000h,020h,010h ; 0xa2(162) - .DB 000h,074h,088h,088h,088h,000h,020h,010h ; 0xa3(163) - .DB 000h,088h,088h,0c8h,0b0h,000h,020h,010h ; 0xa4(164) - .DB 000h,088h,098h,0a8h,0c8h,000h,020h,010h ; 0xa5(165) - .DB 000h,000h,0fch,000h,07ch,088h,088h,070h ; 0xa6(166) - .DB 000h,000h,0fch,000h,070h,088h,088h,070h ; 0xa7(167) - .DB 000h,070h,088h,040h,020h,020h,000h,020h ; 0xa8(168) - .DB 080h,080h,080h,0fch,000h,000h,000h,000h ; 0xa9(169) - .DB 004h,004h,004h,0fch,000h,000h,000h,000h ; 0xaa(170) - .DB 000h,000h,01ch,088h,054h,0a8h,090h,088h ; 0xab(171) - .DB 000h,000h,008h,0b8h,058h,0a8h,090h,088h ; 0xac(172) - .DB 000h,000h,030h,078h,078h,030h,000h,030h ; 0xad(173) - .DB 000h,014h,028h,050h,0a0h,050h,028h,014h ; 0xae(174) - .DB 000h,0a0h,050h,028h,014h,028h,050h,0a0h ; 0xaf(175) - .DB 0aah,054h,0aah,054h,0aah,054h,0aah,054h ; 0xb0(176) - .DB 054h,0aah,054h,0aah,054h,0aah,054h,0aah ; 0xb1(177) - .DB 06ch,0b6h,0dah,06ch,0b6h,0dah,06ch,0b6h ; 0xb2(178) - .DB 018h,018h,018h,018h,018h,018h,018h,018h ; 0xb3(179) - .DB 018h,018h,018h,0f8h,018h,018h,018h,018h ; 0xb4(180) - .DB 018h,018h,0f8h,018h,0f8h,018h,018h,018h ; 0xb5(181) - .DB 06ch,06ch,06ch,0ech,06ch,06ch,06ch,06ch ; 0xb6(182) - .DB 06ch,06ch,06ch,0fch,000h,000h,000h,000h ; 0xb7(183) - .DB 06ch,06ch,0ech,06ch,0fch,000h,000h,000h ; 0xb8(184) - .DB 06ch,06ch,06ch,0ech,00ch,0ech,06ch,06ch ; 0xb9(185) - .DB 06ch,06ch,06ch,06ch,06ch,06ch,06ch,06ch ; 0xba(186) - .DB 06ch,06ch,06ch,0eeh,00eh,0fch,000h,000h ; 0xbb(187) - .DB 000h,000h,000h,0fch,00ch,0ech,06ch,06ch ; 0xbc(188) - .DB 000h,000h,000h,000h,000h,0fch,064h,064h ; 0xbd(189) - .DB 000h,000h,0f0h,030h,030h,0f0h,030h,030h ; 0xbe(190) - .DB 030h,030h,030h,0f0h,000h,000h,000h,000h ; 0xbf(191) - .DB 000h,000h,000h,03ch,030h,030h,030h,030h ; 0xc0(192) - .DB 000h,000h,000h,0fch,030h,030h,030h,030h ; 0xc1(193) - .DB 030h,030h,030h,0fch,000h,000h,000h,000h ; 0xc2(194) - .DB 030h,030h,030h,03ch,030h,030h,030h,030h ; 0xc3(195) - .DB 000h,000h,000h,0fch,000h,000h,000h,000h ; 0xc4(196) - .DB 030h,030h,030h,0fch,030h,030h,030h,030h ; 0xc5(197) - .DB 030h,030h,030h,03ch,030h,03ch,030h,030h ; 0xc6(198) - .DB 0d8h,0d8h,0d8h,0d8h,0d8h,0dch,0d8h,0d8h ; 0xc7(199) - .DB 000h,000h,0fch,0c0h,0dch,0d8h,0d8h,0d8h ; 0xc8(200) - .DB 0d8h,0d8h,0dch,0c0h,0fch,000h,000h,000h ; 0xc9(201) - .DB 000h,0fch,000h,0c0h,0dch,0d8h,0d8h,0d8h ; 0xca(202) - .DB 000h,0fch,000h,000h,000h,000h,000h,000h ; 0xcb(203) - .DB 0d8h,0d8h,0dch,0c0h,0dch,0d8h,0d8h,0d8h ; 0xcc(204) - .DB 000h,000h,0fch,000h,0fch,000h,000h,000h ; 0xcd(205) - .DB 0d8h,0d8h,0dch,000h,0dch,0d8h,0d8h,0d8h ; 0xce(206) - .DB 000h,0fch,000h,000h,0fch,030h,030h,030h ; 0xcf(207) - .DB 000h,000h,000h,000h,0fch,0d8h,0d8h,0d8h ; 0xd0(208) - .DB 030h,030h,0fch,000h,000h,0fch,000h,000h ; 0xd1(209) - .DB 0d8h,0d8h,0fch,000h,000h,000h,000h,000h ; 0xd2(210) - .DB 000h,000h,000h,000h,0fch,0d8h,0d8h,0d8h ; 0xd3(211) - .DB 000h,03ch,030h,030h,03ch,030h,030h,030h ; 0xd4(212) - .DB 030h,030h,03eh,030h,03eh,000h,000h,000h ; 0xd5(213) - .DB 0d8h,0d8h,0fch,000h,000h,000h,000h,000h ; 0xd6(214) - .DB 0d8h,0d8h,0d8h,0dch,0d8h,0d8h,0d8h,0d8h ; 0xd7(215) - .DB 030h,030h,0fch,000h,000h,0fch,030h,030h ; 0xd8(216) - .DB 000h,000h,000h,000h,0f0h,030h,030h,030h ; 0xd9(217) - .DB 030h,030h,030h,030h,03ch,000h,000h,000h ; 0xda(218) - .DB 0fch,0fch,0fch,0fch,0fch,0fch,0fch,0fch ; 0xdb(219) - .DB 0c0h,0c0h,0fch,0fch,0fch,000h,000h,000h ; 0xdc(220) - .DB 0c0h,0c0h,0c0h,0c0h,0c0h,0c0h,0c0h,0c0h ; 0xdd(221) - .DB 00ch,00ch,00ch,00ch,00ch,00ch,00ch,00ch ; 0xde(222) - .DB 000h,0fch,0fch,0fch,00ch,00ch,00ch,00ch ; 0xdf(223) - .DB 000h,068h,090h,090h,068h,000h,000h,000h ; 0xe0(224) - .DB 040h,0f0h,088h,088h,0b0h,088h,088h,070h ; 0xe1(225) - .DB 000h,080h,080h,080h,080h,080h,090h,0f0h ; 0xe2(226) - .DB 000h,000h,048h,050h,050h,050h,050h,0f8h ; 0xe3(227) - .DB 000h,0f8h,088h,040h,020h,040h,088h,0f8h ; 0xe4(228) - .DB 000h,070h,088h,088h,07ch,000h,000h,000h ; 0xe5(229) - .DB 080h,070h,088h,088h,088h,000h,000h,000h ; 0xe6(230) - .DB 000h,01ch,010h,010h,098h,074h,000h,000h ; 0xe7(231) - .DB 000h,0fch,010h,038h,044h,038h,010h,0fch ; 0xe8(232) - .DB 000h,038h,0cch,0cch,0fch,0cch,0cch,030h ; 0xe9(233) - .DB 000h,084h,048h,048h,084h,084h,084h,078h ; 0xea(234) - .DB 000h,038h,044h,044h,044h,038h,040h,03ch ; 0xeb(235) - .DB 000h,000h,06ch,092h,092h,06ch,000h,000h ; 0xec(236) - .DB 000h,070h,068h,058h,038h,000h,000h,000h ; 0xed(237) - .DB 000h,038h,040h,080h,0f8h,080h,040h,038h ; 0xee(238) - .DB 000h,0cch,0cch,0cch,0cch,030h,000h,000h ; 0xef(239) - .DB 000h,0fch,000h,000h,0fch,000h,000h,0fch ; 0xf0(240) - .DB 000h,0feh,000h,010h,010h,07ch,010h,010h ; 0xf1(241) - .DB 000h,0feh,000h,020h,010h,008h,010h,020h ; 0xf2(242) - .DB 000h,0feh,000h,010h,020h,040h,020h,010h ; 0xf3(243) - .DB 030h,030h,030h,030h,030h,030h,034h,018h ; 0xf4(244) - .DB 070h,0b0h,030h,030h,030h,030h,030h,030h ; 0xf5(245) - .DB 000h,030h,030h,000h,0fch,000h,030h,030h ; 0xf6(246) - .DB 000h,000h,098h,064h,000h,098h,064h,000h ; 0xf7(247) - .DB 000h,000h,000h,000h,038h,044h,044h,038h ; 0xf8(248) - .DB 000h,000h,000h,000h,000h,038h,038h,000h ; 0xf9(249) - .DB 000h,000h,000h,000h,000h,038h,000h,000h ; 0xfa(250) - .DB 000h,008h,018h,028h,048h,008h,008h,00eh ; 0xfb(251) - .DB 000h,048h,048h,048h,0b0h,000h,000h,000h ; 0xfc(252) - .DB 000h,0f8h,080h,040h,03ch,0f0h,000h,000h ; 0xfd(253) - .DB 0fch,0fch,0fch,0fch,0fch,0fch,0fch,0fch ; 0xfe(254) - .DB 000h,000h,000h,000h,000h,000h,000h,000h ; 0xff(255) -; eof - tms_font.inc - diff --git a/Source/HBIOS/game.asm b/Source/HBIOS/game.asm new file mode 100644 index 00000000..8bbce38b --- /dev/null +++ b/Source/HBIOS/game.asm @@ -0,0 +1,1234 @@ +; 2048 +; +; Join the numbers and get to the 2048 tile. +; +; Commands: +; +; Use the arrow keys to move the tiles. +; When two tiles with the same number touch, they merge into one. +; +; w, s, a, d - Alternate keys (up, down, left, right) +; CTRL-E, CTRL-X, CTRL-S, CTRL-D - Wordstar-compatible control keys +; +; Compile: +; +; TASM -80 -b 2048.ASM 2048.COM +; +; Credits: +; +; Based on 2048 created by Gabriele Cirulli. +; Based on the console version for GNU/Linux by Maurits van der Schee +; Ported to Z80 and CP/M by Marco Maccaferri +; Ported to ROMWBW ROM by Phil Summers difficultylevelhigh@gmail from https://github.com/maccasoft/z80-apps/2048.asm + +CTRL_A .EQU 1 +CTRL_B .EQU 2 +CTRL_C .EQU 3 +CTRL_D .EQU 4 +CTRL_E .EQU 5 +CTRL_F .EQU 6 +CTRL_G .EQU 7 +CTRL_H .EQU 8 +CTRL_I .EQU 9 +CTRL_J .EQU 10 +CTRL_K .EQU 11 +CTRL_L .EQU 12 +CTRL_M .EQU 13 +CTRL_N .EQU 14 +CTRL_O .EQU 15 +CTRL_P .EQU 16 +CTRL_Q .EQU 17 +CTRL_R .EQU 18 +CTRL_S .EQU 19 +CTRL_T .EQU 20 +CTRL_U .EQU 21 +CTRL_V .EQU 22 +CTRL_W .EQU 23 +CTRL_X .EQU 24 +CTRL_Y .EQU 25 +CTRL_Z .EQU 26 + +BEL .EQU 07H +FF .EQU 0CH +CR .EQU 0DH +LF .EQU 0AH +EOS .EQU 24H + +;BDOS .EQU 0005H + +BOARD_X .EQU 25H +BOARD_Y .EQU 06H + +#include "std.asm" + + .ORG GAM_LOC + +S2048: LD A,R + LD (RAND16+1),A + LD A,R + LD (RAND16+2),A + + LD B,VAREND-BOARD + LD HL,BOARD + XOR A +CLRBOARD: LD (HL),A + INC HL + DJNZ CLRBOARD + + LD DE,INIT +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + + CALL ADDRANDOM + CALL ADDRANDOM + + CALL DRAWBORDER + CALL DRAWBOARD + +LOOP2 LD A,BOARD_X-3 + LD (XPOS),A + LD A,BOARD_Y + ADD A,13H + DAA + LD (YPOS),A + CALL GOTOXY + LD DE,MSG1 +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + +LOOP: +; LD C,06H +; LD E,0FFH +; CALL BDOS +; CP 0 +; JP Z,LOOP + CALL CIN + + CP 'a' + JP C,K3 + CP '{' + JP NC,K3 + AND 5FH + +K3 CP CTRL_C + JP Z,EXIT + + CP 1BH + JP Z,K1 + + CP CTRL_E + JP Z,TOP + CP CTRL_X + JP Z,BOTTOM + CP CTRL_D + JP Z,RIGHT + CP CTRL_S + JP Z,LEFT + + CP 'W' + JP Z,TOP + CP 'S' + JP Z,BOTTOM + CP 'D' + JP Z,RIGHT + CP 'A' + JP Z,LEFT + + CP '8' + JP Z,TOP + CP '2' + JP Z,BOTTOM + CP '6' + JP Z,RIGHT + CP '4' + JP Z,LEFT + + CP 'Q' + JP Z,QUIT + +; LD C,02H +; LD E,BEL +; CALL BDOS +; CALL COUTE + + JP LOOP + +K1: +; LD C,06H +; LD E,0FFH +; CALL BDOS +; CP 0 +; JP Z,K1 + CALL CIN + + CP '[' + JP NZ,LOOP + +K4: +; LD C,06H +; LD E,0FFH +; CALL BDOS +; CP 0 +; JP Z,K4 + CALL CIN + +K2 CP 'A' + JP Z,TOP + CP 'B' + JP Z,BOTTOM + CP 'C' + JP Z,RIGHT + CP 'D' + JP Z,LEFT + +; LD C,02H +; LD E,BEL +; CALL BDOS +; CALL COUTE + + JP LOOP + +LOOP1 LD A,(MOVED) + CP 0 + JP Z,LOOP + + CALL DRAWBOARD + + CALL ADDRANDOM + CALL DRAWBOARD + + CALL ISOVER + CP 1 + JP Z,GAMEOVER + + JP LOOP + +TOP: + CALL ROTATE + CALL ROTATE + CALL ROTATE + CALL MOVEMERGE + CALL ROTATE + JP LOOP1 + +BOTTOM: + CALL ROTATE + CALL MOVEMERGE + CALL ROTATE + CALL ROTATE + CALL ROTATE + JP LOOP1 + +RIGHT: + CALL ROTATE + CALL ROTATE + CALL MOVEMERGE + CALL ROTATE + CALL ROTATE + JP LOOP1 + +LEFT: + CALL MOVEMERGE + JP LOOP1 + +QUIT: + LD A,BOARD_X-3 + LD (XPOS),A + LD A,BOARD_Y + ADD A,13H + DAA + LD (YPOS),A + CALL GOTOXY + LD DE,MSG2 +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + +Q1: +; LD C,06H +; LD E,0FFH +; CALL BDOS +; CP 0 +; JP Z,Q1 + CALL CIN + + CP 'y' + JP Z,EXIT + CP 'Y' + JP Z,EXIT + CP 'n' + JP Z,LOOP2 + CP 'N' + JP Z,LOOP2 + CP 'R' + JP S2048 + CP 'r' + JP S2048 + +; LD C,02H +; LD E,BEL +; CALL BDOS +; CALL COUTE + JP Q1 + +GAMEOVER: + LD A,BOARD_X-3 + LD (XPOS),A + LD A,BOARD_Y+13H + DAA + LD (YPOS),A + CALL GOTOXY + LD DE,MSG3 +; LD C,09H +; CALL BDOS + CALL PRTSTRDE +GAMEMORE: CALL CIN + CP 'y' + JP Z,S2048 + CP 'Y' + JP Z,S2048 + CP 'n' + JP Z,EXIT + CP 'N' + JP Z,EXIT + JR GAMEMORE +EXIT: + LD DE,TERM +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + + LD A,00H + LD HL,0000H + JP 0FFF9H +; RET + +ISOVER: + LD A,1 + LD (RESULT),A + + LD B,16 + LD HL,BOARD +G1: LD A,(HL) + CP 13 + JP Z,GR + INC HL + DJNZ G1 + + CALL CANMERGE + CP 0 + JP Z,G2 + XOR A + LD (RESULT),A + +G2: CALL ROTATE + CALL CANMERGE + CP 0 + JP Z,G3 + XOR A + LD (RESULT),A + +G3: CALL ROTATE + CALL CANMERGE + CP 0 + JP Z,G4 + XOR A + LD (RESULT),A + +G4: CALL ROTATE + CALL CANMERGE + CP 0 + JP Z,G5 + XOR A + LD (RESULT),A + +G5: CALL ROTATE + +GR: LD A,(RESULT) + RET + +CANMERGE: + LD HL,BOARD + LD (BOARDPTR),HL + CALL TESTMERGE + CP 1 + RET Z + LD HL,BOARD+4 + LD (BOARDPTR),HL + CALL TESTMERGE + CP 1 + RET Z + LD HL,BOARD+8 + LD (BOARDPTR),HL + CALL TESTMERGE + CP 1 + RET Z + LD HL,BOARD+12 + LD (BOARDPTR),HL + CALL TESTMERGE + RET + +TESTMERGE: + LD DE,(BOARDPTR) + LD HL,(BOARDPTR) + INC HL + + LD B,3 +M8 LD A,(DE) + CP 0 + JP Z,M7 + LD C,(HL) + CP C + JP Z,M7 + INC HL + INC DE + DJNZ M8 + XOR A + RET +M7 LD A,1 + RET + +MOVEMERGE: + XOR A + LD (MOVED),A + + LD HL,BOARD + LD (BOARDPTR),HL + CALL MERGE + LD HL,BOARD+4 + LD (BOARDPTR),HL + CALL MERGE + LD HL,BOARD+8 + LD (BOARDPTR),HL + CALL MERGE + LD HL,BOARD+12 + LD (BOARDPTR),HL + CALL MERGE + + RET + +ADDRANDOM: + LD DE,0 + LD B,16 + LD HL,BOARD +N2 LD A,(HL) + CP 0 + JP NZ,N1 + INC E +N1 INC HL + DJNZ N2 + + LD A,E + CP 0 + RET Z + + PUSH DE + CALL RAND16 + POP DE + CALL DIV16 + + LD B,L +N4: LD C,16 + LD HL,BOARD +N5: LD A,(HL) + CP 0 + JP NZ,N3 + DEC B + JP Z,N6 +N3: INC HL + DEC C + JP NZ,N5 + JP N4 + +N6 PUSH HL + + CALL RAND16 + LD DE,10 + CALL DIV16 + LD DE,9 + CALL DIV16 + LD A,E + INC A + + POP HL + LD (HL),A + + RET + +MERGE: + CALL COLLAPSE + + ; merge tiles with same value + + LD DE,(BOARDPTR) + LD HL,(BOARDPTR) + INC HL + + LD B,3 +M6: LD A,(DE) + CP 0 + RET Z + LD C,(HL) + CP C + JP NZ,M5 + + INC A + LD (DE),A + CALL ADDPOINTS + XOR A + LD (HL),A + + LD A,(MOVED) + INC A + LD (MOVED),A + + PUSH DE + PUSH HL + CALL COLLAPSE + POP HL + POP DE + +M5: INC HL + INC DE + DJNZ M6 + + RET + +COLLAPSE: + ; collapse all tiles + + LD HL,(BOARDPTR) + + LD B,4 ; find first zero +M1: LD A,(HL) + CP 0 + JP Z,M2 + INC HL + DJNZ M1 + RET + +M2: LD E,L ; DE=fist zero position + LD D,H + +M4: LD A,(HL) ; move all non-zero tiles + CP 0 + JP Z,M3 + + LD (DE),A + INC DE + XOR A + LD (HL),A + + LD A,(MOVED) + INC A + LD (MOVED),A + +M3: INC HL + DJNZ M4 + + RET + +ROTATE: + ; outer ring + + LD B,3 + +R1: LD A,(BOARD+3) + LD C,A + + LD A,(BOARD+2) + LD (BOARD+3),A + LD A,(BOARD+1) + LD (BOARD+2),A + LD A,(BOARD+0) + LD (BOARD+1),A + + LD A,(BOARD+4) + LD (BOARD+0),A + LD A,(BOARD+8) + LD (BOARD+4),A + LD A,(BOARD+12) + LD (BOARD+8),A + + LD A,(BOARD+13) + LD (BOARD+12),A + LD A,(BOARD+14) + LD (BOARD+13),A + LD A,(BOARD+15) + LD (BOARD+14),A + + LD A,(BOARD+11) + LD (BOARD+15),A + LD A,(BOARD+7) + LD (BOARD+11),A + LD A,C + LD (BOARD+7),A + + DJNZ R1 + + ; inner ring + + LD A,(BOARD+6) + LD C,A + + LD A,(BOARD+5) + LD (BOARD+6),A + LD A,(BOARD+9) + LD (BOARD+5),A + LD A,(BOARD+10) + LD (BOARD+9),A + + LD A,C + LD (BOARD+10),A + + RET + +ADDPOINTS: + PUSH DE + PUSH HL + + LD E,A + LD D,0 + LD HL,POINTS + ADD HL,DE + ADD HL,DE + LD E,(HL) + INC HL + LD D,(HL) + + LD A,(SCORE+3) + ADD A,E + DAA + LD (SCORE+3),A + + LD A,(SCORE+2) + ADC A,D + DAA + LD (SCORE+2),A + + LD A,(SCORE+1) + ADC A,00H + DAA + LD (SCORE+1),A + + LD A,(SCORE) + ADC A,00H + DAA + LD (SCORE),A + + POP HL + POP DE + RET + +PRINTSCORE: + LD HL,SCORE + LD DE,SCORESTR+4 + LD B,4 + +P1 LD A,(HL) + RRA + RRA + RRA + RRA + AND 0FH + ADD A,30H + LD (DE),A + INC DE + + LD A,(HL) + AND 0FH + ADD A,30H + LD (DE),A + INC DE + + INC HL + DJNZ P1 + + LD HL,SCORESTR+4 + LD B,7 +P3 LD A,(HL) + CP 30H + JP NZ,P2 + LD A,20H + LD (HL),A + INC HL + DJNZ P3 + +P2 LD A,BOARD_X + ADD A,16H + DAA + LD (XPOS),A + LD A,BOARD_Y + SUB 02H + DAA + LD (YPOS),A + CALL GOTOXY + + LD DE,SCORESTR +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + RET + +DRAWBORDER: + LD A,BOARD_X + LD (XPOS),A + LD A,BOARD_Y + SUB 02H + DAA + LD (YPOS),A + CALL GOTOXY + + LD DE,MSG4 +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + + LD A,BOARD_X + SUB 01H + DAA + LD (XPOS),A + LD A,BOARD_Y + SUB 01H + DAA + LD (YPOS),A + CALL GOTOXY + + LD DE,TOPBORDER +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + + LD B,12 +L4 PUSH BC + LD A,(YPOS) + ADD A,1 + DAA + LD (YPOS),A + CALL GOTOXY +; LD C,02H + LD E,0DBH +; CALL BDOS + CALL COUTE + LD DE,ADVANCE +; LD C,09H +; CALL BDOS + CALL PRTSTRDE +; LD C,02H + LD E,0DBH +; CALL BDOS + CALL COUTE + POP BC + DEC B + JP NZ,L4 + + LD A,(YPOS) + ADD A,1 + DAA + LD (YPOS),A + CALL GOTOXY + LD DE,BTMBORDER +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + + RET + +DRAWBOARD: + CALL PRINTSCORE + + LD A,BOARD_X + LD (XPOS),A + LD A,BOARD_Y + SUB 01H + DAA + LD (YPOS),A + + LD HL,BOARD + LD B,4 +L1 PUSH BC + + PUSH HL + LD A,(YPOS) + ADD A,1 + DAA + LD (YPOS),A + CALL GOTOXY + POP HL + + PUSH HL + LD E,(HL) + CALL PRINTSPACE + POP HL + INC HL + PUSH HL + LD E,(HL) + CALL PRINTSPACE + POP HL + INC HL + PUSH HL + LD E,(HL) + CALL PRINTSPACE + POP HL + INC HL + PUSH HL + LD E,(HL) + CALL PRINTSPACE + CALL NEWLINE + POP HL + + DEC HL + DEC HL + DEC HL + + PUSH HL + LD A,(YPOS) + ADD A,1 + DAA + LD (YPOS),A + CALL GOTOXY + POP HL + + PUSH HL + LD E,(HL) + CALL PRINTLABEL + POP HL + INC HL + PUSH HL + LD E,(HL) + CALL PRINTLABEL + POP HL + INC HL + PUSH HL + LD E,(HL) + CALL PRINTLABEL + POP HL + INC HL + PUSH HL + LD E,(HL) + CALL PRINTLABEL + CALL NEWLINE + POP HL + + DEC HL + DEC HL + DEC HL + + PUSH HL + LD A,(YPOS) + ADD A,1 + DAA + LD (YPOS),A + CALL GOTOXY + POP HL + + PUSH HL + LD E,(HL) + CALL PRINTSPACE + POP HL + INC HL + PUSH HL + LD E,(HL) + CALL PRINTSPACE + POP HL + INC HL + PUSH HL + LD E,(HL) + CALL PRINTSPACE + POP HL + INC HL + PUSH HL + LD E,(HL) + CALL PRINTSPACE + CALL NEWLINE + POP HL + + INC HL + + POP BC + DEC B + JP NZ,L1 + + RET + +PRINTSPACE: + LD D,0 + + PUSH DE + LD HL,COLORPTR + ADD HL,DE + ADD HL,DE + LD E,(HL) + INC HL + LD D,(HL) +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + POP DE + + LD DE,SPACER +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + + RET + +PRINTLABEL: + LD D,0 + + PUSH DE + LD HL,COLORPTR + ADD HL,DE + ADD HL,DE + LD E,(HL) + INC HL + LD D,(HL) +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + POP DE + + PUSH DE + LD HL,LABELPTR + ADD HL,DE + ADD HL,DE + LD E,(HL) + INC HL + LD D,(HL) +; LD C,09H +; CALL BDOS + CALL PRTSTRDE + POP DE + + RET + +GOTOXY: +; LD C,02H + LD E,1BH +; CALL BDOS + CALL COUTE +; LD C,02H + LD E,'[' +; CALL BDOS + CALL COUTE + + LD A,(YPOS) + CP 10H + JP C,L2 + + RRA + RRA + RRA + RRA + AND 0FH + ADD A,'0' + +; LD C,02H + LD E,A +; CALL BDOS + CALL COUTE + + LD A,(YPOS) + +L2 AND 0FH + ADD A,'0' + +; LD C,02H + LD E,A +; CALL BDOS + CALL COUTE + +; LD C,02H + LD E,3BH +; CALL BDOS + CALL COUTE + + LD A,(XPOS) + CP 10H + JP C,L3 + + RRA + RRA + RRA + RRA + AND 0FH + ADD A,'0' + +; LD C,02H + LD E,A +; CALL BDOS + CALL COUTE + + LD A,(XPOS) + +L3 AND 0FH + ADD A,'0' + +; LD C,02H + LD E,A +; CALL BDOS + CALL COUTE + +; LD C,02H + LD E,'H' +; CALL BDOS + CALL COUTE + + RET + +NEWLINE: + +; LD C,02H + LD E,CR +; CALL BDOS + CALL COUTE + +; LD C,02H + LD E,LF +; CALL BDOS + CALL COUTE + + RET + +RAND16 LD DE,0 + LD A,D + LD H,E + LD L,253 + OR A + SBC HL,DE + SBC A,0 + SBC HL,DE + LD D,0 + SBC A,D + LD E,A + SBC HL,DE + JR NC,RAND + INC HL +RAND LD (RAND16+1),HL + RET + +DIV16: + LD A,H ; HL = HL % DE + LD C,L + LD HL,0 + LD B,16 + +DL1 SCF + RL C + RLA + ADC HL,HL + SBC HL,DE + JR NC,$+4 + ADD HL,DE + DEC C + DJNZ DL1 + + LD D,A ; DE = HL / DE + LD E,C + RET + +SPACER .DB " ", '$' + +LABELPTR: .DW LABELS + .DW LABELS + 8 + .DW LABELS + 16 + .DW LABELS + 24 + .DW LABELS + 32 + .DW LABELS + 40 + .DW LABELS + 48 + .DW LABELS + 56 + .DW LABELS + 64 + .DW LABELS + 72 + .DW LABELS + 80 + .DW LABELS + 88 + .DW LABELS + 96 + .DW LABELS + 104 + +LABELS .DB " ", EOS + .DB " 2 ", EOS + .DB " 4 ", EOS + .DB " 8 ", EOS + .DB " 16 ", EOS + .DB " 32 ", EOS + .DB " 64 ", EOS + .DB " 128 ", EOS + .DB " 256 ", EOS + .DB " 512 ", EOS + .DB " 1024 ", EOS + .DB " 2048 ", EOS + .DB " 4096 ", EOS + .DB " 8192 ", EOS + +COLORPTR: .DW COLORS + .DW COLORS + 9 + .DW COLORS + 18 + .DW COLORS + 27 + .DW COLORS + 36 + .DW COLORS + 45 + .DW COLORS + 54 + .DW COLORS + 63 + .DW COLORS + 72 + .DW COLORS + 81 + .DW COLORS + 90 + .DW COLORS + 99 + .DW COLORS + 108 + .DW COLORS + 117 + +COLORS .DB 1BH, "[40;37m", EOS ; 0 + .DB 1BH, "[44;37m", EOS ; 2 + .DB 1BH, "[46;37m", EOS ; 4 + .DB 1BH, "[42;37m", EOS ; 8 + .DB 1BH, "[43;30m", EOS ; 16 + .DB 1BH, "[45;37m", EOS ; 32 + .DB 1BH, "[41;37m", EOS ; 64 + .DB 1BH, "[47;30m", EOS ; 128 + .DB 1BH, "[44;37m", EOS ; 256 + .DB 1BH, "[46;37m", EOS ; 512 + .DB 1BH, "[42;37m", EOS ; 1024 + .DB 1BH, "[43;30m", EOS ; 2048 + .DB 1BH, "[45;37m", EOS ; 4096 + .DB 1BH, "[41;37m", EOS ; 8192 + +POINTS .DW 0000H + .DW 0000H + .DW 0004H + .DW 0008H + .DW 0016H + .DW 0032H + .DW 0064H + .DW 0128H + .DW 0256H + .DW 0512H + .DW 1024H + .DW 2048H + .DW 4096H + .DW 8192H + +INIT: .DB 1BH, "[2J", EOS +TERM: .DB 1BH, "[?25h" +RESET: .DB 1BH, "[0m", EOS + +ADVANCE: .DB 1BH, "[28C", EOS + +TOPBORDER: + .DB 1BH, "[47;37m" + .DB 0DCH + .DB 0DCH, 0DCH, 0DCH, 0DCH, 0DCH, 0DCH, 0DCH + .DB 0DCH, 0DCH, 0DCH, 0DCH, 0DCH, 0DCH, 0DCH + .DB 0DCH, 0DCH, 0DCH, 0DCH, 0DCH, 0DCH, 0DCH + .DB 0DCH, 0DCH, 0DCH, 0DCH, 0DCH, 0DCH, 0DCH + .DB 0DCH, EOS + +BTMBORDER .DB 0DFH + .DB 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH + .DB 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH + .DB 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH + .DB 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH + .DB 0DFH, 1BH, "[0m", EOS + +MSG1: .DB 1BH, "[0m" + .DB "UDLR = wsad ^E^X^S^D 8246 : Q=QUIT" + + .DB EOS +MSG2: .DB 1BH, "[0m" + .DB " QUIT or RESTART (y/n/r) " + .DB EOS +MSG3: .DB 1BH, "[0m" + .DB " GAME OVER! PLAY AGAIN (y/n) " + .DB BEL, CR, LF, EOS +MSG4: .DB 1BH, "[0m" + .DB "2048 pts" + .DB EOS + +; HBIOS functions replacing BDOS functions +; +; +; PRINT A STRING AT ADDRESS SPECIFIED IN DE +; STRING MUST BE TERMINATED BY '$' +; +PRTSTRDE: + PUSH HL + LD H,D + LD L,E + CALL PRTSTR + POP HL + RET +; +; PRINT A STRING AT ADDRESS SPECIFIED IN HL +; STRING MUST BE TERMINATED BY '$' +; USAGE: +; LD HL,MYSTR +; CALL PRTSTR +; ... +; MYSTR: .DB "HELLO$" +; +PRTSTR: + LD A,(HL) + INC HL + CP '$' + RET Z + CALL COUT + JR PRTSTR +; +; OUTPUT CHARACTER IN A TO CONSOLE DEVICE +; +COUT: PUSH AF + PUSH BC + PUSH DE + LD B,01H + LD C,0 + LD E,A + RST 08 + POP DE + POP BC + POP AF + RET +; +; OUTPUT CHARACTER IN A TO CONSOLE DEVICE +; +COUTE: PUSH AF + LD A,E + CALL COUT + POP AF + RET +; +; WAIT FOR A CHARACTER FROM THE CONSOLE DEVICE AND RETURN IT IN A +; +CIN: PUSH BC + LD B,00H + LD C,00H + RST 08 + LD A,E + POP BC + RET + +; variables + +BOARD: + .DB 00, 00, 00, 00 + .DB 00, 00, 00, 00 + .DB 00, 00, 00, 00 + .DB 00, 00, 00, 00 + +XPOS: .DB 0 +YPOS: .DB 0 +BOARDPTR: .DW 0 +MOVED: .DB 0 +RESULT: .DB 0 +SCORE .DB 00H, 00H, 00H, 00H +VAREND .EQU $ +SCORESTR .DB 1BH, "[0m $" + +LASTBYTE .EQU $ + +SLACK .EQU (GAM_END - LASTBYTE) + .FILL SLACK,'e' +; + .ECHO "GAME space remaining: " + .ECHO SLACK + .ECHO " bytes.\n" + + .END + diff --git a/Source/HBIOS/hbios.asm b/Source/HBIOS/hbios.asm index 6b31ade4..36a35b15 100644 --- a/Source/HBIOS/hbios.asm +++ b/Source/HBIOS/hbios.asm @@ -54,6 +54,7 @@ ; - config/_.asm ; - cfg_.asm ; - .asm +; - .asm ; - util.asm ; - time.asm ; - bcd.asm @@ -61,6 +62,7 @@ ; - encode.asm ; - [xio|mio].asm ; - dsky.asm +; - unlzsa2s.asm ; ; ; INCLUDE GENERIC STUFF @@ -656,7 +658,7 @@ HBX_BUF .FILL HBX_BUFSIZ,0 .DW HBX_IDENT ; ADDRESS OF HBIOS PROXY START (DEPRECATED) .DW HBX_IDENT ; ADDRESS OF HBIOS IDENT INFO DATA BLOCK ; - .FILL $MEMTOP - $ ; FILL TO END OF MEMORY (AS NEEDED) + .FILL MEMTOP - $ ; FILL TO END OF MEMORY (AS NEEDED) .ORG HBX_IMG + HBX_SIZ ; RESET ORG ; ;================================================================================================== @@ -773,6 +775,7 @@ HB_START: ; MASK OFF TIMER INTERRUPTS XOR A OUT0 (Z180_TCR),A + OUT0 (Z180_ITC),A ; SET DEFAULT CPU CLOCK MULTIPLIERS (XTAL / 2) ; @@ -903,12 +906,53 @@ HB_START1: ; BNKCALL ARRIVES HERE, BUT NOW RUNNING IN RAM BANK LD ($0039),HL #ELSE ; RETI ($ED, $4D) IF NON-INTERRUPT MODE - LD HL,($0038) + LD HL,$0038 LD (HL),$ED INC HL LD (HL),$4D #ENDIF #ENDIF +; +;================================================================================================== +; RECOVERY MODE +;================================================================================================== +; +; PLATFORM SPECIFIC CODE FOR DETECTING RECOVERY MODE SWITCH +; +#IF (BT_REC_TYPE != BT_REC_NONE) + #IF (BT_REC_TYPE == BT_REC_FORCE) + LD A,1 ; SET FOR RECOVERY MODE + LD (HB_BOOT_REC),A ; SAVE FOR LATER + #ENDIF + #IF (PLATFORM == PLT_SBC) + #IF (BT_REC_TYPE == BT_REC_SBC01) + LD A,%00100000 ; DISABLE RTC AND + OUT (RTCIO),A ; DRQ DRIVER READ + IN A,(RTCIO) ; BIT 0 (DRQ). + CPL ; PULLED HIGH + AND 1 ; IS RECOVERY MODE + LD (HB_BOOT_REC),A ; SAVE FOR LATER + #ENDIF + #IF (BT_REC_TYPE == BT_REC_SBC1B) + IN A,(RTCIO) ; RTC PORT, BIT 6 HAS THE + BIT 6,A ; STATE OF CONFIG JUMPER + LD A,1 ; JUMPER INSTALLED + JR Z,SAVE_REC_M ; IS RECOVERY MODE + LD A,0 +SAVE_REC_M: + LD (HB_BOOT_REC),A ; SAVE FOR LATER + #ENDIF + #IF (BT_REC_TYPE == BT_REC_SBCRI) + IN A,($68 + 6) ; UART_MSR MODEM + BIT 6,A ; STATUS REGISTER + LD A,0 ; BIT 6 + JR Z,SAVE_REC_M ; IS RECOVERY MODE + LD A,1 +SAVE_REC_M: + LD (HB_BOOT_REC),A ; SAVE FOR LATER + #ENDIF + #ENDIF +#ENDIF ; DIAG(%00001111) ; @@ -1137,8 +1181,8 @@ HB_CPU2: ; LD A,FORCECON ; CALCULATE PRE-INIT TABLE ; A IS INDEX OF CONSOLE DEVICE ENTRY RLCA ; ENTRY THAT WE WANT TO ; A IS OFFSET OF CONSOLE DEVICE ENTRY - LD DE,(PC_INITTBL) ; EXECUTE FIRST ; DE IS VALUE OF TOP ENTRY - LD HL,PC_INITTBL ; HL IS ADDRESS OF TOP OF TABLE + LD DE,(HB_PCINITTBL) ; EXECUTE FIRST ; DE IS VALUE OF TOP ENTRY + LD HL,HB_PCINITTBL ; HL IS ADDRESS OF TOP OF TABLE PUSH HL ; PUSH (1) TOP OF TABLE PUSH DE ; PUSH (2) VALUE OF TOP ENTRY PUSH HL ; PUSH (3) TOP OF TABLE @@ -1152,9 +1196,19 @@ HB_CPU2: LD (HL),E ; SAVE DE OVER ORIGINAL ENTRY INC HL LD (HL),D - LD B,PC_INITTBLLEN + LD B,HB_PCINITTBLLEN POP DE ; POP (1) DE IS ADDRESS OF TOP OF TABLE - CALL CALLLIST ; PROCESS THE PRE-INIT CALL TABLE + +#IF (BT_REC_TYPE != BT_REC_NONE) + LD A,(HB_BOOT_REC) ; IF WE ARE IN RECOVERY MODE + OR A ; POINT TO THE RECOVER MODE + JR Z,NOT_REC_M0 ; INITIALIZATION TABLE + LD B,HB_PCINITRLEN + LD DE,HB_PCINIT_REC +NOT_REC_M0: + +#ENDIF + CALL CALLLIST ; PROCESS THE PRE-INIT CALL TABLE ; #IF 0 ; @@ -1327,8 +1381,20 @@ HB_SPDTST: ; PERFORM DEVICE INITIALIZATION ; CALL NEWLINE + +#IF (BT_REC_TYPE != BT_REC_NONE) + LD A,(HB_BOOT_REC) ; IF WE ARE IN RECOVERY MODE + OR A ; POINT TO THE RECOVER MODE + JR Z,NOT_REC_M1 ; INITIALIZATION TABLE + LD B,HB_INITRLEN + LD DE,HB_INIT_REC + JR IS_REC_M1 +#ENDIF + +NOT_REC_M1: LD B,HB_INITTBLLEN LD DE,HB_INITTBL +IS_REC_M1: CALL CALLLIST ; ; RECORD HEAP CURB AT THE CURRENT VALUE OF HEAP TOP. HEAP CURB @@ -1429,12 +1495,37 @@ CALLLIST: DJNZ CALLLIST CALLDUMMY: RET + +; +;================================================================================================== +; TABLE OF RECOVERY MODE INITIALIZATION ENTRY POINTS +;================================================================================================== +; +; USE "CALLDUMMY" IF NO ENTRY REQUIRED +; +#IF (BT_REC_TYPE != BT_REC_NONE) +; +HB_PCINIT_REC: + #IF (PLATFORM == PLT_SBC) + .DW UART_PREINIT +; .DW CALLDUMMY + #ENDIF +HB_PCINITRLEN .EQU (($ - HB_PCINIT_REC) / 2) +; +HB_INIT_REC: + #IF (PLATFORM == PLT_SBC) + .DW UART_INIT + .DW MD_INIT + #ENDIF +HB_INITRLEN .EQU (($ - HB_INIT_REC) / 2) +; +#ENDIF ; ;================================================================================================== ; TABLE OF PRE-CONSOLE INITIALIZATION ENTRY POINTS ;================================================================================================== - -PC_INITTBL: +; +HB_PCINITTBL: .DW CALLDUMMY ; RESERVED FOR FORCING PRIMARY CONSOLE #IF (ASCIENABLE) .DW ASCI_PREINIT @@ -1454,7 +1545,7 @@ PC_INITTBL: #IF (UFENABLE) .DW UF_PREINIT #ENDIF -PC_INITTBLLEN .EQU (($ - PC_INITTBL) / 2) +HB_PCINITTBLLEN .EQU (($ - HB_PCINITTBL) / 2) ;================================================================================================== ; TABLE OF INITIALIZATION ENTRY POINTS @@ -2849,50 +2940,47 @@ SIZ_NEC .EQU $ - ORG_NEC .ECHO " bytes.\n" #ENDIF ; -; CVDU AND VGA3 CAN USE THE SAME FONT ROM UNLESS VGA3 MODE IS USING V80X43 MODE +; FONTS AREA ; -#IF (CVDUENABLE | VGAENABLE) -ORG_FONTHI .EQU $ -#IF (VGAENABLE & CVDUENABLE) - #IF (VGASIZ=V80X43) -VGA_FONT: - #INCLUDE "font8043.asm" -CVDU_FONT: - #INCLUDE "font_hi.asm" - #ELSE -VGA_FONT: -CVDU_FONT: - #INCLUDE "font_hi.asm" - #ENDIF -#ELSE - #IF (VDUENABLE) -VGA_FONT: - #IF VGASIZ=(V80X43) - #INCLUDE "font8043.asm" - #ELSE - #INCLUDE "font_hi.asm" - #ENDIF +ORG_FONTS .EQU $ +; + .ECHO "FONTS" +; +#IFDEF USEFONT8X8 +FONT8X8: + #IF USELZSA2 + #INCLUDE "font8x8c.asm" + #ELSE + #INCLUDE "font8x8u.asm" #ENDIF - #IF (CVDUENABLE) -CVDU_FONT: - #INCLUDE "font_hi.asm" - #ENDIF -#ENDIF -SIZ_FONTHI .EQU $ - ORG_FONTHI - .ECHO "FONTS occupy " - .ECHO SIZ_FONTHI - .ECHO " bytes.\n" + .ECHO " 8X8" #ENDIF ; -#IF (TMSENABLE) -ORG_FONTTMS .EQU $ - #INCLUDE "font_tms.asm" -SIZ_FONTTMS .EQU $ - ORG_FONTTMS - .ECHO "FONTTMS occupies " - .ECHO SIZ_FONTTMS - .ECHO " bytes.\n" +#IFDEF USEFONT8X11 +FONT8X11: + #IF USELZSA2 + #INCLUDE "font8x11c.asm" + #ELSE + #INCLUDE "font8x11u.asm" + #ENDIF + .ECHO " 8X11" #ENDIF ; +#IFDEF USEFONT8X16 +FONT8X16: + #IF USELZSA2 + #INCLUDE "font8x16c.asm" + #ELSE + #INCLUDE "font8x16u.asm" + #ENDIF + .ECHO " 8X16" +#ENDIF +; +SIZ_FONTS .EQU $ - ORG_FONTS + .ECHO " occupy " + .ECHO SIZ_FONTS + .ECHO " bytes.\n" +; #IF (CVDUENABLE | VGAENABLE) ORG_KBD .EQU $ #INCLUDE "kbd.asm" @@ -3065,6 +3153,12 @@ SIZ_CTC .EQU $ - ORG_CTC #INCLUDE "dsky.asm" #ENDIF ; +; INCLUDE LZSA2 decompression engine if required. +; +#IF ((VGAENABLE | CVDUENABLE | TMSENABLE) & USELZSA2) +#INCLUDE "unlzsa2s.asm" +#ENDIF +; ; DETECT CPU SPEED USING DS-1302 RTC ; HB_CPUSPD: @@ -3684,7 +3778,7 @@ PS_FLP_DSTR: .TEXT "SD$" ; PS_FLPSD ; CHARACTER DEVICE STRINGS ; PS_SDSTRREF: - .DW PS_SDUART, PS_SDASCI, PS_SDTERM, + .DW PS_SDUART, PS_SDASCI, PS_SDTERM .DW PS_SDPRPCON, PS_SDPPPCON, PS_SDSIO, PS_SDACIA, PS_SDPIO,PS_SDUF ; PS_SDUART .TEXT "UART$" @@ -3905,6 +3999,10 @@ RTCVAL .DB 0 ; SHADOW VALUE FOR RTC LATCH PORT ; HB_BATCOND .DB 0 ; BATTERY CONDITION (0=LOW, 1=OK) ; +#IF (BT_REC_TYPE != BT_REC_NONE) +HB_BOOT_REC .DB 0 ; BOOT MODE (0=NORMAL, 1=RECOVERY MODE) +#ENDIF +; STR_BANNER .DB "RetroBrew HBIOS v", BIOSVER, ", ", TIMESTAMP, "$" STR_PLATFORM .DB PLATFORM_NAME, "$" STR_SWITCH .DB "*** Activating CRT Console ***$" diff --git a/Source/HBIOS/hbios.inc b/Source/HBIOS/hbios.inc index 87016ecd..444bae8b 100644 --- a/Source/HBIOS/hbios.inc +++ b/Source/HBIOS/hbios.inc @@ -124,13 +124,13 @@ VDADEV_VDU .EQU $00 ; ECB VDU - MOTOROLA 6545 VDADEV_CVDU .EQU $10 ; ECB COLOR VDU - MOS 8563 VDADEV_NEC .EQU $20 ; ECB UPD7220 - NEC UPD7220 VDADEV_TMS .EQU $30 ; N8 ONBOARD VDA SUBSYSTEM - TMS 9918 -VDADEV_VGA .EQU $40 ; VGA -; -; EMULATION TYPES -; -EMUTYP_NONE .EQU 0 ; NONE -EMUTYP_TTY .EQU 1 ; TTY -EMUTYP_ANSI .EQU 2 ; ANSI +VDADEV_VGA .EQU $40 ; ECB VGA3 - HITACHI HD6445 +;; +;; EMULATION TYPES - moved to std.asm +;; +;EMUTYP_NONE .EQU 0 ; NONE +;EMUTYP_TTY .EQU 1 ; TTY +;EMUTYP_ANSI .EQU 2 ; ANSI ; ; HBIOS CONTROL BLOCK OFFSETS ; WARNING: THESE OFFSETS WILL CHANGE SIGNIFICANTLY BETWEEN RELEASES diff --git a/Source/HBIOS/ide.asm b/Source/HBIOS/ide.asm index c9149ca7..b2f771f3 100644 --- a/Source/HBIOS/ide.asm +++ b/Source/HBIOS/ide.asm @@ -4,9 +4,20 @@ ;============================================================================= ; ; TODO: -; - IMPLEMENT IDE_INITDEVICE -; - HANDLE SECONDARY INTERFACE ON DIDE -; - IMPLEMENT INTELLIGENT RESET, CHECK IF DEVICE IS ACTUALLY BROKEN BEFORE RESET +; - FIX SCALER CONSTANT +; - GOPARTNER NEEDS TO HANDLE "NO PARTNER" CONDITION +; - IMPLEMENT H/W PROBES FOR DIO AND DIDE +; +; NOTES: +; - WELL KNOWN IDE PORT ADDRESSES: +; BOARD BASE DATLO DATHI +; ------ ------ ------ ------ +; DIO $20 $20 $28 +; DIDE-A $20 $28 $28 +; DIDE-B $30 $38 $38 +; MK4 $80 N/A N/A +; RC $10 N/A N/A +; SMB $E0 N/A N/A ; ; +-----------------------------------------------------------------------+ ; | CONTROL BLOCK REGISTERS | @@ -100,77 +111,31 @@ #DEFINE DCALL \; #ENDIF ; -; UNIT MAPPING IS AS FOLLOWS: -; IDE0: PRIMARY MASTER -; IDE1: PRIMARY SLAVE -; IDE2: SECONDARY MASTER -; IDE3: SECONDARY SLAVE -; -IDE_DEVCNT .EQU 2 ; ASSUME ONLY PRIMARY INTERFACE -IDE_IO_BASE .EQU $20 ; DEFAULT IO BASE (NOTE OVERRIDES BELOW) -; -#IF (IDEMODE == IDEMODE_MK4) -IDE_IO_BASE .SET $80 -IDE_XAR .EQU $88 ; EXTERNAL ADDRESS REGISTER (XAR)#ENDIF -#ENDIF - -#IF (IDEMODE == IDEMODE_RC) -IDE_IO_BASE .SET $10 -#ENDIF - -#IF (IDEMODE == IDEMODE_SMB) -IDE_IO_BASE .SET $E0 -#ENDIF - -#IF ((IDEMODE == IDEMODE_DIO) | (IDEMODE == IDEMODE_MK4)) -#IF (IDE8BIT) -IDE_IO_DATA .EQU $IDE_IO_BASE + $00 ; DATA PORT (8 BIT PIO) (R/W) -#ELSE -IDE_IO_DATALO .EQU $IDE_IO_BASE + $00 ; DATA PORT (16 BIT PIO LO BYTE) (R/W) -IDE_IO_DATAHI .EQU $IDE_IO_BASE + $08 ; DATA PORT (16 BIT PIO HI BYTE) (R/W) -IDE_IO_DATA .EQU IDE_IO_DATALO -#ENDIF -#ENDIF -; -#IF (IDEMODE == IDEMODE_DIDE) -IDE_DEVCNT .SET 4 ; DIDE HAS PRIMARY AND SECONDARY INTERACES -#IF (IDE8BIT) -IDE_IO_DATA .EQU $IDE_IO_BASE + $00 ; DATA PORT (8 BIT PIO) (R/W) -#ELSE -IDE_IO_DATA .EQU $IDE_IO_BASE + $08 ; DATA PORT (16 BIT PIO LO/HI BYTES) (R/W) -IDE_IO_DMA .EQU $IDE_IO_BASE + $09 ; DATA PORT (16 BIT DMA LO/HI BYTES) (R/W) -#ENDIF -#ENDIF -; -#IF ((IDEMODE == IDEMODE_RC) | (IDEMODE == IDEMODE_SMB)) -IDE_DEVCNT .SET 1 ; RC2014 COMPACT FLASH SUPPORTS ONLY 1 DEVICE -IDE_IO_DATA .EQU $IDE_IO_BASE + $00 ; DATA PORT (8 BIT) (R/W) -#ENDIF -; -;IDE_IO_DATA .EQU $IDE_IO_BASE + $00 ; DATA INPUT/OUTPUT (R/W) -IDE_IO_ERR .EQU $IDE_IO_BASE + $01 ; ERROR REGISTER (R) -IDE_IO_FEAT .EQU $IDE_IO_BASE + $01 ; FEATURES REGISTER (W) -IDE_IO_COUNT .EQU $IDE_IO_BASE + $02 ; SECTOR COUNT REGISTER (R/W) -IDE_IO_SECT .EQU $IDE_IO_BASE + $03 ; SECTOR NUMBER REGISTER (R/W) -IDE_IO_CYLLO .EQU $IDE_IO_BASE + $04 ; CYLINDER NUM REGISTER (LSB) (R/W) -IDE_IO_CYLHI .EQU $IDE_IO_BASE + $05 ; CYLINDER NUM REGISTER (MSB) (R/W) -IDE_IO_DRVHD .EQU $IDE_IO_BASE + $06 ; DRIVE/HEAD REGISTER (R/W) -IDE_IO_LBA0 .EQU $IDE_IO_BASE + $03 ; LBA BYTE 0 (BITS 0-7) (R/W) -IDE_IO_LBA1 .EQU $IDE_IO_BASE + $03 ; LBA BYTE 1 (BITS 8-15) (R/W) -IDE_IO_LBA2 .EQU $IDE_IO_BASE + $03 ; LBA BYTE 2 (BITS 16-23) (R/W) -IDE_IO_LBA3 .EQU $IDE_IO_BASE + $03 ; LBA BYTE 3 (BITS 24-27) (R/W) -IDE_IO_STAT .EQU $IDE_IO_BASE + $07 ; STATUS REGISTER (R) -IDE_IO_CMD .EQU $IDE_IO_BASE + $07 ; COMMAND REGISTER (EXECUTE) (W) -IDE_IO_ALTSTAT .EQU $IDE_IO_BASE + $0E ; ALTERNATE STATUS REGISTER (R) -IDE_IO_CTRL .EQU $IDE_IO_BASE + $0E ; DEVICE CONTROL REGISTER (W) -IDE_IO_DRVADR .EQU $IDE_IO_BASE + $0F ; DRIVE ADDRESS REGISTER (R) +IDE_REG_DATA .EQU $00 ; DATA /OUTPUT (R/W) +IDE_REG_ERR .EQU $01 ; ERROR REGISTER (R) +IDE_REG_FEAT .EQU $01 ; FEATURES REGISTER (W) +IDE_REG_COUNT .EQU $02 ; SECTOR COUNT REGISTER (R/W) +IDE_REG_SECT .EQU $03 ; SECTOR NUMBER REGISTER (R/W) +IDE_REG_CYLLO .EQU $04 ; CYLINDER NUM REGISTER (LSB) (R/W) +IDE_REG_CYLHI .EQU $05 ; CYLINDER NUM REGISTER (MSB) (R/W) +IDE_REG_DRVHD .EQU $06 ; DRIVE/HEAD REGISTER (R/W) +IDE_REG_LBA0 .EQU $03 ; LBA BYTE 0 (BITS 0-7) (R/W) +IDE_REG_LBA1 .EQU $04 ; LBA BYTE 1 (BITS 8-15) (R/W) +IDE_REG_LBA2 .EQU $05 ; LBA BYTE 2 (BITS 16-23) (R/W) +IDE_REG_LBA3 .EQU $06 ; LBA BYTE 3 (BITS 24-27) (R/W) +IDE_REG_STAT .EQU $07 ; STATUS REGISTER (R) +IDE_REG_CMD .EQU $07 ; COMMAND REGISTER (EXECUTE) (W) +IDE_REG_XAR .EQU $08 ; ECB DIDE EXTERNAL ADDRESS REGISTER (W) +IDE_REG_ALTSTAT .EQU $0E ; ALTERNATE STATUS REGISTER (R) +IDE_REG_CTRL .EQU $0E ; DEVICE CONTROL REGISTER (W) +IDE_REG_DRVADR .EQU $0F ; DRIVE ADDRESS REGISTER (R) ; ; COMMAND BYTES ; -IDE_CIDE_RECAL .EQU $10 -IDE_CIDE_READ .EQU $20 -IDE_CIDE_WRITE .EQU $30 -IDE_CIDE_IDDEV .EQU $EC +IDE_CIDE_RECAL .EQU $10 +IDE_CIDE_READ .EQU $20 +IDE_CIDE_WRITE .EQU $30 +IDE_CIDE_IDDEV .EQU $EC IDE_CIDE_SETFEAT .EQU $EF ; ; FEATURE BYTES @@ -197,57 +162,130 @@ IDE_STBSYTO .EQU -7 ; ; DRIVE SELECTION BYTES (FOR USE IN DRIVE/HEAD REGISTER) ; -IDE_DRVSEL: -IDE_DRVMASTER .DB %11100000 ; LBA, MASTER DEVICE -IDE_DRVSLAVE .DB %11110000 ; LBA, SLAVE DEVICE +;IDE_DRVSEL: +IDE_DRVMASTER .EQU %11100000 ; LBA, MASTER DEVICE +IDE_DRVSLAVE .EQU %11110000 ; LBA, SLAVE DEVICE ; ; IDE DEVICE CONFIGURATION ; -IDE_CFGSIZ .EQU 12 ; SIZE OF CFG TBL ENTRIES +IDE_CFGSIZ .EQU 19 ; SIZE OF CFG TBL ENTRIES ; ; PER DEVICE DATA OFFSETS ; IDE_DEV .EQU 0 ; OFFSET OF DEVICE NUMBER (BYTE) -IDE_STAT .EQU 1 ; LAST STATUS (BYTE) -IDE_TYPE .EQU 2 ; DEVICE TYPE (BYTE) -IDE_FLAGS .EQU 3 ; FLAG BITS BIT 0=CF, 1=LBA (BYTE) -IDE_MEDCAP .EQU 4 ; MEDIA CAPACITY (DWORD) -IDE_LBA .EQU 8 ; OFFSET OF LBA (DWORD) +IDE_MODE .EQU 1 ; OPERATION MODE: IDE MODE (BYTE) +IDE_STAT .EQU 2 ; LAST STATUS (BYTE) +IDE_TYPE .EQU 3 ; DEVICE TYPE (BYTE) +IDE_ACC .EQU 4 ; ACCESS FLAG BITS BIT 0=MASTER, 1=8BIT (BYTE) +IDE_MED .EQU 5 ; MEDIA FLAG BITS BIT 0=CF, 1=LBA (BYTE) +IDE_MEDCAP .EQU 6 ; MEDIA CAPACITY (DWORD) +IDE_LBA .EQU 10 ; OFFSET OF LBA (DWORD) +IDE_IOBASE .EQU 14 ; IO BASE ADDRESS (BYTE) +IDE_DATALO .EQU 15 ; 16 BIT DATA LO BYTE +IDE_DATAHI .EQU 16 ; 16 BIT DATA HI BYTE +IDE_PARTNER .EQU 17 ; PARTNER DEVICE (MASTER <-> SLAVE) (WORD) +; +IDE_ACC_MAS .EQU %00000001 ; UNIT IS MASTER (ELSE SLAVE) +IDE_ACC_8BIT .EQU %00000010 ; UNIT WANTS 8 BIT I/O (ELSE 16 BIT) +; +IDE_MED_CF .EQU %00000001 ; MEDIA IS CF CARD +IDE_MED_LBA .EQU %00000010 ; MEDIA HAS LBA CAPABILITY +; +IDE_DEVCNT .EQU IDECNT * 2 ; IDE_CFGTBL: - ; DEVICE 0, PRIMARY MASTER +; +#IF (IDECNT >= 1) +; +IDE_DEV0M: ; DEVICE 0, MASTER .DB 0 ; DRIVER DEVICE NUMBER + .DB IDE0MODE ; DRIVER DEVICE MODE .DB 0 ; DEVICE STATUS .DB 0 ; DEVICE TYPE - .DB 0 ; FLAGS BYTE + .DB IDE_ACC_MAS | (IDE0A8BIT & IDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA -#IF (IDE_DEVCNT >= 2) - ; DEVICE 1, PRIMARY SLAVE - .DB 1 ; DRIVER DEVICE NUMBER + .DB IDE0BASE ; IO BASE ADDRESS + .DB IDE0DATLO ; IO BASE ADDRESS + .DB IDE0DATHI ; IO BASE ADDRESS + .DW IDE_DEV0S ; PARTNER +; +IDE_DEV0S: ; DEVICE 0, SLAVE + .DB 0 ; DRIVER DEVICE NUMBER + .DB IDE0MODE ; DRIVER DEVICE MODE .DB 0 ; DEVICE STATUS .DB 0 ; DEVICE TYPE - .DB 0 ; FLAGS BYTE + .DB (IDE0B8BIT & IDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA + .DB IDE0BASE ; IO BASE ADDRESS + .DB IDE0DATLO ; IO BASE ADDRESS + .DB IDE0DATHI ; IO BASE ADDRESS + .DW IDE_DEV0M ; PARTNER #ENDIF -#IF (IDE_DEVCNT >= 3) - ; DEVICE 2, SECONDARY MASTER - .DB 2 ; DRIVER DEVICE NUMBER +; +#IF (IDECNT >= 2) +; +IDE_DEV1M: ; DEVICE 1, MASTER + .DB 0 ; DRIVER DEVICE NUMBER + .DB IDE1MODE ; DRIVER DEVICE MODE .DB 0 ; DEVICE STATUS .DB 0 ; DEVICE TYPE - .DB 0 ; FLAGS BYTE + .DB IDE_ACC_MAS | (IDE1A8BIT & IDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA + .DB IDE1BASE ; IO BASE ADDRESS + .DB IDE1DATLO ; IO BASE ADDRESS + .DB IDE1DATHI ; IO BASE ADDRESS + .DW IDE_DEV1S ; PARTNER +; +IDE_DEV1S: ; DEVICE 1, SLAVE + .DB 0 ; DRIVER DEVICE NUMBER + .DB IDE1MODE ; DRIVER DEVICE MODE + .DB 0 ; DEVICE STATUS + .DB 0 ; DEVICE TYPE + .DB (IDE1B8BIT & IDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS + .DW 0,0 ; DEVICE CAPACITY + .DW 0,0 ; CURRENT LBA + .DB IDE1BASE ; IO BASE ADDRESS + .DB IDE1DATLO ; IO BASE ADDRESS + .DB IDE1DATHI ; IO BASE ADDRESS + .DW IDE_DEV1M ; PARTNER #ENDIF -#IF (IDE_DEVCNT >= 4) - ; DEVICE 2, SECONDARY SLAVE - .DB 3 ; DRIVER DEVICE NUMBER +; +#IF (IDECNT >= 3) +; +IDE_DEV2M: ; DEVICE 2, MASTER + .DB 0 ; DRIVER DEVICE NUMBER + .DB IDE2MODE ; DRIVER DEVICE MODE .DB 0 ; DEVICE STATUS .DB 0 ; DEVICE TYPE - .DB 0 ; FLAGS BYTE + .DB IDE_ACC_MAS | (IDE2A8BIT & IDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA + .DB IDE2BASE ; IO BASE ADDRESS + .DB IDE2DATLO ; IO BASE ADDRESS + .DB IDE2DATHI ; IO BASE ADDRESS + .DW IDE_DEV2S ; PARTNER +; +IDE_DEV2S: ; DEVICE 2, SLAVE + .DB 0 ; DRIVER DEVICE NUMBER + .DB IDE2MODE ; DRIVER DEVICE MODE + .DB 0 ; DEVICE STATUS + .DB 0 ; DEVICE TYPE + .DB (IDE2B8BIT & IDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS + .DW 0,0 ; DEVICE CAPACITY + .DW 0,0 ; CURRENT LBA + .DB IDE2BASE ; IO BASE ADDRESS + .DB IDE2DATLO ; IO BASE ADDRESS + .DB IDE2DATHI ; IO BASE ADDRESS + .DW IDE_DEV1M ; PARTNER #ENDIF ; #IF ($ - IDE_CFGTBL) != (IDE_DEVCNT * IDE_CFGSIZ) @@ -273,78 +311,87 @@ IDE_TOFAST .EQU 10 ; FAST TIMEOUT IS 0.5 SECS ;============================================================================= ; IDE_INIT: - CALL NEWLINE ; FORMATTING - PRTS("IDE:$") -; ; COMPUTE CPU SPEED COMPENSATED TIMEOUT SCALER - ; AT 1MHZ, THE SCALER IS 961 (50000US / 52TS = 961) - ; SCALER IS THEREFORE 961 * CPU SPEED IN MHZ + ; AT 1MHZ, THE SCALER IS 218 (50000US / 229TS = 218) + ; SCALER IS THEREFORE 218 * CPU SPEED IN MHZ LD DE,961 ; LOAD SCALER FOR 1MHZ LD A,(CB_CPUMHZ) ; LOAD CPU SPEED IN MHZ CALL MULT8X16 ; HL := DE * A LD (IDE_TOSCALER),HL ; SAVE IT ; -#IF (IDEMODE == IDEMODE_DIO) - PRTS(" MODE=DIO$") -#ENDIF -#IF (IDEMODE == IDEMODE_DIDE) - PRTS(" MODE=DIDE$") -#ENDIF -#IF (IDEMODE == IDEMODE_MK4) - PRTS(" MODE=MK4$") -#ENDIF - ; PRINT IDE INTERFACE PORT ADDRESS - PRTS(" IO=0x$") ; LABEL FOR IO ADDRESS - LD A,IDE_IO_DATA ; GET IO ADDRESS - CALL PRTHEXBYTE ; PRINT IT -; + XOR A ; ZERO ACCUM + LD (IDE_DEVNUM),A ; INIT DEV UNIT NUM FOR DYNAMIC ASSIGNMENT + LD IY,IDE_CFGTBL ; POINT TO START OF CONFIG TABLE ; - CALL IDE_DETECT ; CHECK FOR HARDWARE - JR Z,IDE_INIT00 ; CONTINUE IF PRESENT -; - ; HARDWARE NOT PRESENT - PRTS(" NOT PRESENT$") - OR $FF ; SIGNAL FAILURE - RET +IDE_INIT1: + LD A,(IY) ; LOAD FIRST BYTE TO CHECK FOR END + CP $FF ; CHECK FOR END OF TABLE VALUE + JR NZ,IDE_INIT2 ; IF NOT END OF TABLE, CONTINUE + XOR A ; SIGNAL SUCCESS + RET ; AND RETURN ; -IDE_INIT00: - ; PRINT UNIT COUNT - PRTS(" DEVICES=$") ; PRINT LABEL FOR DEVICE COUNT - LD A,IDE_DEVCNT ; GET UNIT COUNT - CALL PRTDECB ; PRINT IT IN DECIMAL +IDE_INIT2: + BIT 0,(IY+IDE_ACC) ; MASTER? + JR Z,IDE_INIT4 ; IF NOT MASTER, SKIP AHEAD ; -; SETUP THE DISPATCH TABLE ENTRIES + CALL NEWLINE ; FORMATTING + PRTS("IDE:$") ; LABEL FOR IO ADDRESS ; - LD B,IDE_DEVCNT ; LOOP CONTROL - LD IY,IDE_CFGTBL ; START OF CFG TABLE -IDE_INIT0: - PUSH BC ; SAVE LOOP CONTROL + PRTS(" IO=0x$") ; LABEL FOR IO ADDRESS + LD A,(IY+IDE_IOBASE) ; GET IO BASE ADDRES + CALL PRTHEXBYTE ; DISPLAY IT +; + PRTS(" MODE=$") ; LABEL FOR MODE + LD A,(IY+IDE_MODE) ; GET MODE BITS + LD DE,IDE_STR_MODE_DIO ; MODE LABEL + CP IDEMODE_DIO ; TEST FOR MODE + JR Z,IDE_INIT2A ; IF SO, DISPLAY IT + LD DE,IDE_STR_MODE_DIDE ; MODE LABEL + CP IDEMODE_DIDE ; TEST FOR MODE + JR Z,IDE_INIT2A ; IF SO, DISPLAY IT + LD DE,IDE_STR_MODE_MK4 ; MODE LABEL + CP IDEMODE_MK4 ; TEST FOR MODE + JR Z,IDE_INIT2A ; IF SO, DISPLAY IT + LD DE,IDE_STR_MODE_RC ; MODE LABEL + CP IDEMODE_RC ; TEST FOR MODE + JR Z,IDE_INIT2A ; IF SO, DISPLAY IT + JR IDE_INIT4 ; NO MODE? BYPASS ENTRY +IDE_INIT2A: + CALL WRITESTR ; DISPLAY MODE +; + CALL IDE_DETECT ; PROBE FOR INTERFACE + JR Z,IDE_INIT3 ; GOT IT, MOVE ON TO INIT UNITS + CALL PC_SPACE ; FORMATTING + LD DE,IDE_STR_NOHW ; NOT PRESENT MESSAGE + CALL WRITESTR ; DISPLAY IT + JR IDE_INIT4 ; SKIP CFG ENTRY +; +IDE_INIT3: + CALL IDE_RESET ; RESET THE BUS + CALL IDE_INIT5 ; DETECT/INIT MASTER + PUSH IY ; SAVE CFG PTR + CALL IDE_GOPARTNER ; SWITCH IY TO PARTNER CFG + CALL IDE_INIT5 ; DETECT/INIT SLAVE + POP IY ; RESTORE CFG PTR +; +IDE_INIT4: + LD DE,IDE_CFGSIZ ; SIZE OF CFG TABLE ENTRY + ADD IY,DE ; BUMP POINTER + JP IDE_INIT1 ; AND LOOP +; +IDE_INIT5: + ; UPDATE DRIVER RELATIVE UNIT NUMBER IN CONFIG TABLE + LD A,(IDE_DEVNUM) ; GET NEXT UNIT NUM TO ASSIGN + LD (IY+IDE_DEV),A ; UPDATE IT + INC A ; BUMP TO NEXT UNIT NUM TO ASSIGN + LD (IDE_DEVNUM),A ; SAVE IT +; + ; ADD UNIT TO GLOBAL DISK UNIT TABLE LD BC,IDE_FNTBL ; BC := FUNC TABLE ADR PUSH IY ; CFG ENTRY POINTER POP DE ; COPY TO DE - CALL DIO_ADDENT ; ADD ENTRY, BC IS NOT DESTROYED - LD BC,IDE_CFGSIZ ; SIZE OF CFG ENTRY - ADD IY,BC ; BUMP IY TO NEXT ENTRY - POP BC ; RESTORE BC - DJNZ IDE_INIT0 ; LOOP AS NEEDED -; - ; INITIALIZE THE IDE INTERFACE NOW - CALL IDE_RESET ; DO HARDWARE SETUP/INIT - RET NZ ; ABORT IF RESET FAILS -; - ; DEVICE DISPLAY LOOP - LD B,IDE_DEVCNT ; LOOP ONCE PER DEVICE - LD IY,IDE_CFGTBL ; START OF CFG TABLE -IDE_INIT1: - PUSH BC ; SAVE LOOP CONTROL - CALL IDE_INIT2 ; DISPLAY UNIT INFO - LD BC,IDE_CFGSIZ ; SIZE OF CFG ENTRY - ADD IY,BC ; BUMP IY TO NEXT ENTRY - POP BC ; RESTORE LOOP CONTROL - DJNZ IDE_INIT1 ; LOOP UNTIL DONE - RET ; DONE + CALL DIO_ADDENT ; ADD ENTRY TO GLOBAL DISK DEV TABLE ; -IDE_INIT2: ; CHECK FOR BAD STATUS LD A,(IY+IDE_STAT) ; GET STATUS OR A ; SET FLAGS @@ -352,13 +399,13 @@ IDE_INIT2: ; CALL IDE_PRTPREFIX ; PRINT DEVICE PREFIX ; -#IF (IDE8BIT) - PRTS(" 8BIT$") -#ENDIF + LD DE,IDE_STR_8BIT + BIT 1,(IY+IDE_ACC) ; 8 BIT ACCESS? + CALL NZ,WRITESTR ; ; PRINT LBA/NOLBA CALL PC_SPACE ; FORMATTING - BIT 1,(IY+IDE_FLAGS) ; TEST LBA FLAG + BIT 1,(IY+IDE_MED) ; TEST LBA FLAG LD DE,IDE_STR_NO ; POINT TO "NO" STRING CALL Z,WRITESTR ; PRINT "NO" BEFORE "LBA" IF LBA NOT SUPPORTED PRTS("LBA$") ; PRINT "LBA" REGARDLESS @@ -377,8 +424,7 @@ IDE_INIT2: CALL PRTDEC ; PRINT LOW WORD IN DECIMAL (HIGH WORD DISCARDED) PRTS("MB$") ; PRINT SUFFIX ; - XOR A ; SIGNAL SUCCESS - RET ; RETURN WITH A=0, AND Z SET + RET ; ;---------------------------------------------------------------------- ; PROBE FOR IDE HARDWARE @@ -388,8 +434,11 @@ IDE_INIT2: ; IDE_DETECT: ; -#IF (IDEMODE == IDEMODE_DIDE) -#ENDIF +;#IF (IDEMODE == IDEMODE_DIDE) +;#ENDIF +;; +;#IF (IDEMODE == IDEMODE_DIO) +;#ENDIF ; XOR A ; SIGNAL SUCCESS RET ; AND RETURN @@ -451,7 +500,7 @@ IDE_IO: #ENDIF PUSH BC ; SAVE COUNTERS CALL IDE_SELUNIT ; HARDWARE SELECTION OF TARGET UNIT - CALL IDE_CHKDEVICE ; CHECK DEVICE AND CLEAR STATUS + CALL IDE_CHKERR ; CHECK FOR ERR STATUS AND RESET IF SO POP BC ; RESTORE COUNTERS JR NZ,IDE_IO3 ; BAIL OUT ON ERROR IDE_IO1: @@ -492,7 +541,7 @@ IDE_STATUS: IDE_DEVICE: LD D,DIODEV_IDE ; D := DEVICE TYPE LD E,(IY+IDE_DEV) ; E := PHYSICAL DEVICE NUMBER - BIT 0,(IY+IDE_FLAGS) ; TEST CF BIT IN FLAGS + BIT 0,(IY+IDE_MED) ; TEST CF BIT IN FLAGS LD C,%00000000 ; ASSUME NON-REMOVABLE HARD DISK JR Z,IDE_DEVICE1 ; IF Z, WE ARE DONE LD C,%01001000 ; OTHERWISE REMOVABLE COMPACT FLASH @@ -582,11 +631,15 @@ IDE_SETFEAT: PRTS(" SETFEAT$") #ENDIF LD A,(IDE_DRVHD) - OUT (IDE_IO_DRVHD),A + ;OUT (IDE_IO_DRVHD),A + CALL IDE_OUT + .DB IDE_REG_DRVHD DCALL PC_SPACE DCALL PRTHEXBYTE POP AF - OUT (IDE_IO_FEAT),A ; SET THE FEATURE VALUE + ;OUT (IDE_IO_FEAT),A ; SET THE FEATURE VALUE + CALL IDE_OUT + .DB IDE_REG_FEAT DCALL PC_SPACE DCALL PRTHEXBYTE LD A,IDE_CIDE_SETFEAT ; CMD = SETFEAT @@ -601,7 +654,9 @@ IDE_IDENTIFY: PRTS(" IDDEV$") #ENDIF LD A,(IDE_DRVHD) - OUT (IDE_IO_DRVHD),A + ;OUT (IDE_IO_DRVHD),A + CALL IDE_OUT + .DB IDE_REG_DRVHD DCALL PC_SPACE DCALL PRTHEXBYTE LD A,IDE_CIDE_IDDEV @@ -620,7 +675,9 @@ IDE_RDSEC: PRTS(" READ$") #ENDIF LD A,(IDE_DRVHD) - OUT (IDE_IO_DRVHD),A + ;OUT (IDE_IO_DRVHD),A + CALL IDE_OUT + .DB IDE_REG_DRVHD DCALL PC_SPACE DCALL PRTHEXBYTE CALL IDE_SETADDR ; SETUP CYL, TRK, HEAD @@ -640,7 +697,9 @@ IDE_WRSEC: PRTS(" WRITE$") #ENDIF LD A,(IDE_DRVHD) - OUT (IDE_IO_DRVHD),A + ;OUT (IDE_IO_DRVHD),A + CALL IDE_OUT + .DB IDE_REG_DRVHD DCALL PC_SPACE DCALL PRTHEXBYTE CALL IDE_SETADDR ; SETUP CYL, TRK, HEAD @@ -657,33 +716,29 @@ IDE_SETADDR: ; SEND 3 LOWEST BYTES OF LBA IN REVERSE ORDER ; IDE_IO_LBA3 HAS ALREADY BEEN SET ; HSTLBA2-0 --> IDE_IO_LBA2-0 - LD C,IDE_IO_LBA0 + 3 ; STARTING IO PORT (NOT PRE-DEC BELOW) - LD A,IDE_LBA + 2 ; OFFSET OF 3RD BYTE OF LBA IN CFG - CALL LDHLIYA ; HL := IY + A, REG A TRASHED - LD B,3 ; SEND 3 BYTES -IDE_SETADDR1: -; -#IF (IDETRACE >= 3) - LD A,(HL) - CALL PC_SPACE - CALL PRTHEXBYTE -#ENDIF -; - DEC C ; NEXT PORT - OUTD ; SEND NEXT BYTE - JR NZ,IDE_SETADDR1 ; LOOP TILL DONE + LD A,(IY+IDE_LBA+2) + DCALL PC_SPACE + DCALL PRTHEXBYTE + CALL IDE_OUT + .DB IDE_REG_LBA2 ; - ; SEND COUNT OF BLOCKS TO TRANSFER - ; 1 --> IDE_IO_COUNT - LD A,1 ; COUNT VALUE IS 1 BLOCK + LD A,(IY+IDE_LBA+1) + DCALL PC_SPACE + DCALL PRTHEXBYTE + CALL IDE_OUT + .DB IDE_REG_LBA1 ; -#IF (IDETRACE >= 3) + LD A,(IY+IDE_LBA+0) DCALL PC_SPACE DCALL PRTHEXBYTE -#ENDIF + CALL IDE_OUT + .DB IDE_REG_LBA0 ; - DEC C ; PORT := IDE_IO_COUNT - OUT (C),A ; SEND IT + LD A,1 + DCALL PC_SPACE + DCALL PRTHEXBYTE + CALL IDE_OUT + .DB IDE_REG_COUNT ; #IF (DSKYENABLE) CALL IDE_DSKY @@ -702,7 +757,9 @@ IDE_RUNCMD: LD A,(IDE_CMD) ; GET THE COMMAND DCALL PC_SPACE DCALL PRTHEXBYTE - OUT (IDE_IO_CMD),A ; SEND IT (STARTS EXECUTION) + ;OUT (IDE_IO_CMD),A ; SEND IT (STARTS EXECUTION) + CALL IDE_OUT + .DB IDE_REG_CMD #IF (IDETRACE >= 3) PRTS(" -->$") #ENDIF @@ -720,79 +777,116 @@ IDE_GETBUF: #IF (IDETRACE >= 3) PRTS(" GETBUF$") #ENDIF - +; CALL IDE_WAITDRQ ; WAIT FOR BUFFER READY RET NZ ; BAIL OUT IF TIMEOUT - +; LD B,0 - -#IF (IDE8BIT | (IDEMODE == IDEMODE_DIDE)) - LD C,IDE_IO_DATA - INIR - INIR -;X1: -; NOP -; INI -; JR NZ,X1 -;X2: -; NOP -; INI -; JR NZ,X2 -#ELSE - LD C,IDE_IO_DATAHI +; + BIT 1,(IY+IDE_ACC) ; 8 BIT? + JR Z,IDE_GETBUF1 ; IF NOT, DO 16 BIT + CALL IDE_GETBUF8 ; DO 8 BIT + JR IDE_GETBUF2 +; IDE_GETBUF1: - IN A,(IDE_IO_DATALO) ; READ THE LO BYTE - LD (HL),A ; SAVE IN BUFFER - INC HL ; INC BUFFER POINTER - INI ; READ AND SAVE HI BYTE, INC HL, DEC B - JP NZ,IDE_GETBUF1 ; LOOP AS NEEDED -#ENDIF + CALL IDE_GETBUF16 +; +IDE_GETBUF2: CALL IDE_WAITRDY ; PROBLEMS IF THIS IS REMOVED! CALL IDE_GETRES JP NZ,IDE_IOERR RET ; +IDE_GETBUF8: + ; 8 BIT I/O + ;LD C,IDE_IO_DATA + LD C,(IY+IDE_IOBASE) + INIR + INIR + RET +; +IDE_GETBUF16: + ; 16 BIT I/O + ;LD C,IDE_IO_DATAHI + LD D,(IY+IDE_DATALO) + LD E,(IY+IDE_DATAHI) + CALL IDE_GETBUF16A ; GET FIRST 256 BYTES + CALL IDE_GETBUF16A ; GET SECOND 256 BYTES + RET +; +IDE_GETBUF16A: + LD C,D ; PORT FOR LSB + INI ; GET IT, SAVE IT, AND DEC B + LD C,E ; PORT FOR MSB + INI ; GET IT, SAVE IT, AND DEC B + JR NZ,IDE_GETBUF16A ; LOOP TILL COUNTER EXHAUSTED + RET +; ; ; IDE_PUTBUF: #IF (IDETRACE >= 3) - PRTS(" GETBUF$") + PRTS(" PUTBUF$") #ENDIF - +; CALL IDE_WAITDRQ ; WAIT FOR BUFFER READY RET NZ ; BAIL OUT IF TIMEOUT ; - ;LD HL,(IDE_DSKBUF) LD B,0 - -#IF (IDE8BIT | (IDEMODE == IDEMODE_DIDE)) - LD C,IDE_IO_DATA - OTIR - OTIR -#ELSE - LD C,IDE_IO_DATAHI +; + BIT 1,(IY+IDE_ACC) ; 8 BIT? + JR Z,IDE_PUTBUF1 ; IF NOT, DO 16 BIT + CALL IDE_PUTBUF8 ; DO 8 BIT + JR IDE_PUTBUF2 +; IDE_PUTBUF1: - LD A,(HL) ; GET THE LO BYTE AND KEEP IT IN A FOR LATER - INC HL ; BUMP TO NEXT BYTE IN BUFFER - OUTI ; WRITE HI BYTE, INC HL, DEC B - OUT (IDE_IO_DATALO),A ; NOW WRITE THE SAVED LO BYTE TO LO BYTE - JP NZ,IDE_PUTBUF1 ; LOOP AS NEEDED -#ENDIF + CALL IDE_PUTBUF16 +; +IDE_PUTBUF2: CALL IDE_WAITRDY ; PROBLEMS IF THIS IS REMOVED! CALL IDE_GETRES JP NZ,IDE_IOERR RET ; +IDE_PUTBUF8: + ; 8 BIT I/O + ;LD C,IDE_IO_DATA + LD C,(IY+IDE_IOBASE) + OTIR + OTIR + RET +; +IDE_PUTBUF16: + ; 16 BIT I/O + ;LD C,IDE_IO_DATAHI + LD D,(IY+IDE_DATALO) + LD E,(IY+IDE_DATAHI) + CALL IDE_PUTBUF16A ; PUT FIRST 256 BYTES + CALL IDE_PUTBUF16A ; PUT SECOND 256 BYTES + RET +; +IDE_PUTBUF16A: + LD C,D ; PORT FOR LSB + OUTI ; PUT IT AND DEC B + LD C,E ; PORT FOR MSB + OUTI ; PUT IT AND DEC B + JR NZ,IDE_PUTBUF16A ; LOOP TILL COUNTER EXHAUSTED + RET +; ; ; IDE_GETRES: - IN A,(IDE_IO_STAT) ; GET STATUS + ;IN A,(IDE_IO_STAT) ; GET STATUS + CALL IDE_IN + .DB IDE_REG_STAT DCALL PC_SPACE DCALL PRTHEXBYTE AND %00000001 ; ERROR BIT SET? RET Z ; NOPE, RETURN WITH ZF ; - IN A,(IDE_IO_ERR) ; READ ERROR REGISTER + ;IN A,(IDE_IO_ERR) ; READ ERROR REGISTER + CALL IDE_IN + .DB IDE_REG_ERR DCALL PC_SPACE DCALL PRTHEXBYTE OR $FF ; FORCE NZ TO SIGNAL ERROR @@ -805,44 +899,110 @@ IDE_GETRES: ; RESET ALL DEVICES ON BUS ; IDE_RESET: +#IF (IDETRACE >= 3) + CALL IDE_PRTPREFIX + PRTS(" RESET$") +#ENDIF +; +;#IF (IDEMODE == IDEMODE_MK4) +; + LD A,(IY+IDE_MODE) ; GET MODE + CP IDEMODE_MK4 ; MK4? + JR NZ,IDE_RESET1 ; IF NOT, BYPASS ; -#IF (IDEMODE == IDEMODE_MK4) ; USE HARDWARE RESET LINE +#IF (IDETRACE >= 3) + PRTS(" HARD$") +#ENDIF LD A,$80 ; HIGH BIT OF XAR IS IDE RESET - OUT (IDE_XAR),A + ;OUT (IDE_IO_XAR),A + CALL IDE_OUT + .DB IDE_REG_XAR LD DE,2 ; DELAY 32US (SPEC IS >= 25US) CALL VDELAY XOR A ; CLEAR RESET BIT - OUT (IDE_XAR),A -#ENDIF - -#IF ((IDEMODE == IDEMODE_RC) | (IDEMODE == IDEMODE_SMB)) + ;OUT (IDE_IO_XAR),A + CALL IDE_OUT + .DB IDE_REG_XAR +; +IDE_RESET1: +; +;#ENDIF +; +;#IF (IDEMODE == IDEMODE_RC) +; + LD A,(IY+IDE_MODE) ; GET MODE + CP IDEMODE_RC ; RC2014? + JR NZ,IDE_RESET2 ; IF NOT, BYPASS +; ; RC2014 CANNOT ADDRESS THE DEVICE CONTROL PORT AND ; HAS NO WAY TO PERFORM A HARD RESET FROM SOFTWARE, ; SO FAKE IT BY SETTING THE REGISTERS TO THE SAME ; VALUES THAT A RESET WOULD CAUSE. +#IF (IDETRACE >= 3) + PRTS(" FAKE$") +#ENDIF XOR A - OUT (IDE_IO_CYLLO),A - OUT (IDE_IO_CYLHI),A + ;OUT (IDE_IO_CYLLO),A + CALL IDE_OUT + .DB IDE_REG_CYLLO + ;OUT (IDE_IO_CYLHI),A + CALL IDE_OUT + .DB IDE_REG_CYLHI INC A - OUT (IDE_IO_COUNT),A - OUT (IDE_IO_SECT),A -#ENDIF - -#IF ((IDEMODE != IDEMODE_MK4) & (IDEMODE != IDEMODE_RC) & (IDEMODE != IDEMODE_SMB)) + ;OUT (IDE_IO_COUNT),A + CALL IDE_OUT + .DB IDE_REG_COUNT + ;OUT (IDE_IO_SECT),A + CALL IDE_OUT + .DB IDE_REG_SECT +; +IDE_RESET2: +; +;#ENDIF +; +;#IF ((IDEMODE != IDEMODE_MK4) & (IDEMODE != IDEMODE_RC)) +; + LD A,(IY+IDE_MODE) ; GET MODE + CP IDEMODE_MK4 ; MK4? + JR Z,IDE_RESET3 ; IF SO, BYPASS + CP IDEMODE_RC ; RC2014? + JR Z,IDE_RESET3 ; IF SO, BYPASS +; ; INITIATE SOFT RESET - LD A,%00001110 ; NO INTERRUPTS, ASSERT RESET BOTH DRIVES - OUT (IDE_IO_CTRL),A +#IF (IDETRACE >= 3) + PRTS(" SOFT$") #ENDIF + LD A,%00001110 ; NO INTERRUPTS, ASSERT RESET BOTH DRIVES + ;OUT (IDE_IO_CTRL),A + CALL IDE_OUT + .DB IDE_REG_CTRL +; +IDE_RESET3: +; +;#ENDIF ; LD DE,2 ; DELAY 32US (SPEC IS >= 25US) CALL VDELAY ; -#IF ((IDEMODE != IDEMODE_RC) & (IDEMODE != IDEMODE_SMB)) +;#IF (IDEMODE != IDEMODE_RC) +; + LD A,(IY+IDE_MODE) ; GET MODE + CP IDEMODE_RC ; RC2014? + JR Z,IDE_RESET4 ; IF SO, BYPASS +; ; CONFIGURE OPERATION AND END SOFT RESET - LD A,%00001010 ; NO INTERRUPTS, DEASSERT RESET - OUT (IDE_IO_CTRL),A ; PUSH TO REGISTER +#IF (IDETRACE >= 3) + PRTS(" CONFIG$") #ENDIF + LD A,%00001010 ; NO INTERRUPTS, DEASSERT RESET + ;OUT (IDE_IO_CTRL),A ; PUSH TO REGISTER + CALL IDE_OUT + .DB IDE_REG_CTRL +; +IDE_RESET4: +; +;#ENDIF ; ; SPEC ALLOWS UP TO 450MS FOR DEVICES TO ASSERT THEIR PRESENCE ; VIA -DASP. I ENCOUNTER PROBLEMS LATER ON IF I DON'T WAIT HERE @@ -854,28 +1014,16 @@ IDE_RESET: LD DE,150000/16 ; ~???MS CALL VDELAY ; - ;; CLEAR OUT ALL DATA (FOR ALL UNITS) - ;LD HL,IDE_UDATA - ;LD BC,IDE_UDLEN - ;XOR A - ;CALL FILL -; - ;LD A,(IDE_UNIT) ; GET THE CURRENT UNIT SELECTION - ;PUSH AF ; AND SAVE IT - PUSH IY ; SAVE CURRENT DEVICE CFG PTR - - ; PROBE / INITIALIZE ALL UNITS - LD B,IDE_DEVCNT ; NUMBER OF UNITS TO TRY - LD IY,IDE_CFGTBL ; START OF CFG TABLE -IDE_RESET1: - PUSH BC ; SAVE LOOP CONTROL - CALL IDE_INITUNIT ; PROBE/INIT UNIT - LD BC,IDE_CFGSIZ ; SIZE OF CFG ENTRY - ADD IY,BC ; BUMP IY TO NEXT ENTRY - POP BC ; RESTORE BC - DJNZ IDE_RESET1 ; LOOP AS NEEDED -; - POP IY ; RECOVER DEVICE CFG PTR + ; INITIALIZE THE INDIVIDUAL UNITS (MASTER AND SLAVE). + ; BASED ON TESTING, IT APPEARS THAT THE MASTER UNIT MUST + ; BE DONE FIRST OR THIS BEHAVES BADLY. + PUSH IY ; SAVE CFG PTR + BIT 0,(IY+IDE_ACC) ; MASTER? + CALL Z,IDE_GOPARTNER ; IF NOT, SWITCH TO MASTER + CALL IDE_INITUNIT ; INIT CURRENT UNIT + CALL IDE_GOPARTNER ; POINT TO SLAVE + CALL IDE_INITUNIT ; INIT PARTNER UNIT + POP IY ; RECOVER ORIG CFG PTR ; XOR A ; SIGNAL SUCCESS RET ; AND DONE @@ -886,19 +1034,14 @@ IDE_INITUNIT: CALL IDE_SELUNIT ; SELECT UNIT RET NZ ; ABORT IF ERROR - ;LD HL,IDE_TIMEOUT ; POINT TO TIMEOUT - ;LD (HL),IDE_TOFAST ; USE FAST TIMEOUT DURING INIT - LD HL,IDE_TIMEOUT ; POINT TO TIMEOUT LD (HL),IDE_TONORM ; SET NORMAL TIMEOUT CALL IDE_PROBE ; DO PROBE - CALL Z,IDE_INITDEV ; IF FOUND, ATTEMPT TO INIT DEVICE + RET NZ ; JUST RETURN IF NOTHING THERE - ;LD HL,IDE_TIMEOUT ; POINT TO TIMEOUT - ;LD (HL),IDE_TONORM ; BACK TO NORMAL TIMEOUT - - RET + CALL IDE_INITDEV ; IF FOUND, ATTEMPT TO INIT DEVICE + RET ; DONE ; ; TAKE ANY ACTIONS REQUIRED TO SELECT DESIRED PHYSICAL UNIT ; UNIT IS SPECIFIED IN IDE_UNIT @@ -910,22 +1053,16 @@ IDE_SELUNIT: PRTS(" SELUNIT$") #ENDIF ; -#IF (IDEMODE == IDEMODE_DIDE) - ; SELECT PRIMARY/SECONDARY INTERFACE FOR DIDE HARDWARE -#ENDIF - PUSH HL ; SAVE HL, IT IS DESTROYED BELOW - PUSH IY - POP BC - LD A,(IY+IDE_DEV) ; GET DEVICE - AND $01 ; LS BIT DETERMINES MASTER/SLAVE - LD HL,IDE_DRVSEL - CALL ADDHLA - LD A,(HL) ; LOAD DRIVE/HEAD VALUE - POP HL ; RECOVER HL + BIT 0,(IY+IDE_ACC) ; MASTER? + JR Z,IDE_SELUNIT1 ; HANDLE SLAVE + LD A,IDE_DRVMASTER ; MASTER + JR IDE_SELUNIT2 +IDE_SELUNIT1: + LD A,IDE_DRVSLAVE ; SLAVE +IDE_SELUNIT2: LD (IDE_DRVHD),A ; SAVE IT -; - XOR A ; SIGNAL SUCCESS - RET ; AND DONE + XOR A ; SUCCESS + RET ; ; ; @@ -936,14 +1073,18 @@ IDE_PROBE: #ENDIF ; LD A,(IDE_DRVHD) - OUT (IDE_IO_DRVHD),A + ;OUT (IDE_IO_DRVHD),A + CALL IDE_OUT + .DB IDE_REG_DRVHD DCALL PC_SPACE DCALL PRTHEXBYTE CALL DELAY ; DELAY ~16US ; - LD C,IDE_IO_STAT - IN A,(C) + ;LD C,IDE_IO_STAT + ;IN A,(C) + CALL IDE_IN + .DB IDE_REG_STAT DCALL PC_SPACE DCALL PRTHEXBYTE CP $FF @@ -962,7 +1103,9 @@ IDE_PROBE0: DCALL IDE_REGDUMP ; ; CHECK STATUS - IN A,(IDE_IO_STAT) ; GET STATUS + ;IN A,(IDE_IO_STAT) ; GET STATUS + CALL IDE_IN + .DB IDE_REG_STAT DCALL PC_SPACE DCALL PRTHEXBYTE ; IF DEBUG, PRINT STATUS OR A ; SET FLAGS TO TEST FOR ZERO @@ -970,22 +1113,30 @@ IDE_PROBE0: ; ; CHECK SIGNATURE DCALL PC_SPACE - IN A,(IDE_IO_COUNT) + ;IN A,(IDE_IO_COUNT) + CALL IDE_IN + .DB IDE_REG_COUNT DCALL PRTHEXBYTE CP $01 JP NZ,IDE_NOMEDIA DCALL PC_SPACE - IN A,(IDE_IO_SECT) + ;IN A,(IDE_IO_SECT) + CALL IDE_IN + .DB IDE_REG_SECT DCALL PRTHEXBYTE CP $01 JP NZ,IDE_NOMEDIA DCALL PC_SPACE - IN A,(IDE_IO_CYLLO) + ;IN A,(IDE_IO_CYLLO) + CALL IDE_IN + .DB IDE_REG_CYLLO DCALL PRTHEXBYTE CP $00 JP NZ,IDE_NOMEDIA DCALL PC_SPACE - IN A,(IDE_IO_CYLHI) + ;IN A,(IDE_IO_CYLHI) + CALL IDE_IN + .DB IDE_REG_CYLHI DCALL PRTHEXBYTE CP $00 JP NZ,IDE_NOMEDIA @@ -1005,33 +1156,21 @@ IDE_INITDEV: OR A ; SET FLAGS JP Z,IDE_NOMEDIA ; EXIT SETTING NO MEDIA STATUS ; - ; CLEAR OUT UNIT SPECIFIC DATA, BUT PRESERVE THE EXISTING - ; VALUE OF THE UNIT TYPE WHICH WAS ESTABLISHED BY THE DEVICE - ; PROBES WHEN THE IDE BUS WAS RESET - ;PUSH AF ; SAVE UNIT TYPE VALUE FROM ABOVE - ;PUSH HL ; SAVE UNIT TYPE FIELD POINTER - ;IDE_DPTR(0) ; SET HL TO START OF UNIT DATA - ;LD BC,IDE_UDLEN - ;XOR A - ;CALL FILL - ;POP HL ; RECOVER UNIT TYPE FIELD POINTER - ;POP AF ; RECOVER UNIT TYPE VALUE - ;LD (HL),A ; AND PUT IT BACK -; -#IF (IDE8BIT) + BIT 1,(IY+IDE_ACC) ; 8 BIT ACCESS? + JR Z,IDE_INITDEV0 ; NO, DO 16 BIT INIT LD A,IDE_FEAT_ENABLE8BIT ; FEATURE VALUE = ENABLE 8-BIT PIO -#ELSE - LD A,IDE_FEAT_DISABLE8BIT ; FEATURE VALUE = DISABLE 8-BIT PIO -#ENDIF - CALL IDE_SETFEAT ; SET FEATURE - -#IF (IDE8BIT) + RET NZ ; BAIL OUT ON ERROR + JR IDE_INITDEV00 ; CONTINUE +; +IDE_INITDEV0: ; "REAL" IDE DRIVES MAY NOT ACCEPT THE DISABLE8BIT FEATURE COMMAND, ; SO IT IS ONLY AN ERROR IF WE ARE ATTEMPTING TO ENABLE8BIT. - ; CREDIT TO ED BRINDLEY FOR THIS CORRECTION. - RET NZ ; BAIL OUT ON ERROR -#ENDIF + ; CREDIT TO ED BRINDLEY FOR THIS CORRECTION. SO ERROR RETURN IGNORED HERE. + LD A,IDE_FEAT_DISABLE8BIT ; FEATURE VALUE = ENABLE 8-BIT PIO + CALL IDE_SETFEAT ; SET FEATURE, IGNORE ERRORS +; +IDE_INITDEV00: ; CALL IDE_IDENTIFY ; EXECUTE IDENTIFY COMMAND RET NZ ; BAIL OUT ON ERROR @@ -1040,7 +1179,7 @@ IDE_INITDEV: DCALL DUMP_BUFFER ; DUMP IT IF DEBUGGING ; XOR A - LD (IY+IDE_FLAGS),0 ; CLEAR FLAGS + LD (IY+IDE_MED),0 ; CLEAR FLAGS ; DETERMINE IF CF DEVICE LD HL,HB_WRKBUF ; FIRST WORD OF IDENTIFY DATA HAS CF FLAG @@ -1051,14 +1190,14 @@ IDE_INITDEV: LD A,$84 ; SECOND BYTE OF MARKER IS $84 CP (HL) ; COMPARE JR NZ,IDE_INITDEV1 ; IF NOT MATCH, NOT CF - SET 0,(IY+IDE_FLAGS) ; SET FLAGS BIT FOR CF MEDIA + SET 0,(IY+IDE_MED) ; SET FLAGS BIT FOR CF MEDIA ; IDE_INITDEV1: ; DETERMINE IF LBA CAPABLE LD A,(HB_WRKBUF+98+1) ; GET BYTE WITH LBA BIT FROM BUFFER BIT 1,A ; CHECK THE LBA BIT JR Z,IDE_INITDEV2 ; NOT SET, BYPASS - SET 1,(IY+IDE_FLAGS) ; SET FLAGS BIT FOR LBA + SET 1,(IY+IDE_MED) ; SET FLAGS BIT FOR LBA ; IDE_INITDEV2: ; GET DEVICE CAPACITY AND SAVE IT @@ -1078,15 +1217,24 @@ IDE_INITDEV2: ; RET ; RETURN, A=0, Z SET ; +; SWITCH IY POINTER FROM CURRENT UNIT CFG TO PARTNER UNIT CFG +; +IDE_GOPARTNER: + PUSH HL ; SAVE HL + LD L,(IY+IDE_PARTNER) ; GET PARTNER ENTRY + LD H,(IY+IDE_PARTNER+1) ; ... + PUSH HL ; MOVE HL + POP IY ; ... TO IY + POP HL ; RESTORE INCOMING HL + RET ; AND DONE ; +; CHECK CURRENT DEVICE FOR ERROR STATUS AND ATTEMPT TO RECOVER +; VIA RESET IF DEVICE IS IN ERROR. ; -IDE_CHKDEVICE: +IDE_CHKERR: LD A,(IY+IDE_STAT) ; GET STATUS OR A ; SET FLAGS - RET Z ; RETURN IF ALL IS WELL -; - ; ATTEMPT TO REINITIALIZE HERE??? - JP IDE_ERR + CALL NZ,IDE_RESET ; IF ERROR STATUS, RESET BUS RET ; ; @@ -1097,7 +1245,9 @@ IDE_WAITRDY: IDE_WAITRDY1: LD DE,(IDE_TOSCALER) ; CPU SPPED SCALER TO INNER LOOP VAR IDE_WAITRDY2: - IN A,(IDE_IO_STAT) ; READ STATUS + ;IN A,(IDE_IO_STAT) ; READ STATUS + CALL IDE_IN + .DB IDE_REG_STAT LD C,A ; SAVE IT AND %11000000 ; ISOLATE BUSY AND RDY BITS XOR %01000000 ; WE WANT BUSY(7) TO BE 0 AND RDY(6) TO BE 1 @@ -1117,7 +1267,9 @@ IDE_WAITDRQ: IDE_WAITDRQ1: LD DE,(IDE_TOSCALER) ; CPU SPPED SCALER TO INNER LOOP VAR IDE_WAITDRQ2: - IN A,(IDE_IO_STAT) ; WAIT FOR DRIVE'S 512 BYTE READ BUFFER + ;IN A,(IDE_IO_STAT) ; WAIT FOR DRIVE'S 512 BYTE READ BUFFER + CALL IDE_IN + .DB IDE_REG_STAT LD C,A ; SAVE IT AND %10001000 ; TO FILL (OR READY TO FILL) XOR %00001000 @@ -1137,7 +1289,9 @@ IDE_WAITBSY: IDE_WAITBSY1: LD DE,(IDE_TOSCALER) ; CPU SPPED SCALER TO INNER LOOP VAR IDE_WAITBSY2: - IN A,(IDE_IO_STAT) ; WAIT FOR DRIVE'S 512 BYTE READ BUFFER ; 11TS + ;IN A,(IDE_IO_STAT) ; WAIT FOR DRIVE'S 512 BYTE READ BUFFER ; 11TS + CALL IDE_IN ; 17TS + ???TS + .DB IDE_REG_STAT ; 0TS LD C,A ; SAVE IT ; 4TS AND %10000000 ; TO FILL (OR READY TO FILL) ; 7TS RET Z ; 5TS @@ -1146,7 +1300,39 @@ IDE_WAITBSY2: OR E ; 4TS JR NZ,IDE_WAITBSY2 ; 12TS DJNZ IDE_WAITBSY1 ; ----- - JP IDE_BSYTO ; EXIT WITH BSYTO ERR ; 52TS + JP IDE_BSYTO ; EXIT WITH BSYTO ERR ; ??TS +; +; READ A VALUE FROM THE DEVICE POINTED TO BY IY AND RETURN IT IN A +; +IDE_IN: + EX (SP),HL ; GET PARM POINTER + PUSH BC + LD A,(HL) + INC HL + LD C,(IY+IDE_IOBASE) + ADD A,C + LD C,A + IN A,(C) + POP BC + EX (SP),HL ; RESTORE STACK + RET +; +; OUTPUT VALUE IN A TO THE DEVICE POINTED TO BY IY +; +IDE_OUT: + EX (SP),HL ; GET PARM POINTER + PUSH BC + PUSH AF + LD A,(HL) + INC HL + LD C,(IY+IDE_IOBASE) + ADD A,C + LD C,A + POP AF + OUT (C),A + POP BC + EX (SP),HL ; RESTORE STACK + RET ; ;============================================================================= ; ERROR HANDLING AND DIAGNOSTICS @@ -1252,7 +1438,10 @@ IDE_REGDUMP: PUSH BC CALL PC_SPACE CALL PC_LBKT - LD C,IDE_IO_CMD + ;LD C,IDE_IO_CMD + LD A,(IY+IDE_IOBASE) + ADD A,IDE_REG_CMD + LD C,A LD B,7 IDE_REGDUMP1: IN A,(C) @@ -1284,16 +1473,20 @@ IDE_PRTPREFIX: #IF (DSKYENABLE) IDE_DSKY: LD HL,DSKY_HEXBUF ; POINT TO DSKY BUFFER - IN A,(IDE_IO_DRVHD) ; GET DRIVE/HEAD + CALL IDE_IN + .DB IDE_REG_DRVHD LD (HL),A ; SAVE IN BUFFER INC HL ; INCREMENT BUFFER POINTER - IN A,(IDE_IO_CYLHI) ; GET DRIVE/HEAD + CALL IDE_IN + .DB IDE_REG_CYLHI LD (HL),A ; SAVE IN BUFFER INC HL ; INCREMENT BUFFER POINTER - IN A,(IDE_IO_CYLLO) ; GET DRIVE/HEAD + CALL IDE_IN + .DB IDE_REG_CYLLO LD (HL),A ; SAVE IN BUFFER INC HL ; INCREMENT BUFFER POINTER - IN A,(IDE_IO_SECT) ; GET DRIVE/HEAD + CALL IDE_IN + .DB IDE_REG_SECT LD (HL),A ; SAVE IN BUFFER CALL DSKY_HEXOUT ; SEND IT TO DSKY RET @@ -1314,6 +1507,13 @@ IDE_STR_STBSYTO .TEXT "BUSY TIMEOUT$" IDE_STR_STUNK .TEXT "UNKNOWN ERROR$" ; IDE_STR_NO .TEXT "NO$" +IDE_STR_NOHW .TEXT "NOT PRESENT$" +IDE_STR_8BIT .TEXT " 8-BIT$" +; +IDE_STR_MODE_DIO .TEXT "DIO$" +IDE_STR_MODE_DIDE .TEXT "DIDE$" +IDE_STR_MODE_MK4 .TEXT "MK4$" +IDE_STR_MODE_RC .TEXT "RC$" ; ;============================================================================= ; DATA STORAGE @@ -1327,3 +1527,5 @@ IDE_IOFNADR .DW 0 ; PENDING IO FUNCTION ADDRESS IDE_DRVHD .DB 0 ; CURRENT DRIVE/HEAD MASK ; IDE_DSKBUF .DW 0 ; ACTIVE DISK BUFFER +; +IDE_DEVNUM .DB 0 ; TEMP DEVICE NUM USED DURING INIT diff --git a/Source/HBIOS/imgpad0.asm b/Source/HBIOS/imgpad0.asm index e2822918..2aa54841 100644 --- a/Source/HBIOS/imgpad0.asm +++ b/Source/HBIOS/imgpad0.asm @@ -1,6 +1,6 @@ #INCLUDE "std.asm" ; -SLACK .EQU ($8000-BAS_SIZ-TBC_SIZ-FTH_SIZ) +SLACK .EQU ($8000-BAS_SIZ-TBC_SIZ-FTH_SIZ-GAM_SIZ-USR_SIZ) .FILL SLACK,00H ; MON_STACK .EQU $ diff --git a/Source/HBIOS/nascom.asm b/Source/HBIOS/nascom.asm index 4cc8f66d..e0447606 100644 --- a/Source/HBIOS/nascom.asm +++ b/Source/HBIOS/nascom.asm @@ -302,26 +302,26 @@ WORDS: .BYTE 'E'+80H,"ND" ; PEND: .BYTE 'R'+80H,"EM" ; REM: .BYTE 'S'+80H,"TOP" ; STOP: .BYTE 'O'+80H,"UT" ; POUT: - .BYTE 'O'+80H,"N" : ON: + .BYTE 'O'+80H,"N" ; ON: .BYTE 'N'+80H,"ULL" ; NULL: - .BYTE 'W'+80H,"AIT" : WAIT: - .BYTE 'D'+80H,"EF" : DEF: - .BYTE 'P'+80H,"OKE" : POKE: - .BYTE 'D'+80H,"OKE" : DOKE: - .BYTE 'S'+80H,"CREEN" : REM: NOT IMPLEMENTED - .BYTE 'L'+80H,"INES" : LINES - .BYTE 'C'+80H,"LS" : CLS: - .BYTE 'W'+80H,"IDTH" : WIDTH: - .BYTE 'B'+80H,"YE" : MONITR: - .BYTE 'S'+80H,"ET" : PSET: + .BYTE 'W'+80H,"AIT" ; WAIT: + .BYTE 'D'+80H,"EF" ; DEF: + .BYTE 'P'+80H,"OKE" ; POKE: + .BYTE 'D'+80H,"OKE" ; DOKE: + .BYTE 'S'+80H,"CREEN" ; REM: NOT IMPLEMENTED + .BYTE 'L'+80H,"INES" ; LINES + .BYTE 'C'+80H,"LS" ; CLS: + .BYTE 'W'+80H,"IDTH" ; WIDTH: + .BYTE 'B'+80H,"YE" ; MONITR: + .BYTE 'S'+80H,"ET" ; PSET: .BYTE 'R'+80H,"ESET" ; RESET: - .BYTE 'P'+80H,"RINT" : PRINT: - .BYTE 'C'+80H,"ONT" : CONT: - .BYTE 'L'+80H,"IST" : LIST: - .BYTE 'C'+80H,"LEAR" : CLEAR: - .BYTE 'P'+80H,"LAY" : PLAY: WAS CLOAD - .BYTE 'C'+80H,"SAVE" : REM: NOT IMPLEMENTED - .BYTE 'N'+80H,"EW" : NEW + .BYTE 'P'+80H,"RINT" ; PRINT: + .BYTE 'C'+80H,"ONT" ; CONT: + .BYTE 'L'+80H,"IST" ; LIST: + .BYTE 'C'+80H,"LEAR" ; CLEAR: + .BYTE 'P'+80H,"LAY" ; PLAY: WAS CLOAD + .BYTE 'C'+80H,"SAVE" ; REM: NOT IMPLEMENTED + .BYTE 'N'+80H,"EW" ; NEW .BYTE 'T'+80H,"AB(" .BYTE 'T'+80H,"O" @@ -481,7 +481,7 @@ ERRORS: .BYTE "NF" ; NEXT without FOR .BYTE "DD" ; Re-DIMensioned array .BYTE "/0" ; Division by zero .BYTE "ID" ; Illegal direct - .BYTE "TM" ; Type mis-match + .BYTE "TM" ; Type mismatch .BYTE "OS" ; Out of string space .BYTE "LS" ; String too long .BYTE "ST" ; String formula too complex @@ -503,11 +503,11 @@ ERRORS: .BYTE "NEXT without FOR",0 .BYTE "Re-DIMensioned array",0 .BYTE "Division by zero",0 .BYTE "Illegal direct",0 - .BYTE "Type mis-match",0 + .BYTE "Type mismatch",0 .BYTE "Out of string space",0 .BYTE "String too long",0 .BYTE "String formula too complex",0 - .BYTE "Can't CONTinue",0 + .BYTE "Can",$27,"t CONTinue",0 .BYTE "Undefined FN function",0 .BYTE "Missing operand",0 .BYTE "HEX",0 diff --git a/Source/HBIOS/ppide.asm b/Source/HBIOS/ppide.asm index 478739de..3cb48b86 100644 --- a/Source/HBIOS/ppide.asm +++ b/Source/HBIOS/ppide.asm @@ -4,35 +4,16 @@ ;============================================================================= ; ; TODO: -; - IMPLEMENT PPIDE_INITDEVICE -; - IMPLEMENT INTELLIGENT RESET, CHECK IF DEVICE IS ACTUALLY BROKEN BEFORE RESET ; - FIX SCALER CONSTANT +; - GOPARTNER NEEDS TO HANDLE "NO PARTNER" CONDITION ; -; -#IF (PPIDEMODE == PPIDEMODE_SBC) -PPIDE_IO_BASE .EQU $60 -#ENDIF -; -#IF ((PPIDEMODE == PPIDEMODE_DIO3) | (PPIDEMODE == PPIDEMODE_RC)) -PPIDE_IO_BASE .EQU $20 -#ENDIF -; -#IF (PPIDEMODE == PPIDEMODE_MFP) -PPIDE_IO_BASE .EQU $44 -#ENDIF -; -#IF (PPIDEMODE == PPIDEMODE_N8) -PPIDE_IO_BASE .EQU $80 -#ENDIF -; -#IF (PPIDEMODE == PPIDEMODE_DYNO) -PPIDE_IO_BASE .EQU $4C -#ENDIF -; -PPIDE_IO_DATALO .EQU PPIDE_IO_BASE + 0 ; IDE DATA BUS LSB (8255 PORT A) -PPIDE_IO_DATAHI .EQU PPIDE_IO_BASE + 1 ; IDE DATA BUS MSB (8255 PORT B) -PPIDE_IO_CTL .EQU PPIDE_IO_BASE + 2 ; IDE ADDRESS BUS AND CONTROL SIGNALS (8255 PORT C) -PPIDE_IO_PPI .EQU PPIDE_IO_BASE + 3 ; 8255 CONTROL PORT +; NOTES: +; - WELL KNOWN PPIDE PORT ADDRESSES: +; $60 - SBC/ZETA ONBOARD PPI +; $20 - ECB DISKIO3, RC FAMILY +; $44 - ECB MULTI-FUNCTION PIC +; $80 - N8 ONBOARD PPI +; $4C - DYNO ONBOARD PPI ; ; THE CONTROL PORT OF THE 8255 IS PROGRAMMED AS NEEDED TO READ OR WRITE ; DATA ON THE IDE BUS. PORT C OF THE 8255 IS ALWAYS IN OUTPUT MODE BECAUSE @@ -49,8 +30,8 @@ PPIDE_DIR_WRITE .EQU %10000000 ; IDE BUS DATA OUTPUT MODE PPIDE_CTL_DA0 .EQU %00000001 ; DRIVE ADDRESS BUS - BIT 0 (DA0) PPIDE_CTL_DA1 .EQU %00000010 ; DRIVE ADDRESS BUS - BIT 1 (DA1) PPIDE_CTL_DA2 .EQU %00000100 ; DRIVE ADDRESS BUS - BIT 2 (DA2) -PPIDE_CTL_CS1FX .EQU %00001000 ; DRIVE CHIP SELECT 0 (ACTIVE LOW, INVERTED) -PPIDE_CTL_CS3FX .EQU %00010000 ; DRIVE CHIP SELECT 1 (ACTIVE LOW, INVERTED) +PPIDE_CTL_CS1 .EQU %00001000 ; DRIVE CHIP SELECT 0 (ACTIVE LOW, INVERTED) +PPIDE_CTL_CS3 .EQU %00010000 ; DRIVE CHIP SELECT 1 (ACTIVE LOW, INVERTED) PPIDE_CTL_DIOW .EQU %00100000 ; DRIVE I/O WRITE (ACTIVE LOW, INVERTED) PPIDE_CTL_DIOR .EQU %01000000 ; DRIVE I/O READ (ACTIVE LOW, INVERTED) PPIDE_CTL_RESET .EQU %10000000 ; DRIVE RESET (ACTIVE LOW, INVERTED) @@ -85,7 +66,7 @@ PPIDE_CTL_RESET .EQU %10000000 ; DRIVE RESET (ACTIVE LOW, INVERTED) ; | PPIDE_REG_STAT | 0x07 | R | STATUS REGISTER | ; | PPIDE_REG_CMD | 0x07 | W | COMMAND REGISTER (EXECUTE) | ; +-----------------------+-------+-------+-------------------------------+ -; * LBA0-4 ARE ALTERNATE DEFINITIONS OF SECT, CYL, AND DRVHD PORTS +; * LBA0-3 ARE ALTERNATE DEFINITIONS OF SECT, CYL, AND DRVHD PORTS ; ; === STATUS REGISTER === ; @@ -141,39 +122,31 @@ PPIDE_CTL_RESET .EQU %10000000 ; DRIVE RESET (ACTIVE LOW, INVERTED) ; SRST: SOFTWARE RESET ; ~IEN: INTERRUPT ENABLE ; -; CONTROL VALUES TO USE WHEN ACCESSING THE VARIOUS IDE DEVICE REGISTERS -; -PPIDE_REG_DATA .EQU PPIDE_CTL_CS1FX | $00 ; DATA INPUT/OUTPUT (R/W) -PPIDE_REG_ERR .EQU PPIDE_CTL_CS1FX | $01 ; ERROR REGISTER (R) -PPIDE_REG_FEAT .EQU PPIDE_CTL_CS1FX | $01 ; FEATURES REGISTER (W) -PPIDE_REG_COUNT .EQU PPIDE_CTL_CS1FX | $02 ; SECTOR COUNT REGISTER (R/W) -PPIDE_REG_SECT .EQU PPIDE_CTL_CS1FX | $03 ; SECTOR NUMBER REGISTER (R/W) -PPIDE_REG_CYLLO .EQU PPIDE_CTL_CS1FX | $04 ; CYLINDER NUM REGISTER (LSB) (R/W) -PPIDE_REG_CYLHI .EQU PPIDE_CTL_CS1FX | $05 ; CYLINDER NUM REGISTER (MSB) (R/W) -PPIDE_REG_DRVHD .EQU PPIDE_CTL_CS1FX | $06 ; DRIVE/HEAD REGISTER (R/W) -PPIDE_REG_LBA0 .EQU PPIDE_CTL_CS1FX | $03 ; LBA BYTE 0 (BITS 0-7) (R/W) -PPIDE_REG_LBA1 .EQU PPIDE_CTL_CS1FX | $04 ; LBA BYTE 1 (BITS 8-15) (R/W) -PPIDE_REG_LBA2 .EQU PPIDE_CTL_CS1FX | $05 ; LBA BYTE 2 (BITS 16-23) (R/W) -PPIDE_REG_LBA3 .EQU PPIDE_CTL_CS1FX | $06 ; LBA BYTE 3 (BITS 24-27) (R/W) -PPIDE_REG_STAT .EQU PPIDE_CTL_CS1FX | $07 ; STATUS REGISTER (R) -PPIDE_REG_CMD .EQU PPIDE_CTL_CS1FX | $07 ; COMMAND REGISTER (EXECUTE) (W) -PPIDE_REG_ALTSTAT .EQU PPIDE_CTL_CS3FX | $06 ; ALTERNATE STATUS REGISTER (R) -PPIDE_REG_CTRL .EQU PPIDE_CTL_CS3FX | $06 ; DEVICE CONTROL REGISTER (W) -PPIDE_REG_DRVADR .EQU PPIDE_CTL_CS3FX | $07 ; DRIVE ADDRESS REGISTER (R) -; #IF (PPIDETRACE >= 3) #DEFINE DCALL CALL #ELSE #DEFINE DCALL \; #ENDIF ; -; UNIT MAPPING IS AS FOLLOWS: -; PPIDE0: PRIMARY MASTER -; PPIDE1: PRIMARY SLAVE -; PPIDE2: SECONDARY MASTER -; PPIDE3: SECONDARY SLAVE +; CONTROL VALUES TO USE WHEN ACCESSING THE VARIOUS IDE DEVICE REGISTERS ; -PPIDE_DEVCNT .EQU 2 ; ASSUME ONLY PRIMARY INTERFACE +PPIDE_REG_DATA .EQU PPIDE_CTL_CS1 | $00 ; DATA INPUT/OUTPUT (R/W) +PPIDE_REG_ERR .EQU PPIDE_CTL_CS1 | $01 ; ERROR REGISTER (R) +PPIDE_REG_FEAT .EQU PPIDE_CTL_CS1 | $01 ; FEATURES REGISTER (W) +PPIDE_REG_COUNT .EQU PPIDE_CTL_CS1 | $02 ; SECTOR COUNT REGISTER (R/W) +PPIDE_REG_SECT .EQU PPIDE_CTL_CS1 | $03 ; SECTOR NUMBER REGISTER (R/W) +PPIDE_REG_CYLLO .EQU PPIDE_CTL_CS1 | $04 ; CYLINDER NUM REGISTER (LSB) (R/W) +PPIDE_REG_CYLHI .EQU PPIDE_CTL_CS1 | $05 ; CYLINDER NUM REGISTER (MSB) (R/W) +PPIDE_REG_DRVHD .EQU PPIDE_CTL_CS1 | $06 ; DRIVE/HEAD REGISTER (R/W) +PPIDE_REG_LBA0 .EQU PPIDE_CTL_CS1 | $03 ; LBA BYTE 0 (BITS 0-7) (R/W) +PPIDE_REG_LBA1 .EQU PPIDE_CTL_CS1 | $04 ; LBA BYTE 1 (BITS 8-15) (R/W) +PPIDE_REG_LBA2 .EQU PPIDE_CTL_CS1 | $05 ; LBA BYTE 2 (BITS 16-23) (R/W) +PPIDE_REG_LBA3 .EQU PPIDE_CTL_CS1 | $06 ; LBA BYTE 3 (BITS 24-27) (R/W) +PPIDE_REG_STAT .EQU PPIDE_CTL_CS1 | $07 ; STATUS REGISTER (R) +PPIDE_REG_CMD .EQU PPIDE_CTL_CS1 | $07 ; COMMAND REGISTER (EXECUTE) (W) +PPIDE_REG_ALTSTAT .EQU PPIDE_CTL_CS3 | $06 ; ALTERNATE STATUS REGISTER (R) +PPIDE_REG_CTRL .EQU PPIDE_CTL_CS3 | $06 ; DEVICE CONTROL REGISTER (W) +PPIDE_REG_DRVADR .EQU PPIDE_CTL_CS3 | $07 ; DRIVE ADDRESS REGISTER (R) ; ; COMMAND BYTES ; @@ -194,7 +167,7 @@ PPIDE_TYPEUNK .EQU 0 PPIDE_TYPEATA .EQU 1 PPIDE_TYPEATAPI .EQU 2 ; -; PPIDE DEVICE STATUS +; PPIDE DEVICE STATUS CODES ; PPIDE_STOK .EQU 0 PPIDE_STINVUNIT .EQU -1 @@ -207,44 +180,133 @@ PPIDE_STBSYTO .EQU -7 ; ; DRIVE SELECTION BYTES (FOR USE IN DRIVE/HEAD REGISTER) ; -PPIDE_DRVSEL: -PPIDE_DRVMASTER .DB %11100000 ; LBA, MASTER DEVICE -PPIDE_DRVSLAVE .DB %11110000 ; LBA, SLAVE DEVICE +;PPIDE_DRVSEL: +PPIDE_DRVMASTER .EQU %11100000 ; LBA, MASTER DEVICE +PPIDE_DRVSLAVE .EQU %11110000 ; LBA, SLAVE DEVICE ; ; PPIDE DEVICE CONFIGURATION ; -PPIDE_CFGSIZ .EQU 12 ; SIZE OF CFG TBL ENTRIES +PPIDE_CFGSIZ .EQU 18 ; SIZE OF CFG TBL ENTRIES ; ; PER DEVICE DATA OFFSETS ; PPIDE_DEV .EQU 0 ; OFFSET OF DEVICE NUMBER (BYTE) PPIDE_STAT .EQU 1 ; LAST STATUS (BYTE) PPIDE_TYPE .EQU 2 ; DEVICE TYPE (BYTE) -PPIDE_FLAGS .EQU 3 ; FLAG BITS BIT 0=CF, 1=LBA (BYTE) -PPIDE_MEDCAP .EQU 4 ; MEDIA CAPACITY (DWORD) -PPIDE_LBA .EQU 8 ; OFFSET OF LBA (DWORD) +PPIDE_ACC .EQU 3 ; ACCESS FLAG BITS BIT 0=MASTER, 1=8BIT (BYTE) +PPIDE_MED .EQU 4 ; MEDIA FLAG BITS BIT 0=CF, 1=LBA (BYTE) +PPIDE_MEDCAP .EQU 5 ; MEDIA CAPACITY (DWORD) +PPIDE_LBA .EQU 9 ; OFFSET OF LBA (DWORD) +PPIDE_DATALO .EQU 13 ; BASE PORT AND IDE DATA BUS LSB (8255 PORT A) (BYTE) +PPIDE_CTL .EQU 14 ; IDE ADDRESS BUS AND CONTROL SIGNALS (8255 PORT C)(BYTE) +PPIDE_PPI .EQU 15 ; 8255 CONTROL PORT(BYTE) +PPIDE_PARTNER .EQU 16 ; PARTNER DEVICE (MASTER <-> SLAVE) (WORD) +; +PPIDE_ACC_MAS .EQU %00000001 ; UNIT IS MASTER (ELSE SLAVE) +PPIDE_ACC_8BIT .EQU %00000010 ; UNIT WANTS 8 BIT I/O (ELSE 16 BIT) +; +PPIDE_MED_CF .EQU %00000001 ; MEDIA IS CF CARD +PPIDE_MED_LBA .EQU %00000010 ; MEDIA HAS LBA CAPABILITY +; +PPIDE_DEVCNT .EQU PPIDECNT * 2 ; PPIDE_CFGTBL: - ; DEVICE 0, PRIMARY MASTER - .DB 0 ; DRIVER DEVICE NUMBER +; +#IF (PPIDECNT >= 1) +; +PPIDE_DEV0M: ; DEVICE 0, MASTER + .DB 0 ; DRIVER RELATIVE DEVICE NUMBER (ASSIGNED DURING INIT) + .DB 0 ; DEVICE STATUS + .DB 0 ; DEVICE TYPE + .DB PPIDE_ACC_MAS | (PPIDE0A8BIT & PPIDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS + .DW 0,0 ; DEVICE CAPACITY + .DW 0,0 ; CURRENT LBA + .DB PPIDE0BASE ; DATALO + .DB PPIDE0BASE+2 ; CTL + .DB PPIDE0BASE+3 ; PPI + .DW PPIDE_DEV0S ; PARTNER +; +PPIDE_DEV0S: ; DEVICE 0, SLAVE + .DB 0 ; DRIVER RELATIVE DEVICE NUMBER (ASSIGNED DURING INIT) .DB 0 ; DEVICE STATUS .DB 0 ; DEVICE TYPE - .DB 0 ; FLAGS BYTE + .DB (PPIDE0B8BIT & PPIDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA - ; DEVICE 1, PRIMARY SLAVE - .DB 1 ; DRIVER DEVICE NUMBER + .DB PPIDE0BASE ; DATALO + .DB PPIDE0BASE+2 ; CTL + .DB PPIDE0BASE+3 ; PPI + .DW PPIDE_DEV0M ; PARTNER +; +#ENDIF +; +#IF (PPIDECNT >= 2) +; +PPIDE_DEV1M: ; DEVICE 1, MASTER + .DB 0 ; DRIVER RELATIVE DEVICE NUMBER (ASSIGNED DURING INIT) .DB 0 ; DEVICE STATUS .DB 0 ; DEVICE TYPE - .DB 0 ; FLAGS BYTE + .DB PPIDE_ACC_MAS | (PPIDE1A8BIT & PPIDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA + .DB PPIDE1BASE ; DATALO + .DB PPIDE1BASE+2 ; CTL + .DB PPIDE1BASE+3 ; PPI + .DW PPIDE_DEV1S ; PARTNER +; +PPIDE_DEV1S: ; DEVICE 1, SLAVE + .DB 0 ; DRIVER RELATIVE DEVICE NUMBER (ASSIGNED DURING INIT) + .DB 0 ; DEVICE STATUS + .DB 0 ; DEVICE TYPE + .DB (PPIDE1B8BIT & PPIDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS + .DW 0,0 ; DEVICE CAPACITY + .DW 0,0 ; CURRENT LBA + .DB PPIDE1BASE ; DATALO + .DB PPIDE1BASE+2 ; CTL + .DB PPIDE1BASE+3 ; PPI + .DW PPIDE_DEV1M ; PARTNER +; +#ENDIF +; +#IF (PPIDECNT >= 3) +; +PPIDE_DEV2M: ; DEVICE 2, MASTER + .DB 0 ; DRIVER RELATIVE DEVICE NUMBER (ASSIGNED DURING INIT) + .DB 0 ; DEVICE STATUS + .DB 0 ; DEVICE TYPE + .DB PPIDE_ACC_MAS | (PPIDE2A8BIT & PPIDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS + .DW 0,0 ; DEVICE CAPACITY + .DW 0,0 ; CURRENT LBA + .DB PPIDE2BASE ; DATALO + .DB PPIDE2BASE+2 ; CTL + .DB PPIDE2BASE+3 ; PPI + .DW PPIDE_DEV2S ; PARTNER +; +PPIDE_DEV2S: ; DEVICE 2, SLAVE + .DB 0 ; DRIVER RELATIVE DEVICE NUMBER (ASSIGNED DURING INIT) + .DB 0 ; DEVICE STATUS + .DB 0 ; DEVICE TYPE + .DB (PPIDE2B8BIT & PPIDE_ACC_8BIT) ; UNIT ACCESS FLAGS + .DB 0 ; MEDIA FLAGS + .DW 0,0 ; DEVICE CAPACITY + .DW 0,0 ; CURRENT LBA + .DB PPIDE2BASE ; DATALO + .DB PPIDE2BASE+2 ; CTL + .DB PPIDE2BASE+3 ; PPI + .DW PPIDE_DEV2M ; PARTNER +; +#ENDIF ; #IF ($ - PPIDE_CFGTBL) != (PPIDE_DEVCNT * PPIDE_CFGSIZ) .ECHO "*** INVALID PPIDE CONFIG TABLE ***\n" #ENDIF ; - .DB $FF ; END MARKER + .DB $FF ; END OF TABLE MARKER ; ; THE IDE_WAITXXX FUNCTIONS ARE BUILT TO TIMEOUT AS NEEDED SO DRIVER WILL ; NOT HANG IF DEVICE IS UNRESPONSIVE. DIFFERENT TIMEOUTS ARE USED DEPENDING @@ -263,9 +325,6 @@ PPIDE_TOFAST .EQU 10 ; FAST TIMEOUT IS 0.5 SECS ;============================================================================= ; PPIDE_INIT: - CALL NEWLINE ; FORMATTING - PRTS("PPIDE:$") ; LABEL FOR IO ADDRESS -; ; COMPUTE CPU SPEED COMPENSATED TIMEOUT SCALER ; AT 1MHZ, THE SCALER IS 218 (50000US / 229TS = 218) ; SCALER IS THEREFORE 218 * CPU SPEED IN MHZ @@ -274,58 +333,61 @@ PPIDE_INIT: CALL MULT8X16 ; HL := DE * A LD (PPIDE_TOSCALER),HL ; SAVE IT ; - PRTS(" IO=0x$") ; LABEL FOR IO ADDRESS - LD A,PPIDE_IO_BASE - CALL PRTHEXBYTE + XOR A ; ZERO ACCUM + LD (PPIDE_DEVNUM),A ; INIT DEV UNIT NUM FOR DYNAMIC ASSIGNMENT + LD IY,PPIDE_CFGTBL ; POINT TO START OF CONFIG TABLE ; -#IF (PPIDE8BIT) - PRTS(" 8BIT$") -#ENDIF - CALL PPIDE_DETECT ; CHECK FOR HARDWARE - JR Z,PPIDE_INIT00 ; CONTINUE IF PRESENT +PPIDE_INIT1: + LD A,(IY) ; LOAD FIRST BYTE TO CHECK FOR END + CP $FF ; CHECK FOR END OF TABLE VALUE + JR NZ,PPIDE_INIT2 ; IF NOT END OF TABLE, CONTINUE + XOR A ; SIGNAL SUCCESS + RET ; AND RETURN ; - ; HARDWARE NOT PRESENT - PRTS(" NOT PRESENT$") - OR $FF ; SIGNAL FAILURE - RET +PPIDE_INIT2: + BIT 0,(IY+PPIDE_ACC) ; MASTER? + JR Z,PPIDE_INIT4 ; IF NOT MASTER, SKIP AHEAD ; -PPIDE_INIT00: - PRTS(" DEVICES=$") - LD A,PPIDE_DEVCNT - CALL PRTDECB + CALL NEWLINE ; FORMATTING + PRTS("PPIDE:$") ; LABEL FOR IO ADDRESS ; -; SETUP THE DISPATCH TABLE ENTRIES + PRTS(" IO=0x$") ; LABEL FOR IO ADDRESS + LD A,(IY+PPIDE_DATALO) ; GET IO BASE ADDRES + CALL PRTHEXBYTE ; DISPLAY IT ; - LD B,PPIDE_DEVCNT ; LOOP CONTROL - LD IY,PPIDE_CFGTBL ; START OF CFG TABLE -PPIDE_INIT0: - PUSH BC ; SAVE LOOP CONTROL + CALL PPIDE_DETECT ; PROBE FOR INTERFACE + JR Z,PPIDE_INIT3 ; GOT IT, MOVE ON TO INIT UNITS + CALL PC_SPACE ; FORMATTING + LD DE,PPIDE_STR_NOPPI ; NO PPI MESSAGE + CALL WRITESTR ; DISPLAY IT + JR PPIDE_INIT4 ; SKIP CFG ENTRY +; +PPIDE_INIT3: + CALL PPIDE_RESET ; RESET THE BUS + CALL PPIDE_INIT5 ; DETECT/INIT MASTER + PUSH IY ; SAVE CFG PTR + CALL PPIDE_GOPARTNER ; SWITCH IY TO PARTNER CFG + CALL PPIDE_INIT5 ; DETECT/INIT SLAVE + POP IY ; RESTORE CFG PTR +; +PPIDE_INIT4: + LD DE,PPIDE_CFGSIZ ; SIZE OF CFG TABLE ENTRY + ADD IY,DE ; BUMP POINTER + JR PPIDE_INIT1 ; AND LOOP +; +PPIDE_INIT5: + ; UPDATE DRIVER RELATIVE UNIT NUMBER IN CONFIG TABLE + LD A,(PPIDE_DEVNUM) ; GET NEXT UNIT NUM TO ASSIGN + LD (IY+PPIDE_DEV),A ; UPDATE IT + INC A ; BUMP TO NEXT UNIT NUM TO ASSIGN + LD (PPIDE_DEVNUM),A ; SAVE IT +; + ; ADD UNIT TO GLOBAL DISK UNIT TABLE LD BC,PPIDE_FNTBL ; BC := FUNC TABLE ADR PUSH IY ; CFG ENTRY POINTER POP DE ; COPY TO DE CALL DIO_ADDENT ; ADD ENTRY TO GLOBAL DISK DEV TABLE - LD BC,PPIDE_CFGSIZ ; SIZE OF CFG ENTRY - ADD IY,BC ; BUMP IY TO NEXT ENTRY - POP BC ; RESTORE BC - DJNZ PPIDE_INIT0 ; LOOP AS NEEDED -; - ; INITIALIZE THE PPIDE INTERFACE NOW - CALL PPIDE_RESET ; DO HARDWARE SETUP/INIT - RET NZ ; ABORT IF RESET FAILS -; - ; DEVICE DISPLAY LOOP - LD B,PPIDE_DEVCNT ; LOOP ONCE PER DEVICE - LD IY,PPIDE_CFGTBL ; START OF CFG TABLE -PPIDE_INIT1: - PUSH BC ; SAVE LOOP CONTROL - CALL PPIDE_INIT2 ; DISPLAY UNIT INFO - LD BC,PPIDE_CFGSIZ ; SIZE OF CFG ENTRY - ADD IY,BC ; BUMP IY TO NEXT ENTRY - POP BC ; RESTORE LOOP CONTROL - DJNZ PPIDE_INIT1 ; LOOP UNTIL DONE - RET ; DONE ; -PPIDE_INIT2: ; CHECK FOR BAD STATUS LD A,(IY+PPIDE_STAT) ; GET STATUS OR A ; SET FLAGS @@ -333,13 +395,13 @@ PPIDE_INIT2: ; CALL PPIDE_PRTPREFIX ; PRINT DEVICE PREFIX ; -#IF (PPIDE8BIT) - PRTS(" 8BIT$") -#ENDIF + LD DE,PPIDE_STR_8BIT + BIT 1,(IY+PPIDE_ACC) ; 8 BIT ACCESS? + CALL NZ,WRITESTR ; ; PRINT LBA/NOLBA CALL PC_SPACE ; FORMATTING - BIT 1,(IY+PPIDE_FLAGS) ; TEST LBA FLAG + BIT 1,(IY+PPIDE_MED) ; TEST LBA FLAG LD DE,PPIDE_STR_NO ; POINT TO "NO" STRING CALL Z,WRITESTR ; PRINT "NO" BEFORE "LBA" IF LBA NOT SUPPORTED PRTS("LBA$") ; PRINT "LBA" REGARDLESS @@ -358,11 +420,10 @@ PPIDE_INIT2: CALL PRTDEC ; PRINT LOW WORD IN DECIMAL (HIGH WORD DISCARDED) PRTS("MB$") ; PRINT SUFFIX ; - XOR A ; SIGNAL SUCCESS - RET ; RETURN WITH A=0, AND Z SET + RET ; ;---------------------------------------------------------------------- -; PROBE FOR PPIDE HARDWARE +; PROBE FOR PPI HARDWARE ;---------------------------------------------------------------------- ; ; ON RETURN, ZF SET INDICATES HARDWARE FOUND @@ -375,9 +436,12 @@ PPIDE_DETECT: ; THEN THE BUS HOLD CIRCUITRY WILL READ BACK THE ZERO. SINCE ; WE ARE IN WRITE MODE, AN IDE CONTROLLER WILL NOT BE ABLE TO ; INTERFERE WITH THE VALUE BEING READ. +; LD A,PPIDE_DIR_WRITE ; SET DATA BUS DIRECTION TO WRITE - OUT (PPIDE_IO_PPI),A ; OUTPUT TO CONTROL WORD - LD C,PPIDE_IO_DATALO ; PPI PORT A + LD C,(IY+PPIDE_PPI) ; PPI CONTROL WORD + OUT (C),A ; WRITE IT +; + LD C,(IY+PPIDE_DATALO) ; PPI PORT A, DATALO XOR A ; VALUE ZERO OUT (C),A ; PUSH VALUE TO PORT IN A,(C) ; GET PORT VALUE @@ -443,7 +507,7 @@ PPIDE_IO: #ENDIF PUSH BC ; SAVE COUNTERS CALL PPIDE_SELUNIT ; HARDWARE SELECTION OF TARGET UNIT - CALL PPIDE_CHKDEVICE ; CHECK DEVICE AND CLEAR STATUS + CALL PPIDE_CHKERR ; CHECK FOR ERR STATUS AND RESET IF SO POP BC ; RESTORE COUNTERS JR NZ,PPIDE_IO3 ; BAIL OUT ON ERROR PPIDE_IO1: @@ -484,7 +548,7 @@ PPIDE_STATUS: PPIDE_DEVICE: LD D,DIODEV_PPIDE ; D := DEVICE TYPE LD E,(IY+PPIDE_DEV) ; E := PHYSICAL DEVICE NUMBER - BIT 0,(IY+PPIDE_FLAGS) ; TEST CF BIT IN FLAGS + BIT 0,(IY+PPIDE_MED) ; TEST CF BIT IN FLAGS LD C,%00000000 ; ASSUME NON-REMOVABLE HARD DISK JR Z,PPIDE_DEVICE1 ; IF Z, WE ARE DONE LD C,%01001000 ; OTHERWISE REMOVABLE COMPACT FLASH @@ -658,7 +722,6 @@ PPIDE_WRSEC: ; ; PPIDE_SETADDR: - ; XXX ; SEND 3 LOWEST BYTES OF LBA IN REVERSE ORDER ; IDE_IO_LBA3 HAS ALREADY BEEN SET ; HSTLBA2-0 --> IDE_IO_LBA2-0 @@ -667,19 +730,19 @@ PPIDE_SETADDR: DCALL PRTHEXBYTE CALL PPIDE_OUT .DB PPIDE_REG_LBA2 - +; LD A,(IY+PPIDE_LBA+1) DCALL PC_SPACE DCALL PRTHEXBYTE CALL PPIDE_OUT .DB PPIDE_REG_LBA1 - +; LD A,(IY+PPIDE_LBA+0) DCALL PC_SPACE DCALL PRTHEXBYTE CALL PPIDE_OUT .DB PPIDE_REG_LBA0 - +; LD A,1 DCALL PC_SPACE DCALL PRTHEXBYTE @@ -730,49 +793,64 @@ PPIDE_GETBUF: ; ; SETUP PPI TO READ LD A,PPIDE_DIR_READ ; SET DATA BUS DIRECTION TO READ - OUT (PPIDE_IO_PPI),A ; DO IT + ;OUT (PPIDE_IO_PPI),A ; DO IT + LD C,(IY+PPIDE_PPI) ; PPI CONTROL WORD + OUT (C),A ; WRITE IT ; ; SELECT READ/WRITE IDE REGISTER LD A,PPIDE_REG_DATA ; DATA REGISTER - OUT (PPIDE_IO_CTL),A ; DO IT + ;OUT (PPIDE_IO_CTL),A ; DO IT + LD C,(IY+PPIDE_CTL) ; SET IDE ADDRESS + OUT (C),A ; DO IT LD E,A ; E := READ UNASSERTED XOR PPIDE_CTL_DIOR ; SWAP THE READ LINE BIT LD D,A ; D := READ ASSERTED ; ; LOOP SETUP + XOR A ; IMPORTANT, NEEDED FOR LOOP END COMPARISON LD B,0 ; 256 ITERATIONS - LD C,PPIDE_IO_DATALO ; SETUP C WITH IO PORT (LSB) -; -#IF (!PPIDE8BIT) - INC C ; PRE-INCREMENT C -#ENDIF -; - CALL PPIDE_GETBUF1 ; FIRST PASS (FIRST 256 BYTES) - CALL PPIDE_GETBUF1 ; SECOND PASS (LAST 256 BYTES) -; - ;; CLEAN UP - ;XOR A ; ZERO A - ;OUT (PPIDE_IO_CTL),A ; RELEASE ALL BUS SIGNALS -; + LD C,(IY+PPIDE_CTL) ; SET IDE ADDRESS +; + BIT 1,(IY+PPIDE_ACC) ; 8 BIT? + JR Z,PPIDE_GETBUF1 ; IF NOT, DO 16 BIT + CALL PPIDE_GETBUF8 ; FIRST PASS (FIRST 256 BYTES) + CALL PPIDE_GETBUF8 ; SECOND PASS (LAST 256 BYTES) + JR PPIDE_GETBUF2 ; CONTINUE +PPIDE_GETBUF1: + CALL PPIDE_GETBUF16 ; FIRST PASS (FIRST 256 BYTES) + CALL PPIDE_GETBUF16 ; SECOND PASS (LAST 256 BYTES) +PPIDE_GETBUF2: CALL PPIDE_WAITRDY ; PROBLEMS IF THIS IS REMOVED! RET NZ CALL PPIDE_GETRES JP NZ,PPIDE_IOERR RET ; -PPIDE_GETBUF1: ; START OF READ LOOP - LD A,D ; ASSERT READ - OUT (PPIDE_IO_CTL),A ; DO IT -#IF (!PPIDE8BIT) - DEC C - INI ; GET AND SAVE NEXT BYTE +PPIDE_GETBUF8: ; 8 BIT WIDE READ LOOP + ; ENTER W/ C = PPIDE_IO_CTL + OUT (C),D ; ASSERT READ + DEC C ; CTL -> MSB + DEC C ; MSB -> LSB + INI ; READ FROM LSB INC C ; LSB -> MSB -#ENDIF - INI ; GET AND SAVE NEXT BYTE - LD A,E ; DEASSERT READ - OUT (PPIDE_IO_CTL),A ; DO IT + INC C ; MSB -> CTL + OUT (C),E ; DEASSERT READ + CP B ; B == A == 0? + JR NZ,PPIDE_GETBUF8 ; LOOP UNTIL DONE + RET ; - JR NZ,PPIDE_GETBUF1 ; LOOP UNTIL DONE +PPIDE_GETBUF16: ; 16 BIT WIDE READ LOOP + ; ENTER W/ C = PPIDE_IO_CTL + OUT (C),D ; ASSERT READ + DEC C ; CTL -> MSB + DEC C ; MSB -> LSB + INI ; READ FROM LSB + INC C ; LSB -> MSB + INI ; READ MSB FOR 16 BIT + INC C ; MSB -> CTL + OUT (C),E ; DEASSERT READ + CP B ; B == A == 0? + JR NZ,PPIDE_GETBUF16 ; LOOP UNTIL DONE RET ; ; @@ -781,56 +859,69 @@ PPIDE_PUTBUF: #IF (PPIDETRACE >= 3) PRTS(" PUTBUF$") #ENDIF - +; ; WAIT FOR BUFFER CALL PPIDE_WAITDRQ ; WAIT FOR BUFFER READY RET NZ ; BAIL OUT IF TIMEOUT ; ; SETUP PPI TO WRITE LD A,PPIDE_DIR_WRITE ; SET DATA BUS DIRECTION TO WRITE - OUT (PPIDE_IO_PPI),A ; DO IT + ;OUT (PPIDE_IO_PPI),A ; DO IT + LD C,(IY+PPIDE_PPI) ; PPI CONTROL WORD + OUT (C),A ; WRITE IT ; ; SELECT READ/WRITE IDE REGISTER LD A,PPIDE_REG_DATA ; DATA REGISTER - OUT (PPIDE_IO_CTL),A ; DO IT + ;OUT (PPIDE_IO_CTL),A ; DO IT + LD C,(IY+PPIDE_CTL) ; SET IDE ADDRESS + OUT (C),A ; DO IT LD E,A ; E := WRITE UNASSERTED XOR PPIDE_CTL_DIOW ; SWAP THE READ LINE BIT LD D,A ; D := WRITE ASSERTED ; ; LOOP SETUP + XOR A ; IMPORTANT, NEEDED FOR LOOP END COMPARISON LD B,0 ; 256 ITERATIONS - LD C,PPIDE_IO_DATALO ; SETUP C WITH IO PORT (LSB) -; -#IF (!PPIDE8BIT) - INC C ; PRE-INCREMENT C -#ENDIF -; - CALL PPIDE_PUTBUF1 ; FIRST PASS (FIRST 256 BYTES) - CALL PPIDE_PUTBUF1 ; SECOND PASS (LAST 256 BYTES) -; - ;; CLEAN UP - ;XOR A ; ZERO A - ;OUT (PPIDE_IO_CTL),A ; RELEASE ALL BUS SIGNALS -; + LD C,(IY+PPIDE_CTL) ; SET IDE ADDRESS +; + BIT 1,(IY+PPIDE_ACC) ; 8 BIT? + JR Z,PPIDE_PUTBUF1 ; IF NOT, DO 16 BIT + CALL PPIDE_PUTBUF8 ; FIRST PASS (FIRST 256 BYTES) + CALL PPIDE_PUTBUF8 ; SECOND PASS (LAST 256 BYTES) + JR PPIDE_PUTBUF2 ; CONTINUE +PPIDE_PUTBUF1: + CALL PPIDE_PUTBUF16 ; FIRST PASS (FIRST 256 BYTES) + CALL PPIDE_PUTBUF16 ; SECOND PASS (LAST 256 BYTES) +PPIDE_PUTBUF2: CALL PPIDE_WAITRDY ; PROBLEMS IF THIS IS REMOVED! RET NZ CALL PPIDE_GETRES JP NZ,PPIDE_IOERR RET ; -PPIDE_PUTBUF1: ; START OF READ LOOP -#IF (!PPIDE8BIT) - DEC C - OUTI ; PUT NEXT BYTE ON THE BUS (LSB) - INC C -#ENDIF - OUTI - LD A,D ; ASSERT WRITE - OUT (PPIDE_IO_CTL),A ; DO IT - LD A,E ; DEASSERT WRITE - OUT (PPIDE_IO_CTL),A ; DO IT +PPIDE_PUTBUF8: ; 8 BIT WIDE WRITE LOOP + DEC C ; CTL -> MSB + DEC C ; MSB -> LSB + OUTI ; WRITE NEXT BYTE (LSB) + INC C ; LSB -> MSB + INC C ; MSB -> CTL + OUT (C),D ; ASSERT WRITE + OUT (C),E ; DEASSERT WRITE + CP B ; B == A == 0? + JR NZ,PPIDE_PUTBUF8 ; LOOP UNTIL DONE + RET ; - JR NZ,PPIDE_PUTBUF1 ; LOOP UNTIL DONE +PPIDE_PUTBUF16: ; 16 BIT WIDE WRITE LOOP + DEC C ; CTL -> MSB + DEC C ; MSB -> LSB + OUTI ; WRITE NEXT BYTE (LSB) + INC C ; LSB -> MSB + OUTI ; WRITE NEXT BYTE (MSB) + INC C ; MSB -> CTL + OUT (C),D ; ASSERT WRITE + OUT (C),E ; DEASSERT WRITE + CP B ; B == A == 0? + JR NZ,PPIDE_PUTBUF16 ; LOOP UNTIL DONE RET ; ; @@ -859,18 +950,27 @@ PPIDE_GETRES: ; SOFT RESET OF ALL DEVICES ON BUS ; PPIDE_RESET: +#IF (PPIDETRACE >= 3) + CALL PPIDE_PRTPREFIX + PRTS(" RESET$") +#ENDIF ; ; SETUP PPI TO READ LD A,PPIDE_DIR_READ ; SET DATA BUS DIRECTION TO READ - OUT (PPIDE_IO_PPI),A ; DO IT + ;OUT (PPIDE_IO_PPI),A ; DO IT + LD C,(IY+PPIDE_PPI) ; PPI CONTROL WORD + OUT (C),A ; WRITE IT ; ; PULSE IDE RESET LINE LD A,PPIDE_CTL_RESET - OUT (PPIDE_IO_CTL),A + ;OUT (PPIDE_IO_CTL),A + LD C,(IY+PPIDE_CTL) ; SET IDE ADDRESS + OUT (C),A LD DE,20 CALL VDELAY XOR A - OUT (PPIDE_IO_CTL),A + ;OUT (PPIDE_IO_CTL),A + OUT (C),A LD DE,20 CALL VDELAY ; @@ -889,27 +989,17 @@ PPIDE_RESET: LD DE,150000/16 ; ~???MS CALL VDELAY ; - ;; CLEAR OUT ALL DATA (FOR ALL UNITS) - ;LD HL,PPIDE_UDATA - ;LD BC,PPIDE_UDLEN - ;XOR A - ;CALL FILL -; - PUSH IY ; SAVE CURRENT DEVICE CFG PTR -; - ; PROBE / INITIALIZE ALL UNITS - LD B,PPIDE_DEVCNT ; NUMBER OF UNITS TO TRY - LD IY,PPIDE_CFGTBL ; START OF CFG TABLE -PPIDE_RESET1: - PUSH BC ; SAVE LOOP CONTROL - CALL PPIDE_INITUNIT ; PROBE/INIT UNIT - LD BC,PPIDE_CFGSIZ ; SIZE OF CFG ENTRY - ADD IY,BC ; BUMP IY TO NEXT ENTRY - POP BC ; RESTORE BC - DJNZ PPIDE_RESET1 ; LOOP AS NEEDED -; - POP IY ; RECOVER DEVICE CFG PTR -; + ; INITIALIZE THE INDIVIDUAL UNITS (MASTER AND SLAVE). + ; BASED ON TESTING, IT APPEARS THAT THE MASTER UNIT MUST + ; BE DONE FIRST OR THIS BEHAVES BADLY. + PUSH IY ; SAVE CFG PTR + BIT 0,(IY+PPIDE_ACC) ; MASTER? + CALL Z,PPIDE_GOPARTNER ; IF NOT, SWITCH TO MASTER + CALL PPIDE_INITUNIT ; INIT CURRENT UNIT + CALL PPIDE_GOPARTNER ; POINT TO SLAVE + CALL PPIDE_INITUNIT ; INIT PARTNER UNIT + POP IY ; RECOVER ORIG CFG PTR +; XOR A ; SIGNAL SUCCESS RET ; AND DONE ; @@ -923,9 +1013,10 @@ PPIDE_INITUNIT: LD (HL),PPIDE_TONORM ; SET NORMAL TIMEOUT CALL PPIDE_PROBE ; DO PROBE - CALL Z,PPIDE_INITDEV ; IF FOUND, ATTEMPT TO INIT DEVICE + RET NZ ; JUST RETURN IF NOTHING THERE - RET + CALL PPIDE_INITDEV ; IF FOUND, ATTEMPT TO INIT DEVICE + RET ; DONE ; ; TAKE ANY ACTIONS REQUIRED TO SELECT DESIRED PHYSICAL UNIT ; @@ -934,18 +1025,15 @@ PPIDE_SELUNIT: CALL PPIDE_PRTPREFIX PRTS(" SELUNIT$") #ENDIF - PUSH HL ; SAVE HL, IT IS DESTROYED BELOW - PUSH IY - POP BC - LD A,(IY+PPIDE_DEV) ; GET DEVICE - AND $01 ; LS BIT DETERMINES MASTER/SLAVE - LD HL,PPIDE_DRVSEL - CALL ADDHLA - LD A,(HL) ; LOAD DRIVE/HEAD VALUE - POP HL ; RECOVER HL + BIT 0,(IY+PPIDE_ACC) ; MASTER? + JR Z,PPIDE_SELUNIT1 ; HANDLE SLAVE + LD A,PPIDE_DRVMASTER ; MASTER + JR PPIDE_SELUNIT2 +PPIDE_SELUNIT1: + LD A,PPIDE_DRVSLAVE ; SLAVE +PPIDE_SELUNIT2: LD (PPIDE_DRVHD),A ; SAVE IT -; - XOR A + XOR A ; SUCCESS RET ; ; @@ -974,7 +1062,9 @@ PPIDE_PROBE: ; THERE, THEN THE VALUE WRITTEN TO PPI PORT A IS IGNORED ; BECAUSE THE WRITE SIGNAL IS NEVER PULSED. XOR A - OUT (PPIDE_IO_DATALO),A + ;OUT (PPIDE_IO_DATALO),A + LD C,(IY+PPIDE_DATALO) ; PPI PORT A, DATALO + OUT (C),A ; IN A,(PPIDE_REG_STAT) ; GET STATUS CALL PPIDE_IN .DB PPIDE_REG_STAT @@ -1047,42 +1137,21 @@ PPIDE_INITDEV: OR A ; SET FLAGS JP Z,PPIDE_NOMEDIA ; EXIT SETTING NO MEDIA STATUS ; - ; CLEAR OUT UNIT SPECIFIC DATA, BUT PRESERVE THE EXISTING - ; VALUE OF THE UNIT TYPE WHICH WAS ESTABLISHED BY THE DEVICE - ; PROBES WHEN THE PPIDE BUS WAS RESET - ;PUSH AF ; SAVE UNIT TYPE VALUE FROM ABOVE - ;LD A,(IY+PPIDE_DEV) ; GET CURRENT DEVICE NUMBER - ;PUSH AF ; ... AND SAVE IT - ;;PUSH HL ; SAVE UNIT TYPE FIELD POINTER - ;;PPIDE_DPTR(0) ; SET HL TO START OF UNIT DATA - ;;LD BC,PPIDE_UDLEN - ;;XOR A - ;;CALL FILL - ;;POP HL ; RECOVER UNIT TYPE FIELD POINTER - ;;POP AF ; RECOVER UNIT TYPE VALUE - ;PUSH IY ; SET HL TO - ;POP HL ; ... START OF DEVICE INSTANCE DATA - ;LD BC,PPIDE_CFGSIZ ; SIZE OF CONFGI DATA TO CLEAR - ;XOR A ; FILL WITH ZERO - ;CALL FILL ; DO IT - ;POP AF ; RECOVER DEVICE NUMBER VALUE - ;LD (IY+PPIDE_DEV),A ; ... AND PUT IT BACK - ;POP AF ; RECOVER DEVICE TYPE VALUE - ;LD (IY+PPIDE_TYPE),A ; ... AND PUT IT BACK -; -#IF (PPIDE8BIT) + BIT 1,(IY+PPIDE_ACC) ; 8 BIT ACCESS? + JR Z,PPIDE_INITDEV0 ; NO, DO 16 BIT INIT LD A,PPIDE_FEAT_ENABLE8BIT ; FEATURE VALUE = ENABLE 8-BIT PIO -#ELSE - LD A,PPIDE_FEAT_DISABLE8BIT ; FEATURE VALUE = DISABLE 8-BIT PIO -#ENDIF CALL PPIDE_SETFEAT ; SET FEATURE - -#IF (PPIDE8BIT) + RET NZ ; BAIL OUT ON ERROR + JR PPIDE_INITDEV00 ; CONTINUE +; +PPIDE_INITDEV0: ; "REAL" IDE DRIVES MAY NOT ACCEPT THE DISABLE8BIT FEATURE COMMAND, ; SO IT IS ONLY AN ERROR IF WE ARE ATTEMPTING TO ENABLE8BIT. - ; CREDIT TO ED BRINDLEY FOR THIS CORRECTION. - RET NZ ; BAIL OUT ON ERROR -#ENDIF + ; CREDIT TO ED BRINDLEY FOR THIS CORRECTION. SO ERROR RETURN IGNORED HERE. + LD A,PPIDE_FEAT_DISABLE8BIT ; FEATURE VALUE = ENABLE 8-BIT PIO + CALL PPIDE_SETFEAT ; SET FEATURE, IGNORE ERRORS +; +PPIDE_INITDEV00: ; CALL PPIDE_IDENTIFY ; EXECUTE PPIDENTIFY COMMAND RET NZ ; BAIL OUT ON ERROR @@ -1090,9 +1159,8 @@ PPIDE_INITDEV: LD DE,HB_WRKBUF ; POINT TO BUFFER DCALL DUMP_BUFFER ; DUMP IT IF DEBUGGING ; - XOR A - LD (IY+PPIDE_FLAGS),0 ; CLEAR FLAGS - + LD (IY+PPIDE_MED),0 ; CLEAR MEDIA FLAGS +; ; DETERMINE IF CF DEVICE LD HL,HB_WRKBUF ; FIRST WORD OF IDENTIFY DATA HAS CF FLAG LD A,$8A ; FIRST BYTE OF MARKER IS $8A @@ -1102,14 +1170,14 @@ PPIDE_INITDEV: LD A,$84 ; SECOND BYTE OF MARKER IS $84 CP (HL) ; COMPARE JR NZ,PPIDE_INITDEV1 ; IF NOT MATCH, NOT CF - SET 0,(IY+PPIDE_FLAGS) ; SET FLAGS BIT FOR CF MEDIA + SET 0,(IY+PPIDE_MED) ; SET FLAGS BIT FOR CF MEDIA ; PPIDE_INITDEV1: ; DETERMINE IF LBA CAPABLE LD A,(HB_WRKBUF+98+1) ; GET BYTE WITH LBA BIT FROM BUFFER BIT 1,A ; CHECK THE LBA BIT JR Z,PPIDE_INITDEV2 ; NOT SET, BYPASS - SET 1,(IY+PPIDE_FLAGS) ; SET FLAGS BIT FOR LBA + SET 1,(IY+PPIDE_MED) ; SET FLAGS BIT FOR LBA ; PPIDE_INITDEV2: ; GET DEVICE CAPACITY AND SAVE IT @@ -1129,15 +1197,24 @@ PPIDE_INITDEV2: ; RET ; RETURN, A=0, Z SET ; +; SWITCH IY POINTER FROM CURRENT UNIT CFG TO PARTNER UNIT CFG ; +PPIDE_GOPARTNER: + PUSH HL ; SAVE HL + LD L,(IY+PPIDE_PARTNER) ; GET PARTNER ENTRY + LD H,(IY+PPIDE_PARTNER+1) ; ... + PUSH HL ; MOVE HL + POP IY ; ... TO IY + POP HL ; RESTORE INCOMING HL + RET ; AND DONE ; -PPIDE_CHKDEVICE: +; CHECK CURRENT DEVICE FOR ERROR STATUS AND ATTEMPT TO RECOVER +; VIA RESET IF DEVICE IS IN ERROR. +; +PPIDE_CHKERR: LD A,(IY+PPIDE_STAT) ; GET STATUS OR A ; SET FLAGS - RET Z ; RETURN IF ALL IS WELL -; - ; ATTEMPT TO REINITIALIZE HERE??? - JP PPIDE_ERR + CALL NZ,PPIDE_RESET ; IF ERROR STATUS, RESET BUS RET ; ; @@ -1146,7 +1223,7 @@ PPIDE_WAITRDY: LD A,(PPIDE_TIMEOUT) ; GET TIMEOUT IN 0.05 SECS LD B,A ; PUT IN OUTER LOOP VAR PPIDE_WAITRDY1: - LD DE,(PPIDE_TOSCALER) ; CPU SPPED SCALER TO INNER LOOP VAR + LD DE,(PPIDE_TOSCALER) ; CPU SPEED SCALER TO INNER LOOP VAR PPIDE_WAITRDY2: ;IN A,(PPIDE_REG_STAT) ; READ STATUS CALL PPIDE_IN @@ -1168,7 +1245,7 @@ PPIDE_WAITDRQ: LD A,(PPIDE_TIMEOUT) ; GET TIMEOUT IN 0.05 SECS LD B,A ; PUT IN OUTER LOOP VAR PPIDE_WAITDRQ1: - LD DE,(PPIDE_TOSCALER) ; CPU SPPED SCALER TO INNER LOOP VAR + LD DE,(PPIDE_TOSCALER) ; CPU SPEED SCALER TO INNER LOOP VAR PPIDE_WAITDRQ2: ;IN A,(PPIDE_REG_STAT) ; READ STATUS CALL PPIDE_IN @@ -1190,7 +1267,7 @@ PPIDE_WAITBSY: LD A,(PPIDE_TIMEOUT) ; GET TIMEOUT IN 0.05 SECS LD B,A ; PUT IN OUTER LOOP VAR PPIDE_WAITBSY1: - LD DE,(PPIDE_TOSCALER) ; CPU SPPED SCALER TO INNER LOOP VAR + LD DE,(PPIDE_TOSCALER) ; CPU SPEED SCALER TO INNER LOOP VAR PPIDE_WAITBSY2: ;IN A,(PPIDE_REG_STAT) ; READ STATUS CALL PPIDE_IN ; 17TS + 170TS @@ -1205,49 +1282,66 @@ PPIDE_WAITBSY2: DJNZ PPIDE_WAITBSY1 ; ----- JP PPIDE_BSYTO ; EXIT WITH BSYTO ERR ; 229TS ; -; -; +; READ A VALUE FROM THE DEVICE POINTED TO BY IY AND RETURN IT IN A +; PPIDE_IN: - LD A,PPIDE_DIR_READ ; SET DATA BUS DIRECTION TO READ ; 7TS - OUT (PPIDE_IO_PPI),A ; DO IT ; 11TS EX (SP),HL ; GET PARM POINTER ; 19TS PUSH BC ; SAVE INCOMING BC ; 11TS + LD A,PPIDE_DIR_READ ; SET DATA BUS DIRECTION TO READ ; 7TS + ;OUT (PPIDE_IO_PPI),A ; DO IT ; 11TS + LD C,(IY+PPIDE_PPI) ; PPI CONTROL WORD + OUT (C),A ; WRITE IT +; LD B,(HL) ; GET CTL PORT VALUE ; 7TS - LD C,PPIDE_IO_CTL ; SETUP PORT TO WRITE ; 7TS + ;LD C,PPIDE_IO_CTL ; SETUP PORT TO WRITE ; 7TS + ;LD C,(IY+PPIDE_CTL) ; SET IDE ADDRESS + DEC C ; SET IDE ADDRESS OUT (C),B ; SET ADDRESS LINES ; 12TS - SET 6,B ; TURN ON WRITE BIT ; 8TS - OUT (C),B ; ASSERT WRITE LINE ; 12TS - ;NOP - ;NOP - IN A,(PPIDE_IO_DATALO) ; GET DATA VALUE FROM DEVICE ; 11TS - ;NOP - ;NOP - RES 6,B ; CLEAR WRITE BIT ; 8TS - OUT (C),B ; DEASSERT WRITE LINE ; 12TS + SET 6,B ; TURN ON READ BIT ; 8TS + OUT (C),B ; ASSERT READ LINE ; 12TS +; + ;IN A,(PPIDE_IO_DATALO) ; GET DATA VALUE FROM DEVICE ; 11TS + DEC C + DEC C + IN A,(C) ; GET DATA VALUE FROM DEVICE + INC C + INC C +; + RES 6,B ; CLEAR READ BIT ; 8TS + OUT (C),B ; DEASSERT READ LINE ; 12TS POP BC ; RECOVER INCOMING BC ; 10TS INC HL ; POINT PAST PARM ; 6TS EX (SP),HL ; RESTORE STACK ; 19TS RET ; 10TS -; ; ----- -; ; 170TS ; +; OUTPUT VALUE IN A TO THE DEVICE POINTED TO BY IY +; PPIDE_OUT: + ; *** TODO *** FIX ORDER OF SET/CLEAR WRITE LINE + EX (SP),HL ; GET PARM POINTER + PUSH BC ; SAVE INCOMING BC PUSH AF ; PRESERVE INCOMING VALUE LD A,PPIDE_DIR_WRITE ; SET DATA BUS DIRECTION TO WRITE - OUT (PPIDE_IO_PPI),A ; DO IT + ;OUT (PPIDE_IO_PPI),A ; DO IT + LD C,(IY+PPIDE_PPI) ; PPI CONTROL WORD + OUT (C),A ; WRITE IT POP AF ; RECOVER VALUE TO WRITE - EX (SP),HL ; GET PARM POINTER - PUSH BC ; SAVE INCOMING BC +; LD B,(HL) ; GET IDE ADDRESS VALUE - LD C,PPIDE_IO_CTL ; SETUP PORT TO WRITE + ;LD C,PPIDE_IO_CTL ; SETUP PORT TO WRITE + ;LD C,(IY+PPIDE_CTL) ; SET IDE ADDRESS + DEC C ; SET IDE ADDRESS OUT (C),B ; SET ADDRESS LINES SET 5,B ; TURN ON WRITE BIT OUT (C),B ; ASSERT WRITE LINE - ;NOP - ;NOP - OUT (PPIDE_IO_DATALO),A ; SEND DATA VALUE TO DEVICE - ;NOP - ;NOP +; + DEC C + DEC C + ;OUT (PPIDE_IO_DATALO),A ; SEND DATA VALUE TO DEVICE + OUT (C),A ; SEND DATA VALUE TO DEVICE + INC C + INC C +; RES 5,B ; CLEAR WRITE BIT OUT (C),B ; DEASSERT WRITE LINE POP BC ; RECOVER INCOMING BC @@ -1357,26 +1451,40 @@ PPIDE_PRTSTAT3: PPIDE_REGDUMP: PUSH AF PUSH BC + push DE CALL PC_SPACE CALL PC_LBKT LD A,PPIDE_DIR_READ ; SET DATA BUS DIRECTION TO READ - OUT (PPIDE_IO_PPI),A ; DO IT - LD C,PPIDE_REG_CMD + ;OUT (PPIDE_IO_PPI),A ; DO IT + LD C,(IY+PPIDE_PPI) ; PPI CONTROL WORD + OUT (C),A ; WRITE IT + LD C,(IY+PPIDE_CTL) ; SET IDE ADDRESS + LD E,PPIDE_REG_CMD LD B,7 PPIDE_REGDUMP1: - LD A,C ; REGISTER ADDRESS - OUT (PPIDE_IO_CTL),A ; SET IT + LD A,E ; REGISTER ADDRESS + ;OUT (PPIDE_IO_CTL),A ; SET IT + OUT (C),A ; REGISTER ADDRESS XOR PPIDE_CTL_DIOR ; SET BIT TO ASSERT READ LINE - OUT (PPIDE_IO_CTL),A ; ASSERT READ - IN A,(PPIDE_IO_DATALO) ; GET VALUE + ;OUT (PPIDE_IO_CTL),A ; ASSERT READ + OUT (C),A ; ASSERT READ + ;IN A,(PPIDE_IO_DATALO) ; GET VALUE + DEC C ; CTL -> MSB + DEC C ; MSB -> LSB + IN A,(C) ; GET VALUE + INC C ; LSB -> MSB + INC C ; MSB -> CTL CALL PRTHEXBYTE ; DISPLAY IT - LD A,C ; RELOAD ADDRESS W/ READ UNASSERTED - OUT (PPIDE_IO_CTL),A ; AND SET IT - DEC C ; NEXT LOWER REGISTER + ;LD A,C ; RELOAD ADDRESS W/ READ UNASSERTED + ;OUT (PPIDE_IO_CTL),A ; AND SET IT + OUT (C),E ; RELOAD ADDRESS W/ READ UNASSERTED + ;DEC C ; NEXT LOWER REGISTER + DEC E ; NEXT LOWER REGISTER DEC B ; DEC LOOP COUNTER CALL NZ,PC_SPACE ; FORMATTING JR NZ,PPIDE_REGDUMP1 ; LOOP AS NEEDED CALL PC_RBKT ; FORMATTING + POP DE POP BC POP AF RET @@ -1433,6 +1541,8 @@ PPIDE_STR_STBSYTO .TEXT "BUSY TIMEOUT$" PPIDE_STR_STUNK .TEXT "UNKNOWN ERROR$" ; PPIDE_STR_NO .TEXT "NO$" +PPIDE_STR_NOPPI .TEXT "PPI NOT PRESENT$" +PPIDE_STR_8BIT .TEXT " 8-BIT$" ; ;============================================================================= ; DATA STORAGE @@ -1446,3 +1556,5 @@ PPIDE_IOFNADR .DW 0 ; PENDING IO FUNCTION ADDRESS PPIDE_DRVHD .DB 0 ; CURRENT DRIVE/HEAD MASK ; PPIDE_DSKBUF .DW 0 ; ACTIVE DISK BUFFER +; +PPIDE_DEVNUM .DB 0 ; TEMP DEVICE NUM USED DURING INIT diff --git a/Source/HBIOS/rf.asm b/Source/HBIOS/rf.asm index a259a58a..30588a57 100644 --- a/Source/HBIOS/rf.asm +++ b/Source/HBIOS/rf.asm @@ -7,6 +7,8 @@ ; RF_U0IO .EQU $A0 RF_U1IO .EQU $A4 +RF_U2IO .EQU $A8 +RF_U3IO .EQU $AC ; ; IO PORT OFFSETS ; @@ -18,24 +20,51 @@ RF_ST .EQU 3 ; MD DEVICE CONFIGURATION ; RF_DEVCNT .EQU RFCNT ; NUMBER OF RF DEVICES SUPPORTED -RF_CFGSIZ .EQU 6 ; SIZE OF CFG TBL ENTRIES +RF_CFGSIZ .EQU 8 ; SIZE OF CFG TBL ENTRIES +RF_MAXRF .EQU 4 ; MAXIMUM NUMBERS OF DEVICES SUPPORTED ; RF_DEV .EQU 0 ; OFFSET OF DEVICE NUMBER (BYTE) RF_STAT .EQU 1 ; OFFSET OF STATUS (BYTE) RF_LBA .EQU 2 ; OFFSET OF LBA (DWORD) +RF_IOAD .EQU 7 ; OFFSET OF DEVICE IO ADDRESS + +#IF ($RF_DEVCNT > RF_MAXRF) + .ECHO "*** ONLY 4 RAM FLOPPY DEVICES SUPPORTED ***\n" +RF_DEVCNT .SET RF_MAXRF +#ENDIF ; ; DEVICE CONFIG TABLE (RAM DEVICE FIRST TO MAKE IT ALWAYS FIRST DRIVE) ; RF_CFGTBL: - ; DEVICE 0 ($A0) + ; DEVICE 0 .DB 0 ; DRIVER DEVICE NUMBER .DB 0 ; DEVICE STATUS .DW 0,0 ; CURRENT LBA -#IF (RF_DEVCNT >= 2) - ; DEVICE 1 ($A4) + .DB 0 ; UNUSED + .DB RF_U0IO ; DEVICE BASE ADDR +#IF (RF_DEVCNT > 1) + ; DEVICE 1 .DB 1 ; DEVICE NUMBER .DB 0 ; DEVICE STATUS .DW 0,0 ; CURRENT LBA + .DB 0 ; UNUSED + .DB RF_U1IO ; DEVICE BASE ADDR +#ENDIF +#IF (RF_DEVCNT > 2) + ; DEVICE 2 + .DB 2 ; DRIVER DEVICE NUMBER + .DB 0 ; DEVICE STATUS + .DW 0,0 ; CURRENT LBA + .DB 0 ; UNUSED + .DB RF_U2IO ; DEVICE BASE ADDR +#ENDIF +; ; DEVICE 3 +#IF (RF_DEVCNT > 3) + .DB 3 ; DEVICE NUMBER + .DB 0 ; DEVICE STATUS + .DW 0,0 ; CURRENT LBA + .DB 0 ; UNUSED + .DB RF_U3IO ; DEVICE BASE ADDR #ENDIF ; #IF ($ - RF_CFGTBL) != (RF_DEVCNT * RF_CFGSIZ) @@ -48,38 +77,12 @@ RF_CFGTBL: ; RF_INIT: CALL NEWLINE ; FORMATTING - PRTS("RF: IO=0x$") - LD A,RF_U0IO - CALL PRTHEXBYTE - PRTS(" DEVICES=$") - LD A,RF_DEVCNT - CALL PRTDECB - PRTS(" WP=$") - IN A,(RF_U0IO+3) - AND 1 - JR Z,NO_WP1 - PRTS("ON$") - JR D2_WP1 -NO_WP1: - PRTS("OFF$") -D2_WP1: - #IF (RF_DEVCNT >= 2) - IN A,(RF_U1IO+3) - AND 1 - JR Z,NO_WP2 - PRTS("-ON$") - JR D2_WP2 -NO_WP2: - PRTS("-OFF$") -D2_WP2: - - #ENDIF -; -; SETUP THE DIO TABLE ENTRIES -; + PRTS("RF:$") + LD B,RF_DEVCNT ; LOOP CONTROL LD IY,RF_CFGTBL ; START OF CFG TABLE RF_INIT0: + CALL RF_UNIT PUSH BC ; SAVE LOOP CONTROL LD BC,RF_FNTBL ; BC := FUNC TABLE ADR PUSH IY ; CFG ENTRY POINTER @@ -89,10 +92,30 @@ RF_INIT0: ADD IY,BC ; BUMP IY TO NEXT ENTRY POP BC ; RESTORE BC DJNZ RF_INIT0 ; LOOP AS NEEDED + + PRTS(" DEVICES=$") ; DISPLAY NUMBER + LD A,RF_DEVCNT ; OF DEVICES + CALL PRTDECB ; XOR A ; INIT SUCCEEDED RET ; RETURN ; +RF_UNIT: + PRTS(" IO=0x$") ; DISPLAY + LD A,(IY+RF_IOAD) ; PORT AND + CALL PRTHEXBYTE ; WRITE + PRTS(" WP=$") ; PROTECT + ADD A,RF_ST ; STATUS OF + LD C,A ; THIS DEVICE + IN A,(C) + AND 1 + JR Z,RF_NO_WP1 + PRTS("ON$") + RET +RF_NO_WP1: + PRTS("OFF$") + RET +; ; ; RF_FNTBL: @@ -265,21 +288,9 @@ RF_WRSEC: ; ; RF_SETIO: - LD A,(IY+RF_DEV) ; GET DEVICE NUM - OR A ; SET FLAGS - JR NZ,RF_SETIO1 - LD A,RF_U0IO - JR RF_SETIO3 -RF_SETIO1: - DEC A - JR NZ,RF_SETIO2 - LD A,RF_U1IO - JR RF_SETIO3 -RF_SETIO2: - CALL PANIC ; INVALID UNIT -RF_SETIO3: - LD (RF_IO),A - RET + LD A,(IY+RF_IOAD) ; GET THE IO PORT + LD (RF_IO),A ; OF THE DEVICE WE + RET ; ARE WORKING ON ; ; ; diff --git a/Source/HBIOS/romldr.asm b/Source/HBIOS/romldr.asm index 59256504..844a1de4 100644 --- a/Source/HBIOS/romldr.asm +++ b/Source/HBIOS/romldr.asm @@ -206,8 +206,7 @@ MENU4: ; BOOT SELECTION PROCESSING ;================================================================================================== ; -SEL: - ; HANDLE SERIAL CONSOLE INPUT +SEL: ; HANDLE SERIAL CONSOLE INPUT CALL CST ; CHECK CONSOLE INPUT OR A ; ZERO? JR Z,SEL1 ; IF NOT, CONTINUE @@ -221,9 +220,13 @@ SEL: OUT (LEDPORT),A ; CLEAR LED #ENDIF #ENDIF - CALL CINUC ; GET THE KEY - CALL COUT ; ECHO KEY - JR MATS ; AND HANDLE IT + CALL CINUC ; GET THE KEY + CALL COUT ; ECHO KEY + CP 'R' ; CHECK FOR + JP Z,REBOOT ; REBOOT REQUEST + LD DE,MENU_S+10-MENU_V ; POINT TO SERIAL MENU COLUMN + LD C,2 ; SET SERIAL FLAG + JR MATS ; GO CHECK MENU SELECTION ; SEL1: #IF (DSKYENABLE) @@ -241,8 +244,14 @@ SEL1: OUT (LEDPORT),A ; CLEAR LED #ENDIF #ENDIF - CALL DSKY_GETKEY ; GET PENDING KEY PRESS - JR MATK ; AND HANDLE IT + CALL DSKY_GETKEY ; GET PENDING KEY PRESS ; NOTE DESKY_GETKEY + CP $FF ; CHECK FOR ERROR + JR Z,SEL2 ; IF SO, IGNORE KEY, AND CONT LOOPING + CP KY_BO ; CHECK FOR REBOOT ; CAN RETURN AN INVALID + JP Z,REBOOT ; REBOOT REQUEST ; KEYSCAN AS FFH WHICH + LD DE,MENU_S+11-MENU_V ; POINT TO DSKY MENU COLUMN ; MAY BE MATCHED WITH + LD C,1 ; SET DSKY FLAG ; DUMMY MENU ENTRIES + JR MATS ; GO CHECK MENU SELECTION #ENDIF ; SEL2: @@ -267,8 +276,10 @@ SEL2: OUT (LEDPORT),A ; CLEAR LED #ENDIF #ENDIF - LD A,BOOT_DEFAULT ; TIMEOUT EXPIRED, - JR MATS ; PERFORM DEFAULT BOOT ACTION + LD A,BOOT_DEFAULT ; TIMEOUT EXPIRED, + LD DE,MENU_S+10-MENU_V ; POINT TO SERIAL MENU COLUMN + LD C,2 ; SET SERIAL FLAG + JR MATS ; PERFORM DEFAULT BOOT ACTION #ENDIF ; SEL3: @@ -279,51 +290,101 @@ SEL3: ; ROM MENU TABLE MATCHING ;================================================================================================== ; -MATS: ; MATCH SERIAL INPUT TO MENU - CP 'R' - JP Z,REBOOT - LD B,MENU_N - LD DE,MENU_S+10-MENU_V - LD HL,MENU_V -MATS1: EX DE,HL - ADD HL,DE - CP (HL) - EX DE,HL +MATS: LD B,MENU_N ; LOOP THROUGH THE ; ON ENTRY DE POINTS TO + LD HL,MENU_V ; MENU TABLE AND ; THE MENU COLUMN WE ARE +MATS1: EX DE,HL ; CHECK IF THE ; CHECKING AND C CONTAINS + ADD HL,DE ; KEYPRESS MATCHES ; A FLAG TELLING US IF WE + CP (HL) ; ANY OF ; HAVE DSKY OR SERIAL INPUT + EX DE,HL ; THE MENU ITEMS. JR Z,MATS2 - DJNZ MATS1 ; FALL THRU IF IT DOES NOT MATCH ROM MENU - SUB '0' ; CONVERT ASCII TO BINARY - JR MATD ; AND TRY DISK BOOT -MATS2: INC DE ; SKIP MENU SEL CHAR - INC DE ; SKIP DSKY SEL CHAR - JP GOROM ; BOOT FROM ROM + DJNZ MATS1 ; IF WE REACH THE TABLE END AND DON'T HAVE + JR MATD ; A MATCH GO AND CHECK FOR A DISK SELECTION +; +MATS2: LD B,0 ; WE GOT A MATCH FROM THE MENU TABLE. POINT + EX DE,HL ; TO THE ROM ADDRESS TO EXECUTE. ADJUST THE + ADD HL,BC ; POINTER TO THE ROM ENTRY BASED ON WHETHER WE + EX DE,HL ; GOT A MATCH IN THE DSKY OR SERIAL MENU COLUMN + JP GOROM ; JUMP TO THE ROM HANDLER. +; +MATD: LD B,A + LD A,C ; IF INPUT WAS SERIAL + LD (BL_INPFLG),A ; SAVE INPUT FLAG + DEC C ; CONVERT TO FROM. + LD A,B ; ASCII TO DECIMAL. + JR Z,MATD1 ; DSKY NUMBERS ARE + SUB '0' ; ALREADY DECIMAL +MATD1: CP 10 ; DO A RANGE CHECK + JR NC,MATX ; NOT VALID, HANDLE IT BELOW ; -#IF (DSKYENABLE) -MATK: ; MATCH DSKY INPUT TO MENU - CP KY_BO - JP Z,REBOOT - LD B,MENU_N - LD DE,MENU_S+11-MENU_V - LD HL,MENU_V -MATK1: EX DE,HL - ADD HL,DE - CP (HL) - EX DE,HL - JR Z,MATK2 - DJNZ MATK1 ; FALL THRU IF IT DOES NOT MATCH DSKY MENU - JR MATD ; TRY DISK BOOT -MATK2: INC DE ; SKIP DSKY SEL CHAR - JP GOROM ; BOOT FROM ROM +#IF (BIOS == BIOS_WBW) + PUSH BC + PUSH AF ; HOW MANY DISK + LD B,BF_SYSGET ; DEVICES DO WE + LD C,BF_SYSGET_DIOCNT ; HAVE IN THE + RST 08 ; SYSTEM ? + POP AF + POP BC +#ELSE + LD E,9 ; HACK TO HANDLE UNA, NEED TO FIX #ENDIF +; JR MATD2 ; IF MORE THEN 9 ; UNCOMMENT TO TEST DOUBLE CHAR ENTRY + CP 10 ; THEN WE NEED TO GET + JR NC,MATD2 ; ANOTHER CHARACTER ; -MATD: ; CHECK FOR DISK BOOT REQUEST - CP 10 ; 0-9, DISK DEVICE - JR NC,MATX ; NOT VALID, HANDLE IT BELOW - JP GOBOOTDISK ; BOOT FROM DISK + CP E ; WE DON'T HAVE MORE THAN 10 DEVICES SO ; A = REQUESTED UNIT + JP C,GOBOOTDISK ; CHECK IT IS IN RANGE. BOOT IF IT IS ; E = AVAILABLE UNITS + JR MATX ; IT IF NOT VALID, HANDLE IT BELOW + +MATD2: LD B,A ; PROCESS FURTHER INPUT ; B = REQUESTED UNIT + LD A,C ; CHECK WHERE TO GET ; C = DSKY/SERIAL FLAG + DEC C ; THE INPUT FROM AND GO + JR NZ,MATD3 ; GET ANOTHER CHARACTER +; +#IF (DSKYENABLE) ; INPUT DSKY +; +MATD4: ;CALL DSKY_STAT ; WAIT FOR + ;OR A ; ANOTHER + ;JR Z,MATD4 ; KEY FROM + ;CALL DSKY_GETKEY ; DSKY + CALL DSKY_KEY + + CP KY_EN ; IF NEXT KEY IS ENTER + JR Z,MATD6 ; OR GO, PROCESS AS A + CP KY_GO ; SINGLE DIGIT NUMBER + JR Z,MATD6 ; OTHERWISE JOIN TWO + JR MATD5 ; CHARCTERS IN ONE DECIMAL +#ENDIF +; +; ; INPUT SERIAL ; -MATX: - ; SET ERROR STRING AND LOOP - LD DE,STR_INVALID ; INVALID SEL MSG - JP MENU ; RESTART MENU LOOP +MATD3: ;CALL CST ; WAIT FOR + ;OR A ; ANOTHER + ;JR Z,MATD3 ; KEY FROM + CALL CINUC ; SERIAL + CALL COUT +; + CP CHR_CR ; IF NEXT KEY IS RETURN PROCESS + JR Z,MATD6 ; AS A SINGLE DIGIT NUMBER +; + SUB '0' ; CONVERT THE SERIAL NUMBER TO DECIMAL + CP 10 ; DO A RANGE CHECK + JR NC,MATX ; NOT VALID, HANDLE IT BELOW + +MATD5: LD C,A ; C CONTAINS SECOND CHARACTER INPUT 0..9 + LD A,B ; A CONTAINS FIRST NUMBER INPUT 0..9 + ADD A,A + LD B,A ; MULTIPLY FIRST DIGIT BY 10 + ADD A,A ; AND ADD SECOND DIGIT + ADD A,A + ADD A,B ; CONVERT TWO INPUTTED + ADD A,C ; CHARACTERS TO DECIMAL. + LD B,A +; +MATD6: LD A,B ; PUT THE DEVICE NUMBER TO BOOT + JP GOBOOTDISK ; IN A AND GO BOOT DEVICE +; +MATX: LD DE,STR_INVALID ; SET ERROR STRING MESSAGE + JP MENU ; AND RESTART MENU LOOP ; ;================================================================================================== ; ROM MENU TABLE @@ -368,6 +429,8 @@ MENU_1: MENU_L("~CP/M$ ", "C", KY_BK, CPM_ENT, 2000h, CPM_LOC, CPM_SIZ MENU_L("~Forth$ ", "F", KY_EX, FTH_LOC, 0000h, FTH_LOC, FTH_SIZ, BID_IMG1, BID_USR, "Camel Forth$ ") MENU_L("~BASIC$ ", "B", KY_DE, BAS_LOC, 1700h, BAS_LOC, BAS_SIZ, BID_IMG1, BID_USR, "Nascom BASIC$") MENU_L("~T-BASIC$ ", "T", KY_EN, TBC_LOC, 3700h, TBC_LOC, TBC_SIZ, BID_IMG1, BID_USR, "Tasty BASIC$ ") + MENU_L("~PLAY$ ", "P", $FF, GAM_LOC, 4000h, GAM_LOC, GAM_SIZ, BID_IMG1, BID_USR, "Game$ ") + MENU_L("~USER ROM$", "U", $FF, USR_LOC, 7000h, USR_LOC, USR_SIZ, BID_IMG1, BID_USR, "User ROM$ ") #ENDIF #IF (DSKYENABLE) MENU_L("~DSKY$ ", "D", KY_GO, MON_DSKY, 1000h, MON_LOC, MON_SIZ, BID_CUR, BID_USR, "DSKY Monitor$") @@ -382,7 +445,10 @@ MENU_N .EQU ((MENU_E - MENU_S) / MENU_V) ; NUMBER OF MENU ITEMS ; SYSTEM REBOOT HANDLER ;================================================================================================== ; -REBOOT: LD DE,STR_REBOOT ; POINT TO MESSAGE +REBOOT: +; +#IF (BIOS == BIOS_WBW) + LD DE,STR_REBOOT ; POINT TO MESSAGE CALL WRITESTR ; PRINT IT #IF (DSKYENABLE) LD HL,MSG_BOOT ; POINT TO BOOT MESSAGE @@ -391,6 +457,13 @@ REBOOT: LD DE,STR_REBOOT ; POINT TO MESSAGE LD A,BID_BOOT ; BOOT BANK LD HL,0 ; ADDRESS ZERO CALL HB_BNKCALL ; DOES NOT RETURN +#ENDIF +#IF (BIOS == BIOS_UNA) + LD BC,$01FB ; UNA FUNC = SET BANK + LD DE,0 ; ROM BANK 0 + RST 08 ; DO IT + JP 0 ; JUMP TO RESTART ADDRESS +#ENDIF ; ;================================================================================================== ; ROM IMAGE LOAD HANDLER @@ -504,17 +577,61 @@ GOROM2: LD B,BF_SYSSETCPY ; HBIOS FUNC: SETUP BANK COPY ;================================================================================================== ; GOBOOTDISK: - LD (BL_BOOTID),A + LD (BL_BOOTID),A ; SAVE INCOMING BOOTID +; + ; SET THE INITIAL BOOT UNIT AND SLICE + ;LD A,(BL_BOOTID) ; GET BOOTID + LD (BL_DEVICE),A ; STORE IT + XOR A ; LU DEFAULTS TO 0 + LD (BL_LU),A ; STORE IT +; +#IF (BIOS == BIOS_WBW) +; + LD A,(BL_INPFLG) ; GET INPUT FLAG + CP 1 ; DSKY? + JR Z,GOBOOTDISK1 ; IF SO, SLICE 0 IS ASSUMED +; + LD A,(BL_DEVICE) ; GET BOOT DEVICE + LD C,A ; PUT IN C + LD B,BF_DIODEVICE ; HBIOS: DIO DEVICE FUNC + RST 08 + LD A,D ; DEVICE TYPE TO A + CP DIODEV_IDE ; HARD DISK DEVICE? + JR C,GOBOOTDISK1 ; NOT SLICE WORTHY, SKIP AHEAD +; + LD DE,STR_SLICESEL ; SLICE SELECTION STRING + CALL WRITESTR ; DISPLAY IT + CALL CINUC ; GET THE KEY + CALL COUT ; ECHO KEY +; + LD DE,STR_INVALID ; SETUP IN CASE OF INVALID + CP 13 ; ENTER? + JR Z,GOBOOTDISK1 ; IF SO, DONE + CP '0' ; START OF RANGE? + JP C,MENU ; BACK TO MENU IF TOO LOW + CP '9' + 1 ; END OF RANGE + JP NC,MENU ; BACK TO MENU IF TOO HIGH + SUB '0' ; CONVERT TO BINARY + LD (BL_LU),A ; AND SAVE IT +GOBOOTDISK1: +; +#ENDIF +; LD DE,STR_BOOTDISK CALL WRITESTR - CALL PRTHEXBYTE + LD A,(BL_DEVICE) + CALL PRTDECB + LD DE,STR_BOOTDISK1 + CALL WRITESTR + LD A,(BL_LU) + CALL PRTDECB PRTS("...$") #IF (DSKYENABLE) LD HL,MSG_LOAD ; POINT TO LOAD MESSAGE CALL DSKY_SHOWSEG ; DISPLAY MESSAGE #ENDIF ; - LD DE,STR_BOOTDISK1 ; DISK BOOT MESSAGE + LD DE,STR_BOOTREAD ; DISK BOOT MESSAGE CALL WRITESTR ; PRINT IT ; #IF (BIOS == BIOS_UNA) @@ -543,12 +660,6 @@ GOBOOTDISK: POP AF ; RESTORE BOOT DEVICE CP E ; CHECK MAX (INDEX - COUNT) JP NC,DB_NODISK ; HANDLE INVALID SELECTION -; - ; SET THE BOOT UNIT AND SLICE - LD A,(BL_BOOTID) ; GET BOOTID - LD (BL_DEVICE),A ; STORE IT - XOR A ; LU ALWAYS ZERO - LD (BL_LU),A ; STORE IT ; ; SENSE MEDIA LD A,(BL_DEVICE) ; GET DEVICE/UNIT @@ -622,10 +733,19 @@ GOBOOTDISK: OR A ; CLEAR CARRY SBC HL,DE ; HL := LENGTH TO LOAD LD A,H ; DETERMINE 512 BYTE SECTOR COUNT - RRCA ; ... BY DIVIDING MSB BY TWO + RRA ; ... BY DIVIDING MSB BY TWO LD (BL_COUNT),A ; ... AND SAVE IT ; #IF (BIOS == BIOS_UNA) +; + ; START OS LOAD AT SECTOR 3 + LD C,$41 ; UNA FUNC: SET LBA + LD A,(BL_BOOTID) ; GET BOOT DEVICE ID + LD B,A ; MOVE TO B + LD DE,0 ; HI WORD OF LBA IS ALWAYS ZERO + LD HL,3 ; LOAD STARTING INFO SECTOR 2 + RST 08 ; SET LBA + JP NZ,DB_ERR ; HANDLE ERROR ; ; READ OS IMAGE INTO MEMORY LD C,$42 ; UNA FUNC: READ SECTORS @@ -736,8 +856,7 @@ PRTDRV: LD A,'(' ; NEWLINE AND SPACING CALL COUT ; PRINT IT LD A,B ; DRIVE LETTER TO A - ADD A,'0' ; MAKE IT DISPLAY NUMERIC - CALL COUT ; PRINT IT + CALL PRTDECB LD A,')' ; DRIVE LETTER COLON CALL COUT ; PRINT IT POP DE ; RECOVER DISK TYPE @@ -806,8 +925,7 @@ PRTALL1: LD A,'(' ; FORMATTING CALL COUT ; PRINT IT LD A,C ; INDEX TO A - ADD A,'0' ; MAKE NUMERIC CHAR - CALL COUT ; PRINT IT + CALL PRTDECB LD A,')' ; FORMATTING CALL COUT ; PRINT IT PUSH BC ; SAVE LOOP CONTROL @@ -882,14 +1000,16 @@ DEV15 .EQU DEVUNK ; STR_BANNER .DB "\r\n\r\n", PLATFORM_NAME, " Boot Loader$" STR_BOOTSEL .DB "\r\n\r\nBoot Selection? $" +STR_SLICESEL .DB " Slice(0-9)[0]? $" STR_BOOTDISK .DB "\r\n\r\nBooting Disk Unit $" +STR_BOOTDISK1 .DB ", Slice $" STR_BOOTROM .DB "\r\n\r\nLoading $" STR_REBOOT .DB "\r\n\r\nRestarting System...$" STR_INVALID .DB "\r\n\r\n*** Invalid Selection ***$" STR_NODISK .DB "\r\n\r\nNo disk!$" STR_NOBOOT .DB "\r\n\r\nDisk not bootable!$" STR_BOOTERR .DB "\r\n\r\nBoot failure!$" -STR_BOOTDISK1 .DB "\r\n\r\nReading disk information...$" +STR_BOOTREAD .DB "\r\n\r\nReading disk information...$" STR_LOADING .DB "\r\n\r\nLoading...$" ; #IF (DSKYENABLE) @@ -1076,6 +1196,7 @@ SLACK: .EQU ($8000 + LDR_SIZ - $) .DS 64 ; 32 LEVEL STACK BL_STACK .EQU $ ; ... TOP IS HERE ; +BL_INPFLG .DS 1 ; INPUT FLAG, 1=DSKY, 2=SERIAL BL_COUNT .DS 1 ; LOAD COUNTER BL_TIMEOUT .DS 2 ; AUTOBOOT TIMEOUT COUNTDOWN COUNTER BL_BOOTID .DS 1 ; BOOT DEVICE ID CHOSEN BY USER diff --git a/Source/HBIOS/sd.asm b/Source/HBIOS/sd.asm index 22e23c31..2930fa72 100644 --- a/Source/HBIOS/sd.asm +++ b/Source/HBIOS/sd.asm @@ -735,7 +735,7 @@ SD_RESET: CALL SD_SELUNIT ; SET CUR UNIT ; RE-INITIALIZE THE SD CARD TO ACCOMMODATE HOT SWAPPING CALL SD_INITCARD ; RE-INIT SELECTED UNIT -#IF (SDTRACE == 1) +#IF (SDTRACE >= 3) CALL SD_PRTERR ; PRINT ANY ERRORS #ENDIF OR A ; SET RESULT FLAGS diff --git a/Source/HBIOS/std.asm b/Source/HBIOS/std.asm index 4d0e2359..89f5fcfd 100644 --- a/Source/HBIOS/std.asm +++ b/Source/HBIOS/std.asm @@ -23,7 +23,7 @@ FALSE .EQU 0 TRUE .EQU ~FALSE ; -; DEBUGGNG OPTIONS +; DEBUGGING OPTIONS ; USENONE .EQU 0 ; NO DEBUG USEXIO .EQU 1 ; BASIC SERIAL DRIVER @@ -44,9 +44,6 @@ PLT_EZZ80 .EQU 9 ; EASY Z80 PLT_SCZ180 .EQU 10 ; SCZ180 PLT_DYNO .EQU 11 ; DYNO MICRO-ATX MOTHERBOARD ; -#IF (BIOS == BIOS_WBW) -#INCLUDE "hbios.inc" -#ENDIF ; ; CPU TYPES ; @@ -74,6 +71,16 @@ MM_Z180 .EQU 4 ; Z180 NATIVE MEMORY MANAGER BT_MENU .EQU 1 ; WAIT FOR MENU SELECTION AT LOADER PROMPT BT_AUTO .EQU 2 ; AUTO SELECT BOOT_DEFAULT AFTER BOOT_TIMEOUT ; +; BOOT RECOVERY METHODS +; +BT_REC_NONE .EQU 0 ; NO RECOVERY MODE +BT_REC_FORCE .EQU 1 ; FORCE BOOT RECOVERY MODE +BT_REC_SBC01 .EQU 2 ; ECB-SBCV2 - BIT 1 RTC HIGH +BT_REC_SBC1B .EQU 3 ; ECB-SBCV2 - 1-BIT IO PORT +BT_REC_SBCRI .EQU 4 ; ECB-SBCV2 - 16550 UART RING INDICATOR LINE +; +BT_REC_TYPE .EQU BT_REC_NONE ; BOOT RECOVERY METHOD TO USE +; ; FLOPPY DISK MEDIA SELECTIONS (ID'S MUST BE INDEX OF ENTRY IN FCD_TBL) ; FDM720 .EQU 0 ; 3.5" FLOPPY, 720KB, 2 SIDES, 80 TRKS, 9 SECTORS @@ -141,10 +148,8 @@ FDMODE_DYNO .EQU 9 ; DYNO WDC 37C65 @ $84 IDEMODE_NONE .EQU 0 IDEMODE_DIO .EQU 1 ; DISKIO V1 IDEMODE_DIDE .EQU 2 ; DUAL IDE -IDEMODE_MK4 .EQU 3 ; MARK IV ONBOARD IDE (8 BIT) -IDEMODE_RC .EQU 4 ; RC2014 CF MODULE (8 BIT) @ $10 (SPENCER OWEN) -IDEMODE_SMB .EQU 5 ; RC2014 IDE MODULE (8 BIT) @ $E0 (SCOTT BAKER) -IDEMODE_DYNO .EQU 6 ; DYNO IDE MODULE (8 BIT) @4A +IDEMODE_MK4 .EQU 3 ; MARK IV ONBOARD IDE (8 BIT ONLY) +IDEMODE_RC .EQU 4 ; RC2014 CF MODULE (8 BIT ONLY) ; ; PPIDE MODE SELECTIONS ; @@ -285,12 +290,23 @@ V80X30 .EQU 2 ; ECB-VDU, ECB-VGA3 V80X25B .EQU 3 ; ECB-VDU V80X24B .EQU 4 ; ECB-VDU V80X43 .EQU 5 ; ECB-VGA3 +V80X60 .EQU 6 ; ECB-VGA3 +; +; FONTS +; +USELZSA2 .EQU FALSE ; USE COMPRESSED FONTS. ; ; KEYBOARD LAYOUTS ; KBD_US .EQU 0 ; US ENGLISH KBD_DE .EQU 1 ; GERMAN ; +; EMULATION TYPES +; +EMUTYP_NONE .EQU 0 ; NONE +EMUTYP_TTY .EQU 1 ; TTY +EMUTYP_ANSI .EQU 2 ; ANSI +; ; DEVICE DRIVER TO BE INITIALIZED FIRST. FIRST CIO DRIVER, UNIT 0 INITIALIZED BECOMES PRIMARY CONSOLE. ; IS AN INDEX INTO THE ENABLED INITIALIZATION DRIVER LIST i.e. ASCI, UART, SIO, ACIA, PIO, UF ETC. ; EXAMPLE: IF ONLY UART, SIO AND PIO ARE ENABLE AND THE SIO IS DESIRED AS THE PRIMARY CONSOLE, @@ -300,6 +316,15 @@ FORCECON .EQU 0 ; DEFAULT IS TO FOLLOW NORMAL SEQUENCE ; #INCLUDE "build.inc" ; INCLUDE USER CONFIG, ADD VARIANT, TIMESTAMP, & ROMSIZE ; +#IF (BIOS == BIOS_WBW) +#INCLUDE "hbios.inc" +#ENDIF +; +#IF (BIOS == BIOS_UNA) +#INCLUDE "../UBIOS/ubios.inc" +#ENDIF +; +; ; INCLUDE Z180 REGISTER DEFINITIONS ; #IF (BIOS == BIOS_WBW) @@ -398,7 +423,7 @@ TBC_LOC .EQU $0A00 ; TASTYBASIC TBC_SIZ .EQU $0900 TBC_END .EQU TBC_LOC + TBC_SIZ -EGG_LOC .EQU $0A00 ; EASTER EGG +EGG_LOC .EQU $F000 ; EASTER EGG EGG_SIZ .EQU $0200 EGG_END .EQU EGG_LOC + EGG_SIZ @@ -406,6 +431,14 @@ FTH_LOC .EQU $0200 ; CAMEL FORTH FTH_SIZ .EQU $1700 FTH_END .EQU FTH_LOC + FTH_SIZ +GAM_LOC .EQU $0200 ; GAME 2048 +GAM_SIZ .EQU $0900 +GAM_END .EQU GAM_LOC + GAM_SIZ + +USR_LOC .EQU $0200 ; USER +USR_SIZ .EQU $1000 +USR_END .EQU USR_LOC + USR_SIZ + MON_DSKY .EQU MON_LOC + (0 * 3) ; MONITOR ENTRY (DSKY) MON_SERIAL .EQU MON_LOC + (1 * 3) ; MONITOR ENTRY (SERIAL PORT) ; diff --git a/Source/HBIOS/tastybasic.asm b/Source/HBIOS/tastybasic.asm index 3366737b..7d009815 100644 --- a/Source/HBIOS/tastybasic.asm +++ b/Source/HBIOS/tastybasic.asm @@ -40,6 +40,7 @@ TBC_LOC .equ 0 #else ; RomWBW #include "std.asm" #endif + .org TBC_LOC start: ld sp,stack ; ** Cold Start ** diff --git a/Source/HBIOS/tms.asm b/Source/HBIOS/tms.asm index ff9e0beb..2fb8b48a 100644 --- a/Source/HBIOS/tms.asm +++ b/Source/HBIOS/tms.asm @@ -42,6 +42,9 @@ TMS_PPIX .EQU 0 ; PPI CONTROL PORT TMS_ROWS .EQU 24 TMS_COLS .EQU 40 ; +#DEFINE USEFONT8X8 +#DEFINE TMS_FONT FONT8X8 +; TERMENABLE .SET TRUE ; INCLUDE TERMINAL PSEUDODEVICE DRIVER ; ; TMS_IODELAY IS USED TO ADD RECOVERY TIME TO TMS9918 ACCESSES @@ -366,31 +369,43 @@ TMS_LOADFONT: ; SET WRITE ADDRESS TO $800 LD HL,$800 CALL TMS_WR + +#IF USELZSA2 + LD (TMS_STACK),SP ; SAVE STACK + LD HL,(TMS_STACK) ; AND SHIFT IT + LD DE,$2000 ; DOWN 4KB TO + CCF ; CREATE A + SBC HL,DE ; DECOMPRESSION BUFFER + LD SP,HL ; HL POINTS TO BUFFER + EX DE,HL ; START OF STACK BUFFER + PUSH DE ; SAVE IT + LD HL,TMS_FONT ; START OF FONT DATA + CALL DLZSA2 ; DECOMPRESS TO DE + POP HL ; RECALL STACK BUFFER POSITION +#ELSE + LD HL,TMS_FONT ; START OF FONT DATA +#ENDIF ; ; FILL $800 BYTES FROM FONTDATA - LD HL,FONT_TMS - LD DE,$100 * 8 + LD DE,$800 TMS_LOADFONT1: - LD B,8 -TMS_LOADFONT2: LD A,(HL) - PUSH AF - INC HL - DJNZ TMS_LOADFONT2 -; - LD B,8 -TMS_LOADFONT3: - POP AF OUT (TMS_DATREG),A TMS_IODELAY ; DELAY + INC HL DEC DE - DJNZ TMS_LOADFONT3 -; LD A,D OR E JR NZ,TMS_LOADFONT1 ; +#IF USELZSA2 + LD HL,(TMS_STACK) ; ERASE DECOMPRESS BUFFER + LD SP,HL ; BY RESTORING THE STACK + RET ; DONE +TMS_STACK .DW 0 +#ELSE RET +#ENDIF ; ;---------------------------------------------------------------------- ; VIRTUAL CURSOR MANAGEMENT diff --git a/Source/HBIOS/uart.asm b/Source/HBIOS/uart.asm index b9a72422..d29ccc46 100644 --- a/Source/HBIOS/uart.asm +++ b/Source/HBIOS/uart.asm @@ -584,7 +584,7 @@ UART_CFG: .DB 0 ; UART TYPE .DB $80 ; IO PORT BASE (RBR, THR) .DB $80 + UART_LSR ; LINE STATUS PORT (LSR) - .DW SER_300_8N1 ; LINE CONFIGURATION + .DW UARTCASSPD ; LINE CONFIGURATION .FILL 2,$FF ; FILLER #ENDIF #IF (UARTMFP) diff --git a/Source/HBIOS/uf.asm b/Source/HBIOS/uf.asm index 4b13ecab..efc3d9b3 100644 --- a/Source/HBIOS/uf.asm +++ b/Source/HBIOS/uf.asm @@ -64,7 +64,7 @@ UF_INIT: OR A ; REQUIRES TERMINAL PROGRAM RET NZ ; TO HAVE INITIALIZED PORT CALL PRTSTRD ; ON PC SIDE. - .TEXT "No connection$" + .TEXT " No connection$" RET ; ; INPUT A CHARACTER AND RETURN IT IN E diff --git a/Source/HBIOS/unlzsa2s.asm b/Source/HBIOS/unlzsa2s.asm new file mode 100644 index 00000000..5709fc07 --- /dev/null +++ b/Source/HBIOS/unlzsa2s.asm @@ -0,0 +1,166 @@ +; +; Size-optimized LZSA2 decompressor by spke & uniabis (139 bytes) +; +; ver.00 by spke for LZSA 1.0.0 (02-09/06/2019, 145 bytes); +; ver.01 by spke for LZSA 1.0.5 (24/07/2019, added support for backward decompression); +; ver.02 by uniabis (30/07/2019, 144(-1) bytes, +3.3% speed and support for Hitachi HD64180); +; ver.03 by spke for LZSA 1.0.7 (01/08/2019, 140(-4) bytes, -1.4% speed and small re-organization of macros); +; ver.04 by spke for LZSA 1.1.0 (26/09/2019, removed usage of IY, added full revision history) +; ver.05 by spke for LZSA 1.1.1 (11/10/2019, 139(-1) bytes, +0.1% speed) +; ver.051 by PSummers (14/1/2020), ROMWBW version. +; +; The data must be compressed using the command line compressor by Emmanuel Marty +; The compression is done as follows: +; +; lzsa.exe -f2 -r +; +; where option -r asks for the generation of raw (frame-less) data. +; +; The decompression is done in the standard way: +; +; ld hl,FirstByteOfCompressedData +; ld de,FirstByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; Backward compression is also supported; you can compress files backward using: +; +; lzsa.exe -f2 -r -b +; +; and decompress the resulting files using: +; +; ld hl,LastByteOfCompressedData +; ld de,LastByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; (do not forget to uncomment the BACKWARD_DECOMPRESS option in the decompressor). +; +; Of course, LZSA2 compression algorithms are (c) 2019 Emmanuel Marty, +; see https://github.com/emmanuel-marty/lzsa for more information +; +; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; + +; DEFINE BACKWARD_DECOMPRESS ; uncomment for data compressed with option -b +; DEFINE HD64180 ; uncomment for systems using Hitachi HD64180 + + #IFNDEF BACKWARD_DECOMPRESS + + #DEFINE NEXT_HL \ + #DEFCONT \ inc hl + + #DEFINE ADD_OFFSET \ + #DEFCONT \ ex de,hl \ add hl,de + + #DEFINE BLOCKCOPY \ + #DEFCONT \ ldir + + #ELSE + + #DEFINE NEXT_HL \ + #DEFCONT \ dec hl + + #DEFINE ADD_OFFSET \ + #DEFCONT \ push hl \ or a \ sbc hl,de \ pop de + + #DEFINE BLOCKCOPY \ + #DEFCONT \ lddr + + #ENDIF + + #IFDEF HD64180 + + .ECHO "HD64180 " + + #DEFINE LD_IX_DE \ + #DEFCONT \ ld ixl,e \ ld ixh,d + + #DEFINE LD_DE_IX \ + #DEFCONT \ ld e,ixl \ ld d,ixh + + #ELSE + + .ECHO "Z80 " + + #DEFINE LD_IX_DE \ + #DEFCONT \ push de \ pop ix + + #DEFINE LD_DE_IX \ + #DEFCONT \ push ix \ pop de + + #ENDIF + +DLZSA2: + xor a \ ld b,a \ ex af,af' \ jr ReadToken + +CASE00x: call ReadNibble + ld e,a \ ld a,c + cp %00100000 \ rl e \ jr SaveOffset + +CASE0xx ld d,$FF \ cp %01000000 \ jr c,CASE00x + +CASE01x: cp %01100000 \ rl d + +OffsetReadE: ld e,(hl) \ NEXT_HL + +SaveOffset: LD_IX_DE + +MatchLen: and %00000111 \ add a,2 \ cp 9 \ call z,ExtendedCode + +CopyMatch: ld c,a + ex (sp),hl ; BC = len, DE = -offset, HL = dest, SP -> [src] + ADD_OFFSET ; BC = len, DE = dest, HL = dest+(-offset), SP -> [src] + BLOCKCOPY ; BC = 0, DE = dest + pop hl ; HL = src + +ReadToken: ld a,(hl) \ NEXT_HL \ push af + and %00011000 \ jr z,NoLiterals + + rrca \ rrca \ rrca + call pe,ExtendedCode + + ld c,a + BLOCKCOPY + +NoLiterals: pop af \ push de + or a \ jp p,CASE0xx + +CASE1xx: cp %11000000 \ jr nc,CASE11x + +CASE10x: call ReadNibble + ld d,a \ ld a,c + cp %10100000 ;: rl d + dec d \ rl d \ .DB $CA ; jr OffsetReadE ; #CA is JP Z,.. to skip all commands in CASE110 before jr OffsetReadE + +CASE110: ld d,(hl) \ NEXT_HL \ jr OffsetReadE + +CASE11x: cp %11100000 \ jr c,CASE110 + +CASE111: LD_DE_IX \ jr MatchLen + +ExtendedCode: call ReadNibble \ inc a \ jr z,ExtraByte + sub $F0+1 \ add a,c \ ret +ExtraByte ld a,15 \ add a,c \ add a,(hl) \ NEXT_HL \ ret nc + ld a,(hl) \ NEXT_HL + ld b,(hl) \ NEXT_HL \ ret nz + pop de \ pop de ; RET is not needed, because RET from ReadNibble is sufficient + +ReadNibble: ld c,a \ xor a \ ex af,af' \ ret m +UpdateNibble ld a,(hl) \ or $F0 \ ex af,af' + ld a,(hl) \ NEXT_HL \ or $0F + rrca \ rrca \ rrca \ rrca \ ret diff --git a/Source/HBIOS/usrrom.asm b/Source/HBIOS/usrrom.asm new file mode 100644 index 00000000..5d4ff8e7 --- /dev/null +++ b/Source/HBIOS/usrrom.asm @@ -0,0 +1,80 @@ +; +; USRROM TEMPLATE FOR A CUSTOM USER ROM +; +#INCLUDE "std.asm" +; +CR .EQU 0DH +LF .EQU 0AH +; + .ORG USR_LOC +; + LD HL,BOOTMSG ; INTRODUCTION + CALL PRTSTR + + CALL CIN ; DO STUFF +; + LD A,00H ; RETURN TO ROM LOADER + LD HL,0000H + JP 0FFF9H +; RET +; +; +; +; PRINT A STRING AT ADDRESS SPECIFIED IN HL +; STRING MUST BE TERMINATED BY '$' +; USAGE: +; LD HL,MYSTR +; CALL PRTSTR +; ... +; MYSTR: .DB "HELLO$" +; +PRTSTR: LD A,(HL) + INC HL + CP '$' + RET Z + CALL COUT + JR PRTSTR +; +; OUTPUT CHARACTER IN A TO CONSOLE DEVICE +; +COUT: PUSH AF + PUSH BC + PUSH DE + LD B,01H + LD C,0 + LD E,A + RST 08 + POP DE + POP BC + POP AF + RET +; +; OUTPUT CHARACTER IN A TO CONSOLE DEVICE +; +COUTE: PUSH AF + LD A,E + CALL COUT + POP AF + RET +; +; WAIT FOR A CHARACTER FROM THE CONSOLE DEVICE AND RETURN IT IN A +; +CIN: PUSH BC + LD B,00H + LD C,00H + RST 08 + LD A,E + POP BC + RET +; +BOOTMSG:.DB "No User ROM Installed." + .DB CR,LF + .DB "Press a key to return to Boot Loader.$" +; +SLACK .EQU (USR_END - $) + .FILL SLACK,00 + .ECHO "User ROM space remaining: " + .ECHO SLACK + .ECHO " bytes.\n" + .END + diff --git a/Source/HBIOS/ver.inc b/Source/HBIOS/ver.inc index ed96d795..8320a3c2 100644 --- a/Source/HBIOS/ver.inc +++ b/Source/HBIOS/ver.inc @@ -2,4 +2,4 @@ #DEFINE RMN 9 #DEFINE RUP 2 #DEFINE RTP 0 -#DEFINE BIOSVER "2.9.2-pre.25" +#DEFINE BIOSVER "2.9.2-pre.33" diff --git a/Source/HBIOS/vga.asm b/Source/HBIOS/vga.asm index e6875dbb..5a285376 100644 --- a/Source/HBIOS/vga.asm +++ b/Source/HBIOS/vga.asm @@ -31,21 +31,42 @@ VGA_ULIN .EQU 1 ; UNDERLINE CURSOR ; VGA_CSTY .EQU VGA_BLOK ; DEFAULT CURSOR STYLE VGA_BLNK .EQU VGA_NOBL ; DEFAULT BLINK RATE +VGA_9BIT .EQU $0101 ; 9 BIT MSK-CFG +VGA_8BIT .EQU $0000 ; 8 BIT MSK-CFG + +VGA_NICE .EQU FALSE ; TRUE = SLOW BUT PRETTY ; #IF (VGASIZ=V80X25) VGA_ROWS .EQU 25 VGA_COLS .EQU 80 VGA_SCANL .EQU 16 +VGA_89BIT .EQU VGA_8BIT +#DEFINE USEFONT8X16 +#DEFINE VGA_FONT FONT8X16 #ENDIF #IF (VGASIZ=V80X30) VGA_ROWS .EQU 30 VGA_COLS .EQU 80 VGA_SCANL .EQU 16 +VGA_89BIT .EQU VGA_8BIT +#DEFINE USEFONT8X16 +#DEFINE VGA_FONT FONT8X16 #ENDIF #IF (VGASIZ=V80X43) VGA_ROWS .EQU 43 VGA_COLS .EQU 80 VGA_SCANL .EQU 11 +VGA_89BIT .EQU VGA_8BIT +#DEFINE USEFONT8X11 +#DEFINE VGA_FONT FONT8X11 +#ENDIF +#IF (VGASIZ=V80X60) +VGA_ROWS .EQU 60 +VGA_COLS .EQU 80 +VGA_SCANL .EQU 8 +VGA_89BIT .EQU VGA_8BIT +#DEFINE USEFONT8X8 +#DEFINE VGA_FONT FONT8X8 #ENDIF ; #IF VGA_CSTY=VGA_BLOK @@ -168,12 +189,12 @@ VGA_VDARES: LD DE,0 ; ROW = 0, COL = 0 CALL VGA_XY ; SEND CURSOR TO TOP LEFT LD A,' ' ; BLANK THE SCREEN - LD DE,(VGA_ROWS*VGA_COLS) ; FILL ENTIRE BUFFER + LD DE,VGA_ROWS*VGA_COLS ; FILL ENTIRE BUFFER CALL VGA_FILL ; DO IT LD DE,0 ; ROW = 0, COL = 0 CALL VGA_XY ; SEND CURSOR TO TOP LEFT - LD HL,$0404 ; SET VIDEO ENABLE BIT + LD HL,$0404 | VGA_89BIT; SET VIDEO ENABLE BIT CALL VGA_SETCFG ; DO IT XOR A @@ -450,7 +471,7 @@ VGA_PROBE: ;---------------------------------------------------------------------- ; VGA_CRTINIT: - LD HL,$FF00 ; ZERO ALL CFG BITS + LD HL,$FF00 | VGA_89BIT ; INITIAL CFG BITS CALL VGA_SETCFG ; DO IT CALL VGA_RES ; RESET CRTC (ALL REGS TO ZERO) @@ -491,24 +512,57 @@ VGA_CRTCDUMP1: ;---------------------------------------------------------------------- ; LOAD FONT DATA ;---------------------------------------------------------------------- + ; VGA_LOADFONT: - LD HL,$7000 ; CLEAR FONT PAGE NUM + LD HL,$7000 | VGA_89BIT ; CLEAR FONT PAGE NUM CALL VGA_SETCFG - LD DE,$7000 ; PAGE 7 OF VIDEO RAM +#IF USELZSA2 + LD (VGA_STACK),SP ; SAVE STACK + LD HL,(VGA_STACK) ; AND SHIFT IT + LD DE,$2000 ; DOWN 4KB TO + CCF ; CREATE A + SBC HL,DE ; DECOMPRESSION BUFFER + LD SP,HL ; HL POINTS TO BUFFER + EX DE,HL ; START OF STACK BUFFER + PUSH DE ; SAVE IT + LD HL,VGA_FONT ; START OF FONT DATA + CALL DLZSA2 ; DECOMPRESS TO DE + POP HL ; RECALL STACK BUFFER POSITION +#ELSE LD HL,VGA_FONT ; START OF FONT DATA +#ENDIF + + LD DE,$7000 ; PAGE 7 OF VIDEO RAM VGA_LOADFONT1: + LD B,VGA_SCANL ; # BYTES FOR EACH CHAR +VGA_LOADFONT2: LD A,(HL) ; GET NEXT BYTE CALL VGA_MEMWR ; MEM(DE) := A INC HL ; NEXT FONT BYTE INC DE ; NEXT MEM BYTE + DJNZ VGA_LOADFONT2 + + LD BC,16-VGA_SCANL ; MOVE TO NEXT + EX DE,HL ; 16 BYTE + ADD HL,BC ; CHARACTER + EX DE,HL + LD A,D CP $80 ; CHECK FOR END JR NZ,VGA_LOADFONT1 ; LOOP - LD HL,$7070 ; SET FONT PAGE NUM TO 7 + LD HL,$7070 | VGA_89BIT ; SET FONT PAGE NUM TO 7 CALL VGA_SETCFG + +#IF USELZSA2 + LD HL,(VGA_STACK) ; ERASE DECOMPRESS BUFFER + LD SP,HL ; BY RESTORING THE STACK RET ; DONE +VGA_STACK .DW 0 +#ELSE + RET +#ENDIF ; ;---------------------------------------------------------------------- ; SET CURSOR POSITION TO ROW IN D AND COLUMN IN E @@ -561,7 +615,9 @@ VGA_PUTCHAR: LD A,(VGA_ATTR) ; ATTRIBUTE LD L,A ; ... TO L ; WRITE CHAR & ATTR - ;CALL VGA_WAITSB ; WAIT FOR RETRACE +#IF (VGA_NICE) + CALL VGA_WAITSB ; WAIT FOR RETRACE +#ENDIF CALL VGA_MEMWRX ; UPDATE CURRENT POSITION LD HL,(VGA_POS) ; GET CURSOR POSITION @@ -608,7 +664,9 @@ VGA_FILL1: DEC C ; C := VGA ADDR LO OUT (C),L ; UDPATE LO ADDR INC C ; POINT TO DATA REG - ;CALL VGA_WAITSB ; WAIT FOR RETRACE +#IF (VGA_NICE) + CALL VGA_WAITSB ; WAIT FOR RETRACE +#ENDIF LD A,(VGA_ATTR) ; GET CUR ATTR OUT (C),A ; OUTPUT ATTR @@ -772,7 +830,9 @@ VGA_BLKCPY: EX DE,HL ; SWAP BACK VGA_BLKCPY1: - ;CALL VGA_WAITSB ; WAIT FOR RETRACE +#IF (VGA_NICE) + CALL VGA_WAITSB ; WAIT FOR RETRACE +#ENDIF ; GET NEXT SOURCE BYTE LD C,VGA_HI ; C := VGA_HI @@ -865,19 +925,19 @@ VGA_RUB .DB 0 ; REVERSE/UNDERLINE/BLINK (-----RUB) ; #IF (VGASIZ=V80X25) ;=============================================================================== -; 80x25 70hz REGISTER VALUES +; 80x25x8 70hz REGISTER VALUES ;=============================================================================== ; REGS_VGA: .DB 0,100 - 1 ; HORZ TOT - 1 - .DB 1,80 ; HORZ DISP - .DB 2,80 + 2 ; HORZ DISP + HORZ FP + .DB 1,VGA_COLS ; HORZ DISP + .DB 2,VGA_COLS + 2 ; HORZ DISP + HORZ FP .DB 3,(2 << 4) | (12 & $0F) ; VERT SW, HORZ SW .DB 4,28 - 1 ; VERT TOT - 1 .DB 5,1 ; VERT TOT ADJ - .DB 6,25 ; VERT DISP - .DB 7,25 + 0 ; VERT DISP + VERT FP ROWS - .DB 9,16 - 1 ; CHAR HEIGHT - 1 + .DB 6,VGA_ROWS ; VERT DISP + .DB 7,VGA_ROWS + 0 ; VERT DISP + VERT FP ROWS + .DB 9,VGA_SCANL - 1 ; CHAR HEIGHT - 1 .DB 10,VGA_R10 ; CURSOR START & CURSOR BLINK .DB 11,VGA_R11 ; CURSOR END .DB 12,($0000 >> 8) & $FF ; SCRN 1 START (HI) @@ -890,19 +950,19 @@ REGS_VGA: #ENDIF #IF (VGASIZ=V80X30) ;=============================================================================== -; 80x30 60hz REGISTER VALUES +; 80x30x8 60hz REGISTER VALUES ;=============================================================================== ; REGS_VGA: .DB 0,100 - 1 ; HORZ TOT - 1 - .DB 1,80 ; HORZ DISP - .DB 2,80 + 2 ; HORZ DISP + HORZ FP + .DB 1,VGA_COLS ; HORZ DISP + .DB 2,VGA_COLS + 2 ; HORZ DISP + HORZ FP .DB 3,44 ; VERT SW, HORZ SW .DB 4,33 - 1 ; VERT TOT - 1 .DB 5,13 ; VERT TOT ADJ - .DB 6,30 ; VERT DISP - .DB 7,30 + 0 ; VERT DISP + VERT FP ROWS - .DB 9,16 - 1 ; CHAR HEIGHT - 1 + .DB 6,VGA_ROWS ; VERT DISP + .DB 7,VGA_ROWS + 0 ; VERT DISP + VERT FP ROWS + .DB 9,VGA_SCANL - 1 ; CHAR HEIGHT - 1 .DB 10,VGA_R10 ; CURSOR START & CURSOR BLINK .DB 11,VGA_R11 ; CURSOR END .DB 12,0 ; SCRN 1 START (HI) @@ -914,19 +974,43 @@ REGS_VGA: #ENDIF #IF (VGASIZ=V80X43) ;=============================================================================== -; 80x43 60hz REGISTER VALUES +; 80x43x8 60hz REGISTER VALUES ;=============================================================================== ; REGS_VGA: .DB 0,100 - 1 ; HORZ TOT - 1 - .DB 1,80 ; HORZ DISP - .DB 2,80 + 2 ; HORZ DISP + HORZ FP + .DB 1,VGA_COLS ; HORZ DISP + .DB 2,VGA_COLS + 2 ; HORZ DISP + HORZ FP .DB 3,44 ; VERT SW, HORZ SW .DB 4,47 - 1 ; VERT TOT - 1 .DB 5,8 ; VERT TOT ADJ - .DB 6,43 ; VERT DISP - .DB 7,43 + 0 ; VERT DISP + VERT FP ROWS - .DB 9,11 - 1 ; CHAR HEIGHT - 1 + .DB 6,VGA_ROWS ; VERT DISP + .DB 7,VGA_ROWS + 0 ; VERT DISP + VERT FP ROWS + .DB 9,VGA_SCANL - 1 ; CHAR HEIGHT - 1 + .DB 10,VGA_R10 ; CURSOR START & CURSOR BLINK + .DB 11,VGA_R11 ; CURSOR END + .DB 12,0 ; SCRN 1 START (HI) + .DB 13,0 ; SCRN 1 START (LO) + .DB 18,-1 ; S2 ROW - 1 + .DB 27,0 ; VERT SYNC POS ADJ + .DB 30,$01 | $08 ; CTL 1, 2 WINDOWS & ENABLE R27 VSYNC FINE ADJ + .DB $FF ; END MARKER +#ENDIF +#IF (VGASIZ=V80X60) +;=============================================================================== +; 80x60X8 60hz REGISTER VALUES +;=============================================================================== +; +REGS_VGA: + .DB 0,100 - 1 ; HORZ TOT - 1 + .DB 1,VGA_COLS ; HORZ DISP + .DB 2,VGA_COLS + 2 ; HORZ DISP + HORZ FP + .DB 3,44 ; VERT SW, HORZ SW + .DB 4,66 - 1 ; VERT TOT - 1 + .DB 5,0 ; VERT TOT ADJ + .DB 6,VGA_ROWS ; VERT DISP + .DB 7,VGA_ROWS + 0 ; VERT DISP + VERT FP ROWS + .DB 9,VGA_SCANL - 1 ; CHAR HEIGHT - 1 .DB 10,VGA_R10 ; CURSOR START & CURSOR BLINK .DB 11,VGA_R11 ; CURSOR END .DB 12,0 ; SCRN 1 START (HI) diff --git a/Source/Images/Build.cmd b/Source/Images/Build.cmd index 4d38b33c..ac05ba65 100644 --- a/Source/Images/Build.cmd +++ b/Source/Images/Build.cmd @@ -5,11 +5,27 @@ echo : echo : Cleaning... echo : call Clean.cmd + echo : echo : Building Floppy Disk Images... echo : -call BuildFD.cmd +call BuildFD.cmd cpm22 ..\cpm22\cpm_wbw.sys +call BuildFD.cmd zsdos ..\zsdos\zsys_wbw.sys +call BuildFD.cmd nzcom ..\zsdos\zsys_wbw.sys +call BuildFD.cmd cpm3 ..\cpm3\cpmldr.sys +call BuildFD.cmd zpm3 ..\cpm3\cpmldr.sys +call BuildFD.cmd ws4 + echo : echo : Building Hard Disk Images... echo : -call BuildHD.cmd +call BuildHD.cmd cpm22 ..\cpm22\cpm_wbw.sys +call BuildHD.cmd zsdos ..\zsdos\zsys_wbw.sys +call BuildHD.cmd nzcom ..\zsdos\zsys_wbw.sys +call BuildHD.cmd cpm3 ..\cpm3\cpmldr.sys +call BuildHD.cmd zpm3 ..\cpm3\cpmldr.sys +call BuildHD.cmd ws4 + +if exist ..\BPBIOS\bpbio-ww.rel call BuildHD.cmd bp + +copy /b ..\..\Binary\hd_cpm22.img + ..\..\Binary\hd_zsdos.img + ..\..\Binary\hd_nzcom.img + ..\..\Binary\hd_cpm3.img + ..\..\Binary\hd_zpm3.img + ..\..\Binary\hd_ws4.img ..\..\Binary\hd_combo.img diff --git a/Source/Images/BuildFD.ps1 b/Source/Images/BuildFD.ps1 index ac5a4268..f482bc10 100644 --- a/Source/Images/BuildFD.ps1 +++ b/Source/Images/BuildFD.ps1 @@ -1,33 +1,69 @@ +#Param([Parameter(Mandatory)]$Disk, $SysFile="") +Param($Disk, $SysFile="") + $ErrorAction = 'Stop' +$ImgFile = "fd_${Disk}.img" +$Fmt = "wbw_fd144" +$Size = 1440KB + $CpmToolsPath = '../../Tools/cpmtools' $env:PATH = $CpmToolsPath + ';' + $env:PATH -$Blank = ([byte[]](0xE5) * 1440KB) +if (-not (Test-Path("d_${Disk}/"))) +{ + "Source directory d_${Disk} for disk ${Disk} not found!" + return +} -"Creating work file..." -if (!(Test-Path('Blank.tmp'))) {Set-Content -Value $Blank -Encoding byte -Path 'Blank.tmp'} +"Generating Floppy Disk ${Disk}..." + +#$Blank = ([string]([char]0xE5)) * $Size +#Set-Content -Value $Blank -NoNewLine -Path $ImgFile +$Blank = ([byte[]](0xE5) * $Size) +[System.IO.File]::WriteAllBytes($ImgFile, $Blank) + +if ($SysFile.Length -gt 0) +{ + "Adding System Image $SysFile..." + #$Sys = Get-Content -Path "$SysFile.sys" -Raw + #$Img = Get-Content -Path $ImgFile -Raw + #$NewImg = $Sys + $Img.SubString($Sys.Length, $Img.Length - $Sys.Length) + #Set-Content -NoNewLine -Path $ImgFile $NewImg + + $Cmd = "mkfs.cpm -f $Fmt -b $SysFile $ImgFile" + $Cmd + Invoke-Expression $Cmd +} + +for ($Usr=0; $Usr -lt 16; $Usr++) +{ + if (Test-Path ("d_${Disk}/u${Usr}/*")) + { + $Cmd = "cpmcp -f $Fmt $ImgFile d_${Disk}/u${Usr}/*.* ${Usr}:" + $Cmd + Invoke-Expression $Cmd + } +} -"Creating floppy disk images..." -foreach ($Dsk in @("cpm3","cpm22","nzcom","ws4","zpm3","zsdos")) +if (Test-Path("d_${Disk}.txt")) { - "Generating Floppy Disk ${Dsk}..." - copy "Blank.tmp" "fd_${Dsk}.img" - for ($Usr=0; $Usr -lt 16; $Usr++) + foreach($Line in Get-Content "d_${Disk}.txt") { - if (Test-Path ("d_${Dsk}/u${Usr}/*")) + $Spec = $Line.Trim() + if (($Spec.Length -gt 0) -and ($Spec.Substring(0,1) -ne "#")) { - $Cmd = "cpmcp -f wbw_fd144 fd_${Dsk}.img d_${Dsk}/u${Usr}/*.* ${Usr}:" + $Cmd = "cpmcp -f $Fmt $ImgFile ${Spec}" $Cmd Invoke-Expression $Cmd } } } -"Moving images into output directory..." -&$env:COMSPEC /c move fd_*.img ..\..\Binary\ +"Moving image $ImgFile into output directory..." -Remove-Item *.tmp +#&$env:COMSPEC /c move $ImgFile ..\..\Binary\ +Move-Item $ImgFile -Destination "..\..\Binary\" -Force return \ No newline at end of file diff --git a/Source/Images/BuildHD.ps1 b/Source/Images/BuildHD.ps1 index a45b98f3..eedfa4aa 100644 --- a/Source/Images/BuildHD.ps1 +++ b/Source/Images/BuildHD.ps1 @@ -1,33 +1,69 @@ +#Param([Parameter(Mandatory)]$Disk, $SysFile="") +Param($Disk, $SysFile="") + $ErrorAction = 'Stop' +$ImgFile = "hd_${Disk}.img" +$Fmt = "wbw_hd0" +$Size = (128KB * 65) + $CpmToolsPath = '../../Tools/cpmtools' $env:PATH = $CpmToolsPath + ';' + $env:PATH -$Blank = ([byte[]](0xE5) * (128KB * 65)) +if (-not (Test-Path("d_${Disk}/"))) +{ + "Source directory d_${Disk} for disk ${Disk} not found!" + return +} -"Creating work file..." -if (!(Test-Path('Blank.tmp'))) {Set-Content -Value $Blank -Encoding byte -Path 'Blank.tmp'} +"Generating Hard Disk ${Disk}..." + +#$Blank = ([string]([char]0xE5)) * $Size +#Set-Content -Value $Blank -NoNewLine -Path $ImgFile +$Blank = ([byte[]](0xE5) * $Size) +[System.IO.File]::WriteAllBytes($ImgFile, $Blank) + +if ($SysFile.Length -gt 0) +{ + "Adding System Image $SysFile..." + #$Sys = Get-Content -Path "$SysFile.sys" -Raw + #$Img = Get-Content -Path $ImgFile -Raw + #$NewImg = $Sys + $Img.SubString($Sys.Length, $Img.Length - $Sys.Length) + #Set-Content -NoNewLine -Path $ImgFile $NewImg + + $Cmd = "mkfs.cpm -f $Fmt -b $SysFile $ImgFile" + $Cmd + Invoke-Expression $Cmd +} + +for ($Usr=0; $Usr -lt 16; $Usr++) +{ + if (Test-Path ("d_${Disk}/u${Usr}/*")) + { + $Cmd = "cpmcp -f $Fmt $ImgFile d_${Disk}/u${Usr}/*.* ${Usr}:" + $Cmd + Invoke-Expression $Cmd + } +} -"Creating hard disk images..." -foreach ($Dsk in @("cpm3","cpm22","nzcom","ws4","zpm3","zsdos")) +if (Test-Path("d_${Disk}.txt")) { - "Generating Hard Disk ${Dsk}..." - copy "Blank.tmp" "hd_${Dsk}.img" - for ($Usr=0; $Usr -lt 16; $Usr++) + foreach($Line in Get-Content "d_${Disk}.txt") { - if (Test-Path ("d_${Dsk}/u${Usr}/*")) + $Spec = $Line.Trim() + if (($Spec.Length -gt 0) -and ($Spec.Substring(0,1) -ne "#")) { - $Cmd = "cpmcp -f wbw_hd0 hd_${Dsk}.img d_${Dsk}/u${Usr}/*.* ${Usr}:" + $Cmd = "cpmcp -f $Fmt $ImgFile ${Spec}" $Cmd Invoke-Expression $Cmd } } } -"Moving images into output directory..." -&$env:COMSPEC /c move hd_*.img ..\..\Binary\ +"Moving image $ImgFile into output directory..." -Remove-Item *.tmp +#&$env:COMSPEC /c move $ImgFile ..\..\Binary\ +Move-Item $ImgFile -Destination "..\..\Binary\" -Force return \ No newline at end of file diff --git a/Source/Images/Clean.cmd b/Source/Images/Clean.cmd index ae874cd3..026d5786 100644 --- a/Source/Images/Clean.cmd +++ b/Source/Images/Clean.cmd @@ -3,3 +3,4 @@ setlocal if exist *.tmp del *.tmp if exist *.img del *.img +if exist *.sys del *.sys diff --git a/Source/Images/Makefile b/Source/Images/Makefile new file mode 100644 index 00000000..b7953099 --- /dev/null +++ b/Source/Images/Makefile @@ -0,0 +1,101 @@ +# +# this makefile subsumes all the work done in Build.cmd, Build{Hd,Fd}.* +# +SYSTEMS = ../CPM22/cpm_wbw.sys ../ZSDOS/zsys_wbw.sys ../CPM3/cpmldr.sys + +FDIMGS = fd_cpm22.img fd_zsdos.img fd_nzcom.img \ + fd_cpm3.img fd_zpm3.img fd_ws4.img +HDIMGS = hd_cpm22.img hd_zsdos.img hd_nzcom.img \ + hd_cpm3.img hd_zpm3.img hd_ws4.img hd_bp.img + +OBJECTS = $(FDIMGS) $(HDIMGS) hd_combo.img +OTHERS = blank144 blankhd + +DEST=../../Binary + +TOOLS = ../../Tools +include $(TOOLS)/Makefile.inc + +DIFFPATH = $(DIFFTO)/Binary + +hd_combo.img: $(HDIMGS) + cat $(HDIMGS) > $@ + +# +# this somewhat impenetrable and fragile code is used to build each of the images +# at build time, a few variables are set (sys, fmt, type, size, d) based on the +# target to build. first, we build an empty image using the a tr, dd pipeline. +# we then scan the d_{d}/u* directories, copying in files to user numbers +# then process the d_{d}.txt file, copying in those files, and finally maybe put +# an OS at the start of each image +# +blank144: + @echo Making Blank Floppy of size 1440k + @LANG=en_US.US-ASCII tr '\000' '\345' /dev/null + +HDSIZE := $(shell expr 128 '*' 65) + +blankhd: + @echo Making Blank Hd of size $(HDSIZE)k + @LANG=en_US.US-ASCII tr '\000' '\345' /dev/null + +%.img: $(SYSTEMS) blank144 blankhd Makefile + @sys= ; \ + case $@ in \ + (*cpm22*) sys=../CPM22/cpm_wbw.sys;; \ + (*zsdos* | *nzcom*) sys=../ZSDOS/zsys_wbw.sys;; \ + (*cpm3* | *zpm3*) sys=../CPM3/cpmldr.sys;; \ + esac ; \ + if echo $@ | grep -q ^f ; then \ + fmt=wbw_fd144 ; type=fd_ ; proto=blank144 ; \ + else \ + fmt=wbw_hd0 ; type=hd_ ; proto=blankhd ; \ + fi ; \ + d=$$(echo $(basename $@) | sed s/$$type//) ; \ + echo Generating $@ ; \ + cp $$proto $@ ; \ + if [ "$$sys" ] ; then \ + echo copying system $$sys to $@ ; \ + $(BINDIR)/mkfs.cpm -f $$fmt -b $$sys $@ ; \ + fi ; \ + for u in $$(seq 0 15) ; do \ + dir=d_$$d/u$$u ; \ + if [ -d $$dir ] ; then \ + echo " " copying directory $$dir ; \ + for i in $$dir/* ; do \ + f=$$($(CASEFN) $$i) ; \ + echo " " $$f ; \ + $(CPMCP) -f $$fmt $@ $$f $$u: ; \ + done ; \ + fi ; \ + done ; \ + if [ -f d_$$d.txt ] ; then \ + echo " " copying files from d_$$d.txt ; \ + grep -v ^# d_$$d.txt | tr -d '\r' | while read file user ; do \ + hack= ; \ + if [ "$$file" = "../../Binary/Apps/Tunes/*.*" ] ; then hack=../../Binary/Apps/Tunes/Makefile ; fi ; \ + rf=$$($(CASEFN) $$hack $$file | sort -V) ; \ + echo " " $$rf ; \ + if [ -z "$$rf" ] ; then \ + echo " " $$file missing ; \ + else \ + $(CPMCP) -f $$fmt $@ $$rf $$user ; \ + fi ; \ + done ; \ + fi ; \ + + +clean:: + @rm -f *.ls + +imgdiff: + @for i in $(FDIMGS) $(HDIMGS) ; do \ + echo $$i ; \ + if echo $$i | grep -q ^f ; then \ + fmt=wbw_fd144 ; \ + else \ + fmt=wbw_hd0 ; \ + fi ; \ + $(BINDIR)/cpmls -i -f $$fmt $$i > $$i.ls ; \ + $(BINDIR)/cpmls -i -f $$fmt $(DIFFPATH)/$$i > $$i.diff.ls ; \ + done \ diff --git a/Source/Images/ReadMe.txt b/Source/Images/ReadMe.txt index 2c68f5eb..b4fea94a 100644 --- a/Source/Images/ReadMe.txt +++ b/Source/Images/ReadMe.txt @@ -57,8 +57,8 @@ command prompt. Build.cmd in turn invokes separate scripts to create the floppy and hard disk images. As distributed, you will see that there are several d_ directories -populated with files. If you look at the BuildFD.ps1 and BuildHD.ps1 -scripts, you will find that the names of each of these directories is +populated with files. If you look at the Build.cmd +script, you will find that the names of each of these directories is listed. If you want to add a new d_ directory to be converted into a disk image, you will need to add the name of your new directory to this list. Note that each d_ directory may be turned into a floppy @@ -75,49 +75,339 @@ described above and builds a raw disk image for each floppy disk or hard disk. Note that cpmtools is used to generate the images and is included in the distribution under the Tools directory. +Many of the disk images depend upon files that are produced by +building the shared components of RomWBW. Prior to running +the Build command in the Images directory, you should first +run the BuildShared command in the Source directory. + The scripts are intended to be run from a command prompt. Open a -command prompt and navigate to the Images directory. To build the -floppy disk images, use the command "BuildFD". To build the hard disk -images, use the command "BuildHD". You can use the command "Build" -to build both the floppy and hard disk images in one run. +command prompt and navigate to the Images directory. Use the command +"Build" to build both the floppy and hard disk images in one run. +You can build a single disk image by running either BuildFD.cmd or +BuildHD.cmd with a single parameter specifying the disk name. After completion of the script, the resultant image files are placed in the Binary directory with names such as fd_xxx.img and hd_xxx.img. Below is sample output from building the hard disk images: - | C:\Users\Wayne\Projects\RBC\Build\RomWBW\Source\Images>BuildHD.cmd - | Creating work file... - | Creating hard disk images... - | Generating Hard Disk cpm3... - | cpmcp -f wbw_hd0 hd_cpm3.img d_cpm3/u0/*.* 0: +C:\Users\Wayne\Projects\RBC\Build\RomWBW\Source\Images>Build.cmd + | : + | : Cleaning... + | : + | : + | : Creating System Images + | : + | ..\bl\bl.bin + | ..\cpm22\os2ccp.bin + | ..\cpm22\os3bdos.bin + | ..\cbios\cbios_wbw.bin + | 1 file(s) copied. + | ..\bl\bl.bin + | ..\cpm22\os2ccp.bin + | ..\cpm22\os3bdos.bin + | ..\cbios\cbios_una.bin + | 1 file(s) copied. + | ..\bl\bl.bin + | ..\zcpr-dj\zcpr.bin + | ..\zsdos\zsdos.bin + | ..\cbios\cbios_wbw.bin + | 1 file(s) copied. + | ..\bl\bl.bin + | ..\zcpr-dj\zcpr.bin + | ..\zsdos\zsdos.bin + | ..\cbios\cbios_una.bin + | 1 file(s) copied. + | : + | : Building Floppy Disk Images... + | : + | Generating Floppy Disk cpm22... + | cpmcp -f wbw_fd144 fd_cpm22.img d_cpm22/u0/*.* 0: + | cpmcp -f wbw_fd144 fd_cpm22.img d_cpm22/u1/*.* 1: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_fd144 fd_cpm22.img ../../Binary/Apps/Tunes/*.* 3: + | Adding System Image cpm_wbw... + | Moving image fd_cpm22.img into output directory... + | 1 file(s) moved. + | Generating Floppy Disk zsdos... + | cpmcp -f wbw_fd144 fd_zsdos.img d_zsdos/u0/*.* 0: + | cpmcp -f wbw_fd144 fd_zsdos.img d_zsdos/u1/*.* 1: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_fd144 fd_zsdos.img ../../Binary/Apps/Tunes/*.* 3: + | Adding System Image zsys_wbw... + | Moving image fd_zsdos.img into output directory... + | 1 file(s) moved. + | Generating Floppy Disk nzcom... + | cpmcp -f wbw_fd144 fd_nzcom.img d_nzcom/u0/*.* 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_fd144 fd_nzcom.img ../../Binary/Apps/Tunes/*.* 3: + | Adding System Image zsys_wbw... + | Moving image fd_nzcom.img into output directory... + | 1 file(s) moved. + | Generating Floppy Disk cpm3... + | cpmcp -f wbw_fd144 fd_cpm3.img d_cpm3/u0/*.* 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/cpmldr.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/ccp.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/gencpm.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/genres.dat 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/genbnk.dat 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/bios3.spr 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/bnkbios3.spr 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/bdos3.spr 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/bnkbdos3.spr 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/resbdos3.spr 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/cpm3res.sys 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/cpm3bnk.sys 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/gencpm.dat 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/cpm3.sys 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/readme.1st 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../CPM3/cpm3fix.pat 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_fd144 fd_cpm3.img ../../Binary/Apps/Tunes/*.* 3: + | Moving image fd_cpm3.img into output directory... + | 1 file(s) moved. + | Generating Floppy Disk zpm3... + | cpmcp -f wbw_fd144 fd_zpm3.img d_zpm3/u0/*.* 0: + | cpmcp -f wbw_fd144 fd_zpm3.img d_zpm3/u10/*.* 10: + | cpmcp -f wbw_fd144 fd_zpm3.img d_zpm3/u14/*.* 14: + | cpmcp -f wbw_fd144 fd_zpm3.img d_zpm3/u15/*.* 15: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/zpmldr.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/cpmldr.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/autotog.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/clrhist.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/setz3.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/cpm3.sys 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/zccp.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/zinstal.zpm 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/startzpm.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/makedos.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/gencpm.dat 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/bnkbios3.spr 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/bnkbdos3.spr 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../ZPM3/resbdos3.spr 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_fd144 fd_zpm3.img ../../Binary/Apps/Tunes/*.* 3: + | Moving image fd_zpm3.img into output directory... + | 1 file(s) moved. + | Generating Floppy Disk ws4... + | cpmcp -f wbw_fd144 fd_ws4.img d_ws4/u0/*.* 0: + | Moving image fd_ws4.img into output directory... + | 1 file(s) moved. + | : + | : Building Hard Disk Images... + | : | Generating Hard Disk cpm22... | cpmcp -f wbw_hd0 hd_cpm22.img d_cpm22/u0/*.* 0: | cpmcp -f wbw_hd0 hd_cpm22.img d_cpm22/u1/*.* 1: - | cpmcp -f wbw_hd0 hd_cpm22.img d_cpm22/u3/*.* 3: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_hd0 hd_cpm22.img ../../Binary/Apps/Tunes/*.* 3: + | Adding System Image cpm_wbw... + | Moving image hd_cpm22.img into output directory... + | 1 file(s) moved. + | Generating Hard Disk zsdos... + | cpmcp -f wbw_hd0 hd_zsdos.img d_zsdos/u0/*.* 0: + | cpmcp -f wbw_hd0 hd_zsdos.img d_zsdos/u1/*.* 1: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_hd0 hd_zsdos.img ../../Binary/Apps/Tunes/*.* 3: + | Adding System Image zsys_wbw... + | Moving image hd_zsdos.img into output directory... + | 1 file(s) moved. | Generating Hard Disk nzcom... | cpmcp -f wbw_hd0 hd_nzcom.img d_nzcom/u0/*.* 0: - | Generating Hard Disk ws4... - | cpmcp -f wbw_hd0 hd_ws4.img d_ws4/u0/*.* 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_hd0 hd_nzcom.img ../../Binary/Apps/Tunes/*.* 3: + | Adding System Image zsys_wbw... + | Moving image hd_nzcom.img into output directory... + | 1 file(s) moved. + | Generating Hard Disk cpm3... + | cpmcp -f wbw_hd0 hd_cpm3.img d_cpm3/u0/*.* 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/cpmldr.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/ccp.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/gencpm.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/genres.dat 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/genbnk.dat 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/bios3.spr 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/bnkbios3.spr 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/bdos3.spr 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/bnkbdos3.spr 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/resbdos3.spr 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/cpm3res.sys 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/cpm3bnk.sys 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/gencpm.dat 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/cpm3.sys 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/readme.1st 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../CPM3/cpm3fix.pat 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_hd0 hd_cpm3.img ../../Binary/Apps/Tunes/*.* 3: + | Moving image hd_cpm3.img into output directory... + | 1 file(s) moved. | Generating Hard Disk zpm3... | cpmcp -f wbw_hd0 hd_zpm3.img d_zpm3/u0/*.* 0: | cpmcp -f wbw_hd0 hd_zpm3.img d_zpm3/u10/*.* 10: | cpmcp -f wbw_hd0 hd_zpm3.img d_zpm3/u14/*.* 14: | cpmcp -f wbw_hd0 hd_zpm3.img d_zpm3/u15/*.* 15: - | Generating Hard Disk zsdos... - | cpmcp -f wbw_hd0 hd_zsdos.img d_zsdos/u0/*.* 0: - | cpmcp -f wbw_hd0 hd_zsdos.img d_zsdos/u1/*.* 1: - | cpmcp -f wbw_hd0 hd_zsdos.img d_zsdos/u3/*.* 3: - | Moving images into output directory... - | C:\Users\Wayne\Projects\RBC\Build\RomWBW\Source\Images\hd_cpm22.img - | C:\Users\Wayne\Projects\RBC\Build\RomWBW\Source\Images\hd_cpm3.img - | C:\Users\Wayne\Projects\RBC\Build\RomWBW\Source\Images\hd_nzcom.img - | C:\Users\Wayne\Projects\RBC\Build\RomWBW\Source\Images\hd_ws4.img - | C:\Users\Wayne\Projects\RBC\Build\RomWBW\Source\Images\hd_zpm3.img - | C:\Users\Wayne\Projects\RBC\Build\RomWBW\Source\Images\hd_zsdos.img - | 6 file(s) moved. - | - | C:\Users\Wayne\Projects\RBC\Build\RomWBW\Source\Images> + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/zpmldr.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/cpmldr.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/autotog.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/clrhist.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/setz3.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/cpm3.sys 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/zccp.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/zinstal.zpm 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/startzpm.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/makedos.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/gencpm.dat 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/bnkbios3.spr 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/bnkbdos3.spr 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../ZPM3/resbdos3.spr 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/assign.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/fat.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/fdu.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/format.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/mode.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/osldr.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/rtc.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/survey.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/syscopy.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/sysgen.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/talk.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/timer.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/xm.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/inttest.com 0: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/tune.com 3: + | cpmcp -f wbw_hd0 hd_zpm3.img ../../Binary/Apps/Tunes/*.* 3: + | Moving image hd_zpm3.img into output directory... + | 1 file(s) moved. + | Generating Hard Disk ws4... + | cpmcp -f wbw_hd0 hd_ws4.img d_ws4/u0/*.* 0: + | Moving image hd_ws4.img into output directory... + | 1 file(s) moved. Be aware that the script always builds the image file from scratch. It will not update the previous contents. Any contents of a diff --git a/Source/Images/d_bp.txt b/Source/Images/d_bp.txt new file mode 100644 index 00000000..331a4b24 --- /dev/null +++ b/Source/Images/d_bp.txt @@ -0,0 +1,33 @@ +# +# Add RomWBW utilities +# +../../Binary/Apps/assign.com 0: +../../Binary/Apps/fat.com 0: +../../Binary/Apps/fdu.com 0: +../../Binary/Apps/format.com 0: +../../Binary/Apps/mode.com 0: +../../Binary/Apps/osldr.com 0: +../../Binary/Apps/rtc.com 0: +../../Binary/Apps/survey.com 0: +../../Binary/Apps/syscopy.com 0: +../../Binary/Apps/sysgen.com 0: +../../Binary/Apps/talk.com 0: +../../Binary/Apps/timer.com 0: +../../Binary/Apps/xm.com 0: +../../Binary/Apps/inttest.com 0: +# +# Add Tune application and sample files +# +../../Binary/Apps/tune.com 3: +../../Binary/Apps/Tunes/*.pt? 3: +../../Binary/Apps/Tunes/*.mym 3: +# +# Add ZSystem images +# +../BPBIOS/*.img 0: +../BPBIOS/*.rel 0: +../BPBIOS/*.zrl 0: +../BPBIOS/*.zex 0: +../BPBIOS/myterm.z3t 0: +../BPBIOS/Z34RCP11/z34rcp11.rel 0:rcp.zrl +../BPBIOS/NZFCP13/nzfcp13.rel 0:fcp.zrl diff --git a/Source/Images/d_bp/u0/FAT.COM b/Source/Images/d_bp/u0/FAT.COM deleted file mode 100644 index 1dd7dcca..00000000 Binary files a/Source/Images/d_bp/u0/FAT.COM and /dev/null differ diff --git a/Source/Images/d_bp/u0/HDIR.COM b/Source/Images/d_bp/u0/HDIR.COM deleted file mode 100644 index 17096622..00000000 Binary files a/Source/Images/d_bp/u0/HDIR.COM and /dev/null differ diff --git a/Source/Images/d_bp/u0/JETLDR.COM b/Source/Images/d_bp/u0/JETLDR.COM new file mode 100644 index 00000000..c33c0738 Binary files /dev/null and b/Source/Images/d_bp/u0/JETLDR.COM differ diff --git a/Source/Images/d_bp/u0/R.COM b/Source/Images/d_bp/u0/R.COM deleted file mode 100644 index fecaa4ac..00000000 Binary files a/Source/Images/d_bp/u0/R.COM and /dev/null differ diff --git a/Source/Images/d_bp/u0/RSETSIMH.COM b/Source/Images/d_bp/u0/RSETSIMH.COM deleted file mode 100644 index e055f07a..00000000 Binary files a/Source/Images/d_bp/u0/RSETSIMH.COM and /dev/null differ diff --git a/Source/Images/d_bp/u0/TIMER.COM b/Source/Images/d_bp/u0/TIMER.COM deleted file mode 100644 index 83e6e688..00000000 Binary files a/Source/Images/d_bp/u0/TIMER.COM and /dev/null differ diff --git a/Source/Images/d_bp/u0/W.COM b/Source/Images/d_bp/u0/W.COM deleted file mode 100644 index 2e5ab12a..00000000 Binary files a/Source/Images/d_bp/u0/W.COM and /dev/null differ diff --git a/Source/Images/d_bp/u0/Z41.ZRL b/Source/Images/d_bp/u0/Z41.ZRL deleted file mode 100644 index ed10dfad..00000000 Binary files a/Source/Images/d_bp/u0/Z41.ZRL and /dev/null differ diff --git a/Source/Images/d_bp/u0/ZCPR33.REL b/Source/Images/d_bp/u0/ZCPR33.REL deleted file mode 100644 index 7c0a2567..00000000 Binary files a/Source/Images/d_bp/u0/ZCPR33.REL and /dev/null differ diff --git a/Source/Images/d_bp/u0/ZS203.ZRL b/Source/Images/d_bp/u0/ZS203.ZRL deleted file mode 100644 index 11be02dd..00000000 Binary files a/Source/Images/d_bp/u0/ZS203.ZRL and /dev/null differ diff --git a/Source/Images/d_bp/u0/ZSDOS.ZRL b/Source/Images/d_bp/u0/ZSDOS.ZRL deleted file mode 100644 index e43d1b0a..00000000 Binary files a/Source/Images/d_bp/u0/ZSDOS.ZRL and /dev/null differ diff --git a/Source/Images/d_bp/u0/z34.rel b/Source/Images/d_bp/u0/z34.rel deleted file mode 100644 index 0e9d5236..00000000 Binary files a/Source/Images/d_bp/u0/z34.rel and /dev/null differ diff --git a/Source/Images/d_cpm22.txt b/Source/Images/d_cpm22.txt new file mode 100644 index 00000000..45e9990a --- /dev/null +++ b/Source/Images/d_cpm22.txt @@ -0,0 +1,27 @@ +# +# Add RomWBW utilities +# +../../Binary/Apps/assign.com 0: +../../Binary/Apps/fat.com 0: +../../Binary/Apps/fdu.com 0: +../../Binary/Apps/format.com 0: +../../Binary/Apps/mode.com 0: +../../Binary/Apps/osldr.com 0: +../../Binary/Apps/rtc.com 0: +../../Binary/Apps/survey.com 0: +../../Binary/Apps/syscopy.com 0: +../../Binary/Apps/sysgen.com 0: +../../Binary/Apps/talk.com 0: +../../Binary/Apps/timer.com 0: +../../Binary/Apps/xm.com 0: +../../Binary/Apps/inttest.com 0: +# +# Add Tune application and sample files +# +../../Binary/Apps/tune.com 3: +../../Binary/Apps/Tunes/*.pt? 3: +../../Binary/Apps/Tunes/*.mym 3: +# +# Add OS image +# +../CPM22/cpm_wbw.sys 0:cpm.sys diff --git a/Source/Images/d_cpm22/u0/FAT.COM b/Source/Images/d_cpm22/u0/FAT.COM deleted file mode 100644 index 1dd7dcca..00000000 Binary files a/Source/Images/d_cpm22/u0/FAT.COM and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Attack.pt3 b/Source/Images/d_cpm22/u3/Attack.pt3 deleted file mode 100644 index dc9d04a9..00000000 Binary files a/Source/Images/d_cpm22/u3/Attack.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Backup.pt3 b/Source/Images/d_cpm22/u3/Backup.pt3 deleted file mode 100644 index e9141fe4..00000000 Binary files a/Source/Images/d_cpm22/u3/Backup.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/BadMice.pt3 b/Source/Images/d_cpm22/u3/BadMice.pt3 deleted file mode 100644 index aa78a8d2..00000000 Binary files a/Source/Images/d_cpm22/u3/BadMice.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Demo.mym b/Source/Images/d_cpm22/u3/Demo.mym deleted file mode 100644 index 255fc160..00000000 Binary files a/Source/Images/d_cpm22/u3/Demo.mym and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Demo1.mym b/Source/Images/d_cpm22/u3/Demo1.mym deleted file mode 100644 index b224f321..00000000 Binary files a/Source/Images/d_cpm22/u3/Demo1.mym and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Demo3.mym b/Source/Images/d_cpm22/u3/Demo3.mym deleted file mode 100644 index 808db891..00000000 Binary files a/Source/Images/d_cpm22/u3/Demo3.mym and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Demo3mix.mym b/Source/Images/d_cpm22/u3/Demo3mix.mym deleted file mode 100644 index b5981848..00000000 Binary files a/Source/Images/d_cpm22/u3/Demo3mix.mym and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Demo4.mym b/Source/Images/d_cpm22/u3/Demo4.mym deleted file mode 100644 index ed2e8a85..00000000 Binary files a/Source/Images/d_cpm22/u3/Demo4.mym and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/HowRU.pt3 b/Source/Images/d_cpm22/u3/HowRU.pt3 deleted file mode 100644 index 47fb464e..00000000 Binary files a/Source/Images/d_cpm22/u3/HowRU.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Iteratn.pt3 b/Source/Images/d_cpm22/u3/Iteratn.pt3 deleted file mode 100644 index 575cccf4..00000000 Binary files a/Source/Images/d_cpm22/u3/Iteratn.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/LookBack.pt3 b/Source/Images/d_cpm22/u3/LookBack.pt3 deleted file mode 100644 index 1dd15ac7..00000000 Binary files a/Source/Images/d_cpm22/u3/LookBack.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Louboutn.pt3 b/Source/Images/d_cpm22/u3/Louboutn.pt3 deleted file mode 100644 index 8b4b0b15..00000000 Binary files a/Source/Images/d_cpm22/u3/Louboutn.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Namida.pt3 b/Source/Images/d_cpm22/u3/Namida.pt3 deleted file mode 100644 index 2a2d8562..00000000 Binary files a/Source/Images/d_cpm22/u3/Namida.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Recoll.pt3 b/Source/Images/d_cpm22/u3/Recoll.pt3 deleted file mode 100644 index 5a2744ab..00000000 Binary files a/Source/Images/d_cpm22/u3/Recoll.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Sanxion.pt3 b/Source/Images/d_cpm22/u3/Sanxion.pt3 deleted file mode 100644 index 25a77aab..00000000 Binary files a/Source/Images/d_cpm22/u3/Sanxion.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Synch.pt3 b/Source/Images/d_cpm22/u3/Synch.pt3 deleted file mode 100644 index 16149d1a..00000000 Binary files a/Source/Images/d_cpm22/u3/Synch.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/ToStar.pt3 b/Source/Images/d_cpm22/u3/ToStar.pt3 deleted file mode 100644 index 9c77ed4f..00000000 Binary files a/Source/Images/d_cpm22/u3/ToStar.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Tune.com b/Source/Images/d_cpm22/u3/Tune.com deleted file mode 100644 index 9f5ba036..00000000 Binary files a/Source/Images/d_cpm22/u3/Tune.com and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Victory.pt3 b/Source/Images/d_cpm22/u3/Victory.pt3 deleted file mode 100644 index d2c6f3e7..00000000 Binary files a/Source/Images/d_cpm22/u3/Victory.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Wicked.pt3 b/Source/Images/d_cpm22/u3/Wicked.pt3 deleted file mode 100644 index 840a58d1..00000000 Binary files a/Source/Images/d_cpm22/u3/Wicked.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/YeOlde.pt3 b/Source/Images/d_cpm22/u3/YeOlde.pt3 deleted file mode 100644 index ba99371b..00000000 Binary files a/Source/Images/d_cpm22/u3/YeOlde.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm22/u3/Yeovil.pt3 b/Source/Images/d_cpm22/u3/Yeovil.pt3 deleted file mode 100644 index 694de685..00000000 Binary files a/Source/Images/d_cpm22/u3/Yeovil.pt3 and /dev/null differ diff --git a/Source/Images/d_cpm3.txt b/Source/Images/d_cpm3.txt new file mode 100644 index 00000000..4e7f63cc --- /dev/null +++ b/Source/Images/d_cpm3.txt @@ -0,0 +1,43 @@ +# +# Add files from CPM3 build +# +../CPM3/cpmldr.com 0: +../CPM3/cpmldr.sys 0: +../CPM3/ccp.com 0: +../CPM3/gencpm.com 0: +../CPM3/genres.dat 0: +../CPM3/genbnk.dat 0: +../CPM3/bios3.spr 0: +../CPM3/bnkbios3.spr 0: +../CPM3/bdos3.spr 0: +../CPM3/bnkbdos3.spr 0: +../CPM3/resbdos3.spr 0: +../CPM3/cpm3res.sys 0: +../CPM3/cpm3bnk.sys 0: +../CPM3/gencpm.dat 0: +../CPM3/cpm3.sys 0: +../CPM3/readme.1st 0: +../CPM3/cpm3fix.pat 0: +# +# Add RomWBW utilities +# +../../Binary/Apps/assign.com 0: +../../Binary/Apps/fat.com 0: +../../Binary/Apps/fdu.com 0: +../../Binary/Apps/format.com 0: +../../Binary/Apps/mode.com 0: +../../Binary/Apps/osldr.com 0: +../../Binary/Apps/rtc.com 0: +../../Binary/Apps/survey.com 0: +../../Binary/Apps/syscopy.com 0: +../../Binary/Apps/sysgen.com 0: +../../Binary/Apps/talk.com 0: +../../Binary/Apps/timer.com 0: +../../Binary/Apps/xm.com 0: +../../Binary/Apps/inttest.com 0: +# +# Add Tune application and sample files +# +../../Binary/Apps/tune.com 3: +../../Binary/Apps/Tunes/*.pt? 3: +../../Binary/Apps/Tunes/*.mym 3: diff --git a/Source/Images/d_cpm3/u0/GENCPM.COM b/Source/Images/d_cpm3/u0/GENCPM.COM deleted file mode 100644 index d9d67499..00000000 Binary files a/Source/Images/d_cpm3/u0/GENCPM.COM and /dev/null differ diff --git a/Source/Images/d_nzcom.txt b/Source/Images/d_nzcom.txt new file mode 100644 index 00000000..b5c98944 --- /dev/null +++ b/Source/Images/d_nzcom.txt @@ -0,0 +1,28 @@ +# +# Add RomWBW utilities +# +../../Binary/Apps/assign.com 0: +../../Binary/Apps/fat.com 0: +../../Binary/Apps/fdu.com 0: +../../Binary/Apps/format.com 0: +../../Binary/Apps/mode.com 0: +../../Binary/Apps/osldr.com 0: +../../Binary/Apps/rtc.com 0: +../../Binary/Apps/survey.com 0: +../../Binary/Apps/syscopy.com 0: +../../Binary/Apps/sysgen.com 0: +../../Binary/Apps/talk.com 0: +../../Binary/Apps/timer.com 0: +../../Binary/Apps/xm.com 0: +../../Binary/Apps/inttest.com 0: +# +# Add Tune application and sample files +# +../../Binary/Apps/tune.com 3: +../../Binary/Apps/Tunes/*.pt? 3: +../../Binary/Apps/Tunes/*.mym 3: +# +# Add OS images +# +../CPM22/cpm_wbw.sys 0:cpm.sys +../ZSDOS/zsys_wbw.sys 0:zsys.sys \ No newline at end of file diff --git a/Source/Images/d_nzcom/u0/!(C)1988 b/Source/Images/d_nzcom/u0/!(C)1988 new file mode 100644 index 00000000..14367066 Binary files /dev/null and b/Source/Images/d_nzcom/u0/!(C)1988 differ diff --git a/Source/Images/d_nzcom/u0/!NZ-COM b/Source/Images/d_nzcom/u0/!NZ-COM new file mode 100644 index 00000000..e69de29b diff --git a/Source/Images/d_nzcom/u0/!VERS--1.2H b/Source/Images/d_nzcom/u0/!VERS--1.2H new file mode 100644 index 00000000..e69de29b diff --git a/Source/Images/d_nzcom/u0/ALIAS.CMD b/Source/Images/d_nzcom/u0/ALIAS.CMD index 4e5e0850..936006c3 100644 Binary files a/Source/Images/d_nzcom/u0/ALIAS.CMD and b/Source/Images/d_nzcom/u0/ALIAS.CMD differ diff --git a/Source/Images/d_nzcom/u0/ARUNZ.COM b/Source/Images/d_nzcom/u0/ARUNZ.COM index e36a3be6..041b87c4 100644 Binary files a/Source/Images/d_nzcom/u0/ARUNZ.COM and b/Source/Images/d_nzcom/u0/ARUNZ.COM differ diff --git a/Source/Images/d_nzcom/u0/BGZRDS19.LBR b/Source/Images/d_nzcom/u0/BGZRDS19.LBR new file mode 100644 index 00000000..5849efa9 Binary files /dev/null and b/Source/Images/d_nzcom/u0/BGZRDS19.LBR differ diff --git a/Source/Images/d_nzcom/u0/CLEDINST.COM b/Source/Images/d_nzcom/u0/CLEDINST.COM new file mode 100644 index 00000000..c26a3cf0 Binary files /dev/null and b/Source/Images/d_nzcom/u0/CLEDINST.COM differ diff --git a/Source/Images/d_nzcom/u0/CLEDSAVE.COM b/Source/Images/d_nzcom/u0/CLEDSAVE.COM new file mode 100644 index 00000000..dde04bc1 Binary files /dev/null and b/Source/Images/d_nzcom/u0/CLEDSAVE.COM differ diff --git a/Source/Images/d_nzcom/u0/CMD.COM b/Source/Images/d_nzcom/u0/CMD.COM deleted file mode 100644 index 9ac106c1..00000000 Binary files a/Source/Images/d_nzcom/u0/CMD.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/COMP.COM b/Source/Images/d_nzcom/u0/COMP.COM deleted file mode 100644 index 194c0a97..00000000 Binary files a/Source/Images/d_nzcom/u0/COMP.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/CONFIG.LBR b/Source/Images/d_nzcom/u0/CONFIG.LBR new file mode 100644 index 00000000..40d87ae7 Binary files /dev/null and b/Source/Images/d_nzcom/u0/CONFIG.LBR differ diff --git a/Source/Images/d_nzcom/u0/COPY.COM b/Source/Images/d_nzcom/u0/COPY.COM index 693fa09d..734953d9 100644 Binary files a/Source/Images/d_nzcom/u0/COPY.COM and b/Source/Images/d_nzcom/u0/COPY.COM differ diff --git a/Source/Images/d_nzcom/u0/CPSET.COM b/Source/Images/d_nzcom/u0/CPSET.COM index eaf91fa2..54462ce0 100644 Binary files a/Source/Images/d_nzcom/u0/CPSET.COM and b/Source/Images/d_nzcom/u0/CPSET.COM differ diff --git a/Source/Images/d_nzcom/u0/CRUNCH.COM b/Source/Images/d_nzcom/u0/CRUNCH.COM index d3a121a9..ac17854a 100644 Binary files a/Source/Images/d_nzcom/u0/CRUNCH.COM and b/Source/Images/d_nzcom/u0/CRUNCH.COM differ diff --git a/Source/Images/d_nzcom/u0/DFA.COM b/Source/Images/d_nzcom/u0/DFA.COM deleted file mode 100644 index 2b008416..00000000 Binary files a/Source/Images/d_nzcom/u0/DFA.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/DFA.NOT b/Source/Images/d_nzcom/u0/DFA.NOT deleted file mode 100644 index ef50301a..00000000 --- a/Source/Images/d_nzcom/u0/DFA.NOT +++ /dev/null @@ -1,66 +0,0 @@ - Ne÷ Display/Definå Filå Attributeó Program - bù Joå Wright - -Filå  attributeó  arå  maintaineä  bù thå BDOÓ iî  thå  filespeã  portioî  oæ -directorù  entrieó foò eacè filå oî thå disk® Eacè filespeã containó  eleveî -ascié characteró comprisinç thå eighô characteò filenamå anä threå  characteò -type® Ascié characteró requirå onlù seveî oæ thå eighô bitó iî eacè oæ theså -bytes¬  thå  eightè biô ió thereforå availablå foò  assigninç  aî  attribute® -Theså eleveî 'high§ bitó arå referreä tï aó follows: - - f± f² f³ f´ fµ f¶ f· f¸ . t± t² t3 - -CP/Í  assignó t± aó thå Reaä Onlù attributå anä t² aó thå  Systeí  attribute® -MP/Í furtheò assigneä t³ aó thå Archiveä attribute® ZRDOÓ furtheò assignó f¸ -aó  thå Wheeì Protecô attribute® ZSDOÓ assignó f² aó thå  Publiã  attribute® -BackGroundeò  ié useó f± tï inhibiô itó keù-bindinç functioî anä  DateStampeò -useó f³ tï inhibiô stampinç oæ certaiî files® Thå resô arå unassigneä aó faò -aó É kno÷ anä maù bå useä aó yoõ wish. - - - Displaù syntaxº DFÁ [dir:][filespec] - -Displaù  modå takeó á singlå tokeî oò nï tokens® Botè dirº anä filespeã  arå -optional® Defaulô dirº ió thå currenô directorù anä defaulô filespeã ió wilä -(*.*)®   Useò ió giveî á sorteä filå lisô showinç thå  attributeó  associateä -witè each¬ sixteeî aô á time® - -DFÁ Displaù attributeó oæ alì fileó iî thå currenô directory. -DFÁ B2º Displaù foò alì fileó iî directorù B2: -DFÁ *.REÌ Displaù foò alì .REÌ fileó iî thå currenô directory. -DFÁ B2:*.REÌ Displaù foò alì .REÌ fileó iî B2: - - - Definå syntaxº DFÁ [dir:][filspecÝ - -Definitioî  modå  ió  defineä  bù á  seconä  anä  perhapó  subsequenô  tokenó -representinç  thå  variouó  attributes®   Thå tokeî  'f1§  wilì  seô  thå  f± -attributå foò alì filespeã files¬ §-f1§ wilì reseô it® Nameó associateä witè -thå variouó attributeó arå alsï accepteä aó tokens: - - f± oò nkâ Nï Keù Bindinç (BGii) - f² oò puâ Publiã File - f³ oò dsï DateStampeò Off - f´ - fµ - f¶ - f· - f¸ oò whì Wheeì Protected - . - t± or r/ï Reaä Only - t² or syó System - t³ or arã Archived - -Iæ  thå firsô letteò oæ á tokeî ió noô 'f§ oò 't'¬ thaô letteò ió  sufficienô -tï  identifù thå token® 'S'¬ 'sys§ oò 'system§ alì identifù t2® Anù oò  alì Šoæ thå eleveî attributeó maù bå defineä witè á singlå command. - -DFÁ *.TYÐ F± Seô f± oæ alì fileó .TYÐ iî thå currenô directory. -DFÁ A1:*.REÌ F² Ò Seô f² anä t1 oæ alì .REÌ fileó iî A1: -DFÁ *.Z8° -Ò Reseô t± oæ alì .Z8° fileó iî thå currenô directory. -DFÁ Aº -SYÓ Reseô t² oæ alì fileó iî A:¬ currenô user. -DFÁ ROOT:*.COÍ × Seô thå Wheeì attributå (f8© oæ alì .COÍ files - iî thå directorù nameä ROOT: - - - -end- - \ No newline at end of file diff --git a/Source/Images/d_nzcom/u0/DIR.COM b/Source/Images/d_nzcom/u0/DIR.COM deleted file mode 100644 index 84b03395..00000000 Binary files a/Source/Images/d_nzcom/u0/DIR.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/DOCFILES.LBR b/Source/Images/d_nzcom/u0/DOCFILES.LBR new file mode 100644 index 00000000..c96cd424 Binary files /dev/null and b/Source/Images/d_nzcom/u0/DOCFILES.LBR differ diff --git a/Source/Images/d_nzcom/u0/EASE.COM b/Source/Images/d_nzcom/u0/EASE.COM deleted file mode 100644 index 5a95e78e..00000000 Binary files a/Source/Images/d_nzcom/u0/EASE.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/EASECMD.COM b/Source/Images/d_nzcom/u0/EASECMD.COM deleted file mode 100644 index 410848a4..00000000 Binary files a/Source/Images/d_nzcom/u0/EASECMD.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/EDITNDR.COM b/Source/Images/d_nzcom/u0/EDITNDR.COM index e0c84d5f..149cb98c 100644 Binary files a/Source/Images/d_nzcom/u0/EDITNDR.COM and b/Source/Images/d_nzcom/u0/EDITNDR.COM differ diff --git a/Source/Images/d_nzcom/u0/ERA.COM b/Source/Images/d_nzcom/u0/ERA.COM deleted file mode 100644 index 35f9653f..00000000 Binary files a/Source/Images/d_nzcom/u0/ERA.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/FCP.LBR b/Source/Images/d_nzcom/u0/FCP.LBR new file mode 100644 index 00000000..8eb74a42 Binary files /dev/null and b/Source/Images/d_nzcom/u0/FCP.LBR differ diff --git a/Source/Images/d_nzcom/u0/FF.COM b/Source/Images/d_nzcom/u0/FF.COM index 933b39e3..b68b2add 100644 Binary files a/Source/Images/d_nzcom/u0/FF.COM and b/Source/Images/d_nzcom/u0/FF.COM differ diff --git a/Source/Images/d_nzcom/u0/FINDF.COM b/Source/Images/d_nzcom/u0/FINDF.COM deleted file mode 100644 index 4a143711..00000000 Binary files a/Source/Images/d_nzcom/u0/FINDF.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/HELP.COM b/Source/Images/d_nzcom/u0/HELP.COM index 162d3250..58b4d6fc 100644 Binary files a/Source/Images/d_nzcom/u0/HELP.COM and b/Source/Images/d_nzcom/u0/HELP.COM differ diff --git a/Source/Images/d_nzcom/u0/HLPFILES.LBR b/Source/Images/d_nzcom/u0/HLPFILES.LBR new file mode 100644 index 00000000..32200cdd Binary files /dev/null and b/Source/Images/d_nzcom/u0/HLPFILES.LBR differ diff --git a/Source/Images/d_nzcom/u0/IF.COM b/Source/Images/d_nzcom/u0/IF.COM index 1b26ef65..c51cd0bd 100644 Binary files a/Source/Images/d_nzcom/u0/IF.COM and b/Source/Images/d_nzcom/u0/IF.COM differ diff --git a/Source/Images/d_nzcom/u0/JETLDR.COM b/Source/Images/d_nzcom/u0/JETLDR.COM new file mode 100644 index 00000000..c33c0738 Binary files /dev/null and b/Source/Images/d_nzcom/u0/JETLDR.COM differ diff --git a/Source/Images/d_nzcom/u0/LBREXT.COM b/Source/Images/d_nzcom/u0/LBREXT.COM new file mode 100644 index 00000000..591922b7 Binary files /dev/null and b/Source/Images/d_nzcom/u0/LBREXT.COM differ diff --git a/Source/Images/d_nzcom/u0/LBRHELP.COM b/Source/Images/d_nzcom/u0/LBRHELP.COM new file mode 100644 index 00000000..eb1ef6ee Binary files /dev/null and b/Source/Images/d_nzcom/u0/LBRHELP.COM differ diff --git a/Source/Images/d_nzcom/u0/LDIR.COM b/Source/Images/d_nzcom/u0/LDIR.COM index 763f2a81..d72eeef7 100644 Binary files a/Source/Images/d_nzcom/u0/LDIR.COM and b/Source/Images/d_nzcom/u0/LDIR.COM differ diff --git a/Source/Images/d_nzcom/u0/LGET.COM b/Source/Images/d_nzcom/u0/LGET.COM deleted file mode 100644 index 29929e94..00000000 Binary files a/Source/Images/d_nzcom/u0/LGET.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/LPUT.COM b/Source/Images/d_nzcom/u0/LPUT.COM index 81d9f4ff..72716963 100644 Binary files a/Source/Images/d_nzcom/u0/LPUT.COM and b/Source/Images/d_nzcom/u0/LPUT.COM differ diff --git a/Source/Images/d_nzcom/u0/LSH-HELP.COM b/Source/Images/d_nzcom/u0/LSH-HELP.COM new file mode 100644 index 00000000..0103795b Binary files /dev/null and b/Source/Images/d_nzcom/u0/LSH-HELP.COM differ diff --git a/Source/Images/d_nzcom/u0/LSH.COM b/Source/Images/d_nzcom/u0/LSH.COM new file mode 100644 index 00000000..6ec314f7 Binary files /dev/null and b/Source/Images/d_nzcom/u0/LSH.COM differ diff --git a/Source/Images/d_nzcom/u0/LSH.WZ b/Source/Images/d_nzcom/u0/LSH.WZ new file mode 100644 index 00000000..690f9918 Binary files /dev/null and b/Source/Images/d_nzcom/u0/LSH.WZ differ diff --git a/Source/Images/d_nzcom/u0/LSHINST.COM b/Source/Images/d_nzcom/u0/LSHINST.COM new file mode 100644 index 00000000..a9beb351 Binary files /dev/null and b/Source/Images/d_nzcom/u0/LSHINST.COM differ diff --git a/Source/Images/d_nzcom/u0/LX.COM b/Source/Images/d_nzcom/u0/LX.COM index 285aea41..d424f9fb 100644 Binary files a/Source/Images/d_nzcom/u0/LX.COM and b/Source/Images/d_nzcom/u0/LX.COM differ diff --git a/Source/Images/d_nzcom/u0/MKZCM.COM b/Source/Images/d_nzcom/u0/MKZCM.COM index ccf4b11d..ad0ce7b0 100644 Binary files a/Source/Images/d_nzcom/u0/MKZCM.COM and b/Source/Images/d_nzcom/u0/MKZCM.COM differ diff --git a/Source/Images/d_nzcom/u0/NAME.COM b/Source/Images/d_nzcom/u0/NAME.COM new file mode 100644 index 00000000..d3a8cdf5 Binary files /dev/null and b/Source/Images/d_nzcom/u0/NAME.COM differ diff --git a/Source/Images/d_nzcom/u0/NZ-DBASE.INF b/Source/Images/d_nzcom/u0/NZ-DBASE.INF new file mode 100644 index 00000000..72f21691 Binary files /dev/null and b/Source/Images/d_nzcom/u0/NZ-DBASE.INF differ diff --git a/Source/Images/d_nzcom/u0/NZBLITZ.COM b/Source/Images/d_nzcom/u0/NZBLITZ.COM new file mode 100644 index 00000000..03552792 Binary files /dev/null and b/Source/Images/d_nzcom/u0/NZBLITZ.COM differ diff --git a/Source/Images/d_nzcom/u0/NZBLTZ14.CFG b/Source/Images/d_nzcom/u0/NZBLTZ14.CFG new file mode 100644 index 00000000..5ffb8751 Binary files /dev/null and b/Source/Images/d_nzcom/u0/NZBLTZ14.CFG differ diff --git a/Source/Images/d_nzcom/u0/NZBLTZ14.HZP b/Source/Images/d_nzcom/u0/NZBLTZ14.HZP new file mode 100644 index 00000000..86d8590c Binary files /dev/null and b/Source/Images/d_nzcom/u0/NZBLTZ14.HZP differ diff --git a/Source/Images/d_nzcom/u0/NZCOM.COM b/Source/Images/d_nzcom/u0/NZCOM.COM index f9d67d16..b80c4d3a 100644 Binary files a/Source/Images/d_nzcom/u0/NZCOM.COM and b/Source/Images/d_nzcom/u0/NZCOM.COM differ diff --git a/Source/Images/d_nzcom/u0/NZCOM.LBR b/Source/Images/d_nzcom/u0/NZCOM.LBR index 2ed5afbd..bf432b41 100644 Binary files a/Source/Images/d_nzcom/u0/NZCOM.LBR and b/Source/Images/d_nzcom/u0/NZCOM.LBR differ diff --git a/Source/Images/d_nzcom/u0/NZCPR.LBR b/Source/Images/d_nzcom/u0/NZCPR.LBR index 74d412a1..1f51b0f6 100644 Binary files a/Source/Images/d_nzcom/u0/NZCPR.LBR and b/Source/Images/d_nzcom/u0/NZCPR.LBR differ diff --git a/Source/Images/d_nzcom/u0/NZFCP.LBR b/Source/Images/d_nzcom/u0/NZFCP.LBR deleted file mode 100644 index 9426dc12..00000000 Binary files a/Source/Images/d_nzcom/u0/NZFCP.LBR and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/NZRCP.LBR b/Source/Images/d_nzcom/u0/NZRCP.LBR deleted file mode 100644 index da47328e..00000000 Binary files a/Source/Images/d_nzcom/u0/NZRCP.LBR and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/NZSUB.COM b/Source/Images/d_nzcom/u0/NZSUB.COM deleted file mode 100644 index 2c4c4ba2..00000000 Binary files a/Source/Images/d_nzcom/u0/NZSUB.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/NZSUB.SUB b/Source/Images/d_nzcom/u0/NZSUB.SUB deleted file mode 100644 index d3614775..00000000 --- a/Source/Images/d_nzcom/u0/NZSUB.SUB +++ /dev/null @@ -1,67 +0,0 @@ - -; Batch File: NZSUB.SUB -; Author: Joe Wright -; Date: 8 November 1988 - -; This file demonstrates NZSUB's ability to handle 'formatted' -; input files. The programmer will note the similarity to -; annotated assembly source files. - -; This file will also serve as the documentation of NZSUB's -; features and function. - -; As these lines suggest, any line that begins with a ; is considered -; a full-line comment and is ignored. - -û         Thå  lefô squigglù brackeô '{§ aó  thå  firsô -          characteò  oæ  á  linå  wilì  puô  NZSUÂ   iî -          'comment§  mode® Alì  subsequenô  characteró -          untiì  á  righô  squigglù  brackeô  wilì   bå -          ignored® Checë thió out® } - -; The ; character is usually the command separator for a Z3 -; multiple command line. It is therefore a 'normal' character -; unless it is the first one and may appear freely in the command -; line preceded by anything except a space. - -; Blank lines are ignored in any case. - -{ -Alì oæ thå abovå lineó anä theså lineó arå someho÷ commenteä  ouô -oò  arå blanë anä thereforå wilì noô appeaò iî thå  $$$.SUÂ  filå -whicè wilì bå thå resulô oæ alì this® - -NZSUÂ  ió  completelù CP/Í compatible® Nonetheless¬  iæ  runninç -undeò  Ú-System¬  DUº anä DIRº formó maù bå useä tï  declarå  thå -sourcå file® Thå sourcå filå ió assumeä tï havå typå .SUÂ unlesó -otherwiså declared® Iî anù case¬ $$$.SUÂ wilì bå writteî tï  thå -currenô directory® -} - -» Ok¬ dowî tï business. The main point of all this is to relax -; some of the constraints on command line formatting so that -; a batch file becomes more legible and therefore maintainable. - -; Command lines may be indented with any combination of spaces -; and tabs. A tab within a command line will be converted to -; a space. Spaces between the last command character and the -; in-line comment ';' or ';;' will be removed and the comment -; ignored. The ';;' ZEX form may be used but is unnecessary -; if the ';' is preceded by at least one space (or tab). - -;; -;; NZSUB Demo Program -;; - $; nzsub demonstration ;; Use $; for literal ; - DIR *.COM ;; Let's see what we've got - - echo display source files? (yes or no) - - IF INPUT ; Ask the user (indented) - DIR *.Z80 - FI ; endif -;; -;; End of NZSUB Demo Program -; -; End of NZSUB.SUB - \ No newline at end of file diff --git a/Source/Images/d_nzcom/u0/P.COM b/Source/Images/d_nzcom/u0/P.COM deleted file mode 100644 index b71218b8..00000000 Binary files a/Source/Images/d_nzcom/u0/P.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/PATH.COM b/Source/Images/d_nzcom/u0/PATH.COM index 89b73ede..1245c1ad 100644 Binary files a/Source/Images/d_nzcom/u0/PATH.COM and b/Source/Images/d_nzcom/u0/PATH.COM differ diff --git a/Source/Images/d_nzcom/u0/POKE.COM b/Source/Images/d_nzcom/u0/POKE.COM deleted file mode 100644 index c9d5f763..00000000 Binary files a/Source/Images/d_nzcom/u0/POKE.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/PWD.COM b/Source/Images/d_nzcom/u0/PWD.COM new file mode 100644 index 00000000..dda7ce75 Binary files /dev/null and b/Source/Images/d_nzcom/u0/PWD.COM differ diff --git a/Source/Images/d_nzcom/u0/RCP.LBR b/Source/Images/d_nzcom/u0/RCP.LBR new file mode 100644 index 00000000..fdc452f6 Binary files /dev/null and b/Source/Images/d_nzcom/u0/RCP.LBR differ diff --git a/Source/Images/d_nzcom/u0/RELEASE.NOT b/Source/Images/d_nzcom/u0/RELEASE.NOT index 9cdf40fc..6b006422 100644 --- a/Source/Images/d_nzcom/u0/RELEASE.NOT +++ b/Source/Images/d_nzcom/u0/RELEASE.NOT @@ -1,78 +1,266 @@ - -- RELEASE.NOT -- - January 20, 1989 + RELEASE.NOT - UPDATE INFORMATION ON NZCOM - This file contains last-minute information about NZ-COM. +Please understand that unlike purely commercial enterprises, Z-System is +mainly the work of enthusiasts. As such, Z-System never truly reaches +completion; each new development is more of a plateau upon which further +innovation occurs. Though we have done our best, the printed documentation +inevitably lags behind the most recent enhancements. Try to consult as +much material as possible about a given command before proceeding; if there +is a help or document file pertaining to the command, it supercedes printed +instructions, especially with regard to such matters as syntax or technical +specifications. ----------- NZ-COM RELEASE 1.2d January 20, 1989 -NZCOM.COM version 1.2d fixes yet another minor bug by forcing a warm boot -whenever a new DOS module is loaded by itself. The progress report is also -a little less verbose unless declaring the /T(est) option. If the /Q(uiet) -option is declared, NZCOM is now completely silent. +Notes of September 12, 1991 +=========================== -New versions of SAVE.COM, ERA.COM, REN.COM, P.COM, POKE.COM and their Type -3 counterparts are included in this release. + Release 1.2H involves a significant updating of the support utilities that +we distribute as a courtesy with NZCOM. We suggest that any Z-System user who +is not in regular contact with a Z-Node consider taking advantage of the Z- +System Software Update Service (ZSUS). Here are some of the important changes +with this release. -NZSUB version 1.0 is released. Unlike SUB.COM, NZSUB is not ZCPR3 specific -and runs as well under CP/M. Its batch files can be formatted and -commented much like those of ZEX4. See NZSUB.SUB for an example. +ZCNFG.COM, CONFIG.LBR: + Al Hawley has introduced a powerful and convenient method for + configuring programs. ZCNFG works either with individual CFG files or + with CFG files stored in the CONFIG.LBR library. For example, try + running the command "ZCNFG ZLT". ZCNFG will automatically extract + ZLT15.CFG from CONFIG.LBR. -TCSELECT version 1.2 can now be run successfully under CP/M. +NZBLITZ.COM, NZBLTZ14.HZP, NZBLTZ14.CFG: + These programs can be used to coldboot an NZCOM system very rapidly + with the complete system, including error handlers, shells, and TCAPs + already in place. Run "HELP NZBLTZ14" to learn more about it. Some + of its features can be tailored by running "ZCNFG NZBLITZ" with the + CFG file in the same directory. +LBREXT.COM: + This replaces LGET for extracting member files from LBR library files. ----------- NZ-COM RELEASE 1.2 November 1, 1988 +HELP.COM, LBRHELP.COM, HLPFILES.LBR: + HELP (actually HELPC14) is an improved version of the help utility. + It can work with normal help files (HLP) and crunched help files + (HZP). LBRHELP can work with normal or crunched help files that are + stored in a library (which is where we have put all the help files + distributed with NZCOM and Z3PLUS). -NZCOM.COM version 1.2 includes two changes of significance. First, due to -an oversight, when version 1.0 was told to load a new ZCM or ENV file for -which no new modules had to be loaded, it also failed to update data in the -environment, such as printer characteristics, maximum drives and users, and -so on. It will now do so. +DOCFILES.LBR: + Documentation and help files have been collected into an LBR file. -The second change was in the safety checking performed by NZCOM.COM to make -sure that it was being requested to load a version that was consistent with -the underlying CP/M system. This check turned out to be stricter than -necessary and was preventing NZ-COM from running on some systems (e.g., -Lobo Max) that, in fact, could support it quite nicely. +ZLT.COM: + This is a full Z-System replacement for LT, and it handles the latest + LZH-compressed files. +COPY.COM: + This is the version of COPY from the ZSDOS/ZDDOS release. Enter "COPY + //" for syntax information. It replaces a dangerously defective copy + program provided with earlier releases. ----------- NEW FILES: NZCPR.LBR, NZFCP.LBR, NZRCP.LBR +LSH.COM, LSH.WZ, LSH-HELP.COM, LSHINST.COM, ZERR.COM: + These are the latest LSH command history shell and command-line editor + and the associated error handler (fixed-log versions). These + completely replace EASE. -The main NZCOM.LBR now contains only the standard configurations of the +CLEDINST.COM, CLEDSAVE.COM: + A transient history shell like LSH can be slow on floppy systems with + sluggish disk drives, even if the files have been placed in optimal + locations. Some of the RCP modules supplied no include an RCP- + resident command-line editor called CLED. Its features can be + configured using CLEDINST.COM, and the history can be saved to a file + using CLEDSAVE.COM. + +TCAP.LBR, XTCAP.COM: + A number of programs now require a terminal capabilities descriptor + (TCAP) with extended functions. The standard TCAPs loaded with + TCSELECT do not have these functions. TCAP.LBR is a collection of + extended TCAPs for some terminals. XTCAP.COM is a program that can + add the most important extensions to a standard TCAP (it is a quick- + and-dirty fix until the full set of TCAPs is updated). + +VIEW.COM: + This is Bridger Mitchell's file viewing utility. It is very powerful + (but it requires an extended TCAP). + +NAME.COM: + This program can quickly add or remove a name for a single directory. + +TCJ.INF: + TCJ has a new publisher (one of our own Z-Node sysops), and this file + tells how to take out a subscription (which all Z-System users + absolutely should do!). + +ZFILEB38.LZT: + This is the BRIEF listing of all the support programs currently + available for use with Z-System. There is another file which includes + descriptions of all the programs, but it would fill up an entire + diskette! + + +Notes of November 5, 1989 +========================= + + 1. NEW FILES: NZCPR.LBR, FCP.LBR, RCP.LBR. + 2. TCSELECT PROBLEM. + 3. ERA.COM, REN.COM, SAVE.COM, ETC. + 4. LSH REPLACES EASE. + 5. NZBLITZ IS HERE! + 6. MINOR UPDATE NOTES. + 7. TO NEW Z-SYSTEM USERS. + 8. ALERT ABOUT NZCPM.COM AND NZCOM.CCP + 9. CHANGE IN MKZCM DEFAULT AND STANDARD RCP + 10. NZBIO+.ZRL + +1. NEW FILES: NZCPR.LBR, FCP.LBR, RCP.LBR. + +The main NZCOM.LBR now contains only the standard configurations of the CPR, FCP, and RCP modules to minimize disk space requirements. Alternative -versions of these modules are now supplied in separate libraries. Modules -can be loaded directly from these libraries, or individual files can be -extracted and put into NZCOM.LBR to replace the default files. Each -library has a brief DOC file describing the modules (one of which is the +versions of these modules are now supplied in separate libraries. Modules +can be loaded directly from these libraries, or individual files can be +extracted and put into NZCOM.LBR to replace the default files. Each +library has a brief DOC file describing the modules (one of which is the default version included in NZCOM.LBR). ----------- NEW PROGRAM: ZEX TYPE 4 +2. TCSELECT PROBLEM. + +A problem has been discovered with the operation of TCSELECT as described +in the manual. TCSELECT is a Z-System program and does not function +reliably under CP/M (on some systems it causes a crash). Fortunately there +is a simple fix: reverse the order of the instructions in the manual. Boot +up NZ-COM before attempting to create MYTERM.Z3T, and run TCSELECT only +after NZ-COM is running. Remember that you need both TCSELECT.COM and +Z3TCAP.TCP to generate your .Z3T file. The entries in the Z3TCAP library +for the Xerox computers has been patched to correct a long-standing error. + + +3. ERA.COM, REN.COM, SAVE.COM, ETC. + +The standard configuration of Z-System does not include resident commands +for REN, DIR, or SAVE. These are provided instead as transient programs +(COM files), and you should copy any that you need to your working disk. +Several other functions that are often resident (but not always) are also +provided as COM files. Many of these are type-4 programs (see the manual). +We recommend using SDZ.COM as your standard directory display utility. It +is far more functional than the resident DIR commands in either Z-System or +CP/M, and it is designed to work properly under both. + + +4. LSH REPLACES EASE. + +EASE has been replaced by a newer, more versatile, and well-behaved program +named LSH (Log SHell). Like EASE, LSH allows you to edit command lines +using WordStar-like control. Consult LSH.WZ before use for general +information and/or run HELPLSH while running LSH for a display of LSH's +capabilities. LSHINST installs and customizes LSH to taste. Error +handling is now taken care of by ZERR.COM, a separate program. Our great +thanks to Rob Friefeld for writing these superb Z-System tools and for +allowing us to include them with NZ-COM. + + +5. NZBLITZ IS HERE! + +NZBLITZ is now included with NZ-COM. True to its name, NZBLITZ loads NZCOM +(and ZSDOS/ZDDOS, if present) in a flash, making it extremely helpful in +saving and reloading a given system configuration. Once you have configured +your system as wanted, log to drive A0: and type NZBLITZ NZLOAD. From now +on, your startup to Z-System is MUCH faster; just type "NZLOAD". Be +forewarned that NZBLITZ saves EVERYTHING as currently active, so be sure you +are at the directory (usually A0:) you wish the system to start in, and be +sure all active shells, flow states, terminal definitions, and so on are the +desired ones before proceeding. See NZBLITZ.NZT for more information. + + +6. MINOR UPDATE NOTES. + +- The RCP WHLQ command no longer used. "WHL" alone displays the current + wheel status. As before, "WHL password" turns the wheel byte on. "WHL + xxx" now turns the wheel byte OFF if xxx is something other than the + correct password. + +- ARUNZ is now a type-4 program which loads at the highest possible + memory location so as to save lower memory for immediate re-execution + with GO. See ARUNZ09R.DZC and TCJ31.MZG (as well as your NZ-COM or + Z3PLUS manual) for more on the amazing ARUNZ. + +- VLU is no longer included, as it was not reliable. Please use LDIR, + LGET, LPUT, and LT instead to manipulate library files. + +- At this writing, we are sorry to note that both Z-Node Central and the + Lillipute Z-Nodes mentioned in Chapter 7 of your manual are out of + service. Z-Nodes 2 and 3 are still going strong, however, as are the + many others listed in ZNODES.LST. Z-Node 2 is the new Z-Node Central. + We cannot recommend highly enough that you get a modem and investigate at + least one Z-Node as a source of inspiration and support. + +- Version 5.0 of the ZEX batch processor replaces earlier versions. ZEX + now runs under both NZ-COM and Z3PLUS and is a very powerful means of + customizing programs and commands. The ZEX.RSX file mentioned in the + manual is no longer required. See ZEX50.DZC for help. + + +7. TO NEW Z-SYSTEM USERS. + +- All files whose middle filetype character is "Z" (e.g., SAMPLE.DZC) are + "crunched" files which must be uncompressed with LT.COM or UNCRUNCH.COM + before use. + +- As outlined in section 4.3.2 of your manual, most Z-System programs + have built-in help; type the name of any program followed by "//" for a + short description if you are confused. + +- Finally, it is not at all necessary to master every nuance of the system + before it becomes useful. Please don't try to devour all of Z-System at + once. Instead, we suggest that you decide what aspects of the system you + will find most helpful and try to master one or two of those before + exploring further. We think you will find that if you choose wisely, + learning one aspect fully not only makes that aspect of your computing + world easier, it also equips you with the skills and confidence with + which to surmount other aspects of Z-System more confidently. + + +8. ALERT ABOUT NZCPM.COM AND NZCOM.CCP + +Some users have been tripped up by the way NZCOM handles the generation of +an NZCPM.COM file: it writes such a file only if one does not already exist. +This speeds up loading of the system. However, should you ever make any +change to your base CP/M system (such as installing ZSDOS/ZDDOS or a new +version of the BIOS), you should be sure to erase the NZCPM.COM file so that +NZCOM will create a new one the next time it is run. You should also be +sure to use NZCOM and not NZBLITZ to load the system the first time after +such a change. + +You should also be aware that warmboots of the NZ-COM Z-System are performed +by loading the NZCOM.CCP file containing the image of the current command +processor. This means that you must not change the diskette in the A: drive +unless you have copied the **CURRENT** NZCOM.CCP file (if you only use one +system configuration, the current version will not change) to the diskette +you are going to place in the A: drive. + -Named ZEX4.COM in this release, this is a new version of ZEX with many -features not seen before (See ZEX4.DOC). This program is brand new and may -well exhibit one or two un-planned 'features'. Please report any problems. +9. CHANGE IN MKZCM DEFAULT AND STANDARD RCP +Carson Wilson and Rob Friefeld have made some major changes in the RCP code +(see the DOC file in RCP.LBR). The standard RCP module is now 18 records +long instead of 16. MKZCM.COM has been patched to provide this value as a +default. ----------- EASE PROBLEMS -There are unfortunately serious defects in the coding of EASE. On many -computers EASE appears to work without problems, on some it crashes -completely, and on others it works but causes other strange behavior to -occur. We tried using it on a Kaypro with the K-ROM (?), and as soon as we -loaded ease we started to get BIOS errors on disk reads. We hope that the -author of EASE (or someone else) will discover and correct these problems. -The program is so nice conceptually. Use it with caution. +10. NZBIO+.ZRL +The BIOS in some computers uses the Z80 index and alternate registers +without +saving them on the stack and restoring them. These computers include the +TeleVideo 80x machines, the Oneac On!, the Zorba, and a number of other +machines. Some programs (JETLDR and EDITNDR, for example) will not function +properly under these conditions. The file NZBIO+.ZRL in NZCOM.LBR is a +special version of the NZ-COM virtual BIOS that protects the Zilog registers +across all BIOS calls. To use this BIOS, you much use MKZCM to allocate 4 +records to the BIOS. If you observe strange behavior on your system with +NZ-COM, you might want to try using this special BIOS. ----------- VLU VERSIONS -We forgot when writing the manual that VLU, like ZFILER, has two quite -different versions depending on whether your terminal uses reverse or dim -video for highlighting. Therefore, you will find on the release disk VLU- -REV.COM and VLU-DIM.COM. Copy the appropriate one to your working disk -under the name VLU.COM. - End of RELEASE.NOT - - \ No newline at end of file + \ No newline at end of file diff --git a/Source/Images/d_nzcom/u0/SAINST.COM b/Source/Images/d_nzcom/u0/SAINST.COM new file mode 100644 index 00000000..1e6db48f Binary files /dev/null and b/Source/Images/d_nzcom/u0/SAINST.COM differ diff --git a/Source/Images/d_nzcom/u0/SALIAS.COM b/Source/Images/d_nzcom/u0/SALIAS.COM index 1b0b13e9..9b85e041 100644 Binary files a/Source/Images/d_nzcom/u0/SALIAS.COM and b/Source/Images/d_nzcom/u0/SALIAS.COM differ diff --git a/Source/Images/d_nzcom/u0/SAVE.COM b/Source/Images/d_nzcom/u0/SAVE.COM deleted file mode 100644 index 76254cd7..00000000 Binary files a/Source/Images/d_nzcom/u0/SAVE.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/SAVENDR.COM b/Source/Images/d_nzcom/u0/SAVENDR.COM index 4dcfd14e..bf8d1125 100644 Binary files a/Source/Images/d_nzcom/u0/SAVENDR.COM and b/Source/Images/d_nzcom/u0/SAVENDR.COM differ diff --git a/Source/Images/d_nzcom/u0/SHOW.COM b/Source/Images/d_nzcom/u0/SHOW.COM index 3fbe6069..b22ce699 100644 Binary files a/Source/Images/d_nzcom/u0/SHOW.COM and b/Source/Images/d_nzcom/u0/SHOW.COM differ diff --git a/Source/Images/d_nzcom/u0/SHSET.COM b/Source/Images/d_nzcom/u0/SHSET.COM deleted file mode 100644 index a351d9e5..00000000 Binary files a/Source/Images/d_nzcom/u0/SHSET.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/SPOP.COM b/Source/Images/d_nzcom/u0/SPOP.COM deleted file mode 100644 index 471d9383..00000000 Binary files a/Source/Images/d_nzcom/u0/SPOP.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/SUBMIT.COM b/Source/Images/d_nzcom/u0/SUBMIT.COM new file mode 100644 index 00000000..2e788827 Binary files /dev/null and b/Source/Images/d_nzcom/u0/SUBMIT.COM differ diff --git a/Source/Images/d_nzcom/u0/TCAP.LBR b/Source/Images/d_nzcom/u0/TCAP.LBR new file mode 100644 index 00000000..9a3fd9c5 Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCAP.LBR differ diff --git a/Source/Images/d_nzcom/u0/TCJ.INF b/Source/Images/d_nzcom/u0/TCJ.INF new file mode 100644 index 00000000..7d9322c1 --- /dev/null +++ b/Source/Images/d_nzcom/u0/TCJ.INF @@ -0,0 +1,31 @@ + + Information About Subscriptions to + + The Computer Journal + + +The subscription rates for TCJ as of September 1, 1991, are as follows: + + 1 year 2 years + ------ ------- + U.S. $18 $32 + Foreign (surface mail) $24 $44 + Foreign (air mail) $38 $72 + +There are six issues per year. To place a subscription, contact the +new publisher (as of July 1992): + + The Computer Journal + P.O. Box 535 + Lincoln, CA 95658 + 916-645-1670 (answering machine and FAX) + +You may order a trial subscription. Just place an order. If you decide +that TCJ is not for you, then just mark the invoice "cancel" and send it +back. + +Payments for TCJ must normally be in the form of a money order or a check +drawn on a U.S. bank in U.S. funds or a postal money order in U.S. funds. +It is expected that MasterCard and VISA will again be acceptable in the +future. + \ No newline at end of file diff --git a/Source/Images/d_nzcom/u0/TCJ25.WZ b/Source/Images/d_nzcom/u0/TCJ25.WZ new file mode 100644 index 00000000..ad1a14e6 Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCJ25.WZ differ diff --git a/Source/Images/d_nzcom/u0/TCJ26.WZ b/Source/Images/d_nzcom/u0/TCJ26.WZ new file mode 100644 index 00000000..97410f76 Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCJ26.WZ differ diff --git a/Source/Images/d_nzcom/u0/TCJ27.WZ b/Source/Images/d_nzcom/u0/TCJ27.WZ new file mode 100644 index 00000000..35905c13 Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCJ27.WZ differ diff --git a/Source/Images/d_nzcom/u0/TCJ28.WZ b/Source/Images/d_nzcom/u0/TCJ28.WZ new file mode 100644 index 00000000..ba0bdd5a Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCJ28.WZ differ diff --git a/Source/Images/d_nzcom/u0/TCJ29.WZ b/Source/Images/d_nzcom/u0/TCJ29.WZ new file mode 100644 index 00000000..116420dd Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCJ29.WZ differ diff --git a/Source/Images/d_nzcom/u0/TCJ30.WZ b/Source/Images/d_nzcom/u0/TCJ30.WZ new file mode 100644 index 00000000..dca4d430 Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCJ30.WZ differ diff --git a/Source/Images/d_nzcom/u0/TCJ31UPD.WZ b/Source/Images/d_nzcom/u0/TCJ31UPD.WZ new file mode 100644 index 00000000..f55b7042 Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCJ31UPD.WZ differ diff --git a/Source/Images/d_nzcom/u0/TCJ32.WZ b/Source/Images/d_nzcom/u0/TCJ32.WZ new file mode 100644 index 00000000..87a3caac Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCJ32.WZ differ diff --git a/Source/Images/d_nzcom/u0/TCJ33UPD.WZ b/Source/Images/d_nzcom/u0/TCJ33UPD.WZ new file mode 100644 index 00000000..782a9e35 Binary files /dev/null and b/Source/Images/d_nzcom/u0/TCJ33UPD.WZ differ diff --git a/Source/Images/d_nzcom/u0/TCSELECT.COM b/Source/Images/d_nzcom/u0/TCSELECT.COM index 2fb5f322..4b29b83e 100644 Binary files a/Source/Images/d_nzcom/u0/TCSELECT.COM and b/Source/Images/d_nzcom/u0/TCSELECT.COM differ diff --git a/Source/Images/d_nzcom/u0/TY3ERA.COM b/Source/Images/d_nzcom/u0/TY3ERA.COM new file mode 100644 index 00000000..ac18c870 Binary files /dev/null and b/Source/Images/d_nzcom/u0/TY3ERA.COM differ diff --git a/Source/Images/d_nzcom/u0/TY3REN.COM b/Source/Images/d_nzcom/u0/TY3REN.COM new file mode 100644 index 00000000..e5746c57 Binary files /dev/null and b/Source/Images/d_nzcom/u0/TY3REN.COM differ diff --git a/Source/Images/d_nzcom/u0/TY4ERA.COM b/Source/Images/d_nzcom/u0/TY4ERA.COM new file mode 100644 index 00000000..77dac7fd Binary files /dev/null and b/Source/Images/d_nzcom/u0/TY4ERA.COM differ diff --git a/Source/Images/d_nzcom/u0/REN.COM b/Source/Images/d_nzcom/u0/TY4REN.COM similarity index 100% rename from Source/Images/d_nzcom/u0/REN.COM rename to Source/Images/d_nzcom/u0/TY4REN.COM diff --git a/Source/Images/d_nzcom/u0/TY4SAVE.COM b/Source/Images/d_nzcom/u0/TY4SAVE.COM new file mode 100644 index 00000000..59f9d77f Binary files /dev/null and b/Source/Images/d_nzcom/u0/TY4SAVE.COM differ diff --git a/Source/Images/d_nzcom/u0/SP.COM b/Source/Images/d_nzcom/u0/TY4SP.COM similarity index 100% rename from Source/Images/d_nzcom/u0/SP.COM rename to Source/Images/d_nzcom/u0/TY4SP.COM diff --git a/Source/Images/d_nzcom/u0/UNCR.COM b/Source/Images/d_nzcom/u0/UNCR.COM deleted file mode 100644 index 0dfe8947..00000000 Binary files a/Source/Images/d_nzcom/u0/UNCR.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/UNCRUNCH.COM b/Source/Images/d_nzcom/u0/UNCRUNCH.COM new file mode 100644 index 00000000..5ffb68ef Binary files /dev/null and b/Source/Images/d_nzcom/u0/UNCRUNCH.COM differ diff --git a/Source/Images/d_nzcom/u0/VARPACK.COM b/Source/Images/d_nzcom/u0/VARPACK.COM deleted file mode 100644 index 5760a8c7..00000000 Binary files a/Source/Images/d_nzcom/u0/VARPACK.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/VIEW.COM b/Source/Images/d_nzcom/u0/VIEW.COM index d1a17acb..c812f75e 100644 Binary files a/Source/Images/d_nzcom/u0/VIEW.COM and b/Source/Images/d_nzcom/u0/VIEW.COM differ diff --git a/Source/Images/d_nzcom/u0/XTCAP.COM b/Source/Images/d_nzcom/u0/XTCAP.COM new file mode 100644 index 00000000..06b26f08 Binary files /dev/null and b/Source/Images/d_nzcom/u0/XTCAP.COM differ diff --git a/Source/Images/d_nzcom/u0/Z3LOC.COM b/Source/Images/d_nzcom/u0/Z3LOC.COM index 563ad51b..fab1359c 100644 Binary files a/Source/Images/d_nzcom/u0/Z3LOC.COM and b/Source/Images/d_nzcom/u0/Z3LOC.COM differ diff --git a/Source/Images/d_nzcom/u0/Z3TCAP.TCP b/Source/Images/d_nzcom/u0/Z3TCAP.TCP index 98896073..07adc281 100644 Binary files a/Source/Images/d_nzcom/u0/Z3TCAP.TCP and b/Source/Images/d_nzcom/u0/Z3TCAP.TCP differ diff --git a/Source/Images/d_nzcom/u0/ZCNFG.COM b/Source/Images/d_nzcom/u0/ZCNFG.COM new file mode 100644 index 00000000..b88a2d0c Binary files /dev/null and b/Source/Images/d_nzcom/u0/ZCNFG.COM differ diff --git a/Source/Images/d_nzcom/u0/ZERR.COM b/Source/Images/d_nzcom/u0/ZERR.COM new file mode 100644 index 00000000..4565e7a7 Binary files /dev/null and b/Source/Images/d_nzcom/u0/ZERR.COM differ diff --git a/Source/Images/d_nzcom/u0/ZEX.COM b/Source/Images/d_nzcom/u0/ZEX.COM index b4cccec4..cd46405d 100644 Binary files a/Source/Images/d_nzcom/u0/ZEX.COM and b/Source/Images/d_nzcom/u0/ZEX.COM differ diff --git a/Source/Images/d_nzcom/u0/ZEX.RSX b/Source/Images/d_nzcom/u0/ZEX.RSX deleted file mode 100644 index ef10b0cf..00000000 Binary files a/Source/Images/d_nzcom/u0/ZEX.RSX and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/ZEX4.COM b/Source/Images/d_nzcom/u0/ZEX4.COM deleted file mode 100644 index d6058440..00000000 Binary files a/Source/Images/d_nzcom/u0/ZEX4.COM and /dev/null differ diff --git a/Source/Images/d_nzcom/u0/ZEX4.DOC b/Source/Images/d_nzcom/u0/ZEX4.DOC deleted file mode 100644 index 514f417c..00000000 --- a/Source/Images/d_nzcom/u0/ZEX4.DOC +++ /dev/null @@ -1,329 +0,0 @@ - - ZEØ Typå ´ ö 0.4 - bù Joå Wright - ¹ Noö 88 - -Linå  formattinç  haó beeî furtheò relaxeä bù allowinç  á  singlå -semicoloî ';§ tï denotå á commenô iæ iô ió thå firsô characteò oî -thå  linå  oò  iæ iô ió precedeä bù á  spacå  oò  tab®   Multiplå -Commanä syntaø ERÁ *.BAK;ERÁ *.PRN;DIÒ *.Z8° stilì workó  becauså -thå  characteò prioò tï » ió noô á space® Thå ';;§  ZEØ  commenô -forí  stilì  workó buô ió unnecessary® Iæ » ió requireä  aô  thå -beginninç  oæ á linå oò afteò á space¬ uså thå $»  literaì  form® -Alsï  addeä  ió  thå 'field§ comment®   - -     û Á lefô 'curly§ brackeô '{§ aó thå firsô characteò  oæ -     á particulaò linå wilì causå alì subsequenô  characteró -     anä  lineó  tï  bå treateä  aó  commenô  anä  otherwiså -     ignoreä  untiì  thå correspondinç closinç  brackeô   ió -     encountered® Thió entirå paragrapè woulä bå treateä aó -     á 'field§ comment® } - - - ZEØ Typå ´ ö 0.3 - bù Joå Wright - 2· Ocô 88 - -Thå formidablå Howarä Goldsteiî haó strucë again® Thå  followinç -changeó aô hió suggestion® Thankó Howard. - -1® Thå '¤ § literaì describeä belo÷ ió actuallù implementeä now. - -2®   Thå  senså oæ thå ^¡ aborô controì ió changeä tï  bå  activå -wheî thå flo÷ statå ió TRUÅ ratheò thaî FALSE® Morå logical. - -3®   Thå  ^º Rå-executå controì ió changeä  tï  rå-establisè  thå -defaulô flagó ratheò thaî settinç theí tï 0. - - - ZEØ Typå ´ ö 0.2 - bù Joå Wright - 24 Ocô 88 - -Alì thió starteä becauså É inadvertantlù lefô ZEX.COÍ ouô oæ  thå -originaì  NÚ-COÍ  release®   Afteò  somå  complaintó  abouô  thió -oversight¬ É pickeä uð Jay'ó NZEØ-Ä froí Ú-Nodå Centraì anä trieä -iô out® Althougè admittedlù stilì undeò development¬ NZEØ didn'ô -seeí tï worë right® É theî examineä thå ZEØ 3.² releaså  versioî -anä founä iô eveî worse® Mù buttoî waó pushed. - -Oveò  thå pasô fouò weekó oò sï É havå rå-writteî ZEØ morå tï  mù -liking®   ZEØ ió no÷ á Typå ´ utilitù foò NÚ-COÍ anä  Z3PLUS®   É -havå addeä thå controló Jaù mentionó iî NZEØ-D.DOÃ anä onå oò twï -oæ mù owî iî thå meantime. - -ZCPR3´  ió  changeä  sï thaô intrinsiã (GET¬  JUMÐ  anä  GO©  anä -residenô (POKE¬ PEEK¬ etc.© commandó caî geô theiò argumentó froí -ZEØ  script® Previously¬ ZCPR3ø turneä ofæ ZEØ  redirectioî  foò -alì  buô transienô (.COM© commands® No÷ Z3´ enableó ZEØ foò  alì -CPR¬  FCÐ anä RCÐ commandó aó welì aó transients®   NZCPR.ZRÌ  iî -thió  packagå ió thå latesô ZCPR3´ anä shoulä bå  'installed§  oî -youò  NÚ-COÍ systeí beforå attemptinç tï ruî Ne÷ ZEØ Typå  ´  anä -itó demonstratioî .ZEØ files. - -Thå  majoò  changå  tï ZEØ waó iî thå  CONST¬  CONIÎ  anä  CONOUÔ Šdepartmentó  sï  thaô ZEØ maù no÷ bå useä tï  'drive§  dBASÅ  II¬ -WordStaò  anä MultiPlan® Theså programó (anä others© attempô  tï -flusè  keyboarä inpuô froí timå tï timå anä reallù gavå  thå  olä -ZEØ á fit® Theså programó ruî correctlù witè ZEØ Typå 4. - -Anotheò probleí witè olä ZEØ waó thaô iô requireä á calì tï CONIÎ -tï dï anything® Manù programó calì CONSÔ tï seå iæ á keù ió dowî -and¬ iæ not¬ gï abouô theiò business® Witè olä ZEX¬ thå ^"ß useò -inpuô  commanä woulä noô gï intï effecô untiì CONIÎ  waó  called® -Thå  ^"ß  commanä haó nï datá tï return® Whaô tï  do¿   Olä  ZEØ -waiteä foò thå useò tï presó á key® Buô whaô iæ hå doesn't¿ Olä -ZEØ simplù hunç uð anä waiteä foò it¬ eveî iæ iô wasn'ô necessarù -foò thå program® Sï mucè foò unattendeä operation¡ - -ZEØ  Typå ´ haó á looë-aheaä featurå tï finä thå ^"ß commanä  anä -executå  iô beforå returninç thå previouó character®   Subsequenô -calló  tï  CONSÔ  yielä  reaì  keyboarä  statuó  (ZEØ  inpuô   ió -suspended)® Wheî ZEØ ió turneä oî agaiî (witè thå 'trigger§ oò á -ne÷ command© iô wilì picë uð witè thå characteò followinç thå ^"ß -commanä  anä continuå normally® Notå thaô almosô  anù  characteò -maù bå useä aó thå Useò Inpuô Triggeò excepô 'space§ anä 'tab'. - -ZCPR3´ anä ZEØ Typå ´ arå verù closelù coupleä anä leavå messageó -tï  eacè other® ZCPR3´ controló ZEØ througè thå ZEXINÐ  flaç  aô -Z3MSG+7®   ZEØ checkó anä maù changå thå ZEXINÐ flaç foò itó  owî -purposeó aó well® ZEØ maintainó pointeró tï thå currenô bytå  oæ -thå  ZEØ  inpuô  strinç anä tï thå beginninç  oæ  thå  strinç  aô -Z3MSG+¹  anä  Z3MSG+11¬ respectively® - -ZEØ  caî alsï controì thå operatioî oæ ZCPR3´ bù manipulatioî  oæ -thå  QUIEÔ  flag®   ZCPR3´ ió assembleä witè  itó  ZEXNOISÅ  (anä -SUBNOISE© equaì 1® Thió meanó thå Z3´ commanä prompô  (A0:BASE>© -maù  bå suppresseä undeò ZEØ (oò SUB© bù settinç thå QUIEÔ  flag® -Thió begó á ne÷ commanä foò ZEØ (Þ-)® - -Iæ thå QUIEÔ flaç ió set¬ ZEØ wilì alsï suppresó thå echï oæ  thå -commanä  linå  froí  Z34®   Further¬  iæ  thå  ^£  (Suppresó  ZEØ -messages©  commanä ió alsï iî force¬ ZEØ anä Z3´  arå  completelù -silenô  anä  thå batcè commandó executå aó iæ froí  thå  multiplå -commanä  linå oò aliaó scripô witè nï extraneouó reportó  tï  thå -console. - -Therå  arå á numbeò oæ 'flag§ optionó whicè telì ZEØ ho÷  tï  acô -whilå executing® - - ^½ XSUÂ Enablå ZEØ inpuô tï COMmand - ^­ QUIEÔ Seô ZCPR³ Quieô flag - ^£ MSUÐ Suppresó ZEØ messages - ^® PSUÐ Suppresó alì Consolå output - ^¦ IPSUÐ Suppresó Console iæ false - -ZEØ  Typå ´ defaultó tï alì flagó (excepô XSUB© OFÆ  anä  inviteó -thå useò tï specifù hió optionó oî thå commanä linå oò tï specifù -theí iî thå batcè filå (Theså flags¬ anä others¬ maù bå 'patched§ -OÎ  witè  ZPATCÈ  oò bù otheò meanó tï customizå ZEØ  iæ  yoõ  sï -desire® Seå PATCHEÓ below.)® - - zeø batcè parm± parm² ^­ ^£ ^. - -ZEØ  Typå ´ wilì picë uð thå twï parameteró anä thå ZEØ  controló -beforå  processinç thå batcè file¬ thuó establishinç  'defaults'® -Iî  thió case¬ Þ­ setó thå QUIEÔ flag¬ ^£ turnó ZEØ messageó  ofæ -anä  ^® suppresseó consolå output® Controló oî thå commanä  taiì -arå processeä firsô anä becomå thå initiaì characteró oæ thå  ZEØ -string®   Thå batcè filå ió theî appendeä tï them® Iî thió  way¬ -thå ^º re-executå controì wilì alsï re-executå them. - -Notå thaô ZEØ Typå ´ treató alì 'flag§ commandó aó 'toggles§ sucè Šthaô thå firsô ^£ wilì suppresó ZEØ messageó anä thå nexô ^£ wilì -turî theí oî again® Á thirä onå wilì turî theí off¬ etc. - -Yoõ  caî  probablù  uså ZEØ Typå ´ witè mosô  oæ  youò  olä  NZEØ -scriptó witè littlå oò nï changå (^Û anä ^Ý controló oæ NZEØ  arå -noô supporteä becauså therå ió nï discerniblå neeä foò them). - -É  havå addeä thå XSUÂ (^=© togglå tï helð controì ZEØ  inpuô  tï -thå  runninç  program®   ^½ anä ^¥ arå  useä  iî  combinatioî  tï -controì  ZEØ input® Notå thaô theù arå complementary®   XSUÂ  OÎ -wilì  allo÷  ZEØ inpuô foò thå remaindeò oæ thå filå  excepô  foò -thoså  commandó whicè begiî witè ^%® Conversely¬ XSUÂ  OFÆ  wilì -turî  ZEØ Inpuô OFÆ afteò eacè commanä excepô foò thoså  precedeä -bù ^%. - -Notå  thaô thå normaì statå oæ ZEØ Inpuô ió ON® ZCPR3´ setó  ZEØ -Statuó tï 0± wheî promptinç anä tï 0° wheî executinç thå COMmand® -Iî  botè cases¬ ZEØ Inpuô ió ON® Iô ió uð tï ZEX¬ anä nï  simplå -matter¬  tï seô ZEØ Statuó tï 0² tï suspenä ZEØ Inpuô aô thå  enä -oæ  thå commanä oò aó thå situatioî maù require® Iô ió safeò  tï -assumå  ZEØ Inpuô OÎ anä turî iô ofæ selectivelù thaî tï turî  iô -OFÆ witè XSUB=° anä assumå thaô iô stayó off® - -Consideò  thaô ZEØ maù executå aî aliaó anä thå programmeò  wantó -Useò  Inpuô  aô  thå  enä oæ itó  execution®   ZEØ  wilì  executå -alias.COÍ  anä  politelù turî itselæ off®   Thå  alias¬  however¬ -causeó Z3´ tï executå yeô anotheò commanä whicè ZEØ knowó nothinç -about®   Z3´  theî turnó ZEØ Inpuô bacë on® Noô whaô  yoõ  mighô -expect®   Foò thió reason¬ É havå defaulteä thå XSUÂ flaç OÎ  foò -mù  purposes®   É havå donå extensivå testinç witè XSUÂ  ofæ  anä -finä thaô iô workó perfectlù welì excepô foò thå caså oæ multiplå -commanä aliaseó anä sucè aó explaineä above® - -ZEØ  Typå ´ startó uð witè ZEØ Inpuô (XSUB© activå  anä  requireó -thå  ZEØ OFÆ commanä ^¥ aô thå beginninç oæ thå linå oò thå  Useò -Inpuô  commanä  ^"ß  tï  suspenä ZEX® Iæ  ZEØ  inpuô  shoulä  bå -suspendeä foò thå entirå command... - - ^%ddô [parms] - -wilì  turî  ZEØ ofæ untiì DDÔ quits® Morå elaboratå  ZEØ  on/ofæ -controló  uså thå Useò Inpuô commanä anä itó trigger®   Therå  ió -onå 'gotcha§ witè thå ^"ß however® Thå Useò Inpuô commanä cannoô -immediatelù  follo÷ á COMmanä invocation® Thió ió  becauså  eveî -thougè ZEØ haó turneä itselæ off¬ thå commanä processoò wilì turî -ZEØ bacë oî whilå executinç thå command® Therå musô bå aô  leasô -onå  interveninç characteò oò controì betweeî thå CÒ  whicè  wilì -executå  thå  commanä anä thå ^"ß whicè wilì suspenä  ZEØ  input® -Consideò thå ZEØ script: - - ddt|d100,17f|^"~g0 - Þ ^ -ZEØ  Typå ´ findó thå Useò Inpuô command¬ executeó iô  anä  moveó -itó  pointeò  pasô iô beforå returninç thå CÒ tï DDT®   Wheî  thå -useò  typeó thå triggeò (þ iî thió case© ZEØ resumeó witè thå  g° -commanä causinç DDÔ tï quit. - - ddt|^"~d100... - Þ ^ -Thió forí wilì noô work® Aó before¬ ZEØ doeó turî oî Useò  Inpuô -buô  Z3´ wilì turî iô ofæ aó iô executeó DDT.COM® Therå musô  bå -aô  leasô onå interveninç character® Knowinç thaô DDÔ useó  BDOÓ -functioî 1° tï reaä itó commandó anä knowinç thaô á backspacå  ió -ignoreä aó thå firsô characteò oæ thå line.. - - ddt|^h^"~d100... - -workó  perfectly® Somå programó (WordStaò foò example©  continuå -testinç  thå  keyboarä  eveî  aó  theù  quit®   Iô  ió  thereforå -necessarù  tï  seô  Useò Inpuô  immediatelù  followinç  thå  quiô -commandó  sï  thaô WordStaò won'ô trù tï reaä  thå  ZEØ  commandó -whilå quitting.. Š - wó file.ws|^qfDear||^"~^kx^" - Þ ^ -Thå firsô UÉ commanä suspendó ZEØ untiì thå useò typeó '~'®   Thå -seconä UÉ suspendó ZEØ untiì WordStaò actuallù quits. - -Commanä  linå  parsinç haó changeä witè respecô tï  thå  TAÂ  anä -SPACÅ characters® Tabó anä Spaceó aô thå beginninç oæ á  commanä -linå  arå  ignored®   Subsequenô tabó arå  converteä  tï  spaces® -Subsequenô  spaceó prioò tï thå ZEØ commenô oò CÒ thaô  endó  thå -linå arå removed® - -Iæ  yoõ musô precedå á commanä witè á Space¬ uså thå ZEØ  literaì -'¤ § tï dï so® Thió allowó morå freedoí tï formaô thå inpuô filå -foò readability® ZEØ Commentó ';;§ caî bå useä oî anù line® Foò -example: - -;» Takå á letteò Misó Jones - - wó $1.ltò ;» Creatå thå letteò file - ^krform.ltò ;» Reaä iî thå standarä form - ^kfDearü ;» Finä thå salutation - -;» Positioî thå cursoò anä starô UÉ untiì '~'¬ resumå ZEØ -;» tï telì WordStaò tï savå thå letteò anä starô UÉ again. - ^qd^"~^kx^" - -;» Enä oæ thió letter - -Wå caî alsï makå logiã flowó morå readablå thió way: - -» Assemblå $± Sourcå Filå ;» Echï froí Z3´ controlleä bù QUIET - - ^-^£ ;» Z3´ Quiet¬ nï ZEØ messages - iæ eø $1.z8° ;» Tesô .Z8° first - ^%z80así $± ;» ZEØ ofæ foò z80asm - else - iæ eø $1.maã »» Theî .MAC - m8° =$1 - elså ;» Assumå .ASM - así $1 - fi - fi - ^­ ;» Cleaò Quiet - -» Enä oæ $± Assembly - -Notå agaiî thaô Z80ASÍ ió onå oæ thoså programó thaô periodicallù -checë  foò keyboarä inpuô sï ZEØ musô bå turneä ofæ untiì  Z80ASÍ -quits® M80.COÍ anä ASM.COÍ don'ô seeí tï asë anything. - -Therå arå generallù threå sourceó foò Consolå Output® - -1® Thå Z3´ prompô anä commanä echo. -2® ZEØ witè itó variouó messages. Š3® Thå runninç program. - -Witè  ZEXNOISÅ eqõ ± (Z34HDR.LIB© ZCPR3´ useó thå QUIEÔ  flaç  tï -determinå  whetheò tï prinô thå commanä prompô beforå  requestinç -thå nexô commanä (QUIEÔ ½ nï prompt)® ZEØ wilì alsï suppresó thå -echï  oæ  thå commanä taiì froí Z3´ iæ QUIET® Thå statå  oæ  thå -QUIEÔ flaç ió toggleä bù thå ne÷ Þ­ ZEØ command® ZEØ Typå ´ wilì -restorå thå QUIEÔ flaç tï itó originaì valuå wheî Done. - -ZEØ  haó á numbeò oæ messageó oæ itó own¬ ZEX:¬ Done¬  etc®   ZEØ -messageó arå controlleä bù thå ^£ (MSUP© toggle® Thå ^£  controì -suppresseó ZEØ messageó untiì thå nexô ^#. - -Anù  Consolå Outpuô maù bå suppresseä durinç á falså  flo÷  statå -witè  thå ^¦ (IPSUP© toggle® Alì Consolå Outpuô  maù  suppresseä -maù bå suppresseä witè thå ^® (PSUP© toggle. - -Iî  variouó  combination¬  theså toggleó  shoulä  givå  thå  .ZEØ -programmeò fulì controì oæ whaô mighô bå printeä oî thå screen. - - ­ PATCHEÓ - - -ZEØ  Typå  ´  haó á patcè areá righô afteò thå  Z3ENÖ  headeò  aô -0200h® Eacè patchablå bytå ió precedeä bù itó namå anä á '>§ foò -easù identification. - - KDEL¾ 05 - CDEL¾ 0F - -Thå reasoî thaô ZEØ caî successfullù 'drive§ programó thaô  woulä -otherwiså  'eat§ thå keyboarä inpuô string¬ ió thaô wå liå tï  iô -abouô   keyboarä  status®   Havinç  delivereä  (oò   printed©   á -character¬  ZEØ wilì reporô CONSÔ falså á numbeò oæ timeó  beforå -shippinç  thå nexô character® Thió allowó 'smart§ programó  likå -dBASÅ IÉ tï thinë theù havå flusheä thå keyboarä oò thaô á  humaî -ió typing. - -KDEÌ  representó  thå numbeò oæ falså  responseó  betweeî  normaì -characteró anä CDEÌ thå numbeò oæ lieó afteò Carriagå Return® Iî -thå schemå oæ things¬ theså valueó arå decrementeä beforå testinç -sï  thaô á valuå oæ ± woulä returî gooä statuó immediatelù anä  á -valuå   oæ  °  woulä  returî  falså  statuó  25µ  timeó   betweeî -characters®   Theså  values¬ µ anä 15¬ werå choseî bù  triaì  anä -erroò  anä seeí tï worë fine® ³ anä ¹ alsï worë aô mù houså  buô -loweò valueó DÏ NOÔ WORË iî manù cases® Sticë witè thå defaults. - -Foò thå followinç flags¬ 0° ió falså anä FÆ ió true. - - XSUB¾ 0° oò FÆ ZEØ Inpuô Modå (FF) ^= - MSUP¾ 0° oò FÆ Suppresó ZEØ messageó ^# - PSUP¾ 0° oò FÆ Suppresó Consolå Outpuô ^. - IPSIP¾ 0° oò FÆ Suppresó Conouô IF falså ^& - QUIET¾ 0° oò FÆ Initiaì Z³ quieô flaç ^- - -Thå  nexô followinç havå tï dï witè findinç thå ZEØ  inpuô  file® ŠIæ  nï  expliciô DIRº ió invoked¬ ZEØ wilì  checë  thå  followinç -flagó tï determinå wherå tï looë foò it. - -ZEØ maù bå forceä tï searcè á specifiã directorù foò inpuô  fileó -bù  declarinç  iô  here®   Ordeò ió  significant®   Iæ  ZEXDÕ  ió -declared¬  nonå  oæ ROOT¬ CURDÕ oò Patè wilì eveî bå  tried®   Iæ -ROOÔ ió set¬ neitheò CURDÕ noò Patè wilì bå tried. - - ZEXDU¾ 0° 0° Nï ZEØ directorù declareä or.. - ZEXDU¾ 0³ 0± Searcè directorù A3º only - - ROOT¾ 0° oò FÆ Searcè Rooô directorù only - CURDU¾ 0° oò FÆ Searcè Currenô directorù only - -Iæ  nonå  oæ thå above¬ ZEØ wilì searcè foò thå  filå  alonç  thå -ZCPR3´  Externaì Path® ZEØ Typå ´ wilì invokå thå  ZCPR3´  Erroò -Handleò appropriatelù foò alì detecteä errors. - - -30- - \ No newline at end of file diff --git a/Source/Images/d_nzcom/u0/ZF-DIM.COM b/Source/Images/d_nzcom/u0/ZF-DIM.COM new file mode 100644 index 00000000..1b1332ce Binary files /dev/null and b/Source/Images/d_nzcom/u0/ZF-DIM.COM differ diff --git a/Source/Images/d_nzcom/u0/ZF-REV.COM b/Source/Images/d_nzcom/u0/ZF-REV.COM new file mode 100644 index 00000000..b0cc279b Binary files /dev/null and b/Source/Images/d_nzcom/u0/ZF-REV.COM differ diff --git a/Source/Images/d_nzcom/u0/ZFILEB38.LZT b/Source/Images/d_nzcom/u0/ZFILEB38.LZT new file mode 100644 index 00000000..6c2c5399 Binary files /dev/null and b/Source/Images/d_nzcom/u0/ZFILEB38.LZT differ diff --git a/Source/Images/d_nzcom/u0/ZFILER.CMD b/Source/Images/d_nzcom/u0/ZFILER.CMD new file mode 100644 index 00000000..3fddad5c --- /dev/null +++ b/Source/Images/d_nzcom/u0/ZFILER.CMD @@ -0,0 +1,28 @@ + Z System Distribution ZFILER.CMD, 11 Oct 89 by Carson Wilson +0 ! $"Enter ZFILER macro script: " +E ! echo f%>ull file spec:%< $p;echo f%>ile directory:%< $d$u:;echo f%>ile name.....:%< $n;echo f%>ile type.....:%< $t +K ! $d$u:;$!crunch $f $"Destination directory: ";$h: +L ! $!if eq $t lbr;ldir $p;else;echo f%>ile %<$f%> is not a library;fi +T ! $!lt $p +U ! $d$u:;uncr $f;$h: +X ! if ~eq $t com;echo n%>ot a % file;else;$d$u:;:$n $" Command Tail: ";$h:;fi +Z ! $d$u:;$" Command to perform on file: " $f $" Tail: ";$h: +# + SAMPLE ZFILER COMMAND MACROS FOR USE WITH NZCOM AND Z3PLUS + +macros: 0. on-line macro + E. Echo data about file name + K. Krunch the file + L. display directory of Library + T. Type the file + U. Uncrunch the file + X. eXecute the file + Z. perform command on file + +ZFILER parameters for use with macro '0' + + $! ZEX 'GO' $P DU:FN.FT $D DRIVE + $".." PROMPT $F FN.FT $U USER + $'..' PROMPT $N FN $H HOME DU + $T FT + \ No newline at end of file diff --git a/Source/Images/d_nzcom/u0/ZHELPERS.LZT b/Source/Images/d_nzcom/u0/ZHELPERS.LZT new file mode 100644 index 00000000..976755a3 Binary files /dev/null and b/Source/Images/d_nzcom/u0/ZHELPERS.LZT differ diff --git a/Source/Images/d_nzcom/u0/ZLT.COM b/Source/Images/d_nzcom/u0/ZLT.COM new file mode 100644 index 00000000..57d04c5e Binary files /dev/null and b/Source/Images/d_nzcom/u0/ZLT.COM differ diff --git a/Source/Images/d_nzcom/u0/ZNODES66.LZT b/Source/Images/d_nzcom/u0/ZNODES66.LZT new file mode 100644 index 00000000..0f529921 Binary files /dev/null and b/Source/Images/d_nzcom/u0/ZNODES66.LZT differ diff --git a/Source/Images/d_nzcom/u0/ZSYSTEM.IZF b/Source/Images/d_nzcom/u0/ZSYSTEM.IZF new file mode 100644 index 00000000..87fb58ee Binary files /dev/null and b/Source/Images/d_nzcom/u0/ZSYSTEM.IZF differ diff --git a/Source/Images/d_zpm3.txt b/Source/Images/d_zpm3.txt new file mode 100644 index 00000000..44b67b06 --- /dev/null +++ b/Source/Images/d_zpm3.txt @@ -0,0 +1,42 @@ +# +# Add files from CPM3 build +# +../ZPM3/zpmldr.com 0: +../ZPM3/zpmldr.sys 0: +../CPM3/cpmldr.com 0: +../CPM3/cpmldr.sys 0: +../ZPM3/autotog.com 0: +../ZPM3/clrhist.com 0: +../ZPM3/setz3.com 0: +../ZPM3/cpm3.sys 0: +../ZPM3/zccp.com 0: +../ZPM3/zinstal.zpm 0: +../ZPM3/startzpm.com 0: +../ZPM3/makedos.com 0: +../ZPM3/gencpm.dat 0: +../ZPM3/bnkbios3.spr 0: +../ZPM3/bnkbdos3.spr 0: +../ZPM3/resbdos3.spr 0: +# +# Add RomWBW utilities +# +../../Binary/Apps/assign.com 0: +../../Binary/Apps/fat.com 0: +../../Binary/Apps/fdu.com 0: +../../Binary/Apps/format.com 0: +../../Binary/Apps/mode.com 0: +../../Binary/Apps/osldr.com 0: +../../Binary/Apps/rtc.com 0: +../../Binary/Apps/survey.com 0: +../../Binary/Apps/syscopy.com 0: +../../Binary/Apps/sysgen.com 0: +../../Binary/Apps/talk.com 0: +../../Binary/Apps/timer.com 0: +../../Binary/Apps/xm.com 0: +../../Binary/Apps/inttest.com 0: +# +# Add Tune application and sample files +# +../../Binary/Apps/tune.com 3: +../../Binary/Apps/Tunes/*.pt? 3: +../../Binary/Apps/Tunes/*.mym 3: diff --git a/Source/Images/d_zsdos.txt b/Source/Images/d_zsdos.txt new file mode 100644 index 00000000..2e9fe5ad --- /dev/null +++ b/Source/Images/d_zsdos.txt @@ -0,0 +1,27 @@ +# +# Add RomWBW utilities +# +../../Binary/Apps/assign.com 0: +../../Binary/Apps/fat.com 0: +../../Binary/Apps/fdu.com 0: +../../Binary/Apps/format.com 0: +../../Binary/Apps/mode.com 0: +../../Binary/Apps/osldr.com 0: +../../Binary/Apps/rtc.com 0: +../../Binary/Apps/survey.com 0: +../../Binary/Apps/syscopy.com 0: +../../Binary/Apps/sysgen.com 0: +../../Binary/Apps/talk.com 0: +../../Binary/Apps/timer.com 0: +../../Binary/Apps/xm.com 0: +../../Binary/Apps/inttest.com 0: +# +# Add Tune application and sample files +# +../../Binary/Apps/tune.com 3: +../../Binary/Apps/Tunes/*.pt? 3: +../../Binary/Apps/Tunes/*.mym 3: +# +# Add OS image +# +../ZSDOS/zsys_wbw.sys 0:zsys.sys \ No newline at end of file diff --git a/Source/Images/d_zsdos/u0/FAT.COM b/Source/Images/d_zsdos/u0/FAT.COM deleted file mode 100644 index 1dd7dcca..00000000 Binary files a/Source/Images/d_zsdos/u0/FAT.COM and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Attack.pt3 b/Source/Images/d_zsdos/u3/Attack.pt3 deleted file mode 100644 index dc9d04a9..00000000 Binary files a/Source/Images/d_zsdos/u3/Attack.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Backup.pt3 b/Source/Images/d_zsdos/u3/Backup.pt3 deleted file mode 100644 index e9141fe4..00000000 Binary files a/Source/Images/d_zsdos/u3/Backup.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/BadMice.pt3 b/Source/Images/d_zsdos/u3/BadMice.pt3 deleted file mode 100644 index aa78a8d2..00000000 Binary files a/Source/Images/d_zsdos/u3/BadMice.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Demo.mym b/Source/Images/d_zsdos/u3/Demo.mym deleted file mode 100644 index 255fc160..00000000 Binary files a/Source/Images/d_zsdos/u3/Demo.mym and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Demo1.mym b/Source/Images/d_zsdos/u3/Demo1.mym deleted file mode 100644 index b224f321..00000000 Binary files a/Source/Images/d_zsdos/u3/Demo1.mym and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Demo3.mym b/Source/Images/d_zsdos/u3/Demo3.mym deleted file mode 100644 index 808db891..00000000 Binary files a/Source/Images/d_zsdos/u3/Demo3.mym and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Demo3mix.mym b/Source/Images/d_zsdos/u3/Demo3mix.mym deleted file mode 100644 index b5981848..00000000 Binary files a/Source/Images/d_zsdos/u3/Demo3mix.mym and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Demo4.mym b/Source/Images/d_zsdos/u3/Demo4.mym deleted file mode 100644 index ed2e8a85..00000000 Binary files a/Source/Images/d_zsdos/u3/Demo4.mym and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/HowRU.pt3 b/Source/Images/d_zsdos/u3/HowRU.pt3 deleted file mode 100644 index 47fb464e..00000000 Binary files a/Source/Images/d_zsdos/u3/HowRU.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Iteratn.pt3 b/Source/Images/d_zsdos/u3/Iteratn.pt3 deleted file mode 100644 index 575cccf4..00000000 Binary files a/Source/Images/d_zsdos/u3/Iteratn.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/LookBack.pt3 b/Source/Images/d_zsdos/u3/LookBack.pt3 deleted file mode 100644 index 1dd15ac7..00000000 Binary files a/Source/Images/d_zsdos/u3/LookBack.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Louboutn.pt3 b/Source/Images/d_zsdos/u3/Louboutn.pt3 deleted file mode 100644 index 8b4b0b15..00000000 Binary files a/Source/Images/d_zsdos/u3/Louboutn.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Namida.pt3 b/Source/Images/d_zsdos/u3/Namida.pt3 deleted file mode 100644 index 2a2d8562..00000000 Binary files a/Source/Images/d_zsdos/u3/Namida.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Recoll.pt3 b/Source/Images/d_zsdos/u3/Recoll.pt3 deleted file mode 100644 index 5a2744ab..00000000 Binary files a/Source/Images/d_zsdos/u3/Recoll.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Sanxion.pt3 b/Source/Images/d_zsdos/u3/Sanxion.pt3 deleted file mode 100644 index 25a77aab..00000000 Binary files a/Source/Images/d_zsdos/u3/Sanxion.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Synch.pt3 b/Source/Images/d_zsdos/u3/Synch.pt3 deleted file mode 100644 index 16149d1a..00000000 Binary files a/Source/Images/d_zsdos/u3/Synch.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/ToStar.pt3 b/Source/Images/d_zsdos/u3/ToStar.pt3 deleted file mode 100644 index 9c77ed4f..00000000 Binary files a/Source/Images/d_zsdos/u3/ToStar.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Tune.com b/Source/Images/d_zsdos/u3/Tune.com deleted file mode 100644 index 9f5ba036..00000000 Binary files a/Source/Images/d_zsdos/u3/Tune.com and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Victory.pt3 b/Source/Images/d_zsdos/u3/Victory.pt3 deleted file mode 100644 index d2c6f3e7..00000000 Binary files a/Source/Images/d_zsdos/u3/Victory.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Wicked.pt3 b/Source/Images/d_zsdos/u3/Wicked.pt3 deleted file mode 100644 index 840a58d1..00000000 Binary files a/Source/Images/d_zsdos/u3/Wicked.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/YeOlde.pt3 b/Source/Images/d_zsdos/u3/YeOlde.pt3 deleted file mode 100644 index ba99371b..00000000 Binary files a/Source/Images/d_zsdos/u3/YeOlde.pt3 and /dev/null differ diff --git a/Source/Images/d_zsdos/u3/Yeovil.pt3 b/Source/Images/d_zsdos/u3/Yeovil.pt3 deleted file mode 100644 index 694de685..00000000 Binary files a/Source/Images/d_zsdos/u3/Yeovil.pt3 and /dev/null differ diff --git a/Source/Makefile b/Source/Makefile new file mode 100644 index 00000000..1204b022 --- /dev/null +++ b/Source/Makefile @@ -0,0 +1,16 @@ +# +# order is actually important, because of build dependencies +# +NOTDONE = Doc +SUBDIRS = Prop +SUBDIRS += Apps +SUBDIRS += CBIOS +SUBDIRS += Forth +SUBDIRS += Fonts +SUBDIRS += CPM22 ZCPR ZCPR-DJ ZSDOS +SUBDIRS += HBIOS CPM3 +SUBDIRS += ZPM3 +SUBDIRS += BPBIOS +SUBDIRS += Images +TOOLS = ../Tools +include $(TOOLS)/Makefile.inc diff --git a/Source/Prop/Makefile b/Source/Prop/Makefile new file mode 100644 index 00000000..ceb364ca --- /dev/null +++ b/Source/Prop/Makefile @@ -0,0 +1,4 @@ +SUBDIRS = Spin +TOOLS = ../../Tools +OTHERS = *.list +include $(TOOLS)/Makefile.inc diff --git a/Source/Prop/Spin/Makefile b/Source/Prop/Spin/Makefile new file mode 100644 index 00000000..fccfb5fe --- /dev/null +++ b/Source/Prop/Spin/Makefile @@ -0,0 +1,7 @@ +OBJECTS = PropIO.eeprom PropIO2.eeprom ParPortProp.eeprom +OTHERS = *.list +DEST = ../../../Binary +TOOLS = ../../../Tools +include $(TOOLS)/Makefile.inc + +DIFFPATH := $(DIFFTO)/Binary diff --git a/Source/ReadMe.txt b/Source/ReadMe.txt index c233dda0..50f41b8c 100644 --- a/Source/ReadMe.txt +++ b/Source/ReadMe.txt @@ -101,8 +101,9 @@ to determine the component of the configuration filename: RC2014 w/ Z80 RCZ80_std.rom RC2014 w/ Z180 RCZ180_nat.rom (native Z180 memory addressing) RC2014 w/ Z180 RCZ180_ext.rom (external 512K RAM/ROM module) - SC-series SC126, SC130 + SCZ180 SC126, SC130 Easy Z80 EZZ180_std.rom + Dyno DYNO_std.rom You can use any name you choose for the component of the configuration filename. So, let's say you want to create a custom @@ -189,7 +190,7 @@ This command will prompt you twice as it runs. These prompts determine the platform and configuration to be built. The first prompt is for the platform, as shown below: - Platform [SBC|ZETA|ZETA2|RCZ80|EZZ80|RCZ180|N8|MK4|UNA]: + Platform [SBC|ZETA|ZETA2|RCZ80|EZZ80|UNA|N8|MK4|RCZ180|SCZ180|DYNO]: Enter the option corresponding to the platform of the ROM firmware you are building. If you enter something other than one of the @@ -304,6 +305,7 @@ BuildImages: RomWBW has the ability to create floppy disk and hard and will turn them into a writable disk image. Refer to the ReadMe.txt document in the Source\Images directory for a detailed description of this process. + N.B., BuildShared must be run prior to BuildImages. BuildBP: This command builds another OS variant called BPBIOS. It is a work in progress and should not be used at this time diff --git a/Source/UBIOS/ubios.inc b/Source/UBIOS/ubios.inc index 580ec42b..546c671d 100644 --- a/Source/UBIOS/ubios.inc +++ b/Source/UBIOS/ubios.inc @@ -11,27 +11,27 @@ BF_CIOOST .EQU BF_CIO + 4 ; CHARACTER OUTPUT STATUS BF_DIO .EQU $40 BF_DIOREAD .EQU BF_DIO + 2 ; DISK READ BF_DIOWRITE .EQU BF_DIO + 3 ; DISK WRITE +;; +;; MEMORY BANK CONFIGURATION +;; +;ROMSIZE .EQU 512 +;RAMSIZE .EQU 512 ; -; MEMORY BANK CONFIGURATION +;BID_ROM0 .EQU $0000 +;BID_ROMN .EQU (BID_ROM0 + ((ROMSIZE / 32) - 1)) +;BID_RAM0 .EQU $8000 +;BID_RAMN .EQU (BID_RAM0 + ((RAMSIZE / 32) - 1)) ; -ROMSIZE .EQU 512 -RAMSIZE .EQU 512 - -BID_ROM0 .EQU $0000 -BID_ROMN .EQU (BID_ROM0 + ((ROMSIZE / 32) - 1)) -BID_RAM0 .EQU $8000 -BID_RAMN .EQU (BID_RAM0 + ((RAMSIZE / 32) - 1)) - -BID_BOOT .EQU BID_ROM0 ; BOOT BANK -BID_BIOSIMG .EQU BID_ROM0 + 1 ; BIOS IMAGE BANK -BID_OSIMG .EQU BID_ROM0 + 2 ; ROM LOADER AND IMAGES BANK -BID_FSFAT .EQU BID_ROM0 + 3 ; FAT FILESYSTEM DRIVER BANK -BID_ROMD0 .EQU BID_ROM0 + 4 ; FIRST ROM DRIVE BANK -BID_ROMDN .EQU BID_ROMN ; LAST ROM DRIVE BANK - -BID_RAMD0 .EQU BID_RAM0 ; FIRST RAM DRIVE BANK -BID_RAMDN .EQU BID_RAMN - 4 ; LAST RAM DRIVE BANK -BID_AUX .EQU BID_RAMN - 3 ; AUX BANK (BPBIOS, ETC.) -BID_BIOS .EQU BID_RAMN - 2 ; BIOS BANK -BID_USR .EQU BID_RAMN - 1 ; USER BANK (CP/M TPA, ETC.) -BID_COM .EQU BID_RAMN ; COMMON BANK, UPPER 32K +;BID_BOOT .EQU BID_ROM0 ; BOOT BANK +;BID_BIOSIMG .EQU BID_ROM0 + 1 ; BIOS IMAGE BANK +;BID_OSIMG .EQU BID_ROM0 + 2 ; ROM LOADER AND IMAGES BANK +;BID_FSFAT .EQU BID_ROM0 + 3 ; FAT FILESYSTEM DRIVER BANK +;BID_ROMD0 .EQU BID_ROM0 + 4 ; FIRST ROM DRIVE BANK +;BID_ROMDN .EQU BID_ROMN ; LAST ROM DRIVE BANK +; +;BID_RAMD0 .EQU BID_RAM0 ; FIRST RAM DRIVE BANK +;BID_RAMDN .EQU BID_RAMN - 4 ; LAST RAM DRIVE BANK +;BID_AUX .EQU BID_RAMN - 3 ; AUX BANK (BPBIOS, ETC.) +;BID_BIOS .EQU BID_RAMN - 2 ; BIOS BANK +;BID_USR .EQU BID_RAMN - 1 ; USER BANK (CP/M TPA, ETC.) +;BID_COM .EQU BID_RAMN ; COMMON BANK, UPPER 32K diff --git a/Source/ZCPR-DJ/Makefile b/Source/ZCPR-DJ/Makefile new file mode 100644 index 00000000..47584a38 --- /dev/null +++ b/Source/ZCPR-DJ/Makefile @@ -0,0 +1,10 @@ + +OBJECTS= zcpr.bin + +OTHERS = zcpr.rel + +TOOLS = ../../Tools +include $(TOOLS)/Makefile.inc + +zcpr.bin: zcpr.rel + $(ZXCC) $(TOOLS)/cpm/bin/L80 -zcpr,zcpr.bin/n/e diff --git a/Source/ZCPR/Makefile b/Source/ZCPR/Makefile new file mode 100644 index 00000000..4e35f6d1 --- /dev/null +++ b/Source/ZCPR/Makefile @@ -0,0 +1,12 @@ + +OBJECTS = zcpr.bin bdloc.com + +OTHERS = *.hex + +TOOLS = ../../Tools +include $(TOOLS)/Makefile.inc + +zcpr.bin: zcpr.asm + $(ZXCC) $(CPM)/MAC -$< -$$PO + $(ZXCC) $(CPM)/MLOAD25 -$@=zcpr.hex + diff --git a/Source/ZPM3/Build.cmd b/Source/ZPM3/Build.cmd index 9537f2e0..47018c83 100644 --- a/Source/ZPM3/Build.cmd +++ b/Source/ZPM3/Build.cmd @@ -3,7 +3,9 @@ setlocal set TOOLS=../../Tools -set PATH=%TOOLS%\zx;%TOOLS%\cpmtools;%PATH% +set PATH=%TOOLS%\tasm32;%TOOLS%\zx;%TOOLS%\cpmtools;%PATH% + +set TASMTABS=%TOOLS%\tasm32 set ZXBINDIR=%TOOLS%/cpm/bin/ set ZXLIBDIR=%TOOLS%/cpm/lib/ @@ -15,15 +17,19 @@ copy ..\ZCCP\startzpm.com . copy ..\CPM3\genbnk.dat . copy ..\CPM3\zpmbios3.spr bnkbios3.spr copy ..\CPM3\gencpm.com . -copy ..\CPM3\biosldr.rel . -copy ..\CPM3\cpmldr.com . +copy ..\CPM3\biosldrd.rel . +copy ..\CPM3\biosldrc.rel . +rem copy ..\CPM3\cpmldr.com . rem ZPM Loader echo. echo. echo *** ZPM Loader *** echo. -zx LINK -ZPMLDR[L100]=ZPM3LDR,BIOSLDR +zx LINK -ZPMLDRD[L100]=ZPM3LDR,BIOSLDRD +move /Y zpmldrd.com zpmldr.bin +zx LINK -ZPMLDRC[L100]=ZPM3LDR,BIOSLDRC +move /Y zpmldrc.com zpmldr.com rem pause rem Banked ZPM3 @@ -35,33 +41,8 @@ copy genbnk.dat gencpm.dat zx gencpm -auto -display rem pause -if not exist ../../Binary/hd_zpm3.img goto :eof +rem Loader -rem Update hd_zpm3.img -echo. -echo. -echo *** Update Disk Image *** -echo. -for %%f in ( - zpmldr.com - cpmldr.com - autotog.com - clrhist.com - setz3.com - cpm3.sys - zccp.com - zinstal.zpm - startzpm.com - makedos.com - gencpm.dat - bnkbios3.spr - bnkbdos3.spr - resbdos3.spr -) do call :upd_img %%f -goto :eof +tasm -t80 -g3 -fFF loader.asm loader.bin loader.lst -:upd_img -echo %1... -cpmrm.exe -f wbw_hd0 ../../Binary/hd_zpm3.img 0:%1 -cpmcp.exe -f wbw_hd0 ../../Binary/hd_zpm3.img %1 0:%1 -goto :eof \ No newline at end of file +copy /b loader.bin + zpmldr.bin zpmldr.sys diff --git a/Source/ZPM3/Clean.cmd b/Source/ZPM3/Clean.cmd index 91ffcb36..57295d69 100644 --- a/Source/ZPM3/Clean.cmd +++ b/Source/ZPM3/Clean.cmd @@ -16,3 +16,6 @@ if exist cpmldr.com del cpmldr.com if exist startzpm.com del startzpm.com if exist gencpm.com del gencpm.com if exist *.dat del *.dat +if exist biosldr?.rel del biosldr?.rel +if exist *.bin del *.bin +if exist *.lst del *.lst diff --git a/Source/ZPM3/Makefile b/Source/ZPM3/Makefile new file mode 100644 index 00000000..d72e77bd --- /dev/null +++ b/Source/ZPM3/Makefile @@ -0,0 +1,54 @@ +OBJECTS = zpmldr.com zpmldr.sys cpm3.sys startzpm.com zccp.com +ifdef REBUILD_ZPM_TOOLS +OBJECTS += setz3.com clrhist.com autotog.com +else +NODELETE = setz3.com clrhist.com autotog.com +endif +OTHERS = zpmldr.bin loader.bin biosldrd.rel biosldrc.rel gencpm.com gencpm.dat bnkbios3.spr +TOOLS =../../Tools + +include $(TOOLS)/Makefile.inc + +zpmldr.bin: zpm3ldr.rel biosldrd.rel + $(ZXCC) $(CPM)/LINK -ZPMLDRD[L100]=ZPM3LDR,BIOSLDRD + mv zpmldrd.com zpmldr.bin + +zpmldr.com: zpm3ldr.rel biosldrc.rel + $(ZXCC) $(CPM)/LINK -ZPMLDRC[L100]=ZPM3LDR,BIOSLDRC + mv zpmldrc.com zpmldr.com + +zpmldr.sys: zpmldr.bin loader.bin + cat loader.bin zpmldr.bin > $@ + +cpm3.sys: gencpm.com gencpm.dat bnkbios3.spr + $(ZXCC) gencpm -auto -display + +bnkbios3.spr: ../CPM3/zpmbios3.spr + cp $< $@ + +gencpm.dat: ../CPM3/genbnk.dat + cp $< $@ + +gencpm.com: ../CPM3/gencpm.com + cp $< $@ + +biosldrc.rel: ../CPM3/biosldrc.rel + cp $< $@ + +biosldrd.rel: ../CPM3/biosldrd.rel + cp $< $@ + +zccp.com: ../ZCCP/ccp.com + cp $< $@ + +zinstal.zpm: ../ZCCP/zinstal.zpm + cp $< $@ + +startzpm.com: ../ZCCP/startzpm.com + cp $< $@ + +setz3.com: setz3.z80 + +clrhist.com: clrhist.z80 + +autotog.com: autotog.z80 diff --git a/Source/ZPM3/diskdefs b/Source/ZPM3/diskdefs deleted file mode 100644 index 937bfcf2..00000000 --- a/Source/ZPM3/diskdefs +++ /dev/null @@ -1,417 +0,0 @@ -diskdef ibm-3740 - seclen 128 - tracks 77 - sectrk 26 - blocksize 1024 - maxdir 64 - skew 6 - boottrk 2 - os p2dos -end - -diskdef 4mb-hd - seclen 128 - tracks 1024 - sectrk 32 - blocksize 2048 - maxdir 256 - skew 1 - boottrk 0 - os p2dos -end - -diskdef pcw - seclen 512 - tracks 40 - sectrk 9 - blocksize 1024 - maxdir 64 - skew 1 - boottrk 1 - os 3 -end - -diskdef pc1.2m - seclen 512 - tracks 80 - # this format uses 15 sectors per track, but 30 per cylinder - sectrk 30 - blocksize 4096 - maxdir 256 - skew 1 - boottrk 0 - os 3 -end - -# CP/M 86 on 1.44MB floppies -diskdef cpm86-144feat - seclen 512 - tracks 160 - sectrk 18 - blocksize 4096 - maxdir 256 - skew 1 - boottrk 2 - os 3 -end - -diskdef cf2dd - seclen 512 - tracks 160 - sectrk 9 - blocksize 2048 - maxdir 256 - skew 1 - boottrk 1 - os 3 -end - -#amstrad: values are read from super block (special name hardcoded) - -# Royal alphatronic -# setfdprm /dev/fd1 dd ssize=256 cyl=40 sect=16 head=2 -diskdef alpha - seclen 256 - tracks 40 - sectrk 32 - blocksize 2048 - maxdir 128 - skew 1 - boottrk 2 - os 2.2 -end - -# Apple II CP/M skew o Apple II DOS 3.3 skew -diskdef apple-do - seclen 256 - tracks 35 - sectrk 16 - blocksize 1024 - maxdir 64 - skew 0 - boottrk 3 - os 2.2 -end - -# Apple II CP/M skew o Apple II PRODOS skew -diskdef apple-po - seclen 256 - tracks 35 - sectrk 16 - blocksize 1024 - maxdir 64 - skew 0 - boottrk 3 - os 2.2 -end - -# MYZ80 hard drive (only works with libdsk, because it has a 256-byte header) -diskdef myz80 - seclen 1024 - tracks 64 - sectrk 128 - blocksize 4096 - maxdir 1024 - skew 1 - boottrk 0 - os 3 -end - -# Despite being Amstrad formats, CPC System and CPC Data don't have an Amstrad -# superblock. You'll need to use libdsk to access them because the Linux -# and Windows kernel drivers won't touch them. -diskdef cpcsys - seclen 512 - tracks 40 - sectrk 9 - blocksize 1024 - maxdir 64 - skew 1 - boottrk 2 - os 3 -end -diskdef cpcdata - seclen 512 - tracks 40 - sectrk 9 - blocksize 1024 - maxdir 64 - skew 1 - boottrk 0 - os 3 -end - -# after being read in with no sector skew. -diskdef nigdos - seclen 512 - # NigDos double sided disk format, 42 tracks * 2 sides - tracks 84 - sectrk 10 - blocksize 2048 - maxdir 128 - skew 1 - boottrk 0 - # this format wastes half of the directory entry - logicalextents 1 - os 3 -end - -diskdef epsqx10 - seclen 512 - tracks 40 - sectrk 20 - blocksize 2048 - maxdir 128 - skew 0 - boottrk 2 - os 2.2 -end - -diskdef ibm-8ss - seclen 512 - tracks 40 - sectrk 8 - blocksize 1024 - maxdir 64 - skew 0 - boottrk 1 - os 2.2 -end - -diskdef ibm-8ds - seclen 512 - tracks 40 - sectrk 8 - blocksize 1024 - maxdir 64 - skew 0 - boottrk 1 - os 2.2 -end - -diskdef electroglas - seclen 512 - tracks 80 - sectrk 10 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 1 - os 3 -end - -# IBM CP/M-86 -# setfdprm /dev/fd1 sect=8 dtr=1 hd ssize=512 tpi=48 head=1 -diskdef ibmpc-514ss - seclen 512 - tracks 40 - sectrk 8 - blocksize 1024 - maxdir 64 - skew 1 - boottrk 1 - os 2.2 -end - -# IBM CP/M-86 -# setfdprm /dev/fd1 sect=8 dtr=1 hd ssize=512 tpi=48 -diskdef ibmpc-514ds - seclen 512 - tracks 80 - sectrk 8 - blocksize 2048 - maxdir 64 - skew 0 - boottrk 2 - os 2.2 -end - -diskdef p112 - seclen 512 - tracks 160 - sectrk 18 - blocksize 2048 - maxdir 256 - skew 1 - boottrk 2 - os 3 -end - -diskdef p112-old - seclen 512 - tracks 160 - sectrk 18 - blocksize 2048 - maxdir 128 - skew 1 - boottrk 1 - os 3 -end - -diskdef kpii - seclen 512 - tracks 40 - sectrk 10 - blocksize 1024 - maxdir 32 - skew 0 - boottrk 1 - os 2.2 -end - -# setfdprm /dev/fd0 dd sect=10 -diskdef interak - seclen 512 - tracks 80 - sectrk 20 - blocksize 4096 - maxdir 256 - skew 0 - boottrk 2 - os 2.2 -end - -# RomWBW 512KB ROM (128KB reserved, 384KB ROM Disk) - -diskdef wbw_rom512 - seclen 512 - tracks 12 - sectrk 64 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 0 - os 2.2 -end - -# RomWBW 1024KB ROM (128KB reserved, 896KB ROM Disk) - -diskdef wbw_rom1024 - seclen 512 - tracks 28 - sectrk 64 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 0 - os 2.2 -end - -# UNA 512KB ROM (128KB reserved, 384KB ROM Disk) - -diskdef una_rom512 - seclen 512 - tracks 12 - sectrk 64 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 0 - os 2.2 -end - -# UNA 512KB ROM (128KB reserved, 896KB ROM Disk) - -diskdef una_rom1024 - seclen 512 - tracks 28 - sectrk 64 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 0 - os 2.2 -end - -# RomWBW 8MB Hard Disk, LU 0-3 -diskdef wbw_hd0 - seclen 512 - tracks 65 - sectrk 256 - blocksize 4096 - maxdir 512 - skew 0 - boottrk 1 - os 2.2 -end - -diskdef wbw_hd1 - seclen 512 - tracks 130 - sectrk 256 - blocksize 4096 - maxdir 512 - skew 0 - boottrk 66 - os 2.2 -end - -diskdef wbw_hd2 - seclen 512 - tracks 195 - sectrk 256 - blocksize 4096 - maxdir 512 - skew 0 - boottrk 131 - os 2.2 -end - -diskdef wbw_hd3 - seclen 512 - tracks 260 - sectrk 256 - blocksize 4096 - maxdir 512 - skew 0 - boottrk 196 - os 2.2 -end - -# RomWBW 720K floppy media -diskdef wbw_fd720 - seclen 512 - tracks 160 - sectrk 9 - blocksize 2048 - maxdir 128 - skew 0 - boottrk 4 - os 2.2 -end - -# RomWBW 1.44M floppy media -diskdef wbw_fd144 - seclen 512 - tracks 160 - sectrk 18 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 2 - os 2.2 -end - -# RomWBW 360K floppy media -diskdef wbw_fd360 - seclen 512 - tracks 80 - sectrk 9 - blocksize 2048 - maxdir 128 - skew 0 - boottrk 4 - os 2.2 -end - -# RomWBW 1.20M floppy media -diskdef wbw_fd120 - seclen 512 - tracks 160 - sectrk 15 - blocksize 2048 - maxdir 256 - skew 0 - boottrk 2 - os 2.2 -end diff --git a/Source/ZPM3/loader.asm b/Source/ZPM3/loader.asm new file mode 100644 index 00000000..982f7205 --- /dev/null +++ b/Source/ZPM3/loader.asm @@ -0,0 +1,228 @@ +;=============================================================================== +; LOADER.ASM +; +; BOOTLOADER FOR ROMWBW DISK OPERATING SYSTEMS. +; +; CP/M DISK FORMATS ALLOW FOR RESERVED TRACKS THAT CONTAIN AN IMAGE OF THE +; OPERATING SYSTEM TO BE LOADED WHEN THE DISK IS BOOTED. THE OPERATING SYSTEM +; IMAGE ITSELF IS NORMALLY PREFIXED BY A 1-N SECTORS CONTAINING OS BOOTSTRAP +; CODE AND DISK METADATA. +; +; THE RETROBREW COMPUTING GROUP HAS BEEN USING A CONVENTION OF PREFIXING THE +; OS IMAGE WITH 3 SECTORS (512 BYTES X 3 FOR A TOTAL OF 1536 BYTES): +; +; SECTOR 1: IBM-PC STYLE BOOT BLOCK CONTAINING BOOTSTRAP, +; PARTITION TABLE, AND BOOT SIGNATURE +; SECTOR 2: RESERVED +; SECTOR 3: METADATA +; +; THE HARDWARE BIOS IS EXPECTED TO READ AND LOAD THE FIRST TWO SECTORS FROM THE +; DISK TO MEMORY ADDRESS $8000 AND JUMP TO THAT LOCATION TO BEGIN THE BOOT +; PROCESS. THE BIOS IS EXPECTED TO VERIFY THAT A STANDARD BOOT SIGNATURE +; OF $55, $AA IS PRESENT AT OFFSET $1FE-$1FF. IF THE SIGNATURE IS NOT FOUND, +; THE BIOS SHOULD ASSUME THE DISK HAS NOT BEEN PROPERLY INITIALIZED AND SHOULD +; NOT JUMP TO THE LOAD ADDRESS. +; +;=============================================================================== +; +#INCLUDE "../HBIOS/ver.inc" +; +SYS_ENT .EQU $0100 ; SYSTEM (OS) ENTRY POINT ADDRESS +SYS_LOC .EQU $0100 ; STARTING ADDRESS TO LOAD SYSTEM IMAGE +SYS_END .EQU $1480 ; ENDING ADDRESS OF SYSTEM IMAGE +; +BYT .EQU 1 ; used to describe METADATA_SIZE below +WRD .EQU 2 +; +SECTOR_SIZE .EQU 512 +BLOCK_SIZE .EQU 128 +PREFIX_SIZE .EQU (3 * SECTOR_SIZE) ; 3 SECTORS +METADATA_SIZE .EQU BYT+WRD+(4*BYT)+16+BYT+WRD+WRD+WRD+WRD ; (as defined below) +; +PARTTBL_LOC .EQU $1BE +PARTTBL_SIZ .EQU $40 +BOOTSIG_LOC .EQU $1FE +; +;------------------------------------------------------------------------------- +; SECTOR 1 +; +; THIS SECTOR FOLLOWS THE CONVENTIONS OF AN IBM-PC MBR CONTAINING THE OS +; BOOTSTRAP CODE, PARTITION TABLE, AND BOOT SIGNATURE +; +;---------------------------------------------------------------------------- +; +; THE FOLLOWING BOOTSTRAP CODE IS BUILT TO ASSUME IT WILL BE EXECUTED AT A STARTING +; ADDRESS OF $8000. +; + .ORG $8000 + JR BOOT +; +BOOT: + LD DE,STR_LOAD ; LOADING STRING + CALL PRTSTR ; PRINT + CALL PRTDOT ; PROGRESS +; + LD BC,$00FC ; UNA FUNC: GET BOOTSTRAP HISTORY + CALL $FFFD ; CALL UNA + JR NZ,ERROR ; HANDLE ERROR + CALL PRTDOT ; PROGRESS + LD B,L ; MOVE BOOT UNIT ID TO B +; + LD C,$41 ; UNA FUNC: SET LBA + LD DE,0 ; HI WORD ALWAYS ZERO + LD HL,3 ; IMAGE STARTS AT FOURTH SECTOR + CALL $FFFD ; SET LBA + JR NZ,ERROR ; HANDLE ERROR + CALL PRTDOT ; PROGRESS +; + LD C,$42 ; UNA FUNC: READ SECTORS + LD DE,$D000 ; STARTING ADDRESS FOR IMAGE + LD L,22 ; READ 22 SECTORS + CALL $FFFD ; DO READ + JR NZ,ERROR ; HANDLE ERROR + CALL PRTDOT ; PROGRESS +; + LD DE,STR_DONE ; DONE MESSAGE + CALL PRTSTR ; PRINT IT +; + LD D,B ; PASS BOOT UNIT TO OS + LD E,0 ; ASSUME LU IS ZERO + JP SYS_ENT ; GO TO SYSTEM +; +PRTCHR: + PUSH BC + PUSH DE + LD BC,$0012 ; UNIT 0, WRITE CHAR + LD E,A ; CHAR TO PRINT + CALL $FFFD ; PRINT + POP DE + POP BC + RET +; +PRTSTR: + PUSH BC + PUSH HL + LD BC,$0015 ; UNIT 0, WRITE CHARS UNTIL TERMINATOR + LD L,0 ; TERMINATOR IS NULL + CALL $FFFD ; PRINT + POP HL + POP BC + RET +; +PRTDOT: + LD A,'.' ; DOT CHARACTER + JR PRTCHR ; PRINT AND RETURN +; +; PRINT THE HEX BYTE VALUE IN A +; +PRTHEXBYTE: + PUSH AF + PUSH DE + CALL HEXASCII + LD A,D + CALL PRTCHR + LD A,E + CALL PRTCHR + POP DE + POP AF + RET +; +; CONVERT BINARY VALUE IN A TO ASCII HEX CHARACTERS IN DE +; +HEXASCII: + LD D,A + CALL HEXCONV + LD E,A + LD A,D + RLCA + RLCA + RLCA + RLCA + CALL HEXCONV + LD D,A + RET +; +; CONVERT LOW NIBBLE OF A TO ASCII HEX +; +HEXCONV: + AND 0FH ;LOW NIBBLE ONLY + ADD A,90H + DAA + ADC A,40H + DAA + RET +; +ERROR: + LD DE,STR_ERR ; POINT TO ERROR STRING + CALL PRTSTR ; PRINT IT + HALT ; HALT +; +; DATA +; +STR_LOAD .DB "\r\nLoading",0 +STR_DONE .DB "\r\n",0 +STR_ERR .DB " Read Error!",0 +; + .ORG $ - $8000 ; RESTORE ORG + .FILL PARTTBL_LOC - $ ; FILL TO START OF PARTITION TABLE +; +; RESERVE SPACE FOR STANDARD IBM-PC PARTITION TABLE. ALTHOUGH A +; PARTITION TABLE IS NOT RELEVANT FOR A FLOPPY DISK, IT DOES NO HARM. +; THE CONTENTS OF THE PARTITION TABLE MUST BE MANAGED BY FDISK80. +; +PARTTBL .FILL PARTTBL_SIZ,$00 ; PARTITION TABLE, FILL WITH ZEROES +; +; THE END OF THE FIRST SECTOR MUST CONTAIN THE TWO BYTE BOOT +; SIGNATURE. +; +BOOTSIG .DB $55,$AA ; STANDARD BOOT SIGNATURE +; +;------------------------------------------------------------------------------- +; SECTOR 2 +; +; THIS SECTOR HAS NOT BEEN DEFINED AND IS RESERVED. +; +;---------------------------------------------------------------------------- +; + .FILL 512,0 ; JUST FILL SECTOR WITH ZEROES +; +;------------------------------------------------------------------------------- +; SECTOR 3 +; +; OS AND DISK METADATA +; +;---------------------------------------------------------------------------- +; + .FILL 128 * 3,0 ; FIRST 384 BYTES ARE NOT YET DEFINED +; +; THE FOLLOWING TWO BYTES ARE AN ADDITIONAL SIGNATURE THAT IS VERIFIED BY +; SOME HARDWARE BIOSES. +; +PR_SIG .DB $5A,$A5 ; SIGNATURE GOES HERE +; +; FIRST CHUNK OF METADATA IMMEDIATELY FOLLOWS THE SIGNATURE BYTES +; +PR_PLATFORM .DB 0 ; PLATFORM ID (SEE STD.ASM) +PR_DEVICE .DB 0 ; ? (PROBABLY UNUSED) +PR_FORMATTER .DB 0,0,0,0,0,0,0,0 ; ? (PROBABLY UNUSED) +PR_DRIVE .DB 0 ; ? (PROBABLY UNUSED) +PR_LOG_UNIT .DW 0 ; ? (PROBABLY UNUSED) +; +; FILLER TO PLACE SECOND CHUNK OF METADATA AT THE END OF THE SECTOR +; + .FILL ((PREFIX_SIZE - METADATA_SIZE) - $),00H +; +; SECOND CHUNK OF METADATA +; +PR_WP .DB 0 ; WRITE PROTECT BOOLEADN +PR_UPDSEQ .DW 0 ; PREFIX UPDATE SEQUENCE NUMBER (DEPRECATED?) +PR_VER .DB RMJ,RMN,RUP,RTP ; OS BUILD VERSION +PR_LABEL .DB "Unlabeled Drive ","$" ; DISK LABEL (EXACTLY 16 BYTES!!!) + .DW 0 ; DEPRECATED +PR_LDLOC .DW SYS_LOC ; ADDRESS TO START LOADING SYSTEM +PR_LDEND .DW SYS_END ; ADDRESS TO STOP LOADING SYSTEM +PR_ENTRY .DW SYS_ENT ; ADDRESS TO ENTER SYSTEM (OS) +; +; +; + .END diff --git a/Source/ZSDOS/Build.cmd b/Source/ZSDOS/Build.cmd index ad45fee5..984b376a 100644 --- a/Source/ZSDOS/Build.cmd +++ b/Source/ZSDOS/Build.cmd @@ -13,3 +13,11 @@ set ZXINCDIR=%TOOLS%/cpm/include/ zx ZMAC -ZSDOS -/P zx LINK -ZSDOS.BIN=ZSDOS[LD800] + +tasm -t80 -g3 -fFF loader.asm loader.bin loader.lst + +copy /b ..\zcpr-dj\zcpr.bin + zsdos.bin + ..\cbios\cbios_wbw.bin zsys_wbw.bin +copy /b ..\zcpr-dj\zcpr.bin + zsdos.bin + ..\cbios\cbios_una.bin zsys_una.bin + +copy /b loader.bin + zsys_wbw.bin zsys_wbw.sys +copy /b loader.bin + zsys_una.bin zsys_una.sys diff --git a/Source/ZSDOS/Clean.cmd b/Source/ZSDOS/Clean.cmd index 1d355724..65ff8877 100644 --- a/Source/ZSDOS/Clean.cmd +++ b/Source/ZSDOS/Clean.cmd @@ -9,3 +9,4 @@ if exist *.hex del *.hex if exist *.rel del *.rel if exist *.sym del *.sym if exist *.err del *.err +if exist *.sys del *.sys diff --git a/Source/ZSDOS/Makefile b/Source/ZSDOS/Makefile new file mode 100644 index 00000000..90963e9d --- /dev/null +++ b/Source/ZSDOS/Makefile @@ -0,0 +1,24 @@ +SYSFILES = zsys_wbw.sys zsys_una.sys +BINFILES = zsys_wbw.bin zsys_una.bin +OBJECTS = $(SYSFILES) $(BINFILES) +OTHERS = zsdos.rel zsdos.err loader.bin zsdos.bin +TOOLS = ../../Tools +CCP = ../ZCPR-DJ/zcpr.bin + +include $(TOOLS)/Makefile.inc + +zsdos.rel: zsdos.z80 + $(ZXCC) $(CPM)/ZMAC -$< -/P + +zsdos.bin: zsdos.rel + $(ZXCC) $(CPM)/LINK -$@=$<[LD800] + +%.sys: %.bin loader.bin + cat loader.bin $*.bin > $@ + +zsys_wbw.bin: $(CCP) zsdos.bin ../CBIOS/cbios_wbw.bin + cat $(CCP) zsdos.bin ../CBIOS/cbios_wbw.bin > $@ + +zsys_una.bin: $(CCP) zsdos.bin ../CBIOS/cbios_una.bin + cat $(CCP) zsdos.bin ../CBIOS/cbios_una.bin > $@ + diff --git a/Source/HBIOS/prefix.asm b/Source/ZSDOS/loader.asm similarity index 88% rename from Source/HBIOS/prefix.asm rename to Source/ZSDOS/loader.asm index 0916b3ef..27dc735c 100644 --- a/Source/HBIOS/prefix.asm +++ b/Source/ZSDOS/loader.asm @@ -1,5 +1,7 @@ ;=============================================================================== -; PREFIX.ASM +; BL.ASM +; +; BOOTLOADER FOR ROMWBW DISK OPERATING SYSTEMS. ; ; CP/M DISK FORMATS ALLOW FOR RESERVED TRACKS THAT CONTAIN AN IMAGE OF THE ; OPERATING SYSTEM TO BE LOADED WHEN THE DISK IS BOOTED. THE OPERATING SYSTEM @@ -23,7 +25,12 @@ ; ;=============================================================================== ; -#INCLUDE "std.asm" +#INCLUDE "ver.inc" +; +SYS_ENT .EQU $E600 ; SYSTEM (OS) ENTRY POINT ADDRESS +SYS_LOC .EQU $D000 ; STARTING ADDRESS TO LOAD SYSTEM IMAGE +SYS_END .EQU $FE00 ; ENDING ADDRESS OF SYSTEM IMAGE +; BYT .EQU 1 ; used to describe METADATA_SIZE below WRD .EQU 2 ; @@ -80,7 +87,7 @@ BOOT: ; LD D,B ; PASS BOOT UNIT TO OS LD E,0 ; ASSUME LU IS ZERO - JP CPM_ENT ; GO TO CPM + JP SYS_ENT ; GO TO SYSTEM ; PRTCHR: PUSH BC @@ -195,7 +202,7 @@ PR_SIG .DB $5A,$A5 ; SIGNATURE GOES HERE ; ; FIRST CHUNK OF METADATA IMMEDIATELY FOLLOWS THE SIGNATURE BYTES ; -PR_PLATFORM .DB PLATFORM ; PLATFORM ID (SEE STD.ASM) +PR_PLATFORM .DB 0 ; PLATFORM ID (SEE STD.ASM) PR_DEVICE .DB 0 ; ? (PROBABLY UNUSED) PR_FORMATTER .DB 0,0,0,0,0,0,0,0 ; ? (PROBABLY UNUSED) PR_DRIVE .DB 0 ; ? (PROBABLY UNUSED) @@ -212,9 +219,9 @@ PR_UPDSEQ .DW 0 ; PREFIX UPDATE SEQUENCE NUMBER (DEPRECATED?) PR_VER .DB RMJ,RMN,RUP,RTP ; OS BUILD VERSION PR_LABEL .DB "Unlabeled Drive ","$" ; DISK LABEL (EXACTLY 16 BYTES!!!) .DW 0 ; DEPRECATED -PR_LDLOC .DW CPM_LOC ; ADDRESS TO START LOADING OS -PR_LDEND .DW CPM_END ; ADDRESS TO STOP LOADING OS -PR_ENTRY .DW CPM_ENT ; ADDRESS TO ENTER OS +PR_LDLOC .DW SYS_LOC ; ADDRESS TO START LOADING SYSTEM +PR_LDEND .DW SYS_END ; ADDRESS TO STOP LOADING SYSTEM +PR_ENTRY .DW SYS_ENT ; ADDRESS TO ENTER SYSTEM (OS) ; ; ; diff --git a/Source/ZSDOS/ver.inc b/Source/ZSDOS/ver.inc new file mode 100644 index 00000000..8320a3c2 --- /dev/null +++ b/Source/ZSDOS/ver.inc @@ -0,0 +1,5 @@ +#DEFINE RMJ 2 +#DEFINE RMN 9 +#DEFINE RUP 2 +#DEFINE RTP 0 +#DEFINE BIOSVER "2.9.2-pre.33" diff --git a/Tools/Makefile.inc b/Tools/Makefile.inc new file mode 100644 index 00000000..4d0c5583 --- /dev/null +++ b/Tools/Makefile.inc @@ -0,0 +1,171 @@ +# +# try to use suffix rules whenever possible in any of the lower makefiles +# in the case of exceptions, just use an explicit build rule +# +# there are also some bizarre things being done with case-sensitive sources +# make is very much case-sensitive, so we use this. if your underlying +# filesystem is not case-preserving, forget it. +# +# .asm: TASM sources, except somewheres it is MAC or RMAC +# .z80: Z80ASM sources, except ZSDOS, where they are ZMAC +# .azm: zsm sources +# +UNAME := $(shell uname) +BINDIR = $(TOOLS)/$(UNAME) + +# +# since this file is included from below, it's handy to have an idea +# where we are relative to the tree +# +TREEROOT := $(shell cd $(TOOLS)/.. ; pwd) +HERE := $(shell pwd) +RELPATH := $(subst $(TREEROOT),,$(HERE)) + +# +# where's a copy of this tree for windows so we can diff binaries +# +WINROOT = $(TREEROOT)/../RomWBW.windows +DIFFTO := $(shell if [ -d $(WINROOT) ] ; then cd $(WINROOT); pwd; fi) +DIFFPATH := $(DIFFTO)/$(RELPATH) + +# +# this is a script that resolves a filename in a case-insensitive way +# to be used in diff'ing objects +# +CASEFN = $(TOOLS)/unix/casefn.sh + +ZXCC=$(BINDIR)/zx +TASM=$(BINDIR)/uz80as -t hd64180 +OPENSPIN=$(BINDIR)/openspin +BSTC=$(BINDIR)//bstc +CPMCP=$(BINDIR)/cpmcp + +# +# directory containing cpm binaries +# +CPM=$(TOOLS)/cpm/bin + +%.com: %.asm + if [ "$(USETASM)" = 1 ] ; then \ + $(TASM) $< $@ ; \ + else \ + $(ZXCC) $(CPM)/MAC -$< -$$PO ; \ + $(ZXCC) $(CPM)/MLOAD25 -tmp.bin=$*.hex ; \ + mv tmp.bin $@ ; \ + rm -f $$($(CASEFN) $*.hex) ; \ + fi + +%.hex: %.asm + $(ZXCC) $(CPM)/MAC -$< -$$PO ; \ + +%.bin: %.ASM + $(ZXCC) $(CPM)/MAC -$< -$$PO + $(ZXCC) $(CPM)/MLOAD25 -tmp.bin=$*.hex + mv tmp.bin $@ + rm -f $$($(CASEFN) $*.hex) + +%.com: %.z80 + $(ZXCC) $(CPM)/Z80ASM -$(basename $<)/F ; \ + mv $$($(CASEFN) $@) tmp.com ; mv tmp.com $@ + +%.bin: %.asm + $(TASM) $< $@ + +%.rel: %.asm + $(ZXCC) $(CPM)/RMAC -$< + +%.rel: %.z80 + $(ZXCC) $(CPM)/Z80ASM -$(basename $<)/MF + +%.hex: %.180 + $(ZXCC) $(CPM)/SLR180 -$(basename $<)/HF + +%.rel: %.azm + $(ZXCC) $(CPM)/ZSM =$< + +%.bin: %.rel + $(ZXCC) $(CPM)/LINK -$@=$< + +%.rel: %.mac + $(ZXCC) $(CPM)/M80 -=$(basename $<) + +ifeq ($(UNAME), Linux) +%.eeprom: %.spin + $(BSTC) -e -l $< +endif + +# +# darwin bstc won't run, since mac os does not do 32 bit binaries any more +# openspin ought to work +# +ifeq ($(UNAME), Darwin) +%.eeprom: %.spin + $(OPENSPIN) -e $< +endif + +# +# first target is default +# +all:: $(OBJECTS) + @for dir in $(SUBDIRS) ; do \ + ( echo "building in `pwd`/$$dir" ; cd "$$dir" ; make all ) ; \ + done + @if [ "$(DEST)" ] ; then for file in $(OBJECTS) ; do \ + mkdir -p $(DEST) ; \ + echo copy $$file to $(DEST) ; \ + cp $$($(CASEFN) $$file) $(DEST) ; \ + done ; fi + @if [ "$(DOCDEST)" ] ; then for file in $(DOCS) ; do \ + mkdir -p $(DOCDEST) ; \ + echo copy $$file to $(DOCDEST) ; \ + cp $$($(CASEFN) $$file) $(DOCDEST) ; \ + done ; fi + +clean:: + @-rm -f $$($(CASEFN) make.out *.sym *.lst *.prn *.diff *.dump $(OTHERS) $(filter-out $(NODELETE),$(OBJECTS))) + @for dir in $(SUBDIRS) ; do \ + ( echo "cleaning in `pwd`/$$dir" ; cd "$$dir" ; make clean ) ; \ + done + +clobber:: clean + @if [ "$(DEST)" ] ; then for file in $(OBJECTS) ; do \ + rm -f $$($(CASEFN) $(DEST)/$$file) ; \ + done ; fi + @-rm -f $$($(CASEFN) $(filter-out $(NODELETE),$(OBJECTS))) + @for dir in $(SUBDIRS) ; do \ + ( echo "clobbering in `pwd`/$$dir" ; cd "$$dir" ; make clobber ) ; \ + done + +# +# this is used to verify that the unix and windows tool chains are generating +# the same objects +# +diff:: +ifneq ($(DIFFTO),) + @for dir in $(SUBDIRS) ; do \ + ( echo "diff in $(HERE)/$$dir" ; cd "$$dir" ; make diff ) ; \ + done + @for i in $(OBJECTS) $(MOREDIFF) ; do \ + sf=$$($(CASEFN) $$i) ; \ + df=$$($(CASEFN) $(DIFFPATH)/$$i) ; \ + if [ -f "$$df" -a -f "$$sf" ] ; then \ + if [ "$(VERBOSEDIFF)" ] ; then \ + echo compare $$sf and $$df ; \ + fi ; \ + if ! cmp -s $$sf $$df ; then \ + echo " " $$sf and $$df differ ; \ + if [ "$(VERBOSEDIFF)" = "2" ] ; then \ + hexdump -Cv $$sf > $$sf.dump ; \ + hexdump -Cv $$df > $$(basename $$df).dump.diff ; \ + fi \ + fi \ + else \ + if [ ! -f "$$sf" ] ; then echo $$i missing ; fi ; \ + if [ ! -f "$$df" ] ; then echo $(DIFFPATH)/$$i missing ; fi ; \ + fi ; \ + done +endif + +vdiff: + make VERBOSEDIFF=2 diff + diff --git a/Tools/cpmtools/cpmchattr.exe b/Tools/cpmtools/cpmchattr.exe index 61a01932..f9d5e044 100644 Binary files a/Tools/cpmtools/cpmchattr.exe and b/Tools/cpmtools/cpmchattr.exe differ diff --git a/Tools/cpmtools/cpmchmod.exe b/Tools/cpmtools/cpmchmod.exe index 65db9af7..2e23a271 100644 Binary files a/Tools/cpmtools/cpmchmod.exe and b/Tools/cpmtools/cpmchmod.exe differ diff --git a/Tools/cpmtools/cpmcp.exe b/Tools/cpmtools/cpmcp.exe index 21c4f9ea..56d401d8 100644 Binary files a/Tools/cpmtools/cpmcp.exe and b/Tools/cpmtools/cpmcp.exe differ diff --git a/Tools/cpmtools/cpmls.exe b/Tools/cpmtools/cpmls.exe index c779134c..960b1773 100644 Binary files a/Tools/cpmtools/cpmls.exe and b/Tools/cpmtools/cpmls.exe differ diff --git a/Tools/cpmtools/cpmrm.exe b/Tools/cpmtools/cpmrm.exe index c44d68db..0a2bba1c 100644 Binary files a/Tools/cpmtools/cpmrm.exe and b/Tools/cpmtools/cpmrm.exe differ diff --git a/Tools/cpmtools/fsck.cpm.exe b/Tools/cpmtools/fsck.cpm.exe index eceeec9a..3e3d9f47 100644 Binary files a/Tools/cpmtools/fsck.cpm.exe and b/Tools/cpmtools/fsck.cpm.exe differ diff --git a/Tools/cpmtools/fsed.cpm.exe b/Tools/cpmtools/fsed.cpm.exe index 2cc97245..c763385c 100644 Binary files a/Tools/cpmtools/fsed.cpm.exe and b/Tools/cpmtools/fsed.cpm.exe differ diff --git a/Tools/cpmtools/mkfs.cpm.exe b/Tools/cpmtools/mkfs.cpm.exe index f8ac9b96..e878d4a1 100644 Binary files a/Tools/cpmtools/mkfs.cpm.exe and b/Tools/cpmtools/mkfs.cpm.exe differ diff --git a/Tools/fonttool/fonttool.exe b/Tools/fonttool/fonttool.exe new file mode 100644 index 00000000..2b5fda71 Binary files /dev/null and b/Tools/fonttool/fonttool.exe differ diff --git a/Tools/lzsa/ReadMe.txt b/Tools/lzsa/ReadMe.txt new file mode 100644 index 00000000..5238957e --- /dev/null +++ b/Tools/lzsa/ReadMe.txt @@ -0,0 +1,16 @@ +Command line compressor by Emmanuel Marty +The compression is done as follows: + +lzsa.exe -f2 -r + +where option -f2 selects lzsa version 2 compression. +where option -r asks for the generation of raw (frame-less) data. + +Original compression source code and files available here: + +Latest github code: + https://github.com/emmanuel-marty/lzsa +Implementation example (older version): + https://cpcrulez.fr/applications_tools_cruncher_LZSA2_Z80.htm +x86 lzsa compressor application: + http://www.pouet.net/prod.php?which=81573 \ No newline at end of file diff --git a/Tools/lzsa/lzsa.exe b/Tools/lzsa/lzsa.exe new file mode 100644 index 00000000..1c1927d7 Binary files /dev/null and b/Tools/lzsa/lzsa.exe differ diff --git a/Tools/unix/Makefile b/Tools/unix/Makefile new file mode 100644 index 00000000..bbe6724b --- /dev/null +++ b/Tools/unix/Makefile @@ -0,0 +1,27 @@ +# +# build the tools for linux and Darwin +# +UNAME := $(shell uname) +ifeq ($(UNAME), Linux) + SUFFIX=linux +endif +ifeq ($(UNAME), Darwin) + SUFFIX=osx +endif + +SUBDIRS= bst uz80as zx cpmtools bin2asm lzsa + +all: + @for i in $(SUBDIRS) ; do \ + (cd $$i ; make all ) \ + done + +clobber: + @for i in $(SUBDIRS) ; do \ + (cd $$i ; make clobber ) \ + done + +clean: + @for i in $(SUBDIRS) ; do \ + (cd $$i ; make clean ) \ + done diff --git a/Tools/unix/bin2asm/LICENSE b/Tools/unix/bin2asm/LICENSE new file mode 100644 index 00000000..b6df5b30 --- /dev/null +++ b/Tools/unix/bin2asm/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 ipatix + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Tools/unix/bin2asm/Makefile b/Tools/unix/bin2asm/Makefile new file mode 100644 index 00000000..27af4088 --- /dev/null +++ b/Tools/unix/bin2asm/Makefile @@ -0,0 +1,29 @@ +CC = gcc +CFLAGS = -Werror -Wall -Wextra -Wconversion -O2 -D NDEBUG +BINARY = bin2asm + +DEST := ../../$(shell uname) + +SRC_FILES = $(wildcard *.c) +OBJ_FILES = $(SRC_FILES:.c=.o) + +all: $(BINARY) $(DEST) + cp $(BINARY) $(DEST) + +$(DEST): + mkdir -p $(DEST) + +.PHONY: clean + +clean: + rm -f $(OBJ_FILES) + +clobber: + rm -f $(DEST)/$(BINARY) $(BINARY) + +$(BINARY): $(OBJ_FILES) + $(CC) -o $@ $^ $(LIBS) + +%.o: %.c + $(CC) -c -o $@ $< $(CFLAGS) $(IMPORT) + diff --git a/Tools/unix/bin2asm/README.md b/Tools/unix/bin2asm/README.md new file mode 100644 index 00000000..cc666ba1 --- /dev/null +++ b/Tools/unix/bin2asm/README.md @@ -0,0 +1,5 @@ +# bin2asm +Converts binary files to ASM compatible assembly files + +Usage: +$ bin2asm hello.bin > hello.asm diff --git a/Tools/unix/bin2asm/bin2asm.c b/Tools/unix/bin2asm/bin2asm.c new file mode 100644 index 00000000..e9af0fab --- /dev/null +++ b/Tools/unix/bin2asm/bin2asm.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +void die(const char msg[]) { + perror(msg); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) { + for (int i = 1; i < argc; i++) { + // determine file size + FILE *input_file = fopen(argv[i], "rb"); + if (!input_file) { + fprintf(stderr, "fail %s\n", argv[i]); + die("Couldn't open input file"); + } + if (fseek(input_file, 0, SEEK_END) == -1) { + die("Error determining file size"); + } + long file_size = ftell(input_file); + if (file_size == -1) { + die("Error determining file size (2)"); + } + if (fseek(input_file, 0, SEEK_SET) == -1) { + die("Error determining file size (3)"); + } + // get file name + char *name = basename(argv[i]); + char *dot = strrchr(name, '.'); + if (dot) { + *dot = '\0'; + } + + // print header +#ifdef notdef + printf(" .section .rodata\r\n"); + printf(" .global %s\r\n", name); + printf(" .align 2\r\n\r\n"); +#endif + printf("%s:\r\n\r\n", name); + + // write lines + while (file_size > 0) { + size_t bytes_read = (file_size > 8) ? 8 : (size_t)file_size; + unsigned char data_buf[8]; + size_t actual_read = fread(data_buf, 1, bytes_read, input_file); + if (actual_read != bytes_read) { + fprintf(stderr, "Error while reading file, only %d read instead of %d\n", (int)actual_read, (int)bytes_read); + if (feof(input_file)) + fprintf(stderr, "Reached end of file\n"); + if (ferror(input_file)) + fprintf(stderr, "An unknown error occured while reading the file\n"); + perror("ERROR"); + exit(EXIT_FAILURE); + } + switch (bytes_read) { + case 1: + printf(" .byte 0x%02X\r\n", + data_buf[0]); + break; + case 2: + printf(" .byte 0x%02X, 0x%02X\r\n", + data_buf[0], data_buf[1]); + break; + case 3: + printf(" .byte 0x%02X, 0x%02X, 0x%02X\r\n", + data_buf[0], data_buf[1], data_buf[2]); + break; + case 4: + printf(" .byte 0x%02X, 0x%02X, 0x%02X, 0x%02X\r\n", + data_buf[0], data_buf[1], data_buf[2], data_buf[3]); + break; + case 5: + printf(" .byte 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\r\n", + data_buf[0], data_buf[1], data_buf[2], data_buf[3], data_buf[4]); + break; + case 6: + printf(" .byte 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\r\n", + data_buf[0], data_buf[1], data_buf[2], data_buf[3], data_buf[4], data_buf[5]); + break; + case 7: + printf(" .byte 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\r\n", + data_buf[0], data_buf[1], data_buf[2], data_buf[3], data_buf[4], data_buf[5], data_buf[6]); + break; + case 8: + printf(" .byte 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\r\n", + data_buf[0], data_buf[1], data_buf[2], data_buf[3], data_buf[4], data_buf[5], data_buf[6], data_buf[7]); + break; + default: + fprintf(stderr, "Invalid program state\n"); + exit(EXIT_FAILURE); + } + file_size -= (long)bytes_read; + } + + printf("\r\n"); + + if (fclose(input_file)) { + die("Error while closing file"); + } + } + return 0; +} diff --git a/Tools/unix/bst/Makefile b/Tools/unix/bst/Makefile new file mode 100644 index 00000000..0d269dea --- /dev/null +++ b/Tools/unix/bst/Makefile @@ -0,0 +1,28 @@ +# +# build the propeller tools for linux and Darwin +# +UNAME := $(shell uname) +ifeq ($(UNAME), Linux) + SUFFIX=linux +endif +ifeq ($(UNAME), Darwin) + SUFFIX=osx +endif + +DEST = ../../$(UNAME) + +all: $(DEST) + -for i in *.$(SUFFIX) ; do \ + chmod +x $$i ; \ + cp $$i $(DEST)/$$(basename $$i .$(SUFFIX)) ; \ + done + +$(DEST): + mkdir $(DEST) + +clobber: + -for i in *.$(SUFFIX) ; do \ + rm $(DEST)/$$(basename $$i .$(SUFFIX)) ; \ + done + +clean: diff --git a/Tools/unix/bst/bstc.linux b/Tools/unix/bst/bstc.linux new file mode 100755 index 00000000..200c412e Binary files /dev/null and b/Tools/unix/bst/bstc.linux differ diff --git a/Tools/unix/bst/bstc.osx b/Tools/unix/bst/bstc.osx new file mode 100755 index 00000000..d69cbe56 Binary files /dev/null and b/Tools/unix/bst/bstc.osx differ diff --git a/Tools/unix/bst/bstl.linux b/Tools/unix/bst/bstl.linux new file mode 100755 index 00000000..03ee91cc Binary files /dev/null and b/Tools/unix/bst/bstl.linux differ diff --git a/Tools/unix/bst/bstl.osx b/Tools/unix/bst/bstl.osx new file mode 100755 index 00000000..18179de2 Binary files /dev/null and b/Tools/unix/bst/bstl.osx differ diff --git a/Tools/unix/bst/openspin.linux b/Tools/unix/bst/openspin.linux new file mode 100755 index 00000000..8bc88e5d Binary files /dev/null and b/Tools/unix/bst/openspin.linux differ diff --git a/Tools/unix/bst/openspin.osx b/Tools/unix/bst/openspin.osx new file mode 100755 index 00000000..f55a8131 Binary files /dev/null and b/Tools/unix/bst/openspin.osx differ diff --git a/Tools/unix/casefn.sh b/Tools/unix/casefn.sh new file mode 100755 index 00000000..34e7eb40 --- /dev/null +++ b/Tools/unix/casefn.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# given filename on the command line, echo the form of the file that +# actually can be opened. this needs to do filesystem case shenanigans +# +# we don't handle files with embedded spaces, a horrible idea anyway +# +# this is a bit slow with lots of files, so there's a cache of the join file +# +# the form of this cache file is gnarly: +# pwd dir:%d dir:%d ... +# where pwd is the current dir, dir are relative paths, and %d are mod times in +# seconds from the epoch +# +pid=.$$ +search=/tmp/casefn.search$pid +join=/tmp/casefn.join$pid +in=/tmp/casefn.in$pid +cache=/tmp/casefn.cache + +function cleanup { + rm -f $join $search $in +} + +trap cleanup EXIT +cleanup + +if [ $# -lt 1 ] ; then + exit 0 +fi + +scmd="stat -c %Y" +if [ $(uname) = "Darwin" ] ; then + scmd="stat -f %m" +fi + +function dirtime { + d=$1 + if [ -d $d ] ; then + $scmd $d + else + echo 0 + fi +} + +here=$(pwd) +chead="$here" + +# +# normalize to lower case all input file names +# while building an enumeration of all distinct directories +# +for infn in $* ; do + dirn=$(dirname $infn) + df= + for dl in $dirs ; do + if [ $dl == $dirn ] ; then + df=$dl + break; + fi + done + if [ -z $df ] ; then + dirs="$dirs $dirn" + chead="$chead $dirn:$(dirtime $dirn)" + fi + echo -n $dirn/ >> $in + basename $infn | tr '[A-Z]' '[a-z]' >> $in +done +sort -u $in > $search + +# +# if our cached join list matches our directory list, use it +# +if [ -f $cache ] ; then + cachedirs="$(head -1 $cache)" + if [ "$chead" = "$cachedirs" ] ; then + # echo hit >/dev/stderr + tail -n +2 $cache > $join + else + # echo miss >/dev/stderr + rm -f $cache + fi +fi + +# +# build join list of file names and lower case forms +# +if [ ! -f $join ] ; then + rm -f $in + for dn in $dirs ; do + cd $here + cd $dn + for i in * ; do + # skip any file names containing a space + if echo "$i" | grep -sq " " ; then + continue + fi + echo $dn/$(echo "$i" | tr '[A-Z]' '[a-z]')",$dn/$i" >> $in + done + done + sort -t, -k 1,1 $in > $join + echo "$chead" > $cache + cat $join >> $cache +fi + +join -t, -o 1.2 $join $search | sort -u > $in +if [ $(wc -l < $in) -gt 0 ] ; then + cat $in + exit 0 +fi +exit 2 diff --git a/Tools/unix/cpmtools/COPYING b/Tools/unix/cpmtools/COPYING new file mode 100644 index 00000000..44325404 --- /dev/null +++ b/Tools/unix/cpmtools/COPYING @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/Tools/unix/cpmtools/Makefile b/Tools/unix/cpmtools/Makefile new file mode 100644 index 00000000..d630b6f2 --- /dev/null +++ b/Tools/unix/cpmtools/Makefile @@ -0,0 +1,61 @@ +# +# cpmtools makefile stripped down to remove autoconf +# + +UNAME := $(shell uname) +DEST = ../../$(UNAME) + +CC = gcc +CFLAGS = -g + +DEFFORMAT = ibm-3740 +DEVICE = posix +DISKDEFS=$(shell cd ../../cpmtools ; pwd)/diskdefs +DEFFORMAT=wbw_fd144 + +# Locate the ncurses libs or try a guess if pkg-config does not exist +NCURSESLIBS=$(shell if env pkg-config --libs ncurses ; then : ; else echo -lncurses ; fi) + +CPPFLAGS = -DDISKDEFS=\"$(DISKDEFS)\" -DFORMAT=\"$(DEFFORMAT)\" + +DEVICEOBJ = device_posix.o + +OBJECTS = cpmls cpmrm cpmcp cpmchmod cpmchattr mkfs.cpm fsck.cpm fsed.cpm + +all: $(OBJECTS) $(DEST) + cp $(OBJECTS) $(DEST) + +cpmls: cpmls.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + $(CC) $(LDFLAGS) -o $@ cpmls.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + +cpmrm: cpmrm.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + $(CC) $(LDFLAGS) -o $@ cpmrm.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + +cpmcp: cpmcp.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + $(CC) $(LDFLAGS) -o $@ cpmcp.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + +cpmchmod: cpmchmod.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + $(CC) $(LDFLAGS) -o $@ cpmchmod.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + +cpmchattr: cpmchattr.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + $(CC) $(LDFLAGS) -o $@ cpmchattr.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + +mkfs.cpm: mkfs.cpm.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + $(CC) $(LDFLAGS) -o $@ mkfs.cpm.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + +fsck.cpm: fsck.cpm.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + $(CC) $(LDFLAGS) -o $@ fsck.cpm.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + +fsed.cpm: fsed.cpm.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) + $(CC) $(LDFLAGS) -o $@ fsed.cpm.o cpmfs.o getopt.o getopt1.o $(DEVICEOBJ) $(NCURSESLIBS) + +$(DEST): + mkdir -p $(DEST) + +clean: + -rm -f *.o $(OBJECTS) + +clobber: clean + -for i in $(OBJECTS) ; do \ + rm -f $(DEST)/$$i ; \ + done diff --git a/Tools/unix/cpmtools/badfs/Makefile b/Tools/unix/cpmtools/badfs/Makefile new file mode 100644 index 00000000..75c4ee00 --- /dev/null +++ b/Tools/unix/cpmtools/badfs/Makefile @@ -0,0 +1,7 @@ +# +# Dummy makefile +# +all: +clean: +distclean: + diff --git a/Tools/unix/cpmtools/badfs/blocknumber b/Tools/unix/cpmtools/badfs/blocknumber new file mode 100644 index 00000000..9259cacb Binary files /dev/null and b/Tools/unix/cpmtools/badfs/blocknumber differ diff --git a/Tools/unix/cpmtools/badfs/doubleext b/Tools/unix/cpmtools/badfs/doubleext new file mode 100644 index 00000000..8ee7d65b Binary files /dev/null and b/Tools/unix/cpmtools/badfs/doubleext differ diff --git a/Tools/unix/cpmtools/badfs/extension b/Tools/unix/cpmtools/badfs/extension new file mode 100644 index 00000000..cc02845f Binary files /dev/null and b/Tools/unix/cpmtools/badfs/extension differ diff --git a/Tools/unix/cpmtools/badfs/extno b/Tools/unix/cpmtools/badfs/extno new file mode 100644 index 00000000..5948b0ee Binary files /dev/null and b/Tools/unix/cpmtools/badfs/extno differ diff --git a/Tools/unix/cpmtools/badfs/hugecom b/Tools/unix/cpmtools/badfs/hugecom new file mode 100644 index 00000000..3d8fea49 Binary files /dev/null and b/Tools/unix/cpmtools/badfs/hugecom differ diff --git a/Tools/unix/cpmtools/badfs/label b/Tools/unix/cpmtools/badfs/label new file mode 100644 index 00000000..982928d1 Binary files /dev/null and b/Tools/unix/cpmtools/badfs/label differ diff --git a/Tools/unix/cpmtools/badfs/lcr b/Tools/unix/cpmtools/badfs/lcr new file mode 100644 index 00000000..da20f4b5 Binary files /dev/null and b/Tools/unix/cpmtools/badfs/lcr differ diff --git a/Tools/unix/cpmtools/badfs/multipleblocks b/Tools/unix/cpmtools/badfs/multipleblocks new file mode 100644 index 00000000..52c1d922 Binary files /dev/null and b/Tools/unix/cpmtools/badfs/multipleblocks differ diff --git a/Tools/unix/cpmtools/badfs/name b/Tools/unix/cpmtools/badfs/name new file mode 100644 index 00000000..67265aca Binary files /dev/null and b/Tools/unix/cpmtools/badfs/name differ diff --git a/Tools/unix/cpmtools/badfs/recordcount b/Tools/unix/cpmtools/badfs/recordcount new file mode 100644 index 00000000..e8633a56 Binary files /dev/null and b/Tools/unix/cpmtools/badfs/recordcount differ diff --git a/Tools/unix/cpmtools/badfs/status b/Tools/unix/cpmtools/badfs/status new file mode 100644 index 00000000..33da2481 Binary files /dev/null and b/Tools/unix/cpmtools/badfs/status differ diff --git a/Tools/unix/cpmtools/badfs/timestamps b/Tools/unix/cpmtools/badfs/timestamps new file mode 100644 index 00000000..a434cc96 Binary files /dev/null and b/Tools/unix/cpmtools/badfs/timestamps differ diff --git a/Tools/unix/cpmtools/config.h b/Tools/unix/cpmtools/config.h new file mode 100644 index 00000000..45bf194d --- /dev/null +++ b/Tools/unix/cpmtools/config.h @@ -0,0 +1,57 @@ +/* config.h. Generated from config.h.in by configure. */ +#define HAVE_FCNTL_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_WINDOWS_H 0 +#define HAVE_WINIOCTL_H 0 +#define HAVE_LIBDSK_H 0 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_MODE_T 0 +#define NEED_NCURSES 1 +#define HAVE_NCURSES_NCURSES_H 0 + +#if HAVE_SYS_STAT_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_LIMITS_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_WINDOWS_H +#include +#endif + +#if HAVE_WINIOCTL_H +#include +#endif + +#if HAVE_LIBDSK_H +#include +#endif + +#if HAVE_FCNTL_H +#include +#endif + +#ifndef _POSIX_PATH_MAX +#define _POSIX_PATH_MAX _MAX_PATH +#endif + +#include + +/* Define either for large file support, if your OS needs them. */ +/* #undef _FILE_OFFSET_BITS */ +/* #undef _LARGE_FILES */ + +/* Define if using dmalloc */ +/* #undef USE_DMALLOC */ diff --git a/Tools/unix/cpmtools/cpm.5 b/Tools/unix/cpmtools/cpm.5 new file mode 100644 index 00000000..9f11ff98 --- /dev/null +++ b/Tools/unix/cpmtools/cpm.5 @@ -0,0 +1,300 @@ +.\" Believe it or not, reportedly there are nroffs which do not know \(en +.if n .ds en - +.if t .ds en \(en +.TH CPM 5 "October 25, 2014" "CP/M tools" "File formats" +.SH NAME \"{{{roff}}}\"{{{ +cpm \- CP/M disk and file system format +.\"}}} +.SH DESCRIPTION \"{{{ +.SS "Characteristic sizes" \"{{{ +Each CP/M disk format is described by the following specific sizes: +.RS +.sp +Sector size in bytes +.br +Number of tracks +.br +Number of sectors +.br +Block size +.br +Number of directory entries +.br +Logical sector skew +.br +Number of reserved system tracks (optional) +.br +Offset to start of volume (optional and not covered by operating system, +but disk driver specific) +.sp +.RE +A block is the smallest allocatable storage unit. CP/M supports block +sizes of 1024, 2048, 4096, 8192 and 16384 bytes. Unfortunately, this +format specification is not stored on the disk and there are lots of +formats. Accessing a block is performed by accessing its sectors, which +are stored with the given software skew. +.\"}}} +.SS "Device areas" \"{{{ +A CP/M disk contains four areas: +.RS +.sp +Volume offset (optional and not covered by operating system, but disk driver specific) +.br +System tracks (optional) +.br +Directory +.br +Data +.sp +.RE +The system tracks store the boot loader and CP/M itself. In order to save +disk space, there are non-bootable formats which omit those system tracks. +The term \fIdisk capacity\fP always excludes the space for system tracks. +Note that there is no bitmap or list for free blocks. When accessing a +drive for the first time, CP/M builds this bitmap in core from the directory. +.LP +A hard disk can have the additional notion of a \fIvolume offset\fP to +locate the start of the drive image (which may or may not have system +tracks associated with it). The base unit for volume offset is byte +count from the beginning of the physical disk, but specifiers of +\fIK\fP, \fIM\fP, \fIT\fP or \fIS\fP may be appended to denote +kilobytes, megabytes, tracks or sectors. If provided, a specifier +must immediately follow the numeric value with no whitespace. For +convenience upper and lower case are both accepted and only the first +letter is significant, thus 2KB, 8MB, 1000trk and 16sec are valid +values. The \fBoffset\fP must appear subsequent to track, sector and sector +length values for the sector and track units to work. +.\"}}} +.SS "Directory entries" \"{{{ +The directory is a sequence of directory entries (also called extents), +which contain 32 bytes of the following structure: +.RS +.sp +.ta 3n 6n 9n 12n 15n 18n 21n 24n 27n 30n 33n 36n 39n 42n 45n +St F0 F1 F2 F3 F4 F5 F6 F7 E0 E1 E2 Xl Bc Xh Rc +.br +Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al +.sp +.RE +.\"{{{ St = status +\fBSt\fP is the status; possible values are: +.RS +.sp +0\*(en15: used for file, status is the user number +.br +16\*(en31: used for file, status is the user number (P2DOS) +or used for password extent (CP/M 3 or higher) +.br +32: disc label +.br +33: time stamp (P2DOS) +.br +0xE5: unused +.sp +.RE +.\"}}} +.LP +.\"{{{ F0-E2 = file name and extension +\fBF0\*(enE2\fP are the file name and its extension. They may consist of +any printable 7 bit ASCII character but: \fB< > . , ; : = ? * [ ]\fP. +The file name must not be empty, the extension may be empty. Both are +padded with blanks. The highest bit of each character of the file name +and extension is used as attribute. The attributes have the following +meaning: +.RS +.sp +F0: requires set wheel byte (Backgrounder II) +.br +F1: public file (P2DOS, ZSDOS), forground-only command (Backgrounder II) +.br +F2: date stamp (ZSDOS), background-only commands (Backgrounder II) +.br +F7: wheel protect (ZSDOS) +.br +E0: read-only +.br +E1: system file +.br +E2: archived +.sp +.RE +Public files (visible under each user number) are not supported by CP/M +2.2, but there is a patch and some free CP/M clones support them without +any patches. +.LP +The wheel byte is (by default) the memory location at 0x4b. If it is +zero, only non-privileged commands may be executed. +.\"}}} +.LP +.\"{{{ Xl, Xh = extent number +\fBXl\fP and \fBXh\fP store the extent number. A file may use more than +one directory entry, if it contains more blocks than an extent can hold. +In this case, more extents are allocated and each of them is numbered +sequentially with an extent number. If a physical extent stores more than +16k, it is considered to contain multiple logical extents, each pointing +to 16k data, and the extent number of the last used logical extent +is stored. Note: Some formats decided to always store only one logical +extent in a physical extent, thus wasting extent space. CP/M 2.2 allows +512 extents per file, CP/M 3 and higher allow up to 2048. Bit 5\*(en7 of +Xl are 0, bit 0\*(en4 store the lower bits of the extent number. Bit 6 +and 7 of Xh are 0, bit 0\*(en5 store the higher bits of the extent number. +.\"}}} +.LP +.\"{{{ Rc, Bc = record count, byte count +\fBRc\fP and \fBBc\fP determine the length of the data used by this extent. The +physical extent is divided into logical extents, each of them being 16k +in size (a physical extent must hold at least one logical extent, e.g. a +blocksize of 1024 byte with two-byte block pointers is not allowed). +Rc stores the number of 128 byte records of the last used logical extent. +Bc stores the number of bytes in the last used record. The value 0 means +128 for backward compatibility with CP/M 2.2, which did not support Bc. +ISX records the number of unused instead of used bytes in Bc. +.\"}}} +.LP +.\"{{{ Al = allocated blocks +\fBAl\fP stores block pointers. If the disk capacity minus boot +tracks but including the directory area is less than 256 blocks, Al +is interpreted as 16 byte-values, otherwise as 8 double-byte-values. +Since the directory area is not subtracted, the directory area starts +with block 0 and files can never allocate block 0, which is why this +value can be given a new meaning: A block pointer of 0 marks a hole in +the file. If a hole covers the range of a full extent, the extent will +not be allocated. In particular, the first extent of a file does not +neccessarily have extent number 0. A file may not share blocks with other +files, as its blocks would be freed if the other files is erased without +a following disk system reset. CP/M returns EOF when it reaches a hole, +whereas UNIX returns zero-value bytes, which makes holes invisible. +.\"}}} +.\"}}} +.SS "Native time stamps" \"{{{ +P2DOS and CP/M Plus support time stamps, which are stored in each fourth +directory entry. This entry contains the time stamps for +the extents using the previous three directory entries. Note that you +really have time stamps for each extent, no matter if it is the first +extent of a file or not. The structure of time stamp entries is: +.RS +.sp +1 byte status 0x21 +.br +8 bytes time stamp for third-last directory entry +.br +2 bytes unused +.br +8 bytes time stamp for second-last directory entry +.br +2 bytes unused +.br +8 bytes time stamp for last directory entry +.sp +.RE +A time stamp consists of two dates: Creation and modification date (the +latter being recorded when the file is closed). CP/M Plus further +allows optionally to record the access instead of creation date as first +time stamp. +.RS +.sp +2 bytes (little-endian) days starting with 1 at 01-01-1978 +.br +1 byte hour in BCD format +.br +1 byte minute in BCD format +.sp +.RE +All time stamps are stored in local time. +.\"}}} +.SS "DateStamper time stamps" \"{{{ +The DateStamper software added functions to the BDOS to manage +time stamps by allocating a read only file with the name "!!!TIME&.DAT" +in the very first directory entry, covering the very first data +blocks. It contains one entry per directory entry with the +following structure of 16 bytes: +.RS +.sp +5 bytes create datefield +.br +5 bytes access datefield +.br +5 bytes modify datefield +.br +1 byte magic number/checksum +.sp +.RE +The magic number is used for the first 7 entries of each 128-byte record +and contains the characters \fB!\fP, \fB!\fP, \fB!\fP, \fBT\fP, \fBI\fP, +\fBM\fP and \fBE\fP. The checksum is used on every 8th entry (last entry +in 128-byte record) and is the sum of the first 127 bytes of the record. +Each datefield has this structure: +.RS +.sp +1 byte BCD coded year (no century, so it is sane assuming any year < 70 +means 21st century) +.br +1 byte BCD coded month +.br +1 byte BCD coded day +.br +1 byte BCD coded hour or, if the high bit is set, the high byte of a +counter for systems without real time clock +.br +1 byte BCD coded minute, or the low byte of the counter +.sp +.DE +.\"}}} +.SS "Disc labels" \"{{{ +CP/M Plus support disc labels, which are stored in an arbitrary directory +entry. +The structure of disc labels is: +.RS +.sp +1 byte status 0x20 +.br +\fBF0\*(enE2\fP are the disc label +.br +1 byte mode: bit 7 activates password protection, bit 6 causes time stamps on +access, but 5 causes time stamps on modifications, bit 4 causes time stamps on +creation and bit 0 is set when a label exists. Bit 4 and 6 are exclusively set. +.br +1 byte password decode byte: To decode the password, xor this byte with the password +bytes in reverse order. To encode a password, add its characters to get the +decode byte. +.br +2 reserved bytes +.br +8 password bytes +.br +4 bytes label creation time stamp +.br +4 bytes label modification time stamp +.sp +.RE +.\"}}} +.SS "Passwords" \"{{{ +CP/M Plus supports passwords, which are stored in an arbitrary directory +entry. +The structure of these entries is: +.RS +.sp +1 byte status (user number plus 16) +.br +\fBF0\*(enE2\fP are the file name and its extension. +.br +1 byte password mode: bit 7 means password required for reading, bit 6 for writing +and bit 5 for deleting. +.br +1 byte password decode byte: To decode the password, xor this byte with the password +bytes in reverse order. To encode a password, add its characters to get the +decode byte. +.br +2 reserved bytes +.br +8 password bytes +.sp +.RE +.\"}}} +.\"}}} +.SH "SEE ALSO" \"{{{ +.IR mkfs.cpm (1), +.IR fsck.cpm (1), +.IR fsed.cpm (1), +.IR cpmls (1) +.\"}}} diff --git a/Tools/unix/cpmtools/cpm.ps b/Tools/unix/cpmtools/cpm.ps new file mode 100644 index 00000000..6c631338 --- /dev/null +++ b/Tools/unix/cpmtools/cpm.ps @@ -0,0 +1,478 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.19 +%%CreationDate: Sun Feb 3 19:48:55 2013 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.19 0 +%%Pages: 4 +%%PageOrder: Ascend +%%DocumentMedia: Default 595 842 0 () () +%%Orientation: Portrait +%%EndComments +%%BeginDefaults +%%PageMedia: Default +%%EndDefaults +%%BeginProlog +%%BeginResource: procset grops 1.19 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/MANUAL{ +statusdict begin/manualfeed true store end +}bind def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/Fr{ +setrgbcolor fill +}bind def +/setcmykcolor where{ +pop +/Fk{ +setcmykcolor fill +}bind def +}if +/Fg{ +setgray fill +}bind def +/FL/fill load def +/LW/setlinewidth load def +/Cr/setrgbcolor load def +/setcmykcolor where{ +pop +/Ck/setcmykcolor load def +}if +/Cg/setgray load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +/setpagedevice{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%BeginFeature: *PageSize Default +<< /PageSize [ 595 842 ] /ImagingBBox null >> setpagedevice +%%EndFeature +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 +def/PL 841.89 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron +/Zcaron/scaron/zcaron/Ydieresis/trademark/quotesingle/Euro/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/space/exclam/quotedbl/numbersign/dollar/percent +/ampersand/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen +/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon +/semicolon/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O +/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright/circumflex +/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y +/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase/guillemotleft +/guillemotright/bullet/florin/fraction/perthousand/dagger/daggerdbl +/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen +/brokenbar/section/dieresis/copyright/ordfeminine/guilsinglleft +/logicalnot/minus/registered/macron/degree/plusminus/twosuperior +/threesuperior/acute/mu/paragraph/periodcentered/cedilla/onesuperior +/ordmasculine/guilsinglright/onequarter/onehalf/threequarters +/questiondown/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE +/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex +/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn +/germandbls/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla +/egrave/eacute/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis +/eth/ntilde/ograve/oacute/ocircumflex/otilde/odieresis/divide/oslash +/ugrave/uacute/ucircumflex/udieresis/yacute/thorn/ydieresis]def +/Times-Italic@0 ENC0/Times-Italic RE/Times-Bold@0 ENC0/Times-Bold RE +/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 174.415(CPM\(5\) File)72 48 R 174.415 +(formats CPM\(5\))2.5 F/F1 10.95/Times-Bold@0 SF -.219(NA)72 84 S(ME) +.219 E F0(cpm \255 CP/M disk and \214le system format)108 96 Q F1 +(DESCRIPTION)72 112.8 Q/F2 10/Times-Bold@0 SF(Characteristic sizes)87 +124.8 Q F0(Each CP/M disk format is described by the follo)108 136.8 Q +(wing speci\214c sizes:)-.25 E(Sector size in bytes)144 160.8 Q +(Number of tracks)144 172.8 Q(Number of sectors)144 184.8 Q(Block size) +144 196.8 Q(Number of directory entries)144 208.8 Q(Logical sector sk) +144 220.8 Q -.25(ew)-.1 G(Number of reserv)144 232.8 Q +(ed system tracks \(optional\))-.15 E(Of)144 244.8 Q(fset to start of v) +-.25 E(olume \(optional\))-.2 E 2.848(Ab)108 268.8 S .348 +(lock is the smallest allocatable storage unit.)-2.848 F .347 +(CP/M supports block sizes of 1024, 2048, 4096, 8192 and)5.348 F .207 +(16384 bytes.)108 280.8 R(Unfortunately)5.207 E 2.707(,t)-.65 G .208(hi\ +s format speci\214cation is not stored on the disk and there are lots o\ +f formats.)-2.707 F(Accessing a block is performed by accessing its sec\ +tors, which are stored with the gi)108 292.8 Q -.15(ve)-.25 G 2.5(ns).15 +G(oftw)-2.5 E(are sk)-.1 E -.25(ew)-.1 G(.)-.4 E F2(De)87 309.6 Q +(vice ar)-.15 E(eas)-.18 E F0 2.5(AC)108 321.6 S +(P/M disk contains three areas:)-2.5 E -1.29(Vo)144 345.6 S(lume of)1.29 +E(fset \(optional\))-.25 E(System tracks \(optional\))144 357.6 Q +(Directory)144 369.6 Q(Data)144 381.6 Q .058 +(The system tracks store the boot loader and CP/M itself.)108 405.6 R +.058(In order to sa)5.058 F .358 -.15(ve d)-.2 H .057 +(isk space, there are non-bootable).15 F 1.55 +(formats which omit those system tracks.)108 417.6 R 1.55(The term)6.55 +F/F3 10/Times-Italic@0 SF 1.55(disk capacity)4.05 F F0(al)4.05 E -.1(wa) +-.1 G 1.55(ys e).1 F 1.55(xcludes the space for system)-.15 F 2.748 +(tracks. Note)108 429.6 R .248 +(that there is no bitmap or list for free blocks.)2.748 F .248 +(When accessing a dri)5.248 F .548 -.15(ve f)-.25 H .248 +(or the \214rst time, CP/M).15 F -.2(bu)108 441.6 S +(ilds this bitmap in core from the directory).2 E(.)-.65 E 3.15(Ah)108 +458.4 S .65(ard disk can ha)-3.15 F .95 -.15(ve t)-.2 H .65 +(he additional notion of a).15 F F3 .65(volume of)3.15 F(fset)-.18 E F0 +.65(to locate the start of the dri)3.15 F .95 -.15(ve i)-.25 H .65 +(mage \(which).15 F .531(may or may not ha)108 470.4 R .831 -.15(ve s) +-.2 H .531(ystem tracks associated with it\). The base unit for v).15 F +.53(olume of)-.2 F .53(fset is byte count from)-.25 F 1.224(the be)108 +482.4 R 1.224(ginning of the ph)-.15 F 1.224(ysical disk, b)-.05 F 1.225 +(ut speci\214ers of)-.2 F F3(K)3.725 E F0(,)A F3(M)3.725 E F0(,)A F3(T) +3.725 E F0(or)3.725 E F3(S)3.725 E F0 1.225 +(may be appended to denote kilobytes,)3.725 F(me)108 494.4 Q -.05(ga) +-.15 G .806(bytes, tracks or sectors.).05 F .806(If pro)5.806 F .805 +(vided, a speci\214er must immediately follo)-.15 F 3.305(wt)-.25 G .805 +(he numeric v)-3.305 F .805(alue with no)-.25 F 2.881(whitespace. F)108 +506.4 R .381(or con)-.15 F -.15(ve)-.4 G .381(nience upper and lo).15 F +.381(wer case are both accepted and only the \214rst letter is signi\ +\214cant,)-.25 F .02(thus 2KB, 8MB, 1000trk and 16sec are v)108 518.4 R +.019(alid v)-.25 F .019(alues. Of)-.25 F .019 +(fset must appear subsequent to track, sector and sec-)-.25 F +(tor length v)108 530.4 Q(alues.)-.25 E F2(Dir)87 547.2 Q +(ectory entries)-.18 E F0 .408 +(The directory is a sequence of directory entries \(also called e)108 +559.2 R .409(xtents\), which contain 32 bytes of the follo)-.15 F(w-) +-.25 E(ing structure:)108 571.2 Q 4.16(St F0)144 595.2 R 1.94 +(F1 F2 F3 F4 F5 F6 F7 E0)4.44 F 1.39(E1 E2 Xl)3.89 F 1.39(Bc Xh)5 F(Rc) +2.78 E 2.5(Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al)144 607.2 R +F2(St)108 631.2 Q F0(is the status; possible v)2.5 E(alues are:)-.25 E +(0\21115: used for \214le, status is the user number)144 655.2 Q .795(1\ +6\21131: used for \214le, status is the user number \(P2DOS\) or used f\ +or passw)144 667.2 R .794(ord e)-.1 F .794(xtent \(CP/M 3 or)-.15 F +(higher\))144 679.2 Q(32: disc label)144 691.2 Q +(33: time stamp \(P2DOS\))144 703.2 Q(0xE5: unused)144 715.2 Q +(CP/M tools)72 768 Q(February 18, 2012)151.35 E(1)192.2 E 0 Cg EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 174.415(CPM\(5\) File)72 48 R 174.415 +(formats CPM\(5\))2.5 F/F1 10/Times-Bold@0 SF(F0\211E2)108 84 Q F0 .412 +(are the \214le name and its e)2.912 F 2.913(xtension. The)-.15 F 2.913 +(ym)-.15 G .413(ay consist of an)-2.913 F 2.913(yp)-.15 G .413 +(rintable 7 bit ASCII character b)-2.913 F(ut:)-.2 E F1(<)2.913 E 3.362 +(>.,;:=?*[])108 96 S F0 5.862(.T)-3.362 G .862 +(he \214le name must not be empty)-5.862 F 3.361(,t)-.65 G .861(he e) +-3.361 F .861(xtension may be empty)-.15 F 5.861(.B)-.65 G .861 +(oth are padded with)-5.861 F 2.831(blanks. The)108 108 R .331 +(highest bit of each character of the \214le name and e)2.831 F .331 +(xtension is used as attrib)-.15 F 2.832(ute. The)-.2 F(attrib)2.832 E +(utes)-.2 E(ha)108 120 Q .3 -.15(ve t)-.2 H(he follo).15 E +(wing meaning:)-.25 E(F0: requires set wheel byte \(Backgrounder II\)) +144 144 Q(F1: public \214le \(P2DOS, ZSDOS\), for)144 156 Q +(ground-only command \(Backgrounder II\))-.18 E +(F2: date stamp \(ZSDOS\), background-only commands \(Backgrounder II\)) +144 168 Q(F7: wheel protect \(ZSDOS\))144 180 Q(E0: read-only)144 192 Q +(E1: system \214le)144 204 Q(E2: archi)144 216 Q -.15(ve)-.25 G(d).15 E +.338(Public \214les \(visible under each user number\) are not supporte\ +d by CP/M 2.2, b)108 240 R .338(ut there is a patch and some)-.2 F +(free CP/M clones support them without an)108 252 Q 2.5(yp)-.15 G +(atches.)-2.5 E .827(The wheel byte is \(by def)108 268.8 R .828 +(ault\) the memory location at 0x4b)-.1 F 5.828(.I)-.4 G 3.328(fi)-5.828 +G 3.328(ti)-3.328 G 3.328(sz)-3.328 G .828(ero, only non-pri)-3.328 F +(vile)-.25 E .828(ged commands)-.15 F(may be e)108 280.8 Q -.15(xe)-.15 +G(cuted.).15 E F1(Xl)108 297.6 Q F0(and)2.546 E F1(Xh)2.546 E F0 .046 +(store the e)2.546 F .046(xtent number)-.15 F 5.046(.A)-.55 G .045 +(\214le may use more than one directory entry)-2.5 F 2.545(,i)-.65 G +2.545(fi)-2.545 G 2.545(tc)-2.545 G .045(ontains more blocks)-2.545 F +.21(than an e)108 309.6 R .21(xtent can hold.)-.15 F .21 +(In this case, more e)5.21 F .21 +(xtents are allocated and each of them is numbered sequentially)-.15 F +.457(with an e)108 321.6 R .457(xtent number)-.15 F 5.457(.I)-.55 G +2.957(fap)-5.457 G -.05(hy)-2.957 G .457(sical e).05 F .456 +(xtent stores more than 16k, it is considered to contain multiple logi-) +-.15 F .234(cal e)108 333.6 R .234 +(xtents, each pointing to 16k data, and the e)-.15 F .234 +(xtent number of the last used logical e)-.15 F .235(xtent is stored.) +-.15 F(Note:)5.235 E 1.55(Some formats decided to al)108 345.6 R -.1(wa) +-.1 G 1.549(ys store only one logical e).1 F 1.549(xtent in a ph)-.15 F +1.549(ysical e)-.05 F 1.549(xtent, thus w)-.15 F 1.549(asting e)-.1 F +(xtent)-.15 E 2.81(space. CP/M)108 357.6 R .31(2.2 allo)2.81 F .31 +(ws 512 e)-.25 F .31(xtents per \214le, CP/M 3 and higher allo)-.15 F +2.811(wu)-.25 G 2.811(pt)-2.811 G 2.811(o2)-2.811 G 2.811(048. Bit) +-2.811 F .311(5\2117 of Xl are 0, bit)2.811 F .577(0\2114 store the lo) +108 369.6 R .577(wer bits of the e)-.25 F .576(xtent number)-.15 F 5.576 +(.B)-.55 G .576 +(it 6 and 7 of Xh are 0, bit 0\2115 store the higher bits of the)-5.576 +F -.15(ex)108 381.6 S(tent number).15 E(.)-.55 E F1(Rc)108 398.4 Q F0 +(and)2.946 E F1(Bc)2.946 E F0 .446 +(determine the length of the data used by this e)2.946 F 2.946 +(xtent. The)-.15 F(ph)2.947 E .447(ysical e)-.05 F .447(xtent is di)-.15 +F .447(vided into logical)-.25 F -.15(ex)108 410.4 S .156 +(tents, each of them being 16k in size \(a ph).15 F .156(ysical e)-.05 F +.156(xtent must hold at least one logical e)-.15 F .156 +(xtent, e.g. a block-)-.15 F .053(size of 1024 byte with tw)108 422.4 R +.054(o-byte block pointers is not allo)-.1 F 2.554(wed\). Rc)-.25 F .054 +(stores the number of 128 byte records of)2.554 F .457 +(the last used logical e)108 434.4 R 2.957(xtent. Bc)-.15 F .456 +(stores the number of bytes in the last used record.)2.957 F .456(The v) +5.456 F .456(alue 0 means 128)-.25 F .654(for backw)108 446.4 R .654 +(ard compatibility with CP/M 2.2, which did not support Bc.)-.1 F .655 +(ISX records the number of unused)5.655 F(instead of used bytes in Bc.) +108 458.4 Q F1(Al)108 475.2 Q F0 .9(stores block pointers.)3.4 F .899(I\ +f the disk capacity is less than 256 blocks, Al is interpreted as 16 by\ +te-v)5.9 F(alues,)-.25 E .243(otherwise as 8 double-byte-v)108 487.2 R +2.743(alues. A)-.25 F .243 +(block pointer of 0 marks a hole in the \214le.)2.743 F .243 +(If a hole co)5.243 F -.15(ve)-.15 G .243(rs the range).15 F .341 +(of a full e)108 499.2 R .341(xtent, the e)-.15 F .341 +(xtent will not be allocated.)-.15 F .34(In particular)5.341 F 2.84(,t) +-.4 G .34(he \214rst e)-2.84 F .34 +(xtent of a \214le does not neccessarily)-.15 F(ha)108 511.2 Q .479 -.15 +(ve ex)-.2 H .179(tent number 0.).15 F 2.679<418c>5.179 G .18 +(le may not share blocks with other \214les, as its blocks w)-2.679 F +.18(ould be freed if the other)-.1 F .822 +(\214les is erased without a follo)108 523.2 R .822 +(wing disk system reset.)-.25 F .822 +(CP/M returns EOF when it reaches a hole, whereas)5.822 F +(UNIX returns zero-v)108 535.2 Q(alue bytes, which mak)-.25 E +(es holes in)-.1 E(visible.)-.4 E F1(Nati)87 552 Q .2 -.1(ve t)-.1 H +(ime stamps).1 E F0 1.053(P2DOS and CP/M Plus support time stamps, whic\ +h are stored in each fourth directory entry)108 564 R 6.054(.T)-.65 G +1.054(his entry)-6.054 F 1.3(contains the time stamps for the e)108 576 +R 1.299(xtents using the pre)-.15 F 1.299 +(vious three directory entries.)-.25 F 1.299(Note that you really)6.299 +F(ha)108 588 Q 1.294 -.15(ve t)-.2 H .994(ime stamps for each e).15 F +.994(xtent, no matter if it is the \214rst e)-.15 F .994 +(xtent of a \214le or not.)-.15 F .995(The structure of time)5.994 F +(stamp entries is:)108 600 Q 2.5(1b)144 624 S(yte status 0x21)-2.5 E 2.5 +(8b)144 636 S(ytes time stamp for third-last directory entry)-2.5 E 2.5 +(2b)144 648 S(ytes unused)-2.5 E 2.5(8b)144 660 S +(ytes time stamp for second-last directory entry)-2.5 E 2.5(2b)144 672 S +(ytes unused)-2.5 E 2.5(8b)144 684 S +(ytes time stamp for last directory entry)-2.5 E 2.872(At)108 708 S .372 +(ime stamp consists of tw)-2.872 F 2.872(od)-.1 G .372(ates: Creation a\ +nd modi\214cation date \(the latter being recorded when the \214le) +-2.872 F .935(is closed\).)108 720 R .936(CP/M Plus further allo)5.935 F +.936(ws optionally to record the access instead of creation date as \ +\214rst time)-.25 F(CP/M tools)72 768 Q(February 18, 2012)151.35 E(2) +192.2 E 0 Cg EP +%%Page: 3 3 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 174.415(CPM\(5\) File)72 48 R 174.415 +(formats CPM\(5\))2.5 F(stamp.)108 84 Q 2.5(2b)144 108 S +(ytes \(little-endian\) days starting with 1 at 01-01-1978)-2.5 E 2.5 +(1b)144 120 S(yte hour in BCD format)-2.5 E 2.5(1b)144 132 S +(yte minute in BCD format)-2.5 E/F1 10/Times-Bold@0 SF +(DateStamper time stamps)87 160.8 Q F0 .552(The DateStamper softw)108 +172.8 R .552(are added functions to the BDOS to manage time stamps by a\ +llocating a read only)-.1 F .441(\214le with the name "!!!TIME&.D)108 +184.8 R -1.11(AT)-.4 G 2.941("i)1.11 G 2.941(nt)-2.941 G .441(he v) +-2.941 F .441(ery \214rst directory entry)-.15 F 2.941(,c)-.65 G -.15 +(ove)-2.941 G .441(ring the v).15 F .442(ery \214rst data blocks.)-.15 F +(It)5.442 E(contains one entry per directory entry with the follo)108 +196.8 Q(wing structure of 16 bytes:)-.25 E 2.5(5b)144 220.8 S +(ytes create date\214eld)-2.5 E 2.5(5b)144 232.8 S +(ytes access date\214eld)-2.5 E 2.5(5b)144 244.8 S +(ytes modify date\214eld)-2.5 E 2.5(1b)144 256.8 S(yte checksum)-2.5 E +.237(The checksum is only used on e)108 280.8 R -.15(ve)-.25 G .236(ry \ +8th entry \(last entry in 128-byte record\) and is the sum of the \214r\ +st 127).15 F(bytes of the record.)108 292.8 Q +(Each date\214eld has this structure:)5 E 2.5(1b)144 316.8 S +(yte BCD coded year \(no century)-2.5 E 2.5(,s)-.65 G 2.5(oi)-2.5 G 2.5 +(ti)-2.5 G 2.5(ss)-2.5 G(ane assuming an)-2.5 E 2.5(yy)-.15 G +(ear < 70 means 21st century\))-2.5 E 2.5(1b)144 328.8 S +(yte BCD coded month)-2.5 E 2.5(1b)144 340.8 S(yte BCD coded day)-2.5 E +2.608(1b)144 352.8 S .108(yte BCD coded hour or)-2.608 F 2.608(,i)-.4 G +2.608(ft)-2.608 G .108(he high bit is set, the high byte of a counter f\ +or systems without real)-2.608 F(time clock)144 364.8 Q 2.5(1b)144 376.8 +S(yte BCD coded minute, or the lo)-2.5 E 2.5(wb)-.25 G +(yte of the counter)-2.5 E F1(Disc labels)87 405.6 Q F0 .258(CP/M Plus \ +support disc labels, which are stored in an arbitrary directory entry) +108 417.6 R 5.257(.T)-.65 G .257(he structure of disc labels)-5.257 F +(is:)108 429.6 Q 2.5(1b)144 453.6 S(yte status 0x20)-2.5 E F1(F0\211E2) +144 465.6 Q F0(are the disc label)2.5 E 2.886(1b)144 477.6 S .386 +(yte mode: bit 7 acti)-2.886 F -.25(va)-.25 G .386(tes passw).25 F .387 +(ord protection, bit 6 causes time stamps on access, b)-.1 F .387 +(ut 5 causes)-.2 F .874(time stamps on modi\214cations, bit 4 causes ti\ +me stamps on creation and bit 0 is set when a label)144 489.6 R -.15(ex) +144 501.6 S 2.5(ists. Bit).15 F 2.5(4a)2.5 G(nd 6 are e)-2.5 E(xclusi) +-.15 E -.15(ve)-.25 G(ly set.).15 E 3.45(1b)144 513.6 S .95(yte passw) +-3.45 F .95(ord decode byte: T)-.1 F 3.45(od)-.8 G .951(ecode the passw) +-3.45 F .951(ord, xor this byte with the passw)-.1 F .951(ord bytes in) +-.1 F(re)144 525.6 Q -.15(ve)-.25 G(rse order).15 E 5(.T)-.55 G 2.5(oe) +-5.8 G(ncode a passw)-2.5 E +(ord, add its characters to get the decode byte.)-.1 E 2.5(2r)144 537.6 +S(eserv)-2.5 E(ed bytes)-.15 E 2.5(8p)144 549.6 S(assw)-2.5 E(ord bytes) +-.1 E 2.5(4b)144 561.6 S(ytes label creation time stamp)-2.5 E 2.5(4b) +144 573.6 S(ytes label modi\214cation time stamp)-2.5 E F1 -.1(Pa)87 +602.4 S(ssw).1 E(ords)-.1 E F0 1.484(CP/M Plus supports passw)108 614.4 +R 1.484(ords, which are stored in an arbitrary directory entry)-.1 F +6.484(.T)-.65 G 1.484(he structure of these)-6.484 F(entries is:)108 +626.4 Q 2.5(1b)144 650.4 S(yte status \(user number plus 16\))-2.5 E F1 +(F0\211E2)144 662.4 Q F0(are the \214le name and its e)2.5 E(xtension.) +-.15 E 3.171(1b)144 674.4 S .671(yte passw)-3.171 F .671 +(ord mode: bit 7 means passw)-.1 F .672 +(ord required for reading, bit 6 for writing and bit 5 for)-.1 F +(deleting.)144 686.4 Q 3.451(1b)144 698.4 S .951(yte passw)-3.451 F .951 +(ord decode byte: T)-.1 F 3.451(od)-.8 G .951(ecode the passw)-3.451 F +.95(ord, xor this byte with the passw)-.1 F .95(ord bytes in)-.1 F(re) +144 710.4 Q -.15(ve)-.25 G(rse order).15 E 5(.T)-.55 G 2.5(oe)-5.8 G +(ncode a passw)-2.5 E(ord, add its characters to get the decode byte.) +-.1 E 2.5(2r)144 722.4 S(eserv)-2.5 E(ed bytes)-.15 E(CP/M tools)72 768 +Q(February 18, 2012)151.35 E(3)192.2 E 0 Cg EP +%%Page: 4 4 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 174.415(CPM\(5\) File)72 48 R 174.415 +(formats CPM\(5\))2.5 F 2.5(8p)144 84 S(assw)-2.5 E(ord bytes)-.1 E/F1 +10.95/Times-Bold@0 SF(SEE ALSO)72 112.8 Q/F2 10/Times-Italic@0 SF +(mkfs.cpm)108 124.8 Q F0(\(1\),).32 E F2(fsc)2.5 E(k.cpm)-.2 E F0 +(\(1\),).32 E F2(fsed.cpm)2.5 E F0(\(1\),).32 E F2(cpmls)2.5 E F0(\(1\)) +.27 E(CP/M tools)72 768 Q(February 18, 2012)151.35 E(4)192.2 E 0 Cg EP +%%Trailer +end +%%EOF diff --git a/Tools/unix/cpmtools/cpmchattr.1 b/Tools/unix/cpmtools/cpmchattr.1 new file mode 100644 index 00000000..f130d5c2 --- /dev/null +++ b/Tools/unix/cpmtools/cpmchattr.1 @@ -0,0 +1,92 @@ +.TH CPMCHATTR 1 "October 25, 2014" "CP/M tools" "User commands" +.SH NAME \"{{{roff}}}\"{{{ +cpmchattr \- change file attributes on CP/M files +.\"}}} +.SH SYNOPSIS \"{{{ +.ad l +.B cpmchattr +.RB [ \-f +.IR format ] +.I image +.I attrib +.I file-pattern +\&... +.ad b +.\"}}} +.SH DESCRIPTION \"{{{ +\fBCpmchattr\fP changes the file attributes for files on CP/M disks. +.\"}}} +.SH OPTIONS \"{{{ +.IP "\fB\-f\fP \fIformat\fP" +Use the given CP/M disk \fIformat\fP instead of the default format. +.IP "\fB\-T\fP \fIlibdsk-type\fP" +libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images +(requires building cpmtools with support for libdsk). +.IP "\fIattrib\fP" +Set the file attributes as given. +.\"}}} +.SH "FILE ATTRIBUTES" \"{{{ +The file attribute string can contain the characters +1,2,3,4,r,s,a,n and m. +The meanings of these are: +.TP +.B 1-4 +The CP/M "user attributes" F1-F4. CP/M does not assign any +meaning to these attributes, though MP/M does. +.TP +.B r +The file is read-only. This is the same as using +.I cpmchmod(1) +to revoke write permissions. +.TP +.B s +The file is a system file. This attribute can also be set by +.I cpmchmod(1). +.TP +.B a +The file has been backed up. +.TP +.B n +Reset all attributes to zero. So the string "n1r" resets all attributes and +then sets F1 and Read-Only. +.TP +.B m +Attributes after an m are unset rather than set. The string "12m34" sets +atttributes F1 and F2, and unsets F3 and F4. +.\"}}} +.SH "RETURN VALUE" \"{{{ +Upon successful completion, exit code 0 is returned. +.\"}}} +.SH ERRORS \"{{{ +Any errors are indicated by exit code 1. +.\"}}} +.SH ENVIRONMENT \"{{{ +CPMTOOLSFMT Default format +.\"}}} +.SH FILES \"{{{ +${prefix}/share/diskdefs CP/M disk format definitions +.\"}}} +.SH AUTHORS \"{{{ +This program is copyright 1997\(en2012 Michael Haardt + and copyright 2000, 2001, 2011 John Elliott +. +.PP +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +.PP +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +.PP +You should have received a copy of the GNU General Public License along +with this program. If not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.\"}}} +.SH "SEE ALSO" \"{{{ +.IR cpmls (1), +.IR cpmchmod (1), +.IR cpm (5) +.\"}}} diff --git a/Tools/unix/cpmtools/cpmchattr.c b/Tools/unix/cpmtools/cpmchattr.c new file mode 100644 index 00000000..be39130b --- /dev/null +++ b/Tools/unix/cpmtools/cpmchattr.c @@ -0,0 +1,119 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include + +#include "getopt_.h" +#include "cpmfs.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ + +const char cmd[]="cpmchattr"; + +int main(int argc, char *argv[]) /*{{{*/ +{ + /* variables */ /*{{{*/ + const char *err; + const char *image; + const char *format; + const char *devopts=NULL; + int c,i,usage=0,exitcode=0; + struct cpmSuperBlock drive; + struct cpmInode root; + int gargc; + char **gargv; + const char *attrs; + /*}}}*/ + + /* parse options */ /*{{{*/ + if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; + while ((c=getopt(argc,argv,"T:f:h?"))!=EOF) switch(c) + { + case 'T': devopts=optarg; break; + case 'f': format=optarg; break; + case 'h': + case '?': usage=1; break; + } + + if (optind>=(argc-2)) usage=1; + else + { + image=argv[optind++]; + attrs = argv[optind++]; + } + + if (usage) + { + fprintf(stderr,"Usage: %s [-f format] [-T dsktype] image [NMrsa1234] pattern ...\n",cmd); + exit(1); + } + /*}}}*/ + /* open image */ /*{{{*/ + if ((err=Device_open(&drive.dev, image, O_RDWR, devopts))) + { + fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err); + exit(1); + } + if (cpmReadSuper(&drive,&root,format)==-1) + { + fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); + exit(1); + } + /*}}}*/ + cpmglob(optind,argc,argv,&root,&gargc,&gargv); + for (i=0; i and copyright 2000, 2001, 2011 John Elliott +. +.PP +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +.PP +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +.PP +You should have received a copy of the GNU General Public License along +with this program. If not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.\"}}} +.SH "SEE ALSO" \"{{{ +.IR cpmls (1), +.IR chmod (1), +.IR cpm (5) +.\"}}} diff --git a/Tools/unix/cpmtools/cpmchmod.c b/Tools/unix/cpmtools/cpmchmod.c new file mode 100644 index 00000000..ad146965 --- /dev/null +++ b/Tools/unix/cpmtools/cpmchmod.c @@ -0,0 +1,89 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include +#include + +#include "getopt_.h" +#include "cpmfs.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ + +const char cmd[]="cpmchmod"; + +int main(int argc, char *argv[]) /*{{{*/ +{ + /* variables */ /*{{{*/ + const char *err; + const char *image; + const char *format; + const char *devopts=NULL; + int c,i,usage=0,exitcode=0; + struct cpmSuperBlock drive; + struct cpmInode root; + int gargc; + char **gargv; + unsigned int mode; + /*}}}*/ + + /* parse options */ /*{{{*/ + if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; + while ((c=getopt(argc,argv,"T:f:h?"))!=EOF) switch(c) + { + case 'T': devopts=optarg; break; + case 'f': format=optarg; break; + case 'h': + case '?': usage=1; break; + } + + if (optind>=(argc-2)) usage=1; + else + { + image=argv[optind++]; + if (!sscanf(argv[optind++], "%o", &mode)) usage=1; + } + + if (usage) + { + fprintf(stderr,"Usage: %s [-f format] image mode pattern ...\n",cmd); + exit(1); + } + /*}}}*/ + /* open image */ /*{{{*/ + if ((err=Device_open(&drive.dev, image, O_RDWR, devopts))) + { + fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err); + exit(1); + } + if (cpmReadSuper(&drive,&root,format)==-1) + { + fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); + exit(1); + } + /*}}}*/ + cpmglob(optind,argc,argv,&root,&gargc,&gargv); + for (i=0; i. The Windows port is copyright 2000, 2001, 2011 John Elliott +. +.PP +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +.PP +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +.PP +You should have received a copy of the GNU General Public License along +with this program. If not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.\"}}} +.SH "SEE ALSO" \"{{{ +.IR cpmls (1), +.IR cpm (5) +.\"}}} diff --git a/Tools/unix/cpmtools/cpmcp.c b/Tools/unix/cpmtools/cpmcp.c new file mode 100644 index 00000000..561c451f --- /dev/null +++ b/Tools/unix/cpmtools/cpmcp.c @@ -0,0 +1,299 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "getopt_.h" +#include "cpmfs.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ + +const char cmd[]="cpmcp"; +static int text=0; +static int preserve=0; + +/** + * Return the user number. + * @param s CP/M filename in 0[0]:aaaaaaaa.bbb format. + * @returns The user number or -1 for no match. + */ +static int userNumber(const char *s) /*{{{*/ +{ + if (isdigit(*s) && *(s+1)==':') return (*s-'0'); + if (isdigit(*s) && isdigit(*(s+1)) && *(s+2)==':') return (10*(*s-'0')+(*(s+1)-'0')); + return -1; +} +/*}}}*/ + +/** + * Copy one file from CP/M to UNIX. + * @param root The inode for the root directory. + * @param src The CP/M filename in 00aaaaaaaabbb format. + * @param dest The UNIX filename. + * @returns 0 for success, 1 for error. + */ +static int cpmToUnix(const struct cpmInode *root, const char *src, const char *dest) /*{{{*/ +{ + struct cpmInode ino; + int exitcode=0; + + if (cpmNamei(root,src,&ino)==-1) { fprintf(stderr,"%s: can not open `%s': %s\n",cmd,src,boo); exitcode=1; } + else + { + struct cpmFile file; + FILE *ufp; + + cpmOpen(&ino,&file,O_RDONLY); + if ((ufp=fopen(dest,text ? "w" : "wb"))==(FILE*)0) { fprintf(stderr,"%s: can not create %s: %s\n",cmd,dest,strerror(errno)); exitcode=1; } + else + { + int crpending=0; + int ohno=0; + int res; + char buf[4096]; + + while ((res=cpmRead(&file,buf,sizeof(buf)))>0) + { + int j; + + for (j=0; j=argc) usage(); + image=argv[optind++]; + + if (userNumber(argv[optind])>=0) /* cpm -> unix? */ /*{{{*/ + { + int i; + struct stat statbuf; + + for (i=optind; i<(argc-1); ++i) if (userNumber(argv[i])==-1) usage(); + todir=((argc-optind)>2); + if (stat(argv[argc-1],&statbuf)==-1) { if (todir) usage(); } + else if (S_ISDIR(statbuf.st_mode)) todir=1; else if (todir) usage(); + readcpm=1; + } + /*}}}*/ + else if (userNumber(argv[argc-1])>=0) /* unix -> cpm */ /*{{{*/ + { + int i; + + todir=0; + for (i=optind; i<(argc-1); ++i) if (userNumber(argv[i])>=0) usage(); + if ((argc-optind)>2 && *(strchr(argv[argc-1],':')+1)!='\0') usage(); + if (*(strchr(argv[argc-1],':')+1)=='\0') todir=1; + readcpm=0; + } + /*}}}*/ + else usage(); + /*}}}*/ + /* open image file */ /*{{{*/ + if ((err=Device_open(&super.dev,image,readcpm ? O_RDONLY : O_RDWR, devopts))) + { + fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err); + exit(1); + } + if (cpmReadSuper(&super,&root,format)==-1) + { + fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); + exit(1); + } + /*}}}*/ + if (readcpm) /* copy from CP/M to UNIX */ /*{{{*/ + { + int i; + char *last=argv[argc-1]; + + cpmglob(optind,argc-1,argv,&root,&gargc,&gargv); + /* trying to copy multiple files to a file? */ + if (gargc>1 && !todir) usage(); + for (i=0; i=' ' && !((c)&~0x7f) && (c)!='<' && (c)!='>' && (c)!='.' && (c)!=',' && (c)!=';' && (c)!=':' && (c)!='=' && (c)!='?' && (c)!='*' && (c)!= '[' && (c)!=']') +#define EXTENT(low,high) (((low)&0x1f)|(((high)&0x3f)<<5)) +#define EXTENTL(extent) ((extent)&0x1f) +#define EXTENTH(extent) (((extent>>5))&0x3f) + +#endif diff --git a/Tools/unix/cpmtools/cpmfs.c b/Tools/unix/cpmtools/cpmfs.c new file mode 100644 index 00000000..11e7d2e6 --- /dev/null +++ b/Tools/unix/cpmtools/cpmfs.c @@ -0,0 +1,1935 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpmdir.h" +#include "cpmfs.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ +/* #defines */ /*{{{*/ +#undef CPMFS_DEBUG + +/* Number of _used_ bits per int */ + +#define INTBITS ((int)(sizeof(int)*8)) + +/* Convert BCD datestamp digits to binary */ + +#define BCD2BIN(x) ((((x)>>4)&0xf)*10 + ((x)&0xf)) + +#define BIN2BCD(x) (((((x)/10)&0xf)<<4) + (((x)%10)&0xf)) + +/* There are four reserved directory entries: ., .., [passwd] and [label]. +The first two of them refer to the same inode. */ + +#define RESERVED_ENTRIES 4 + +/* CP/M does not support any kind of inodes, so they are simulated. +Inode 0-(maxdir-1) correlate to the lowest extent number (not the first +extent of the file in the directory) of a file. Inode maxdir is the +root directory, inode maxdir+1 is the optional passwd file and inode +maxdir+2 the optional disk label. */ + +#define RESERVED_INODES 3 + +#define PASSWD_RECLEN 24 +/*}}}*/ + +extern char **environ; +const char *boo; +static mode_t s_ifdir=1; +static mode_t s_ifreg=1; + +/* memcpy7 -- Copy string, leaving 8th bit alone */ /*{{{*/ +static void memcpy7(char *dest, const char *src, int count) +{ + while (count--) + { + *dest = ((*dest) & 0x80) | ((*src) & 0x7F); + ++dest; + ++src; + } +} +/*}}}*/ + +/* file name conversions */ +/* splitFilename -- split file name into name and extension */ /*{{{*/ +static int splitFilename(const char *fullname, int type, char *name, char *ext, int *user) +{ + int i,j; + + assert(fullname!=(const char*)0); + assert(name!=(char*)0); + assert(ext!=(char*)0); + assert(user!=(int*)0); + memset(name,' ',8); + memset(ext,' ',3); + if (!isdigit(fullname[0]) || !isdigit(fullname[1])) + { + boo="illegal CP/M filename"; + return -1; + } + *user=10*(fullname[0]-'0')+(fullname[1]-'0'); + fullname+=2; + if ((fullname[0]=='\0') || *user>=((type&CPMFS_HI_USER) ? 32 : 16)) + { + boo="illegal CP/M filename"; + return -1; + } + for (i=0; i<8 && fullname[i] && fullname[i]!='.'; ++i) if (!ISFILECHAR(i,fullname[i])) + { + boo="illegal CP/M filename"; + return -1; + } + else name[i]=toupper(fullname[i]); + if (fullname[i]=='.') + { + ++i; + for (j=0; j<3 && fullname[i]; ++i,++j) if (!ISFILECHAR(1,fullname[i])) + { + boo="illegal CP/M filename"; + return -1; + } + else ext[j]=toupper(fullname[i]); + if (i==1 && j==0) + { + boo="illegal CP/M filename"; + return -1; + } + } + return 0; +} +/*}}}*/ +/* isMatching -- do two file names match? */ /*{{{*/ +static int isMatching(int user1, const char *name1, const char *ext1, int user2, const char *name2, const char *ext2) +{ + int i; + + assert(name1!=(const char*)0); + assert(ext1!=(const char*)0); + assert(name2!=(const char*)0); + assert(ext2!=(const char*)0); + if (user1!=user2) return 0; + for (i=0; i<8; ++i) if ((name1[i]&0x7f)!=(name2[i]&0x7f)) return 0; + for (i=0; i<3; ++i) if ((ext1[i]&0x7f)!=(ext2[i]&0x7f)) return 0; + return 1; +} +/*}}}*/ + +/* time conversions */ +/* cpm2unix_time -- convert CP/M time to UTC */ /*{{{*/ +static time_t cpm2unix_time(int days, int hour, int min) +{ + /* CP/M stores timestamps in local time. We don't know which */ + /* timezone was used and if DST was in effect. Assuming it was */ + /* the current offset from UTC is most sensible, but not perfect. */ + + int year,days_per_year; + static int days_per_month[]={31,0,31,30,31,30,31,31,30,31,30,31}; + char **old_environ; + static char gmt0[]="TZ=GMT0"; + static char *gmt_env[]={ gmt0, (char*)0 }; + struct tm tms; + time_t lt,t; + + time(<); + t=lt; + tms=*localtime(<); + old_environ=environ; + environ=gmt_env; + lt=mktime(&tms); + lt-=t; + tms.tm_sec=0; + tms.tm_min=((min>>4)&0xf)*10+(min&0xf); + tms.tm_hour=((hour>>4)&0xf)*10+(hour&0xf); + tms.tm_mday=1; + tms.tm_mon=0; + tms.tm_year=78; + tms.tm_isdst=-1; + for (;;) + { + year=tms.tm_year+1900; + days_per_year=((year%4)==0 && ((year%100) || (year%400)==0)) ? 366 : 365; + if (days>days_per_year) + { + days-=days_per_year; + ++tms.tm_year; + } + else break; + } + for (;;) + { + days_per_month[1]=(days_per_year==366) ? 29 : 28; + if (days>days_per_month[tms.tm_mon]) + { + days-=days_per_month[tms.tm_mon]; + ++tms.tm_mon; + } + else break; + } + t=mktime(&tms)+(days-1)*24*3600; + environ=old_environ; + t-=lt; + return t; +} +/*}}}*/ +/* unix2cpm_time -- convert UTC to CP/M time */ /*{{{*/ +static void unix2cpm_time(time_t now, int *days, int *hour, int *min) +{ + struct tm *tms; + int i; + + tms=localtime(&now); + *min=((tms->tm_min/10)<<4)|(tms->tm_min%10); + *hour=((tms->tm_hour/10)<<4)|(tms->tm_hour%10); + for (i=1978,*days=0; i<1900+tms->tm_year; ++i) + { + *days+=365; + if (i%4==0 && (i%100!=0 || i%400==0)) ++*days; + } + *days += tms->tm_yday+1; +} +/*}}}*/ +/* ds2unix_time -- convert DS to Unix time */ /*{{{*/ +static time_t ds2unix_time(const struct dsEntry *entry) +{ + struct tm tms; + int yr; + + if (entry->minute==0 && + entry->hour==0 && + entry->day==0 && + entry->month==0 && + entry->year==0) return 0; + + tms.tm_isdst = -1; + tms.tm_sec = 0; + tms.tm_min = BCD2BIN( entry->minute ); + tms.tm_hour = BCD2BIN( entry->hour ); + tms.tm_mday = BCD2BIN( entry->day ); + tms.tm_mon = BCD2BIN( entry->month ) - 1; + + yr = BCD2BIN(entry->year); + if (yr<70) yr+=100; + tms.tm_year = yr; + + return mktime(&tms); +} +/*}}}*/ +/* unix2ds_time -- convert Unix to DS time */ /*{{{*/ +static void unix2ds_time(time_t now, struct dsEntry *entry) +{ + struct tm *tms; + int yr; + + if ( now==0 ) + { + entry->minute=entry->hour=entry->day=entry->month=entry->year = 0; + } + else + { + tms=localtime(&now); + entry->minute = BIN2BCD( tms->tm_min ); + entry->hour = BIN2BCD( tms->tm_hour ); + entry->day = BIN2BCD( tms->tm_mday ); + entry->month = BIN2BCD( tms->tm_mon + 1 ); + + yr = tms->tm_year; + if ( yr>100 ) yr -= 100; + entry->year = BIN2BCD( yr ); + } +} +/*}}}*/ + +/* allocation vector bitmap functions */ +/* alvInit -- init allocation vector */ /*{{{*/ +static void alvInit(const struct cpmSuperBlock *d) +{ + int i,j,offset,block; + + assert(d!=(const struct cpmSuperBlock*)0); + /* clean bitmap */ /*{{{*/ + memset(d->alv,0,d->alvSize*sizeof(int)); + /*}}}*/ + /* mark directory blocks as used */ /*{{{*/ + *d->alv=(1<<((d->maxdir*32+d->blksiz-1)/d->blksiz))-1; + /*}}}*/ + for (i=0; imaxdir; ++i) /* mark file blocks as used */ /*{{{*/ + { + if (d->dir[i].status>=0 && d->dir[i].status<=(d->type&CPMFS_HI_USER ? 31 : 15)) + { +#ifdef CPMFS_DEBUG + fprintf(stderr,"alvInit: allocate extent %d\n",i); +#endif + for (j=0; j<16; ++j) + { + block=(unsigned char)d->dir[i].pointers[j]; + if (d->size>=256) block+=(((unsigned char)d->dir[i].pointers[++j])<<8); + if (block && blocksize) + { +#ifdef CPMFS_DEBUG + fprintf(stderr,"alvInit: allocate block %d\n",block); +#endif + offset=block/INTBITS; + d->alv[offset]|=(1<alvSize; ++i) + { + for (j=0,bits=drive->alv[i]; j=drive->size) + { + boo="device full"; + return -1; + } + drive->alv[i] |= (1<>= 1; + } + } + boo="device full"; + return -1; +} +/*}}}*/ + +/* logical block I/O */ +/* readBlock -- read a (partial) block */ /*{{{*/ +static int readBlock(const struct cpmSuperBlock *d, int blockno, char *buffer, int start, int end) +{ + int sect, track, counter; + + assert(d); + assert(blockno>=0); + assert(buffer); + if (blockno>=d->size) + { + boo="Attempting to access block beyond end of disk"; + return -1; + } + if (end<0) end=d->blksiz/d->secLength-1; + sect=(blockno*(d->blksiz/d->secLength)+ d->sectrk*d->boottrk)%d->sectrk; + track=(blockno*(d->blksiz/d->secLength)+ d->sectrk*d->boottrk)/d->sectrk; + for (counter=0; counter<=end; ++counter) + { + const char *err; + + assert(d->skewtab[sect]>=0); + assert(d->skewtab[sect]sectrk); + if (counter>=start && (err=Device_readSector(&d->dev,track,d->skewtab[sect],buffer+(d->secLength*counter)))) + { + boo=err; + return -1; + } + ++sect; + if (sect>=d->sectrk) + { + sect = 0; + ++track; + } + } + return 0; +} +/*}}}*/ +/* writeBlock -- write a (partial) block */ /*{{{*/ +static int writeBlock(const struct cpmSuperBlock *d, int blockno, const char *buffer, int start, int end) +{ + int sect, track, counter; + + assert(blockno>=0); + assert(blocknosize); + assert(buffer!=(const char*)0); + if (end < 0) end=d->blksiz/d->secLength-1; + sect = (blockno*(d->blksiz/d->secLength))%d->sectrk; + track = (blockno*(d->blksiz/d->secLength))/d->sectrk+d->boottrk; + for (counter = 0; counter<=end; ++counter) + { + const char *err; + + if (counter>=start && (err=Device_writeSector(&d->dev,track,d->skewtab[sect],buffer+(d->secLength*counter)))) + { + boo=err; + return -1; + } + ++sect; + if (sect>=d->sectrk) + { + sect=0; + ++track; + } + } + return 0; +} +/*}}}*/ + +/* directory management */ +/* findFileExtent -- find first/next extent for a file */ /*{{{*/ +static int findFileExtent(const struct cpmSuperBlock *sb, int user, const char *name, const char *ext, int start, int extno) +{ + boo="file already exists"; + for (; startmaxdir; ++start) + { + if + ( + ((unsigned char)sb->dir[start].status)<=(sb->type&CPMFS_HI_USER ? 31 : 15) + && (extno==-1 || (EXTENT(sb->dir[start].extnol,sb->dir[start].extnoh)/sb->extents)==(extno/sb->extents)) + && isMatching(user,name,ext,sb->dir[start].status,sb->dir[start].name,sb->dir[start].ext) + ) return start; + } + boo="file not found"; + return -1; +} +/*}}}*/ +/* findFreeExtent -- find first free extent */ /*{{{*/ +static int findFreeExtent(const struct cpmSuperBlock *drive) +{ + int i; + + for (i=0; imaxdir; ++i) if (drive->dir[i].status==(char)0xe5) return (i); + boo="directory full"; + return -1; +} +/*}}}*/ +/* updateTimeStamps -- convert time stamps to CP/M format */ /*{{{*/ +static void updateTimeStamps(const struct cpmInode *ino, int extent) +{ + struct PhysDirectoryEntry *date; + int i; + int ca_min,ca_hour,ca_days,u_min,u_hour,u_days; + + if (!S_ISREG(ino->mode)) return; +#ifdef CPMFS_DEBUG + fprintf(stderr,"CPMFS: updating time stamps for inode %d (%d)\n",extent,extent&3); +#endif + unix2cpm_time(ino->sb->cnotatime ? ino->ctime : ino->atime,&ca_days,&ca_hour,&ca_min); + unix2cpm_time(ino->mtime,&u_days,&u_hour,&u_min); + if ((ino->sb->type&CPMFS_CPM3_DATES) && (date=ino->sb->dir+(extent|3))->status==0x21) + { + ino->sb->dirtyDirectory=1; + switch (extent&3) + { + case 0: /* first entry */ /*{{{*/ + { + date->name[0]=ca_days&0xff; date->name[1]=ca_days>>8; + date->name[2]=ca_hour; + date->name[3]=ca_min; + date->name[4]=u_days&0xff; date->name[5]=u_days>>8; + date->name[6]=u_hour; + date->name[7]=u_min; + break; + } + /*}}}*/ + case 1: /* second entry */ /*{{{*/ + { + date->ext[2]=ca_days&0xff; date->extnol=ca_days>>8; + date->lrc=ca_hour; + date->extnoh=ca_min; + date->blkcnt=u_days&0xff; date->pointers[0]=u_days>>8; + date->pointers[1]=u_hour; + date->pointers[2]=u_min; + break; + } + /*}}}*/ + case 2: /* third entry */ /*{{{*/ + { + date->pointers[5]=ca_days&0xff; date->pointers[6]=ca_days>>8; + date->pointers[7]=ca_hour; + date->pointers[8]=ca_min; + date->pointers[9]=u_days&0xff; date->pointers[10]=u_days>>8; + date->pointers[11]=u_hour; + date->pointers[12]=u_min; + break; + } + /*}}}*/ + } + } +} +/*}}}*/ +/* updateDsStamps -- set time in datestamper file */ /*{{{*/ +static void updateDsStamps(const struct cpmInode *ino, int extent) +{ + int yr; + struct tm *cpm_time; + struct dsDate *stamp; + + if (!S_ISREG(ino->mode)) return; + if ( !(ino->sb->type&CPMFS_DS_DATES) ) return; + +#ifdef CPMFS_DEBUG + fprintf(stderr,"CPMFS: updating ds stamps for inode %d (%d)\n",extent,extent&3); +#endif + + /* Get datestamp struct */ + stamp = ino->sb->ds+extent; + + unix2ds_time( ino->mtime, &stamp->modify ); + unix2ds_time( ino->ctime, &stamp->create ); + unix2ds_time( ino->atime, &stamp->access ); + + ino->sb->dirtyDs = 1; +} +/*}}}*/ +/* readTimeStamps -- read CP/M time stamp */ /*{{{*/ +static int readTimeStamps(struct cpmInode *i, int lowestExt) +{ + /* variables */ /*{{{*/ + struct PhysDirectoryEntry *date; + int u_days=0,u_hour=0,u_min=0; + int ca_days=0,ca_hour=0,ca_min=0; + int protectMode=0; + /*}}}*/ + + if ( (i->sb->type&CPMFS_CPM3_DATES) && (date=i->sb->dir+(lowestExt|3))->status==0x21 ) + { + switch (lowestExt&3) + { + case 0: /* first entry of the four */ /*{{{*/ + { + ca_days=((unsigned char)date->name[0])+(((unsigned char)date->name[1])<<8); + ca_hour=(unsigned char)date->name[2]; + ca_min=(unsigned char)date->name[3]; + u_days=((unsigned char)date->name[4])+(((unsigned char)date->name[5])<<8); + u_hour=(unsigned char)date->name[6]; + u_min=(unsigned char)date->name[7]; + protectMode=(unsigned char)date->ext[0]; + break; + } + /*}}}*/ + case 1: /* second entry */ /*{{{*/ + { + ca_days=((unsigned char)date->ext[2])+(((unsigned char)date->extnol)<<8); + ca_hour=(unsigned char)date->lrc; + ca_min=(unsigned char)date->extnoh; + u_days=((unsigned char)date->blkcnt)+(((unsigned char)date->pointers[0])<<8); + u_hour=(unsigned char)date->pointers[1]; + u_min=(unsigned char)date->pointers[2]; + protectMode=(unsigned char)date->pointers[3]; + break; + } + /*}}}*/ + case 2: /* third one */ /*{{{*/ + { + ca_days=((unsigned char)date->pointers[5])+(((unsigned char)date->pointers[6])<<8); + ca_hour=(unsigned char)date->pointers[7]; + ca_min=(unsigned char)date->pointers[8]; + u_days=((unsigned char)date->pointers[9])+(((unsigned char)date->pointers[10])<<8); + u_hour=(unsigned char)date->pointers[11]; + u_min=(unsigned char)date->pointers[12]; + protectMode=(unsigned char)date->pointers[13]; + break; + } + /*}}}*/ + } + if (i->sb->cnotatime) + { + i->ctime=cpm2unix_time(ca_days,ca_hour,ca_min); + i->atime=0; + } + else + { + i->ctime=0; + i->atime=cpm2unix_time(ca_days,ca_hour,ca_min); + } + i->mtime=cpm2unix_time(u_days,u_hour,u_min); + } + else + { + i->atime=i->mtime=i->ctime=0; + protectMode=0; + } + + return protectMode; +} +/*}}}*/ +/* readDsStamps -- read datestamper time stamp */ /*{{{*/ +static void readDsStamps(struct cpmInode *i, int lowestExt) +{ + struct dsDate *stamp; + + if ( !(i->sb->type&CPMFS_DS_DATES) ) return; + + /* Get datestamp */ + stamp = i->sb->ds+lowestExt; + + i->mtime = ds2unix_time(&stamp->modify); + i->ctime = ds2unix_time(&stamp->create); + i->atime = ds2unix_time(&stamp->access); +} +/*}}}*/ + +/* match -- match filename against a pattern */ /*{{{*/ +static int recmatch(const char *a, const char *pattern) +{ + int first=1; + + assert(a); + assert(pattern); + while (*pattern) + { + switch (*pattern) + { + case '*': + { + if (*a=='.' && first) return 1; + ++pattern; + while (*a) if (recmatch(a,pattern)) return 1; else ++a; + break; + } + case '?': + { + if (*a) { ++a; ++pattern; } else return 0; + break; + } + default: if (tolower(*a)==tolower(*pattern)) { ++a; ++pattern; } else return 0; + } + first=0; + } + return (*pattern=='\0' && *a=='\0'); +} + +int match(const char *a, const char *pattern) +{ + int user; + char pat[255]; + + assert(a); + assert(pattern); + assert(strlen(pattern)<255); + if (isdigit(*pattern) && *(pattern+1)==':') { user=(*pattern-'0'); pattern+=2; } + else if (isdigit(*pattern) && isdigit(*(pattern+1)) && *(pattern+2)==':') { user=(10*(*pattern-'0')+(*(pattern+1)-'0')); pattern+=3; } + else user=-1; + if (user==-1) sprintf(pat,"??%s",pattern); + else sprintf(pat,"%02d%s",user,pattern); + return recmatch(a,pat); +} + +/*}}}*/ +/* cpmglob -- expand CP/M style wildcards */ /*{{{*/ +void cpmglob(int optin, int argc, char * const argv[], struct cpmInode *root, int *gargc, char ***gargv) +{ + struct cpmFile dir; + int entries,dirsize=0; + struct cpmDirent *dirent=(struct cpmDirent*)0; + int gargcap=0,i,j; + + *gargv=(char**)0; + *gargc=0; + cpmOpendir(root,&dir); + entries=0; + dirsize=8; + dirent=malloc(sizeof(struct cpmDirent)*dirsize); + while (cpmReaddir(&dir,&dirent[entries])) + { + ++entries; + if (entries==dirsize) dirent=realloc(dirent,sizeof(struct cpmDirent)*(dirsize*=2)); + } + for (i=optin; ilibdskGeometry[0] = '\0'; + d->type=0; + fp = open_diskdefs(); + + while (fgets(line,sizeof(line),fp)!=(char*)0) + { + int argc; + char *argv[2]; + char *s; + + /* Allow inline comments preceded by ; or # */ + s = strchr(line, '#'); + if (s) strcpy(s, "\n"); + s = strchr(line, ';'); + if (s) strcpy(s, "\n"); + + for (argc=0; argc<1 && (argv[argc]=strtok(argc ? (char*)0 : line," \t\n")); ++argc); + if ((argv[argc]=strtok((char*)0,"\n"))!=(char*)0) ++argc; + if (insideDef) + { + if (argc==1 && strcmp(argv[0],"end")==0) + { + insideDef=0; + d->size=(d->secLength*d->sectrk*(d->tracks-d->boottrk))/d->blksiz; + if (d->extents==0) d->extents=((d->size>=256 ? 8 : 16)*d->blksiz)/16384; + if (d->extents==0) d->extents=1; + if (found) break; + } + else if (argc==2) + { + if (strcmp(argv[0],"seclen")==0) d->secLength=strtol(argv[1],(char**)0,0); + else if (strcmp(argv[0],"tracks")==0) d->tracks=strtol(argv[1],(char**)0,0); + else if (strcmp(argv[0],"sectrk")==0) d->sectrk=strtol(argv[1],(char**)0,0); + else if (strcmp(argv[0],"blocksize")==0) d->blksiz=strtol(argv[1],(char**)0,0); + else if (strcmp(argv[0],"maxdir")==0) d->maxdir=strtol(argv[1],(char**)0,0); + else if (strcmp(argv[0],"skew")==0) d->skew=strtol(argv[1],(char**)0,0); + else if (strcmp(argv[0],"skewtab")==0) + { + int pass,sectors; + + for (pass=0; pass<2; ++pass) + { + sectors=0; + for (s=argv[1]; *s; ) + { + int phys; + char *end; + + phys=strtol(s,&end,10); + if (pass==1) d->skewtab[sectors]=phys; + if (end==s) + { + fprintf(stderr,"%s: invalid skewtab `%s' at `%s'\n",cmd,argv[1],s); + exit(1); + } + s=end; + ++sectors; + if (*s==',') ++s; + } + if (pass==0) d->skewtab=malloc(sizeof(int)*sectors); + } + } + else if (strcmp(argv[0],"boottrk")==0) d->boottrk=strtol(argv[1],(char**)0,0); + else if (strcmp(argv[0],"offset")==0) + { + off_t val; + unsigned int multiplier; + char *endptr; + + errno=0; + multiplier=1; + val = strtol(argv[1],&endptr,10); + if ((errno==ERANGE && val==LONG_MAX)||(errno!=0 && val<=0)) + { + fprintf(stderr,"%s: invalid offset value \"%s\" - %s\n",cmd,argv[1],strerror(errno)); + exit(1); + } + if (endptr==argv[1]) + { + fprintf(stderr,"%s: offset value \"%s\" is not a number\n",cmd,argv[1]); + exit(1); + } + if (*endptr!='\0') + { + /* Have a unit specifier */ + switch (toupper(*endptr)) + { + case 'K': + multiplier=1024; + break; + case 'M': + multiplier=1024*1024; + break; + case 'T': + if (d->sectrk<0||d->tracks<0||d->secLength<0) + { + fprintf(stderr,"%s: offset must be specified after sectrk, tracks and secLength\n",cmd); + exit(1); + } + multiplier=d->sectrk*d->secLength; + break; + case 'S': + if (d->sectrk<0||d->tracks<0||d->secLength<0) + { + fprintf(stderr,"%s: offset must be specified after sectrk, tracks and secLength\n",cmd); + exit(1); + } + multiplier=d->secLength; + break; + default: + fprintf(stderr,"%s: unknown unit specifier \"%c\"\n",cmd,*endptr); + exit(1); + } + } + if (val*multiplier>INT_MAX) + { + fprintf(stderr,"%s: effective offset is out of range\n",cmd); + exit(1); + } + d->offset=val*multiplier; + } + else if (strcmp(argv[0],"logicalextents")==0) d->extents=strtol(argv[1],(char**)0,0); + else if (strcmp(argv[0],"os")==0) + { + if (strcmp(argv[1],"2.2" )==0) d->type|=CPMFS_DR22; + else if (strcmp(argv[1],"3" )==0) d->type|=CPMFS_DR3; + else if (strcmp(argv[1],"isx" )==0) d->type|=CPMFS_ISX; + else if (strcmp(argv[1],"p2dos")==0) d->type|=CPMFS_P2DOS; + else if (strcmp(argv[1],"zsys" )==0) d->type|=CPMFS_ZSYS; + else + { + fprintf(stderr, "%s: invalid OS type `%s'\n", cmd, argv[1]); + exit(1); + } + } + else if (strcmp(argv[0], "libdsk:format")==0) + { + strncpy(d->libdskGeometry, argv[1], sizeof(d->libdskGeometry) - 1); + d->libdskGeometry[sizeof(d->libdskGeometry) - 1] = 0; + } + } + else if (argc>0 && argv[0][0]!='#' && argv[0][0]!=';') + { + fprintf(stderr,"%s: invalid keyword `%s'\n",cmd,argv[0]); + exit(1); + } + } + else if (argc==2 && strcmp(argv[0],"diskdef")==0) + { + insideDef=1; + d->skew=1; + d->extents=0; + d->type=CPMFS_DR22; + d->skewtab=(int*)0; + d->offset=0; + d->boottrk=d->secLength=d->sectrk=d->tracks=-1; + d->libdskGeometry[0] = 0; + if (strcmp(argv[1],format)==0) found=1; + } + } + fclose(fp); + if (!found) + { + fprintf(stderr,"%s: unknown format %s\n",cmd,format); + exit(1); + } + if (d->boottrk<0) + { + fprintf(stderr, "%s: boottrk parameter invalid or missing from diskdef\n",cmd); + exit(1); + } + if (d->secLength<0) + { + fprintf(stderr, "%s: secLength parameter invalid or missing from diskdef\n",cmd); + exit(1); + } + if (d->sectrk<0) + { + fprintf(stderr, "%s: sectrk parameter invalid or missing from diskdef\n",cmd); + exit(1); + } + if (d->tracks<0) + { + fprintf(stderr, "%s: tracks parameter invalid or missing from diskdef\n",cmd); + exit(1); + } + return 0; +} +/*}}}*/ +/* amsReadSuper -- read super block from amstrad disk */ /*{{{*/ +static int amsReadSuper(struct cpmSuperBlock *d, const char *format) +{ + unsigned char boot_sector[512], *boot_spec; + const char *err; + + Device_setGeometry(&d->dev,512,9,40,0,"pcw180"); + if ((err=Device_readSector(&d->dev, 0, 0, (char *)boot_sector))) + { + fprintf(stderr,"%s: Failed to read Amstrad superblock (%s)\n",cmd,err); + exit(1); + } + boot_spec=(boot_sector[0] == 0 || boot_sector[0] == 3)?boot_sector:(unsigned char*)0; + /* Check for JCE's extension to allow Amstrad and MSDOS superblocks + * in the same sector (for the PCW16) + */ + if + ( + (boot_sector[0] == 0xE9 || boot_sector[0] == 0xEB) + && !memcmp(boot_sector + 0x2B, "CP/M", 4) + && !memcmp(boot_sector + 0x33, "DSK", 3) + && !memcmp(boot_sector + 0x7C, "CP/M", 4) + ) boot_spec = boot_sector + 128; + if (boot_spec==(unsigned char*)0) + { + fprintf(stderr,"%s: Amstrad superblock not present\n",cmd); + exit(1); + } + /* boot_spec[0] = format number: 0 for SS SD, 3 for DS DD + [1] = single/double sided and density flags + [2] = cylinders per side + [3] = sectors per cylinder + [4] = Physical sector shift, 2 => 512 + [5] = Reserved track count + [6] = Block shift + [7] = No. of directory blocks + */ + d->type = 0; + d->type |= CPMFS_DR3; /* Amstrads are CP/M 3 systems */ + d->secLength = 128 << boot_spec[4]; + d->tracks = boot_spec[2]; + if (boot_spec[1] & 3) d->tracks *= 2; + d->sectrk = boot_spec[3]; + d->blksiz = 128 << boot_spec[6]; + d->maxdir = (d->blksiz / 32) * boot_spec[7]; + d->skew = 1; /* Amstrads skew at the controller level */ + d->skewtab = (int*)0; + d->boottrk = boot_spec[5]; + d->offset = 0; + d->size = (d->secLength*d->sectrk*(d->tracks-d->boottrk))/d->blksiz; + d->extents = ((d->size>=256 ? 8 : 16)*d->blksiz)/16384; + d->libdskGeometry[0] = 0; /* LibDsk can recognise an Amstrad superblock + * and autodect */ + + return 0; +} +/*}}}*/ +/* cpmCheckDs -- read all datestamper timestamps */ /*{{{*/ +int cpmCheckDs(struct cpmSuperBlock *sb) +{ + int dsoffset, dsblks, dsrecs, off, i; + unsigned char *buf; + + if (!isMatching(0,"!!!TIME&","DAT",sb->dir->status,sb->dir->name,sb->dir->ext)) return -1; + + /* Offset to ds file in alloc blocks */ + dsoffset=(sb->maxdir*32+(sb->blksiz-1))/sb->blksiz; + + dsrecs=(sb->maxdir+7)/8; + dsblks=(dsrecs*128+(sb->blksiz-1))/sb->blksiz; + + /* Allocate buffer */ + sb->ds=malloc(dsblks*sb->blksiz); + + /* Read ds file in its entirety */ + off=0; + for (i=dsoffset; ids)+off,0,-1)==-1) return -1; + off+=sb->blksiz; + } + + /* Verify checksums */ + buf = (unsigned char *)sb->ds; + for (i=0; ids); + sb->ds = (struct dsDate *)0; + return -1; + } + buf += 128; + } + return 0; +} +/*}}}*/ +/* cpmReadSuper -- get DPB and init in-core data for drive */ /*{{{*/ +int cpmReadSuper(struct cpmSuperBlock *d, struct cpmInode *root, const char *format) +{ + while (s_ifdir && !S_ISDIR(s_ifdir)) s_ifdir<<=1; + assert(s_ifdir); + while (s_ifreg && !S_ISREG(s_ifreg)) s_ifreg<<=1; + assert(s_ifreg); + if (strcmp(format,"amstrad")==0) amsReadSuper(d,format); + else diskdefReadSuper(d,format); + boo = Device_setGeometry(&d->dev,d->secLength,d->sectrk,d->tracks,d->offset,d->libdskGeometry); + if (boo) return -1; + + if (d->skewtab==(int*)0) /* generate skew table */ /*{{{*/ + { + int i,j,k; + + if (( d->skewtab = malloc(d->sectrk*sizeof(int))) == (int*)0) + { + boo=strerror(errno); + return -1; + } + memset(d->skewtab,0,d->sectrk*sizeof(int)); + for (i=j=0; isectrk; ++i,j=(j+d->skew)%d->sectrk) + { + while (1) + { + assert(isectrk); + assert(jsectrk); + for (k=0; kskewtab[k]!=j; ++k); + if (ksectrk; + else break; + } + d->skewtab[i]=j; + } + } + /*}}}*/ + /* initialise allocation vector bitmap */ /*{{{*/ + { + d->alvSize=((d->secLength*d->sectrk*(d->tracks-d->boottrk))/d->blksiz+INTBITS-1)/INTBITS; + if ((d->alv=malloc(d->alvSize*sizeof(int)))==(int*)0) + { + boo=strerror(errno); + return -1; + } + } + /*}}}*/ + /* allocate directory buffer */ /*{{{*/ + assert(sizeof(struct PhysDirectoryEntry)==32); + if ((d->dir=malloc(((d->maxdir*32+d->blksiz-1)/d->blksiz)*d->blksiz))==(struct PhysDirectoryEntry*)0) + { + boo=strerror(errno); + return -1; + } + /*}}}*/ + if (d->dev.opened==0) /* create empty directory in core */ /*{{{*/ + { + memset(d->dir,0xe5,d->maxdir*32); + } + /*}}}*/ + else /* read directory in core */ /*{{{*/ + { + int i,blocks,entry; + + blocks=(d->maxdir*32+d->blksiz-1)/d->blksiz; + entry=0; + for (i=0; idir+entry),0,-1)==-1) return -1; + entry+=(d->blksiz/32); + } + } + /*}}}*/ + alvInit(d); + if (d->type&CPMFS_CPM3_OTHER) /* read additional superblock information */ /*{{{*/ + { + int i; + + /* passwords */ /*{{{*/ + { + int passwords=0; + + for (i=0; imaxdir; ++i) if (d->dir[i].status>=16 && d->dir[i].status<=31) ++passwords; +#ifdef CPMFS_DEBUG + fprintf(stderr,"getformat: found %d passwords\n",passwords); +#endif + if ((d->passwdLength=passwords*PASSWD_RECLEN)) + { + if ((d->passwd=malloc(d->passwdLength))==(char*)0) + { + boo="out of memory"; + return -1; + } + for (i=0,passwords=0; imaxdir; ++i) if (d->dir[i].status>=16 && d->dir[i].status<=31) + { + int j,pb; + char *p=d->passwd+(passwords++*PASSWD_RECLEN); + + p[0]='0'+(d->dir[i].status-16)/10; + p[1]='0'+(d->dir[i].status-16)%10; + for (j=0; j<8; ++j) p[2+j]=d->dir[i].name[j]&0x7f; + p[10]=(d->dir[i].ext[0]&0x7f)==' ' ? ' ' : '.'; + for (j=0; j<3; ++j) p[11+j]=d->dir[i].ext[j]&0x7f; + p[14]=' '; + pb=(unsigned char)d->dir[i].lrc; + for (j=0; j<8; ++j) p[15+j]=((unsigned char)d->dir[i].pointers[7-j])^pb; +#ifdef CPMFS_DEBUG + p[23]='\0'; + fprintf(stderr,"getformat: %s\n",p); +#endif + p[23]='\n'; + } + } + } + /*}}}*/ + /* disc label */ /*{{{*/ + for (i=0; imaxdir; ++i) if (d->dir[i].status==(char)0x20) + { + int j; + + d->cnotatime=d->dir[i].extnol&0x10; + if (d->dir[i].extnol&0x1) + { + d->labelLength=12; + if ((d->label=malloc(d->labelLength))==(char*)0) + { + boo="out of memory"; + return -1; + } + for (j=0; j<8; ++j) d->label[j]=d->dir[i].name[j]&0x7f; + for (j=0; j<3; ++j) d->label[8+j]=d->dir[i].ext[j]&0x7f; + d->label[11]='\n'; + } + else + { + d->labelLength=0; + } + break; + } + if (i==d->maxdir) + { + d->cnotatime=1; + d->labelLength=0; + } + /*}}}*/ + } + /*}}}*/ + else + { + d->passwdLength=0; + d->cnotatime=1; + d->labelLength=0; + } + d->root=root; + d->dirtyDirectory = 0; + root->ino=d->maxdir; + root->sb=d; + root->mode=(s_ifdir|0777); + root->size=0; + root->atime=root->mtime=root->ctime=0; + + d->dirtyDs=0; + if (cpmCheckDs(d)==0) d->type|=CPMFS_DS_DATES; + else d->ds=(struct dsDate*)0; + + return 0; +} +/*}}}*/ +/* syncDs -- write all datestamper timestamps */ /*{{{*/ +static int syncDs(const struct cpmSuperBlock *sb) +{ + if (sb->dirtyDs) + { + int dsoffset, dsblks, dsrecs, off, i; + unsigned char *buf; + + dsrecs=(sb->maxdir+7)/8; + + /* Re-calculate checksums */ + buf = (unsigned char *)sb->ds; + for ( i=0; imaxdir*32+(sb->blksiz-1))/sb->blksiz; + dsblks=(dsrecs*128+(sb->blksiz-1))/sb->blksiz; + + off=0; + for (i=dsoffset; ids))+off,0,-1)==-1) return -1; + off+=sb->blksiz; + } + } + return 0; +} +/*}}}*/ +/* cpmSync -- write directory back */ /*{{{*/ +int cpmSync(struct cpmSuperBlock *sb) +{ + if (sb->dirtyDirectory) + { + int i,blocks,entry; + + blocks=(sb->maxdir*32+sb->blksiz-1)/sb->blksiz; + entry=0; + for (i=0; idir+entry),0,-1)==-1) return -1; + entry+=(sb->blksiz/32); + } + sb->dirtyDirectory=0; + } + if (sb->type&CPMFS_DS_DATES) syncDs(sb); + return 0; +} +/*}}}*/ +/* cpmUmount -- free super block */ /*{{{*/ +void cpmUmount(struct cpmSuperBlock *sb) +{ + cpmSync(sb); + if (sb->type&CPMFS_DS_DATES) free(sb->ds); + free(sb->alv); + free(sb->skewtab); + free(sb->dir); + if (sb->passwdLength) free(sb->passwd); +} +/*}}}*/ + +/* cpmNamei -- map name to inode */ /*{{{*/ +int cpmNamei(const struct cpmInode *dir, const char *filename, struct cpmInode *i) +{ + /* variables */ /*{{{*/ + int user; + char name[8],extension[3]; + int highestExtno,highestExt=-1,lowestExtno,lowestExt=-1; + int protectMode=0; + /*}}}*/ + + if (!S_ISDIR(dir->mode)) + { + boo="No such file"; + return -1; + } + if (strcmp(filename,".")==0 || strcmp(filename,"..")==0) /* root directory */ /*{{{*/ + { + *i=*dir; + return 0; + } + /*}}}*/ + else if (strcmp(filename,"[passwd]")==0 && dir->sb->passwdLength) /* access passwords */ /*{{{*/ + { + i->attr=0; + i->ino=dir->sb->maxdir+1; + i->mode=s_ifreg|0444; + i->sb=dir->sb; + i->atime=i->mtime=i->ctime=0; + i->size=i->sb->passwdLength; + return 0; + } + /*}}}*/ + else if (strcmp(filename,"[label]")==0 && dir->sb->labelLength) /* access label */ /*{{{*/ + { + i->attr=0; + i->ino=dir->sb->maxdir+2; + i->mode=s_ifreg|0444; + i->sb=dir->sb; + i->atime=i->mtime=i->ctime=0; + i->size=i->sb->labelLength; + return 0; + } + /*}}}*/ + if (splitFilename(filename,dir->sb->type,name,extension,&user)==-1) return -1; + /* find highest and lowest extent */ /*{{{*/ + { + int extent; + + i->size=0; + extent=-1; + highestExtno=-1; + lowestExtno=2049; + while ((extent=findFileExtent(dir->sb,user,name,extension,extent+1,-1))!=-1) + { + int extno=EXTENT(dir->sb->dir[extent].extnol,dir->sb->dir[extent].extnoh); + + if (extno>highestExtno) + { + highestExtno=extno; + highestExt=extent; + } + if (extnosize=highestExtno*16384; + if (dir->sb->size<256) for (block=15; block>=0; --block) + { + if (dir->sb->dir[highestExt].pointers[block]) break; + } + else for (block=7; block>=0; --block) + { + if (dir->sb->dir[highestExt].pointers[2*block] || dir->sb->dir[highestExt].pointers[2*block+1]) break; + } + if (dir->sb->dir[highestExt].blkcnt) i->size+=((dir->sb->dir[highestExt].blkcnt&0xff)-1)*128; + if (dir->sb->type & CPMFS_ISX) + { + i->size += (128 - dir->sb->dir[highestExt].lrc); + } + else + { + i->size+=dir->sb->dir[highestExt].lrc ? (dir->sb->dir[highestExt].lrc&0xff) : 128; + } +#ifdef CPMFS_DEBUG + fprintf(stderr,"cpmNamei: size=%ld\n",(long)i->size); +#endif + } + /*}}}*/ + i->ino=lowestExt; + i->mode=s_ifreg; + i->sb=dir->sb; + + /* read timestamps */ /*{{{*/ + protectMode = readTimeStamps(i,lowestExt); + /*}}}*/ + + /* Determine the inode attributes */ + i->attr = 0; + if (dir->sb->dir[lowestExt].name[0]&0x80) i->attr |= CPM_ATTR_F1; + if (dir->sb->dir[lowestExt].name[1]&0x80) i->attr |= CPM_ATTR_F2; + if (dir->sb->dir[lowestExt].name[2]&0x80) i->attr |= CPM_ATTR_F3; + if (dir->sb->dir[lowestExt].name[3]&0x80) i->attr |= CPM_ATTR_F4; + if (dir->sb->dir[lowestExt].ext [0]&0x80) i->attr |= CPM_ATTR_RO; + if (dir->sb->dir[lowestExt].ext [1]&0x80) i->attr |= CPM_ATTR_SYS; + if (dir->sb->dir[lowestExt].ext [2]&0x80) i->attr |= CPM_ATTR_ARCV; + if (protectMode&0x20) i->attr |= CPM_ATTR_PWDEL; + if (protectMode&0x40) i->attr |= CPM_ATTR_PWWRITE; + if (protectMode&0x80) i->attr |= CPM_ATTR_PWREAD; + + if (dir->sb->dir[lowestExt].ext[1]&0x80) i->mode|=01000; + i->mode|=0444; + if (!(dir->sb->dir[lowestExt].ext[0]&0x80)) i->mode|=0222; + if (extension[0]=='C' && extension[1]=='O' && extension[2]=='M') i->mode|=0111; + + readDsStamps(i,lowestExt); + + return 0; +} +/*}}}*/ +/* cpmStatFS -- statfs */ /*{{{*/ +void cpmStatFS(const struct cpmInode *ino, struct cpmStatFS *buf) +{ + int i; + struct cpmSuperBlock *d; + + d=ino->sb; + buf->f_bsize=d->blksiz; + buf->f_blocks=d->size; + buf->f_bfree=0; + buf->f_bused=-(d->maxdir*32+d->blksiz-1)/d->blksiz; + for (i=0; ialvSize; ++i) + { + int temp,j; + + temp = *(d->alv+i); + for (j=0; jsize) + { + if (1&temp) + { +#ifdef CPMFS_DEBUG + fprintf(stderr,"cpmStatFS: block %d allocated\n",(i*INTBITS+j)); +#endif + ++buf->f_bused; + } + else ++buf->f_bfree; + } + temp >>= 1; + } + } + buf->f_bavail=buf->f_bfree; + buf->f_files=d->maxdir; + buf->f_ffree=0; + for (i=0; imaxdir; ++i) + { + if (d->dir[i].status==(char)0xe5) ++buf->f_ffree; + } + buf->f_namelen=11; +} +/*}}}*/ +/* cpmUnlink -- unlink */ /*{{{*/ +int cpmUnlink(const struct cpmInode *dir, const char *fname) +{ + int user; + char name[8],extension[3]; + int extent; + struct cpmSuperBlock *drive; + + if (!S_ISDIR(dir->mode)) + { + boo="No such file"; + return -1; + } + drive=dir->sb; + if (splitFilename(fname,dir->sb->type,name,extension,&user)==-1) return -1; + if ((extent=findFileExtent(drive,user,name,extension,0,-1))==-1) return -1; + drive->dirtyDirectory=1; + drive->dir[extent].status=(char)0xe5; + do + { + drive->dir[extent].status=(char)0xe5; + } while ((extent=findFileExtent(drive,user,name,extension,extent+1,-1))>=0); + alvInit(drive); + return 0; +} +/*}}}*/ +/* cpmRename -- rename */ /*{{{*/ +int cpmRename(const struct cpmInode *dir, const char *old, const char *new) +{ + struct cpmSuperBlock *drive; + int extent; + int olduser; + char oldname[8], oldext[3]; + int newuser; + char newname[8], newext[3]; + + if (!S_ISDIR(dir->mode)) + { + boo="No such file"; + return -1; + } + drive=dir->sb; + if (splitFilename(old,dir->sb->type, oldname, oldext,&olduser)==-1) return -1; + if (splitFilename(new,dir->sb->type, newname, newext,&newuser)==-1) return -1; + if ((extent=findFileExtent(drive,olduser,oldname,oldext,0,-1))==-1) return -1; + if (findFileExtent(drive,newuser,newname, newext,0,-1)!=-1) + { + boo="file already exists"; + return -1; + } + do + { + drive->dirtyDirectory=1; + drive->dir[extent].status=newuser; + memcpy7(drive->dir[extent].name, newname, 8); + memcpy7(drive->dir[extent].ext, newext, 3); + } while ((extent=findFileExtent(drive,olduser,oldname,oldext,extent+1,-1))!=-1); + return 0; +} +/*}}}*/ +/* cpmOpendir -- opendir */ /*{{{*/ +int cpmOpendir(struct cpmInode *dir, struct cpmFile *dirp) +{ + if (!S_ISDIR(dir->mode)) + { + boo="No such file"; + return -1; + } + dirp->ino=dir; + dirp->pos=0; + dirp->mode=O_RDONLY; + return 0; +} +/*}}}*/ +/* cpmReaddir -- readdir */ /*{{{*/ +int cpmReaddir(struct cpmFile *dir, struct cpmDirent *ent) +{ + /* variables */ /*{{{*/ + struct PhysDirectoryEntry *cur=(struct PhysDirectoryEntry*)0; + char buf[2+8+1+3+1]; /* 00foobarxy.zzy\0 */ + char *bufp; + int hasext; + /*}}}*/ + + if (!(S_ISDIR(dir->ino->mode))) /* error: not a directory */ /*{{{*/ + { + boo="not a directory"; + return -1; + } + /*}}}*/ + while (1) + { + int i; + + if (dir->pos==0) /* first entry is . */ /*{{{*/ + { + ent->ino=dir->ino->sb->maxdir; + ent->reclen=1; + strcpy(ent->name,"."); + ent->off=dir->pos; + ++dir->pos; + return 1; + } + /*}}}*/ + else if (dir->pos==1) /* next entry is .. */ /*{{{*/ + { + ent->ino=dir->ino->sb->maxdir; + ent->reclen=2; + strcpy(ent->name,".."); + ent->off=dir->pos; + ++dir->pos; + return 1; + } + /*}}}*/ + else if (dir->pos==2) + { + if (dir->ino->sb->passwdLength) /* next entry is [passwd] */ /*{{{*/ + { + ent->ino=dir->ino->sb->maxdir+1; + ent->reclen=8; + strcpy(ent->name,"[passwd]"); + ent->off=dir->pos; + ++dir->pos; + return 1; + } + /*}}}*/ + } + else if (dir->pos==3) + { + if (dir->ino->sb->labelLength) /* next entry is [label] */ /*{{{*/ + { + ent->ino=dir->ino->sb->maxdir+2; + ent->reclen=7; + strcpy(ent->name,"[label]"); + ent->off=dir->pos; + ++dir->pos; + return 1; + } + /*}}}*/ + } + else if (dir->pos>=RESERVED_ENTRIES && dir->pos<(int)dir->ino->sb->maxdir+RESERVED_ENTRIES) + { + int first=dir->pos-RESERVED_ENTRIES; + + if ((cur=dir->ino->sb->dir+(dir->pos-RESERVED_ENTRIES))->status>=0 && cur->status<=(dir->ino->sb->type&CPMFS_HI_USER ? 31 : 15)) + { + /* determine first extent for the current file */ /*{{{*/ + for (i=0; iino->sb->maxdir; ++i) if (i!=(dir->pos-RESERVED_ENTRIES)) + { + if (isMatching(cur->status,cur->name,cur->ext,dir->ino->sb->dir[i].status,dir->ino->sb->dir[i].name,dir->ino->sb->dir[i].ext) && EXTENT(cur->extnol,cur->extnoh)>EXTENT(dir->ino->sb->dir[i].extnol,dir->ino->sb->dir[i].extnoh)) first=i; + } + /*}}}*/ + if (first==(dir->pos-RESERVED_ENTRIES)) + { + ent->ino=dir->pos-RESERVED_INODES; + /* convert file name to UNIX style */ /*{{{*/ + buf[0]='0'+cur->status/10; + buf[1]='0'+cur->status%10; + for (bufp=buf+2,i=0; i<8 && (cur->name[i]&0x7f)!=' '; ++i) *bufp++=tolower(cur->name[i]&0x7f); + for (hasext=0,i=0; i<3 && (cur->ext[i]&0x7f)!=' '; ++i) + { + if (!hasext) { *bufp++='.'; hasext=1; } + *bufp++=tolower(cur->ext[i]&0x7f); + } + *bufp='\0'; + /*}}}*/ + assert(bufp<=buf+sizeof(buf)); + ent->reclen=bufp-buf; + strcpy(ent->name,buf); + ent->off=dir->pos; + ++dir->pos; + return 1; + } + } + } + else return 0; + ++dir->pos; + } +} +/*}}}*/ +/* cpmStat -- stat */ /*{{{*/ +void cpmStat(const struct cpmInode *ino, struct cpmStat *buf) +{ + buf->ino=ino->ino; + buf->mode=ino->mode; + buf->size=ino->size; + buf->atime=ino->atime; + buf->mtime=ino->mtime; + buf->ctime=ino->ctime; +} +/*}}}*/ +/* cpmOpen -- open */ /*{{{*/ +int cpmOpen(struct cpmInode *ino, struct cpmFile *file, mode_t mode) +{ + if (S_ISREG(ino->mode)) + { + if ((mode&O_WRONLY) && (ino->mode&0222)==0) + { + boo="permission denied"; + return -1; + } + file->pos=0; + file->ino=ino; + file->mode=mode; + return 0; + } + else + { + boo="not a regular file"; + return -1; + } +} +/*}}}*/ +/* cpmRead -- read */ /*{{{*/ +int cpmRead(struct cpmFile *file, char *buf, int count) +{ + int findext=1,findblock=1,extent=-1,block=-1,extentno=-1,got=0,nextblockpos=-1,nextextpos=-1; + int blocksize=file->ino->sb->blksiz; + int extcap; + + extcap=(file->ino->sb->size<256 ? 16 : 8)*blocksize; + if (extcap>16384) extcap=16384*file->ino->sb->extents; + if (file->ino->ino==(ino_t)file->ino->sb->maxdir+1) /* [passwd] */ /*{{{*/ + { + if ((file->pos+count)>file->ino->size) count=file->ino->size-file->pos; + if (count) memcpy(buf,file->ino->sb->passwd+file->pos,count); + file->pos+=count; +#ifdef CPMFS_DEBUG + fprintf(stderr,"cpmRead passwd: read %d bytes, now at position %ld\n",count,(long)file->pos); +#endif + return count; + } + /*}}}*/ + else if (file->ino->ino==(ino_t)file->ino->sb->maxdir+2) /* [label] */ /*{{{*/ + { + if ((file->pos+count)>file->ino->size) count=file->ino->size-file->pos; + if (count) memcpy(buf,file->ino->sb->label+file->pos,count); + file->pos+=count; +#ifdef CPMFS_DEBUG + fprintf(stderr,"cpmRead label: read %d bytes, now at position %ld\n",count,(long)file->pos); +#endif + return count; + } + /*}}}*/ + else while (count>0 && file->posino->size) + { + char buffer[16384]; + + if (findext) + { + extentno=file->pos/16384; + extent=findFileExtent(file->ino->sb,file->ino->sb->dir[file->ino->ino].status,file->ino->sb->dir[file->ino->ino].name,file->ino->sb->dir[file->ino->ino].ext,0,extentno); + nextextpos=(file->pos/extcap)*extcap+extcap; + findext=0; + findblock=1; + } + if (findblock) + { + if (extent!=-1) + { + int start,end,ptr; + + ptr=(file->pos%extcap)/blocksize; + if (file->ino->sb->size>=256) ptr*=2; + block=(unsigned char)file->ino->sb->dir[extent].pointers[ptr]; + if (file->ino->sb->size>=256) block+=((unsigned char)file->ino->sb->dir[extent].pointers[ptr+1])<<8; + if (block==0) + { + memset(buffer,0,blocksize); + } + else + { + start=(file->pos%blocksize)/file->ino->sb->secLength; + end=((file->pos%blocksize+count)>blocksize ? blocksize-1 : (file->pos%blocksize+count-1))/file->ino->sb->secLength; + if (readBlock(file->ino->sb,block,buffer,start,end)==-1) + { + if (got==0) got=-1; + break; + } + } + } + nextblockpos=(file->pos/blocksize)*blocksize+blocksize; + findblock=0; + } + if (file->pospos%blocksize]; + ++file->pos; + ++got; + --count; + } + else if (file->pos==nextextpos) findext=1; else findblock=1; + } +#ifdef CPMFS_DEBUG + fprintf(stderr,"cpmRead: read %d bytes, now at position %ld\n",got,(long)file->pos); +#endif + return got; +} +/*}}}*/ +/* cpmWrite -- write */ /*{{{*/ +int cpmWrite(struct cpmFile *file, const char *buf, int count) +{ + int findext=1,findblock=-1,extent=-1,extentno=-1,got=0,nextblockpos=-1,nextextpos=-1; + int blocksize=file->ino->sb->blksiz; + int extcap=(file->ino->sb->size<256 ? 16 : 8)*blocksize; + int block=-1,start=-1,end=-1,ptr=-1,last=-1; + char buffer[16384]; + + while (count>0) + { + if (findext) /*{{{*/ + { + extentno=file->pos/16384; + extent=findFileExtent(file->ino->sb,file->ino->sb->dir[file->ino->ino].status,file->ino->sb->dir[file->ino->ino].name,file->ino->sb->dir[file->ino->ino].ext,0,extentno); + nextextpos=(file->pos/extcap)*extcap+extcap; + if (extent==-1) + { + if ((extent=findFreeExtent(file->ino->sb))==-1) return (got==0 ? -1 : got); + file->ino->sb->dir[extent]=file->ino->sb->dir[file->ino->ino]; + memset(file->ino->sb->dir[extent].pointers,0,16); + file->ino->sb->dir[extent].extnol=EXTENTL(extentno); + file->ino->sb->dir[extent].extnoh=EXTENTH(extentno); + file->ino->sb->dir[extent].blkcnt=0; + file->ino->sb->dir[extent].lrc=0; + time(&file->ino->ctime); + updateTimeStamps(file->ino,extent); + updateDsStamps(file->ino,extent); + } + findext=0; + findblock=1; + } + /*}}}*/ + if (findblock) /*{{{*/ + { + ptr=(file->pos%extcap)/blocksize; + if (file->ino->sb->size>=256) ptr*=2; + block=(unsigned char)file->ino->sb->dir[extent].pointers[ptr]; + if (file->ino->sb->size>=256) block+=((unsigned char)file->ino->sb->dir[extent].pointers[ptr+1])<<8; + if (block==0) /* allocate new block, set start/end to cover it */ /*{{{*/ + { + if ((block=allocBlock(file->ino->sb))==-1) return (got==0 ? -1 : got); + file->ino->sb->dir[extent].pointers[ptr]=block&0xff; + if (file->ino->sb->size>=256) file->ino->sb->dir[extent].pointers[ptr+1]=(block>>8)&0xff; + start=0; + end=(blocksize-1)/file->ino->sb->secLength; + memset(buffer,0,blocksize); + time(&file->ino->ctime); + updateTimeStamps(file->ino,extent); + updateDsStamps(file->ino,extent); + } + /*}}}*/ + else /* read existing block and set start/end to cover modified parts */ /*{{{*/ + { + start=(file->pos%blocksize)/file->ino->sb->secLength; + end=((file->pos%blocksize+count)>blocksize ? blocksize-1 : (file->pos%blocksize+count-1))/file->ino->sb->secLength; + if (file->pos%file->ino->sb->secLength) + { + if (readBlock(file->ino->sb,block,buffer,start,start)==-1) + { + if (got==0) got=-1; + break; + } + } + if (end!=start && (file->pos+count-1)ino->sb,block,buffer+end*file->ino->sb->secLength,end,end)==-1) + { + if (got==0) got=-1; + break; + } + } + } + /*}}}*/ + nextblockpos=(file->pos/blocksize)*blocksize+blocksize; + findblock=0; + } + /*}}}*/ + /* fill block and write it */ /*{{{*/ + file->ino->sb->dirtyDirectory=1; + while (file->pos!=nextblockpos && count) + { + buffer[file->pos%blocksize]=*buf++; + ++file->pos; + if (file->ino->sizepos) file->ino->size=file->pos; + ++got; + --count; + } + (void)writeBlock(file->ino->sb,block,buffer,start,end); + time(&file->ino->mtime); + if (file->ino->sb->size<256) for (last=15; last>=0; --last) + { + if (file->ino->sb->dir[extent].pointers[last]) + { + break; + } + } + else for (last=14; last>0; last-=2) + { + if (file->ino->sb->dir[extent].pointers[last] || file->ino->sb->dir[extent].pointers[last+1]) + { + last/=2; + break; + } + } + if (last>0) extentno+=(last*blocksize)/extcap; + file->ino->sb->dir[extent].extnol=EXTENTL(extentno); + file->ino->sb->dir[extent].extnoh=EXTENTH(extentno); + file->ino->sb->dir[extent].blkcnt=((file->pos-1)%16384)/128+1; + if (file->ino->sb->type & CPMFS_EXACT_SIZE) + { + file->ino->sb->dir[extent].lrc = (128 - (file->pos%128)) & 0x7F; + } + else + { + file->ino->sb->dir[extent].lrc=file->pos%128; + } + updateTimeStamps(file->ino,extent); + updateDsStamps(file->ino,extent); + /*}}}*/ + if (file->pos==nextextpos) findext=1; + else if (file->pos==nextblockpos) findblock=1; + } + return got; +} +/*}}}*/ +/* cpmClose -- close */ /*{{{*/ +int cpmClose(struct cpmFile *file) +{ + return 0; +} +/*}}}*/ +/* cpmCreat -- creat */ /*{{{*/ +int cpmCreat(struct cpmInode *dir, const char *fname, struct cpmInode *ino, mode_t mode) +{ + int user; + char name[8],extension[3]; + int extent; + struct cpmSuperBlock *drive; + struct PhysDirectoryEntry *ent; + + if (!S_ISDIR(dir->mode)) + { + boo="No such file or directory"; + return -1; + } + if (splitFilename(fname,dir->sb->type,name,extension,&user)==-1) return -1; +#ifdef CPMFS_DEBUG + fprintf(stderr,"cpmCreat: %s -> %d:%-.8s.%-.3s\n",fname,user,name,extension); +#endif + if (findFileExtent(dir->sb,user,name,extension,0,-1)!=-1) return -1; + drive=dir->sb; + if ((extent=findFreeExtent(dir->sb))==-1) return -1; + ent=dir->sb->dir+extent; + drive->dirtyDirectory=1; + memset(ent,0,32); + ent->status=user; + memcpy(ent->name,name,8); + memcpy(ent->ext,extension,3); + ino->ino=extent; + ino->mode=s_ifreg|mode; + ino->size=0; + + time(&ino->atime); + time(&ino->mtime); + time(&ino->ctime); + ino->sb=dir->sb; + updateTimeStamps(ino,extent); + updateDsStamps(ino,extent); + return 0; +} +/*}}}*/ + +/* cpmAttrGet -- get CP/M attributes */ /*{{{*/ +int cpmAttrGet(struct cpmInode *ino, cpm_attr_t *attrib) +{ + *attrib = ino->attr; + return 0; +} +/*}}}*/ +/* cpmAttrSet -- set CP/M attributes */ /*{{{*/ +int cpmAttrSet(struct cpmInode *ino, cpm_attr_t attrib) +{ + struct cpmSuperBlock *drive; + int extent; + int user; + char name[8], extension[3]; + + memset(name, 0, sizeof(name)); + memset(extension, 0, sizeof(extension)); + drive = ino->sb; + extent = ino->ino; + + drive->dirtyDirectory=1; + /* Strip off existing attribute bits */ + memcpy7(name, drive->dir[extent].name, 8); + memcpy7(extension, drive->dir[extent].ext, 3); + user = drive->dir[extent].status; + + /* And set new ones */ + if (attrib & CPM_ATTR_F1) name[0] |= 0x80; + if (attrib & CPM_ATTR_F2) name[1] |= 0x80; + if (attrib & CPM_ATTR_F3) name[2] |= 0x80; + if (attrib & CPM_ATTR_F4) name[3] |= 0x80; + if (attrib & CPM_ATTR_RO) extension[0] |= 0x80; + if (attrib & CPM_ATTR_SYS) extension[1] |= 0x80; + if (attrib & CPM_ATTR_ARCV) extension[2] |= 0x80; + + do + { + memcpy(drive->dir[extent].name, name, 8); + memcpy(drive->dir[extent].ext, extension, 3); + } while ((extent=findFileExtent(drive, user,name,extension,extent+1,-1))!=-1); + + /* Update the stored (inode) copies of the file attributes and mode */ + ino->attr=attrib; + if (attrib&CPM_ATTR_RO) ino->mode&=~(S_IWUSR|S_IWGRP|S_IWOTH); + else ino->mode|=(S_IWUSR|S_IWGRP|S_IWOTH); + + return 0; +} +/*}}}*/ +/* cpmChmod -- set CP/M r/o & sys */ /*{{{*/ +int cpmChmod(struct cpmInode *ino, mode_t mode) +{ + /* Convert the chmod() into a chattr() call that affects RO */ + int newatt = ino->attr & ~CPM_ATTR_RO; + + if (!(mode & (S_IWUSR|S_IWGRP|S_IWOTH))) newatt |= CPM_ATTR_RO; + return cpmAttrSet(ino, newatt); +} +/*}}}*/ +/* cpmUtime -- set timestamps */ /*{{{*/ +void cpmUtime(struct cpmInode *ino, struct utimbuf *times) +{ + ino->atime = times->actime; + ino->mtime = times->modtime; + time(&ino->ctime); + updateTimeStamps(ino,ino->ino); + updateDsStamps(ino,ino->ino); +} +/*}}}*/ diff --git a/Tools/unix/cpmtools/cpmfs.h b/Tools/unix/cpmtools/cpmfs.h new file mode 100644 index 00000000..06126c86 --- /dev/null +++ b/Tools/unix/cpmtools/cpmfs.h @@ -0,0 +1,206 @@ +#ifndef CPMFS_H +#define CPMFS_H + +#include +#include +#include + +#ifdef _WIN32 + #include + #include + /* To make it compile on NT: extracts from Linux 2.0 * + * and */ + #define __S_IFMT 0170000 /* These bits determine file type. */ + #define __S_IFDIR 0040000 /* Directory. */ + #define __S_IFREG 0100000 /* Regular file. */ + #define __S_IWUSR 0000200 /* Writable for user. */ + #define __S_IWGRP 0000200 /* Writable for group. */ + #define __S_IWOTH 0000200 /* Writable for others. */ + + #define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask)) + #define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask)) + /* These bits are defined in Borland C++ 5 but not in MS Visual C++ */ + #ifndef S_ISDIR + # define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) + #endif + #ifndef S_ISREG + # define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) + #endif + #ifndef S_IWUSR + #define S_IWUSR __S_IWUSR + #endif + #ifndef S_IWGRP + #define S_IWGRP __S_IWGRP + #endif + #ifndef S_IWOTH + #define S_IWOTH __S_IWOTH + #endif + + #include /* For open(), lseek() etc. */ + #ifndef HAVE_MODE_T + typedef int mode_t; + #endif +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#include "device.h" + +/* CP/M file attributes */ +#define CPM_ATTR_F1 1 +#define CPM_ATTR_F2 2 +#define CPM_ATTR_F3 4 +#define CPM_ATTR_F4 8 +/* F5-F8 are banned in CP/M 2 & 3, F7 is used by ZSDOS */ +#define CPM_ATTR_RO 256 /* Read-only */ +#define CPM_ATTR_SYS 512 /* System */ +#define CPM_ATTR_ARCV 1024 /* Archive */ +#define CPM_ATTR_PWDEL 2048 /* Password required to delete */ +#define CPM_ATTR_PWWRITE 4096 /* Password required to write */ +#define CPM_ATTR_PWREAD 8192 /* Password required to read */ + +typedef int cpm_attr_t; + +struct cpmInode +{ + ino_t ino; + mode_t mode; + off_t size; + cpm_attr_t attr; + time_t atime; + time_t mtime; + time_t ctime; + struct cpmSuperBlock *sb; +}; + +struct cpmFile +{ + mode_t mode; + off_t pos; + struct cpmInode *ino; +}; + +struct cpmDirent +{ + ino_t ino; + off_t off; + size_t reclen; + char name[2+8+1+3+1]; /* 00foobarxy.zzy\0 */ +}; + +struct cpmStat +{ + ino_t ino; + mode_t mode; + off_t size; + time_t atime; + time_t mtime; + time_t ctime; +}; + +#define CPMFS_HI_USER (0x1<<0) /* has user numbers up to 31 */ +#define CPMFS_CPM3_DATES (0x1<<1) /* has CP/M+ style time stamps */ +#define CPMFS_CPM3_OTHER (0x1<<2) /* has passwords and disc label */ +#define CPMFS_DS_DATES (0x1<<3) /* has datestamper timestamps */ +#define CPMFS_EXACT_SIZE (0x1<<4) /* has reverse exact file size */ + +#define CPMFS_DR22 0 +#define CPMFS_P2DOS (CPMFS_CPM3_DATES|CPMFS_HI_USER) +#define CPMFS_DR3 (CPMFS_CPM3_DATES|CPMFS_CPM3_OTHER|CPMFS_HI_USER) +#define CPMFS_ISX (CPMFS_EXACT_SIZE) +#define CPMFS_ZSYS (CPMFS_HI_USER) + +struct dsEntry +{ + char year; + char month; + char day; + char hour; + char minute; +}; + +struct dsDate +{ + struct dsEntry create; + struct dsEntry access; + struct dsEntry modify; + char checksum; +}; + +struct cpmSuperBlock +{ + struct Device dev; + + int secLength; + int tracks; + int sectrk; + int blksiz; + int maxdir; + int skew; + int boottrk; + off_t offset; + int type; + int size; + int extents; /* logical extents per physical extent */ + struct PhysDirectoryEntry *dir; + int alvSize; + int *alv; + int *skewtab; + int cnotatime; + char *label; + size_t labelLength; + char *passwd; + size_t passwdLength; + struct cpmInode *root; + int dirtyDirectory; + struct dsDate *ds; + int dirtyDs; + char libdskGeometry[256]; +}; + +struct cpmStatFS +{ + long f_bsize; + long f_blocks; + long f_bfree; + long f_bused; + long f_bavail; + long f_files; + long f_ffree; + long f_namelen; +}; + +extern const char cmd[]; +extern const char *boo; + +int match(const char *a, const char *pattern); +void cpmglob(int opti, int argc, char * const argv[], struct cpmInode *root, int *gargc, char ***gargv); + +int cpmReadSuper(struct cpmSuperBlock *drive, struct cpmInode *root, const char *format); +int cpmNamei(const struct cpmInode *dir, const char *filename, struct cpmInode *i); +void cpmStatFS(const struct cpmInode *ino, struct cpmStatFS *buf); +int cpmUnlink(const struct cpmInode *dir, const char *fname); +int cpmRename(const struct cpmInode *dir, const char *old, const char *newname); +int cpmOpendir(struct cpmInode *dir, struct cpmFile *dirp); +int cpmReaddir(struct cpmFile *dir, struct cpmDirent *ent); +void cpmStat(const struct cpmInode *ino, struct cpmStat *buf); +int cpmAttrGet(struct cpmInode *ino, cpm_attr_t *attrib); +int cpmAttrSet(struct cpmInode *ino, cpm_attr_t attrib); +int cpmChmod(struct cpmInode *ino, mode_t mode); +int cpmOpen(struct cpmInode *ino, struct cpmFile *file, mode_t mode); +int cpmRead(struct cpmFile *file, char *buf, int count); +int cpmWrite(struct cpmFile *file, const char *buf, int count); +int cpmClose(struct cpmFile *file); +int cpmCreat(struct cpmInode *dir, const char *fname, struct cpmInode *ino, mode_t mode); +void cpmUtime(struct cpmInode *ino, struct utimbuf *times); +int cpmSync(struct cpmSuperBlock *sb); +void cpmUmount(struct cpmSuperBlock *sb); +int cpmCheckDs(struct cpmSuperBlock *sb); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/Tools/unix/cpmtools/cpmls.1 b/Tools/unix/cpmtools/cpmls.1 new file mode 100644 index 00000000..91326400 --- /dev/null +++ b/Tools/unix/cpmtools/cpmls.1 @@ -0,0 +1,75 @@ +.TH CPMLS 1 "October 25, 2014" "CP/M tools" "User commands" +.SH NAME \"{{{roff}}}\"{{{ +cpmls \- list sorted contents of directory +.\"}}} +.SH SYNOPSIS \"{{{ +.ad l +.B cpmls +.RB [ \-f +.IR format ] +.RB [ \-T +.IR libdsk-type ] +.RB [ \-d | \-D | \-F | \-A | \-l [ \-c ][ \-i ]] +.I image +.RI [ file-pattern "...]" +.ad b +.\"}}} +.SH DESCRIPTION \"{{{ +\fBCpmls\fP lists the sorted contents of the directory. +.\"}}} +.SH OPTIONS \"{{{ +.IP "\fB\-f\fP \fIformat\fP" +Use the given CP/M disk \fIformat\fP instead of the default format. +.IP "\fB\-T\fP \fIlibdsk-type\fP" +libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images +(requires building cpmtools with support for libdsk). +.IP \fB\-d\fP +Old CP/M 2.2 dir output. +.IP \fB\-D\fP +P2DOS 2.3 ddir-like output. +.IP \fB\-F\fp +CP/M 3.x dir output. +.IP \fB\-A\fp +E2fs lsattr-like output. +.IP \fB\-l\fP +Long UNIX-style directory listing including size, time stamp and user number. +.IP \fB\-c\fP +Output the creation time, not the modification time. +.IP \fB\-i\fP +Print index number of each file. +.\"}}} +.SH "RETURN VALUE" \"{{{ +Upon successful completion, exit code 0 is returned. +.\"}}} +.SH ERRORS \"{{{ +Any errors are indicated by exit code 1. +.\"}}} +.SH ENVIRONMENT \"{{{ +CPMTOOLSFMT Default format +.\"}}} +.SH FILES \"{{{ +${prefix}/share/diskdefs CP/M disk format definitions +.\"}}} +.SH AUTHORS \"{{{ +This program is copyright 1997\(en2012 Michael Haardt +. The Windows port is copyright 2000, 2001, 2011 John Elliott +. +.PP +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +.PP +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +.PP +You should have received a copy of the GNU General Public License along +with this program. If not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.\"}}} +.SH "SEE ALSO" \"{{{ +.IR cpmcp (1), +.IR cpm (5) +.\"}}} diff --git a/Tools/unix/cpmtools/cpmls.c b/Tools/unix/cpmtools/cpmls.c new file mode 100644 index 00000000..3721d2e0 --- /dev/null +++ b/Tools/unix/cpmtools/cpmls.c @@ -0,0 +1,400 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "getopt_.h" +#include "cpmfs.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ + +/* variables */ /*{{{*/ +static const char * const month[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; +/*}}}*/ + +/* namecmp -- compare two entries */ /*{{{*/ +static int namecmp(const void *a, const void *b) +{ + if (**((const char * const *)a)=='[') return -1; + return strcmp(*((const char * const *)a),*((const char * const *)b)); +} +/*}}}*/ +/* olddir -- old style output */ /*{{{*/ +static void olddir(char **dirent, int entries) +{ + int i,j,k,l,user,announce; + + announce=0; + for (user=0; user<32; ++user) + { + for (i=l=0; itm_mday,month[tmp->tm_mon],tmp->tm_year+1900,tmp->tm_hour,tmp->tm_min); + } + else if (statbuf.ctime) printf(" "); + if (statbuf.ctime) + { + tmp=localtime(&statbuf.ctime); + printf(" %02d-%s-%04d %02d:%02d",tmp->tm_mday,month[tmp->tm_mon],tmp->tm_year+1900,tmp->tm_hour,tmp->tm_min); + } + putchar('\n'); + ++l; + } + } + if (announce==2) announce=1; + } + printf("%5.1d Files occupying %6.1ldK",l,(buf.f_bused*buf.f_bsize)/1024); + printf(", %7.1ldK Free.\n",(buf.f_bfree*buf.f_bsize)/1024); + } + else printf("No files found\n"); +} +/*}}}*/ +/* old3dir -- old CP/M Plus style long output */ /*{{{*/ +static void old3dir(char **dirent, int entries, struct cpmInode *ino) +{ + struct cpmStatFS buf; + struct cpmStat statbuf; + struct cpmInode file; + + if (entries) + { + int i,j,k,l,announce,user, attrib; + int totalBytes=0,totalRecs=0; + + qsort(dirent,entries,sizeof(char*),namecmp); + cpmStatFS(ino,&buf); + announce=1; + for (l=0,user=0; user<32; ++user) + { + for (i=0; itm_mon+1,tmp->tm_mday,tmp->tm_year%100,tmp->tm_hour,tmp->tm_min); + } + else if (statbuf.ctime) printf(" "); + if (statbuf.ctime) + { + tmp=localtime(&statbuf.ctime); + printf("%02d/%02d/%02d %02d:%02d",tmp->tm_mon+1,tmp->tm_mday,tmp->tm_year%100,tmp->tm_hour,tmp->tm_min); + } + putchar('\n'); + ++l; + } + } + if (announce==2) announce=1; + } + printf("\nTotal Bytes = %6.1dk ",(totalBytes+1023)/1024); + printf("Total Records = %7.1d ",totalRecs); + printf("Files Found = %4.1d\n",l); + printf("Total 1k Blocks = %6.1ld ",(buf.f_bused*buf.f_bsize)/1024); + printf("Used/Max Dir Entries For Drive A: %4.1ld/%4.1ld\n",buf.f_files-buf.f_ffree,buf.f_files); + } + else printf("No files found\n"); +} +/*}}}*/ +/* ls -- UNIX style output */ /*{{{*/ +static void ls(char **dirent, int entries, struct cpmInode *ino, int l, int c, int iflag) +{ + int i,user,announce,any; + time_t now; + struct cpmStat statbuf; + struct cpmInode file; + + time(&now); + qsort(dirent,entries,sizeof(char*),namecmp); + announce=0; + any=0; + for (user=0; user<32; ++user) + { + announce=0; + for (i=0; itm_mon],tmp->tm_mday); + if ((c ? statbuf.ctime : statbuf.mtime)<(now-182*24*3600)) printf("%04d ",tmp->tm_year+1900); + else printf("%02d:%02d ",tmp->tm_hour,tmp->tm_min); + } + printf("%s\n",dirent[i]+2); + } + } + } +} +/*}}}*/ +/* lsattr -- output something like e2fs lsattr */ /*{{{*/ +static void lsattr(char **dirent, int entries, struct cpmInode *ino) +{ + int i,user,announce,any; + struct cpmStat statbuf; + struct cpmInode file; + cpm_attr_t attrib; + + qsort(dirent,entries,sizeof(char*),namecmp); + announce=0; + any=0; + for (user=0; user<32; ++user) + { + announce=0; + for (i=0; i. The Windows port is copyright 2000, 2001, 2011 John Elliott +. +.PP +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +.PP +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +.PP +You should have received a copy of the GNU General Public License along +with this program. If not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.\"}}} +.SH "SEE ALSO" \"{{{ +.IR cpmls (1), +.IR cpm (5) +.\"}}} diff --git a/Tools/unix/cpmtools/cpmrm.c b/Tools/unix/cpmtools/cpmrm.c new file mode 100644 index 00000000..65430da6 --- /dev/null +++ b/Tools/unix/cpmtools/cpmrm.c @@ -0,0 +1,77 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include +#include + +#include "getopt_.h" +#include "cpmfs.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ + +const char cmd[]="cpmrm"; + +int main(int argc, char *argv[]) /*{{{*/ +{ + /* variables */ /*{{{*/ + const char *err; + const char *image; + const char *format; + const char *devopts=NULL; + int c,i,usage=0,exitcode=0; + struct cpmSuperBlock drive; + struct cpmInode root; + int gargc; + char **gargv; + /*}}}*/ + + /* parse options */ /*{{{*/ + if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; + while ((c=getopt(argc,argv,"T:f:h?"))!=EOF) switch(c) + { + case 'T': devopts=optarg; break; + case 'f': format=optarg; break; + case 'h': + case '?': usage=1; break; + } + + if (optind>=(argc-1)) usage=1; + else image=argv[optind++]; + + if (usage) + { + fprintf(stderr,"Usage: %s [-f format] [-T dsktype] image pattern ...\n",cmd); + exit(1); + } + /*}}}*/ + /* open image */ /*{{{*/ + if ((err=Device_open(&drive.dev, image, O_RDWR, devopts))) + { + fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err); + exit(1); + } + if (cpmReadSuper(&drive,&root,format)==-1) + { + fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); + exit(1); + } + /*}}}*/ + cpmglob(optind,argc,argv,&root,&gargc,&gargv); + for (i=0; i +#include +#include +#include + +#include "device.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ + +static const char *lookupFormat(DSK_GEOMETRY *geom, const char *name) +{ + dsk_format_t fmt = FMT_180K; + const char *fname; + + while (dg_stdformat(NULL, fmt, &fname, NULL) == DSK_ERR_OK) + { + if (!strcmp(name, fname)) + { + dg_stdformat(geom, fmt, &fname, NULL); + return NULL; + } + ++fmt; + } + return "Unrecognised LibDsk geometry specification"; +} + +/* Device_open -- Open an image file */ /*{{{*/ +const char *Device_open(struct Device *this, const char *filename, int mode, const char *deviceOpts) +{ + char *format; + char driverName[80]; + const char *boo; + dsk_err_t e; + + /* Assume driver name & format name both fit in 80 characters, rather than + * malloccing the exact size */ + if (deviceOpts == NULL) + { + e = dsk_open(&this->dev, filename, NULL, NULL); + format = NULL; + } + else + { + strncpy(driverName, deviceOpts, 79); + driverName[79] = 0; + format = strchr(driverName, ','); + if (format) + { + *format = 0; + ++format; + } + e = dsk_open(&this->dev, filename, driverName, NULL); + } + this->opened = 0; + if (e) return dsk_strerror(e); + this->opened = 1; + if (format) + { + boo = lookupFormat(&this->geom, format); + if (boo) return boo; + } + else + { + dsk_getgeom(this->dev, &this->geom); + } + return NULL; +} +/*}}}*/ +/* Device_setGeometry -- Set disk geometry */ /*{{{*/ +const char *Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks, off_t offset, const char *libdskGeometry) +{ + char *boo; + + this->secLength=secLength; + this->sectrk=sectrk; + this->tracks=tracks; + /* Must be an even multiple of sector size */ + assert(offset%secLength==0); + this->offset=offset; + /* If a geometry is named in diskdefs, use it */ + if (libdskGeometry && libdskGeometry[0]) + { + return lookupFormat(&this->geom, libdskGeometry); + } + + this->geom.dg_secsize = secLength; + this->geom.dg_sectors = sectrk; + /* Did the autoprobe guess right about the number of sectors & cylinders? */ + if (this->geom.dg_cylinders * this->geom.dg_heads == tracks) return NULL; + /* Otherwise we guess: <= 43 tracks: single-sided. Else double. This + * fails for 80-track single-sided if there are any such beasts */ + if (tracks <= 43) + { + this->geom.dg_cylinders = tracks; + this->geom.dg_heads = 1; + } + else + { + this->geom.dg_cylinders = tracks/2; + this->geom.dg_heads = 2; + } + return NULL; +} +/*}}}*/ +/* Device_close -- Close an image file */ /*{{{*/ +const char *Device_close(struct Device *this) +{ + dsk_err_t e; + this->opened=0; + e = dsk_close(&this->dev); + return (e?dsk_strerror(e):(const char*)0); +} +/*}}}*/ +/* Device_readSector -- read a physical sector */ /*{{{*/ +const char *Device_readSector(const struct Device *this, int track, int sector, char *buf) +{ + dsk_err_t e; + e = dsk_lread(this->dev, &this->geom, buf, (track * this->sectrk) + sector + this->offset/this->secLength); + return (e?dsk_strerror(e):(const char*)0); +} +/*}}}*/ +/* Device_writeSector -- write physical sector */ /*{{{*/ +const char *Device_writeSector(const struct Device *this, int track, int sector, const char *buf) +{ + dsk_err_t e; + e = dsk_lwrite(this->dev, &this->geom, buf, (track * this->sectrk) + sector + this->offset/this->secLength); + return (e?dsk_strerror(e):(const char*)0); +} +/*}}}*/ diff --git a/Tools/unix/cpmtools/device_posix.c b/Tools/unix/cpmtools/device_posix.c new file mode 100644 index 00000000..5a28dcdc --- /dev/null +++ b/Tools/unix/cpmtools/device_posix.c @@ -0,0 +1,82 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include +#include + +#include "device.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ + +/* Device_open -- Open an image file */ /*{{{*/ +const char *Device_open(struct Device *this, const char *filename, int mode, const char *deviceOpts) +{ + this->fd=open(filename,mode); + this->opened=(this->fd==-1?0:1); + return ((this->fd==-1)?strerror(errno):(const char*)0); +} +/*}}}*/ +/* Device_setGeometry -- Set disk geometry */ /*{{{*/ +const char *Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks, off_t offset, const char *libdskGeometry) +{ + this->secLength=secLength; + this->sectrk=sectrk; + this->tracks=tracks; + this->offset=offset; + return NULL; +} +/*}}}*/ +/* Device_close -- Close an image file */ /*{{{*/ +const char *Device_close(struct Device *this) +{ + this->opened=0; + return ((close(this->fd)==-1)?strerror(errno):(const char*)0); +} +/*}}}*/ +/* Device_readSector -- read a physical sector */ /*{{{*/ +const char *Device_readSector(const struct Device *this, int track, int sector, char *buf) +{ + int res; + + assert(this); + assert(sector>=0); + assert(sectorsectrk); + assert(track>=0); + assert(tracktracks); + assert(buf); + if (lseek(this->fd,(off_t)(((sector+track*this->sectrk)*this->secLength)+this->offset),SEEK_SET)==-1) + { + return strerror(errno); + } + if ((res=read(this->fd, buf, this->secLength)) != this->secLength) + { + if (res==-1) + { + return strerror(errno); + } + else memset(buf+res,0,this->secLength-res); /* hit end of disk image */ + } + return (const char*)0; +} +/*}}}*/ +/* Device_writeSector -- write physical sector */ /*{{{*/ +const char *Device_writeSector(const struct Device *this, int track, int sector, const char *buf) +{ + assert(sector>=0); + assert(sectorsectrk); + assert(track>=0); + assert(tracktracks); + if (lseek(this->fd,(off_t)(((sector+track*this->sectrk)*this->secLength)+this->offset),SEEK_SET)==-1) + { + return strerror(errno); + } + if (write(this->fd, buf, this->secLength) == this->secLength) return (const char*)0; + return strerror(errno); +} +/*}}}*/ diff --git a/Tools/unix/cpmtools/device_win32.c b/Tools/unix/cpmtools/device_win32.c new file mode 100644 index 00000000..18294432 --- /dev/null +++ b/Tools/unix/cpmtools/device_win32.c @@ -0,0 +1,670 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include + +#include "cpmdir.h" +#include "cpmfs.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ +/* types */ /*{{{*/ +#define PHYSICAL_SECTOR_1 1 /* First physical sector */ + +/* Use the INT13 interface rather than INT25/INT26. This appears to + * improve performance, but is less well tested. */ +#define USE_INT13 + +/* Windows 95 disk I/O functions - based on Stan Mitchell's DISKDUMP.C */ +#define VWIN32_DIOC_DOS_IOCTL 1 /* DOS ioctl calls 4400h-4411h */ +#define VWIN32_DIOC_DOS_INT25 2 /* absolute disk read, DOS int 25h */ +#define VWIN32_DIOC_DOS_INT26 3 /* absolute disk write, DOS int 26h */ +#define VWIN32_DIOC_DOS_INT13 4 /* BIOS INT13 functions */ + +typedef struct _DIOC_REGISTERS { + DWORD reg_EBX; + DWORD reg_EDX; + DWORD reg_ECX; + DWORD reg_EAX; + DWORD reg_EDI; + DWORD reg_ESI; + DWORD reg_Flags; + } + DIOC_REGISTERS, *PDIOC_REGISTERS; + +#define LEVEL0_LOCK 0 +#define LEVEL1_LOCK 1 +#define LEVEL2_LOCK 2 +#define LEVEL3_LOCK 3 +#define LEVEL1_LOCK_MAX_PERMISSION 0x0001 + +#define DRIVE_IS_REMOTE 0x1000 +#define DRIVE_IS_SUBST 0x8000 + +/********************************************************* + **** Note: all MS-DOS data structures must be packed **** + **** on a one-byte boundary. **** + *********************************************************/ +#pragma pack(1) + +typedef struct _DISKIO { + DWORD diStartSector; /* sector number to start at */ + WORD diSectors; /* number of sectors */ + DWORD diBuffer; /* address of buffer */ + } + DISKIO, *PDISKIO; + +typedef struct MID { + WORD midInfoLevel; /* information level, must be 0 */ + DWORD midSerialNum; /* serial number for the medium */ + char midVolLabel[11]; /* volume label for the medium */ + char midFileSysType[8]; /* type of file system as 8-byte ASCII */ + } + MID, *PMID; + +typedef struct driveparams { /* Disk geometry */ + BYTE special; + BYTE devicetype; + WORD deviceattrs; + WORD cylinders; + BYTE mediatype; + /* BPB starts here */ + WORD bytespersector; + BYTE sectorspercluster; + WORD reservedsectors; + BYTE numberofFATs; + WORD rootdirsize; + WORD totalsectors; + BYTE mediaid; + WORD sectorsperfat; + WORD sectorspertrack; + WORD heads; + DWORD hiddensectors; + DWORD bigtotalsectors; + BYTE reserved[6]; + /* BPB ends here */ + WORD sectorcount; + WORD sectortable[80]; + } DRIVEPARAMS, *PDRIVEPARAMS; +/*}}}*/ + +static char *strwin32error(void) /*{{{*/ +{ + static char buffer[1024]; + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + (LPTSTR)buffer, + 1023, NULL); + return buffer; +} +/*}}}*/ +static BOOL LockVolume( HANDLE hDisk ) /*{{{*/ +{ + DWORD ReturnedByteCount; + + return DeviceIoControl( hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, + 0, &ReturnedByteCount, NULL ); +} +/*}}}*/ +static BOOL UnlockVolume( HANDLE hDisk ) /*{{{*/ +{ + DWORD ReturnedByteCount; + + return DeviceIoControl( hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, + 0, &ReturnedByteCount, NULL ); +} +/*}}}*/ +static BOOL DismountVolume( HANDLE hDisk ) /*{{{*/ +{ + DWORD ReturnedByteCount; + + return DeviceIoControl( hDisk, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, + 0, &ReturnedByteCount, NULL ); +} +/*}}}*/ +static int GetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/ + { + DIOC_REGISTERS reg; + BOOL bResult; + DWORD cb; + + reg.reg_EAX = 0x440d; /* IOCTL for block device */ + reg.reg_EBX = volume; /* one-based drive number */ + reg.reg_ECX = 0x0860; /* Get Device params */ + reg.reg_EDX = (DWORD)pParam; + reg.reg_Flags = 1; /* preset the carry flag */ + + bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); + + if ( !bResult || (reg.reg_Flags & 1) ) + return (reg.reg_EAX & 0xffff); + + return 0; + } +/*}}}*/ +static int SetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/ + { + DIOC_REGISTERS reg; + BOOL bResult; + DWORD cb; + + reg.reg_EAX = 0x440d; /* IOCTL for block device */ + reg.reg_EBX = volume; /* one-based drive number */ + reg.reg_ECX = 0x0840; /* Set Device params */ + reg.reg_EDX = (DWORD)pParam; + reg.reg_Flags = 1; /* preset the carry flag */ + + bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); + + if ( !bResult || (reg.reg_Flags & 1) ) + return (reg.reg_EAX & 0xffff); + + return 0; + } +/*}}}*/ +static int GetMediaID( HANDLE hVWin32Device, int volume, MID* pMid ) /*{{{*/ + { + DIOC_REGISTERS reg; + BOOL bResult; + DWORD cb; + + reg.reg_EAX = 0x440d; /* IOCTL for block device */ + reg.reg_EBX = volume; /* one-based drive number */ + reg.reg_ECX = 0x0866; /* Get Media ID */ + reg.reg_EDX = (DWORD)pMid; + reg.reg_Flags = 1; /* preset the carry flag */ + + bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); + + if ( !bResult || (reg.reg_Flags & 1) ) + return (reg.reg_EAX & 0xffff); + + return 0; + } +/*}}}*/ +static int VolumeCheck(HANDLE hVWin32Device, int volume, WORD* flags ) /*{{{*/ +{ + DIOC_REGISTERS reg; + BOOL bResult; + DWORD cb; + + reg.reg_EAX = 0x4409; /* Is Drive Remote */ + reg.reg_EBX = volume; /* one-based drive number */ + reg.reg_Flags = 1; /* preset the carry flag */ + + bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); + + if ( !bResult || (reg.reg_Flags & 1) ) + return (reg.reg_EAX & 0xffff); + + *flags = (WORD)(reg.reg_EDX & 0xffff); + return 0; +} +/*}}}*/ +static int LockLogicalVolume(HANDLE hVWin32Device, int volume, int lock_level, int permissions) /*{{{*/ +{ + DIOC_REGISTERS reg; + BOOL bResult; + DWORD cb; + + reg.reg_EAX = 0x440d; /* generic IOCTL */ + reg.reg_ECX = 0x084a; /* lock logical volume */ + reg.reg_EBX = volume | (lock_level << 8); + reg.reg_EDX = permissions; + reg.reg_Flags = 1; /* preset the carry flag */ + + bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); + + if ( !bResult || (reg.reg_Flags & 1) ) + return (reg.reg_EAX & 0xffff); + + return 0; +} +/*}}}*/ +static int UnlockLogicalVolume( HANDLE hVWin32Device, int volume ) /*{{{*/ +{ + DIOC_REGISTERS reg; + BOOL bResult; + DWORD cb; + + reg.reg_EAX = 0x440d; + reg.reg_ECX = 0x086a; /* lock logical volume */ + reg.reg_EBX = volume; + reg.reg_Flags = 1; /* preset the carry flag */ + + bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); + + if ( !bResult || (reg.reg_Flags & 1) ) return -1; + return 0; +} +/*}}}*/ +static int w32mode(int mode) /*{{{*/ +{ + switch(mode) + { + case O_RDONLY: return GENERIC_READ; + case O_WRONLY: return GENERIC_WRITE; + } + return GENERIC_READ | GENERIC_WRITE; +} +/*}}}*/ + +/* Device_open -- Open an image file */ /*{{{*/ +const char *Device_open(struct Device *sb, const char *filename, int mode, const char *deviceOpts) +{ + /* Windows 95/NT: floppy drives using handles */ + if (strlen(filename) == 2 && filename[1] == ':') /* Drive name */ + { + char vname[20]; + DWORD dwVers; + + sb->fd = -1; + dwVers = GetVersion(); + + if (dwVers & 0x80000000L) /* Win32s (3.1) or Win32c (Win95) */ + { + int lock, driveno, res, permissions; + unsigned short drive_flags; + MID media; + + vname[0] = toupper(filename[0]); + driveno = vname[0] - 'A' + 1; /* 1=A: 2=B: */ + sb->drvtype = CPMDRV_WIN95; + sb->hdisk = CreateFile( "\\\\.\\vwin32", + 0, + 0, + NULL, + 0, + FILE_FLAG_DELETE_ON_CLOSE, + NULL ); + if (!sb->hdisk) + { + return "Failed to open VWIN32 driver."; + } + if (VolumeCheck(sb->hdisk, driveno, &drive_flags)) + { + CloseHandle(sb->hdisk); + return "Invalid drive"; + } + res = GetMediaID( sb->hdisk, driveno, &media ); + if ( res ) + { + const char *lboo = NULL; + + if ( res == ERROR_INVALID_FUNCTION && + (drive_flags & DRIVE_IS_REMOTE )) + lboo = "Network drive"; + else if (res == ERROR_ACCESS_DENIED) lboo = "Access denied"; + /* nb: It's perfectly legitimate for GetMediaID() to fail; most CP/M */ + /* CP/M disks won't have a media ID. */ + + if (lboo != NULL) + { + CloseHandle(sb->hdisk); + return lboo; + } + } + if (!res && + (!memcmp( media.midFileSysType, "CDROM", 5 ) || + !memcmp( media.midFileSysType, "CD001", 5 ) || + !memcmp( media.midFileSysType, "CDAUDIO", 5 ))) + { + CloseHandle(sb->hdisk); + return "CD-ROM drive"; + } + if (w32mode(mode) & GENERIC_WRITE) + { + lock = LEVEL0_LOCK; /* Exclusive access */ + permissions = 0; + } + else + { + lock = LEVEL1_LOCK; /* Allow other processes access */ + permissions = LEVEL1_LOCK_MAX_PERMISSION; + } + if (LockLogicalVolume( sb->hdisk, driveno, lock, permissions)) + { + CloseHandle(sb->hdisk); + return "Could not acquire a lock on the drive."; + } + + sb->fd = driveno; /* 1=A: 2=B: etc - we will need this later */ + + } + else + { + sprintf(vname, "\\\\.\\%s", filename); + sb->drvtype = CPMDRV_WINNT; + sb->hdisk = CreateFile(vname, /* Name */ + w32mode(mode), /* Access mode */ + FILE_SHARE_READ|FILE_SHARE_WRITE, /*Sharing*/ + NULL, /* Security attributes */ + OPEN_EXISTING, /* See MSDN */ + 0, /* Flags & attributes */ + NULL); /* Template file */ + + if (sb->hdisk != INVALID_HANDLE_VALUE) + { + sb->fd = 1; /* Arbitrary value >0 */ + if (LockVolume(sb->hdisk) == FALSE) /* Lock drive */ + { + char *lboo = strwin32error(); + CloseHandle(sb->hdisk); + sb->fd = -1; + return lboo; + } + } + else return strwin32error(); + } + sb->opened = 1; + return NULL; + } + + /* Not a floppy. Treat it as a normal file */ + + mode |= O_BINARY; + sb->fd = open(filename, mode); + if (sb->fd == -1) return strerror(errno); + sb->drvtype = CPMDRV_FILE; + sb->opened = 1; + return NULL; +} +/*}}}*/ +/* Device_setGeometry -- Set disk geometry */ /*{{{*/ +const char * Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks, off_t offset, const char *libdskGeometry) +{ + int n; + + this->secLength=secLength; + this->sectrk=sectrk; + this->tracks=tracks; + // Bill Buckels - add this->offset + this->offset=offset; + + + // Bill Buckels - not sure what to do here + if (this->drvtype == CPMDRV_WIN95) + { + DRIVEPARAMS drvp; + memset(&drvp, 0, sizeof(drvp)); + if (GetDriveParams( this->hdisk, this->fd, &drvp )) return "GetDriveParams failed"; + + drvp.bytespersector = secLength; + drvp.sectorspertrack = sectrk; + drvp.totalsectors = sectrk * tracks; + +/* Guess the cylinder/head configuration from the track count. This will + * get single-sided 80-track discs wrong, but it's that or double-sided + * 40-track (or add cylinder/head counts to diskdefs) + */ + if (tracks < 44) + { + drvp.cylinders = tracks; + drvp.heads = 1; + } + else + { + drvp.cylinders = tracks / 2; + drvp.heads = 2; + } + +/* Set up "reasonable" values for the other members */ + + drvp.sectorspercluster = 1024 / secLength; + drvp.reservedsectors = 1; + drvp.numberofFATs = 2; + drvp.sectorcount = sectrk; + drvp.rootdirsize = 64; + drvp.mediaid = 0xF0; + drvp.hiddensectors = 0; + drvp.sectorsperfat = 3; + for (n = 0; n < sectrk; n++) + { + drvp.sectortable[n*2] = n + PHYSICAL_SECTOR_1; /* Physical sector numbers */ + drvp.sectortable[n*2+1] = secLength; + } + drvp.special = 6; +/* We have not set: + + drvp.mediatype + drvp.devicetype + drvp.deviceattrs + + which should have been read correctly by GetDriveParams(). + */ + SetDriveParams( this->hdisk, this->fd, &drvp ); + } + return NULL; +} +/*}}}*/ +/* Device_close -- Close an image file */ /*{{{*/ +const char *Device_close(struct Device *sb) +{ + sb->opened = 0; + switch(sb->drvtype) + { + case CPMDRV_WIN95: + UnlockLogicalVolume(sb->hdisk, sb->fd ); + if (!CloseHandle( sb->hdisk )) return strwin32error(); + return NULL; + + case CPMDRV_WINNT: + DismountVolume(sb->hdisk); + UnlockVolume(sb->hdisk); + if (!CloseHandle(sb->hdisk)) return strwin32error(); + return NULL; + } + if (close(sb->fd)) return strerror(errno); + return NULL; +} +/*}}}*/ +/* Device_readSector -- read a physical sector */ /*{{{*/ +const char *Device_readSector(const struct Device *drive, int track, int sector, char *buf) +{ + int res; + off_t offset; + + assert(sector>=0); + assert(sectorsectrk); + assert(track>=0); + assert(tracktracks); + + offset = ((sector+track*drive->sectrk)*drive->secLength); + + if (drive->drvtype == CPMDRV_WINNT) + { + LPVOID iobuffer; + DWORD bytesread; + + // Bill Buckels - add drive->offset + if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE) + { + return strwin32error(); + } + iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE); + if (!iobuffer) + { + return strwin32error(); + } + res = ReadFile(drive->hdisk, iobuffer, drive->secLength, &bytesread, NULL); + if (!res) + { + char *lboo = strwin32error(); + VirtualFree(iobuffer, drive->secLength, MEM_RELEASE); + return lboo; + } + + memcpy(buf, iobuffer, drive->secLength); + VirtualFree(iobuffer, drive->secLength, MEM_RELEASE); + + if (bytesread < (unsigned)drive->secLength) + { + memset(buf + bytesread, 0, drive->secLength - bytesread); + } + return NULL; + } + + // Bill Buckels - not sure what to do here + if (drive->drvtype == CPMDRV_WIN95) + { + DIOC_REGISTERS reg; + BOOL bResult; + DWORD cb; + +#ifdef USE_INT13 + int cyl, head; + + if (drive->tracks < 44) { cyl = track; head = 0; } + else { cyl = track/2; head = track & 1; } + + reg.reg_EAX = 0x0201; /* Read 1 sector */ + reg.reg_EBX = (DWORD)buf; + reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1); + reg.reg_EDX = (head << 8) | (drive->fd - 1); + reg.reg_Flags = 1; /* preset the carry flag */ + bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); +#else + DISKIO di; + + reg.reg_EAX = drive->fd - 1; /* zero-based volume number */ + reg.reg_EBX = (DWORD)&di; + reg.reg_ECX = 0xffff; /* use DISKIO structure */ + reg.reg_Flags = 1; /* preset the carry flag */ + di.diStartSector = sector+track*drive->sectrk; + di.diSectors = 1; + di.diBuffer = (DWORD)buf; + bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT25, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); + +#endif + if ( !bResult || (reg.reg_Flags & 1) ) + { + if (GetLastError()) return strwin32error(); + return "Unknown read error."; + } + return 0; + } + + // Bill Buckels - add drive->offset + if (lseek(drive->fd,offset+drive->offset,SEEK_SET)==-1) + { + return strerror(errno); + } + if ((res=read(drive->fd, buf, drive->secLength)) != drive->secLength) + { + if (res==-1) + { + return strerror(errno); + } + else memset(buf+res,0,drive->secLength-res); /* hit end of disk image */ + } + return NULL; +} +/*}}}*/ +/* Device_writeSector -- write physical sector */ /*{{{*/ +const char *Device_writeSector(const struct Device *drive, int track, int sector, const char *buf) +{ + off_t offset; + int res; + + assert(sector>=0); + assert(sectorsectrk); + assert(track>=0); + assert(tracktracks); + + offset = ((sector+track*drive->sectrk)*drive->secLength); + + if (drive->drvtype == CPMDRV_WINNT) + { + LPVOID iobuffer; + DWORD byteswritten; + + // Bill Buckels - add drive->offset + if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE) + { + return strwin32error(); + } + iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE); + if (!iobuffer) + { + return strwin32error(); + } + memcpy(iobuffer, buf, drive->secLength); + res = WriteFile(drive->hdisk, iobuffer, drive->secLength, &byteswritten, NULL); + if (!res || (byteswritten < (unsigned)drive->secLength)) + { + char *lboo = strwin32error(); + VirtualFree(iobuffer, drive->secLength, MEM_RELEASE); + return lboo; + } + + VirtualFree(iobuffer, drive->secLength, MEM_RELEASE); + return NULL; + } + + // Bill Buckels - not sure what to do here + if (drive->drvtype == CPMDRV_WIN95) + { + DIOC_REGISTERS reg; + BOOL bResult; + DWORD cb; + +#ifdef USE_INT13 + int cyl, head; + + if (drive->tracks < 44) { cyl = track; head = 0; } + else { cyl = track/2; head = track & 1; } + + reg.reg_EAX = 0x0301; /* Write 1 sector */ + reg.reg_EBX = (DWORD)buf; + reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1); + reg.reg_EDX = (head << 8) | (drive->fd - 1); + reg.reg_Flags = 1; /* preset the carry flag */ + bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); +#else + DISKIO di; + + reg.reg_EAX = drive->fd - 1; /* zero-based volume number */ + reg.reg_EBX = (DWORD)&di; + reg.reg_ECX = 0xffff; /* use DISKIO structure */ + reg.reg_Flags = 1; /* preset the carry flag */ + di.diStartSector = sector+track*drive->sectrk; + di.diSectors = 1; + di.diBuffer = (DWORD)buf; + bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT26, + ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); +#endif + + if ( !bResult || (reg.reg_Flags & 1) ) + { + if (GetLastError()) return strwin32error(); + return "Unknown write error."; + } + return NULL; + } + + // Bill Buckels - add drive->offset + if (lseek(drive->fd,offset+drive->offset, SEEK_SET)==-1) + { + return strerror(errno); + } + if (write(drive->fd, buf, drive->secLength) == drive->secLength) return NULL; + return strerror(errno); +} +/*}}}*/ diff --git a/Tools/unix/cpmtools/diskdefs b/Tools/unix/cpmtools/diskdefs new file mode 100644 index 00000000..630317a0 --- /dev/null +++ b/Tools/unix/cpmtools/diskdefs @@ -0,0 +1,1396 @@ +diskdef ibm-3740 + seclen 128 + tracks 77 + sectrk 26 + blocksize 1024 + maxdir 64 + skew 6 + boottrk 2 + os 2.2 +end + +diskdef 4mb-hd + seclen 128 + tracks 1024 + sectrk 32 + blocksize 2048 + maxdir 256 + skew 1 + boottrk 0 + os p2dos +end + +diskdef pcw + seclen 512 + tracks 40 + sectrk 9 + blocksize 1024 + maxdir 64 + skew 1 + boottrk 1 + os 3 + libdsk:format pcw180 +end + +diskdef pc1.2m + seclen 512 + tracks 80 + # this format uses 15 sectors per track, but 30 per cylinder + sectrk 30 + blocksize 4096 + maxdir 256 + skew 1 + boottrk 0 + os 3 +end + +# CP/M 86 on 1.44MB floppies +diskdef cpm86-144feat + seclen 512 + tracks 160 + sectrk 18 + blocksize 4096 + maxdir 256 + skew 1 + boottrk 2 + os 3 + libdsk:format ibm1440 +end + +# CP/M 86 on 720KB floppies +diskdef cpm86-720 + seclen 512 + tracks 160 + sectrk 9 + blocksize 2048 + maxdir 256 + skew 1 + boottrk 2 + os 3 +end + +diskdef cf2dd + seclen 512 + tracks 160 + sectrk 9 + blocksize 2048 + maxdir 256 + skew 1 + boottrk 1 + os 3 + libdsk:format pcw720 +end + +#amstrad: values are read from super block (special name hardcoded) + +# Royal alphatronic +# setfdprm /dev/fd1 dd ssize=256 cyl=40 sect=16 head=2 +diskdef alpha + seclen 256 + tracks 40 + sectrk 32 + blocksize 2048 + maxdir 128 + skew 1 + boottrk 2 + os 2.2 +end + +# Apple II CP/M skew o Apple II DOS 3.3 skew +diskdef apple-do + seclen 256 + tracks 35 + sectrk 16 + blocksize 1024 + maxdir 64 + skewtab 0,6,12,3,9,15,14,5,11,2,8,7,13,4,10,1 + boottrk 3 + os 2.2 +end + +# Apple II CP/M skew o Apple II PRODOS skew +diskdef apple-po + seclen 256 + tracks 35 + sectrk 16 + blocksize 1024 + maxdir 64 + skewtab 0,9,3,12,6,15,1,10,4,13,7,8,2,11,5,14 + boottrk 3 + os 2.2 +end + +# MYZ80 hard drive (only works with libdsk, because it has a 256-byte header) +diskdef myz80 + seclen 1024 + tracks 64 + sectrk 128 + blocksize 4096 + maxdir 1024 + skew 1 + boottrk 0 + os 3 + libdsk:format pcw720 +end + +# Despite being Amstrad formats, CPC System and CPC Data don't have an Amstrad +# superblock. You'll need to use libdsk to access them because the Linux +# and Windows kernel drivers won't touch them. +diskdef cpcsys + seclen 512 + tracks 40 + sectrk 9 + blocksize 1024 + maxdir 64 + skew 1 + boottrk 2 + os 3 + libdsk:format cpcsys +end +diskdef cpcdata + seclen 512 + tracks 40 + sectrk 9 + blocksize 1024 + maxdir 64 + skew 1 + boottrk 0 + os 3 + libdsk:format cpcdata +end + +# after being read in with no sector skew. +diskdef nigdos + seclen 512 + # NigDos double sided disk format, 42 tracks * 2 sides + tracks 84 + sectrk 10 + blocksize 2048 + maxdir 128 + skew 1 + boottrk 0 + # this format wastes half of the directory entry + logicalextents 1 + os 3 +end + +diskdef epsqx10 + seclen 512 + tracks 40 + sectrk 20 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 2.2 +end + +diskdef ibm-8ss + seclen 512 + tracks 40 + sectrk 8 + blocksize 1024 + maxdir 64 + skew 0 + boottrk 1 + os 2.2 +end + +diskdef ibm-8ds + seclen 512 + tracks 40 + sectrk 8 + blocksize 1024 + maxdir 64 + skew 0 + boottrk 1 + os 2.2 +end + +diskdef electroglas + seclen 512 + tracks 80 + sectrk 10 + blocksize 2048 + maxdir 256 + skew 0 + boottrk 1 + os 3 +end + +# IBM CP/M-86 +# setfdprm /dev/fd1 sect=8 dtr=1 hd ssize=512 tpi=48 head=1 +diskdef ibmpc-514ss + seclen 512 + tracks 40 + sectrk 8 + blocksize 1024 + maxdir 64 + skew 1 + boottrk 1 + os 2.2 + libdsk:format ibm160 +end + +# IBM CP/M-86 +# setfdprm /dev/fd1 sect=8 dtr=1 hd ssize=512 tpi=48 +diskdef ibmpc-514ds + seclen 512 + tracks 80 + sectrk 8 + blocksize 2048 + maxdir 64 + skew 0 + boottrk 2 + os 2.2 + libdsk:format ibm320 +end + +diskdef p112 + seclen 512 + tracks 160 + sectrk 18 + blocksize 2048 + maxdir 256 + skew 1 + boottrk 2 + os 3 +end + +diskdef p112-old + seclen 512 + tracks 160 + sectrk 18 + blocksize 2048 + maxdir 128 + skew 1 + boottrk 1 + os 3 +end + +diskdef gide-cfa + seclen 512 + tracks 1000 + sectrk 16 + blocksize 4096 + maxdir 1024 + skew 0 + boottrk 2 + os 3 +end + +diskdef gide-cfb + seclen 512 + tracks 1000 + sectrk 16 + blocksize 4096 + maxdir 1024 + skew 0 + boottrk 0 +# Start of second partition + offset 1000trk + os 3 +end + +# AT&T/Olivetti Word Processor +diskdef attwp + seclen 256 + tracks 80 + sectrk 32 + blocksize 2048 + maxdir 128 + boottrk 1 + logicalextents 1 + skewtab 0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15,16,18,20,22,24,26,28,30,17,19,21,23,25,27,29,31 + os 2.2 +end + +# setfdprm /dev/fd0 zerobased SS DD ssize=512 cyl=40 sect=10 head=1 +# Kaypro II +diskdef kpii + seclen 512 + tracks 40 + sectrk 10 + blocksize 1024 + maxdir 64 + skew 0 + boottrk 1 + os 2.2 +end + +# setfdprm /dev/fd0 zerobased DS DD ssize=512 cyl=40 sect=10 head=2 +# Kayro IV +diskdef kpiv + seclen 512 + tracks 80 + sectrk 10 + blocksize 2048 + maxdir 64 + skew 0 + boottrk 1 + os 2.2 +end + +# setfdprm /dev/fd0 dd sect=10 +diskdef interak + seclen 512 + tracks 80 + sectrk 20 + blocksize 4096 + maxdir 256 + skew 1 + boottrk 2 + os 2.2 +end + +# Timex FDD3000 3" +diskdef fdd3000 + seclen 256 + tracks 40 + sectrk 16 + blocksize 1024 + maxdir 128 + boottrk 4 + os 2.2 + skew 7 +end + +# Timex FDD3000 3" +diskdef fdd3000_2 + seclen 256 + tracks 40 + sectrk 16 + blocksize 1024 + maxdir 128 + boottrk 2 + os 2.2 + skew 5 +end + +# Robotron 1715 +diskdef 1715 + seclen 1024 + tracks 40 + sectrk 5 + blocksize 1024 + maxdir 64 + skew 0 + boottrk 3 + os 2.2 +end + +# Robotron 1715 with SCP3 +diskdef 17153 + seclen 1024 + tracks 160 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 4 + os 3 +end + +#DDR +diskdef scp624 + seclen 256 + tracks 160 + sectrk 16 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 2.2 +end + +diskdef scp640 + seclen 256 + tracks 160 + sectrk 16 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 0 + os 2.2 +end + +diskdef scp780 + seclen 1024 + tracks 160 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 2.2 +end + +diskdef scp800 + seclen 1024 + tracks 160 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 0 + os 2.2 +end + +diskdef z9001 + seclen 1024 + tracks 160 + sectrk 5 + blocksize 2048 + maxdir 192 + skew 0 + boottrk 0 + os 2.2 +end + +# Visual Technology Visual 1050 computer +diskdef v1050 + seclen 512 + tracks 80 + sectrk 10 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 3 +end + +# Microbee 40 track 5.25" disks +diskdef microbee40 + seclen 512 + tracks 80 + sectrk 10 + blocksize 2048 + maxdir 128 + skewtab 1,4,7,0,3,6,9,2,5,8 + boottrk 2 + os 2.2 +end + +diskdef dreamdisk40 + seclen 512 + tracks 80 + sectrk 10 + blocksize 2048 + maxdir 128 + skewtab 1,4,7,0,3,6,9,2,5,8 + boottrk 2 + os 2.2 +end + +diskdef dreamdisk80 + seclen 512 + tracks 160 + sectrk 10 + blocksize 2048 + maxdir 256 + skewtab 1,4,7,0,3,6,9,2,5,8 + boottrk 2 + os 2.2 +end + +diskdef rc759 + seclen 1024 + tracks 154 + sectrk 8 + blocksize 2048 + maxdir 512 + boottrk 4 + os 3 +end + +# ICL Comet: 40 track 5.25" Single Sided +# +diskdef icl-comet-525ss + seclen 512 + tracks 40 + sectrk 10 + blocksize 1024 + maxdir 64 + skewtab 0,3,6,9,2,5,8,1,4,7 + boottrk 2 + os 2.2 +end + +diskdef z80pack-hd + seclen 128 + tracks 255 + sectrk 128 + blocksize 2048 + maxdir 1024 + skew 0 + boottrk 0 + os 2.2 +end + +diskdef z80pack-hdb + seclen 128 + tracks 256 + sectrk 16384 + blocksize 16384 + maxdir 8192 + skew 0 + boottrk 0 + os 2.2 +end + +# Bondwell 12 and 14 disk images in IMD raw binary format +diskdef bw12 + seclen 256 + tracks 40 + sectrk 18 + blocksize 2048 + maxdir 64 + skew 1 + boottrk 2 + os 2.2 +end + +diskdef bw14 + seclen 256 + tracks 80 + sectrk 18 + blocksize 2048 + maxdir 64 + skew 1 + boottrk 2 + os 2.2 +end + +############################ +# north star cp/m disks +############################ + +#North Star floppy 360K + +diskdef nsfd + seclen 512 + tracks 70 + sectrk 10 + blocksize 2048 + maxdir 64 + skew 5 + boottrk 2 + os 2.2 +end + + +#North Star CP/M Virtual-Disk file on Hard Disk +# prepared with allocation factor = 4 +# as in "CR CPMB 4000 4" +# needs to be copied off hard drive before you can +# work on it with cpmtools + +diskdef nshd4 + seclen 512 + tracks 512 + sectrk 16 + blocksize 4096 + maxdir 256 + skew 0 + boottrk 0 + os 2.2 +end + + +#North Star CP/M Virtual-Disk file on Hard Disk +# prepared with allocation factor = 8 +# as in "CR CPMB 6000 8" +# needs to be copied off hard drive before you can +# work on it with cpmtools + +diskdef nshd8 + seclen 512 + tracks 1024 + sectrk 16 + blocksize 8192 + maxdir 256 + skew 0 + boottrk 0 + os 2.2 +end + +# Northstar Micro-Disk System MDS-A-D 175 +diskdef mdsad175 + seclen 512 + blocksize 1024 + tracks 35 + maxdir 64 + boottrk 2 + sectrk 10 + skew 5 + os 2.2 +end + + +# Northstar Micro-Disk System MDS-A-D 350 +diskdef mdsad350 + seclen 512 + blocksize 2048 + tracks 70 + maxdir 64 + boottrk 2 + sectrk 10 + skew 5 + os 2.2 +end + + +# Osborne 1 +diskdef osborne1 + seclen 1024 + tracks 40 + sectrk 5 + blocksize 1024 + maxdir 64 + boottrk 3 + os 2.2 +end + +# Osborne Nuevo/Vixen/4 +diskdef osborne4 + seclen 1024 + tracks 80 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 2 + boottrk 2 + os 2.2 +end + +# Lobo Max-80 8" CP/M 2 +diskdef lobo2 + seclen 256 + tracks 77 + sectrk 30 + blocksize 2048 + maxdir 64 + skew 0 + boottrk 2 + os 2.2 +end + +#Lobo Max-80 8" CP/M 3 +diskdef lobo3 + seclen 512 + tracks 77 + sectrk 17 + blocksize 2048 + maxdir 64 + skew 0 + boottrk 2 + os 3 +end + +# PRO CP/M RZ50 DZ format (Perhaps only 79 tracks should be used?) +diskdef dec_pro + seclen 512 + tracks 80 + sectrk 10 + blocksize 2048 + maxdir 128 + skew 2 + boottrk 2 + os 2.2 +end + +# TDOS with DateStamper +diskdef tdos-ds + seclen 1024 + tracks 77 + sectrk 16 + blocksize 2048 + maxdir 256 + skew 0 + boottrk 1 + os zsys +end + +# The following entires are tested and working +# Most of the images are either from Don Maslin's archive or from +# Dave Dunfield's site, but not all - they are noted as well as +# their size. + +# PMC Micromate +# Dave Dunfield's Imagedisk information from DSK conversion from IMD: +# IMageDisk Utility 1.18 / Mar 07 2012 +# IMD 1.14: 10/03/2007 11:13:27 +# PMC-101 MicroMate +# CP/M Plus +# System Master +# Assuming 1:1 for Binary output +# 0/0 250 kbps DD 5x1024 +# 80 tracks(40/40), 400 sectors (12 Compressed) +# Entry derived from above - image size = 409,600, from Dave Dunfield +diskdef pmc101 + seclen 1024 + tracks 80 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 3 +end + +# BEGIN td143ssdd8 Turbo Dos 1.43 - SSDD 8" - 512 x 16 +# Test OK - image size = 630,784, from Don Maslin's archive +diskdef td143ssdd8 + seclen 512 + tracks 77 + sectrk 9 + blocksize 1024 + maxdir 64 + skew 0 + boottrk 0 + os 2.2 +# DENSITY MFM ,LOW +end + +# BEGIN headsdd8 Heath H89, Magnolia CP/M - SSDD 8" - 512 x 16 +# Test OK - image size = 630,784, from Don Maslin's archive +diskdef heassdd8 + seclen 512 + tracks 77 + sectrk 16 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 2.2 +# DENSITY MFM ,LOW +end + +# Morrow Designs Micro-Decision DOUBLE +# 64k CP/M Vers. 2.2 Rev.2.3 SIDED +# Copyright '76, '77, '78, '79, '80 +# Digital Research +# Copyright 1982,1983 Morrow Designs, Inc. +# Assuming 1:1 for Binary output +# 0/0 250 kbps DD 5x1024 +# 80 tracks(40/40), 400 sectors (128 Compressed) +# Entry derived from above data +# Test OK - image siae = 409600, from Dave Dunfield +diskdef mordsdd + seclen 1024 + tracks 80 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 3 + boottrk 2 + OS 2.2 +end + + +# BEGIN morsddd Morrow MD2 - SSDD 48 tpi 5.25" - 1024 x 5 +# Test OK - image size = 204,800, from Don Maslin's archive +# Also tested with image from Dave Dunfield +diskdef morsddd + seclen 1024 + tracks 40 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 3 + boottrk 2 + os 2.2 +# DENSITY MFM ,LOW +# BSH 4 BLM 15 EXM 1 DSM 94 DRM 127 AL0 0C0H AL1 0 OFS 2 +end + +# BEGIN osb1sssd Osborne 1 - SSSD 48 tpi 5.25" - 256 x 10 +# Test OK - image size = 102,400, from Don Maslin's archive +diskdef osb1sssd + seclen 256 + tracks 40 + sectrk 10 + blocksize 2048 + maxdir 64 + skew 2 + boottrk 3 + os 2.2 +# DENSITY MFM ,LOW +# BSH 4 BLM 15 EXM 1 DSM 45 DRM 63 AL0 080H AL1 0 OFS 3 +end + +# BEGIN ampdsdd Ampro - DSDD 48 tpi 5.25" - 512 x 10 +# Test OK - image size = 409,600, from Don Maslin's archive +diskdef ampdsdd + seclen 1024 + tracks 80 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 2.2 + libdsk:format ampro400d +# DENSITY MFM ,LOW +# BSH 4 BLM 15 EXM 1 DSM 194 DRM 127 AL0 0C0H AL1 0 OFS 2 +end + +# BEGIN ampdsdd80 Ampro - DSDD 96 tpi 5.25" - 512 x 10 +# Test OK - image size = 819,200, from Don Maslin's archive +diskdef ampdsdd80 + seclen 1024 + tracks 160 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 2.2 + libdsk:format ampro800 +# DENSITY MFM ,LOW +# BSH 4 BLM 15 EXM 1 DSM 194 DRM 127 AL0 0C0H AL1 0 OFS 2 +end + +# BEGIN altdsdd Altos - DSDD 5" - 512 x 9 +# Test OK - both CP/M and MP/M - image size = 737,280, from Dave Dunfield +diskdef altdsdd + seclen 512 + tracks 160 + sectrk 9 + blocksize 4096 + maxdir 177 + skew 0 + boottrk 2 + os 3 +# DENSITY MFM ,HIGH +# BSH 5 BLM 31 EXM 3 DSM 176 DRM 176 AL0 0C0H AL1 0 OFS 2 +end + +# BEGIN trsomsssd TRS-80 Model 1, Omikron CP/M - SSSD 48 tpi 5.25" - 128 x 18 +# Test OK - image size = 80,640, from TRS-80 Yahoo Group posting +diskdef trsomsssd + seclen 128 + tracks 35 + sectrk 18 + blocksize 1024 + maxdir 64 + skew 4 + boottrk 3 + os 2.2 +# DENSITY FM ,LOW +# BSH 3 BLM 7 EXM 0 DSM 71 DRM 63 AL0 0C0H AL1 0 OFS 3 +end + +# Memotech type 03, ie: 3.5" or 5.25", D/S, D/D, S/T +# 40 tracks, 2 sides, 16 sectors/track, 256 bytes/sector +# Bytes on the media = 2*40*16*256 = 327680 +# CP/M sees 26 128 byte records per track (similar to 8" disks). +# Tracks = 327680/(26*128) = 98 +# Data is in 2048 byte blocks, on track 2 onwards +# Blocks = ((98-2)*26*128)/2048 = 156, which agrees with DPB + +diskdef memotech-type03 + seclen 128 + tracks 98 + sectrk 26 + blocksize 2048 + maxdir 64 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 07, ie: 3.5" or 5.25", D/S, D/D, D/T +# 80 tracks, 2 sides, 16 sectors/track, 256 bytes/sector +# Bytes on the media = 2*80*16*256 = 655360 +# CP/M sees 26 128 byte records per track (similar to 8" disks). +# Tracks = 655360/(26*128) = 196 +# Data is in 2048 byte blocks, on track 2 onwards +# Blocks = ((196-2)*26*128)/2048 = 315, which agrees with DPB + +diskdef memotech-type07 + seclen 128 + tracks 196 + sectrk 26 + blocksize 2048 + maxdir 128 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 43, ie: 1MB Silicon Disc +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for Silicon Discs includes blocks on the last incomplete track +# Tracks = 1048576/(26*128) = 315.07 +# Data is in 4096 byte blocks, on track 2 onwards +# Blocks = (1048576-2*26*128)/4096 = 254, which agrees with DPB +# Blocks = ((315-2)*26*128)/4096 = 254, so we don't need the 0.07 track +diskdef memotech-type43 + seclen 128 + tracks 315 + sectrk 26 + blocksize 4096 + maxdir 256 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 47, ie: 2MB Silicon Disc +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for Silicon Discs includes blocks on the last incomplete track +# Tracks = 2097152/(26*128) = 630.15 +# Data is in 4096 byte blocks, on track 2 onwards +# Blocks = (2097152-2*26*128)/4096 = 510, which agrees with DPB +# Blocks = ((630-2)*26*128)/4096 = 510, so we don't need the 0.15 track +diskdef memotech-type47 + seclen 128 + tracks 630 + sectrk 26 + blocksize 4096 + maxdir 256 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 4B, ie: 4MB Silicon Disc +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for Silicon Discs includes blocks on the last incomplete track +# Tracks = 4194304/(26*128) = 1260.3 +# Data is in 4096 byte blocks, on track 2 onwards +# Blocks = (4194304-2*26*128)/4096 = 1022, which agrees with DPB +# Blocks = ((1260-2)*26*128)/4096 = 1022, so we don't need the 0.3 track +diskdef memotech-type4B + seclen 128 + tracks 1260 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 4F, ie: 8MB Silicon Disc +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for Silicon Discs includes blocks on the last incomplete track +# Tracks = 8388608/(26*128) = 2520.61 +# Data is in 4096 byte blocks, on track 2 onwards +# Blocks = (8388608-2*26*128)/4096 = 2046, which agrees with DPB +# Blocks = ((2520-2)*26*128)/4096 = 2045, so we need the extra 0.61 track +diskdef memotech-type4F + seclen 128 + tracks 2521 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 18, ie: 8MB SD Card +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for SD Cards includes blocks on the last incomplete track +# Tracks = 8388608/(26*128) = 2520.61 +# Data is in 4096 byte blocks, on track 2 onwards +# Blocks = (8388608-2*26*128)/4096 = 2046, which agrees with DPB +# Blocks = ((2520-2)*26*128)/4096 = 2045, so we need the extra 0.61 track +diskdef memotech-type18 + seclen 128 + tracks 2521 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 19, ie: 8MB SD Card +diskdef memotech-type19 + seclen 128 + tracks 2521 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 + offset 8M +end + +# Memotech type 1A, ie: 8MB SD Card +diskdef memotech-type1A + seclen 128 + tracks 2521 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 + offset 16M +end + +# Memotech type 1B, ie: 8MB SD Card +diskdef memotech-type1B + seclen 128 + tracks 2521 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 + offset 24M +end + +# Memotech type 1C, ie: 8MB SD Card +diskdef memotech-type1C + seclen 128 + tracks 2521 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 + offset 32M +end + +# Memotech type 1D, ie: 8MB SD Card +diskdef memotech-type1D + seclen 128 + tracks 2521 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 + offset 40M +end + +# Memotech type 1E, ie: 8MB SD Card +diskdef memotech-type1E + seclen 128 + tracks 2521 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 + offset 48M +end + +# Memotech type 1F, ie: 8MB SD Card +diskdef memotech-type1F + seclen 128 + tracks 2521 + sectrk 26 + blocksize 4096 + maxdir 512 + skew 1 + boottrk 2 + os 2.2 + offset 56M +end + +# Memotech type 50, ie: 256KB RAM Disc +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for RAM Discs includes blocks on the last incomplete track +# Tracks = 262144/(26*128) = 78.76 +# Data is in 1024 byte blocks, on track 2 onwards +# Blocks = (262144-2*26*128)/1024 = 249, which agrees with DPB +# Blocks = ((78-2)*26*128)/1024 = 247, so we need the extra 0.76 track +diskdef memotech-type50 + seclen 128 + tracks 79 + sectrk 26 + blocksize 1024 + maxdir 64 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 51, ie: 512KB RAM Disc +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for RAM Discs includes blocks on the last incomplete track +# Tracks = 524288/(26*128) = 157.53 +# Data is in 2048 byte blocks, on track 2 onwards +# Blocks = (524288-2*26*128)/2048 = 252, which agrees with DPB +# Blocks = ((157-2)*26*128)/2048 = 251, so we need the extra 0.53 track +diskdef memotech-type51 + seclen 128 + tracks 158 + sectrk 26 + blocksize 2048 + maxdir 128 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 51, as used in Italy, ie: 480KB RAM Disc +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for RAM Discs includes blocks on the last incomplete track +# Tracks = 491520/(26*128) = 147.69 +# Data is in 2048 byte blocks, on track 2 onwards +# Blocks = (491520-2*26*128)/2048 = 236, which agrees with DPB +# Blocks = ((147-2)*26*128)/2048 = 235, so we need the extra 0.69 track +diskdef memotech-type51-italy + seclen 128 + tracks 148 + sectrk 26 + blocksize 2048 + maxdir 128 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 51, after S2R64.COM, ie: 448KB RAM Disc +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for RAM Discs includes blocks on the last incomplete track +# Tracks = 458752/(26*128) = 137.84 +# Data is in 2048 byte blocks, on track 2 onwards +# Blocks = (458752-2*26*128)/2048 = 220, which agrees with DPB, after S2R64.COM +# Blocks = ((137-2)*26*128)/2048 = 219, so we need the extra 0.84 track +diskdef memotech-type51-s2r64 + seclen 128 + tracks 138 + sectrk 26 + blocksize 2048 + maxdir 128 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 51, after S2R.COM, ie: 144KB RAM Disc +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for RAM Discs includes blocks on the last incomplete track +# Tracks = 147456/(26*128) = 44.3 +# Data is in 2048 byte blocks, on track 2 onwards +# Blocks = (147456-2*26*128)/2048 = 68, which agrees with DPB, after S2R.COM +# Blocks = ((44-2)*26*128)/2048 = 68, so we don't need the extra 0.3 track +diskdef memotech-type51-s2r + seclen 128 + tracks 44 + sectrk 26 + blocksize 2048 + maxdir 128 + skew 1 + boottrk 2 + os 2.2 +end + +# Memotech type 52, ie: 320KB RAM Disc +# Added for REMEMOTECH +# CP/M sees 26 128 byte records per track +# Note: Unlike common practice with real physical disks, with real geometry, +# the DPB for RAM Discs includes blocks on the last incomplete track +# Tracks = 327680/(26*128) = 98.46 +# Data is in 2048 byte blocks, on track 2 onwards +# Blocks = (327680-2*26*128)/2048 = 156 +# Blocks = ((98-2)*26*128)/2048 = 156, so we don't need the extra 0.46 track +# This type very deliberately and conveniently exactly matches type 03 +diskdef memotech-type52 + seclen 128 + tracks 98 + sectrk 26 + blocksize 2048 + maxdir 64 + skew 1 + boottrk 2 + os 2.2 +end + +# Research Machines 380Z/480Z 5.25" "Single Density" or "MDS" format. +# All tracks are formatted FM 16x128. +diskdef rm-sd + seclen 128 + tracks 40 + sectrk 16 + blocksize 1024 + maxdir 64 + skew 3 + boottrk 3 + os 2.2 +end + +# Research Machines 380Z/480Z 5.25" "Double Density" or "MD" format. +# Track 0 is formatted FM 16x128; 1+ are MFM 9x512. +# If you're working with an image file, make sure that track 0 is +# padded to be the same size as the other tracks. +diskdef rm-dd + seclen 512 + tracks 40 + sectrk 9 + blocksize 1024 + maxdir 64 + skew 5 + boottrk 3 + os 2.2 +end + +# Research Machines 380Z/480Z 5.25" "Quad Density" or "MQ" format. +# Track 0 is formatted FM 16x128; 1+ are MFM 9x512. +diskdef rm-qd + seclen 512 + tracks 80 + sectrk 9 + blocksize 2048 + maxdir 128 + skew 5 + boottrk 3 + os 2.2 +end + +# Ampro Little Board Z80 running CP/M 2.21 +# BEGIN AMP1 Ampro - SSDD 48 tpi 5.25" +# DENSITY MFM, LOW +# CYLINDERS 40 SIDES 1 SECTORS 10,512 SKEW 2 +# SIDE1 0 1,2,3,4,5,6,7,8,9,10 +# BSH 4 BLM 15 EXM 1 DSM 94 DRM 63 AL0 080H AL1 0 OFS 2 +# END + +diskdef amp1 + seclen 512 #= Sectors xx,512 + tracks 40 #= (Cylinders * Sides) = 40*1 = 40 + sectrk 10 #= Sectors 10,xxx + blocksize 2048 #= (128*(BLM+1)) = 2048 + maxdir 64 #(DRM+1) = 64 + skew 0 #= SKEW = 0 + boottrk 2 #= OFS = 2 + os 2.2 +end + +#BEGIN AMP2 Ampro - DSDD 48 tpi 5.25" +#DENSITY MFM, LOW +#CYLINDERS 40 SIDES 2 +#SECTORS 10,512 +#SKEW 2 +#SIDE1 0 17,18,19,20,21,22,23,24,25,26 +#SIDE2 1 17,18,19,20,21,22,23,24,25,26 +#ORDER SIDES +#BSH 4 BLM 15 EXM 1 DSM 194 DRM 127 AL0 0C0H AL1 0 OFS 2 +#END + +# setfdprm /dev/fd0 DS DD ssize=512 cyl=40 sect=10 head=2 +diskdef amp2 + seclen 512 + tracks 80 + sectrk 10 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 2.2 +end + +#BEGIN AMP3 Ampro - SSDD 96 tpi 3.5" +#DENSITY MFM, LOW +#CYLINDERS 80 SIDES 1 SECTORS 5,1024 SKEW 2 +#SIDE1 0 1,2,3,4,5 +#BSH 4 BLM 15 EXM 1 DSM 194 DRM 127 AL0 0C0H AL1 0 OFS 2 +#END + +# setfdprm /dev/fd0 SS DD ssize=1024 cyl=80 sect=5 head=1 +diskdef amp3 + seclen 1024 + tracks 80 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 2.2 +end + +#BEGIN AMP4 Ampro - DSDD 96 tpi 3.5" +#DENSITY MFM, LOW +#CYLINDERS 80 SIDES 2 SECTORS 5,1024 SKEW 2 +#SIDE1 0 17,18,19,20,21 +#SIDE2 1 17,18,19,20,21 +#ORDER SIDES +#BSH 4 BLM 15 EXM 0 DSM 394 DRM 255 AL0 0F0H AL1 0 OFS 2 +#END + +# setfdprm /dev/fd0 DS DD ssize=1024 cyl=80 sect=5 head=2 +diskdef amp4 + seclen 1024 + tracks 160 + sectrk 5 + blocksize 2048 + maxdir 256 + skew 0 + boottrk 2 + os 2.2 +end + +#BEGIN AMP5 Ampro - SSDD 3.5" +#DENSITY MFM, LOW +#CYLINDERS 80 SIDES 1 SECTORS 5,1024 SKEW 2 +#SIDE1 0 1,2,3,4,5 +#BSH 4 BLM 15 EXM 1 DSM 194 DRM 127 AL0 0C0H AL1 0 OFS 2 +#END + +# setfdprm /dev/fd0 SS DD ssize=1024 cyl=80 sect=5 head=1 +diskdef amp5 + seclen 1024 + tracks 80 + sectrk 5 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 2 + os 2.2 +end + +#BEGIN AMP6 Ampro - DSDD 3.5" +#DENSITY MFM, LOW +#CYLINDERS 80 SIDES 2 SECTORS 5,1024 SKEW 2 +#SIDE1 0 17,18,19,20,21 +#SIDE2 1 17,18,19,20,21 +#ORDER SIDES +#BSH 4 BLM 15 EXM 0 DSM 394 DRM 255 AL0 0F0H AL1 0 OFS 2 +#END + +# setfdprm /dev/fd0 DS DD ssize=1024 cyl=80 sect=5 head=2 +diskdef amp6 + seclen 1024 + tracks 160 + sectrk 5 + blocksize 2048 + maxdir 256 + skew 0 + boottrk 2 + os 2.2 +end + +diskdef ampro800 + seclen 1024 + tracks 160 + sectrk 5 + blocksize 2048 + maxdir 256 + skew 0 + boottrk 2 + os 2.2 +end diff --git a/Tools/unix/cpmtools/fsck.cpm.1 b/Tools/unix/cpmtools/fsck.cpm.1 new file mode 100644 index 00000000..a167ef0e --- /dev/null +++ b/Tools/unix/cpmtools/fsck.cpm.1 @@ -0,0 +1,80 @@ +.TH FSCK.CPM 1 "October 25, 2014" "CP/M tools" "User commands" +.SH NAME ..\"{{{roff}}}\"{{{ +fsck.cpm \- check a CP/M file system +.\"}}} +.SH SYNOPSIS .\"{{{ +.ad l +.B fsck.cpm +.RB [ \-f +.IR format ] +.RB [ \-n ] +.I image +.ad b +.\"}}} +.SH DESCRIPTION .\"{{{ +\fBfsck.cpm\fP is used to check and repair a CP/M file system. After +reading the directory, it makes two passes. The first pass checks extent +fields for range and format violations (bad status, extent number, last +record byte count, file name, extension, block number, record count, +size of \&.COM files, time stamp format, invalid password characters, +invalid time stamp mode). The second pass checks extent connectivity +(multiple allocated blocks and duplicate directory entries). +.P +\fBfsck.cpm\fP can not yet repair all errors. +.\"}}} +.SH OPTIONS .\"{{{ +.IP "\fB\-f\fP \fIformat\fP" +Use the given CP/M disk \fIformat\fP instead of the default format. +.IP "\fB\-T\fP \fIlibdsk-type\fP" +libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images +(requires building cpmtools with support for libdsk). +.IP "\fB\-n\fP" +Open the file system read-only and do not repair any errors. +.\"}}} +.SH "RETURN VALUE" .\"{{{ +Upon successful completion, exit code 0 is returned. +.\"}}} +.SH ERRORS .\"{{{ +Any errors are indicated by exit code 1. +.\"}}} +.SH FILES .\"{{{ +${prefix}/share/diskdefs CP/M disk format definitions +.\"}}} +.SH ENVIRONMENT \"{{{ +CPMTOOLSFMT Default format +.\"}}} +.SH DIAGNOSTICS .\"{{{ +.IP "\fIimage\fP: \fIused\fP/\fItotal\fP files (\fIn\fP.\fIn\fP% non-contiguos), \fIused\fP/\fItotal\fP blocks" +No inconsistencies could be found. The number of used files actually +is the number of used extents. Since a file may use more than +one extent, this may be greather than the actual number of files, but a +correct measure would not reflect how many files could still be created +at most. A file is considered fragmented, if sequential data blocks +pointed to by the same extent do not have sequential block numbers. +The number of used blocks includes the blocks used for system tracks +and the directory. +.\"}}} +.SH AUTHORS .\"{{{ +This program is copyright 1997\(en2012 Michael Haardt +. The Windows port is copyright 2000, 2001, 2011 John Elliott +. +.PP +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +.PP +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +.PP +You should have received a copy of the GNU General Public License along +with this program. If not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.\"}}} +.SH "SEE ALSO" .\"{{{ +.IR fsck (8), +.IR mkfs.cpm (1), +.IR cpm (5) +.\"}}} diff --git a/Tools/unix/cpmtools/fsck.cpm.c b/Tools/unix/cpmtools/fsck.cpm.c new file mode 100644 index 00000000..585015b0 --- /dev/null +++ b/Tools/unix/cpmtools/fsck.cpm.c @@ -0,0 +1,632 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "getopt_.h" +#include "cpmdir.h" +#include "cpmfs.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ +/* #defines */ /*{{{*/ +/* your favourite password *:-) */ + +#define T0 'G' +#define T1 'E' +#define T2 'H' +#define T3 'E' +#define T4 'I' +#define T5 'M' +#define T6 ' ' +#define T7 ' ' + +#define PB ((char)(T0+T1+T2+T3+T4+T5+T6+T7)) +#define P0 ((char)(T7^PB)) +#define P1 ((char)(T6^PB)) +#define P2 ((char)(T5^PB)) +#define P3 ((char)(T4^PB)) +#define P4 ((char)(T3^PB)) +#define P5 ((char)(T2^PB)) +#define P6 ((char)(T1^PB)) +#define P7 ((char)(T0^PB)) +/*}}}*/ + +/* types */ /*{{{*/ +enum Result { OK=0, MODIFIED=1, BROKEN=2 }; +/*}}}*/ +/* variables */ /*{{{*/ +static int norepair=0; +/*}}}*/ + +/* bcdCheck -- check format and range of BCD digit */ /*{{{*/ +static int bcdCheck(int n, int max, const char *msg, const char *unit, int extent1, int extent2) +{ + if (((n>>4)&0xf)>10 || (n&0xf)>10 || (((n>>4)&0xf)*10+(n&0xf))>=max) + { + printf("Error: Bad %s %s (extent=%d/%d, %s=%02x)\n",msg,unit,extent1,extent2,unit,n&0xff); + return -1; + } + else return 0; +} +/*}}}*/ +/* pwdCheck -- check password */ /*{{{*/ +static int pwdCheck(int extent, const char *pwd, char decode) +{ + char c; + int i; + + for (i=0; i<8; ++i) if ((c=((char)(pwd[7-i]^decode)))<' ' || c&0x80) + { + printf("Error: non-printable character in password (extent=%d, password=",extent); + for (i=0; i<8; ++i) + { + c=pwd[7-i]^decode; + if (c<' ' || c&0x80) + { + putchar('\\'); putchar('0'+((c>>6)&0x01)); + putchar('0'+((c>>3)&0x03)); + putchar('0'+(c&0x03)); + } + else putchar(c); + } + printf(")\n"); + return -1; + } + return 0; +} +/*}}}*/ +/* ask -- ask user and return answer */ /*{{{*/ +static int ask(const char *msg) +{ + while (1) + { + char buf[80]; + + if (norepair) return 0; + printf("%s [Y]? ",msg); fflush(stdout); + if (fgets(buf,sizeof(buf),stdin)==(char*)0) exit(1); + switch (toupper(buf[0])) + { + case '\n': + case 'Y': return 1; + case 'N': return 0; + } + } +} +/*}}}*/ +/* prfile -- print file name */ /*{{{*/ +static char *prfile(struct cpmSuperBlock *sb, int extent) +{ + struct PhysDirectoryEntry *dir; + static char name[80]; + char *s=name; + int i; + char c; + + dir=sb->dir+extent; + for (i=0; i<8; ++i) + { + c=dir->name[i]; + if ((c&0x7f)<' ') + { + *s++='\\'; *s++=('0'+((c>>6)&0x01)); + *s++=('0'+((c>>3)&0x03)); + *s++=('0'+(c&0x03)); + } + else *s++=(c&0x7f); + } + *s++='.'; + for (i=0; i<3; ++i) + { + c=dir->ext[i]; + if ((c&0x7f)<' ') + { + *s++='\\'; *s++=('0'+((c>>6)&0x01)); + *s++=('0'+((c>>3)&0x03)); + *s++=('0'+(c&0x03)); + } + else *s++=(c&0x7f); + } + *s='\0'; + return name; +} +/*}}}*/ +/* fsck -- file system check */ /*{{{*/ +static int fsck(struct cpmInode *root, const char *image) +{ + /* variables */ /*{{{*/ + enum Result ret=OK; + int extent,extent2; + struct PhysDirectoryEntry *dir,*dir2; + struct cpmSuperBlock *sb=root->sb; + /*}}}*/ + + /* Phase 1: check extent fields */ /*{{{*/ + printf("Phase 1: check extent fields\n"); + for (extent=0; extentmaxdir; ++extent) + { + char *status; + int usedBlocks=0; + + dir=sb->dir+extent; + status=&dir->status; + if (*status>=0 && *status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* directory entry */ /*{{{*/ + { + /* check name and extension */ /*{{{*/ + { + int i; + char *c; + + for (i=0; i<8; ++i) + { + c=&(dir->name[i]); + if (!ISFILECHAR(i,*c&0x7f) || islower(*c&0x7f)) + { + printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i); + if (ask("Remove file")) + { + *status=(char)0xE5; + ret|=MODIFIED; + break; + } + else ret|=BROKEN; + } + } + if (*status==(char)0xe5) continue; + for (i=0; i<3; ++i) + { + c=&(dir->ext[i]); + if (!ISFILECHAR(1,*c&0x7f) || islower(*c&0x7f)) + { + printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i); + if (ask("Remove file")) + { + *status=(char)0xE5; + ret|=MODIFIED; + break; + } + else ret|=BROKEN; + } + } + if (*status==(char)0xe5) continue; + } + /*}}}*/ + /* check extent number */ /*{{{*/ + if ((dir->extnol&0xff)>0x1f) + { + printf("Error: Bad lower bits of extent number (extent=%d, name=\"%s\", low bits=%d)\n",extent,prfile(sb,extent),dir->extnol&0xff); + if (ask("Remove file")) + { + *status=(char)0xE5; + ret|=MODIFIED; + } + else ret|=BROKEN; + } + if (*status==(char)0xe5) continue; + if ((dir->extnoh&0xff)>0x3f) + { + printf("Error: Bad higher bits of extent number (extent=%d, name=\"%s\", high bits=%d)\n",extent,prfile(sb,extent),dir->extnoh&0xff); + if (ask("Remove file")) + { + *status=(char)0xE5; + ret|=MODIFIED; + } + else ret|=BROKEN; + } + if (*status==(char)0xe5) continue; + /*}}}*/ + /* check last record byte count */ /*{{{*/ + if ((dir->lrc&0xff)>128) + { + printf("Error: Bad last record byte count (extent=%d, name=\"%s\", lrc=%d)\n",extent,prfile(sb,extent),dir->lrc&0xff); + if (ask("Clear last record byte count")) + { + dir->lrc=(char)0; + ret|=MODIFIED; + } + else ret|=BROKEN; + } + if (*status==(char)0xe5) continue; + /*}}}*/ + /* check block number range */ /*{{{*/ + { + int block,min,max,i; + + min=(sb->maxdir*32+sb->blksiz-1)/sb->blksiz; + max=sb->size; + for (i=0; i<16; ++i) + { + block=dir->pointers[i]&0xff; + if (sb->size>=256) block+=(dir->pointers[++i]&0xff)<<8; + if (block>0) + { + ++usedBlocks; + if (block=max) + { + printf("Error: Bad block number (extent=%d, name=\"%s\", block=%d)\n",extent,prfile(sb,extent),block); + if (ask("Remove file")) + { + *status=(char)0xE5; + ret|=MODIFIED; + break; + } + else ret|=BROKEN; + } + } + } + if (*status==(char)0xe5) continue; + } + /*}}}*/ + /* check number of used blocks ? */ /*{{{*/ + /*}}}*/ + /* check record count */ /*{{{*/ + { + int i,min,max,recordsInBlocks,used=0; + + min=(dir->extnol%sb->extents)*16/sb->extents; + max=((dir->extnol%sb->extents)+1)*16/sb->extents; + assert(minpointers[i] || (sb->size>=256 && dir->pointers[i+1])) ++used; + if (sb->size >= 256) ++i; + } + recordsInBlocks=(((unsigned char)dir->blkcnt)*128+sb->blksiz-1)/sb->blksiz; + if (recordsInBlocks!=used) + { + printf("Error: Bad record count (extent=%d, name=\"%s\", record count=%d)\n",extent,prfile(sb,extent),dir->blkcnt&0xff); + if (ask("Remove file")) + { + *status=(char)0xE5; + ret|=MODIFIED; + } + else ret|=BROKEN; + } + if (*status==(char)0xe5) continue; + } + /*}}}*/ + /* check for too large .com files */ /*{{{*/ + if (((EXTENT(dir->extnol,dir->extnoh)==3 && dir->blkcnt>=126) || EXTENT(dir->extnol,dir->extnoh)>=4) && (dir->ext[0]&0x7f)=='C' && (dir->ext[1]&0x7f)=='O' && (dir->ext[2]&0x7f)=='M') + { + printf("Warning: Oversized .COM file (extent=%d, name=\"%s\")\n",extent,prfile(sb,extent)); + } + /*}}}*/ + } + /*}}}*/ + else if ((sb->type==CPMFS_P2DOS || sb->type==CPMFS_DR3) && *status==33) /* check time stamps ? */ /*{{{*/ + { + unsigned long created,modified; + char s; + + if ((s=sb->dir[extent2=(extent&~3)].status)>=0 && s<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* time stamps for first of the three extents */ /*{{{*/ + { + bcdCheck(dir->name[2],24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent2); + bcdCheck(dir->name[3],60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent2); + bcdCheck(dir->name[6],24,"modification date","hour",extent,extent2); + bcdCheck(dir->name[7],60,"modification date","minute",extent,extent2); + created=(dir->name[4]+(dir->name[1]<<8))*(0x60*0x60)+dir->name[2]*0x60+dir->name[3]; + modified=(dir->name[0]+(dir->name[5]<<8))*(0x60*0x60)+dir->name[6]*0x60+dir->name[7]; + if (sb->cnotatime && modifieddir[extent2=(extent&~3)+1].status)>=0 && s<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* time stamps for second */ /*{{{*/ + { + bcdCheck(dir->lrc,24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent2); + bcdCheck(dir->extnoh,60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent2); + bcdCheck(dir->pointers[1],24,"modification date","hour",extent,extent2); + bcdCheck(dir->pointers[2],60,"modification date","minute",extent,extent2); + created=(dir->ext[2]+(dir->extnol<<8))*(0x60*0x60)+dir->lrc*0x60+dir->extnoh; + modified=(dir->blkcnt+(dir->pointers[0]<<8))*(0x60*0x60)+dir->pointers[1]*0x60+dir->pointers[2]; + if (sb->cnotatime && modifieddir[extent2=(extent&~3)+2].status)>=0 && s<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* time stamps for third */ /*{{{*/ + { + bcdCheck(dir->pointers[7],24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent2); + bcdCheck(dir->pointers[8],60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent2); + bcdCheck(dir->pointers[11],24,"modification date","hour",extent,extent2); + bcdCheck(dir->pointers[12],60,"modification date","minute",extent,extent2); + created=(dir->pointers[5]+(dir->pointers[6]<<8))*(0x60*0x60)+dir->pointers[7]*0x60+dir->pointers[8]; + modified=(dir->pointers[9]+(dir->pointers[10]<<8))*(0x60*0x60)+dir->pointers[11]*0x60+dir->pointers[12]; + if (sb->cnotatime && modifiedtype==CPMFS_DR3 && *status==32) /* disc label */ /*{{{*/ + { + unsigned long created,modified; + + bcdCheck(dir->pointers[10],24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent); + bcdCheck(dir->pointers[11],60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent); + bcdCheck(dir->pointers[14],24,"modification date","hour",extent,extent); + bcdCheck(dir->pointers[15],60,"modification date","minute",extent,extent); + created=(dir->pointers[8]+(dir->pointers[9]<<8))*(0x60*0x60)+dir->pointers[10]*0x60+dir->pointers[11]; + modified=(dir->pointers[12]+(dir->pointers[13]<<8))*(0x60*0x60)+dir->pointers[14]*0x60+dir->pointers[15]; + if (sb->cnotatime && modifiedextnol&0x40 && dir->extnol&0x10) + { + printf("Error: Bit 4 and 6 can only be exclusively be set (extent=%d, label byte=0x%02x)\n",extent,(unsigned char)dir->extnol); + if (ask("Time stamp on creation")) + { + dir->extnol&=~0x40; + ret|=MODIFIED; + } + else if (ask("Time stamp on access")) + { + dir->extnol&=~0x10; + ret|=MODIFIED; + } + else ret|=BROKEN; + } + if (dir->extnol&0x80 && pwdCheck(extent,dir->pointers,dir->lrc)) + { + char msg[80]; + + sprintf(msg,"Set password to %c%c%c%c%c%c%c%c",T0,T1,T2,T3,T4,T5,T6,T7); + if (ask(msg)) + { + dir->pointers[0]=P0; + dir->pointers[1]=P1; + dir->pointers[2]=P2; + dir->pointers[3]=P3; + dir->pointers[4]=P4; + dir->pointers[5]=P5; + dir->pointers[6]=P6; + dir->pointers[7]=P7; + dir->lrc=PB; + ret|=MODIFIED; + } + else ret|=BROKEN; + } + } + /*}}}*/ + else if (sb->type==CPMFS_DR3 && *status>=16 && *status<=31) /* password */ /*{{{*/ + { + /* check name and extension */ /*{{{*/ + { + int i; + char *c; + + for (i=0; i<8; ++i) + { + c=&(dir->name[i]); + if (!ISFILECHAR(i,*c&0x7f) || islower(*c&0x7f)) + { + printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i); + if (ask("Clear password entry")) + { + *status=(char)0xE5; + ret|=MODIFIED; + break; + } + else ret|=BROKEN; + } + } + if (*status==(char)0xe5) continue; + for (i=0; i<3; ++i) + { + c=&(dir->ext[i]); + if (!ISFILECHAR(1,*c&0x7f) || islower(*c&0x7f)) + { + printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i); + if (ask("Clear password entry")) + { + *status=(char)0xE5; + ret|=MODIFIED; + break; + } + else ret|=BROKEN; + } + } + if (*status==(char)0xe5) continue; + } + /*}}}*/ + /* check password */ /*{{{*/ + if (dir->extnol&(0x80|0x40|0x20) && pwdCheck(extent,dir->pointers,dir->lrc)) + { + char msg[80]; + + sprintf(msg,"Set password to %c%c%c%c%c%c%c%c",T0,T1,T2,T3,T4,T5,T6,T7); + if (ask(msg)) + { + dir->pointers[0]=P0; + dir->pointers[1]=P1; + dir->pointers[2]=P2; + dir->pointers[3]=P3; + dir->pointers[4]=P4; + dir->pointers[5]=P5; + dir->pointers[6]=P6; + dir->pointers[7]=P7; + dir->lrc=PB; + ret|=MODIFIED; + } + else ret|=BROKEN; + } + /*}}}*/ + } + /*}}}*/ + else if (*status!=(char)0xe5) /* bad status */ /*{{{*/ + { + printf("Error: Bad status (extent=%d, name=\"%s\", status=0x%02x)\n",extent,prfile(sb,extent),*status&0xff); + if (ask("Clear entry")) + { + *status=(char)0xE5; + ret|=MODIFIED; + } + else ret|=BROKEN; + continue; + } + /*}}}*/ + } + /*}}}*/ + /* Phase 2: check extent connectivity */ /*{{{*/ + printf("Phase 2: check extent connectivity\n"); + /* check multiple allocated blocks */ /*{{{*/ + for (extent=0; extentmaxdir; ++extent) if ((dir=sb->dir+extent)->status>=0 && dir->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) + { + int i,j,block,block2; + + for (i=0; i<16; ++i) + { + block=dir->pointers[i]&0xff; + if (sb->size>=256) block+=(dir->pointers[++i]&0xff)<<8; + for (extent2=0; extent2maxdir; ++extent2) if ((dir2=sb->dir+extent2)->status>=0 && dir2->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) + { + for (j=0; j<16; ++j) + { + block2=dir2->pointers[j]&0xff; + if (sb->size>=256) block2+=(dir2->pointers[++j]&0xff)<<8; + if (block!=0 && block2!=0 && block==block2 && !(extent==extent2 && i==j)) + { + printf("Error: Multiple allocated block (extent=%d,%d, name=\"%s\"",extent,extent2,prfile(sb,extent)); + printf(",\"%s\" block=%d)\n",prfile(sb,extent2),block); + ret|=BROKEN; + } + } + } + } + } + /*}}}*/ + /* check multiple extents */ /*{{{*/ + for (extent=0; extentmaxdir; ++extent) if ((dir=sb->dir+extent)->status>=0 && dir->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) + { + for (extent2=0; extent2maxdir; ++extent2) if ((dir2=sb->dir+extent2)->status>=0 && dir2->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) + { + if (extent!=extent2 && EXTENT(dir->extnol,dir->extnoh)==EXTENT(dir2->extnol,dir2->extnoh) && dir->status==dir2->status) + { + int i; + + for (i=0; i<8 && (dir->name[i]&0x7f)==(dir2->name[i]&0x7f); ++i); + if (i==8) + { + for (i=0; i<3 && (dir->ext[i]&0x7f)==(dir2->ext[i]&0x7f); ++i); + if (i==3) + { + printf("Error: Duplicate extent (extent=%d,%d)\n",extent,extent2); + ret|=BROKEN; + } + } + } + } + } + /*}}}*/ + /*}}}*/ + if (ret==0) /* print statistics */ /*{{{*/ + { + struct cpmStatFS statfsbuf; + int fragmented=0,borders=0; + + cpmStatFS(root,&statfsbuf); + for (extent=0; extentmaxdir; ++extent) if ((dir=sb->dir+extent)->status>=0 && dir->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) + { + int i,block,previous=-1; + + for (i=0; i<16; ++i) + { + block=dir->pointers[i]&0xff; + if (sb->size>=256) block+=(dir->pointers[++i]&0xff)<<8; + if (previous!=-1) + { + if (block!=0 && block!=(previous+1)) ++fragmented; + ++borders; + } + previous=block; + } + } + fragmented=(borders ? (1000*fragmented)/borders : 0); + printf("%s: %ld/%ld files (%d.%d%% non-contigous), %ld/%ld blocks\n",image,statfsbuf.f_files-statfsbuf.f_ffree,statfsbuf.f_files,fragmented/10,fragmented%10,statfsbuf.f_blocks-statfsbuf.f_bfree,statfsbuf.f_blocks); + } + /*}}}*/ + return ret; +} +/*}}}*/ + +const char cmd[]="fsck.cpm"; + +/* main */ /*{{{*/ +int main(int argc, char *argv[]) +{ + const char *err; + const char *image; + const char *format; + const char *devopts=NULL; + int c,usage=0; + struct cpmSuperBlock sb; + struct cpmInode root; + enum Result ret; + + if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; + while ((c=getopt(argc,argv,"T:f:nh?"))!=EOF) switch(c) + { + case 'f': format=optarg; break; + case 'T': devopts=optarg; break; + case 'n': norepair=1; break; + case 'h': + case '?': usage=1; break; + } + + if (optind!=(argc-1)) usage=1; + else image=argv[optind++]; + + if (usage) + { + fprintf(stderr,"Usage: %s [-f format] [-n] image\n",cmd); + exit(1); + } + if ((err=Device_open(&sb.dev, image, (norepair ? O_RDONLY : O_RDWR), devopts))) + { + if ((err=Device_open(&sb.dev, image,O_RDONLY, devopts))) + { + fprintf(stderr,"%s: cannot open %s: %s\n",cmd,image,err); + exit(1); + } + else + { + fprintf(stderr,"%s: cannot open %s for writing, no repair possible\n",cmd,image); + } + } + if (cpmReadSuper(&sb,&root,format)==-1) + { + fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); + exit(1); + } + ret=fsck(&root,image); + if (ret&MODIFIED) + { + if (cpmSync(&sb)==-1) + { + fprintf(stderr,"%s: write error on %s: %s\n",cmd,image,strerror(errno)); + ret|=BROKEN; + } + fprintf(stderr,"%s: FILE SYSTEM ON %s MODIFIED",cmd,image); + if (ret&BROKEN) fprintf(stderr,", PLEASE CHECK AGAIN"); + fprintf(stderr,"\n"); + } + cpmUmount(&sb); + if (ret&BROKEN) return 2; + else return 0; +} +/*}}}*/ diff --git a/Tools/unix/cpmtools/fsed.cpm.1 b/Tools/unix/cpmtools/fsed.cpm.1 new file mode 100644 index 00000000..a6d58738 --- /dev/null +++ b/Tools/unix/cpmtools/fsed.cpm.1 @@ -0,0 +1,62 @@ +.TH FSED.CPM 1 "October 25, 2014" "CP/M tools" "User commands" +.SH NAME ..\"{{{roff}}}\"{{{ +fsed.cpm \- edit a CP/M file system +.\"}}} +.SH SYNOPSIS .\"{{{ +.ad l +.B fsed.cpm +.RB [ \-f +.IR format ] +.I image +.ad b +.\"}}} +.SH DESCRIPTION .\"{{{ +\fBfsed.cpm\fP edits a CP/M file system on an image file or device. +It knows about the system, directory and data area, using sector skew on +the last two. Directory entries are decoded. The interactive usage is +self-explanatory. +.\"}}} +.SH OPTIONS .\"{{{ +.IP "\fB\-f\fP \fIformat\fP" +Use the given CP/M disk \fIformat\fP instead of the default format. +.IP "\fB\-T\fP \fIlibdsk-type\fP" +libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images +(requires building cpmtools with support for libdsk). +.\"}}} +.SH "RETURN VALUE" .\"{{{ +Upon successful completion, exit code 0 is returned. +.\"}}} +.SH ERRORS .\"{{{ +Any errors are indicated by exit code 1. +.\"}}} +.SH ENVIRONMENT \"{{{ +CPMTOOLSFMT Default format +.\"}}} +.SH FILES .\"{{{ +${prefix}/share/diskdefs CP/M disk format definitions +.\"}}} +.SH AUTHORS \"{{{ +This program is copyright 1997\(en2012 Michael Haardt +. The Windows port is copyright 2000, 2001, 2011 John Elliott +. +.PP +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +.PP +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +.PP +You should have received a copy of the GNU General Public License along +with this program. If not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.\"}}} +.SH "SEE ALSO" .\"{{{ +.IR fsck.cpm (1), +.IR mkfs.cpm (1), +.IR cpmls (1), +.IR cpm (5) +.\"}}} diff --git a/Tools/unix/cpmtools/fsed.cpm.c b/Tools/unix/cpmtools/fsed.cpm.c new file mode 100644 index 00000000..f120b77c --- /dev/null +++ b/Tools/unix/cpmtools/fsed.cpm.c @@ -0,0 +1,748 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#if NEED_NCURSES +#if HAVE_NCURSES_NCURSES_H +#include +#else +#include +#endif +#else +#include +#endif +#include +#include +#include +#include +#include + +#include "cpmfs.h" +#include "getopt_.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ + +extern char **environ; + +static char *mapbuf; + +static struct tm *cpmtime(char lday, char hday, char hour, char min) /*{{{*/ +{ + static struct tm tm; + unsigned long days=(lday&0xff)|((hday&0xff)<<8); + int d; + unsigned int md[12]={31,0,31,30,31,30,31,31,30,31,30,31}; + + tm.tm_sec=0; + tm.tm_min=((min>>4)&0xf)*10+(min&0xf); + tm.tm_hour=((hour>>4)&0xf)*10+(hour&0xf); + tm.tm_mon=0; + tm.tm_year=1978; + tm.tm_isdst=-1; + if (days) --days; + while (days>=(d=(((tm.tm_year%400)==0 || ((tm.tm_year%4)==0 && (tm.tm_year%100))) ? 366 : 365))) + { + days-=d; + ++tm.tm_year; + } + md[1]=((tm.tm_year%400)==0 || ((tm.tm_year%4)==0 && (tm.tm_year%100))) ? 29 : 28; + while (days>=md[tm.tm_mon]) + { + days-=md[tm.tm_mon]; + ++tm.tm_mon; + } + tm.tm_mday=days+1; + tm.tm_year-=1900; + return &tm; +} +/*}}}*/ +static void info(struct cpmSuperBlock *sb, const char *format, const char *image) /*{{{*/ +{ + const char *msg; + + clear(); + msg="File system characteristics"; + move(0,(COLS-strlen(msg))/2); printw(msg); + move(2,0); printw(" Image: %s",image); + move(3,0); printw(" Format: %s",format); + move(4,0); printw(" File system: "); + switch (sb->type) + { + case CPMFS_DR22: printw("CP/M 2.2"); break; + case CPMFS_P2DOS: printw("P2DOS 2.3"); break; + case CPMFS_DR3: printw("CP/M Plus"); break; + } + + move(6,0); printw(" Sector length: %d",sb->secLength); + move(7,0); printw(" Number of tracks: %d",sb->tracks); + move(8,0); printw(" Sectors per track: %d",sb->sectrk); + + move(10,0);printw(" Block size: %d",sb->blksiz); + move(11,0);printw("Number of directory entries: %d",sb->maxdir); + move(12,0);printw(" Logical sector skew: %d",sb->skew); + move(13,0);printw(" Number of system tracks: %d",sb->boottrk); + move(14,0);printw(" Logical extents per extent: %d",sb->extents); + move(15,0);printw(" Allocatable data blocks: %d",sb->size-(sb->maxdir*32+sb->blksiz-1)/sb->blksiz); + + msg="Any key to continue"; + move(23,(COLS-strlen(msg))/2); printw(msg); + getch(); +} +/*}}}*/ +static void map(struct cpmSuperBlock *sb) /*{{{*/ +{ + const char *msg; + char bmap[18*80]; + int secmap,sys,directory; + int pos; + + clear(); + msg="Data map"; + move(0,(COLS-strlen(msg))/2); printw(msg); + + secmap=(sb->tracks*sb->sectrk+80*18-1)/(80*18); + memset(bmap,' ',sizeof(bmap)); + sys=sb->boottrk*sb->sectrk; + memset(bmap,'S',sys/secmap); + directory=(sb->maxdir*32+sb->secLength-1)/sb->secLength; + memset(bmap+sys/secmap,'D',directory/secmap); + memset(bmap+(sys+directory)/secmap,'.',sb->sectrk*sb->tracks/secmap); + + for (pos=0; pos<(sb->maxdir*32+sb->secLength-1)/sb->secLength; ++pos) + { + int entry; + + Device_readSector(&sb->dev,sb->boottrk+pos/(sb->sectrk*sb->secLength),pos/sb->secLength,mapbuf); + for (entry=0; entrysecLength/32 && (pos*sb->secLength/32)+entrymaxdir; ++entry) + { + int i; + + if (mapbuf[entry*32]>=0 && mapbuf[entry*32]<=(sb->type==CPMFS_P2DOS ? 31 : 15)) + { + for (i=0; i<16; ++i) + { + int sector; + + sector=mapbuf[entry*32+16+i]&0xff; + if (sb->size>=256) sector|=(((mapbuf[entry*32+16+ ++i]&0xff)<<8)); + if (sector>0 && sector<=sb->size) + { + /* not entirely correct without the last extent record count */ + sector=sector*(sb->blksiz/sb->secLength)+sb->sectrk*sb->boottrk; + memset(bmap+sector/secmap,'#',sb->blksiz/(sb->secLength*secmap)); + } + } + } + } + } + + for (pos=0; pos<(int)sizeof(bmap); ++pos) + { + move(2+pos%18,pos/18); + addch(bmap[pos]); + } + move(21,0); printw("S=System area D=Directory area #=File data .=Free"); + msg="Any key to continue"; + move(23,(COLS-strlen(msg))/2); printw(msg); + getch(); +} +/*}}}*/ +static void data(struct cpmSuperBlock *sb, const char *buf, unsigned long int pos) /*{{{*/ +{ + int offset=(pos%sb->secLength)&~0x7f; + unsigned int i; + + for (i=0; i<128; ++i) + { + move(4+(i>>4),(i&0x0f)*3+!!(i&0x8)); printw("%02x",buf[i+offset]&0xff); + if (pos%sb->secLength==i+offset) attron(A_REVERSE); + move(4+(i>>4),50+(i&0x0f)); printw("%c",isprint(buf[i+offset]) ? buf[i+offset] : '.'); + attroff(A_REVERSE); + } + move(4+((pos&0x7f)>>4),((pos&0x7f)&0x0f)*3+!!((pos&0x7f)&0x8)+1); +} +/*}}}*/ + +const char cmd[]="fsed.cpm"; + +int main(int argc, char *argv[]) /*{{{*/ +{ + /* variables */ /*{{{*/ + const char *devopts=(const char*)0; + char *image; + const char *err; + struct cpmSuperBlock drive; + struct cpmInode root; + const char *format; + int c,usage=0; + off_t pos; + chtype ch; + int reload; + char *buf; + /*}}}*/ + + /* parse options */ /*{{{*/ + if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; + while ((c=getopt(argc,argv,"T:f:h?"))!=EOF) switch(c) + { + case 'f': format=optarg; break; + case 'T': devopts=optarg; break; + case 'h': + case '?': usage=1; break; + } + + if (optind!=(argc-1)) usage=1; + else image=argv[optind++]; + + if (usage) + { + fprintf(stderr,"Usage: fsed.cpm [-f format] image\n"); + exit(1); + } + /*}}}*/ + /* open image */ /*{{{*/ + if ((err=Device_open(&drive.dev,image,O_RDONLY,devopts))) + { + fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err); + exit(1); + } + if (cpmReadSuper(&drive,&root,format)==-1) + { + fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); + exit(1); + } + /*}}}*/ + /* alloc sector buffers */ /*{{{*/ + if ((buf=malloc(drive.secLength))==(char*)0 || (mapbuf=malloc(drive.secLength))==(char*)0) + { + fprintf(stderr,"fsed.cpm: can not allocate sector buffer (%s).\n",strerror(errno)); + exit(1); + } + /*}}}*/ + /* init curses */ /*{{{*/ + initscr(); + noecho(); + raw(); + nonl(); + idlok(stdscr,TRUE); + idcok(stdscr,TRUE); + keypad(stdscr,TRUE); + clear(); + /*}}}*/ + + pos=0; + reload=1; + do + { + /* display position and load data */ /*{{{*/ + clear(); + move(2,0); printw("Byte %8lu (0x%08lx) ",pos,pos); + if (pos<(drive.boottrk*drive.sectrk*drive.secLength)) + { + printw("Physical sector %3lu ",((pos/drive.secLength)%drive.sectrk)+1); + } + else + { + printw("Sector %3lu ",((pos/drive.secLength)%drive.sectrk)+1); + printw("(physical %3d) ",drive.skewtab[(pos/drive.secLength)%drive.sectrk]+1); + } + printw("Offset %5lu ",pos%drive.secLength); + printw("Track %5lu",pos/(drive.secLength*drive.sectrk)); + move(LINES-3,0); printw("N)ext track P)revious track"); + move(LINES-2,0); printw("n)ext record p)revious record f)orward byte b)ackward byte"); + move(LINES-1,0); printw("i)nfo q)uit"); + if (reload) + { + if (pos<(drive.boottrk*drive.sectrk*drive.secLength)) + { + err=Device_readSector(&drive.dev,pos/(drive.secLength*drive.sectrk),(pos/drive.secLength)%drive.sectrk,buf); + } + else + { + err=Device_readSector(&drive.dev,pos/(drive.secLength*drive.sectrk),drive.skewtab[(pos/drive.secLength)%drive.sectrk],buf); + } + if (err) + { + move(4,0); printw("Data can not be read: %s",err); + } + else reload=0; + } + /*}}}*/ + + if /* position before end of system area */ /*{{{*/ + (pos<(drive.boottrk*drive.sectrk*drive.secLength)) + { + const char *msg; + + msg="System area"; move(0,(COLS-strlen(msg))/2); printw(msg); + move(LINES-3,36); printw("F)orward 16 byte B)ackward 16 byte"); + if (!reload) data(&drive,buf,pos); + switch (ch=getch()) + { + case 'F': /* next 16 byte */ /*{{{*/ + { + if (pos+16<(drive.sectrk*drive.tracks*(off_t)drive.secLength)) + { + if (pos/drive.secLength!=(pos+16)/drive.secLength) reload=1; + pos+=16; + } + break; + } + /*}}}*/ + case 'B': /* previous 16 byte */ /*{{{*/ + { + if (pos>=16) + { + if (pos/drive.secLength!=(pos-16)/drive.secLength) reload=1; + pos-=16; + } + break; + } + /*}}}*/ + } + } + /*}}}*/ + else if /* position before end of directory area */ /*{{{*/ + (pos<(drive.boottrk*drive.sectrk*drive.secLength+drive.maxdir*32)) + { + const char *msg; + unsigned long entrystart=(pos&~0x1f)%drive.secLength; + int entry=(pos-(drive.boottrk*drive.sectrk*drive.secLength))>>5; + int offset=pos&0x1f; + + msg="Directory area"; move(0,(COLS-strlen(msg))/2); printw(msg); + move(LINES-3,36); printw("F)orward entry B)ackward entry"); + + move(13,0); printw("Entry %3d: ",entry); + if /* free or used directory entry */ /*{{{*/ + ((buf[entrystart]>=0 && buf[entrystart]<=(drive.type==CPMFS_P2DOS ? 31 : 15)) || buf[entrystart]==(char)0xe5) + { + int i; + + if (buf[entrystart]==(char)0xe5) + { + if (offset==0) attron(A_REVERSE); + printw("Free"); + attroff(A_REVERSE); + } + else printw("Directory entry"); + move(15,0); + if (buf[entrystart]!=(char)0xe5) + { + printw("User: "); + if (offset==0) attron(A_REVERSE); + printw("%2d",buf[entrystart]); + attroff(A_REVERSE); + printw(" "); + } + printw("Name: "); + for (i=0; i<8; ++i) + { + if (offset==1+i) attron(A_REVERSE); + printw("%c",buf[entrystart+1+i]&0x7f); + attroff(A_REVERSE); + } + printw(" Extension: "); + for (i=0; i<3; ++i) + { + if (offset==9+i) attron(A_REVERSE); + printw("%c",buf[entrystart+9+i]&0x7f); + attroff(A_REVERSE); + } + move(16,0); printw("Extent: %3d",((buf[entrystart+12]&0xff)+((buf[entrystart+14]&0xff)<<5))/drive.extents); + printw(" (low: "); + if (offset==12) attron(A_REVERSE); + printw("%2d",buf[entrystart+12]&0xff); + attroff(A_REVERSE); + printw(", high: "); + if (offset==14) attron(A_REVERSE); + printw("%2d",buf[entrystart+14]&0xff); + attroff(A_REVERSE); + printw(")"); + move(17,0); printw("Last extent record count: "); + if (offset==15) attron(A_REVERSE); + printw("%3d",buf[entrystart+15]&0xff); + attroff(A_REVERSE); + move(18,0); printw("Last record byte count: "); + if (offset==13) attron(A_REVERSE); + printw("%3d",buf[entrystart+13]&0xff); + attroff(A_REVERSE); + move(19,0); printw("Data blocks:"); + for (i=0; i<16; ++i) + { + unsigned int block=buf[entrystart+16+i]&0xff; + if (drive.size>=256) + { + printw(" "); + if (offset==16+i || offset==16+i+1) attron(A_REVERSE); + printw("%5d",block|(((buf[entrystart+16+ ++i]&0xff)<<8))); + attroff(A_REVERSE); + } + else + { + printw(" "); + if (offset==16+i) attron(A_REVERSE); + printw("%3d",block); + attroff(A_REVERSE); + } + } + } + /*}}}*/ + else if /* disc label */ /*{{{*/ + (buf[entrystart]==0x20 && drive.type==CPMFS_DR3) + { + int i; + const struct tm *tm; + char s[30]; + + if (offset==0) attron(A_REVERSE); + printw("Disc label"); + attroff(A_REVERSE); + move(15,0); + printw("Label: "); + for (i=0; i<11; ++i) + { + if (i+1==offset) attron(A_REVERSE); + printw("%c",buf[entrystart+1+i]&0x7f); + attroff(A_REVERSE); + } + move(16,0); + printw("Bit 0,7: "); + if (offset==12) attron(A_REVERSE); + printw("Label %s",buf[entrystart+12]&1 ? "set" : "not set"); + printw(", password protection %s",buf[entrystart+12]&0x80 ? "set" : "not set"); + attroff(A_REVERSE); + move(17,0); + printw("Bit 4,5,6: "); + if (offset==12) attron(A_REVERSE); + printw("Time stamp "); + if (buf[entrystart+12]&0x10) printw("on create, "); + else printw("not on create, "); + if (buf[entrystart+12]&0x20) printw("on modification, "); + else printw("not on modifiction, "); + if (buf[entrystart+12]&0x40) printw("on access"); + else printw("not on access"); + attroff(A_REVERSE); + move(18,0); + printw("Password: "); + for (i=0; i<8; ++i) + { + char printable; + + if (offset==16+(7-i)) attron(A_REVERSE); + printable=(buf[entrystart+16+(7-i)]^buf[entrystart+13])&0x7f; + printw("%c",isprint(printable) ? printable : ' '); + attroff(A_REVERSE); + } + printw(" XOR value: "); + if (offset==13) attron(A_REVERSE); + printw("0x%02x",buf[entrystart+13]&0xff); + attroff(A_REVERSE); + move(19,0); + printw("Created: "); + tm=cpmtime(buf[entrystart+24],buf[entrystart+25],buf[entrystart+26],buf[entrystart+27]); + if (offset==24 || offset==25) attron(A_REVERSE); + strftime(s,sizeof(s),"%x",tm); + printw("%s",s); + attroff(A_REVERSE); + printw(" "); + if (offset==26) attron(A_REVERSE); + printw("%2d",tm->tm_hour); + attroff(A_REVERSE); + printw(":"); + if (offset==27) attron(A_REVERSE); + printw("%02d",tm->tm_min); + attroff(A_REVERSE); + printw(" Updated: "); + tm=cpmtime(buf[entrystart+28],buf[entrystart+29],buf[entrystart+30],buf[entrystart+31]); + if (offset==28 || offset==29) attron(A_REVERSE); + strftime(s,sizeof(s),"%x",tm); + printw("%s",s); + attroff(A_REVERSE); + printw(" "); + if (offset==30) attron(A_REVERSE); + printw("%2d",tm->tm_hour); + attroff(A_REVERSE); + printw(":"); + if (offset==31) attron(A_REVERSE); + printw("%02d",tm->tm_min); + attroff(A_REVERSE); + } + /*}}}*/ + else if /* time stamp */ /*{{{*/ + (buf[entrystart]==0x21 && (drive.type==CPMFS_P2DOS || drive.type==CPMFS_DR3)) + { + const struct tm *tm; + char s[30]; + + if (offset==0) attron(A_REVERSE); + printw("Time stamps"); + attroff(A_REVERSE); + move(15,0); + printw("3rd last extent: Created/Accessed "); + tm=cpmtime(buf[entrystart+1],buf[entrystart+2],buf[entrystart+3],buf[entrystart+4]); + if (offset==1 || offset==2) attron(A_REVERSE); + strftime(s,sizeof(s),"%x",tm); + printw("%s",s); + attroff(A_REVERSE); + printw(" "); + if (offset==3) attron(A_REVERSE); + printw("%2d",tm->tm_hour); + attroff(A_REVERSE); + printw(":"); + if (offset==4) attron(A_REVERSE); + printw("%02d",tm->tm_min); + attroff(A_REVERSE); + printw(" Modified "); + tm=cpmtime(buf[entrystart+5],buf[entrystart+6],buf[entrystart+7],buf[entrystart+8]); + if (offset==5 || offset==6) attron(A_REVERSE); + strftime(s,sizeof(s),"%x",tm); + printw("%s",s); + attroff(A_REVERSE); + printw(" "); + if (offset==7) attron(A_REVERSE); + printw("%2d",tm->tm_hour); + attroff(A_REVERSE); + printw(":"); + if (offset==8) attron(A_REVERSE); + printw("%02d",tm->tm_min); + attroff(A_REVERSE); + + move(16,0); + printw("2nd last extent: Created/Accessed "); + tm=cpmtime(buf[entrystart+11],buf[entrystart+12],buf[entrystart+13],buf[entrystart+14]); + if (offset==11 || offset==12) attron(A_REVERSE); + strftime(s,sizeof(s),"%x",tm); + printw("%s",s); + attroff(A_REVERSE); + printw(" "); + if (offset==13) attron(A_REVERSE); + printw("%2d",tm->tm_hour); + attroff(A_REVERSE); + printw(":"); + if (offset==14) attron(A_REVERSE); + printw("%02d",tm->tm_min); + attroff(A_REVERSE); + printw(" Modified "); + tm=cpmtime(buf[entrystart+15],buf[entrystart+16],buf[entrystart+17],buf[entrystart+18]); + if (offset==15 || offset==16) attron(A_REVERSE); + strftime(s,sizeof(s),"%x",tm); + printw("%s",s); + attroff(A_REVERSE); + printw(" "); + if (offset==17) attron(A_REVERSE); + printw("%2d",tm->tm_hour); + attroff(A_REVERSE); + printw(":"); + if (offset==18) attron(A_REVERSE); + printw("%02d",tm->tm_min); + attroff(A_REVERSE); + + move(17,0); + printw(" Last extent: Created/Accessed "); + tm=cpmtime(buf[entrystart+21],buf[entrystart+22],buf[entrystart+23],buf[entrystart+24]); + if (offset==21 || offset==22) attron(A_REVERSE); + strftime(s,sizeof(s),"%x",tm); + printw("%s",s); + attroff(A_REVERSE); + printw(" "); + if (offset==23) attron(A_REVERSE); + printw("%2d",tm->tm_hour); + attroff(A_REVERSE); + printw(":"); + if (offset==24) attron(A_REVERSE); + printw("%02d",tm->tm_min); + attroff(A_REVERSE); + printw(" Modified "); + tm=cpmtime(buf[entrystart+25],buf[entrystart+26],buf[entrystart+27],buf[entrystart+28]); + if (offset==25 || offset==26) attron(A_REVERSE); + strftime(s,sizeof(s),"%x",tm); + printw("%s",s); + attroff(A_REVERSE); + printw(" "); + if (offset==27) attron(A_REVERSE); + printw("%2d",tm->tm_hour); + attroff(A_REVERSE); + printw(":"); + if (offset==28) attron(A_REVERSE); + printw("%02d",tm->tm_min); + attroff(A_REVERSE); + } + /*}}}*/ + else if /* password */ /*{{{*/ + (buf[entrystart]>=16 && buf[entrystart]<=31 && drive.type==CPMFS_DR3) + { + int i; + + if (offset==0) attron(A_REVERSE); + printw("Password"); + attroff(A_REVERSE); + + move(15,0); + printw("Name: "); + for (i=0; i<8; ++i) + { + if (offset==1+i) attron(A_REVERSE); + printw("%c",buf[entrystart+1+i]&0x7f); + attroff(A_REVERSE); + } + printw(" Extension: "); + for (i=0; i<3; ++i) + { + if (offset==9+i) attron(A_REVERSE); + printw("%c",buf[entrystart+9+i]&0x7f); + attroff(A_REVERSE); + } + + move(16,0); + printw("Password required for: "); + if (offset==12) attron(A_REVERSE); + if (buf[entrystart+12]&0x80) printw("Reading "); + if (buf[entrystart+12]&0x40) printw("Writing "); + if (buf[entrystart+12]&0x20) printw("Deleting "); + attroff(A_REVERSE); + + move(17,0); + printw("Password: "); + for (i=0; i<8; ++i) + { + char printable; + + if (offset==16+(7-i)) attron(A_REVERSE); + printable=(buf[entrystart+16+(7-i)]^buf[entrystart+13])&0x7f; + printw("%c",isprint(printable) ? printable : ' '); + attroff(A_REVERSE); + } + printw(" XOR value: "); + if (offset==13) attron(A_REVERSE); + printw("0x%02x",buf[entrystart+13]&0xff); + attroff(A_REVERSE); + } + /*}}}*/ + else /* bad status */ /*{{{*/ + { + printw("Bad status "); + if (offset==0) attron(A_REVERSE); + printw("0x%02x",buf[entrystart]); + attroff(A_REVERSE); + } + /*}}}*/ + if (!reload) data(&drive,buf,pos); + switch (ch=getch()) + { + case 'F': /* next entry */ /*{{{*/ + { + if (pos+32<(drive.sectrk*drive.tracks*(off_t)drive.secLength)) + { + if (pos/drive.secLength!=(pos+32)/drive.secLength) reload=1; + pos+=32; + } + break; + } + /*}}}*/ + case 'B': /* previous entry */ /*{{{*/ + { + if (pos>=32) + { + if (pos/drive.secLength!=(pos-32)/drive.secLength) reload=1; + pos-=32; + } + break; + } + /*}}}*/ + } + } + /*}}}*/ + else /* data area */ /*{{{*/ + { + const char *msg; + + msg="Data area"; move(0,(COLS-strlen(msg))/2); printw(msg); + if (!reload) data(&drive,buf,pos); + ch=getch(); + } + /*}}}*/ + + /* process common commands */ /*{{{*/ + switch (ch) + { + case 'n': /* next record */ /*{{{*/ + { + if (pos+128<(drive.sectrk*drive.tracks*(off_t)drive.secLength)) + { + if (pos/drive.secLength!=(pos+128)/drive.secLength) reload=1; + pos+=128; + } + break; + } + /*}}}*/ + case 'p': /* previous record */ /*{{{*/ + { + if (pos>=128) + { + if (pos/drive.secLength!=(pos-128)/drive.secLength) reload=1; + pos-=128; + } + break; + } + /*}}}*/ + case 'N': /* next track */ /*{{{*/ + { + if ((pos+drive.sectrk*drive.secLength)<(drive.sectrk*drive.tracks*drive.secLength)) + { + pos+=drive.sectrk*drive.secLength; + reload=1; + } + break; + } + /*}}}*/ + case 'P': /* previous track */ /*{{{*/ + { + if (pos>=drive.sectrk*drive.secLength) + { + pos-=drive.sectrk*drive.secLength; + reload=1; + } + break; + } + /*}}}*/ + case 'b': /* byte back */ /*{{{*/ + { + if (pos) + { + if (pos/drive.secLength!=(pos-1)/drive.secLength) reload=1; + --pos; + } + break; + } + /*}}}*/ + case 'f': /* byte forward */ /*{{{*/ + { + if (pos+1 +#include +#include +#include + +#ifdef __VMS +# include +#endif + +#ifdef _LIBC +# include +#else +#if 0 +# include +# define _(msgid) gettext (msgid) +#else +# define _(msgid) (msgid) +#endif +#endif + +#if defined _LIBC && defined USE_IN_LIBIO +# include +#endif + +#ifndef attribute_hidden +# define attribute_hidden +#endif + +/* Unlike standard Unix `getopt', functions like `getopt_long' + let the user intersperse the options with the other arguments. + + As `getopt_long' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Using `getopt' or setting the environment variable POSIXLY_CORRECT + disables permutation. + Then the application's behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt_int.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Keep a global copy of all internal members of getopt_data. */ + +static struct _getopt_data getopt_data; + + +#if defined HAVE_DECL_GETENV && !HAVE_DECL_GETENV +extern char *getenv (); +#endif + +#ifdef _LIBC +/* Stored original parameters. + XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ + if (d->__nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (char **argv, struct _getopt_data *d) +{ + int bottom = d->__first_nonopt; + int middle = d->__last_nonopt; + int top = d->optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + d->__nonoption_flags_max_len), + '\0', top + 1 - d->__nonoption_flags_max_len); + d->__nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + d->__first_nonopt += (d->optind - d->__last_nonopt); + d->__last_nonopt = d->optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (int argc, char **argv, const char *optstring, + int posixly_correct, struct _getopt_data *d) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + d->__first_nonopt = d->__last_nonopt = d->optind; + + d->__nextchar = NULL; + + d->__posixly_correct = posixly_correct || !!getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + d->__ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + d->__ordering = REQUIRE_ORDER; + ++optstring; + } + else if (d->__posixly_correct) + d->__ordering = REQUIRE_ORDER; + else + d->__ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (!d->__posixly_correct + && argc == __libc_argc && argv == __libc_argv) + { + if (d->__nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + d->__nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = d->__nonoption_flags_max_len = strlen (orig_str); + if (d->__nonoption_flags_max_len < argc) + d->__nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (d->__nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + d->__nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', d->__nonoption_flags_max_len - len); + } + } + d->__nonoption_flags_len = d->__nonoption_flags_max_len; + } + else + d->__nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. + + If POSIXLY_CORRECT is nonzero, behave as if the POSIXLY_CORRECT + environment variable were set. */ + +int +_getopt_internal_r (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only, int posixly_correct, struct _getopt_data *d) +{ + int print_errors = d->opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + d->optarg = NULL; + + if (d->optind == 0 || !d->__initialized) + { + if (d->optind == 0) + d->optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring, + posixly_correct, d); + d->__initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \ + || (d->optind < d->__nonoption_flags_len \ + && __getopt_nonoption_flags[d->optind] == '1')) +#else +# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') +#endif + + if (d->__nextchar == NULL || *d->__nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (d->__last_nonopt > d->optind) + d->__last_nonopt = d->optind; + if (d->__first_nonopt > d->optind) + d->__first_nonopt = d->optind; + + if (d->__ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange ((char **) argv, d); + else if (d->__last_nonopt != d->optind) + d->__first_nonopt = d->optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (d->optind < argc && NONOPTION_P) + d->optind++; + d->__last_nonopt = d->optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (d->optind != argc && !strcmp (argv[d->optind], "--")) + { + d->optind++; + + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange ((char **) argv, d); + else if (d->__first_nonopt == d->__last_nonopt) + d->__first_nonopt = d->optind; + d->__last_nonopt = argc; + + d->optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (d->optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (d->__first_nonopt != d->__last_nonopt) + d->optind = d->__first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (d->__ordering == REQUIRE_ORDER) + return -1; + d->optarg = argv[d->optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + d->__nextchar = (argv[d->optind] + 1 + + (longopts != NULL && argv[d->optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[d->optind][1] == '-' + || (long_only && (argv[d->optind][2] + || !strchr (optstring, argv[d->optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) + { + if ((unsigned int) (nameend - d->__nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[d->optind]) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[d->optind]); +#endif + } + d->__nextchar += strlen (d->__nextchar); + d->optind++; + d->optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + d->optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + if (argv[d->optind - 1][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#else + fprintf (stderr, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("\ +%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[d->optind - 1][0], + pfound->name); +#else + fprintf (stderr, _("\ +%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[d->optind - 1][0], + pfound->name); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (n >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#endif + } + + d->__nextchar += strlen (d->__nextchar); + + d->optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]); +#endif + } + d->__nextchar += strlen (d->__nextchar); + d->optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + d->__nextchar += strlen (d->__nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[d->optind][1] == '-' + || strchr (optstring, *d->__nextchar) == NULL) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + if (argv[d->optind][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), + argv[0], d->__nextchar); +#else + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], d->__nextchar); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[d->optind][0], d->__nextchar); +#else + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[d->optind][0], d->__nextchar); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (n >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#endif + } + d->__nextchar = (char *) ""; + d->optind++; + d->optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *d->__nextchar++; + char *temp = strchr (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*d->__nextchar == '\0') + ++d->optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + if (d->__posixly_correct) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: illegal option -- %c\n"), + argv[0], c); +#else + fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); +#endif + } + else + { +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: invalid option -- %c\n"), + argv[0], c); +#else + fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (n >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#endif + } + d->optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + d->optind++; + } + else if (d->optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, + _("%s: option requires an argument -- %c\n"), + argv[0], c) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `d->optind' once; + increment it again when taking next ARGV-elt as argument. */ + d->optarg = argv[d->optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; + nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) + { + if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[d->optind]) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[d->optind]); +#endif + } + d->__nextchar += strlen (d->__nextchar); + d->optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + + d->__nextchar += strlen (d->__nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]); +#endif + } + d->__nextchar += strlen (d->__nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + d->__nextchar += strlen (d->__nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + d->__nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + d->optind++; + } + else + d->optarg = NULL; + d->__nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + d->optind++; + } + else if (d->optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option requires an argument -- %c\n"), + argv[0], c) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + d->optarg = argv[d->optind++]; + d->__nextchar = NULL; + } + } + return c; + } +} + +int +_getopt_internal (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only, int posixly_correct) +{ + int result; + + getopt_data.optind = optind; + getopt_data.opterr = opterr; + + result = _getopt_internal_r (argc, argv, optstring, longopts, longind, + long_only, posixly_correct, &getopt_data); + + optind = getopt_data.optind; + optarg = getopt_data.optarg; + optopt = getopt_data.optopt; + + return result; +} + +/* glibc gets a LSB-compliant getopt. + Standalone applications get a POSIX-compliant getopt. */ +#if _LIBC +enum { POSIXLY_CORRECT = 0 }; +#else +enum { POSIXLY_CORRECT = 1 }; +#endif + +int +getopt (int argc, char *const *argv, const char *optstring) +{ + return _getopt_internal (argc, (char **) argv, optstring, NULL, NULL, 0, + POSIXLY_CORRECT); +} + + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/Tools/unix/cpmtools/getopt1.c b/Tools/unix/cpmtools/getopt1.c new file mode 100644 index 00000000..7fa0c4e5 --- /dev/null +++ b/Tools/unix/cpmtools/getopt1.c @@ -0,0 +1,171 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2004,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifdef _LIBC +# include +#else +# include "config.h" +# include "getopt_.h" +#endif +#include "getopt_int.h" + +#include + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 0, 0); +} + +int +_getopt_long_r (int argc, char **argv, const char *options, + const struct option *long_options, int *opt_index, + struct _getopt_data *d) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 0, 0, d); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (int argc, char *__getopt_argv_const *argv, + const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 1, 0); +} + +int +_getopt_long_only_r (int argc, char **argv, const char *options, + const struct option *long_options, int *opt_index, + struct _getopt_data *d) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 1, 0, d); +} + + +#ifdef TEST + +#include + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/Tools/unix/cpmtools/getopt_.h b/Tools/unix/cpmtools/getopt_.h new file mode 100644 index 00000000..615ef9a3 --- /dev/null +++ b/Tools/unix/cpmtools/getopt_.h @@ -0,0 +1,226 @@ +/* Declarations for getopt. + Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2005,2006,2007 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* Standalone applications should #define __GETOPT_PREFIX to an + identifier that prefixes the external functions and variables + defined in this header. When this happens, include the + headers that might declare getopt so that they will not cause + confusion if included after this file. Then systematically rename + identifiers so that they do not collide with the system functions + and variables. Renaming avoids problems with some compilers and + linkers. */ +#if defined __GETOPT_PREFIX && !defined __need_getopt +# include +# include +# include +# undef __need_getopt +# undef getopt +# undef getopt_long +# undef getopt_long_only +# undef optarg +# undef opterr +# undef optind +# undef optopt +# define __GETOPT_CONCAT(x, y) x ## y +# define __GETOPT_XCONCAT(x, y) __GETOPT_CONCAT (x, y) +# define __GETOPT_ID(y) __GETOPT_XCONCAT (__GETOPT_PREFIX, y) +# define getopt __GETOPT_ID (getopt) +# define getopt_long __GETOPT_ID (getopt_long) +# define getopt_long_only __GETOPT_ID (getopt_long_only) +# define optarg __GETOPT_ID (optarg) +# define opterr __GETOPT_ID (opterr) +# define optind __GETOPT_ID (optind) +# define optopt __GETOPT_ID (optopt) +#endif + +/* Standalone applications get correct prototypes for getopt_long and + getopt_long_only; they declare "char **argv". libc uses prototypes + with "char *const *argv" that are incorrect because getopt_long and + getopt_long_only can permute argv; this is required for backward + compatibility (e.g., for LSB 2.0.1). + + This used to be `#if defined __GETOPT_PREFIX && !defined __need_getopt', + but it caused redefinition warnings if both unistd.h and getopt.h were + included, since unistd.h includes getopt.h having previously defined + __need_getopt. + + The only place where __getopt_argv_const is used is in definitions + of getopt_long and getopt_long_only below, but these are visible + only if __need_getopt is not defined, so it is quite safe to rewrite + the conditional as follows: +*/ +#if !defined __need_getopt +# if defined __GETOPT_PREFIX +# define __getopt_argv_const /* empty */ +# else +# define __getopt_argv_const const +# endif +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include , but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include , which will pull in for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include +#endif + +#ifndef __THROW +# ifndef __GNUC_PREREQ +# define __GNUC_PREREQ(maj, min) (0) +# endif +# if defined __cplusplus && __GNUC_PREREQ (2,8) +# define __THROW throw () +# else +# define __THROW +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `-', then non-option arguments are treated as + arguments to the option '\1'. This behavior is specific to the GNU + `getopt'. If OPTS begins with `+', or POSIXLY_CORRECT is set in + the environment, then do not permute arguments. */ + +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) + __THROW; + +#ifndef __need_getopt +extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; +extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; + +#endif + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/Tools/unix/cpmtools/getopt_int.h b/Tools/unix/cpmtools/getopt_int.h new file mode 100644 index 00000000..401579fd --- /dev/null +++ b/Tools/unix/cpmtools/getopt_int.h @@ -0,0 +1,131 @@ +/* Internal declarations for getopt. + Copyright (C) 1989-1994,1996-1999,2001,2003,2004 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GETOPT_INT_H +#define _GETOPT_INT_H 1 + +extern int _getopt_internal (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct); + + +/* Reentrant versions which can handle parsing multiple argument + vectors at the same time. */ + +/* Data type for reentrant functions. */ +struct _getopt_data +{ + /* These have exactly the same meaning as the corresponding global + variables, except that they are used for the reentrant + versions of getopt. */ + int optind; + int opterr; + int optopt; + char *optarg; + + /* Internal members. */ + + /* True if the internal members have been initialized. */ + int __initialized; + + /* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + char *__nextchar; + + /* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters, or by calling getopt. + + PERMUTE is the default. We permute the contents of ARGV as we + scan, so that eventually all the non-options are at the end. + This allows options to be given in any order, even with programs + that were not written to expect this. + + RETURN_IN_ORDER is an option available to programs that were + written to expect options and other ARGV-elements in any order + and that care about the ordering of the two. We describe each + non-option ARGV-element as if it were the argument of an option + with character code 1. Using `-' as the first character of the + list of option characters selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + + enum + { + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER + } __ordering; + + /* If the POSIXLY_CORRECT environment variable is set + or getopt was called. */ + int __posixly_correct; + + + /* Handle permutation of arguments. */ + + /* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first + of them; `last_nonopt' is the index after the last of them. */ + + int __first_nonopt; + int __last_nonopt; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + int __nonoption_flags_max_len; + int __nonoption_flags_len; +# endif +}; + +/* The initializer is necessary to set OPTIND and OPTERR to their + default values and to clear the initialization flag. */ +#define _GETOPT_DATA_INITIALIZER { 1, 1 } + +extern int _getopt_internal_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct, + struct _getopt_data *__data); + +extern int _getopt_long_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + struct _getopt_data *__data); + +extern int _getopt_long_only_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, + int *__longind, + struct _getopt_data *__data); + +#endif /* getopt_int.h */ diff --git a/Tools/unix/cpmtools/mkfs.cpm.1 b/Tools/unix/cpmtools/mkfs.cpm.1 new file mode 100644 index 00000000..b31fb7ce --- /dev/null +++ b/Tools/unix/cpmtools/mkfs.cpm.1 @@ -0,0 +1,70 @@ +.TH MKFS.CPM 1 "October 25, 2014" "CP/M tools" "User commands" +.SH NAME \"{{{roff}}}\"{{{ +mkfs.cpm \- make a CP/M file system +.\"}}} +.SH SYNOPSIS \"{{{ +.ad l +.B mkfs.cpm +.RB [ \-f +.IR format ] +.RB [ \-b +.IR boot ] +.RB [ \-L +.IR label ] +.RB [ \-t ] +.I image +.ad b +.\"}}} +.SH DESCRIPTION \"{{{ +\fBmkfs.cpm\fP makes a CP/M file system on an image file or device. +.\"}}} +.SH OPTIONS \"{{{ +.IP "\fB\-f\fP \fIformat\fP" +Use the given CP/M disk \fIformat\fP instead of the default format. +.IP "\fB\-b\fP \fIbootblock\fP" +Write the contents of the file \fIbootblock\fP to the system tracks +instead of filling them with 0xe5. This option can be used up to four +times. The file contents (typically boot block, CCP, BDOS and BIOS) +are written to sequential sectors, padding with 0xe5 if needed. +.IP "\fB\-L\fP \fIlabel\fP" +Label the file system. This is only supported by CP/M Plus. +.IP "\fB\-t\fP" +Create time stamps. +.\"}}} +.SH "RETURN VALUE" \"{{{ +Upon successful completion, exit code 0 is returned. +.\"}}} +.SH ERRORS \"{{{ +Any errors are indicated by exit code 1. +.\"}}} +.SH ENVIRONMENT \"{{{ +CPMTOOLSFMT Default format +.\"}}} +.SH FILES \"{{{ +${prefix}/share/diskdefs CP/M disk format definitions +.\"}}} +.SH AUTHORS \"{{{ +This program is copyright 1997\(en2012 Michael Haardt +. The Windows port is copyright 2000, 2001, 2011 John Elliott +. +.PP +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +.PP +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +.PP +You should have received a copy of the GNU General Public License along +with this program. If not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +.\"}}} +.SH "SEE ALSO" \"{{{ +.IR fsck.cpm (1), +.IR cpmls (1), +.IR mkfs (1), +.IR cpm (5) +.\"}}} diff --git a/Tools/unix/cpmtools/mkfs.cpm.c b/Tools/unix/cpmtools/mkfs.cpm.c new file mode 100644 index 00000000..2c37bdfd --- /dev/null +++ b/Tools/unix/cpmtools/mkfs.cpm.c @@ -0,0 +1,234 @@ +/* #includes */ /*{{{C}}}*//*{{{*/ +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "getopt_.h" +#include "cpmfs.h" + +#ifdef USE_DMALLOC +#include +#endif +/*}}}*/ +/* #defines */ /*{{{*/ +#ifndef O_BINARY +#define O_BINARY 0 +#endif +/*}}}*/ + +/* mkfs -- make file system */ /*{{{*/ +static int mkfs(struct cpmSuperBlock *drive, const char *name, const char *format, const char *label, char *bootTracks, int timeStamps) +{ + /* variables */ /*{{{*/ + unsigned int i; + char buf[128]; + char firstbuf[128]; + int fd; + unsigned int bytes; + unsigned int trkbytes; + /*}}}*/ + + /* open image file */ /*{{{*/ + if ((fd = open(name, O_BINARY|O_CREAT|O_WRONLY, 0666)) < 0) + { + boo=strerror(errno); + return -1; + } + /*}}}*/ + /* write system tracks */ /*{{{*/ + /* this initialises only whole tracks, so it skew is not an issue */ + trkbytes=drive->secLength*drive->sectrk; + for (i=0; iboottrk; i+=drive->secLength) if (write(fd, bootTracks+i, drive->secLength)!=(ssize_t)drive->secLength) + { + boo=strerror(errno); + close(fd); + return -1; + } + /*}}}*/ + /* write directory */ /*{{{*/ + memset(buf,0xe5,128); + bytes=drive->maxdir*32; + if (bytes%trkbytes) bytes=((bytes+trkbytes)/trkbytes)*trkbytes; + if (timeStamps && (drive->type==CPMFS_P2DOS || drive->type==CPMFS_DR3)) buf[3*32]=0x21; + memcpy(firstbuf,buf,128); + if (drive->type==CPMFS_DR3) + { + time_t now; + struct tm *t; + int min,hour,days; + + firstbuf[0]=0x20; + for (i=0; i<11 && *label; ++i,++label) firstbuf[1+i]=toupper(*label&0x7f); + while (i<11) firstbuf[1+i++]=' '; + firstbuf[12]=timeStamps ? 0x11 : 0x01; /* label set and first time stamp is creation date */ + memset(&firstbuf[13],0,1+2+8); + if (timeStamps) + { + int year; + + /* Stamp label. */ + time(&now); + t=localtime(&now); + min=((t->tm_min/10)<<4)|(t->tm_min%10); + hour=((t->tm_hour/10)<<4)|(t->tm_hour%10); + for (year=1978,days=0; year<1900+t->tm_year; ++year) + { + days+=365; + if (year%4==0 && (year%100!=0 || year%400==0)) ++days; + } + days += t->tm_yday + 1; + firstbuf[24]=firstbuf[28]=days&0xff; firstbuf[25]=firstbuf[29]=days>>8; + firstbuf[26]=firstbuf[30]=hour; + firstbuf[27]=firstbuf[31]=min; + } + } + for (i=0; itype==CPMFS_P2DOS || drive->type==CPMFS_DR3)) /*{{{*/ + { + int offset,j; + struct cpmInode ino, root; + static const char sig[] = "!!!TIME"; + unsigned int records; + struct dsDate *ds; + struct cpmSuperBlock super; + const char *err; + + if ((err=Device_open(&super.dev,name,O_RDWR,NULL))) + { + fprintf(stderr,"%s: can not open %s (%s)\n",cmd,name,err); + exit(1); + } + cpmReadSuper(&super,&root,format); + + records=root.sb->maxdir/8; + if (!(ds=malloc(records*128))) + { + cpmUmount(&super); + return -1; + } + memset(ds,0,records*128); + offset=15; + for (i=0; ids=ds; + root.sb->dirtyDs=1; + cpmUmount(&super); + } + /*}}}*/ + + return 0; +} +/*}}}*/ + +const char cmd[]="mkfs.cpm"; + +int main(int argc, char *argv[]) /*{{{*/ +{ + char *image; + const char *format; + int c,usage=0; + struct cpmSuperBlock drive; + struct cpmInode root; + const char *label="unlabeled"; + int timeStamps=0; + size_t bootTrackSize,used; + char *bootTracks; + const char *boot[4]={(const char*)0,(const char*)0,(const char*)0,(const char*)0}; + + if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; + while ((c=getopt(argc,argv,"b:f:L:th?"))!=EOF) switch(c) + { + case 'b': + { + if (boot[0]==(const char*)0) boot[0]=optarg; + else if (boot[1]==(const char*)0) boot[1]=optarg; + else if (boot[2]==(const char*)0) boot[2]=optarg; + else if (boot[3]==(const char*)0) boot[3]=optarg; + else usage=1; + break; + } + case 'f': format=optarg; break; + case 'L': label=optarg; break; + case 't': timeStamps=1; break; + case 'h': + case '?': usage=1; break; + } + + if (optind!=(argc-1)) usage=1; + else image=argv[optind++]; + + if (usage) + { + fprintf(stderr,"Usage: %s [-f format] [-b boot] [-L label] [-t] image\n",cmd); + exit(1); + } + drive.dev.opened=0; + cpmReadSuper(&drive,&root,format); + bootTrackSize=drive.boottrk*drive.secLength*drive.sectrk; + if ((bootTracks=malloc(bootTrackSize))==(void*)0) + { + fprintf(stderr,"%s: can not allocate boot track buffer: %s\n",cmd,strerror(errno)); + exit(1); + } + memset(bootTracks,0xe5,bootTrackSize); + used=0; + for (c=0; c<4 && boot[c]; ++c) + { + int fd; + size_t size; + + if ((fd=open(boot[c],O_BINARY|O_RDONLY))==-1) + { + fprintf(stderr,"%s: can not open %s: %s\n",cmd,boot[c],strerror(errno)); + exit(1); + } + size=read(fd,bootTracks+used,bootTrackSize-used); +#if 0 + fprintf(stderr,"%d %04x %s\n",c,used+0x800,boot[c]); +#endif + if (size%drive.secLength) size=(size|(drive.secLength-1))+1; + used+=size; + close(fd); + } + if (mkfs(&drive,image,format,label,bootTracks,timeStamps)==-1) + { + fprintf(stderr,"%s: can not make new file system: %s\n",cmd,boo); + exit(1); + } + else exit(0); +} +/*}}}*/ diff --git a/Tools/unix/lzsa/BlockFormat_LZSA1.md b/Tools/unix/lzsa/BlockFormat_LZSA1.md new file mode 100644 index 00000000..5264354e --- /dev/null +++ b/Tools/unix/lzsa/BlockFormat_LZSA1.md @@ -0,0 +1,65 @@ +# Block data format (LZSA1) + +Blocks encoded as LZSA1 are composed from consecutive commands. Each command follows this format: + +* token: +* optional extra literal length +* literal values +* match offset low +* optional match offset high +* optional extra encoded match length + +**token** + +The token byte is broken down into three parts: + + 7 6 5 4 3 2 1 0 + O L L L M M M M + +* L: 3-bit literals length (0-6, or 7 if extended). If the number of literals for this command is 0 to 6, the length is encoded in the token and no extra bytes are required. Otherwise, a value of 7 is encoded and extra bytes follow as 'optional extra literal length' +* M: 4-bit encoded match length (0-14, or 15 if extended). Likewise, if the encoded match length for this command is 0 to 14, it is directly stored, otherwise 15 is stored and extra bytes follow as 'optional extra encoded match length'. Except for the last command in a block, a command always contains a match, so the encoded match length is the actual match length offset by the minimum, which is 3 bytes. For instance, an actual match length of 10 bytes to be copied, is encoded as 7. +* O: set for a 2-bytes match offset, clear for a 1-byte match offset + +**optional extra literal length** + +If the literals length is 7 or more, the 'L' bits in the token form the value 7, and an extra byte follows here, with three possible types of value: + +* 0-248: the value is added to the 7 stored in the token, to compose the final literals length. For instance a length of 206 will be stored as 7 in the token + a single byte with the value of 199, as 7 + 199 = 206. +* 250: a second byte follows. The final literals value is 256 + the second byte. For instance, a literals length of 499 is encoded as 7 in the token, a byte with the value of 250, and a final byte with the value of 243, as 256 + 243 = 499. +* 249: a second and third byte follow, forming a little-endian 16-bit value. The final literals value is that 16-bit value. For instance, a literals length of 1024 is stored as 7 in the token, then byte values of 249, 0 and 4, as (4 * 256) = 1024. + +The extension byte values are chosen so that all three cases can be detected on 8-bit CPUs with a simple addition and overflow check. + +**literal values** + +Literal bytes, whose number is specified by the literals length, follow here. There can be zero literals in a command. + +Important note: for blocks that are part of a stream, the last command in a block ends here, as it always contains literals only. For raw blocks, the last command does contain the match offset and match length, see the note below for EOD detection. + +**match offset low** + +The low 8 bits of the match offset follows. + +**optional match offset high** + +If the 'O' bit (bit 7) is set in the token, the high 8 bits of the match offset follow, otherwise they are understood to be all set to 1. For instance, a short offset of 0x70 is interpreted as 0xff70. + +**important note regarding match offsets: stored as negative values** + +Note that the match offset is negative: it is added to the current decompressed location and not substracted, in order to locate the back-reference to copy. + +**optional extra encoded match length** + +If the encoded match length is 15 or more, the 'M' bits in the token form the value 15, and an extra byte follows here, with three possible types of value. + +* 0-237: the value is added to the 15 stored in the token. The final value is 3 + 15 + this byte. +* 239: a second byte follows. The final match length is 256 + the second byte. +* 238: a second and third byte follow, forming a little-endian 16-bit value. The final encoded match length is that 16-bit value. + +Again, the extension byte values are chosen so that all cases can be detected with a simple addition and overflow check on 8-bit CPUs. + +# End Of Data detection for raw blocks + +When the LZSA1 block is part of a stream (see StreamFormat.md), as previously mentioned, the block ends after the literal values of the last command, without a match offset or match length. + +However, in a raw LZSA1 block, the last command does include a 1-byte match offset (set to zero) and a match length. The match length is encoded as a long zero: the 'M' bits in the token form the value 15, then an extra match length byte is present, with the value 238 ("two match length bytes follow"). Finally, a two-byte zero match length follows, indicating the end of the block. EOD is the only time a zero match length (which normally would indicate a copy of 3 bytes) is encoded as a large 2-byte match value. This allows the EOD test to exist in a rarely used code branch. diff --git a/Tools/unix/lzsa/BlockFormat_LZSA2.md b/Tools/unix/lzsa/BlockFormat_LZSA2.md new file mode 100644 index 00000000..15ec3781 --- /dev/null +++ b/Tools/unix/lzsa/BlockFormat_LZSA2.md @@ -0,0 +1,89 @@ +# Block data format (LZSA2) + +Blocks encoded as LZSA2 are composed from consecutive commands. Each command follows this format: + +* token: +* optional extra literal length +* literal values +* match offset +* optional extra encoded match length + +**token** + +The token byte is broken down into three parts: + + 7 6 5 4 3 2 1 0 + X Y Z L L M M M + +* L: 2-bit literals length (0-2, or 3 if extended). If the number of literals for this command is 0 to 2, the length is encoded in the token and no extra bytes are required. Otherwise, a value of 3 is encoded and extra nibbles or bytes follow as 'optional extra literal length' +* M: 3-bit encoded match length (0-6, or 7 if extended). Likewise, if the encoded match length for this command is 0 to 6, it is directly stored, otherwise 7 is stored and extra nibbles or bytes follow as 'optional extra encoded match length'. Except for the last command in a block, a command always contains a match, so the encoded match length is the actual match length offset by the minimum, which is 2 bytes. For instance, an actual match length of 5 bytes to be copied, is encoded as 3. +* XYZ: 3-bit value that indicates how to decode the match offset + +**optional extra literal length** + +If the literals length is 3 or more, the 'L' bits in the token form the value 3, and an extra nibble is read: + +* 0-14: the value is added to the 3 stored in the token, to compose the final literals length. +* 15: an extra byte follows + +If an extra byte follows, it can have two possible types of value: + +* 0-237: 18 is added to the value (3 from the token + 15 from the nibble), to compose the final literals length. For instance a length of 206 will be stored as 3 in the token + a nibble with the value of 15 + a single byte with the value of 188. +* 239: a second and third byte follow, forming a little-endian 16-bit value. The final literals value is that 16-bit value. For instance, a literals length of 1027 is stored as 3 in the token, a nibble with the value of 15, then byte values of 239, 3 and 4, as 3 + (4 * 256) = 1027. + +**literal values** + +Literal bytes, whose number is specified by the literals length, follow here. There can be zero literals in a command. + +Important note: for blocks that are part of a stream, the last command in a block ends here, as it always contains literals only. For raw blocks, the last command does contain the match offset and match length, see the note below for EOD detection. + +**match offset** + +The match offset is decoded according to the XYZ bits in the token + + XYZ + 00Z 5-bit offset: read a nibble for offset bits 1-4 and use the inverted bit Z of the token as bit 0 of the offset. set bits 5-15 of the offset to 1. + 01Z 9-bit offset: read a byte for offset bits 0-7 and use the inverted bit Z for bit 8 of the offset. set bits 9-15 of the offset to 1. + 10Z 13-bit offset: read a nibble for offset bits 9-12 and use the inverted bit Z for bit 8 of the offset, then read a byte for offset bits 0-7. set bits 13-15 of the offset to 1. + 110 16-bit offset: read a byte for offset bits 8-15, then another byte for offset bits 0-7. + 111 repeat offset: reuse the offset value of the previous match command. + +The bit ordering and inversion helps optimize the decoder for size and speed on 8-bit CPUs. + +**important note regarding match offsets: stored as negative values** + +Note that the match offset is negative: it is added to the current decompressed location and not substracted, in order to locate the back-reference to copy. For this reason, as already indicated, unexpressed offset bits are set to 1 instead of 0. + +**optional extra encoded match length** + +If the encoded match length is 7 or more, the 'M' bits in the token form the value 7, and an extra nibble is read: + +* 0-14: the value is added to the 3 stored in the token, and then the minmatch of 2 is added, to compose the final match length. +* 15: an extra byte follows + +If an extra byte follows here, it can have two possible types of value: + +* 0-231: 24 is added to the value (7 from the token + 15 from the nibble + minmatch of 2), to compose the final match length. For instance a length of 150 will be stored as 7 in the token + a nibble with the value of 15 + a single byte with the value of 126. +* 233: a second and third byte follow, forming a little-endian 16-bit value. The final encoded match length is that 16-bit value. + +# End Of Data detection for raw blocks + +When the LZSA2 block is part of a stream (see StreamFormat.md), as previously mentioned, the block ends after the literal values of the last command, without a match offset or match length. + +However, in a raw LZSA2 block, the last command does include a 9-bit match offset (set to zero, to be ignored) and a EOD marker as the match length. The EOD match length marker is encoded as such: the 'M' bits in the token form the value 7, then a nibble with the value of 15 is present, then a single extra match length byte with the value of 232, indicating the end of the block. This allows the EOD test to exist in a rarely used code branch. + +The EOD condition can be easily checked as part of the tri-state condition when handling long matches. When 24 is added to the match byte value: +- If the byte doesn't overflow, the final match length is ready +- If the byte overflows and equals zero, the EOD marker has been hit +- Otherwise, if the overflows and doesn't equal zero, a 16-bit match length must be read. + +This tri-state test translates to only an addition and two branches on 8-bit CPUs. + +The equivalent EOD condition in literal lengths (which would be byte 238, that would overflow to exactly 0 when adding 18) is never emitted, so for size-optimized decompressors, the same code can be used to read both types of lengths. + +# Reading nibbles + +When the specification indicates that a nibble (4 bit value) must be read: + +* If there are no nibbles ready, read a byte immediately. Return the high 4 bits (bits 4-7) as the nibble and store the low 4 bits for later. Flag that a nibble is ready for next time. +* If a nibble is ready, return the previously stored low 4 bits (bits 0-3) and flag that no nibble is ready for next time. diff --git a/Tools/unix/lzsa/LICENSE b/Tools/unix/lzsa/LICENSE new file mode 100644 index 00000000..29b28c12 --- /dev/null +++ b/Tools/unix/lzsa/LICENSE @@ -0,0 +1,3 @@ +The LZSA code is available under the Zlib license, except for src/matchfinder.c which is placed under the Creative Commons CC0 license. + +Please consult LICENSE.zlib.md and LICENSE.CC0.md for more information. diff --git a/Tools/unix/lzsa/LICENSE.cc0.md b/Tools/unix/lzsa/LICENSE.cc0.md new file mode 100644 index 00000000..139c68e0 --- /dev/null +++ b/Tools/unix/lzsa/LICENSE.cc0.md @@ -0,0 +1,43 @@ +## creative commons + +# CC0 1.0 Universal + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. + +### Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + +1. __Copyright and Related Rights.__ A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + +2. __Waiver.__ To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + +3. __Public License Fallback.__ Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + +4. __Limitations and Disclaimers.__ + + a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. diff --git a/Tools/unix/lzsa/LICENSE.zlib.md b/Tools/unix/lzsa/LICENSE.zlib.md new file mode 100644 index 00000000..e1296a1f --- /dev/null +++ b/Tools/unix/lzsa/LICENSE.zlib.md @@ -0,0 +1,19 @@ +Copyright (c) 2019 Emmanuel Marty + +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use of +this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject to +the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/Tools/unix/lzsa/Makefile b/Tools/unix/lzsa/Makefile new file mode 100644 index 00000000..0e0e7b28 --- /dev/null +++ b/Tools/unix/lzsa/Makefile @@ -0,0 +1,46 @@ +CC=gcc +CFLAGS=-O3 -fomit-frame-pointer -Isrc/libdivsufsort/include -Isrc +OBJDIR=obj +LDFLAGS= +STRIP=strip + +$(OBJDIR)/%.o: src/../%.c + @mkdir -p '$(@D)' + $(CC) $(CFLAGS) -c $< -o $@ + +APP := lzsa + +OBJS += $(OBJDIR)/src/lzsa.o +OBJS += $(OBJDIR)/src/dictionary.o +OBJS += $(OBJDIR)/src/expand_block_v1.o +OBJS += $(OBJDIR)/src/expand_block_v2.o +OBJS += $(OBJDIR)/src/expand_context.o +OBJS += $(OBJDIR)/src/expand_inmem.o +OBJS += $(OBJDIR)/src/expand_streaming.o +OBJS += $(OBJDIR)/src/frame.o +OBJS += $(OBJDIR)/src/matchfinder.o +OBJS += $(OBJDIR)/src/shrink_block_v1.o +OBJS += $(OBJDIR)/src/shrink_block_v2.o +OBJS += $(OBJDIR)/src/shrink_context.o +OBJS += $(OBJDIR)/src/shrink_inmem.o +OBJS += $(OBJDIR)/src/shrink_streaming.o +OBJS += $(OBJDIR)/src/stream.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort_utils.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/sssort.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/trsort.o + +UNAME := $(shell uname) + +all: $(APP) + cp $(APP) ../../$(UNAME) + +$(APP): $(OBJS) + @mkdir -p ../../bin/posix + $(CC) $^ $(LDFLAGS) -o $(APP) + +clean: + @rm -rf $(APP) $(OBJDIR) + +clobber: + rm -f ../../$(UNAME)/$(APP) diff --git a/Tools/unix/lzsa/README.md b/Tools/unix/lzsa/README.md new file mode 100644 index 00000000..6430ee5c --- /dev/null +++ b/Tools/unix/lzsa/README.md @@ -0,0 +1,84 @@ +LZSA is a collection of byte-aligned compression formats that are specifically engineered for very fast decompression on 8-bit systems. It can compress files of any size by using blocks of a maximum size of 64 Kb with block-interdependent compression and up to 64 Kb of back-references for matches. + +![Pareto frontier](pareto_graph.png) +*ZX Spectrum + +Check out [The Hollow](https://www.pouet.net/prod.php?which=81909) by Darklite and Offense, winner of the Solskogen 2019 wild compo, that uses LZSA on Z80. + +[Gabba](https://www.pouet.net/prod.php?which=83539) by Stardust ranked 2nd in the ZX Spectrum demo compo at CAFe demoparty 2019 and also used LZSA on Z80. + +The LZSA compression tool uses an aggressive optimal packing strategy to try to find the sequence of commands that gives the smallest packed file that decompresses to the original while maintaining the maximum possible decompression speed. + +The compression formats give the user choices that range from decompressing faster than LZ4 on 8-bit systems with better compression, to compressing as well as ZX7 with much better decompression speed. LZSA1 is designed to replace LZ4 and LZSA2 to replace ZX7, in 8-bit scenarios. + +Compression ratio comparison between LZSA and other optimal packers, for a workload composed of ZX Spectrum and C64 files: + + Bytes Ratio Decompression speed vs. LZ4 + LZSA2 676681 52,49% <------ 75% + MegaLZ 4.89 679041 52,68% Not measured + ZX7 687133 53,30% 47,73% + LZ5 1.4.1 727107 56,40% 75% + LZSA1 735785 57,08% <------ 90% + Lizard -29 776122 60,21% Not measured + LZ4_HC -19 -B4 -BD 781049 60,59% 100% + Uncompressed 1289127 100% N/A + +Performance over well-known compression corpus files: + + Uncompressed LZ4_HC -19 -B4 -BD LZSA1 LZSA2 + Canterbury 2810784 935827 (33,29%) 850792 (30,27%) 770877 (27,43%) + Silesia 211938580 77299725 (36,47%) 73706340 (34,78%) 68928564 (32,52%) + Calgary 3251493 1248780 (38,40%) 1192123 (36,67%) 1110290 (34,15%) + Large 11159482 3771025 (33,79%) 3648393 (32,69%) 3519480 (31,54%) + enwik9 1000000000 371841591 (37,18%) 355360043 (35,54%) 334900611 (33,49%) + +As an example of LZSA1's simplicity, a size-optimized decompressor on Z80 has been implemented in 67 bytes. + +The compressor is approximately 2X slower than LZ4_HC but compresses better while maintaining similar decompression speeds and decompressor simplicity. + +The main differences between LZSA1 and the LZ4 compression format are: + +* The use of short (8-bit) match offsets where possible. The match-finder and optimizer cooperate to try and use the shortest match offsets possible. +* Shorter encoding of lengths. As blocks are maximum 64 Kb in size, lengths can only be up to 64 Kb. +* As a result of the smaller commands due to the possibly shorter match offsets, a minimum match size of 3 bytes instead of 4. The use of small matches is driven by the optimizer, and used where they provide gains. + +As for LZSA2: +* 5-bit, 9-bit, 13-bit and 16-bit match offsets, using nibble encoding +* Rep-matches +* Shorter encoding of lengths, also using nibbles +* A minmatch of 2 bytes +* No (slow) bit-packing. LZSA2 uses byte alignment in the hot path, and nibbles. + +Inspirations: + +* [LZ4](https://github.com/lz4/lz4) by Yann Collet. +* [LZ5/Lizard](https://github.com/inikep/lizard) by Przemyslaw Skibinski and Yann Collet. +* The suffix array intervals in [Wimlib](https://wimlib.net/git/?p=wimlib;a=tree) by Eric Biggers. +* ZX7 by Einar Saukas +* [apc](https://github.com/svendahl/cap) by Sven-Åke Dahl +* [Charles Bloom](http://cbloomrants.blogspot.com/)'s compression blog + +License: + +* The LZSA code is available under the Zlib license. +* The match finder (matchfinder.c) is available under the CC0 license due to using portions of code from Eric Bigger's Wimlib in the suffix array-based matchfinder. + +8-bit assembly code: + +* Z80 decompressors (size- and speed-optimized) written by [introspec](https://github.com/specke) +* 6502 and 8088 size-optimized improvements by [Peter Ferrie](https://github.com/peterferrie) +* 8088 speed-optimized decompressor by [Jim Leonard](https://github.com/mobygamer) + +External links: + +* [i8080 decompressors](https://gitlab.com/ivagor/lzsa8080/tree/master) by Ivan Gorodetsky +* [PDP-11 decompressors](https://gitlab.com/ivagor/lzsa8080/tree/master/PDP11) also by Ivan Gorodetsky +* LZSA's page on [Pouet](https://www.pouet.net/prod.php?which=81573) + +# Compressed format + +Decompression code is provided for common 8-bit CPUs such as Z80 and 6502. However, if you would like to write your own, or understand the encoding, LZSA compresses data to a format that is fast and simple to decompress on 8-bit CPUs. It is encoded in either a stream of blocks, or as a single raw block, depending on command-line settings. The encoding is deliberately designed to avoid complicated operations on 8-bits (such as 16-bit math). + +* [Stream format](https://github.com/emmanuel-marty/lzsa/blob/master/StreamFormat.md) +* [Block encoding for LZSA1](https://github.com/emmanuel-marty/lzsa/blob/master/BlockFormat_LZSA1.md) +* [Block encoding for LZSA2](https://github.com/emmanuel-marty/lzsa/blob/master/BlockFormat_LZSA2.md) diff --git a/Tools/unix/lzsa/StreamFormat.md b/Tools/unix/lzsa/StreamFormat.md new file mode 100644 index 00000000..3f37f868 --- /dev/null +++ b/Tools/unix/lzsa/StreamFormat.md @@ -0,0 +1,39 @@ +# Stream format + +The stream format is composed of: + +* a header +* one or more frames +* a footer + +# Header format + +The 3-bytes LZSA header contains a signature and a traits byte: + + 0 1 2 + 0x7b 0x9e 7 6 5 4 3 2 1 + V V V Z Z Z Z + <--- signature ---> <- traits -> + +Trait bits: + +* V: 3 bit code that indicates which block data encoding is used. 0 is LZSA1 and 2 is LZSA2. +* Z: these bits in the traits are set to 0 for LZSA1 and LZSA2. + +# Frame format + +Each frame contains a 3-bytes length followed by block data that expands to up to 64 Kb of decompressed data. The block data is encoded either as LZSA1 or LZSA2 depending on the V bits of the traits byte in the header. + + 0 1 2 + DSZ0 DSZ1 U|DSZ2 + +* DSZ0 (length byte 0) contains bits 0-7 of the block data size +* DSZ1 (length byte 1) contains bits 8-15 of the block data size +* DSZ2 (bit 0 of length byte 2) contains bit 16 of the block data size +* U (bit 7 of length byte 2) is set if the block data is uncompressed, and clear if the block data is compressed. +* Bits 1..6 of length byte 2 are currently undefined and must be set to 0. + +# Footer format + +The stream ends with the EOD frame: the 3 length bytes are set to 0x00, 0x00, 0x00, and no block data follows. + diff --git a/Tools/unix/lzsa/VS2017/lzsa.sln b/Tools/unix/lzsa/VS2017/lzsa.sln new file mode 100644 index 00000000..ffd10e37 --- /dev/null +++ b/Tools/unix/lzsa/VS2017/lzsa.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.489 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lzsa", "lzsa.vcxproj", "{3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x64.ActiveCfg = Debug|x64 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x64.Build.0 = Debug|x64 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x86.ActiveCfg = Debug|Win32 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x86.Build.0 = Debug|Win32 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x64.ActiveCfg = Release|x64 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x64.Build.0 = Release|x64 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x86.ActiveCfg = Release|Win32 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A1E1655C-AA9F-41F0-80C9-18DD0B859D7C} + EndGlobalSection +EndGlobal diff --git a/Tools/unix/lzsa/VS2017/lzsa.vcxproj b/Tools/unix/lzsa/VS2017/lzsa.vcxproj new file mode 100644 index 00000000..f9275dcd --- /dev/null +++ b/Tools/unix/lzsa/VS2017/lzsa.vcxproj @@ -0,0 +1,225 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8} + Win32Proj + lzsa + 8.1 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)bin\ + $(ProjectName)_debug + + + true + $(ProjectDir)bin\ + $(ProjectName)_debug + + + false + $(ProjectDir)bin\ + + + false + $(ProjectDir)bin\ + + + + NotUsing + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + MultiThreadedDebug + ..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories) + + + Console + true + $(ProjectDir)bin\$(TargetName)$(TargetExt) + + + + + NotUsing + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + MultiThreadedDebug + ..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories) + + + Console + true + $(ProjectDir)bin\$(TargetName)$(TargetExt) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + MultiThreaded + Speed + ..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories) + true + + + Console + true + true + true + $(ProjectDir)bin\$(TargetName)$(TargetExt) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + MultiThreaded + Speed + ..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories) + true + + + Console + true + true + true + $(ProjectDir)bin\$(TargetName)$(TargetExt) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tools/unix/lzsa/VS2017/lzsa.vcxproj.filters b/Tools/unix/lzsa/VS2017/lzsa.vcxproj.filters new file mode 100644 index 00000000..415b4dd8 --- /dev/null +++ b/Tools/unix/lzsa/VS2017/lzsa.vcxproj.filters @@ -0,0 +1,147 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {5ec09c0d-19f7-4a6f-b524-f405fb99e48c} + + + {a922f475-1322-496d-8a6d-7f1c6b92423d} + + + {bd05c6e8-af92-4ab8-8916-0424cd8d186b} + + + + + Fichiers d%27en-tête + + + Fichiers sources + + + Fichiers sources\libdivsufsort\include + + + Fichiers sources\libdivsufsort\include + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources\libdivsufsort\include + + + + + Fichiers sources\libdivsufsort\lib + + + Fichiers sources\libdivsufsort\lib + + + Fichiers sources\libdivsufsort\lib + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources\libdivsufsort\lib + + + \ No newline at end of file diff --git a/Tools/unix/lzsa/VS2017/lzsa.vcxproj.user b/Tools/unix/lzsa/VS2017/lzsa.vcxproj.user new file mode 100644 index 00000000..58ecafc9 --- /dev/null +++ b/Tools/unix/lzsa/VS2017/lzsa.vcxproj.user @@ -0,0 +1,27 @@ + + + + $(TargetPath) + -f2 -c -v corpus/zxspectrum/graphics/bfox-dont_go_away_(2010).mg1 bfox.lzs + WindowsLocalDebugger + $(ProjectDir)..\ + + + $(TargetPath) + -f2 -c -v corpus/zxspectrum/graphics/bfox-dont_go_away_(2010).mg1 bfox.lzs + WindowsLocalDebugger + $(ProjectDir)..\ + + + $(TargetPath) + -f2 -c -v corpus/zxspectrum/graphics/bfox-dont_go_away_(2010).mg1 bfox.lzs + WindowsLocalDebugger + $(ProjectDir)..\ + + + $(TargetPath) + -f2 -c -v corpus/zxspectrum/graphics/bfox-dont_go_away_(2010).mg1 bfox.lzs + WindowsLocalDebugger + $(ProjectDir)..\ + + \ No newline at end of file diff --git a/Tools/unix/lzsa/Xcode/lzsa.xcodeproj/project.pbxproj b/Tools/unix/lzsa/Xcode/lzsa.xcodeproj/project.pbxproj new file mode 100644 index 00000000..608a7d6c --- /dev/null +++ b/Tools/unix/lzsa/Xcode/lzsa.xcodeproj/project.pbxproj @@ -0,0 +1,429 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 0CADC63122AAD8EB003E9821 /* shrink_inmem.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC5EE22AAD8EA003E9821 /* shrink_inmem.c */; }; + 0CADC63222AAD8EB003E9821 /* frame.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC5F322AAD8EB003E9821 /* frame.c */; }; + 0CADC63322AAD8EB003E9821 /* matchfinder.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC5F422AAD8EB003E9821 /* matchfinder.c */; }; + 0CADC63422AAD8EB003E9821 /* shrink_block_v1.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC5FA22AAD8EB003E9821 /* shrink_block_v1.c */; }; + 0CADC63A22AAD8EB003E9821 /* trsort.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC61622AAD8EB003E9821 /* trsort.c */; }; + 0CADC63B22AAD8EB003E9821 /* divsufsort.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC61722AAD8EB003E9821 /* divsufsort.c */; }; + 0CADC63D22AAD8EB003E9821 /* sssort.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC61922AAD8EB003E9821 /* sssort.c */; }; + 0CADC63E22AAD8EB003E9821 /* expand_block_v1.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62122AAD8EB003E9821 /* expand_block_v1.c */; }; + 0CADC63F22AAD8EB003E9821 /* lzsa.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62222AAD8EB003E9821 /* lzsa.c */; }; + 0CADC64022AAD8EB003E9821 /* shrink_streaming.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62322AAD8EB003E9821 /* shrink_streaming.c */; }; + 0CADC64122AAD8EB003E9821 /* expand_inmem.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62522AAD8EB003E9821 /* expand_inmem.c */; }; + 0CADC64222AAD8EB003E9821 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62922AAD8EB003E9821 /* stream.c */; }; + 0CADC64322AAD8EB003E9821 /* expand_block_v2.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62A22AAD8EB003E9821 /* expand_block_v2.c */; }; + 0CADC64422AAD8EB003E9821 /* shrink_context.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62B22AAD8EB003E9821 /* shrink_context.c */; }; + 0CADC64522AAD8EB003E9821 /* expand_streaming.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62D22AAD8EB003E9821 /* expand_streaming.c */; }; + 0CADC64622AAD8EB003E9821 /* dictionary.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62E22AAD8EB003E9821 /* dictionary.c */; }; + 0CADC64722AAD8EB003E9821 /* expand_context.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62F22AAD8EB003E9821 /* expand_context.c */; }; + 0CADC64822AAD8EB003E9821 /* shrink_block_v2.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC63022AAD8EB003E9821 /* shrink_block_v2.c */; }; + 0CADC64A22AB8DAD003E9821 /* divsufsort_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC64922AB8DAD003E9821 /* divsufsort_utils.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 0CADC57622A65EA4003E9821 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0CADC57822A65EA5003E9821 /* lzsa */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lzsa; sourceTree = BUILT_PRODUCTS_DIR; }; + 0CADC5ED22AAD8EA003E9821 /* expand_streaming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_streaming.h; path = ../../src/expand_streaming.h; sourceTree = ""; }; + 0CADC5EE22AAD8EA003E9821 /* shrink_inmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_inmem.c; path = ../../src/shrink_inmem.c; sourceTree = ""; }; + 0CADC5EF22AAD8EB003E9821 /* stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stream.h; path = ../../src/stream.h; sourceTree = ""; }; + 0CADC5F022AAD8EB003E9821 /* expand_block_v1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_block_v1.h; path = ../../src/expand_block_v1.h; sourceTree = ""; }; + 0CADC5F122AAD8EB003E9821 /* shrink_block_v1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_block_v1.h; path = ../../src/shrink_block_v1.h; sourceTree = ""; }; + 0CADC5F222AAD8EB003E9821 /* lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lib.h; path = ../../src/lib.h; sourceTree = ""; }; + 0CADC5F322AAD8EB003E9821 /* frame.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = frame.c; path = ../../src/frame.c; sourceTree = ""; }; + 0CADC5F422AAD8EB003E9821 /* matchfinder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = matchfinder.c; path = ../../src/matchfinder.c; sourceTree = ""; }; + 0CADC5F522AAD8EB003E9821 /* matchfinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = matchfinder.h; path = ../../src/matchfinder.h; sourceTree = ""; }; + 0CADC5F622AAD8EB003E9821 /* dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dictionary.h; path = ../../src/dictionary.h; sourceTree = ""; }; + 0CADC5F722AAD8EB003E9821 /* shrink_context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_context.h; path = ../../src/shrink_context.h; sourceTree = ""; }; + 0CADC5F822AAD8EB003E9821 /* shrink_inmem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_inmem.h; path = ../../src/shrink_inmem.h; sourceTree = ""; }; + 0CADC5F922AAD8EB003E9821 /* expand_block_v2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_block_v2.h; path = ../../src/expand_block_v2.h; sourceTree = ""; }; + 0CADC5FA22AAD8EB003E9821 /* shrink_block_v1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_block_v1.c; path = ../../src/shrink_block_v1.c; sourceTree = ""; }; + 0CADC5FB22AAD8EB003E9821 /* expand_context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_context.h; path = ../../src/expand_context.h; sourceTree = ""; }; + 0CADC60922AAD8EB003E9821 /* divsufsort_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = divsufsort_private.h; sourceTree = ""; }; + 0CADC60A22AAD8EB003E9821 /* divsufsort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = divsufsort.h; sourceTree = ""; }; + 0CADC61622AAD8EB003E9821 /* trsort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = trsort.c; sourceTree = ""; }; + 0CADC61722AAD8EB003E9821 /* divsufsort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = divsufsort.c; sourceTree = ""; }; + 0CADC61922AAD8EB003E9821 /* sssort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sssort.c; sourceTree = ""; }; + 0CADC62122AAD8EB003E9821 /* expand_block_v1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_block_v1.c; path = ../../src/expand_block_v1.c; sourceTree = ""; }; + 0CADC62222AAD8EB003E9821 /* lzsa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzsa.c; path = ../../src/lzsa.c; sourceTree = ""; }; + 0CADC62322AAD8EB003E9821 /* shrink_streaming.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_streaming.c; path = ../../src/shrink_streaming.c; sourceTree = ""; }; + 0CADC62422AAD8EB003E9821 /* format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = format.h; path = ../../src/format.h; sourceTree = ""; }; + 0CADC62522AAD8EB003E9821 /* expand_inmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_inmem.c; path = ../../src/expand_inmem.c; sourceTree = ""; }; + 0CADC62622AAD8EB003E9821 /* shrink_block_v2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_block_v2.h; path = ../../src/shrink_block_v2.h; sourceTree = ""; }; + 0CADC62722AAD8EB003E9821 /* expand_inmem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_inmem.h; path = ../../src/expand_inmem.h; sourceTree = ""; }; + 0CADC62822AAD8EB003E9821 /* shrink_streaming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_streaming.h; path = ../../src/shrink_streaming.h; sourceTree = ""; }; + 0CADC62922AAD8EB003E9821 /* stream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = stream.c; path = ../../src/stream.c; sourceTree = ""; }; + 0CADC62A22AAD8EB003E9821 /* expand_block_v2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_block_v2.c; path = ../../src/expand_block_v2.c; sourceTree = ""; }; + 0CADC62B22AAD8EB003E9821 /* shrink_context.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_context.c; path = ../../src/shrink_context.c; sourceTree = ""; }; + 0CADC62C22AAD8EB003E9821 /* frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = frame.h; path = ../../src/frame.h; sourceTree = ""; }; + 0CADC62D22AAD8EB003E9821 /* expand_streaming.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_streaming.c; path = ../../src/expand_streaming.c; sourceTree = ""; }; + 0CADC62E22AAD8EB003E9821 /* dictionary.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dictionary.c; path = ../../src/dictionary.c; sourceTree = ""; }; + 0CADC62F22AAD8EB003E9821 /* expand_context.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_context.c; path = ../../src/expand_context.c; sourceTree = ""; }; + 0CADC63022AAD8EB003E9821 /* shrink_block_v2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_block_v2.c; path = ../../src/shrink_block_v2.c; sourceTree = ""; }; + 0CADC64922AB8DAD003E9821 /* divsufsort_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = divsufsort_utils.c; sourceTree = ""; }; + 0CADC64B22AB8DC3003E9821 /* divsufsort_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = divsufsort_config.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0CADC57522A65EA4003E9821 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0CADC56F22A65EA4003E9821 = { + isa = PBXGroup; + children = ( + 0CADC57A22A65EA5003E9821 /* lzsa */, + 0CADC57922A65EA5003E9821 /* Products */, + ); + sourceTree = ""; + }; + 0CADC57922A65EA5003E9821 /* Products */ = { + isa = PBXGroup; + children = ( + 0CADC57822A65EA5003E9821 /* lzsa */, + ); + name = Products; + sourceTree = ""; + }; + 0CADC57A22A65EA5003E9821 /* lzsa */ = { + isa = PBXGroup; + children = ( + 0CADC62E22AAD8EB003E9821 /* dictionary.c */, + 0CADC5F622AAD8EB003E9821 /* dictionary.h */, + 0CADC62122AAD8EB003E9821 /* expand_block_v1.c */, + 0CADC5F022AAD8EB003E9821 /* expand_block_v1.h */, + 0CADC62A22AAD8EB003E9821 /* expand_block_v2.c */, + 0CADC5F922AAD8EB003E9821 /* expand_block_v2.h */, + 0CADC62F22AAD8EB003E9821 /* expand_context.c */, + 0CADC5FB22AAD8EB003E9821 /* expand_context.h */, + 0CADC62522AAD8EB003E9821 /* expand_inmem.c */, + 0CADC62722AAD8EB003E9821 /* expand_inmem.h */, + 0CADC62D22AAD8EB003E9821 /* expand_streaming.c */, + 0CADC5ED22AAD8EA003E9821 /* expand_streaming.h */, + 0CADC62422AAD8EB003E9821 /* format.h */, + 0CADC5F322AAD8EB003E9821 /* frame.c */, + 0CADC62C22AAD8EB003E9821 /* frame.h */, + 0CADC5F222AAD8EB003E9821 /* lib.h */, + 0CADC5FC22AAD8EB003E9821 /* libdivsufsort */, + 0CADC62222AAD8EB003E9821 /* lzsa.c */, + 0CADC5F422AAD8EB003E9821 /* matchfinder.c */, + 0CADC5F522AAD8EB003E9821 /* matchfinder.h */, + 0CADC5FA22AAD8EB003E9821 /* shrink_block_v1.c */, + 0CADC5F122AAD8EB003E9821 /* shrink_block_v1.h */, + 0CADC63022AAD8EB003E9821 /* shrink_block_v2.c */, + 0CADC62622AAD8EB003E9821 /* shrink_block_v2.h */, + 0CADC62B22AAD8EB003E9821 /* shrink_context.c */, + 0CADC5F722AAD8EB003E9821 /* shrink_context.h */, + 0CADC5EE22AAD8EA003E9821 /* shrink_inmem.c */, + 0CADC5F822AAD8EB003E9821 /* shrink_inmem.h */, + 0CADC62322AAD8EB003E9821 /* shrink_streaming.c */, + 0CADC62822AAD8EB003E9821 /* shrink_streaming.h */, + 0CADC62922AAD8EB003E9821 /* stream.c */, + 0CADC5EF22AAD8EB003E9821 /* stream.h */, + ); + path = lzsa; + sourceTree = ""; + }; + 0CADC5FC22AAD8EB003E9821 /* libdivsufsort */ = { + isa = PBXGroup; + children = ( + 0CADC60322AAD8EB003E9821 /* include */, + 0CADC61422AAD8EB003E9821 /* lib */, + ); + name = libdivsufsort; + path = ../../src/libdivsufsort; + sourceTree = ""; + }; + 0CADC60322AAD8EB003E9821 /* include */ = { + isa = PBXGroup; + children = ( + 0CADC64B22AB8DC3003E9821 /* divsufsort_config.h */, + 0CADC60922AAD8EB003E9821 /* divsufsort_private.h */, + 0CADC60A22AAD8EB003E9821 /* divsufsort.h */, + ); + path = include; + sourceTree = ""; + }; + 0CADC61422AAD8EB003E9821 /* lib */ = { + isa = PBXGroup; + children = ( + 0CADC64922AB8DAD003E9821 /* divsufsort_utils.c */, + 0CADC61622AAD8EB003E9821 /* trsort.c */, + 0CADC61722AAD8EB003E9821 /* divsufsort.c */, + 0CADC61922AAD8EB003E9821 /* sssort.c */, + ); + path = lib; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0CADC57722A65EA4003E9821 /* lzsa */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0CADC57F22A65EA5003E9821 /* Build configuration list for PBXNativeTarget "lzsa" */; + buildPhases = ( + 0CADC57422A65EA4003E9821 /* Sources */, + 0CADC57522A65EA4003E9821 /* Frameworks */, + 0CADC57622A65EA4003E9821 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = lzsa; + productName = lzsa; + productReference = 0CADC57822A65EA5003E9821 /* lzsa */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0CADC57022A65EA4003E9821 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = Emmanuel; + TargetAttributes = { + 0CADC57722A65EA4003E9821 = { + CreatedOnToolsVersion = 10.2.1; + }; + }; + }; + buildConfigurationList = 0CADC57322A65EA4003E9821 /* Build configuration list for PBXProject "lzsa" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 0CADC56F22A65EA4003E9821; + productRefGroup = 0CADC57922A65EA5003E9821 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0CADC57722A65EA4003E9821 /* lzsa */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 0CADC57422A65EA4003E9821 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CADC64822AAD8EB003E9821 /* shrink_block_v2.c in Sources */, + 0CADC63D22AAD8EB003E9821 /* sssort.c in Sources */, + 0CADC64322AAD8EB003E9821 /* expand_block_v2.c in Sources */, + 0CADC63F22AAD8EB003E9821 /* lzsa.c in Sources */, + 0CADC64422AAD8EB003E9821 /* shrink_context.c in Sources */, + 0CADC64522AAD8EB003E9821 /* expand_streaming.c in Sources */, + 0CADC63E22AAD8EB003E9821 /* expand_block_v1.c in Sources */, + 0CADC63122AAD8EB003E9821 /* shrink_inmem.c in Sources */, + 0CADC63B22AAD8EB003E9821 /* divsufsort.c in Sources */, + 0CADC64622AAD8EB003E9821 /* dictionary.c in Sources */, + 0CADC63422AAD8EB003E9821 /* shrink_block_v1.c in Sources */, + 0CADC64A22AB8DAD003E9821 /* divsufsort_utils.c in Sources */, + 0CADC64222AAD8EB003E9821 /* stream.c in Sources */, + 0CADC64022AAD8EB003E9821 /* shrink_streaming.c in Sources */, + 0CADC63A22AAD8EB003E9821 /* trsort.c in Sources */, + 0CADC64122AAD8EB003E9821 /* expand_inmem.c in Sources */, + 0CADC63322AAD8EB003E9821 /* matchfinder.c in Sources */, + 0CADC64722AAD8EB003E9821 /* expand_context.c in Sources */, + 0CADC63222AAD8EB003E9821 /* frame.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 0CADC57D22A65EA5003E9821 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../src/libdivsufsort/include, + ../src/xxhash, + ../src, + ); + LLVM_LTO = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ""; + SDKROOT = macosx; + }; + name = Debug; + }; + 0CADC57E22A65EA5003E9821 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../src/libdivsufsort/include, + ../src/xxhash, + ../src, + ); + LLVM_LTO = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ""; + SDKROOT = macosx; + }; + name = Release; + }; + 0CADC58022A65EA5003E9821 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 0CADC58122A65EA5003E9821 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0CADC57322A65EA4003E9821 /* Build configuration list for PBXProject "lzsa" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0CADC57D22A65EA5003E9821 /* Debug */, + 0CADC57E22A65EA5003E9821 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0CADC57F22A65EA5003E9821 /* Build configuration list for PBXNativeTarget "lzsa" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0CADC58022A65EA5003E9821 /* Debug */, + 0CADC58122A65EA5003E9821 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0CADC57022A65EA4003E9821 /* Project object */; +} diff --git a/Tools/unix/lzsa/asm/6502/decompress_fast_v1.asm b/Tools/unix/lzsa/asm/6502/decompress_fast_v1.asm new file mode 100644 index 00000000..b36cc176 --- /dev/null +++ b/Tools/unix/lzsa/asm/6502/decompress_fast_v1.asm @@ -0,0 +1,305 @@ +; ----------------------------------------------------------------------------- +; Decompress raw LZSA1 block. Create one with lzsa -r +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty, Peter Ferrie +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +DECOMPRESS_LZSA1_FAST + LDY #$00 + +DECODE_TOKEN + JSR GETSRC ; read token byte: O|LLL|MMMM + PHA ; preserve token on stack + + AND #$70 ; isolate literals count + BEQ NO_LITERALS ; skip if no literals to copy + CMP #$70 ; LITERALS_RUN_LEN? + BNE PREPARE_COPY_LITERALS ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$F9 ; (LITERALS_RUN_LEN) + BCC PREPARE_COPY_LITERALS_DIRECT + BEQ LARGE_VARLEN_LITERALS ; if adding up to zero, go grab 16-bit count + + JSR GETSRC ; get single extended byte of variable literals count + INY ; add 256 to literals count + BCS PREPARE_COPY_LITERALS_DIRECT ; (*like JMP PREPARE_COPY_LITERALS_DIRECT but shorter) + +LARGE_VARLEN_LITERALS ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + TXA + JMP PREPARE_COPY_LARGE_LITERALS + +PREPARE_COPY_LITERALS + TAX + LDA SHIFT_TABLE-1,X ; shift literals length into place + ; -1 because position 00 is reserved +PREPARE_COPY_LITERALS_DIRECT + TAX + +PREPARE_COPY_LARGE_LITERALS + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + BMI GET_LONG_OFFSET ; $80: 16 bit offset + + JSR GETSRC ; get 8 bit offset from stream in A + TAX ; save for later + LDA #$FF ; high 8 bits + BNE GOT_OFFSET ; go prepare match + ; (*like JMP GOT_OFFSET but shorter) + +SHORT_VARLEN_MATCHLEN + JSR GETSRC ; get single extended byte of variable match len + INY ; add 256 to match length + +PREPARE_COPY_MATCH + TAX +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + LDA COPY_MATCH_LOOP+1 + BEQ GETMATCH_ADJ_HI +GETMATCH_DONE + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + BEQ GETMATCH_ADJ_HI +GETMATCH_DONE + +} + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + BEQ DECODE_TOKEN ; (*like JMP DECODE_TOKEN but shorter) + +!ifdef BACKWARD_DECOMPRESS { + +GETMATCH_ADJ_HI + DEC COPY_MATCH_LOOP+2 + JMP GETMATCH_DONE + +} else { + +GETMATCH_ADJ_HI + INC COPY_MATCH_LOOP+2 + JMP GETMATCH_DONE + +} + +GET_LONG_OFFSET ; handle 16 bit offset: + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + +GOT_OFFSET + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + STA OFFSHI ; store high 8 bits of offset + STX OFFSLO + + SEC ; substract dest - match offset + LDA PUTDST+1 +OFFSLO = *+1 + SBC #$AA ; low 8 bits + STA COPY_MATCH_LOOP+1 ; store back reference address + LDA PUTDST+2 +OFFSHI = *+1 + SBC #$AA ; high 8 bits + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + SEC + +} else { + + ; Forward decompression - add match offset + + STA OFFSHI ; store high 8 bits of offset + TXA + + CLC ; add dest + match offset + ADC PUTDST+1 ; low 8 bits + STA COPY_MATCH_LOOP+1 ; store back reference address +OFFSHI = *+1 + LDA #$AA ; high 8 bits + + ADC PUTDST+2 + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + +} + + PLA ; retrieve token from stack again + AND #$0F ; isolate match len (MMMM) + ADC #$02 ; plus carry which is always set by the high ADC + CMP #$12 ; MATCH_RUN_LEN? + BCC PREPARE_COPY_MATCH ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; add MATCH_RUN_LEN and MIN_MATCH_SIZE to match length + BCC PREPARE_COPY_MATCH + BNE SHORT_VARLEN_MATCHLEN + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + ; large match length with zero high byte? + BNE PREPARE_COPY_MATCH_Y ; if not, continue + +DECOMPRESSION_DONE + RTS + +SHIFT_TABLE + !BYTE $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + !BYTE $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01 + !BYTE $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02 + !BYTE $03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03 + !BYTE $04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04 + !BYTE $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05 + !BYTE $06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06 + !BYTE $07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07 + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + LDA PUTDST+1 + BEQ PUTDST_ADJ_HI + DEC PUTDST+1 + RTS + +PUTDST_ADJ_HI + DEC PUTDST+2 + DEC PUTDST+1 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + PHA + LDA GETSRC+1 + BEQ GETSRC_ADJ_HI + DEC GETSRC+1 + PLA + RTS + +GETSRC_ADJ_HI + DEC GETSRC+2 + DEC GETSRC+1 + PLA + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + INC PUTDST+1 + BEQ PUTDST_ADJ_HI + RTS + +PUTDST_ADJ_HI + INC PUTDST+2 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + INC GETSRC+1 + BEQ GETSRC_ADJ_HI + RTS + +GETSRC_ADJ_HI + INC GETSRC+2 + RTS +} diff --git a/Tools/unix/lzsa/asm/6502/decompress_fast_v2.asm b/Tools/unix/lzsa/asm/6502/decompress_fast_v2.asm new file mode 100644 index 00000000..681d42d9 --- /dev/null +++ b/Tools/unix/lzsa/asm/6502/decompress_fast_v2.asm @@ -0,0 +1,363 @@ +; ----------------------------------------------------------------------------- +; Decompress raw LZSA2 block. +; Create one with lzsa -r -f2 +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b -f2 +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty, Peter Ferrie +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +NIBCOUNT = $FC ; zero-page location for temp offset + +DECOMPRESS_LZSA2_FAST + LDY #$00 + STY NIBCOUNT + +DECODE_TOKEN + JSR GETSRC ; read token byte: XYZ|LL|MMM + PHA ; preserve token on stack + + AND #$18 ; isolate literals count (LL) + BEQ NO_LITERALS ; skip if no literals to copy + CMP #$18 ; LITERALS_RUN_LEN_V2? + BCC PREPARE_COPY_LITERALS ; if less, count is directly embedded in token + + JSR GETNIBBLE ; get extra literals length nibble + ; add nibble to len from token + ADC #$02 ; (LITERALS_RUN_LEN_V2) minus carry + CMP #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + BCC PREPARE_COPY_LITERALS_DIRECT ; if less, literals count is complete + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; overflow? + JMP PREPARE_COPY_LITERALS_DIRECT + +PREPARE_COPY_LITERALS_LARGE + ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + BCS PREPARE_COPY_LITERALS_HIGH ; (*same as JMP PREPARE_COPY_LITERALS_HIGH but shorter) + +PREPARE_COPY_LITERALS + LSR ; shift literals count into place + LSR + LSR + +PREPARE_COPY_LITERALS_DIRECT + TAX + BCS PREPARE_COPY_LITERALS_LARGE ; if so, literals count is large + +PREPARE_COPY_LITERALS_HIGH + TXA + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + ASL + BCS REPMATCH_OR_LARGE_OFFSET ; 1YZ: rep-match or 13/16 bit offset + + ASL ; 0YZ: 5 or 9 bit offset + BCS OFFSET_9_BIT + + ; 00Z: 5 bit offset + + LDX #$FF ; set offset bits 15-8 to 1 + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 0, read nibble for bits 4-1 + ORA #$E0 ; set bits 7-5 to 1 + BNE GOT_OFFSET_LO ; go store low byte of match offset and prepare match + +OFFSET_9_BIT ; 01Z: 9 bit offset + ;;ASL ; shift Z (offset bit 8) in place + ROL + ROL + AND #$01 + EOR #$FF ; set offset bits 15-9 to 1 + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_LARGE_OFFSET + ASL ; 13 bit offset? + BCS REPMATCH_OR_16_BIT ; handle rep-match or 16-bit offset if not + + ; 10Z: 13 bit offset + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 8, read nibble for bits 12-9 + ADC #$DE ; set bits 15-13 to 1 and substract 2 (to substract 512) + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_16_BIT ; rep-match or 16 bit offset + ;;ASL ; XYZ=111? + BMI REP_MATCH ; reuse previous offset if so (rep-match) + + ; 110: handle 16 bit offset + JSR GETSRC ; grab high 8 bits +GOT_OFFSET_HI + TAX + JSR GETSRC ; grab low 8 bits +GOT_OFFSET_LO + STA OFFSLO ; store low byte of match offset + STX OFFSHI ; store high byte of match offset + +REP_MATCH +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + SEC ; add dest + match offset + LDA PUTDST+1 ; low 8 bits +OFFSLO = *+1 + SBC #$AA + STA COPY_MATCH_LOOP+1 ; store back reference address + LDA PUTDST+2 +OFFSHI = *+1 + SBC #$AA ; high 8 bits + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + SEC + +} else { + + ; Forward decompression - add match offset + + CLC ; add dest + match offset + LDA PUTDST+1 ; low 8 bits +OFFSLO = *+1 + ADC #$AA + STA COPY_MATCH_LOOP+1 ; store back reference address +OFFSHI = *+1 + LDA #$AA ; high 8 bits + ADC PUTDST+2 + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + +} + + PLA ; retrieve token from stack again + AND #$07 ; isolate match len (MMM) + ADC #$01 ; add MIN_MATCH_SIZE_V2 and carry + CMP #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + BCC PREPARE_COPY_MATCH ; if less, length is directly embedded in token + + JSR GETNIBBLE ; get extra match length nibble + ; add nibble to len from token + ADC #$08 ; (MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2) minus carry + CMP #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + BCC PREPARE_COPY_MATCH ; if less, match length is complete + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$E8 ; overflow? + +PREPARE_COPY_MATCH + TAX + BCC PREPARE_COPY_MATCH_Y ; if not, the match length is complete + BEQ DECOMPRESSION_DONE ; if EOD code, bail + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + LDA COPY_MATCH_LOOP+1 + BEQ GETMATCH_ADJ_HI +GETMATCH_DONE + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + BEQ GETMATCH_ADJ_HI +GETMATCH_DONE + +} + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + JMP DECODE_TOKEN + +!ifdef BACKWARD_DECOMPRESS { + +GETMATCH_ADJ_HI + DEC COPY_MATCH_LOOP+2 + JMP GETMATCH_DONE + +} else { + +GETMATCH_ADJ_HI + INC COPY_MATCH_LOOP+2 + JMP GETMATCH_DONE + +} + +GETCOMBINEDBITS + EOR #$80 + ASL + PHP + + JSR GETNIBBLE ; get nibble into bits 0-3 (for offset bits 1-4) + PLP ; merge Z bit as the carry bit (for offset bit 0) +COMBINEDBITZ + ROL ; nibble -> bits 1-4; carry(!Z bit) -> bit 0 ; carry cleared +DECOMPRESSION_DONE + RTS + +GETNIBBLE +NIBBLES = *+1 + LDA #$AA + LSR NIBCOUNT + BCC NEED_NIBBLES + AND #$0F ; isolate low 4 bits of nibble + RTS + +NEED_NIBBLES + INC NIBCOUNT + JSR GETSRC ; get 2 nibbles + STA NIBBLES + LSR + LSR + LSR + LSR + SEC + RTS + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + LDA PUTDST+1 + BEQ PUTDST_ADJ_HI + DEC PUTDST+1 + RTS + +PUTDST_ADJ_HI + DEC PUTDST+2 + DEC PUTDST+1 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + PHA + LDA GETSRC+1 + BEQ GETSRC_ADJ_HI + DEC GETSRC+1 + PLA + RTS + +GETSRC_ADJ_HI + DEC GETSRC+2 + DEC GETSRC+1 + PLA + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + INC PUTDST+1 + BEQ PUTDST_ADJ_HI + RTS + +PUTDST_ADJ_HI + INC PUTDST+2 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + INC GETSRC+1 + BEQ GETSRC_ADJ_HI + RTS + +GETSRC_ADJ_HI + INC GETSRC+2 + RTS +} + diff --git a/Tools/unix/lzsa/asm/6502/decompress_faster_v1.asm b/Tools/unix/lzsa/asm/6502/decompress_faster_v1.asm new file mode 100644 index 00000000..93619070 --- /dev/null +++ b/Tools/unix/lzsa/asm/6502/decompress_faster_v1.asm @@ -0,0 +1,392 @@ +; *************************************************************************** +; *************************************************************************** +; +; lzsa1_6502.s +; +; NMOS 6502 decompressor for data stored in Emmanuel Marty's LZSA1 format. +; +; This code is written for the ACME assembler. +; +; Optional code is presented for one minor 6502 optimization that breaks +; compatibility with the current LZSA1 format standard. +; +; The code is 168 bytes for the small version, and 205 bytes for the normal. +; +; Copyright John Brandwood 2019. +; +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) +; +; *************************************************************************** +; *************************************************************************** + + + +; *************************************************************************** +; *************************************************************************** +; +; Decompression Options & Macros +; + + ; + ; Save 6 bytes of code and 21 cycles by swapping the order + ; of bytes in the 16-bit length encoding? + ; + ; N.B. Setting this breaks compatibility with LZSA v1.2 + ; + +LZSA_SWAP_LEN16 = 0 + + ; + ; Choose size over space (within sane limits)? + ; + +LZSA_SMALL_SIZE = 0 + + ; + ; Remove code inlining to save space? + ; + ; This saves 15 bytes of code at the cost of 7% speed. + ; + + !if LZSA_SMALL_SIZE { +LZSA_NO_INLINE = 1 + } else { +LZSA_NO_INLINE = 0 + } + + ; + ; Use smaller code for copying literals? + ; + ; This saves 11 bytes of code at the cost of 15% speed. + ; + + !if LZSA_SMALL_SIZE { +LZSA_SHORT_CP = 1 + } else { +LZSA_SHORT_CP = 0 + } + + ; + ; Use smaller code for copying literals? + ; + ; This saves 11 bytes of code at the cost of 30% speed. + ; + + !if LZSA_SMALL_SIZE { +LZSA_SHORT_LZ = 1 + } else { +LZSA_SHORT_LZ = 0 + } + + ; + ; Assume that we're decompessing from a large multi-bank + ; compressed data file, and that the next bank may need to + ; paged in when a page-boundary is crossed. + ; + +LZSA_FROM_BANK = 0 + + ; + ; Macro to increment the source pointer to the next page. + ; + ; This should call a subroutine to determine if a bank + ; has been crossed, and a new bank should be paged in. + ; + + !if LZSA_FROM_BANK { + !macro LZSA_INC_PAGE { + jsr lzsa1_next_page + } + } else { + !macro LZSA_INC_PAGE { + inc +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +DECOMPRESS_LZSA1 + LDY #$00 + +DECODE_TOKEN + JSR GETSRC ; read token byte: O|LLL|MMMM + PHA ; preserve token on stack + + AND #$70 ; isolate literals count + BEQ NO_LITERALS ; skip if no literals to copy + LSR ; shift literals count into place + LSR + LSR + LSR + CMP #$07 ; LITERALS_RUN_LEN? + BCC PREPARE_COPY_LITERALS ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$F9 ; (LITERALS_RUN_LEN) + BCC PREPARE_COPY_LITERALS + BEQ LARGE_VARLEN_LITERALS ; if adding up to zero, go grab 16-bit count + + JSR GETSRC ; get single extended byte of variable literals count + INY ; add 256 to literals count + BCS PREPARE_COPY_LITERALS ; (*like JMP PREPARE_COPY_LITERALS but shorter) + +LARGE_VARLEN_LITERALS ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + TXA + +PREPARE_COPY_LITERALS + TAX + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + BMI GET_LONG_OFFSET ; $80: 16 bit offset + + JSR GETSRC ; get 8 bit offset from stream in A + TAX ; save for later + LDA #$FF ; high 8 bits + BNE GOT_OFFSET ; go prepare match + ; (*like JMP GOT_OFFSET but shorter) + +SHORT_VARLEN_MATCHLEN + JSR GETSRC ; get single extended byte of variable match len + INY ; add 256 to match length + +PREPARE_COPY_MATCH + TAX +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + LDA COPY_MATCH_LOOP+1 + BNE GETMATCH_DONE + DEC COPY_MATCH_LOOP+2 +GETMATCH_DONE + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + BNE GETMATCH_DONE + INC COPY_MATCH_LOOP+2 +GETMATCH_DONE + +} + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + BEQ DECODE_TOKEN ; (*like JMP DECODE_TOKEN but shorter) + +GET_LONG_OFFSET ; handle 16 bit offset: + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + +GOT_OFFSET + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + STA OFFSHI ; store high 8 bits of offset + STX OFFSLO + + SEC ; substract dest - match offset + LDA PUTDST+1 +OFFSLO = *+1 + SBC #$AA ; low 8 bits + STA COPY_MATCH_LOOP+1 ; store back reference address + LDA PUTDST+2 +OFFSHI = *+1 + SBC #$AA ; high 8 bits + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + SEC + +} else { + + ; Forward decompression - add match offset + + STA OFFSHI ; store high 8 bits of offset + TXA + + CLC ; add dest + match offset + ADC PUTDST+1 ; low 8 bits + STA COPY_MATCH_LOOP+1 ; store back reference address +OFFSHI = *+1 + LDA #$AA ; high 8 bits + + ADC PUTDST+2 + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + +} + + PLA ; retrieve token from stack again + AND #$0F ; isolate match len (MMMM) + ADC #$02 ; plus carry which is always set by the high ADC + CMP #$12 ; MATCH_RUN_LEN? + BCC PREPARE_COPY_MATCH ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; add MATCH_RUN_LEN and MIN_MATCH_SIZE to match length + BCC PREPARE_COPY_MATCH + BNE SHORT_VARLEN_MATCHLEN + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + ; large match length with zero high byte? + BNE PREPARE_COPY_MATCH_Y ; if not, continue + +DECOMPRESSION_DONE + RTS + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + LDA PUTDST+1 + BNE PUTDST_DONE + DEC PUTDST+2 +PUTDST_DONE + DEC PUTDST+1 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + PHA + LDA GETSRC+1 + BNE GETSRC_DONE + DEC GETSRC+2 +GETSRC_DONE + DEC GETSRC+1 + PLA + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + INC PUTDST+1 + BNE PUTDST_DONE + INC PUTDST+2 +PUTDST_DONE + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + INC GETSRC+1 + BNE GETSRC_DONE + INC GETSRC+2 +GETSRC_DONE + RTS + +} diff --git a/Tools/unix/lzsa/asm/6502/decompress_small_v2.asm b/Tools/unix/lzsa/asm/6502/decompress_small_v2.asm new file mode 100644 index 00000000..6daa1bdb --- /dev/null +++ b/Tools/unix/lzsa/asm/6502/decompress_small_v2.asm @@ -0,0 +1,336 @@ +; ----------------------------------------------------------------------------- +; Decompress raw LZSA2 block. +; Create one with lzsa -r -f2 +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b -f2 +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +NIBCOUNT = $FC ; zero-page location for temp offset + +DECOMPRESS_LZSA2 + LDY #$00 + STY NIBCOUNT + +DECODE_TOKEN + JSR GETSRC ; read token byte: XYZ|LL|MMM + PHA ; preserve token on stack + + AND #$18 ; isolate literals count (LL) + BEQ NO_LITERALS ; skip if no literals to copy + LSR ; shift literals count into place + LSR + LSR + CMP #$03 ; LITERALS_RUN_LEN_V2? + BCC PREPARE_COPY_LITERALS ; if less, count is directly embedded in token + + JSR GETNIBBLE ; get extra literals length nibble + ; add nibble to len from token + ADC #$02 ; (LITERALS_RUN_LEN_V2) minus carry + CMP #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + BCC PREPARE_COPY_LITERALS ; if less, literals count is complete + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; overflow? + +PREPARE_COPY_LITERALS + TAX + BCC PREPARE_COPY_LITERALS_HIGH ; if not, literals count is complete + + ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + +PREPARE_COPY_LITERALS_HIGH + TXA + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + ASL + BCS REPMATCH_OR_LARGE_OFFSET ; 1YZ: rep-match or 13/16 bit offset + + ASL ; 0YZ: 5 or 9 bit offset + BCS OFFSET_9_BIT + + ; 00Z: 5 bit offset + + LDX #$FF ; set offset bits 15-8 to 1 + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 0, read nibble for bits 4-1 + ORA #$E0 ; set bits 7-5 to 1 + BNE GOT_OFFSET_LO ; go store low byte of match offset and prepare match + +OFFSET_9_BIT ; 01Z: 9 bit offset + ;;ASL ; shift Z (offset bit 8) in place + ROL + ROL + AND #$01 + EOR #$FF ; set offset bits 15-9 to 1 + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_LARGE_OFFSET + ASL ; 13 bit offset? + BCS REPMATCH_OR_16_BIT ; handle rep-match or 16-bit offset if not + + ; 10Z: 13 bit offset + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 8, read nibble for bits 12-9 + ADC #$DE ; set bits 15-13 to 1 and substract 2 (to substract 512) + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_16_BIT ; rep-match or 16 bit offset + ;;ASL ; XYZ=111? + BMI REP_MATCH ; reuse previous offset if so (rep-match) + + ; 110: handle 16 bit offset + JSR GETSRC ; grab high 8 bits +GOT_OFFSET_HI + TAX + JSR GETSRC ; grab low 8 bits +GOT_OFFSET_LO + STA OFFSLO ; store low byte of match offset + STX OFFSHI ; store high byte of match offset + +REP_MATCH +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + SEC ; add dest + match offset + LDA PUTDST+1 ; low 8 bits +OFFSLO = *+1 + SBC #$AA + STA COPY_MATCH_LOOP+1 ; store back reference address + LDA PUTDST+2 +OFFSHI = *+1 + SBC #$AA ; high 8 bits + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + SEC + +} else { + + ; Forward decompression - add match offset + + CLC ; add dest + match offset + LDA PUTDST+1 ; low 8 bits +OFFSLO = *+1 + ADC #$AA + STA COPY_MATCH_LOOP+1 ; store back reference address +OFFSHI = *+1 + LDA #$AA ; high 8 bits + ADC PUTDST+2 + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + +} + + PLA ; retrieve token from stack again + AND #$07 ; isolate match len (MMM) + ADC #$01 ; add MIN_MATCH_SIZE_V2 and carry + CMP #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + BCC PREPARE_COPY_MATCH ; if less, length is directly embedded in token + + JSR GETNIBBLE ; get extra match length nibble + ; add nibble to len from token + ADC #$08 ; (MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2) minus carry + CMP #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + BCC PREPARE_COPY_MATCH ; if less, match length is complete + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$E8 ; overflow? + +PREPARE_COPY_MATCH + TAX + BCC PREPARE_COPY_MATCH_Y ; if not, the match length is complete + BEQ DECOMPRESSION_DONE ; if EOD code, bail + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + LDA COPY_MATCH_LOOP+1 + BNE GETMATCH_DONE + DEC COPY_MATCH_LOOP+2 +GETMATCH_DONE + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + BNE GETMATCH_DONE + INC COPY_MATCH_LOOP+2 +GETMATCH_DONE + +} + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + JMP DECODE_TOKEN + +GETCOMBINEDBITS + EOR #$80 + ASL + PHP + + JSR GETNIBBLE ; get nibble into bits 0-3 (for offset bits 1-4) + PLP ; merge Z bit as the carry bit (for offset bit 0) +COMBINEDBITZ + ROL ; nibble -> bits 1-4; carry(!Z bit) -> bit 0 ; carry cleared +DECOMPRESSION_DONE + RTS + +GETNIBBLE +NIBBLES = *+1 + LDA #$AA + LSR NIBCOUNT + BCS HAS_NIBBLES + + INC NIBCOUNT + JSR GETSRC ; get 2 nibbles + STA NIBBLES + LSR + LSR + LSR + LSR + SEC + +HAS_NIBBLES + AND #$0F ; isolate low 4 bits of nibble + RTS + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + LDA PUTDST+1 + BNE PUTDST_DONE + DEC PUTDST+2 +PUTDST_DONE + DEC PUTDST+1 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + PHA + LDA GETSRC+1 + BNE GETSRC_DONE + DEC GETSRC+2 +GETSRC_DONE + DEC GETSRC+1 + PLA + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + INC PUTDST+1 + BNE PUTDST_DONE + INC PUTDST+2 +PUTDST_DONE + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + INC GETSRC+1 + BNE GETSRC_DONE + INC GETSRC+2 +GETSRC_DONE + RTS + +} + diff --git a/Tools/unix/lzsa/asm/8088/LZSA1FTA.ASM b/Tools/unix/lzsa/asm/8088/LZSA1FTA.ASM new file mode 100644 index 00000000..b342a230 --- /dev/null +++ b/Tools/unix/lzsa/asm/8088/LZSA1FTA.ASM @@ -0,0 +1,250 @@ +; lzsa1fta.asm time-efficient decompressor implementation for 8088 +; Turbo Assembler IDEAL mode dialect; can also be assembled with NASM. +; +; Usual DOS assembler SMALL model assumptions apply. This code: +; - Assumes it was invoked via NEAR call (change RET to RETF for FAR calls) +; - Is interrupt-safe +; - Is not re-entrant (do not decompress while already running decompression) +; - Trashes all data and segment registers +; +; Copyright (C) 2019 Jim Leonard, Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + IDEAL + P8086 + +SEGMENT CODE para public + +ASSUME cs:CODE, ds:CODE + +PUBLIC lzsa1_decompress_speed + +; --------------------------------------------------------------------------- +; Decompress raw LZSA1 block +; inputs: +; * ds:si: raw LZSA1 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +; Must declare this in the code segment: +SHR4table: + DB 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h + DB 01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h + DB 02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h + DB 03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h + DB 04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h + DB 05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h + DB 06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h + DB 07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h + +PROC lzsa1_decompress_speed NEAR + +lzsa1_start: + push di ;remember decompression offset + cld ;ensure string ops move forward + mov bx,offset SHR4table + xor cx,cx + +@@decode_token: + xchg cx,ax ;clear ah (cx = 0 from match copy's rep movsb) + lodsb ;read token byte: O|LLL|MMMM + mov dx,ax ;copy our token to dl for later MMMM handling + + and al,070H ;isolate literals length in token (LLL) + jz @@check_offset_size ;if LLL=0, we have no literals; goto match + cmp al,070H ;LITERALS_RUN_LEN? + jne @@got_literals ;no, we have full count from token; go copy + + lodsb ;grab extra length byte + add al,07H ;add LITERALS_RUN_LEN + jnc @@got_literals_exact ;if no overflow, we have full count + je @@big_literals + +@@mid_literals: + lodsb ;grab single extra length byte + inc ah ;add 256 + xchg cx,ax ;with longer counts, we can save some time + shr cx,1 ;by doing a word copy instead of a byte copy. + rep movsw ;We don't need to account for overlap because + adc cx,0 ;source for literals isn't the output buffer. + rep movsb + jmp @@check_offset_size + +@@big_literals: + lodsw ;grab 16-bit extra length + xchg cx,ax ;with longer counts, we can save some time + shr cx,1 ;by doing a word copy instead of a byte copy. + rep movsw + adc cx,0 + rep movsb + jmp @@check_offset_size + +@@got_literals: + segcs xlat ;shift literals length into place +@@got_literals_exact: + xchg cx,ax + rep movsb ;copy cx literals from ds:si to es:di + +@@check_offset_size: + test dl,dl ;check match offset size in token (O bit) + js @@get_long_offset ;load absolute 16-bit match offset + + mov ah,0ffh ;set up high byte + lodsb ;load low byte + +@@get_match_length: + xchg dx,ax ;dx: match offset ax: original token + and al,0FH ;isolate match length in token (MMMM) + cmp al,0FH ;MATCH_RUN_LEN? + jne @@got_matchlen_short ;no, we have the full match length from the token, go copy + + lodsb ;grab extra length byte + add al,012H ;add MIN_MATCH_SIZE + MATCH_RUN_LEN + jnc @@do_long_copy ;if no overflow, we have the entire length + jne @@mid_matchlen + + lodsw ;grab 16-bit length + xchg cx,ax ;get ready to do a long copy + jcxz @@done_decompressing ;wait, is it the EOD marker? Exit if so + jmp @@copy_len_preset ;otherwise, do the copy + +@@get_long_offset: + lodsw ;Get 2-byte match offset + jmp @@get_match_length + +@@got_matchlen_short: + add al,3 ;add MIN_MATCH_SIZE + xchg cx,ax ;copy match length into cx + mov bp,ds ;save ds + mov ax,es + mov ds,ax ;ds=es + xchg ax,si ;save si + mov si,di ;ds:si now points at back reference in output data + add si,dx + rep movsb ;copy match + xchg si,ax ;restore si + mov ds,bp ;restore ds + jmp @@decode_token ;go decode another token + +@@done_decompressing: + pop ax ;retrieve the original decompression offset + xchg di,ax ;compute decompressed size + sub ax,di + ret ;done decompressing, exit to caller + +;With a confirmed longer match length, we have an opportunity to optimize for +;the case where a single byte is repeated long enough that we can benefit +;from rep movsw to perform the run (instead of rep movsb). +@@mid_matchlen: + lodsb ;grab single extra length byte + inc ah ;add 256 +@@do_long_copy: + xchg cx,ax ;copy match length into cx +@@copy_len_preset: + push ds ;save ds + mov bp,es + mov ds,bp ;ds=es + mov bp,si ;save si + mov si,di ;ds:si now points at back reference in output data + add si,dx + cmp dx,-2 ;do we have a byte/word run to optimize? + jae @@do_run ;perform a run +;You may be tempted to change "jae" to "jge" because DX is a signed number. +;Don't! The total window is 64k, so if you treat this as a signed comparison, +;you will get incorrect results for offsets over 32K. + +;If we're here, we have a long copy and it isn't byte-overlapping (if it +;overlapped, we'd be in @@do_run_1) So, let's copy faster with REP MOVSW. +;This won't affect 8088 that much, but it speeds up 8086 and higher. + shr cx,1 + rep movsw + adc cx,0 + rep movsb + mov si,bp ;restore si + pop ds + jmp @@decode_token ;go decode another token + +@@do_run: + je @@do_run_2 ;fall through to byte (common) if not word run + +@@do_run_1: + lodsb ;load first byte of run into al + mov ah,al + shr cx,1 + rep stosw ;perform word run + adc cx,0 + rep stosb ;finish word run + mov si,bp ;restore si + pop ds + jmp @@decode_token ;go decode another token + +@@do_run_2: + lodsw ;load first word of run + shr cx,1 + rep stosw ;perform word run + adc cx,0 ;despite 2-byte offset, compressor might + rep stosb ;output odd length. better safe than sorry. + mov si,bp ;restore si + pop ds + jmp @@decode_token ;go decode another token + +ENDP lzsa1_decompress_speed + +ENDS CODE + +END + +;Speed optimization history (decompression times in microseconds @ 4.77 MHz): +; original E. Marty code shuttle 123208 alice 65660 robotron 407338 *** +; table for shr al,4 shuttle 120964 alice 63230 robotron 394733 +++ +; push/pop to mov/mov shuttle 118176 alice 61835 robotron 386762 +++ +; movsw for literalcpys shuttle 124102 alice 64908 robotron 400220 --- rb +; stosw for byte runs shuttle 118897 alice 65040 robotron 403518 --- rb +; better stosw for runs shuttle 117712 alice 65040 robotron 403343 +-- +; disable RLE by default shuttle 116924 alice 60783 robotron 381226 +++ +; optimize got_matchlen shuttle 115294 alice 59588 robotron 374330 +++ +; fall through to getML shuttle 113258 alice 59572 robotron 372004 +++ +; fall through to midLI shuttle 113258 alice 59572 robotron 375060 ..- rb +; fall through midMaLen shuttle 113247 alice 59572 robotron 372004 +.+ +; movsw for litlen > 255 shuttle 113247 alice 59572 robotron 371612 ..+ +; rep stosw for long runs shuttle 113247 alice 59572 robotron 371612 ... +; rep movsw for long cpys shuttle 113247 alice 59572 robotron 371035 ..+ +; xchg/dec ah -> mov ah,val shuttle 112575 alice 59272 robotron 369198 +++ +; force >12h len.to longcpy shuttle 101998 alice 59266 robotron 364459 +.+ +; more efficient run branch shuttle 102239 alice 59297 robotron 364716 --- rb +; even more eff. run branch shuttle 101998 alice 59266 robotron 364459 *** +; BUGFIX - bad sign compare shuttle 101955 alice 59225 robotron 364117 +++ +; reverse 16-bit len compar shuttle 102000 alice 59263 robotron 364460 --- rb +; jcxz for EOD detection no change to speed, but is 1 byte shorter +++ +; force movsw for literals shuttle 107183 alice 62555 robotron 379524 --- rb +; defer shr4 until necessry shuttle 102069 alice 60236 robotron 364096 --- +; skip literals if LLL=0 shuttle 98655 alice 57849 robotron 363358 --- +; fall through to mid_liter shuttle 98595 alice 57789 robotron 361998 +++ +; == jumptable experiments begin == +; jumptable for small copys shuttle 101594 alice 61078 robotron 386018 --- +; start:xchg instead of mov shuttle 100948 alice 60467 robotron 381112 +++ +; use table for LLL=0 check shuttle 106972 alice 63333 robotron 388304 --- rb +; jmptbl to fallthrough mov shuttle 102532 alice 60760 robotron 383070 --- +; cpy fallthrough check_ofs shuttle 98939 alice 58917 robotron 371019 +** +; single jumptable jump shuttle 97528 alice 57264 robotron 362194 ++* +; conditional check for L=7 shuttle 98610 alice 58521 robotron 368153 --- rb +; rip out the jumptable :-/ shuttle 97616 alice 57128 robotron 360697 +++ +; defer add MIN_MATCH_SIZE shuttle 97250 alice 57004 robotron 361191 ++? +; cache constants in regs shuttle 104681 alice 59939 robotron 380125 --- rb diff --git a/Tools/unix/lzsa/asm/8088/LZSA1JMP.ASM b/Tools/unix/lzsa/asm/8088/LZSA1JMP.ASM new file mode 100644 index 00000000..b96b4989 --- /dev/null +++ b/Tools/unix/lzsa/asm/8088/LZSA1JMP.ASM @@ -0,0 +1,523 @@ +; lzsa1fta.asm time-efficient decompressor implementation for 808x CPUs. +; Turbo Assembler IDEAL mode dialect. +; (Is supposed to also assemble with NASM's IDEAL mode support, but YMMV.) +; +; This code assembles to about 3K of lookup tables and unrolled code, +; but the tradeoff for that size is the absolute fastest decompressor +; of LZSA1 block data for 808x CPUs. +; If you need moderately fast code with less size, see LZSA1FTA.ASM. +; If you need the smallest decompression code, see decompress_small_v1.S. +; +; Usual DOS assembler SMALL model assumptions apply. This code: +; - Assumes it was invoked via NEAR call (change RET to RETF for FAR calls) +; - Is interrupt-safe +; - Is not re-entrant (do not decompress while already running decompression) +; - Trashes all data and segment registers +; +; Copyright (C) 2019 Jim Leonard, Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; +; =========================================================================== +; +; The key area to concentrate on when optimizing LZSA1 decompression speed is +; reducing time spent handling the shortest matches. This is for two reasons: +; 1. shorter matches are more common +; 2. short matches are least efficient in terms of decomp speed per byte +; You can confirm #1 using the --stats mode of the compressor. +; +; Branches are costly on 8086. To ensure we branch as little as possible, a +; jumptable will be used to branch directly to as many direct decode paths as +; possible. This will burn up 512 bytes of RAM for a jumptable, and a few +; hundred bytes of duplicated program code (rather than JMP/CALL common code +; blocks, we inline them to avoid the branch overhead). +; +; =========================================================================== +; +; === LZSA1 block reference: +; +; Blocks encoded as LZSA1 are composed from consecutive commands. +; Each command follows this format: +; +; token: +; optional extra literal length +; literal values +; match offset low +; optional match offset high +; optional extra encoded match length +; +; +; === LZSA1 Token Reference: +; +; 7 6 5 4 3 2 1 0 +; O L L L M M M M +; +; L: 3-bit literals length (0-6, or 7 if extended). If the number of literals for +; this command is 0 to 6, the length is encoded in the token and no extra bytes +; are required. Otherwise, a value of 7 is encoded and extra bytes follow as +; 'optional extra literal length' +; +; M: 4-bit encoded match length (0-14, or 15 if extended). Likewise, if the +; encoded match length for this command is 0 to 14, it is directly stored, +; otherwise 15 is stored and extra bytes follow as 'optional extra encoded match +; length'. Except for the last command in a block, a command always contains a +; match, so the encoded match length is the actual match length, offset by the +; minimum which is 3 bytes. For instance, an actual match length of 10 bytes to +; be copied, is encoded as 7. +; +; O: set for a 2-bytes match offset, clear for a 1-byte match offset +; +; +; === Decoding extended literal length: +; +; If the literals length is 7 or more, then an extra byte follows here, with +; three possible values: +; +; 0-248: the value is added to the 7 stored in the token. +; 250: a second byte follows. The final literals value is 256 + the second byte. +; 249: a little-endian 16-bit value follows, forming the final literals value. +; +; +; === Decoding match offsets: +; +; match offset low: The low 8 bits of the match offset follows. +; +; optional match offset high: If the 'O' bit (bit 7) is set in the token, the +; high 8 bits of the match offset follow, otherwise they are understood to be all +; set to 1. For instance, a short offset of 0x70 is interpreted as 0xff70 +; +; +; === Decoding extra encoded match length: +; +; optional extra encoded match length: If the encoded match length is 15 or more, +; the 'M' bits in the token form the value 15, and an extra byte follows here, +; with three possible types of value. +; +; 0-237: the value is added to the 15 stored in the token. The final value is 3 + 15 + this byte. +; 239: a second byte follows. The final match length is 256 + the second byte. +; 238: a second and third byte follow, forming a little-endian 16-bit value. +; The final encoded match length is that 16-bit value. +; +; =========================================================================== + + IDEAL ; Use Turbo Assembler IDEAL syntax checking + P8086 ; Restrict code generation to the 808x and later + JUMPS ; Perform fixups for out-of-bound conditional jumps + ; This is required for the (L=07 & M=0Fh) decode paths as they + ; have the most code, but these are uncommon paths so the + ; tiny speed loss in just these paths is not a concern. + +SEGMENT CODE para public + +ASSUME cs:CODE, ds:CODE + +PUBLIC lzsa1_decompress_speed_jumptable + +; EQU helper statements (so we can construct a jump table without going crazy) + +minmatch EQU 3 +litrunlen EQU 7 + +leml1 EQU OFFSET lit_ext_mat_len_1b +leme1 EQU OFFSET lit_ext_mat_ext_1b +leml2 EQU OFFSET lit_ext_mat_len_2b +leme2 EQU OFFSET lit_ext_mat_ext_2b + +;short-circuit special cases for 0 through 6 literal copies: +l6ml1 EQU OFFSET lit_len_mat_len_1b +l6me1 EQU OFFSET lit_len_mat_ext_1b +l6ml2 EQU OFFSET lit_len_mat_len_2b +l6me2 EQU OFFSET lit_len_mat_ext_2b +l5ml1 EQU OFFSET lit_len_mat_len_1b + 1 +l5me1 EQU OFFSET lit_len_mat_ext_1b + 1 +l5ml2 EQU OFFSET lit_len_mat_len_2b + 1 +l5me2 EQU OFFSET lit_len_mat_ext_2b + 1 +l4ml1 EQU OFFSET lit_len_mat_len_1b + 2 +l4me1 EQU OFFSET lit_len_mat_ext_1b + 2 +l4ml2 EQU OFFSET lit_len_mat_len_2b + 2 +l4me2 EQU OFFSET lit_len_mat_ext_2b + 2 +l3ml1 EQU OFFSET lit_len_mat_len_1b + 3 +l3me1 EQU OFFSET lit_len_mat_ext_1b + 3 +l3ml2 EQU OFFSET lit_len_mat_len_2b + 3 +l3me2 EQU OFFSET lit_len_mat_ext_2b + 3 +l2ml1 EQU OFFSET lit_len_mat_len_1b + 4 +l2me1 EQU OFFSET lit_len_mat_ext_1b + 4 +l2ml2 EQU OFFSET lit_len_mat_len_2b + 4 +l2me2 EQU OFFSET lit_len_mat_ext_2b + 4 +l1ml1 EQU OFFSET lit_len_mat_len_1b + 5 +l1me1 EQU OFFSET lit_len_mat_ext_1b + 5 +l1ml2 EQU OFFSET lit_len_mat_len_2b + 5 +l1me2 EQU OFFSET lit_len_mat_ext_2b + 5 +l0ml1 EQU OFFSET lit_len_mat_len_1b + 6 ; MMMM handling comes after LLL code +l0me1 EQU OFFSET lit_len_mat_ext_1b + 6 ; MMMM handling comes after LLL code +l0ml2 EQU OFFSET lit_len_mat_len_2b + 6 ; MMMM handling comes after LLL code +l0me2 EQU OFFSET lit_len_mat_ext_2b + 6 ; MMMM handling comes after LLL code + +; === Hand-written (!) jumptable actually begins here. +; Located before the program code results in an extra JMP and 3 wasted bytes, +; but it makes the code easier to follow in this location. +; Relocate the jump table after the ENDP directive to save 3 bytes. +; +; 7 6 5 4 3 2 1 0 +; O L L L M M M M +; +; 0 1 2 3 4 5 6 7 8 9 a b c d e f +jtbl DW l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0me1 ;0 + DW l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1me1 ;1 + DW l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2me1 ;2 + DW l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3me1 ;3 + DW l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4me1 ;4 + DW l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5me1 ;5 + DW l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6me1 ;6 + DW leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leme1 ;7 + DW l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0me2 ;8 + DW l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1me2 ;9 + DW l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2me2 ;a + DW l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3me2 ;b + DW l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4me2 ;c + DW l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5me2 ;d + DW l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6me2 ;e + DW leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leme2 ;f + +PROC lzsa1_decompress_speed_jumptable NEAR +; --------------------------------------------------------------------------- +; Decompress raw LZSA1 block +; inputs: +; * ds:si: raw LZSA1 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +MACRO get_byte_match_offset + mov ah,0ffh ;O=0, so set up offset's high byte + lodsb ;load low byte; ax=match offset + xchg bp,ax ;bp=match offset ax=00 + original token +ENDM + +MACRO get_word_match_offset + lodsw ;ax=match offset + xchg bp,ax ;bp=match offset ax=00 + original token +ENDM + +MACRO do_match_copy_long +LOCAL do_run, do_run_w +; Copies a long match as optimally as possible. +; requirements: cx=length, bp=negative offset, ds:si=compdata, es:di=output +; trashes: ax, bx +; must leave cx=0 at exit + mov bx,ds ;save ds + mov ax,es + mov ds,ax ;ds=es + xchg ax,si ;save si + lea si,[bp+di] ;si = output buffer + negative match offset + cmp bp,-2 ;do we have a byte/word run to optimize? + jae do_run ;perform a run if so, otherwise fall through +;You may be tempted to change "jae" to "jge" because DX is a signed number. +;Don't! The total window is 64k, so if you treat this as a signed comparison, +;you will get incorrect results for offsets over 32K. + +;If we're here, we have a long copy and it isn't byte-overlapping (if it +;overlapped, we'd be in @@do_run) So, let's copy faster with REP MOVSW. +;This affects 8088 only slightly, but is a bigger win on 8086 and higher. + shr cx,1 + rep movsw + adc cl,0 + rep movsb + xchg si,ax ;restore si + mov ds,bx ;restore ds + jmp decode_token + +do_run: + je do_run_w ;if applicable, handle word-sized value faster + xchg dx,ax ;save si into dx, as ax is getting trashed + lodsb ;load first byte of run into al + mov ah,al + shr cx,1 + rep stosw ;perform word run + adc cl,0 + rep stosb ;finish word run + mov si,dx ;restore si + mov ds,bx ;restore ds + jmp decode_token + +do_run_w: + xchg dx,ax ;save si into dx, as ax is getting trashed + lodsw ;load first word of run + shr cx,1 + rep stosw ;perform word run + adc cl,0 ;despite 2-byte offset, compressor might + rep stosb ;output odd length. better safe than sorry. + mov si,dx ;restore si + mov ds,bx ;restore ds + jmp decode_token +ENDM + +MACRO do_match_copy +; Copies a shorter match with as little overhead as possible. +; requirements: cx=length, bp=negative offset, ds:si=compdata, es:di=output +; trashes: ax, bx +; must leave cx=0 at exit + mov bx,ds ;save ds + mov ax,es + mov ds,ax ;ds=es + xchg ax,si ;save si + lea si,[bp+di] ;si = output buffer + negative match offset + rep movsb + xchg si,ax ;restore si + mov ds,bx ;restore ds + jmp decode_token +ENDM + +MACRO do_literal_copy +; Copies a literal sequence using words. +; Meant for longer lengths; for 128 bytes or less, use REP MOVSB. +; requirements: cx=length, ds:si=compdata, es:di=output +; must leave cx=0 at exit + shr cx,1 + rep movsw + adc cl,0 + rep movsb +ENDM + +MACRO copy_small_match_len + and al,0FH ;isolate length in token (MMMM) + add al,minmatch ;ax=match length + xchg cx,ax ;cx=match length + do_match_copy ;copy match with cx=length, bp=offset +ENDM + +MACRO copy_large_match_len +LOCAL val239, val238, EOD +; Handle MMMM=Fh +; Assumptions: ah=0 from get_????_match_offset's xchg + lodsb ;grab extra match length byte + add al,0Fh+minmatch ;add MATCH_RUN_LEN + MIN_MATCH_SIZE + jz val238 ;if zf & cf, 238: get 16-bit match length + jc val239 ;if cf, 239: get extra match length byte + xchg cx,ax ;otherwise, we have our match length + do_match_copy_long ;copy match with cx=length, bp=offset +val239: + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_match_copy_long ;copy match with cx=length, bp=offset +val238: + lodsw ;grab 16-bit length + xchg cx,ax + jcxz EOD ;is it the EOD marker? Exit if so + do_match_copy_long ;copy match with cx=length, bp=offset +EOD: + jmp done_decompressing +ENDM + + +lzsa1_start: + push di ;remember decompression offset + cld ;ensure string ops move forward + xor cx,cx + +decode_token: + xchg cx,ax ;clear ah (cx = 0 from match copy's REP) + lodsb ;read token byte: O|LLL|MMMM + mov bp,ax ;preserve 0+token in bp for later MMMM handling + mov bx,ax ;prep for table lookup + shl bx,1 ;adjust for offset word size + jmp [cs:jtbl+bx] ;jump directly to relevant decode path + +; There are eight basic decode paths for an LZSA1 token. Each of these +; paths perform only the necessary actions to decode the token and then +; fetch the next token. This results in a lot of code duplication, but +; it is the only way to get down to two branches per token (jump to unique +; decode path, then jump back to next token) for the most common cases. + +; Path #1: LLL=0-6, MMMM=0-Eh, O=0 (1-byte match offset) +; Handle LLL=0-6 by jumping directly into # of bytes to copy (6 down to 1) +lit_len_mat_len_1b: + movsb + movsb + movsb + movsb + movsb + movsb + get_byte_match_offset + copy_small_match_len + + +; Path #2: LLL=0-6, MMMM=Fh, O=0 (1-byte match offset) +lit_len_mat_ext_1b: + movsb + movsb + movsb + movsb + movsb + movsb + get_byte_match_offset + copy_large_match_len + + +; Path #3: LLL=7, MMMM=0-Eh, O=0 (1-byte match offset) +lit_ext_mat_len_1b: +; on entry: ax=0 + token, bp=ax + lodsb ;grab extra literal length byte + add al,litrunlen ;add 7h literal run length + jz @@val249_3 ;if zf & cf, 249: get 16-bit literal length + jc @@val250_3 ;if cf, 250: get extra literal length byte + xchg cx,ax ;otherwise, we have our literal length + do_literal_copy ;this might be better as rep movsw !!! benchmark + get_byte_match_offset + copy_small_match_len +@@val250_3: + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_literal_copy + get_byte_match_offset + copy_small_match_len +@@val249_3: + lodsw ;grab 16-bit length + xchg cx,ax + do_literal_copy + get_byte_match_offset + copy_small_match_len + + +; Path #4: LLL=7, MMMM=Fh, O=0 (1-byte match offset) +lit_ext_mat_ext_1b: +; on entry: ax=0 + token, bp=ax + lodsb ;grab extra literal length byte + add al,litrunlen ;add 7h literal run length + jz @@val249_4 ;if zf & cf, 249: get 16-bit literal length + jc @@val250_4 ;if cf, 250: get extra literal length byte + xchg cx,ax ;otherwise, we have our literal length + do_literal_copy ;this might be better as rep movsw !!! benchmark + get_byte_match_offset + copy_large_match_len +@@val250_4: + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_literal_copy + get_byte_match_offset + copy_large_match_len +@@val249_4: + lodsw ;grab 16-bit length + xchg cx,ax + do_literal_copy + get_byte_match_offset + copy_large_match_len + + +; Path #5: LLL=0-6, MMMM=0-Eh, O=1 (2-byte match offset) +; Handle LLL=0-6 by jumping directly into # of bytes to copy (6 down to 1) +lit_len_mat_len_2b: + movsb + movsb + movsb + movsb + movsb + movsb + get_word_match_offset + copy_small_match_len + + +; Path #6: LLL=0-6, MMMM=Fh, O=1 (2-byte match offset) +lit_len_mat_ext_2b: + movsb + movsb + movsb + movsb + movsb + movsb + get_word_match_offset + copy_large_match_len + + +; Path #7: LLL=7, MMMM=0-Eh, O=1 (2-byte match offset) +lit_ext_mat_len_2b: +; on entry: ax=0 + token, bp=ax + lodsb ;grab extra literal length byte + add al,litrunlen ;add 7h literal run length + jz @@val249_7 ;if zf & cf, 249: get 16-bit literal length + jc @@val250_7 ;if cf, 250: get extra literal length byte + xchg cx,ax ;otherwise, we have our literal length + do_literal_copy ;this might be better as rep movsw !!! benchmark + get_word_match_offset + copy_small_match_len +@@val250_7: + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_literal_copy + get_word_match_offset + copy_small_match_len +@@val249_7: + lodsw ;grab 16-bit length + xchg cx,ax + do_literal_copy + get_word_match_offset + copy_small_match_len + + +; Path #8: LLL=7, MMMM=Fh, O=1 (2-byte match offset) +lit_ext_mat_ext_2b: +; on entry: ax=0 + token, bp=ax + lodsb ;grab extra literal length byte + add al,litrunlen ;add 7h literal run length + jz @@val249_8 ;if zf & cf, 249: get 16-bit literal length + jc @@val250_8 ;if cf, 250: get extra literal length byte + xchg cx,ax ;otherwise, we have our literal length + do_literal_copy ;this might be better as rep movsw !!! benchmark + get_word_match_offset + copy_large_match_len +@@val250_8: + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_literal_copy + get_word_match_offset + copy_large_match_len +@@val249_8: + lodsw ;grab 16-bit length + xchg cx,ax + do_literal_copy + get_word_match_offset + copy_large_match_len + + +done_decompressing: +;return # of decompressed bytes in ax + pop ax ;retrieve the original decompression offset + sub di,ax ;adjust for original offset + xchg di,ax ;return adjusted value in ax + ret ;done decompressing, exit to caller + +ENDP lzsa1_decompress_speed_jumptable + +ENDS CODE + +END + +;Speed optimization history (decompression times in microseconds @ 4.77 MHz): +; defer add MIN_MATCH_SIZE shuttle 97207 alice 57200 robotron 362884 ++* +; jumptable rewrite, no RLE shuttle 97744 alice 46905 robotron 309032 -++ +; adc cx,0 -> adc cl,0 shuttle 97744 alice 46893 robotron 309032 .+.! +; jumptable rewrite w/RLE shuttle 88776 alice 50433 robotron 319222 +-- +; short match copies movsb shuttle 97298 alice 49769 robotron 326282 ---rb +; long match copy #1 16-bit shuttle 92490 alice 46905 robotron 308722 +*+ +; long match copy #2 extraB shuttle 92464 alice 46905 robotron 308371 +.+ +; long match copy #3 0f->ed shuttle 86765 alice 46864 robotron 303895 +++! diff --git a/Tools/unix/lzsa/asm/8088/LZSA2FTA.ASM b/Tools/unix/lzsa/asm/8088/LZSA2FTA.ASM new file mode 100644 index 00000000..bf38e18c --- /dev/null +++ b/Tools/unix/lzsa/asm/8088/LZSA2FTA.ASM @@ -0,0 +1,302 @@ +; lzsa2fta.asm - LZSA v2 time-efficient decompressor implementation for 8088 +; Turbo Assembler IDEAL mode dialect; can also be assembled with NASM. +; +; Usual DOS assembler SMALL model assumptions apply. This code: +; - Assumes it was invoked via NEAR call (change RET to RETF for FAR calls) +; - Is interrupt-safe +; - Is not re-entrant (do not decompress while already running decompression) +; - Trashes all data and segment registers +; +; Copyright (C) 2019 Jim Leonard, Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + IDEAL + P8086 + MODEL SMALL + + CODESEG + +;While LZSA2 is technically capable of generating a match offset of -2, +;this sequence never actually showed up in my LZSA2 test corpus, likely due +;to compressor optimizations and the LZSA2 format itself. If you know your +;test data will contain a match offset of -2, you can enable code to write +;out the sequence very quickly at the cost of 18 bytes of code. +HANDLE_WORD_RUN EQU 0 + +PUBLIC lzsa2_decompress_speed + +; --------------------------------------------------------------------------- +; Decompress raw LZSA2 block +; inputs: +; * ds:si: raw LZSA2 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +PROC lzsa2_decompress_speed NEAR + +MACRO get_nybble +LOCAL has_nybble + neg bh ;nybble ready? + jns has_nybble + xchg bx,ax + lodsb ;load two nybbles + xchg bx,ax +has_nybble: + mov cl,4 ;swap 4 high and low bits of nybble + ror bl,cl + mov cl,0FH + and cl,bl +ENDM + +lzsa2_speed_start: + push di ;remember decompression offset + cld ;make string operations go forward + xor cx,cx + mov bx,0100H ;bx used by get_nybble + +@@decode_token: + mov ax,cx ;clear ah - cx is zero (and must stay that way) + lodsb ;read token byte: XYZ|LL|MMMM + mov dx,ax ;keep copy of token in dl + + and al,018H ;isolate literals length in token (LL) + jz @@check_offset ;no literals? stop decoding, go to matches + +;At this point, al can be in three (unshifted) states: 1, 2, or 3. +;3 = not done yet. + cmp al,(2 shl 3) ;LITERALS_RUN_LEN_V2? (original: cmp al,03h) + jb @@lit1b ;LZSA2 output 1-byte more often, so test first + je @@lit2b + + mov cl,3 + shr al,cl ;shift literals length into place + get_nybble ;cl := get extra literals length nybble + add al,cl ;add len from token to nybble + cmp al,012H ;LITERALS_RUN_LEN_V2 + 15 ? + jne @@got_literals ;if not, we have the full literals count + lodsb ;grab extra length byte + add al,012H ;overflow? + jnc @@got_literals_big ;if not, we have a big full literals count + lodsw ;grab 16-bit extra length + +;For larger counts, it pays to set up a faster copy +@@got_literals_big: + xchg cx,ax + shr cx,1 + rep movsw + adc cx,0 + rep movsb + jmp @@check_offset + +@@got_literals: + xchg cx,ax + rep movsb ;copy cx literals from ds:si to es:di + jmp @@check_offset + +;LZSA2 likes to produce tiny literals of 1 or 2 bytes. Handle them here. +@@lit2b:movsb +@@lit1b:movsb + +@@check_offset: + test dl,dl ;check match offset mode in token (X bit) + js @@rep_match_or_large_offset + + cmp dl,040H ;check if this is a 5 or 9-bit offset (Y bit) + jnb @@offset_9_bit + + ;5 bit offset: + xchg cx,ax ;clear ah - cx is zero from prior rep movs + mov al,020H ;shift Z (offset bit 4) in place + and al,dl + shl al,1 + shl al,1 + get_nybble ;get nybble for offset bits 0-3 + or al,cl ;merge nybble + rol al,1 + xor al,0E1H ;set offset bits 7-5 to 1 + dec ah ;set offset bits 15-8 to 1 + jmp @@get_match_length + +@@rep_match_or_16_bit: + test dl,020H ;test bit Z (offset bit 8) + jne @@repeat_match ;rep-match + + ;16 bit offset: + lodsw ;Get 2-byte match offset + xchg ah,al + jmp @@get_match_length + +@@offset_9_bit: + ;9 bit offset: + xchg cx,ax ;clear ah - cx is zero from prior rep movs + lodsb ;get 8 bit offset from stream in A + dec ah ;set offset bits 15-8 to 1 + test dl,020H ;test bit Z (offset bit 8) + je @@get_match_length + dec ah ;clear bit 8 if Z bit is clear + jmp @@get_match_length + +@@rep_match_or_large_offset: + cmp dl,0c0H ;check if this is a 13-bit offset + ;or a 16-bit offset/rep match (Y bit) + jnb @@rep_match_or_16_bit + + ;13 bit offset: + mov ah,020H ;shift Z (offset bit 12) in place + and ah,dl + shl ah,1 + shl ah,1 + get_nybble ;get nybble for offset bits 8-11 + or ah,cl ;merge nybble + rol ah,1 + xor ah,0E1H ;set offset bits 15-13 to 1 + sub ah,2 ;substract 512 + lodsb ;load match offset bits 0-7 + +@@get_match_length: + mov bp,ax ;bp:=offset +@@repeat_match: + mov ax,dx ;ax: original token + and al,07H ;isolate match length in token (MMM) + add al,2 ;add MIN_MATCH_SIZE_V2 + + cmp al,09H ;MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + jne @@got_matchlen ;no, we have full match length from token + + get_nybble ;get extra literals length nybble + add al,cl ;add len from token to nybble + cmp al,018H ;MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + jne @@got_matchlen ;no, we have full match length from token + + lodsb ;grab extra length byte + add al,018H ;overflow? + jnc @@got_matchlen_big ;if not, we have entire (big) length + je @@done_decompressing ; detect EOD code + + lodsw ;grab 16-bit length + +;If we're here, we have a larger match copy and can optimize how we do that +@@got_matchlen_big: + xchg cx,ax ;copy match length into cx + mov dx,ds ;save ds + mov ax,es + mov ds,ax ;ds:=es + xchg si,ax ;dx:ax = old ds:si + mov si,di ;ds:si now points at back reference in output data + add si,bp +IF HANDLE_WORD_RUN + cmp bp,-2 ;do we have a byte/word run to optimize? + jae @@do_run ;perform a run +ELSE + cmp bp,-1 ;do we have a byte run to optimize? + je @@do_run_1 ;perform a byte run +ENDIF +;You may be tempted to change "jae" to "jge" because DX is a signed number. +;Don't! The total window is 64k, so if you treat this as a signed comparison, +;you will get incorrect results for offsets over 32K. +; +;If we're here, we have a long copy and it isn't byte-overlapping (if it +;overlapped, we'd be in @@do_run_1) So, let's copy faster with REP MOVSW. +;This won't affect 8088 that much, but it speeds up 8086 and higher. + shr cx,1 + rep movsw + adc cx,0 + rep movsb + xchg si,ax + mov ds,dx ;restore ds:si + jmp @@decode_token ;go decode another token + +;Smaller match copies handled here: +@@got_matchlen: + xchg cx,ax ;copy match length into cx + mov dx,ds ;save ds + mov ax,es + mov ds,ax ;ds:=es + xchg si,ax ;dx:ax = old ds:si + mov si,di ;ds:si = back reference in output data + add si,bp + rep movsb ;copy match + xchg si,ax + mov ds,dx ;restore ds:si + jmp @@decode_token ;go decode another token + +@@done_decompressing: + pop ax ;retrieve the original decompression offset + xchg di,ax ;compute decompressed size + sub ax,di + ret ;done + +IF HANDLE_WORD_RUN +@@do_run: + je @@do_run_2 ;fall through to byte (common) if not word run +ENDIF + +@@do_run_1: + push ax + lodsb ;load first byte of run into al + mov ah,al + shr cx,1 + rep stosw ;perform word run + adc cx,0 + rep stosb ;finish word run + pop si + mov ds,dx + jmp @@decode_token ;go decode another token + +IF HANDLE_WORD_RUN +@@do_run_2: + push ax + lodsw ;load first word of run + shr cx,1 + rep stosw ;perform word run + adc cx,0 ;despite 2-byte offset, compressor might + rep stosb ;output odd length. better safe than sorry. + pop si + mov ds,dx + jmp @@decode_token ;go decode another token +ENDIF + +ENDP lzsa2_decompress_speed + +ENDS + +END + +;Speed optimization history (decompression times in microseconds @ 4.77 MHz): +;Compression corpus:shuttle alice robotro rletest largetx linewar ...... .. +;Start of exercise 160828 113311 665900 238507 1053865 1004237 ****** +;add al,val -> al,cl 160813 113296 668721 237484 1053604 1003815 ++-+++ +;sub ah,2 -> dec dec 160907 113585 666744 237484 1056651 1005172 --+*-- rb +;mov ax,cx->xchgcxax 159741 112460 660594 237477 1046770 998323 ++++++ +;unroll get_nibble 152552 106327 621119 237345 982381 942373 ++++++ +;early exit if LL=0 147242 103842 615559 239318 946863 942932 +++-+- +;push/pop->mov/mov 145447 100832 604822 237288 927017 931366 ++++++ +;push/pop->mov/mov(2)143214 98817 592920 239298 908217 910955 +++-++ +;rep stos for -1, -2 143289 102812 617087 237164 942081 940688 ---+-- rb +;larger literal cpys 143214 98817 591940 238296 907237 909657 **++++ +;larger copys & runs 132440 98802 586551 178768 904129 896709 ++++++ :-) +;smaller lit. copies 131991 99131 583933 177760 901824 898308 +-+++- +;swap smal lit compa 131828 99022 585121 177757 901793 894054 ++-*++ +;compare before shif 130587 95970 569908 177753 889221 872461 +++*++ +;getmatchlength base 130587 95970 570634 177753 893536 871556 ...... === +; f->rep_match_or_16 xxxxxx xxxxx 569910 xxxxxx 889266 871435 ..+.++ +; f->rep_match_or_la 129966 94748 566169 xxxxxx 880870 867030 +++.++ +++ +; f->offset_9_bit 132126 95258 568869 xxxxxx 893169 870364 -++.-+ +;final fallthrough 129966 94748 566169 177753 880870 865023 ****** diff --git a/Tools/unix/lzsa/asm/8088/decompress_small_v1.S b/Tools/unix/lzsa/asm/8088/decompress_small_v1.S new file mode 100644 index 00000000..ddb9196f --- /dev/null +++ b/Tools/unix/lzsa/asm/8088/decompress_small_v1.S @@ -0,0 +1,120 @@ +; decompress_small.S - space-efficient decompressor implementation for 8088 +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 16 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA1 block +; inputs: +; * ds:si: raw LZSA1 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +lzsa1_decompress: + push di ; remember decompression offset + cld ; make string operations (lods, movs, stos..) move forward + + xor cx,cx + +.decode_token: + mov ax,cx ; clear ah - cx is zero from above or from after rep movsb in .copy_match + lodsb ; read token byte: O|LLL|MMMM + mov dx,ax ; keep token in dl + + and al,070H ; isolate literals length in token (LLL) + mov cl,4 + shr al,cl ; shift literals length into place + + cmp al,07H ; LITERALS_RUN_LEN? + jne .got_literals ; no, we have the full literals count from the token, go copy + + lodsb ; grab extra length byte + add al,07H ; add LITERALS_RUN_LEN + jnc .got_literals ; if no overflow, we have the full literals count, go copy + jne .mid_literals + + lodsw ; grab 16-bit extra length + db 81H ; mask inc ah/lodsb + ; (*like jmp short .got_literals but faster) + +.mid_literals: + inc ah ; add 256 + lodsb ; grab single extra length byte + +.got_literals: + xchg cx,ax + rep movsb ; copy cx literals from ds:si to es:di + + test dl,dl ; check match offset size in token (O bit) + js .get_long_offset + + dec cx + xchg cx,ax ; ah to 0xff - cx was zero from the rep movsb above + lodsb + db 3CH ; mask lodsw + ; (*like jmp short .get_match_length but faster) + +.get_long_offset: + lodsw ; Get 2-byte match offset + +.get_match_length: + xchg dx,ax ; dx: match offset ax: original token + and al,0FH ; isolate match length in token (MMMM) + add al,3 ; add MIN_MATCH_SIZE + + cmp al,012H ; MATCH_RUN_LEN? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + lodsb ; grab extra length byte + add al,012H ; add MIN_MATCH_SIZE + MATCH_RUN_LEN + jnc .got_matchlen ; if no overflow, we have the entire length + jne .mid_matchlen + + lodsw ; grab 16-bit length + test ax,ax ; bail if we hit EOD + je short .done_decompressing + + db 81H ; mask inc ah/lodsb + ; (*like jmp short .got_literals but faster) +.mid_matchlen: + inc ah ; add 256 + lodsb ; grab single extra length byte + +.got_matchlen: + xchg cx,ax ; copy match length into cx + push ds ; save ds:si (current pointer to compressed data) + xchg si,ax + push es + pop ds + mov si,di ; ds:si now points at back reference in output data + add si,dx + rep movsb ; copy match + xchg si,ax ; restore ds:si + pop ds + jmp short .decode_token ; go decode another token + +.done_decompressing: + pop ax ; retrieve the original decompression offset + xchg ax,di ; compute decompressed size + sub ax,di + ret ; done diff --git a/Tools/unix/lzsa/asm/8088/decompress_small_v2.S b/Tools/unix/lzsa/asm/8088/decompress_small_v2.S new file mode 100644 index 00000000..16a95df5 --- /dev/null +++ b/Tools/unix/lzsa/asm/8088/decompress_small_v2.S @@ -0,0 +1,176 @@ +; decompress_small.S - space-efficient decompressor implementation for 8088 +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 16 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA2 block +; inputs: +; * ds:si: raw LZSA2 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +lzsa2_decompress: + push di ; remember decompression offset + cld ; make string operations (lods, movs, stos..) move forward + + xor cx,cx + mov bx,0100H + xor bp,bp + +.decode_token: + mov ax,cx ; clear ah - cx is zero from above or from after rep movsb in .copy_match + lodsb ; read token byte: XYZ|LL|MMMM + mov dx,ax ; keep token in dl + + and al,018H ; isolate literals length in token (LL) + mov cl,3 + shr al,cl ; shift literals length into place + + cmp al,03H ; LITERALS_RUN_LEN_V2? + jne .got_literals ; no, we have the full literals count from the token, go copy + + call .get_nibble ; get extra literals length nibble + add al,cl ; add len from token to nibble + cmp al,012H ; LITERALS_RUN_LEN_V2 + 15 ? + jne .got_literals ; if not, we have the full literals count, go copy + + lodsb ; grab extra length byte + add al,012H ; overflow? + jnc .got_literals ; if not, we have the full literals count, go copy + + lodsw ; grab 16-bit extra length + +.got_literals: + xchg cx,ax + rep movsb ; copy cx literals from ds:si to es:di + + test dl,0C0h ; check match offset mode in token (X bit) + js .rep_match_or_large_offset + + ;;cmp dl,040H ; check if this is a 5 or 9-bit offset (Y bit) + ; discovered via the test with bit 6 set + xchg cx,ax ; clear ah - cx is zero from the rep movsb above + jne .offset_9_bit + + ; 5 bit offset + cmp dl,020H ; test bit 5 + call .get_nibble_x + jmp short .dec_offset_top + +.offset_9_bit: ; 9 bit offset + lodsb ; get 8 bit offset from stream in A + dec ah ; set offset bits 15-8 to 1 + test dl,020H ; test bit Z (offset bit 8) + je .get_match_length +.dec_offset_top: + dec ah ; clear bit 8 if Z bit is clear + ; or set offset bits 15-8 to 1 + jmp short .get_match_length + +.rep_match_or_large_offset: + ;;cmp dl,0c0H ; check if this is a 13-bit offset or a 16-bit offset/rep match (Y bit) + jpe .rep_match_or_16_bit + + ; 13 bit offset + + cmp dl,0A0H ; test bit 5 (knowing that bit 7 is also set) + xchg ah,al + call .get_nibble_x + sub al,2 ; substract 512 + jmp short .get_match_length_1 + +.rep_match_or_16_bit: + test dl,020H ; test bit Z (offset bit 8) + jne .repeat_match ; rep-match + + ; 16 bit offset + lodsb ; Get 2-byte match offset + +.get_match_length_1: + xchg ah,al + lodsb ; load match offset bits 0-7 + +.get_match_length: + xchg bp,ax ; bp: offset +.repeat_match: + xchg ax,dx ; ax: original token + and al,07H ; isolate match length in token (MMM) + add al,2 ; add MIN_MATCH_SIZE_V2 + + cmp al,09H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + call .get_nibble ; get extra literals length nibble + add al,cl ; add len from token to nibble + cmp al,018H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + lodsb ; grab extra length byte + add al,018H ; overflow? + jnc .got_matchlen ; if not, we have the entire length + je short .done_decompressing ; detect EOD code + + lodsw ; grab 16-bit length + +.got_matchlen: + xchg cx,ax ; copy match length into cx + push ds ; save ds:si (current pointer to compressed data) + xchg si,ax + push es + pop ds + lea si,[bp+di] ; ds:si now points at back reference in output data + rep movsb ; copy match + xchg si,ax ; restore ds:si + pop ds + jmp .decode_token ; go decode another token + +.done_decompressing: + pop ax ; retrieve the original decompression offset + xchg di,ax ; compute decompressed size + sub ax,di + ret ; done + +.get_nibble_x: + cmc ; carry set if bit 4 was set + rcr al,1 + call .get_nibble ; get nibble for offset bits 0-3 + or al,cl ; merge nibble + rol al,1 + xor al,0E1H ; set offset bits 7-5 to 1 + ret + +.get_nibble: + neg bh ; nibble ready? + jns .has_nibble + + xchg bx,ax + lodsb ; load two nibbles + xchg bx,ax + +.has_nibble: + mov cl,4 ; swap 4 high and low bits of nibble + ror bl,cl + mov cl,0FH + and cl,bl + ret diff --git a/Tools/unix/lzsa/asm/8088/decompress_speed_v1.S b/Tools/unix/lzsa/asm/8088/decompress_speed_v1.S new file mode 100644 index 00000000..142bd15a --- /dev/null +++ b/Tools/unix/lzsa/asm/8088/decompress_speed_v1.S @@ -0,0 +1,236 @@ +; decompress_speed_v1.S - time-efficient decompressor implementation for 8088 +; NASM syntax. +; +; Usual DOS assembler SMALL model assumptions apply. This code: +; - Assumes it was invoked via NEAR call (change RET to RETF for FAR calls) +; - Is interrupt-safe +; - Is not re-entrant (do not decompress while already running decompression) +; - Trashes all data and segment registers +; +; Copyright (C) 2019 Jim Leonard, Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 16 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA1 block +; inputs: +; * ds:si: raw LZSA1 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +; Must declare this in the code segment: +SHR4table: + DB 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h + DB 01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h + DB 02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h + DB 03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h + DB 04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h + DB 05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h + DB 06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h + DB 07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h + +lzsa1_decompress_speed: + push di ;remember decompression offset + cld ;ensure string ops move forward + mov bx,SHR4table + xor cx,cx + +.decode_token: + xchg cx,ax ;clear ah (cx = 0 from match copy's rep movsb) + lodsb ;read token byte: O|LLL|MMMM + mov dx,ax ;copy our token to dl for later MMMM handling + + and al,070H ;isolate literals length in token (LLL) + jz .check_offset_size ;if LLL=0, we have no literals; goto match + cmp al,070H ;LITERALS_RUN_LEN? + jne .got_literals ;no, we have full count from token; go copy + + lodsb ;grab extra length byte + add al,07H ;add LITERALS_RUN_LEN + jnc .got_literals_exact ;if no overflow, we have full count + je .big_literals + +.mid_literals: + lodsb ;grab single extra length byte + inc ah ;add 256 + xchg cx,ax ;with longer counts, we can save some time + shr cx,1 ;by doing a word copy instead of a byte copy. + rep movsw ;We don't need to account for overlap because + adc cx,0 ;source for literals isn't the output buffer. + rep movsb + jmp .check_offset_size + +.big_literals: + lodsw ;grab 16-bit extra length + xchg cx,ax ;with longer counts, we can save some time + shr cx,1 ;by doing a word copy instead of a byte copy. + rep movsw + adc cx,0 + rep movsb + jmp .check_offset_size + +.got_literals: + cs xlat ;shift literals length into place +.got_literals_exact: + xchg cx,ax + rep movsb ;copy cx literals from ds:si to es:di + +.check_offset_size: + test dl,dl ;check match offset size in token (O bit) + js .get_long_offset ;load absolute 16-bit match offset + + mov ah,0ffh ;set up high byte + lodsb ;load low byte + +.get_match_length: + xchg dx,ax ;dx: match offset ax: original token + and al,0FH ;isolate match length in token (MMMM) + cmp al,0FH ;MATCH_RUN_LEN? + jne .got_matchlen_short ;no, we have the full match length from the token, go copy + + lodsb ;grab extra length byte + add al,012H ;add MIN_MATCH_SIZE + MATCH_RUN_LEN + jnc .do_long_copy ;if no overflow, we have the entire length + jne .mid_matchlen + + lodsw ;grab 16-bit length + xchg cx,ax ;get ready to do a long copy + jcxz .done_decompressing ;wait, is it the EOD marker? Exit if so + jmp .copy_len_preset ;otherwise, do the copy + +.get_long_offset: + lodsw ;Get 2-byte match offset + jmp .get_match_length + +.got_matchlen_short: + add al,3 ;add MIN_MATCH_SIZE + xchg cx,ax ;copy match length into cx + mov bp,ds ;save ds + mov ax,es + mov ds,ax ;ds=es + xchg ax,si ;save si + mov si,di ;ds:si now points at back reference in output data + add si,dx + rep movsb ;copy match + xchg si,ax ;restore si + mov ds,bp ;restore ds + jmp .decode_token ;go decode another token + +.done_decompressing: + pop ax ;retrieve the original decompression offset + xchg di,ax ;compute decompressed size + sub ax,di + ret ;done decompressing, exit to caller + +;With a confirmed longer match length, we have an opportunity to optimize for +;the case where a single byte is repeated long enough that we can benefit +;from rep movsw to perform the run (instead of rep movsb). +.mid_matchlen: + lodsb ;grab single extra length byte + inc ah ;add 256 +.do_long_copy: + xchg cx,ax ;copy match length into cx +.copy_len_preset: + push ds ;save ds + mov bp,es + mov ds,bp ;ds=es + mov bp,si ;save si + mov si,di ;ds:si now points at back reference in output data + add si,dx + cmp dx,-2 ;do we have a byte/word run to optimize? + jae .do_run ;perform a run +;You may be tempted to change "jae" to "jge" because DX is a signed number. +;Don't! The total window is 64k, so if you treat this as a signed comparison, +;you will get incorrect results for offsets over 32K. + +;If we're here, we have a long copy and it isn't byte-overlapping (if it +;overlapped, we'd be in .do_run_1) So, let's copy faster with REP MOVSW. +;This won't affect 8088 that much, but it speeds up 8086 and higher. + shr cx,1 + rep movsw + adc cx,0 + rep movsb + mov si,bp ;restore si + pop ds + jmp .decode_token ;go decode another token + +.do_run: + je .do_run_2 ;fall through to byte (common) if not word run + +.do_run_1: + lodsb ;load first byte of run into al + mov ah,al + shr cx,1 + rep stosw ;perform word run + adc cx,0 + rep stosb ;finish word run + mov si,bp ;restore si + pop ds + jmp .decode_token ;go decode another token + +.do_run_2: + lodsw ;load first word of run + shr cx,1 + rep stosw ;perform word run + adc cx,0 ;despite 2-byte offset, compressor might + rep stosb ;output odd length. better safe than sorry. + mov si,bp ;restore si + pop ds + jmp .decode_token ;go decode another token + +;Speed optimization history (decompression times in microseconds @ 4.77 MHz): +; original E. Marty code shuttle 123208 alice 65660 robotron 407338 *** +; table for shr al,4 shuttle 120964 alice 63230 robotron 394733 +++ +; push/pop to mov/mov shuttle 118176 alice 61835 robotron 386762 +++ +; movsw for literalcpys shuttle 124102 alice 64908 robotron 400220 --- rb +; stosw for byte runs shuttle 118897 alice 65040 robotron 403518 --- rb +; better stosw for runs shuttle 117712 alice 65040 robotron 403343 +-- +; disable RLE by default shuttle 116924 alice 60783 robotron 381226 +++ +; optimize got_matchlen shuttle 115294 alice 59588 robotron 374330 +++ +; fall through to getML shuttle 113258 alice 59572 robotron 372004 +++ +; fall through to midLI shuttle 113258 alice 59572 robotron 375060 ..- rb +; fall through midMaLen shuttle 113247 alice 59572 robotron 372004 +.+ +; movsw for litlen > 255 shuttle 113247 alice 59572 robotron 371612 ..+ +; rep stosw for long runs shuttle 113247 alice 59572 robotron 371612 ... +; rep movsw for long cpys shuttle 113247 alice 59572 robotron 371035 ..+ +; xchg/dec ah -> mov ah,val shuttle 112575 alice 59272 robotron 369198 +++ +; force >12h len.to longcpy shuttle 101998 alice 59266 robotron 364459 +.+ +; more efficient run branch shuttle 102239 alice 59297 robotron 364716 --- rb +; even more eff. run branch shuttle 101998 alice 59266 robotron 364459 *** +; BUGFIX - bad sign compare shuttle 101955 alice 59225 robotron 364117 +++ +; reverse 16-bit len compar shuttle 102000 alice 59263 robotron 364460 --- rb +; jcxz for EOD detection no change to speed, but is 1 byte shorter +++ +; force movsw for literals shuttle 107183 alice 62555 robotron 379524 --- rb +; defer shr4 until necessry shuttle 102069 alice 60236 robotron 364096 --- +; skip literals if LLL=0 shuttle 98655 alice 57849 robotron 363358 --- +; fall through to mid_liter shuttle 98595 alice 57789 robotron 361998 +++ +; == jumptable experiments begin == +; jumptable for small copys shuttle 101594 alice 61078 robotron 386018 --- +; start:xchg instead of mov shuttle 100948 alice 60467 robotron 381112 +++ +; use table for LLL=0 check shuttle 106972 alice 63333 robotron 388304 --- rb +; jmptbl to fallthrough mov shuttle 102532 alice 60760 robotron 383070 --- +; cpy fallthrough check_ofs shuttle 98939 alice 58917 robotron 371019 +** +; single jumptable jump shuttle 97528 alice 57264 robotron 362194 ++* +; conditional check for L=7 shuttle 98610 alice 58521 robotron 368153 --- rb +; rip out the jumptable :-/ shuttle 97616 alice 57128 robotron 360697 +++ +; defer add MIN_MATCH_SIZE shuttle 97250 alice 57004 robotron 361191 ++? +; cache constants in regs shuttle 104681 alice 59939 robotron 380125 --- rb diff --git a/Tools/unix/lzsa/asm/8088/decompress_speed_v2.S b/Tools/unix/lzsa/asm/8088/decompress_speed_v2.S new file mode 100644 index 00000000..973ae01b --- /dev/null +++ b/Tools/unix/lzsa/asm/8088/decompress_speed_v2.S @@ -0,0 +1,288 @@ +; decompress_speed_v2.S - LZSA v2 time-efficient decompressor implementation for 8088 +; NASM syntax. +; +; Usual DOS assembler SMALL model assumptions apply. This code: +; - Assumes it was invoked via NEAR call (change RET to RETF for FAR calls) +; - Is interrupt-safe +; - Is not re-entrant (do not decompress while already running decompression) +; - Trashes all data and segment registers +; +; Copyright (C) 2019 Jim Leonard, Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 16 + +;While LZSA2 is technically capable of generating a match offset of -2, +;this sequence never actually showed up in my LZSA2 test corpus, likely due +;to compressor optimizations and the LZSA2 format itself. If you know your +;test data will contain a match offset of -2, you can enable code to write +;out the sequence very quickly at the cost of 18 bytes of code. +HANDLE_WORD_RUN EQU 0 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA2 block +; inputs: +; * ds:si: raw LZSA2 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +%macro get_nybble 0 + neg bh ;nybble ready? + jns %%has_nybble + xchg bx,ax + lodsb ;load two nybbles + xchg bx,ax +%%has_nybble: + mov cl,4 ;swap 4 high and low bits of nybble + ror bl,cl + mov cl,0FH + and cl,bl +%endmacro + +lzsa2_decompress_speed: + push di ;remember decompression offset + cld ;make string operations go forward + xor cx,cx + mov bx,0100H ;bx used by get_nybble + +.decode_token: + mov ax,cx ;clear ah - cx is zero (and must stay that way) + lodsb ;read token byte: XYZ|LL|MMMM + mov dx,ax ;keep copy of token in dl + + and al,018H ;isolate literals length in token (LL) + jz .check_offset ;no literals? stop decoding, go to matches + +;At this point, al can be in three (unshifted) states: 1, 2, or 3. +;3 = not done yet. + cmp al,(2 << 3) ;LITERALS_RUN_LEN_V2? (original: cmp al,03h) + jb .lit1b ;LZSA2 output 1-byte more often, so test first + je .lit2b + + mov cl,3 + shr al,cl ;shift literals length into place + get_nybble ;cl := get extra literals length nybble + add al,cl ;add len from token to nybble + cmp al,012H ;LITERALS_RUN_LEN_V2 + 15 ? + jne .got_literals ;if not, we have the full literals count + lodsb ;grab extra length byte + add al,012H ;overflow? + jnc .got_literals_big ;if not, we have a big full literals count + lodsw ;grab 16-bit extra length + +;For larger counts, it pays to set up a faster copy +.got_literals_big: + xchg cx,ax + shr cx,1 + rep movsw + adc cx,0 + rep movsb + jmp .check_offset + +.got_literals: + xchg cx,ax + rep movsb ;copy cx literals from ds:si to es:di + jmp .check_offset + +;LZSA2 likes to produce tiny literals of 1 or 2 bytes. Handle them here. +.lit2b:movsb +.lit1b:movsb + +.check_offset: + test dl,dl ;check match offset mode in token (X bit) + js .rep_match_or_large_offset + + cmp dl,040H ;check if this is a 5 or 9-bit offset (Y bit) + jnb .offset_9_bit + + ;5 bit offset: + xchg cx,ax ;clear ah - cx is zero from prior rep movs + mov al,020H ;shift Z (offset bit 4) in place + and al,dl + shl al,1 + shl al,1 + get_nybble ;get nybble for offset bits 0-3 + or al,cl ;merge nybble + rol al,1 + xor al,0E1H ;set offset bits 7-5 to 1 + dec ah ;set offset bits 15-8 to 1 + jmp .get_match_length + +.rep_match_or_16_bit: + test dl,020H ;test bit Z (offset bit 8) + jne .repeat_match ;rep-match + + ;16 bit offset: + lodsw ;Get 2-byte match offset + xchg ah,al + jmp .get_match_length + +.offset_9_bit: + ;9 bit offset: + xchg cx,ax ;clear ah - cx is zero from prior rep movs + lodsb ;get 8 bit offset from stream in A + dec ah ;set offset bits 15-8 to 1 + test dl,020H ;test bit Z (offset bit 8) + je .get_match_length + dec ah ;clear bit 8 if Z bit is clear + jmp .get_match_length + +.rep_match_or_large_offset: + cmp dl,0c0H ;check if this is a 13-bit offset + ;or a 16-bit offset/rep match (Y bit) + jnb .rep_match_or_16_bit + + ;13 bit offset: + mov ah,020H ;shift Z (offset bit 12) in place + and ah,dl + shl ah,1 + shl ah,1 + get_nybble ;get nybble for offset bits 8-11 + or ah,cl ;merge nybble + rol ah,1 + xor ah,0E1H ;set offset bits 15-13 to 1 + sub ah,2 ;substract 512 + lodsb ;load match offset bits 0-7 + +.get_match_length: + mov bp,ax ;bp:=offset +.repeat_match: + mov ax,dx ;ax: original token + and al,07H ;isolate match length in token (MMM) + add al,2 ;add MIN_MATCH_SIZE_V2 + + cmp al,09H ;MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + jne .got_matchlen ;no, we have full match length from token + + get_nybble ;get extra literals length nybble + add al,cl ;add len from token to nybble + cmp al,018H ;MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + jne .got_matchlen ;no, we have full match length from token + + lodsb ;grab extra length byte + add al,018H ;overflow? + jnc .got_matchlen_big ;if not, we have entire (big) length + je .done_decompressing ; detect EOD code + + lodsw ;grab 16-bit length + +;If we're here, we have a larger match copy and can optimize how we do that +.got_matchlen_big: + xchg cx,ax ;copy match length into cx + mov dx,ds ;save ds + mov ax,es + mov ds,ax ;ds:=es + xchg si,ax ;dx:ax = old ds:si + mov si,di ;ds:si now points at back reference in output data + add si,bp +%if HANDLE_WORD_RUN + cmp bp,-2 ;do we have a byte/word run to optimize? + jae .do_run ;perform a run +%else + cmp bp,-1 ;do we have a byte run to optimize? + je .do_run_1 ;perform a byte run +%endif +;You may be tempted to change "jae" to "jge" because DX is a signed number. +;Don't! The total window is 64k, so if you treat this as a signed comparison, +;you will get incorrect results for offsets over 32K. +; +;If we're here, we have a long copy and it isn't byte-overlapping (if it +;overlapped, we'd be in .do_run_1) So, let's copy faster with REP MOVSW. +;This won't affect 8088 that much, but it speeds up 8086 and higher. + shr cx,1 + rep movsw + adc cx,0 + rep movsb + xchg si,ax + mov ds,dx ;restore ds:si + jmp .decode_token ;go decode another token + +;Smaller match copies handled here: +.got_matchlen: + xchg cx,ax ;copy match length into cx + mov dx,ds ;save ds + mov ax,es + mov ds,ax ;ds:=es + xchg si,ax ;dx:ax = old ds:si + mov si,di ;ds:si = back reference in output data + add si,bp + rep movsb ;copy match + xchg si,ax + mov ds,dx ;restore ds:si + jmp .decode_token ;go decode another token + +.done_decompressing: + pop ax ;retrieve the original decompression offset + xchg di,ax ;compute decompressed size + sub ax,di + ret ;done + +%if HANDLE_WORD_RUN +.do_run: + je .do_run_2 ;fall through to byte (common) if not word run +%endif + +.do_run_1: + push ax + lodsb ;load first byte of run into al + mov ah,al + shr cx,1 + rep stosw ;perform word run + adc cx,0 + rep stosb ;finish word run + pop si + mov ds,dx + jmp .decode_token ;go decode another token + +%if HANDLE_WORD_RUN +.do_run_2: + push ax + lodsw ;load first word of run + shr cx,1 + rep stosw ;perform word run + adc cx,0 ;despite 2-byte offset, compressor might + rep stosb ;output odd length. better safe than sorry. + pop si + mov ds,dx + jmp .decode_token ;go decode another token +%endif + +;Speed optimization history (decompression times in microseconds @ 4.77 MHz): +;Compression corpus:shuttle alice robotro rletest largetx linewar ...... .. +;Start of exercise 160828 113311 665900 238507 1053865 1004237 ****** +;add al,val -> al,cl 160813 113296 668721 237484 1053604 1003815 ++-+++ +;sub ah,2 -> dec dec 160907 113585 666744 237484 1056651 1005172 --+*-- rb +;mov ax,cx->xchgcxax 159741 112460 660594 237477 1046770 998323 ++++++ +;unroll get_nibble 152552 106327 621119 237345 982381 942373 ++++++ +;early exit if LL=0 147242 103842 615559 239318 946863 942932 +++-+- +;push/pop->mov/mov 145447 100832 604822 237288 927017 931366 ++++++ +;push/pop->mov/mov(2)143214 98817 592920 239298 908217 910955 +++-++ +;rep stos for -1, -2 143289 102812 617087 237164 942081 940688 ---+-- rb +;larger literal cpys 143214 98817 591940 238296 907237 909657 **++++ +;larger copys & runs 132440 98802 586551 178768 904129 896709 ++++++ :-) +;smaller lit. copies 131991 99131 583933 177760 901824 898308 +-+++- +;swap smal lit compa 131828 99022 585121 177757 901793 894054 ++-*++ +;compare before shif 130587 95970 569908 177753 889221 872461 +++*++ +;getmatchlength base 130587 95970 570634 177753 893536 871556 ...... === +; f->rep_match_or_16 xxxxxx xxxxx 569910 xxxxxx 889266 871435 ..+.++ +; f->rep_match_or_la 129966 94748 566169 xxxxxx 880870 867030 +++.++ +++ +; f->offset_9_bit 132126 95258 568869 xxxxxx 893169 870364 -++.-+ +;final fallthrough 129966 94748 566169 177753 880870 865023 ****** diff --git a/Tools/unix/lzsa/asm/x86/decompress_small_v1.asm b/Tools/unix/lzsa/asm/x86/decompress_small_v1.asm new file mode 100644 index 00000000..41ce991c --- /dev/null +++ b/Tools/unix/lzsa/asm/x86/decompress_small_v1.asm @@ -0,0 +1,120 @@ +; decompress_small_v1.asm - space-efficient decompressor implementation for x86 +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 32 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA1 block +; inputs: +; * esi: raw LZSA1 block +; * edi: output buffer +; output: +; * eax: decompressed size +; --------------------------------------------------------------------------- + + %ifndef BIN + global lzsa1_decompress + global _lzsa1_decompress + %endif + +lzsa1_decompress: +_lzsa1_decompress: + pushad + + ;mov edi, [esp+32+4] ; edi = outbuf + ;mov esi, [esp+32+8] ; esi = inbuf + + xor ecx, ecx +.decode_token: + mul ecx + lodsb ; read token byte: O|LLL|MMMM + mov dl, al ; keep token in dl + + and al, 070H ; isolate literals length in token (LLL) + shr al, 4 ; shift literals length into place + + cmp al, 07H ; LITERALS_RUN_LEN? + jne .got_literals ; no, we have the full literals count from the token, go copy + + lodsb ; grab extra length byte + add al, 07H ; add LITERALS_RUN_LEN + jnc .got_literals ; if no overflow, we have the full literals count, go copy + jne .mid_literals + + lodsw ; grab 16-bit extra length + jmp .got_literals + +.mid_literals: + lodsb ; grab single extra length byte + inc ah ; add 256 + +.got_literals: + xchg ecx, eax + rep movsb ; copy cx literals from ds:si to es:di + + test dl, dl ; check match offset size in token (O bit) + js .get_long_offset + + dec ecx + xchg eax, ecx ; clear ah - cx is zero from the rep movsb above + lodsb + jmp .get_match_length + +.get_long_offset: + lodsw ; Get 2-byte match offset + +.get_match_length: + xchg eax, edx ; edx: match offset eax: original token + and al, 0FH ; isolate match length in token (MMMM) + add al, 3 ; add MIN_MATCH_SIZE + + cmp al, 012H ; MATCH_RUN_LEN? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + lodsb ; grab extra length byte + add al,012H ; add MIN_MATCH_SIZE + MATCH_RUN_LEN + jnc .got_matchlen ; if no overflow, we have the entire length + jne .mid_matchlen + + lodsw ; grab 16-bit length + test eax, eax ; bail if we hit EOD + je .done_decompressing + jmp .got_matchlen + +.mid_matchlen: + lodsb ; grab single extra length byte + inc ah ; add 256 + +.got_matchlen: + xchg ecx, eax ; copy match length into ecx + xchg esi, eax + mov esi, edi ; esi now points at back reference in output data + movsx edx, dx ; sign-extend dx to 32-bits. + add esi, edx + rep movsb ; copy match + xchg esi, eax ; restore esi + jmp .decode_token ; go decode another token + +.done_decompressing: + sub edi, [esp+32+4] + mov [esp+28], edi ; eax = decompressed size + popad + ret ; done diff --git a/Tools/unix/lzsa/asm/x86/decompress_small_v2.asm b/Tools/unix/lzsa/asm/x86/decompress_small_v2.asm new file mode 100644 index 00000000..fe185c1d --- /dev/null +++ b/Tools/unix/lzsa/asm/x86/decompress_small_v2.asm @@ -0,0 +1,181 @@ +; decompress_small_v2.asm - space-efficient decompressor implementation for x86 +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 32 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA2 block +; inputs: +; * esi: raw LZSA2 block +; * edi: output buffer +; output: +; * eax: decompressed size +; --------------------------------------------------------------------------- + + %ifndef BIN + global lzsa2_decompress + global _lzsa2_decompress + %endif + +lzsa2_decompress: +_lzsa2_decompress: + pushad + + ;mov edi, [esp+32+4] ; edi = outbuf + ;mov esi, [esp+32+8] ; esi = inbuf + + xor ecx, ecx + xor ebx, ebx ; ebx = 0100H + inc bh + xor ebp, ebp + +.decode_token: + mul ecx + lodsb ; read token byte: XYZ|LL|MMMM + mov dl, al ; keep token in dl + + and al, 018H ; isolate literals length in token (LL) + shr al, 3 ; shift literals length into place + + cmp al, 03H ; LITERALS_RUN_LEN_V2? + jne .got_literals ; no, we have the full literals count from the token, go copy + + call .get_nibble ; get extra literals length nibble + add al, cl ; add len from token to nibble + cmp al, 012H ; LITERALS_RUN_LEN_V2 + 15 ? + jne .got_literals ; if not, we have the full literals count, go copy + + lodsb ; grab extra length byte + add al,012H ; overflow? + jnc .got_literals ; if not, we have the full literals count, go copy + + lodsw ; grab 16-bit extra length + +.got_literals: + xchg ecx, eax + rep movsb ; copy ecx literals from esi to edi + + test dl, 0C0h ; check match offset mode in token (X bit) + js .rep_match_or_large_offset + + ;;cmp dl,040H ; check if this is a 5 or 9-bit offset (Y bit) + ; discovered via the test with bit 6 set + xchg ecx, eax ; clear ah - cx is zero from the rep movsb above + jne .offset_9_bit + + ; 5 bit offset + cmp dl, 020H ; test bit 5 + call .get_nibble_x + jmp .dec_offset_top + +.offset_9_bit: ; 9 bit offset + lodsb ; get 8 bit offset from stream in A + dec ah ; set offset bits 15-8 to 1 + test dl, 020H ; test bit Z (offset bit 8) + je .get_match_length +.dec_offset_top: + dec ah ; clear bit 8 if Z bit is clear + ; or set offset bits 15-8 to 1 + jmp .get_match_length + +.rep_match_or_large_offset: + ;;cmp dl,0c0H ; check if this is a 13-bit offset or a 16-bit offset/rep match (Y bit) + jpe .rep_match_or_16_bit + + ; 13 bit offset + + cmp dl, 0A0H ; test bit 5 (knowing that bit 7 is also set) + xchg ah, al + call .get_nibble_x + sub al, 2 ; substract 512 + jmp .get_match_length_1 + +.rep_match_or_16_bit: + test dl, 020H ; test bit Z (offset bit 8) + jne .repeat_match ; rep-match + + ; 16 bit offset + lodsb ; Get 2-byte match offset + +.get_match_length_1: + xchg ah, al + lodsb ; load match offset bits 0-7 + +.get_match_length: + xchg ebp, eax ; ebp: offset +.repeat_match: + xchg eax, edx ; ax: original token + and al, 07H ; isolate match length in token (MMM) + add al, 2 ; add MIN_MATCH_SIZE_V2 + + cmp al, 09H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + call .get_nibble ; get extra literals length nibble + add al, cl ; add len from token to nibble + cmp al, 018H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + lodsb ; grab extra length byte + add al,018H ; overflow? + jnc .got_matchlen ; if not, we have the entire length + je .done_decompressing ; detect EOD code + + lodsw ; grab 16-bit length + +.got_matchlen: + xchg ecx, eax ; copy match length into ecx + xchg esi, eax + movsx ebp, bp ; sign-extend bp to 32-bits + lea esi,[ebp+edi] ; esi now points at back reference in output data + rep movsb ; copy match + xchg esi, eax ; restore esi + jmp .decode_token ; go decode another token + +.done_decompressing: + sub edi, [esp+32+4] + mov [esp+28], edi + popad + ret ; done + +.get_nibble_x: + cmc ; carry set if bit 4 was set + rcr al, 1 + call .get_nibble ; get nibble for offset bits 0-3 + or al, cl ; merge nibble + rol al, 1 + xor al, 0E1H ; set offset bits 7-5 to 1 + ret + +.get_nibble: + neg bh ; nibble ready? + jns .has_nibble + + xchg ebx, eax + lodsb ; load two nibbles + xchg ebx, eax + +.has_nibble: + mov cl, 4 ; swap 4 high and low bits of nibble + ror bl, cl + mov cl, 0FH + and cl, bl + ret diff --git a/Tools/unix/lzsa/asm/z80/unlzsa1_fast.asm b/Tools/unix/lzsa/asm/z80/unlzsa1_fast.asm new file mode 100644 index 00000000..79b5c613 --- /dev/null +++ b/Tools/unix/lzsa/asm/z80/unlzsa1_fast.asm @@ -0,0 +1,201 @@ +; +; Speed-optimized LZSA1 decompressor by spke & uniabis (109 bytes) +; +; ver.00 by spke for LZSA 0.5.4 (03-24/04/2019, 134 bytes); +; ver.01 by spke for LZSA 0.5.6 (25/04/2019, 110(-24) bytes, +0.2% speed); +; ver.02 by spke for LZSA 1.0.5 (24/07/2019, added support for backward decompression); +; ver.03 by uniabis (30/07/2019, 109(-1) bytes, +3.5% speed); +; ver.04 by spke (31/07/2019, small re-organization of macros); +; ver.05 by uniabis (22/08/2019, 107(-2) bytes, same speed); +; ver.06 by spke for LZSA 1.0.7 (27/08/2019, 111(+4) bytes, +2.1% speed); +; ver.07 by spke for LZSA 1.1.0 (25/09/2019, added full revision history); +; ver.08 by spke for LZSA 1.1.2 (22/10/2019, re-organized macros and added an option for unrolled copying of long matches); +; ver.09 by spke for LZSA 1.2.1 (02/01/2020, 109(-2) bytes, same speed) +; +; The data must be compressed using the command line compressor by Emmanuel Marty +; The compression is done as follows: +; +; lzsa.exe -f1 -r +; +; where option -r asks for the generation of raw (frame-less) data. +; +; The decompression is done in the standard way: +; +; ld hl,FirstByteOfCompressedData +; ld de,FirstByteOfMemoryForDecompressedData +; call DecompressLZSA1 +; +; Backward compression is also supported; you can compress files backward using: +; +; lzsa.exe -f1 -r -b +; +; and decompress the resulting files using: +; +; ld hl,LastByteOfCompressedData +; ld de,LastByteOfMemoryForDecompressedData +; call DecompressLZSA1 +; +; (do not forget to uncomment the BACKWARD_DECOMPRESS option in the decompressor). +; +; Of course, LZSA compression algorithms are (c) 2019 Emmanuel Marty, +; see https://github.com/emmanuel-marty/lzsa for more information +; +; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +; DEFINE UNROLL_LONG_MATCHES ; uncomment for faster decompression of very compressible data (+57 bytes) +; DEFINE BACKWARD_DECOMPRESS + + IFNDEF BACKWARD_DECOMPRESS + + MACRO NEXT_HL + inc hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : add hl,de + ENDM + + MACRO COPY1 + ldi + ENDM + + MACRO COPYBC + ldir + ENDM + + ELSE + + MACRO NEXT_HL + dec hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : ld a,e : sub l : ld l,a + ld a,d : sbc h : ld h,a ; 4*4+3*4 = 28t / 7 bytes + ENDM + + MACRO COPY1 + ldd + ENDM + + MACRO COPYBC + lddr + ENDM + + ENDIF + +@DecompressLZSA1: + ld b,0 : jr ReadToken + +NoLiterals: xor (hl) : NEXT_HL : jp m,LongOffset + +ShortOffset: push de : ld e,(hl) : ld d,#FF + + ; short matches have length 0+3..14+3 + add 3 : cp 15+3 : jr nc,LongerMatch + + ; placed here this saves a JP per iteration +CopyMatch: ld c,a +.UseC NEXT_HL : ex (sp),hl ; BC = len, DE = offset, HL = dest, SP ->[dest,src] + ADD_OFFSET ; BC = len, DE = dest, HL = dest-offset, SP->[src] + COPY1 : COPY1 : COPYBC ; BC = 0, DE = dest +.popSrc pop hl ; HL = src + +ReadToken: ; first a byte token "O|LLL|MMMM" is read from the stream, + ; where LLL is the number of literals and MMMM is + ; a length of the match that follows after the literals + ld a,(hl) : and #70 : jr z,NoLiterals + + cp #70 : jr z,MoreLiterals ; LLL=7 means 7+ literals... + rrca : rrca : rrca : rrca : ld c,a ; LLL<7 means 0..6 literals... + + ld a,(hl) : NEXT_HL + COPYBC + + ; the top bit of token is set if the offset contains two bytes + and #8F : jp p,ShortOffset + +LongOffset: ; read second byte of the offset + push de : ld e,(hl) : NEXT_HL : ld d,(hl) + add -128+3 : cp 15+3 : jp c,CopyMatch + + IFNDEF UNROLL_LONG_MATCHES + + ; MMMM=15 indicates a multi-byte number of literals +LongerMatch: NEXT_HL : add (hl) : jr nc,CopyMatch + + ; the codes are designed to overflow; + ; the overflow value 1 means read 1 extra byte + ; and overflow value 0 means read 2 extra bytes +.code1 ld b,a : NEXT_HL : ld c,(hl) : jr nz,CopyMatch.UseC +.code0 NEXT_HL : ld b,(hl) + + ; the two-byte match length equal to zero + ; designates the end-of-data marker + ld a,b : or c : jr nz,CopyMatch.UseC + pop de : ret + + ELSE + + ; MMMM=15 indicates a multi-byte number of literals +LongerMatch: NEXT_HL : add (hl) : jr c,VeryLongMatch + + ld c,a +.UseC NEXT_HL : ex (sp),hl + ADD_OFFSET + COPY1 : COPY1 + + ; this is an unrolled equivalent of LDIR + xor a : sub c + and 16-1 : add a + ld (.jrOffset),a : jr nz,$+2 +.jrOffset EQU $-1 +.fastLDIR DUP 16 + COPY1 + EDUP + jp pe,.fastLDIR + jp CopyMatch.popSrc + +VeryLongMatch: ; the codes are designed to overflow; + ; the overflow value 1 means read 1 extra byte + ; and overflow value 0 means read 2 extra bytes +.code1 ld b,a : NEXT_HL : ld c,(hl) : jr nz,LongerMatch.UseC +.code0 NEXT_HL : ld b,(hl) + + ; the two-byte match length equal to zero + ; designates the end-of-data marker + ld a,b : or c : jr nz,LongerMatch.UseC + pop de : ret + + ENDIF + +MoreLiterals: ; there are three possible situations here + xor (hl) : NEXT_HL : exa + ld a,7 : add (hl) : jr c,ManyLiterals + +CopyLiterals: ld c,a +.UseC NEXT_HL : COPYBC + + exa : jp p,ShortOffset : jr LongOffset + +ManyLiterals: +.code1 ld b,a : NEXT_HL : ld c,(hl) : jr nz,CopyLiterals.UseC +.code0 NEXT_HL : ld b,(hl) : jr CopyLiterals.UseC + + diff --git a/Tools/unix/lzsa/asm/z80/unlzsa1_small.asm b/Tools/unix/lzsa/asm/z80/unlzsa1_small.asm new file mode 100644 index 00000000..8592f928 --- /dev/null +++ b/Tools/unix/lzsa/asm/z80/unlzsa1_small.asm @@ -0,0 +1,135 @@ +; +; Size-optimized LZSA1 decompressor by spke & uniabis (67 bytes) +; +; ver.00 by spke for LZSA 0.5.4 (23/04/2019, 69 bytes); +; ver.01 by spke for LZSA 1.0.5 (24/07/2019, added support for backward decompression); +; ver.02 by uniabis (30/07/2019, 68(-1) bytes, +3.2% speed); +; ver.03 by spke for LZSA 1.0.7 (31/07/2019, small re-organization of macros); +; ver.04 by spke (06/08/2019, 67(-1) bytes, -1.2% speed); +; ver.05 by spke for LZSA 1.1.0 (25/09/2019, added full revision history) +; +; The data must be compressed using the command line compressor by Emmanuel Marty +; The compression is done as follows: +; +; lzsa.exe -f1 -r +; +; where option -r asks for the generation of raw (frame-less) data. +; +; The decompression is done in the standard way: +; +; ld hl,FirstByteOfCompressedData +; ld de,FirstByteOfMemoryForDecompressedData +; call DecompressLZSA1 +; +; Backward compression is also supported; you can compress files backward using: +; +; lzsa.exe -f1 -r -b +; +; and decompress the resulting files using: +; +; ld hl,LastByteOfCompressedData +; ld de,LastByteOfMemoryForDecompressedData +; call DecompressLZSA1 +; +; (do not forget to uncomment the BACKWARD_DECOMPRESS option in the decompressor). +; +; Of course, LZSA compression algorithms are (c) 2019 Emmanuel Marty, +; see https://github.com/emmanuel-marty/lzsa for more information +; +; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +; DEFINE BACKWARD_DECOMPRESS + + IFNDEF BACKWARD_DECOMPRESS + + MACRO NEXT_HL + inc hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : add hl,de + ENDM + + MACRO BLOCKCOPY + ldir + ENDM + + ELSE + + MACRO NEXT_HL + dec hl + ENDM + + MACRO ADD_OFFSET + push hl : or a : sbc hl,de : pop de ; 11+4+15+10 = 40t / 5 bytes + ENDM + + MACRO BLOCKCOPY + lddr + ENDM + + ENDIF + +@DecompressLZSA1: + ld b,0 + + ; first a byte token "O|LLL|MMMM" is read from the stream, + ; where LLL is the number of literals and MMMM is + ; a length of the match that follows after the literals +ReadToken: ld a,(hl) : NEXT_HL : push af + and #70 : jr z,NoLiterals + + rrca : rrca : rrca : rrca ; LLL<7 means 0..6 literals... + cp #07 : call z,ReadLongBA ; LLL=7 means 7+ literals... + + ld c,a : BLOCKCOPY + + ; next we read the low byte of the -offset +NoLiterals: pop af : push de : ld e,(hl) : NEXT_HL : ld d,#FF + ; the top bit of token is set if + ; the offset contains the high byte as well + or a : jp p,ShortOffset + +LongOffset: ld d,(hl) : NEXT_HL + + ; last but not least, the match length is read +ShortOffset: and #0F : add 3 ; MMMM<15 means match lengths 0+3..14+3 + cp 15+3 : call z,ReadLongBA ; MMMM=15 means lengths 14+3+ + ld c,a + + ex (sp),hl ; BC = len, DE = -offset, HL = dest, SP -> [src] + ADD_OFFSET ; BC = len, DE = dest, HL = dest+(-offset), SP -> [src] + BLOCKCOPY ; BC = 0, DE = dest + pop hl : jr ReadToken ; HL = src + + ; a standard routine to read extended codes + ; into registers B (higher byte) and A (lower byte). +ReadLongBA: add (hl) : NEXT_HL : ret nc + + ; the codes are designed to overflow; + ; the overflow value 1 means read 1 extra byte + ; and overflow value 0 means read 2 extra bytes +.code1: ld b,a : ld a,(hl) : NEXT_HL : ret nz +.code0: ld c,a : ld b,(hl) : NEXT_HL + + ; the two-byte match length equal to zero + ; designates the end-of-data marker + or b : ld a,c : ret nz + pop de : pop de : ret + diff --git a/Tools/unix/lzsa/asm/z80/unlzsa2_fast.asm b/Tools/unix/lzsa/asm/z80/unlzsa2_fast.asm new file mode 100644 index 00000000..57026da7 --- /dev/null +++ b/Tools/unix/lzsa/asm/z80/unlzsa2_fast.asm @@ -0,0 +1,281 @@ +; +; Speed-optimized LZSA2 decompressor by spke & uniabis (216 bytes) +; +; ver.00 by spke for LZSA 1.0.0 (02-07/06/2019, 218 bytes); +; ver.01 by spke for LZSA 1.0.5 (24/07/2019, added support for backward decompression); +; ver.02 by spke for LZSA 1.0.6 (27/07/2019, fixed a bug in the backward decompressor); +; ver.03 by uniabis (30/07/2019, 213(-5) bytes, +3.8% speed and support for Hitachi HD64180); +; ver.04 by spke for LZSA 1.0.7 (01/08/2019, 214(+1) bytes, +0.2% speed and small re-organization of macros); +; ver.05 by spke (27/08/2019, 216(+2) bytes, +1.1% speed); +; ver.06 by spke for LZSA 1.1.0 (26/09/2019, added full revision history); +; ver.07 by spke for LZSA 1.1.1 (10/10/2019, +0.2% speed and an option for unrolled copying of long matches) +; +; The data must be compressed using the command line compressor by Emmanuel Marty +; The compression is done as follows: +; +; lzsa.exe -f2 -r +; +; where option -r asks for the generation of raw (frame-less) data. +; +; The decompression is done in the standard way: +; +; ld hl,FirstByteOfCompressedData +; ld de,FirstByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; Backward compression is also supported; you can compress files backward using: +; +; lzsa.exe -f2 -r -b +; +; and decompress the resulting files using: +; +; ld hl,LastByteOfCompressedData +; ld de,LastByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; (do not forget to uncomment the BACKWARD_DECOMPRESS option in the decompressor). +; +; Of course, LZSA2 compression algorithms are (c) 2019 Emmanuel Marty, +; see https://github.com/emmanuel-marty/lzsa for more information +; +; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +; DEFINE UNROLL_LONG_MATCHES ; uncomment for faster decompression of very compressible data (+38 bytes) +; DEFINE BACKWARD_DECOMPRESS ; uncomment for data compressed with option -b +; DEFINE HD64180 ; uncomment for systems using Hitachi HD64180 + + IFNDEF BACKWARD_DECOMPRESS + + MACRO NEXT_HL + inc hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : add hl,de + ENDM + + MACRO COPY1 + ldi + ENDM + + MACRO COPYBC + ldir + ENDM + + ELSE + + MACRO NEXT_HL + dec hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : ld a,e : sub l : ld l,a + ld a,d : sbc h : ld h,a ; 4*4+3*4 = 28t / 7 bytes + ENDM + + MACRO COPY1 + ldd + ENDM + + MACRO COPYBC + lddr + ENDM + + ENDIF + + IFNDEF HD64180 + + MACRO LD_IX_DE + ld ixl,e : ld ixh,d + ENDM + + MACRO LD_DE_IX + ld e,ixl : ld d,ixh + ENDM + + ELSE + + MACRO LD_IX_DE + push de : pop ix + ENDM + + MACRO LD_DE_IX + push ix : pop de + ENDM + + ENDIF + +@DecompressLZSA2: + ; A' stores next nibble as %1111.... or assumed to contain trash + ; B is assumed to be 0 + ld b,0 : scf : exa : jr ReadToken + + + + +ManyLiterals: ld a,18 : add (hl) : NEXT_HL : jr nc,CopyLiterals + ld c,(hl) : NEXT_HL + ld a,b : ld b,(hl) + jr ReadToken.NEXTHLuseBC + + + + +MoreLiterals: ld b,(hl) : NEXT_HL + scf : exa : jr nc,.noUpdate + + ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca + +.noUpdate ;sub #F0-3 : cp 15+3 : jr z,ManyLiterals + inc a : jr z,ManyLiterals : sub #F0-3+1 + +CopyLiterals: ld c,a : ld a,b : ld b,0 + COPYBC + push de : or a : jp p,CASE0xx ;: jr CASE1xx + + cp %11000000 : jr c,CASE10x + +CASE11x cp %11100000 : jr c,CASE110 + + ; "111": repeated offset +CASE111: LD_DE_IX : jr MatchLen + + + + +Literals0011: jr nz,MoreLiterals + + ; if "LL" of the byte token is equal to 0, + ; there are no literals to copy +NoLiterals: or (hl) : NEXT_HL + push de : jp m,CASE1xx + + ; short (5 or 9 bit long) offsets +CASE0xx ld d,#FF : cp %01000000 : jr c,CASE00x + + ; "01x": the case of the 9-bit offset +CASE01x: cp %01100000 : rl d + +ReadOffsetE ld e,(hl) : NEXT_HL + +SaveOffset: LD_IX_DE + +MatchLen: inc a : and %00000111 : jr z,LongerMatch : inc a + +CopyMatch: ld c,a +.useC ex (sp),hl ; BC = len, DE = offset, HL = dest, SP ->[dest,src] + ADD_OFFSET ; BC = len, DE = dest, HL = dest-offset, SP->[src] + COPY1 + COPYBC +.popSrc pop hl + + ; compressed data stream contains records + ; each record begins with the byte token "XYZ|LL|MMM" +ReadToken: ld a,(hl) : and %00011000 : jp pe,Literals0011 ; process the cases 00 and 11 separately + + rrca : rrca : rrca + + ld c,a : ld a,(hl) ; token is re-read for further processing +.NEXTHLuseBC NEXT_HL + COPYBC + + ; the token and literals are followed by the offset + push de : or a : jp p,CASE0xx + +CASE1xx cp %11000000 : jr nc,CASE11x + + ; "10x": the case of the 13-bit offset +CASE10x: ld c,a : exa : jr nc,.noUpdate + + ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca + +.noUpdate ld d,a : ld a,c + cp %10100000 : dec d : rl d : jr ReadOffsetE + + + + ; "110": 16-bit offset +CASE110: ld d,(hl) : NEXT_HL : jr ReadOffsetE + + + + + ; "00x": the case of the 5-bit offset +CASE00x: ld c,a : exa : jr nc,.noUpdate + + ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca + +.noUpdate ld e,a : ld a,c + cp %00100000 : rl e : jp SaveOffset + + + + + +LongerMatch: scf : exa : jr nc,.noUpdate + + ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca + +.noUpdate sub #F0-9 : cp 15+9 : jr c,CopyMatch + + IFNDEF UNROLL_LONG_MATCHES + +LongMatch: add (hl) : NEXT_HL : jr nc,CopyMatch + ld c,(hl) : NEXT_HL + ld b,(hl) : NEXT_HL : jr nz,CopyMatch.useC + pop de : ret + + ELSE + +LongMatch: add (hl) : NEXT_HL : jr c,VeryLongMatch + + ld c,a +.useC ex (sp),hl + ADD_OFFSET + COPY1 + + ; this is an unrolled equivalent of LDIR + xor a : sub c + and 8-1 : add a + ld (.jrOffset),a : jr nz,$+2 +.jrOffset EQU $-1 +.fastLDIR DUP 8 + COPY1 + EDUP + jp pe,.fastLDIR + jp CopyMatch.popSrc + +VeryLongMatch: ld c,(hl) : NEXT_HL + ld b,(hl) : NEXT_HL : jr nz,LongMatch.useC + pop de : ret + + ENDIF + + + + + diff --git a/Tools/unix/lzsa/asm/z80/unlzsa2_small.asm b/Tools/unix/lzsa/asm/z80/unlzsa2_small.asm new file mode 100644 index 00000000..0544c9a7 --- /dev/null +++ b/Tools/unix/lzsa/asm/z80/unlzsa2_small.asm @@ -0,0 +1,187 @@ +; +; Size-optimized LZSA2 decompressor by spke & uniabis (139 bytes) +; +; ver.00 by spke for LZSA 1.0.0 (02-09/06/2019, 145 bytes); +; ver.01 by spke for LZSA 1.0.5 (24/07/2019, added support for backward decompression); +; ver.02 by uniabis (30/07/2019, 144(-1) bytes, +3.3% speed and support for Hitachi HD64180); +; ver.03 by spke for LZSA 1.0.7 (01/08/2019, 140(-4) bytes, -1.4% speed and small re-organization of macros); +; ver.04 by spke for LZSA 1.1.0 (26/09/2019, removed usage of IY, added full revision history) +; ver.05 by spke for LZSA 1.1.1 (11/10/2019, 139(-1) bytes, +0.1% speed) +; +; The data must be compressed using the command line compressor by Emmanuel Marty +; The compression is done as follows: +; +; lzsa.exe -f2 -r +; +; where option -r asks for the generation of raw (frame-less) data. +; +; The decompression is done in the standard way: +; +; ld hl,FirstByteOfCompressedData +; ld de,FirstByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; Backward compression is also supported; you can compress files backward using: +; +; lzsa.exe -f2 -r -b +; +; and decompress the resulting files using: +; +; ld hl,LastByteOfCompressedData +; ld de,LastByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; (do not forget to uncomment the BACKWARD_DECOMPRESS option in the decompressor). +; +; Of course, LZSA2 compression algorithms are (c) 2019 Emmanuel Marty, +; see https://github.com/emmanuel-marty/lzsa for more information +; +; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; + +; DEFINE BACKWARD_DECOMPRESS ; uncomment for data compressed with option -b +; DEFINE HD64180 ; uncomment for systems using Hitachi HD64180 + + IFNDEF BACKWARD_DECOMPRESS + + MACRO NEXT_HL + inc hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : add hl,de + ENDM + + MACRO BLOCKCOPY + ldir + ENDM + + ELSE + + MACRO NEXT_HL + dec hl + ENDM + + MACRO ADD_OFFSET + push hl : or a : sbc hl,de : pop de ; 11+4+15+10 = 40t / 5 bytes + ENDM + + MACRO BLOCKCOPY + lddr + ENDM + + ENDIF + + IFNDEF HD64180 + + MACRO LD_IX_DE + ld ixl,e : ld ixh,d + ENDM + + MACRO LD_DE_IX + ld e,ixl : ld d,ixh + ENDM + + ELSE + + MACRO LD_IX_DE + push de : pop ix + ENDM + + MACRO LD_DE_IX + push ix : pop de + ENDM + + ENDIF + +@DecompressLZSA2: + xor a : ld b,a : exa : jr ReadToken + +CASE00x: call ReadNibble + ld e,a : ld a,c + cp %00100000 : rl e : jr SaveOffset + +CASE0xx ld d,#FF : cp %01000000 : jr c,CASE00x + +CASE01x: cp %01100000 : rl d + +OffsetReadE: ld e,(hl) : NEXT_HL + +SaveOffset: LD_IX_DE + +MatchLen: and %00000111 : add 2 : cp 9 : call z,ExtendedCode + +CopyMatch: ld c,a + ex (sp),hl ; BC = len, DE = -offset, HL = dest, SP -> [src] + ADD_OFFSET ; BC = len, DE = dest, HL = dest+(-offset), SP -> [src] + BLOCKCOPY ; BC = 0, DE = dest + pop hl ; HL = src + +ReadToken: ld a,(hl) : NEXT_HL : push af + and %00011000 : jr z,NoLiterals + + rrca : rrca : rrca + call pe,ExtendedCode + + ld c,a + BLOCKCOPY + +NoLiterals: pop af : push de + or a : jp p,CASE0xx + +CASE1xx cp %11000000 : jr nc,CASE11x + +CASE10x: call ReadNibble + ld d,a : ld a,c + cp %10100000 ;: rl d + dec d : rl d : DB #CA ; jr OffsetReadE ; #CA is JP Z,.. to skip all commands in CASE110 before jr OffsetReadE + +CASE110: ld d,(hl) : NEXT_HL : jr OffsetReadE + +CASE11x cp %11100000 : jr c,CASE110 + +CASE111: LD_DE_IX : jr MatchLen + +ExtendedCode: call ReadNibble : inc a : jr z,ExtraByte + sub #F0+1 : add c : ret +ExtraByte ld a,15 : add c : add (hl) : NEXT_HL : ret nc + ld a,(hl) : NEXT_HL + ld b,(hl) : NEXT_HL : ret nz + pop de : pop de ; RET is not needed, because RET from ReadNibble is sufficient + +ReadNibble: ld c,a : xor a : exa : ret m +UpdateNibble ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca : ret + + + + + + + + + + + + + + + + diff --git a/Tools/unix/lzsa/pareto_graph.png b/Tools/unix/lzsa/pareto_graph.png new file mode 100644 index 00000000..5ab3a914 Binary files /dev/null and b/Tools/unix/lzsa/pareto_graph.png differ diff --git a/Tools/unix/lzsa/src/dictionary.c b/Tools/unix/lzsa/src/dictionary.c new file mode 100644 index 00000000..fbc4f526 --- /dev/null +++ b/Tools/unix/lzsa/src/dictionary.c @@ -0,0 +1,101 @@ +/* + * dictionary.c - dictionary implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "format.h" +#include "lib.h" + +/** + * Load dictionary contents + * + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param ppDictionaryData pointer to returned dictionary contents, or NULL for none + * @param pDictionaryDataSize pointer to returned size of dictionary contents, or 0 + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +int lzsa_dictionary_load(const char *pszDictionaryFilename, void **ppDictionaryData, int *pDictionaryDataSize) { + unsigned char *pDictionaryData = NULL; + int nDictionaryDataSize = 0; + + if (pszDictionaryFilename) { + pDictionaryData = (unsigned char *)malloc(BLOCK_SIZE); + if (!pDictionaryData) { + return LZSA_ERROR_MEMORY; + } + + FILE *pDictionaryFile = fopen(pszDictionaryFilename, "rb"); + if (!pDictionaryFile) { + free(pDictionaryData); + pDictionaryData = NULL; + return LZSA_ERROR_DICTIONARY; + } + + fseek(pDictionaryFile, 0, SEEK_END); +#ifdef _WIN32 + __int64 nDictionaryFileSize = _ftelli64(pDictionaryFile); +#else + off_t nDictionaryFileSize = ftello(pDictionaryFile); +#endif + if (nDictionaryFileSize > BLOCK_SIZE) { + /* Use the last BLOCK_SIZE bytes of the dictionary */ + fseek(pDictionaryFile, -BLOCK_SIZE, SEEK_END); + } + else { + fseek(pDictionaryFile, 0, SEEK_SET); + } + + nDictionaryDataSize = (int)fread(pDictionaryData, 1, BLOCK_SIZE, pDictionaryFile); + if (nDictionaryDataSize < 0) + nDictionaryDataSize = 0; + + fclose(pDictionaryFile); + pDictionaryFile = NULL; + } + + *ppDictionaryData = pDictionaryData; + *pDictionaryDataSize = nDictionaryDataSize; + return LZSA_OK; +} + +/** + * Free dictionary contents + * + * @param ppDictionaryData pointer to pointer to dictionary contents + */ +void lzsa_dictionary_free(void **ppDictionaryData) { + if (*ppDictionaryData) { + free(*ppDictionaryData); + *ppDictionaryData = NULL; + } +} diff --git a/Tools/unix/lzsa/src/dictionary.h b/Tools/unix/lzsa/src/dictionary.h new file mode 100644 index 00000000..a3635640 --- /dev/null +++ b/Tools/unix/lzsa/src/dictionary.h @@ -0,0 +1,64 @@ +/* + * dictionary.h - dictionary definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _DICTIONARY_H +#define _DICTIONARY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Load dictionary contents + * + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param ppDictionaryData pointer to returned dictionary contents, or NULL for none + * @param pDictionaryDataSize pointer to returned size of dictionary contents, or 0 + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +int lzsa_dictionary_load(const char *pszDictionaryFilename, void **ppDictionaryData, int *pDictionaryDataSize); + +/** + * Free dictionary contents + * + * @param ppDictionaryData pointer to pointer to dictionary contents + */ +void lzsa_dictionary_free(void **ppDictionaryData); + +#ifdef __cplusplus +} +#endif + +#endif /* _DICTIONARY_H */ diff --git a/Tools/unix/lzsa/src/expand_block_v1.c b/Tools/unix/lzsa/src/expand_block_v1.c new file mode 100644 index 00000000..c248fb33 --- /dev/null +++ b/Tools/unix/lzsa/src/expand_block_v1.c @@ -0,0 +1,224 @@ +/* + * expand_block_v1.c - LZSA1 block decompressor implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "format.h" +#include "expand_block_v1.h" + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#else /* _MSC_VER */ +#define FORCE_INLINE __attribute__((always_inline)) +#endif /* _MSC_VER */ + +static inline FORCE_INLINE int lzsa_build_literals_len_v1(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, unsigned int *nLiterals) { + unsigned int nByte; + const unsigned char *pInBlock = *ppInBlock; + + if (pInBlock < pInBlockEnd) { + nByte = *pInBlock++; + (*nLiterals) += nByte; + + if (nByte == 250) { + if (pInBlock < pInBlockEnd) { + (*nLiterals) = 256 + ((unsigned int)*pInBlock++); + } + else { + return -1; + } + } + else if (nByte == 249) { + if ((pInBlock + 1) < pInBlockEnd) { + (*nLiterals) = ((unsigned int)*pInBlock++); + (*nLiterals) |= (((unsigned int)*pInBlock++) << 8); + } + else { + return -1; + } + } + + *ppInBlock = pInBlock; + return 0; + } + else { + return -1; + } +} + +static inline FORCE_INLINE int lzsa_build_match_len_v1(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, unsigned int *nMatchLen) { + unsigned int nByte; + const unsigned char *pInBlock = *ppInBlock; + + if (pInBlock < pInBlockEnd) { + nByte = *pInBlock++; + (*nMatchLen) += nByte; + + if (nByte == 239) { + if (pInBlock < pInBlockEnd) { + (*nMatchLen) = 256 + ((unsigned int)*pInBlock++); + } + else { + return -1; + } + } + else if (nByte == 238) { + if ((pInBlock + 1) < pInBlockEnd) { + (*nMatchLen) = ((unsigned int)*pInBlock++); + (*nMatchLen) |= (((unsigned int)*pInBlock++) << 8); + } + else { + return -1; + } + } + + *ppInBlock = pInBlock; + return 0; + } + else { + return -1; + } +} + +/** + * Decompress one LZSA1 data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block_v1(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize) { + const unsigned char *pInBlockEnd = pInBlock + nBlockSize; + unsigned char *pCurOutData = pOutData + nOutDataOffset; + const unsigned char *pOutDataEnd = pCurOutData + nBlockMaxSize; + const unsigned char *pOutDataFastEnd = pOutDataEnd - 18; + + while (pInBlock < pInBlockEnd) { + const unsigned char token = *pInBlock++; + unsigned int nLiterals = (unsigned int)((token & 0x70) >> 4); + + if (nLiterals != LITERALS_RUN_LEN_V1 && (pInBlock + 8) <= pInBlockEnd && pCurOutData < pOutDataFastEnd) { + memcpy(pCurOutData, pInBlock, 8); + pInBlock += nLiterals; + pCurOutData += nLiterals; + } + else { + if (nLiterals == LITERALS_RUN_LEN_V1) { + if (lzsa_build_literals_len_v1(&pInBlock, pInBlockEnd, &nLiterals)) + return -1; + } + + if (nLiterals != 0) { + if ((pInBlock + nLiterals) <= pInBlockEnd && + (pCurOutData + nLiterals) <= pOutDataEnd) { + memcpy(pCurOutData, pInBlock, nLiterals); + pInBlock += nLiterals; + pCurOutData += nLiterals; + } + else { + return -1; + } + } + } + + if ((pInBlock + 1) < pInBlockEnd) { /* The last token in the block does not include match information */ + unsigned int nMatchOffset; + + nMatchOffset = ((unsigned int)(*pInBlock++)) ^ 0xff; + if (token & 0x80) { + nMatchOffset |= (((unsigned int)(*pInBlock++)) << 8) ^ 0xff00; + } + nMatchOffset++; + + const unsigned char *pSrc = pCurOutData - nMatchOffset; + if (pSrc >= pOutData) { + unsigned int nMatchLen = (unsigned int)(token & 0x0f); + if (nMatchLen != MATCH_RUN_LEN_V1 && nMatchOffset >= 8 && pCurOutData < pOutDataFastEnd && (pSrc + 18) <= pOutDataEnd) { + memcpy(pCurOutData, pSrc, 8); + memcpy(pCurOutData + 8, pSrc + 8, 8); + memcpy(pCurOutData + 16, pSrc + 16, 2); + pCurOutData += (MIN_MATCH_SIZE_V1 + nMatchLen); + } + else { + nMatchLen += MIN_MATCH_SIZE_V1; + if (nMatchLen == (MATCH_RUN_LEN_V1 + MIN_MATCH_SIZE_V1)) { + if (lzsa_build_match_len_v1(&pInBlock, pInBlockEnd, &nMatchLen)) + return -1; + if (nMatchLen == 0) + break; + } + + if ((pSrc + nMatchLen) <= pOutDataEnd) { + if ((pCurOutData + nMatchLen) <= pOutDataEnd) { + /* Do a deterministic, left to right byte copy instead of memcpy() so as to handle overlaps */ + + if (nMatchOffset >= 16 && (pCurOutData + nMatchLen) < (pOutDataFastEnd - 15)) { + const unsigned char *pCopySrc = pSrc; + unsigned char *pCopyDst = pCurOutData; + const unsigned char *pCopyEndDst = pCurOutData + nMatchLen; + + do { + memcpy(pCopyDst, pCopySrc, 16); + pCopySrc += 16; + pCopyDst += 16; + } while (pCopyDst < pCopyEndDst); + + pCurOutData += nMatchLen; + } + else { + while (nMatchLen) { + *pCurOutData++ = *pSrc++; + nMatchLen--; + } + } + } + else { + return -1; + } + } + else { + return -1; + } + } + } + else { + return -1; + } + } + } + + return (int)(pCurOutData - (pOutData + nOutDataOffset)); +} diff --git a/Tools/unix/lzsa/src/expand_block_v1.h b/Tools/unix/lzsa/src/expand_block_v1.h new file mode 100644 index 00000000..a1d5651a --- /dev/null +++ b/Tools/unix/lzsa/src/expand_block_v1.h @@ -0,0 +1,49 @@ +/* + * expand_block_v1.h - LZSA1 block decompressor definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_BLOCK_V1_H +#define _EXPAND_BLOCK_V1_H + +/** + * Decompress one LZSA1 data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block_v1(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize); + +#endif /* _EXPAND_BLOCK_V1_H */ diff --git a/Tools/unix/lzsa/src/expand_block_v2.c b/Tools/unix/lzsa/src/expand_block_v2.c new file mode 100644 index 00000000..e024b4c9 --- /dev/null +++ b/Tools/unix/lzsa/src/expand_block_v2.c @@ -0,0 +1,253 @@ +/* + * expand_block_v2.c - LZSA2 block decompressor implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "format.h" +#include "expand_block_v2.h" + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#else /* _MSC_VER */ +#define FORCE_INLINE __attribute__((always_inline)) +#endif /* _MSC_VER */ + +static inline FORCE_INLINE unsigned int lzsa_get_nibble_v2(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, int *nCurNibbles, unsigned char *nibbles, unsigned int *nValue) { + if ((*nCurNibbles ^= 1) != 0) { + const unsigned char *pInBlock = *ppInBlock; + if (pInBlock < pInBlockEnd) { + (*nibbles) = *pInBlock++; + *ppInBlock = pInBlock; + (*nValue) = ((unsigned int)((*nibbles) & 0xf0)) >> 4; + return 0; + } + else { + return -1; + } + } + + (*nValue) = (unsigned int)((*nibbles) & 0x0f); + return 0; +} + +static inline FORCE_INLINE int lzsa_build_len_v2(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, int *nCurNibbles, unsigned char *nibbles, unsigned int *nLength) { + unsigned int nValue; + + if (!lzsa_get_nibble_v2(ppInBlock, pInBlockEnd, nCurNibbles, nibbles, &nValue)) { + (*nLength) += nValue; + + if (nValue == 15) { + const unsigned char *pInBlock = *ppInBlock; + + if (pInBlock < pInBlockEnd) { + (*nLength) += ((unsigned int)*pInBlock++); + + if ((*nLength) == 257) { + if ((pInBlock + 1) < pInBlockEnd) { + (*nLength) = ((unsigned int)*pInBlock++); + (*nLength) |= (((unsigned int)*pInBlock++) << 8); + } + else { + return -1; + } + } + else if ((*nLength) == 256) { + (*nLength) = 0; + } + } + else { + return -1; + } + + *ppInBlock = pInBlock; + } + + return 0; + } + else { + return -1; + } +} + +/** + * Decompress one LZSA2 data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block_v2(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize) { + const unsigned char *pInBlockEnd = pInBlock + nBlockSize; + unsigned char *pCurOutData = pOutData + nOutDataOffset; + const unsigned char *pOutDataEnd = pCurOutData + nBlockMaxSize; + const unsigned char *pOutDataFastEnd = pOutDataEnd - 20; + int nCurNibbles = 0; + unsigned char nibbles; + int nMatchOffset = 0; + + while (pInBlock < pInBlockEnd) { + const unsigned char token = *pInBlock++; + unsigned int nLiterals = (unsigned int)((token & 0x18) >> 3); + + if (nLiterals != LITERALS_RUN_LEN_V2 && (pInBlock + 4) <= pInBlockEnd && pCurOutData < pOutDataFastEnd) { + memcpy(pCurOutData, pInBlock, 4); + pInBlock += nLiterals; + pCurOutData += nLiterals; + } + else { + if (nLiterals == LITERALS_RUN_LEN_V2) { + if (lzsa_build_len_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles, &nLiterals)) + return -1; + } + + if (nLiterals != 0) { + if ((pInBlock + nLiterals) <= pInBlockEnd && + (pCurOutData + nLiterals) <= pOutDataEnd) { + memcpy(pCurOutData, pInBlock, nLiterals); + pInBlock += nLiterals; + pCurOutData += nLiterals; + } + else { + return -1; + } + } + } + + if (pInBlock < pInBlockEnd) { /* The last token in the block does not include match information */ + unsigned char nOffsetMode = token & 0xc0; + unsigned int nValue; + + switch (nOffsetMode) { + case 0x00: + /* 5 bit offset */ + if (lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles, &nValue)) + return -1; + nMatchOffset = nValue << 1; + nMatchOffset |= ((token & 0x20) >> 5); + nMatchOffset ^= 0x1e; + nMatchOffset++; + break; + + case 0x40: + /* 9 bit offset */ + nMatchOffset = (unsigned int)(*pInBlock++); + nMatchOffset |= (((unsigned int)(token & 0x20)) << 3); + nMatchOffset ^= 0x0ff; + nMatchOffset++; + break; + + case 0x80: + /* 13 bit offset */ + if (lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles, &nValue)) + return -1; + nMatchOffset = (unsigned int)(*pInBlock++); + nMatchOffset |= (nValue << 9); + nMatchOffset |= (((unsigned int)(token & 0x20)) << 3); + nMatchOffset ^= 0x1eff; + nMatchOffset += (512 + 1); + break; + + default: + /* Check if this is a 16 bit offset or a rep-match */ + if ((token & 0x20) == 0) { + /* 16 bit offset */ + nMatchOffset = (((unsigned int)(*pInBlock++)) << 8); + if (pInBlock >= pInBlockEnd) return -1; + nMatchOffset |= (unsigned int)(*pInBlock++); + nMatchOffset ^= 0xffff; + nMatchOffset++; + } + break; + } + + const unsigned char *pSrc = pCurOutData - nMatchOffset; + if (pSrc >= pOutData) { + unsigned int nMatchLen = (unsigned int)(token & 0x07); + if (nMatchLen != MATCH_RUN_LEN_V2 && nMatchOffset >= 8 && pCurOutData < pOutDataFastEnd && (pSrc + 10) <= pOutDataEnd) { + memcpy(pCurOutData, pSrc, 8); + memcpy(pCurOutData + 8, pSrc + 8, 2); + pCurOutData += (MIN_MATCH_SIZE_V2 + nMatchLen); + } + else { + nMatchLen += MIN_MATCH_SIZE_V2; + if (nMatchLen == (MATCH_RUN_LEN_V2 + MIN_MATCH_SIZE_V2)) { + if (lzsa_build_len_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles, &nMatchLen)) + return -1; + if (nMatchLen == 0) + break; + } + + if ((pSrc + nMatchLen) <= pOutDataEnd) { + if ((pCurOutData + nMatchLen) <= pOutDataEnd) { + /* Do a deterministic, left to right byte copy instead of memcpy() so as to handle overlaps */ + + if (nMatchOffset >= 16 && (pCurOutData + nMatchLen) < (pOutDataFastEnd - 15)) { + const unsigned char *pCopySrc = pSrc; + unsigned char *pCopyDst = pCurOutData; + const unsigned char *pCopyEndDst = pCurOutData + nMatchLen; + + do { + memcpy(pCopyDst, pCopySrc, 16); + pCopySrc += 16; + pCopyDst += 16; + } while (pCopyDst < pCopyEndDst); + + pCurOutData += nMatchLen; + } + else { + while (nMatchLen) { + *pCurOutData++ = *pSrc++; + nMatchLen--; + } + } + } + else { + return -1; + } + } + else { + return -1; + } + } + } + else { + return -1; + } + } + } + + return (int)(pCurOutData - (pOutData + nOutDataOffset)); +} diff --git a/Tools/unix/lzsa/src/expand_block_v2.h b/Tools/unix/lzsa/src/expand_block_v2.h new file mode 100644 index 00000000..f612f7a1 --- /dev/null +++ b/Tools/unix/lzsa/src/expand_block_v2.h @@ -0,0 +1,49 @@ +/* + * expand_block_v2.h - LZSA2 block decompressor definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_BLOCK_V2_H +#define _EXPAND_BLOCK_V2_H + +/** + * Decompress one LZSA2 data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block_v2(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize); + +#endif /* _EXPAND_BLOCK_V2_H */ diff --git a/Tools/unix/lzsa/src/expand_context.c b/Tools/unix/lzsa/src/expand_context.c new file mode 100644 index 00000000..b02620d1 --- /dev/null +++ b/Tools/unix/lzsa/src/expand_context.c @@ -0,0 +1,76 @@ +/* + * expand_context.h - decompressor context definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "expand_context.h" +#include "expand_block_v1.h" +#include "expand_block_v2.h" +#include "lib.h" + +/** + * Decompress one data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * @param nFormatVersion version of format to use (1-2) + * @param nFlags compression flags (LZSA_FLAG_xxx) + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block(unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize, const int nFormatVersion, const int nFlags) { + int nDecompressedSize; + + if (nFlags & LZSA_FLAG_RAW_BACKWARD) { + lzsa_reverse_buffer(pInBlock, nBlockSize); + } + + if (nFormatVersion == 1) + nDecompressedSize = lzsa_decompressor_expand_block_v1(pInBlock, nBlockSize, pOutData, nOutDataOffset, nBlockMaxSize); + else if (nFormatVersion == 2) + nDecompressedSize = lzsa_decompressor_expand_block_v2(pInBlock, nBlockSize, pOutData, nOutDataOffset, nBlockMaxSize); + else + nDecompressedSize = -1; + + if (nDecompressedSize != -1 && (nFlags & LZSA_FLAG_RAW_BACKWARD)) { + lzsa_reverse_buffer(pOutData + nOutDataOffset, nDecompressedSize); + } + + if (nFlags & LZSA_FLAG_RAW_BACKWARD) { + lzsa_reverse_buffer(pInBlock, nBlockSize); + } + + return nDecompressedSize; +} diff --git a/Tools/unix/lzsa/src/expand_context.h b/Tools/unix/lzsa/src/expand_context.h new file mode 100644 index 00000000..698e0392 --- /dev/null +++ b/Tools/unix/lzsa/src/expand_context.h @@ -0,0 +1,61 @@ +/* + * expand_context.h - decompressor context definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_CONTEXT_H +#define _EXPAND_CONTEXT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Decompress one data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * @param nFormatVersion version of format to use (1-2) + * @param nFlags compression flags (LZSA_FLAG_xxx) + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block(unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize, const int nFormatVersion, const int nFlags); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXPAND_CONTEXT_H */ diff --git a/Tools/unix/lzsa/src/expand_inmem.c b/Tools/unix/lzsa/src/expand_inmem.c new file mode 100644 index 00000000..050bdbca --- /dev/null +++ b/Tools/unix/lzsa/src/expand_inmem.c @@ -0,0 +1,163 @@ +/* + * expand_inmem.c - in-memory decompression implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "expand_inmem.h" +#include "lib.h" +#include "frame.h" + +#define BLOCK_SIZE 65536 + +/** + * Get maximum decompressed size of compressed data + * + * @param pFileData compressed data + * @param nFileSize compressed size in bytes + * + * @return maximum decompressed size + */ +size_t lzsa_get_max_decompressed_size_inmem(const unsigned char *pFileData, size_t nFileSize) { + const unsigned char *pCurFileData = pFileData; + const unsigned char *pEndFileData = pCurFileData + nFileSize; + int nFormatVersion = 0; + size_t nMaxDecompressedSize = 0; + const int nHeaderSize = lzsa_get_header_size(); + + /* Check header */ + if ((pCurFileData + nHeaderSize) > pEndFileData || + lzsa_decode_header(pCurFileData, nHeaderSize, &nFormatVersion) != 0) + return -1; + + pCurFileData += nHeaderSize; + + while (pCurFileData < pEndFileData) { + unsigned int nBlockDataSize = 0; + int nIsUncompressed = 0; + const int nFrameSize = lzsa_get_frame_size(); + + /* Decode frame header */ + if ((pCurFileData + nFrameSize) > pEndFileData || + lzsa_decode_frame(pCurFileData, nFrameSize, &nBlockDataSize, &nIsUncompressed) != 0) + return -1; + pCurFileData += nFrameSize; + + if (!nBlockDataSize) + break; + + /* Add one potentially full block to the decompressed size */ + nMaxDecompressedSize += BLOCK_SIZE; + + if ((pCurFileData + nBlockDataSize) > pEndFileData) + return -1; + + pCurFileData += nBlockDataSize; + } + + return nMaxDecompressedSize; +} + +/** + * Decompress data in memory + * + * @param pFileData compressed data + * @param pOutBuffer buffer for decompressed data + * @param nFileSize compressed size in bytes + * @param nMaxOutBufferSize maximum capacity of decompression buffer + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param pFormatVersion pointer to format version, updated if this function is successful + * + * @return actual decompressed size, or -1 for error + */ +size_t lzsa_decompress_inmem(unsigned char *pFileData, unsigned char *pOutBuffer, size_t nFileSize, size_t nMaxOutBufferSize, const unsigned int nFlags, int *pFormatVersion) { + unsigned char *pCurFileData = pFileData; + const unsigned char *pEndFileData = pCurFileData + nFileSize; + unsigned char *pCurOutBuffer = pOutBuffer; + const unsigned char *pEndOutBuffer = pCurOutBuffer + nMaxOutBufferSize; + int nPreviousBlockSize; + const int nHeaderSize = lzsa_get_header_size(); + + if (nFlags & LZSA_FLAG_RAW_BLOCK) { + return (size_t)lzsa_decompressor_expand_block(pFileData, (int)nFileSize, pOutBuffer, 0, (int)nMaxOutBufferSize, *pFormatVersion, nFlags); + } + + /* Check header */ + if ((pCurFileData + nHeaderSize) > pEndFileData || + lzsa_decode_header(pCurFileData, nHeaderSize, pFormatVersion) != 0) + return -1; + + pCurFileData += nHeaderSize; + nPreviousBlockSize = 0; + + while (pCurFileData < pEndFileData) { + unsigned int nBlockDataSize = 0; + int nIsUncompressed = 0; + const int nFrameSize = lzsa_get_frame_size(); + + /* Decode frame header */ + if ((pCurFileData + nFrameSize) > pEndFileData || + lzsa_decode_frame(pCurFileData, nFrameSize, &nBlockDataSize, &nIsUncompressed) != 0) + return -1; + pCurFileData += nFrameSize; + + if (!nBlockDataSize) + break; + + if (!nIsUncompressed) { + int nDecompressedSize; + + /* Decompress block */ + if ((pCurFileData + nBlockDataSize) > pEndFileData) + return -1; + + nDecompressedSize = lzsa_decompressor_expand_block(pCurFileData, nBlockDataSize, pCurOutBuffer - nPreviousBlockSize, nPreviousBlockSize, (int)(pEndOutBuffer - pCurOutBuffer + nPreviousBlockSize), *pFormatVersion, nFlags); + if (nDecompressedSize < 0) + return -1; + + pCurOutBuffer += nDecompressedSize; + nPreviousBlockSize = nDecompressedSize; + } + else { + /* Copy uncompressed block */ + if ((pCurFileData + nBlockDataSize) > pEndFileData) + return -1; + if ((pCurOutBuffer + nBlockDataSize) > pEndOutBuffer) + return -1; + memcpy(pCurOutBuffer, pCurFileData, nBlockDataSize); + pCurOutBuffer += nBlockDataSize; + } + + pCurFileData += nBlockDataSize; + } + + return (int)(pCurOutBuffer - pOutBuffer); +} diff --git a/Tools/unix/lzsa/src/expand_inmem.h b/Tools/unix/lzsa/src/expand_inmem.h new file mode 100644 index 00000000..b52ee687 --- /dev/null +++ b/Tools/unix/lzsa/src/expand_inmem.h @@ -0,0 +1,70 @@ +/* + * expand_inmem.h - in-memory decompression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_INMEM_H +#define _EXPAND_INMEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get maximum decompressed size of compressed data + * + * @param pFileData compressed data + * @param nFileSize compressed size in bytes + * + * @return maximum decompressed size + */ +size_t lzsa_get_max_decompressed_size_inmem(const unsigned char *pFileData, size_t nFileSize); + +/** + * Decompress data in memory + * + * @param pFileData compressed data + * @param pOutBuffer buffer for decompressed data + * @param nFileSize compressed size in bytes + * @param nMaxOutBufferSize maximum capacity of decompression buffer + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param pFormatVersion pointer to format version, updated if this function is successful + * + * @return actual decompressed size, or -1 for error + */ +size_t lzsa_decompress_inmem(unsigned char *pFileData, unsigned char *pOutBuffer, size_t nFileSize, size_t nMaxOutBufferSize, const unsigned int nFlags, int *pFormatVersion); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXPAND_INMEM_H */ diff --git a/Tools/unix/lzsa/src/expand_streaming.c b/Tools/unix/lzsa/src/expand_streaming.c new file mode 100644 index 00000000..b241c68b --- /dev/null +++ b/Tools/unix/lzsa/src/expand_streaming.c @@ -0,0 +1,236 @@ +/* + * expand_streaming.c - streaming decompression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + + +#include +#include +#include "expand_streaming.h" +#include "format.h" +#include "frame.h" +#include "lib.h" + +/*-------------- File API -------------- */ + +/** + * Decompress file + * + * @param pszInFilename name of input(compressed) file to decompress + * @param pszOutFilename name of output(decompressed) file to generate + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param nFlags compression flags (LZSA_FLAG_RAW_BLOCK to decompress a raw block, or 0) + * @param nFormatVersion default version of format to use (1-2). This is used when decompressing a raw block, otherwise the version is extracted from the source file + * @param pOriginalSize pointer to returned output(decompressed) size, updated when this function is successful + * @param pCompressedSize pointer to returned input(compressed) size, updated when this function is successful + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_decompress_file(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nFlags, int nFormatVersion, + long long *pOriginalSize, long long *pCompressedSize) { + lzsa_stream_t inStream, outStream; + void *pDictionaryData = NULL; + int nDictionaryDataSize = 0; + lzsa_status_t nStatus; + + if (lzsa_filestream_open(&inStream, pszInFilename, "rb") < 0) { + return LZSA_ERROR_SRC; + } + + if (lzsa_filestream_open(&outStream, pszOutFilename, "wb") < 0) { + inStream.close(&inStream); + return LZSA_ERROR_DST; + } + + nStatus = lzsa_dictionary_load(pszDictionaryFilename, &pDictionaryData, &nDictionaryDataSize); + if (nStatus) { + outStream.close(&outStream); + inStream.close(&inStream); + return nStatus; + } + + nStatus = lzsa_decompress_stream(&inStream, &outStream, pDictionaryData, nDictionaryDataSize, nFlags, nFormatVersion, pOriginalSize, pCompressedSize); + + lzsa_dictionary_free(&pDictionaryData); + outStream.close(&outStream); + inStream.close(&inStream); + + return nStatus; +} + +/*-------------- Streaming API -------------- */ + +/** + * Decompress stream + * + * @param pInStream input(compressed) stream to decompress + * @param pOutStream output(decompressed) stream to write to + * @param pDictionaryData dictionary contents, or NULL for none + * @param nDictionaryDataSize size of dictionary contents, or 0 + * @param nFlags compression flags (LZSA_FLAG_RAW_BLOCK to decompress a raw block, or 0) + * @param nFormatVersion default version of format to use (1-2). This is used when decompressing a raw block, otherwise the version is extracted from the source file + * @param pOriginalSize pointer to returned output(decompressed) size, updated when this function is successful + * @param pCompressedSize pointer to returned input(compressed) size, updated when this function is successful + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_decompress_stream(lzsa_stream_t *pInStream, lzsa_stream_t *pOutStream, const void *pDictionaryData, int nDictionaryDataSize, const unsigned int nFlags, int nFormatVersion, + long long *pOriginalSize, long long *pCompressedSize) { + long long nOriginalSize = 0LL, nCompressedSize = 0LL; + unsigned char cFrameData[16]; + unsigned char *pInBlock; + unsigned char *pOutData; + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + const int nHeaderSize = lzsa_get_header_size(); + + memset(cFrameData, 0, 16); + if (pInStream->read(pInStream, cFrameData, nHeaderSize) != nHeaderSize) { + return LZSA_ERROR_SRC; + } + + if (lzsa_decode_header(cFrameData, nHeaderSize, &nFormatVersion) < 0) { + return LZSA_ERROR_FORMAT; + } + + nCompressedSize += (long long)nHeaderSize; + } + + pInBlock = (unsigned char*)malloc(BLOCK_SIZE); + if (!pInBlock) { + return LZSA_ERROR_MEMORY; + } + + pOutData = (unsigned char*)malloc(BLOCK_SIZE * 2); + if (!pOutData) { + free(pInBlock); + pInBlock = NULL; + + return LZSA_ERROR_MEMORY; + } + + int nDecompressionError = 0; + int nPrevDecompressedSize = 0; + int nNumBlocks = 0; + + while (!pInStream->eof(pInStream) && !nDecompressionError) { + unsigned int nBlockSize = 0; + int nIsUncompressed = 0; + + if (nPrevDecompressedSize != 0) { + memcpy(pOutData + BLOCK_SIZE - nPrevDecompressedSize, pOutData + BLOCK_SIZE, nPrevDecompressedSize); + } + else if (nDictionaryDataSize && pDictionaryData) { + nPrevDecompressedSize = nDictionaryDataSize; + memcpy(pOutData + BLOCK_SIZE - nPrevDecompressedSize, pDictionaryData, nPrevDecompressedSize); + } + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + const int nFrameSize = lzsa_get_frame_size(); + + memset(cFrameData, 0, 16); + if (pInStream->read(pInStream, cFrameData, nFrameSize) == nFrameSize) { + if (lzsa_decode_frame(cFrameData, nFrameSize, &nBlockSize, &nIsUncompressed) < 0) { + nDecompressionError = LZSA_ERROR_FORMAT; + nBlockSize = 0; + } + + nCompressedSize += (long long)nFrameSize; + } + else { + nDecompressionError = LZSA_ERROR_SRC; + nBlockSize = 0; + } + } + else { + if (!nNumBlocks) + nBlockSize = BLOCK_SIZE; + else + nBlockSize = 0; + } + + if (nBlockSize != 0) { + int nDecompressedSize = 0; + + if ((int)nBlockSize > BLOCK_SIZE) { + nDecompressionError = LZSA_ERROR_FORMAT; + break; + } + size_t nReadBytes = pInStream->read(pInStream, pInBlock, nBlockSize); + if (nFlags & LZSA_FLAG_RAW_BLOCK) { + nBlockSize = (unsigned int)nReadBytes; + } + + if (nReadBytes == nBlockSize) { + nCompressedSize += (long long)nReadBytes; + + if (nIsUncompressed) { + memcpy(pOutData + BLOCK_SIZE, pInBlock, nBlockSize); + nDecompressedSize = nBlockSize; + } + else { + nDecompressedSize = lzsa_decompressor_expand_block(pInBlock, nBlockSize, pOutData, BLOCK_SIZE, BLOCK_SIZE, nFormatVersion, nFlags); + if (nDecompressedSize < 0) { + nDecompressionError = LZSA_ERROR_DECOMPRESSION; + break; + } + } + + if (nDecompressedSize != 0) { + nOriginalSize += (long long)nDecompressedSize; + + if (pOutStream->write(pOutStream, pOutData + BLOCK_SIZE, nDecompressedSize) != nDecompressedSize) + nDecompressionError = LZSA_ERROR_DST; + nPrevDecompressedSize = nDecompressedSize; + nDecompressedSize = 0; + } + } + else { + break; + } + + nNumBlocks++; + } + else { + break; + } + } + + free(pOutData); + pOutData = NULL; + + free(pInBlock); + pInBlock = NULL; + + *pOriginalSize = nOriginalSize; + *pCompressedSize = nCompressedSize; + return nDecompressionError; +} + diff --git a/Tools/unix/lzsa/src/expand_streaming.h b/Tools/unix/lzsa/src/expand_streaming.h new file mode 100644 index 00000000..30dd88d8 --- /dev/null +++ b/Tools/unix/lzsa/src/expand_streaming.h @@ -0,0 +1,86 @@ +/* + * expand_streaming.h - streaming decompression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_STREAMING_H +#define _EXPAND_STREAMING_H + +#include "stream.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration */ +typedef enum _lzsa_status_t lzsa_status_t; + +/*-------------- File API -------------- */ + +/** + * Decompress file + * + * @param pszInFilename name of input(compressed) file to decompress + * @param pszOutFilename name of output(decompressed) file to generate + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param nFlags compression flags (LZSA_FLAG_RAW_BLOCK to decompress a raw block, or 0) + * @param nFormatVersion default version of format to use (1-2). This is used when decompressing a raw block, otherwise the version is extracted from the source file + * @param pOriginalSize pointer to returned output(decompressed) size, updated when this function is successful + * @param pCompressedSize pointer to returned input(compressed) size, updated when this function is successful + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_decompress_file(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nFlags, int nFormatVersion, + long long *pOriginalSize, long long *pCompressedSize); + +/*-------------- Streaming API -------------- */ + +/** + * Decompress stream + * + * @param pInStream input(compressed) stream to decompress + * @param pOutStream output(decompressed) stream to write to + * @param pDictionaryData dictionary contents, or NULL for none + * @param nDictionaryDataSize size of dictionary contents, or 0 + * @param nFlags compression flags (LZSA_FLAG_RAW_BLOCK to decompress a raw block, or 0) + * @param nFormatVersion default version of format to use (1-2). This is used when decompressing a raw block, otherwise the version is extracted from the source file + * @param pOriginalSize pointer to returned output(decompressed) size, updated when this function is successful + * @param pCompressedSize pointer to returned input(compressed) size, updated when this function is successful + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_decompress_stream(lzsa_stream_t *pInStream, lzsa_stream_t *pOutStream, const void *pDictionaryData, int nDictionaryDataSize, const unsigned int nFlags, int nFormatVersion, + long long *pOriginalSize, long long *pCompressedSize); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXPAND_STREAMING_H */ diff --git a/Tools/unix/lzsa/src/format.h b/Tools/unix/lzsa/src/format.h new file mode 100644 index 00000000..ace4f15c --- /dev/null +++ b/Tools/unix/lzsa/src/format.h @@ -0,0 +1,51 @@ +/* + * format.h - byte stream format definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _FORMAT_H +#define _FORMAT_H + +#define MIN_OFFSET 1 +#define MAX_OFFSET 0xffff + +#define MAX_VARLEN 0xffff + +#define BLOCK_SIZE 65536 + +#define MIN_MATCH_SIZE_V1 3 +#define LITERALS_RUN_LEN_V1 7 +#define MATCH_RUN_LEN_V1 15 + +#define MIN_MATCH_SIZE_V2 2 +#define LITERALS_RUN_LEN_V2 3 +#define MATCH_RUN_LEN_V2 7 + +#endif /* _FORMAT_H */ diff --git a/Tools/unix/lzsa/src/frame.c b/Tools/unix/lzsa/src/frame.c new file mode 100644 index 00000000..f1d6be2d --- /dev/null +++ b/Tools/unix/lzsa/src/frame.c @@ -0,0 +1,189 @@ +/* + * frame.c - frame implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "frame.h" + +#define LZSA_ID_0 0x7b +#define LZSA_ID_1 0x9e + +/** + * Get compressed file header size + * + * @return file header size + */ +int lzsa_get_header_size(void) { + return 3; +} + +/** + * Get compressed frame header size + * + * @return frame header size + */ +int lzsa_get_frame_size(void) { + return 3; +} + +/** + * Encode file header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize, int nFormatVersion) { + if (nMaxFrameDataSize >= 3 && (nFormatVersion == 1 || nFormatVersion == 2)) { + pFrameData[0] = LZSA_ID_0; /* Magic number */ + pFrameData[1] = LZSA_ID_1; + pFrameData[2] = (nFormatVersion == 2) ? 0x20 : 0; /* Format version 1 */ + + return 3; + } + else { + return -1; + } +} + +/** + * Encode compressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize compressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_compressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize) { + if (nMaxFrameDataSize >= 3 && nBlockDataSize <= 0x7fffff) { + pFrameData[0] = nBlockDataSize & 0xff; + pFrameData[1] = (nBlockDataSize >> 8) & 0xff; + pFrameData[2] = (nBlockDataSize >> 16) & 0x7f; + + return 3; + } + else { + return -1; + } +} + +/** + * Encode uncompressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize uncompressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_uncompressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize) { + if (nMaxFrameDataSize >= 3 && nBlockDataSize <= 0x7fffff) { + pFrameData[0] = nBlockDataSize & 0xff; + pFrameData[1] = (nBlockDataSize >> 8) & 0xff; + pFrameData[2] = ((nBlockDataSize >> 16) & 0x7f) | 0x80; /* Uncompressed block */ + + return 3; + } + else { + return -1; + } +} + +/** + * Encode terminal frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_footer_frame(unsigned char *pFrameData, const int nMaxFrameDataSize) { + if (nMaxFrameDataSize >= 3) { + pFrameData[0] = 0x00; /* EOD frame */ + pFrameData[1] = 0x00; + pFrameData[2] = 0x00; + + return 3; + } + else { + return -1; + } +} + +/** + * Decode file header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize, int *nFormatVersion) { + if (nFrameDataSize != 3 || + pFrameData[0] != LZSA_ID_0 || + pFrameData[1] != LZSA_ID_1 || + (pFrameData[2] & 0x1f) != 0 || + ((pFrameData[2] & 0xe0) != 0x00 && (pFrameData[2] & 0xe0) != 0x20)) { + return -1; + } + else { + *nFormatVersion = (pFrameData[2] & 0xe0) ? 2 : 1; + return 0; + } +} + +/** + * Decode frame header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * @param nBlockSize pointer to block size, updated if this function succeeds (set to 0 if this is the terminal frame) + * @param nIsUncompressed pointer to compressed block flag, updated if this function succeeds + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_frame(const unsigned char *pFrameData, const int nFrameDataSize, unsigned int *nBlockSize, int *nIsUncompressed) { + if (nFrameDataSize == 3) { + *nBlockSize = ((unsigned int)pFrameData[0]) | + (((unsigned int)pFrameData[1]) << 8) | + (((unsigned int)pFrameData[2]) << 16); + + *nIsUncompressed = ((*nBlockSize & 0x800000) != 0) ? 1 : 0; + *nBlockSize &= 0x7fffff; + return 0; + } + else { + return -1; + } +} diff --git a/Tools/unix/lzsa/src/frame.h b/Tools/unix/lzsa/src/frame.h new file mode 100644 index 00000000..7b5db99f --- /dev/null +++ b/Tools/unix/lzsa/src/frame.h @@ -0,0 +1,122 @@ +/* + * frame.h - frame definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _FRAME_H +#define _FRAME_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get compressed file header size + * + * @return file header size + */ +int lzsa_get_header_size(void); + +/** + * Get compressed frame header size + * + * @return frame header size + */ +int lzsa_get_frame_size(void); + +/** + * Encode file header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize, int nFormatVersion); + +/** + * Encode compressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize compressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_compressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize); + +/** + * Encode uncompressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize uncompressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_uncompressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize); + +/** + * Encode terminal frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_footer_frame(unsigned char *pFrameData, const int nMaxFrameDataSize); + +/** + * Decode file header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize, int *nFormatVersion); + +/** + * Decode frame header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * @param nBlockSize pointer to block size, updated if this function succeeds (set to 0 if this is the terminal frame) + * @param nIsUncompressed pointer to compressed block flag, updated if this function succeeds + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_frame(const unsigned char *pFrameData, const int nFrameDataSize, unsigned int *nBlockSize, int *nIsUncompressed); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRAME_H */ diff --git a/Tools/unix/lzsa/src/lib.h b/Tools/unix/lzsa/src/lib.h new file mode 100644 index 00000000..5556d2a9 --- /dev/null +++ b/Tools/unix/lzsa/src/lib.h @@ -0,0 +1,95 @@ +/* + * lib.h - LZSA library definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _LIB_H +#define _LIB_H + +#include "stream.h" +#include "dictionary.h" +#include "frame.h" +#include "format.h" +#include "shrink_context.h" +#include "shrink_streaming.h" +#include "shrink_inmem.h" +#include "expand_context.h" +#include "expand_streaming.h" +#include "expand_inmem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** High level status for compression and decompression */ +typedef enum _lzsa_status_t { + LZSA_OK = 0, /**< Success */ + LZSA_ERROR_SRC, /**< Error reading input */ + LZSA_ERROR_DST, /**< Error reading output */ + LZSA_ERROR_DICTIONARY, /**< Error reading dictionary */ + LZSA_ERROR_MEMORY, /**< Out of memory */ + + /* Compression-specific status codes */ + LZSA_ERROR_COMPRESSION, /**< Internal compression error */ + LZSA_ERROR_RAW_TOOLARGE, /**< Input is too large to be compressed to a raw block */ + LZSA_ERROR_RAW_UNCOMPRESSED, /**< Input is incompressible and raw blocks don't support uncompressed data */ + + /* Decompression-specific status codes */ + LZSA_ERROR_FORMAT, /**< Invalid input format or magic number when decompressing */ + LZSA_ERROR_DECOMPRESSION, /**< Internal decompression error */ +} lzsa_status_t; + +/* Compression flags */ +#define LZSA_FLAG_FAVOR_RATIO (1<<0) /**< 1 to compress with the best ratio, 0 to trade some compression ratio for extra decompression speed */ +#define LZSA_FLAG_RAW_BLOCK (1<<1) /**< 1 to emit raw block */ +#define LZSA_FLAG_RAW_BACKWARD (1<<2) /**< 1 to compress or decompress raw block backward */ + +/** + * Reverse bytes in the specified buffer + * + * @param pBuffer pointer to buffer whose contents are to be reversed + * @param nBufferSize size of buffer in bytes + */ +static inline void lzsa_reverse_buffer(unsigned char *pBuffer, const int nBufferSize) { + int nMidPoint = nBufferSize / 2; + int i, j; + + for (i = 0, j = nBufferSize - 1; i < nMidPoint; i++, j--) { + unsigned char c = pBuffer[i]; + pBuffer[i] = pBuffer[j]; + pBuffer[j] = c; + } +} + +#ifdef __cplusplus +} +#endif + +#endif /* _LIB_H */ diff --git a/Tools/unix/lzsa/src/libdivsufsort/CHANGELOG.md b/Tools/unix/lzsa/src/libdivsufsort/CHANGELOG.md new file mode 100644 index 00000000..fe9d0040 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/CHANGELOG.md @@ -0,0 +1,21 @@ +# libdivsufsort Change Log + +See full changelog at: https://github.com/y-256/libdivsufsort/commits + +## [2.0.1] - 2010-11-11 +### Fixed +* Wrong variable used in `divbwt` function +* Enclose some string variables with double quotation marks in include/CMakeLists.txt +* Fix typo in include/CMakeLists.txt + +## 2.0.0 - 2008-08-23 +### Changed +* Switch the build system to [CMake](http://www.cmake.org/) +* Improve the performance of the suffix-sorting algorithm + +### Added +* OpenMP support +* 64-bit version of divsufsort + +[Unreleased]: https://github.com/y-256/libdivsufsort/compare/2.0.1...HEAD +[2.0.1]: https://github.com/y-256/libdivsufsort/compare/2.0.0...2.0.1 diff --git a/Tools/unix/lzsa/src/libdivsufsort/CMakeLists.txt b/Tools/unix/lzsa/src/libdivsufsort/CMakeLists.txt new file mode 100644 index 00000000..78599439 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/CMakeLists.txt @@ -0,0 +1,99 @@ +### cmake file for building libdivsufsort Package ### +cmake_minimum_required(VERSION 2.4.4) +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") +include(AppendCompilerFlags) + +## Project information ## +project(libdivsufsort C) +set(PROJECT_VENDOR "Yuta Mori") +set(PROJECT_CONTACT "yuta.256@gmail.com") +set(PROJECT_URL "https://github.com/y-256/libdivsufsort") +set(PROJECT_DESCRIPTION "A lightweight suffix sorting library") +include(VERSION.cmake) + +## CPack configuration ## +set(CPACK_GENERATOR "TGZ;TBZ2;ZIP") +set(CPACK_SOURCE_GENERATOR "TGZ;TBZ2;ZIP") +include(ProjectCPack) + +## Project options ## +option(BUILD_SHARED_LIBS "Set to OFF to build static libraries" ON) +option(BUILD_EXAMPLES "Build examples" ON) +option(BUILD_DIVSUFSORT64 "Build libdivsufsort64" OFF) +option(USE_OPENMP "Use OpenMP for parallelization" OFF) +option(WITH_LFS "Enable Large File Support" ON) + +## Installation directories ## +set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32 or 64)") + +set(CMAKE_INSTALL_RUNTIMEDIR "" CACHE PATH "Specify the output directory for dll runtimes (default is bin)") +if(NOT CMAKE_INSTALL_RUNTIMEDIR) + set(CMAKE_INSTALL_RUNTIMEDIR "${CMAKE_INSTALL_PREFIX}/bin") +endif(NOT CMAKE_INSTALL_RUNTIMEDIR) + +set(CMAKE_INSTALL_LIBDIR "" CACHE PATH "Specify the output directory for libraries (default is lib)") +if(NOT CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}") +endif(NOT CMAKE_INSTALL_LIBDIR) + +set(CMAKE_INSTALL_INCLUDEDIR "" CACHE PATH "Specify the output directory for header files (default is include)") +if(NOT CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include") +endif(NOT CMAKE_INSTALL_INCLUDEDIR) + +set(CMAKE_INSTALL_PKGCONFIGDIR "" CACHE PATH "Specify the output directory for pkgconfig files (default is lib/pkgconfig)") +if(NOT CMAKE_INSTALL_PKGCONFIGDIR) + set(CMAKE_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif(NOT CMAKE_INSTALL_PKGCONFIGDIR) + +## Build type ## +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_VERBOSE_MAKEFILE ON) +endif(NOT CMAKE_BUILD_TYPE) + +## Compiler options ## +if(MSVC) + append_c_compiler_flags("/W4" "VC" CMAKE_C_FLAGS) + append_c_compiler_flags("/Oi;/Ot;/Ox;/Oy" "VC" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("/openmp" "VC" CMAKE_C_FLAGS) + endif(USE_OPENMP) +elseif(BORLAND) + append_c_compiler_flags("-w" "BCC" CMAKE_C_FLAGS) + append_c_compiler_flags("-Oi;-Og;-Os;-Ov;-Ox" "BCC" CMAKE_C_FLAGS_RELEASE) +else(MSVC) + if(CMAKE_COMPILER_IS_GNUCC) + append_c_compiler_flags("-Wall" "GCC" CMAKE_C_FLAGS) + append_c_compiler_flags("-fomit-frame-pointer" "GCC" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("-fopenmp" "GCC" CMAKE_C_FLAGS) + endif(USE_OPENMP) + else(CMAKE_COMPILER_IS_GNUCC) + append_c_compiler_flags("-Wall" "UNKNOWN" CMAKE_C_FLAGS) + append_c_compiler_flags("-fomit-frame-pointer" "UNKNOWN" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("-fopenmp;-openmp;-omp" "UNKNOWN" CMAKE_C_FLAGS) + endif(USE_OPENMP) + endif(CMAKE_COMPILER_IS_GNUCC) +endif(MSVC) + +## Add definitions ## +add_definitions(-DHAVE_CONFIG_H=1 -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS) + +## Add subdirectories ## +add_subdirectory(pkgconfig) +add_subdirectory(include) +add_subdirectory(lib) +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif(BUILD_EXAMPLES) + +## Add 'uninstall' target ## +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/CMakeModules/cmake_uninstall.cmake" + IMMEDIATE @ONLY) +ADD_CUSTOM_TARGET(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/CMakeModules/cmake_uninstall.cmake") diff --git a/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake new file mode 100644 index 00000000..58d3f99e --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake @@ -0,0 +1,38 @@ +include(CheckCSourceCompiles) +include(CheckCXXSourceCompiles) + +macro(append_c_compiler_flags _flags _name _result) + set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") + string(TOUPPER "${cname}" cname) + foreach(flag ${_flags}) + string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") + string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${cname}_${flagname}") + set(CMAKE_REQUIRED_FLAGS "${flag}") + check_c_source_compiles("int main() { return 0; }" ${have_flag}) + if(${have_flag}) + set(${_result} "${${_result}} ${flag}") + endif(${have_flag}) + endforeach(flag) + set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) +endmacro(append_c_compiler_flags) + +macro(append_cxx_compiler_flags _flags _name _result) + set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") + string(TOUPPER "${cname}" cname) + foreach(flag ${_flags}) + string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") + string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${cname}_${flagname}") + set(CMAKE_REQUIRED_FLAGS "${flag}") + check_cxx_source_compiles("int main() { return 0; }" ${have_flag}) + if(${have_flag}) + set(${_result} "${${_result}} ${flag}") + endif(${have_flag}) + endforeach(flag) + set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) +endmacro(append_cxx_compiler_flags) diff --git a/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake new file mode 100644 index 00000000..44601fd4 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake @@ -0,0 +1,15 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") +# break() + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords) diff --git a/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/CheckLFS.cmake b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/CheckLFS.cmake new file mode 100644 index 00000000..e2b00991 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/CheckLFS.cmake @@ -0,0 +1,109 @@ +## Checks for large file support ## +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckTypeSize) + +macro(check_lfs _isenable) + set(LFS_OFF_T "") + set(LFS_FOPEN "") + set(LFS_FSEEK "") + set(LFS_FTELL "") + set(LFS_PRID "") + + if(${_isenable}) + set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") + set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + -D_LARGEFILE_SOURCE -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 + -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS) + + check_include_file("sys/types.h" HAVE_SYS_TYPES_H) + check_include_file("inttypes.h" HAVE_INTTYPES_H) + check_include_file("stddef.h" HAVE_STDDEF_H) + check_include_file("stdint.h" HAVE_STDINT_H) + + # LFS type1: 8 <= sizeof(off_t), fseeko, ftello + check_type_size("off_t" SIZEOF_OFF_T) + if(SIZEOF_OFF_T GREATER 7) + check_symbol_exists("fseeko" "stdio.h" HAVE_FSEEKO) + check_symbol_exists("ftello" "stdio.h" HAVE_FTELLO) + if(HAVE_FSEEKO AND HAVE_FTELLO) + set(LFS_OFF_T "off_t") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "fseeko") + set(LFS_FTELL "ftello") + check_symbol_exists("PRIdMAX" "inttypes.h" HAVE_PRIDMAX) + if(HAVE_PRIDMAX) + set(LFS_PRID "PRIdMAX") + else(HAVE_PRIDMAX) + check_type_size("long" SIZEOF_LONG) + check_type_size("int" SIZEOF_INT) + if(SIZEOF_OFF_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"lld\"") + elseif(SIZEOF_LONG GREATER SIZEOF_INT) + set(LFS_PRID "\"ld\"") + else(SIZEOF_OFF_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"d\"") + endif(SIZEOF_OFF_T GREATER SIZEOF_LONG) + endif(HAVE_PRIDMAX) + endif(HAVE_FSEEKO AND HAVE_FTELLO) + endif(SIZEOF_OFF_T GREATER 7) + + # LFS type2: 8 <= sizeof(off64_t), fopen64, fseeko64, ftello64 + if(NOT LFS_OFF_T) + check_type_size("off64_t" SIZEOF_OFF64_T) + if(SIZEOF_OFF64_T GREATER 7) + check_symbol_exists("fopen64" "stdio.h" HAVE_FOPEN64) + check_symbol_exists("fseeko64" "stdio.h" HAVE_FSEEKO64) + check_symbol_exists("ftello64" "stdio.h" HAVE_FTELLO64) + if(HAVE_FOPEN64 AND HAVE_FSEEKO64 AND HAVE_FTELLO64) + set(LFS_OFF_T "off64_t") + set(LFS_FOPEN "fopen64") + set(LFS_FSEEK "fseeko64") + set(LFS_FTELL "ftello64") + check_symbol_exists("PRIdMAX" "inttypes.h" HAVE_PRIDMAX) + if(HAVE_PRIDMAX) + set(LFS_PRID "PRIdMAX") + else(HAVE_PRIDMAX) + check_type_size("long" SIZEOF_LONG) + check_type_size("int" SIZEOF_INT) + if(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"lld\"") + elseif(SIZEOF_LONG GREATER SIZEOF_INT) + set(LFS_PRID "\"ld\"") + else(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"d\"") + endif(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + endif(HAVE_PRIDMAX) + endif(HAVE_FOPEN64 AND HAVE_FSEEKO64 AND HAVE_FTELLO64) + endif(SIZEOF_OFF64_T GREATER 7) + endif(NOT LFS_OFF_T) + + # LFS type3: 8 <= sizeof(__int64), _fseeki64, _ftelli64 + if(NOT LFS_OFF_T) + check_type_size("__int64" SIZEOF___INT64) + if(SIZEOF___INT64 GREATER 7) + check_symbol_exists("_fseeki64" "stdio.h" HAVE__FSEEKI64) + check_symbol_exists("_ftelli64" "stdio.h" HAVE__FTELLI64) + if(HAVE__FSEEKI64 AND HAVE__FTELLI64) + set(LFS_OFF_T "__int64") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "_fseeki64") + set(LFS_FTELL "_ftelli64") + set(LFS_PRID "\"I64d\"") + endif(HAVE__FSEEKI64 AND HAVE__FTELLI64) + endif(SIZEOF___INT64 GREATER 7) + endif(NOT LFS_OFF_T) + + set(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") + endif(${_isenable}) + + if(NOT LFS_OFF_T) + ## not found + set(LFS_OFF_T "long") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "fseek") + set(LFS_FTELL "ftell") + set(LFS_PRID "\"ld\"") + endif(NOT LFS_OFF_T) + +endmacro(check_lfs) diff --git a/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/ProjectCPack.cmake b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/ProjectCPack.cmake new file mode 100644 index 00000000..7c105f93 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/ProjectCPack.cmake @@ -0,0 +1,38 @@ +# If the cmake version includes cpack, use it +IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}") + SET(CPACK_PACKAGE_VENDOR "${PROJECT_VENDOR}") + SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") + SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") + SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") + SET(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") +# SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME} ${PROJECT_VERSION}") + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_FULL}") + + IF(NOT DEFINED CPACK_SYSTEM_NAME) + SET(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") + ENDIF(NOT DEFINED CPACK_SYSTEM_NAME) + + IF(${CPACK_SYSTEM_NAME} MATCHES Windows) + IF(CMAKE_CL_64) + SET(CPACK_SYSTEM_NAME win64-${CMAKE_SYSTEM_PROCESSOR}) + ELSE(CMAKE_CL_64) + SET(CPACK_SYSTEM_NAME win32-${CMAKE_SYSTEM_PROCESSOR}) + ENDIF(CMAKE_CL_64) + ENDIF(${CPACK_SYSTEM_NAME} MATCHES Windows) + + IF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) + SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}") + ENDIF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) + + SET(CPACK_PACKAGE_CONTACT "${PROJECT_CONTACT}") + IF(UNIX) + SET(CPACK_STRIP_FILES "") + SET(CPACK_SOURCE_STRIP_FILES "") +# SET(CPACK_PACKAGE_EXECUTABLES "ccmake" "CMake") + ENDIF(UNIX) + SET(CPACK_SOURCE_IGNORE_FILES "/CVS/" "/build/" "/\\\\.build/" "/\\\\.svn/" "~$") + # include CPack model once all variables are set + INCLUDE(CPack) +ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") diff --git a/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in new file mode 100644 index 00000000..8366a835 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in @@ -0,0 +1,36 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") + +SET(NUM 0) +FOREACH(file ${files}) + IF(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "Looking for \"$ENV{DESTDIR}${file}\" - found") + SET(UNINSTALL_CHECK_${NUM} 1) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "Looking for \"$ENV{DESTDIR}${file}\" - not found") + SET(UNINSTALL_CHECK_${NUM} 0) + ENDIF(EXISTS "$ENV{DESTDIR}${file}") + MATH(EXPR NUM "1 + ${NUM}") +ENDFOREACH(file) + +SET(NUM 0) +FOREACH(file ${files}) + IF(${UNINSTALL_CHECK_${NUM}}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ENDIF(${UNINSTALL_CHECK_${NUM}}) + MATH(EXPR NUM "1 + ${NUM}") +ENDFOREACH(file) + +FILE(REMOVE "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") diff --git a/Tools/unix/lzsa/src/libdivsufsort/LICENSE b/Tools/unix/lzsa/src/libdivsufsort/LICENSE new file mode 100644 index 00000000..249efa49 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2003 Yuta Mori All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Tools/unix/lzsa/src/libdivsufsort/README.md b/Tools/unix/lzsa/src/libdivsufsort/README.md new file mode 100644 index 00000000..381a1888 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/README.md @@ -0,0 +1,140 @@ +# libdivsufsort + +libdivsufsort is a software library that implements a lightweight suffix array construction algorithm. + +## News +* 2015-03-21: The project has moved from [Google Code](http://code.google.com/p/libdivsufsort/) to [GitHub](https://github.com/y-256/libdivsufsort) + +## Introduction +This library provides a simple and an efficient C API to construct a suffix array and a Burrows-Wheeler transformed string from a given string over a constant-size alphabet. +The algorithm runs in O(n log n) worst-case time using only 5n+O(1) bytes of memory space, where n is the length of +the string. + +## Build requirements +* An ANSI C Compiler (e.g. GNU GCC) +* [CMake](http://www.cmake.org/ "CMake") version 2.4.2 or newer +* CMake-supported build tool + +## Building on GNU/Linux +1. Get the source code from GitHub. You can either + * use git to clone the repository + ``` + git clone https://github.com/y-256/libdivsufsort.git + ``` + * or download a [zip file](../../archive/master.zip) directly +2. Create a `build` directory in the package source directory. +```shell +$ cd libdivsufsort +$ mkdir build +$ cd build +``` +3. Configure the package for your system. +If you want to install to a different location, change the -DCMAKE_INSTALL_PREFIX option. +```shell +$ cmake -DCMAKE_BUILD_TYPE="Release" \ +-DCMAKE_INSTALL_PREFIX="/usr/local" .. +``` +4. Compile the package. +```shell +$ make +``` +5. (Optional) Install the library and header files. +```shell +$ sudo make install +``` + +## API +```c +/* Data types */ +typedef int32_t saint_t; +typedef int32_t saidx_t; +typedef uint8_t sauchar_t; + +/* + * Constructs the suffix array of a given string. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array or suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +saint_t +divsufsort(const sauchar_t *T, saidx_t *SA, saidx_t n); + +/* + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); +``` + +## Example Usage +```c +#include +#include +#include + +#include + +int main() { + // intput data + char *Text = "abracadabra"; + int n = strlen(Text); + int i, j; + + // allocate + int *SA = (int *)malloc(n * sizeof(int)); + + // sort + divsufsort((unsigned char *)Text, SA, n); + + // output + for(i = 0; i < n; ++i) { + printf("SA[%2d] = %2d: ", i, SA[i]); + for(j = SA[i]; j < n; ++j) { + printf("%c", Text[j]); + } + printf("$\n"); + } + + // deallocate + free(SA); + + return 0; +} +``` +See the [examples](examples) directory for a few other examples. + +## Benchmarks +See [Benchmarks](https://github.com/y-256/libdivsufsort/blob/wiki/SACA_Benchmarks.md) page for details. + +## License +libdivsufsort is released under the [MIT license](LICENSE "MIT license"). +> The MIT License (MIT) +> +> Copyright (c) 2003 Yuta Mori All rights reserved. +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +## Author +* Yuta Mori diff --git a/Tools/unix/lzsa/src/libdivsufsort/VERSION.cmake b/Tools/unix/lzsa/src/libdivsufsort/VERSION.cmake new file mode 100644 index 00000000..3f11ac18 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/VERSION.cmake @@ -0,0 +1,23 @@ +set(PROJECT_VERSION_MAJOR "2") +set(PROJECT_VERSION_MINOR "0") +set(PROJECT_VERSION_PATCH "2") +set(PROJECT_VERSION_EXTRA "-1") +set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +set(PROJECT_VERSION_FULL "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_EXTRA}") + +set(LIBRARY_VERSION "3.0.1") +set(LIBRARY_SOVERSION "3") + +## Git revision number ## +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") + execute_process(COMMAND git describe --tags HEAD + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE GIT_DESCRIBE_TAGS ERROR_QUIET) + if(GIT_DESCRIBE_TAGS) + string(REGEX REPLACE "^v(.*)" "\\1" GIT_REVISION "${GIT_DESCRIBE_TAGS}") + string(STRIP "${GIT_REVISION}" GIT_REVISION) + if(GIT_REVISION) + set(PROJECT_VERSION_FULL "${GIT_REVISION}") + endif(GIT_REVISION) + endif(GIT_DESCRIBE_TAGS) +endif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") diff --git a/Tools/unix/lzsa/src/libdivsufsort/examples/CMakeLists.txt b/Tools/unix/lzsa/src/libdivsufsort/examples/CMakeLists.txt new file mode 100644 index 00000000..e801c81a --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/examples/CMakeLists.txt @@ -0,0 +1,11 @@ +## Add definitions ## +add_definitions(-D_LARGEFILE_SOURCE -D_LARGE_FILES -D_FILE_OFFSET_BITS=64) + +## Targets ## +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_BINARY_DIR}/../include") +link_directories("${CMAKE_CURRENT_BINARY_DIR}/../lib") +foreach(src suftest mksary sasearch bwt unbwt) + add_executable(${src} ${src}.c) + target_link_libraries(${src} divsufsort) +endforeach(src) diff --git a/Tools/unix/lzsa/src/libdivsufsort/examples/bwt.c b/Tools/unix/lzsa/src/libdivsufsort/examples/bwt.c new file mode 100644 index 00000000..5a362d01 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/examples/bwt.c @@ -0,0 +1,220 @@ +/* + * bwt.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +size_t +write_int(FILE *fp, saidx_t n) { + unsigned char c[4]; + c[0] = (unsigned char)((n >> 0) & 0xff), c[1] = (unsigned char)((n >> 8) & 0xff), + c[2] = (unsigned char)((n >> 16) & 0xff), c[3] = (unsigned char)((n >> 24) & 0xff); + return fwrite(c, sizeof(unsigned char), 4, fp); +} + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "bwt, a burrows-wheeler transform program, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s [-b num] INFILE OUTFILE\n", progname); + fprintf(stderr, " -b num set block size to num MiB [1..512] (default: 32)\n\n"); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + size_t m; + saidx_t pidx; + clock_t start,finish; + saint_t i, blocksize = 32, needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if((argc != 3) && (argc != 5)) { print_help(argv[0], EXIT_FAILURE); } + i = 1; + if(argc == 5) { + if(strcmp(argv[i], "-b") != 0) { print_help(argv[0], EXIT_FAILURE); } + blocksize = atoi(argv[i + 1]); + if(blocksize < 0) { blocksize = 1; } + else if(512 < blocksize) { blocksize = 512; } + i += 2; + } + blocksize <<= 20; + + /* Open a file for reading. */ + if(strcmp(argv[i], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[i], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[i], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + i += 1; + + /* Open a file for writing. */ + if(strcmp(argv[i], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[i], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[i], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x20000000L < n) { n = 0x20000000L; } + if((blocksize == 0) || (n < blocksize)) { blocksize = (saidx_t)n; } + } else if(blocksize == 0) { blocksize = 32 << 20; } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc(blocksize * sizeof(sauchar_t)); + SA = (saidx_t *)malloc(blocksize * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Write the blocksize. */ + if(write_int(ofp, blocksize) != 4) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + + fprintf(stderr, " BWT (blocksize %" PRIdSAINT_T ") ... ", blocksize); + start = clock(); + for(n = 0; 0 < (m = fread(T, sizeof(sauchar_t), blocksize, fp)); n += m) { + /* Burrows-Wheeler Transform. */ + pidx = divbwt(T, T, SA, m); + if(pidx < 0) { + fprintf(stderr, "%s (bw_transform): %s.\n", + argv[0], + (pidx == -1) ? "Invalid arguments" : "Cannot allocate memory"); + exit(EXIT_FAILURE); + } + + /* Write the bwted data. */ + if((write_int(ofp, pidx) != 4) || + (fwrite(T, sizeof(sauchar_t), m, ofp) != m)) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } + if(ferror(fp)) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%" PRIdOFF_T " bytes: %.4f sec\n", + n, (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Close files */ + if(needclose & 1) { fclose(fp); } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/Tools/unix/lzsa/src/libdivsufsort/examples/mksary.c b/Tools/unix/lzsa/src/libdivsufsort/examples/mksary.c new file mode 100644 index 00000000..b48177cf --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/examples/mksary.c @@ -0,0 +1,193 @@ +/* + * mksary.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "mksary, a simple suffix array builder, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s INFILE OUTFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + clock_t start, finish; + saint_t needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 3) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + + /* Open a file for writing. */ + if(strcmp(argv[2], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[2], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[2], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x7fffffff <= n) { + fprintf(stderr, "%s: Input file `%s' is too big.\n", argv[0], fname); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 1) { fclose(fp); } + + /* Construct the suffix array. */ + fprintf(stderr, "%s: %" PRIdOFF_T " bytes ... ", fname, n); + start = clock(); + if(divsufsort(T, SA, (saidx_t)n) != 0) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%.4f sec\n", (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Write the suffix array. */ + if(fwrite(SA, sizeof(saidx_t), (size_t)n, ofp) != (size_t)n) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/Tools/unix/lzsa/src/libdivsufsort/examples/sasearch.c b/Tools/unix/lzsa/src/libdivsufsort/examples/sasearch.c new file mode 100644 index 00000000..7e5ca4fe --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/examples/sasearch.c @@ -0,0 +1,165 @@ +/* + * sasearch.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "sasearch, a simple SA-based full-text search tool, version %s\n", + divsufsort_version()); + fprintf(stderr, "usage: %s PATTERN FILE SAFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp; + const char *P; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + size_t Psize; + saidx_t i, size, left; + + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 4) { print_help(argv[0], EXIT_FAILURE); } + + P = argv[1]; + Psize = strlen(P); + + /* Open a file for reading. */ +#if HAVE_FOPEN_S + if(fopen_s(&fp, argv[2], "rb") != 0) { +#else + if((fp = LFS_FOPEN(argv[2], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5n bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + fclose(fp); + + /* Open the SA file for reading. */ +#if HAVE_FOPEN_S + if(fopen_s(&fp, argv[3], "rb") != 0) { +#else + if((fp = LFS_FOPEN(argv[3], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], argv[3]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Read n * sizeof(saidx_t) bytes of data. */ + if(fread(SA, sizeof(saidx_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[3]); + perror(NULL); + exit(EXIT_FAILURE); + } + fclose(fp); + + /* Search and print */ + size = sa_search(T, (saidx_t)n, + (const sauchar_t *)P, (saidx_t)Psize, + SA, (saidx_t)n, &left); + for(i = 0; i < size; ++i) { + fprintf(stdout, "%" PRIdSAIDX_T "\n", SA[left + i]); + } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/Tools/unix/lzsa/src/libdivsufsort/examples/suftest.c b/Tools/unix/lzsa/src/libdivsufsort/examples/suftest.c new file mode 100644 index 00000000..71892ac1 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/examples/suftest.c @@ -0,0 +1,164 @@ +/* + * suftest.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "suftest, a suffixsort tester, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s FILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp; + const char *fname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + clock_t start, finish; + saint_t needclose = 1; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 2) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose = 0; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x7fffffff <= n) { + fprintf(stderr, "%s: Input file `%s' is too big.\n", argv[0], fname); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5n bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[1]); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 1) { fclose(fp); } + + /* Construct the suffix array. */ + fprintf(stderr, "%s: %" PRIdOFF_T " bytes ... ", fname, n); + start = clock(); + if(divsufsort(T, SA, (saidx_t)n) != 0) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%.4f sec\n", (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Check the suffix array. */ + if(sufcheck(T, SA, (saidx_t)n, 1) != 0) { exit(EXIT_FAILURE); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/Tools/unix/lzsa/src/libdivsufsort/examples/unbwt.c b/Tools/unix/lzsa/src/libdivsufsort/examples/unbwt.c new file mode 100644 index 00000000..c0f19e97 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/examples/unbwt.c @@ -0,0 +1,207 @@ +/* + * unbwt.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +size_t +read_int(FILE *fp, saidx_t *n) { + unsigned char c[4]; + size_t m = fread(c, sizeof(unsigned char), 4, fp); + if(m == 4) { + *n = (c[0] << 0) | (c[1] << 8) | + (c[2] << 16) | (c[3] << 24); + } + return m; +} + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "unbwt, an inverse burrows-wheeler transform program, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s INFILE OUTFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *A; + LFS_OFF_T n; + size_t m; + saidx_t pidx; + clock_t start, finish; + saint_t err, blocksize, needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 3) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + + /* Open a file for writing. */ + if(strcmp(argv[2], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[2], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[2], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Read the blocksize. */ + if(read_int(fp, &blocksize) != 4) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc(blocksize * sizeof(sauchar_t)); + A = (saidx_t *)malloc(blocksize * sizeof(saidx_t)); + if((T == NULL) || (A == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "UnBWT (blocksize %" PRIdSAINT_T ") ... ", blocksize); + start = clock(); + for(n = 0; (m = read_int(fp, &pidx)) != 0; n += m) { + /* Read blocksize bytes of data. */ + if((m != 4) || ((m = fread(T, sizeof(sauchar_t), blocksize, fp)) == 0)) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Inverse Burrows-Wheeler Transform. */ + if((err = inverse_bw_transform(T, T, A, m, pidx)) != 0) { + fprintf(stderr, "%s (reverseBWT): %s.\n", + argv[0], + (err == -1) ? "Invalid data" : "Cannot allocate memory"); + exit(EXIT_FAILURE); + } + + /* Write m bytes of data. */ + if(fwrite(T, sizeof(sauchar_t), m, ofp) != m) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } + if(ferror(fp)) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%" PRIdOFF_T " bytes: %.4f sec\n", + n, (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Close files */ + if(needclose & 1) { fclose(fp); } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(A); + free(T); + + return 0; +} diff --git a/Tools/unix/lzsa/src/libdivsufsort/include/CMakeLists.txt b/Tools/unix/lzsa/src/libdivsufsort/include/CMakeLists.txt new file mode 100644 index 00000000..37781ccb --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/include/CMakeLists.txt @@ -0,0 +1,162 @@ +include(CheckIncludeFiles) +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckTypeSize) +include(CheckFunctionKeywords) +include(CheckLFS) + +## Checks for header files ## +check_include_file("inttypes.h" HAVE_INTTYPES_H) +check_include_file("memory.h" HAVE_MEMORY_H) +check_include_file("stddef.h" HAVE_STDDEF_H) +check_include_file("stdint.h" HAVE_STDINT_H) +check_include_file("stdlib.h" HAVE_STDLIB_H) +check_include_file("string.h" HAVE_STRING_H) +check_include_file("strings.h" HAVE_STRINGS_H) +check_include_file("sys/types.h" HAVE_SYS_TYPES_H) +if(HAVE_INTTYPES_H) + set(INCFILE "#include ") +elseif(HAVE_STDINT_H) + set(INCFILE "#include ") +else(HAVE_INTTYPES_H) + set(INCFILE "") +endif(HAVE_INTTYPES_H) + +## create configuration files from .cmake file ## +if(BUILD_EXAMPLES) + ## Checks for WinIO ## + if(WIN32) + check_include_file("io.h" HAVE_IO_H) + check_include_file("fcntl.h" HAVE_FCNTL_H) + check_symbol_exists("_setmode" "io.h;fcntl.h" HAVE__SETMODE) + if(NOT HAVE__SETMODE) + check_symbol_exists("setmode" "io.h;fcntl.h" HAVE_SETMODE) + endif(NOT HAVE__SETMODE) + check_symbol_exists("_fileno" "stdio.h" HAVE__FILENO) + check_symbol_exists("fopen_s" "stdio.h" HAVE_FOPEN_S) + check_symbol_exists("_O_BINARY" "fcntl.h" HAVE__O_BINARY) + endif(WIN32) + + ## Checks for large file support ## + check_lfs(WITH_LFS) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lfs.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/lfs.h" @ONLY) +endif(BUILD_EXAMPLES) + +## generate config.h ## +check_function_keywords("inline;__inline;__inline__;__declspec(dllexport);__declspec(dllimport)") +if(HAVE_INLINE) + set(INLINE "inline") +elseif(HAVE___INLINE) + set(INLINE "__inline") +elseif(HAVE___INLINE__) + set(INLINE "__inline__") +else(HAVE_INLINE) + set(INLINE "") +endif(HAVE_INLINE) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") + +## Checks for types ## +# sauchar_t (8bit) +check_type_size("uint8_t" UINT8_T) +if(HAVE_UINT8_T) + set(SAUCHAR_TYPE "uint8_t") +else(HAVE_UINT8_T) + check_type_size("unsigned char" SIZEOF_UNSIGNED_CHAR) + if("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") + set(SAUCHAR_TYPE "unsigned char") + else("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") + message(FATAL_ERROR "Cannot find unsigned 8-bit integer type") + endif("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") +endif(HAVE_UINT8_T) +# saint_t (32bit) +check_type_size("int32_t" INT32_T) +if(HAVE_INT32_T) + set(SAINT32_TYPE "int32_t") + check_symbol_exists("PRId32" "inttypes.h" HAVE_PRID32) + if(HAVE_PRID32) + set(SAINT32_PRId "PRId32") + else(HAVE_PRID32) + set(SAINT32_PRId "\"d\"") + endif(HAVE_PRID32) +else(HAVE_INT32_T) + check_type_size("int" SIZEOF_INT) + check_type_size("long" SIZEOF_LONG) + check_type_size("short" SIZEOF_SHORT) + check_type_size("__int32" SIZEOF___INT32) + if("${SIZEOF_INT}" STREQUAL "4") + set(SAINT32_TYPE "int") + set(SAINT32_PRId "\"d\"") + elseif("${SIZEOF_LONG}" STREQUAL "4") + set(SAINT32_TYPE "long") + set(SAINT32_PRId "\"ld\"") + elseif("${SIZEOF_SHORT}" STREQUAL "4") + set(SAINT32_TYPE "short") + set(SAINT32_PRId "\"d\"") + elseif("${SIZEOF___INT32}" STREQUAL "4") + set(SAINT32_TYPE "__int32") + set(SAINT32_PRId "\"d\"") + else("${SIZEOF_INT}" STREQUAL "4") + message(FATAL_ERROR "Cannot find 32-bit integer type") + endif("${SIZEOF_INT}" STREQUAL "4") +endif(HAVE_INT32_T) +# saint64_t (64bit) +if(BUILD_DIVSUFSORT64) + check_type_size("int64_t" INT64_T) + if(HAVE_INT64_T) + set(SAINT64_TYPE "int64_t") + check_symbol_exists("PRId64" "inttypes.h" HAVE_PRID64) + if(HAVE_PRID64) + set(SAINT64_PRId "PRId64") + else(HAVE_PRID64) + set(SAINT64_PRId "\"lld\"") + endif(HAVE_PRID64) + else(HAVE_INT64_T) + check_type_size("int" SIZEOF_INT) + check_type_size("long" SIZEOF_LONG) + check_type_size("long long" SIZEOF_LONG_LONG) + check_type_size("__int64" SIZEOF___INT64) + if("${SIZEOF_INT}" STREQUAL "8") + set(SAINT64_TYPE "int") + set(SAINT64_PRId "\"d\"") + elseif("${SIZEOF_LONG}" STREQUAL "8") + set(SAINT64_TYPE "long") + set(SAINT64_PRId "\"ld\"") + elseif("${SIZEOF_LONG_LONG}" STREQUAL "8") + set(SAINT64_TYPE "long long") + set(SAINT64_PRId "\"lld\"") + elseif("${SIZEOF___INT64}" STREQUAL "8") + set(SAINT64_TYPE "__int64") + set(SAINT64_PRId "\"I64d\"") + else("${SIZEOF_INT}" STREQUAL "8") + message(SEND_ERROR "Cannot find 64-bit integer type") + set(BUILD_DIVSUFSORT64 OFF) + endif("${SIZEOF_INT}" STREQUAL "8") + endif(HAVE_INT64_T) +endif(BUILD_DIVSUFSORT64) + +## generate divsufsort.h ## +set(DIVSUFSORT_IMPORT "") +set(DIVSUFSORT_EXPORT "") +if(BUILD_SHARED_LIBS) + if(HAVE___DECLSPEC_DLLIMPORT_) + set(DIVSUFSORT_IMPORT "__declspec(dllimport)") + endif(HAVE___DECLSPEC_DLLIMPORT_) + if(HAVE___DECLSPEC_DLLEXPORT_) + set(DIVSUFSORT_EXPORT "__declspec(dllexport)") + endif(HAVE___DECLSPEC_DLLEXPORT_) +endif(BUILD_SHARED_LIBS) +set(W64BIT "") +set(SAINDEX_TYPE "${SAINT32_TYPE}") +set(SAINDEX_PRId "${SAINT32_PRId}") +set(SAINT_PRId "${SAINT32_PRId}") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/divsufsort.h.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/divsufsort.h" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/divsufsort.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +if(BUILD_DIVSUFSORT64) + set(W64BIT "64") + set(SAINDEX_TYPE "${SAINT64_TYPE}") + set(SAINDEX_PRId "${SAINT64_PRId}") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/divsufsort.h.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/divsufsort64.h" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/divsufsort64.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +endif(BUILD_DIVSUFSORT64) diff --git a/Tools/unix/lzsa/src/libdivsufsort/include/config.h.cmake b/Tools/unix/lzsa/src/libdivsufsort/include/config.h.cmake new file mode 100644 index 00000000..6a1cf47d --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/include/config.h.cmake @@ -0,0 +1,81 @@ +/* + * config.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CONFIG_H +#define _CONFIG_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** Define to the version of this package. **/ +#cmakedefine PROJECT_VERSION_FULL "${PROJECT_VERSION_FULL}" + +/** Define to 1 if you have the header files. **/ +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_STDDEF_H 1 +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_STDLIB_H 1 +#cmakedefine HAVE_STRING_H 1 +#cmakedefine HAVE_STRINGS_H 1 +#cmakedefine HAVE_MEMORY_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 + +/** for WinIO **/ +#cmakedefine HAVE_IO_H 1 +#cmakedefine HAVE_FCNTL_H 1 +#cmakedefine HAVE__SETMODE 1 +#cmakedefine HAVE_SETMODE 1 +#cmakedefine HAVE__FILENO 1 +#cmakedefine HAVE_FOPEN_S 1 +#cmakedefine HAVE__O_BINARY 1 +#ifndef HAVE__SETMODE +# if HAVE_SETMODE +# define _setmode setmode +# define HAVE__SETMODE 1 +# endif +# if HAVE__SETMODE && !HAVE__O_BINARY +# define _O_BINARY 0 +# define HAVE__O_BINARY 1 +# endif +#endif + +/** for inline **/ +#ifndef INLINE +# define INLINE @INLINE@ +#endif + +/** for VC++ warning **/ +#ifdef _MSC_VER +#pragma warning(disable: 4127) +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _CONFIG_H */ diff --git a/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort.h b/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort.h new file mode 100644 index 00000000..7ebb4126 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort.h @@ -0,0 +1,189 @@ +/* + * divsufsort.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_H +#define _DIVSUFSORT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DIVSUFSORT_API + +/*- Datatypes -*/ +#ifndef SAUCHAR_T +#define SAUCHAR_T +typedef unsigned char sauchar_t; +#endif /* SAUCHAR_T */ +#ifndef SAINT_T +#define SAINT_T +typedef int saint_t; +#endif /* SAINT_T */ +#ifndef SAIDX_T +#define SAIDX_T +typedef int saidx_t; +#endif /* SAIDX_T */ +#ifndef PRIdSAIDX_T +#define PRIdSAIDX_T "d" +#endif + +/*- divsufsort context */ +typedef struct _divsufsort_ctx_t { + saidx_t *bucket_A; + saidx_t *bucket_B; +} divsufsort_ctx_t; + +/*- Prototypes -*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx); + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx); + +/** + * Constructs the suffix array of a given string. + * @param ctx suffix array context + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n); + +#if 0 +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); + +/** + * Returns the version of the divsufsort library. + * @return The version number string. + */ +DIVSUFSORT_API +const char * +divsufsort_version(void); + + +/** + * Constructs the burrows-wheeler transformed string of a given string and suffix array. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param SA[0..n-1] The suffix array. (can be NULL) + * @param n The length of the given string. + * @param idx The output primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *SA /* can NULL */, + saidx_t n, saidx_t *idx); + +/** + * Inverse BW-transforms a given BWTed string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param idx The primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *A /* can NULL */, + saidx_t n, saidx_t idx); + +/** + * Checks the correctness of a given suffix array. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The input suffix array. + * @param n The length of the given string. + * @param verbose The verbose mode. + * @return 0 if no error occurred. + */ +DIVSUFSORT_API +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, saidx_t n, saint_t verbose); + +/** + * Search for the pattern P in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param P[0..Psize-1] The input pattern string. + * @param Psize The length of the given pattern string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *left); + +/** + * Search for the character c in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param c The input character. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *left); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_H */ diff --git a/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort.h.cmake b/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort.h.cmake new file mode 100644 index 00000000..bcaba7c6 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort.h.cmake @@ -0,0 +1,180 @@ +/* + * divsufsort@W64BIT@.h for libdivsufsort@W64BIT@ + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT@W64BIT@_H +#define _DIVSUFSORT@W64BIT@_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +@INCFILE@ + +#ifndef DIVSUFSORT_API +# ifdef DIVSUFSORT_BUILD_DLL +# define DIVSUFSORT_API @DIVSUFSORT_EXPORT@ +# else +# define DIVSUFSORT_API @DIVSUFSORT_IMPORT@ +# endif +#endif + +/*- Datatypes -*/ +#ifndef SAUCHAR_T +#define SAUCHAR_T +typedef @SAUCHAR_TYPE@ sauchar_t; +#endif /* SAUCHAR_T */ +#ifndef SAINT_T +#define SAINT_T +typedef @SAINT32_TYPE@ saint_t; +#endif /* SAINT_T */ +#ifndef SAIDX@W64BIT@_T +#define SAIDX@W64BIT@_T +typedef @SAINDEX_TYPE@ saidx@W64BIT@_t; +#endif /* SAIDX@W64BIT@_T */ +#ifndef PRIdSAINT_T +#define PRIdSAINT_T @SAINT_PRId@ +#endif /* PRIdSAINT_T */ +#ifndef PRIdSAIDX@W64BIT@_T +#define PRIdSAIDX@W64BIT@_T @SAINDEX_PRId@ +#endif /* PRIdSAIDX@W64BIT@_T */ + + +/*- Prototypes -*/ + +/** + * Constructs the suffix array of a given string. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +divsufsort@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t *SA, saidx@W64BIT@_t n); + +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +divbwt@W64BIT@(const sauchar_t *T, sauchar_t *U, saidx@W64BIT@_t *A, saidx@W64BIT@_t n); + +/** + * Returns the version of the divsufsort library. + * @return The version number string. + */ +DIVSUFSORT_API +const char * +divsufsort@W64BIT@_version(void); + + +/** + * Constructs the burrows-wheeler transformed string of a given string and suffix array. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param SA[0..n-1] The suffix array. (can be NULL) + * @param n The length of the given string. + * @param idx The output primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +bw_transform@W64BIT@(const sauchar_t *T, sauchar_t *U, + saidx@W64BIT@_t *SA /* can NULL */, + saidx@W64BIT@_t n, saidx@W64BIT@_t *idx); + +/** + * Inverse BW-transforms a given BWTed string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param idx The primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +inverse_bw_transform@W64BIT@(const sauchar_t *T, sauchar_t *U, + saidx@W64BIT@_t *A /* can NULL */, + saidx@W64BIT@_t n, saidx@W64BIT@_t idx); + +/** + * Checks the correctness of a given suffix array. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The input suffix array. + * @param n The length of the given string. + * @param verbose The verbose mode. + * @return 0 if no error occurred. + */ +DIVSUFSORT_API +saint_t +sufcheck@W64BIT@(const sauchar_t *T, const saidx@W64BIT@_t *SA, saidx@W64BIT@_t n, saint_t verbose); + +/** + * Search for the pattern P in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param P[0..Psize-1] The input pattern string. + * @param Psize The length of the given pattern string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +sa_search@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t Tsize, + const sauchar_t *P, saidx@W64BIT@_t Psize, + const saidx@W64BIT@_t *SA, saidx@W64BIT@_t SAsize, + saidx@W64BIT@_t *left); + +/** + * Search for the character c in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param c The input character. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +sa_simplesearch@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t Tsize, + const saidx@W64BIT@_t *SA, saidx@W64BIT@_t SAsize, + saint_t c, saidx@W64BIT@_t *left); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT@W64BIT@_H */ diff --git a/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort_config.h b/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort_config.h new file mode 100644 index 00000000..4054a8aa --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort_config.h @@ -0,0 +1,9 @@ +#define HAVE_STRING_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STDINT_H 1 +#define INLINE inline + +#ifdef _MSC_VER +#pragma warning( disable : 4244 ) +#endif /* _MSC_VER */ diff --git a/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort_private.h b/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort_private.h new file mode 100644 index 00000000..b4d97ad4 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/include/divsufsort_private.h @@ -0,0 +1,205 @@ +/* + * divsufsort_private.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_PRIVATE_H +#define _DIVSUFSORT_PRIVATE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "divsufsort_config.h" +#include +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if defined(BUILD_DIVSUFSORT64) +# include "divsufsort64.h" +# ifndef SAIDX_T +# define SAIDX_T +# define saidx_t saidx64_t +# endif /* SAIDX_T */ +# ifndef PRIdSAIDX_T +# define PRIdSAIDX_T PRIdSAIDX64_T +# endif /* PRIdSAIDX_T */ +# define divsufsort divsufsort64 +# define divbwt divbwt64 +# define divsufsort_version divsufsort64_version +# define bw_transform bw_transform64 +# define inverse_bw_transform inverse_bw_transform64 +# define sufcheck sufcheck64 +# define sa_search sa_search64 +# define sa_simplesearch sa_simplesearch64 +# define sssort sssort64 +# define trsort trsort64 +#else +# include "divsufsort.h" +#endif + + +/*- Constants -*/ +#if !defined(UINT8_MAX) +# define UINT8_MAX (255) +#endif /* UINT8_MAX */ +#if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1) +# undef ALPHABET_SIZE +#endif +#if !defined(ALPHABET_SIZE) +# define ALPHABET_SIZE (UINT8_MAX + 1) +#endif +/* for divsufsort.c */ +#define BUCKET_A_SIZE (ALPHABET_SIZE) +#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) +/* for sssort.c */ +#if defined(SS_INSERTIONSORT_THRESHOLD) +# if SS_INSERTIONSORT_THRESHOLD < 1 +# undef SS_INSERTIONSORT_THRESHOLD +# define SS_INSERTIONSORT_THRESHOLD (1) +# endif +#else +# define SS_INSERTIONSORT_THRESHOLD (8) +#endif +#if defined(SS_BLOCKSIZE) +# if SS_BLOCKSIZE < 0 +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (0) +# elif 32768 <= SS_BLOCKSIZE +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (32767) +# endif +#else +# define SS_BLOCKSIZE (1024) +#endif +/* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) +# define SS_MISORT_STACKSIZE (96) +# else +# define SS_MISORT_STACKSIZE (64) +# endif +#elif SS_BLOCKSIZE <= 4096 +# define SS_MISORT_STACKSIZE (16) +#else +# define SS_MISORT_STACKSIZE (24) +#endif +#if defined(BUILD_DIVSUFSORT64) +# define SS_SMERGE_STACKSIZE (64) +#else +# define SS_SMERGE_STACKSIZE (32) +#endif +/* for trsort.c */ +#define TR_INSERTIONSORT_THRESHOLD (8) +#if defined(BUILD_DIVSUFSORT64) +# define TR_STACKSIZE (96) +#else +# define TR_STACKSIZE (64) +#endif + + +/*- Macros -*/ +#ifndef SWAP +# define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0) +#endif /* SWAP */ +#ifndef MIN +# define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif /* MIN */ +#ifndef MAX +# define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif /* MAX */ +#define STACK_PUSH(_a, _b, _c, _d)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize++].d = (_d);\ + } while(0) +#define STACK_PUSH5(_a, _b, _c, _d, _e)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ + } while(0) +#define STACK_POP(_a, _b, _c, _d)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ + } while(0) +#define STACK_POP5(_a, _b, _c, _d, _e)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ + } while(0) +/* for divsufsort.c */ +#define BUCKET_A(_c0) bucket_A[(_c0)] +#if ALPHABET_SIZE == 256 +#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) +#else +#define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) +#endif + + +/*- Private Prototypes -*/ +/* sssort.c */ +void +sssort(const sauchar_t *Td, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix); +/* trsort.c */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_PRIVATE_H */ diff --git a/Tools/unix/lzsa/src/libdivsufsort/include/lfs.h.cmake b/Tools/unix/lzsa/src/libdivsufsort/include/lfs.h.cmake new file mode 100644 index 00000000..d5b84a84 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/include/lfs.h.cmake @@ -0,0 +1,56 @@ +/* + * lfs.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LFS_H +#define _LFS_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef __STRICT_ANSI__ +# define LFS_OFF_T @LFS_OFF_T@ +# define LFS_FOPEN @LFS_FOPEN@ +# define LFS_FTELL @LFS_FTELL@ +# define LFS_FSEEK @LFS_FSEEK@ +# define LFS_PRId @LFS_PRID@ +#else +# define LFS_OFF_T long +# define LFS_FOPEN fopen +# define LFS_FTELL ftell +# define LFS_FSEEK fseek +# define LFS_PRId "ld" +#endif +#ifndef PRIdOFF_T +# define PRIdOFF_T LFS_PRId +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _LFS_H */ diff --git a/Tools/unix/lzsa/src/libdivsufsort/lib/CMakeLists.txt b/Tools/unix/lzsa/src/libdivsufsort/lib/CMakeLists.txt new file mode 100644 index 00000000..abc90e61 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/lib/CMakeLists.txt @@ -0,0 +1,31 @@ +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_BINARY_DIR}/../include") + +set(divsufsort_SRCS divsufsort.c sssort.c trsort.c utils.c) + +## libdivsufsort ## +add_library(divsufsort ${divsufsort_SRCS}) +install(TARGETS divsufsort + RUNTIME DESTINATION ${CMAKE_INSTALL_RUNTIMEDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +set_target_properties(divsufsort PROPERTIES + VERSION "${LIBRARY_VERSION}" + SOVERSION "${LIBRARY_SOVERSION}" + DEFINE_SYMBOL DIVSUFSORT_BUILD_DLL + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../examples") + +## libdivsufsort64 ## +if(BUILD_DIVSUFSORT64) + add_library(divsufsort64 ${divsufsort_SRCS}) + install(TARGETS divsufsort64 + RUNTIME DESTINATION ${CMAKE_INSTALL_RUNTIMEDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set_target_properties(divsufsort64 PROPERTIES + VERSION "${LIBRARY_VERSION}" + SOVERSION "${LIBRARY_SOVERSION}" + DEFINE_SYMBOL DIVSUFSORT_BUILD_DLL + COMPILE_FLAGS "-DBUILD_DIVSUFSORT64" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../examples") +endif(BUILD_DIVSUFSORT64) diff --git a/Tools/unix/lzsa/src/libdivsufsort/lib/divsufsort.c b/Tools/unix/lzsa/src/libdivsufsort/lib/divsufsort.c new file mode 100644 index 00000000..50631ace --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/lib/divsufsort.c @@ -0,0 +1,431 @@ +/* + * divsufsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" +#ifdef _OPENMP +# include +#endif + + +/*- Private Functions -*/ + +/* Sorts suffixes of type B*. */ +static +saidx_t +sort_typeBstar(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n) { + saidx_t *PAb, *ISAb, *buf; +#ifdef _OPENMP + saidx_t *curbuf; + saidx_t l; +#endif + saidx_t i, j, k, t, m, bufsize; + saint_t c0, c1; +#ifdef _OPENMP + saint_t d0, d1; + int tmp; +#endif + + /* Initialize bucket arrays. */ + for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } + for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } + + /* Count the number of occurrences of the first one or two characters of each + type A, B and B* suffix. Moreover, store the beginning position of all + type B* suffixes into the array SA. */ + for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { + /* type A suffix. */ + do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); + if(0 <= i) { + /* type B* suffix. */ + ++BUCKET_BSTAR(c0, c1); + SA[--m] = i; + /* type B suffix. */ + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { + ++BUCKET_B(c0, c1); + } + } + } + m = n - m; +/* +note: + A type B* suffix is lexicographically smaller than a type B suffix that + begins with the same first two characters. +*/ + + /* Calculate the index of start/end point of each bucket. */ + for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { + t = i + BUCKET_A(c0); + BUCKET_A(c0) = i + j; /* start point */ + i = t + BUCKET_B(c0, c0); + for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { + j += BUCKET_BSTAR(c0, c1); + BUCKET_BSTAR(c0, c1) = j; /* end point */ + i += BUCKET_B(c0, c1); + } + } + + if(0 < m) { + /* Sort the type B* suffixes by their first two characters. */ + PAb = SA + n - m; ISAb = SA + m; + for(i = m - 2; 0 <= i; --i) { + t = PAb[i], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = i; + } + t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = m - 1; + + /* Sort the type B* substrings using sssort. */ +#ifdef _OPENMP + tmp = omp_get_max_threads(); + buf = SA + m, bufsize = (n - (2 * m)) / tmp; + c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m; +#pragma omp parallel default(shared) private(curbuf, k, l, d0, d1, tmp) + { + tmp = omp_get_thread_num(); + curbuf = buf + tmp * bufsize; + k = 0; + for(;;) { + #pragma omp critical(sssort_lock) + { + if(0 < (l = j)) { + d0 = c0, d1 = c1; + do { + k = BUCKET_BSTAR(d0, d1); + if(--d1 <= d0) { + d1 = ALPHABET_SIZE - 1; + if(--d0 < 0) { break; } + } + } while(((l - k) <= 1) && (0 < (l = k))); + c0 = d0, c1 = d1, j = k; + } + } + if(l == 0) { break; } + sssort(T, PAb, SA + k, SA + l, + curbuf, bufsize, 2, n, *(SA + k) == (m - 1)); + } + } +#else + buf = SA + m, bufsize = n - (2 * m); + for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { + for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { + i = BUCKET_BSTAR(c0, c1); + if(1 < (j - i)) { + sssort(T, PAb, SA + i, SA + j, + buf, bufsize, 2, n, *(SA + i) == (m - 1)); + } + } + } +#endif + + /* Compute ranks of type B* substrings. */ + for(i = m - 1; 0 <= i; --i) { + if(0 <= SA[i]) { + j = i; + do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); + SA[i + 1] = i - j; + if(i <= 0) { break; } + } + j = i; + do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); + ISAb[SA[i]] = j; + } + + /* Construct the inverse suffix array of type B* suffixes using trsort. */ + trsort(ISAb, SA, m, 1); + + /* Set the sorted order of tyoe B* suffixes. */ + for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } + if(0 <= i) { + t = i; + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } + SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; + } + } + + /* Calculate the index of start/end point of each bucket. */ + BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ + for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { + i = BUCKET_A(c0 + 1) - 1; + for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { + t = i - BUCKET_B(c0, c1); + BUCKET_B(c0, c1) = i; /* end point */ + + /* Move all type B* suffixes to the correct position. */ + for(i = t, j = BUCKET_BSTAR(c0, c1); + j <= k; + --i, --k) { SA[i] = SA[k]; } + } + BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ + BUCKET_B(c0, c0) = i; /* end point */ + } + } + + return m; +} + +/* Constructs the suffix array by using the sorted order of type B* suffixes. */ +static +void +construct_SA(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + *j = ~s; + c0 = T[--s]; + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else { + assert(((s == 0) && (T[s] == c1)) || (s < 0)); + *j = ~s; + } + } + } + } + + /* Construct the suffix array by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + if((s == 0) || (T[s - 1] < c0)) { s = ~s; } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else { + assert(s < 0); + *i = ~s; + } + } +} + +#if 0 +/* Constructs the burrows-wheeler transformed string directly + by using the sorted order of type B* suffixes. */ +static +saidx_t +construct_BWT(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k, *orig; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + c0 = T[--s]; + *j = ~((saidx_t)c0); + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else if(s != 0) { + *j = ~s; +#ifndef NDEBUG + } else { + assert(T[s] == c1); +#endif + } + } + } + } + + /* Construct the BWTed string by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~((saidx_t)T[n - 2]) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n, orig = SA; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + *i = c0; + if((0 < s) && (T[s - 1] < c0)) { s = ~((saidx_t)T[s - 1]); } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else if(s != 0) { + *i = ~s; + } else { + orig = i; + } + } + + return orig - SA; +} +#endif + +/*---------------------------------------------------------------------------*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx) { + ctx->bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + ctx->bucket_B = NULL; + + if (ctx->bucket_A) { + ctx->bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + if (ctx->bucket_B) + return 0; + } + + divsufsort_destroy(ctx); + return -1; +} + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx) { + if (ctx->bucket_B) { + free(ctx->bucket_B); + ctx->bucket_B = NULL; + } + + if (ctx->bucket_A) { + free(ctx->bucket_A); + ctx->bucket_A = NULL; + } +} + +/*- Function -*/ + +saint_t +divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n) { + saidx_t m; + saint_t err = 0; + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } + else if(n == 0) { return 0; } + else if(n == 1) { SA[0] = 0; return 0; } + else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; } + + /* Suffixsort. */ + if((ctx->bucket_A != NULL) && (ctx->bucket_B != NULL)) { + m = sort_typeBstar(T, SA, ctx->bucket_A, ctx->bucket_B, n); + construct_SA(T, SA, ctx->bucket_A, ctx->bucket_B, n, m); + } else { + err = -2; + } + + return err; +} + +#if 0 +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n) { + saidx_t *B; + saidx_t *bucket_A, *bucket_B; + saidx_t m, pidx, i; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0)) { return -1; } + else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } + + if((B = A) == NULL) { B = (saidx_t *)malloc((size_t)(n + 1) * sizeof(saidx_t)); } + bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + /* Burrows-Wheeler Transform. */ + if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) { + m = sort_typeBstar(T, B, bucket_A, bucket_B, n); + pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m); + + /* Copy to output string. */ + U[0] = T[n - 1]; + for(i = 0; i < pidx; ++i) { U[i + 1] = (sauchar_t)B[i]; } + for(i += 1; i < n; ++i) { U[i] = (sauchar_t)B[i]; } + pidx += 1; + } else { + pidx = -2; + } + + free(bucket_B); + free(bucket_A); + if(A == NULL) { free(B); } + + return pidx; +} + +const char * +divsufsort_version(void) { + return PROJECT_VERSION_FULL; +} +#endif diff --git a/Tools/unix/lzsa/src/libdivsufsort/lib/divsufsort_utils.c b/Tools/unix/lzsa/src/libdivsufsort/lib/divsufsort_utils.c new file mode 100644 index 00000000..f7cbc0d5 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/lib/divsufsort_utils.c @@ -0,0 +1,383 @@ +/* + * utils.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Function -*/ + +#if 0 +/* Binary search for inverse bwt. */ +static +saidx_t +binarysearch_lower(const saidx_t *A, saidx_t size, saidx_t value) { + saidx_t half, i; + for(i = 0, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + if(A[i + half] < value) { + i += half + 1; + half -= (size & 1) ^ 1; + } + } + return i; +} + + +/*- Functions -*/ + +/* Burrows-Wheeler transform. */ +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *SA, + saidx_t n, saidx_t *idx) { + saidx_t *A, i, j, p, t; + saint_t c; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx == NULL)) { return -1; } + if(n <= 1) { + if(n == 1) { U[0] = T[0]; } + *idx = n; + return 0; + } + + if((A = SA) == NULL) { + i = divbwt(T, U, NULL, n); + if(0 <= i) { *idx = i; i = 0; } + return (saint_t)i; + } + + /* BW transform. */ + if(T == U) { + t = n; + for(i = 0, j = 0; i < n; ++i) { + p = t - 1; + t = A[i]; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + j++; + } else { + *idx = i; + } + } + p = t - 1; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + } else { + *idx = i; + } + } else { + U[0] = T[n - 1]; + for(i = 0; A[i] != 0; ++i) { U[i + 1] = T[A[i] - 1]; } + *idx = i + 1; + for(++i; i < n; ++i) { U[i] = T[A[i] - 1]; } + } + + if(SA == NULL) { + /* Deallocate memory. */ + free(A); + } + + return 0; +} + +/* Inverse Burrows-Wheeler transform. */ +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *A, + saidx_t n, saidx_t idx) { + saidx_t C[ALPHABET_SIZE]; + sauchar_t D[ALPHABET_SIZE]; + saidx_t *B; + saidx_t i, p; + saint_t c, d; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx < 0) || + (n < idx) || ((0 < n) && (idx == 0))) { + return -1; + } + if(n <= 1) { return 0; } + + if((B = A) == NULL) { + /* Allocate n*sizeof(saidx_t) bytes of memory. */ + if((B = (saidx_t *)malloc((size_t)n * sizeof(saidx_t))) == NULL) { return -2; } + } + + /* Inverse BW transform. */ + for(c = 0; c < ALPHABET_SIZE; ++c) { C[c] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(c = 0, d = 0, i = 0; c < ALPHABET_SIZE; ++c) { + p = C[c]; + if(0 < p) { + C[c] = i; + D[d++] = (sauchar_t)c; + i += p; + } + } + for(i = 0; i < idx; ++i) { B[C[T[i]]++] = i; } + for( ; i < n; ++i) { B[C[T[i]]++] = i + 1; } + for(c = 0; c < d; ++c) { C[c] = C[D[c]]; } + for(i = 0, p = idx; i < n; ++i) { + U[i] = D[binarysearch_lower(C, d, p)]; + p = B[p - 1]; + } + + if(A == NULL) { + /* Deallocate memory. */ + free(B); + } + + return 0; +} + +/* Checks the suffix array SA of the string T. */ +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, + saidx_t n, saint_t verbose) { + saidx_t C[ALPHABET_SIZE]; + saidx_t i, p, q, t; + saint_t c; + + if(verbose) { fprintf(stderr, "sufcheck: "); } + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { + if(verbose) { fprintf(stderr, "Invalid arguments.\n"); } + return -1; + } + if(n == 0) { + if(verbose) { fprintf(stderr, "Done.\n"); } + return 0; + } + + /* check range: [0..n-1] */ + for(i = 0; i < n; ++i) { + if((SA[i] < 0) || (n <= SA[i])) { + if(verbose) { + fprintf(stderr, "Out of the range [0,%" PRIdSAIDX_T "].\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + n - 1, i, SA[i]); + } + return -2; + } + } + + /* check first characters. */ + for(i = 1; i < n; ++i) { + if(T[SA[i - 1]] > T[SA[i]]) { + if(verbose) { + fprintf(stderr, "Suffixes in wrong order.\n" + " T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d" + " > T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d\n", + i - 1, SA[i - 1], T[SA[i - 1]], i, SA[i], T[SA[i]]); + } + return -3; + } + } + + /* check suffixes. */ + for(i = 0; i < ALPHABET_SIZE; ++i) { C[i] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(i = 0, p = 0; i < ALPHABET_SIZE; ++i) { + t = C[i]; + C[i] = p; + p += t; + } + + q = C[T[n - 1]]; + C[T[n - 1]] += 1; + for(i = 0; i < n; ++i) { + p = SA[i]; + if(0 < p) { + c = T[--p]; + t = C[c]; + } else { + c = T[p = n - 1]; + t = q; + } + if((t < 0) || (p != SA[t])) { + if(verbose) { + fprintf(stderr, "Suffix in wrong position.\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T " or\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + t, (0 <= t) ? SA[t] : -1, i, SA[i]); + } + return -4; + } + if(t != q) { + ++C[c]; + if((n <= C[c]) || (T[SA[C[c]]] != c)) { C[c] = -1; } + } + } + + if(1 <= verbose) { fprintf(stderr, "Done.\n"); } + return 0; +} + + +static +int +_compare(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + saidx_t suf, saidx_t *match) { + saidx_t i, j; + saint_t r; + for(i = suf + *match, j = *match, r = 0; + (i < Tsize) && (j < Psize) && ((r = T[i] - P[j]) == 0); ++i, ++j) { } + *match = j; + return (r == 0) ? -(j != Psize) : r; +} + +/* Search for the pattern P in the string T. */ +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t match, lmatch, rmatch; + saidx_t llmatch, lrmatch, rlmatch, rrmatch; + saidx_t i, j, k; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (P == NULL) || (SA == NULL) || + (Tsize < 0) || (Psize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + if(Psize == 0) { if(idx != NULL) { *idx = 0; } return SAsize; } + + for(i = j = k = 0, lmatch = rmatch = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + match = MIN(lmatch, rmatch); + r = _compare(T, Tsize, P, Psize, SA[i + half], &match); + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + lmatch = match; + } else if(r > 0) { + rmatch = match; + } else { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(llmatch = lmatch, lrmatch = match, half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + lmatch = MIN(llmatch, lrmatch); + r = _compare(T, Tsize, P, Psize, SA[j + half], &lmatch); + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + llmatch = lmatch; + } else { + lrmatch = lmatch; + } + } + + /* right part */ + for(rlmatch = match, rrmatch = rmatch, half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + rmatch = MIN(rlmatch, rrmatch); + r = _compare(T, Tsize, P, Psize, SA[k + half], &rmatch); + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + rlmatch = rmatch; + } else { + rrmatch = rmatch; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} + +/* Search for the character c in the string T. */ +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t i, j, k, p; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (SA == NULL) || (Tsize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + + for(i = j = k = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + p = SA[i + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + } else if(r == 0) { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + p = SA[j + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + } + } + + /* right part */ + for(half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + p = SA[k + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} +#endif diff --git a/Tools/unix/lzsa/src/libdivsufsort/lib/sssort.c b/Tools/unix/lzsa/src/libdivsufsort/lib/sssort.c new file mode 100644 index 00000000..4a18fd2a --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/lib/sssort.c @@ -0,0 +1,815 @@ +/* + * sssort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +saint_t +ss_ilg(saidx_t n) { +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +# else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +# endif +#elif SS_BLOCKSIZE < 256 + return lg_table[n]; +#else + return (n & 0xff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]; +#endif +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + +#if SS_BLOCKSIZE != 0 + +static const saint_t sqq_table[256] = { + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, + 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, + 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, +110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, +128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, +143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, +156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, +169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, +181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, +192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, +202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, +212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, +221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, +230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, +239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, +247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 +}; + +static INLINE +saidx_t +ss_isqrt(saidx_t x) { + saidx_t y, e; + + if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } + e = (x & 0xffff0000) ? + ((x & 0xff000000) ? + 24 + lg_table[(x >> 24) & 0xff] : + 16 + lg_table[(x >> 16) & 0xff]) : + ((x & 0x0000ff00) ? + 8 + lg_table[(x >> 8) & 0xff] : + 0 + lg_table[(x >> 0) & 0xff]); + + if(e >= 16) { + y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); + if(e >= 24) { y = (y + 1 + x / y) >> 1; } + y = (y + 1 + x / y) >> 1; + } else if(e >= 8) { + y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; + } else { + return sqq_table[x] >> 4; + } + + return (x < (y * y)) ? y - 1 : y; +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/* Compares two suffixes. */ +static INLINE +saint_t +ss_compare(const sauchar_t *T, + const saidx_t *p1, const saidx_t *p2, + saidx_t depth) { + const sauchar_t *U1, *U2, *U1n, *U2n; + + for(U1 = T + depth + *p1, + U2 = T + depth + *p2, + U1n = T + *(p1 + 1) + 2, + U2n = T + *(p2 + 1) + 2; + (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); + ++U1, ++U2) { + } + + return U1 < U1n ? + (U2 < U2n ? *U1 - *U2 : 1) : + (U2 < U2n ? -1 : 0); +} + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) + +/* Insertionsort for small size groups */ +static +void +ss_insertionsort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *i, *j; + saidx_t t; + saint_t r; + + for(i = last - 2; first <= i; --i) { + for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { + do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); + if(last <= j) { break; } + } + if(r == 0) { *j = ~*j; } + *(j - 1) = t; + } +} + +#endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +void +ss_fixdown(const sauchar_t *Td, const saidx_t *PA, + saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saint_t c, d, e; + + for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = Td[PA[SA[k = j++]]]; + if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +ss_heapsort(const sauchar_t *Td, const saidx_t *PA, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + ss_fixdown(Td, PA, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +ss_median3(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } + if(Td[PA[*v2]] > Td[PA[*v3]]) { + if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +ss_median5(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } + if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } + if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } + if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } + if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } + if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +ss_pivot(const sauchar_t *Td, const saidx_t *PA, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return ss_median3(Td, PA, first, middle, last - 1); + } else { + t >>= 2; + return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = ss_median3(Td, PA, first, first + t, first + (t << 1)); + middle = ss_median3(Td, PA, middle - t, middle, middle + t); + last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); + return ss_median3(Td, PA, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +/* Binary partition for substrings. */ +static INLINE +saidx_t * +ss_partition(const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *a, *b; + saidx_t t; + for(a = first - 1, b = last;;) { + for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } + for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } + if(b <= a) { break; } + t = ~*b; + *b = *a; + *a = t; + } + if(first < a) { *first = ~*first; } + return a; +} + +/* Multikey introsort for medium size groups. */ +static +void +ss_mintrosort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t depth) { +#define STACK_SIZE SS_MISORT_STACKSIZE + struct { saidx_t *a, *b, c; saint_t d; } stack[STACK_SIZE]; + const sauchar_t *Td; + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t s, t; + saint_t ssize; + saint_t limit; + saint_t v, x = 0; + + for(ssize = 0, limit = ss_ilg(last - first);;) { + + if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { +#if 1 < SS_INSERTIONSORT_THRESHOLD + if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } +#endif + STACK_POP(first, last, depth, limit); + continue; + } + + Td = T + depth; + if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } + if(limit < 0) { + for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { + if((x = Td[PA[*a]]) != v) { + if(1 < (a - first)) { break; } + v = x; + first = a; + } + } + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, a, depth); + } + if((a - first) <= (last - a)) { + if(1 < (a - first)) { + STACK_PUSH(a, last, depth, -1); + last = a, depth += 1, limit = ss_ilg(a - first); + } else { + first = a, limit = -1; + } + } else { + if(1 < (last - a)) { + STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); + first = a, limit = -1; + } else { + last = a, depth += 1, limit = ss_ilg(a - first); + } + } + continue; + } + + /* choose pivot */ + a = ss_pivot(Td, PA, first, last); + v = Td[PA[*a]]; + SWAP(*first, *a); + + /* partition */ + for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + + a = first + (b - a), c = last - (d - c); + b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); + + if((a - first) <= (last - c)) { + if((last - c) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(c, last, depth, limit); + last = a; + } else if((a - first) <= (c - b)) { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + last = a; + } else { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(first, a, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } else { + if((a - first) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(first, a, depth, limit); + first = c; + } else if((last - c) <= (c - b)) { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + first = c; + } else { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(c, last, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } + } else { + limit += 1; + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, last, depth); + limit = ss_ilg(last - first); + } + depth += 1; + } + } +#undef STACK_SIZE +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + + +/*---------------------------------------------------------------------------*/ + +#if SS_BLOCKSIZE != 0 + +static INLINE +void +ss_blockswap(saidx_t *a, saidx_t *b, saidx_t n) { + saidx_t t; + for(; 0 < n; --n, ++a, ++b) { + t = *a, *a = *b, *b = t; + } +} + +static INLINE +void +ss_rotate(saidx_t *first, saidx_t *middle, saidx_t *last) { + saidx_t *a, *b, t; + saidx_t l, r; + l = middle - first, r = last - middle; + for(; (0 < l) && (0 < r);) { + if(l == r) { ss_blockswap(first, middle, l); break; } + if(l < r) { + a = last - 1, b = middle - 1; + t = *a; + do { + *a-- = *b, *b-- = *a; + if(b < first) { + *a = t; + last = a; + if((r -= l + 1) <= l) { break; } + a -= 1, b = middle - 1; + t = *a; + } + } while(1); + } else { + a = first, b = middle; + t = *a; + do { + *a++ = *b, *b++ = *a; + if(last <= b) { + *a = t; + first = a + 1; + if((l -= r + 1) <= r) { break; } + a += 1, b = middle; + t = *a; + } + } while(1); + } + } +} + + +/*---------------------------------------------------------------------------*/ + +static +void +ss_inplacemerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t depth) { + const saidx_t *p; + saidx_t *a, *b; + saidx_t len, half; + saint_t q, r; + saint_t x; + + for(;;) { + if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } + else { x = 0; p = PA + *(last - 1); } + for(a = first, len = middle - first, half = len >> 1, r = -1; + 0 < len; + len = half, half >>= 1) { + b = a + half; + q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); + if(q < 0) { + a = b + 1; + half -= (len & 1) ^ 1; + } else { + r = q; + } + } + if(a < middle) { + if(r == 0) { *a = ~*a; } + ss_rotate(a, middle, last); + last -= middle - a; + middle = a; + if(first == middle) { break; } + } + --last; + if(x != 0) { while(*--last < 0) { } } + if(middle == last) { break; } + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Merge-forward with internal buffer. */ +static +void +ss_mergeforward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + + bufend = buf + (middle - first) - 1; + ss_blockswap(buf, first, middle - first); + + for(t = *(a = first), b = buf, c = middle;;) { + r = ss_compare(T, PA + *b, PA + *c, depth); + if(r < 0) { + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + } else if(r > 0) { + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } else { + *c = ~*c; + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } + } +} + +/* Merge-backward with internal buffer. */ +static +void +ss_mergebackward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + const saidx_t *p1, *p2; + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + saint_t x; + + bufend = buf + (last - middle) - 1; + ss_blockswap(buf, middle, last - middle); + + x = 0; + if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } + else { p1 = PA + *bufend; } + if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } + else { p2 = PA + *(middle - 1); } + for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { + r = ss_compare(T, p1, p2, depth); + if(0 < r) { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = *b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + } else if(r < 0) { + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } else { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = ~*b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } + } +} + +/* D&C based merge. */ +static +void +ss_swapmerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t bufsize, saidx_t depth) { +#define STACK_SIZE SS_SMERGE_STACKSIZE +#define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) +#define MERGE_CHECK(a, b, c)\ + do {\ + if(((c) & 1) ||\ + (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ + *(a) = ~*(a);\ + }\ + if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ + *(b) = ~*(b);\ + }\ + } while(0) + struct { saidx_t *a, *b, *c; saint_t d; } stack[STACK_SIZE]; + saidx_t *l, *r, *lm, *rm; + saidx_t m, len, half; + saint_t ssize; + saint_t check, next; + + for(check = 0, ssize = 0;;) { + if((last - middle) <= bufsize) { + if((first < middle) && (middle < last)) { + ss_mergebackward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + if((middle - first) <= bufsize) { + if(first < middle) { + ss_mergeforward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; + 0 < len; + len = half, half >>= 1) { + if(ss_compare(T, PA + GETIDX(*(middle + m + half)), + PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { + m += half + 1; + half -= (len & 1) ^ 1; + } + } + + if(0 < m) { + lm = middle - m, rm = middle + m; + ss_blockswap(lm, middle, m); + l = r = middle, next = 0; + if(rm < last) { + if(*rm < 0) { + *rm = ~*rm; + if(first < lm) { for(; *--l < 0;) { } next |= 4; } + next |= 1; + } else if(first < lm) { + for(; *r < 0; ++r) { } + next |= 2; + } + } + + if((l - first) <= (last - r)) { + STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); + middle = lm, last = l, check = (check & 3) | (next & 4); + } else { + if((next & 2) && (r == middle)) { next ^= 6; } + STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); + first = r, middle = rm, check = (next & 3) | (check & 4); + } + } else { + if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { + *middle = ~*middle; + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + } + } +#undef STACK_SIZE +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Substring sort */ +void +sssort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix) { + saidx_t *a; +#if SS_BLOCKSIZE != 0 + saidx_t *b, *middle, *curbuf; + saidx_t j, k, curbufsize, limit; +#endif + saidx_t i; + + if(lastsuffix != 0) { ++first; } + +#if SS_BLOCKSIZE == 0 + ss_mintrosort(T, PA, first, last, depth); +#else + if((bufsize < SS_BLOCKSIZE) && + (bufsize < (last - first)) && + (bufsize < (limit = ss_isqrt(last - first)))) { + if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } + buf = middle = last - limit, bufsize = limit; + } else { + middle = last, limit = 0; + } + for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); +#endif + curbufsize = last - (a + SS_BLOCKSIZE); + curbuf = a + SS_BLOCKSIZE; + if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } + for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { + ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); + } + } +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, middle, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, middle, depth); +#endif + for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { + if(i & 1) { + ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); + a -= k; + } + } + if(limit != 0) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, middle, last, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, middle, last, depth); +#endif + ss_inplacemerge(T, PA, first, middle, last, depth); + } +#endif + + if(lastsuffix != 0) { + /* Insert last type B* suffix. */ + saidx_t PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; + for(a = first, i = *(first - 1); + (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); + ++a) { + *(a - 1) = *a; + } + *(a - 1) = i; + } +} diff --git a/Tools/unix/lzsa/src/libdivsufsort/lib/trsort.c b/Tools/unix/lzsa/src/libdivsufsort/lib/trsort.c new file mode 100644 index 00000000..6fe3e67b --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/lib/trsort.c @@ -0,0 +1,586 @@ +/* + * trsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +static INLINE +saint_t +tr_ilg(saidx_t n) { +#if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +#else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +#endif +} + + +/*---------------------------------------------------------------------------*/ + +/* Simple insertionsort for small size groups. */ +static +void +tr_insertionsort(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *a, *b; + saidx_t t, r; + + for(a = first + 1; a < last; ++a) { + for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { + do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); + if(b < first) { break; } + } + if(r == 0) { *b = ~*b; } + *(b + 1) = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_fixdown(const saidx_t *ISAd, saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saidx_t c, d, e; + + for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = ISAd[SA[k = j++]]; + if(d < (e = ISAd[SA[j]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +tr_heapsort(const saidx_t *ISAd, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + tr_fixdown(ISAd, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +tr_median3(const saidx_t *ISAd, saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } + if(ISAd[*v2] > ISAd[*v3]) { + if(ISAd[*v1] > ISAd[*v3]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +tr_median5(const saidx_t *ISAd, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } + if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } + if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } + if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } + if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } + if(ISAd[*v3] > ISAd[*v4]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +tr_pivot(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return tr_median3(ISAd, first, middle, last - 1); + } else { + t >>= 2; + return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = tr_median3(ISAd, first, first + t, first + (t << 1)); + middle = tr_median3(ISAd, middle - t, middle, middle + t); + last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); + return tr_median3(ISAd, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +typedef struct _trbudget_t trbudget_t; +struct _trbudget_t { + saidx_t chance; + saidx_t remain; + saidx_t incval; + saidx_t count; +}; + +static INLINE +void +trbudget_init(trbudget_t *budget, saidx_t chance, saidx_t incval) { + budget->chance = chance; + budget->remain = budget->incval = incval; +} + +static INLINE +saint_t +trbudget_check(trbudget_t *budget, saidx_t size) { + if(size <= budget->remain) { budget->remain -= size; return 1; } + if(budget->chance == 0) { budget->count += size; return 0; } + budget->remain += budget->incval - size; + budget->chance -= 1; + return 1; +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_partition(const saidx_t *ISAd, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t **pa, saidx_t **pb, saidx_t v) { + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t t, s; + saidx_t x = 0; + + for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + first += (b - a), last -= (d - c); + } + *pa = first, *pb = last; +} + +static +void +tr_copy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + /* sort suffixes of middle partition + by using sorted order of suffixes of left and right partition. */ + saidx_t *c, *d, *e; + saidx_t s, v; + + v = b - SA - 1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + ISA[s] = d - SA; + } + } + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + ISA[s] = d - SA; + } + } +} + +static +void +tr_partialcopy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + saidx_t *c, *d, *e; + saidx_t s, v; + saidx_t rank, lastrank, newrank = -1; + + v = b - SA - 1; + lastrank = -1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } + + lastrank = -1; + for(e = d; first <= e; --e) { + rank = ISA[*e]; + if(lastrank != rank) { lastrank = rank; newrank = e - SA; } + if(newrank != rank) { ISA[*e] = newrank; } + } + + lastrank = -1; + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } +} + +static +void +tr_introsort(saidx_t *ISA, const saidx_t *ISAd, + saidx_t *SA, saidx_t *first, saidx_t *last, + trbudget_t *budget) { +#define STACK_SIZE TR_STACKSIZE + struct { const saidx_t *a; saidx_t *b, *c; saint_t d, e; }stack[STACK_SIZE]; + saidx_t *a, *b, *c; + saidx_t t; + saidx_t v, x = 0; + saidx_t incr = ISAd - ISA; + saint_t limit, next; + saint_t ssize, trlink = -1; + + for(ssize = 0, limit = tr_ilg(last - first);;) { + + if(limit < 0) { + if(limit == -1) { + /* tandem repeat partition */ + tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); + + /* update ranks */ + if(a < last) { + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + } + if(b < last) { + for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } + } + + /* push */ + if(1 < (b - a)) { + STACK_PUSH5(NULL, a, b, 0, 0); + STACK_PUSH5(ISAd - incr, first, last, -2, trlink); + trlink = ssize - 2; + } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); + last = a, limit = tr_ilg(a - first); + } else if(1 < (last - b)) { + first = b, limit = tr_ilg(last - b); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); + first = b, limit = tr_ilg(last - b); + } else if(1 < (a - first)) { + last = a, limit = tr_ilg(a - first); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else if(limit == -2) { + /* tandem repeat copy */ + a = stack[--ssize].b, b = stack[ssize].c; + if(stack[ssize].d == 0) { + tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); + } + STACK_POP5(ISAd, first, last, limit, trlink); + } else { + /* sorted partition */ + if(0 <= *first) { + a = first; + do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); + first = a; + } + if(first < last) { + a = first; do { *a = ~*a; } while(*++a < 0); + next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; + if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } + + /* push */ + if(trbudget_check(budget, a - first)) { + if((a - first) <= (last - a)) { + STACK_PUSH5(ISAd, a, last, -3, trlink); + ISAd += incr, last = a, limit = next; + } else { + if(1 < (last - a)) { + STACK_PUSH5(ISAd + incr, first, a, next, trlink); + first = a, limit = -3; + } else { + ISAd += incr, last = a, limit = next; + } + } + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + if(1 < (last - a)) { + first = a, limit = -3; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + continue; + } + + if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { + tr_insertionsort(ISAd, first, last); + limit = -3; + continue; + } + + if(limit-- == 0) { + tr_heapsort(ISAd, first, last - first); + for(a = last - 1; first < a; a = b) { + for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } + } + limit = -3; + continue; + } + + /* choose pivot */ + a = tr_pivot(ISAd, first, last); + SWAP(*first, *a); + v = ISAd[*first]; + + /* partition */ + tr_partition(ISAd, first, first + 1, last, &a, &b, v); + if((last - first) != (b - a)) { + next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; + + /* update ranks */ + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } + + /* push */ + if((1 < (b - a)) && (trbudget_check(budget, b - a))) { + if((a - first) <= (last - b)) { + if((last - b) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((a - first) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + if((a - first) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((last - b) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } + } else { + if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + first = b; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + last = a; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } + } else { + if(trbudget_check(budget, last - first)) { + limit = tr_ilg(last - first), ISAd += incr; + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } +#undef STACK_SIZE +} + + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Tandem repeat sort */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth) { + saidx_t *ISAd; + saidx_t *first, *last; + trbudget_t budget; + saidx_t t, skip, unsorted; + + trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); +/* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ + for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { + first = SA; + skip = 0; + unsorted = 0; + do { + if((t = *first) < 0) { first -= t; skip += t; } + else { + if(skip != 0) { *(first + skip) = skip; skip = 0; } + last = SA + ISA[t] + 1; + if(1 < (last - first)) { + budget.count = 0; + tr_introsort(ISA, ISAd, SA, first, last, &budget); + if(budget.count != 0) { unsorted += budget.count; } + else { skip = first - last; } + } else if((last - first) == 1) { + skip = -1; + } + first = last; + } + } while(first < (SA + n)); + if(skip != 0) { *(first + skip) = skip; } + if(unsorted == 0) { break; } + } +} diff --git a/Tools/unix/lzsa/src/libdivsufsort/pkgconfig/CMakeLists.txt b/Tools/unix/lzsa/src/libdivsufsort/pkgconfig/CMakeLists.txt new file mode 100644 index 00000000..ee7063c9 --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/pkgconfig/CMakeLists.txt @@ -0,0 +1,9 @@ +## generate libdivsufsort.pc ## +set(W64BIT "") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libdivsufsort.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort.pc" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort.pc" DESTINATION ${CMAKE_INSTALL_PKGCONFIGDIR}) +if(BUILD_DIVSUFSORT64) + set(W64BIT "64") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libdivsufsort.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort64.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort64.pc" DESTINATION ${CMAKE_INSTALL_PKGCONFIGDIR}) +endif(BUILD_DIVSUFSORT64) diff --git a/Tools/unix/lzsa/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake b/Tools/unix/lzsa/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake new file mode 100644 index 00000000..6419d1ea --- /dev/null +++ b/Tools/unix/lzsa/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@CMAKE_INSTALL_LIBDIR@ +includedir=@CMAKE_INSTALL_INCLUDEDIR@ + +Name: @PROJECT_NAME@@W64BIT@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION_FULL@ +URL: @PROJECT_URL@ +Libs: -L${libdir} -ldivsufsort@W64BIT@ +Cflags: -I${includedir} diff --git a/Tools/unix/lzsa/src/lzsa.c b/Tools/unix/lzsa/src/lzsa.c new file mode 100644 index 00000000..4cce4041 --- /dev/null +++ b/Tools/unix/lzsa/src/lzsa.c @@ -0,0 +1,1110 @@ +/* + * lzsa.c - command line compression utility for the LZSA format + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif +#include "lib.h" + +#define OPT_VERBOSE 1 +#define OPT_RAW 2 +#define OPT_FAVOR_RATIO 4 +#define OPT_RAW_BACKWARD 8 +#define OPT_STATS 16 + +#define TOOL_VERSION "1.2.0" + +/*---------------------------------------------------------------------------*/ + +#ifdef _WIN32 +LARGE_INTEGER hpc_frequency; +BOOL hpc_available = FALSE; +#endif + +static void do_init_time() { +#ifdef _WIN32 + hpc_frequency.QuadPart = 0; + hpc_available = QueryPerformanceFrequency(&hpc_frequency); +#endif +} + +static long long do_get_time() { + long long nTime; + +#ifdef _WIN32 + if (hpc_available) { + LARGE_INTEGER nCurTime; + + /* Use HPC hardware for best precision */ + QueryPerformanceCounter(&nCurTime); + nTime = (long long)(nCurTime.QuadPart * 1000000LL / hpc_frequency.QuadPart); + } + else { + struct _timeb tb; + _ftime(&tb); + + nTime = ((long long)tb.time * 1000LL + (long long)tb.millitm) * 1000LL; + } +#else + struct timeval tm; + gettimeofday(&tm, NULL); + + nTime = (long long)tm.tv_sec * 1000000LL + (long long)tm.tv_usec; +#endif + return nTime; +} + +/*---------------------------------------------------------------------------*/ + +static void compression_progress(long long nOriginalSize, long long nCompressedSize) { + if (nOriginalSize >= 1024 * 1024) { + fprintf(stdout, "\r%lld => %lld (%g %%) \b\b\b\b\b", nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize)); + fflush(stdout); + } +} + +static int do_compress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const int nMinMatchSize, const int nFormatVersion) { + long long nStartTime = 0LL, nEndTime = 0LL; + long long nOriginalSize = 0LL, nCompressedSize = 0LL; + int nCommandCount = 0, nSafeDist = 0; + int nFlags; + lzsa_status_t nStatus; + lzsa_stats stats; + + nFlags = 0; + if (nOptions & OPT_FAVOR_RATIO) + nFlags |= LZSA_FLAG_FAVOR_RATIO; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nStatus = lzsa_compress_file(pszInFilename, pszOutFilename, pszDictionaryFilename, nFlags, nMinMatchSize, nFormatVersion, compression_progress, &nOriginalSize, &nCompressedSize, &nCommandCount, &nSafeDist, &stats); + + if ((nOptions & OPT_VERBOSE)) { + nEndTime = do_get_time(); + } + + switch (nStatus) { + case LZSA_ERROR_SRC: fprintf(stderr, "error reading '%s'\n", pszInFilename); break; + case LZSA_ERROR_DST: fprintf(stderr, "error writing '%s'\n", pszOutFilename); break; + case LZSA_ERROR_DICTIONARY: fprintf(stderr, "error reading dictionary '%s'\n", pszDictionaryFilename); break; + case LZSA_ERROR_MEMORY: fprintf(stderr, "out of memory\n"); break; + case LZSA_ERROR_COMPRESSION: fprintf(stderr, "internal compression error\n"); break; + case LZSA_ERROR_RAW_TOOLARGE: fprintf(stderr, "error: raw blocks can only be used with files <= 64 Kb\n"); break; + case LZSA_ERROR_RAW_UNCOMPRESSED: fprintf(stderr, "error: incompressible data needs to be <= 64 Kb in raw blocks\n"); break; + case LZSA_OK: break; + default: fprintf(stderr, "unknown compression error %d\n", nStatus); break; + } + + if (nStatus) + return 100; + + if ((nOptions & OPT_VERBOSE)) { + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "\rCompressed '%s' in %g seconds, %.02g Mb/s, %d tokens (%g bytes/token), %lld into %lld bytes ==> %g %%\n", + pszInFilename, fDelta, fSpeed, nCommandCount, (double)nOriginalSize / (double)nCommandCount, + nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize)); + if (nOptions & OPT_RAW) { + fprintf(stdout, "Safe distance: %d (0x%X)\n", nSafeDist, nSafeDist); + } + } + + if (nOptions & OPT_STATS) { + if (stats.literals_divisor > 0) + fprintf(stdout, "Literals: min: %d avg: %d max: %d count: %d\n", stats.min_literals, stats.total_literals / stats.literals_divisor, stats.max_literals, stats.literals_divisor); + else + fprintf(stdout, "Literals: none\n"); + if (stats.match_divisor > 0) { + fprintf(stdout, "Offsets: min: %d avg: %d max: %d reps: %d count: %d\n", stats.min_offset, stats.total_offsets / stats.match_divisor, stats.max_offset, stats.num_rep_offsets, stats.match_divisor); + fprintf(stdout, "Match lens: min: %d avg: %d max: %d count: %d\n", stats.min_match_len, stats.total_match_lens / stats.match_divisor, stats.max_match_len, stats.match_divisor); + } + else { + fprintf(stdout, "Offsets: none\n"); + fprintf(stdout, "Match lens: none\n"); + } + if (stats.rle1_divisor > 0) { + fprintf(stdout, "RLE1 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle1_len, stats.total_rle1_lens / stats.rle1_divisor, stats.max_rle1_len, stats.rle1_divisor); + } + else { + fprintf(stdout, "RLE1 lens: none\n"); + } + if (stats.rle2_divisor > 0) { + fprintf(stdout, "RLE2 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle2_len, stats.total_rle2_lens / stats.rle2_divisor, stats.max_rle2_len, stats.rle2_divisor); + } + else { + fprintf(stdout, "RLE2 lens: none\n"); + } + } + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_decompress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, int nFormatVersion) { + long long nStartTime = 0LL, nEndTime = 0LL; + long long nOriginalSize = 0LL, nCompressedSize = 0LL; + lzsa_status_t nStatus; + int nFlags; + + nFlags = 0; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nStatus = lzsa_decompress_file(pszInFilename, pszOutFilename, pszDictionaryFilename, nFlags, nFormatVersion, &nOriginalSize, &nCompressedSize); + + switch (nStatus) { + case LZSA_ERROR_SRC: fprintf(stderr, "error reading '%s'\n", pszInFilename); break; + case LZSA_ERROR_DST: fprintf(stderr, "error writing '%s'\n", pszOutFilename); break; + case LZSA_ERROR_DICTIONARY: fprintf(stderr, "error reading dictionary '%s'\n", pszDictionaryFilename); break; + case LZSA_ERROR_MEMORY: fprintf(stderr, "out of memory\n"); break; + case LZSA_ERROR_DECOMPRESSION: fprintf(stderr, "internal decompression error\n"); break; + case LZSA_ERROR_FORMAT: fprintf(stderr, "invalid magic number or format version in input file\n"); break; + case LZSA_OK: break; + default: fprintf(stderr, "unknown decompression error %d\n", nStatus); break; + } + + if (nStatus) { + fprintf(stderr, "decompression error for '%s'\n", pszInFilename); + return 100; + } + else { + if (nOptions & OPT_VERBOSE) { + nEndTime = do_get_time(); + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "Decompressed '%s' in %g seconds, %g Mb/s\n", + pszInFilename, fDelta, fSpeed); + } + + return 0; + } +} + +/*---------------------------------------------------------------------------*/ + +typedef struct { + FILE *f; + void *pCompareDataBuf; + size_t nCompareDataSize; +} compare_stream_t; + +void comparestream_close(lzsa_stream_t *stream) { + if (stream->obj) { + compare_stream_t *pCompareStream = (compare_stream_t *)stream->obj; + if (pCompareStream->pCompareDataBuf) { + free(pCompareStream->pCompareDataBuf); + pCompareStream->pCompareDataBuf = NULL; + } + + fclose(pCompareStream->f); + free(pCompareStream); + + stream->obj = NULL; + stream->read = NULL; + stream->write = NULL; + stream->eof = NULL; + stream->close = NULL; + } +} + +size_t comparestream_read(lzsa_stream_t *stream, void *ptr, size_t size) { + return 0; +} + +size_t comparestream_write(lzsa_stream_t *stream, void *ptr, size_t size) { + compare_stream_t *pCompareStream = (compare_stream_t *)stream->obj; + + if (!pCompareStream->pCompareDataBuf || pCompareStream->nCompareDataSize < size) { + pCompareStream->nCompareDataSize = size; + pCompareStream->pCompareDataBuf = realloc(pCompareStream->pCompareDataBuf, pCompareStream->nCompareDataSize); + if (!pCompareStream->pCompareDataBuf) + return 0; + } + + size_t nReadBytes = fread(pCompareStream->pCompareDataBuf, 1, size, pCompareStream->f); + if (nReadBytes != size) { + return 0; + } + + if (memcmp(ptr, pCompareStream->pCompareDataBuf, size)) { + return 0; + } + + return size; +} + +int comparestream_eof(lzsa_stream_t *stream) { + compare_stream_t *pCompareStream = (compare_stream_t *)stream->obj; + return feof(pCompareStream->f); +} + +int comparestream_open(lzsa_stream_t *stream, const char *pszCompareFilename, const char *pszMode) { + compare_stream_t *pCompareStream; + + pCompareStream = (compare_stream_t*)malloc(sizeof(compare_stream_t)); + if (!pCompareStream) + return -1; + + pCompareStream->pCompareDataBuf = NULL; + pCompareStream->nCompareDataSize = 0; + pCompareStream->f = (void*)fopen(pszCompareFilename, pszMode); + + if (pCompareStream->f) { + stream->obj = pCompareStream; + stream->read = comparestream_read; + stream->write = comparestream_write; + stream->eof = comparestream_eof; + stream->close = comparestream_close; + return 0; + } + else { + free(pCompareStream); + return -1; + } +} + +static int do_compare(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, int nFormatVersion) { + lzsa_stream_t inStream, compareStream; + long long nStartTime = 0LL, nEndTime = 0LL; + long long nOriginalSize = 0LL; + long long nCompressedSize = 0LL; + void *pDictionaryData = NULL; + int nDictionaryDataSize = 0; + lzsa_status_t nStatus; + int nFlags; + + if (lzsa_filestream_open(&inStream, pszInFilename, "rb") < 0) { + fprintf(stderr, "error opening compressed input file\n"); + return 100; + } + + if (comparestream_open(&compareStream, pszOutFilename, "rb") < 0) { + fprintf(stderr, "error opening original uncompressed file\n"); + inStream.close(&inStream); + return 100; + } + + nStatus = lzsa_dictionary_load(pszDictionaryFilename, &pDictionaryData, &nDictionaryDataSize); + if (nStatus) { + compareStream.close(&compareStream); + inStream.close(&inStream); + fprintf(stderr, "error reading dictionary '%s'\n", pszDictionaryFilename); + return 100; + } + + nFlags = 0; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nStatus = lzsa_decompress_stream(&inStream, &compareStream, pDictionaryData, nDictionaryDataSize, nFlags, nFormatVersion, &nOriginalSize, &nCompressedSize); + + switch (nStatus) { + case LZSA_ERROR_SRC: fprintf(stderr, "error reading '%s'\n", pszInFilename); break; + case LZSA_ERROR_DST: fprintf(stderr, "error comparing compressed file '%s' with original '%s'\n", pszInFilename, pszOutFilename); break; + case LZSA_ERROR_MEMORY: fprintf(stderr, "out of memory\n"); break; + case LZSA_ERROR_DECOMPRESSION: fprintf(stderr, "internal decompression error\n"); break; + case LZSA_ERROR_FORMAT: fprintf(stderr, "invalid magic number or format version in input file\n"); break; + case LZSA_OK: break; + default: fprintf(stderr, "unknown decompression error %d\n", nStatus); break; + } + + lzsa_dictionary_free(&pDictionaryData); + compareStream.close(&compareStream); + inStream.close(&inStream); + + if (nStatus) { + return 100; + } + else { + if (nOptions & OPT_VERBOSE) { + nEndTime = do_get_time(); + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "Compared '%s' in %g seconds, %g Mb/s\n", + pszInFilename, fDelta, fSpeed); + } + + return 0; + } +} + +/*---------------------------------------------------------------------------*/ + +static void generate_compressible_data(unsigned char *pBuffer, size_t nBufferSize, int nMinMatchSize, unsigned int nSeed, int nNumLiteralValues, float fMatchProbability) { + size_t nIndex = 0; + int nMatchProbability = (int)(fMatchProbability * 1023.0f); + + srand(nSeed); + + if (nIndex >= nBufferSize) return; + pBuffer[nIndex++] = rand() % nNumLiteralValues; + + while (nIndex < nBufferSize) { + if ((rand() & 1023) >= nMatchProbability) { + size_t nLiteralCount = rand() & 127; + if (nLiteralCount > (nBufferSize - nIndex)) + nLiteralCount = nBufferSize - nIndex; + + while (nLiteralCount--) + pBuffer[nIndex++] = rand() % nNumLiteralValues; + } + else { + size_t nMatchLength = nMinMatchSize + (rand() & 1023); + size_t nMatchOffset; + + if (nMatchLength > (nBufferSize - nIndex)) + nMatchLength = nBufferSize - nIndex; + if (nMatchLength > nIndex) + nMatchLength = nIndex; + + if (nMatchLength < nIndex) + nMatchOffset = rand() % (nIndex - nMatchLength); + else + nMatchOffset = 0; + + while (nMatchLength--) { + pBuffer[nIndex] = pBuffer[nIndex - nMatchOffset]; + nIndex++; + } + } + } +} + +static void xor_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, float fXorProbability) { + size_t nIndex = 0; + int nXorProbability = (int)(fXorProbability * 1023.0f); + + srand(nSeed); + + if (nIndex >= nBufferSize) return; + + while (nIndex < nBufferSize) { + if ((rand() & 1023) < nXorProbability) { + pBuffer[nIndex] ^= 0xff; + } + nIndex++; + } +} + +static int do_self_test(const unsigned int nOptions, const int nMinMatchSize, int nFormatVersion) { + unsigned char *pGeneratedData; + unsigned char *pCompressedData; + unsigned char *pTmpCompressedData; + unsigned char *pTmpDecompressedData; + size_t nGeneratedDataSize; + size_t nMaxCompressedDataSize; + unsigned int nSeed = 123; + int nFlags; + int i; + + nFlags = 0; + if (nOptions & OPT_FAVOR_RATIO) + nFlags |= LZSA_FLAG_FAVOR_RATIO; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + pGeneratedData = (unsigned char*)malloc(4 * BLOCK_SIZE); + if (!pGeneratedData) { + fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE); + return 100; + } + + nMaxCompressedDataSize = lzsa_get_max_compressed_size_inmem(4 * BLOCK_SIZE); + pCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize); + if (!pCompressedData) { + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %zd bytes needed\n", nMaxCompressedDataSize); + return 100; + } + + pTmpCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize); + if (!pTmpCompressedData) { + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %zd bytes needed\n", nMaxCompressedDataSize); + return 100; + } + + pTmpDecompressedData = (unsigned char*)malloc(4 * BLOCK_SIZE); + if (!pTmpDecompressedData) { + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE); + return 100; + } + + memset(pGeneratedData, 0, 4 * BLOCK_SIZE); + memset(pCompressedData, 0, nMaxCompressedDataSize); + memset(pTmpCompressedData, 0, nMaxCompressedDataSize); + + /* Test compressing with a too small buffer to do anything, expect to fail cleanly */ + for (i = 0; i < 12; i++) { + generate_compressible_data(pGeneratedData, i, nMinMatchSize, nSeed, 256, 0.5f); + lzsa_compress_inmem(pGeneratedData, pCompressedData, i, i, nFlags, nMinMatchSize, nFormatVersion); + } + + size_t nDataSizeStep = 128; + float fProbabilitySizeStep = 0.0005f; + + for (nGeneratedDataSize = 1024; nGeneratedDataSize <= ((size_t)((nOptions & OPT_RAW) ? BLOCK_SIZE : (4 * BLOCK_SIZE))); nGeneratedDataSize += nDataSizeStep) { + float fMatchProbability; + + fprintf(stdout, "size %zd", nGeneratedDataSize); + for (fMatchProbability = 0; fMatchProbability <= 0.995f; fMatchProbability += fProbabilitySizeStep) { + int nNumLiteralValues[12] = { 1, 2, 3, 15, 30, 56, 96, 137, 178, 191, 255, 256 }; + float fXorProbability; + + fputc('.', stdout); + fflush(stdout); + + for (i = 0; i < 12; i++) { + /* Generate data to compress */ + generate_compressible_data(pGeneratedData, nGeneratedDataSize, nMinMatchSize, nSeed, nNumLiteralValues[i], fMatchProbability); + + /* Try to compress it, expected to succeed */ + size_t nActualCompressedSize = lzsa_compress_inmem(pGeneratedData, pCompressedData, nGeneratedDataSize, lzsa_get_max_compressed_size_inmem(nGeneratedDataSize), + nFlags, nMinMatchSize, nFormatVersion); + if (nActualCompressedSize == -1 || (int)nActualCompressedSize < (lzsa_get_header_size() + lzsa_get_frame_size() + lzsa_get_frame_size() /* footer */)) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error compressing size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + /* Try to decompress it, expected to succeed */ + size_t nActualDecompressedSize; + int nDecFormatVersion = nFormatVersion; + nActualDecompressedSize = lzsa_decompress_inmem(pCompressedData, pTmpDecompressedData, nActualCompressedSize, nGeneratedDataSize, nFlags, &nDecFormatVersion); + if (nActualDecompressedSize == -1) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error decompressing size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + if (memcmp(pGeneratedData, pTmpDecompressedData, nGeneratedDataSize)) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error comparing decompressed and original data, size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + /* Try to decompress corrupted data, expected to fail cleanly, without crashing or corrupting memory outside the output buffer */ + for (fXorProbability = 0.05f; fXorProbability <= 0.5f; fXorProbability += 0.05f) { + memcpy(pTmpCompressedData, pCompressedData, nActualCompressedSize); + xor_data(pTmpCompressedData + lzsa_get_header_size() + lzsa_get_frame_size(), nActualCompressedSize - lzsa_get_header_size() - lzsa_get_frame_size() - lzsa_get_frame_size() /* footer */, nSeed, fXorProbability); + nDecFormatVersion = nFormatVersion; + lzsa_decompress_inmem(pTmpCompressedData, pGeneratedData, nActualCompressedSize, nGeneratedDataSize, nFlags, &nDecFormatVersion); + } + } + + nSeed++; + } + + fputc(10, stdout); + fflush(stdout); + + nDataSizeStep <<= 1; + if (nDataSizeStep > (128 * 4096)) + nDataSizeStep = 128 * 4096; + fProbabilitySizeStep *= 1.25; + if (fProbabilitySizeStep > (0.0005f * 4096)) + fProbabilitySizeStep = 0.0005f * 4096; + } + + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + + free(pTmpCompressedData); + pTmpCompressedData = NULL; + + free(pCompressedData); + pCompressedData = NULL; + + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stdout, "All tests passed.\n"); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_compr_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const int nMinMatchSize, int nFormatVersion) { + size_t nFileSize, nMaxCompressedSize; + unsigned char *pFileData; + unsigned char *pCompressedData; + int nFlags; + int i; + + nFlags = 0; + if (nOptions & OPT_FAVOR_RATIO) + nFlags |= LZSA_FLAG_FAVOR_RATIO; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (pszDictionaryFilename) { + fprintf(stderr, "in-memory benchmarking does not support dictionaries\n"); + return 100; + } + + /* Read the whole original file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nFileSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pFileData = (unsigned char*)malloc(nFileSize); + if (!pFileData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nFileSize); + return 100; + } + + if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) { + free(pFileData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + /* Allocate max compressed size */ + + nMaxCompressedSize = lzsa_get_max_compressed_size_inmem(nFileSize); + + pCompressedData = (unsigned char*)malloc(nMaxCompressedSize + 2048); + if (!pCompressedData) { + free(pFileData); + fprintf(stderr, "out of memory for compressing '%s', %zd bytes needed\n", pszInFilename, nMaxCompressedSize); + return 100; + } + + memset(pCompressedData + 1024, 0, nMaxCompressedSize); + + long long nBestCompTime = -1; + + size_t nActualCompressedSize = 0; + size_t nRightGuardPos = nMaxCompressedSize; + + for (i = 0; i < 5; i++) { + unsigned char nGuard = 0x33 + i; + int j; + + /* Write guard bytes around the output buffer, to help check for writes outside of it by the compressor */ + memset(pCompressedData, nGuard, 1024); + memset(pCompressedData + 1024 + nRightGuardPos, nGuard, 1024); + + long long t0 = do_get_time(); + nActualCompressedSize = lzsa_compress_inmem(pFileData, pCompressedData + 1024, nFileSize, nRightGuardPos, nFlags, nMinMatchSize, nFormatVersion); + long long t1 = do_get_time(); + if (nActualCompressedSize == -1) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "compression error\n"); + return 100; + } + + long long nCurDecTime = t1 - t0; + if (nBestCompTime == -1 || nBestCompTime > nCurDecTime) + nBestCompTime = nCurDecTime; + + /* Check guard bytes before the output buffer */ + for (j = 0; j < 1024; j++) { + if (pCompressedData[j] != nGuard) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j - 1024); + return 100; + } + } + + /* Check guard bytes after the output buffer */ + for (j = 0; j < 1024; j++) { + if (pCompressedData[1024 + nRightGuardPos + j] != nGuard) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j); + return 100; + } + } + + nRightGuardPos = nActualCompressedSize; + } + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole compressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pCompressedData + 1024, 1, nActualCompressedSize, f_out); + fclose(f_out); + } + } + + free(pCompressedData); + free(pFileData); + + fprintf(stdout, "compressed size: %zd bytes\n", nActualCompressedSize); + fprintf(stdout, "compression time: %lld microseconds (%g Mb/s)\n", nBestCompTime, ((double)nActualCompressedSize / 1024.0) / ((double)nBestCompTime / 1000.0)); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_dec_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, int nFormatVersion) { + size_t nFileSize, nMaxDecompressedSize; + unsigned char *pFileData; + unsigned char *pDecompressedData; + int nFlags; + int i; + + nFlags = 0; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (pszDictionaryFilename) { + fprintf(stderr, "in-memory benchmarking does not support dictionaries\n"); + return 100; + } + + /* Read the whole compressed file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nFileSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pFileData = (unsigned char*)malloc(nFileSize); + if (!pFileData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nFileSize); + return 100; + } + + if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) { + free(pFileData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + /* Allocate max decompressed size */ + + if (nOptions & OPT_RAW) + nMaxDecompressedSize = 65536; + else + nMaxDecompressedSize = lzsa_get_max_decompressed_size_inmem(pFileData, nFileSize); + if (nMaxDecompressedSize == -1) { + free(pFileData); + fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename); + return 100; + } + + pDecompressedData = (unsigned char*)malloc(nMaxDecompressedSize); + if (!pDecompressedData) { + free(pFileData); + fprintf(stderr, "out of memory for decompressing '%s', %zd bytes needed\n", pszInFilename, nMaxDecompressedSize); + return 100; + } + + memset(pDecompressedData, 0, nMaxDecompressedSize); + + long long nBestDecTime = -1; + + size_t nActualDecompressedSize = 0; + for (i = 0; i < 50; i++) { + long long t0 = do_get_time(); + nActualDecompressedSize = lzsa_decompress_inmem(pFileData, pDecompressedData, nFileSize, nMaxDecompressedSize, nFlags, &nFormatVersion); + long long t1 = do_get_time(); + if (nActualDecompressedSize == -1) { + free(pDecompressedData); + free(pFileData); + fprintf(stderr, "decompression error\n"); + return 100; + } + + long long nCurDecTime = t1 - t0; + if (nBestDecTime == -1 || nBestDecTime > nCurDecTime) + nBestDecTime = nCurDecTime; + } + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole decompressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pDecompressedData, 1, nActualDecompressedSize, f_out); + fclose(f_out); + } + } + + free(pDecompressedData); + free(pFileData); + + fprintf(stdout, "format: LZSA%d\n", nFormatVersion); + fprintf(stdout, "decompressed size: %zd bytes\n", nActualDecompressedSize); + fprintf(stdout, "decompression time: %lld microseconds (%g Mb/s)\n", nBestDecTime, ((double)nActualDecompressedSize / 1024.0) / ((double)nBestDecTime / 1000.0)); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +int main(int argc, char **argv) { + int i; + const char *pszInFilename = NULL; + const char *pszOutFilename = NULL; + const char *pszDictionaryFilename = NULL; + bool bArgsError = false; + bool bCommandDefined = false; + bool bVerifyCompression = false; + bool bMinMatchDefined = false; + bool bFormatVersionDefined = false; + char cCommand = 'z'; + int nMinMatchSize = 0; + unsigned int nOptions = OPT_FAVOR_RATIO; + int nFormatVersion = 1; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-d")) { + if (!bCommandDefined) { + bCommandDefined = true; + cCommand = 'd'; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-z")) { + if (!bCommandDefined) { + bCommandDefined = true; + cCommand = 'z'; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-c")) { + if (!bVerifyCompression) { + bVerifyCompression = true; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-cbench")) { + if (!bCommandDefined) { + bCommandDefined = true; + cCommand = 'B'; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-dbench")) { + if (!bCommandDefined) { + bCommandDefined = true; + cCommand = 'b'; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-test")) { + if (!bCommandDefined) { + bCommandDefined = true; + cCommand = 't'; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-D")) { + if (!pszDictionaryFilename && (i + 1) < argc) { + pszDictionaryFilename = argv[i + 1]; + i++; + } + else + bArgsError = true; + } + else if (!strncmp(argv[i], "-D", 2)) { + if (!pszDictionaryFilename) { + pszDictionaryFilename = argv[i] + 2; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-m")) { + if (!bMinMatchDefined && (i + 1) < argc) { + char *pEnd = NULL; + nMinMatchSize = (int)strtol(argv[i + 1], &pEnd, 10); + if (pEnd && pEnd != argv[i + 1] && (nMinMatchSize >= 2 && nMinMatchSize <= 5)) { + i++; + bMinMatchDefined = true; + nOptions &= (~OPT_FAVOR_RATIO); + } + else { + bArgsError = true; + } + } + else + bArgsError = true; + } + else if (!strncmp(argv[i], "-m", 2)) { + if (!bMinMatchDefined) { + char *pEnd = NULL; + nMinMatchSize = (int)strtol(argv[i] + 2, &pEnd, 10); + if (pEnd && pEnd != (argv[i]+2) && (nMinMatchSize >= 2 && nMinMatchSize <= 5)) { + bMinMatchDefined = true; + nOptions &= (~OPT_FAVOR_RATIO); + } + else { + bArgsError = true; + } + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "--prefer-ratio")) { + if (!bMinMatchDefined) { + nMinMatchSize = 0; + bMinMatchDefined = true; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "--prefer-speed")) { + if (!bMinMatchDefined) { + nMinMatchSize = 3; + nOptions &= (~OPT_FAVOR_RATIO); + bMinMatchDefined = true; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-f")) { + if (!bFormatVersionDefined && (i + 1) < argc) { + char *pEnd = NULL; + nFormatVersion = (int)strtol(argv[i + 1], &pEnd, 10); + if (pEnd && pEnd != argv[i + 1] && (nFormatVersion >= 1 && nFormatVersion <= 2)) { + i++; + bFormatVersionDefined = true; + } + else { + bArgsError = true; + } + } + else + bArgsError = true; + } + else if (!strncmp(argv[i], "-f", 2)) { + if (!bFormatVersionDefined) { + char *pEnd = NULL; + nFormatVersion = (int)strtol(argv[i] + 2, &pEnd, 10); + if (pEnd && pEnd != (argv[i] + 2) && (nFormatVersion >= 1 && nFormatVersion <= 2)) { + bFormatVersionDefined = true; + } + else { + bArgsError = true; + } + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-v")) { + if ((nOptions & OPT_VERBOSE) == 0) { + nOptions |= OPT_VERBOSE; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-r")) { + if ((nOptions & OPT_RAW) == 0) { + nOptions |= OPT_RAW; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-b")) { + if ((nOptions & OPT_RAW_BACKWARD) == 0) { + nOptions |= OPT_RAW_BACKWARD; + } + else + bArgsError = true; + } + else if (!strcmp(argv[i], "-stats")) { + if ((nOptions & OPT_STATS) == 0) { + nOptions |= OPT_STATS; + } + else + bArgsError = true; + } + else { + if (!pszInFilename) + pszInFilename = argv[i]; + else { + if (!pszOutFilename) + pszOutFilename = argv[i]; + else + bArgsError = true; + } + } + } + + if (!bArgsError && (nOptions & OPT_RAW_BACKWARD) && !(nOptions & OPT_RAW)) { + fprintf(stderr, "error: -b (compress backwards) requires -r (raw block format)\n"); + return 100; + } + + if (!bArgsError && cCommand == 't') { + return do_self_test(nOptions, nMinMatchSize, nFormatVersion); + } + + if (bArgsError || !pszInFilename || !pszOutFilename) { + fprintf(stderr, "lzsa command-line tool v" TOOL_VERSION " by Emmanuel Marty and spke\n"); + fprintf(stderr, "usage: %s [-c] [-d] [-v] [-r] \n", argv[0]); + fprintf(stderr, " -c: check resulting stream after compressing\n"); + fprintf(stderr, " -d: decompress (default: compress)\n"); + fprintf(stderr, " -cbench: benchmark in-memory compression\n"); + fprintf(stderr, " -dbench: benchmark in-memory decompression\n"); + fprintf(stderr, " -test: run automated self-tests\n"); + fprintf(stderr, " -stats: show compressed data stats\n"); + fprintf(stderr, " -v: be verbose\n"); + fprintf(stderr, " -f : LZSA compression format (1-2)\n"); + fprintf(stderr, " -r: raw block format (max. 64 Kb files)\n"); + fprintf(stderr, " -b: compress backward (requires -r and a backward decompressor)\n"); + fprintf(stderr, " -D : use dictionary file\n"); + fprintf(stderr, " -m : minimum match size (3-5) (default: 3)\n"); + fprintf(stderr, " --prefer-ratio: favor compression ratio (default)\n"); + fprintf(stderr, " --prefer-speed: favor decompression speed (same as -m3)\n"); + return 100; + } + + do_init_time(); + + if (cCommand == 'z') { + int nResult = do_compress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMinMatchSize, nFormatVersion); + if (nResult == 0 && bVerifyCompression) { + return do_compare(pszOutFilename, pszInFilename, pszDictionaryFilename, nOptions, nFormatVersion); + } else { + return nResult; + } + } + else if (cCommand == 'd') { + return do_decompress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nFormatVersion); + } + else if (cCommand == 'B') { + return do_compr_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMinMatchSize, nFormatVersion); + } + else if (cCommand == 'b') { + return do_dec_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nFormatVersion); + } + else { + return 100; + } +} diff --git a/Tools/unix/lzsa/src/matchfinder.c b/Tools/unix/lzsa/src/matchfinder.c new file mode 100644 index 00000000..fbdc5ca3 --- /dev/null +++ b/Tools/unix/lzsa/src/matchfinder.c @@ -0,0 +1,361 @@ +/* + * matchfinder.c - LZ match finder implementation + * + * The following copying information applies to this specific source code file: + * + * Written in 2019 by Emmanuel Marty + * Portions written in 2014-2015 by Eric Biggers + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide via the Creative Commons Zero 1.0 Universal Public Domain + * Dedication (the "CC0"). + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the CC0 for more details. + * + * You should have received a copy of the CC0 along with this software; if not + * see . + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "matchfinder.h" +#include "format.h" +#include "lib.h" + +/** + * Hash index into TAG_BITS + * + * @param nIndex index value + * + * @return hash + */ +static inline int lzsa_get_index_tag(unsigned int nIndex) { + return (int)(((unsigned long long)nIndex * 11400714819323198485ULL) >> (64ULL - TAG_BITS)); +} + +/** + * Parse input data, build suffix array and overlaid data structures to speed up match finding + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return 0 for success, non-zero for failure + */ +int lzsa_build_suffix_array(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize) { + unsigned int *intervals = pCompressor->intervals; + + /* Build suffix array from input data */ + if (divsufsort_build_array(&pCompressor->divsufsort_context, pInWindow, (saidx_t*)intervals, nInWindowSize) != 0) { + return 100; + } + + int *PLCP = (int*)pCompressor->pos_data; /* Use temporarily */ + int *Phi = PLCP; + int nCurLen = 0; + int i; + + /* Compute the permuted LCP first (Kärkkäinen method) */ + Phi[intervals[0]] = -1; + for (i = 1; i < nInWindowSize; i++) + Phi[intervals[i]] = intervals[i - 1]; + for (i = 0; i < nInWindowSize; i++) { + if (Phi[i] == -1) { + PLCP[i] = 0; + continue; + } + int nMaxLen = (i > Phi[i]) ? (nInWindowSize - i) : (nInWindowSize - Phi[i]); + while (nCurLen < nMaxLen && pInWindow[i + nCurLen] == pInWindow[Phi[i] + nCurLen]) nCurLen++; + PLCP[i] = nCurLen; + if (nCurLen > 0) + nCurLen--; + } + + /* Rotate permuted LCP into the LCP. This has better cache locality than the direct Kasai LCP method. This also + * saves us from having to build the inverse suffix array index, as the LCP is calculated without it using this method, + * and the interval builder below doesn't need it either. */ + intervals[0] &= POS_MASK; + int nMinMatchSize = pCompressor->min_match_size; + + if (pCompressor->format_version >= 2) { + for (i = 1; i < nInWindowSize; i++) { + int nIndex = (int)(intervals[i] & POS_MASK); + int nLen = PLCP[nIndex]; + if (nLen < nMinMatchSize) + nLen = 0; + if (nLen > LCP_MAX) + nLen = LCP_MAX; + int nTaggedLen = 0; + if (nLen) + nTaggedLen = (nLen << TAG_BITS) | (lzsa_get_index_tag((unsigned int)nIndex) & ((1 << TAG_BITS) - 1)); + intervals[i] = ((unsigned int)nIndex) | (((unsigned int)nTaggedLen) << LCP_SHIFT); + } + } + else { + for (i = 1; i < nInWindowSize; i++) { + int nIndex = (int)(intervals[i] & POS_MASK); + int nLen = PLCP[nIndex]; + if (nLen < nMinMatchSize) + nLen = 0; + if (nLen > LCP_AND_TAG_MAX) + nLen = LCP_AND_TAG_MAX; + intervals[i] = ((unsigned int)nIndex) | (((unsigned int)nLen) << LCP_SHIFT); + } + } + + /** + * Build intervals for finding matches + * + * Methodology and code fragment taken from wimlib (CC0 license): + * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD + */ + unsigned int * const SA_and_LCP = intervals; + unsigned int *pos_data = pCompressor->pos_data; + unsigned int next_interval_idx; + unsigned int *top = pCompressor->open_intervals; + unsigned int prev_pos = SA_and_LCP[0] & POS_MASK; + + *top = 0; + intervals[0] = 0; + next_interval_idx = 1; + + for (int r = 1; r < nInWindowSize; r++) { + const unsigned int next_pos = SA_and_LCP[r] & POS_MASK; + const unsigned int next_lcp = SA_and_LCP[r] & LCP_MASK; + const unsigned int top_lcp = *top & LCP_MASK; + + if (next_lcp == top_lcp) { + /* Continuing the deepest open interval */ + pos_data[prev_pos] = *top; + } + else if (next_lcp > top_lcp) { + /* Opening a new interval */ + *++top = next_lcp | next_interval_idx++; + pos_data[prev_pos] = *top; + } + else { + /* Closing the deepest open interval */ + pos_data[prev_pos] = *top; + for (;;) { + const unsigned int closed_interval_idx = *top-- & POS_MASK; + const unsigned int superinterval_lcp = *top & LCP_MASK; + + if (next_lcp == superinterval_lcp) { + /* Continuing the superinterval */ + intervals[closed_interval_idx] = *top; + break; + } + else if (next_lcp > superinterval_lcp) { + /* Creating a new interval that is a + * superinterval of the one being + * closed, but still a subinterval of + * its superinterval */ + *++top = next_lcp | next_interval_idx++; + intervals[closed_interval_idx] = *top; + break; + } + else { + /* Also closing the superinterval */ + intervals[closed_interval_idx] = *top; + } + } + } + prev_pos = next_pos; + } + + /* Close any still-open intervals. */ + pos_data[prev_pos] = *top; + for (; top > pCompressor->open_intervals; top--) + intervals[*top & POS_MASK] = *(top - 1); + + /* Success */ + return 0; +} + +/** + * Find matches at the specified offset in the input window + * + * @param pCompressor compression context + * @param nOffset offset to find matches at, in the input window + * @param pMatches pointer to returned matches + * @param nMaxMatches maximum number of matches to return (0 for none) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return number of matches + */ +int lzsa_find_matches_at(lzsa_compressor *pCompressor, const int nOffset, lzsa_match *pMatches, const int nMaxMatches, const int nInWindowSize) { + unsigned int *intervals = pCompressor->intervals; + unsigned int *pos_data = pCompressor->pos_data; + unsigned int ref; + unsigned int super_ref; + unsigned int match_pos; + lzsa_match *matchptr; + int nPrevOffset = 0; + + /** + * Find matches using intervals + * + * Taken from wimlib (CC0 license): + * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD + */ + + /* Get the deepest lcp-interval containing the current suffix. */ + ref = pos_data[nOffset]; + + pos_data[nOffset] = 0; + + /* Ascend until we reach a visited interval, the root, or a child of the + * root. Link unvisited intervals to the current suffix as we go. */ + while ((super_ref = intervals[ref & POS_MASK]) & LCP_MASK) { + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + ref = super_ref; + } + + if (super_ref == 0) { + /* In this case, the current interval may be any of: + * (1) the root; + * (2) an unvisited child of the root */ + + if (ref != 0) /* Not the root? */ + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + return 0; + } + + /* Ascend indirectly via pos_data[] links. */ + match_pos = super_ref & EXCL_VISITED_MASK; + matchptr = pMatches; + + if (pCompressor->format_version >= 2 && nInWindowSize < 65536) { + if ((matchptr - pMatches) < nMaxMatches) { + int nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= MAX_OFFSET) { + matchptr->length = (unsigned short)(ref >> (LCP_SHIFT + TAG_BITS)); + matchptr->offset = (unsigned short)nMatchOffset; + matchptr++; + + nPrevOffset = nMatchOffset; + } + } + } + + for (;;) { + if ((super_ref = pos_data[match_pos]) > ref) { + match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK; + + if (pCompressor->format_version >= 2 && nInWindowSize < 65536) { + if ((matchptr - pMatches) < nMaxMatches) { + int nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= MAX_OFFSET && nMatchOffset != nPrevOffset) { + matchptr->length = ((unsigned short)(ref >> (LCP_SHIFT + TAG_BITS))) | 0x8000; + matchptr->offset = (unsigned short)nMatchOffset; + matchptr++; + + nPrevOffset = nMatchOffset; + } + } + } + } + + while ((super_ref = pos_data[match_pos]) > ref) + match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK; + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + pos_data[match_pos] = ref; + + if ((matchptr - pMatches) < nMaxMatches) { + int nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= MAX_OFFSET && nMatchOffset != nPrevOffset) { + if (pCompressor->format_version >= 2) { + matchptr->length = (unsigned short)(ref >> (LCP_SHIFT + TAG_BITS)); + } + else { + matchptr->length = (unsigned short)(ref >> LCP_SHIFT); + } + matchptr->offset = (unsigned short)nMatchOffset; + matchptr++; + } + } + + if (super_ref == 0) + break; + ref = super_ref; + match_pos = intervals[ref & POS_MASK] & EXCL_VISITED_MASK; + + if (pCompressor->format_version >= 2 && nInWindowSize < 65536) { + if ((matchptr - pMatches) < nMaxMatches) { + int nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= MAX_OFFSET && nMatchOffset != nPrevOffset) { + matchptr->length = ((unsigned short)(ref >> (LCP_SHIFT + TAG_BITS))) | 0x8000; + matchptr->offset = (unsigned short)nMatchOffset; + + if ((matchptr->length & 0x7fff) > 2) { + matchptr++; + + nPrevOffset = nMatchOffset; + } + } + } + } + } + + return (int)(matchptr - pMatches); +} + +/** + * Skip previously compressed bytes + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically 0) + * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes) + */ +void lzsa_skip_matches(lzsa_compressor *pCompressor, const int nStartOffset, const int nEndOffset) { + lzsa_match match; + int i; + + /* Skipping still requires scanning for matches, as this also performs a lazy update of the intervals. However, + * we don't store the matches. */ + for (i = nStartOffset; i < nEndOffset; i++) { + lzsa_find_matches_at(pCompressor, i, &match, 0, 0); + } +} + +/** + * Find all matches for the data to be compressed + * + * @param pCompressor compression context + * @param nMatchesPerOffset maximum number of matches to store for each offset + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + */ +void lzsa_find_all_matches(lzsa_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset) { + lzsa_match *pMatch = pCompressor->match; + int i; + + for (i = nStartOffset; i < nEndOffset; i++) { + int nMatches = lzsa_find_matches_at(pCompressor, i, pMatch, nMatchesPerOffset, nEndOffset - nStartOffset); + + while (nMatches < nMatchesPerOffset) { + pMatch[nMatches].length = 0; + pMatch[nMatches].offset = 0; + nMatches++; + } + + pMatch += nMatchesPerOffset; + } +} diff --git a/Tools/unix/lzsa/src/matchfinder.h b/Tools/unix/lzsa/src/matchfinder.h new file mode 100644 index 00000000..cc3b5abd --- /dev/null +++ b/Tools/unix/lzsa/src/matchfinder.h @@ -0,0 +1,91 @@ +/* + * matchfinder.h - LZ match finder definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _MATCHFINDER_H +#define _MATCHFINDER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations */ +typedef struct _lzsa_match lzsa_match; +typedef struct _lzsa_compressor lzsa_compressor; + +/** + * Parse input data, build suffix array and overlaid data structures to speed up match finding + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return 0 for success, non-zero for failure + */ +int lzsa_build_suffix_array(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize); + +/** + * Find matches at the specified offset in the input window + * + * @param pCompressor compression context + * @param nOffset offset to find matches at, in the input window + * @param pMatches pointer to returned matches + * @param nMaxMatches maximum number of matches to return (0 for none) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return number of matches + */ +int lzsa_find_matches_at(lzsa_compressor *pCompressor, const int nOffset, lzsa_match *pMatches, const int nMaxMatches, const int nInWindowSize); + +/** + * Skip previously compressed bytes + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically 0) + * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes) + */ +void lzsa_skip_matches(lzsa_compressor *pCompressor, const int nStartOffset, const int nEndOffset); + +/** + * Find all matches for the data to be compressed + * + * @param pCompressor compression context + * @param nMatchesPerOffset maximum number of matches to store for each offset + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + */ +void lzsa_find_all_matches(lzsa_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset); + +#ifdef __cplusplus +} +#endif + +#endif /* _MATCHFINDER_H */ diff --git a/Tools/unix/lzsa/src/shrink_block_v1.c b/Tools/unix/lzsa/src/shrink_block_v1.c new file mode 100644 index 00000000..c30e4a9f --- /dev/null +++ b/Tools/unix/lzsa/src/shrink_block_v1.c @@ -0,0 +1,702 @@ +/* + * shrink_block_v1.c - LZSA1 block compressor implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "lib.h" +#include "shrink_block_v1.h" +#include "format.h" + +/** + * Get the number of extra bits required to represent a literals length + * + * @param nLength literals length + * + * @return number of extra bits required + */ +static inline int lzsa_get_literals_varlen_size_v1(const int nLength) { + if (nLength < LITERALS_RUN_LEN_V1) { + return 0; + } + else { + if (nLength < 256) + return 8; + else { + if (nLength < 512) + return 16; + else + return 24; + } + } +} + +/** + * Write extra literals length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nLength literals length + */ +static inline int lzsa_write_literals_varlen_v1(unsigned char *pOutData, int nOutOffset, int nLength) { + if (nLength >= LITERALS_RUN_LEN_V1) { + if (nLength < 256) + pOutData[nOutOffset++] = nLength - LITERALS_RUN_LEN_V1; + else { + if (nLength < 512) { + pOutData[nOutOffset++] = 250; + pOutData[nOutOffset++] = nLength - 256; + } + else { + pOutData[nOutOffset++] = 249; + pOutData[nOutOffset++] = nLength & 0xff; + pOutData[nOutOffset++] = (nLength >> 8) & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Get the number of extra bits required to represent an encoded match length + * + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V1) + * + * @return number of extra bits required + */ +static inline int lzsa_get_match_varlen_size_v1(const int nLength) { + if (nLength < MATCH_RUN_LEN_V1) { + return 0; + } + else { + if ((nLength + MIN_MATCH_SIZE_V1) < 256) + return 8; + else { + if ((nLength + MIN_MATCH_SIZE_V1) < 512) + return 16; + else + return 24; + } + } +} + +/** + * Write extra encoded match length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V1) + */ +static inline int lzsa_write_match_varlen_v1(unsigned char *pOutData, int nOutOffset, int nLength) { + if (nLength >= MATCH_RUN_LEN_V1) { + if ((nLength + MIN_MATCH_SIZE_V1) < 256) + pOutData[nOutOffset++] = nLength - MATCH_RUN_LEN_V1; + else { + if ((nLength + MIN_MATCH_SIZE_V1) < 512) { + pOutData[nOutOffset++] = 239; + pOutData[nOutOffset++] = nLength + MIN_MATCH_SIZE_V1 - 256; + } + else { + pOutData[nOutOffset++] = 238; + pOutData[nOutOffset++] = (nLength + MIN_MATCH_SIZE_V1) & 0xff; + pOutData[nOutOffset++] = ((nLength + MIN_MATCH_SIZE_V1) >> 8) & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Get offset encoding cost in bits + * + * @param nMatchOffset offset to get cost of + * + * @return cost in bits + */ +static inline int lzsa_get_offset_cost_v1(const unsigned int nMatchOffset) { + return (nMatchOffset <= 256) ? 8 : 16; +} + +/** + * Attempt to pick optimal matches using a forward arrivals parser, so as to produce the smallest possible output that decompresses to the same input + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + */ +static void lzsa_optimize_forward_v1(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset, const int nReduce) { + lzsa_arrival *arrival = pCompressor->arrival - (nStartOffset << MATCHES_PER_ARRIVAL_SHIFT); + const int nMinMatchSize = pCompressor->min_match_size; + const int nFavorRatio = (pCompressor->flags & LZSA_FLAG_FAVOR_RATIO) ? 1 : 0; + const int nDisableScore = nReduce ? 0 : (2 * BLOCK_SIZE); + int i, j, n; + + if ((nEndOffset - nStartOffset) > BLOCK_SIZE) return; + + memset(arrival + (nStartOffset << MATCHES_PER_ARRIVAL_SHIFT), 0, sizeof(lzsa_arrival) * ((nEndOffset - nStartOffset + 1) << MATCHES_PER_ARRIVAL_SHIFT)); + + arrival[nStartOffset << MATCHES_PER_ARRIVAL_SHIFT].from_slot = -1; + + for (i = nStartOffset; i != nEndOffset; i++) { + int m; + + for (j = 0; j < NMATCHES_PER_ARRIVAL_V1 && arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].from_slot; j++) { + int nPrevCost = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].cost; + int nCodingChoiceCost = nPrevCost + 8 /* literal */; + int nScore = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].score + 1; + int nNumLiterals = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].num_literals + 1; + + if (nNumLiterals == LITERALS_RUN_LEN_V1 || nNumLiterals == 256 || nNumLiterals == 512) { + nCodingChoiceCost += 8; + } + + if (!nFavorRatio && nNumLiterals == 1) + nCodingChoiceCost += MODESWITCH_PENALTY; + + for (n = 0; n < NMATCHES_PER_ARRIVAL_V1 /* we only need the literals + short match cost + long match cost cases */; n++) { + lzsa_arrival *pDestArrival = &arrival[((i + 1) << MATCHES_PER_ARRIVAL_SHIFT) + n]; + + if (pDestArrival->from_slot == 0 || + nCodingChoiceCost < pDestArrival->cost || + (nCodingChoiceCost == pDestArrival->cost && nScore < (pDestArrival->score + nDisableScore))) { + memmove(&arrival[((i + 1) << MATCHES_PER_ARRIVAL_SHIFT) + n + 1], + &arrival[((i + 1) << MATCHES_PER_ARRIVAL_SHIFT) + n], + sizeof(lzsa_arrival) * (NMATCHES_PER_ARRIVAL_V1 - n - 1)); + + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_offset = 0; + pDestArrival->match_len = 0; + pDestArrival->num_literals = nNumLiterals; + pDestArrival->score = nScore; + pDestArrival->rep_offset = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_offset; + break; + } + } + } + + const lzsa_match *match = pCompressor->match + ((i - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V1); + + for (m = 0; m < NMATCHES_PER_INDEX_V1 && match[m].length; m++) { + int nMatchLen = match[m].length; + int nMatchOffsetCost = lzsa_get_offset_cost_v1(match[m].offset); + int nStartingMatchLen, k; + + if ((i + nMatchLen) > (nEndOffset - LAST_LITERALS)) + nMatchLen = nEndOffset - LAST_LITERALS - i; + + if (nMatchLen >= LEAVE_ALONE_MATCH_SIZE) + nStartingMatchLen = nMatchLen; + else + nStartingMatchLen = nMinMatchSize; + for (k = nStartingMatchLen; k <= nMatchLen; k++) { + int nMatchLenCost = lzsa_get_match_varlen_size_v1(k - MIN_MATCH_SIZE_V1); + + for (j = 0; j < NMATCHES_PER_ARRIVAL_V1 && arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].from_slot; j++) { + int nPrevCost = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].cost; + int nCodingChoiceCost = nPrevCost + 8 /* token */ /* the actual cost of the literals themselves accumulates up the chain */ + nMatchOffsetCost + nMatchLenCost; + int nScore = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].score + 5; + int exists = 0; + + if (!nFavorRatio && !arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].num_literals) + nCodingChoiceCost += MODESWITCH_PENALTY; + + for (n = 0; + n < NMATCHES_PER_ARRIVAL_V1 && arrival[((i + k) << MATCHES_PER_ARRIVAL_SHIFT) + n].from_slot && arrival[((i + k) << MATCHES_PER_ARRIVAL_SHIFT) + n].cost <= nCodingChoiceCost; + n++) { + if (lzsa_get_offset_cost_v1(arrival[((i + k) << MATCHES_PER_ARRIVAL_SHIFT) + n].rep_offset) == lzsa_get_offset_cost_v1(match[m].offset)) { + exists = 1; + break; + } + } + + for (n = 0; !exists && n < NMATCHES_PER_ARRIVAL_V1 /* we only need the literals + short match cost + long match cost cases */; n++) { + lzsa_arrival *pDestArrival = &arrival[((i + k) << MATCHES_PER_ARRIVAL_SHIFT) + n]; + + if (pDestArrival->from_slot == 0 || + nCodingChoiceCost < pDestArrival->cost || + (nCodingChoiceCost == pDestArrival->cost && nScore < (pDestArrival->score + nDisableScore))) { + memmove(&arrival[((i + k) << MATCHES_PER_ARRIVAL_SHIFT) + n + 1], + &arrival[((i + k) << MATCHES_PER_ARRIVAL_SHIFT) + n], + sizeof(lzsa_arrival) * (NMATCHES_PER_ARRIVAL_V1 - n - 1)); + + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_offset = match[m].offset; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + pDestArrival->rep_offset = match[m].offset; + break; + } + } + } + } + } + } + + lzsa_arrival *end_arrival = &arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + 0]; + + while (end_arrival->from_slot > 0 && end_arrival->from_pos >= 0) { + if (end_arrival->from_pos >= nEndOffset) return; + pBestMatch[end_arrival->from_pos].length = end_arrival->match_len; + pBestMatch[end_arrival->from_pos].offset = end_arrival->match_offset; + + end_arrival = &arrival[(end_arrival->from_pos << MATCHES_PER_ARRIVAL_SHIFT) + (end_arrival->from_slot - 1)]; + } +} + +/** + * Attempt to minimize the number of commands issued in the compressed data block, in order to speed up decompression without + * impacting the compression ratio + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param pBestMatch optimal matches to emit + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return non-zero if the number of tokens was reduced, 0 if it wasn't + */ +static int lzsa_optimize_command_count_v1(lzsa_compressor *pCompressor, const unsigned char *pInWindow, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nDidReduce = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length == 0 && + (i + 1) < (nEndOffset - LAST_LITERALS) && + pBestMatch[i + 1].length >= MIN_MATCH_SIZE_V1 && + pBestMatch[i + 1].length < MAX_VARLEN && + pBestMatch[i + 1].offset && + i >= pBestMatch[i + 1].offset && + (i + pBestMatch[i + 1].length + 1) <= (nEndOffset - LAST_LITERALS) && + !memcmp(pInWindow + i - (pBestMatch[i + 1].offset), pInWindow + i, pBestMatch[i + 1].length + 1)) { + int nCurLenSize = lzsa_get_match_varlen_size_v1(pBestMatch[i + 1].length - MIN_MATCH_SIZE_V1); + int nReducedLenSize = lzsa_get_match_varlen_size_v1(pBestMatch[i + 1].length + 1 - MIN_MATCH_SIZE_V1); + + if ((nReducedLenSize - nCurLenSize) <= 8) { + /* Merge */ + pBestMatch[i].length = pBestMatch[i + 1].length + 1; + pBestMatch[i].offset = pBestMatch[i + 1].offset; + pBestMatch[i + 1].length = 0; + pBestMatch[i + 1].offset = 0; + nDidReduce = 1; + continue; + } + } + + if (pMatch->length >= MIN_MATCH_SIZE_V1) { + if (pMatch->length <= 9 /* Don't waste time considering large matches, they will always win over literals */ && + (i + pMatch->length) < nEndOffset /* Don't consider the last token in the block, we can only reduce a match inbetween other tokens */) { + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < MIN_MATCH_SIZE_V1) { + nNextLiterals++; + nNextIndex++; + } + + /* This command is a match, is followed by 'nNextLiterals' literals and then by another match, or the end of the input. Calculate this command's current cost (excluding 'nNumLiterals' bytes) */ + if ((8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + ((pMatch->offset <= 256) ? 8 : 16) /* match offset */ + lzsa_get_match_varlen_size_v1(pMatch->length - MIN_MATCH_SIZE_V1) + + 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNextLiterals)) >= + (8 /* token */ + (pMatch->length << 3) + lzsa_get_literals_varlen_size_v1(nNumLiterals + pMatch->length + nNextLiterals))) { + /* Reduce */ + int nMatchLen = pMatch->length; + int j; + + for (j = 0; j < nMatchLen; j++) { + pBestMatch[i + j].length = 0; + } + + nDidReduce = 1; + continue; + } + } + + if ((i + pMatch->length) <= nEndOffset && pMatch->offset > 0 && pMatch->length >= MIN_MATCH_SIZE_V1 && + pBestMatch[i + pMatch->length].offset > 0 && + pBestMatch[i + pMatch->length].length >= MIN_MATCH_SIZE_V1 && + (pMatch->length + pBestMatch[i + pMatch->length].length) >= LEAVE_ALONE_MATCH_SIZE && + (pMatch->length + pBestMatch[i + pMatch->length].length) <= MAX_VARLEN && + (i + pMatch->length) > pMatch->offset && + (i + pMatch->length) > pBestMatch[i + pMatch->length].offset && + (i + pMatch->length + pBestMatch[i + pMatch->length].length) <= nEndOffset && + !memcmp(pInWindow + i - pMatch->offset + pMatch->length, + pInWindow + i + pMatch->length - pBestMatch[i + pMatch->length].offset, + pBestMatch[i + pMatch->length].length)) { + + int nCurPartialSize = lzsa_get_match_varlen_size_v1(pMatch->length - MIN_MATCH_SIZE_V1); + nCurPartialSize += 8 /* token */ + lzsa_get_literals_varlen_size_v1(0) + ((pBestMatch[i + pMatch->length].offset <= 256) ? 8 : 16) /* match offset */ + lzsa_get_match_varlen_size_v1(pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V1); + + int nReducedPartialSize = lzsa_get_match_varlen_size_v1(pMatch->length + pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V1); + + if (nCurPartialSize >= nReducedPartialSize) { + int nMatchLen = pMatch->length; + + /* Join */ + + pMatch->length += pBestMatch[i + nMatchLen].length; + pBestMatch[i + nMatchLen].offset = 0; + pBestMatch[i + nMatchLen].length = -1; + continue; + } + } + + i += pMatch->length; + nNumLiterals = 0; + } + else { + nNumLiterals++; + i++; + } + } + + return nDidReduce; +} + +/** + * Get compressed data block size + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return size of compressed data that will be written to output buffer + */ +static int lzsa_get_compressed_size_v1(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nCompressedSize = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length >= MIN_MATCH_SIZE_V1) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V1; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V1) ? LITERALS_RUN_LEN_V1 : nNumLiterals; + int nTokenMatchLen = (nEncodedMatchLen >= MATCH_RUN_LEN_V1) ? MATCH_RUN_LEN_V1 : nEncodedMatchLen; + int nTokenLongOffset = (nMatchOffset <= 256) ? 0x00 : 0x80; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3) + (nTokenLongOffset ? 16 : 8) /* match offset */ + lzsa_get_match_varlen_size_v1(nEncodedMatchLen); + + nCompressedSize += nCommandSize; + nNumLiterals = 0; + i += nMatchLen; + } + else { + nNumLiterals++; + i++; + } + } + + { + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V1) ? LITERALS_RUN_LEN_V1 : nNumLiterals; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3); + + nCompressedSize += nCommandSize; + nNumLiterals = 0; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nCompressedSize += 8 * 4; + } + + return nCompressedSize; +} + +/** + * Emit block of compressed data + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_block_v1(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int i; + int nNumLiterals = 0; + int nInFirstLiteralOffset = 0; + int nOutOffset = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length >= MIN_MATCH_SIZE_V1) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V1; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V1) ? LITERALS_RUN_LEN_V1 : nNumLiterals; + int nTokenMatchLen = (nEncodedMatchLen >= MATCH_RUN_LEN_V1) ? MATCH_RUN_LEN_V1 : nEncodedMatchLen; + int nTokenLongOffset = (nMatchOffset <= 256) ? 0x00 : 0x80; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3) + (nTokenLongOffset ? 16 : 8) /* match offset */ + lzsa_get_match_varlen_size_v1(nEncodedMatchLen); + + if ((nOutOffset + (nCommandSize >> 3)) > nMaxOutDataSize) + return -1; + if (nMatchOffset < MIN_OFFSET || nMatchOffset > MAX_OFFSET) + return -1; + + pOutData[nOutOffset++] = nTokenLongOffset | (nTokenLiteralsLen << 4) | nTokenMatchLen; + nOutOffset = lzsa_write_literals_varlen_v1(pOutData, nOutOffset, nNumLiterals); + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + pOutData[nOutOffset++] = (-nMatchOffset) & 0xff; + if (nTokenLongOffset) { + pOutData[nOutOffset++] = (-nMatchOffset) >> 8; + } + nOutOffset = lzsa_write_match_varlen_v1(pOutData, nOutOffset, nEncodedMatchLen); + + if (nMatchOffset < pCompressor->stats.min_offset || pCompressor->stats.min_offset == -1) + pCompressor->stats.min_offset = nMatchOffset; + if (nMatchOffset > pCompressor->stats.max_offset) + pCompressor->stats.max_offset = nMatchOffset; + pCompressor->stats.total_offsets += nMatchOffset; + + if (nMatchLen < pCompressor->stats.min_match_len || pCompressor->stats.min_match_len == -1) + pCompressor->stats.min_match_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_match_len) + pCompressor->stats.max_match_len = nMatchLen; + pCompressor->stats.total_match_lens += nMatchLen; + pCompressor->stats.match_divisor++; + + if (nMatchOffset == 1) { + if (nMatchLen < pCompressor->stats.min_rle1_len || pCompressor->stats.min_rle1_len == -1) + pCompressor->stats.min_rle1_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle1_len) + pCompressor->stats.max_rle1_len = nMatchLen; + pCompressor->stats.total_rle1_lens += nMatchLen; + pCompressor->stats.rle1_divisor++; + } + else if (nMatchOffset == 2) { + if (nMatchLen < pCompressor->stats.min_rle2_len || pCompressor->stats.min_rle2_len == -1) + pCompressor->stats.min_rle2_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle2_len) + pCompressor->stats.max_rle2_len = nMatchLen; + pCompressor->stats.total_rle2_lens += nMatchLen; + pCompressor->stats.rle2_divisor++; + } + + i += nMatchLen; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + else { + if (nNumLiterals == 0) + nInFirstLiteralOffset = i; + nNumLiterals++; + i++; + } + } + + { + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V1) ? LITERALS_RUN_LEN_V1 : nNumLiterals; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3); + + if ((nOutOffset + (nCommandSize >> 3)) > nMaxOutDataSize) + return -1; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) + pOutData[nOutOffset++] = (nTokenLiteralsLen << 4) | 0x0f; + else + pOutData[nOutOffset++] = (nTokenLiteralsLen << 4) | 0x00; + nOutOffset = lzsa_write_literals_varlen_v1(pOutData, nOutOffset, nNumLiterals); + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + /* Emit EOD marker for raw block */ + + if ((nOutOffset + 4) > nMaxOutDataSize) + return -1; + + pOutData[nOutOffset++] = 0; + pOutData[nOutOffset++] = 238; + pOutData[nOutOffset++] = 0; + pOutData[nOutOffset++] = 0; + } + + return nOutOffset; +} + +/** + * Emit raw block of uncompressible data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_raw_uncompressed_block_v1(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int nNumLiterals = nEndOffset - nStartOffset; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V1) ? LITERALS_RUN_LEN_V1 : nNumLiterals; + int nOutOffset = 0; + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3) + 4; + if ((nOutOffset + (nCommandSize >> 3)) > nMaxOutDataSize) + return -1; + + pCompressor->num_commands = 0; + pOutData[nOutOffset++] = (nTokenLiteralsLen << 4) | 0x0f; + + nOutOffset = lzsa_write_literals_varlen_v1(pOutData, nOutOffset, nNumLiterals); + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nStartOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + pCompressor->num_commands++; + + /* Emit EOD marker for raw block */ + + pOutData[nOutOffset++] = 0; + pOutData[nOutOffset++] = 238; + pOutData[nOutOffset++] = 0; + pOutData[nOutOffset++] = 0; + + return nOutOffset; +} + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed LZSA1 data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_optimize_and_write_block_v1(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize) { + int nResult, nBaseCompressedSize; + + /* Compress optimally without breaking ties in favor of less tokens */ + + memset(pCompressor->best_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v1(pCompressor, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 0 /* reduce */); + + int nDidReduce; + int nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v1(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nBaseCompressedSize = lzsa_get_compressed_size_v1(pCompressor, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + lzsa_match *pBestMatch = pCompressor->best_match - nPreviousBlockSize; + + if (nBaseCompressedSize > 0 && nInDataSize < 65536) { + int nReducedCompressedSize; + + /* Compress optimally and do break ties in favor of less tokens */ + memset(pCompressor->improved_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v1(pCompressor, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 1 /* reduce */); + + nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v1(pCompressor, pInWindow, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nReducedCompressedSize = lzsa_get_compressed_size_v1(pCompressor, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + if (nReducedCompressedSize > 0 && nReducedCompressedSize <= nBaseCompressedSize) { + /* Pick the parse with the reduced number of tokens as it didn't negatively affect the size */ + pBestMatch = pCompressor->improved_match - nPreviousBlockSize; + } + } + + nResult = lzsa_write_block_v1(pCompressor, pBestMatch, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + if (nResult < 0 && pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nResult = lzsa_write_raw_uncompressed_block_v1(pCompressor, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + } + + return nResult; +} diff --git a/Tools/unix/lzsa/src/shrink_block_v1.h b/Tools/unix/lzsa/src/shrink_block_v1.h new file mode 100644 index 00000000..cc89cde0 --- /dev/null +++ b/Tools/unix/lzsa/src/shrink_block_v1.h @@ -0,0 +1,53 @@ +/* + * shrink_block_v1.h - LZSA1 block compressor definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_BLOCK_V1_H +#define _SHRINK_BLOCK_V1_H + +/* Forward declarations */ +typedef struct _lzsa_compressor lzsa_compressor; + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed LZSA1 data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_optimize_and_write_block_v1(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize); + +#endif /* _SHRINK_BLOCK_V1_H */ diff --git a/Tools/unix/lzsa/src/shrink_block_v2.c b/Tools/unix/lzsa/src/shrink_block_v2.c new file mode 100644 index 00000000..eb4a16ed --- /dev/null +++ b/Tools/unix/lzsa/src/shrink_block_v2.c @@ -0,0 +1,1183 @@ +/* + * shrink_block_v2.c - LZSA2 block compressor implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "lib.h" +#include "shrink_block_v2.h" +#include "format.h" + +/** + * Write 4-bit nibble to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurNibbleOffset write index into output buffer, of current byte being filled with nibbles + * @param nCurFreeNibbles current number of free nibbles in byte + * @param nNibbleValue value to write (0..15) + */ +static int lzsa_write_nibble_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int *nCurFreeNibbles, int nNibbleValue) { + if (nOutOffset < 0) return -1; + + if ((*nCurNibbleOffset) == -1) { + if (nOutOffset >= nMaxOutDataSize) return -1; + (*nCurNibbleOffset) = nOutOffset; + (*nCurFreeNibbles) = 2; + pOutData[nOutOffset++] = 0; + } + + pOutData[*nCurNibbleOffset] = (pOutData[*nCurNibbleOffset] << 4) | (nNibbleValue & 0x0f); + (*nCurFreeNibbles)--; + if ((*nCurFreeNibbles) == 0) { + (*nCurNibbleOffset) = -1; + } + + return nOutOffset; +} + +/** + * Get the number of extra bits required to represent a literals length + * + * @param nLength literals length + * + * @return number of extra bits required + */ +static inline int lzsa_get_literals_varlen_size_v2(const int nLength) { + if (nLength < LITERALS_RUN_LEN_V2) { + return 0; + } + else { + if (nLength < (LITERALS_RUN_LEN_V2 + 15)) { + return 4; + } + else { + if (nLength < 256) + return 4+8; + else { + return 4+24; + } + } + } +} + +/** + * Write extra literals length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nLength literals length + */ +static inline int lzsa_write_literals_varlen_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int *nCurFreeNibbles, int nLength) { + if (nLength >= LITERALS_RUN_LEN_V2) { + if (nLength < (LITERALS_RUN_LEN_V2 + 15)) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nCurFreeNibbles, nLength - LITERALS_RUN_LEN_V2); + } + else { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nCurFreeNibbles, 15); + if (nOutOffset < 0) return -1; + + if (nLength < 256) + pOutData[nOutOffset++] = nLength - 18; + else { + pOutData[nOutOffset++] = 239; + pOutData[nOutOffset++] = nLength & 0xff; + pOutData[nOutOffset++] = (nLength >> 8) & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Get the number of extra bits required to represent an encoded match length + * + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V2) + * + * @return number of extra bits required + */ +static inline int lzsa_get_match_varlen_size_v2(const int nLength) { + if (nLength < MATCH_RUN_LEN_V2) { + return 0; + } + else { + if (nLength < (MATCH_RUN_LEN_V2 + 15)) + return 4; + else { + if ((nLength + MIN_MATCH_SIZE_V2) < 256) + return 4+8; + else { + return 4 + 24; + } + } + } +} + +/** + * Write extra encoded match length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V2) + */ +static inline int lzsa_write_match_varlen_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int *nCurFreeNibbles, int nLength) { + if (nLength >= MATCH_RUN_LEN_V2) { + if (nLength < (MATCH_RUN_LEN_V2 + 15)) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nCurFreeNibbles, nLength - MATCH_RUN_LEN_V2); + } + else { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nCurFreeNibbles, 15); + if (nOutOffset < 0) return -1; + + if ((nLength + MIN_MATCH_SIZE_V2) < 256) + pOutData[nOutOffset++] = nLength + MIN_MATCH_SIZE_V2 - 24; + else { + pOutData[nOutOffset++] = 233; + pOutData[nOutOffset++] = (nLength + MIN_MATCH_SIZE_V2) & 0xff; + pOutData[nOutOffset++] = ((nLength + MIN_MATCH_SIZE_V2) >> 8) & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Insert forward rep candidate + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param i input data window position whose matches are being considered + * @param nMatchOffset match offset to use as rep candidate + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nMatchesPerArrival number of arrivals to record per input buffer position + * @param nDepth current insertion depth + */ +static void lzsa_insert_forward_match_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int i, const int nMatchOffset, const int nStartOffset, const int nEndOffset, const int nMatchesPerArrival, int nDepth) { + lzsa_arrival *arrival = pCompressor->arrival - (nStartOffset << MATCHES_PER_ARRIVAL_SHIFT); + int j; + + if (nDepth >= 10) return; + + for (j = 0; j < nMatchesPerArrival && arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].from_slot; j++) { + int nRepOffset = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_offset; + + if (nMatchOffset != nRepOffset && nRepOffset && arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_len >= MIN_MATCH_SIZE_V2) { + int nRepPos = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_pos; + int nRepLen = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_len; + + if (nRepPos > nMatchOffset && + (nRepPos - nMatchOffset + nRepLen) <= (nEndOffset - LAST_LITERALS) && + !memcmp(pInWindow + nRepPos - nRepOffset, pInWindow + nRepPos - nMatchOffset, nRepLen)) { + int nCurRepLen = nRepLen; + + int nMaxRepLen = nEndOffset - nRepPos; + if (nMaxRepLen > LCP_MAX) + nMaxRepLen = LCP_MAX; + while ((nCurRepLen + 8) < nMaxRepLen && !memcmp(pInWindow + nRepPos + nCurRepLen, pInWindow + nRepPos - nMatchOffset + nCurRepLen, 8)) + nCurRepLen += 8; + while ((nCurRepLen + 4) < nMaxRepLen && !memcmp(pInWindow + nRepPos + nCurRepLen, pInWindow + nRepPos - nMatchOffset + nCurRepLen, 4)) + nCurRepLen += 4; + while (nCurRepLen < nMaxRepLen && pInWindow[nRepPos + nCurRepLen] == pInWindow[nRepPos - nMatchOffset + nCurRepLen]) + nCurRepLen++; + + lzsa_match *fwd_match = pCompressor->match + ((nRepPos - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V2); + int exists = 0; + int r; + + for (r = 0; r < NMATCHES_PER_INDEX_V2 && fwd_match[r].length >= MIN_MATCH_SIZE_V2; r++) { + if (fwd_match[r].offset == nMatchOffset) { + exists = 1; + + if (fwd_match[r].length < nCurRepLen) { + fwd_match[r].length = nCurRepLen; + lzsa_insert_forward_match_v2(pCompressor, pInWindow, nRepPos, nMatchOffset, nStartOffset, nEndOffset, nMatchesPerArrival, nDepth + 1); + } + break; + } + } + + if (!exists && r < NMATCHES_PER_INDEX_V2) { + fwd_match[r].offset = nMatchOffset; + fwd_match[r].length = nCurRepLen; + + lzsa_insert_forward_match_v2(pCompressor, pInWindow, nRepPos, nMatchOffset, nStartOffset, nEndOffset, nMatchesPerArrival, nDepth + 1); + } + } + } + } +} + +/** + * Attempt to pick optimal matches using a forward arrivals parser, so as to produce the smallest possible output that decompresses to the same input + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param pBestMatch pointer to buffer for outputting optimal matches + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nReduce non-zero to reduce the number of tokens when the path costs are equal, zero not to + * @param nInsertForwardReps non-zero to insert forward repmatch candidates, zero to use the previously inserted candidates + * @param nMatchesPerArrival number of arrivals to record per input buffer position + */ +static void lzsa_optimize_forward_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset, const int nReduce, const int nInsertForwardReps, const int nMatchesPerArrival) { + lzsa_arrival *arrival = pCompressor->arrival - (nStartOffset << MATCHES_PER_ARRIVAL_SHIFT); + const int nFavorRatio = (pCompressor->flags & LZSA_FLAG_FAVOR_RATIO) ? 1 : 0; + const int nMinMatchSize = pCompressor->min_match_size; + const int nDisableScore = nReduce ? 0 : (2 * BLOCK_SIZE); + const int nLeaveAloneMatchSize = (nMatchesPerArrival == NMATCHES_PER_ARRIVAL_V2_SMALL) ? LEAVE_ALONE_MATCH_SIZE_SMALL : LEAVE_ALONE_MATCH_SIZE; + int i, j, n; + + if ((nEndOffset - nStartOffset) > BLOCK_SIZE) return; + + memset(arrival + (nStartOffset << MATCHES_PER_ARRIVAL_SHIFT), 0, sizeof(lzsa_arrival) * ((nEndOffset - nStartOffset + 1) << MATCHES_PER_ARRIVAL_SHIFT)); + + for (i = (nStartOffset << MATCHES_PER_ARRIVAL_SHIFT); i != ((nEndOffset + 1) << MATCHES_PER_ARRIVAL_SHIFT); i++) { + arrival[i].cost = 0x40000000; + } + + arrival[nStartOffset << MATCHES_PER_ARRIVAL_SHIFT].from_slot = -1; + + for (i = nStartOffset; i != nEndOffset; i++) { + int m; + + for (j = 0; j < nMatchesPerArrival && arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].from_slot; j++) { + const int nPrevCost = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].cost & 0x3fffffff; + int nCodingChoiceCost = nPrevCost + 8 /* literal */; + int nNumLiterals = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].num_literals + 1; + + if (nNumLiterals == LITERALS_RUN_LEN_V2) { + nCodingChoiceCost += 4; + } + else if (nNumLiterals == (LITERALS_RUN_LEN_V2 + 15)) { + nCodingChoiceCost += 8; + } + else if (nNumLiterals == 256) { + nCodingChoiceCost += 16; + } + + if (!nFavorRatio && nNumLiterals == 1) + nCodingChoiceCost += MODESWITCH_PENALTY; + + lzsa_arrival *pDestSlots = &arrival[(i + 1) << MATCHES_PER_ARRIVAL_SHIFT]; + if (nCodingChoiceCost <= pDestSlots[nMatchesPerArrival - 1].cost) { + int exists = 0; + for (n = 0; + n < nMatchesPerArrival && pDestSlots[n].cost <= nCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_offset) { + exists = 1; + break; + } + } + + if (!exists) { + int nScore = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].score + 1; + for (n = 0; n < nMatchesPerArrival; n++) { + lzsa_arrival *pDestArrival = &pDestSlots[n]; + if (nCodingChoiceCost < pDestArrival->cost || + (nCodingChoiceCost == pDestArrival->cost && nScore < (pDestArrival->score + nDisableScore))) { + + if (pDestArrival->from_slot) { + int z; + + for (z = n; z < nMatchesPerArrival - 1; z++) { + if (pDestSlots[z].rep_offset == arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_offset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (z - n)); + } + + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_offset = 0; + pDestArrival->match_len = 0; + pDestArrival->num_literals = nNumLiterals; + pDestArrival->score = nScore; + pDestArrival->rep_offset = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_offset; + pDestArrival->rep_pos = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_pos; + pDestArrival->rep_len = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_len; + break; + } + } + } + } + } + + lzsa_match *match = pCompressor->match + ((i - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V2); + + int nMinRepLen[NMATCHES_PER_ARRIVAL_V2_BIG]; + memset(nMinRepLen, 0, nMatchesPerArrival * sizeof(int)); + + for (m = 0; m < NMATCHES_PER_INDEX_V2 && match[m].length; m++) { + int nMatchLen = match[m].length & 0x7fff; + int nMatchOffset = match[m].offset; + int nScorePenalty = ((match[m].length & 0x8000) >> 15); + int nNoRepmatchOffsetCost = (nMatchOffset <= 32) ? 4 : ((nMatchOffset <= 512) ? 8 : ((nMatchOffset <= (8192 + 512)) ? 12 : 16)); + int nStartingMatchLen, k; + int nMaxRepLen[NMATCHES_PER_ARRIVAL_V2_BIG]; + + if ((i + nMatchLen) > (nEndOffset - LAST_LITERALS)) + nMatchLen = nEndOffset - LAST_LITERALS - i; + + for (j = 0; j < nMatchesPerArrival && arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].from_slot; j++) { + int nRepOffset = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_offset; + int nCurMaxRepLen = 0; + + if (nRepOffset) { + if (nMatchOffset == nRepOffset) + nCurMaxRepLen = nMatchLen; + else { + if (i > nRepOffset && + (i - nRepOffset + nMatchLen) <= (nEndOffset - LAST_LITERALS)) { + nCurMaxRepLen = nMinRepLen[j]; + while ((nCurMaxRepLen + 8) < nMatchLen && !memcmp(pInWindow + i - nRepOffset + nCurMaxRepLen, pInWindow + i + nCurMaxRepLen, 8)) + nCurMaxRepLen += 8; + while ((nCurMaxRepLen + 4) < nMatchLen && !memcmp(pInWindow + i - nRepOffset + nCurMaxRepLen, pInWindow + i + nCurMaxRepLen, 4)) + nCurMaxRepLen += 4; + while (nCurMaxRepLen < nMatchLen && pInWindow[i - nRepOffset + nCurMaxRepLen] == pInWindow[i + nCurMaxRepLen]) + nCurMaxRepLen++; + nMinRepLen[j] = nCurMaxRepLen; + } + } + } + + nMaxRepLen[j] = nCurMaxRepLen; + } + while (j < nMatchesPerArrival) + nMaxRepLen[j++] = 0; + + if (nInsertForwardReps) + lzsa_insert_forward_match_v2(pCompressor, pInWindow, i, nMatchOffset, nStartOffset, nEndOffset, nMatchesPerArrival, 0); + + int nMatchLenCost = 0; + if (nMatchLen >= nLeaveAloneMatchSize) { + nStartingMatchLen = nMatchLen; + nMatchLenCost = 4 + 24; + } + else { + nStartingMatchLen = nMinMatchSize; + nMatchLenCost = 0; + } + + for (k = nStartingMatchLen; k <= nMatchLen; k++) { + if (k == (MATCH_RUN_LEN_V2 + MIN_MATCH_SIZE_V2)) { + nMatchLenCost = 4; + } + else { + if (k == (MATCH_RUN_LEN_V2 + 15 + MIN_MATCH_SIZE_V2)) + nMatchLenCost = 4 + 8; + else { + if (k == 256) + nMatchLenCost = 4 + 24; + } + } + + lzsa_arrival *pDestSlots = &arrival[(i + k) << MATCHES_PER_ARRIVAL_SHIFT]; + int nInsertedNoRepMatchCandidate = 0; + + for (j = 0; j < nMatchesPerArrival && arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].from_slot; j++) { + const int nPrevCost = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].cost & 0x3fffffff; + int nRepCodingChoiceCost = nPrevCost + 8 /* token */ /* the actual cost of the literals themselves accumulates up the chain */ + nMatchLenCost; + + if (nRepCodingChoiceCost <= pDestSlots[nMatchesPerArrival - 1].cost) { + int nRepOffset = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].rep_offset; + + if (nMatchOffset != nRepOffset && !nInsertedNoRepMatchCandidate) { + int nCodingChoiceCost = nRepCodingChoiceCost + nNoRepmatchOffsetCost; + + if (!nFavorRatio && !arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].num_literals) + nCodingChoiceCost += MODESWITCH_PENALTY; + + if (nCodingChoiceCost <= pDestSlots[nMatchesPerArrival - 1].cost) { + int exists = 0; + int nScore = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].score + 3 + nScorePenalty; + + for (n = 0; + n < nMatchesPerArrival && pDestSlots[n].cost <= nCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nMatchOffset && + (!nInsertForwardReps || pDestSlots[n].cost != nCodingChoiceCost || pDestSlots[n].rep_pos >= i || nScore >= (pDestSlots[n].score + nDisableScore) || + pDestSlots[nMatchesPerArrival - 1].from_slot)) { + exists = 1; + break; + } + } + + if (!exists) { + for (n = 0; n < nMatchesPerArrival - 1; n++) { + lzsa_arrival *pDestArrival = &pDestSlots[n]; + + if (nCodingChoiceCost < pDestArrival->cost || + (nCodingChoiceCost == pDestArrival->cost && nScore < (pDestArrival->score + nDisableScore))) { + if (pDestArrival->from_slot) { + int z; + + for (z = n; z < nMatchesPerArrival - 1; z++) { + if (pDestSlots[z].rep_offset == nMatchOffset) + break; + } + + if (z == (nMatchesPerArrival - 1) && pDestSlots[z].from_slot && pDestSlots[z].match_len < MIN_MATCH_SIZE_V2) + z--; + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (z - n)); + } + + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_offset = nMatchOffset; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + pDestArrival->rep_offset = nMatchOffset; + pDestArrival->rep_pos = i; + pDestArrival->rep_len = k; + nInsertedNoRepMatchCandidate = 1; + break; + } + } + } + } + } + + /* If this coding choice doesn't rep-match, see if we still get a match by using the current repmatch offset for this arrival. This can occur (and not have the + * matchfinder offer the offset in the first place, or have too many choices with the same cost to retain the repmatchable offset) when compressing regions + * of identical bytes, for instance. Checking for this provides a big compression win on some files. */ + + if (nMaxRepLen[j] >= k) { + int exists = 0; + + /* A match is possible at the rep offset; insert the extra coding choice. */ + + for (n = 0; + n < nMatchesPerArrival && pDestSlots[n].cost <= nRepCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int nScore = arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + j].score + 2; + + for (n = 0; n < nMatchesPerArrival; n++) { + lzsa_arrival *pDestArrival = &pDestSlots[n]; + + if (nRepCodingChoiceCost < pDestArrival->cost || + (nRepCodingChoiceCost == pDestArrival->cost && nScore < (pDestArrival->score + nDisableScore))) { + if (pDestArrival->from_slot) { + int z; + + for (z = n; z < nMatchesPerArrival - 1; z++) { + if (pDestSlots[z].rep_offset == nRepOffset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (z - n)); + } + + pDestArrival->cost = nRepCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_offset = nRepOffset; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + pDestArrival->rep_offset = nRepOffset; + pDestArrival->rep_pos = i; + pDestArrival->rep_len = k; + break; + } + } + } + } + } + else { + break; + } + } + } + + if (nMatchLen >= LCP_MAX && ((m + 1) >= NMATCHES_PER_INDEX_V2 || match[m + 1].length < LCP_MAX)) + break; + } + } + + lzsa_arrival *end_arrival = &arrival[(i << MATCHES_PER_ARRIVAL_SHIFT) + 0]; + + while (end_arrival->from_slot > 0 && end_arrival->from_pos >= 0) { + if (end_arrival->from_pos >= nEndOffset) return; + pBestMatch[end_arrival->from_pos].length = end_arrival->match_len; + pBestMatch[end_arrival->from_pos].offset = end_arrival->match_offset; + end_arrival = &arrival[(end_arrival->from_pos << MATCHES_PER_ARRIVAL_SHIFT) + (end_arrival->from_slot - 1)]; + } +} + +/** + * Attempt to minimize the number of commands issued in the compressed data block, in order to speed up decompression without + * impacting the compression ratio + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param pBestMatch optimal matches to evaluate and update + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return non-zero if the number of tokens was reduced, 0 if it wasn't + */ +static int lzsa_optimize_command_count_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nPrevRepMatchOffset = 0; + int nRepMatchOffset = 0; + int nRepMatchLen = 0; + int nRepIndex = 0; + int nDidReduce = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length == 0 && + (i + 1) < (nEndOffset - LAST_LITERALS) && + pBestMatch[i + 1].length >= MIN_MATCH_SIZE_V2 && + pBestMatch[i + 1].length < MAX_VARLEN && + pBestMatch[i + 1].offset && + i >= pBestMatch[i + 1].offset && + (i + pBestMatch[i + 1].length + 1) <= (nEndOffset - LAST_LITERALS) && + !memcmp(pInWindow + i - (pBestMatch[i + 1].offset), pInWindow + i, pBestMatch[i + 1].length + 1)) { + int nCurLenSize = lzsa_get_match_varlen_size_v2(pBestMatch[i + 1].length - MIN_MATCH_SIZE_V2); + int nReducedLenSize = lzsa_get_match_varlen_size_v2(pBestMatch[i + 1].length + 1 - MIN_MATCH_SIZE_V2); + + if ((nReducedLenSize - nCurLenSize) <= 8) { + /* Merge */ + pBestMatch[i].length = pBestMatch[i + 1].length + 1; + pBestMatch[i].offset = pBestMatch[i + 1].offset; + pBestMatch[i + 1].length = 0; + pBestMatch[i + 1].offset = 0; + nDidReduce = 1; + continue; + } + } + + if (pMatch->length >= MIN_MATCH_SIZE_V2) { + if ((i + pMatch->length) < nEndOffset /* Don't consider the last match in the block, we can only reduce a match inbetween other tokens */) { + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < MIN_MATCH_SIZE_V2) { + nNextLiterals++; + nNextIndex++; + } + + if (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length >= MIN_MATCH_SIZE_V2) { + /* This command is a match, is followed by 'nNextLiterals' literals and then by another match */ + + if (nRepMatchOffset && pMatch->offset != nRepMatchOffset && (pBestMatch[nNextIndex].offset != pMatch->offset || pBestMatch[nNextIndex].offset == nRepMatchOffset || + ((pMatch->offset <= 32) ? 4 : ((pMatch->offset <= 512) ? 8 : ((pMatch->offset <= (8192 + 512)) ? 12 : 16))) > + ((pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16))))) { + /* Check if we can change the current match's offset to be the same as the previous match's offset, and get an extra repmatch. This will occur when + * matching large regions of identical bytes for instance, where there are too many offsets to be considered by the parser, and when not compressing to favor the + * ratio (the forward arrivals parser already has this covered). */ + if (i > nRepMatchOffset && + (i - nRepMatchOffset + pMatch->length) <= (nEndOffset - LAST_LITERALS) && + !memcmp(pInWindow + i - nRepMatchOffset, pInWindow + i - pMatch->offset, pMatch->length)) { + pMatch->offset = nRepMatchOffset; + nDidReduce = 1; + } + } + + if (pBestMatch[nNextIndex].offset && pMatch->offset != pBestMatch[nNextIndex].offset && nRepMatchOffset != pBestMatch[nNextIndex].offset) { + /* Otherwise, try to gain a match forward as well */ + if (i > pBestMatch[nNextIndex].offset && (i - pBestMatch[nNextIndex].offset + pMatch->length) <= (nEndOffset - LAST_LITERALS)) { + int nMaxLen = 0; + while (nMaxLen < pMatch->length && pInWindow[i - pBestMatch[nNextIndex].offset + nMaxLen] == pInWindow[i - pMatch->offset + nMaxLen]) + nMaxLen++; + if (nMaxLen >= pMatch->length) { + /* Replace */ + pMatch->offset = pBestMatch[nNextIndex].offset; + nDidReduce = 1; + } + else if (nMaxLen >= 2 && pMatch->offset != nRepMatchOffset) { + int nPartialSizeBefore, nPartialSizeAfter; + + nPartialSizeBefore = lzsa_get_match_varlen_size_v2(pMatch->length - MIN_MATCH_SIZE_V2); + nPartialSizeBefore += (pMatch->offset <= 32) ? 4 : ((pMatch->offset <= 512) ? 8 : ((pMatch->offset <= (8192 + 512)) ? 12 : 16)); + nPartialSizeBefore += lzsa_get_literals_varlen_size_v2(nNextLiterals); + + nPartialSizeAfter = lzsa_get_match_varlen_size_v2(nMaxLen - MIN_MATCH_SIZE_V2); + nPartialSizeAfter += lzsa_get_literals_varlen_size_v2(nNextLiterals + (pMatch->length - nMaxLen)) + ((pMatch->length - nMaxLen) << 3); + + if (nPartialSizeAfter < nPartialSizeBefore) { + int j; + + /* We gain a repmatch that is shorter than the original match as this is the best we can do, so it is followed by extra literals, but + * we have calculated that this is shorter */ + pMatch->offset = pBestMatch[nNextIndex].offset; + for (j = nMaxLen; j < pMatch->length; j++) { + pBestMatch[i + j].length = 0; + } + pMatch->length = nMaxLen; + nDidReduce = 1; + } + } + } + } + + if (pMatch->length < 9 /* Don't waste time considering large matches, they will always win over literals */) { + /* Calculate this command's current cost (excluding 'nNumLiterals' bytes) */ + + int nCurCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + lzsa_get_match_varlen_size_v2(pMatch->length - MIN_MATCH_SIZE_V2); + if (pMatch->offset != nRepMatchOffset) + nCurCommandSize += (pMatch->offset <= 32) ? 4 : ((pMatch->offset <= 512) ? 8 : ((pMatch->offset <= (8192 + 512)) ? 12 : 16)); + + /* Calculate the next command's current cost */ + int nNextCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNextLiterals) + (nNextLiterals << 3) + lzsa_get_match_varlen_size_v2(pBestMatch[nNextIndex].length - MIN_MATCH_SIZE_V2); + if (pBestMatch[nNextIndex].offset != pMatch->offset) + nNextCommandSize += (pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16)); + + int nOriginalCombinedCommandSize = nCurCommandSize + nNextCommandSize; + + /* Calculate the cost of replacing this match command by literals + the next command with the cost of encoding these literals (excluding 'nNumLiterals' bytes) */ + int nReducedCommandSize = (pMatch->length << 3) + 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals + pMatch->length + nNextLiterals) + (nNextLiterals << 3) + lzsa_get_match_varlen_size_v2(pBestMatch[nNextIndex].length - MIN_MATCH_SIZE_V2); + if (pBestMatch[nNextIndex].offset != nRepMatchOffset) + nReducedCommandSize += (pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16)); + + int nReplaceRepOffset = 0; + if (nRepMatchOffset && nRepMatchOffset != nPrevRepMatchOffset && nRepMatchLen >= MIN_MATCH_SIZE_V2 && nRepMatchOffset != pBestMatch[nNextIndex].offset && nRepIndex > pBestMatch[nNextIndex].offset && + (nRepIndex - pBestMatch[nNextIndex].offset + nRepMatchLen) <= (nEndOffset - LAST_LITERALS) && + !memcmp(pInWindow + nRepIndex - nRepMatchOffset, pInWindow + nRepIndex - pBestMatch[nNextIndex].offset, nRepMatchLen)) { + /* Replacing this match command by literals would let us create a repmatch */ + nReplaceRepOffset = 1; + nReducedCommandSize -= (nRepMatchOffset <= 32) ? 4 : ((nRepMatchOffset <= 512) ? 8 : ((nRepMatchOffset <= (8192 + 512)) ? 12 : 16)); + } + + if (nOriginalCombinedCommandSize >= nReducedCommandSize) { + /* Reduce */ + int nMatchLen = pMatch->length; + int j; + + for (j = 0; j < nMatchLen; j++) { + pBestMatch[i + j].length = 0; + } + + nDidReduce = 1; + + if (nReplaceRepOffset) { + pBestMatch[nRepIndex].offset = pBestMatch[nNextIndex].offset; + nRepMatchOffset = pBestMatch[nNextIndex].offset; + } + continue; + } + } + } + } + + if ((i + pMatch->length) <= nEndOffset && pMatch->offset > 0 && pMatch->length >= MIN_MATCH_SIZE_V2 && + pBestMatch[i + pMatch->length].offset > 0 && + pBestMatch[i + pMatch->length].length >= MIN_MATCH_SIZE_V2 && + (pMatch->length + pBestMatch[i + pMatch->length].length) >= LEAVE_ALONE_MATCH_SIZE && + (pMatch->length + pBestMatch[i + pMatch->length].length) <= MAX_VARLEN && + (i + pMatch->length) > pMatch->offset && + (i + pMatch->length) > pBestMatch[i + pMatch->length].offset && + (i + pMatch->length + pBestMatch[i + pMatch->length].length) <= nEndOffset && + !memcmp(pInWindow + i - pMatch->offset + pMatch->length, + pInWindow + i + pMatch->length - pBestMatch[i + pMatch->length].offset, + pBestMatch[i + pMatch->length].length)) { + + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < MIN_MATCH_SIZE_V2) { + nNextLiterals++; + nNextIndex++; + } + + int nCurPartialSize = lzsa_get_match_varlen_size_v2(pMatch->length - MIN_MATCH_SIZE_V2); + + nCurPartialSize += 8 /* token */ + lzsa_get_literals_varlen_size_v2(0) + lzsa_get_match_varlen_size_v2(pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V2); + if (pBestMatch[i + pMatch->length].offset != pMatch->offset) + nCurPartialSize += (pBestMatch[i + pMatch->length].offset <= 32) ? 4 : ((pBestMatch[i + pMatch->length].offset <= 512) ? 8 : ((pBestMatch[i + pMatch->length].offset <= (8192 + 512)) ? 12 : 16)); + + if (pBestMatch[nNextIndex].offset != pBestMatch[i + pMatch->length].offset) + nCurPartialSize += (pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16)); + + int nReducedPartialSize = lzsa_get_match_varlen_size_v2(pMatch->length + pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V2); + + if (pBestMatch[nNextIndex].offset != pMatch->offset) + nReducedPartialSize += (pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16)); + + if (nCurPartialSize >= nReducedPartialSize) { + int nMatchLen = pMatch->length; + + /* Join */ + + pMatch->length += pBestMatch[i + nMatchLen].length; + pBestMatch[i + nMatchLen].offset = 0; + pBestMatch[i + nMatchLen].length = -1; + nDidReduce = 1; + continue; + } + } + + nPrevRepMatchOffset = nRepMatchOffset; + nRepMatchOffset = pMatch->offset; + nRepMatchLen = pMatch->length; + nRepIndex = i; + + i += pMatch->length; + nNumLiterals = 0; + } + else { + nNumLiterals++; + i++; + } + } + + return nDidReduce; +} + +/** + * Get compressed data block size + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return size of compressed data that will be written to output buffer + */ +static int lzsa_get_compressed_size_v2(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nOutOffset = 0; + int nRepMatchOffset = 0; + int nCompressedSize = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length >= MIN_MATCH_SIZE_V2) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V2; + int nOffsetSize; + + if (nMatchOffset == nRepMatchOffset) { + nOffsetSize = 0; + } + else { + if (nMatchOffset <= 32) { + nOffsetSize = 4; + } + else if (nMatchOffset <= 512) { + nOffsetSize = 8; + } + else if (nMatchOffset <= (8192 + 512)) { + nOffsetSize = 12; + } + else { + nOffsetSize = 16; + } + } + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3) + nOffsetSize /* match offset */ + lzsa_get_match_varlen_size_v2(nEncodedMatchLen); + nCompressedSize += nCommandSize; + + nNumLiterals = 0; + nRepMatchOffset = nMatchOffset; + i += nMatchLen; + } + else { + nNumLiterals++; + i++; + } + } + + { + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3); + + nCompressedSize += nCommandSize; + nNumLiterals = 0; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nCompressedSize += (8 + 4 + 8); + } + + return nCompressedSize; +} + +/** + * Emit block of compressed data + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_block_v2(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int i; + int nNumLiterals = 0; + int nInFirstLiteralOffset = 0; + int nOutOffset = 0; + int nCurNibbleOffset = -1, nCurFreeNibbles = 0; + int nRepMatchOffset = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length >= MIN_MATCH_SIZE_V2) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V2; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nTokenMatchLen = (nEncodedMatchLen >= MATCH_RUN_LEN_V2) ? MATCH_RUN_LEN_V2 : nEncodedMatchLen; + int nTokenOffsetMode; + int nOffsetSize; + + if (nMatchOffset == nRepMatchOffset) { + nTokenOffsetMode = 0xe0; + nOffsetSize = 0; + } + else { + if (nMatchOffset <= 32) { + nTokenOffsetMode = 0x00 | ((((-nMatchOffset) & 0x01) << 5) ^ 0x20); + nOffsetSize = 4; + } + else if (nMatchOffset <= 512) { + nTokenOffsetMode = 0x40 | ((((-nMatchOffset) & 0x100) >> 3) ^ 0x20); + nOffsetSize = 8; + } + else if (nMatchOffset <= (8192 + 512)) { + nTokenOffsetMode = 0x80 | ((((-(nMatchOffset - 512)) & 0x0100) >> 3) ^ 0x20); + nOffsetSize = 12; + } + else { + nTokenOffsetMode = 0xc0; + nOffsetSize = 16; + } + } + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3) + nOffsetSize /* match offset */ + lzsa_get_match_varlen_size_v2(nEncodedMatchLen); + + if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize) + return -1; + if (nMatchOffset < MIN_OFFSET || nMatchOffset > MAX_OFFSET) + return -1; + + pOutData[nOutOffset++] = nTokenOffsetMode | (nTokenLiteralsLen << 3) | nTokenMatchLen; + nOutOffset = lzsa_write_literals_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, nNumLiterals); + if (nOutOffset < 0) return -1; + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + if (nTokenOffsetMode == 0x00 || nTokenOffsetMode == 0x20) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, ((-nMatchOffset) & 0x1e) >> 1); + if (nOutOffset < 0) return -1; + } + else if (nTokenOffsetMode == 0x40 || nTokenOffsetMode == 0x60) { + pOutData[nOutOffset++] = (-nMatchOffset) & 0xff; + } + else if (nTokenOffsetMode == 0x80 || nTokenOffsetMode == 0xa0) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, ((-(nMatchOffset - 512)) >> 9) & 0x0f); + if (nOutOffset < 0) return -1; + pOutData[nOutOffset++] = (-(nMatchOffset - 512)) & 0xff; + } + else if (nTokenOffsetMode == 0xc0) { + pOutData[nOutOffset++] = (-nMatchOffset) >> 8; + pOutData[nOutOffset++] = (-nMatchOffset) & 0xff; + } + + if (nMatchOffset == nRepMatchOffset) + pCompressor->stats.num_rep_offsets++; + + nRepMatchOffset = nMatchOffset; + + nOutOffset = lzsa_write_match_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, nEncodedMatchLen); + if (nOutOffset < 0) return -1; + + if (nMatchOffset < pCompressor->stats.min_offset || pCompressor->stats.min_offset == -1) + pCompressor->stats.min_offset = nMatchOffset; + if (nMatchOffset > pCompressor->stats.max_offset) + pCompressor->stats.max_offset = nMatchOffset; + pCompressor->stats.total_offsets += nMatchOffset; + + if (nMatchLen < pCompressor->stats.min_match_len || pCompressor->stats.min_match_len == -1) + pCompressor->stats.min_match_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_match_len) + pCompressor->stats.max_match_len = nMatchLen; + pCompressor->stats.total_match_lens += nMatchLen; + pCompressor->stats.match_divisor++; + + if (nMatchOffset == 1) { + if (nMatchLen < pCompressor->stats.min_rle1_len || pCompressor->stats.min_rle1_len == -1) + pCompressor->stats.min_rle1_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle1_len) + pCompressor->stats.max_rle1_len = nMatchLen; + pCompressor->stats.total_rle1_lens += nMatchLen; + pCompressor->stats.rle1_divisor++; + } + else if (nMatchOffset == 2) { + if (nMatchLen < pCompressor->stats.min_rle2_len || pCompressor->stats.min_rle2_len == -1) + pCompressor->stats.min_rle2_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle2_len) + pCompressor->stats.max_rle2_len = nMatchLen; + pCompressor->stats.total_rle2_lens += nMatchLen; + pCompressor->stats.rle2_divisor++; + } + + i += nMatchLen; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + else { + if (nNumLiterals == 0) + nInFirstLiteralOffset = i; + nNumLiterals++; + i++; + } + } + + { + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3); + + if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize) + return -1; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) + pOutData[nOutOffset++] = (nTokenLiteralsLen << 3) | 0x47; + else + pOutData[nOutOffset++] = (nTokenLiteralsLen << 3) | 0x00; + nOutOffset = lzsa_write_literals_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, nNumLiterals); + if (nOutOffset < 0) return -1; + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + /* Emit EOD marker for raw block */ + + if (nOutOffset >= nMaxOutDataSize) + return -1; + pOutData[nOutOffset++] = 0; /* Match offset */ + + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, 15); /* Extended match length nibble */ + if (nOutOffset < 0) return -1; + + if ((nOutOffset + 1) > nMaxOutDataSize) + return -1; + + pOutData[nOutOffset++] = 232; /* EOD match length byte */ + } + + if (nCurNibbleOffset != -1) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, 0); + if (nOutOffset < 0 || nCurNibbleOffset != -1) + return -1; + } + + return nOutOffset; +} + +/** + * Emit raw block of uncompressible data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_raw_uncompressed_block_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int nCurNibbleOffset = -1, nCurFreeNibbles = 0; + int nNumLiterals = nEndOffset - nStartOffset; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nOutOffset = 0; + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3) + 8 + 4 + 8; + if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize) + return -1; + + pCompressor->num_commands = 0; + pOutData[nOutOffset++] = (nTokenLiteralsLen << 3) | 0x47; + + nOutOffset = lzsa_write_literals_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, nNumLiterals); + if (nOutOffset < 0) return -1; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nStartOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + /* Emit EOD marker for raw block */ + + pOutData[nOutOffset++] = 0; /* Match offset */ + + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, 15); /* Extended match length nibble */ + if (nOutOffset < 0) return -1; + + if ((nOutOffset + 1) > nMaxOutDataSize) + return -1; + + pOutData[nOutOffset++] = 232; /* EOD match length byte */ + + pCompressor->num_commands++; + + if (nCurNibbleOffset != -1) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, &nCurFreeNibbles, 0); + if (nOutOffset < 0 || nCurNibbleOffset != -1) + return -1; + } + + return nOutOffset; +} + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed LZSA2 data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_optimize_and_write_block_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize) { + int nResult, nBaseCompressedSize; + int nMatchesPerArrival = (nInDataSize < 65536) ? NMATCHES_PER_ARRIVAL_V2_BIG : NMATCHES_PER_ARRIVAL_V2_SMALL; + + /* Compress optimally without breaking ties in favor of less tokens */ + + memset(pCompressor->best_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 0 /* reduce */, (nInDataSize < 65536) ? 1 : 0 /* insert forward reps */, nMatchesPerArrival); + + int nDidReduce; + int nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nBaseCompressedSize = lzsa_get_compressed_size_v2(pCompressor, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + lzsa_match *pBestMatch = pCompressor->best_match - nPreviousBlockSize; + + if (nBaseCompressedSize > 0 && nInDataSize < 65536) { + int nReducedCompressedSize; + + /* Compress optimally and do break ties in favor of less tokens */ + memset(pCompressor->improved_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v2(pCompressor, pInWindow, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 1 /* reduce */, 0 /* use forward reps */, nMatchesPerArrival); + + nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v2(pCompressor, pInWindow, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nReducedCompressedSize = lzsa_get_compressed_size_v2(pCompressor, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + if (nReducedCompressedSize > 0 && nReducedCompressedSize <= nBaseCompressedSize) { + /* Pick the parse with the reduced number of tokens as it didn't negatively affect the size */ + pBestMatch = pCompressor->improved_match - nPreviousBlockSize; + } + } + + nResult = lzsa_write_block_v2(pCompressor, pBestMatch, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + if (nResult < 0 && pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nResult = lzsa_write_raw_uncompressed_block_v2(pCompressor, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + } + + return nResult; +} diff --git a/Tools/unix/lzsa/src/shrink_block_v2.h b/Tools/unix/lzsa/src/shrink_block_v2.h new file mode 100644 index 00000000..4a836087 --- /dev/null +++ b/Tools/unix/lzsa/src/shrink_block_v2.h @@ -0,0 +1,53 @@ +/* + * shrink_block_v2.h - LZSA2 block compressor definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_BLOCK_V2_H +#define _SHRINK_BLOCK_V2_H + +/* Forward declarations */ +typedef struct _lzsa_compressor lzsa_compressor; + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed LZSA2 data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_optimize_and_write_block_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize); + +#endif /* _SHRINK_BLOCK_V2_H */ diff --git a/Tools/unix/lzsa/src/shrink_context.c b/Tools/unix/lzsa/src/shrink_context.c new file mode 100644 index 00000000..c1e7ab3d --- /dev/null +++ b/Tools/unix/lzsa/src/shrink_context.c @@ -0,0 +1,221 @@ +/* + * shrink_context.c - compression context implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "shrink_context.h" +#include "shrink_block_v1.h" +#include "shrink_block_v2.h" +#include "format.h" +#include "matchfinder.h" +#include "lib.h" + +/** + * Initialize compression context + * + * @param pCompressor compression context to initialize + * @param nMaxWindowSize maximum size of input data window (previously compressed bytes + bytes to compress) + * @param nMinMatchSize minimum match size (cannot be less than MIN_MATCH_SIZE) + * @param nFlags compression flags + * + * @return 0 for success, non-zero for failure + */ +int lzsa_compressor_init(lzsa_compressor *pCompressor, const int nMaxWindowSize, const int nMinMatchSize, const int nFormatVersion, const int nFlags) { + int nResult; + int nMinMatchSizeForFormat = (nFormatVersion == 1) ? MIN_MATCH_SIZE_V1 : MIN_MATCH_SIZE_V2; + int nMaxMinMatchForFormat = (nFormatVersion == 1) ? 5 : 3; + + nResult = divsufsort_init(&pCompressor->divsufsort_context); + pCompressor->intervals = NULL; + pCompressor->pos_data = NULL; + pCompressor->open_intervals = NULL; + pCompressor->match = NULL; + pCompressor->best_match = NULL; + pCompressor->improved_match = NULL; + pCompressor->arrival = NULL; + pCompressor->min_match_size = nMinMatchSize; + if (pCompressor->min_match_size < nMinMatchSizeForFormat) + pCompressor->min_match_size = nMinMatchSizeForFormat; + else if (pCompressor->min_match_size > nMaxMinMatchForFormat) + pCompressor->min_match_size = nMaxMinMatchForFormat; + pCompressor->format_version = nFormatVersion; + pCompressor->flags = nFlags; + pCompressor->safe_dist = 0; + pCompressor->num_commands = 0; + + memset(&pCompressor->stats, 0, sizeof(pCompressor->stats)); + pCompressor->stats.min_literals = -1; + pCompressor->stats.min_match_len = -1; + pCompressor->stats.min_offset = -1; + pCompressor->stats.min_rle1_len = -1; + pCompressor->stats.min_rle2_len = -1; + + if (!nResult) { + pCompressor->intervals = (unsigned int *)malloc(nMaxWindowSize * sizeof(unsigned int)); + + if (pCompressor->intervals) { + pCompressor->pos_data = (unsigned int *)malloc(nMaxWindowSize * sizeof(unsigned int)); + + if (pCompressor->pos_data) { + pCompressor->open_intervals = (unsigned int *)malloc((LCP_AND_TAG_MAX + 1) * sizeof(unsigned int)); + + if (pCompressor->open_intervals) { + pCompressor->arrival = (lzsa_arrival *)malloc(((BLOCK_SIZE + 1) << MATCHES_PER_ARRIVAL_SHIFT) * sizeof(lzsa_arrival)); + + if (pCompressor->arrival) { + pCompressor->best_match = (lzsa_match *)malloc(BLOCK_SIZE * sizeof(lzsa_match)); + + if (pCompressor->best_match) { + pCompressor->improved_match = (lzsa_match *)malloc(BLOCK_SIZE * sizeof(lzsa_match)); + + if (pCompressor->improved_match) { + if (pCompressor->format_version == 2) + pCompressor->match = (lzsa_match *)malloc(BLOCK_SIZE * NMATCHES_PER_INDEX_V2 * sizeof(lzsa_match)); + else + pCompressor->match = (lzsa_match *)malloc(BLOCK_SIZE * NMATCHES_PER_INDEX_V1 * sizeof(lzsa_match)); + if (pCompressor->match) + return 0; + } + } + } + } + } + } + } + + lzsa_compressor_destroy(pCompressor); + return 100; +} + +/** + * Clean up compression context and free up any associated resources + * + * @param pCompressor compression context to clean up + */ +void lzsa_compressor_destroy(lzsa_compressor *pCompressor) { + divsufsort_destroy(&pCompressor->divsufsort_context); + + if (pCompressor->match) { + free(pCompressor->match); + pCompressor->match = NULL; + } + + if (pCompressor->improved_match) { + free(pCompressor->improved_match); + pCompressor->improved_match = NULL; + } + + if (pCompressor->arrival) { + free(pCompressor->arrival); + pCompressor->arrival = NULL; + } + + if (pCompressor->best_match) { + free(pCompressor->best_match); + pCompressor->best_match = NULL; + } + + if (pCompressor->open_intervals) { + free(pCompressor->open_intervals); + pCompressor->open_intervals = NULL; + } + + if (pCompressor->pos_data) { + free(pCompressor->pos_data); + pCompressor->pos_data = NULL; + } + + if (pCompressor->intervals) { + free(pCompressor->intervals); + pCompressor->intervals = NULL; + } +} + +/** + * Compress one block of data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_compressor_shrink_block(lzsa_compressor *pCompressor, unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize) { + int nCompressedSize; + + if (pCompressor->flags & LZSA_FLAG_RAW_BACKWARD) { + lzsa_reverse_buffer(pInWindow + nPreviousBlockSize, nInDataSize); + } + + if (lzsa_build_suffix_array(pCompressor, pInWindow, nPreviousBlockSize + nInDataSize)) + nCompressedSize = -1; + else { + if (nPreviousBlockSize) { + lzsa_skip_matches(pCompressor, 0, nPreviousBlockSize); + } + lzsa_find_all_matches(pCompressor, (pCompressor->format_version == 2) ? NMATCHES_PER_INDEX_V2 : NMATCHES_PER_INDEX_V1, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + + if (pCompressor->format_version == 1) { + nCompressedSize = lzsa_optimize_and_write_block_v1(pCompressor, pInWindow, nPreviousBlockSize, nInDataSize, pOutData, nMaxOutDataSize); + if (nCompressedSize != -1 && (pCompressor->flags & LZSA_FLAG_RAW_BACKWARD)) { + lzsa_reverse_buffer(pOutData, nCompressedSize); + } + } + else if (pCompressor->format_version == 2) { + nCompressedSize = lzsa_optimize_and_write_block_v2(pCompressor, pInWindow, nPreviousBlockSize, nInDataSize, pOutData, nMaxOutDataSize); + if (nCompressedSize != -1 && (pCompressor->flags & LZSA_FLAG_RAW_BACKWARD)) { + lzsa_reverse_buffer(pOutData, nCompressedSize); + } + } + else { + nCompressedSize = -1; + } + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BACKWARD) { + lzsa_reverse_buffer(pInWindow + nPreviousBlockSize, nInDataSize); + } + + return nCompressedSize; +} + +/** + * Get the number of compression commands issued in compressed data blocks + * + * @return number of commands + */ +int lzsa_compressor_get_command_count(lzsa_compressor *pCompressor) { + return pCompressor->num_commands; +} diff --git a/Tools/unix/lzsa/src/shrink_context.h b/Tools/unix/lzsa/src/shrink_context.h new file mode 100644 index 00000000..70245cf6 --- /dev/null +++ b/Tools/unix/lzsa/src/shrink_context.h @@ -0,0 +1,183 @@ +/* + * shrink_context.h - compression context definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_CONTEXT_H +#define _SHRINK_CONTEXT_H + +#include "divsufsort.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LCP_BITS 14 +#define TAG_BITS 4 +#define LCP_MAX ((1U<<(LCP_BITS - TAG_BITS)) - 1) +#define LCP_AND_TAG_MAX (1U<<(LCP_BITS - 1)) +#define LCP_SHIFT (31-LCP_BITS) +#define LCP_MASK (((1U< + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "shrink_inmem.h" +#include "shrink_context.h" +#include "frame.h" +#include "format.h" +#include "lib.h" + +/** + * Get maximum compressed size of input(source) data + * + * @param nInputSize input(source) size in bytes + * + * @return maximum compressed size + */ +size_t lzsa_get_max_compressed_size_inmem(size_t nInputSize) { + return lzsa_get_header_size() + ((nInputSize + (BLOCK_SIZE - 1)) >> 16) * lzsa_get_frame_size() + nInputSize + lzsa_get_frame_size() /* footer */; +} + +/** + * Compress memory + * + * @param pInputData pointer to input(source) data to compress + * @param pOutBuffer buffer for compressed data + * @param nInputSize input(source) size in bytes + * @param nMaxOutBufferSize maximum capacity of compression buffer + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * + * @return actual compressed size, or -1 for error + */ +size_t lzsa_compress_inmem(unsigned char *pInputData, unsigned char *pOutBuffer, size_t nInputSize, size_t nMaxOutBufferSize, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion) { + lzsa_compressor compressor; + size_t nOriginalSize = 0; + size_t nCompressedSize = 0L; + int nResult; + int nError = 0; + + nResult = lzsa_compressor_init(&compressor, BLOCK_SIZE * 2, nMinMatchSize, nFormatVersion, nFlags); + if (nResult != 0) { + return -1; + } + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + int nHeaderSize = lzsa_encode_header(pOutBuffer, (int)nMaxOutBufferSize, nFormatVersion); + if (nHeaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + nCompressedSize += nHeaderSize; + } + } + + int nPreviousBlockSize = 0; + int nNumBlocks = 0; + + while (nOriginalSize < nInputSize && !nError) { + int nInDataSize; + + nInDataSize = (int)(nInputSize - nOriginalSize); + if (nInDataSize > BLOCK_SIZE) + nInDataSize = BLOCK_SIZE; + + if (nInDataSize > 0) { + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0 && nNumBlocks) { + nError = LZSA_ERROR_RAW_TOOLARGE; + break; + } + + int nOutDataSize; + int nOutDataEnd = (int)(nMaxOutBufferSize - (lzsa_get_frame_size() + nCompressedSize + lzsa_get_frame_size() /* footer */)); + int nFrameSize = lzsa_get_frame_size(); + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nFrameSize = 0; + nOutDataEnd = (int)(nMaxOutBufferSize - nCompressedSize); + } + + if (nOutDataEnd > BLOCK_SIZE) + nOutDataEnd = BLOCK_SIZE; + + nOutDataSize = lzsa_compressor_shrink_block(&compressor, pInputData + nOriginalSize - nPreviousBlockSize, nPreviousBlockSize, nInDataSize, pOutBuffer + nFrameSize + nCompressedSize, nOutDataEnd); + if (nOutDataSize >= 0) { + /* Write compressed block */ + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + int nBlockheaderSize = lzsa_encode_compressed_block_frame(pOutBuffer + nCompressedSize, (int)(nMaxOutBufferSize - nCompressedSize), nOutDataSize); + if (nBlockheaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + nCompressedSize += nBlockheaderSize; + } + } + + if (!nError) { + nOriginalSize += nInDataSize; + nCompressedSize += nOutDataSize; + } + } + else { + /* Write uncompressible, literal block */ + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nError = LZSA_ERROR_RAW_UNCOMPRESSED; + break; + } + + int nBlockheaderSize = lzsa_encode_uncompressed_block_frame(pOutBuffer + nCompressedSize, (int)(nMaxOutBufferSize - nCompressedSize), nInDataSize); + if (nBlockheaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + if ((size_t)nInDataSize > (nMaxOutBufferSize - (nCompressedSize + nBlockheaderSize))) + nError = LZSA_ERROR_DST; + else { + memcpy(pOutBuffer + nBlockheaderSize + nCompressedSize, pInputData + nOriginalSize, nInDataSize); + + nOriginalSize += nInDataSize; + nCompressedSize += nBlockheaderSize + nInDataSize; + } + } + } + + nPreviousBlockSize = nInDataSize; + nNumBlocks++; + } + } + + if (!nError) { + int nFooterSize; + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nFooterSize = 0; + } + else { + nFooterSize = lzsa_encode_footer_frame(pOutBuffer + nCompressedSize, (int)(nMaxOutBufferSize - nCompressedSize)); + if (nFooterSize < 0) + nError = LZSA_ERROR_COMPRESSION; + } + + nCompressedSize += nFooterSize; + } + + lzsa_compressor_destroy(&compressor); + + if (nError) { + return -1; + } + else { + return nCompressedSize; + } +} + diff --git a/Tools/unix/lzsa/src/shrink_inmem.h b/Tools/unix/lzsa/src/shrink_inmem.h new file mode 100644 index 00000000..2bd8f277 --- /dev/null +++ b/Tools/unix/lzsa/src/shrink_inmem.h @@ -0,0 +1,71 @@ +/* + * shrink_inmem.h - in-memory compression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_INMEM_H +#define _SHRINK_INMEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get maximum compressed size of input(source) data + * + * @param nInputSize input(source) size in bytes + * + * @return maximum compressed size + */ +size_t lzsa_get_max_compressed_size_inmem(size_t nInputSize); + +/** + * Compress memory + * + * @param pInputData pointer to input(source) data to compress + * @param pOutBuffer buffer for compressed data + * @param nInputSize input(source) size in bytes + * @param nMaxOutBufferSize maximum capacity of compression buffer + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * + * @return actual compressed size, or -1 for error + */ +size_t lzsa_compress_inmem(unsigned char *pInputData, unsigned char *pOutBuffer, size_t nInputSize, size_t nMaxOutBufferSize, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion); + +#ifdef __cplusplus +} +#endif + +#endif /* _SHRINK_INMEM_H */ diff --git a/Tools/unix/lzsa/src/shrink_streaming.c b/Tools/unix/lzsa/src/shrink_streaming.c new file mode 100644 index 00000000..2e1cf125 --- /dev/null +++ b/Tools/unix/lzsa/src/shrink_streaming.c @@ -0,0 +1,320 @@ +/* + * shrink_streaming.c - streaming compression implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "shrink_streaming.h" +#include "format.h" +#include "frame.h" +#include "lib.h" +#ifdef _WIN32 +#include +#else +#include +#endif + +/** + * Delete file + * + * @param pszInFilename name of file to delete + */ +static void lzsa_delete_file(const char *pszInFilename) { +#ifdef _WIN32 + DeleteFileA(pszInFilename); +#else + remove(pszInFilename); +#endif +} + +/*-------------- File API -------------- */ + +/** + * Compress file + * + * @param pszInFilename name of input(source) file to compress + * @param pszOutFilename name of output(compressed) file to generate + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pOriginalSize pointer to returned input(source) size, updated when this function is successful + * @param pCompressedSize pointer to returned output(compressed) size, updated when this function is successful + * @param pCommandCount pointer to returned token(compression commands) count, updated when this function is successful + * @param pSafeDist pointer to return safe distance for raw blocks, updated when this function is successful + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_compress_file(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion, + void(*progress)(long long nOriginalSize, long long nCompressedSize), long long *pOriginalSize, long long *pCompressedSize, int *pCommandCount, int *pSafeDist, lzsa_stats *pStats) { + lzsa_stream_t inStream, outStream; + void *pDictionaryData = NULL; + int nDictionaryDataSize = 0; + lzsa_status_t nStatus; + + if (lzsa_filestream_open(&inStream, pszInFilename, "rb") < 0) { + return LZSA_ERROR_SRC; + } + + if (lzsa_filestream_open(&outStream, pszOutFilename, "wb") < 0) { + inStream.close(&inStream); + return LZSA_ERROR_DST; + } + + nStatus = lzsa_dictionary_load(pszDictionaryFilename, &pDictionaryData, &nDictionaryDataSize); + + if (nStatus) { + outStream.close(&outStream); + inStream.close(&inStream); + lzsa_delete_file(pszOutFilename); + return nStatus; + } + + nStatus = lzsa_compress_stream(&inStream, &outStream, pDictionaryData, nDictionaryDataSize, nFlags, nMinMatchSize, nFormatVersion, progress, pOriginalSize, pCompressedSize, pCommandCount, pSafeDist, pStats); + + lzsa_dictionary_free(&pDictionaryData); + outStream.close(&outStream); + inStream.close(&inStream); + + if (nStatus) { + lzsa_delete_file(pszOutFilename); + } + + return nStatus; +} + +/*-------------- Streaming API -------------- */ + +/** + * Compress stream + * + * @param pInStream input(source) stream to compress + * @param pOutStream output(compressed) stream to write to + * @param pDictionaryData dictionary contents, or NULL for none + * @param nDictionaryDataSize size of dictionary contents, or 0 + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pOriginalSize pointer to returned input(source) size, updated when this function is successful + * @param pCompressedSize pointer to returned output(compressed) size, updated when this function is successful + * @param pCommandCount pointer to returned token(compression commands) count, updated when this function is successful + * @param pSafeDist pointer to return safe distance for raw blocks, updated when this function is successful + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_compress_stream(lzsa_stream_t *pInStream, lzsa_stream_t *pOutStream, const void *pDictionaryData, int nDictionaryDataSize, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion, + void(*progress)(long long nOriginalSize, long long nCompressedSize), long long *pOriginalSize, long long *pCompressedSize, int *pCommandCount, int *pSafeDist, lzsa_stats *pStats) { + unsigned char *pInData, *pOutData; + lzsa_compressor compressor; + long long nOriginalSize = 0LL, nCompressedSize = 0LL; + int nResult; + unsigned char cFrameData[16]; + int nError = 0; + int nRawPadding = (nFlags & LZSA_FLAG_RAW_BLOCK) ? 8 : 0; + + pInData = (unsigned char*)malloc(BLOCK_SIZE * 2); + if (!pInData) { + return LZSA_ERROR_MEMORY; + } + memset(pInData, 0, BLOCK_SIZE * 2); + + pOutData = (unsigned char*)malloc(BLOCK_SIZE); + if (!pOutData) { + free(pInData); + pInData = NULL; + + return LZSA_ERROR_MEMORY; + } + memset(pOutData, 0, BLOCK_SIZE); + + nResult = lzsa_compressor_init(&compressor, BLOCK_SIZE * 2, nMinMatchSize, nFormatVersion, nFlags); + if (nResult != 0) { + free(pOutData); + pOutData = NULL; + + free(pInData); + pInData = NULL; + + return LZSA_ERROR_MEMORY; + } + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + int nHeaderSize = lzsa_encode_header(cFrameData, 16, nFormatVersion); + if (nHeaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + if (pOutStream->write(pOutStream, cFrameData, nHeaderSize) != nHeaderSize) + nError = LZSA_ERROR_DST; + nCompressedSize += (long long)nHeaderSize; + } + } + + int nPreviousBlockSize = 0; + int nNumBlocks = 0; + + while (!pInStream->eof(pInStream) && !nError) { + int nInDataSize; + + if (nPreviousBlockSize) { + memcpy(pInData + BLOCK_SIZE - nPreviousBlockSize, pInData + BLOCK_SIZE, nPreviousBlockSize); + } + else if (nDictionaryDataSize && pDictionaryData) { + nPreviousBlockSize = nDictionaryDataSize; + memcpy(pInData + BLOCK_SIZE - nPreviousBlockSize, pDictionaryData, nPreviousBlockSize); + } + + nInDataSize = (int)pInStream->read(pInStream, pInData + BLOCK_SIZE, BLOCK_SIZE); + if (nInDataSize > 0) { + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0 && nNumBlocks) { + nError = LZSA_ERROR_RAW_TOOLARGE; + break; + } + nDictionaryDataSize = 0; + + int nOutDataSize; + + nOutDataSize = lzsa_compressor_shrink_block(&compressor, pInData + BLOCK_SIZE - nPreviousBlockSize, nPreviousBlockSize, nInDataSize, pOutData, ((nInDataSize + nRawPadding) >= BLOCK_SIZE) ? BLOCK_SIZE : (nInDataSize + nRawPadding)); + if (nOutDataSize >= 0) { + /* Write compressed block */ + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + int nBlockheaderSize = lzsa_encode_compressed_block_frame(cFrameData, 16, nOutDataSize); + if (nBlockheaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + nCompressedSize += (long long)nBlockheaderSize; + if (pOutStream->write(pOutStream, cFrameData, nBlockheaderSize) != (size_t)nBlockheaderSize) { + nError = LZSA_ERROR_DST; + } + } + } + + if (!nError) { + if (pOutStream->write(pOutStream, pOutData, (size_t)nOutDataSize) != (size_t)nOutDataSize) { + nError = LZSA_ERROR_DST; + } + else { + nOriginalSize += (long long)nInDataSize; + nCompressedSize += (long long)nOutDataSize; + } + } + } + else { + /* Write uncompressible, literal block */ + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nError = LZSA_ERROR_RAW_UNCOMPRESSED; + break; + } + + int nBlockheaderSize = lzsa_encode_uncompressed_block_frame(cFrameData, 16, nInDataSize); + if (nBlockheaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + if (pOutStream->write(pOutStream, cFrameData, nBlockheaderSize) != (size_t)nBlockheaderSize) { + nError = LZSA_ERROR_DST; + } + else { + if (pOutStream->write(pOutStream, pInData + BLOCK_SIZE, (size_t)nInDataSize) != (size_t)nInDataSize) { + nError = LZSA_ERROR_DST; + } + else { + nOriginalSize += (long long)nInDataSize; + nCompressedSize += (long long)nBlockheaderSize + (long long)nInDataSize; + } + } + } + } + + nPreviousBlockSize = nInDataSize; + nNumBlocks++; + } + + if (!nError && !pInStream->eof(pInStream)) { + if (progress) + progress(nOriginalSize, nCompressedSize); + } + } + + if (!nError) { + int nFooterSize; + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nFooterSize = 0; + } + else { + nFooterSize = lzsa_encode_footer_frame(cFrameData, 16); + if (nFooterSize < 0) + nError = LZSA_ERROR_COMPRESSION; + } + + if (pOutStream->write(pOutStream, cFrameData, nFooterSize) != nFooterSize) + nError = LZSA_ERROR_DST; + nCompressedSize += (long long)nFooterSize; + } + + if (progress) + progress(nOriginalSize, nCompressedSize); + + int nCommandCount = lzsa_compressor_get_command_count(&compressor); + int nSafeDist = compressor.safe_dist; + + if (pStats) + *pStats = compressor.stats; + + lzsa_compressor_destroy(&compressor); + + free(pOutData); + pOutData = NULL; + + free(pInData); + pInData = NULL; + + if (nError) { + return nError; + } + else { + if (pOriginalSize) + *pOriginalSize = nOriginalSize; + if (pCompressedSize) + *pCompressedSize = nCompressedSize; + if (pCommandCount) + *pCommandCount = nCommandCount; + if (pSafeDist) + *pSafeDist = nSafeDist; + return LZSA_OK; + } +} diff --git a/Tools/unix/lzsa/src/shrink_streaming.h b/Tools/unix/lzsa/src/shrink_streaming.h new file mode 100644 index 00000000..0920edfd --- /dev/null +++ b/Tools/unix/lzsa/src/shrink_streaming.h @@ -0,0 +1,99 @@ +/* + * shrink_streaming.h - streaming compression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_STREAMING_H +#define _SHRINK_STREAMING_H + +#include "stream.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration */ +typedef enum _lzsa_status_t lzsa_status_t; +typedef struct _lzsa_stats lzsa_stats; + +/*-------------- File API -------------- */ + +/** + * Compress file + * + * @param pszInFilename name of input(source) file to compress + * @param pszOutFilename name of output(compressed) file to generate + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pOriginalSize pointer to returned input(source) size, updated when this function is successful + * @param pCompressedSize pointer to returned output(compressed) size, updated when this function is successful + * @param pCommandCount pointer to returned token(compression commands) count, updated when this function is successful + * @param pSafeDist pointer to return safe distance for raw blocks, updated when this function is successful + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_compress_file(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion, + void(*progress)(long long nOriginalSize, long long nCompressedSize), long long *pOriginalSize, long long *pCompressedSize, int *pCommandCount, int *pSafeDist, lzsa_stats *pStats); + +/*-------------- Streaming API -------------- */ + +/** + * Compress stream + * + * @param pInStream input(source) stream to compress + * @param pOutStream output(compressed) stream to write to + * @param pDictionaryData dictionary contents, or NULL for none + * @param nDictionaryDataSize size of dictionary contents, or 0 + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pOriginalSize pointer to returned input(source) size, updated when this function is successful + * @param pCompressedSize pointer to returned output(compressed) size, updated when this function is successful + * @param pCommandCount pointer to returned token(compression commands) count, updated when this function is successful + * @param pSafeDist pointer to return safe distance for raw blocks, updated when this function is successful + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_compress_stream(lzsa_stream_t *pInStream, lzsa_stream_t *pOutStream, const void *pDictionaryData, int nDictionaryDataSize, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion, + void(*progress)(long long nOriginalSize, long long nCompressedSize), long long *pOriginalSize, long long *pCompressedSize, int *pCommandCount, int *pSafeDist, lzsa_stats *pStats); + +#ifdef __cplusplus +} +#endif + +#endif /* _SHRINK_STREAMING_H */ diff --git a/Tools/unix/lzsa/src/stream.c b/Tools/unix/lzsa/src/stream.c new file mode 100644 index 00000000..89374872 --- /dev/null +++ b/Tools/unix/lzsa/src/stream.c @@ -0,0 +1,111 @@ +/* + * stream.c - streaming I/O implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include +#include "stream.h" + +/** + * Close file stream + * + * @param stream stream + */ +static void lzsa_filestream_close(lzsa_stream_t *stream) { + if (stream->obj) { + fclose((FILE*)stream->obj); + stream->obj = NULL; + stream->read = NULL; + stream->write = NULL; + stream->eof = NULL; + stream->close = NULL; + } +} + +/** + * Read from file stream + * + * @param stream stream + * @param ptr buffer to read into + * @param size number of bytes to read + * + * @return number of bytes read + */ +static size_t lzsa_filestream_read(lzsa_stream_t *stream, void *ptr, size_t size) { + return fread(ptr, 1, size, (FILE*)stream->obj); +} + +/** + * Write to file stream + * + * @param stream stream + * @param ptr buffer to write from + * @param size number of bytes to write + * + * @return number of bytes written + */ +static size_t lzsa_filestream_write(lzsa_stream_t *stream, void *ptr, size_t size) { + return fwrite(ptr, 1, size, (FILE*)stream->obj); +} + +/** + * Check if file stream has reached the end of the data + * + * @param stream stream + * + * @return nonzero if the end of the data has been reached, 0 if there is more data + */ +static int lzsa_filestream_eof(lzsa_stream_t *stream) { + return feof((FILE*)stream->obj); +} + +/** + * Open file and create an I/O stream from it + * + * @param stream stream to fill out + * @param pszInFilename filename + * @param pszMode open mode, as with fopen() + * + * @return 0 for success, nonzero for failure + */ +int lzsa_filestream_open(lzsa_stream_t *stream, const char *pszInFilename, const char *pszMode) { + stream->obj = (void*)fopen(pszInFilename, pszMode); + if (stream->obj) { + stream->read = lzsa_filestream_read; + stream->write = lzsa_filestream_write; + stream->eof = lzsa_filestream_eof; + stream->close = lzsa_filestream_close; + return 0; + } + else + return -1; +} diff --git a/Tools/unix/lzsa/src/stream.h b/Tools/unix/lzsa/src/stream.h new file mode 100644 index 00000000..a8b79229 --- /dev/null +++ b/Tools/unix/lzsa/src/stream.h @@ -0,0 +1,103 @@ +/* + * stream.h - streaming I/O definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _STREAM_H +#define _STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration */ +typedef struct _lzsa_stream_t lzsa_stream_t; + +/* I/O stream */ +typedef struct _lzsa_stream_t { + /** Opaque stream-specific pointer */ + void *obj; + + /** + * Read from stream + * + * @param stream stream + * @param ptr buffer to read into + * @param size number of bytes to read + * + * @return number of bytes read + */ + size_t(*read)(lzsa_stream_t *stream, void *ptr, size_t size); + + /** + * Write to stream + * + * @param stream stream + * @param ptr buffer to write from + * @param size number of bytes to write + * + * @return number of bytes written + */ + size_t(*write)(lzsa_stream_t *stream, void *ptr, size_t size); + + + /** + * Check if stream has reached the end of the data + * + * @param stream stream + * + * @return nonzero if the end of the data has been reached, 0 if there is more data + */ + int(*eof)(lzsa_stream_t *stream); + + /** + * Close stream + * + * @param stream stream + */ + void(*close)(lzsa_stream_t *stream); +} lzsa_stream_t; + +/** + * Open file and create an I/O stream from it + * + * @param stream stream to fill out + * @param pszInFilename filename + * @param pszMode open mode, as with fopen() + * + * @return 0 for success, nonzero for failure + */ +int lzsa_filestream_open(lzsa_stream_t *stream, const char *pszInFilename, const char *pszMode); + +#ifdef __cplusplus +} +#endif + +#endif /* _STREAM_H */ diff --git a/Tools/unix/uz80as/Makefile b/Tools/unix/uz80as/Makefile new file mode 100644 index 00000000..712c47b0 --- /dev/null +++ b/Tools/unix/uz80as/Makefile @@ -0,0 +1,72 @@ +# =========================================================================== +# uz80as, an assembler for the Zilog Z80 and several other microprocessors. +# =========================================================================== + +DEST = ../../`uname` +CC = gcc +CFLAGS = -g + +OBJECTS = ngetopt.o main.o options.o \ + utils.o err.o incl.o sym.o \ + expr.o exprint.o pp.o list.o \ + prtable.o uz80as.o targets.o \ + z80.o gbcpu.o \ + dp2200.o i4004.o \ + i8008.o i8048.o \ + i8051.o i8080.o \ + mos6502.o mc6800.o + +SOURCES = \ + config.h \ + ngetopt.c ngetopt.h \ + main.c \ + options.c options.h \ + utils.c utils.h \ + err.c err.h \ + incl.c incl.h \ + sym.c sym.h \ + expr.c expr.h \ + exprint.c exprint.h \ + pp.c pp.h \ + list.c list.h \ + prtable.c prtable.h \ + uz80as.c uz80as.h \ + targets.c targets.h \ + z80.c \ + gbcpu.c \ + dp2200.c \ + i4004.c \ + i8008.c \ + i8048.c \ + i8051.c \ + i8080.c \ + mos6502.c \ + mc6800.c + +all: uz80as $(DEST) + cp uz80as $(DEST) + +$(DEST): + mkdir -p $(DEST) + +clobber: clean + -rm -f uz80as $(DEST)/uz80as + +clean: + -rm -f $(OBJECTS) + +uz80as: $(OBJECTS) + $(CC) $(CFLAGS) -o uz80as $(OBJECTS) + +TESTS=test*.asm + +test: uz80as + echo "running tests" + for i in $(TESTS) ; do \ + echo "====== $$i ======" ; \ + ./uz80as $$i ; \ + cat $$(basename $$i .asm).lst ; \ + done + +.c.o: + $(CC) $(CFLAGS) -I. -c $< -o $@ diff --git a/Tools/unix/uz80as/config.h b/Tools/unix/uz80as/config.h new file mode 100644 index 00000000..cca14cbd --- /dev/null +++ b/Tools/unix/uz80as/config.h @@ -0,0 +1,29 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Years of copyright */ +#define COPYRIGHT_YEARS "2018" + +/* Name of package */ +#define PACKAGE "uz80as" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "jorge.giner@hotmail.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "uz80as" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "uz80as 1.10" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "uz80as" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "https://jorgicor.niobe.org/uz80as" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.10" + +/* Version number of package */ +#define VERSION "1.10" diff --git a/Tools/unix/uz80as/dp2200.c b/Tools/unix/uz80as/dp2200.c new file mode 100644 index 00000000..c6599247 --- /dev/null +++ b/Tools/unix/uz80as/dp2200.c @@ -0,0 +1,208 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Datapoint 2200. + * =========================================================================== + */ + +/* + * Datapoint 2200 Version I, 2K to 8K mem (program counter 13 bits). + * Datapoint 2200 Version II, 2K to 16K mem (protram counter 14 bits). + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: ADA,ADB,ADC,ADD,ADH,ADL,ADM, + * ACA,ACB,ACC,ACD,ACH,ACL,ACM, + * SUA,SUB,SUC,SUD,SUH,SUL,SUM, + * SBA,SBB,SBC,SBD,SBH,SBL,SBM, + * NDA,NDB,NDC,NDD,NDH,NDL,NDM, + * XRA,XRB,XRC,XRD,XRH,XRL,XRM, + * ORA,ORB,ORC,ORD,ORH,ORL,ORM, + * CPA,CPB,CPC,CPD,CPH,CPL,CPM + * c: NOP,LAB,LAC,LAD,LAE,LAH,LAL,LAM, + * LBA,LBC,LBD,LBE,LBH,LBL,LBM, + * LCA,LCB,LCD,LCE,LCH,LCL,LCM, + * LDA,LDB,LDC,LDE,LDH,LDL,LDM, + * LEA,LEB,LEC,LED,LEH,LEL,LEM, + * LHA,LHB,LHC,LHD,LHE,LHL,LHM, + * LLA,LLB,LLC,LLD,LLE,LLH,LLM, + * LMA,LMB,LMC,LMD,LME,LMH,LML,HALT + * d: ADR,STATUS,DATA,WRITE,COM1,COM2,COM3,COM4 + * BEEP,CLICK,DECK1,DECK2, + * RBK,WBK,BSP,SF,SB,REWND,TSTOP + * e: RFC,RFS,RTC,RTS,RFZ,RFP,RTZ,RTP + * f: JFC,JFZ,JFS,JFP,JTC,JTZ,JTS,JTP + * g: AD,SU,ND,OR,AC,SB,XR,CP + * h: LA,LB,LC,LD,LE,LH,LL + * i: CFC,CFZ,CFS,CFP,CTC,CTZ,CTS,CTP + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 1) + lastbyte + * g: (op << 4) | lastbyte + */ + +const struct matchtab s_matchtab_dp2200[] = { + { "SLC", "02.", 3, 0 }, + { "SRC", "0A.", 3, 0 }, + { "RETURN", "07.", 3, 0 }, + { "INPUT", "41.", 3, 0 }, + { "b", "80c0.", 3, 0 }, + { "c", "C0c0.", 3, 0 }, + { "EX d", "51f0.", 3, 0 }, + { "e", "03b0.", 3, 0 }, + { "g a", "04b0.d1.", 3, 0, "e8" }, + { "h a", "06b0.d1.", 3, 0, "e8"}, + { "f a", "40b0.e1", 3, 0 }, + { "i a", "42b0.e1", 3, 0 }, + { "JMP a", "44.e0", 3, 0 }, + { "CALL a", "46.e0", 3, 0 }, + /* version II */ + { "BETA", "10.", 2, 0 }, + { "DI", "20.", 2, 0 }, + { "POP", "30.", 2, 0 }, + { "ALPHA", "18.", 2, 0 }, + { "EI", "28.", 2, 0 }, + { "PUSH", "38.", 2, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"ADA", "ADB", "ADC", "ADD", "ADE", "ADH", "ADL", "ADM", +"ACA", "ACB", "ACC", "ACD", "ACE", "ACH", "ACL", "ACM", +"SUA", "SUB", "SUC", "SUD", "SUE", "SUH", "SUL", "SUM", +"SBA", "SBB", "SBC", "SBD", "SBE", "SBH", "SBL", "SBM", +"NDA", "NDB", "NDC", "NDD", "NDE", "NDH", "NDL", "NDM", +"XRA", "XRB", "XRC", "XRD", "XRE", "XRH", "XRL", "XRM", +"ORA", "ORB", "ORC", "ORD", "ORE", "ORH", "ORL", "ORM", +"CPA", "CPB", "CPC", "CPD", "CPE", "CPH", "CPL", "CPM", +NULL }; + +static const char *const cval[] = { +"NOP", "LAB", "LAC", "LAD", "LAE", "LAH", "LAL", "LAM", +"LBA", "", "LBC", "LBD", "LBE", "LBH", "LBL", "LBM", +"LCA", "LCB", "", "LCD", "LCE", "LCH", "LCL", "LCM", +"LDA", "LDB", "LDC", "", "LDE", "LDH", "LDL", "LDM", +"LEA", "LEB", "LEC", "LED", "", "LEH", "LEL", "LEM", +"LHA", "LHB", "LHC", "LHD", "LHE", "", "LHL", "LHM", +"LLA", "LLB", "LLC", "LLD", "LLE", "LLH", "", "LLM", +"LMA", "LMB", "LMC", "LMD", "LME", "LMH", "LML", "HALT", +NULL }; + +static const char *const dval[] = { +"ADR", "STATUS", "DATA", "WRITE", "COM1", "COM2", "COM3", "COM4", +"", "", "", "", "BEEP", "CLICK", "DECK1", "DECK2", +"RBK", "WBK", "", "BSP", "SF", "SB", "REWND", "TSTOP", +NULL }; + +static const char *const eval[] = { "RFC", "RFZ", "RFS", "RFP", + "RTC", "RTZ", "RTS", "RTP", + NULL }; + +static const char *const fval[] = { "JFC", "JFZ", "JFS", "JFP", + "JTC", "JTZ", "JTS", "JTP", + NULL }; + +static const char *const gval[] = { "AD", "AC", "SU", "SB", + "ND", "XR", "OR", "CP", + NULL }; + +static const char *const hval[] = { "LA", "LB", "LC", "LD", + "LE", "LH", "LL", + NULL }; + +static const char *const ival[] = { "CFC", "CFZ", "CFS", "CFP", + "CTC", "CTZ", "CTS", "CTP", + NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival +}; + +static int match_dp2200(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'i') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_dp2200(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b += (vs[i] << 1); break; + case 'g': b |= (vs[i] << 4); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_dp2200(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_dp2200(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'n') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_dp2200 = { + .id = "dp2200", + .descr = "Datapoint 2200 Version I", + .matcht = s_matchtab_dp2200, + .matchf = match_dp2200, + .genf = gen_dp2200, + .pat_char_rewind = pat_char_rewind_dp2200, + .pat_next_str = pat_next_str_dp2200, + .mask = 1 +}; + +const struct target s_target_dp2200ii = { + .id = "dp2200ii", + .descr = "Datapoint 2200 Version II", + .matcht = s_matchtab_dp2200, + .matchf = match_dp2200, + .genf = gen_dp2200, + .pat_char_rewind = pat_char_rewind_dp2200, + .pat_next_str = pat_next_str_dp2200, + .mask = 2 +}; diff --git a/Tools/unix/uz80as/err.c b/Tools/unix/uz80as/err.c new file mode 100644 index 00000000..b6745ae8 --- /dev/null +++ b/Tools/unix/uz80as/err.c @@ -0,0 +1,196 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Error reporting. + * =========================================================================== + */ + +#include "config.h" +#include "err.h" +#include "incl.h" + +#ifndef ASSERT_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +#ifndef STDARG_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +/* Max number of errors before halt. */ +#define MAXERR 64 + +int s_nerrors; + +static void eprfl(void) +{ + fprintf(stderr, "%s:%d: ", curfile()->name, curfile()->linenum); +} + +static void eprwarn(void) +{ + fputs(_("warning:"), stderr); + fputc(' ', stderr); +} + +/* Print the characters in [p, q[ to stderr. */ +void echars(const char *p, const char *q) +{ + while (*p != '\0' && p != q) { + fputc(*p, stderr); + p++; + } +} + +/* + * Print a space, an opening parenthesis, the characters in [p, q[, + * and a closing parenthesis to stderr. + */ +void epchars(const char *p, const char *q) +{ + fputs(" (", stderr); + echars(p, q); + fputs(")", stderr); +} + +/* + * Increments the number of errors, and exit with failure if + * maximum number of errors allowed is reached. + */ +void newerr(void) +{ + s_nerrors++; + if (s_nerrors >= MAXERR) { + eprogname(); + fprintf(stderr, _("exiting: too many errors")); + enl(); + exit(EXIT_FAILURE); + } +} + +static void evprint(int warn, const char *ecode, va_list args) +{ + if (nfiles() > 0) + eprfl(); + else + eprogname(); + + if (warn) + eprwarn(); + + assert(ecode != NULL); + vfprintf(stderr, ecode, args); +} + +/* Prints only the printable characters, the rst as space. */ +static void eprint_printable(const char *p) +{ + for (; *p != '\0'; p++) { + if (isprint(*p)) + putc(*p, stderr); + else + putc(' ', stderr); + } +} + +/* Prints the line and a marker pointing to the charcater q inside line. */ +void eprcol(const char *line, const char *q) +{ + putc(' ', stderr); + eprint_printable(line); + fputs("\n ", stderr); + while (line != q) { + putc(' ', stderr); + line++; + } + fputs("^\n", stderr); +} + +/* + * Like fprintf but prints to stderr. + * If we are parsing any file (incl.c), print first the file and the line. + * If not, print first the program name. + */ +void eprint(const char *ecode, ...) +{ + va_list args; + + va_start(args, ecode); + evprint(0, ecode, args); + va_end(args); +} + +/* Same as eprint, but print "warning: " before ecode str. */ +void wprint(const char *ecode, ...) +{ + va_list args; + + va_start(args, ecode); + evprint(1, ecode, args); + va_end(args); +} + +/* Print \n on stderr. */ +void enl(void) +{ + fputc('\n', stderr); +} + +/* Print the program name on stderr. */ +void eprogname(void) +{ + fprintf(stderr, PACKAGE": "); +} + +/* Call malloc, but if no memory, print that error and exit with failure. */ +void *emalloc(size_t n) +{ + void *p; + + if ((p = malloc(n)) == NULL) { + eprint(_("malloc fail\n")); + exit(EXIT_FAILURE); + } + // printf("emalloc: %d = %x\n", n, p); + return p; +} + +/* Call realloc, but if no memory, print that error and exit with failure. */ +void *erealloc(void *p, size_t n) +{ + // void *q = p; + if ((p = realloc(p, n)) == NULL) { + eprint(_("realloc fail\n")); + exit(EXIT_FAILURE); + } + // printf("erealloc: %x %d = %x\n", q, n, p); + return p; +} + +/* Call fopen, but if any error, print it and exit with failure. */ +FILE *efopen(const char *fname, const char *ops) +{ + FILE *fp; + + if ((fp = fopen(fname, ops)) == NULL) { + eprint(_("cannot open file %s\n"), fname); + exit(EXIT_FAILURE); + } + return fp; +} diff --git a/Tools/unix/uz80as/err.h b/Tools/unix/uz80as/err.h new file mode 100644 index 00000000..86d8e1dc --- /dev/null +++ b/Tools/unix/uz80as/err.h @@ -0,0 +1,32 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Error reporting. + * =========================================================================== + */ + +#ifndef ERR_H +#define ERR_H + +#ifndef STDIO_H +#define STDIO_H +#include +#endif + +#define _(str) (str) + +extern int s_nerrors; + +void newerr(void); +void eprogname(void); +void echars(const char *p, const char *q); +void epchars(const char *p, const char *q); +void eprint(const char *ecode, ...); +void wprint(const char *ecode, ...); +void eprcol(const char *line, const char *q); +void enl(void); +void *emalloc(size_t n); +void *erealloc(void *p, size_t n); +FILE *efopen(const char *fname, const char *ops); + +#endif diff --git a/Tools/unix/uz80as/expr.c b/Tools/unix/uz80as/expr.c new file mode 100644 index 00000000..445e36f0 --- /dev/null +++ b/Tools/unix/uz80as/expr.c @@ -0,0 +1,459 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Expression parsing. + * =========================================================================== + */ + +#include "config.h" +#include "expr.h" +#include "utils.h" +#include "err.h" +#include "sym.h" + +#ifndef ASSERT_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +#ifndef LIMITS_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +/* Max nested expressions. */ +#define ESTKSZ 16 +#define ESTKSZ2 (ESTKSZ*2) + +/* Return -1 on syntax error. + * *p must be a digit already. + * *q points to one past the end of the number without suffix. + */ +static int takenum(const char *p, const char *q, int radix) +{ + int k, n; + + n = 0; + while (p != q) { + k = hexval(*p); + p++; + if (k >= 0 && k < radix) + n = n * radix + k; + else + return -1; + } + return n; +} + +/* Go to the end of a number (advance all digits or letters). */ +static const char *goendnum(const char *p) +{ + const char *q; + + for (q = p; isalnum(*q); q++) + ; + return q; +} + +/* + * Returns NULL on error. + * '*p' must be a digit already. + */ +static const char *getnum(const char *p, int *v) +{ + int n; + char c; + const char *q; + + assert(isdigit(*p)); + + n = 0; + q = goendnum(p) - 1; + if (isalpha(*q)) { + c = toupper(*q); + if (c == 'H') { + n = takenum(p, q, 16); + } else if (c == 'D') { + n = takenum(p, q, 10); + } else if (c == 'O') { + n = takenum(p, q, 8); + } else if (c == 'B') { + n = takenum(p, q, 2); + } else { + return NULL; + } + } else { + n = takenum(p, q + 1, 10); + } + + if (n < 0) + return NULL; + + *v = n; + return q + 1; +} + +/* + * Gets a number that was prefixed. + * Returns NULL on error. + */ +static const char *getpnum(const char *p, int radix, int *v) +{ + const char *q; + int n; + + q = goendnum(p); + n = takenum(p, q, radix); + if (n < 0) + return NULL; + *v = n; + return q; +} + +/* Left shift */ +static int shl(int r, int n) +{ + n &= int_precission(); + return r << n; +} + + +/* Portable arithmetic right shift. */ +static int ashr(int r, int n) +{ + n &= int_precission(); + if (r & INT_MIN) { + return ~(~r >> n); + } else { + return r >> n; + } +} + +/* Parses expression pointed by 'p'. + * If success, returns pointer to the end of parsed expression, and + * 'v' contains the calculated value of the expression. + * Returns NULL if a syntactic error has occurred. + * Operators are evaluated left to right. + * To allow precedence use parenthesis. + * 'linepc' is the program counter to consider when we find the $ current + * pointer location symbol ($). + * 'allowfr' stands for 'allow forward references'. We will issue an error + * if we find a label that is not defined. + * 'ecode' will be valid if NULL is returned. NULL can be passed as ecode. + * 'ep' is the pointer to the position where the error ocurred. NULL can be + * passed as ep. + */ +const char *expr(const char *p, int *v, int linepc, int allowfr, + enum expr_ecode *ecode, const char **ep) +{ + int si, usi, usl; + const char *q; + char last; + int stack[ESTKSZ2]; + int uopstk[ESTKSZ]; + int r, n; + struct sym *sym; + int err; + enum expr_ecode ec; + + ec = EXPR_E_NO_EXPR; + err = 0; + usi = 0; + si = 0; + r = 0; + last = 'V'; /* first void */ + usl = 0; +loop: + p = skipws(p); + if (*p == '(') { + if (last == 'n') { + goto end; + } else { + if (si >= ESTKSZ2) { + eprint(_("expression too complex\n")); + exit(EXIT_FAILURE); + } + stack[si++] = last; + stack[si++] = r; + stack[si++] = usl; + usl = usi; + p++; + r = 0; + last = 'v'; /* void */ + } + } else if (*p == ')') { + if (last != 'n') { + ec = EXPR_E_CPAR; + goto esyntax; + } else if (si == 0) { + goto end; + } else { + p++; + n = r; + usl = stack[--si]; + r = stack[--si]; + last = (char) stack[--si]; + goto oper; + } + } else if (*p == '+') { + p++; + if (last == 'n') + last = '+'; + } else if (*p == '-') { + if (last == 'n') { + p++; + last = '-'; + } else { + goto uoper; + } + } else if (*p == '~') { + goto uoper; + } else if (*p == '!') { + if (*(p + 1) == '=') { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } else { + p += 2; + last = 'N'; + } + } else { + goto uoper; + } + } else if (*p == '*') { + if (last == 'n') { + last = *p++; + } else { + p++; + n = linepc; + goto oper; + } + } else if (*p == '/' || *p == '&' || *p == '|' + || *p == '^') + { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } else { + last = *p++; + } + } else if (*p == '>') { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } + p++; + if (*p == '=') { + last = 'G'; + p++; + } else if (*p == '>') { + last = 'R'; + p++; + } else { + last = '>'; + } + } else if (*p == '<') { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } + p++; + if (*p == '=') { + last = 'S'; + p++; + } else if (*p == '<') { + last = 'L'; + p++; + } else { + last = '<'; + } + } else if (*p == '=') { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } + p++; + if (*p == '=') + p++; + last = '='; + } else if (*p == '\'') { + if (last == 'n') + goto end; + p++; + n = *p++; + if (*p != '\'') { + ec = EXPR_E_CHAR; + goto esyntax; + } + p++; + goto oper; + } else if (*p == '$') { + if (last == 'n') + goto end; + p++; + if (hexval(*p) < 0) { + n = linepc; + goto oper; + } + q = getpnum(p, 16, &n); + if (q == NULL) { + p--; + ec = EXPR_E_HEX; + goto esyntax; + } + p = q; + goto oper; + } else if (*p == '@') { + if (last == 'n') + goto end; + p++; + q = getpnum(p, 8, &n); + if (q == NULL) { + p--; + ec = EXPR_E_OCTAL; + goto esyntax; + } + p = q; + goto oper; + } else if (*p == '%') { + if (last == 'n') { + last = *p; + p++; + } else { + p++; + q = getpnum(p, 2, &n); + if (q == NULL) { + ec = EXPR_E_BIN; + goto esyntax; + } + p = q; + goto oper; + } + } else if ((p[0] == '0') && (p[1] == 'x')) { + p+=2; + q = getpnum(p, 16, &n); + if (q == NULL) { + p--; + ec = EXPR_E_HEX; + goto esyntax; + } + p = q; + goto oper; + } else if (isdigit(*p)) { + if (last == 'n') + goto end; + q = getnum(p, &n); + if (q == NULL) { + ec = EXPR_E_DEC; + goto esyntax; + } + p = q; + goto oper; + } else if (isidc0(*p)) { + if (last == 'n') + goto end; + q = p; + while (isidc(*p)) + p++; + sym = lookup(q, p, 0, 0); + if (sym == NULL) { + n = 0; + if (!allowfr) { + err = 1; + eprint(_("undefined label")); + epchars(q, p); + enl(); + newerr(); + } + } else { + n = sym->val; + } + goto oper; + } else if (last == 'V') { + goto esyntax; + } else if (last != 'n') { + ec = EXPR_E_SYNTAX; + goto esyntax; + } else { +end: if (v != NULL) + *v = r; + return p; + } + goto loop; +uoper: + if (last == 'n') + goto end; + if (usi >= ESTKSZ) { + eprint(_("expression too complex\n")); + exit(EXIT_FAILURE); + } + uopstk[usi++] = *p++; + goto loop; +oper: + while (usi > usl) { + usi--; + switch (uopstk[usi]) { + case '~': n = ~n; break; + case '-': n = -n; break; + case '!': n = !n; break; + } + } + switch (last) { + case 'V': r = n; break; + case 'v': r = n; break; + case '+': r += n; break; + case '-': r -= n; break; + case '*': r *= n; break; + case '&': r &= n; break; + case '|': r |= n; break; + case '^': r ^= n; break; + case '=': r = r == n; break; + case '<': r = r < n; break; + case '>': r = r > n ; break; + case 'G': r = r >= n; break; + case 'S': r = r <= n; break; + case 'N': r = r != n; break; + /* This would be logical right shift: + * case 'R': r = (unsigned int) r >> n; break; + */ + case 'R': r = ashr(r, n); break; + case 'L': r = shl(r, n); break; + case '~': r = ~n; break; + case '%': + if (n != 0) { + r %= n; + } else if (!err && !allowfr) { + err = 1; + eprint(_("modulo by zero\n")); + exit(EXIT_FAILURE); + } + break; + case '/': + if (n != 0) { + r /= n; + } else if (!err && !allowfr) { + err = 1; + eprint(_("division by zero\n")); + exit(EXIT_FAILURE); + } + break; + } + last = 'n'; + goto loop; +esyntax: + if (ecode != NULL) + *ecode = ec; + if (ep != NULL) + *ep = p; + return NULL; +} diff --git a/Tools/unix/uz80as/expr.h b/Tools/unix/uz80as/expr.h new file mode 100644 index 00000000..e1e55fc2 --- /dev/null +++ b/Tools/unix/uz80as/expr.h @@ -0,0 +1,26 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Expression parsing. + * =========================================================================== + */ + +#ifndef EXPR_H +#define EXPR_H + +enum expr_ecode { + EXPR_E_NO_EXPR, /* There was no expression parsed. */ + EXPR_E_SYNTAX, /* Syntax error. */ + EXPR_E_CPAR, + EXPR_E_OPER, + EXPR_E_CHAR, + EXPR_E_HEX, + EXPR_E_OCTAL, + EXPR_E_BIN, + EXPR_E_DEC, +}; + +const char *expr(const char *p, int *v, int linepc, int allowfr, + enum expr_ecode *ecode, const char **ep); + +#endif diff --git a/Tools/unix/uz80as/exprint.c b/Tools/unix/uz80as/exprint.c new file mode 100644 index 00000000..67699636 --- /dev/null +++ b/Tools/unix/uz80as/exprint.c @@ -0,0 +1,32 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Expression error reporting. + * =========================================================================== + */ + +#include "config.h" +#include "exprint.h" +#include "err.h" + +static const char *expr_get_error_str(enum expr_ecode ecode) +{ + switch (ecode) { + case EXPR_E_NO_EXPR: return _("expression expected\n"); + case EXPR_E_SYNTAX: return _("syntax error in expression\n"); + case EXPR_E_CPAR: return _("unexpected ')'\n"); + case EXPR_E_OPER: return _("misplaced operator\n"); + case EXPR_E_CHAR: return _("invalid character code\n"); + case EXPR_E_HEX: return _("invalid hexadecimal constant\n"); + case EXPR_E_OCTAL: return _("invalid octal constant\n"); + case EXPR_E_BIN: return _("invalid binary constant\n"); + case EXPR_E_DEC: return _("invalid decimal constant\n"); + default: return "\n"; + } +} + +void exprint(enum expr_ecode ecode, const char *pline, const char *ep) +{ + eprint(expr_get_error_str(ecode)); + eprcol(pline, ep); +} diff --git a/Tools/unix/uz80as/exprint.h b/Tools/unix/uz80as/exprint.h new file mode 100644 index 00000000..00b43c2f --- /dev/null +++ b/Tools/unix/uz80as/exprint.h @@ -0,0 +1,17 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Expression error reporting. + * =========================================================================== + */ + +#ifndef EXPRINT_H +#define EXPRINT_H + +#ifndef EXPR_H +#include "expr.h" +#endif + +void exprint(enum expr_ecode ecode, const char *pline, const char *ep); + +#endif diff --git a/Tools/unix/uz80as/gbcpu.c b/Tools/unix/uz80as/gbcpu.c new file mode 100644 index 00000000..d6b347f3 --- /dev/null +++ b/Tools/unix/uz80as/gbcpu.c @@ -0,0 +1,301 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Sharp LR35902 (Nintendo Gameboy CPU). + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +#if 0 +Opcode Z80 GMB + --------------------------------------- + 08 EX AF,AF LD (nn),SP + 10 DJNZ PC+dd STOP + 22 LD (nn),HL LDI (HL),A + 2A LD HL,(nn) LDI A,(HL) + 32 LD (nn),A LDD (HL),A + 3A LD A,(nn) LDD A,(HL) + D3 OUT (n),A - + D9 EXX RETI + DB IN A,(n) - + DD - + E0 RET PO LD (FF00+n),A + E2 JP PO,nn LD (FF00+C),A + E3 EX (SP),HL - + E4 CALL PO,nn - + E8 RET PE ADD SP,dd + EA JP PE,nn LD (nn),A + EB EX DE,HL - + EC CALL PE,nn - + ED - + F0 RET P LD A,(FF00+n) + F2 JP P,nn LD A,(FF00+C) + F4 CALL P,nn - + F8 RET M LD HL,SP+dd + FA JP M,nn LD A,(nn) + FC CALL M,nn - + FD - + CB3X SLL r/(HL) SWAP r/(HL) +#endif + +/* pat: + * a: expr + * b: B,C,D,E,H,L,A + * c: * + * d: BC,DE,HL,SP + * e: * + * f: BC,DE,HL,AF + * g: ADD,ADC,SUB,SBC,AND,XOR,OR,CP + * h: INC,DEC + * i: * + * j: * + * k: * + * l: BIT,RES,SET + * m: * + * n: NZ,Z,NC,C + * o: RLC,RRC,RL,RR,SLA,SRA,SWAP,SRL + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 4) | lastbyte + * g: (op << 6) | lastbyte + * h: if op >= FF00 output last byte and then op as 8 bit value; + * else output (lastbyte | 0x0A) and output op as word + * (no '.' should follow) + * i: relative jump to op + * j: possible value to RST + * k: possible value to IM + * l: * + * m: check arithmetic used with A register + * n: check arithmetic used without A register + */ + +const struct matchtab s_matchtab_gbcpu[] = { + { "LD b,b", "40b0c1.", 1, 0 }, + { "LD b,(HL)", "46b0.", 1, 0 }, + { "LD A,(C)", "F2.", 1, 0 }, // * LD A,(FF00+C) + { "LD A,(BC)", "0A.", 1, 0 }, + { "LD A,(DE)", "1A.", 1, 0 }, + { "LD A,(HLI)", "2A.", 1, 0 }, // * + { "LD A,(HLD)", "3A.", 1, 0 }, // * + { "LD A,(a)", "F0h0", 1, 0 }, // * LD A,(nn) & LD A,(FF00+n) + { "LD b,a", "06b0.d1.", 1, 0, "e8" }, + { "LD SP,HL", "F9.", 1, 0 }, + { "LDHL SP,a", "F8.d0.", 1, 0, "e8" }, // * LD HL,SP+n + { "LD d,a", "01f0.e1", 1, 0 }, + { "LD (C),A", "E2.", 1, 0 }, // * LD (FF00+C),A + { "LD (HL),b", "70c0.", 1, 0 }, + { "LD (HL),a", "36.d0.", 1, 0, "e8" }, + { "LD (HLI),A", "22.", 1, 0 }, // * + { "LD (HLD),A", "32.", 1, 0 }, // * + { "LD (BC),A", "02.", 1, 0 }, + { "LD (DE),A", "12.", 1, 0 }, + { "LD (a),A", "E0h0", 1, 0 }, // * LD (nn),A & LD (FF00+n),A + { "LD (a),SP", "08.e0", 1, 0 }, // * + { "LDH A,(a)", "F0.d0.", 1, 0, "e8" }, // * LD A,(FF00+n) + { "LDH (a),A", "E0.d0.", 1, 0, "e8" }, // * LD (FF00+n),A + { "PUSH f", "C5f0.", 1, 0 }, + { "POP f", "C1f0.", 1, 0 }, + { "ADD HL,d", "09f0.", 1, 0 }, + { "ADD SP,a", "E8.d0.", 1, 0, "e8" }, // * + { "g A,b", "m080b0c1.", 1, 0 }, + { "g A,(HL)", "m086b0.", 1, 0 }, + { "g A,a", "m0C6b0.d1.", 1, 0, "e8" }, + { "g b", "n080b0c1.", 1, 0 }, + { "g (HL)", "n086b0.", 1, 0 }, + { "g a", "n0C6b0.d1.", 1, 0, "e8" }, + { "h b", "04b1c0.", 1, 0 }, + { "h (HL)", "34c0.", 1, 0 }, + { "INC d", "03f0.", 1, 0 }, + { "DEC d", "0Bf0.", 1, 0 }, + { "DAA", "27.", 1, 0 }, + { "CPL", "2F.", 1, 0 }, + { "CCF", "3F.", 1, 0 }, + { "SCF", "37.", 1, 0 }, + { "NOP", "00.", 1, 0 }, + { "HALT", "76.", 1, 0 }, + { "DI", "F3.", 1, 0 }, + { "EI", "FB.", 1, 0 }, + { "RLCA", "07.", 1, 0 }, + { "RLA", "17.", 1, 0 }, + { "RRCA", "0F.", 1, 0 }, + { "RRA", "1F.", 1, 0 }, + { "o b", "CB.00b0c1.", 1, 0 }, + { "o (HL)", "CB.06b0.", 1, 0 }, + { "l a,b", "CB.00g0b1c2.", 1, 0, "b3" }, + { "l a,(HL)", "CB.06g0b1.", 1, 0, "b3" }, + { "JP (HL)", "E9.", 1, 0 }, + { "JP n,a", "C2b0.e1", 1, 0 }, // * + { "JP a", "C3.e0", 1, 0 }, + { "JR n,a", "20b0.i1.", 1, 0, "r8" }, + { "JR a", "18.i0.", 1, 0, "r8" }, + { "STOP", "10.00.", 1, 0 }, // * + { "CALL n,a", "C4b0.e1", 1, 0 }, // * + { "CALL a", "CD.e0", 1, 0 }, + { "RETI", "D9.", 1, 0 }, // * + { "RET n", "C0b0.", 1, 0 }, + { "RET", "C9.", 1, 0 }, + { "RST a", "C7j0.", 1, 0, "ss" }, + { NULL, NULL }, +}; + +static const char *const bval[] = { "B", "C", "D", "E", + "H", "L", "", "A", NULL }; +static const char *const dval[] = { "BC", "DE", "HL", "SP", NULL }; +static const char *const fval[] = { "BC", "DE", "HL", "AF", NULL }; +static const char *const gval[] = { "ADD", "ADC", "SUB", "SBC", + "AND", "XOR", "OR", "CP", NULL }; +static const char *const hval[] = { "INC", "DEC", NULL }; +static const char *const lval[] = { "", "BIT", "RES", "SET", NULL }; +static const char *const nval[] = { "NZ", "Z", "NC", "C", NULL }; +static const char *const oval[] = { "RLC", "RRC", "RL", "RR", + "SLA", "SRA", "SWAP", "SRL", NULL }; +static const char *const nullv[] = { NULL }; + +static const char *const *const valtab[] = { + bval, nullv, dval, nullv, fval, + gval, hval, nullv, nullv, nullv, + lval, nullv, nval, oval +}; + +static int match_gbcpu(char c, const char *p, const char **q) +{ + int v; + + switch (c) { + case 'b': + case 'd': + case 'f': + case 'g': + case 'h': + case 'l': + case 'n': + case 'o': + v = mreg(p, valtab[(int) (c - 'b')], q); + break; + default: + v = -1; + } + + return v; +} + +static int gen_gbcpu(int *eb, char p, const int *vs, int i, int savepc) +{ + int w, b; + + b = *eb; + switch (p) { + case 'f': b |= (vs[i] << 4); break; + case 'g': b |= (vs[i] << 6); break; + case 'h': w = vs[i] & 0xffff; + if (w >= 0xff00) { + genb(b, s_pline_ep); + b = 0; + genb(w & 0xff, s_pline_ep); + } else { + b |= 0x0A; + genb(b, s_pline_ep); + b = 0; + genb(w & 0xff, s_pline_ep); + genb(w >> 8, s_pline_ep); + } + break; + case 'i': b = (vs[i] - savepc - 2); break; + case 'j': if (s_pass > 0 && (vs[i] & ~56) != 0) { + eprint(_("invalid RST argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= vs[i]; + break; + case 'k': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 2)) { + eprint(_("invalid IM argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b = 0x46; + if (vs[i] == 1) + b = 0x56; + else if (vs[i] == 2) + b = 0x5E; + break; + case 'm': if (s_pass == 0 && !s_extended_op) { + if (vs[i] != 0 && vs[i] != 1 && vs[i] != 3) { + eprint(_("unofficial syntax\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } + break; + case 'n': if (s_pass == 0 && !s_extended_op) { + if (vs[i] == 0 || vs[i] == 1 || vs[i] == 3) { + eprint(_("unofficial syntax\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_gbcpu(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_gbcpu(void) +{ + const char *s; + + switch (s_pat_char) { + case 'b': + case 'd': + case 'f': + case 'g': + case 'h': + case 'l': + case 'n': + case 'o': + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + break; + default: + s = NULL; + } + + return s; +}; + +const struct target s_target_gbcpu = { + .id = "gbcpu", + .descr = "Sharp LR35902 (Nintendo Gameboy CPU)", + .matcht = s_matchtab_gbcpu, + .matchf = match_gbcpu, + .genf = gen_gbcpu, + .pat_char_rewind = pat_char_rewind_gbcpu, + .pat_next_str = pat_next_str_gbcpu, + .mask = 1 +}; diff --git a/Tools/unix/uz80as/i4004.c b/Tools/unix/uz80as/i4004.c new file mode 100644 index 00000000..98649abd --- /dev/null +++ b/Tools/unix/uz80as/i4004.c @@ -0,0 +1,189 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 4004. + * Intel 4040. + * =========================================================================== + */ + +/* Intel 4004. Max. memory 4K (12 bit addresses). + * Intel 4040. Max. memory 8K (13 bit addresses). + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: ADD,SUB,LD,XCH,BBL,LDM + * c: WRM,WMP,WRR,WPM,WR0,WR1,WR2,WR3 + * SBM,RDM,RDR,ADM,RD0,RD1,RD2,RD3 + * d: CLB,CLC,IAC,CMC,CMA,RAL,RAR,TCC, + * DAC,TCS,STC,DAA,KBP,DCL, + * e: HLT,BBS,LCR,OR4,OR5,AN6,AN7 + * DB0,DB1,SB0,SB1,EIN,DIN,RPM + * f: 0P,1P,2P,3P,4P,5P,6P,7P + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: op | lastbyte, op in [0-15] + * g: op | lastbyte, op in [0,2,4,6,8,10,12,14] + * h: output (op & 0xff0000 >> 8) | lastbyte; + * then ouput op as 8 bit value + * i: (op << 4) | lastbyte + * j: (op << 1) | lastbyte + */ + +static const struct matchtab s_matchtab_i4004[] = { + { "NOP", "00.", 1, 0 }, + { "JCN a,a", "10f0.d1.", 1, 0, "b4e8" }, + { "FIM f,a", "20j0.d1.", 1, 0, "e8" }, + { "FIM a,a", "20g0.d1.", 1, 0, "ppe8" }, + { "SRC f", "21j0.", 1, 0 }, + { "SRC a", "21g0.", 1, 0, "pp" }, + { "FIN f", "30j0.", 1, 0 }, + { "FIN a", "30g0.", 1, 0, "pp" }, + { "JIN f", "31j0.", 1, 0 }, + { "JIN a", "31g0.", 1, 0, "pp" }, + { "JUN a", "40h0", 1, 0 }, + { "JMS a", "50h0", 1, 0 }, + { "INC a", "60f0.", 1, 0, "b4" }, + { "ISZ a,a", "70f0.d1.", 1, 0, "b4e8" }, + { "b a", "80i0f1.", 1, 0, "b4" }, + { "c", "E0c0.", 1, 0 }, + { "d", "F0c0.", 1, 0 }, + { "e", "00c0.", 2, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"ADD", "SUB", "LD", "XCH", "BBL", "LDM", +NULL }; + +static const char *const cval[] = { +"WRM", "WMP", "WRR", "WPM", "WR0", "WR1", "WR2", "WR3", +"SBM", "RDM", "RDR", "ADM", "RD0", "RD1", "RD2", "RD3", +NULL }; + +static const char *const dval[] = { +"CLB", "CLC", "IAC", "CMC", "CMA", "RAL", "RAR", "TCC", +"DAC", "TCS", "STC", "DAA", "KBP", "DCL", +NULL }; + +static const char *const eval[] = { +"", "HLT", "BBS", "LCR", "OR4", "OR5", "AN6", "AN7", +"DB0", "DB1", "SB0", "SB1", "EIN", "DIN", "RPM", +NULL }; + +static const char *const fval[] = { +"0P", "1P", "2P", "3P", "4P", "5P", "6P", "7P", +NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval +}; + +static int match_i4004(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'f') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i4004(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 15)) { + eprint(_("argument (%d) must be in range [0-15]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= vs[i]; + break; + case 'g': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 14 || (vs[i] & 1))) { + eprint( + _("argument (%d) must be an even number in range [0-14]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= vs[i]; + break; + case 'h': b |= ((vs[i] >> 8) & 0x0f); + genb(b, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'i': b |= (vs[i] << 4); break; + case 'j': b |= (vs[i] << 1); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i4004(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i4004(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'f') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i4004 = { + .id = "i4004", + .descr = "Intel 4004", + .matcht = s_matchtab_i4004, + .matchf = match_i4004, + .genf = gen_i4004, + .pat_char_rewind = pat_char_rewind_i4004, + .pat_next_str = pat_next_str_i4004, + .mask = 1 +}; + +const struct target s_target_i4040 = { + .id = "i4040", + .descr = "Intel 4040", + .matcht = s_matchtab_i4004, + .matchf = match_i4004, + .genf = gen_i4004, + .pat_char_rewind = pat_char_rewind_i4004, + .pat_next_str = pat_next_str_i4004, + .mask = 3 +}; + diff --git a/Tools/unix/uz80as/i8008.c b/Tools/unix/uz80as/i8008.c new file mode 100644 index 00000000..a7fd9f58 --- /dev/null +++ b/Tools/unix/uz80as/i8008.c @@ -0,0 +1,220 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 8008. + * =========================================================================== + */ + +/* Max. memory 16K (14 bits addresses). */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: ADA,ADB,ADC,ADD,ADH,ADL,ADM, + * ACA,ACB,ACC,ACD,ACH,ACL,ACM, + * SUA,SUB,SUC,SUD,SUH,SUL,SUM, + * SBA,SBB,SBC,SBD,SBH,SBL,SBM, + * NDA,NDB,NDC,NDD,NDH,NDL,NDM, + * XRA,XRB,XRC,XRD,XRH,XRL,XRM, + * ORA,ORB,ORC,ORD,ORH,ORL,ORM, + * CPA,CPB,CPC,CPD,CPH,CPL,CPM + * c: NOP,LAB,LAC,LAD,LAE,LAH,LAL,LAM, + * LBA,LBB,LBC,LBD,LBE,LBH,LBL,LBM, + * LCA,LCB,LCC,LCD,LCE,LCH,LCL,LCM, + * LDA,LDB,LDC,LDD,LDE,LDH,LDL,LDM, + * LEA,LEB,LEC,LED,LEE,LEH,LEL,LEM, + * LHA,LHB,LHC,LHD,LHE,LHH,LHL,LHM, + * LLA,LLB,LLC,LLD,LLE,LLH,LLL,LLM, + * LMA,LMB,LMC,LMD,LME,LMH,LML,HLT + * d: JFC,CFC,JMP,CAL,JFZ,CFZ, + * JFS,CFS,JFP,CFP, + * JTC,CTC,JTZ,CTZ, + * JTS,CTS,JTP,CTP + * e: INB,INC,IND,INE,INH,INL + * f: DCB,DCC,DCD,DCE,DCH,DCL + * g: ADI,ACI,SUI,SBI,NDI,XRI,ORI,CPI + * h: LAI,LBI,LCI,LDI,LEI,LHI,LLI,LMI + * i: RFC,RFS,RTC,RTS,RFZ,RFP,RTZ,RTP + * j: RLC,RRC,RAL,RAR + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 1) | lastbyte, op in [0-7] + * g: (op << 1) | lastbyte, op in [8-31] + * h: (op << 4) | lastbyte + * i: (op << 1) | lastbyte + * j: (op << 3) | lastbyte, op in [0-7] + */ + +static const struct matchtab s_matchtab_i8008[] = { + { "RET", "07.", 1, 0 }, + { "j", "02b0.", 1, 0 }, + { "i", "03b0.", 1, 0 }, + { "h a", "06b0.d1.", 1, 0, "e8" }, + { "g a", "04b0.d1.", 1, 0, "e8" }, + { "e", "00b0.", 1, 0 }, + { "f", "01b0.", 1, 0 }, + { "RST a", "05j0.", 1, 0, "b3" }, + { "d a", "40i0.e1", 1, 0 }, + { "INP a", "41f0.", 1, 0, "b3" }, + { "OUT a", "41g0.", 1, 0, "kk" }, + { "b", "80c0.", 1, 0 }, + { "c", "C0c0.", 1, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"ADA", "ADB", "ADC", "ADD", "ADE", "ADH", "ADL", "ADM", +"ACA", "ACB", "ACC", "ACD", "ACE", "ACH", "ACL", "ACM", +"SUA", "SUB", "SUC", "SUD", "SUE", "SUH", "SUL", "SUM", +"SBA", "SBB", "SBC", "SBD", "SBE", "SBH", "SBL", "SBM", +"NDA", "NDB", "NDC", "NDD", "NDE", "NDH", "NDL", "NDM", +"XRA", "XRB", "XRC", "XRD", "XRE", "XRH", "XRL", "XRM", +"ORA", "ORB", "ORC", "ORD", "ORE", "ORH", "ORL", "ORM", +"CPA", "CPB", "CPC", "CPD", "CPE", "CPH", "CPL", "CPM", +NULL }; + +static const char *const cval[] = { +"NOP", "LAB", "LAC", "LAD", "LAE", "LAH", "LAL", "LAM", +"LBA", "LBB", "LBC", "LBD", "LBE", "LBH", "LBL", "LBM", +"LCA", "LCB", "LCC", "LCD", "LCE", "LCH", "LCL", "LCM", +"LDA", "LDB", "LDC", "LDD", "LDE", "LDH", "LDL", "LDM", +"LEA", "LEB", "LEC", "LED", "LEE", "LEH", "LEL", "LEM", +"LHA", "LHB", "LHC", "LHD", "LHE", "LHH", "LHL", "LHM", +"LLA", "LLB", "LLC", "LLD", "LLE", "LLH", "LLL", "LLM", +"LMA", "LMB", "LMC", "LMD", "LME", "LMH", "LML", "HLT", +NULL }; + +static const char *const dval[] = { +"JFC", "CFC", "JMP", "CAL", "JFZ", "CFZ", "", "", +"JFS", "CFS", "", "", "JFP", "CFP", "", "", +"JTC", "CTC", "", "", "JTZ", "CTZ", "", "", +"JTS", "CTS", "", "", "JTP", "CTP", +NULL }; + +static const char *const eval[] = { "", "INB", "INC", "IND", + "INE", "INH", "INL", + NULL }; + +static const char *const fval[] = { "", "DCB", "DCC", "DCD", + "DCE", "DCH", "DCL", + NULL }; + +static const char *const gval[] = { "ADI", "ACI", "SUI", "SBI", + "NDI", "XRI", "ORI", "CPI", + NULL }; + +static const char *const hval[] = { "LAI", "LBI", "LCI", "LDI", + "LEI", "LHI", "LLI", "LMI", + NULL }; + +static const char *const ival[] = { "RFC", "RFZ", "RFS", "RFP", + "RTC", "RTZ", "RTS", "RTP", + NULL }; + +static const char *const jval[] = { "RLC", "RRC", "RAL", "RAR", + NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival, jval +}; + +static int match_i8008(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'j') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i8008(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 7)) { + eprint(_("argument (%d) must be in range [0-7]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= (vs[i] << 1); + break; + case 'g': if (s_pass > 0 && (vs[i] < 8 || vs[i] > 31)) { + eprint(_("argument (%d) must be in range [8-31]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= (vs[i] << 1); + break; + case 'h': b |= (vs[i] << 4); break; + case 'i': b |= (vs[i] << 1); break; + case 'j': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 7)) { + eprint(_("argument (%d) must be in range [0-7]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= (vs[i] << 3); + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i8008(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i8008(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'j') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i8008 = { + .id = "i8008", + .descr = "Intel 8008", + .matcht = s_matchtab_i8008, + .matchf = match_i8008, + .genf = gen_i8008, + .pat_char_rewind = pat_char_rewind_i8008, + .pat_next_str = pat_next_str_i8008, + .mask = 1 +}; + diff --git a/Tools/unix/uz80as/i8048.c b/Tools/unix/uz80as/i8048.c new file mode 100644 index 00000000..1526d252 --- /dev/null +++ b/Tools/unix/uz80as/i8048.c @@ -0,0 +1,276 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 8021. + * Intel 8022. + * Intel 8041. + * Intel 8048. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: R0,R1,R2,R3,R4,R5,R6,R7 + * c: R0,R1 + * d: P1,P2 + * e: P4,P5,P6,P7 + * f: JB0,JB1,JB2,JB3,JB4,JB5,JB6,JB7 + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: output lastbyte | ((op & 0x700) >> 3) + * output op as 8 bit value + * (no '.' should follow) + * g: (op << 5) | lastbyte + */ + +static const struct matchtab s_matchtab_i8048[] = { + { "NOP", "00.", 1, 0 }, + { "ADD A,b", "68c0.", 1, 0 }, + { "ADD A,@c", "60c0.", 1, 0 }, + { "ADD A,#a", "03.d0.", 1, 0, "e8" }, + { "ADDC A,b", "78c0.", 1, 0 }, + { "ADDC A,@c", "70c0.", 1, 0 }, + { "ADDC A,#a", "13.d0.", 1, 0, "e8" }, + { "ANL A,b", "58c0.", 1, 0 }, + { "ANL A,@c", "50c0.", 1, 0 }, + { "ANL A,#a", "53.d0.", 1, 0, "e8" }, + { "ANL BUS,#a", "98.d0.", 32, 0, "e8" }, + { "ANL d,#a", "98c0.d1.", 4, 0, "e8" }, + { "ANLD e,A", "9Cc0.", 1, 0 }, + { "CALL a", "14f0", 1, 0 }, + { "CLR A", "27.", 1, 0 }, + { "CLR C", "97.", 1, 0 }, + { "CLR F1", "A5.", 4, 0 }, + { "CLR F0", "85.", 4, 0 }, + { "CPL A", "37.", 1, 0 }, + { "CPL C", "A7.", 1, 0 }, + { "CPL F0", "95.", 4, 0 }, + { "CPL F1", "B5.", 4, 0 }, + { "DA A", "57.", 1, 0 }, + { "DEC A", "07.", 1, 0 }, + { "DEC b", "C8c0.", 4, 0 }, + { "DIS I", "15.", 2, 0 }, + { "DIS TCNTI", "35.", 2, 0 }, + { "DJNZ b,a", "E8c0.d1.", 1, 0, "e8" }, + { "EN DMA", "E5.", 64, 0 }, + { "EN FLAGS", "F5.", 64, 0 }, + { "EN I", "05.", 2, 0 }, + { "EN TCNTI", "25.", 2, 0 }, + { "ENT0 CLK", "75.", 32, 0 }, + { "IN A,DBB", "22.", 64, 0 }, + { "IN A,P0", "08.", 8, 0 }, + { "IN A,d", "08c0.", 1, 0 }, + { "INC A", "17.", 1, 0 }, + { "INC b", "18c0.", 1, 0 }, + { "INC @c", "10c0.", 1, 0 }, + { "INS A,BUS", "08.", 32, 0 }, + { "f a", "12g0.d1.", 4, 0, "e8" }, + { "JC a", "F6.d0.", 1, 0, "e8" }, + { "JF0 a", "B6.d0.", 4, 0, "e8" }, + { "JF1 a", "76.d0.", 4, 0, "e8" }, + { "JMP a", "04f0", 1, 0, "e11" }, + { "JMPP @A", "B3.", 1, 0 }, + { "JNC a", "E6.d0.", 1, 0, "e8" }, + { "JNI a", "86.d0.", 32, 0, "e8" }, + { "JNIBF a", "D6.d0.", 64, 0, "e8" }, + { "JNT0 a", "26.d0.", 2, 0, "e8" }, + { "JNT1 a", "46.d0.", 1, 0, "e8" }, + { "JNZ a", "96.d0.", 1, 0, "e8" }, + { "JOBF a", "86.d0.", 64, 0, "e8" }, + { "JTF a", "16.d0.", 1, 0, "e8" }, + { "JT0 a", "36.d0.", 2, 0, "e8" }, + { "JT1 a", "56.d0.", 1, 0, "e8" }, + { "JZ a", "C6.d0.", 1, 0, "e8" }, + { "MOV A,#a", "23.d0.", 1, 0, "e8" }, + { "MOV A,PSW", "C7.", 4, 0 }, + { "MOV A,b", "F8c0.", 1, 0 }, + { "MOV A,@c", "F0c0.", 1, 0 }, + { "MOV A,T", "42.", 1, 0 }, + { "MOV PSW,A", "D7.", 4, 0 }, + { "MOV b,A", "A8c0.", 1, 0 }, + { "MOV b,#a", "B8c0.d1.", 1, 0, "e8" }, + { "MOV @c,A", "A0c0.", 1, 0 }, + { "MOV @c,#a", "B0c0.d1.", 1, 0, "e8" }, + { "MOV STS,A", "90.", 64, 0 }, + { "MOV T,A", "62.", 1, 0 }, + { "MOVD A,e", "0Cc0.", 1, 0 }, + { "MOVD e,A", "3Cc0.", 1, 0 }, + { "MOVP A,@A", "A3.", 1, 0 }, + { "MOVP3 A,@A", "E3.", 4, 0 }, + { "MOVX A,@c", "80c0.", 32, 0 }, + { "MOVX @c,A", "90c0.", 32, 0 }, + { "NOP", "00.", 1, 0 }, + { "ORL A,b", "48c0.", 1, 0 }, + { "ORL A,@c", "40c0.", 1, 0 }, + { "ORL A,#a", "43.d0.", 1, 0, "e8" }, + { "ORL BUS,#a", "88.d0.", 32, 0, "e8" }, + { "ORL d,#a", "88c0.d1.", 4, 0, "e8" }, + { "ORLD e,A", "8Cc0.", 1, 0 }, + { "OUT DBB,A", "02.", 64, 0 }, + { "OUTL BUS,A", "02.", 32, 0 }, + { "OUTL P0,A", "90.", 8, 0 }, + { "OUTL d,A", "38c0.", 1, 0 }, + { "RAD", "80.", 16, 0 }, + { "RET", "83.", 1, 0 }, + { "RETR", "93.", 4, 0 }, + { "RETI", "93.", 16, 0 }, + { "RL A", "E7.", 1, 0 }, + { "RLC A", "F7.", 1, 0 }, + { "RR A", "77.", 1, 0 }, + { "RRC A", "67.", 1, 0 }, + { "SEL AN0", "85.", 16, 0 }, + { "SEL AN1", "95.", 16, 0 }, + { "SEL MB0", "E5.", 32, 0 }, + { "SEL MB1", "F5.", 32, 0 }, + { "SEL RB0", "C5.", 4, 0 }, + { "SEL RB1", "D5.", 4, 0 }, + { "STOP TCNT", "65.", 1, 0 }, + { "STRT CNT", "45.", 1, 0 }, + { "STRT T", "55.", 1, 0 }, + { "SWAP A", "47.", 1, 0 }, + { "XCH A,b", "28c0.", 1, 0 }, + { "XCH A,@c", "20c0.", 1, 0 }, + { "XCHD A,@c", "30c0.", 1, 0 }, + { "XRL A,b", "D8c0.", 1, 0 }, + { "XRL A,@c", "D0c0.", 1, 0 }, + { "XRL A,#a", "D3.d0.", 1, 0, "e8" }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", +NULL }; + +static const char *const cval[] = { +"R0", "R1", +NULL }; + +static const char *const dval[] = { +"", "P1", "P2", +NULL }; + +static const char *const eval[] = { +"P4", "P5", "P6", "P7", +NULL }; + +static const char *const fval[] = { +"JB0", "JB1", "JB2", "JB3", "JB4", "JB5", "JB6", "JB7", +NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval +}; + +static int match_i8048(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'f') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i8048(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b |= ((vs[i] & 0x700) >> 3); + genb(b, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'g': b |= (vs[i] << 5); + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i8048(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i8048(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'f') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i8041 = { + .id = "i8041", + .descr = "Intel 8041", + .matcht = s_matchtab_i8048, + .matchf = match_i8048, + .genf = gen_i8048, + .pat_char_rewind = pat_char_rewind_i8048, + .pat_next_str = pat_next_str_i8048, + .mask = 71 +}; + +const struct target s_target_i8048 = { + .id = "i8048", + .descr = "Intel 8048", + .matcht = s_matchtab_i8048, + .matchf = match_i8048, + .genf = gen_i8048, + .pat_char_rewind = pat_char_rewind_i8048, + .pat_next_str = pat_next_str_i8048, + .mask = 39 +}; + +const struct target s_target_i8021 = { + .id = "i8021", + .descr = "Intel 8021", + .matcht = s_matchtab_i8048, + .matchf = match_i8048, + .genf = gen_i8048, + .pat_char_rewind = pat_char_rewind_i8048, + .pat_next_str = pat_next_str_i8048, + .mask = 9 +}; + +const struct target s_target_i8022 = { + .id = "i8022", + .descr = "Intel 8022", + .matcht = s_matchtab_i8048, + .matchf = match_i8048, + .genf = gen_i8048, + .pat_char_rewind = pat_char_rewind_i8048, + .pat_next_str = pat_next_str_i8048, + .mask = 27 +}; + diff --git a/Tools/unix/uz80as/i8051.c b/Tools/unix/uz80as/i8051.c new file mode 100644 index 00000000..034e1292 --- /dev/null +++ b/Tools/unix/uz80as/i8051.c @@ -0,0 +1,244 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 8051. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: R0,R1,R2,R3,R4,R5,R6,R7 + * c: R0,R1 + * d: ADD,ADDC,ORL,ANL,XRL,SUBB,XCH,MOV + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: output lastbyte | ((op & 0x700) >> 3) + * output op as 8 bit value + * (no '.' should follow) + * g: relative jump -2 + * h: relative jump -3 + * i: ouput op as big endian word + * j: b + (op << 4) + */ + +static const struct matchtab s_matchtab_i8051[] = { + { "ACALL a", "11f0", 1, 0, "e11" }, + { "d A,b", "08j0c1.", 1, 0 }, + { "d A,@c", "06j0c1.", 1, 0 }, + { "ADD A,#a", "24.d0.", 1, 0, "e8" }, + { "ADDC A,#a", "34.d0.", 1, 0, "e8" }, + { "ORL A,#a", "44.d0.", 1, 0, "e8" }, + { "ANL A,#a", "54.d0.", 1, 0, "e8" }, + { "XRL A,#a", "64.d0.", 1, 0, "e8" }, + { "SUBB A,#a", "94.d0.", 1, 0, "e8" }, + { "MOV A,#a", "74.d0.", 1, 0, "e8" }, + { "d A,a", "05j0.d1.", 1, 0, "e8" }, + // { "ADD A,b", "28c0.", 1, 0 }, + // { "ADD A,@c", "26c0.", 1, 0 }, + // { "ADD A,a", "25.d0.", 1, 0, "e8" }, + // { "ADDC A,b", "38c0.", 1, 0 }, + // { "ADDC A,@c", "36c0.", 1, 0 }, + // { "ADDC A,a", "35.d0.", 1, 0, "e8" }, + { "AJMP a", "01f0", 1, 0, "e11" }, + // { "ANL A,b", "58c0.", 1, 0 }, + // { "ANL A,@c", "56c0.", 1, 0 }, + // { "ANL A,a", "55.d0.", 1, 0, "e8" }, + { "ANL C,/a", "B0.d0.", 1, 0, "e8" }, + { "ANL C,a", "82.d0.", 1, 0, "e8" }, + { "ANL a,A", "52.d0.", 1, 0 }, + { "ANL a,#a", "53.d0.d1.", 1, 0, "e8e8" }, + { "CJNE A,#a,a", "B4.d0.h1.", 1, 0, "e8r8" }, + { "CJNE b,#a,a", "B8c0.d1.h2.", 1, 0, "e8r8" }, + { "CJNE @c,#a,a", "B6c0.d1.h2.", 1, 0, "e8r8" }, + { "CJNE A,a,a", "B5.d0.h1.", 1, 0, "e8r8" }, + { "CLR A", "E4.", 1, 0 }, + { "CLR C", "C3.", 1, 0 }, + { "CLR a", "C2.d0.", 1, 0, "e8" }, + { "CPL A", "F4.", 1, 0 }, + { "CPL C", "B3.", 1, 0 }, + { "CPL a", "B2.d0.", 1, 0, "e8" }, + { "DA A", "D4.", 1, 0 }, + { "DEC A", "14.", 1, 0 }, + { "DEC b", "18c0.", 1, 0 }, + { "DEC @c", "16c0.", 1, 0 }, + { "DEC a", "15.d0.", 1, 0, "e8" }, + { "DIV AB", "84.", 1, 0 }, + { "DJNZ b,a", "D8c0.g1.", 1, 0, "r8" }, + { "DJNZ a,a", "D5.d0.h1.", 1, 0, "e8r8" }, + { "INC DPTR", "A3.", 1, 0 }, + { "INC A", "04.", 1, 0 }, + { "INC b", "08c0.", 1, 0 }, + { "INC @c", "06c0.", 1, 0 }, + { "INC a", "05.d0.", 1, 0, "e8" }, + { "JB a,a", "20.d0.h1.", 1, 0, "e8r8" }, + { "JBC a,a", "10.d0.h1.", 1, 0, "e8r8" }, + { "JC a", "40.g0.", 1, 0, "r8" }, + { "JMP @A+DPTR", "73.", 1, 0 }, + { "JNB a,a", "30.d0.h1.", 1, 0, "e8r8" }, + { "JNC a", "50.g0.", 1, 0, "r8" }, + { "JNZ a", "70.g0.", 1, 0, "r8" }, + { "JZ a", "60.g0.", 1, 0, "r8" }, + { "LCALL a", "12.i0", 1, 0 }, + { "LJMP a", "02.i0", 1, 0 }, + // { "MOV A,b", "E8c0.", 1, 0 }, + // { "MOV A,@c", "E6c0.", 1, 0 }, + // { "MOV A,a", "E5.d0.", 1, 0, "e8" }, // MOV A,ACC not valid? + { "MOV b,A", "F8c0.", 1, 0 }, + { "MOV b,#a", "78c0.d1.", 1, 0, "e8" }, + { "MOV b,a", "A8c0.d1.", 1, 0, "e8" }, + { "MOV @c,A", "F6c0.", 1, 0 }, + { "MOV @c,#a", "76c0.d1.", 1, 0, "e8" }, + { "MOV @c,a", "A6c0.d1.", 1, 0, "e8" }, + { "MOV C,a", "A2.d0.", 1, 0, "e8" }, + { "MOV DPTR,#a", "90.i0", 1, 0, "e8" }, + { "MOV a,A", "F5.d0.", 1, 0, "e8" }, + { "MOV a,C", "92.d0.", 1, 0, "e8" }, + { "MOV a,b", "88c1.d0.", 1, 0, "e8" }, + { "MOV a,@c", "86c1.d0.", 1, 0, "e8" }, + { "MOV a,#a", "75.d0.d1.", 1, 0, "e8e8" }, + { "MOV a,a", "85.d1.d0.", 1, 0, "e8e8" }, + { "MOVC A,@A+DPTR", "93.", 1, 0 }, + { "MOVC A,@A+PC", "83.", 1, 0 }, + { "MOVX A,@c", "E2c0.", 1, 0 }, + { "MOVX A,@DPTR", "E0.", 1, 0 }, + { "MOVX @c,A", "F2c0.", 1, 0 }, + { "MOVX @DPTR,A", "F0.", 1, 0 }, + { "MUL AB", "A4.", 1, 0 }, + { "NOP", "00.", 1, 0 }, + // { "ORL A,b", "48c0.", 1, 0 }, + // { "ORL A,@c", "46c0.", 1, 0 }, + // { "ORL A,a", "45.d0.", 1, 0, "e8" }, + { "ORL C,/a", "A0.d0.", 1, 0, "e8" }, + { "ORL C,a", "72.d0.", 1, 0, "e8" }, + { "ORL a,A", "42.d0.", 1, 0, "e8" }, + { "ORL a,#a", "43.d0.d1.", 1, 0, "e8e8" }, + { "POP a", "D0.d0.", 1, 0, "e8" }, + { "PUSH a", "C0.d0.", 1, 0, "e8" }, + { "RET", "22.", 1, 0 }, + { "RETI", "32.", 1, 0 }, + { "RL A", "23.", 1, 0 }, + { "RLC A", "33.", 1, 0 }, + { "RR A", "03.", 1, 0 }, + { "RRC A", "13.", 1, 0 }, + { "SETB C", "D3.", 1, 0 }, + { "SETB a", "D2.d0.", 1, 0, "e8" }, + { "SJMP a", "80.g0.", 1, 0, "r8" }, + // { "SUBB A,b", "98c0.", 1, 0 }, + // { "SUBB A,@c", "96c0.", 1, 0 }, + // { "SUBB A,a", "95.d0.", 1, 0, "e8" }, + { "SWAP A", "C4.", 1, 0 }, + // { "XCH A,b", "C8c0.", 1, 0 }, + // { "XCH A,@c", "C6c0.", 1, 0 }, + // { "XCH A,a", "C5.d0.", 1, 0, "e8" }, + { "XCHD A,@c", "D6c0.", 1, 0 }, + // { "XRL A,b", "68c0.", 1, 0 }, + // { "XRL A,@c", "66c0.", 1, 0 }, + // { "XRL A,a", "65.d0.", 1, 0, "e8" }, + { "XRL a,A", "62.d0.", 1, 0, "e8" }, + { "XRL a,#a", "63.d0.d1.", 1, 0, "e8e8" }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", +NULL }; + +static const char *const cval[] = { +"R0", "R1", +NULL }; + +static const char *const dval[] = { +"", "", "ADD", "ADDC", "ORL", "ANL", "XRL", "", +"", "SUBB", "", "", "XCH", "", "MOV", +NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval +}; + +static int match_i8051(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'd') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i8051(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b |= ((vs[i] & 0x700) >> 3); + genb(b, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'g': b = (vs[i] - savepc - 2); break; + break; + case 'h': b = (vs[i] - savepc - 3); break; + break; + case 'i': genb(vs[i] >> 8, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'j': b += (vs[i] << 4); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i8051(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i8051(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'd') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i8051 = { + .id = "i8051", + .descr = "Intel 8051", + .matcht = s_matchtab_i8051, + .matchf = match_i8051, + .genf = gen_i8051, + .pat_char_rewind = pat_char_rewind_i8051, + .pat_next_str = pat_next_str_i8051, + .mask = 1 +}; + diff --git a/Tools/unix/uz80as/i8080.c b/Tools/unix/uz80as/i8080.c new file mode 100644 index 00000000..e0f7f51b --- /dev/null +++ b/Tools/unix/uz80as/i8080.c @@ -0,0 +1,214 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 8080. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: B,C,D,E,H,L,M,A + * c: B,D,H,SP + * d: B,D + * e: B,D,H,PSW + * f: JNZ,JZ,JNC,JC,JPO,JPE,JP,JM + * g: CNZ,CZ,CNC,CC,CPO,CPE,CP,CM + * h: RNZ,RZ,RNC,RC,RPO,RPE,RP,RM + * i: B,C,D,E,H,L,A + * j: ADD,ADC,SUB,SBB,ANA,XRA,ORA,CMP + * k: RLC,RRC,RAL,RAR + * l: ADI,ACI,SUI,SBI,ANI,XRI,ORI,CPI + * m: SHLD,LHLD,STA,LDA + * n: DI,EI + * o: OUT,IN + * p: STC,CMC + * q: POP,PUSH + * r: STAX,LDAX + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 4) | lastbyte + * g: possible value to RST + * h: (op << 2) | lastbyte + */ + +static const struct matchtab s_matchtab_i8080[] = { + { "MOV M,i", "70c0.", 3, 0 }, + { "MOV i,M", "46b0.", 3, 0 }, + { "MOV i,i", "40b0c1.", 3, 0 }, + { "MVI b,a", "06b0.d1.", 3, 0, "e8" }, + { "LXI c,a", "01f0.e1", 3, 0 }, + { "m a", "22b0.e1", 3, 0 }, + { "r d", "02b0f1.", 3, 0 }, + { "XCHG", "EB.", 3, 0 }, + { "j b", "80b0c1.", 3, 0 }, + { "l a", "C6b0.d1.", 3, 0, "e8" }, + { "INR b", "04b0.", 3, 0 }, + { "DCR b", "05b0.", 3, 0 }, + { "INX c", "03f0.", 3, 0 }, + { "DCX c", "0Bf0.", 3, 0 }, + { "DAD c", "09f0.", 3, 0 }, + { "DAA", "27.", 3, 0 }, + { "k", "07b0.", 3, 0 }, + { "CMA", "2F.", 3, 0 }, + { "p", "37b0.", 3, 0 }, + { "JMP a", "C3.e0", 3, 0 }, + { "f a", "C2b0.e1", 3, 0 }, + { "CALL a", "CD.e0", 3, 0 }, + { "g a", "C4b0.e1", 3, 0 }, + { "RET", "C9.", 3, 0 }, + { "h", "C0b0.", 3, 0 }, + { "RST a", "C7g0.", 3, 0, "b3" }, + { "PCHL", "E9.", 3, 0 }, + { "q e", "C1h0f1.", 3, 0 }, + { "XTHL", "E3.", 3, 0 }, + { "SPHL", "F9.", 3, 0 }, + { "o a", "D3b0.d1.", 3, 0, "e8" }, + { "n", "F3b0.", 3, 0 }, + { "HLT", "76.", 3, 0 }, + { "NOP", "00.", 3, 0 }, + /* 8085 added instructions */ + { "RIM", "20.", 2, 0 }, + { "SIM", "30.", 2, 0 }, + { "ARHL", "10.", 2, 2 }, + { "DSUB", "08.", 2, 2 }, + { "RDEL", "18.", 2, 2 }, + { "LDHI a", "28.d0.", 2, 2, "e8" }, + { "LDSI a", "38.d0.", 2, 2, "e8" }, + { "RSTV", "CB.", 2, 2 }, + { "SHLX", "D9.", 2, 2 }, + { "LHLX", "ED.", 2, 2 }, + { "JNK a", "DD.e0", 2, 2 }, + { "JNX5 a", "DD.e0", 2, 2 }, + { "JNUI a", "DD.e0", 2, 2 }, + { "JK a", "FD.e0", 2, 2 }, + { "JX5 a", "FD.e0", 2, 2 }, + { "JUI a", "FD.e0", 2, 2 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { "B", "C", "D", "E", + "H", "L", "M", "A", NULL }; +static const char *const cval[] = { "B", "D", "H", "SP", NULL }; +static const char *const dval[] = { "B", "D", NULL }; +static const char *const eval[] = { "B", "D", "H", "PSW", NULL }; +static const char *const fval[] = { "JNZ", "JZ", "JNC", "JC", + "JPO", "JPE", "JP", "JM", NULL }; +static const char *const gval[] = { "CNZ", "CZ", "CNC", "CC", + "CPO", "CPE", "CP", "CM", NULL }; +static const char *const hval[] = { "RNZ", "RZ", "RNC", "RC", + "RPO", "RPE", "RP", "RM", NULL }; +static const char *const ival[] = { "B", "C", "D", "E", + "H", "L", "", "A", NULL }; +static const char *const jval[] = { "ADD", "ADC", "SUB", "SBB", + "ANA", "XRA", "ORA", "CMP", NULL }; +static const char *const kval[] = { "RLC", "RRC", "RAL", "RAR", NULL }; +static const char *const lval[] = { "ADI", "ACI", "SUI", "SBI", + "ANI", "XRI", "ORI", "CPI", NULL }; +static const char *const mval[] = { "SHLD", "LHLD", "STA", "LDA", NULL }; +static const char *const nval[] = { "DI", "EI", NULL }; +static const char *const oval[] = { "OUT", "IN", NULL }; +static const char *const pval[] = { "STC", "CMC", NULL }; +static const char *const qval[] = { "POP", "PUSH", NULL }; +static const char *const rval[] = { "STAX", "LDAX", NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival, jval, kval, + lval, mval, nval, oval, pval, + qval, rval +}; + +static int match_i8080(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'r') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i8080(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b |= (vs[i] << 4); break; + case 'g': if (s_pass > 0 && (vs[i] & ~7) != 0) { + eprint(_("invalid RST argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= (vs[i] << 3); + break; + case 'h': b |= (vs[i] << 2); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i8080(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i8080(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'r') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i8080 = { + .id = "i8080", + .descr = "Intel 8080", + .matcht = s_matchtab_i8080, + .matchf = match_i8080, + .genf = gen_i8080, + .pat_char_rewind = pat_char_rewind_i8080, + .pat_next_str = pat_next_str_i8080, + .mask = 1 +}; + +const struct target s_target_i8085 = { + .id = "i8085", + .descr = "Intel 8085", + .matcht = s_matchtab_i8080, + .matchf = match_i8080, + .genf = gen_i8080, + .pat_char_rewind = pat_char_rewind_i8080, + .pat_next_str = pat_next_str_i8080, + .mask = 2 +}; diff --git a/Tools/unix/uz80as/incl.c b/Tools/unix/uz80as/incl.c new file mode 100644 index 00000000..e11d4675 --- /dev/null +++ b/Tools/unix/uz80as/incl.c @@ -0,0 +1,81 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Include file stack. + * =========================================================================== + */ + +#include "config.h" +#include "incl.h" +#include "utils.h" +#include "err.h" + +#ifndef ASSERT_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +/* Max number of nested included files. */ +#define NFILES 128 + +/* Number of nested files. */ +static int s_nfiles; + +/* Current file. */ +static struct incfile *s_curfile; + +/* Get the current file. Never returns NULL. */ +struct incfile *curfile(void) +{ + assert(s_curfile != NULL); + return s_curfile; +} + +/* The number of nested files. 0 means no file loaded. */ +int nfiles(void) +{ + return s_nfiles; +} + +/* Leave the current included file. */ +void popfile(void) +{ + struct incfile *ifile; + + assert(s_curfile != NULL); + fclose(s_curfile->fin); + ifile = s_curfile; + s_curfile = ifile->prev; + free(ifile); + s_nfiles--; +} + +/* Include a file whose name is [p, q[. */ +void pushfile(const char *p, const char *q) +{ + struct incfile *ifile; + + if (s_nfiles == NFILES) { + eprint(_("maximum number of nested includes exceeded (%d)\n"), + NFILES); + exit(EXIT_FAILURE); + } + + // printf("pushfile: %s\n", p); + ifile = emalloc((sizeof *ifile) + (q - p) + 1); + ifile->name = (char *) ((unsigned char *) ifile + sizeof *ifile); + copychars(ifile->name, p, q); + + ifile->fin = efopen(ifile->name, "r"); + ifile->linenum = 0; + ifile->prev = s_curfile; + s_curfile = ifile; + s_nfiles++; +} diff --git a/Tools/unix/uz80as/incl.h b/Tools/unix/uz80as/incl.h new file mode 100644 index 00000000..1da62f61 --- /dev/null +++ b/Tools/unix/uz80as/incl.h @@ -0,0 +1,28 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Include file stack. + * =========================================================================== + */ + +#ifndef INCL_H +#define INCL_H + +#ifndef STDIO_H +#define STDIO_H +#include +#endif + +struct incfile { + struct incfile *prev; + FILE *fin; + int linenum; + char *name; +}; + +void pushfile(const char *p, const char *q); +void popfile(void); +struct incfile *curfile(void); +int nfiles(void); + +#endif diff --git a/Tools/unix/uz80as/list.c b/Tools/unix/uz80as/list.c new file mode 100644 index 00000000..f340e30b --- /dev/null +++ b/Tools/unix/uz80as/list.c @@ -0,0 +1,164 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Assembly listing generation. + * =========================================================================== + */ + +#include "config.h" +#include "list.h" +#include "err.h" + +#ifndef STDIO_H +#include +#endif + +#ifndef STLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +static FILE *s_list_file; + +/* N characters in current line. */ +static int s_nchars; + +/* Generated data bytes in this line. */ +static int s_nbytes; + +/* Line text. */ +static const char *s_line; + +/* Line number. */ +static int s_linenum; + +/* Program counter. */ +static int s_pc; + +/* Number of current nested files. */ +static int s_nfiles; + +/* If listing line number, etc are generated or just the line. */ +int s_codes = 1; + +/* If listing is enabled or not. */ +int s_list_on = 1; + +/* If we are skipping lines. */ +static int s_skip_on = 0; + +void list_open(const char *fname) +{ + s_list_file = fopen(fname, "w"); + if (s_list_file == NULL) { + eprint(_("cannot open file %s\n"), fname); + } +} + +void list_close(void) +{ + if (s_list_file != NULL) + fclose(s_list_file); +} + +static void prhead(void) +{ + int i, j, n; + + s_nchars = fprintf(s_list_file, "%-.4d", s_linenum); + + n = 7 - s_nchars; + if (n <= 0) + n = 1; + j = 0; + if (s_nfiles > 0) + j = s_nfiles - 1; + if (j > n) + j = n; + for (i = 0; i < j; i++) + fputc('+', s_list_file); + j = n - j; + while (j--) + fputc(' ', s_list_file); + s_nchars += n; + + s_nchars += fprintf(s_list_file, "%.4X", s_pc); + if (s_skip_on) + fputc('~', s_list_file); + else + fputc(' ', s_list_file); + s_nchars += 1; +} + +static void prline(void) +{ + if (s_line == NULL) { + fputs("\n", s_list_file); + } else { + if (s_codes) { + while (s_nchars < 24) { + s_nchars++; + fputc(' ', s_list_file); + } + } + fprintf(s_list_file, "%s\n", s_line); + s_line = NULL; + } + s_nchars = 0; + s_nbytes = 0; +} + +void list_startln(const char *line, int linenum, int pc, int nested_files) +{ + if (s_list_file == NULL) + return; + s_linenum = linenum; + s_pc = pc; + s_line = line; + s_nchars = 0; + s_nbytes = 0; + s_nfiles = nested_files; +} + +void list_setpc(int pc) +{ + s_pc = pc; +} + +void list_skip(int on) +{ + s_skip_on = on; +} + +void list_eject(void) +{ + if (s_list_file == NULL || !s_list_on) + return; +} + +void list_genb(int b) +{ + if (s_list_file == NULL || !s_codes || !s_list_on) + return; + if (s_nchars == 0) + prhead(); + if (s_nbytes >= 4) { + prline(); + prhead(); + } + s_nchars += fprintf(s_list_file, "%2.2X ", (b & 0xff)); + s_nbytes++; + s_pc++; +} + +void list_endln(void) +{ + if (s_list_file == NULL || !s_list_on) + return; + if (s_codes && s_nchars == 0) + prhead(); + prline(); +} diff --git a/Tools/unix/uz80as/list.h b/Tools/unix/uz80as/list.h new file mode 100644 index 00000000..7868cd29 --- /dev/null +++ b/Tools/unix/uz80as/list.h @@ -0,0 +1,23 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Assembly listing generation. + * =========================================================================== + */ + +#ifndef LIST_H +#define LIST_H + +extern int s_codes; +extern int s_list_on; + +void list_open(const char *fname); +void list_close(void); +void list_startln(const char *line, int linenum, int pc, int nested_files); +void list_setpc(int pc); +void list_skip(int on); +void list_eject(void); +void list_genb(int b); +void list_endln(void); + +#endif diff --git a/Tools/unix/uz80as/main.c b/Tools/unix/uz80as/main.c new file mode 100644 index 00000000..e3e3ff1e --- /dev/null +++ b/Tools/unix/uz80as/main.c @@ -0,0 +1,303 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * main(), handling of command line options. + * =========================================================================== + */ + +#include "config.h" +#include "ngetopt.h" +#include "options.h" +#include "utils.h" +#include "err.h" +#include "uz80as.h" +#include "prtable.h" +#include "targets.h" + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +void print_copyright(FILE *f) +{ + static const char *copyright = +"Copyright (C) " COPYRIGHT_YEARS " Jorge Giner Cordero.\n"; + + fputs(copyright, f); +} + +static void print_license(FILE *f) +{ + static const char *license[] = { +"Permission is hereby granted, free of charge, to any person obtaining", +"a copy of this software and associated documentation files (the", +"\"Software\"), to deal in the Software without restriction, including", +"without limitation the rights to use, copy, modify, merge, publish,", +"distribute, sublicense, and/or sell copies of the Software, and to", +"permit persons to whom the Software is furnished to do so, subject to", +"the following conditions:", +"", +"The above copyright notice and this permission notice shall be included", +"in all copies or substantial portions of the Software.", +"", +"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", +"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", +"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.", +"IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", +"CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", +"TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE", +"SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + }; + + int i; + + for (i = 0; i < NELEMS(license); i++) { + fprintf(f, "%s\n", license[i]); + } +} + +static void print_version(FILE *f) +{ + const struct target *p; + + fputs(PACKAGE_STRING, f); + fputs("\n", f); + fputs("Targets:", f); + for (p = first_target(); p != NULL; p = next_target()) { + fprintf(f, " %s", p->id); + } + fputs("\n", f); +} + +static void print_help(const char *argv0) +{ + static const char *help = +"Usage: %s [OPTION]... ASM_FILE [OBJ_FILE [LST_FILE]]\n" +"\n" +"Assemble ASM_FILE into OBJ_FILE and generate the listing LST_FILE.\n" +"If not specified, OBJ_FILE is ASM_FILE with the extension changed to .obj.\n" +"If not specified, LST_FILE is ASM_FILE with the extension changed to .lst.\n" +"\n" +"Options:\n" +" -h, --help Display this help and exit.\n" +" -V, --verbose be chatty.\n" +" -v, --version Output version information and exit.\n" +" -l, --license Display the license text and exit.\n" +" -d, --define=MACRO Define a macro.\n" +" -f, --fill=n Fill memory with value n.\n" +" -q, --quiet Do not generate the listing file.\n" +" -x, --extended Enable extended instruction syntax.\n" +" -u, --undocumented Enable undocumented instructions.\n" +" -t, --target=NAME Select the target micro. z80 is the default.\n" +" -e, --list-targets List the targets supported.\n" +"\n" +"Examples:\n" +" " PACKAGE " p.asm Assemble p.asm into p.obj\n" +" " PACKAGE " p.asm p.bin Assemble p.asm into p.bin\n" +" " PACKAGE " -d\"MUL(a,b) (a*b)\" p.asm Define the macro MUL and assemble p.asm\n" +"\n" +"Report bugs to: <" PACKAGE_BUGREPORT ">.\n" +"Home page: <" PACKAGE_URL ">.\n"; + + printf(help, argv0); +} + +/* + * Get the filename part of fname (that is, for ../../fname.abc, get + * fname.abc). + * Then substitute the extension .abd by .ext or append .ext. + */ +static char *mkfname(const char *fname, const char *ext) +{ + size_t alen, elen; + const char *p, *q; + char *s; + + alen = strlen(fname); + elen = strlen(ext); + + /* Find start of filename in path string */ + p = fname + alen; + while (p > fname && *p != '/' && *p != '\\') + p--; + + if (*p == '/' || *p == '\\') + p++; + + /* Find the extension */ + q = fname + alen; + while (q > p && *q != '.') + q--; + + if (*q != '.') + q = fname + alen; + + s = emalloc((q - p) + 1 + elen + 1); + if (q > p) + memmove(s, p, (q - p)); + s[q - p] = '\0'; + strcat(s, "."); + strcat(s, ext); + return s; +} + +static void parse_fill_byte(const char *optarg) +{ + int hi, lo; + + if (strlen(optarg) != 2) + goto error; + + if ((hi = hexval(optarg[0])) < 0) + goto error; + if ((lo = hexval(optarg[1])) < 0) + goto error; + + s_mem_fillval = hi * 16 + lo; + return; + +error: eprogname(); + fprintf(stderr, _("invalid command line fill value (%s)\n"), optarg); + eprogname(); + fprintf(stderr, " "); + fprintf(stderr, _("Please, use two hexadecimal digits.\n")); + exit(EXIT_FAILURE); +} + +static void parse_target_id(const char *optarg) +{ + const struct target *p; + + p = find_target(optarg); + if (p == NULL) { + eprogname(); + fprintf(stderr, _("invalid target '%s'\n"), optarg); + exit(EXIT_FAILURE); + } else { + s_target_id = p->id; + } +} + +static void list_targets(FILE *f) +{ + const struct target *p; + + for (p = first_target(); p != NULL; p = next_target()) { + fprintf(f, "%-14s%s\n", p->id, p->descr); + } +} + +int main(int argc, char *argv[]) +{ + int c; + struct ngetopt ngo; + + static struct ngetopt_opt ops[] = { + { "define", 1, 'd' }, + { "extended", 0, 'x' }, + { "list-targets", 0, 'e' }, + { "fill", 1, 'f' }, + { "help", 0, 'h' }, + { "license", 0, 'l' }, + { "quiet", 0, 'q' }, + { "target", 1, 't' }, + { "undocumented", 0, 'u' }, + { "version", 0, 'v' }, + { "print-table", 1, 0 }, + { "print-delta", 1, 0 }, + { "verbose", 0, 'V' }, + { NULL, 0, 0 }, + }; + + ngetopt_init(&ngo, argc, argv, ops); + do { + c = ngetopt_next(&ngo); + switch (c) { + case 'V': + verbose++; + break; + case 'v': + print_version(stdout); + exit(EXIT_SUCCESS); + case 'h': + print_help(argv[0]); + exit(EXIT_SUCCESS); + case 'l': + print_copyright(stdout); + fputs("\n", stdout); + print_license(stdout); + exit(EXIT_SUCCESS); + case 't': + parse_target_id(ngo.optarg); + break; + case 'e': + list_targets(stdout); + exit(EXIT_SUCCESS); + break; + case 'd': + predefine(ngo.optarg); + break; + case 'f': + parse_fill_byte(ngo.optarg); + break; + case 'q': + s_listing = 0; + break; + case 'x': + s_extended_op = 1; + break; + case 'u': + s_undocumented_op = 1; + break; + case '?': + eprint(_("unrecognized option %s\n"), + ngo.optarg); + exit(EXIT_FAILURE); + case ':': + eprint(_("%s needs an argument\n"), + ngo.optarg); + exit(EXIT_FAILURE); + case ';': + eprint(_("%s does not allow for arguments\n"), + ngo.optarg); + exit(EXIT_FAILURE); + case 0: + if (strcmp(ngo.optstr, "print-table") == 0) { + print_table(stdout, ngo.optarg); + exit(EXIT_SUCCESS); + } + } + } while (c != -1); + + if (argc == ngo.optind) { + eprint(_("wrong number of arguments\n")); + exit(EXIT_FAILURE); + } + + s_asmfname = argv[ngo.optind]; + + if (argc - ngo.optind > 1) + s_objfname = argv[ngo.optind + 1]; + else + s_objfname = mkfname(s_asmfname, "obj"); + + if (argc - ngo.optind > 2) + s_lstfname = argv[ngo.optind + 2]; + else + s_lstfname = mkfname(s_asmfname, "lst"); + + uz80as(); + return 0; +} diff --git a/Tools/unix/uz80as/mc6800.c b/Tools/unix/uz80as/mc6800.c new file mode 100644 index 00000000..4ff80be5 --- /dev/null +++ b/Tools/unix/uz80as/mc6800.c @@ -0,0 +1,338 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Motorola 6800, 6801. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: NEGA,COMA,LSRA,RORA,ASRA, + * ROLA,DECA,INCA,TSTA,CLRA + * c: NEGB,COMB,LSRB,RORB,ASRB, + * ROLB,DECB,INCB,TSTB,CLRB + * d: NEG,COM,LSR,ROR,ASR, + * ROL,DEC,INC,TST,JMP,CLR + * e: SUBA,CMPA,SBCA,ANDA,BITA,LDAA, + * EORA,ADCA,ORAA,ADDA + * f: SUBB,CMPB,SBCB,ANDB,BITB,LDAB, + * EORB,ADCB,ORAB,ADDB + * g: INX,DEX,CLV,SEV,CLC,SEC,CLI,SEI + * h: BRA,BHI,BLS,BCC,BCS,BNE,BEQ, + * BVC,BVS,BPL,BMI,BGE,BLT,BGT,BLE + * i: TSX,INS,PULA,PULB,DES,TXS,PSHA, + * PSHB,RTS,RTI,WAI,SWI + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: ouput op as big endian word (no '.' should follow) + * g: if op<=$ff output lastbyte and output op as byte + * else output (lastbyte | 0x20) and output op as big endian word + * (no '.' should follow) + * h: relative - 2 + * i: relative - 4 + * i: relative - 5 + */ + +static const struct matchtab s_matchtab_mc6800[] = { + { "NOP", "01.", 1, 0 }, + { "TAP", "06.", 1, 0 }, + { "TPA", "07.", 1, 0 }, + { "g", "08c0.", 1, 0 }, + { "SBA", "10.", 1, 0 }, + { "CBA", "11.", 1, 0 }, + { "TAB", "16.", 1, 0 }, + { "TBA", "17.", 1, 0 }, + { "DAA", "19.", 1, 0 }, + { "ABA", "1B.", 1, 0 }, + { "i", "30c0.", 1, 0 }, + { "h a", "20c0.h1.", 1, 0, "r8" }, + { "b", "40c0.", 1, 0 }, + { "c", "50c0.", 1, 0 }, + { "d a,X", "60c0.d1.", 1, 0, "e8" }, + { "d a,Y", "18.60c0.d1.", 8, 0, "e8" }, + { "d a", "70c0.f1", 1, 0, "e16" }, + { "e #a", "80c0.d1.", 1, 0, "e8" }, + { "f #a", "C0c0.d1.", 1, 0, "e8" }, + { "e >a", "B0c0.f1", 1, 0, "e16" }, + { "f >a", "F0c0.f1", 1, 0, "e16" }, + { "e a,X", "A0c0.d1.", 1, 0, "e8" }, + { "f a,X", "E0c0.d1.", 1, 0, "e8" }, + { "e a,Y", "18.A0c0.d1.", 8, 0, "e8" }, + { "f a,Y", "18.E0c0.d1.", 8, 0, "e8" }, + { "e a", "90c0g1", 1, 0 }, + { "f a", "D0c0g1", 1, 0 }, + { "STAA >a", "B7.f0", 1, 0, "e16" }, + { "STAA a,X", "A7.d0.", 1, 0, "e8" }, + { "STAA a,Y", "18.A7.d0.", 8, 0, "e8" }, + { "STAA a", "97g0", 1, 0 }, + { "STAB >a", "F7.f0", 1, 0, "e16" }, + { "STAB a,X", "E7.d0.", 1, 0, "e8" }, + { "STAB a,Y", "18.E7.d0.", 8, 0, "e8" }, + { "STAB a", "D7g0", 1, 0 }, + { "CPX #a", "8C.f0", 1, 0, "e16" }, + { "CPX >a", "BC.f0", 1, 0, "e16" }, + { "CPX a,X", "AC.d0.", 1, 0, "e8" }, + { "CPX a,Y", "CD.AC.d0.", 8, 0, "e8" }, + { "CPX a", "9Cg0", 1, 0 }, + { "LDS #a", "8E.f0", 1, 0, "e16" }, + { "LDS >a", "BE.f0", 1, 0, "e16" }, + { "LDS a,X", "AE.d0.", 1, 0, "e8" }, + { "LDS a,Y", "18.AE.d0.", 8, 0, "e8" }, + { "LDS a", "9Eg0", 1, 0 }, + { "STS >a", "BF.f0", 1, 0, "e16" }, + { "STS a,X", "AF.d0.", 1, 0, "e8" }, + { "STS a,Y", "18.AF.d0.", 8, 0, "e8" }, + { "STS a", "9Fg0", 1, 0 }, + { "LDX #a", "CE.f0", 1, 0, "e16" }, + { "LDX >a", "FE.f0", 1, 0, "e16" }, + { "LDX a,X", "EE.d0.", 1, 0, "e8" }, + { "LDX a,Y", "CD.EE.d0.", 8, 0, "e8" }, + { "LDX a", "DEg0", 1, 0 }, + { "STX >a", "FF.f0", 1, 0, "e16" }, + { "STX a,X", "EF.d0.", 1, 0, "e8" }, + { "STX a,Y", "CD.EF.d0.", 8, 0, "e8" }, + { "STX a", "DFg0", 1, 0 }, + { "BSR a", "8D.h0.", 1, 0, "r8" }, + { "JSR >a", "BD.f0", 4, 0, "e16" }, + { "JSR a,X", "AD.d0.", 1, 0, "e8" }, + { "JSR a,Y", "18.AD.d0.", 8, 0, "e8" }, + { "JSR a", "BD.f0", 2, 0, "e16" }, + { "JSR a", "9Dg0", 4, 0 }, + { "ABX", "3A.", 4, 0 }, + { "ADDD #a", "C3.f0", 4, 0, "e16" }, + { "ADDD >a", "F3.f0", 4, 0, "e16" }, + { "ADDD a,X", "E3.d0.", 4, 0, "e8" }, + { "ADDD a,Y", "18.E3.d0.", 8, 0, "e8" }, + { "ADDD a", "D3g0", 4, 0 }, + { "ASLD", "05.", 4, 0 }, + { "LSLD", "05.", 4, 0 }, + { "BHS a", "24.h0.", 4, 0, "r8" }, + { "BLO a", "25.h0.", 4, 0, "r8" }, + { "BRN a", "21.h0.", 4, 0, "r8" }, + { "LDD #a", "CC.f0", 4, 0, "e16" }, + { "LDD >a", "FC.f0", 4, 0, "e16" }, + { "LDD a,X", "EC.d0.", 4, 0, "e8" }, + { "LDD a,Y", "18.EC.d0.", 8, 0, "e8" }, + { "LDD a", "DCg0", 4, 0 }, + { "LSL a,X", "68.d0.", 4, 0, "e8" }, + { "LSL a,Y", "18.68.d0.", 8, 0, "e8" }, + { "LSL a", "78.f0", 4, 0, "e16" }, + { "LSRD", "04.", 4, 0 }, + { "MUL", "3D.", 4, 0 }, + { "PSHX", "3C.", 4, 0 }, + { "PSHY", "18.3C.", 8, 0 }, + { "PULX", "38.", 4, 0 }, + { "PULY", "18.38.", 8, 0 }, + { "STD >a", "FD.f0", 4, 0, "e16" }, + { "STD a,X", "ED.d0.", 4, 0, "e8" }, + { "STD a,Y", "18.ED.d0.", 8, 0, "e8" }, + { "STD a", "DDg0", 4, 0 }, + { "SUBD #a", "83.f0", 4, 0, "e16" }, + { "SUBD >a", "B3.f0", 4, 0, "e16" }, + { "SUBD a,X", "A3.d0.", 4, 0, "e8" }, + { "SUBD a,Y", "18.A3.d0.", 8, 0, "e8" }, + { "SUBD a", "93g0", 4, 0 }, + { "TEST", "00.", 8, 0 }, + { "IDIV", "02.", 8, 0 }, + { "FDIV", "03.", 8, 0 }, + { "BRSET a,X,a,a", "1E.d0.d1.i2.", 8, 0, "e8e8r8" }, + { "BRSET a,Y,a,a", "18.1E.d0.d1.j2.", 8, 0, "e8e8r8" }, + { "BRSET a,a,a", "12.d0.d1.i2.", 8, 0, "e8e8r8" }, + { "BRCLR a,X,a,a", "1F.d0.d1.i2.", 8, 0, "e8e8r8" }, + { "BRCLR a,Y,a,a", "18.1F.d0.d1.j2.", 8, 0, "e8e8r8" }, + { "BRCLR a,a,a", "13.d0.d1.i2.", 8, 0, "e8e8r8" }, + { "BSET a,X,a", "1C.d0.d1.", 8, 0, "e8e8" }, + { "BSET a,Y,a", "18.1C.d0.d1.", 8, 0, "e8e8" }, + { "BSET a,a", "14.d0.d1.", 8, 0, "e8e8" }, + { "BCLR a,X,a", "1D.d0.d1.", 8, 0, "e8e8" }, + { "BCLR a,Y,a", "18.1D.d0.d1.", 8, 0, "e8e8" }, + { "BCLR a,a", "15.d0.d1.", 8, 0, "e8e8" }, + { "LSLA", "48.", 8, 0 }, + { "LSLB", "58.", 8, 0 }, + { "XGDX", "8F.", 8, 0 }, + { "STOP", "CF.", 8, 0 }, + { "ABY", "18.3A.", 8, 0 }, + { "CPY #a", "18.8C.f0", 8, 0, "e16" }, + { "CPY >a", "18.BC.f0", 8, 0, "e16" }, + { "CPY a,X", "1A.AC.d0.", 8, 0, "e8" }, + { "CPY a,Y", "18.AC.d0.", 8, 0, "e8" }, + { "CPY a", "18.9Cg0", 8, 0 }, + { "DEY", "18.09.", 8, 0 }, + { "INY", "18.08.", 8, 0 }, + { "LDY #a", "18.CE.f0", 8, 0, "e16" }, + { "LDY >a", "18.FE.f0", 8, 0, "e16" }, + { "LDY a,X", "1A.EE.d0.", 8, 0, "e8" }, + { "LDY a,Y", "18.EE.d0.", 8, 0, "e8" }, + { "LDY a", "18.DEg0", 8, 0 }, + { "STY >a", "18.FF.f0", 8, 0, "e16" }, + { "STY a,X", "1A.EF.d0.", 8, 0, "e8" }, + { "STY a,Y", "18.EF.d0.", 8, 0, "e8" }, + { "STY a", "18.DFg0", 8, 0 }, + { "TSY", "18.30.", 8, 0 }, + { "TYS", "18.35.", 8, 0 }, + { "XGDY", "18.8F.", 8, 0 }, + { "CPD #a", "1A.83.f0", 8, 0, "e16" }, + { "CPD >a", "1A.B3.f0", 8, 0, "e16" }, + { "CPD a,X", "1A.A3.d0.", 8, 0, "e8" }, + { "CPD a,Y", "CD.A3.d0.", 8, 0, "e8" }, + { "CPD a", "1A.93g0", 8, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"NEGA", "", "", "COMA", "LSRA", "", "RORA", "ASRA", +"ASLA", "ROLA", "DECA", "", "INCA", "TSTA", "", "CLRA", +NULL }; + +static const char *const cval[] = { +"NEGB", "", "", "COMB", "LSRB", "", "RORB", "ASRB", +"ASLB", "ROLB", "DECB", "", "INCB", "TSTB", "", "CLRB", +NULL }; + +static const char *const dval[] = { +"NEG", "", "", "COM", "LSR", "", "ROR", "ASR", +"ASL", "ROL", "DEC", "", "INC", "TST", "JMP", "CLR", +NULL }; + +static const char *const eval[] = { +"SUBA", "CMPA", "SBCA", "", "ANDA", "BITA", "LDAA", "", +"EORA", "ADCA", "ORAA", "ADDA", +NULL }; + +static const char *const fval[] = { +"SUBB", "CMPB", "SBCB", "", "ANDB", "BITB", "LDAB", "", +"EORB", "ADCB", "ORAB", "ADDB", +NULL }; + +static const char *const gval[] = { +"INX", "DEX", "CLV", "SEV", "CLC", "SEC", "CLI", "SEI", +NULL }; + +static const char *const hval[] = { +"BRA", "", "BHI", "BLS", "BCC", "BCS", "BNE", "BEQ", +"BVC", "BVS", "BPL", "BMI", "BGE", "BLT", "BGT", "BLE", +NULL }; + +static const char *const ival[] = { +"TSX", "INS", "PULA", "PULB", "DES", "TXS", "PSHA", +"PSHB", "", "RTS", "", "RTI", "", "", "WAI", "SWI", +NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival +}; + +static int match_mc6800(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'i') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_mc6800(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': genb(vs[i] >> 8, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'g': if (vs[i] <= 255) { + genb(b, s_pline_ep); + genb(vs[i], s_pline_ep); + } else { + genb(b | 0x20, s_pline_ep); + genb(vs[i] >> 8, s_pline_ep); + genb(vs[i], s_pline_ep); + } + break; + case 'h': b = (vs[i] - savepc - 2); + break; + case 'i': b = (vs[i] - savepc - 4); + break; + case 'j': b = (vs[i] - savepc - 5); + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_mc6800(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_mc6800(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'i') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_mc6800 = { + .id = "mc6800", + .descr = "Motorola 6800", + .matcht = s_matchtab_mc6800, + .matchf = match_mc6800, + .genf = gen_mc6800, + .pat_char_rewind = pat_char_rewind_mc6800, + .pat_next_str = pat_next_str_mc6800, + .mask = 3 +}; + +const struct target s_target_mc6801 = { + .id = "mc6801", + .descr = "Motorola 6801", + .matcht = s_matchtab_mc6800, + .matchf = match_mc6800, + .genf = gen_mc6800, + .pat_char_rewind = pat_char_rewind_mc6800, + .pat_next_str = pat_next_str_mc6800, + .mask = 5 +}; + +const struct target s_target_m68hc11 = { + .id = "m68hc11", + .descr = "Motorola 68HC11", + .matcht = s_matchtab_mc6800, + .matchf = match_mc6800, + .genf = gen_mc6800, + .pat_char_rewind = pat_char_rewind_mc6800, + .pat_next_str = pat_next_str_mc6800, + .mask = 13 +}; diff --git a/Tools/unix/uz80as/mos6502.c b/Tools/unix/uz80as/mos6502.c new file mode 100644 index 00000000..f1041b24 --- /dev/null +++ b/Tools/unix/uz80as/mos6502.c @@ -0,0 +1,385 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * MOS Technology 6502. + * Rockwell R6501. + * California Micro Devices G65SC02. + * Rockwell R65C02. + * Rockwell R65C29. + * Western Design Center W65C02S. + * =========================================================================== + */ + +/* mos6502, the original + * + * g65sc02 California Micro Devices, adds to mos6502: + * - zp ADC,AND,CMP,EOR,LDA,ORA,SBC,STA + * - DEC A, INC A + * - JMP (abs,X) + * - BRA + * - PHX,PHY,PLX,PLY + * - STZ + * - TRB + * - TSB + * - More addressing modes for BIT, etc + * + * r6501 Rockwell, adds to mos6502: + * - BBR, BBS + * - RMB, SMB + * + * r65c02 Rockwell, adds the instructions of the g65sc02 and r6501 + * + * r65c29 Rockwell, adds to r65c02: + * - MUL + * + * w65c02s Western Design Center, adds to r65c02: + * - STP,WAI + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: ORA,AND,EOR,ADC,STA,LDA,CMP,SBC + * c: ORA,AND,EOR,ADC,LDA,CMP,SBC + * d: PHP,CLC,PLP,SEC,PHA,CLI,PLA,SEI, + * DEY,TYA,TAY,CLV,INY,CLD,INX,SED + * e: ASL,ROL,LSR,ROR + * f: DEC, INC + * g: BPL,BMI,BVC,BVS,BCC,BCS,BNE,BEQ + * h: TXA,TXS,TAX,TSX,DEX,NOP + * i: CPY,CPX + * j: TSB,TRB + * k: BBR0,BBR1,BBR2,BBR3,BBR4,BBR5,BBR6,BBR6, + * BBS0,BBS1,BBS2,BBS3,BBS4,BBS5,BBS6,BBS7 + * l: RMB0,RMB1,RMB2,RMB3,RMB4,RMB5,EMB6,RMB7, + * SMB0,SMB1,SMB2,SMB3,SMB4,SMB5,SMB6,SMB7 + * m: PHY,PLY + * n: PHX,PLX + * o: INC, DEC + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 5) | lastbyte + * g: if op <= $FF output last byte and then op as 8 bit value; + * else output (lastbyte | 0x08) and output op as word + * (no '.' should follow) + * h: (op << 4) | lastbyte + * i: relative jump to op (-2) + * j: if op <= $FF output $64 and op as 8 bit + * else output $9C and op as word + * (no '.' should follow) + * k: if op <= $FF ouput $74 and op as 8 bit + * else output $9E and op as word + * (no '.' should follow) + * l: relative jump to op (-3) + */ + +static const struct matchtab s_matchtab_mos6502[] = { + { "BRK", "00.", 1, 0 }, + { "JSR a", "20.e0", 1, 0 }, + { "RTI", "40.", 1, 0 }, + { "RTS", "60.", 1, 0 }, + { "h", "8Ah0.", 1, 0 }, + { "d", "08h0.", 1, 0 }, + { "c #a", "09f0.d1.", 1, 0, "e8" }, + { "b (a,X)", "01f0.d1.", 1, 0, "e8" }, + { "b (a),Y", "11f0.d1.", 1, 0, "e8" }, + { "b (a)", "12f0.d1.", 2, 0, "e8" }, + { "b a", "05f0g1", 1, 0 }, + { "b a,X", "15f0g1", 1, 0 }, + { "b a,Y", "19f0.e1", 1, 0 }, + { "e A", "0Af0.", 1, 0 }, + { "e a", "06f0g1", 1, 0 }, + { "e a,X", "16f0g1", 1, 0 }, + { "STX a", "86g0", 1, 0 }, + { "STX a,Y", "96.d0.", 1, 0, "e8" }, + { "LDX #a", "A2.d0.", 1, 0, "e8" }, + { "LDX a", "A6g0", 1, 0 }, + { "LDX a,Y", "B6g0", 1, 0 }, + { "o A", "1Af0.", 2, 0 }, + { "f a", "C6f0g1", 1, 0 }, + { "f a,X", "D6f0g1", 1, 0 }, + { "g a", "10f0.i1.", 1, 0, "r8" }, + { "BIT #a", "89.d0.", 2, 0, "e8" }, + { "BIT a", "24g0", 1, 0 }, + { "BIT a,X", "34g0", 2, 0 }, + { "JMP (a)", "6C.e0", 1, 0 }, + { "JMP (a,X)", "7C.e0", 2, 0 }, + { "JMP a", "4C.e0", 1, 0 }, + { "STY a", "84g0", 1, 0 }, + { "STY a,X", "94.d0.", 1, 0, "e8" }, + { "LDY #a", "A0.d0.", 1, 0, "e8" }, + { "LDY a", "A4g0", 1, 0 }, + { "LDY a,X", "B4g0", 1, 0 }, + { "i #a", "C0f0.d1.", 1, 0, "e8" }, + { "i a", "C4f0g1", 1, 0 }, + { "j a", "04h0g1", 2, 0 }, + { "k a,a", "0Fh0.d1.l2.", 4, 0, "e8r8" }, + { "l a", "07h0.d1.", 4, 0, "e8" }, + { "m", "5Af0.", 2, 0 }, + { "n", "DAf0.", 2, 0 }, + { "BRA a", "80.i0.", 2, 0, "r8" }, + { "STZ a,X", "k1", 2, 0 }, + { "STZ a", "j1", 2, 0 }, + { "MUL", "02.", 8, 0 }, + { "WAI", "CB.", 16, 0 }, + { "STP", "DB.", 16, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { + "ORA", "AND", "EOR", "ADC", + "STA", "LDA", "CMP", "SBC", + NULL +}; + +static const char *const cval[] = { + "ORA", "AND", "EOR", "ADC", + "", "LDA", "CMP", "SBC", NULL +}; + +static const char *const dval[] = { + "PHP", "CLC", "PLP", "SEC", + "PHA", "CLI", "PLA", "SEI", + "DEY", "TYA", "TAY", "CLV", + "INY", "CLD", "INX", "SED", + NULL +}; + + +static const char *const eval[] = { + "ASL", "ROL", "LSR", "ROR", + NULL +}; + +static const char *const fval[] = { + "DEC", "INC", + NULL +}; + +static const char *const gval[] = { + "BPL", "BMI", "BVC", "BVS", + "BCC", "BCS", "BNE", "BEQ", + NULL +}; + +static const char *const hval[] = { + "TXA", "TXS", "TAX", "TSX", + "DEX", "", "NOP", + NULL +}; + +static const char *const ival[] = { + "CPY", "CPX", + NULL +}; + +static const char *const jval[] = { + "TSB", "TRB", + NULL +}; + +static const char *const kval[] = { + "BBR0", "BBR1", "BBR2", "BBR3", + "BBR4", "BBR5", "BBR6", "BBR7", + "BBS0", "BBS1", "BBS2", "BBS3", + "BBS4", "BBS5", "BBS6", "BBS7", + NULL +}; + +static const char *const lval[] = { + "RMB0", "RMB1", "RMB2", "RMB3", + "RMB4", "RMB5", "RMB6", "RMB7", + "SMB0", "SMB1", "SMB2", "SMB3", + "SMB4", "SMB5", "SMB6", "SMB7", + NULL +}; + +static const char *const mval[] = { + "PHY", "PLY", + NULL +}; + +static const char *const nval[] = { + "PHX", "PLX", + NULL +}; + +static const char *const oval[] = { + "INC", "DEC", + NULL +}; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival, jval, kval, + lval, mval, nval, oval +}; + +static int match_mos6502(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'o') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_mos6502(int *eb, char p, const int *vs, int i, int savepc) +{ + int b, w; + + b = *eb; + switch (p) { + case 'f': b |= (vs[i] << 5); break; + case 'g': w = vs[i] & 0xffff; + if (w <= 0xff) { + genb(b, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + } else { + b |= 0x08; + genb(b, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + genb(w >> 8, s_pline_ep); + } + break; + case 'h': b |= (vs[i] << 4); break; + case 'i': b = (vs[i] - savepc - 2); break; + case 'j': w = vs[i] & 0xffff; + if (w <= 0xff) { + genb(0x64, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + } else { + genb(0x9C, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + genb(w >> 8, s_pline_ep); + } + break; + case 'k': w = vs[i] & 0xffff; + if (w <= 0xff) { + genb(0x74, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + } else { + genb(0x9E, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + genb(w >> 8, s_pline_ep); + } + break; + case 'l': b = (vs[i] - savepc - 3); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_mos6502(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_mos6502(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'o') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_mos6502 = { + .id = "mos6502", + .descr = "MOS Technology 6502", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 1 +}; + +const struct target s_target_r6501 = { + .id = "r6501", + .descr = "Rockwell R6501", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 5 +}; + +const struct target s_target_g65sc02 = { + .id = "g65sc02", + .descr = "California Micro Devices G65SC02", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 3 +}; + +const struct target s_target_r65c02 = { + .id = "r65c02", + .descr = "Rockwell R65C02", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 7 +}; + +const struct target s_target_r65c29 = { + .id = "r65c29", + .descr = "Rockwell R65C29, R65C00/21", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 15 +}; + +const struct target s_target_w65c02s = { + .id = "w65c02s", + .descr = "Western Design Center W65C02S", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 027 +}; diff --git a/Tools/unix/uz80as/ngetopt.c b/Tools/unix/uz80as/ngetopt.c new file mode 100644 index 00000000..c0404012 --- /dev/null +++ b/Tools/unix/uz80as/ngetopt.c @@ -0,0 +1,237 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Handling of command line options, similar to getopt. + * =========================================================================== + */ + +/* + * Changes: + * + * - Jul 22 2018: long options without short option character recognized. + * + */ + +#include "ngetopt.h" + +#ifndef STRING_H +#include +#endif + +static int find_short_opt(int val, struct ngetopt_opt *ops) +{ + int i; + + i = 0; + while (ops[i].name != NULL) { + if (ops[i].val > 0 && ops[i].val == val) + return i; + i++; + } + + return -1; +} + +static int find_long_opt(char *str, struct ngetopt_opt *ops) +{ + int i; + const char *p, *q; + + i = 0; + while (ops[i].name != NULL) { + p = ops[i].name; + q = str; + while (*p != '\0' && *p == *q) { + p++; + q++; + } + if (*p == '\0' && (*q == '\0' || *q == '=')) { + return i; + } + i++; + } + + return -1; +} + +void ngetopt_init(struct ngetopt *p, int argc, char *const *argv, + struct ngetopt_opt *ops) +{ + p->argc = argc; + p->argv = argv; + p->ops = ops; + p->optind = 1; + p->subind = 0; + strcpy(p->str, "-X"); +} + +static int get_short_opt(struct ngetopt *p) +{ + int i; + char *opt; + + opt = p->argv[p->optind]; + i = find_short_opt(opt[p->subind], p->ops); + if (i < 0) { + /* unrecognized option */ + p->str[1] = (char) opt[p->subind]; + p->optarg = p->str; + p->subind++; + return '?'; + } + + if (!p->ops[i].has_arg) { + /* it's ok */ + p->subind++; + return p->ops[i].val; + } + + /* needs an argument */ + if (opt[p->subind + 1] != '\0') { + /* the argument is the suffix */ + p->optarg = &opt[p->subind + 1]; + p->subind = 0; + p->optind++; + return p->ops[i].val; + } + + /* the argument is the next token */ + p->optind++; + p->subind = 0; + if (p->optind < p->argc) { + p->optarg = p->argv[p->optind]; + p->optind++; + return p->ops[i].val; + } + + /* ups, argument missing */ + p->str[1] = (char) p->ops[i].val; + p->optarg = p->str; + return ':'; +} + +static int get_opt(struct ngetopt *p) +{ + int i; + char *opt, *optnext; + + /* all arguments consumed */ + if (p->optind >= p->argc) + return -1; + + opt = p->argv[p->optind]; + if (opt[0] != '-') { + /* non option */ + return -1; + } + + /* - */ + if (opt[1] == '\0') { + /* stdin */ + return -1; + } + + if (opt[1] != '-') { + /* -xxxxx */ + p->subind = 1; + return get_short_opt(p); + } + + /* -- */ + if (opt[2] == '\0') { + /* found "--" */ + p->optind++; + return -1; + } + + /* long option */ + i = find_long_opt(&opt[2], p->ops); + if (i < 0) { + /* not found */ + p->optind++; + p->optarg = opt; + while (*opt != '\0' && *opt != '=') { + opt++; + } + *opt = '\0'; + return '?'; + } + + /* found, go to end of option */ + optnext = opt + 2 + strlen(p->ops[i].name); + + if (*optnext == '\0' && !p->ops[i].has_arg) { + /* doesn't need arguments */ + p->optind++; + p->optstr = opt + 2; + return p->ops[i].val; + } + + if (*optnext == '=' && !p->ops[i].has_arg) { + /* does not need arguments but argument supplied */ + *optnext = '\0'; + p->optarg = opt; + return ';'; + } + + /* the argument is the next token */ + if (*optnext == '\0') { + p->optind++; + if (p->optind < p->argc) { + p->optstr = opt + 2; + p->optarg = p->argv[p->optind]; + p->optind++; + return p->ops[i].val; + } + + /* ups, argument missing */ + p->optarg = opt; + p->optind++; + return ':'; + } + + /* *optnext == '=' */ + *optnext = '\0'; + p->optstr = opt + 2; + p->optarg = optnext + 1; + p->optind++; + return p->ops[i].val; +} + +/* + * If ok: + * + * - For a long option with a zero value single character option, 0 is + * returned, optstr is the string of the long option (without '-' or '--') + * and optarg is the option argument or NULL. + * + * - For anything else the single option character is returned and optarg + * is the option argument or NULL. + * + * If the option is not recognized, '?' is returned, and optarg is the + * literal string of the option not recognized (already with '-' or '--' + * prefixed). + * + * If the option is recognized but the argument is missing, ':' is + * returned and optarg is the option as supplied (with '-' or '--' prefixed). + * + * If the option is recognized and it is a long option followed by '=', but the + * option does not take arguments, ';' is returned and optarg is the option + * (with '-' or '--' prefixed). + * + * -1 is returned if no more options. + */ +int ngetopt_next(struct ngetopt *p) +{ + if (p->subind == 0) + return get_opt(p); + + /* p->subind > 0 */ + if (p->argv[p->optind][p->subind] != '\0') + return get_short_opt(p); + + /* no more options in this list of short options */ + p->subind = 0; + p->optind++; + return get_opt(p); +} diff --git a/Tools/unix/uz80as/ngetopt.h b/Tools/unix/uz80as/ngetopt.h new file mode 100644 index 00000000..31ff8a60 --- /dev/null +++ b/Tools/unix/uz80as/ngetopt.h @@ -0,0 +1,40 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Handling of command line options, similar to getopt. + * =========================================================================== + */ + +#ifndef NGETOPT_H +#define NGETOPT_H + +/* + * Changelog: + * + * - Jul 21 2018: long options without short option character recognized. + * + */ + +struct ngetopt_opt { + const char *name; + int has_arg; + int val; +}; + +struct ngetopt { + char *optstr; + char *optarg; + /* private */ + int optind; + int argc; + char *const *argv; + struct ngetopt_opt *ops; + int subind; + char str[3]; +}; + +void ngetopt_init(struct ngetopt *p, int argc, char *const *argv, + struct ngetopt_opt *ops); +int ngetopt_next(struct ngetopt *p); + +#endif diff --git a/Tools/unix/uz80as/options.c b/Tools/unix/uz80as/options.c new file mode 100644 index 00000000..c3c24cd2 --- /dev/null +++ b/Tools/unix/uz80as/options.c @@ -0,0 +1,33 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Global options, normally coming from the command line. + * =========================================================================== + */ + +#include "config.h" +#include "options.h" +#include "err.h" + +const char *s_asmfname; /* Name of source file. */ +const char *s_objfname; /* Name of generated binary file. */ +const char *s_lstfname; /* Name of listing file. */ +const char *s_target_id = "z80"; /* ID of target */ +int s_listing = 1; /* If we generate the listing file or not. */ +int s_extended_op = 0; /* Allow extended instruction syntax. */ +int s_undocumented_op = 0; /* Allow undocumented instructions. */ +int s_mem_fillval = 0; /* Default value to fill the 64K memory. */ + +/* Command line macro definitions. */ +struct predef *s_predefs; + +/* Predefine a macro in the command line that must persist between passes. */ +void predefine(const char *text) +{ + struct predef *pdef; + + pdef = emalloc(sizeof(*pdef)); + pdef->name = text; + pdef->next = s_predefs; + s_predefs = pdef; +} diff --git a/Tools/unix/uz80as/options.h b/Tools/unix/uz80as/options.h new file mode 100644 index 00000000..c23ed913 --- /dev/null +++ b/Tools/unix/uz80as/options.h @@ -0,0 +1,29 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Global options, normally coming from the command line. + * =========================================================================== + */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +/* Predefined macro at the command line. */ +struct predef { + struct predef *next; + const char *name; +}; + +extern const char *s_asmfname; +extern const char *s_objfname; +extern const char *s_lstfname; +extern const char *s_target_id; +extern int s_listing; +extern int s_extended_op; +extern int s_undocumented_op; +extern int s_mem_fillval; +extern struct predef *s_predefs; + +void predefine(const char *name); + +#endif diff --git a/Tools/unix/uz80as/pp.c b/Tools/unix/uz80as/pp.c new file mode 100644 index 00000000..b3b69f2a --- /dev/null +++ b/Tools/unix/uz80as/pp.c @@ -0,0 +1,745 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Preprocessor. + * =========================================================================== + */ + +#include "config.h" +#include "pp.h" +#include "utils.h" +#include "err.h" +#include "incl.h" +#include "expr.h" +#include "exprint.h" + +#ifndef CTYPE_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +/* Max number of macros. */ +#define NMACROS 1000 + +/* Closest prime to NMACROS / 4. */ +#define MACTABSZ 241 + +/* Max number of macro arguments. */ +#define NPARAMS 20 + +#define DEFINESTR "DEFINE" +#define DEFCONTSTR "DEFCONT" +#define INCLUDESTR "INCLUDE" +#define IFSTR "IF" +#define IFDEFSTR "IFDEF" +#define IFNDEFSTR "IFNDEF" +#define ENDIFSTR "ENDIF" +#define ELSESTR "ELSE" + +/* + * Macro. + * + * For example, the macro: + * + * #define SUM(a,b) (a+b) + * + * is: + * + * name = SUM + * pars = a\0b\0 + * ppars[0] points to &pars[0], that is to "a" + * ppars[1] points to &pars[2], that is to "b" + * npars = 2 + * text is "(a+b)" + */ +struct macro { + struct macro *next; /* Next in hash chain. */ + char *name; /* Identifier. */ + char *pars; /* String with params separated by '\0'. */ + char *text; /* Text to expand. */ + char *ppars[NPARAMS]; /* Pointers to the beginning of each param. */ + int npars; /* Valid number of params in ppars. */ +}; + +/* Hash table of preprocessor symbols. */ +static struct macro *s_mactab[MACTABSZ]; + +/* Preprocessing line buffers. */ +static char s_ppbuf[2][LINESZ]; + +/* If we are discarding lines; if not 0, level of if. */ +int s_skipon; + +/* Number of nested #if or #ifdef or #ifndef. */ +static int s_nifs; + +/* Last defined macro. */ +static struct macro *s_lastmac; + +/* Number of macros in table. */ +static int s_nmacs; + +/* The preprocessed line, points to one of s_ppbuf. */ +char *s_pline; + +/* Current program counter. */ +int s_pc; + +/* Current pass. */ +int s_pass; + + +/* Only valid while in the call to pp_line(). */ +static const char *s_line; /* original line */ +static const char *s_line_ep; /* pointer inside s_line for error reporting */ + +/* + * Copy [p, q[ to [dp, dq[. + */ +static char *copypp(char *dp, char *dq, const char *p, const char *q) +{ + while (dp < dq && p < q) + *dp++ = *p++; + return dp; +} + +/* + * Find the 'argnum' argument in 'args' and return a pointer to it. + * + * 'args' is a list of arguments "([id [,id]*). + * 'argnum' is the argument number to find. + * + * Return not found. + */ +static const char *findarg(const char *args, int argnum) +{ + if (*args == '(') { + do { + args++; + if (argnum == 0) + return args; + argnum--; + while (*args != '\0' && *args != ',' + && *args != ')') + { + args++; + } + } while (*args == ','); + } + return NULL; +} + +/* + * Find the 'argnum' argument in 'args' and copy it to [dp, dq[. + * + * 'args' points to a list of arguments "([id [,id]*). + * 'argnum' is the argument number to copy. + * + * Return the new 'dp' after copying. + */ +static char *copyarg(char *dp, char *dq, const char *args, int argnum) +{ + const char *p; + + p = findarg(args, argnum); + if (p == NULL) + return dp; + + while (dp < dq && *p != '\0' && *p != ',' && *p != ')') + *dp++ = *p++; + return dp; +} + +/* + * Sees if [idp, idq[ is a parameter of the macro 'pps'. + * If it is, return the number of parameter. + * Else return -1. + */ +static int findparam(const char *idp, const char *idq, struct macro *pps) +{ + int i; + const char *p, *r; + + for (i = 0; i < pps->npars; i++) { + p = pps->ppars[i]; + r = idp; + while (*p != '\0' && r < idq && *p == *r) { + p++; + r++; + } + if (*p == '\0' && r == idq) + return i; + } + return -1; +} + +/* + * Lookup the string in [p, q[ in 's_mactab'. + * Return the symbol or NULL if it is not in the table. + */ +static struct macro *pplookup(const char *p, const char *q) +{ + int h; + struct macro *nod; + + h = hash(p, q, MACTABSZ); + for (nod = s_mactab[h]; nod != NULL; nod = nod->next) + if (scmp(p, q, nod->name) == 0) + return nod; + + return nod; +} + +/* + * Expand macro in [dp, dq[. + * + * 'pps' is the macro to expand. + * 'args' points to the start of the arguments to substitute, if any. + * + * Return new dp. + */ +static char *expandid(char *dp, char *dq, struct macro *pps, const char *args) +{ + const char *p, *q; + int validid, argnum; + + validid = 1; + p = pps->text; + while (*p != '\0' && dp < dq) { + if (isidc0(*p)) { + for (q = p; isidc(*q); q++) + ; + if (validid) { + argnum = findparam(p, q, pps); + if (argnum >= 0) + dp = copyarg(dp, dq, args, argnum); + else + dp = copypp(dp, dq, p, q); + } else { + dp = copypp(dp, dq, p, q); + } + p = q; + validid = 1; + } else { + validid = !isidc(*p); + *dp++ = *p++; + } + } + return dp; +} + +/* + * If 'p' points the the start of an argument list, that is, '(', + * point to one character past the first ')' after 'p'. + * Else return 'p'. + */ +static const char *skipargs(const char *p) +{ + if (*p == '(') { + while (*p != '\0' && *p != ')') + p++; + if (*p == ')') + p++; + } + return p; +} + +/* + * Expand macros found in 'p' (null terminated) into [dp, dq[. + * dq must be writable to put a final '\0'. + */ +static int expand_line(char *dp, char *dq, const char *p) +{ + char *op; + int expanded, validid; + const char *s; + struct macro *nod; + + validid = 1; + expanded = 0; + while (dp < dq && *p != '\0' && *p != ';') { + if (*p == '\'' && *(p + 1) != '\0' && *(p + 2) == '\'') { + /* characters */ + dp = copypp(dp, dq, p, p + 3); + p += 3; + validid = 1; + } else if (*p == '\"') { + /* strings */ + s = p; + p++; + /* skip over the string literal */ + while (*p != '\0' && *p != '\"') { + if (p[0] == '\\' && p[1] == '\"') + p++; + p++; + } + if (*p == '\"') + p++; + dp = copypp(dp, dq, s, p); + validid = 1; + } else if (isidc0(*p)) { + s = p; + while (isidc(*p)) + p++; + if (validid) { + nod = pplookup(s, p); + if (nod != NULL) { + op = dp; + dp = expandid(dp, dq, nod, p); + expanded = dp != op; + p = skipargs(p); + } else { + dp = copypp(dp, dq, s, p); + } + } else { + dp = copypp(dp, dq, s, p); + } + validid = 1; + } else { + validid = *p != '.' && !isalnum(*p); + *dp++ = *p++; + } + } + *dp = '\0'; + return expanded; +} + +/* + * Expand macros found in 'p' (null terminated). + * Return a pointer to an internal preprocessed line (null terminated). + */ +static char *expand_line0(const char *p) +{ + int iter, expanded; + char *np, *nq, *op; + + iter = 0; + np = &s_ppbuf[iter & 1][0]; + nq = &s_ppbuf[iter & 1][LINESZ - 1]; + expanded = expand_line(np, nq, p); + /* TODO: recursive macro expansion limit */ + while (expanded && iter < 5) { + op = np; + iter++; + np = &s_ppbuf[iter & 1][0]; + nq = &s_ppbuf[iter & 1][LINESZ - 1]; + expanded = expand_line(np, nq, op); + } + return np; +} + +/* + * Check if 'p' starts with the preprocessor directive 'ucq', that must be in + * upper case. + * 'p' can have any case. + * After the preprocessor directive must be a space or '\0'. + * Return 1 if all the above is true. 0 otherwise. + */ +static int isppid(const char *p, const char *ucq) +{ + while (*p != '\0' && *ucq != '\0' && toupper(*p) == *ucq) { + p++; + ucq++; + } + return (*ucq == '\0') && (*p == '\0' || isspace(*p)); +} + +/* + * Define a macro. + * + * [idp, idq[ is the macro id. + * [ap, aq[ is the macro argument list. If ap == aq there are no arguments. + * [tp, tq[ is the macro text. + */ +static void define(const char *idp, const char *idq, + const char *tp, const char *tq, + const char *ap, const char *aq) +{ + int h; + char *p; + struct macro *nod; + + h = hash(idp, idq, MACTABSZ); + for (nod = s_mactab[h]; nod != NULL; nod = nod->next) { + if (scmp(idp, idq, nod->name) == 0) { + /* Already defined. */ + return; + } + } + + s_nmacs++; + if (s_nmacs >= NMACROS) { + eprint(_("maximum number of macros exceeded (%d)\n"), NMACROS); + exit(EXIT_FAILURE); + } + + nod = emalloc((sizeof *nod) + (idq - idp) + (aq - ap) + 2); + nod->text = emalloc(tq - tp + 1); + nod->name = (char *) ((unsigned char *) nod + (sizeof *nod)); + nod->pars = nod->name + (idq - idp + 1); + + copychars(nod->name, idp, idq); + copychars(nod->text, tp, tq); + copychars(nod->pars, ap, aq); + + // printf("DEF %s(%s) %s\n", nod->name, nod->pars, nod->text); + + /* We don't check whether the arguments are different. */ + + /* + * Make ppars point to each argument and null terminate each one. + * Count the number of arguments. + */ + nod->npars = 0; + p = nod->pars; + while (*p != '\0') { + nod->ppars[nod->npars++] = p; + while (*p != '\0' && *p != ',') + p++; + if (*p == ',') + *p++ = '\0'; + } + + nod->next = s_mactab[h]; + s_mactab[h] = nod; + s_lastmac = nod; +} + +/* Add the text [p, q[ to the last macro text. */ +static void defcont(const char *p, const char *q) +{ + char *nt; + size_t len; + + len = strlen(s_lastmac->text); + nt = erealloc(s_lastmac->text, (q - p) + len + 1); + copychars(nt + len, p, q); + s_lastmac->text = nt; +} + +/* + * If 'p' points to a valid identifier start, go to the end of the identifier. + * Else return 'p'. + */ +static const char *getid(const char *p) +{ + if (isidc0(*p)) { + while (isidc(*p)) + p++; + } + return p; +} + +/* Issues error in a macro definition. */ +static void macdeferr(int cmdline, const char *estr, const char *ep) +{ + if (cmdline) { + eprint(_("error in command line macro definition\n")); + } + eprint(estr); + eprcol(s_line, ep); + if (cmdline) { + exit(EXIT_FAILURE); + } else { + newerr(); + } +} + +/* Parse macro definition. */ +static void pmacdef(const char *p, int cmdline) +{ + const char *q, *ap, *aq, *idp, *idq; + + idp = p; + idq = getid(idp); + if (idq == idp) { + macdeferr(cmdline, _("identifier excepted\n"), p); + return; + } + p = idq; + ap = aq = p; + if (*p == '(') { + p++; + ap = p; + while (isidc0(*p)) { + p = getid(p); + if (*p != ',') + break; + p++; + } + if (*p != ')') { + macdeferr(cmdline, _("')' expected\n"), p); + return; + } + aq = p; + p++; + } + if (*p != '\0' && !isspace(*p)) { + macdeferr(cmdline, _("space expected\n"), p); + return; + } + p = skipws(p); + /* go to the end */ + for (q = p; *q != '\0'; q++) + ; + /* go to the first non white from the end */ + while (q > p && isspace(*(q - 1))) + q--; + define(idp, idq, p, q, ap, aq); +} + +/* Parse #define. */ +static void pdefine(const char *p) +{ + p = skipws(p + sizeof(DEFINESTR) - 1); + pmacdef(p, 0); +} + +/* Parse #defcont. */ +static void pdefcont(const char *p) +{ + const char *q; + + p = skipws(p + sizeof(DEFCONTSTR) - 1); + + /* go to the end */ + for (q = p; *q != '\0'; q++) + ; + + /* go to the first non white from the end */ + while (q > p && isspace(*(q - 1))) + q--; + + if (p == q) { + /* nothing to add */ + return; + } + + if (s_lastmac == NULL) { + eprint(_("#DEFCONT without a previous #DEFINE\n")); + eprcol(s_line, s_line_ep); + newerr(); + return; + } + + defcont(p, q); +} + +/* Parse #include. */ +static void pinclude(const char *p) +{ + const char *q; + + p = skipws(p + sizeof(INCLUDESTR) - 1); + if (*p != '\"') { + eprint(_("#INCLUDE expects a filename between quotes\n")); + eprcol(s_line, p); + newerr(); + return; + } + q = ++p; + while (*q != '\0' && *q != '\"') + q++; + if (*q != '\"') { + wprint(_("no terminating quote\n")); + eprcol(s_line, q); + } + pushfile(p, q); +} + +/* + * Parse #ifdef or #ifndef. + * 'idsz' is the length of the string 'ifdef' or 'ifndef', plus '\0'. + * 'ifdef' must be 1 if we are #ifdef, 0 if #ifndef. + */ +static void pifdef(const char *p, size_t idsz, int ifdef) +{ + const char *q; + struct macro *nod; + + s_nifs++; + if (s_skipon) + return; + + p = skipws(p + idsz - 1); + if (!isidc0(*p)) { + s_skipon = s_nifs; + eprint(_("identifier expected\n")); + eprcol(s_line, p); + newerr(); + return; + } + q = p; + while (isidc(*q)) + q++; + nod = pplookup(p, q); + if (ifdef == (nod != NULL)) + s_skipon = 0; + else + s_skipon = s_nifs; +} + +/* Parse #else. */ +static void pelse(const char *p) +{ + if (s_nifs == 0) { + eprint(_("unbalanced #ELSE\n")); + eprcol(s_line, s_line_ep); + newerr(); + return; + } + + if (s_skipon && s_nifs == s_skipon) + s_skipon = 0; + else if (!s_skipon) + s_skipon = s_nifs; +} + +/* Parse #endif. */ +static void pendif(const char *p) +{ + if (s_nifs == 0) { + eprint(_("unbalanced #ENDIF\n")); + eprcol(s_line, s_line_ep); + newerr(); + return; + } + + if (s_skipon && s_nifs == s_skipon) + s_skipon = 0; + s_nifs--; +} + +/* + * Parse #if. + */ +static void pif(const char *p) +{ + int v; + enum expr_ecode ex_ec; + const char *ep; + + s_nifs++; + if (s_skipon) + return; + + p = skipws(p + sizeof(IFSTR) - 1); + if (!expr(p, &v, s_pc, 1, &ex_ec, &ep)) { + s_skipon = 1; + exprint(ex_ec, s_line, ep); + newerr(); + return; + } + + if (v == 0) + s_skipon = s_nifs; + else + s_skipon = 0; +} + +/* + * Parse a preprocessor line. + * 'p' points to the next character after the '#'. + */ +static int +parse_line(const char *p) +{ + if (isppid(p, IFDEFSTR)) { + pifdef(p, sizeof IFDEFSTR, 1); + } else if (isppid(p, IFNDEFSTR)) { + pifdef(p, sizeof IFNDEFSTR, 0); + } else if (isppid(p, IFSTR)) { + pif(p); + } else if (isppid(p, ELSESTR)) { + pelse(p); + } else if (isppid(p, ENDIFSTR)) { + pendif(p); + } else if (s_skipon) { + ; + } else if (isppid(p, INCLUDESTR)) { + pinclude(p); + } else if (isppid(p, DEFINESTR)) { + pdefine(p); + } else if (isppid(p, DEFCONTSTR)) { + pdefcont(p); + } else { + return 0; +/* + eprint(_("unknown preprocessor directive\n")); + eprcol(s_line, s_line_ep); + newerr(); +*/ + } + return 1; +} + +/* + * Preprocess 'line' in 's_pline'. + * In this module, while we are preprocessing: + * s_line is the original line. + * s_line_ep is a pointer inside line that we keep for error reporting. + */ +void pp_line(const char *line) +{ + const char *p; + + s_line = line; + s_line_ep = line; + + p = skipws(line); + if ((*p == '#') || (*p == '.')) { + s_line_ep = p; + if (parse_line(p + 1)) { + s_ppbuf[0][0] = '\0'; + s_pline = &s_ppbuf[0][0]; + return; + } + } + if (s_skipon) { + s_ppbuf[0][0] = '\0'; + s_pline = &s_ppbuf[0][0]; + return; + } + s_pline = expand_line0(line); +} + +/* Reset the module for other passes. */ +void pp_reset(void) +{ + int i; + struct macro *nod, *cur; + + s_nmacs = 0; + s_nifs = 0; + s_skipon = 0; + s_lastmac = NULL; + for (i = 0; i < MACTABSZ; i++) { + nod = s_mactab[i]; + while (nod != NULL) { + cur = nod; + nod = nod->next; + free(cur->text); + free(cur); + } + } + memset(s_mactab, 0, MACTABSZ * sizeof(s_mactab[0])); +} + +void pp_define(const char *mactext) +{ + s_line = mactext; + s_line_ep = mactext; + pmacdef(mactext, 1); + s_lastmac = NULL; +} diff --git a/Tools/unix/uz80as/pp.h b/Tools/unix/uz80as/pp.h new file mode 100644 index 00000000..74a6d6f2 --- /dev/null +++ b/Tools/unix/uz80as/pp.h @@ -0,0 +1,23 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Preprocessor. + * =========================================================================== + */ + +#ifndef PP_H +#define PP_H + +/* Max line length after macro expansion + '\0'. */ +#define LINESZ 512 + +extern char *s_pline; +extern int s_pc; +extern int s_pass; +extern int s_skipon; + +void pp_line(const char *line); +void pp_reset(void); +void pp_define(const char *name); + +#endif diff --git a/Tools/unix/uz80as/prtable.c b/Tools/unix/uz80as/prtable.c new file mode 100644 index 00000000..1c3be97f --- /dev/null +++ b/Tools/unix/uz80as/prtable.c @@ -0,0 +1,343 @@ +#include "prtable.h" +#include "err.h" +#include "targets.h" +#include "uz80as.h" + +#ifndef STDLIB_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +enum { STRSZ = 32 }; + +struct itext { + struct itext *next; + int undoc; + char str[STRSZ]; +}; + +struct ilist { + struct itext *head; + int nelems; +}; + +struct itable { + struct itext **table; + int nelems; +}; + +static char s_buf[STRSZ]; + +static void nomem(const char *str) +{ + eprogname(); + fprintf(stderr, _("not enough memory (%s)\n"), str); + exit(EXIT_FAILURE); +} + +static int compare(const void *pa, const void *pb) +{ + const struct itext * const *ia; + const struct itext * const *ib; + + ia = (const struct itext * const *) pa; + ib = (const struct itext * const *) pb; + return strcmp((*ia)->str, (*ib)->str); +} + +/* + * Returns a new allocated itable of pointers that point to each element in the + * list ilist, alphabetically sorted. + */ +static struct itable *sort_list(struct ilist *ilist) +{ + int n; + struct itable *itable; + struct itext *p; + + if ((itable = calloc(1, sizeof(*itable))) == NULL) { + return NULL; + } + itable->nelems = 0; + + if (ilist->nelems == 0) { + return itable; + } + + itable->table = malloc(ilist->nelems * sizeof(*itable->table)); + if (itable->table == NULL) { + free(itable); + return NULL; + } + + for (n = 0, p = ilist->head; + p != NULL && n < ilist->nelems; + p = p->next, n++) + { + itable->table[n] = p; + } + itable->nelems = n; + + qsort(itable->table, itable->nelems, sizeof(*itable->table), compare); + return itable; +} + +static void print_itable(struct itable *itable, FILE *f) +{ + int i, col; + struct itext *p; + + if (itable == NULL) { + return; + } + + fputs("@multitable @columnfractions .25 .25 .25 .25\n", f); + col = 0; + for (i = 0; i < itable->nelems; i++) { + p = itable->table[i]; + if (col == 0) { + fputs("@item ", f); + } else { + fputs("@tab ", f); + } + if (p->undoc) { + fputs("* ", f); + } + fprintf(f, "%s\n", p->str); + col++; + if (col >= 4) { + col = 0; + } + } + fputs("@end multitable\n", f); +} + +#if 0 +static void print_ilist(struct ilist *ilist, FILE *f) +{ + int col; + struct itext *p; + + if (ilist == NULL) { + return; + } + + fputs("@multitable @columnfractions .25 .25 .25 .25\n", f); + col = 0; + for (p = ilist->head; p != NULL; p = p->next) { + if (col == 0) { + fputs("@item ", f); + } else { + fputs("@tab ", f); + } + if (p->undoc) { + fputs("* ", f); + } + fprintf(f, "%s\n", p->str); + col++; + if (col >= 4) { + col = 0; + } + } + fputs("@end multitable\n", f); +} +#endif + +static void bufset(int i, char c) +{ + if (i >= STRSZ) { + eprogname(); + fputs(_("prtable: please, increase s_buf size\n"), stderr); + exit(EXIT_FAILURE); + } else { + s_buf[i] = c; + } +} + +static void gen_inst2(struct ilist *ilist, char *instr, int undoc) +{ + struct itext *p; + + if ((p = malloc(sizeof(*p))) == NULL) { + nomem("gen_inst2"); + } + + snprintf(p->str, STRSZ, "%s", instr); + p->undoc = undoc; + p->next = ilist->head; + ilist->head = p; + ilist->nelems++; +} + +static void gen_inst(struct ilist *ilist, const struct target *t, + unsigned char undoc, const char *p, size_t bufi, + const char *pr) +{ + size_t bufk; + const char *s; + + while (*p) { + if (!islower(*p)) { + if (*p == '@') { + bufset(bufi++, '@'); + } + bufset(bufi++, *p); + p++; + } else if (*p == 'a') { + if (pr == NULL) { + bufset(bufi++, 'e'); + } else if (pr[0] && pr[1]) { + if (pr[0] == pr[1]) { + bufset(bufi++, pr[0]); + pr += 2; + } else if (isdigit(pr[1])) { + bufset(bufi++, *pr); + pr++; + while (isdigit(*pr)) { + bufset(bufi++, *pr); + pr++; + } + } else { + bufset(bufi++, pr[0]); + bufset(bufi++, pr[1]); + pr += 2; + } + } else { + bufset(bufi++, 'e'); + } + p++; + } else { + break; + } + } + + if (*p == '\0') { + bufset(bufi, '\0'); + gen_inst2(ilist, s_buf, t->mask & undoc); + } else { + t->pat_char_rewind(*p); + while ((s = t->pat_next_str()) != NULL) { + if (s[0] != '\0') { + bufset(bufi, '\0'); + bufk = bufi; + while (*s != '\0') { + bufset(bufk++, *s); + s++; + } + bufset(bufk, '\0'); + gen_inst(ilist, t, undoc, p + 1, bufk, pr); + } + } + } +} + +/* Generate a list of instructions. */ +static struct ilist *gen_list(const struct target *t, unsigned char mask2, + int delta) +{ + int i, pr; + const struct matchtab *mt; + struct ilist *ilist; + + if ((ilist = calloc(1, sizeof(*ilist))) == NULL) { + return NULL; + } + + i = 0; + mt = t->matcht; + while (mt[i].pat != NULL) { + pr = 0; + if (t->mask == 1 && (mt[i].mask & 1)) { + pr = 1; + } else if (delta) { + if ((mt[i].mask & t->mask) && + !(mt[i].mask & mask2)) + { + pr = 1; + } + } else if (t->mask & mt[i].mask) { + pr = 1; + } + if (pr) { + gen_inst(ilist, t, mt[i].undoc, mt[i].pat, + 0, mt[i].pr); + } + i++; + } + + return ilist; +} + +/* + * Prints the instruction set of a target or if target_id is "target2,target1" + * prints the instructions in target2 not in target1. + */ +void print_table(FILE *f, const char *target_id) +{ + struct ilist *ilist; + struct itable *itable; + const struct target *t, *t2; + char target1[STRSZ]; + const char *target2; + unsigned char mask2; + int delta; + + /* check if we have "target" or "target,target" as arguments */ + if ((target2 = strchr(target_id, ',')) != NULL) { + delta = 1; + snprintf(target1, sizeof(target1), "%s", target_id); + target1[target2 - target_id] = '\0'; + target2++; + } else { + delta = 0; + snprintf(target1, sizeof(target1), "%s", target_id); + target2 = NULL; + } + + t = find_target(target1); + if (t == NULL) { + eprogname(); + fprintf(stderr, _("invalid target '%s'\n"), target1); + exit(EXIT_FAILURE); + } + + if (target2) { + t2 = find_target(target2); + if (t2 == NULL) { + eprogname(); + fprintf(stderr, _("invalid target '%s'\n"), target2); + exit(EXIT_FAILURE); + } + if (t->matcht != t2->matcht) { + eprogname(); + fprintf(stderr, _("unrelated targets %s,%s\n"), + target1, target2); + exit(EXIT_FAILURE); + } + mask2 = t2->mask; + } else { + mask2 = 1; + } + + if ((ilist = gen_list(t, mask2, delta)) == NULL) { + nomem("gen_list"); + } + + if ((itable = sort_list(ilist)) == NULL) { + nomem("sort_list"); + } + + print_itable(itable, f); + + /* We don't free ilist nor itable for now, since this is called + * from main and then the program terminated. + */ +} + diff --git a/Tools/unix/uz80as/prtable.h b/Tools/unix/uz80as/prtable.h new file mode 100644 index 00000000..41eb646a --- /dev/null +++ b/Tools/unix/uz80as/prtable.h @@ -0,0 +1,11 @@ +#ifndef PRTABLE_H +#define PRTABLE_H + +#ifndef STDIO_H +#define STDIO_H +#include +#endif + +void print_table(FILE *f, const char *target_id); + +#endif diff --git a/Tools/unix/uz80as/sym.c b/Tools/unix/uz80as/sym.c new file mode 100644 index 00000000..53fc2625 --- /dev/null +++ b/Tools/unix/uz80as/sym.c @@ -0,0 +1,103 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Symbol table for labels. + * =========================================================================== + */ + +#include "config.h" +#include "sym.h" +#include "utils.h" +#include "err.h" + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +/* + * Maximum number of symbols (labels) allowed. + * Must not be more than 64K. + */ +#define NSYMS 15000 + +/* Closest prime to NSYMS / 4. */ +#define SYMTABSZ 3739 + +/* + * Nodes for the s_symtab hash table. + * The symbol at index 0 is never used. + */ +static struct sym s_symlist[NSYMS]; + +/* + * Hash table of indexes into s_symlist. + * 0 means that the bucket is empty. + */ +static unsigned short s_symtab[SYMTABSZ]; + +/* Next free symbol in s_symlist. Note: 0 not used. */ +static int s_nsyms = 1; + +/* + * Lookups the string in [p, q[ in s_symtab. + * If !insert, returns the symbol or NULL if it is not in the table. + * If insert, inserts the symbol in the table if it is not there, and + * sets its .val to 'pc'. + */ +struct sym *lookup(const char *p, const char *q, int insert, int pc) +{ + int h, k; + struct sym *nod; + + if (q - p > SYMLEN - 1) { + /* Label too long, don't add. */ + eprint(_("label too long")); + epchars(p, q); + enl(); + newerr(); + /* + * This would truncate: + * q = p + (SYMLEN - 1); + */ + return NULL; + } + + h = hash(p, q, SYMTABSZ); + for (k = s_symtab[h]; k != 0; k = s_symlist[k].next) { + if (scmp(p, q, s_symlist[k].name) == 0) { + if (insert) { + if (!s_symlist[k].isequ) { + wprint("duplicate label (%s)\n", + s_symlist[k].name); + } + } + return &s_symlist[k]; + } + } + + if (insert) { + if (s_nsyms == NSYMS) { + eprint(_("maximum number of labels exceeded (%d)\n"), + NSYMS); + exit(EXIT_FAILURE); + } + + nod = &s_symlist[s_nsyms]; + nod->next = s_symtab[h]; + s_symtab[h] = (unsigned short) s_nsyms; + s_nsyms++; + + k = 0; + while (p != q && k < SYMLEN - 1) + nod->name[k++] = *p++; + nod->name[k] = '\0'; + nod->val = pc; + return nod; + } + + return NULL; +} diff --git a/Tools/unix/uz80as/sym.h b/Tools/unix/uz80as/sym.h new file mode 100644 index 00000000..43108c68 --- /dev/null +++ b/Tools/unix/uz80as/sym.h @@ -0,0 +1,23 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Symbol table for labels. + * =========================================================================== + */ + +#ifndef SYM_H +#define SYM_H + +/* Max symbol length + '\0'. */ +#define SYMLEN 32 + +struct sym { + char name[SYMLEN]; /* null terminated string */ + int val; /* value of symbol */ + unsigned short next; /* index into symlist; 0 is no next */ + unsigned char isequ; /* if val comes from EQU */ +}; + +struct sym *lookup(const char *p, const char *q, int insert, int pc); + +#endif diff --git a/Tools/unix/uz80as/targets.c b/Tools/unix/uz80as/targets.c new file mode 100644 index 00000000..742ef674 --- /dev/null +++ b/Tools/unix/uz80as/targets.c @@ -0,0 +1,96 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Target list. + * =========================================================================== + */ + +#include "targets.h" +#include "uz80as.h" + +#ifndef STRING_H +#include +#endif + +extern const struct target s_target_z80; +extern const struct target s_target_hd64180; +extern const struct target s_target_gbcpu; +extern const struct target s_target_dp2200; +extern const struct target s_target_dp2200ii; +extern const struct target s_target_i4004; +extern const struct target s_target_i4040; +extern const struct target s_target_i8008; +extern const struct target s_target_i8021; +extern const struct target s_target_i8022; +extern const struct target s_target_i8041; +extern const struct target s_target_i8048; +extern const struct target s_target_i8051; +extern const struct target s_target_i8080; +extern const struct target s_target_i8085; +extern const struct target s_target_mos6502; +extern const struct target s_target_r6501; +extern const struct target s_target_g65sc02; +extern const struct target s_target_r65c02; +extern const struct target s_target_r65c29; +extern const struct target s_target_w65c02s; +extern const struct target s_target_mc6800; +extern const struct target s_target_mc6801; +extern const struct target s_target_m68hc11; + +static const struct target *s_targets[] = { + &s_target_z80, + &s_target_hd64180, + &s_target_gbcpu, + &s_target_dp2200, + &s_target_dp2200ii, + &s_target_i4004, + &s_target_i4040, + &s_target_i8008, + &s_target_i8021, + &s_target_i8022, + &s_target_i8041, + &s_target_i8048, + &s_target_i8051, + &s_target_i8080, + &s_target_i8085, + &s_target_mos6502, + &s_target_r6501, + &s_target_g65sc02, + &s_target_r65c02, + &s_target_r65c29, + &s_target_w65c02s, + &s_target_mc6800, + &s_target_mc6801, + &s_target_m68hc11, + NULL, +}; + +static int s_index; + +const struct target *find_target(const char *id) +{ + const struct target **p; + + for (p = s_targets; *p != NULL; p++) { + if (strcmp(id, (*p)->id) == 0) { + return *p; + } + } + + return NULL; +} + +const struct target *first_target(void) +{ + s_index = 0; + return next_target(); +} + +const struct target *next_target(void) +{ + if (s_targets[s_index] != NULL) { + return s_targets[s_index++]; + } else { + return NULL; + } +} diff --git a/Tools/unix/uz80as/targets.h b/Tools/unix/uz80as/targets.h new file mode 100644 index 00000000..12b6f64c --- /dev/null +++ b/Tools/unix/uz80as/targets.h @@ -0,0 +1,18 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Target list. + * =========================================================================== + */ + +#ifndef TARGETS_H +#define TARGETS_H + +struct target; + +const struct target *find_target(const char *id); + +const struct target *first_target(void); +const struct target *next_target(void); + +#endif diff --git a/Tools/unix/uz80as/test.asm b/Tools/unix/uz80as/test.asm new file mode 100644 index 00000000..28fdff3b --- /dev/null +++ b/Tools/unix/uz80as/test.asm @@ -0,0 +1,8 @@ + .org 0x100 +beep: .text "\"\"\"" ; comment + .align 4 + .text "foo" + .align 16 + .text "bar" + .db $45,0x67,'7' + .end diff --git a/Tools/unix/uz80as/test1.asm b/Tools/unix/uz80as/test1.asm new file mode 100644 index 00000000..1133b01f --- /dev/null +++ b/Tools/unix/uz80as/test1.asm @@ -0,0 +1,3 @@ + .org $100 +beep: .text "x" ; comment + .end diff --git a/Tools/unix/uz80as/utils.c b/Tools/unix/uz80as/utils.c new file mode 100644 index 00000000..a361f2fc --- /dev/null +++ b/Tools/unix/uz80as/utils.c @@ -0,0 +1,137 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Generic functions. + * =========================================================================== + */ + +#include "config.h" +#include "utils.h" + +#ifndef CTYPE_H +#include +#endif + +#ifndef LIMITS_H +#include +#endif + +/* + * Copy [p, q[ to dst and null terminate dst. + */ +void copychars(char *dst, const char *p, const char *q) +{ +// int i = 0; +// printf("copychars %x->%x to %x \'", p, q, dst); + while (p != q) { +// printf("%c", *p); + *dst++ = *p++; +// i++; + } + *dst = '\0'; +// printf("\' %d %x %d\n", *dst, dst, i); +} + +/* Skip space. */ +const char *skipws(const char *p) +{ + while (isspace(*p)) + p++; + return p; +} + +/* Return 1 if *p is a valid start character for an identifier. */ +int isidc0(char c) +{ + return (c == '_') || isalpha(c); +} + +/* + * Return 1 if *p is a valid character for an identifier. + * Don't use for the first character. + */ +int isidc(char c) +{ + return (c == '_') || (c == '.') || isalnum(c); +} + +/* Hash the string in [p, q[ to give a bucket in symtab. */ +int hash(const char *p, const char *q, unsigned int tabsz) +{ + unsigned int h; + + h = 0; + while (p != q) { + h = 31 * h + (unsigned char) *p; + p++; + } + + return h % tabsz; +} + +/* + * Compare the string in [p, q[ with the null-terminated string s. + * Return 0 if equal. + */ +int scmp(const char *p, const char *q, const char *s) +{ + while (p < q) { + if (*p == *s) { + p++; + s++; + } else if (*s == '\0') { + return 1; + } else if (*p < *s) { + return -1; + } else { + return 1; + } + } + + if (*s == '\0') + return 0; + else + return -1; +} +/* + * Given a hexadecimal character (in upper case), returns its integer value. + * Returns -1 if c is not a hexadecimal character. + */ +int hexvalu(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return (c - 'A') + 10; + else + return -1; +} + +/* + * Given a hexadecimal character, returns its integer value. + * Returns -1 if c is not a hexadecimal character. + */ +int hexval(char c) +{ + if (c >= 'a' && c <= 'f') + return (c - 'a') + 10; + else + return hexvalu(c); +} + +int int_precission(void) +{ + static int bits = 0; + unsigned int i; + + if (bits > 0) + return bits; + + i = INT_MAX; + bits = 0; + while (i) { + bits++; + i >>= 1; + } + return bits; +} diff --git a/Tools/unix/uz80as/utils.h b/Tools/unix/uz80as/utils.h new file mode 100644 index 00000000..2d22db6a --- /dev/null +++ b/Tools/unix/uz80as/utils.h @@ -0,0 +1,26 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Generic functions. + * =========================================================================== + */ + +#ifndef UTILS_H +#define UTILS_H + +#define NELEMS(a) (sizeof(a)/sizeof(a[0])) + +#define XSTR(n) STR(n) +#define STR(n) #n + +void copychars(char *dst, const char *p, const char *q); +int hash(const char *p, const char *q, unsigned int tabsz); +int isidc0(char c); +int isidc(char c); +int scmp(const char *p, const char *q, const char *s); +const char *skipws(const char *p); +int hexvalu(char c); +int hexval(char c); +int int_precission(void); + +#endif diff --git a/Tools/unix/uz80as/uz80as.c b/Tools/unix/uz80as/uz80as.c new file mode 100644 index 00000000..35c13ac0 --- /dev/null +++ b/Tools/unix/uz80as/uz80as.c @@ -0,0 +1,1240 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Assembler. + * =========================================================================== + */ + +#include "config.h" +#include "uz80as.h" +#include "options.h" +#include "utils.h" +#include "err.h" +#include "incl.h" +#include "sym.h" +#include "expr.h" +#include "exprint.h" +#include "pp.h" +#include "list.h" +#include "targets.h" + +#ifndef ASSERT_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +static void output(); + +static const char *d_align(const char *); +static const char *d_null(const char *); +static const char *d_block(const char *); +static const char *d_byte(const char *); +static const char *d_chk(const char *); +static const char *d_codes(const char *); +static const char *d_echo(const char *); +static const char *d_eject(const char *); +static const char *d_export(const char *); +static const char *d_end(const char *); +static const char *d_equ(const char *); +static const char *d_fill(const char *); +static const char *d_ds(const char *); +static const char *d_list(const char *); +static const char *d_lsfirst(const char *); +static const char *d_module(const char *); +static const char *d_msfirst(const char *); +static const char *d_nocodes(const char *); +static const char *d_nolist(const char *); +static const char *d_org(const char *); +static const char *d_set(const char *); +static const char *d_text(const char *); +static const char *d_title(const char *); +static const char *d_word(const char *); + +/* + * Directives. + * This table must be sorted, to allow for binary search. + */ +static struct direc { + const char *name; + const char *(*fun)(const char *); +} s_directab[] = { + { "ALIGN", d_align }, + { "BLOCK", d_block }, + { "BYTE", d_byte }, + { "CHK", d_chk }, + { "CODES", d_codes }, + { "DB", d_byte }, + { "DS", d_ds }, + { "DW", d_word }, + { "ECHO", d_echo }, + { "EJECT", d_eject }, + { "END", d_end }, + { "EQU", d_equ }, + { "EXPORT", d_export }, + { "FILL", d_fill }, + { "GLOBAL", d_export }, + { "LIST", d_list }, + { "LSFIRST", d_lsfirst }, + { "MODULE", d_module }, + { "MSFIRST", d_msfirst }, + { "NOCODES", d_nocodes }, + { "NOLIST", d_nolist }, + { "NOPAGE", d_null }, + { "ORG", d_org }, + { "PAGE", d_null }, + { "SECTION", d_null }, + { "SET", d_set }, + { "TEXT", d_text }, + { "TITLE", d_title }, + { "WORD", d_word }, +}; + +/* binary output file */ +FILE *fout; + +/* output in source order */ +int b_flag = 1; + +/* The target. */ +const struct target *s_target; + +/* The z80 addressable memory. The object code. */ +static unsigned char s_mem[64 * 1024]; + +/* Program counter min and max ([s_minpc, s_maxpc[). */ +static int s_minpc, s_maxpc; + +/* Original input line. */ +static char s_line[LINESZ]; + +/* Label defined on this line. */ +static struct sym *s_lastsym; + +/* Output words the most significant byte first */ +static int s_msbword; + +/* If we have seen the .END directive. */ +static int s_end_seen; + +/* We have issued the error of generating things after an .END. */ +static int s_gen_after_end; + +/* The empty line, to pass to listing, for compatibility with TASM. */ +static const char *s_empty_line = ""; + +/* Pointer in s_pline for error reporting. */ +const char *s_pline_ep; + +/* We skip characters until endline or backslash or comment. */ +static const char *sync(const char *p) +{ + while (*p != '\0' && *p != '\\' && *p != ';') + p++; + return p; +} + +/* the written bitmap */ +unsigned char membit[65536 / 8]; + +void +setbit(int pc) +{ + membit[pc / 8] |= (1 << (pc % 8)); +} + +int +isset(int pc) +{ + return membit[pc / 8] & (1 << (pc % 8)); +} + +void +open_output() +{ + fout = efopen(s_objfname, "wb"); +} + +void +close_output() +{ + if (fclose(fout) == EOF) { + eprint(_("cannot close file %s\n"), s_objfname); + } +} + +/* + * Generates a byte to the output and updates s_pc, s_minpc and s_maxpc. + * Will issue a fatal error if we write beyong 64k. + */ +void genb(int b, const char *ep) +{ + if (s_pass == 0 && s_end_seen && !s_gen_after_end) { + s_gen_after_end = 1; + eprint(_("generating code after .END\n")); + eprcol(s_pline, ep); + newerr(); + } + if (s_minpc < 0) + s_minpc = s_pc; + if (s_pc >= 65536) { + eprint(_("generating code beyond address 65535\n")); + eprcol(s_pline, ep); + exit(EXIT_FAILURE); + } + s_mem[s_pc] = (unsigned char) b; + setbit(s_pc); + + if (s_pass == 1) { + list_genb(b); + if (b_flag) { + fwrite(&s_mem[s_pc], 1, 1, fout); + } + } + + if (s_pc < s_minpc) + s_minpc = s_pc; + s_pc++; + if (s_pc > s_maxpc) + s_maxpc = s_pc; +} + +/* + * Generate 'n' as a 16 bit word, little endian or big endian depending on + * s_msbword. + */ +static void genw(int n, const char *ep) +{ + if (s_msbword) + genb(n >> 8, ep); + genb(n, ep); + if (!s_msbword) + genb(n >> 8, ep); +} + +/* + * We have matched an instruction in the table. + * Generate the machine code for the instruction using the generation + * pattern 'p. 'vs are the arguments generated during the matching process. + */ +static void gen(const char *p, const int *vs) +{ + // int w, b, i, savepc; + int b, i, savepc; + const char *p_orig; + + savepc = s_pc; + p_orig = p; + b = 0; +loop: + i = hexvalu(*p); + if (i >= 0) { + p++; + b = (i << 4) | hexval(*p); + } else if (*p == '.') { + genb(b, s_pline_ep); + b = 0; + } else if (*p == '\0') { + return; + } else { + i = *(p + 1) - '0'; + switch (*p) { + case 'b': b |= (vs[i] << 3); break; + case 'c': b |= vs[i]; break; + case 'd': b = vs[i]; break; + case 'e': genb(vs[i] & 0xff, s_pline_ep); + genb(vs[i] >> 8, s_pline_ep); + break; + default: + if (s_target->genf(&b, *p, vs, i, savepc) == -1) { + eprogname(); + fprintf(stderr, + _("fatal: bad pattern %s ('%c')"), + p_orig, *p); + enl(); + exit(EXIT_FAILURE); + } + } + p++; + } + p++; + goto loop; +} + +/* + * Tries to match *p with any of the strings in list. + * If matched, returns the index in list and r points to the position + * in p past the matched string. + */ +int mreg(const char *p, const char *const list[], const char **r) +{ + const char *s; + const char *q; + int i; + + i = 0; + while ((s = list[i++]) != NULL) { + if (*s == '\0') + continue; + q = p; + while (toupper(*q++) == *s++) { + if (*s == '\0') { + if (!isalnum(*q)) { + *r = q; + return i - 1; + } else { + break; + } + } + } + } + return -1; +} + +static int isoctal(int c) +{ + return c >= '0' && c <= '7'; +} + +/* + * Read an octal of 3 digits, being the maximum value 377 (255 decimal); + * Return -1 if there is an error in the syntax. + */ +static int readoctal(const char *p) +{ + int n; + const char *q; + + if (*p >= '0' && *p <= '3' && isoctal(*(p + 1)) && isoctal(*(p + 2))) { + n = 0; + q = p + 3; + while (p < q) { + n *= 8; + n += (*p - '0'); + p++; + } + return n; + } + + return -1; +} + +enum strmode { + STRMODE_ECHO, + STRMODE_NULL, + STRMODE_BYTE, + STRMODE_WORD +}; + +/* + * Generate the string bytes until double quote or null char. + * Return a pointer to the ending double quote character or '\0'. + * 'p must point to the starting double quote. + * If mode: + * STRMODE_ECHO only echo to stderr the characters. + * STRMODE_NULL only parses the string. + * STRMODE_BYTE generate the characters in the binary file as bytes. + * STRMODE_WORD generate the characters in the binary file as words. + */ +static const char *genstr(const char *p, enum strmode mode) +{ + int c; + + for (p = p + 1; *p != '\0' && *p != '\"'; p++) { + c = *p; + if (c == '\\') { + p++; + switch (*p) { + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 'b': c = '\b'; break; + case 't': c = '\t'; break; + case 'f': c = '\f'; break; + case '\\': c = '\\'; break; + case '\"': c = '\"'; break; + default: + c = readoctal(p); + if (c < 0) { + eprint(_("bad character escape " + "sequence\n")); + eprcol(s_pline, p - 1); + newerr(); + p--; + } else { + p += 2; + } + } + } + switch (mode) { + case STRMODE_ECHO: fputc(c, stderr); break; + case STRMODE_NULL: break; + case STRMODE_BYTE: genb(c, p); break; + case STRMODE_WORD: genw(c, p); break; + } + } + + return p; +} + +/* Match an instruction. + * If no match returns NULL; else returns one past end of match. + * p should point to no whitespace. + */ +static const char *match(const char *p) +{ + const struct matchtab *mtab; + const char *s, *pp, *q; + int v, n, vi, linepc; + int vs[4]; + + assert(!isspace(*p)); + + mtab = s_target->matcht; + linepc = s_pc; + pp = p; + n = -1; +next: + n++; + s = mtab[n].pat; + if (s == NULL) { + return NULL; + } else if ((s_target->mask & mtab[n].mask) == 0) { + goto next; + } else if (!s_undocumented_op && (s_target->mask & mtab[n].undoc)) { + goto next; + } + p = pp; + vi = 0; +loop: + if (*s == '\0') { + p = skipws(p); + if (*p != ';' && *p != '\0' && *p != '\\') + goto next; + else + goto found; + } else if (*s == ' ') { + if (!isspace(*p)) + goto next; + p = skipws(p); + } else if ((*s == ',' || *s == '(' || *s == ')') && isspace(*p)) { + p = skipws(p); + if (*s != *p) + goto next; + p = skipws(p + 1); + } else if (*s == 'a') { + p = expr(p, &v, linepc, s_pass == 0, NULL, NULL); + if (p == NULL) + return NULL; + vs[vi++] = v; + } else if (*s >= 'b' && *s <= 'z') { + v = s_target->matchf(*s, p, &q); + goto reg; + } else if (*p == *s && *p == ',') { + p = skipws(p + 1); + } else if (toupper(*p) == *s) { + p++; + } else { + goto next; + } +freg: + s++; + goto loop; +reg: + if (v < 0) { + goto next; + } else { + assert(vi < sizeof(vs)); + vs[vi++] = v; + p = q; + } + goto freg; +found: + // printf("%s\n", s_matchtab[n].pat); + gen(mtab[n].gen, vs); + return p; +} + +static const char * +d_null(const char *p) +{ + p = sync(p); + while (*p != '\0' && *p != '\\') { + if (!isspace(*p)) { + wprint(_("invalid characters after directive\n")); + eprcol(s_pline, p); + return sync(p); + } else { + p++; + } + } + return p; +} + +static const char *d_end(const char *p) +{ + enum expr_ecode ecode; + const char *q; + const char *ep; + + if (s_pass == 0) { + if (s_end_seen) { + eprint(_("duplicate .END\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } else { + s_end_seen = 1; + } + } + + q = expr(p, NULL, s_pc, s_pass == 0, &ecode, &ep); + if (q == NULL && ecode == EXPR_E_NO_EXPR) { + return p; + } else if (q == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } else { + return q; + } +} + +static const char *d_codes(const char *p) +{ + s_codes = 1; + return p; +} + +static const char *d_module(const char *p) +{ + p = sync(p); + while (*p != '\0' && *p != '\\') { + if (!isspace(*p)) { + wprint(_("invalid characters after directive\n")); + eprcol(s_pline, p); + return sync(p); + } else { + p++; + } + } + return p; +} + +static const char *d_nocodes(const char *p) +{ + s_codes = 0; + return p; +} + +static const char *d_list(const char *p) +{ + s_list_on = 1; + return p; +} + +static const char *d_nolist(const char *p) +{ + s_list_on = 0; + return p; +} + +static const char *d_eject(const char *p) +{ + list_eject(); + return p; +} + +static const char *d_echo(const char *p) +{ + int n; + int mode; + enum expr_ecode ecode; + const char *ep; + + mode = (s_pass == 0) ? STRMODE_NULL : STRMODE_ECHO; + if (*p == '\"') { + p = genstr(p, mode); + if (*p == '\"') { + p++; + } else if (s_pass == 0) { + wprint(_("no terminating quote\n")); + eprcol(s_pline, p); + } + } else if (*p != '\0') { + p = expr(p, &n, s_pc, s_pass == 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + if (mode == STRMODE_ECHO) { + fprintf(stderr, "%d", n); + } + } + return p; +} + +static const char *d_equ(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep; + + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (s_lastsym == NULL) { + eprint(_(".EQU without label\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } else { + /* TODO: check label misalign? */ + s_lastsym->val = n; + s_lastsym->isequ = 1; + } + return p; +} + +static const char *d_set(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep; + + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (s_lastsym == NULL) { + eprint(_(".EQU without label\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } else { + /* TODO: check label misalign? */ + s_lastsym->val = n; + s_lastsym->isequ = 1; + } + return p; +} + +static const char *d_export(const char *p) +{ + /* TODO */ + return NULL; +} + +static const char *d_fill(const char *p) +{ + int n, v, er; + const char *q; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + er = 0; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (n < 0) { + eprint(_("number of positions to fill is negative (%d)\n"), n); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + v = 255; + p = skipws(p); + if (*p == ',') { + p = skipws(p + 1); + q = expr(p, &v, s_pc, s_pass == 0, &ecode, &ep); + if (q == NULL) { + er = 1; + exprint(ecode, s_pline, ep); + newerr(); + } else { + p = q; + } + } + + while (n--) + genb(v, eps); + + if (er) + return NULL; + else + return p; +} + +static const char *d_ds(const char *p) +{ + int n, v, er; + const char *q; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + er = 0; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (n < 0) { + eprint(_("number of positions to space over is negative (%d)\n"), n); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + v = 255; + p = skipws(p); + if (*p == ',') { + p = skipws(p + 1); + q = expr(p, &v, s_pc, s_pass == 0, &ecode, &ep); + if (q == NULL) { + er = 1; + exprint(ecode, s_pline, ep); + newerr(); + } else { + p = q; + } + } + + s_pc += n; + + if (er) + return NULL; + else + return p; +} + +static const char *d_lsfirst(const char *p) +{ + s_msbword = 0; + return p; +} + +static const char *d_msfirst(const char *p) +{ + s_msbword = 1; + return p; +} + +static const char *d_org(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (n < 0 || n > 65536) { + eprint(_(".ORG address (%d) is not in range [0, 65536]\n"), n); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + s_pc = n; + + /* Change the listing PC so in orgs we print the changed PC. */ + if (s_pass > 0) + list_setpc(s_pc); + + if (s_lastsym != NULL) { + /* TODO: check label misalign? */ + s_lastsym->val = s_pc; + s_lastsym->isequ = 1; + } + + return p; +} + +static const char *d_lst(const char *p, int w) +{ + enum strmode mode; + int n, linepc; + enum expr_ecode ecode; + const char *ep, *eps; + + if (w) + mode = STRMODE_WORD; + else + mode = STRMODE_BYTE; + + linepc = s_pc; +dnlst: + if (*p == '\"') { + p = genstr(p, mode); + if (*p == '\"') { + p++; + } else { + wprint(_("no terminating quote\n")); + eprcol(s_pline, p); + } + } else { + eps = p; + p = expr(p, &n, linepc, s_pass == 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + if (w) + genw(n, eps); + else + genb(n, eps); + } + p = skipws(p); + if (*p == ',') { + p++; + p = skipws(p); + goto dnlst; + } + return p; +} + +static const char *d_align(const char *p) +{ + int n, v, er; + const char *q; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + er = 0; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (n < 0) { + eprint(_("align is negative (%d)\n"), n); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + while (s_pc % n) { + genb(0, eps); + } + + if (er) + return NULL; + else + return p; +} + +static const char *d_byte(const char *p) +{ + return d_lst(p, 0); +} + +static const char *d_word(const char *p) +{ + return d_lst(p, 1); +} + +static const char *d_text(const char *p) +{ + if (*p == '\"') { + p = genstr(p, STRMODE_BYTE); + if (*p == '\"') { + p++; + } else { + wprint(_("no terminating quote\n")); + eprcol(s_pline, p); + } + return p; + } else { + eprint(_(".TEXT directive needs a quoted string argument\n")); + eprcol(s_pline, p); + newerr(); + return NULL; + } +} + +static const char *d_title(const char *p) +{ + return NULL; +} + +static const char *d_block(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + s_pc += n; + if (s_pc < 0 || s_pc > 65536) { + eprint(_("address (%d) set by .BLOCK is not in range " + "[0, 65536]\n"), s_pc); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + return p; +} + +/* a must be < b. */ +static int checksum(int a, int b) +{ + int n; + + assert(a < b); + + n = 0; + while (a < b) + n += s_mem[a++]; + + return n; +} + +static const char *d_chk(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + p = expr(p, &n, s_pc, s_pass == 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + genb(0, eps); + return NULL; + } + + if (s_pass == 0) { + genb(0, s_pline_ep); + } else if (n < 0 || n >= s_pc) { + eprint(_(".CHK address (%d) is not in range [0, %d[\n"), n, + s_pc); + eprcol(s_pline, eps); + newerr(); + genb(0, eps); + } else { + genb(checksum(n, s_pc), eps); + } + + return p; +} + +/* Parses an internal directive (those that start with '.'). + * Returns NULL on error; + * If no error returns position past the parsed directive and arguments. */ +static const char *parse_direc(const char *cp) +{ + const char *cq, *p; + int a, b, m = 0; + + a = 0; + b = NELEMS(s_directab) - 1; + while (a <= b) { + m = (a + b) / 2; + cq = cp; + p = s_directab[m].name; + while (*p != '\0' && toupper(*cq) == *p) { + p++; + cq++; + } + if (*p == '\0' && (*cq == '\0' || isspace(*cq))) + break; + else if (toupper(*cq) < *p) + b = m - 1; + else + a = m + 1; + } + + if (a <= b) { + cq = skipws(cq); + return s_directab[m].fun(cq); + } else { + eprint(_("unrecognized directive\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + return NULL; + } +} + +static void parselin(const char *cp) +{ + int col0, alloweq; + const char *q; + + s_pline_ep = cp; +start: s_lastsym = NULL; + alloweq = 0; + col0 = 1; +loop: + if (*cp == '\0' || *cp == ';') { + return; + } else if (*cp == '\\') { + if (s_pass == 1) { + list_endln(); + list_startln(s_empty_line, curfile()->linenum, s_pc, + nfiles()); + } + cp++; + goto start; + } else if (*cp == '.') { + s_pline_ep = cp; + cp++; + q = parse_direc(cp); + if (q == NULL) { + cp = sync(cp); + } else { + cp = d_null(q); + } + } else if ((*cp == '$' || *cp == '*') && cp[1] == '=') { + /* Alternative form of .ORG: *= or $= */ + cp += 2; + q = d_org(cp); + if (q == NULL) { + cp = sync(cp); + } else { + cp = d_null(q); + } + } else if (*cp == '=' && alloweq) { + /* equ */ + s_pline_ep = cp; + cp++; + q = d_equ(cp); + if (q == NULL) { + cp = sync(cp); + } else { + cp = d_null(q); + } + } else if (isidc0(*cp)) { + if (col0 && *cp != '.') { + /* take label */ + s_pline_ep = cp; + q = cp; + col0 = 0; + while (isidc(*cp)) + cp++; + s_lastsym = lookup(q, cp, s_pass == 0, s_pc); + if (*cp == ':' || isspace(*cp)) { + alloweq = 1; + cp++; + } else if (*cp == '=') { + alloweq = 1; + } + if (s_pass == 1 && !s_lastsym->isequ && + s_lastsym->val != s_pc) + { + eprint(_("misaligned label %s\n"), + s_lastsym->name); + fprintf(stderr, _(" Previous value was %XH, " + "new value %XH."), s_lastsym->val, + s_pc); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } else { + cp = skipws(cp); + s_pline_ep = cp; + q = match(cp); + if (q == NULL) { + eprint(_("syntax error\n")); + newerr(); + cp = sync(cp); + } else { + cp = d_null(q); + } + } + } else if (isspace(*cp)) { + col0 = 0; + while (isspace(*cp)) + cp++; + } else { + eprint(_("unexpected character (%c)\n"), *cp); + eprcol(s_pline, cp); + newerr(); + cp = sync(cp + 1); + } + goto loop; +} + +/* + * Gets a new line into 's_line from 'fin. + * Terminates the line with '\0'. + * Does not read more than LINESZ - 1 characters. + * Does not add a '\n' character, thus a line of length 0 it's possible. + * Always advances to the next line. + * Returns -1 for EOF or the line length. + */ +static int getlin(FILE *fin) +{ + int i, c; + + c = EOF; + i = 0; + while (i < LINESZ - 1) { + c = getc(fin); + if (c == EOF || c == '\n') + break; + s_line[i++] = (char) c; + } + if (c != EOF && c != '\n') { + wprint(_("line too long, truncated to %d characters\n"), + LINESZ); + } + while (c != EOF && c != '\n') + c = getc(fin); + if (i == 0 && c == EOF) + return -1; + s_line[i] = '\0'; + return i; +} + +/* Preinstall the macros defined in the command line. */ +static void install_predefs(void) +{ + struct predef *pdef; + + for (pdef = s_predefs; pdef != NULL; pdef = pdef->next) + pp_define(pdef->name); +} + +/* Do a pass through the source. */ +static void dopass(const char *fname) +{ + /* Fill memory with default value. */ + if ((s_pass == 0 && s_mem_fillval != 0) || s_pass > 0) { + memset(s_mem, s_mem_fillval, sizeof(s_mem)); + } + + if (s_pass > 0) { + pp_reset(); + list_open(s_lstfname); + s_codes = 1; + s_list_on = 1; + } + + + install_predefs(); + s_minpc = -1; + s_maxpc = -1; + s_pc = 0; + s_lastsym = NULL; + s_msbword = 0; + + pushfile(fname, fname + strlen(fname)); + while (nfiles() > 0) { + curfile()->linenum++; + if (getlin(curfile()->fin) >= 0) { + if (s_pass == 1) { + list_startln(s_line, curfile()->linenum, s_pc, + nfiles()); + } + pp_line(s_line); + if (s_pass == 1) + list_skip(s_skipon); + parselin(s_pline); + if (s_pass == 1) + list_endln(); + } else { + popfile(); + } + } + + if (s_pass > 0) { + list_close(); + } +} + +/* + * Write the object file in memory order + */ +static void output() +{ + int i; + + // fprintf(stderr, "output: min: %x max: %x\n", s_minpc, s_maxpc); + + if (s_minpc < 0) + s_minpc = 0; + if (s_maxpc < 0) + s_maxpc = 0; + + for (i = s_minpc; i < s_maxpc; i++) { + if (isset(i)) { + fwrite(&s_mem[i], 1, 1, fout); + } + } + if (ferror(fout)) { + eprint(_("cannot write to file %s\n"), s_objfname); + clearerr(fout); + } +} + +/* Start the assembly using the config in options.c. */ +void uz80as(void) +{ + s_target = find_target(s_target_id); + if (s_target == NULL) { + eprint(_("target '%s' not supported\n"), s_target_id); + exit(EXIT_FAILURE); + } + + for (s_pass = 0; s_nerrors == 0 && s_pass < 2; s_pass++) { + if ((s_pass > 0) && (s_nerrors == 0)) { + open_output(); + } + dopass(s_asmfname); + if (s_pass == 0 && !s_end_seen) { + wprint(_("no .END statement in the source\n")); + } + if (s_nerrors == 0) { + if (verbose) printf("Pass %d completed.\n", s_pass + 1); + } + } + + if (s_nerrors > 0) { + exit(EXIT_FAILURE); + } + + if (!b_flag) { + output(); + } + close_output(); +} diff --git a/Tools/unix/uz80as/uz80as.h b/Tools/unix/uz80as/uz80as.h new file mode 100644 index 00000000..511da085 --- /dev/null +++ b/Tools/unix/uz80as/uz80as.h @@ -0,0 +1,63 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Assembler. + * =========================================================================== + */ + +#ifndef UZ80AS_H +#define UZ80AS_H + +int verbose; + +/* matchtab.flags */ +enum { + MATCH_F_UNDOC = 1, + MATCH_F_EXTEN = 2, +}; + +/* pat: + * a: expr + * b - z: used by target + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f - z: used by target + * + * pr: + * 8: e8 + * f: e16 + * r: relative jump + */ + +struct matchtab { + const char *pat; + const char *gen; + unsigned char mask; + unsigned char undoc; + const char *pr; +}; + +struct target { + const char *id; + const char *descr; + const struct matchtab *matcht; + int (*matchf)(char c, const char *p, const char **q); + int (*genf)(int *eb, char p, const int *vs, int i, int savepc); + void (*pat_char_rewind)(int c); + const char * (*pat_next_str)(void); + unsigned char mask; +}; + +extern const char *s_pline_ep; + +void genb(int b, const char *ep); +int mreg(const char *p, const char *const list[], const char **r); + +void uz80as(void); + +#endif diff --git a/Tools/unix/uz80as/z80.c b/Tools/unix/uz80as/z80.c new file mode 100644 index 00000000..4e2db514 --- /dev/null +++ b/Tools/unix/uz80as/z80.c @@ -0,0 +1,362 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Zilog Z80 CPU. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: B,C,D,E,H,L,A + * c: IX,IY (must be followed by + or -) + * d: BC,DE,HL,SP + * e: IX,IY + * f: BC,DE,HL,AF + * g: ADD,ADC,SUB,SBC,AND,XOR,OR,CP + * h: INC,DEC + * i: BC,DE,IX,SP + * j: BC,DE,IY,SP + * k: RLC,RRC,RL,RR,SLA,SRA,SRL + * l: BIT,RES,SET + * m: NZ,Z,NC,C,PO,PE,P,M + * n: NZ,Z,NC,C + * o: * + * p: B,C,D,E,IXH,IXL,A + * q: B,C,D,E,IYH,IYL,A + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 4) | lastbyte + * g: (op << 6) | lastbyte + * h: * + * i: relative jump to op + * j: possible value to RST + * k: possible value to IM + * m: check arithmetic used with A register + * n: check arithmetic used without A register + */ + +static const struct matchtab s_matchtab_z80[] = { + { "LD b,b", "40b0c1.", 3, 0 }, + { "LD p,p", "DD.40b0c1.", 1, 1 }, + { "LD q,q", "FD.40b0c1.", 1, 1 }, + { "LD b,(HL)", "46b0.", 3, 0 }, + { "LD b,(e)", "d1.46b0.00.", 3, 0, "ii" }, + { "LD b,(ca)", "d1.46b0.d2.", 3, 0, "ii" }, + { "LD A,I", "ED.57.", 3, 0 }, + { "LD A,R", "ED.5F.", 3, 0 }, + { "LD A,(BC)", "0A.", 3, 0 }, + { "LD A,(DE)", "1A.", 3, 0 }, + { "LD A,(a)", "3A.e0", 3, 0 }, + { "LD b,a", "06b0.d1.", 3, 0, "e8" }, + { "LD p,a", "DD.06b0.d1.", 1, 1, "e8" }, + { "LD q,a", "FD.06b0.d1.", 1, 1, "e8" }, + { "LD I,A", "ED.47.", 3, 0 }, + { "LD R,A", "ED.4F.", 3, 0 }, + { "LD SP,HL", "F9.", 3, 0 }, + { "LD SP,e", "d0.F9.", 3, 0 }, + { "LD HL,(a)", "2A.e0", 3, 0 }, + { "LD d,(a)", "ED.4Bf0.e1", 3, 0 }, + { "LD d,a", "01f0.e1", 3, 0 }, + { "LD e,(a)", "d0.2A.e1", 3, 0 }, + { "LD e,a", "d0.21.e1", 3, 0 }, + { "LD (HL),b", "70c0.", 3, 0 }, + { "LD (HL),a", "36.d0.", 3, 0, "e8" }, + { "LD (BC),A", "02.", 3, 0 }, + { "LD (DE),A", "12.", 3, 0 }, + { "LD (e),b", "d0.70c1.00.", 3, 0, "ii" }, + { "LD (ca),b", "d0.70c2.d1.", 3, 0, "ii" }, + { "LD (e),a", "d0.36.00.d1.", 3, 0, "iie8" }, + { "LD (ca),a", "d0.36.d1.d2.", 3, 0, "iie8" }, + { "LD (a),A", "32.e0", 3, 0 }, + { "LD (a),HL", "22.e0", 3, 0 }, + { "LD (a),d", "ED.43f1.e0", 3, 0 }, + { "LD (a),e", "d1.22.e0", 3, 0 }, + { "PUSH f", "C5f0.", 3, 0 }, + { "PUSH e", "d0.E5.", 3, 0 }, + { "POP f", "C1f0.", 3, 0 }, + { "POP e", "d0.E1.", 3, 0 }, + { "EX DE,HL", "EB.", 3, 0 }, + { "EX AF,AF'", "08.", 3, 0 }, + { "EX (SP),HL", "E3.", 3, 0 }, + { "EX (SP),e", "d0.E3.", 3, 0 }, + { "EXX", "D9.", 3, 0 }, + { "LDI", "ED.A0.", 3, 0 }, + { "LDIR", "ED.B0.", 3, 0 }, + { "LDD", "ED.A8.", 3, 0 }, + { "LDDR", "ED.B8.", 3, 0 }, + { "CPI", "ED.A1.", 3, 0 }, + { "CPIR", "ED.B1.", 3, 0 }, + { "CPD", "ED.A9.", 3, 0 }, + { "CPDR", "ED.B9.", 3, 0 }, + { "ADD HL,d", "09f0.", 3, 0 }, + { "ADD IX,i", "DD.09f0.", 3, 0 }, + { "ADD IY,j", "FD.09f0.", 3, 0 }, + { "ADC HL,d", "ED.4Af0.", 3, 0 }, + { "SBC HL,d", "ED.42f0.", 3, 0 }, + { "g A,b", "m080b0c1.", 3, 0 }, + { "g A,p", "DD.m080b0c1.", 1, 1 }, + { "g A,q", "FD.m080b0c1.", 1, 1 }, + { "g A,(HL)", "m086b0.", 3, 0 }, + { "g A,(ca)", "m0d1.86b0.d2.", 3, 0, "ii" }, + { "g A,a", "m0C6b0.d1.", 3, 0, "e8" }, + { "g b", "n080b0c1.", 3, 0 }, + { "g p", "DD.n080b0c1.", 1, 1 }, + { "g q", "FD.n080b0c1.", 1, 1 }, + { "g (HL)", "n086b0.", 3, 0 }, + { "g (ca)", "n0d1.86b0.d2.", 3, 0, "ii" }, + { "g a", "n0C6b0.d1.", 3, 0, "e8" }, + { "h b", "04b1c0.", 3, 0 }, + { "h p", "DD.04b1c0.", 1, 1 }, + { "h q", "FD.04b1c0.", 1, 1 }, + { "h (HL)", "34c0.", 3, 0 }, + { "h (ca)", "d1.34c0.d2.", 3, 0, "ii" }, + { "h (e)", "d1.34c0.00.", 3, 0, "ii" }, + { "INC d", "03f0.", 3, 0 }, + { "INC e", "d0.23.", 3, 0 }, + { "DEC d", "0Bf0.", 3, 0 }, + { "DEC e", "d0.2B.", 3, 0 }, + { "DAA", "27.", 3, 0 }, + { "CPL", "2F.", 3, 0 }, + { "NEG", "ED.44.", 3, 0 }, + { "CCF", "3F.", 3, 0 }, + { "SCF", "37.", 3, 0 }, + { "NOP", "00.", 3, 0 }, + { "HALT", "76.", 3, 0 }, + { "DI", "F3.", 3, 0 }, + { "EI", "FB.", 3, 0 }, + { "IM a", "ED.k0.", 3, 0, "tt" }, + { "RLCA", "07.", 3, 0 }, + { "RLA", "17.", 3, 0 }, + { "RRCA", "0F.", 3, 0 }, + { "RRA", "1F.", 3, 0 }, + { "SLL b", "CB.30c0.", 1, 1 }, + { "SLL (HL)", "CB.36.", 1, 1 }, + { "SLL (ca)", "d0.CB.d1.36.", 1, 1, "ii" }, + { "SLL (ca),b", "d0.CB.d1.30c2.", 1, 1, "ii" }, + { "k b", "CB.00b0c1.", 3, 0 }, + { "k (HL)", "CB.06b0.", 3, 0 }, + { "k (ca)", "d1.CB.d2.06b0.", 3, 0, "ii" }, + { "k (ca),b", "d1.CB.d2.00b0c3.", 1, 1, "ii" }, + { "RLD", "ED.6F.", 3, 0 }, + { "RRD", "ED.67.", 3, 0 }, + { "l a,b", "CB.00g0b1c2.", 3, 0, "b3" }, + { "l a,(HL)", "CB.06g0b1.", 3, 0, "b3" }, + { "l a,(ca)", "d2.CB.d3.06g0b1.", 3, 0, "b3ii" }, + { "RES a,(ca),b", "d1.CB.d2.80b0c3.", 1, 1, "b3ii" }, + { "SET a,(ca),b", "d1.CB.d2.C0b0c3.", 1, 1, "b3ii" }, + { "JP (HL)", "E9.", 3, 0 }, + { "JP (e)", "d0.E9.", 3, 0 }, + { "JP m,a", "C2b0.e1", 3, 0 }, + { "JP a", "C3.e0", 3, 0 }, + { "JR n,a", "20b0.i1.", 3, 0, "r8" }, + { "JR a", "18.i0.", 3, 0, "r8" }, + { "DJNZ a", "10.i0.", 3, 0, "r8" }, + { "CALL m,a", "C4b0.e1", 3, 0 }, + { "CALL a", "CD.e0", 3, 0 }, + { "RETI", "ED.4D.", 3, 0 }, + { "RETN", "ED.45.", 3, 0 }, + { "RET m", "C0b0.", 3, 0 }, + { "RET", "C9.", 3, 0 }, + { "RST a", "C7j0.", 3, 0, "ss" }, + { "IN b,(C)", "ED.40b0.", 3, 0 }, + { "IN A,(a)", "DB.d0.", 3, 0, "e8" }, + { "IN F,(a)", "ED.70.", 3, 0 }, + { "IN (C)", "ED.70.", 1, 1 }, + { "INI", "ED.A2.", 3, 0 }, + { "INIR", "ED.B2.", 3, 0 }, + { "IND", "ED.AA.", 3, 0 }, + { "INDR", "ED.BA.", 3, 0 }, + { "OUT (C),0", "ED.71.", 1, 1 }, + { "OUT (C),b", "ED.41b0.", 3, 0 }, + { "OUT (a),A", "D3.d0.", 3, 0, "e8" }, + { "OUTI", "ED.A3.", 3, 0 }, + { "OTIR", "ED.B3.", 3, 0 }, + { "OUTD", "ED.AB.", 3, 0 }, + { "OTDR", "ED.BB.", 3, 0 }, + /* hd64180 added instructions */ + { "IN0 b,(a)", "ED.00b0.d1.", 2, 0, "e8" }, + { "OUT0 (a),b", "ED.01b1.d0.", 2, 0, "e8" }, + { "OTDM", "ED.8B.", 2, 0 }, + { "OTDMR", "ED.9B.", 2, 0 }, + { "OTIM", "ED.83.", 2, 0 }, + { "OTIMR", "ED.93.", 2, 0 }, + { "MLT d", "ED.4Cf0.", 2, 0 }, + { "SLP", "ED.76.", 2, 0 }, + { "TST b", "ED.04b0.", 2, 0 }, + { "TST (HL)", "ED.34.", 2, 0 }, + { "TST a", "ED.64.d0.", 2, 0, "e8" }, + { "TSTIO a", "ED.74.d0.", 2, 0, "e8" }, + { NULL, NULL }, +}; + +static const char *const bval[] = { "B", "C", "D", "E", + "H", "L", "", "A", NULL }; +static const char *const cval[] = { "IX", "IY", NULL }; +static const char *const dval[] = { "BC", "DE", "HL", "SP", NULL }; +static const char *const fval[] = { "BC", "DE", "HL", "AF", NULL }; +static const char *const gval[] = { "ADD", "ADC", "SUB", "SBC", + "AND", "XOR", "OR", "CP", NULL }; +static const char *const hval[] = { "INC", "DEC", NULL }; +static const char *const ival[] = { "BC", "DE", "IX", "SP", NULL }; +static const char *const jval[] = { "BC", "DE", "IY", "SP", NULL }; +static const char *const kval[] = { "RLC", "RRC", "RL", "RR", + "SLA", "SRA", "", "SRL", NULL }; +static const char *const lval[] = { "", "BIT", "RES", "SET", NULL }; +static const char *const mval[] = { "NZ", "Z", "NC", "C", + "PO", "PE", "P", "M", NULL }; +static const char *const nval[] = { "NZ", "Z", "NC", "C", NULL }; +static const char *const pval[] = { "B", "C", "D", "E", + "IXH", "IXL", "", "A", NULL }; +static const char *const qval[] = { "B", "C", "D", "E", + "IYH", "IYL", "", "A", NULL }; +static const char *const nullv[] = { NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, dval, fval, + gval, hval, ival, jval, kval, + lval, mval, nval, nullv, pval, + qval +}; + +static int indval(const char *p, int disp, const char **q) +{ + int v; + const char *r; + + v = mreg(p, cval, &r); + if (v >= 0) { + v = (v == 0) ? 0xDD : 0xFD; + while (*r == ' ') r++; + if (!disp || *r == '+' || *r == '-') { + *q = r; + return v; + } + } + return -1; +} + +static int match_z80(char c, const char *p, const char **q) +{ + int v; + + if (c == 'c' || c == 'e') { + v = indval(p, c == 'c', q); + } else if (c <= 'q') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_z80(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b |= (vs[i] << 4); break; + case 'g': b |= (vs[i] << 6); break; + case 'i': b = (vs[i] - savepc - 2); break; + case 'j': if (s_pass > 0 && (vs[i] & ~56) != 0) { + eprint(_("invalid RST argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= vs[i]; + break; + case 'k': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 2)) { + eprint(_("invalid IM argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b = 0x46; + if (vs[i] == 1) + b = 0x56; + else if (vs[i] == 2) + b = 0x5E; + break; + case 'm': if (s_pass == 0 && !s_extended_op) { + if (vs[i] != 0 && vs[i] != 1 && vs[i] != 3) { + eprint(_("unofficial syntax\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } + break; + case 'n': if (s_pass == 0 && !s_extended_op) { + if (vs[i] == 0 || vs[i] == 1 || vs[i] == 3) { + eprint(_("unofficial syntax\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_z80(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_z80(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'q') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_z80 = { + .id = "z80", + .descr = "Zilog Z80", + .matcht = s_matchtab_z80, + .matchf = match_z80, + .genf = gen_z80, + .pat_char_rewind = pat_char_rewind_z80, + .pat_next_str = pat_next_str_z80, + .mask = 1 +}; + +const struct target s_target_hd64180 = { + .id = "hd64180", + .descr = "Hitachi HD64180", + .matcht = s_matchtab_z80, + .matchf = match_z80, + .genf = gen_z80, + .pat_char_rewind = pat_char_rewind_z80, + .pat_next_str = pat_next_str_z80, + .mask = 2 +}; diff --git a/Tools/unix/zx/Makefile b/Tools/unix/zx/Makefile new file mode 100644 index 00000000..995d1243 --- /dev/null +++ b/Tools/unix/zx/Makefile @@ -0,0 +1,37 @@ +# +# hacked up brute force makefile for linux and osx +# +UNAME := $(shell uname) +ifeq ($(UNAME), Linux) + SUFFIX=linux +endif +ifeq ($(UNAME), Darwin) + SUFFIX=darwin +endif + +DEST = ../../$(UNAME) +CFLAGS = -g # -DDEBUG + +OBJECTS = zx.o cpmdrv.o cpmglob.o cpmparse.o cpmredir.o \ + drdos.o util.o xlt.o zxbdos.o zxcbdos.o zxdbdos.o z80.o +UNUSED = dirent.o + +all: zx $(DEST) + cp bios.bin zx $(DEST) + +$(DEST): + mkdir -p $(DEST) + +clean: + -rm -f $(OBJECTS) config.h + +clobber: clean + -rm -f $(DEST)/zx $(DEST)/bios.bin zx + +$(OBJECTS): config.h + +zx: $(OBJECTS) + $(CC) -o zx $(OBJECTS) + +config.h: config.h.$(SUFFIX) + cp config.h.$(SUFFIX) config.h diff --git a/Tools/unix/zx/bios.bin b/Tools/unix/zx/bios.bin new file mode 100644 index 00000000..b20e62d9 Binary files /dev/null and b/Tools/unix/zx/bios.bin differ diff --git a/Tools/unix/zx/cbops.h b/Tools/unix/zx/cbops.h new file mode 100644 index 00000000..47b69da0 --- /dev/null +++ b/Tools/unix/zx/cbops.h @@ -0,0 +1,172 @@ +/* Emulations of the CB operations of the Z80 instruction set. + * Copyright (C) 1994 Ian Collier. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define var_t unsigned char t +#define rlc(x) (x=(x<<1)|(x>>7),rflags(x,x&1)) +#define rrc(x) do{var_t=x&1;x=(x>>1)|(t<<7);rflags(x,t);}while(0) +#define rl(x) do{var_t=x>>7;x=(x<<1)|(f&1);rflags(x,t);}while(0) +#define rr(x) do{var_t=x&1;x=(x>>1)|(f<<7);rflags(x,t);}while(0) +#define sla(x) do{var_t=x>>7;x<<=1;rflags(x,t);}while(0) +#define sra(x) do{var_t=x&1;x=((signed char)x)>>1;rflags(x,t);}while(0) +#define sll(x) do{var_t=x>>7;x=(x<<1)|1;rflags(x,t);}while(0) +#define srl(x) do{var_t=x&1;x>>=1;rflags(x,t);}while(0) + +#define rflags(x,c) (f=(c)|(x&0xa8)|((!x)<<6)|parity(x)) + +#define bit(n,x) (f=(f&1)|((x&(1<>3)&7; + switch(op&0xc7){ + case 0x40: bit(n,b); break; + case 0x41: bit(n,c); break; + case 0x42: bit(n,d); break; + case 0x43: bit(n,e); break; + case 0x44: bit(n,h); break; + case 0x45: bit(n,l); break; + case 0x46: tstates+=4;val=fetch(addr);bit(n,val);store(addr,val);break; + case 0x47: bit(n,a); break; + case 0x80: res(n,b); break; + case 0x81: res(n,c); break; + case 0x82: res(n,d); break; + case 0x83: res(n,e); break; + case 0x84: res(n,h); break; + case 0x85: res(n,l); break; + case 0x86: tstates+=4;val=fetch(addr);res(n,val);store(addr,val);break; + case 0x87: res(n,a); break; + case 0xc0: set(n,b); break; + case 0xc1: set(n,c); break; + case 0xc2: set(n,d); break; + case 0xc3: set(n,e); break; + case 0xc4: set(n,h); break; + case 0xc5: set(n,l); break; + case 0xc6: tstates+=4;val=fetch(addr);set(n,val);store(addr,val);break; + case 0xc7: set(n,a); break; + } + } + if(ixoriy)switch(reg){ + case 0:b=val; break; + case 1:c=val; break; + case 2:d=val; break; + case 3:e=val; break; + case 4:h=val; break; + case 5:l=val; break; + case 7:a=val; break; + } +} + +#undef var_t +#undef rlc +#undef rrc +#undef rl +#undef rr +#undef sla +#undef sra +#undef sll +#undef srl +#undef rflags +#undef bit +#undef set +#undef res diff --git a/Tools/unix/zx/config.h.darwin b/Tools/unix/zx/config.h.darwin new file mode 100644 index 00000000..cc4b425c --- /dev/null +++ b/Tools/unix/zx/config.h.darwin @@ -0,0 +1,16 @@ +//#define HAVE_WINDOWS_H +#define HAVE_DIRENT_H +#define HAVE_UTIME_H +#define HAVE_FCNTL_H +#define HAVE_UNISTD_H +#define BINDIR80 getenv("ZXBINDIR") +#define LIBDIR80 getenv("ZXLIBDIR") +#define INCDIR80 getenv("ZXINCDIR") +#define DARWIN +#include +#include +#define _S_IFDIR S_IFDIR +#define strcmpi(a,b) strcasecmp(a,b) +//#define WIN32 +//#define WINVER 0x0501 // target Windows XP +//#define _WIN32_WINNNT 0x0501 // target Windows XP diff --git a/Tools/unix/zx/config.h.linux b/Tools/unix/zx/config.h.linux new file mode 100644 index 00000000..a015591c --- /dev/null +++ b/Tools/unix/zx/config.h.linux @@ -0,0 +1,17 @@ +//#define HAVE_WINDOWS_H +#define HAVE_DIRENT_H +#define HAVE_UTIME_H +#define HAVE_FCNTL_H +#define HAVE_SYS_VFS_H +#define HAVE_UNISTD_H +#define BINDIR80 getenv("ZXBINDIR") +#define LIBDIR80 getenv("ZXLIBDIR") +#define INCDIR80 getenv("ZXINCDIR") +#define LINUX +#include +#include +#define _S_IFDIR S_IFDIR +#define strcmpi(a,b) strcasecmp(a,b) +//#define WIN32 +//#define WINVER 0x0501 // target Windows XP +//#define _WIN32_WINNNT 0x0501 // target Windows XP diff --git a/Tools/unix/zx/config.h.windows b/Tools/unix/zx/config.h.windows new file mode 100644 index 00000000..8186c485 --- /dev/null +++ b/Tools/unix/zx/config.h.windows @@ -0,0 +1,9 @@ +#define HAVE_WINDOWS_H +//#define HAVE_DIRENT_H +#define HAVE_FCNTL_H +#define BINDIR80 getenv("ZXBINDIR") +#define LIBDIR80 getenv("ZXLIBDIR") +#define INCDIR80 getenv("ZXINCDIR") +#define WIN32 +#define WINVER 0x0501 // target Windows XP +#define _WIN32_WINNNT 0x0501 // target Windows XP diff --git a/Tools/unix/zx/cpm/bios.bin b/Tools/unix/zx/cpm/bios.bin new file mode 100644 index 00000000..b20e62d9 Binary files /dev/null and b/Tools/unix/zx/cpm/bios.bin differ diff --git a/Tools/unix/zx/cpm/bios.com b/Tools/unix/zx/cpm/bios.com new file mode 100644 index 00000000..b20e62d9 Binary files /dev/null and b/Tools/unix/zx/cpm/bios.com differ diff --git a/Tools/unix/zx/cpm/bios.lst b/Tools/unix/zx/cpm/bios.lst new file mode 100644 index 00000000..d3e97405 --- /dev/null +++ b/Tools/unix/zx/cpm/bios.lst @@ -0,0 +1,106 @@ +Z80ASM SuperFast Relocating Macro Assembler Z80ASM 1.30 Page 1 +BIOS Z80 + + 1 ; BIOS / BDOS for the ZXCC environment. + 2 ; + 3 FE00 org 0FE00h + 4 FE00 5A 58 43 43 DEFB 'ZXCC04' ;Serial number + 5 ; + 6 ; Some CP/M programs expect a jump at the start of BDOS, so here it is. + 7 ; + 8 FE06 C3 FE09 BDOS0: JP BDOS1 + 9 + 10 FE09 3E C0 BDOS1: LD A,0C0h + 11 FE0B ED FE DEFB 0EDh,0FEh + 12 FE0D C9 RET + 13 ; + 14 ; Hack for ZMAC. ZMAC is using contents of 0FE22H to establish a memory pointer. + 15 ; It makes no sense. We stuff 04F4CH here because it is known to work... + 16 ; + 17 FE22 org 0FE22H + 18 FE22 4F4C DEFW 04F4CH + 19 ; + 20 ;This is not a real BIOS, so let its code live below the BIOS jumpblock. + 21 ; + 22 FE24 DD 22 FE33 UBIOS: LD (XIX),IX + 23 FE28 DD E1 POP IX ;IX = address of UBIOS function + 24 FE2A 3E C3 LD A,0C3h ;C3h = BIOS call + 25 FE2C ED FE DEFB 0EDh,0FEh + 26 FE2E DD 2A FE33 LD IX,(XIX) + 27 FE32 C9 RET + 28 ; + 29 FE33 0000 XIX: DEFW 0 + 30 + 31 FE35 21 FF03 CBOOT: LD HL,WBOOT0 + 32 FE38 22 0001 LD (1),HL + 33 FE3B 21 FE06 LD HL,BDOS0 + 34 FE3E 22 0006 LD (6),HL + 35 FE41 3E C3 LD A,0C3h + 36 FE43 32 0000 LD (0),A + 37 FE46 32 0005 LD (5),A + 38 FE49 3E C9 LD A,0C9h + 39 FE4B 32 0038 LD (038h),A + 40 FE4E 3E C1 LD A,0C1h ;C1h = program load + 41 FE50 ED FE DEFB 0EDh,0FEh + 42 FE52 21 0000 LD HL,0 + 43 FE55 E5 PUSH HL ;In case called program tries to RET + 44 FE56 C3 0100 JP 0100h + 45 ; + 46 FE59 3E C3 WBOOT: LD A,0C3h ;Program termination + 47 FE5B DD 21 0006 LD IX,6 ;BIOS call 1 + 48 FE5F ED FE DEFB 0EDh,0FEh + 49 FE61 76 HALT + 50 FE62 C3 FE62 JP $ + 51 ; + 52 FEEC org 0FEECh + 53 FEEC FF tmpdrv: defb 0FFh ;Temp drive = current + 54 + 55 + 56 ; + 57 ;TODO: SCB at FE9Ch + 58 ; + Z80ASM SuperFast Relocating Macro Assembler Z80ASM 1.30 Page 2 +BIOS Z80 + + 59 FF00 org 0FF00h + 60 FF00 C3 FE35 JP CBOOT ;FF00 + 61 FF03 C3 FE59 WBOOT0: JP WBOOT ;03 + 62 FF06 CD FE24 CALL UBIOS ;06 + 63 FF09 CD FE24 CALL UBIOS ;09 + 64 FF0C CD FE24 CALL UBIOS ;0C + 65 FF0F CD FE24 CALL UBIOS ;0F + 66 FF12 CD FE24 CALL UBIOS ;12 + 67 FF15 CD FE24 CALL UBIOS ;15 + 68 FF18 CD FE24 CALL UBIOS ;18 + 69 FF1B CD FE24 CALL UBIOS ;1B + 70 FF1E CD FE24 CALL UBIOS ;1E + 71 FF21 CD FE24 CALL UBIOS ;21 + 72 FF24 CD FE24 CALL UBIOS ;24 + 73 FF27 CD FE24 CALL UBIOS ;27 + 74 FF2A CD FE24 CALL UBIOS ;2A + 75 FF2D CD FE24 CALL UBIOS ;2D + 76 FF30 CD FE24 CALL UBIOS ;30 + 77 FF33 CD FE24 CALL UBIOS ;33 + 78 FF36 CD FE24 CALL UBIOS ;36 + 79 FF39 CD FE24 CALL UBIOS ;39 + 80 FF3C CD FE24 CALL UBIOS ;42 + 81 FF3F CD FE24 CALL UBIOS ;45 + 82 FF42 CD FE24 CALL UBIOS ;48 + 83 FF45 CD FE24 CALL UBIOS ;4B + 84 FF48 CD FE24 CALL UBIOS ;4E + 85 FF4B CD FE24 CALL UBIOS ;51 + 86 FF4E CD FE24 CALL UBIOS ;54 + 87 FF51 CD FE24 CALL UBIOS ;57 + 88 FF54 CD FE24 CALL UBIOS ;5A, USERF + 89 FF57 CD FE24 CALL UBIOS ;5D, RESERV1 + 90 FF5A CD FE24 CALL UBIOS ;60, RESERV2 + 91 ; + 92 FFC0 org 0FFC0h ;Space for DPB + 93 FFC0 0020 dpb: defs 20h + 94 + 95 END + 0 Error(s) Detected. + 480 Absolute Bytes. 9 Symbols Detected. +  UBIOS ;27 + 74 FF2A CD FE24 CALL UBIOS ;2A + 75 FF2D CD FE24 \ No newline at end of file diff --git a/Tools/unix/zx/cpm/bios.z80 b/Tools/unix/zx/cpm/bios.z80 new file mode 100644 index 00000000..fb64e891 --- /dev/null +++ b/Tools/unix/zx/cpm/bios.z80 @@ -0,0 +1,96 @@ +; BIOS / BDOS for the ZXCC environment. +; + org 0FE00h + DEFB 'ZXCC04' ;Serial number +; +; Some CP/M programs expect a jump at the start of BDOS, so here it is. +; +BDOS0: JP BDOS1 + +BDOS1: LD A,0C0h + DEFB 0EDh,0FEh + RET +; +; Hack for ZMAC. ZMAC is using contents of 0FE22H to establish a memory pointer. +; It makes no sense. We stuff 04F4CH here because it is known to work... +; + org 0FE22H + DEFW 04F4CH +; +;This is not a real BIOS, so let its code live below the BIOS jumpblock. +; +UBIOS: LD (XIX),IX + POP IX ;IX = address of UBIOS function + LD A,0C3h ;C3h = BIOS call + DEFB 0EDh,0FEh + LD IX,(XIX) + RET +; +XIX: DEFW 0 + +CBOOT: LD HL,WBOOT0 + LD (1),HL + LD HL,BDOS0 + LD (6),HL + LD A,0C3h + LD (0),A + LD (5),A + LD A,0C9h + LD (038h),A + LD A,0C1h ;C1h = program load + DEFB 0EDh,0FEh + LD HL,0 + PUSH HL ;In case called program tries to RET + JP 0100h +; +WBOOT: LD A,0C3h ;Program termination + LD IX,6 ;BIOS call 1 + DEFB 0EDh,0FEh + HALT + JP $ +; + org 0FEECh +tmpdrv: defb 0FFh ;Temp drive = current + + +; +;TODO: SCB at FE9Ch +; + org 0FF00h + JP CBOOT ;FF00 +WBOOT0: JP WBOOT ;03 + CALL UBIOS ;06 + CALL UBIOS ;09 + CALL UBIOS ;0C + CALL UBIOS ;0F + CALL UBIOS ;12 + CALL UBIOS ;15 + CALL UBIOS ;18 + CALL UBIOS ;1B + CALL UBIOS ;1E + CALL UBIOS ;21 + CALL UBIOS ;24 + CALL UBIOS ;27 + CALL UBIOS ;2A + CALL UBIOS ;2D + CALL UBIOS ;30 + CALL UBIOS ;33 + CALL UBIOS ;36 + CALL UBIOS ;39 + CALL UBIOS ;42 + CALL UBIOS ;45 + CALL UBIOS ;48 + CALL UBIOS ;4B + CALL UBIOS ;4E + CALL UBIOS ;51 + CALL UBIOS ;54 + CALL UBIOS ;57 + CALL UBIOS ;5A, USERF + CALL UBIOS ;5D, RESERV1 + CALL UBIOS ;60, RESERV2 +; + org 0FFC0h ;Space for DPB +dpb: defs 20h + + END + diff --git a/Tools/unix/zx/cpmdrv.c b/Tools/unix/zx/cpmdrv.c new file mode 100644 index 00000000..1faff21b --- /dev/null +++ b/Tools/unix/zx/cpmdrv.c @@ -0,0 +1,275 @@ +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998,2003 John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file deals with drive-based functions. +*/ + +#include "cpmint.h" + +#ifdef WIN32 + +static char *drive_to_hostdrive(int cpm_drive) +{ + static char prefix[CPM_MAXPATH]; + char *lpfp; + DWORD dw; + + if (!redir_drive_prefix[cpm_drive]) return NULL; + dw = GetFullPathName(redir_drive_prefix[cpm_drive], sizeof(prefix), + prefix, &lpfp); + + if (!dw) return NULL; + if (prefix[1] == ':') /* If path starts with a drive, limit it */ + { /* to just that drive */ + prefix[2] = '/'; + prefix[3] = 0; + } + return prefix; +} +#endif + + +cpm_byte fcb_reset(void) +{ +#ifdef __MSDOS__ + bdos(0x0D, 0, 0); +#endif + + redir_l_drives = 0; + redir_cpmdrive = 0; /* A reset forces current drive to A: */ +/* redir_ro_drives = 0; Software write protect not revoked by func 0Dh. + * + * This does not follow true CP/M, but does match many 3rd-party replacements. + */ + return 0; +} + + +cpm_word fcb_drive (cpm_byte drv) +{ + if (redir_drive_prefix[drv][0]) + { + redir_cpmdrive = drv; + redir_log_drv(drv); + return 0; + } + else return 0x04FF; /* Drive doesn't exist */ +} + +cpm_byte fcb_getdrv(void) +{ + return redir_cpmdrive; +} + + +cpm_byte fcb_user (cpm_byte usr) +{ + if (usr != 0xFF) redir_cpmuser = usr % 16; + + redir_Msg("User: parameter %d returns %d\r\n", usr, redir_cpmuser); + + return redir_cpmuser; +} + + + +cpm_word fcb_logvec(void) +{ + return redir_l_drives; +} + + +cpm_word fcb_rovec(void) +{ + return redir_ro_drives; +} + + +cpm_word fcb_rodisk(void) +{ + cpm_word mask = 1; + + if (redir_cpmdrive) mask = mask << redir_cpmdrive; + + redir_ro_drives |= mask; + return 0; +} + + +cpm_word fcb_resro(cpm_word bitmap) +{ + redir_ro_drives &= ~bitmap; + + return 0; +} + + +cpm_word fcb_sync(cpm_byte flag) +{ +#ifdef WIN32 + return 0; +#else + sync(); return 0; /* Apparently some sync()s are void not int */ +#endif +} + + +cpm_word fcb_purge() +{ +#ifdef WIN32 + return 0; +#else + sync(); return 0; /* Apparently some sync()s are void not int */ +#endif +} + + +static cpm_byte exdpb[0x11] = { + 0x80, 0, /* 128 records/track */ + 0x04, 0x0F, /* 2k blocks */ + 0x00, /* 16k / extent */ + 0xFF, 0x0F, /* 4095 blocks */ + 0xFF, 0x03, /* 1024 dir entries */ + 0xFF, 0xFF, /* 16 directory blocks */ + 0x00, 0x80, /* Non-removable media */ + 0x00, 0x00, /* No system tracks */ + 0x02, 0x03 /* 512-byte sectors */ +}; + +cpm_word fcb_getdpb(cpm_byte *dpb) +{ +#ifdef WIN32 + DWORD spc, bps, fc, tc; + unsigned bsh, blm, psh, phm; + char *hostd = drive_to_hostdrive(redir_cpmdrive); + + if (!hostd) return 0x01FF; /* Can't select */ + + if (!GetDiskFreeSpace(hostd, &spc, &bps, &fc, &tc)) + return 0x01FF; /* Can't select */ + + /* Store total clusters */ + //if (tc > 0x10000L) tc = 0x10000L; + if (tc > 0xFFFFL) tc = 0xFFFFL; + + psh = 0; phm = 0; + + while (bps > 128) /* Get sector size */ + { + bps /= 2; + psh++; + phm = (phm << 1) | 1; + } + bsh = psh; blm = phm; + while (spc > 1) /* Get cluster size */ + { + spc /= 2; + bsh++; + blm = (blm << 1) | 1; + } + + + exdpb[2] = bsh; + exdpb[3] = blm; + exdpb[5] = tc & 0xFF; + exdpb[6] = tc >> 8; + + exdpb[15] = psh; + exdpb[16] = phm; +#else + struct statfs buf; + cpm_word nfiles; + + /* Get DPB for redir_cpmdrive. Currently just returns a dummy. */ + if (!statfs(redir_drive_prefix[redir_cpmdrive], &buf)) + { + /* Store correct directory entry count */ + + if (buf.f_files >= 0x10000L) nfiles = 0xFFFF; + else nfiles = buf.f_files; + + exdpb[7] = nfiles & 0xFF; + exdpb[8] = nfiles >> 8; + } +#endif + + memcpy(dpb, &exdpb, 0x11); + return 0x11; +} + + +/* Create an entirely bogus ALV + * TODO: Make it a bit better */ + +cpm_word fcb_getalv(cpm_byte *alv, cpm_word max) +{ + if (max > 1024) max = 1024; + + memset(alv, 0xFF, max / 2); + memset(alv + (max / 2), 0, max / 2); + + return max; +} + +/* Get disk free space */ + +cpm_word fcb_dfree (cpm_byte drive, cpm_byte *dma) +{ +#ifdef WIN32 + DWORD spc, bps, fc, tc; + DWORD freerec; + char *hostd = drive_to_hostdrive(drive); + + if (!hostd) return 0x01FF; + if (!hostd) return 0x01FF; /* Can't select */ + + if (!GetDiskFreeSpace(hostd, &spc, &bps, &fc, &tc)) + return 0x01FF; /* Can't select */ + + freerec = fc; /* Free clusters */ + freerec *= spc; /* Free sectors */ + freerec *= (bps / 128); /* Free CP/M records */ + + /* Limit to maximum CP/M drive size */ + if (freerec > 4194303L) freerec = 4194303L; + redir_wr24(dma, freerec); + +#else + struct statfs buf; + long dfree; + + if (!redir_drive_prefix[drive]) return 0x01FF; /* Can't select */ + + if (statfs(redir_drive_prefix[drive], &buf)) return 0x01FF; + + dfree = (buf.f_bavail * (buf.f_bsize / 128)); + + if (dfree < buf.f_bavail || /* Calculation has wrapped round */ + dfree > 4194303L) /* Bigger than max CP/M drive size */ + { + dfree = 4194303L; + } + + redir_wr24(dma, dfree); +#endif + return 0; +} + + + diff --git a/Tools/unix/zx/cpmglob.c b/Tools/unix/zx/cpmglob.c new file mode 100644 index 00000000..20306e09 --- /dev/null +++ b/Tools/unix/zx/cpmglob.c @@ -0,0 +1,579 @@ +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file implements those BDOS functions that use wildcard expansion. +*/ + +#include "cpmint.h" + +static cpm_byte *find_fcb; +static int find_n; +static int find_ext = 0; +static int find_xfcb = 0; +static int entryno; +static cpm_byte lastdma[0x80]; +static long lastsize; +static char target_name[CPM_MAXPATH]; + +static char upper(char c) +{ + if (islower(c)) return toupper(c); + return c; +} + +/* + * we need to handle case sensitive filesystems correctly. + * underlying files need to just work, irrespective if the files + * in the native filesystem are mixed, upper or lower. + * the naive code in the distributed zx will not work everywhere. + */ + +/* Does the string "s" match the CP/M FCB? */ +/* pattern[0-10] will become a CP/M name parsed from "s" if it matches. */ +/* If 1st byte of FCB is '?' then anything matches. */ + +static int cpm_match(char *s, cpm_byte *fcb, cpm_byte *pattern) +{ + int n, m; + char *dotpos; + + m = strlen(s); + + /* + * let's cook the filename into something that we can match against + * the fcb. we reject any that can't be valid cp/m filenames, + * normalizing case as we go. all this goes into 'pattern' + */ + for (n = 0; n < 11; n++) pattern[n] = ' '; + + /* The name must have 1 or 0 dots */ + dotpos = strchr(s, '.'); + if (!dotpos) { /* No dot. The name must be at most 8 characters */ + if (m > 8) return 0; + for (n = 0; n < m; n++) { + pattern[n] = upper(s[n]) & 0x7F; + } + } else { /* at least one dot */ + if (strchr(dotpos + 1, '.')) { /* More than 1 dot */ + return 0; + } + if (dotpos == s) { /* Dot right at the beginning */ + return 0; + } + if ((dotpos - s) > 8) { /* "name" > 8 characters */ + return 0; + } + if (strlen(dotpos + 1) > 3) { /* "type" > 3 characters */ + return 0; + } + for (n = 0; n < (dotpos - s); n++) { /* copy filename portion */ + pattern[n] = upper(s[n]) & 0x7F; + } + m = strlen(dotpos + 1); + for (n = 0; n < m; n++) { /* copy extention portion */ + pattern[n + 8] = upper(dotpos[n + 1]) & 0x7F; + } + } + + /* + * handle special case where fcb[0] == '?' or fcb[0] & 0x80 + * this is used to return a full directory list on bdos's + */ + if (((fcb[0] & 0x7F) == '?') || (fcb[0] & 0x80)) { + return 1; + } + + for (n = 0; n < 11; n++) { + if (fcb[n+1] == '?') continue; + if ((pattern[n] & 0x7F) != (fcb[n+1] & 0x7F)) { + return 0; + } + } + return 1; /* Success! */ +} + + +/* Get the next entry from the host's directory matching "fcb" */ + +static struct dirent * next_entry(DIR *dir, cpm_byte *fcb, cpm_byte *pattern, + struct stat *st) +{ + struct dirent *en; + int unsatisfied; + int drv = fcb[0] & 0x7F; + + if (drv == '?') drv = 0; + if (!drv) drv = redir_cpmdrive; + else drv--; + + for (unsatisfied = 1; unsatisfied; ) + { + /* 1. Get the next entry */ + + en = readdir(dir); + if (!en) return NULL; /* No next entry */ + ++entryno; /* 0 for 1st, 1 for 2nd, etc. */ + + /* 2. See if it matches. We do this first (in preference to + seeing if it's a subdirectory first) because it doesn't + require disc access */ + if (!cpm_match(en->d_name, fcb, pattern)) + { + continue; + } + /* 3. Stat it, & reject it if it's a directory */ + + strcpy(target_name, redir_drive_prefix[drv]); + strcat(target_name, en->d_name); + + if (stat(target_name, st)) + { + redir_Msg("Can't stat %s so omitting it.\n", target_name); + continue; /* Can't stat */ + } + //if (S_ISDIR(st->st_mode)) + if ((st->st_mode) & _S_IFDIR) + { + /* Searching for files only */ + if (fcb[0] != '?' && fcb[0] < 0x80) + { + continue; + } + } + unsatisfied = 0; + } + return en; +} + + + +void volume_label(int drv, cpm_byte *dma) +{ + struct stat st; + + memset(dma, 0x20, 12); /* Volume label */ + + /* Get label name */ + redir_get_label(drv, (char *)(dma + 1)); + /* [0x0c] = label byte + * [0x0d] = password byte (=0) + * [0x10-0x17] = password + * [0x18] = label create date + * [0x1c] = label update date + */ +#ifdef __MSDOS__ + dma[0x0c] = 0x21; /* Under DOS, only "update" */ + if (redir_drdos) dma[0x0c] |= 0x80; /* Under DRDOS, passwords allowed */ +#else + dma[0x0c] = 0x61; /* Label exists and time stamps allowed */ +#endif /* (update & access) */ + dma[0x0d] = 0; /* Label not passworded */ + dma[0x0f] = 0x80; /* Non-CP/M media */ + + if (stat(redir_drive_prefix[drv], &st)) + { + redir_Msg("stat() fails on '%s'\n", redir_drive_prefix[drv]); + return; + } + + redir_wr32(dma + 0x18, redir_cpmtime(st.st_atime)); + redir_wr32(dma + 0x1C, redir_cpmtime(st.st_mtime)); +} + + + +cpm_word redir_find(int n, cpm_byte *fcb, cpm_byte *dma) +{ + DIR *hostdir; + int drv, attrib; + long recs; + struct stat st; + struct dirent *de; + cpm_word rights; + + drv = (fcb[0] & 0x7F); + if (!drv || drv == '?') drv = redir_cpmdrive; + else drv--; + + if (find_xfcb) /* Return another extent */ + { + memcpy(dma, lastdma, 0x80); + dma[0] |= 0x10; /* XFCB */ + dma[0x0c] = dma[0x69]; /* Password mode */ + dma[0x0d] = 0x0A; /* Password decode byte */ + memset(dma + 0x10, '*', 7); + dma[0x17] = ' '; /* Encoded password */ + memcpy(lastdma, dma, 0x80); + + find_xfcb = 0; + return 0; + } + + if (find_ext) /* Return another extent */ + { + memcpy(dma, lastdma, 0x80); + dma[0x0c]++; + if (dma[0x0c] == 0x20) + { + dma[0x0c] = 0; + dma[0x0e]++; + } + lastsize -= 0x4000; + recs = (lastsize + 127) / 128; + dma[0x0f] = (recs > 127) ? 0x80 : (recs & 0x7F); + + if (lastsize <= 0x4000) find_ext = 0; + memcpy(lastdma, dma, 0x80); + + return 0; + } + + + memset(dma, 0, 128); /* Zap the buffer */ + + /* + If returning all entries, return a volume label. + */ + if ((fcb[0] & 0x7F) == '?') + { + if (!n) + { + volume_label(drv, dma); + return 0; + } + else --n; + } + + /* Note: This implies that opendir() works on a filename with a + trailing slash. It does under Linux, but that's the only assurance + I can give. + */ + + entryno = -1; + hostdir = opendir(redir_drive_prefix[drv]); + + if (!hostdir) + { + redir_Msg("opendir() fails on '%s'\n", redir_drive_prefix[drv]); + return 0xFF; + } + /* We have a handle to the directory. */ + while (n >= 0) + { + de = next_entry(hostdir, fcb, dma + 1, &st); + if (!de) return 0xFF; + --n; + } + /* Valid entry found & statted. dma+1 holds filename. */ + + dma[0] = redir_cpmuser; /* Uid always matches */ + dma[0x0c] = 0; /* Extent counter, low */ + dma[0x0d] = st.st_size & 0x7F; /* Last record byte count */ + dma[0x0e] = 0; /* Extent counter, high */ + +#ifdef __MSDOS__ + _dos_getfileattr(target_name, (unsigned int *)&attrib); + rights = redir_drdos_get_rights(target_name); + if (rights && ((fcb[0] & 0x7F) == '?')) find_xfcb = 1; +#else + attrib = 0; + rights = 0; +#endif + if (attrib & 1) dma[9] |= 0x80; + if (attrib & 4) dma[10] |= 0x80; + if (!(attrib & 0x20)) dma[11] |= 0x80; + + + +/* TODO: Under Unix, work out correct RO setting */ + + recs = (st.st_size + 127) / 128; + dma[0x0f] = (recs > 127) ? 0x80 : (recs & 0x7F); + dma[0x10] = 0x80; + //if (S_ISDIR(st.st_mode)) dma[0x10] |= 0x40; + if ((st.st_mode) & _S_IFDIR) {dma[0x10] |= 0x40;} + if (attrib & 2) dma[0x10] |= 0x20; + dma[0x10] |= ((entryno & 0x1FFF) >> 8); + dma[0x11] = dma[0x10]; + dma[0x12] = entryno & 0xFF; + + redir_wr32(dma + 0x16, st.st_mtime); /* Modification time. */ + /* TODO: It should be in DOS */ + /* format */ + /* TODO: At 0x1A, 1st cluster */ + redir_wr32(dma + 0x1C, st.st_size); /* True size */ + + if (rights) /* Store password mode. Don't return an XFCB. */ + { + dma[0x69] = redir_cpm_pwmode(rights); + memcpy(lastdma, dma, 0x80); + } + + dma[0x60] = 0x21; /* XFCB */ + redir_wr32(dma + 0x61, redir_cpmtime(st.st_atime)); + redir_wr32(dma + 0x65, redir_cpmtime(st.st_mtime)); + + closedir(hostdir); + + if (st.st_size > 0x4000 && (fcb[0x0C] == '?')) /* All extents? */ + { + lastsize = st.st_size; + find_ext = 1; + memcpy(lastdma, dma, 0x80); + } + return 0; +} + + +#ifdef DEBUG +#define SHOWNAME(func) \ + { \ + char fname[CPM_MAXPATH]; \ + redir_fcb2unix(fcb, fname); \ + redir_Msg(func "(\"%s\")\n", fname); \ + } + +#else + #define SHOWNAME(func) +#endif + +cpm_word fcb_find1 (cpm_byte *fcb, cpm_byte *dma) /* 0x11 */ +{ +#ifdef DEBUG + int rv; +#endif + SHOWNAME("fcb_find1") + + redir_log_fcb(fcb); + + find_n = 0; + find_fcb = fcb; + find_ext = 0; + find_xfcb = 0; +#ifdef DEBUG + rv = redir_find(find_n, fcb, dma); + + if (rv < 4) + { + redir_Msg("Ret: %-11.11s\n", dma + 1); + } + else redir_Msg("Ret: Fail\n"); + return rv; +#else + return redir_find(find_n, find_fcb, dma); +#endif +} + +/* We don't bother with the FCB parameter - it's undocmented, and + * programs that do know about it will just pass in the same parameter + * that they did to function 0x11 */ + +cpm_word fcb_find2 (cpm_byte *fcb, cpm_byte *dma) /* 0x12 */ +{ +#ifdef DEBUG + int rv; + + char fname[CPM_MAXPATH]; + redir_fcb2unix(find_fcb, fname); + redir_Msg("fcb_find2(\"%s\") no. %d\n", fname, find_n); +#endif + ++find_n; +#ifdef DEBUG + rv = redir_find(find_n, find_fcb, dma); + + if (rv < 4) + { + redir_Msg("Ret: %-11.11s\n", dma + 1); + } + else redir_Msg("Ret: Fail\n"); + return rv; +#else + return redir_find(find_n, find_fcb, dma); +#endif +} + +/* Under CP/M, unlinking works with wildcards */ + +cpm_word fcb_unlink(cpm_byte *fcb, cpm_byte *dma) +{ + DIR *hostdir; + int drv; + struct dirent *de; + struct stat st; + int handle = 0; + int unpasswd = 0; + char fname[CPM_MAXPATH]; + + SHOWNAME("fcb_unlink") + + if (fcb[5] & 0x80) unpasswd = 1; /* Remove password rather than file */ + + redir_log_fcb(fcb); + + drv = (fcb[0] & 0x7F); + if (!drv || drv == '?') drv = redir_cpmdrive; + else drv--; + + if (redir_ro_drv(drv)) return 0x02FF; /* Error: R/O drive */ + +#ifdef DEBUG + redir_fcb2unix(fcb, fname); + redir_Msg("fcb_unlink(\"%s\")\n", fname); +#endif + + /* Note: This implies that opendir() works on a filename with a + trailing slash. It does under Linux, but that's the only assurance + I can give. + */ + + hostdir = opendir(redir_drive_prefix[drv]); + + if (!hostdir) + { + redir_Msg("opendir() fails on '%s'\n", redir_drive_prefix[drv]); + return 0xFF; + } + /* We have a handle to the directory. */ + do + { + de = next_entry(hostdir, fcb, (cpm_byte *)fname, &st); + if (de) + { + strcpy(target_name, redir_drive_prefix[drv]); + strcat(target_name, de->d_name); + redir_Msg("Deleting %s\n", de->d_name); + if (unpasswd) + { +#ifdef __MSDOS__ + if (redir_drdos) + { + handle = redir_drdos_put_rights (target_name, dma, 0); + } + else handle = 0; +#endif + } + else if (fcb[0] & 0x80) + { + handle = rmdir(target_name); + if (handle && redir_password_error()) + { + redir_password_append(target_name, dma); + handle = rmdir(target_name); + } + } + else + { + handle = unlink(target_name); + if (handle && redir_password_error()) + { + redir_password_append(target_name, dma); + handle = unlink(target_name); + } + } + + if (handle) de = NULL; /* Delete failed */ + } + } + while (de != NULL); + if (handle) + { + redir_Msg("Ret: -1\n"); + closedir(hostdir); + return 0xFF; + } + redir_Msg("Ret: 0\n"); + closedir(hostdir); + return 0; +} + + + + + +#ifdef __MSDOS__ +cpm_word redir_get_label(cpm_byte drv, char *pattern) +{ + char strs[10]; + struct ffblk fblk; + int done; + char *s; + int n; + + /* We need the drive prefix to be of the form "C:\etc..." */ + + memset(pattern, ' ', 11); + if (!redir_drive_prefix[drv][0] || redir_drive_prefix[drv][1] != ':') + return 0; + + sprintf(strs,"%c:/*.*", redir_drive_prefix[drv][0]); + + done = findfirst(strs, &fblk, FA_LABEL); + while (!done) + { + if ((fblk.ff_attrib & FA_LABEL) && + !(fblk.ff_attrib & (FA_SYSTEM | FA_HIDDEN))) + { + s = strchr(fblk.ff_name, '/'); + if (!s) s = strchr(fblk.ff_name, '\\'); + if (!s) s = strchr(fblk.ff_name, ':'); + if (!s) s = fblk.ff_name; + for (n = 0; n < 11; n++) + { + if (!(*s)) break; + if (*s == '.') + { + n = 7; + ++s; + continue; + } + pattern[n] = upper(*s); + ++s; + } + return 1; + } + done = findnext(&fblk); + } + return 0; +} +#else +cpm_word redir_get_label(cpm_byte drv, char *pattern) +{ + char *dname; + int l, n; + + memset(pattern, ' ', 11); + + dname = strrchr(redir_drive_prefix[drv], '/'); + if (dname) + { + ++dname; + l = strlen(dname); + if (l > 11) l = 11; + for (n = 0; n < l; n++) pattern[n] = upper(dname[l]); + } + else + { + pattern[0] = '.'; + } + return 0; +} + + + +#endif diff --git a/Tools/unix/zx/cpmint.h b/Tools/unix/zx/cpmint.h new file mode 100644 index 00000000..f0ec4237 --- /dev/null +++ b/Tools/unix/zx/cpmint.h @@ -0,0 +1,223 @@ +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file holds internal declarations for the library. +*/ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_DIRENT_H +# include +#else +#ifdef __WATCOMC__ +# include +# include +#else +# include "dirent.h" +#endif +#endif +#ifdef HAVE_NDIR_H +# include +#endif +#ifdef HAVE_SYS_DIR_H +# include +#endif +#ifdef HAVE_SYS_NDIR_H +# include +#endif +#ifdef HAVE_WINDOWS_H +# include +#endif +#ifdef HAVE_WINNT_H +# include +#endif +#ifdef HAVE_SYS_VFS_H +# include +#endif +#ifdef HAVE_UTIME_H +# include +#endif +#ifdef HAVE_FCNTL_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + + +#ifdef __MSDOS__ + #include + #include + #include + #ifdef __GO32__ + #include + #include + #include + #endif +#endif + +#define CASE_SENSITIVE_FILESYSTEM 0 + + +#include "cpmredir.h" + +typedef unsigned long dword; /* Must be at least 32 bits, and + >= sizeof(int) */ +#ifdef CPMDEF + #define EXT + #define INIT(x) =x +#else + #define EXT extern + #define INIT(x) +#endif + +/* The 16 directories to which the 16 CP/M drives are mapped */ + +EXT char redir_drive_prefix[16][CPM_MAXPATH]; + +/* Current drive and user */ + +EXT int redir_cpmdrive; +EXT int redir_cpmuser; + +/* Length of 1 read/write operation, bytes */ + +EXT int redir_rec_len INIT(128); + +/* Same, but in 128-byte records */ +EXT int redir_rec_multi INIT(1); + +/* Using a DRDOS system? */ +EXT int redir_drdos INIT(0); + +/* Default password */ +#ifdef __MSDOS__ +EXT char redir_passwd[8] INIT(""); +#endif + +EXT cpm_word redir_l_drives INIT(0); +EXT cpm_word redir_ro_drives INIT(0); + +#undef EXT +#undef INIT + + + +/* Convert FCB to a Unix filename, returning 1 if it's ambiguous */ +int redir_fcb2unix(cpm_byte *fcb, char *fname); + +/* Open FCB, set file attributes */ +int redir_ofile(cpm_byte * fcb, char *s); + +/* Check that the FCB we have is valid */ +int redir_verify_fcb(cpm_byte *fcb); + +#ifndef O_BINARY /* Necessary in DOS, not present in Linux */ +#define O_BINARY 0 +#endif + +/* Facilities for debug tracing */ + + +long zxlseek(int fd, long offset, int wh); + +#ifdef DEBUG + void redir_Msg(char *s, ...); + void redir_showfcb(cpm_byte *fcb); +#else + /* Warning: This is a GCC extension */ + #define redir_Msg(x, ...) + #define redir_showfcb(x) +#endif + + + +/* Get the "sequential access" file pointer out of an FCB */ + +long redir_get_fcb_pos(cpm_byte *fcb); + +/* Write "sequential access" pointer to FCB */ + +void redir_put_fcb_pos(cpm_byte *fcb, long npos); + +/* Convert time_t to CP/M day count/hours/minutes */ +dword redir_cpmtime(time_t t); +/* And back */ +time_t redir_unixtime(cpm_byte *c); + + +/* Functions to access 24-bit & 32-bit words in memory. These are always + little-endian. */ + +void redir_wr24(cpm_byte *addr, dword v); +void redir_wr32(cpm_byte *addr, dword v); +dword redir_rd24(cpm_byte *addr); +dword redir_rd32(cpm_byte *addr); + +/* If you have 64-bit file handles, you'll need to write separate wrhandle() + and rdhandle() routines */ +#define redir_wrhandle redir_wr32 +#define redir_rdhandle redir_rd32 + +/* Mark a drive as logged in */ + +void redir_log_drv(cpm_byte drv); +void redir_log_fcb(cpm_byte *fcb); + +/* Check if a drive is software read-only */ + +int redir_ro_drv(cpm_byte drv); +int redir_ro_fcb(cpm_byte *fcb); + +/* Translate errno to a CP/M error */ + +cpm_word redir_xlt_err(void); + +/* Get disc label */ +cpm_word redir_get_label(cpm_byte drv, char *pattern); + + +/* DRDOS set/get access rights - no-ops under MSDOS and Unix: + * + * CP/M password mode -> DRDOS password mode */ +cpm_word redir_drdos_pwmode(cpm_byte b); + +/* DRDOS password mode to CP/M password mode */ +cpm_byte redir_cpm_pwmode(cpm_word w); + +/* Get DRDOS access rights for a file */ +cpm_word redir_drdos_get_rights(char *path); + +/* Set DRDOS access rights and/or password */ +cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights); + +/* Was the last error caused by invalid password? */ +cpm_word redir_password_error(void); + +/* Append password to filename (FILE.TYP -> FILE.TYP;PASSWORD) */ +void redir_password_append(char *s, cpm_byte *dma); + diff --git a/Tools/unix/zx/cpmparse.c b/Tools/unix/zx/cpmparse.c new file mode 100644 index 00000000..4c709ec1 --- /dev/null +++ b/Tools/unix/zx/cpmparse.c @@ -0,0 +1,126 @@ +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file parses filenames to FCBs. +*/ + +#include "cpmint.h" + +#define is_num(c) ((c >= '0') && (c <= '9')) + +static int parse_drive_user(char *txt, cpm_byte *fcb) +{ + char uid[4], drvid[4]; + int up, dp; + + for (up = dp = 0; *txt != ':'; ++txt) + { + if (is_num (*txt)) uid [up++] = *txt; + if (isalpha(*txt)) drvid[dp++] = *txt; + if (!is_num(*txt) && !isalpha(*txt)) return -1; + } + uid[up] = 0; drvid[dp] = 0; + + if (dp > 1) return -1; /* Invalid driveletter */ + if (up > 2) return -1; /* Invalid uid */ + + fcb[0x0d] = atoi(uid) + 1; if (fcb[0x0d] > 16) return -1; + + if (islower(drvid[0])) drvid[0] = toupper(drvid[0]); + + if (drvid[0] < 'A' || drvid[0] > 'P') return -1; + + fcb[0] = drvid[0] - '@'; + return 0; +} + + + +cpm_word fcb_parse(char *txt, cpm_byte *fcb) +{ + int nl = 0, tl = 0, pl = 0, phase = 0; + char *ntxt, ch; + + memset(fcb, 0, 0x24); + + if (txt[1] == ':' || txt[2] == ':' || txt[3] == ':') + { + if (parse_drive_user(txt, fcb)) return 0xFFFF; + /* Move past the colon */ + ntxt = strchr(txt, ':') + 1; + } + else ntxt = txt; + while (phase < 3) + { + ch = *ntxt; + if (islower(ch)) ch = toupper(ch); + + switch(ch) + { + case 0: + case '\r': /* EOL */ + phase = 4; + break; + + case '.': /* file.typ */ + if (!phase) ++phase; + else phase = 3; + break; + + case ';': /* Password */ + if (phase < 2) phase = 2; + else phase = 3; + break; + + case '[': case ']': case '=': case 9: case ' ': + case '>': case '<': case ':': case ',': case '/': + case '|': /* Terminator */ + phase = 3; + + default: + switch(phase) + { + case 0: + if (nl >= 8) return 0xFFFF; + fcb[++nl] = ch; + break; + + case 1: + if (tl >= 3) return 0xFFFF; + fcb[tl + 9] = ch; + ++tl; + break; + + case 2: + if (pl >= 8) return 0xFFFF; + fcb[pl + 0x10] = ch; + ++pl; + break; + } + break; + } + } + if (!nl) return 0xFFFF; + + fcb[0x1A] = pl; + + if (phase == 4) return 0; + + return ntxt - txt; +} diff --git a/Tools/unix/zx/cpmredir.c b/Tools/unix/zx/cpmredir.c new file mode 100644 index 00000000..4fd5f9ca --- /dev/null +++ b/Tools/unix/zx/cpmredir.c @@ -0,0 +1,931 @@ +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* This file handles actual reading and writing */ + +#define CPMDEF +#include "cpmint.h" + +#ifdef DEBUG +#define SHOWNAME(func) \ + { \ + char fname[CPM_MAXPATH]; \ + redir_fcb2unix(fcb, fname); \ + redir_Msg(func "(\"%s\")\n", fname); \ + } + +#else + #define SHOWNAME(func) +#endif + + +/* DISK BDOS FUNCTIONS */ + +/* General treatment: + * + * We use the "disk block number" fields in the FCB to store our file handle; + * this is a similar trick to that used by DOSPLUS, which stores its cluster + * number in there. It works if: + * + * a) sizeof(int) <= 8 bytes (64 bits). If it's more, this needs rewriting + * to use a hash table; + * b) the program never touches these bytes. Practically no CP/M program does. + * + * We store a "magic number" (0x00FD) in the first two bytes of this field, and + * if the number has been changed then we abort. + * + * nb: Since I wrote ZXCC, I have found that DOSPLUS uses 0x8080 as a magic + * number [well, actually this is an oversimplification, but a hypothetical + * program written against DOSPLUS would work with 0x8080]. Perhaps 0x8080 + * should be used instead. + * + * Format of the field: + * + * [--2 bytes--] magic number + * [--8 bytes--] file handle. 8 bytes reserved but only 4 currently used. + * [--2 bytes--] reserved. + * [--4 bytes--] file length. + */ +#define MAGIC_OFFSET 0x10 +#define HANDLE_OFFSET 0x12 +#define LENGTH_OFFSET 0x1C + +cpm_word fcb_open(cpm_byte *fcb, cpm_byte *dma) +{ + char fname[CPM_MAXPATH]; + int handle; + int drv, l; + char *s; + DIR *dir; + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) return 0x09FF; + + redir_log_fcb(fcb); + + drv = fcb[0] & 0x7F; + if (!drv) drv = redir_cpmdrive; else --drv; + + if (fcb[0] & 0x80) /* Open directory */ + { + if (fcb[0x0C]) return 0x0BFF; /* Can't assign "floating" dir */ + + if (!memcmp(fcb + 1, ". ", 11)) + { + return 0; /* Opening "." */ + } + if (!memcmp(fcb + 1, ".. ", 11)) + { + l = strlen(redir_drive_prefix[drv]) - 1; + s = redir_drive_prefix[drv]; + --l; + while (l > 0) + { + if (s[l] == '/') break; +#ifdef __MSDOS__ + if (s[l] == '\\') break; + if (s[l] == ':') break; +#endif + --l; + } +#ifdef __MSDOS__ + if (l < 2) return 0; /* "C:" */ +#else + if (l <= 0) return 0; /* "/" */ +#endif + ++l; + s[l] = 0; + return 0; + } +/* Opening some other directory */ + + dir = opendir(fname); + if (!dir) return 0xFF; /* Not a directory */ + closedir(dir); + strcpy(redir_drive_prefix[drv], fname); + strcat(redir_drive_prefix[drv], "/"); + return 0; + } + + /* Note: Some programs (MAC is an example) don't close a file + * if they opened it just to do reading. MAC then reopens the + * file (which rewinds it); this causes FCB leaks under some + * DOS-based emulators */ + + handle = redir_ofile(fcb, fname); + redir_Msg("fcb_open(\"%s\")\r\n", fname); + if (handle < 0 && redir_password_error()) + { + redir_Msg("1st chance open failed on %s\r\n", fname); + redir_password_append(fname, dma); + redir_Msg("Trying with %s\r\n", fname); + handle = redir_ofile(fcb, fname); + } + + + if (handle == -1) + { + redir_Msg("Ret: -1\n"); + if (redir_password_error()) return 0x7FF; + return 0xFF; + } + fcb[MAGIC_OFFSET ] = 0xFD; /* "Magic number" */ + fcb[MAGIC_OFFSET + 1] = 0x00; + +/* TODO: Should the magic number perhaps be 0x8080, as in DOSPLUS? */ + + redir_wrhandle(fcb + HANDLE_OFFSET, handle); + + redir_put_fcb_pos(fcb, fcb[0x0C] * 16384); + /* (v1.01) "seek" to beginning of extent, not file. + * This is necessary for the awful I/O code + * in LINK-80 to work + */ + + /* Get the file length */ + redir_wr32(fcb + 0x1C, zxlseek(handle, 0, SEEK_END)); + zxlseek(handle, 0, SEEK_SET); + + /* Set the last record byte count */ + if (fcb[0x20] == 0xFF) fcb[0x20] = fcb[0x1C] & 0x7F; + + redir_Msg("Ret: 0\n"); + + return 0; +} + + +cpm_word fcb_close(cpm_byte *fcb) +{ + int handle, drv; + + SHOWNAME("fcb_close") + + if ((handle = redir_verify_fcb(fcb)) < 0) return -1; + redir_Msg(" (at %lx)\n", zxlseek(handle, 0, SEEK_CUR)); + + if (fcb[0] & 0x80) /* Close directory */ + { + drv = fcb[0] & 0x7F; + if (!drv) drv = redir_cpmdrive; else drv--; +#ifdef __MSDOS__ + strcpy(redir_drive_prefix[drv] + 1, ":/"); +#else + strcpy(redir_drive_prefix[drv], "/"); +#endif + return 0; + } + + if (fcb[5] & 0x80) /* CP/M 3: Flush rather than close */ + { +#ifndef WIN32 + sync(); +#endif + return 0; + } + +#ifdef WIN32 + { + BOOL b; + redir_Msg(">CloseHandle() Handle=%lu\n", handle); + b = CloseHandle((HANDLE)handle); + redir_Msg("80h, let it be 80h + +*/ + + +cpm_word fcb_read(cpm_byte *fcb, cpm_byte *dma) +{ + int handle; + int rv, n, rd_len; + long npos; + + SHOWNAME("fcb_read") + + if ((handle = redir_verify_fcb(fcb)) < 0) return 9; /* Invalid FCB */ + + /* The program may have mucked about with the counters, so + * do an lseek() to where it should be. */ + + npos = redir_get_fcb_pos(fcb); + zxlseek(handle, npos, SEEK_SET); + redir_Msg(" (from %lx)\n", zxlseek(handle, 0, SEEK_CUR)); + + /* Read in the required amount */ + +#ifdef WIN32 + { + BOOL b; + redir_Msg(">ReadFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, dma, redir_rec_len); + b = ReadFile((HANDLE)handle, dma, redir_rec_len, (unsigned long *)(&rv), NULL); + redir_Msg("WriteFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, dma, redir_rec_len); + b = WriteFile((HANDLE)handle, dma, redir_rec_len, (unsigned long *)(&rv), NULL); + redir_Msg("CreateFile([CREATE_ALWAYS]) Name='%s'\n", fname); + handle = (int)CreateFile(fname, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + redir_Msg("ReadFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, dma, redir_rec_len); + b = ReadFile((HANDLE)handle, dma, redir_rec_len, (unsigned long *)(&rv), NULL); + redir_Msg("WriteFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, dma, redir_rec_len); + b = WriteFile((HANDLE)handle, dma, redir_rec_len, (unsigned long *)(&rv), NULL); + redir_Msg("WriteFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, zerorec, rl); + b = WriteFile((HANDLE)handle, zerorec, rl, (unsigned long *)(&rv), NULL); + redir_Msg("= 0) len += rv; + + if (rv < rl) + { + redir_wr32(fcb + LENGTH_OFFSET, len); + return redir_xlt_err(); + } + } + redir_wr32(fcb + LENGTH_OFFSET, offs); + + return fcb_randwr(fcb, dma); +} + +cpm_word fcb_tell(cpm_byte *fcb) +{ + int handle; + off_t rv; + + SHOWNAME("fcb_tell") + + if ((handle = redir_verify_fcb(fcb)) < 0) return 9; /* Invalid FCB */ + + rv = zxlseek(handle, 0, SEEK_CUR); + + if (rv < 0) return 0xFF; + + rv = rv >> 7; + fcb[0x21] = rv & 0xFF; + fcb[0x22] = (rv >> 8) & 0xFF; + fcb[0x23] = (rv >> 16) & 0xFF; + return 0; +} + + +cpm_word fcb_stat(cpm_byte *fcb) +{ + char fname[CPM_MAXPATH]; + struct stat st; + int rv; + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) return 0x09FF; + + rv = stat(fname, &st); + + redir_Msg("fcb_stat(\"%s\") fcb=%x\n", fname, (int)fcb); + if (rv < 0) + { + redir_Msg("ret: -1\n"); + return 0xFF; + } + + redir_wr24(fcb + 0x21, (st.st_size + 127) / 128); + + redir_Msg("ret: 0"); + return 0; +} + + +cpm_word fcb_multirec(cpm_byte rc) +{ + if (rc < 1 || rc > 128) return 0xFF; + + redir_rec_multi = rc; + redir_rec_len = 128 * rc; + redir_Msg("Set read/write to %d bytes\n", redir_rec_len); + return 0; +} + + +cpm_word fcb_date(cpm_byte *fcb) +{ + char fname[CPM_MAXPATH]; + struct stat st; + int rv; + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) return 0x09FF; + + rv = stat(fname, &st); + + redir_Msg("fcb_stat(\"%s\")\n", fname); + if (rv < 0) return 0xFF; + + redir_wr32(fcb + 0x18, redir_cpmtime(st.st_atime)); + redir_wr32(fcb + 0x1C, redir_cpmtime(st.st_ctime)); + + fcb[0x0C] = redir_cpm_pwmode(redir_drdos_get_rights(fname)); + return 0; +} + + + +cpm_word fcb_trunc(cpm_byte *fcb, cpm_byte *dma) +{ + char fname[CPM_MAXPATH]; + dword offs = redir_rd24(fcb + 0x21) * 128; + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) return 0x09FF; + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) return 0x02FF; + + redir_log_fcb(fcb); +#ifdef WIN32 + (void)offs; + return 0x06FF; /* Simply not implemented */ +#else + if (truncate(fname, offs)) + { + if (redir_password_error()) + { + redir_password_append(fname, dma); + if (!truncate(fname, offs)) return 0; + } + return redir_xlt_err(); + } + return 0; +#endif +} + + +cpm_word fcb_sdate(cpm_byte *fcb, cpm_byte *dma) +{ + char fname[CPM_MAXPATH]; +#ifdef WIN32 + /* TODO: Use SetFileTime() here */ + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) return 0x09FF; + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) return 0x02FF; + + redir_log_fcb(fcb); +#else + struct utimbuf buf; + + buf.actime = redir_unixtime(dma); + buf.modtime = redir_unixtime(dma + 4); + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) return 0x09FF; + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) return 0x02FF; + + redir_log_fcb(fcb); + + if (utime(fname, &buf)) + { + if (redir_password_error()) + { + redir_password_append(fname, dma); + if (!utime(fname, &buf)) return 0; + } + return redir_xlt_err(); + } +#endif + return 0; +} + + + +cpm_word fcb_chmod(cpm_byte *fcb, cpm_byte *dma) +{ + char fname[CPM_MAXPATH]; + struct stat st; + int handle, wlen, omode; + long offs, newoffs; + cpm_byte zero[128]; + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) return 0x09FF; + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) return 0x02FF; + + redir_log_fcb(fcb); + + if (stat(fname, &st)) return redir_xlt_err(); + +#ifdef __MSDOS__ + omode = 0; + if (fcb[9] & 0x80) omode |= 1; + if (fcb[10] & 0x80) omode |= 4; + if (!(fcb[11] & 0x80)) omode |= 0x20; + + if (_chmod(fname, 1, omode) < 0) + { + if (redir_password_error()) + { + redir_password_append(fname, dma); + if (_chmod(fname, 1, omode) >= 0) return 0; + } + return redir_xlt_err(); + } +#elif defined (WIN32) + omode = 0; + + if (fcb[9] & 0x80) omode |= FILE_ATTRIBUTE_READONLY; + if (fcb[10] & 0x80) omode |= FILE_ATTRIBUTE_SYSTEM; + if (!(fcb[11] & 0x80)) omode |= FILE_ATTRIBUTE_ARCHIVE; + + if (!omode) omode = FILE_ATTRIBUTE_NORMAL; + + SetFileAttributes(fname, omode); +#else + omode = st.st_mode; + if (fcb[9] & 0x80) /* Read-only */ + { + st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + } + else st.st_mode |= S_IWUSR; + + if (omode != st.st_mode) + { + if (chmod(fname, st.st_mode)) return redir_xlt_err(); + } + +#endif + + if (fcb[6] & 0x80) /* Set exact size */ + { + if (stat(fname, &st)) return redir_xlt_err(); + + handle = open(fname, O_RDWR | O_BINARY); + if (handle < 0) return redir_xlt_err(); + + newoffs = offs = ((st.st_size + 127) / 128) * 128; + if (fcb[0x20] & 0x7F) + { + newoffs -= (0x80 - (fcb[0x20] & 0x7F)); + } + if (newoffs == st.st_size) + { + ; /* Nothing to do! */ + } + else if (newoffs < st.st_size) + { +#ifndef WIN32 /* XXX Do this somehow in Win32 */ + if (ftruncate(handle, newoffs)) + { + close(handle); + return redir_xlt_err(); + } +#endif + } + else while (newoffs > st.st_size) + { + wlen = newoffs - st.st_size; + if (wlen > 0x80) wlen = 0x80; + memset(zero, 0x1A, sizeof(zero)); + if (write(handle, zero, wlen) < wlen) + { + close(handle); + return redir_xlt_err(); + } + st.st_size += wlen; + } + close(handle); + } + return 0; +} + + + + +cpm_word fcb_setpwd(cpm_byte *fcb, cpm_byte *dma) +{ +#ifdef __MSDOS__ + char fname[CPM_MAXPATH]; + cpm_word rv; + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) return 0x09FF; + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) return 0x02FF; + + redir_log_fcb(fcb); + + rv = redir_drdos_put_rights(fname, dma, redir_drdos_pwmode(fcb[0x0c])); + if (rv || !(fcb[0x0c] & 1)) return rv; + return redir_drdos_put_rights(fname, dma, redir_drdos_pwmode(fcb[0x0c]) | 0x8000); +#else + return 0xFF; /* Unix doesn't do this */ +#endif +} + + +cpm_word fcb_getlbl(cpm_byte drv) +{ + redir_Msg("fcb_getlbl()\r\n"); +#ifdef __MSDOS__ + if (redir_drdos) return 0xA1; /* Supports passwords & Update stamps */ + return 0x21; /* Update stamps only */ +#else + return 0x61; /* Update & Access stamps */ +#endif +} + +cpm_word fcb_setlbl(cpm_byte *fcb, cpm_byte *dma) +{ +/* I am not letting CP/M fiddle with the host's FS settings - even if they + * could be altered, which they mostly can't. */ + + return 0x03FF; +} + + + +cpm_word fcb_defpwd(cpm_byte *pwd) +{ +#ifdef __MSDOS__ + union REGS r; + struct SREGS s; + + if (pwd[0] == 0 || pwd[0] == ' ') + { + redir_passwd[0] = 0; + } + else memcpy(redir_passwd, pwd, 8); + if (redir_drdos) + { + dosmemput(pwd, 8, __tb); + r.w.ax = 0x4454; + r.w.dx = __tb & 0x0F; + s.ds = __tb >> 4; + intdosx(&r, &r, &s); + } + +#endif + return 0; +} + + diff --git a/Tools/unix/zx/cpmredir.h b/Tools/unix/zx/cpmredir.h new file mode 100644 index 00000000..2584e239 --- /dev/null +++ b/Tools/unix/zx/cpmredir.h @@ -0,0 +1,151 @@ +/* + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file holds the public interface to CPMREDIR. +*/ + +#ifndef CPMREDIR_H_INCLUDED + +#define CPMREDIR_H_INCLUDED 16-11-1998 + +/* The "cpm_byte" must be exactly 8 bits. + The "cpm_word" must be exactly 16 bits. */ + +typedef unsigned char cpm_byte; +typedef unsigned short cpm_word; + +/* Maximum length of a directory path */ +#ifdef _POSIX_PATH_MAX + #define CPM_MAXPATH _POSIX_PATH_MAX +#else + #ifdef _MAX_PATH + #define CPM_MAXPATH _MAX_PATH + #else + #define CPM_MAXPATH 260 + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialise this library. Call this function first. + * + * Returns 0 if failed to initialise. + */ +int fcb_init(void); + +/* Deinitialise the library. */ + +void fcb_deinit(void); + +/* Translate a name from the host FS to a CP/M name. This will (if necessary) + * create a mapping between a CP/M drive and a host directory path. + * + * CP/M drives A: to O: can be mapped in this way. P: is always the current + * drive. + * + */ + +void xlt_name(char *localname, char *cpmname); + +/* It is sometimes convenient to set some fixed mappings. This will create + * a mapping for a given directory. + * Pass drive = -1 for "first available", or 0-15 for A: to P: + * Returns 1 if OK, 0 if requested drive not available. + * + * NB: It is important that the localname should have a trailing + * directory separator! + */ + +int xlt_map(int drive, char *localdir); + +/* + * This revokes a mapping. No check is made whether CP/M has files open + * on the drive or not. + */ + +int xlt_umap(int drive); + +/* Find out if a drive is mapped, and if so to what directory */ + +char *xlt_getcwd(int drive); + + +/* BDOS functions. Eventually this should handle all disc-related BDOS + * functions. + * + * I am assuming that your emulator has the CP/M RAM in its normal address + * space, accessible as a range 0-64k. If this is not the case + * (eg: you are emulating banked memory, or using a segmented architecture) + * you will have to use "copy in and copy out" techniques. The "fcb" area + * must be 36 bytes long; the "dma" area should be 128 * the value set + * in fcb_multirec() [default is 1, so 128 bytes]. + * + */ + +cpm_byte fcb_reset (void); /* 0x0D */ +cpm_word fcb_drive (cpm_byte drv); /* 0x0E */ +cpm_word fcb_open (cpm_byte *fcb, cpm_byte *dma); /* 0x0F */ +cpm_word fcb_close (cpm_byte *fcb); /* 0x10 */ +cpm_word fcb_find1 (cpm_byte *fcb, cpm_byte *dma); /* 0x11 */ +cpm_word fcb_find2 (cpm_byte *fcb, cpm_byte *dma); /* 0x12 */ +cpm_word fcb_unlink(cpm_byte *fcb, cpm_byte *dma); /* 0x13 */ +cpm_word fcb_read (cpm_byte *fcb, cpm_byte *dma); /* 0x14 */ +cpm_word fcb_write (cpm_byte *fcb, cpm_byte *dma); /* 0x15 */ +cpm_word fcb_creat (cpm_byte *fcb, cpm_byte *dma); /* 0x16 */ +cpm_word fcb_rename(cpm_byte *fcb, cpm_byte *dma); /* 0x17 */ +cpm_word fcb_logvec(void); /* 0x18 */ +cpm_byte fcb_getdrv(void); /* 0x19 */ +/* DMA is a parameter to routines, not a separate call */ +cpm_word fcb_getalv(cpm_byte *alv, cpm_word max); /* 0x1B */ +/* Get alloc vector: caller must provide space and say how big it is. */ +cpm_word fcb_rodisk(void); /* 0x1C */ +cpm_word fcb_rovec (void); /* 0x1D */ +cpm_word fcb_chmod (cpm_byte *fcb, cpm_byte *dma); /* 0x1E */ +cpm_word fcb_getdpb(cpm_byte *dpb); /* 0x1F */ +cpm_byte fcb_user (cpm_byte usr); /* 0x20 */ +cpm_word fcb_randrd(cpm_byte *fcb, cpm_byte *dma); /* 0x21 */ +cpm_word fcb_randwr(cpm_byte *fcb, cpm_byte *dma); /* 0x22 */ +cpm_word fcb_stat (cpm_byte *fcb); /* 0x23 */ +cpm_word fcb_tell (cpm_byte *fcb); /* 0x24 */ +cpm_word fcb_resro (cpm_word bitmap); /* 0x25 */ +/* Access Drives and Free Drives are not supported. */ +cpm_word fcb_randwz(cpm_byte *fcb, cpm_byte *dma); /* 0x28 */ +/* Record locking calls not supported (though they could be) */ +cpm_word fcb_multirec(cpm_byte rc); /* 0x2C */ +/* Set hardware error action must be done by caller */ +cpm_word fcb_dfree (cpm_byte drive, cpm_byte *dma);/* 0x2E */ +cpm_word fcb_sync (cpm_byte flag); /* 0x30 */ +cpm_word fcb_purge (void); /* 0x62 */ +cpm_word fcb_trunc (cpm_byte *fcb, cpm_byte *dma); /* 0x63 */ +cpm_word fcb_setlbl(cpm_byte *fcb, cpm_byte *dma); /* 0x64 */ +cpm_word fcb_getlbl(cpm_byte drive); /* 0x65 */ +cpm_word fcb_date (cpm_byte *fcb); /* 0x66 */ +cpm_word fcb_setpwd(cpm_byte *fcb, cpm_byte *dma); /* 0x67 */ +cpm_word fcb_defpwd(cpm_byte *pwd); /* 0x6A */ +cpm_word fcb_sdate (cpm_byte *fcb, cpm_byte *dma); /* 0x74 */ +cpm_word fcb_parse (char *txt, cpm_byte *fcb); /* 0x98 */ + +/* fcb_parse returns length of filename parsed, 0 if EOL, 0xFFFF if error */ +#ifdef __cplusplus +} +#endif + + +#endif /* def CPMREDIR_H_INCLUDED */ diff --git a/Tools/unix/zx/dirent.c b/Tools/unix/zx/dirent.c new file mode 100644 index 00000000..87cf85a9 --- /dev/null +++ b/Tools/unix/zx/dirent.c @@ -0,0 +1,149 @@ +/* + + Implementation of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003 and July 2012. + Rights: See end of file. + +*/ + +#include "dirent.h" +#include +#include /* _findfirst and _findnext set errno iff they return -1 */ +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +//typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */ +typedef long handle_type; /* C99's intptr_t not sufficiently portable */ + +struct DIR +{ + handle_type handle; /* -1 for failed rewind */ + struct _finddata_t info; + struct dirent result; /* d_name null iff first time */ + char *name; /* null-terminated char string */ +}; + +DIR *opendir(const char *name) +{ + DIR *dir = 0; + + if(name && name[0]) + { + size_t base_length = strlen(name); + const char *all = /* search pattern must end with suitable wildcard */ + strchr("/\\", name[base_length - 1]) ? "*" : "/*"; + + if((dir = (DIR *) malloc(sizeof *dir)) != 0 && + (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0) + { + strcat(strcpy(dir->name, name), all); + + if((dir->handle = + (handle_type) _findfirst(dir->name, &dir->info)) != -1) + { + dir->result.d_name = 0; + } + else /* rollback */ + { + free(dir->name); + free(dir); + dir = 0; + } + } + else /* rollback */ + { + free(dir); + dir = 0; + errno = ENOMEM; + } + } + else + { + errno = EINVAL; + } + + return dir; +} + +int closedir(DIR *dir) +{ + int result = -1; + + if(dir) + { + if(dir->handle != -1) + { + result = _findclose(dir->handle); + } + + free(dir->name); + free(dir); + } + + if(result == -1) /* map all errors to EBADF */ + { + errno = EBADF; + } + + return result; +} + +struct dirent *readdir(DIR *dir) +{ + struct dirent *result = 0; + + if(dir && dir->handle != -1) + { + if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) + { + result = &dir->result; + result->d_name = dir->info.name; + } + } + else + { + errno = EBADF; + } + + return result; +} + +void rewinddir(DIR *dir) +{ + if(dir && dir->handle != -1) + { + _findclose(dir->handle); + dir->handle = (handle_type) _findfirst(dir->name, &dir->info); + dir->result.d_name = 0; + } + else + { + errno = EBADF; + } +} + +#ifdef __cplusplus +} +#endif + +/* + + Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + +*/ diff --git a/Tools/unix/zx/dirent.h b/Tools/unix/zx/dirent.h new file mode 100644 index 00000000..bbbfce52 --- /dev/null +++ b/Tools/unix/zx/dirent.h @@ -0,0 +1,50 @@ +#ifndef DIRENT_INCLUDED +#define DIRENT_INCLUDED + +/* + + Declaration of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003. + Rights: See end of file. + +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct DIR DIR; + +struct dirent +{ + char *d_name; +}; + +DIR *opendir(const char *); +int closedir(DIR *); +struct dirent *readdir(DIR *); +void rewinddir(DIR *); + +/* + + Copyright Kevlin Henney, 1997, 2003. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + +*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Tools/unix/zx/drdos.c b/Tools/unix/zx/drdos.c new file mode 100644 index 00000000..97bd583c --- /dev/null +++ b/Tools/unix/zx/drdos.c @@ -0,0 +1,236 @@ +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file holds DRDOS-specific password code. +*/ + +#include "cpmint.h" + +cpm_word redir_drdos_pwmode(cpm_byte b) +{ + cpm_word mode = 0; + + if (b & 0x80) mode |= 0xddd; + if (b & 0x40) mode |= 0x555; + if (b & 0x20) mode |= 0x111; + + return mode; +} + +cpm_byte redir_cpm_pwmode(cpm_word w) +{ + cpm_byte mode = 0; + + if (w & 0x8) mode |= 0x80; + if (w & 0x4) mode |= 0x40; + if (w & 0x1) mode |= 0x20; + + return mode; +} + +#ifdef __MSDOS__ +#ifdef __GO32__ /* The GO32 extender doesn't understand DRDOS password + * functions, so these are done with __dpmi_int() rather + * than intdos() */ + +cpm_word redir_drdos_get_rights(char *path) +{ + __dpmi_regs r; + + if (!redir_drdos) return 0; + + redir_Msg("Rights for file %s: \n\r", path); + + dosmemput(path, strlen(path) + 1, __tb); + r.x.ax = 0x4302; + r.x.dx = __tb & 0x0F; + r.x.ds = (__tb) >> 4; + + __dpmi_int(0x21, &r); + + redir_Msg(" %04x \n\r", r.x.cx); + + if (r.x.flags & 1) return 0; + return r.x.cx; +} + + +cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) +{ + __dpmi_regs r; + + if (!redir_drdos) return 0; + + redir_Msg("Put rights for file %s: %04x %-8.8s %-8.8s\n\r", path, rights, dma, dma + 8); + + dosmemput(dma+8, 8, __tb); /* Point DTA at password */ + r.x.ax = 0x1A00; + r.x.dx = (__tb & 0x0F); + r.x.ds = (__tb) >> 4; + __dpmi_int(0x21, &r); + + dosmemput(path, strlen(path) + 1, __tb + 0x10); + r.x.ax = 0x4303; /* Set rights */ + r.x.cx = rights; + r.x.dx = (__tb & 0x0F) + 0x10; + r.x.ds = (__tb) >> 4; + + __dpmi_int(0x21, &r); + + if (r.x.flags & 1) + { + redir_Msg(" Try 1 failed. Error %04x\n\r", r.x.ax); + if (redir_password_error()) + { + redir_password_append(path, dma); + + dosmemput(path, strlen(path) + 1, __tb + 0x10); + r.x.ax = 0x4303; /* Set rights */ + r.x.cx = rights; + r.x.dx = (__tb & 0x0F) + 0x10; + r.x.ds = (__tb) >> 4; + + __dpmi_int(0x21, &r); + if (!r.x.flags & 1) return 0; + if (redir_password_error()) return 0x7FF; + } + return 0xFF; + } + return 0; +} + +#else /* __GO32__ */ + +cpm_word redir_drdos_get_rights(char *path) +{ + union REGS r; + struct SREGS s; + + if (!redir_drdos) return 0; + + redir_Msg("Rights for file %s: \n\r", path); + + dosmemput(path, strlen(path) + 1, __tb); + r.w.ax = 0x4302; + r.w.dx = __tb & 0x0F; + s.ds = (__tb) >> 4; + + intdosx(&r, &r, &s); + + redir_Msg(" %04x \n\r", r.w.cx); + + if (r.w.cflag) return 0; + return r.w.cx; +} + + +cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) +{ + union REGS r; + struct SREGS s; + + if (!redir_drdos) return 0; + + redir_Msg("Put rights for file %s: %04x\n\r", path, rights); + + dosmemput(dma, 8, __tb); /* Point DTA at password */ + r.w.ax = 0x1A00; + r.w.dx = (__tb & 0x0F); + s.ds = (__tb) >> 4; + intdosx(&r, &r, &s); + + dosmemput(path, strlen(path) + 1, __tb + 0x10); + r.w.ax = 0x4303; /* Set rights */ + r.w.cx = rights; + r.w.dx = (__tb & 0x0F) + 0x10; + s.ds = (__tb) >> 4; + + intdosx(&r, &r, &s); + + if (r.w.cflag) + { + redir_Msg(" Try 1 failed. Error %04x \n\r", r.w.ax); + if (redir_password_error()) + { + redir_password_append(path, dma); + + dosmemput(path, strlen(path) + 1, __tb + 0x10); + r.w.ax = 0x4303; /* Set rights */ + r.w.cx = rights; + r.w.dx = (__tb & 0x0F) + 0x10; + s.ds = (__tb) >> 4; + + intdosx(&r, &r, &s); + if (!r.w.cflag) return 0; + } + return 0xFF; + } + return 0; +} + +#endif /* __GO32__ */ + + +cpm_word redir_password_error(void) +{ + union REGS r; + + if (!redir_drdos) return 0; + + r.w.ax = 0x5900; + r.w.bx = 0x0000; + + intdos(&r, &r); + + redir_Msg("Last error was: %04x\r\n", r.w.ax); + + if (r.w.ax == 0x56) return 1; /* Bad password */ + return 0; +} + + +void redir_password_append(char *s, cpm_byte *dma) +{ + int n, m; + + if (!redir_drdos) return; + + if (dma[0] == 0 || dma[0] == 0x20) return; + + strcat(s, ";"); + m = strlen(s); + + for (n = 0; n < 8; n++) + { + if (dma[n] == ' ') s[m] = 0; + else s[m] = dma[n]; + ++m; + } + s[m] = 0; + +} +#else /* __MSDOS__ */ +void redir_password_append(char *s, cpm_byte *dma) {} +cpm_word redir_password_error(void) { return 0; } +cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) +{ return 0; } +cpm_word redir_drdos_get_rights(char *path) { return 0; } +#endif /* __MSDOS__ */ + + diff --git a/Tools/unix/zx/edops.h b/Tools/unix/zx/edops.h new file mode 100644 index 00000000..6b04c96a --- /dev/null +++ b/Tools/unix/zx/edops.h @@ -0,0 +1,567 @@ +/* Emulations of the ED operations of the Z80 instruction set. + * Copyright (C) 1994 Ian Collier. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define input(var) { unsigned short u;\ + var=u=in(tstates,b,c);\ + tstates+=u>>8;\ + f=(f&1)|(var&0xa8)|((!var)<<6)|parity(var);\ + } +#define sbchl(x) { unsigned short z=(x);\ + unsigned long t=(hl-z-cy)&0x1ffff;\ + f=((t>>8)&0xa8)|(t>>16)|2|\ + (((hl&0xfff)<(z&0xfff)+cy)<<4)|\ + (((hl^z)&(hl^t)&0x8000)>>13)|\ + ((!(t&0xffff))<<6)|2;\ + l=t;\ + h=t>>8;\ + } + +#define adchl(x) { unsigned short z=(x);\ + unsigned long t=hl+z+cy;\ + f=((t>>8)&0xa8)|(t>>16)|\ + (((hl&0xfff)+(z&0xfff)+cy>0xfff)<<4)|\ + (((~hl^z)&(hl^t)&0x8000)>>13)|\ + ((!(t&0xffff))<<6)/*|2*/;\ + l=t;\ + h=t>>8;\ + } +/* [JCE] The "|2" should not be there, at least according to my tests on + * a PCW16. The PCW16's ADC always resets that bit. */ + + +#define neg (a=-a,\ + f=(a&0xa8)|((!a)<<6)|(((a&15)>0)<<4)|((a==128)<<2)|2|(a>0)) + +{ + unsigned char op=fetch(pc); + pc++; + radjust++; + switch(op){ +instr(0x40,8); + input(b); +endinstr; + +instr(0x41,8); + tstates+=out(tstates,b,c,b); +endinstr; + +instr(0x42,11); + sbchl(bc); +endinstr; + +instr(0x43,16); + {unsigned short addr=fetch2(pc); + pc+=2; + store2b(addr,b,c); + } +endinstr; + +instr(0x44,4); + neg; +endinstr; + +instr(0x45,4); + iff1=iff2; + ret; +endinstr; + +instr(0x46,4); + im=0; +endinstr; + +instr(0x47,5); + i=a; +endinstr; + +instr(0x48,8); + input(c); +endinstr; + +instr(0x49,8); + tstates+=out(tstates,b,c,c); +endinstr; + +instr(0x4a,11); + adchl(bc); +endinstr; + +instr(0x4b,16); + {unsigned short addr=fetch2(pc); + pc+=2; + c=fetch(addr); + b=fetch(addr+1); + } +endinstr; + +instr(0x4c,4); + neg; +endinstr; + +instr(0x4d,4); + ret; +endinstr; + +instr(0x4e,4); + im=1; +endinstr; + +instr(0x4f,5); + r=a; + radjust=r; +endinstr; + +instr(0x50,8); + input(d); +endinstr; + +instr(0x51,8); + tstates+=out(tstates,b,c,d); +endinstr; + +instr(0x52,11); + sbchl(de); +endinstr; + +instr(0x53,16); + {unsigned short addr=fetch2(pc); + pc+=2; + store2b(addr,d,e); + } +endinstr; + +instr(0x54,4); + neg; +endinstr; + +instr(0x55,4); + ret; +endinstr; + +instr(0x56,4); + im=2; +endinstr; + +instr(0x57,5); + a=i; + f=(f&1)|(a&0xa8)|((!a)<<6)|(iff2<<2); +endinstr; + +instr(0x58,8); + input(e); +endinstr; + +instr(0x59,8); + tstates+=out(tstates,b,c,e); +endinstr; + +instr(0x5a,11); + adchl(de); +endinstr; + +instr(0x5b,16); + {unsigned short addr=fetch2(pc); + pc+=2; + e=fetch(addr); + d=fetch(addr+1); + } +endinstr; + +instr(0x5c,4); + neg; +endinstr; + +instr(0x5d,4); + ret; +endinstr; + +instr(0x5e,4); + im=3; +endinstr; + +instr(0x5f,5); + r=(r&0x80)|(radjust&0x7f); + a=r; + f=(f&1)|(a&0xa8)|((!a)<<6)|(iff2<<2); +endinstr; + +instr(0x60,8); + input(h); +endinstr; + +instr(0x61,8); + tstates+=out(tstates,b,c,h); +endinstr; + +instr(0x62,11); + sbchl(hl); +endinstr; + +instr(0x63,16); + {unsigned short addr=fetch2(pc); + pc+=2; + store2b(addr,h,l); + } +endinstr; + +instr(0x64,4); + neg; +endinstr; + +instr(0x65,4); + ret; +endinstr; + +instr(0x66,4); + im=0; +endinstr; + +instr(0x67,14); + {unsigned char t=fetch(hl); + unsigned char u=(a<<4)|(t>>4); + a=(a&0xf0)|(t&0x0f); + store(hl,u); + f=(f&1)|(a&0xa8)|((!a)<<6)|parity(a); + } +endinstr; + +instr(0x68,8); + input(l); +endinstr; + +instr(0x69,8); + tstates+=out(tstates,b,c,l); +endinstr; + +instr(0x6a,11); + adchl(hl); +endinstr; + +instr(0x6b,16); + {unsigned short addr=fetch2(pc); + pc+=2; + l=fetch(addr); + h=fetch(addr+1); + } +endinstr; + +instr(0x6c,4); + neg; +endinstr; + +instr(0x6d,4); + ret; +endinstr; + +instr(0x6e,4); + im=1; +endinstr; + +instr(0x6f,5); + {unsigned char t=fetch(hl); + unsigned char u=(a&0x0f)|(t<<4); + a=(a&0xf0)|(t>>4); + store(hl,u); + f=(f&1)|(a&0xa8)|((!a)<<6)|parity(a); + } +endinstr; + +instr(0x70,8); + {unsigned char x;input(x);} +endinstr; + +instr(0x71,8); + tstates+=out(tstates,b,c,0); +endinstr; + +instr(0x72,11); + sbchl(sp); +endinstr; + +instr(0x73,16); + {unsigned short addr=fetch2(pc); + pc+=2; + store2(addr,sp); + } +endinstr; + +instr(0x74,4); + neg; +endinstr; + +instr(0x75,4); + ret; +endinstr; + +instr(0x76,4); + im=2; +endinstr; + +instr(0x78,8); + input(a); +endinstr; + +instr(0x79,8); + tstates+=out(tstates,b,c,a); +endinstr; + +instr(0x7a,11); + adchl(sp); +endinstr; + +instr(0x7b,16); + {unsigned short addr=fetch2(pc); + pc+=2; + sp=fetch2(addr); + } +endinstr; + +instr(0x7c,4); + neg; +endinstr; + +instr(0x7d,4); + ret; +endinstr; + +instr(0x7e,4); + im=3; +endinstr; + +instr(0xa0,12); + {unsigned char x=fetch(hl); + store(de,x); + if(!++l)h++; + if(!++e)d++; + if(!c--)b--; + f=(f&0xc1)|(x&0x28)|(((b|c)>0)<<2); + } +endinstr; + +instr(0xa1,12); + {unsigned char carry=cy; + cpa(fetch(hl)); + if(!++l)h++; + if(!c--)b--; + f=(f&0xfa)|carry|(((b|c)>0)<<2); + } +endinstr; + +instr(0xa2,12); + {unsigned short t=in(tstates,b,c); + store(hl,t); + tstates+=t>>8; + if(!++l)h++; + b--; + f=(b&0xa8)|((b==0)<<6)|2|((parity(b)^c)&4); + } +endinstr; + +instr(0xa3,12); /* I can't determine the correct flags outcome for the + block OUT instructions. Spec says that the carry + flag is left unchanged and N is set to 1, but that + doesn't seem to be the case... */ + {unsigned char x=fetch(hl); + tstates+=out(tstates,b,c,x); + if(!++l)h++; + b--; + f=(f&1)|0x12|(b&0xa8)|((b==0)<<6); + } +endinstr; + +instr(0xa8,12); + {unsigned char x=fetch(hl); + store(de,x); + if(!l--)h--; + if(!e--)d--; + if(!c--)b--; + f=(f&0xc1)|(x&0x28)|(((b|c)>0)<<2); + } +endinstr; + +instr(0xa9,12); + {unsigned char carry=cy; + cpa(fetch(hl)); + if(!l--)h--; + if(!c--)b--; + f=(f&0xfa)|carry|(((b|c)>0)<<2); + } +endinstr; + +instr(0xaa,12); + {unsigned short t=in(tstates,b,c); + store(hl,t); + tstates+=t>>8; + if(!l--)h--; + b--; + f=(b&0xa8)|((b==0)<<6)|2|((parity(b)^c^4)&4); + } +endinstr; + +instr(0xab,12); + {unsigned char x=fetch(hl); + tstates+=out(tstates,b,c,x); + if(!l--)h--; + b--; + f=(f&1)|0x12|(b&0xa8)|((b==0)<<6); + } +endinstr; + +/* Note: the Z80 implements "*R" as "*" followed by JR -2. No reason + to change this... */ + +instr(0xb0,12); + {unsigned char x=fetch(hl); + store(de,x); + if(!++l)h++; + if(!++e)d++; + if(!c--)b--; + f=(f&0xc1)|(x&0x28)|(((b|c)>0)<<2); + if(b|c)pc-=2,tstates+=5; + } +endinstr; + +instr(0xb1,12); + {unsigned char carry=cy; + cpa(fetch(hl)); + if(!++l)h++; + if(!c--)b--; + f=(f&0xfa)|carry|(((b|c)>0)<<2); + if((f&0x44)==4)pc-=2,tstates+=5; + } +endinstr; + +instr(0xb2,12); + {unsigned short t=in(tstates,b,c); + store(hl,t); + tstates+=t>>8; + if(!++l)h++; + b--; + f=(b&0xa8)|((b==0)<<6)|2|((parity(b)^c)&4); + if(b)pc-=2,tstates+=5; + } +endinstr; + +instr(0xb3,12); + {unsigned char x=fetch(hl); + tstates+=out(tstates,b,c,x); + if(!++l)h++; + b--; + f=(f&1)|0x12|(b&0xa8)|((b==0)<<6); + if(b)pc-=2,tstates+=5; + } +endinstr; + +instr(0xb8,12); + {unsigned char x=fetch(hl); + store(de,x); + if(!l--)h--; + if(!e--)d--; + if(!c--)b--; + f=(f&0xc1)|(x&0x28)|(((b|c)>0)<<2); + if(b|c)pc-=2,tstates+=5; + } +endinstr; + +instr(0xb9,12); + {unsigned char carry=cy; + cpa(fetch(hl)); + if(!l--)h--; + if(!c--)b--; + f=(f&0xfa)|carry|(((b|c)>0)<<2); + if((f&0x44)==4)pc-=2,tstates+=5; + } +endinstr; + +instr(0xba,12); + {unsigned short t=in(tstates,b,c); + store(hl,t); + tstates+=t>>8; + if(!l--)h--; + b--; + f=(b&0xa8)|((b==0)<<6)|2|((parity(b)^c^4)&4); + if(b)pc-=2,tstates+=5; + } +endinstr; + +instr(0xbb,12); + {unsigned char x=fetch(hl); + tstates+=out(tstates,b,c,x); + if(!l--)h--; + b--; + f=(f&1)|0x12|(b&0xa8)|((b==0)<<6); + if(b)pc-=2,tstates+=5; + } +endinstr; + +/* XZ80 Pseudo-ops +instr(0xfb,4); + multiloader(hl,a); +endinstr; + +instr(0xfc,4); + { int ans; + stopwatch(); + ans=loader(pc,ix,de,a,cy); + if(ans>=0){ + if(ans==1)pc=0x0806; + else if(ans==2)f&=254; + else f|=1; + ix+=de; + d=e=0; + if(ans!=1)ret; + } + startwatch(1); + } +endinstr; + +instr(0xfd,4); + { int ans; + stopwatch(); + ans=saver(pc,ix,de,a); + if(ans>0)pc=0x0806; + else if(ans==0){ + ix+=de; + d=e=0; + ret; + } + startwatch(1); + } +endinstr; +*/ + +/* ZXCC pseudo-op */ +instr(0xfe, 4); +{ + /* Create copies of the registers here so we can take their addresses + * and not lose register optimisation in the rest of the CPU code */ + byte xa,xb,xc,xd,xe,xf,xxh,xxl; + word xp,xx,xy; + + xa = a; xb = b; xc = c; xd = d; xe = e; xf = f; xxh = h; xxl = l; + xp = pc; xx = ix; xy = iy; + + ed_fe(&xa,&xb,&xc,&xd,&xe,&xf,&xxh,&xxl,&xp,&xx,&xy); + + a = xa; b = xb; c = xc; d = xd; e = xe; f = xf; h = xxh; l = xxl; + pc = xp; ix = xx; iy = xy; +} +endinstr; + +default: tstates+=4; + +}} diff --git a/Tools/unix/zx/readme.txt b/Tools/unix/zx/readme.txt new file mode 100644 index 00000000..31c3e320 --- /dev/null +++ b/Tools/unix/zx/readme.txt @@ -0,0 +1,47 @@ +ZX Command + +An adaptation of zxcc-0.5.6 by Wayne Warthen + +This directory contains the source files used to build the "zx" tool. This tool +is essentially just John Elliott's zxcc package version zxcc-0.5.6 modified to +build for Windows and simplified down to just a single command (zx) +which is essentially just the zxcc command. + +Please see http://www.seasip.info/Unix/Zxcc/ for more information on zxcc. + +To build under Open Watcom or Microsoft Visual C++, use the following command: + + cl /Fe"zx.exe" zx.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c + +To build a debug version, use the following command: + + cl /DDEBUG /Fe"zxdbg.exe" zx.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c + +WARNING: There seems to be a rare scenario that breaks zx under the Open Watcom build. CP/M allows a file to be accessed +under multiple FCB's without an error. Open Watcom will see this as an error. At present, the only tool I know of that does +this is M80. + +December 5, 2014 + +After struggling to get the entire zxcc package to build nicely using autoconf, +I finally gave up and took a much more direct approach. I have extracted just +the source files needed and created a simple batch file to build the tool. I +realize this could be done much better, but I cheated in the interest of time. + +The one "real" change I made in the source code was that I modified the tool +to look for bios.bin in the same directory as the executable is in. This +just makes it much easier to set up (for me, anyway). + +The GPL status of everything remains in place and carries forward. + +Wayne Warthen +wwarthen@gmail.com + +March 15, 2017 + +- Updated to compile under Open Watcom. +- Implemented BDOS console status function. +- Set stdin and stdout to binary mode at startup. + +Wayne Warthen +wwarthen@gmail.com \ No newline at end of file diff --git a/Tools/unix/zx/util.c b/Tools/unix/zx/util.c new file mode 100644 index 00000000..21d29728 --- /dev/null +++ b/Tools/unix/zx/util.c @@ -0,0 +1,378 @@ +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file holds miscellaneous utility functions. +*/ + +#include "cpmint.h" +#include + +/* In debug mode, lseek()s can be traced. */ + +#ifdef DEBUG + +long zxlseek(int fd, long offset, int wh) +{ +#ifdef WIN32 + long v; + redir_Msg(">SetFilePointer() Handle=%lu, Offset=%lu, Method=%lu\n", fd, offset, wh); + v = SetFilePointer((HANDLE)fd, offset, NULL, wh); + redir_Msg("= 0) return v; + + redir_Msg("lseek fails with errno = %d\n", errno); + if (errno == EBADF) redir_Msg(" (bad file descriptor %d)\n", fd); + if (errno == ESPIPE) redir_Msg(" (file %d is a pipe)\n", fd); + if (errno == EINVAL) redir_Msg(" (bad parameter %d)\n", wh); + + return -1; +#endif +} + +void redir_showfcb(cpm_byte *fd) +{ + int n; + + for (n = 0; n < 32; n++) + { + if (!n || n>= 12) printf("%02x ", fd[n]); + else printf("%c", fd[n] & 0x7F); + } + printf("\r\n"); +} + +#else + +long zxlseek(int fd, long offset, int wh) +{ +#ifdef WIN32 + return SetFilePointer((HANDLE)fd, offset, NULL, wh); +#else + return lseek(fd, offset, wh); +#endif +} + + +#endif + +/* Get the "sequential access" file pointer out of an FCB */ + +long redir_get_fcb_pos(cpm_byte *fcb) +{ + long npos; + + npos = 524288L * fcb[0x0E]; /* S2 */ + npos += 16384L * fcb[0x0C]; /* Extent */ + npos += 128L * fcb[0x20]; /* Record */ + + return npos; +} + +void redir_put_fcb_pos(cpm_byte *fcb, long npos) +{ + fcb[0x20] = (npos / 128) % 128; + fcb[0x0C] = (npos / 16384) % 32; + fcb[0x0E] = (npos / 524288L) % 64; +} + + +/* + * find a filename that works. + * note that this is where we handle the case sensitivity/non-case sensitivity + * horror. + * the name that is passed in should be in lower case. + * we'll modify it to the first one that matches + */ +void +swizzle(char *fullpath) +{ + struct stat ss; + char *slash; + DIR *dirp; + struct dirent *dentry; + + /* short circuit if ok */ + if (stat(fullpath, &ss) == 0) { + return; + } + + slash = rindex(fullpath, '/'); + if (!slash) { + return; + } + *slash = '\0'; + dirp = opendir(fullpath); + *slash = '/'; + while ((dentry = readdir(dirp)) != NULL) { + if (strcasecmp(dentry->d_name, slash + 1) == 0) { + strcpy(slash + 1, dentry->d_name); + break; + } + } + closedir(dirp); +} + +/* + * Passed a CP/M FCB, convert it to a unix filename. Turn its drive back into + * a path. + */ + +int redir_fcb2unix(cpm_byte *fcb, char *fname) +{ + int n, q, drv, ddrv; + char s[2]; + + s[1] = 0; + q = 0; + drv = fcb[0] & 0x7F; + if (drv == '?') drv = 0; + + ddrv = fcb[0] & 0x7F; + if (ddrv < 0x1F) ddrv += '@'; + + redir_Msg("%c:%-8.8s.%-3.3s\n", + ddrv, + fcb + 1, + fcb + 9); + + if (!drv) strcpy(fname, redir_drive_prefix[redir_cpmdrive]); + else strcpy(fname, redir_drive_prefix[drv - 1]); + + for (n = 1; n < 12; n++) + { + s[0] = (fcb[n] & 0x7F); + if (s[0] == '?') q = 1; + if (isupper(s[0])) s[0] = tolower(s[0]); + if (s[0] != ' ') + { + if (n == 9) strcat(fname, "."); + strcat(fname, s); + } + } + return q; +} + +#ifndef EROFS /* Open fails because of read-only FS */ +#define EROFS EACCES +#endif + +int redir_ofile(cpm_byte *fcb, char *s) +{ + int h, rv; + + /* Software write-protection */ +#ifdef WIN32 + redir_Msg(">CreateFile([OPEN_EXISTING]) Name='%s'\n", s); + h = (int)CreateFile(s, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + redir_Msg("= 0 || (errno != EACCES && errno != EROFS)) return h; + } + redir_Msg("**2**"); + h = open(s, O_RDONLY | O_BINARY); + if (h < 0) return -1; + fcb[9] |= 0x80; + #endif +#endif + + return h; +} + + +/* Extract a file handle from where it was stored in an FCB by fcb_open() + or fcb_creat(). Aborts if the FCB has been tampered with. + + Note: Some programs (like GENCOM) close FCBs they never opened. This causes + the Corrupt FCB message, but no harm seems to ensue. */ + +int redir_verify_fcb(cpm_byte *fcb) +{ + if (fcb[16] != 0xFD || fcb[17] != 0x00) + { + fprintf(stderr,"cpmredir: Corrupt FCB\n"); + return -1; + } + return (int)(redir_rd32(fcb + 18)); + +} + +/* Print a trace message */ + +#ifdef DEBUG + +void redir_Msg(char *s, ...) +{ + va_list ap; + + va_start(ap, s); + printf("cpmredir trace: "); + vprintf(s, ap); + va_end(ap); + fflush(stdout); +} + +#endif + +#define BCD(x) (((x % 10)+16*(x/10)) & 0xFF) + +/* Convert time_t to CP/M day count/hours/minutes */ +dword redir_cpmtime(time_t t) +{ + long d = (t / 86400) - 2921; /* CP/M day 0 is unix day 2921 */ + long h = (t % 86400) / 3600; /* Hour, 0-23 */ + long m = (t % 3600) / 60; /* Minute, 0-59 */ + + return (d | (BCD(h) << 16) | (BCD(m) << 24)); +} + +#undef BCD + +#define UNBCD(x) (((x % 16) + 10 * (x / 16)) & 0xFF) + +time_t redir_unixtime(cpm_byte *c) +{ + time_t t; + cpm_word days; + + days = (c[0] + 256 * c[1]) + 2921; + + t = 60L * UNBCD(c[3]); + t += 3600L * UNBCD(c[2]); + t += 86400L * days; + + return t; +} + +#undef UNBCD + + +/* Functions to access 24-bit & 32-bit words in memory. These are always + little-endian. */ + +void redir_wr24(cpm_byte *addr, dword v) +{ + addr[0] = v & 0xFF; + addr[1] = (v >> 8) & 0xFF; + addr[2] = (v >> 16) & 0xFF; +} + +void redir_wr32(cpm_byte *addr, dword v) +{ + addr[0] = v & 0xFF; + addr[1] = (v >> 8) & 0xFF; + addr[2] = (v >> 16) & 0xFF; + addr[3] = (v >> 24) & 0xFF; +} + +dword redir_rd24(cpm_byte *addr) +{ + register dword rv = addr[2]; + + rv = (rv << 8) | addr[1]; + rv = (rv << 8) | addr[0]; + return rv; +} + + +dword redir_rd32(cpm_byte *addr) +{ + register dword rv = addr[3]; + + rv = (rv << 8) | addr[2]; + rv = (rv << 8) | addr[1]; + rv = (rv << 8) | addr[0]; + return rv; +} + + +void redir_log_drv(cpm_byte drv) +{ + if (!drv) redir_l_drives |= 1; + else redir_l_drives |= (1L << drv); +} + +void redir_log_fcb(cpm_byte *fcb) +{ + int drv = fcb[0] & 0x7F; + + if (drv && drv != '?') redir_log_drv(drv - 1); + else redir_log_drv(redir_cpmdrive); +} + + +int redir_ro_drv(cpm_byte drv) +{ + if (!drv) return redir_ro_drives & 1; + else return redir_ro_drives & (1L << drv); +} + +int redir_ro_fcb(cpm_byte *fcb) +{ + int drv = fcb[0] & 0x7F; + + if (drv && drv != '?') return redir_ro_drv(drv - 1); + else return redir_ro_drv(redir_cpmdrive); +} + + + +cpm_word redir_xlt_err(void) +{ + if (redir_password_error()) return 0x7FF; /* DRDOS pwd error */ + switch(errno) + { + case EISDIR: + case EBADF: return 9; /* Bad FCB */ + case EINVAL: return 0x03FF; /* Readonly file */ + case EPIPE: return 0x01FF; /* Broken pipe */ + case ENOSPC: return 1; /* No space */ + default: return 0xFF; /* Software error */ + } +} + diff --git a/Tools/unix/zx/xlt.c b/Tools/unix/zx/xlt.c new file mode 100644 index 00000000..f0ba1da5 --- /dev/null +++ b/Tools/unix/zx/xlt.c @@ -0,0 +1,191 @@ +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file holds functions dealing with name translation; also the + initialisation code. +*/ + +#include "cpmint.h" + +/* Detect DRDOS */ + +#ifdef __MSDOS__ +static void drdos_init(void) +{ + +/* The DJGPP DOS extender won't detect DRDOS using intdos(), so we have + to use __dpmi_int() instead. */ + +#ifdef __GO32__ + __dpmi_regs ir; + + ir.x.ax = 0x4452; /* "DR" */ + + __dpmi_int(0x21, &ir); + if (ir.x.flags & 1) return; /* Not DRDOS */ + + redir_Msg("DRDOS detected.\r\n"); + + redir_drdos = 1; + +#else /* __GO32__ */ + + union REGS ir, or; + + ir.w.ax = 0x4452; /* "DR" */ + + intdos(&ir, &or); + if (or.w.cflag) return; /* Not DRDOS */ + + redir_Msg("DRDOS detected.\r\n"); + + redir_drdos = 1; +#endif /* __GO32__ */ +} +#endif /* __MSDOS__ */ + + + +int fcb_init(void) +{ + int n; + + /* A: to O: free */ + for (n = 0; n < 15; n++) redir_drive_prefix[n][0] = 0; + + strcpy(redir_drive_prefix[15], "./"); /* P: is current directory */ + + /* Log on to P:. It is the only drive at this point which we + * know works. */ + redir_cpmdrive = 15; +#ifdef __MSDOS__ + drdos_init(); +#endif + + return 1; +} + +/* Deinitialise the library. */ + +void fcb_deinit(void) +{ + /* Nothing */ +} + +/* Translate a name from the host FS to a CP/M name. This will (if necessary) + * create a mapping between a CP/M drive and a host directory path. + * + * CP/M drives A: to O: can be mapped in this way. P: is always the current + * drive. + * + */ + +void xlt_name(char *localname, char *cpmname) +{ + char ibuf[CPM_MAXPATH + 1]; + char nbuf[CPM_MAXPATH + 1]; + char *pname; + int n; + + sprintf(ibuf, "%-.*s", CPM_MAXPATH, localname); + pname = strrchr(ibuf, '/'); +#ifdef __MSDOS__ + if (!pname) pname = strrchr(ibuf,'\\'); + if (!pname) pname = strrchr(ibuf,':'); +#endif + if (!pname) /* No path separators in the name. It is therefore a + local filename, so map it to drive P: */ + { + strcpy(cpmname, "p:"); + strcat(cpmname, ibuf); + return; + } + ++pname; + strcpy(nbuf, pname); /* nbuf holds filename component */ + *pname = 0; /* ibuf holds path component */ + + /* See if the path is one of those already mapped to drives */ + + for (n = 0; n < 15; n++) + { + if (redir_drive_prefix[n][0] && !strcmp(ibuf, redir_drive_prefix[n])) + { + sprintf(cpmname,"%c:%s", n + 'a', nbuf); + return; + } + } + + /* It is not, see if another drive can be allocated */ + + for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0]) + { + strcpy(redir_drive_prefix[n], ibuf); + sprintf(cpmname,"%c:%s", n + 'a', nbuf); + return; + } + + /* No other drive can be allocated */ + + strcpy(cpmname,"p:"); + strcat(cpmname, nbuf); +} + +/* It is sometimes convenient to set some fixed mappings. This will create + * a mapping for a given directory. + * Pass drive = -1 for "first available", or 0-15 for A: to P: + */ + +int xlt_map(int drive, char *localdir) +{ + int n; + + if (drive == -1) + { + for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0]) + { + drive = n; + break; + } + if (drive == -1) return 0; /* No space for mappings */ + } + if (redir_drive_prefix[drive][0]) return 0; /* Drive taken */ + + sprintf(redir_drive_prefix[drive], "%-.*s", CPM_MAXPATH, localdir); + return 1; +} + + +/* Unmap a drive + */ + +int xlt_umap(int drive) +{ + if (!redir_drive_prefix[drive][0]) return 0; /* Drive not taken */ + redir_drive_prefix[drive][0] = 0; + return 1; +} + + +char *xlt_getcwd(int drive) +{ + if (drive < 0 || drive > 16) return ""; + + return redir_drive_prefix[drive]; +} + diff --git a/Tools/unix/zx/z80.c b/Tools/unix/zx/z80.c new file mode 100644 index 00000000..d1d99cd5 --- /dev/null +++ b/Tools/unix/zx/z80.c @@ -0,0 +1,270 @@ +/* Emulation of the Z80 CPU with hooks into the other parts of xz80. + * Copyright (C) 1994 Ian Collier. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include "zx.h" + +#define parity(a) (partable[a]) + +unsigned char partable[256]={ + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4 + }; + +#ifdef DEBUG +static unsigned short breakpoint=0; +static unsigned int breaks=0; + +//static void inline log(fp,name,val) +static void log(fp,name,val) +FILE *fp; +char *name; +unsigned short val; +{ + int i; + fprintf(fp,"%s=%04X ",name,val); + for(i=0;i<8;i++,val++)fprintf(fp," %02X",fetch(val)); + putc('\n',fp); +} +#endif + +void mainloop(word spc, word ssp){ + register unsigned char a, f, b, c, d, e, h, l; + unsigned char r, a1, f1, b1, c1, d1, e1, h1, l1, i, iff1, iff2, im; + register unsigned short pc; + unsigned short ix, iy, sp; + register unsigned long tstates; + register unsigned int radjust; + register unsigned char ixoriy, new_ixoriy; + unsigned char intsample; + register unsigned char op; +#ifdef DEBUG + char flags[9]; + int bit; + FILE *fp=0; + register unsigned short af2=0,bc2=0,de2=0,hl2=0,ix2=0,iy2=0,sp2=0; + register unsigned char i2=0; + //unsigned char *memory=memptr[0]; + struct _next {unsigned char bytes[8];} *next; + unsigned short BC, DE, HL, AF; + + fputs("Press F11 to log\n",stderr); +#endif + a=f=b=c=d=e=h=l=a1=f1=b1=c1=d1=e1=h1=l1=i=r=iff1=iff2=im=0; + ixoriy=new_ixoriy=0; + ix=iy=0; + pc=spc; + sp=ssp; + tstates=radjust=0; + while(1){ + ixoriy=new_ixoriy; + new_ixoriy=0; +#ifdef DEBUG + next=(struct _next *)&fetch(pc); + BC=bc;DE=de;HL=hl;AF=(a<<8)|f; + if(fp && !ixoriy){ + log(fp,"pc",pc); + if(sp!=sp2)log(fp,"sp",sp2=sp); + if(iy!=iy2)log(fp,"iy",iy2=iy); + if(ix!=ix2)log(fp,"ix",ix2=ix); + if(hl!=hl2)log(fp,"hl",hl2=hl); + if(de!=de2)log(fp,"de",de2=de); + if(bc!=bc2)log(fp,"bc",bc2=bc); + if(((a<<8)|f)!=af2){ + af2=(a<<8)|f; + strcpy(flags,"SZ H VNC"); + for(bit=0;bit<8;bit++)if(!(f&(1<<(7-bit))))flags[bit]=' '; + fprintf(fp,"af=%04X %s\n",af2,flags); + } + if(i!=i2)fprintf(fp,"ir=%02X%02X\n",i2=i,r); + putc('\n',fp); + } + if(pc==breakpoint && pc) + breaks++; /* some code at which to set a breakpoint */ + a=AF>>8; f=AF; h=HL>>8; l=HL; d=DE>>8; e=DE; b=BC>>8; c=BC; +#endif +/* +{ + static int tr = 0; + static int id = 0; +// static byte b = 0; +// + if (pc == 0x1177) tr = 1; + if (pc == 0x1185) tr = 0; + if (tr >= 1) ++id; + if (tr >= 1) printf("%d: PC=%04x %02x AF=%02x:%02x BC=%04x DE=%04x HL=%04x IX=%04x IY=%04x\r\n", + id, pc, fetch(pc), a,f, bc, de, hl, ix, iy); +} +*/ + intsample=1; + op=fetch(pc); + pc++; + radjust++; + switch(op){ +#include "z80ops.h" + } +/*** + * ZXCC doesn't do interrupts at all, so all this is commented out + if(tstates>=int_cycles && intsample){ + tstates-=int_cycles; + frames++; + // Carry out X-related tasks (including waiting for timer + // signal if necessary) + switch(interrupt()){ + case Z80_quit: +#ifdef DEBUG + if(fp)fclose(fp); +#endif + return; + case Z80_NMI: + if(fetch(pc)==0x76)pc++; + iff2=iff1; + iff1=0; + // The Z80 performs a machine fetch cycle for 5 Tstates + // but ignores the result. It takes a further 10 Tstates + // to jump to the NMI routine at 0x66. + tstates+=15; + push2(pc); + pc=0x66; + break; + case Z80_reset: + a=f=b=c=d=e=h=l=a1=f1=b1=c1=d1=e1= + h1=l1=i=r=iff1=iff2=im=0; + ix=iy=sp=pc=0; + radjust=0; + break; +#ifdef DEBUG + case Z80_log: + if(fp){ + fclose(fp); + fp=0; + fputs("Logging turned off\n",stderr); + } else { + fp=fopen(config.log,"a"); + if(fp)fprintf(stderr,"Logging to file %s\n",config.log); + else perror(config.log); + } + break; +#endif + + case Z80_load: + stopwatch(); + if(snapload()){ + a=snapa; + f=snapf; + b=snapb; + c=snapc; + d=snapd; + e=snape; + h=snaph; + l=snapl; + a1=snapa1; + f1=snapf1; + b1=snapb1; + c1=snapc1; + d1=snapd1; + e1=snape1; + h1=snaph1; + l1=snapl1; + iff1=snapiff1; + iff2=snapiff2; + i=snapi; + r=snapr; + radjust=r; + im=snapim; + ix=snapix; + iy=snapiy; + sp=snapsp; + pc=snappc; + } + startwatch(1); + break; + case Z80_save: + r=(r&0x80)|(radjust&0x7f); + snapa=a; + snapf=f; + snapb=b; + snapc=c; + snapd=d; + snape=e; + snaph=h; + snapl=l; + snapa1=a1; + snapf1=f1; + snapb1=b1; + snapc1=c1; + snapd1=d1; + snape1=e1; + snaph1=h1; + snapl1=l1; + snapiff1=iff1; + snapiff2=iff2; + snapi=i; + snapr=r; + snapim=im; + snapix=ix; + snapiy=iy; + snapsp=sp; + snappc=pc; + snapsave(); + startwatch(1); + break; + + } + if(iff1){ +#ifdef DEBUG + if(fp)fprintf(fp,"Interrupt (im=%d)\n\n",im); +#endif + if(fetch(pc)==0x76)pc++; + iff1=iff2=0; + tstates+=5; // accompanied by an input from the data bus // + switch(im){ + case 0: // IM 0 // + case 1: // undocumented // + case 2: // IM 1 // + // there is little to distinguish between these cases // + tstates+=8; + push2(pc); + pc=0x38; + break; + case 3: // IM 2 // + tstates+=14; + { + int addr=fetch2((i<<8)|0xff); + push2(pc); + pc=addr; + } + } + } + }*/ + } +} diff --git a/Tools/unix/zx/z80.h b/Tools/unix/zx/z80.h new file mode 100644 index 00000000..89e12a84 --- /dev/null +++ b/Tools/unix/zx/z80.h @@ -0,0 +1,86 @@ +/* Miscellaneous definitions for xz80, copyright (C) 1994 Ian Collier. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* [John Elliott, 15 July 2001] + * Copied this file into ZXCC, a CP/M emulator. + * Since ZXCC's memory is a flat 64k space and will never be bank-switched, + * the bank-switching code is removed. + * Since ZXCC has no memory-mapped screen, all the screen management code + * goes as well. + * Since ZXCC doesn't need its speed regulated, all the speed regulation + * code goes as well. + * Since ZXCC doesn't save or load snapshots... OK, you get the idea. + */ + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#define Z80_quit 1 +#define Z80_NMI 2 +#define Z80_reset 3 +#define Z80_load 4 +#define Z80_save 5 +#define Z80_log 6 + +unsigned int in(); +unsigned int out(); +//int interrupt(); +int snapload(); +void snapsave(); +void mainloop(word xpc, word xsp); +void eachframe(); +void itimeron(); +void itimeroff(); +void startwatch(); +unsigned long stopwatch(); +void requester(); +int loader(); +int saver(); +void multiloader(); +void usage(); +void version(); +void drawborder(); + +#define fetch(x) (RAM[x]) +#define fetch2(x) ((fetch((x)+1)<<8)|fetch(x)) + +#define store(x,y) do { RAM[(x)] = (y); } while(0) + +#define store2b(x,hi,lo) do {\ + RAM[(x)]=(lo); \ + RAM[((x+1) & 0xFFFF)]=(hi); } while(0) + +#define store2(x,y) store2b(x,(y)>>8,y) + +#ifdef __GNUC__ +static void inline storefunc(unsigned short ad,unsigned char b){ + store(ad,b); +} +#undef store +#define store(x,y) storefunc(x,y) + +static void inline store2func(unsigned short ad,unsigned char b1,unsigned char b2){ + store2b(ad,b1,b2); +} +#undef store2b +#define store2b(x,hi,lo) store2func(x,hi,lo) +#endif + +#define bc ((b<<8)|c) +#define de ((d<<8)|e) +#define hl ((h<<8)|l) diff --git a/Tools/unix/zx/z80ops.h b/Tools/unix/zx/z80ops.h new file mode 100644 index 00000000..a9687741 --- /dev/null +++ b/Tools/unix/zx/z80ops.h @@ -0,0 +1,1304 @@ +/* Emulations of the Z80 CPU instruction set - part of xz80. + * Copyright (C) 1994 Ian Collier. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define instr(opcode,cycles) case opcode: {tstates+=cycles +#define HLinstr(opcode,cycles,morecycles) \ + case opcode: {unsigned short addr; \ + tstates+=cycles; \ + if(ixoriy==0)addr=hl; \ + else tstates+=morecycles, \ + addr=(ixoriy==1?ix:iy)+ \ + (signed char)fetch(pc),\ + pc++ +#define endinstr }; break + +#define cy (f&1) + +#define xh (ixoriy==0?h:ixoriy==1?(ix>>8):(iy>>8)) +#define xl (ixoriy==0?l:ixoriy==1?(ix&0xff):(iy&0xff)) + +#define setxh(x) (ixoriy==0?(h=(x)):ixoriy==1?(ix=(ix&0xff)|((x)<<8)):\ + (iy=(iy&0xff)|((x)<<8))) +#define setxl(x) (ixoriy==0?(l=(x)):ixoriy==1?(ix=(ix&0xff00)|(x)):\ + (iy=(iy&0xff00)|(x))) + +#define inc(var) /* 8-bit increment */ ( var++,\ + f=(f&1)|(var&0xa8)|\ + ((!(var&15))<<4)|((!var)<<6)|\ + ((var==128)<<2)\ + ) +#define dec(var) /* 8-bit decrement */ ( f=(f&1)|((!(var&15))<<4)|2,\ + --var,\ + f|=(var&0xa8)|((var==127)<<2)|\ + ((!var)<<6)\ + ) +#define swap(x,y) {unsigned char t=x; x=y; y=t;} +#define addhl(hi,lo) /* 16-bit add */ if(!ixoriy){\ + unsigned short t;\ + l=t=l+(lo);\ + f=(f&0xc4)|(((t>>=8)+(h&0x0f)+((hi)&0x0f)>15)<<4);\ + h=t+=h+(hi);\ + f|=(h&0x28)|(t>>8);\ + }\ + else do{unsigned long t=(ixoriy==1?ix:iy);\ + f=(f&0xc4)|(((t&0xfff)+((hi<<8)|lo)>0xfff)<<4);\ + t+=(hi<<8)|lo;\ + if(ixoriy==1)ix=t; else iy=t;\ + f|=((t>>8)&0x28)|(t>>16);\ + } while(0) +#define adda(x,c) /* 8-bit add */ do{unsigned short y;\ + unsigned char z=(x);\ + y=a+z+(c);\ + f=(y&0xa8)|(y>>8)|(((a&0x0f)+(z&0x0f)+(c)>15)<<4)|\ + (((~a^z)&0x80&(y^a))>>5);\ + f|=(!(a=y))<<6;\ + } while(0) +#define suba(x,c) /* 8-bit subtract */ do{unsigned short y;\ + unsigned char z=(x);\ + y=(a-z-(c))&0x1ff;\ + f=(y&0xa8)|(y>>8)|(((a&0x0f)<(z&0x0f)+(c))<<4)|\ + (((a^z)&0x80&(y^a))>>5)|2;\ + f|=(!(a=y))<<6;\ + } while(0) +#define cpa(x) /* 8-bit compare */ do{unsigned short y;\ + unsigned char z=(x);\ + y=(a-z)&0x1ff;\ + f=(y&0xa8)|(y>>8)|(((a&0x0f)<(z&0x0f))<<4)|\ + (((a^z)&0x80&(y^a))>>5)|2|((!y)<<6);\ + } while(0) +#define anda(x) /* logical and */ do{\ + a&=(x);\ + f=(a&0xa8)|((!a)<<6)|0x10|parity(a);\ + } while(0) +#define xora(x) /* logical xor */ do{\ + a^=(x);\ + f=(a&0xa8)|((!a)<<6)|parity(a);\ + } while(0) +#define ora(x) /* logical or */ do{\ + a|=(x);\ + f=(a&0xa8)|((!a)<<6)|parity(a);\ + } while(0) + +#define jr /* execute relative jump */ do{int j=(signed char)fetch(pc);\ + pc+=j+1;\ + tstates+=5;\ + } while(0) +#define jp /* execute jump */ (pc=fetch2(pc)) +#define call /* execute call */ do{\ + tstates+=7;\ + push2(pc+2);\ + jp;\ + } while(0) +#define ret /* execute return */ do{\ + tstates+=6;\ + pop2(pc);\ + } while(0) +#define pop2(var) /* pop 16-bit register */ (var=fetch2(sp),sp+=2) +#define pop1(v1,v2) /* pop register pair */ (v2=fetch(sp),\ + v1=fetch(sp+1),sp+=2) +#define push2(val) /* push 16-bit register */ do{sp-=2;store2(sp,(val));}\ + while(0) +#define push1(v1,v2) /* push register pair */ do{sp-=2;\ + store2b(sp,v1,v2);\ + }while(0) + +instr(0,4); + /* nop */ +endinstr; + +instr(1,10); + c=fetch(pc),pc++; + b=fetch(pc),pc++; +endinstr; + +instr(2,7); + store(bc,a); +endinstr; + +instr(3,6); + if(!++c)b++; +endinstr; + +instr(4,4); + inc(b); +endinstr; + +instr(5,4); + dec(b); +endinstr; + +instr(6,7); + b=fetch(pc),pc++; +endinstr; + +instr(7,4); + a=(a<<1)|(a>>7); + f=(f&0xc4)|(a&0x29); +endinstr; + +instr(8,4); + swap(a,a1); + swap(f,f1); +endinstr; + +instr(9,11); + addhl(b,c); +endinstr; + +instr(10,7); + a=fetch(bc); +endinstr; + +instr(11,6); + if(!c--)b--; +endinstr; + +instr(12,4); + inc(c); +endinstr; + +instr(13,4); + dec(c); +endinstr; + +instr(14,7); + c=fetch(pc),pc++; +endinstr; + +instr(15,4); + f=(f&0xc4)|(a&1); + a=(a>>1)|(a<<7); + f|=a&0x28; +endinstr; + +instr(16,8); + if(!--b)pc++; + else jr; +endinstr; + +instr(17,10); + e=fetch(pc),pc++; + d=fetch(pc),pc++; +endinstr; + +instr(18,7); + store(de,a); +endinstr; + +instr(19,6); + if(!++e)d++; +endinstr; + +instr(20,4); + inc(d); +endinstr; + +instr(21,4); + dec(d); +endinstr; + +instr(22,7); + d=fetch(pc),pc++; +endinstr; + +instr(23,4); + {int t=a>>7; + a=(a<<1)|(f&1); + f=(f&0xc4)|(a&0x28)|t; + } +endinstr; + +instr(24,7); + jr; +endinstr; + +instr(25,11); + addhl(d,e); +endinstr; + +instr(26,7); + a=fetch(de); +endinstr; + +instr(27,6); + if(!e--)d--; +endinstr; + +instr(28,4); + inc(e); +endinstr; + +instr(29,4); + dec(e); +endinstr; + +instr(30,7); + e=fetch(pc),pc++; +endinstr; + +instr(31,4); + {int t=a&1; + a=(a>>1)|(f<<7); + f=(f&0xc4)|(a&0x28)|t; + } +endinstr; + +instr(32,7); + if(f&0x40)pc++; + else jr; +endinstr; + +instr(33,10); + if(!ixoriy){ + l=fetch(pc),pc++; + h=fetch(pc),pc++; + } + else { + if(ixoriy==1)ix=fetch2(pc); + else iy=fetch2(pc); + pc+=2; + } +endinstr; + +instr(34,16); + {unsigned short addr=fetch2(pc); + pc+=2; + if(!ixoriy)store2b(addr,h,l); + else if(ixoriy==1)store2(addr,ix); + else store2(addr,iy); + } +endinstr; + +instr(35,6); + if(!ixoriy){if(!++l)h++;} + else if(ixoriy==1)ix++; + else iy++; +endinstr; + +instr(36,4); + if(ixoriy==0)inc(h); + else{unsigned char t; + t=(ixoriy==1?ix:iy)>>8; + inc(t); + if(ixoriy==1)ix=(ix&0xff)|(t<<8); + else iy=(iy&0xff)|(t<<8); + } +endinstr; + +instr(37,4); + if(ixoriy==0)dec(h); + else{unsigned char t; + t=(ixoriy==1?ix:iy)>>8; + dec(t); + if(ixoriy==1)ix=(ix&0xff)|(t<<8); + else iy=(iy&0xff)|(t<<8); + } +endinstr; + +instr(38,7); + setxh(fetch(pc)); + pc++; +endinstr; + +instr(39,4); + { + unsigned char incr=0, carry=cy; + if((f&0x10) || (a&0x0f)>9) incr=6; + if((f&1) || (a>>4)>9) incr|=0x60; + if(f&2)suba(incr,0); + else { + if(a>0x90 && (a&15)>9)incr|=0x60; + adda(incr,0); + } + f=((f|carry)&0xfb)|parity(a); + } +endinstr; + +instr(40,7); + if(f&0x40)jr; + else pc++; +endinstr; + +instr(41,11); + if(!ixoriy)addhl(h,l); + else if(ixoriy==1)addhl((ix>>8),(ix&0xff)); + else addhl((iy>>8),(iy&0xff)); +endinstr; + +instr(42,16); + {unsigned short addr=fetch2(pc); + pc+=2; + if(!ixoriy){ + l=fetch(addr); + h=fetch(addr+1); + } + else if(ixoriy==1)ix=fetch2(addr); + else iy=fetch2(addr); + } +endinstr; + +instr(43,6); + if(!ixoriy){if(!l--)h--;} + else if(ixoriy==1)ix--; + else iy--; +endinstr; + +instr(44,4); + if(!ixoriy)inc(l); + else {unsigned char t; + t=(ixoriy==1?ix:iy); + inc(t); + if(ixoriy==1)ix=(ix&0xff00)|t; + else iy=(iy&0xff00)|t; + } +endinstr; + +instr(45,4); + if(!ixoriy)dec(l); + else {unsigned char t; + t=(ixoriy==1?ix:iy); + dec(t); + if(ixoriy==1)ix=(ix&0xff00)|t; + else iy=(iy&0xff00)|t; + } +endinstr; + +instr(46,7); + setxl(fetch(pc)); + pc++; +endinstr; + +instr(47,4); + a=~a; + f=(f&0xc5)|(a&0x28)|0x12; +endinstr; + +instr(48,7); + if(f&1)pc++; + else jr; +endinstr; + +instr(49,10); + sp=fetch2(pc); + pc+=2; +endinstr; + +instr(50,13); + {unsigned short addr=fetch2(pc); + pc+=2; + store(addr,a); + } +endinstr; + +instr(51,6); + sp++; +endinstr; + +HLinstr(52,11,8); + {unsigned char t=fetch(addr); + inc(t); + store(addr,t); + } +endinstr; + +HLinstr(53,11,8); + {unsigned char t=fetch(addr); + dec(t); + store(addr,t); + } +endinstr; + +HLinstr(54,10,5); + store(addr,fetch(pc)); + pc++; +endinstr; + +instr(55,4); + f=(f&0xc4)|1|(a&0x28); +endinstr; + +instr(56,7); + if(f&1)jr; + else pc++; +endinstr; + +instr(57,11); + addhl((sp>>8),(sp&0xff)); +endinstr; + +instr(58,13); + {unsigned short addr=fetch2(pc); + pc+=2; + a=fetch(addr); + } +endinstr; + +instr(59,6); + sp--; +endinstr; + +instr(60,4); + inc(a); +endinstr; + +instr(61,4); + dec(a); +endinstr; + +instr(62,7); + a=fetch(pc),pc++; +endinstr; + +instr(63,4); + f=(f&0xc4)|(cy^1)|(cy<<4)|(a&0x28); +endinstr; + +instr(0x40,4); + /* ld b,b */ +endinstr; + +instr(0x41,4); + b=c; +endinstr; + +instr(0x42,4); + b=d; +endinstr; + +instr(0x43,4); + b=e; +endinstr; + +instr(0x44,4); + b=xh; +endinstr; + +instr(0x45,4); + b=xl; +endinstr; + +HLinstr(0x46,7,8); + b=fetch(addr); +endinstr; + +instr(0x47,4); + b=a; +endinstr; + +instr(0x48,4); + c=b; +endinstr; + +instr(0x49,4); + /* ld c,c */ +endinstr; + +instr(0x4a,4); + c=d; +endinstr; + +instr(0x4b,4); + c=e; +endinstr; + +instr(0x4c,4); + c=xh; +endinstr; + +instr(0x4d,4); + c=xl; +endinstr; + +HLinstr(0x4e,7,8); + c=fetch(addr); +endinstr; + +instr(0x4f,4); + c=a; +endinstr; + +instr(0x50,4); + d=b; +endinstr; + +instr(0x51,4); + d=c; +endinstr; + +instr(0x52,4); + /* ld d,d */ +endinstr; + +instr(0x53,4); + d=e; +endinstr; + +instr(0x54,4); + d=xh; +endinstr; + +instr(0x55,4); + d=xl; +endinstr; + +HLinstr(0x56,7,8); + d=fetch(addr); +endinstr; + +instr(0x57,4); + d=a; +endinstr; + +instr(0x58,4); + e=b; +endinstr; + +instr(0x59,4); + e=c; +endinstr; + +instr(0x5a,4); + e=d; +endinstr; + +instr(0x5b,4); + /* ld e,e */ +endinstr; + +instr(0x5c,4); + e=xh; +endinstr; + +instr(0x5d,4); + e=xl; +endinstr; + +HLinstr(0x5e,7,8); + e=fetch(addr); +endinstr; + +instr(0x5f,4); + e=a; +endinstr; + +instr(0x60,4); + setxh(b); +endinstr; + +instr(0x61,4); + setxh(c); +endinstr; + +instr(0x62,4); + setxh(d); +endinstr; + +instr(0x63,4); + setxh(e); +endinstr; + +instr(0x64,4); + /* ld h,h */ +endinstr; + +instr(0x65,4); + setxh(xl); +endinstr; + +HLinstr(0x66,7,8); + h=fetch(addr); +endinstr; + +instr(0x67,4); + setxh(a); +endinstr; + +instr(0x68,4); + setxl(b); +endinstr; + +instr(0x69,4); + setxl(c); +endinstr; + +instr(0x6a,4); + setxl(d); +endinstr; + +instr(0x6b,4); + setxl(e); +endinstr; + +instr(0x6c,4); + setxl(xh); +endinstr; + +instr(0x6d,4); + /* ld l,l */ +endinstr; + +HLinstr(0x6e,7,8); + l=fetch(addr); +endinstr; + +instr(0x6f,4); + setxl(a); +endinstr; + +HLinstr(0x70,7,8); + store(addr,b); +endinstr; + +HLinstr(0x71,7,8); + store(addr,c); +endinstr; + +HLinstr(0x72,7,8); + store(addr,d); +endinstr; + +HLinstr(0x73,7,8); + store(addr,e); +endinstr; + +HLinstr(0x74,7,8); + store(addr,h); +endinstr; + +HLinstr(0x75,7,8); + store(addr,l); +endinstr; + +instr(0x76,4); + /* Was HALT - ZXCC ignores HALT */ +endinstr; + +HLinstr(0x77,7,8); + store(addr,a); +endinstr; + +instr(0x78,4); + a=b; +endinstr; + +instr(0x79,4); + a=c; +endinstr; + +instr(0x7a,4); + a=d; +endinstr; + +instr(0x7b,4); + a=e; +endinstr; + +instr(0x7c,4); + a=xh; +endinstr; + +instr(0x7d,4); + a=xl; +endinstr; + +HLinstr(0x7e,7,8); + a=fetch(addr); +endinstr; + +instr(0x7f,4); + /* ld a,a */ +endinstr; + +instr(0x80,4); + adda(b,0); +endinstr; + +instr(0x81,4); + adda(c,0); +endinstr; + +instr(0x82,4); + adda(d,0); +endinstr; + +instr(0x83,4); + adda(e,0); +endinstr; + +instr(0x84,4); + adda(xh,0); +endinstr; + +instr(0x85,4); + adda(xl,0); +endinstr; + +HLinstr(0x86,7,8); + adda(fetch(addr),0); +endinstr; + +instr(0x87,4); + adda(a,0); +endinstr; + +instr(0x88,4); + adda(b,cy); +endinstr; + +instr(0x89,4); + adda(c,cy); +endinstr; + +instr(0x8a,4); + adda(d,cy); +endinstr; + +instr(0x8b,4); + adda(e,cy); +endinstr; + +instr(0x8c,4); + adda(xh,cy); +endinstr; + +instr(0x8d,4); + adda(xl,cy); +endinstr; + +HLinstr(0x8e,7,8); + adda(fetch(addr),cy); +endinstr; + +instr(0x8f,4); + adda(a,cy); +endinstr; + +instr(0x90,4); + suba(b,0); +endinstr; + +instr(0x91,4); + suba(c,0); +endinstr; + +instr(0x92,4); + suba(d,0); +endinstr; + +instr(0x93,4); + suba(e,0); +endinstr; + +instr(0x94,4); + suba(xh,0); +endinstr; + +instr(0x95,4); + suba(xl,0); +endinstr; + +HLinstr(0x96,7,8); + suba(fetch(addr),0); +endinstr; + +instr(0x97,4); + suba(a,0); +endinstr; + +instr(0x98,4); + suba(b,cy); +endinstr; + +instr(0x99,4); + suba(c,cy); +endinstr; + +instr(0x9a,4); + suba(d,cy); +endinstr; + +instr(0x9b,4); + suba(e,cy); +endinstr; + +instr(0x9c,4); + suba(xh,cy); +endinstr; + +instr(0x9d,4); + suba(xl,cy); +endinstr; + +HLinstr(0x9e,7,8); + suba(fetch(addr),cy); +endinstr; + +instr(0x9f,4); + suba(a,cy); +endinstr; + +instr(0xa0,4); + anda(b); +endinstr; + +instr(0xa1,4); + anda(c); +endinstr; + +instr(0xa2,4); + anda(d); +endinstr; + +instr(0xa3,4); + anda(e); +endinstr; + +instr(0xa4,4); + anda(xh); +endinstr; + +instr(0xa5,4); + anda(xl); +endinstr; + +HLinstr(0xa6,7,8); + anda(fetch(addr)); +endinstr; + +instr(0xa7,4); + anda(a); +endinstr; + +instr(0xa8,4); + xora(b); +endinstr; + +instr(0xa9,4); + xora(c); +endinstr; + +instr(0xaa,4); + xora(d); +endinstr; + +instr(0xab,4); + xora(e); +endinstr; + +instr(0xac,4); + xora(xh); +endinstr; + +instr(0xad,4); + xora(xl); +endinstr; + +HLinstr(0xae,7,8); + xora(fetch(addr)); +endinstr; + +instr(0xaf,4); + xora(a); +endinstr; + +instr(0xb0,4); + ora(b); +endinstr; + +instr(0xb1,4); + ora(c); +endinstr; + +instr(0xb2,4); + ora(d); +endinstr; + +instr(0xb3,4); + ora(e); +endinstr; + +instr(0xb4,4); + ora(xh); +endinstr; + +instr(0xb5,4); + ora(xl); +endinstr; + +HLinstr(0xb6,7,8); + ora(fetch(addr)); +endinstr; + +instr(0xb7,4); + ora(a); +endinstr; + +instr(0xb8,4); + cpa(b); +endinstr; + +instr(0xb9,4); + cpa(c); +endinstr; + +instr(0xba,4); + cpa(d); +endinstr; + +instr(0xbb,4); + cpa(e); +endinstr; + +instr(0xbc,4); + cpa(xh); +endinstr; + +instr(0xbd,4); + cpa(xl); +endinstr; + +HLinstr(0xbe,7,8); + cpa(fetch(addr)); +endinstr; + +instr(0xbf,4); + cpa(a); +endinstr; + +instr(0xc0,5); + if(!(f&0x40))ret; +endinstr; + +instr(0xc1,10); + pop1(b,c); +endinstr; + +instr(0xc2,10); + if(!(f&0x40))jp; + else pc+=2; +endinstr; + +instr(0xc3,10); + jp; +endinstr; + +instr(0xc4,10); + if(!(f&0x40))call; + else pc+=2; +endinstr; + +instr(0xc5,11); + push1(b,c); +endinstr; + +instr(0xc6,7); + adda(fetch(pc),0); + pc++; +endinstr; + +instr(0xc7,11); + push2(pc); + pc=0; +endinstr; + +instr(0xc8,5); + if(f&0x40)ret; +endinstr; + +instr(0xc9,4); + ret; +endinstr; + +instr(0xca,10); + if(f&0x40)jp; + else pc+=2; +endinstr; + +instr(0xcb,4); +#include "cbops.h" +endinstr; + +instr(0xcc,10); + if(f&0x40)call; + else pc+=2; +endinstr; + +instr(0xcd,10); + call; +endinstr; + +instr(0xce,7); + adda(fetch(pc),cy); + pc++; +endinstr; + +instr(0xcf,11); + push2(pc); + pc=8; +endinstr; + +instr(0xd0,5); + if(!cy)ret; +endinstr; + +instr(0xd1,10); + pop1(d,e); +endinstr; + +instr(0xd2,10); + if(!cy)jp; + else pc+=2; +endinstr; + +instr(0xd3,11); + tstates+=out(tstates,a,fetch(pc),a); + pc++; +endinstr; + +instr(0xd4,10); + if(!cy)call; + else pc+=2; +endinstr; + +instr(0xd5,11); + push1(d,e); +endinstr; + +instr(0xd6,7); + suba(fetch(pc),0); + pc++; +endinstr; + +instr(0xd7,11); + push2(pc); + pc=16; +endinstr; + +instr(0xd8,5); + if(cy)ret; +endinstr; + +instr(0xd9,4); + swap(b,b1); + swap(c,c1); + swap(d,d1); + swap(e,e1); + swap(h,h1); + swap(l,l1); +endinstr; + +instr(0xda,10); + if(cy)jp; + else pc+=2; +endinstr; + +instr(0xdb,11); + {unsigned short t; + a=t=in(tstates,a,fetch(pc)); + tstates+=t>>8; + pc++; + } +endinstr; + +instr(0xdc,10); + if(cy)call; + else pc+=2; +endinstr; + +instr(0xdd,4); + new_ixoriy=1; + intsample=0; +endinstr; + +instr(0xde,7); + suba(fetch(pc),cy); + pc++; +endinstr; + +instr(0xdf,11); + push2(pc); + pc=24; +endinstr; + +instr(0xe0,5); + if(!(f&4))ret; +endinstr; + +instr(0xe1,10); + if(!ixoriy)pop1(h,l); + else if(ixoriy==1)pop2(ix); + else pop2(iy); +endinstr; + +instr(0xe2,10); + if(!(f&4))jp; + else pc+=2; +endinstr; + +instr(0xe3,19); + if(!ixoriy){ + unsigned short t=fetch2(sp); + store2b(sp,h,l); + l=t; + h=t>>8; + } + else if(ixoriy==1){ + unsigned short t=fetch2(sp); + store2(sp,ix); + ix=t; + } + else{ + unsigned short t=fetch2(sp); + store2(sp,iy); + iy=t; + } +endinstr; + +instr(0xe4,10); + if(!(f&4))call; + else pc+=2; +endinstr; + +instr(0xe5,11); + if(!ixoriy)push1(h,l); + else if(ixoriy==1)push2(ix); + else push2(iy); +endinstr; + +instr(0xe6,7); + anda(fetch(pc)); + pc++; +endinstr; + +instr(0xe7,11); + push2(pc); + pc=32; +endinstr; + +instr(0xe8,5); + if(f&4)ret; +endinstr; + +instr(0xe9,4); + pc=!ixoriy?hl:ixoriy==1?ix:iy; +endinstr; + +instr(0xea,10); + if(f&4)jp; + else pc+=2; +endinstr; + +instr(0xeb,4); + swap(h,d); + swap(e,l); +endinstr; + +instr(0xec,10); + if(f&4)call; + else pc+=2; +endinstr; + +instr(0xed,4); +#include"edops.h" +endinstr; + +instr(0xee,7); + xora(fetch(pc)); + pc++; +endinstr; + +instr(0xef,11); + push2(pc); + pc=40; +endinstr; + +instr(0xf0,5); + if(!(f&0x80))ret; +endinstr; + +instr(0xf1,10); + pop1(a,f); +endinstr; + +instr(0xf2,10); + if(!(f&0x80))jp; + else pc+=2; +endinstr; + +instr(0xf3,4); + iff1=iff2=0; + intsample=0; +endinstr; + +instr(0xf4,10); + if(!(f&0x80))call; + else pc+=2; +endinstr; + +instr(0xf5,11); + push1(a,f); +endinstr; + +instr(0xf6,7); + ora(fetch(pc)); + pc++; +endinstr; + +instr(0xf7,11); + push2(pc); + pc=48; +endinstr; + +instr(0xf8,5); + if(f&0x80)ret; +endinstr; + +instr(0xf9,6); + sp=!ixoriy?hl:ixoriy==1?ix:iy; +endinstr; + +instr(0xfa,10); + if(f&0x80)jp; + else pc+=2; +endinstr; + +instr(0xfb,4); + iff1=iff2=1; + intsample=0; +endinstr; + +instr(0xfc,10); + if(f&0x80)call; + else pc+=2; +endinstr; + +instr(0xfd,4); + new_ixoriy=2; + intsample=0; +endinstr; + +instr(0xfe,7); + cpa(fetch(pc)); + pc++; +endinstr; + +instr(0xff,11); + push2(pc); + pc=56; +endinstr; + diff --git a/Tools/unix/zx/zx.c b/Tools/unix/zx/zx.c new file mode 100644 index 00000000..0ea315d0 --- /dev/null +++ b/Tools/unix/zx/zx.c @@ -0,0 +1,437 @@ +#include "zx.h" + +#ifdef WIN32 +#include "windows.h" +#endif + +/* Global variables */ + +char *progname; +char **argv; +int argc; + +byte cpm_drive; +char *mypath; + +byte cpm_user; +extern byte cpm_error; + +byte RAM[65536]; /* The Z80's address space */ + +void load_comfile(void); /* Forward declaration */ + +static int deinit_term, deinit_gsx; + +void dump_regs(FILE *fp, byte a, byte b, byte c, byte d, byte e, byte f, + byte h, byte l, word pc, word ix, word iy) +{ + fprintf(fp, "\tAF=%02x%02x BC=%02x%02x DE=%02x%02x HL=%02x%02x\n" + "\tIX=%04x IY=%04x PC=%04x\n", + a,f,b,c,d,e,h,l,pc,ix,iy); +} + + + +char *parse_to_fcb(char *s, int afcb) +{ + byte *fcb = &RAM[afcb+1]; + + RAM[afcb] = 0; + memset(fcb, ' ', 11); + + while (1) + { + if (s[0] == 0) break; + if (s[0] == ' ') {++s; continue; } + if (s[1] == ':') + { + RAM[afcb] = s[0] - '@'; + if (RAM[afcb] > 16) RAM[afcb] -= 0x20; + s+=2; + continue; + } + if (s[0] == '.') + { + ++s; + fcb = &RAM[afcb+9]; + continue; + } + *fcb = *s; if (islower(*fcb)) *fcb = toupper(*fcb); + ++s; + ++fcb; + if (fcb >= &RAM[afcb+12]) break; + } + return s; +} + + +void Msg(char *s, ...) +{ +#ifdef DEBUG + va_list ap; + + va_start(ap, s); + printf("%s trace: ", progname); + vprintf(s, ap); + fflush(stdout); + if (s[strlen(s) - 1] == '\n') putchar('\r'); + va_end(ap); +#endif +} + + +void ed_fe(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, + byte *h, byte *l, word *pc, word *ix, word *iy) +{ + switch(*a) + { + case 0xC0: + cpmbdos(a,b,c,d,e,f,h,l,pc,ix,iy); + break; + + case 0xC1: + load_comfile(); + break; + + case 0xC2: + fprintf(stderr,"%s: Incompatible BIOS.BIN\n", progname); + zx_term(); + zx_exit(1); + + case 0xC3: + cpmbios(a,b,c,d,e,f,h,l,pc,ix,iy); + break; + + default: + fprintf(stderr, "%s: Z80 encountered invalid trap\n", progname); + dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy); + zx_term(); + zx_exit(1); + + } +} + + +/* + * load_bios() loads the minimal CP/M BIOS and BDOS. + * + */ + +void load_bios(void) +{ + int bios_len; + + FILE * fp = NULL; + char biospath[CPM_MAXPATH + 1] = ""; + +#ifdef WIN32 + if (!fp) + { + GetModuleFileName(NULL, biospath, sizeof(biospath)); + strcpy(strrchr(biospath, '\\'), "\\bios.bin"); + fp = fopen(biospath, "rb"); + } +#else + if (!fp) { + strcpy(biospath, mypath); + strcpy(strrchr(biospath, '/'), "/bios.bin"); + fp = fopen(biospath, "rb"); + } +#endif + + if (!fp && BINDIR80) + { + strcpy(biospath, BINDIR80); + strcat(biospath, "bios.bin"); + fp = fopen(biospath, "rb"); + } + + if (!fp) fp = fopen("bios.bin", "rb"); + + if (!fp) + { + fprintf(stderr,"%s: Cannot locate bios.bin\n", progname); + zx_term(); + zx_exit(1); + } + bios_len = fread(RAM + 0xFE00, 1, 512, fp); + if (bios_len < 1 || ferror(fp)) + { + fclose(fp); + fprintf(stderr,"%s: Cannot load bios.bin\n", progname); + zx_term(); + zx_exit(1); + } + fclose(fp); + + Msg("Loaded %d bytes of BIOS\n", bios_len); +} + +/* + * try_com() attempts to open file, file.com, file.COM, file.cpm and file.CPM + * + */ + +FILE *try_com(char *s) +{ + char fname[CPM_MAXPATH + 1]; + FILE *fp; + + strcpy(fname, s); + fp = fopen(s, "rb"); if (fp) return fp; + sprintf(s,"%s.com", fname); fp = fopen(s, "rb"); if (fp) return fp; + sprintf(s,"%s.COM", fname); fp = fopen(s, "rb"); if (fp) return fp; + sprintf(s,"%s.cpm", fname); fp = fopen(s, "rb"); if (fp) return fp; + sprintf(s,"%s.CPM", fname); fp = fopen(s, "rb"); if (fp) return fp; + + strcpy(s, fname); + return NULL; +} + +/* + * load_comfile() loads the COM file whose name was passed as a parameter. + * + */ + + +void load_comfile(void) +{ + int com_len; + char fname[CPM_MAXPATH + 1] = ""; + FILE *fp; + + if (BINDIR80) strcpy(fname, BINDIR80); + strcat(fname, argv[1]); + fp = try_com(fname); + if (!fp) + { + strcpy(fname, argv[1]); + fp = try_com(fname); + } + if (!fp) + { + fprintf(stderr,"%s: Cannot locate %s, %s.com, %s.COM, %s.cpm _or_ %s.CPM\r\n", + progname, argv[1], argv[1], argv[1], argv[1], argv[1]); + zx_term(); + zx_exit(1); + } + com_len = fread(RAM + 0x0100, 1, 0xFD00, fp); + if (com_len < 1 || ferror(fp)) + { + fclose(fp); + fprintf(stderr,"%s: Cannot load %s\n", progname, fname); + zx_term(); + zx_exit(1); + } + fclose(fp); + + Msg("Loaded %d bytes from %s\n", com_len, fname); +} + +unsigned int in() { return 0; } +unsigned int out() { return 0; } + + + +/* + * xltname: Convert a unix filepath into a CP/M compatible drive:name form. + * The unix filename must be 8.3 or the CP/M code will reject it. + * + * This uses the library xlt_name to do the work, and then just strcat()s + * the result to the command line. + */ + +void zx_xltname(char *name, char *pcmd) +{ + char nbuf[CPM_MAXPATH + 1]; + + xlt_name(pcmd, nbuf); + + strcat(name, nbuf); +} + +/* main() parses the arguments to CP/M form. argv[1] is the name of the CP/M + program to load; the remaining arguments are arguments for the CP/M program. + + main() also loads the vestigial CP/M BIOS and does some sanity checks + on the endianness of the host CPU and the sizes of data types. + */ + +int main(int ac, char **av) +{ + int n; + char *pCmd, *str; + + argc = ac; + argv = av; +#ifdef __PACIFIC__ /* Pacific C doesn't support argv[0] */ + progname="ZX"; +#endif + progname = argv[0]; + mypath = strdup(argv[0]); + + /* DJGPP includes the whole path in the program name, which looks + * untidy... + */ + str = strrchr(progname, '/'); + if (!str) str = strrchr(progname, '\\'); + if (str) progname = str + 1; + + if (_isatty(_fileno(stdin))) + Msg("Using interactive console mode\n"); + else + Msg("Using standard input/ouput mode\n"); + + if (sizeof(int) > 8 || sizeof(byte) != 1 || sizeof(word) != 2) + { + fprintf(stderr,"%s: type lengths incorrect; edit typedefs " + "and recompile.\n", progname); + zx_exit(1); + } + + if (argc < 2) + { + fprintf(stderr,"%s: No CP/M program name provided.\n",progname); + zx_exit(1); + } + + + setmode(_fileno(stdin), O_BINARY ); + setmode(_fileno(stdout), O_BINARY ); + + /* Parse arguments. An argument can be either: + + * preceded by a '-', in which case it is copied in as-is, less the + dash; + * preceded by a '+', in which case it is parsed as a filename and + then concatenated to the previous argument; + * preceded by a '+-', in which case it is concatenated without + parsing; + * not preceded by either, in which case it is parsed as a filename. + + So, the argument string "--a -b c +-=q --x +/dev/null" would be rendered + into CP/M form as "-a b p:c=q -xd:null" */ + + if (!fcb_init()) + { + fprintf(stderr, "Could not initialise CPMREDIR library\n"); + zx_exit(1); + } + + xlt_map(0, BINDIR80); /* Establish the 3 fixed mappings */ + xlt_map(1, LIBDIR80); + xlt_map(2, INCDIR80); + pCmd = (char *)RAM + 0x81; + + for (n = 2; n < argc; n++) + { + if (argv[n][0] == '+' && argv[n][1] == '-') + { + /* Append, no parsing */ + strcat(pCmd, argv[n] + 2); + } + else if (!argv[n][0] || argv[n][0] == '-') + { + /* Append with space; no parsing. */ + strcat(pCmd, " "); + strcat(pCmd, argv[n] + 1); + } + else if (argv[n][0] == '+') + { + zx_xltname(pCmd, argv[n]+1); + } + else /* Translate a filename */ + { + strcat(pCmd, " "); + zx_xltname(pCmd, argv[n]); + } + + } + pCmd[0x7F] = 0; /* Truncate to fit the buffer */ + RAM[0x80] = strlen(pCmd); + + str = parse_to_fcb(pCmd, 0x5C); + parse_to_fcb(str, 0x6C); + +/* This statement is very useful when creating a client like zxc or zxas + + printf("Command tail is %s\n", pCmd); +*/ + load_bios(); + + memset(RAM + 0xFE9C, 0, 0x64); /* Zap the SCB */ + RAM[0xFE98] = 0x06; + RAM[0xFE99] = 0xFE; /* FE06, BDOS entry */ + RAM[0xFEA1] = 0x31; /* BDOS 3.1 */ + RAM[0xFEA8] = 0x01; /* UK date format */ + RAM[0xFEAF] = 0x0F; /* CCP drive */ + +#ifdef USE_CPMIO + RAM[0xFEB6] = cpm_term_direct(CPM_TERM_WIDTH, -1); + RAM[0xFEB8] = cpm_term_direct(CPM_TERM_HEIGHT, -1); +#else + RAM[0xFEB6] = 79; + RAM[0xFEB8] = 23; +#endif + RAM[0xFED1] = 0x80; /* Buffer area */ + RAM[0xFED2] = 0xFF; + RAM[0xFED3] = '$'; + RAM[0xFED6] = 0x9C; + RAM[0xFED7] = 0xFE; /* SCB address */ + RAM[0xFED8] = 0x80; /* DMA address */ + RAM[0xFED9] = 0x00; + RAM[0xFEDA] = 0x0F; /* P: */ + RAM[0xFEE6] = 0x01; /* Multi sector count */ + RAM[0xFEFE] = 0x06; + RAM[0xFEFF] = 0xFE; /* BDOS */ + + cpm_drive = 0x0F; /* Start logged into P: */ + cpm_user = 0; /* and user 0 */ + +#ifdef USE_CPMIO + cpm_scr_init(); deinit_term = 1; +#endif +#ifdef USE_CPMGSX + gsx_init(); deinit_gsx = 1; +#endif + + /* Start the Z80 at 0xFF00, with stack at 0xFE00 */ + mainloop(0xFF00, 0xFE00); + + return zx_term(); +} + +void zx_exit(int code) +{ +#ifdef USE_CPMIO + if (deinit_term) cpm_scr_unit(); +#endif +#ifdef USE_CPMGSX + if (deinit_gsx) gsx_deinit(); +#endif + exit(code); +} + +int zx_term(void) +{ + word n; + + + n = RAM[0x81]; /* Get the return code. This is Hi-Tech C */ + n = (n << 8) | RAM[0x80]; /* specific and fails with other COM files */ + + putchar('\n'); + + if (cpm_error != 0) /* The CP/M "set return code" call was used */ + { /* (my modified Hi-Tech C library uses this */ + n = cpm_error; /* call) */ + } + if (n < 256 || n == 0xFFFF) + { + Msg("Return code %d\n", n); + return n; + } + else return 0; +} + + diff --git a/Tools/unix/zx/zx.h b/Tools/unix/zx/zx.h new file mode 100644 index 00000000..dac69ce0 --- /dev/null +++ b/Tools/unix/zx/zx.h @@ -0,0 +1,95 @@ +/* + * Change the directories in these #defines if necessary. Note trailing slash. + */ + +#include "config.h" + +#ifdef __MSDOS__ + #define BINDIR80 "d:/tools/cpm/bin80/" + #define LIBDIR80 "d:/tools/cpm/lib80/" + #define INCDIR80 "d:/tools/cpm/include/" +#else +/* Unless overridden, these are defined by autoconf. Note trailing slash. + #undef BINDIR80 + #undef LIBDIR80 + #undef INCDIR80 + #define BINDIR80 "/usr/local/lib/cpm/bin80/" + #define LIBDIR80 "/usr/local/lib/cpm/lib80/" + #define INCDIR80 "/usr/local/lib/cpm/include80/" +*/ +#endif + +#define SERIAL "ZXCC05" + +/* System include files */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(LINUX) || defined(DARWIN) +#include +#define _isatty(a) isatty(a) +#define _fileno(a) fileno(a) +#define setmode(a,b) +#define O_BINARY 0 +#endif +#ifdef WIN32 +#include +#endif +#include +#include +#ifdef __MSDOS +#include +#endif + +/* Library includes */ + +#ifdef USE_CPMIO +#include "cpmio.h" +#endif + +#ifdef USE_CPMGSX +#include "cpmgsx.h" +#endif + +#include "cpmredir.h" /* BDOS disc simulation */ + +typedef unsigned char byte; /* Must be exactly 8 bits */ +typedef unsigned short word; /* Must be exactly 16 bits */ + +/* Prototypes */ + +void ed_fe (byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, + byte *h, byte *l, word *pc, word *ix, word *iy); +void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, + byte *h, byte *l, word *pc, word *ix, word *iy); +void cpmbios(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, + byte *h, byte *l, word *pc, word *ix, word *iy); +void dump_regs(FILE *fp, byte a, byte b, byte c, byte d, byte e, byte f, + byte h, byte l, word pc, word ix, word iy); +void Msg(char *s, ...); +int zx_term(void); +void zx_exit(int code); + +byte cin(void); +void cout(byte); +int cstat(void); + +/* Global variables */ + +extern char *progname; +extern char **argv; +extern int argc; +extern byte RAM[65536]; /* The Z80's address space */ + +extern int usestdio; + +/* Z80 CPU emulation */ + +#include "z80.h" + diff --git a/Tools/unix/zx/zx.html b/Tools/unix/zx/zx.html new file mode 100644 index 00000000..b425ee1e --- /dev/null +++ b/Tools/unix/zx/zx.html @@ -0,0 +1,131 @@ +zx CP/M Command Line Emulator + +

zx CP/M Command Line Emulator

+ +

zx allows execution of CP/M 2.2 and 3.X application from a +Windows command line. It is compatible with Windows XP and greater (both +32 and 64 bit).

+ +

zx is basically a port of a subset of the zxcc package by John Elliott. +The GPLv2 licensing carries forward. Please refer to the + +zxcc web page for more information.

+ +

While the original zxcc package was generally intended to allow +execution of the Hi-Tech C CP/M compiler under Unix, zx is slightly +more general and intended to allow running most CP/M tools. Specific +changes were incorporated to improve interactice console operation of +CP/M applications.

+ +

Setup

+ +

The zx application (zx.exe) may be copied to any directory for execution. +The bios.bin file must be copied to the same directory. For ease of use, +you will probably want the directory to part of your PATH environment +variable so that you can run the tool from any location.

+ +

You will also need the CP/M applications that you want to run. +zx will load files fromthe current directory or one of the following +directories based on file type. Any of the following environment +variables may be defined to determine where zx searches for the +respective file types:

+ +
    +
  • ZXBINDIR may contain a single path which will +be searched for executable files (usually *.com)
  • +
  • ZXLIBDIR may contain a single path which will +be search for library files (usually *.lib)
  • +
  • ZXINCDIR may contain a single path which will +be searched for include files (usually *.inc)
  • +
+ +

Usage

+ +

In general CP/M applications are executed by prefixing the CP/M command +line with "zx". So for example, you could assemble a test.asm using +rmac with a command line like:

+ +
zx rmac hello
+ +

In this case, rmac.com would need to be in the directory specified by +environment variable ZXBINDIR or in the current directory. Also, +hello.asm would need to be in the current directory.

+ +

Filenames

+ +

Where you would normally enter a CP/M filename you instead enter +a Windows filename. Note that you will need to use a forward slash +instead of the traditional backslash as a directory separator. The +filename itself (as opposed to any directories in +its path) must obey CP/M 8.3 naming conventions.

+ +

Where the documentation requires a CP/M drive letter/user number +you should enter a path complete with trailing slash, for example:

+
-I/usr/src/linux-80/include/
+ +

Technical

+ +

zx emulates a subset of CP/M 3; hopefully enough to run the +most CP/M tools. It can be used as a limited general-purpose CP/M 3 +emulator provided the emulated program only uses a common subset of +system calls.

+ +

Syntax for zx is:

+ +
+zx comfile.com arg1 arg2 ... +
+ +

The comfile is the program to run; zx searches the current +directory and ZXBINDIR for it.

+ +

The arguments are parsed in this way:

+ +
    +
  • Any argument starting with a - sign is passed to the CP/M program as-is, +minus the leading - sign. +
  • Any argument starting with a + sign is parsed as a filename (see below) +and then concatenated to the previous argument. +
  • Any argument starting "+-" is concatenated without being parsed. +
  • All other arguments are parsed as filenames. The path is +converted to a CP/M driveletter. +
+ +

For example: +

+zx foo.com --Q -A /src/main --I +/src/sub +-, +/foo/bar +
+ +

would pass these arguments to foo.com:

+ +
+-Q A d:main -Id:sub,e:bar +
+ +

The other programs are merely wrappers that convert their command lines +into the form required by zx.

+ +

Errors

+ +

Any errors raised by the zx runtime system will be prefixed +with zx:. Some errors you may encounter are:

+ +
+
Unsupported BDOS call
+
Part of CP/M 3 that the program uses has not been emulated. Add the +required functionality to zxbdos.c and recompile.
+
Z80 encountered invalid trap
+
The CP/M program being run attempted to call the zx runtime +system with an unknown call number.
+
+ +

Acknowledgements

+ +
    +
  • zxcc was written by John Elliott
  • +
  • Hi-Tech C was written by Hi-Tech Software.
  • +
  • The Z80 emulation engine was written by Ian Collier.
  • +
  • Thanks to Jacob Nevins, Andy Parkins and others for bug fix suggestions.
  • +
+ + diff --git a/Tools/unix/zx/zxbdos.c b/Tools/unix/zx/zxbdos.c new file mode 100644 index 00000000..06b951ad --- /dev/null +++ b/Tools/unix/zx/zxbdos.c @@ -0,0 +1,561 @@ +#include "zx.h" + +#define BDOS_DEF +#include "zxbdos.h" +#include "zxcbdos.h" +#include "zxdbdos.h" + +#ifdef __MSDOS__ +#include +#endif + +#define BCD(x) (((x % 10)+16*(x/10)) & 0xFF) + +/* Convert time_t to CP/M day count/hours/minutes */ +dword cpmtime(time_t t) +{ + long d = (t / 86400) - 2921; /* CP/M day 0 is unix day 2921 */ + long h = (t % 86400) / 3600; /* Hour, 0-23 */ + long m = (t % 3600) / 60; /* Minute, 0-59 */ + + return (d | (BCD(h) << 16) | (BCD(m) << 24)); +} + + +byte get_time(cpm_word b) +{ + time_t t; + + time(&t); + wr32(b, cpmtime(t)); + + return (BCD(t % 60)); +} + + +/* Functions to access 24-bit & 32-bit words in memory. These are always + little-endian. */ + +void wr24(word addr, dword v) +{ + RAM[addr ] = v & 0xFF; + RAM[addr + 1] = (v >> 8) & 0xFF; + RAM[addr + 2] = (v >> 16) & 0xFF; +} + +void wr32(word addr, dword v) +{ + RAM[addr ] = v & 0xFF; + RAM[addr + 1] = (v >> 8) & 0xFF; + RAM[addr + 2] = (v >> 16) & 0xFF; + RAM[addr + 3] = (v >> 24) & 0xFF; +} + +dword rd24(word addr) +{ + register dword rv = RAM[addr + 2]; + + rv = (rv << 8) | RAM[addr + 1]; + rv = (rv << 8) | RAM[addr]; + return rv; +} + + +dword rd32(word addr) +{ + register dword rv = RAM[addr + 3]; + + rv = (rv << 8) | RAM[addr + 2]; + rv = (rv << 8) | RAM[addr + 1]; + rv = (rv << 8) | RAM[addr]; + return rv; +} + +#define peekw(addr) ( (((word)(RAM[addr + 1])) << 8) | RAM[addr]) + + +/* Get / set the program return code. We store this in 'C' form: 0 for + success, 1-255 for failure. Translate to/from the CP/M form of: + + 0x0000-0xFEFF for success + 0xFF00-0xFFFE for failure + + We also store the actual value so it can be returned + + */ + +word cpm_errcde(word DE) +{ + static word real_err = 0; + + if (DE == 0xFFFF) return real_err; + real_err = DE; + + if (DE == 0xFF00) cpm_error = 1; + else if (DE > 0xFF00) cpm_error = (DE & 0xFF); + else cpm_error = 0; + return 0; +} + + +#ifdef USE_CPMGSX +gsx_byte gsxrd(gsx_word addr) +{ + return RdZ80(addr); +} + +void gsxwr(gsx_word addr, gsx_byte value) +{ + WrZ80(addr, value); +} + +#endif + +#undef bc +#undef de +#undef hl + +void setw(byte *l, byte *h, word w) +{ + *l = (w & 0xFF); + *h = (w >> 8) & 0xFF; +} + +void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, + byte *h, byte *l, word *pc, word *ix, word *iy) +{ + word de = ((*d) << 8) | *e; + word hl = ((*h) << 8) | *l; + byte *pde = &RAM[de]; + byte *pdma = &RAM[cpm_dma]; + word temp; + int retv; + + Msg("BDOS service invoked: C=%02x DE=%04x\n", *c, de); + + switch(*c) + { + case 0: + *pc = 0; + break; + + case 1: /* Get a character */ +#ifdef USE_CPMIO + retv = cpm_bdos_1(); +#else + retv = cin(); +#endif + if (retv < 0) *pc = 0; + setw(l, h, retv); + break; + + case 2: /* Print a character */ +#ifdef USE_CPMIO + if (cpm_bdos_2(*e)) *pc = 0; +#else + cout(*e); +#endif + break; + + case 3: /* No auxin */ + setw(l, h, 0x1A); + break; + + case 4: /* No auxout */ + break; + + case 5: /* No printer */ + break; + + case 6: /* Direct console I/O */ + retv = cpm_bdos_6(*e); + if (retv < 0) *pc = 0; + setw(l, h, retv); + break; + + case 7: /* No auxist */ + case 8: /* No auxost */ + break; + + case 9: /* Print a $-terminated string */ +#ifdef USE_CPMIO + if (cpm_bdos_9((char *)pde)) *pc = 0; +# else + for (temp = 0; RAM[de + temp] != '$'; ++temp) + { + cout(RAM[de + temp]); + } +#endif + break; + + case 0x0A: + bdos_rdline(de, &(*pc)); + break; + + case 0x0B: /* Console status */ + //*l = *h = 0; /* No keys pressed */ + *l = cstat(); + *h = 0; + break; + + case 0x0C: /* Get CP/M version */ + +/* For GENCOM's benefit, claim to be v3.1 */ + + *l = 0x31; /* v3.1 */ + //*l = 0x22; /* v2.2 */ + *h = 0; /* CP/M, no network */ + break; + + case 0x0D: /* Re-log discs */ + fcb_reset(); + break; + + case 0x0E: /* Set default drive */ + setw(l, h, fcb_drive(*e)); + break; + + case 0x0F: /* Open using FCB */ + setw(l, h, x_fcb_open(pde, pdma)); + break; + + case 0x10: /* Close using FCB */ + setw(l, h, fcb_close(pde)); + break; + + case 0x11: /* Find first */ + setw(l, h, fcb_find1(pde, pdma)); + break; + + case 0x12: + setw(l, h, fcb_find2(pde, pdma)); + break; + + case 0x13: /* Delete using FCB */ + setw(l, h, fcb_unlink(pde, pdma)); + break; + + case 0x14: /* Sequential read using FCB */ + setw(l, h, fcb_read(pde, pdma)); + + //Msg("fcb_read L=%02x H=%02x\n", *l, *h); + + break; + + case 0x15: /* Sequential write using FCB */ + setw(l, h, fcb_write(pde, pdma)); + break; + + case 0x16: /* Create using FCB */ + setw(l, h, fcb_creat(pde, pdma)); + break; + + case 0x17: /* Rename using FCB */ + setw(l, h, fcb_rename(pde, pdma)); + break; + + case 0x18: /* Get login vector */ + setw(l, h, fcb_logvec()); + break; + + case 0x19: /* Get default drive */ + setw(l, h, cpm_drive); + break; + + case 0x1A: /* Set DMA */ + Msg("Set DMA to %04x\n", de); + cpm_dma = de; + break; + + case 0x1B: /* Get alloc vector */ + fcb_getalv(RAM + 0xFF80, 0x40); + setw(l, h, 0xFF80); + break; + + case 0x1C: /* Make disc R/O */ + setw(l, h, fcb_rodisk()); + break; + + case 0x1D: /* Get R/O vector */ + setw(l, h, fcb_rovec()); + break; + + case 0x1E: /* Set attributes */ + setw(l, h, fcb_chmod(pde, pdma)); + break; + + case 0x1F: /* Get DPB */ + fcb_getdpb(RAM + 0xFFC0); + setw(l, h, 0xFFC0); + break; /* Whoops. Missed that 'break'. */ + + case 0x20: /* Get/set uid */ + setw(l, h, fcb_user(*e)); + break; + + case 0x21: /* Read a record */ + setw(l, h, fcb_randrd(pde, pdma)); + break; + + case 0x22: /* Write a record */ + setw(l, h, fcb_randwr(pde, pdma)); + break; + + case 0x23: /* Get file size */ + setw(l, h, x_fcb_stat(pde)); + break; + + case 0x24: /* Get file pointer */ + setw(l, h, fcb_tell(pde)); + break; + + case 0x25: + setw(l, h, fcb_resro(de)); + break; + + /* MP/M drive access functions, not implemented */ + + case 0x28: /* Write with 0 fill */ + setw(l, h, fcb_randwz(pde, pdma)); + break; + + /* MP/M record locking functions, not implemented */ + + case 0x2C: /* Set no. of records to read/write */ + setw(l, h, fcb_multirec(*e)); + break; + + case 0x2D: /* Set error mode */ + err_mode = *e; + break; + + case 0x2E: + setw(l, h, fcb_dfree(*e, pdma)); + break; /* Whoops. Missed that 'break'. */ + + /* 0x2F: Chain */ + + case 0x30: + setw(l, h, fcb_sync(*e)); + break; + + case 0x31: + if (pde[1] == 0xFE) + { + RAM[0xFE9C + *pde] = pde[2]; + RAM[0xFE9D + *pde] = pde[3]; + } + else if (RAM[hl + 1] == 0xFF) + { + RAM[0xFE9C + *pde] = pde[2]; + } + else + { + *l = RAM[0xFE9C + *pde]; + *h = RAM[0xFE9D + *pde]; + } + break; + + case 0x32: + temp = *ix; + *ix = 3 * (pde[0] + 1); + *a = pde[1]; + *c = pde[2]; + *b = pde[3]; + *e = pde[4]; + *d = pde[5]; + *l = pde[6]; + *h = pde[7]; + cpmbios(a,b,c,d,e,f,h,l,pc,ix,iy); + *ix = temp; + break; + + case 0x3C: /* Communicate with RSX */ + *h = *l = 0; + break; + + case 0x62: /* Purge */ + setw(l, h, fcb_purge()); + break; + + case 0x63: /* Truncate file */ + setw(l, h, fcb_trunc(pde, pdma)); + break; + + case 0x64: /* Set label */ + setw(l, h, fcb_setlbl(pde, pdma)); + break; + + case 0x65: /* Get label byte */ + setw(l, h, fcb_getlbl(*e)); + break; + + case 0x66: /* Get file date */ + setw(l, h, fcb_date(pde)); + break; + + case 0x67: /* Set password */ + setw(l, h, fcb_setpwd(pde, pdma)); + break; + + case 0x68: /* Set time of day */ + /* Not advisable to let an emulator play with the clock */ + break; + + case 0x69: /* Get time of day */ + setw(l, h, get_time(de)); + break; + + case 0x6A: /* Set default password */ + setw(l, h, fcb_defpwd(pde)); + break; + + case 0x6B: /* Get serial number */ + memcpy(pde, SERIAL, 6); + break; + + case 0x6C: /* 0.03 set error code */ + setw(l, h, cpm_errcde(de)); + break; + +#ifdef USE_CPMIO + case 0x6D: /* Set/get console mode */ + setw(l, h, cpm_bdos_109(de)); + break; + + case 0x6E: /* Set/get string delimiter */ + setw(l, h, cpm_bdos_110(*e)); + break; + + case 0x6F: /* Send fixed length string to screen */ + if (cpm_bdos_111((char *)RAM + peekw(de), + peekw(de + 2))) + *pc = 0; + break; + + case 0x70: /* Send fixed length string to printer */ + break; + + /* 0x71: Strange PCP/M function */ +#else + case 0x6D: /* Set/get console mode */ + setw(l, h, 0); + break; + +#endif + +#ifdef USE_CPMGSX + case 0x73: /* GSX */ + setw(l, h, gsx80(gsxrd, gsxwr, de)); + break; +#endif + + case 0x74: /* Set date stamp */ + setw(l, h, fcb_sdate(pde, pdma)); + break; + + case 0x98: /* Parse filename */ + setw(l, h, fcb_parse((char *)RAM + peekw(de), + (byte *)RAM + peekw(de + 2))); + break; + + default: +#ifdef USE_CPMIO + cpm_scr_unit(); +#endif +#ifdef USE_CPMGSX + gsx_deinit(); +#endif + + fprintf(stderr,"%s: Unsupported BDOS call %d\n", progname, + (int)(*c)); + dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy); + zx_exit(1); + break; + } + + *a = *l; + *b = *h; + + Msg("BDOS service completion.\n"); +} + + + +void cpmbios(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, + byte *h, byte *l, word *pc, word *ix, word *iy) +{ + int func = (((*ix) & 0xFF) / 3) - 1; + + Msg("BIOS service invoked: func=%02x\n", func); + + switch(func) /* BIOS function */ + { + case 1: + zx_exit(zx_term()); /* Program termination */ + break; + + case 2: /* CONST */ +#ifdef USE_CPMIO + *a = cpm_const(); +#else + *a = cpm_bdos_6(0xFE); +#endif + break; + + case 3: /* CONIN */ +#ifdef USE_CPMIO + *a = cpm_conin(); +#else + *a = cpm_bdos_6(0xFD); +#endif + break; + + case 4: /* CONOUT */ +#ifdef USE_CPMIO + cpm_conout(*c); +#else + cpm_bdos_6(*c); +#endif + break; + + case 20: /* DEVTBL */ + setw(l, h, 0xFFFF); + break; + + case 22: /* DRVTBL */ + setw(l, h, 0xFFFF); + break; + + case 26: /* TIME */ + RAM[0xFEF8] = get_time(0xFEF4); + break; + + case 30: /* USERF!!! */ +#ifdef USE_CPMIO + cpm_bdos_110('$'); + cpm_bdos_9("This program has attempted to call USERF, " + "which is not implemented\r\n$"); +#else + printf("This program has attempted to call USERF, which " + "is not implemented.\n"); +#endif + zx_term(); + zx_exit(1); + break; + + default: +#ifdef USE_CPMIO + cpm_scr_unit(); +#endif +#ifdef USE_CPMGSX + gsx_deinit(); +#endif + + fprintf(stderr,"%s: Unsupported BIOS call %d\n", progname, func); + dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy); + zx_exit(1); + } + + Msg("BIOS service completion.\n"); +} diff --git a/Tools/unix/zx/zxbdos.h b/Tools/unix/zx/zxbdos.h new file mode 100644 index 00000000..4111c78e --- /dev/null +++ b/Tools/unix/zx/zxbdos.h @@ -0,0 +1,50 @@ +extern char *progname; +extern char **argv; +extern int argc; + +extern byte cpm_drive; +extern byte cpm_user; + +extern byte RAM[65536]; /* The Z80's address space */ + +extern void Msg(char *s, ...); + +#ifdef BDOS_DEF + +word cpm_dma = 0x80; /* DMA address */ +byte err_mode = 0xFF; +byte rec_multi = 1; +word rec_len = 128; +word ffirst_fcb = 0xFFFF; +byte cpm_error = 0; /* Error code returned by CP/M */ + +#else /* BDOS_DEF */ + +extern word cpm_dma, rec_len, ffirst_fcb; +extern byte err_mode, rec_multi, cpm_error; + +#endif /* BDOS_DEF */ + +#ifndef O_BINARY /* Necessary in DOS, not present in Linux */ +#define O_BINARY 0 +#endif + +typedef unsigned long dword; + +/* Functions in zxbdos.c */ + +void wr24(word addr, dword v); +void wr32(word addr, dword v); +dword rd24(word addr); +dword rd32(word addr); +dword cpmtime(time_t t); +word cpm_errcde(word DE); + +#ifdef USE_CPMGSX +gsx_byte gsxrd(gsx_word addr); +void gsxwr(gsx_word addr, gsx_byte value); +#endif + +void cpmbdos(); +void cpmbios(); + diff --git a/Tools/unix/zx/zxcbdos.c b/Tools/unix/zx/zxcbdos.c new file mode 100644 index 00000000..b441fa7f --- /dev/null +++ b/Tools/unix/zx/zxcbdos.c @@ -0,0 +1,114 @@ +#include "zx.h" +#include "zxbdos.h" +#include "zxcbdos.h" +#include +#ifdef WIN32 +#include +#endif + +/* Line input */ +#ifdef USE_CPMIO + + +void bdos_rdline(word line, word *PC) +{ + char *buf; + + if (!line) line = cpm_dma; + else RAM[line + 1] = 0; + + buf = (char *)&RAM[line]; + + if (cpm_bdos_10(buf)) *PC = 0; +} + +#else /* def USE_CPMIO */ + +void bdos_rdline(word line, word *PC) +{ + int maxlen; + + if (!line) line = cpm_dma; + maxlen = RAM[line]; + + fgets((char *)(RAM + line + 2), maxlen, stdin); + RAM[line + 1] = strlen((char *)(RAM + line + 2)) - 1; + + Msg("Input: [%d] %-*.*s\n", RAM[line + 1], RAM[line + 1], RAM[line +1], (char *)(RAM+line+2)); +} +#endif /* ndef USE_CPMIO */ + +#ifndef USE_CPMIO + + +int cpm_bdos_6(byte e) +{ + int c; + + switch(e) { + case 0xFF: + if (cstat()) return cin(); + return 0; + + case 0xFE: + return cstat(); + + case 0xFD: + return cin(); + + default: + cout(e); + break; + } + return 0; +} +#endif + +#if defined(__MINGW32__) || defined(_MSC_BUILD) || defined(__WATCOMC__) + +byte cin() +{ + if (_isatty(_fileno(stdin))) + return getch(); + else + return getchar(); +} + +void cout(byte c) +{ + if (_isatty(_fileno(stdout))) + putch(c); + else + putchar(c); +} + +int cstat() +{ + if (_isatty(_fileno(stdin))) + return _kbhit() ? 0xFF : 0; + else + return 0xFF; +} + +#else /* defined(__MINGW32__) || defined(_MSC_BUILD) */ + +byte cin() +{ + return getchar(); +} + +void cout(byte c) +{ + putchar(c); +} + +int cstat() +{ + int i; + + ioctl(_fileno(stdin), FIONREAD, &i); + if (i > 0) return 0xff; + return 0; +} + +#endif diff --git a/Tools/unix/zx/zxcbdos.h b/Tools/unix/zx/zxcbdos.h new file mode 100644 index 00000000..7f724441 --- /dev/null +++ b/Tools/unix/zx/zxcbdos.h @@ -0,0 +1,4 @@ +void bdos_rdline(word line, word *PC); +int cpm_bdos_6(byte e); + + diff --git a/Tools/unix/zx/zxdbdos.c b/Tools/unix/zx/zxdbdos.c new file mode 100644 index 00000000..f5f21f3b --- /dev/null +++ b/Tools/unix/zx/zxdbdos.c @@ -0,0 +1,88 @@ +#include "zx.h" +#include "zxbdos.h" +#include "zxdbdos.h" + +/* This file used to deal with all disc-based BDOS calls. + Now the calls have been moved into libcpmredir, it's a bit empty round + here. + + ZXCC does a few odd things when searching, to make Hi-Tech C behave + properly. +*/ + + +/* If a file could not be found on the default drive, try again on a "search" + drive (A: for .COM files, B: for .LIB and .OBJ files) */ + +int fcbforce(byte *fcb, byte *odrv) +{ + byte drive; + char typ[4]; + int n; + + for (n = 0; n < 3; n++) typ[n] = fcb[n+9] & 0x7F; + typ[3] = 0; + + Msg("fcbforce: typ=%s, fcb=%hhx\r\n", typ, *fcb); + + drive = 0; + if (*fcb) return 0; /* not using default drive */ + //if ((*fcb) != 16) return 0; /* not using default drive */ + if (!strcmpi(typ, "COM")) drive = 1; + if (!strcmpi(typ, "LIB")) drive = 2; + if (!strcmpi(typ, "OBJ")) drive = 2; + if (!strcmpi(typ, "H ")) drive = 3; + + Msg("fcbforce: drive=%i\r\n", drive); + + if (!drive) return 0; + + *odrv = *fcb; + *fcb = drive; + return 1; +} + +/* zxcc has a trick with some filenames: If it can't find them where they + should be, and a drive wasn't specified, it searches BINDIR80, + LIBDIR80 or INCDIR80 (depending on the type of the file). + */ + +word x_fcb_open(byte *fcb, byte *dma) +{ + word rv = fcb_open(fcb, dma); + byte odrv; + + Msg("x_fcb_open: rv=%X\r\n", rv); + + if (rv == 0xFF) + { + if (fcbforce(fcb, &odrv)) + { + rv = fcb_open(fcb, dma); + Msg("x_fcb_open: rv=%X\r\n", rv); + *fcb = odrv; + } + } + return rv; +} + + + +word x_fcb_stat(byte *fcb) +{ + word rv = fcb_stat(fcb); + byte odrv; + + if (rv == 0xFF) + { + if (fcbforce(fcb, &odrv)) + { + rv = fcb_stat(fcb); + *fcb = odrv; + } + } + return rv; +} + + + diff --git a/Tools/unix/zx/zxdbdos.h b/Tools/unix/zx/zxdbdos.h new file mode 100644 index 00000000..7a47b801 --- /dev/null +++ b/Tools/unix/zx/zxdbdos.h @@ -0,0 +1,8 @@ + + + +int fcbforce(byte *fcb, byte *odrv); + +word x_fcb_open(byte *fcb, byte *dma); +word x_fcb_stat(byte *fcb); +